def init_shader(self): vertex_shader = "" fragment_shader = "" with open("pycraft/shaders/world.vert") as handle: vertex_shader = handle.read() with open("pycraft/shaders/world.frag") as handle: fragment_shader = handle.read() self.shader = Shader([vertex_shader], [fragment_shader])
class World: def __init__(self): # A Batch is a collection of vertex lists for batched rendering. self.batch = Batch() # A TextureGroup manages an OpenGL texture. self.group = TextureGroup(image.load(TEXTURE_PATH).get_texture()) # A mapping from position to the texture of the block at that position. # This defines all the blocks that are currently in the world. self.objects = {} # Same mapping as `world` but only contains blocks that are shown. self.shown = {} # Mapping from position to a pyglet `VertextList` for all shown blocks. self._shown = {} # Which sector the player is currently in. self.sector = None # Mapping from sector to a list of positions inside that sector. self.sectors = {} self.shader = None self.show_hide_queue = OrderedDict() self.init_gl() # self._initialize() self.init_shader() def init_gl(self): """Basic OpenGL configuration.""" # Set the color of "clear", i.e. the sky, in rgba. glClearColor(0.5, 0.69, 1.0, 1) # Enable culling (not rendering) of back-facing facets -- facets that aren't # visible to you. glEnable(GL_CULL_FACE) # Set the texture minification/magnification function to GL_NEAREST (nearest # in Manhattan distance) to the specified texture coordinates. GL_NEAREST # "is generally faster than GL_LINEAR, but it can produce textured images # with sharper edges because the transition between texture elements is not # as smooth." glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) self.init_gl_fog() def init_gl_fog(self): """Configure the OpenGL fog properties.""" # Enable fog. Fog "blends a fog color with each rasterized pixel fragment's # post-texturing color." glEnable(GL_FOG) # Set the fog color. glFogfv(GL_FOG_COLOR, (GLfloat * 4)(0.5, 0.69, 1.0, 1)) # Say we have no preference between rendering speed and quality. glHint(GL_FOG_HINT, GL_DONT_CARE) # Specify the equation used to compute the blending factor. glFogi(GL_FOG_MODE, GL_LINEAR) # How close and far away fog starts and ends. The closer the start and end, # the denser the fog in the fog range. glFogf(GL_FOG_START, 20.0) glFogf(GL_FOG_END, 60.0) def _initialize(self): """Initialize the world by placing all the blocks.""" n = 80 # 1/2 width and height of world s = 1 # step size y = 0 # initial y height for x in range(-n, n + 1, s): for z in range(-n, n + 1, s): # create a layer stone an grass everywhere. self.add_block((x, y - 3, z), stone, immediate=False) if x in (-n, n) or z in (-n, n): # create outer walls. for dy in range(-2, 3): self.add_block((x, y + dy, z), stone, immediate=False) else: y_max = int((simplex_noise2(x / 30, z / 30) + 1) * 3) for y_lvl in range(y - 2, y_max): if y_lvl < (y_max - 1): block = brick else: block = grass self.add_block((x, y_lvl, z), block, immediate=False) def hit_test(self, position, vector, max_distance=8): """Line of sight search from current position. If a block is intersected it is returned, along with the block previously in the line of sight. If no block is found, return None, None. Parameters ---------- position : tuple of len 3 The (x, y, z) position to check visibility from. vector : tuple of len 3 The line of sight vector. max_distance : int How many blocks away to search for a hit. """ m = 8 x, y, z = position dx, dy, dz = vector previous = None for _ in range(max_distance * m): key = normalize((x, y, z)) if key != previous and key in self.objects: return key, previous previous = key x, y, z = x + dx / m, y + dy / m, z + dz / m return None, None def exposed(self, position): """Returns False is given `position` is surrounded on all 6 sides by blocks, True otherwise. """ x, y, z = position for dx, dy, dz in FACES: if (x + dx, y + dy, z + dz) not in self.objects: return True return False def add_block(self, position, texture, immediate=True): """Add a block with the given `texture` and `position` to the world. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to add. texture : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. immediate : bool Whether or not to draw the block immediately. """ if position in self.objects: self.remove_block(position, immediate) self.objects[position] = texture self.sectors.setdefault(sectorize(position), [[], False])[0].append(position) if immediate: if self.exposed(position): self.show_block(position) self.check_neighbors(position) def remove_block(self, position, immediate=True): """Remove the block at the given `position`. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to remove. immediate : bool Whether or not to immediately remove block from canvas. """ del self.objects[position] # if all the blocks get removed from a sector the sector will stay in # sectors as a 'ghost sector' or something. I don't know if this matters. self.sectors[sectorize(position)][0].remove(position) if immediate: if position in self.shown: self.hide_block(position) self.check_neighbors(position) def check_neighbors(self, position): """Check all blocks surrounding `position` and ensure their visual state is current. This means hiding blocks that are not exposed and ensuring that all exposed blocks are shown. Usually used after a block is added or removed. """ x, y, z = position for dx, dy, dz in FACES: key = (x + dx, y + dy, z + dz) if key not in self.objects: continue if self.exposed(key): if key not in self.shown: self.show_block(key) else: if key in self.shown: self.hide_block(key) def show_block(self, position): """Show the block at the given `position`. This method assumes the block has already been added with add_block() Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to show. immediate : bool Whether or not to show the block immediately. """ texture = self.objects[position] self.shown[position] = texture self._show_block(position, texture) def _show_block(self, position, block): """Private implementation of the `show_block()` method. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to show. texture : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. """ x, y, z = position vertex_data = cube_vertices(x, y, z, 0.5) self.__show_block(position, vertex_data, block) def __show_block(self, position, vertex_data, block): shade_data = cube_shade(1, 1, 1, 1) texture_data = block.texture self._shown[position] = self.batch.add(24, GL_QUADS, self.group, ('v3f/static', vertex_data), ('c3f/static', shade_data), ('t2f/static', texture_data)) def hide_block(self, position): """Hide the block at the given `position`. Hiding does not remove the block from the world. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to hide. immediate : bool Whether or not to immediately remove the block from the canvas. """ self.shown.pop(position) self._shown.pop(position).delete() def show_sector(self, sector): """Ensure all blocks in the given sector that should be shown are drawn to the canvas. """ if not sector in self.sectors: self.generate_sector(sector) self.show_hide_queue[sector] = True def generate_sector(self, sector): """Generate blocks within sector using simplex_noise2 """ for column in reverse_sectorize(sector): x, z = column y_max = int((simplex_noise2(x / 30, z / 30) + 1) * 3) for y_lvl in range(0 - 2, y_max): self.add_block((x, y_lvl, z), sand, immediate=False) else: self.add_block((x, y_lvl, z), grass, immediate=False) # add the safety stone floor. # don't want anyone falling into the ether. self.add_block((x, 0 - 3, z), stone, immediate=False) def hide_sector(self, sector): """Ensure all blocks in the given sector that should be hidden are removed from the canvas. """ self.show_hide_queue[sector] = False def change_sectors(self, before, after): """Move from sector `before` to sector `after`. A sector is a contiguous x, y sub-region of world. Sectors are used to speed up world rendering. """ before_set = set() after_set = set() pad = 4 for dx in range(-pad, pad + 1): for dy in [0]: # range(-pad, pad + 1): for dz in range(-pad, pad + 1): if dx**2 + dy**2 + dz**2 > (pad + 1)**2: continue if before: x, y, z = before before_set.add((x + dx, y + dy, z + dz)) if after: x, y, z = after after_set.add((x + dx, y + dy, z + dz)) show = after_set - before_set hide = before_set - after_set for sector in show: self.show_sector(sector) for sector in hide: self.hide_sector(sector) def _dequeue(self): """Pop the top function from the internal queue and call it.""" sector, show = self.show_hide_queue.popitem(last=False) try: positions, shown = self.sectors[sector] except KeyError: self.show_hide_queue[sector] = show else: print("drawing sector {}".format(str(sector))) if show and not shown: vertex_datas = batch_cube_vertices(positions) for position, vertex_data in zip(positions, vertex_datas): self.__show_block(position, vertex_data, self.objects[position]) elif shown and not show: for position in positions: self.hide_block(position) def process_queue(self, ticks_per_sec): """Process the entire queue while taking periodic breaks. This allows the game loop to run smoothly. The queue contains calls to _show_block() and _hide_block() so this method should be called if add_block() or remove_block() was called with immediate=False """ start = time.clock() while self.show_hide_queue and time.clock( ) - start < 1.0 / ticks_per_sec: self._dequeue() def process_entire_queue(self): """Process the entire queue with no breaks.""" while self.show_hide_queue: self._dequeue() def init_shader(self): vertex_shader = "" fragment_shader = "" with open("pycraft/shaders/world.vert") as handle: vertex_shader = handle.read() with open("pycraft/shaders/world.frag") as handle: fragment_shader = handle.read() self.shader = Shader([vertex_shader], [fragment_shader]) def start_shader(self): self.shader.bind() def stop_shader(self): self.shader.unbind()
class World: def __init__(self): # A Batch is a collection of vertex lists for batched rendering. self.batch = Batch() # A TextureGroup manages an OpenGL texture. self.group = TextureGroup(image.load(TEXTURE_PATH).get_texture()) # A mapping from position to the texture of the block at that position. # This defines all the blocks that are currently in the world. self.objects = {} # Same mapping as `world` but only contains blocks that are shown. self.shown = {} # Mapping from position to a pyglet `VertextList` for all shown blocks. self._shown = {} # Which sector the player is currently in. self.sector = None # Mapping from sector to a list of positions inside that sector. self.sectors = {} self.shader = None # Simple function queue implementation. The queue is populated with # _show_block() and _hide_block() calls self.queue = deque() self.init_gl() self._initialize() self.init_shader() def init_gl(self): """Basic OpenGL configuration.""" # Set the color of "clear", i.e. the sky, in rgba. glClearColor(0.5, 0.69, 1.0, 1) # Enable culling (not rendering) of back-facing facets -- facets that aren't # visible to you. glEnable(GL_CULL_FACE) # Set the texture minification/magnification function to GL_NEAREST (nearest # in Manhattan distance) to the specified texture coordinates. GL_NEAREST # "is generally faster than GL_LINEAR, but it can produce textured images # with sharper edges because the transition between texture elements is not # as smooth." glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) self.init_gl_fog() def init_gl_fog(self): """Configure the OpenGL fog properties.""" # Enable fog. Fog "blends a fog color with each rasterized pixel fragment's # post-texturing color." glEnable(GL_FOG) # Set the fog color. glFogfv(GL_FOG_COLOR, (GLfloat * 4)(0.5, 0.69, 1.0, 1)) # Say we have no preference between rendering speed and quality. glHint(GL_FOG_HINT, GL_DONT_CARE) # Specify the equation used to compute the blending factor. glFogi(GL_FOG_MODE, GL_LINEAR) # How close and far away fog starts and ends. The closer the start and end, # the denser the fog in the fog range. glFogf(GL_FOG_START, 20.0) glFogf(GL_FOG_END, 60.0) def _initialize(self): """Initialize the world by placing all the blocks.""" n = 80 # 1/2 width and height of world s = 1 # step size y = 0 # initial y height for x in range(-n, n + 1, s): for z in range(-n, n + 1, s): # create a layer stone an grass everywhere. self.add_block((x, y - 3, z), stone, immediate=False) if x in (-n, n) or z in (-n, n): # create outer walls. for dy in range(-2, 3): self.add_block((x, y + dy, z), stone, immediate=False) else: y_max = int((simplex_noise2(x/30, z/30) + 1) * 3) for y_lvl in range(y - 2, y_max): if y_lvl < (y_max-1): block = brick else: block = grass self.add_block((x, y_lvl, z), block, immediate=False) def hit_test(self, position, vector, max_distance=8): """Line of sight search from current position. If a block is intersected it is returned, along with the block previously in the line of sight. If no block is found, return None, None. Parameters ---------- position : tuple of len 3 The (x, y, z) position to check visibility from. vector : tuple of len 3 The line of sight vector. max_distance : int How many blocks away to search for a hit. """ m = 8 x, y, z = position dx, dy, dz = vector previous = None for _ in range(max_distance * m): key = normalize((x, y, z)) if key != previous and key in self.objects: return key, previous previous = key x, y, z = x + dx / m, y + dy / m, z + dz / m return None, None def exposed(self, position): """Returns False is given `position` is surrounded on all 6 sides by blocks, True otherwise. """ x, y, z = position for dx, dy, dz in FACES: if (x + dx, y + dy, z + dz) not in self.objects: return True return False def add_block(self, position, texture, immediate=True): """Add a block with the given `texture` and `position` to the world. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to add. texture : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. immediate : bool Whether or not to draw the block immediately. """ if position in self.objects: self.remove_block(position, immediate) self.objects[position] = texture self.sectors.setdefault(sectorize(position), []).append(position) if immediate: if self.exposed(position): self.show_block(position) self.check_neighbors(position) def remove_block(self, position, immediate=True): """Remove the block at the given `position`. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to remove. immediate : bool Whether or not to immediately remove block from canvas. """ del self.objects[position] self.sectors[sectorize(position)].remove(position) if immediate: if position in self.shown: self.hide_block(position) self.check_neighbors(position) def check_neighbors(self, position): """Check all blocks surrounding `position` and ensure their visual state is current. This means hiding blocks that are not exposed and ensuring that all exposed blocks are shown. Usually used after a block is added or removed. """ x, y, z = position for dx, dy, dz in FACES: key = (x + dx, y + dy, z + dz) if key not in self.objects: continue if self.exposed(key): if key not in self.shown: self.show_block(key) else: if key in self.shown: self.hide_block(key) def show_block(self, position, immediate=True): """Show the block at the given `position`. This method assumes the block has already been added with add_block() Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to show. immediate : bool Whether or not to show the block immediately. """ texture = self.objects[position] self.shown[position] = texture if immediate: self._show_block(position, texture) else: self._enqueue(self._show_block, position, texture) def _show_block(self, position, block): """Private implementation of the `show_block()` method. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to show. texture : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. """ x, y, z = position vertex_data = cube_vertices(x, y, z, 0.5) shade_data = cube_shade(1, 1, 1, 1) texture_data = block.texture self._shown[position] = self.batch.add( 24, GL_QUADS, self.group, ('v3f/static', vertex_data), ('c3f/static', shade_data), ('t2f/static', texture_data)) def hide_block(self, position, immediate=True): """Hide the block at the given `position`. Hiding does not remove the block from the world. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to hide. immediate : bool Whether or not to immediately remove the block from the canvas. """ self.shown.pop(position) if immediate: self._hide_block(position) else: self._enqueue(self._hide_block, position) def _hide_block(self, position): """Private implementation of the 'hide_block()` method.""" self._shown.pop(position).delete() def show_sector(self, sector): """Ensure all blocks in the given sector that should be shown are drawn to the canvas. """ for position in self.sectors.get(sector, []): if position not in self.shown and self.exposed(position): self.show_block(position, False) def hide_sector(self, sector): """Ensure all blocks in the given sector that should be hidden are removed from the canvas. """ for position in self.sectors.get(sector, []): if position in self.shown: self.hide_block(position, False) def change_sectors(self, before, after): """Move from sector `before` to sector `after`. A sector is a contiguous x, y sub-region of world. Sectors are used to speed up world rendering. """ before_set = set() after_set = set() pad = 4 for dx in range(-pad, pad + 1): for dy in [0]: # range(-pad, pad + 1): for dz in range(-pad, pad + 1): if dx ** 2 + dy ** 2 + dz ** 2 > (pad + 1) ** 2: continue if before: x, y, z = before before_set.add((x + dx, y + dy, z + dz)) if after: x, y, z = after after_set.add((x + dx, y + dy, z + dz)) show = after_set - before_set hide = before_set - after_set for sector in show: self.show_sector(sector) for sector in hide: self.hide_sector(sector) def _enqueue(self, func, *args): """Add `func` to the internal queue.""" self.queue.append((func, args)) def _dequeue(self): """Pop the top function from the internal queue and call it.""" func, args = self.queue.popleft() func(*args) def process_queue(self, ticks_per_sec): """Process the entire queue while taking periodic breaks. This allows the game loop to run smoothly. The queue contains calls to _show_block() and _hide_block() so this method should be called if add_block() or remove_block() was called with immediate=False """ start = time.clock() while self.queue and time.clock() - start < 1.0 / ticks_per_sec: self._dequeue() def process_entire_queue(self): """Process the entire queue with no breaks.""" while self.queue: self._dequeue() def init_shader(self): vertex_shader = "" fragment_shader = "" with open("pycraft/shaders/world.vert") as handle: vertex_shader = handle.read() with open("pycraft/shaders/world.frag") as handle: fragment_shader = handle.read() self.shader = Shader([vertex_shader], [fragment_shader]) def start_shader(self): self.shader.bind() def stop_shader(self): self.shader.unbind()
class World: def __init__(self): # A Batch is a collection of vertex lists for batched rendering. self.batch = Batch() # A TextureGroup manages an OpenGL texture. self.texture_group = {} # A mapping from position to the texture of the block at that position. # This defines all the blocks that are currently in the world. self.objects = {} # Same mapping as `world` but only contains blocks that are shown. self.shown = {} # Mapping from position to a pyglet `VertextList` for all shown blocks. self._shown = {} # Which sector the player is currently in. self.sector = None # Mapping from sector to a list of positions inside that sector. self.sectors = {} self.shader = None self.show_hide_queue = OrderedDict() PycraftOpenGL() self.init_shader() def hit_test(self, position, vector, max_distance=8): """Line of sight search from current position. If a block is intersected it is returned, along with the block previously in the line of sight. If no block is found, return None, None. Parameters ---------- position : tuple of len 3 The (x, y, z) position to check visibility from. vector : tuple of len 3 The line of sight vector. max_distance : int How many blocks away to search for a hit. """ m = 8 x, y, z = position dx, dy, dz = vector previous = None for _ in range(max_distance * m): key = normalize((x, y, z)) if key != previous and key in self.objects: return key, previous previous = key x, y, z = x + dx / m, y + dy / m, z + dz / m return None, None def exposed(self, position): """Returns False is given `position` is surrounded on all 6 sides by blocks, True otherwise. """ x, y, z = position for dx, dy, dz in FACES: if (x + dx, y + dy, z + dz) not in self.objects: return True return False def add_block(self, position, texture, immediate=True): """Add a block with the given `texture` and `position` to the world. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to add. texture : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. immediate : bool Whether or not to draw the block immediately. """ if position in self.objects: self.remove_block(position, immediate) self.objects[position] = texture self.sectors.setdefault(sectorize(position), []).append(position) if immediate: if self.exposed(position): self.show_block(position) self.check_neighbors(position) def remove_block(self, position, immediate=True): """Remove the block at the given `position`. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to remove. immediate : bool Whether or not to immediately remove block from canvas. """ del self.objects[position] self.sectors[sectorize(position)].remove(position) if immediate: if position in self.shown: self.hide_block(position) self.check_neighbors(position) def check_neighbors(self, position): """Check all blocks surrounding `position` and ensure their visual state is current. This means hiding blocks that are not exposed and ensuring that all exposed blocks are shown. Usually used after a block is added or removed. """ x, y, z = position for dx, dy, dz in FACES: key = (x + dx, y + dy, z + dz) if key not in self.objects: continue if self.exposed(key): if key not in self.shown: self.show_block(key) else: if key in self.shown: self.hide_block(key) def show_block(self, position, immediate=True): """Show the block at the given `position`. This method assumes the block has already been added with add_block() Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to show. immediate : bool Whether or not to show the block immediately. """ texture = self.objects[position] self.shown[position] = texture if immediate: self._show_block(position, texture) else: self.show_hide_queue[position] = True def _show_block(self, position, block): """Private implementation of the `show_block()` method. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to show. texture : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. """ x, y, z = position vertex_data = cube_vertices(x, y, z, 0.5) shade_data = cube_shade(1, 1, 1, 1) texture_data = block.texture if block.identifier not in self.texture_group: self.texture_group[block.identifier] = TextureGroup(image.load(block.texture_path).get_texture()) self._shown[position] = self.batch.add( 24, GL_QUADS, self.texture_group[block.identifier], ('v3f/static', vertex_data), ('c3f/static', shade_data), ('t2f/static', texture_data)) def hide_block(self, position, immediate=True): """Hide the block at the given `position`. Hiding does not remove the block from the world. Parameters ---------- position : tuple of len 3 The (x, y, z) position of the block to hide. immediate : bool Whether or not to immediately remove the block from the canvas. """ self.shown.pop(position) if immediate: self._hide_block(position) else: self.show_hide_queue[position] = False def _hide_block(self, position): """Private implementation of the 'hide_block()` method.""" self._shown.pop(position).delete() def show_sector(self, sector): """Ensure all blocks in the given sector that should be shown are drawn to the canvas. """ positions = self.sectors.get(sector, []) if positions: for position in positions: if position not in self.shown and self.exposed(position): self.show_block(position, False) else: self.generate_sector(sector) self.show_sector(sector) def generate_sector(self, sector): """Generate blocks within sector using simplex_noise2 """ for column in reverse_sectorize(sector): x, z = column y_max = int((simplex_noise2(x / 30, z / 30) + 1) * 3) for y_lvl in range(0 - 2, y_max): self.add_block((x, y_lvl, z), Sand(), immediate=False) else: self.add_block((x, y_lvl, z), Grass(), immediate=False) # add the safety stone floor. # don't want anyone falling into the ether. self.add_block((x, 0 - 3, z), Stone(), immediate=False) def hide_sector(self, sector): """Ensure all blocks in the given sector that should be hidden are removed from the canvas. """ for position in self.sectors.get(sector, []): if position in self.shown: self.hide_block(position, False) def change_sectors(self, before, after): """Move from sector `before` to sector `after`. A sector is a contiguous x, y sub-region of world. Sectors are used to speed up world rendering. """ before_set = set() after_set = set() pad = 4 for dx in range(-pad, pad + 1): for dy in [0]: # range(-pad, pad + 1): for dz in range(-pad, pad + 1): if dx ** 2 + dy ** 2 + dz ** 2 > (pad + 1) ** 2: continue if before: x, y, z = before before_set.add((x + dx, y + dy, z + dz)) if after: x, y, z = after after_set.add((x + dx, y + dy, z + dz)) show = after_set - before_set hide = before_set - after_set for sector in show: self.show_sector(sector) for sector in hide: self.hide_sector(sector) def _dequeue(self): """Pop the top function from the internal queue and call it.""" position, show = self.show_hide_queue.popitem(last=False) shown = position in self._shown if show and not shown: self._show_block(position, self.objects[position]) elif shown and not show: self._hide_block(position) def process_queue(self, ticks_per_sec): """Process the entire queue while taking periodic breaks. This allows the game loop to run smoothly. The queue contains calls to _show_block() and _hide_block() so this method should be called if add_block() or remove_block() was called with immediate=False """ start = time.clock() while self.show_hide_queue and time.clock() - start < 1.0 / ticks_per_sec: self._dequeue() def process_entire_queue(self): """Process the entire queue with no breaks.""" while self.show_hide_queue: self._dequeue() def init_shader(self): vertex_shader = "" fragment_shader = "" with open("pycraft/shaders/world.vert") as handle: vertex_shader = handle.read() with open("pycraft/shaders/world.frag") as handle: fragment_shader = handle.read() self.shader = Shader([vertex_shader], [fragment_shader]) def start_shader(self): self.shader.bind() def stop_shader(self): self.shader.unbind()
class World: def __init__(self): # A Batch is a collection of vertex lists for batched rendering. self.batch = Batch() # A TextureGroup manages an OpenGL texture. self.texture_group = {} # Mapping from position to a pyglet `VertextList` for all shown blocks. self._shown = {} self.show_hide_queue = OrderedDict() # Which sector the player is currently in. self.sector = None # Mapping from sector to a list of positions inside that sector. self.sectors = {} # Same mapping as `world` but only contains blocks that are shown. self.shown = {} self.shader = None PycraftOpenGL() self.init_shader() # A mapping from position to the texture of the block at that position. # This defines all the blocks that are currently in the world. self.area = Area() def add_block(self, coords, block, immediate=True): """Add a block with the given `texture` and `position` to the world. Parameters ---------- coords : tuple of len 3 The (x, y, z) position of the block to add. block : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. immediate : bool Whether or not to draw the block immediately. """ self.area.add_block(coords, block) # self.sectors.setdefault(sectorize(position), []).append(position) if immediate: if self.area.exposed(coords): self.show_block(coords, block, immediate) neighbors = self.area.get_neighbors(coords) for element in neighbors['hide']: self.hide_block(element['coords']) for element in neighbors['show']: self.show_block(element['coords'], element['block']) def remove_block(self, coords): """ Remove a block from the world. And shows the neighbors :param coords: :return: """ self.area.remove_block(coords) self.hide_block(coords) neighbors = self.area.get_neighbors(coords) for element in neighbors['hide']: self.hide_block(element['coords']) for element in neighbors['show']: self.show_block(element['coords'], element['block']) def show_block(self, coords, block, immediate=False): """Ensure all blocks that should be shown are drawn to the canvas. Parameters ---------- coords : tuple of len 3 The (x, y, z) position of the block to show. block : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. immediate : bool Whether or not to immediately remove the block from the canvas. """ if coords in self.shown: return self.shown[coords] = block if not immediate: self.show_hide_queue[coords] = True return self._show_block(coords, block) def _show_block(self, coords, block): """Private implementation of the `show_block()` method. Parameters ---------- coords : tuple of len 3 The (x, y, z) position of the block to show. block : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. """ x, y, z = coords vertex_data = cube_vertices(x, y, z, 0.5) shade_data = cube_shade(1, 1, 1, 1) texture_data = block.texture if block.identifier not in self.texture_group: self.texture_group[block.identifier] = TextureGroup( image.load(block.texture_path).get_texture()) self._shown[coords] = self.batch.add( 24, GL_QUADS, self.texture_group[block.identifier], ('v3f/static', vertex_data), ('c3f/static', shade_data), ('t2f/static', texture_data)) def hide_block(self, coords, immediate=True): """Ensure all blocks that should be hidden are hide from the canvas.""" if coords not in self.shown: return self.shown.pop(coords) if not immediate: self.show_hide_queue[coords] = False self._hide_block(coords) def _hide_block(self, coords): """Private implementation of the 'hide_block()` method.""" if coords not in self._shown: return self._shown.pop(coords).delete() def _dequeue(self): """Pop the top function from the internal queue and call it.""" coords, show = self.show_hide_queue.popitem(last=False) shown = coords in self._shown if show and not shown: self._show_block(coords, self.area.get_block(coords)) elif shown and not show: self._hide_block(coords) def process_queue(self, ticks_per_sec): """Process the entire queue while taking periodic breaks. This allows the game loop to run smoothly. The queue contains calls to _show_block() and _hide_block() so this method should be called if add_block() or remove_block() was called with immediate=False """ start = time.clock() while self.show_hide_queue and time.clock( ) - start < 1.0 / ticks_per_sec: self._dequeue() def process_entire_queue(self): """Process the entire queue with no breaks.""" while self.show_hide_queue: self._dequeue() def init_shader(self): vertex_shader = "" fragment_shader = "" with open("pycraft/shaders/world.vert") as handle: vertex_shader = handle.read() with open("pycraft/shaders/world.frag") as handle: fragment_shader = handle.read() self.shader = Shader([vertex_shader], [fragment_shader]) def start_shader(self): self.shader.bind() def stop_shader(self): self.shader.unbind() def add_sector(self, sector, coords): self.sector = coords self.sectors[coords] = sector # self.sectors.setdefault(coords, []).append(sector) def show_sector(self, coords, immediate=True): """Ensure all blocks in the given sector that should be shown are drawn to the canvas. """ sector = self.sectors.get(coords) if sector: for position in sector.blocks: if position not in self.shown and self.area.exposed(position): self.show_block(position, immediate) else: sector = Sector(coords, self.area) self.add_sector(sector, coords) self.show_sector(coords) def hide_sector(self, coords): """Ensure all blocks in the given sector that should be hidden are removed from the canvas. """ sector = self.sectors.get(coords) for position in sector.blocks: if position in self.shown: self.hide_block(position, False) def change_sectors(self, before, after): """Move from sector `before` to sector `after`. A sector is a contiguous x, y sub-region of world. Sectors are used to speed up world rendering. """ before_set = set() after_set = set() pad = 4 if not before: self.initial_sector(after) for dx in range(-pad, pad + 1): for dy in [0]: # range(-pad, pad + 1): for dz in range(-pad, pad + 1): if dx**2 + dy**2 + dz**2 > (pad + 1)**2: continue if before: x, y, z = before before_set.add((x + dx, y + dy, z + dz)) if after: x, y, z = after after_set.add((x + dx, y + dy, z + dz)) show = after_set - before_set hide = before_set - after_set for coords in hide: self.hide_sector(coords) for coords in show: self.show_sector(coords) def initial_sector(self, coords): """ Creates initial sectors in spiral, to speed up rendering in front of the player :param coords: :return: """ x, y = 0, 0 dx, dy = 0, -1 X = coords[0] + 4 Y = coords[2] + 4 for i in range(max(X, Y)**2): if (-X / 2 < x <= X / 2) and (-Y / 2 < y <= Y / 2): self.show_sector((x, coords[1], y)) if x == y or (x < 0 and x == -y) or (x > 0 and x == 1 - y): dx, dy = -dy, dx # Corner change direction x, y = x + dx, y + dy
class World: def __init__(self): # A Batch is a collection of vertex lists for batched rendering. self.batch = Batch() # A TextureGroup manages an OpenGL texture. self.texture_group = {} # Mapping from position to a pyglet `VertextList` for all shown blocks. self._shown = {} self.show_hide_queue = OrderedDict() # Which sector the player is currently in. self.sector = None # Mapping from sector to a list of positions inside that sector. self.sectors = {} # Same mapping as `world` but only contains blocks that are shown. self.shown = {} self.shader = None PycraftOpenGL() self.init_shader() # A mapping from position to the texture of the block at that position. # This defines all the blocks that are currently in the world. self.area = Area() def add_block(self, coords, block, immediate=True): """Add a block with the given `texture` and `position` to the world. Parameters ---------- coords : tuple of len 3 The (x, y, z) position of the block to add. block : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. immediate : bool Whether or not to draw the block immediately. """ self.area.add_block(coords, block) # self.sectors.setdefault(sectorize(position), []).append(position) if immediate: if self.area.exposed(coords): self.show_block(coords, block, immediate) neighbors = self.area.get_neighbors(coords) for element in neighbors['hide']: self.hide_block(element['coords']) for element in neighbors['show']: self.show_block(element['coords'], element['block']) def remove_block(self, coords): """ Remove a block from the world. And shows the neighbors :param coords: :return: """ self.area.remove_block(coords) self.hide_block(coords) neighbors = self.area.get_neighbors(coords) for element in neighbors['hide']: self.hide_block(element['coords']) for element in neighbors['show']: self.show_block(element['coords'], element['block']) def show_block(self, coords, block, immediate=False): """Ensure all blocks that should be shown are drawn to the canvas. Parameters ---------- coords : tuple of len 3 The (x, y, z) position of the block to show. block : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. immediate : bool Whether or not to immediately remove the block from the canvas. """ if coords in self.shown: return self.shown[coords] = block if not immediate: self.show_hide_queue[coords] = True return self._show_block(coords, block) def _show_block(self, coords, block): """Private implementation of the `show_block()` method. Parameters ---------- coords : tuple of len 3 The (x, y, z) position of the block to show. block : list of len 3 The coordinates of the texture squares. Use `tex_coords()` to generate. """ x, y, z = coords vertex_data = cube_vertices(x, y, z, 0.5) shade_data = cube_shade(1, 1, 1, 1) texture_data = block.texture if block.identifier not in self.texture_group: self.texture_group[block.identifier] = TextureGroup(image.load(block.texture_path).get_texture()) self._shown[coords] = self.batch.add( 24, GL_QUADS, self.texture_group[block.identifier], ('v3f/static', vertex_data), ('c3f/static', shade_data), ('t2f/static', texture_data)) def hide_block(self, coords, immediate=True): """Ensure all blocks that should be hidden are hide from the canvas.""" if coords not in self.shown: return self.shown.pop(coords) if not immediate: self.show_hide_queue[coords] = False self._hide_block(coords) def _hide_block(self, coords): """Private implementation of the 'hide_block()` method.""" if coords not in self._shown: return self._shown.pop(coords).delete() def _dequeue(self): """Pop the top function from the internal queue and call it.""" coords, show = self.show_hide_queue.popitem(last=False) shown = coords in self._shown if show and not shown: self._show_block(coords, self.area.get_block(coords)) elif shown and not show: self._hide_block(coords) def process_queue(self, ticks_per_sec): """Process the entire queue while taking periodic breaks. This allows the game loop to run smoothly. The queue contains calls to _show_block() and _hide_block() so this method should be called if add_block() or remove_block() was called with immediate=False """ start = time.clock() while self.show_hide_queue and time.clock() - start < 1.0 / ticks_per_sec: self._dequeue() def process_entire_queue(self): """Process the entire queue with no breaks.""" while self.show_hide_queue: self._dequeue() def init_shader(self): vertex_shader = "" fragment_shader = "" with open("pycraft/shaders/world.vert") as handle: vertex_shader = handle.read() with open("pycraft/shaders/world.frag") as handle: fragment_shader = handle.read() self.shader = Shader([vertex_shader], [fragment_shader]) def start_shader(self): self.shader.bind() def stop_shader(self): self.shader.unbind() def add_sector(self, sector, coords): self.sector = coords self.sectors[coords] = sector # self.sectors.setdefault(coords, []).append(sector) def show_sector(self, coords, immediate=True): """Ensure all blocks in the given sector that should be shown are drawn to the canvas. """ sector = self.sectors.get(coords) if sector: for position in sector.blocks: if position not in self.shown and self.area.exposed(position): self.show_block(position, immediate) else: sector = Sector(coords, self.area) self.add_sector(sector, coords) self.show_sector(coords) def hide_sector(self, coords): """Ensure all blocks in the given sector that should be hidden are removed from the canvas. """ sector = self.sectors.get(coords) for position in sector.blocks: if position in self.shown: self.hide_block(position, False) def change_sectors(self, before, after): """Move from sector `before` to sector `after`. A sector is a contiguous x, y sub-region of world. Sectors are used to speed up world rendering. """ before_set = set() after_set = set() pad = 4 if not before: self.initial_sector(after) for dx in range(-pad, pad + 1): for dy in [0]: # range(-pad, pad + 1): for dz in range(-pad, pad + 1): if dx ** 2 + dy ** 2 + dz ** 2 > (pad + 1) ** 2: continue if before: x, y, z = before before_set.add((x + dx, y + dy, z + dz)) if after: x, y, z = after after_set.add((x + dx, y + dy, z + dz)) show = after_set - before_set hide = before_set - after_set for coords in hide: self.hide_sector(coords) for coords in show: self.show_sector(coords) def initial_sector(self, coords): """ Creates initial sectors in spiral, to speed up rendering in front of the player :param coords: :return: """ x, y = 0, 0 dx, dy = 0, -1 X = coords[0] + 4 Y = coords[2] + 4 for i in range(max(X, Y) ** 2): if (-X / 2 < x <= X / 2) and (-Y / 2 < y <= Y / 2): self.show_sector((x, coords[1], y)) if x == y or (x < 0 and x == -y) or (x > 0 and x == 1 - y): dx, dy = -dy, dx # Corner change direction x, y = x + dx, y + dy