def draw_lasers(self): batch = Batch() inner_colors = (0, 200, 255, 0, 200, 255) radius = 3 * SpaceWindow.BULLET_RADIUS_PERCENT * self.width for bullet in self.model.bullets: # self.draw_illumination(self.to_screen_x(bullet[0]), self.to_screen_y(bullet[1]), radius, inner_colors[:3]) batch.add(2, GL_LINES, None, ('v2f', [self.to_screen_x(bullet[0]), self.to_screen_y(bullet[1]), self.to_screen_x(bullet[0]), self.to_screen_y(bullet[1] + int(self.BULLET_HEIGHT_PERCENT * self.main_height))]), ('c3B', inner_colors)) radius = SpaceWindow.BULLET_RADIUS_PERCENT * self.width purple = [255, 0, 255] for x, y in self.model.alien_bullets: self.draw_illumination(self.to_screen_x(x), self.to_screen_y(y), 6 * radius, purple) circ_pts = [self.to_screen_x(x), self.to_screen_y(y) + radius] for theta in np.linspace(0, 2 * math.pi, 8): error = random.randint(-1 * radius // 4, radius // 4) circ_pts.extend([circ_pts[0] + (radius + error) * math.sin(theta), circ_pts[1] + (radius + error) * math.cos(theta)]) num_of_vert = (len(circ_pts) // 2) colors = [255, 255, 255] colors.extend((num_of_vert - 1) * purple) graphics.draw(num_of_vert, GL_TRIANGLE_FAN, ('v2f', circ_pts), ('c3B', colors)) batch.draw()
class Model: def get_texture(self, file): texture = load_img(file).texture glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) return TextureGroup(texture) def __init__(self): self.batch = Batch() self.top = self.get_texture('./resources/grass_top.png') self.side = self.get_texture('./resources/grass_side.png') self.bottom = self.get_texture('./resources/dirt.png') x, y, z = 0, 0, -1 X, Y, Z = x + 1, y + 1, z + 1 texture_coords = ('t2f', (0, 0, 1, 0, 1, 1, 0, 1)) self.batch.add(4, GL_QUADS, self.side, ('v3f', (x, y, z, X, y, z, X, Y, z, x, Y, z)), texture_coords) def draw(self): self.batch.draw()
def cif_power_strip(x, y, z, width=1, height=1, depth=1): batch = Batch() outlet = True for i in range(width): for j in range(height): for k in range(depth): if outlet: strip_texture = texture.textures['cif_power_strip'] else: strip_texture = texture.textures['cif_power_outlet'] outlet = not outlet tile = cube_tile.CubeTile((x+i, y+j, z+k), textures={ 'top': texture.textures['cif_power_strip']['master_coordinates'], 'right': strip_texture['master_coordinates'], 'bottom': texture.textures['cif_power_strip']['master_coordinates'], 'left': strip_texture['master_coordinates'], 'front': strip_texture['master_coordinates'], 'back': strip_texture['master_coordinates'], }) vertex_list = tile.get_vertex_list() texture_list = tile.get_texture_list() vertices = len(vertex_list) / 3 batch.add(vertices, GL_QUADS, texture.textures['master']['group'], ('v3f/static', vertex_list), ('t2f/static', texture_list)) return batch
class Body(object): "A list of primitives. Creates a single batch to draw these primitives." def __init__(self, items=None): self.primitives = [] if items: self.add_items(items) self.batch = None def add_items(self, items): "'items' may contain primitives and/or bodys" for item in items: if isinstance(item, Body): for prim in item.primitives: self.primitives.append(prim) else: self.primitives.append(item) def batch_draw(self): if self.batch is None: self.batch = Batch() for primitive in self.primitives: batchVerts = \ [primitive.verts[0], primitive.verts[1]] + \ primitive.verts + \ [primitive.verts[-2], primitive.verts[-1]] numverts = len(batchVerts) / 2 self.batch.add( numverts, primitive.primtype, None, # draw order group to be implemented later ('v2f/static', batchVerts), ('c3B/static', primitive.color * numverts), ) self.batch.draw()
class Shape(object): "A list of primitives. Creates a single batch to draw these primitives." def __init__(self, items=None): self.primitives = [] if items: self.add_items(items) self.batch = None def add_items(self, items): "'items' may contain primitives and/or shapes" for item in items: if isinstance(item, Shape): for prim in item.primitives: self.primitives.append(prim) else: self.primitives.append(item) def get_batch(self): if self.batch is None: self.batch = Batch() for primitive in self.primitives: batchVerts = \ [primitive.verts[0], primitive.verts[1]] + \ primitive.verts + \ [primitive.verts[-2], primitive.verts[-1]] numverts = len(batchVerts) / 2 self.batch.add( numverts, primitive.primtype, None, # group ('v2f/static', batchVerts), ('c3B/static', primitive.color * numverts), ) return self.batch
class Body(object): "A list of primitives. Creates a single batch to draw these primitives." def __init__(self, items=None): self.primitives = [] if items: self.add_items(items) self.batch = None def add_items(self, items): "'items' may contain primitives and/or bodys" for item in items: if isinstance(item, Body): for prim in item.primitives: self.primitives.append(prim) else: self.primitives.append(item) def batch_draw(self): if self.batch is None: self.batch = Batch() for primitive in self.primitives: batchVerts = \ [primitive.verts[0], primitive.verts[1]] + \ primitive.verts + \ [primitive.verts[-2], primitive.verts[-1]] numverts = len(batchVerts) / 2 self.batch.add( numverts, primitive.primtype, None, # draw order group to be implemented later ('v2f/static', batchVerts), ('c3B/static', primitive.color * numverts), ) self.batch.draw()
class Shape(object): "A list of primitives" def __init__(self, items=None): self.primitives = [] if items: self.add_items(items) self.batch = None def add_items(self, items): "Add a list containing primitives and shapes" for item in items: if isinstance(item, Shape): for prim in item.primitives: self.primitives.append(prim) else: self.primitives.append(item) def get_batch(self): if self.batch is None: self.batch = Batch() for primitive in self.primitives: flatverts = primitive.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add(numverts, primitive.primtype, None, ('v2f/static', flatverts), ('c3B/static', primitive.color * numverts)) return self.batch def offset(self, dx, dy): newprims = [] for prim in self.primitives: newprims.append(prim.offset(dx, dy)) return Shape(newprims)
def draw(self, current_view): batch_to_draw = Batch() for c in self.graphic_components: if c.current_view == current_view: options = c.get_draw_options() batch_to_draw.add(*options) batch_to_draw.draw()
class Shape(object): "A list of primitives" def __init__(self, items=None): self.primitives = [] if items: self.add_items(items) self.batch = None def add_items(self, items): "Add a list of primitives and shapes" for item in items: if isinstance(item, Shape): self.add_shape(item) else: self.primitives.append(item) def add_shape(self, other): "Add the primitives from a given shape" for prim in other.primitives: self.primitives.append(prim) def get_batch(self): if self.batch is None: self.batch = Batch() for primitive in self.primitives: flatverts = primitive.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add(numverts, primitive.primtype, None, ('v2f/static', flatverts), ('c3B/static', primitive.color * numverts)) return self.batch
def addTriangleUnit(batch: Batch, origin: Vec2, ux, uy, tri: Triangle, colors: List[Vec3]): batch.add(3, GL_TRIANGLES, None, ('v2f', [ tri.p0.x * ux + origin.x * ux, tri.p0.y * uy + origin.y * uy, tri.p1.x * ux + origin.x * ux, tri.p1.y * uy + origin.y * uy, tri.p2.x * ux + origin.x * ux, tri.p2.y * uy + origin.y * uy ]), ('c3f', reduce(operator.concat, map(lambda x: x.to_list(), colors))))
class Shape(object): ''' A list of primitives ''' def __init__(self, items=None): self.primitives = [] if items: self.add_items(items) self.batch = None def add_items(self, items): "Add a list of primitives and shapes" for item in items: if isinstance(item, Shape): self.add_shape(item) else: self.primitives.append(item) def add_shape(self, other): "Add the primitives from a given shape" for prim in other.primitives: self.primitives.append(prim) def get_batch(self): if self.batch is None: self.batch = Batch() for prim in self.primitives: flatverts = prim.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add( numverts, prim.primtype, None, ('v2f/static', flatverts), ('c3B/static', prim.color * numverts) ) return self.batch def transform(self,M): """ applies matrix M to all self primitives """ for prim in self.primitives: prim.transform(M) def get_aabb(self): aabb = namedtuple('AABB',['xmin','xmax','ymin','ymax']) _allx=[] _ally=[] for prim in self.primitives: for v in prim.verts: _allx.append(v[0]) _ally.append(v[1]) minx=min(_allx) miny=min(_ally) maxx=max(_allx) maxy=max(_ally) box = (minx,miny,maxx,maxy) return (box)
class OrbitingBody(Body, metaclass=ABCMeta): """ An orbiting body in the solarsystem """ def __init__(self, parent, name, texturename, color, radius, orbit, axial_tilt, sidereal_rotation_period, mass, renderer=OrbitingBodyRenderer()): """ Creates a new body with the given parameters :param parent: Parent body in the system :type parent: :class:`Body`, None :param name: Name of the body :type name: str :param texturename: Name of the texture :type texturename: str :param color: Dictionary with r, g and b values :type color: dict :param radius: Radius of the body :type radius: float :param orbit: Orbit of the body :type orbit: :class:`solarsystem.orbit.Orbit` :param axial_tilt: Axial Tilt in degrees :type axial_tilt: float :param sidereal_rotation_period: Rotation period (siderial) around its own axis :type sidereal_rotation_period: float """ super().__init__(parent, name, texturename, color, radius, axial_tilt, sidereal_rotation_period, mass, renderer=renderer) self.orbit = orbit self.orbit.body = self self.orbit_line_batch = Batch() def post_init(self): self.orbit.post_init() # Plot the orbit to a pyglet batch for faster drawing orbit_line = [] for pos in self.orbit.plot(plot_steps): orbit_line.append(pos.x) orbit_line.append(pos.y) orbit_line.append(pos.z) self.orbit_line_batch.add(int(len(orbit_line) / 3), GL_LINE_LOOP, None, ('v3f', tuple(orbit_line))) def update(self, time): """ Update the body (Calculate current orbit position) :param time: Delta Time :type time: float """ super().update(time) self.xyz = self.orbit.calculate(time) if self.parent: self.xyz += self.parent.xyz
class Body(object): "A list of shapes. Creates a single batch to draw these shapes." __metaclass__ = IterRegistry _registry = [] def __init__(self,items=None,anchor=[0,0],angle=0,drawable=True): self.shapes = [] self._registry.append(self) self.body=body self.anchor=anchor self.angle=angle self.drawable=drawable if items: self.add_items(items) self.batch = None def add_items(self, items): "'items' may contain shapes and/or bodies" for item in items: if isinstance(item, Body): for shp in item.shapes: self.shapes.append(shp) else: self.shapes.append(item) def batch_draw(self): if self.batch is None: self.batch = Batch() for shape in self.shapes: batchVerts = \ [shape.verts[0], shape.verts[1]] + \ shape.verts + \ [shape.verts[-2], shape.verts[-1]] numverts = len(batchVerts) / 2 self.batch.add( numverts, shape.primtype, None, # draw order group to be implemented later ('v2f/static', batchVerts), ('c3B/static', shape.color * numverts), ) self.batch.draw() def paint_all(): for z in Zulu: glPushMatrix() glTranslatef(z.anchor[0],z.anchor[1],0) # Move bac glRotatef(z.angle, 0, 0, 1) z.body.batch_draw() glPopMatrix()
class OrbitalObject(AstronomicalObject): """ An astronomical Object which moves around another """ def __init__(self, parent, name, texture_name, radius, axial_tilt, sidereal_rotation_period, mass, orbit, renderer=AOOrbitingRenderer()): """ Create a new orbiting astronomical Object :param parent: The objects parent (i.e. Sun for Earth) :param name: Name of the object (Earth, Saturn, Pluto, ...) :param texture_name: Name of the texture in the `res` directory :param radius: Radius of object :param axial_tilt: In astronomy, axial tilt is the angle between a planet's rotational axis at its north pole and a line perpendicular to the orbital plane of the planet - given in degrees. :param sidereal_rotation_period: The time required (in days) for a body within the solar system to complete one revolution with respect to the fixed stars—i.e., as observed from some fixed point outside the system. :param mass: Mass of the object in kilograms :param orbit: Orbit Class of this body """ super().__init__(parent, name, texture_name, radius, axial_tilt, sidereal_rotation_period, mass, renderer=renderer) self.orbit = orbit self.orbit.body = self self.orbit_line_batch = Batch() def config(self): """ Configure the Object """ self.orbit.config() orbit_line = [] for pos in self.orbit.pos(1024): orbit_line.append(pos.x) orbit_line.append(pos.y) orbit_line.append(pos.z) self.orbit_line_batch.add(int(len(orbit_line) / 3), GL_LINE_LOOP, None, ('v3f', tuple(orbit_line))) def update(self, time): """ Update the time in the solar system and position the object on its right coordinates :param time: Current solar time """ super().update(time) self.xyz = self.orbit.calculate(time)
def draw_pixel_spills(self): pxl_batch = Batch() for px in self.pixel_spills: px.update(self.tick) self.pixel_spills[:] = [val for val in self.pixel_spills if not val.is_vanished] num_of = 0 px_vertices = [] colours = [] for px in self.pixel_spills: num_of += 4 px_vertices.extend((px.x, px.y, px.x, px.y + px.size, px.x + px.size, px.y + px.size, px.x + px.size, px.y)) colours.extend(px.colour) pxl_batch.add(num_of, GL_QUADS, None, ('v2f', px_vertices), ('c3B', colours)) pxl_batch.draw()
def draw_falling_parts(self): pxl_batch = Batch() for bl in self.falling_parts: bl.update(self.tick) self.falling_parts[:] = [val for val in self.falling_parts if not val.is_vanished] num_of = 0 px_vertices = [] colours = [] px: FallingBlock for px in self.falling_parts: num_of += 4 px_vertices.extend((px.x, px.y, px.x, px.y + px.size, px.x + px.size, px.y + px.size, px.x + px.size, px.y)) colours.extend(px.colour) pxl_batch.add(num_of, GL_QUADS, None, ('v2f', px_vertices), ('c3B', colours)) pxl_batch.draw()
def floor(floor_texture, x, y, z, width=1, height=1, depth=1): batch = Batch() for i in range(width): for j in range(height): for k in range(depth): tile = plane_tile.PlaneTile((x+i, y+j, z+k), side='top', textures={ 'top': floor_texture, }) vertex_list = tile.get_vertex_list() texture_list = tile.get_texture_list() vertices = len(vertex_list) / 3 batch.add(vertices, GL_QUADS, texture.textures['master']['group'], ('v3f/static', vertex_list), ('t2f/static', texture_list)) return batch
def draw_flame(self, x, y, width): flame_height = (self.main_height + self.main_width) // 90 rocket_width = 8 * width // 64 flame_width_reduct = 0 offset = 15 * width // 64 padding = 29 * width // 65 if random.random() < 0.2: self.reset_flame_colours() flame_batch = Batch() srcs = [[x + offset + i * padding, x + offset + rocket_width + i * padding] for i in range(0, 2)] for i, [src_x1, src_x2] in enumerate(srcs): flame_batch.add(4, GL_QUADS, None, ('v2f', [src_x1, y, src_x1 + flame_width_reduct, y - flame_height, src_x2 - flame_width_reduct, y - flame_height, src_x2, y]), ('c4B', self.flame_colours[i])) flame_batch.draw()
class CloudRenderer(CloudChunk): def __init__(self, X, Generator): super(CloudRenderer, self).__init__(X, Generator) self.Batch = Batch() def GenerateFinshed(self): super(CloudRenderer, self).GenerateFinshed() self.Batch.add(self.Length, GL_POINTS, None, ('v2i/static', self.Points), ('c4f/static', self.Colours) ) def Draw(self, X): super(CloudRenderer, self).Draw(X) self.Batch.draw()
class Surface: def __init__(self, poses, material=None): self.poses = poses self.N = (poses[1] - poses[0]).cross(poses[2] - poses[0]).normalize() self.batch = Batch() norms = list(self.N.arr) * len(poses) verts = reduce(lambda x, y: list(x) + list(y), poses) self.batch.add(len(poses), GL_TRIANGLE_FAN, None, ('v3f', verts), ('n3f', norms)) self.material = material def draw(self): if self.material is not None: self.material.set() self.batch.draw() def d(self, p): return self.N.dot(self.poses[0] - p)
class Shape(object): ''' A list of primitives ''' def __init__(self, items=None): self.primitives = [] if items: self.add_items(items) self.batch = None def add_items(self, items): "Add a list of primitives and shapes" for item in items: if isinstance(item, Shape): self.add_shape(item) else: self.primitives.append(item) def add_shape(self, other): "Add the primitives from a given shape" for prim in other.primitives: self.primitives.append(prim) def get_batch(self): if self.batch is None: self.batch = Batch() for prim in self.primitives: flatverts = prim.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add( numverts, prim.primtype, None, ('v2f/static', flatverts), ('c3B/static', prim.color * numverts) ) return self.batch def transform(self,M): """ applies matrix M to all self primitives """ for prim in self.primitives: prim.transform(M)
class Map(object): LAYERS = { 'soil': OrderedGroup(0), 'ground': OrderedGroup(1), 'bodies': OrderedGroup(2), 'trees': OrderedGroup(3), 'berries': OrderedGroup(4) } def __init__(self, width, height): self.width = width self.height = height self._map = Batch() self._create() self._add_hero() self._add_lost() def _create(self): for x in range(0, self.width, Soil.size()[0]): for y in range(0, self.height, Soil.size()[1]): soil = Soil(x, y) self.add(soil) try: soil.grow(self, x, y) except NotFertileError as e: logger.debug(str(e)) def add(self, thing): thing.vertex_list = self._map.add( len(thing.vertices) // 2, GL_QUADS, self.LAYERS[thing.LAYER], ('v2i/dynamic', thing.vertices), ('c4B/static', thing.colors) ) return thing.vertex_list def _add_body(self, body_name, kind): body = getattr(things, body_name)(*START_POS[kind]) setattr(self, kind, body) self.add(body) return body @info("Adding {}".format(BODY_HERO)) def _add_hero(self): self._add_body(BODY_HERO, 'hero') @info("Hiding {}".format(BODY_LOST)) def _add_lost(self): self._add_body(BODY_LOST, 'lost') # keep a list of every tree to hide him def draw(self): self._map.draw()
def cif_old_mac(x, y, z, rotate=0): batch = Batch() bottom_side = texture.textures['old_mac_bottom_side']['master_coordinates'] top_side = texture.textures['old_mac_top_side']['master_coordinates'] bottom_front = texture.textures['old_mac_bottom_front']['master_coordinates'] top_front = texture.textures['old_mac_top_front']['master_coordinates'] if rotate == 0: sides = ['right', 'back', 'front'] elif rotate == 1: sides = ['back', 'left', 'right'] elif rotate == 2: sides = ['left', 'back', 'front'] elif rotate == 3: sides = ['front', 'left', 'right'] front_x = 1 if rotate == 0 else -1 if rotate == 2 else 0 front_z = 1 if rotate == 3 else -1 if rotate == 1 else 0 tiles = [ # Front side plane_tile.PlaneTile((x-front_x, y, z-front_z), side=sides[0], textures={sides[0]: bottom_front}), plane_tile.PlaneTile((x-front_x, y+1, z-front_z), side=sides[0], textures={sides[0]: top_front}), # Left side plane_tile.PlaneTile((x, y, z), side=sides[1], textures={sides[1]: bottom_side}), plane_tile.PlaneTile((x, y+1, z), side=sides[1], textures={sides[1]: top_side}), # Right side plane_tile.PlaneTile((x, y, z), side=sides[2], textures={sides[2]: bottom_side}), plane_tile.PlaneTile((x, y+1, z), side=sides[2], textures={sides[2]: top_side}), # Top side plane_tile.PlaneTile((x, y+1, z), side='top', textures={'top': top_side}), ] for tile in tiles: vertex_list = tile.get_vertex_list() texture_list = tile.get_texture_list() vertices = len(vertex_list) / 3 batch.add(vertices, GL_QUADS, texture.textures['master']['group'], ('v3f/static', vertex_list), ('t2f/static', texture_list)) return batch
def cif_red_shelf(x, y, z): batch = Batch() side = texture.textures['cif_red_shelf_side']['master_coordinates'] front = texture.textures['cif_red_shelf_front']['master_coordinates'] tiles = [ # Left side plane_tile.PlaneTile((x, y, z), side='back', textures={'back': side}), plane_tile.PlaneTile((x, y+1, z), side='back', textures={'back': side}), plane_tile.PlaneTile((x, y+2, z), side='back', textures={'back': side}), plane_tile.PlaneTile((x, y+3, z), side='back', textures={'back': side}), plane_tile.PlaneTile((x, y+4, z), side='back', textures={'back': side}), # Right side plane_tile.PlaneTile((x, y, z), side='front', textures={'front': side}), plane_tile.PlaneTile((x, y+1, z), side='front', textures={'front': side}), plane_tile.PlaneTile((x, y+2, z), side='front', textures={'front': side}), plane_tile.PlaneTile((x, y+3, z), side='front', textures={'front': side}), plane_tile.PlaneTile((x, y+4, z), side='front', textures={'front': side}), # Shelves plane_tile.PlaneTile((x, y, z), side='top', textures={'top': side}), plane_tile.PlaneTile((x, y+1, z), side='top', textures={'top': side}), plane_tile.PlaneTile((x, y+2, z), side='top', textures={'top': side}), plane_tile.PlaneTile((x, y+3, z), side='top', textures={'top': side}), plane_tile.PlaneTile((x, y+4, z), side='top', textures={'top': side}), # Front side plane_tile.PlaneTile((x, y, z), side='right', textures={'right': front}), plane_tile.PlaneTile((x, y+1, z), side='right', textures={'right': front}), plane_tile.PlaneTile((x, y+2, z), side='right', textures={'right': front}), plane_tile.PlaneTile((x, y+3, z), side='right', textures={'right': front}), plane_tile.PlaneTile((x, y+4, z), side='right', textures={'right': front}), ] for tile in tiles: vertex_list = tile.get_vertex_list() texture_list = tile.get_texture_list() vertices = len(vertex_list) / 3 batch.add(vertices, GL_QUADS, texture.textures['master']['group'], ('v3f/static', vertex_list), ('t2f/static', texture_list)) return batch
class Shape(object): "A list of primitives. Creates a single batch to draw these primitives." def __init__(self, items=None): self.primitives = [] if items: self.add_items(items) self.batch = None def add_items(self, items): "'items' may contain primitives and/or shapes" for item in items: if isinstance(item, Shape): for prim in item.primitives: self.primitives.append(prim) else: self.primitives.append(item) def get_batch(self): if self.batch is None: self.batch = Batch() for primitive in self.primitives: batchVerts = \ [primitive.verts[0], primitive.verts[1]] + \ primitive.verts + \ [primitive.verts[-2], primitive.verts[-1]] numverts = len(batchVerts) / 2 self.batch.add( numverts, primitive.primtype, None, # group ('v2f/static', batchVerts), ('c3B/static', primitive.color * numverts), ) return self.batch def offset(self, dx, dy): newprims = [] for prim in self.primitives: newprims.append(prim.offset(dx, dy)) return Shape(newprims)
def cif_table(x, y, z, rotate=False): batch = Batch() table = texture.textures['cif_red_shelf_side']['master_coordinates'] leg = texture.textures['cif_cabinet_base']['master_coordinates'] table_size = (3, .5, 6) if rotate else (6, .5, 3) leg_size = (.5, 2, .5) leg_texture = { 'top': leg, 'right': leg, 'bottom': leg, 'left': leg, 'front': leg, 'back': leg, } top = cube_tile.CubeTile((x, y, z), table_size, textures={ 'top': table, 'right': table, 'bottom': table, 'left': table, 'front': table, 'back': table, }) leg_x = table_size[0] / 2.0 - leg_size[0] leg_z = table_size[2] / 2.0 - leg_size[2] front_left_leg = cube_tile.CubeTile((x-leg_x, y-1, z+leg_z), leg_size, textures=leg_texture) front_right_leg = cube_tile.CubeTile((x+leg_x, y-1, z+leg_z), leg_size, textures=leg_texture) back_left_leg = cube_tile.CubeTile((x-leg_x, y-1, z-leg_z), leg_size, textures=leg_texture) back_right_leg = cube_tile.CubeTile((x+leg_x, y-1, z-leg_z), leg_size, textures=leg_texture) for tile in [top, front_left_leg, front_right_leg, back_left_leg, back_right_leg]: vertex_list = tile.get_vertex_list() texture_list = tile.get_texture_list() vertices = len(vertex_list) / 3 batch.add(vertices, GL_QUADS, texture.textures['master']['group'], ('v3f/static', vertex_list), ('t2f/static', texture_list)) return batch
def draw_header(self): complement = 255 - self.alpha header_batch = Batch() colors = (0, 0, 0, complement, 13, 22, 48, complement, 13, 22, 48, complement, 0, 0, 0, complement) header_batch.add(4, GL_QUADS, None, ('v2f', [0, self.main_height, 0, self.main_height + self.header_height, self.main_width, self.main_height + self.header_height, self.main_width, self.main_height]), ('c4B', colors)) header_batch.add(2, GL_LINES, None, ('v2f', [0, self.main_height, self.main_width, self.main_height]), ('c4B', 2 * (255, 255, 255, complement))) header_batch.draw() self.enemies_lbl = pyglet.text.Label("Enemies Remaining: " + ("" if not self.model else str(self.model.aliens)), font_name='8Bit Wonder', font_size=self.main_width // 65, width=self.main_width, height=self.header_height, x=self.main_width // 40, y=self.main_height + 0.9 * self.header_height, anchor_x='left', anchor_y='top', color=(255, 255, 255, complement)) self.enemies_lbl.draw() self.score_lbl = pyglet.text.Label("Score: " + ("" if not self.model else str(self.model.points)), font_name='8Bit Wonder', font_size=self.main_width // 65, width=self.main_width, height=self.header_height, x=18 * self.main_width // 40, y=self.main_height + 0.9 * self.header_height, anchor_x='left', anchor_y='top', color=(255, 255, 255, complement)) self.score_lbl.draw() self.high_score_lbl = pyglet.text.Label("Highscore: " + ("" if not self.model else str(self.model.highscore)), font_name='8Bit Wonder', font_size=self.main_width // 65, width=self.main_width, height=self.header_height, x=28 * self.main_width // 40, y=self.main_height + 0.9 * self.header_height, anchor_x='left', anchor_y='top', color=(255, 255, 255, complement)) self.high_score_lbl.draw()
class Shape(object): "A list of primitives" def __init__(self, items=None): self.primitives = [] if items: self.add_items(items) self.batch = None def add_items(self, items): "Add a list containing primitives and shapes" for item in items: if isinstance(item, Shape): for prim in item.primitives: self.primitives.append(prim) else: self.primitives.append(item) def get_batch(self): if self.batch is None: self.batch = Batch() for primitive in self.primitives: flatverts = primitive.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add( numverts, primitive.primtype, None, ('v2f/static', flatverts), ('c3B/static', primitive.color * numverts) ) return self.batch def offset(self, dx, dy): newprims = [] for prim in self.primitives: newprims.append(prim.offset(dx, dy)) return Shape(newprims)
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 Shape(object): ''' Shapes have Vertices, a single color, and a single OpenGL primitive ''' _instances = set() def __init__(self, **kwargs): for key in kwargs: setattr(self,key,kwargs[key]) if not hasattr(self,'peg'): # peg must be tuple (x, y, a) self.peg = DOCKED if not hasattr(self,'vel'): # peg must be tuple (vx, vy, av) self.vel = IDLE self.build() self.flat_verts = None self.batch=None self._instances.add(weakref.ref(self)) print "::" print ":: new shape ::::::::::::::::::::::::::::::::::::::::::::::::::" print "::" dumpObj(self) @classmethod def get_instances(cls): dead = set() for ref in cls._instances: obj = ref() if obj is not None: yield obj else: dead.add(ref) cls._instances -= dead def offset(self, dx, dy): newverts = [(v[0] + dx, v[1] + dy) for v in self.verts] self.verts=newverts def copy(self): ssh=Shape(verts=self.verts, color=self.color,\ primtype=self.primtype, peg=self.peg, vel=self.vel) return(ssh) def center(self): pass def transform(self,M): ''' applies matrix M transformation to all self vertexes ''' newverts = [ (M[0]*v[0]+M[1]*v[1]+M[2], M[3]*v[0]+M[4]*v[1]+M[5]) for v in self.verts] self.verts=newverts def get_aabb(self): _allx=[] _ally=[] for v in self.verts: _allx.append(v[0]) _ally.append(v[1]) lox=min(_allx) loy=min(_ally) hix=max(_allx) hiy=max(_ally) return (AABB(lox,loy,hix,hiy)) def get_flat_verts(self): if self.flat_verts is None: self.flat_verts = \ list(self.verts[0]) + \ [x for x in chain(*self.verts)] + \ list(self.verts[-1]) return self.flat_verts def get_batch(self): if self.batch is None: self.batch = Batch() flatverts = self.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add( numverts, self.primtype, None, ('v2f/static', flatverts), ('c4B/static', self.color * numverts) ) return self.batch def paint(self): batch = self.get_batch() glPushMatrix() glTranslatef(self.peg.x, self.peg.y, 0) glRotatef(self.peg.angle, 0, 0, 1) batch.draw() glPopMatrix() def build(self): pass
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.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 UIElement: width = 100 height = 100 line_width = 2 def __init__(self, x, y, window_height, batch=None): self.x = x self.y = y self.batch = Batch() if batch is None else batch self.click_regions = {} self.click_handlers = {} self.ui_elements = [] self.box_vx = [] self.box_modes = [] self.window_height = window_height self.groupNumber = 100 if len(self.box_vx) == 0: self.init_box() def init_box(self): w = self.width h = self.height pos = self.x, self.y bgvx = fix_origin(( pos[0], pos[1], pos[0], pos[1] + h, pos[0] + w, pos[1] + h, pos[0] + w, pos[1] ), self.window_height) bgvx2 = ( bgvx[0] + 2, bgvx[1] - 2, bgvx[2] + 2, bgvx[3] + 2, bgvx[4] - 2, bgvx[5] + 2, bgvx[6] - 2, bgvx[7] - 2 ) self.box_vx.append(self.batch.add(4, GL_QUADS, OrderedGroup(self.groupNumber), ('v2i', bgvx), ('c3B', (0, 0, 0) * 4) )) self.box_vx.append(self.batch.add(4, GL_QUADS, OrderedGroup(self.groupNumber), ('v2i', bgvx2), ('c3B', (255, 255, 255) * 4) )) def handle_region(self, name, handler, x, y, w, h): self.click_regions[name] = (x, y, w, h) self.click_handlers[name] = handler def hover(self, pos): return True def in_region(self, x, y, rx, ry, rw, rh): return x >= rx and x <= rx + rw and \ y >= ry and y <= ry + rh def check_mouse(self, pos, buttons): something = False if len(buttons) == 0: # nothing clicked, hover if self.in_region(*pos, self.x, self.y, self.width, self.height): something = self.hover(pos) else: for region in self.click_regions: r = self.click_regions[region] args = list(pos) args.extend(r) if self.in_region(*args): self.click_handlers[region](region, *pos, buttons) something = True for el in self.ui_elements: something = el.check_mouse(pos, buttons) or something return something
class Shape(object): """ Stores a list of vertices, a single color, and a primitive type Intended to be rendered as a single OpenGL primitive """ def __init__(self, name, **kwargs): global lmnts if name in lmnts: raise ValueError('duplicate shape name', name) exit(1) self.name=name lmnts[self.name] = self for i in kwargs: setattr(self,i,kwargs[i]) self.build() self.aabb = self.get_aabb() self.flat_verts = None self.batch = None def offset(self, vx, vy): newverts = [(v[0] + vx, v[1] + vy) for v in self.verts] self.verts=newverts def transform(self,M): """ applies matrix M transformation to all self vertexes """ newverts = [ (M[0]*v[0]+M[1]*v[1]+M[2], M[3]*v[0]+M[4]*v[1]+M[5]) for v in self.verts] return Shape(newverts, self.color, primtype=self.primtype) #TODO replace shape verts only def get_aabb(self): _allx=[] _ally=[] for v in self.verts: _allx.append(v[0]) _ally.append(v[1]) lox=min(_allx) loy=min(_ally) hix=max(_allx) hiy=max(_ally) return (AABB(lox,loy,hix,hiy)) def get_flat_verts(self): if self.flat_verts is None: self.flat_verts = \ list(self.verts[0]) + \ [x for x in chain(*self.verts)] + \ list(self.verts[-1]) return self.flat_verts def get_batch(self): if self.batch is None: self.batch = Batch() flatverts = self.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add( numverts, self.primtype, None, ('v2f/static', flatverts), ('c4B/static', self.color * numverts) ) return self.batch def paint(self, peg): print 'painting', self, '@', peg batch = self.get_batch() glPushMatrix() glTranslatef(peg.x, peg.y, 0) glRotatef(peg.angle, 0, 0, 1) batch.draw() glPopMatrix()
def initialise(): """Initialises the cube render objects. """ global batch batch = Batch() batch.add( 24, GL_QUADS, None, ( "v3f", ( 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, ), ), ( "c3f", ( # green 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, # orange 1.0, 0.5, 0.0, 1.0, 0.5, 0.0, 1.0, 0.5, 0.0, 1.0, 0.5, 0.0, # blue 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, # violet 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, # yellow 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, # red 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, ), ), )
def draw_stars(self): star_batch = Batch() for i in self.star_pts: star_batch.add(4, GL_QUADS, None, ('v2f', i)) star_batch.draw()
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 Drawing: def __init__(self, width, height, fbo, background=[255,255,255]): self.width = width self.height = height self.triangles = [] self.batch = Batch() self.bg_colour = background self.fb = fbo self.bg = self.batch.add( 6, gl.GL_TRIANGLES,None, ("v2i/static", (0,0,0,height,width,height,width,height,width,0,0,0)), ("c3B/static",background*6) ) def clone(self): global group d = Drawing(self.width, self.height, self.fb, self.bg_colour) bufferlength = len(self.triangles) d.vertex_list = d.batch.add( bufferlength*3, gl.GL_TRIANGLES, group, ("v2i/stream", [0]*bufferlength*6), ("c4B/stream", [0]*bufferlength*12) ) d.triangles = [t.clone() for t in self.triangles] d.refresh_batch() return d def mutate(self, num_mutations): triangles = self.triangles for i in xrange(0, num_mutations): e = randint(0,2) if e == 0: choice = randint(0, len(triangles)-1) triangles[choice].recolor_self_delta(5) self.update_index(choice) elif e == 1: choice = randint(0, len(triangles)-1) triangles[choice].reshape_delta(self.width, self.height, 25) self.update_index(choice) elif e == 2: c1 = randint(0, len(triangles)-1) c2 = clamp(c1 + randint(-5,5), 0, len(triangles)-1) triangles[c1],triangles[c2] = triangles[c2],triangles[c1] self.update_index(c1) self.update_index(c2) def update_index(self, i): vl = self.vertex_list t = self.triangles[i] i1 = i*6 vl.vertices[i1:i1+6] = t.serialize_points() i1 *= 2 vl.colors[i1:i1+12] = t.serialize_color()*3 def draw(self): gl.glBindFramebufferEXT(gl.GL_FRAMEBUFFER_EXT, self.fb) gl.glClear(gl.GL_COLOR_BUFFER_BIT) self.batch.draw() def refresh_batch(self): for i in xrange(0, len(self.triangles)): self.update_index(i) def generate(self, number_triangles): vertices = [] colors = [] for i in xrange(0, number_triangles): t = Triangle() t.generate(self.width, self.height) self.triangles.append(t) vertices.extend(t.serialize_points()) colors.extend(t.serialize_color()*3) self.vertex_list = self.batch.add( number_triangles*3, gl.GL_TRIANGLES, None, ("v2i/stream", vertices), ("c4B/stream", colors) ) self.refresh_batch() def svg_import(self, svg_file): """ Import the drawing from an SVG file. """ xml = open(svg_file).read() soup = BeautifulStoneSoup(xml).svg # Width and Height w = int(soup['width'].replace('px', '')) h = int(soup['height'].replace('px', '')) if w != self.width or h != self.height: raise Exception("Image dimensions don't match.") # two clockwise round triangles make a square self.bg.vertices = [ 0,0, 0,h, w,h, w,h, w,0, 0,0] # Background colours try: name,value = soup.rect['style'].split(':') except ValueError: pass if name == 'fill': self.bg_colour[0] = int(value[1:3], 16) self.bg_colour[1] = int(value[3:5], 16) self.bg_colour[2] = int(value[5:7], 16) self.bg.colors = self.bg_colour*6 # Polygons polygons = soup.findAll('polygon') vertices = [] colors = [] for p in polygons: T = Triangle() T.svg_soup_import(p, self.height) self.triangles.append(T) vertices.extend(T.serialize_points()) colors.extend(T.serialize_color()*3) self.vertex_list = self.batch.add( len(polygons)*3, gl.GL_TRIANGLES, XTranslationGroup(self.width * 2, 1), ("v2i/stream", vertices), ("c4B/stream", colors) ) self.refresh_batch() def svg_export(self, image_file, svg_file): """ Export the drawing to an SVG file. """ f = open(svg_file,"w") f.write('''<?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="%dpx" height="%dpx" viewport="0 0 %d %d" version="1.1" xmlns="http://www.w3.org/2000/svg">''' % (self.width,self.height,self.width,self.height)) f.write('\n\t<rect width="100%%" height="100%%" style="fill:#%02x%02x%02x;" />' % ( self.bg_colour[0],self.bg_colour[1],self.bg_colour[2] ) ) for t in self.triangles: f.write('''\n\t<polygon points="%d,%d %d,%d %d,%d" style="fill:#%02x%02x%02x; fill-opacity:%f;" />''' % ( t.points[0][0], self.height - t.points[0][1], t.points[1][0], self.height - t.points[1][1], t.points[2][0], self.height - t.points[2][1], t.color[0],t.color[1],t.color[2],t.color[3]/255.0 )) f.write("\n</svg>") f.close()
class Model(object): """ Stores world and player data, with methods to modify blocks. """ def __init__(self, world=None): # Batch of pyglet VertexLists; everything loaded into the batch is drawn self.batch = Batch() # TextureGroup for managing OpenGL textures. self.group = TextureGroup(image.load(TEXTURE_PATH).get_texture()) # All of the blocks in the world; key is a tuple of (x, y, z) position if world is None: self.world = {} else: self.world = world # Just the blocks that are visible, i.e. exposed on at least one side self.visible = {} # Mapping from position to a pyglet VertexList (only for visible blocks) self.vertices = {} # Mapping from chunks to block locations within those chunks. self.chunks = {} # The chunk in which the player is currently located. self.chunk = None # The player's position in the world, initially at the origin. self.position = (0, 2, 0) # The rotation of the player's view. # First element is in the xz plane, second in some rotation of the yz plane. # Up-down rotation is constrained to between -90 and 90 (straight up and down) self.rotation = (0, 0) # Initialize player motion in the xz plane. # For the first element, -1 and 1 are left and right. # For the second, -1 and 1 are down and up. # For the third, backwards and forwards. self.motion = [0, 0, 0] # A queue for function calls; this allows blocks to be added and removed # in a way that doesn't slow down the game loop self.queue = deque() # Place blocks in the world self._initialize() def _initialize(self): """ Generate terrain to initialize the world. """ for location in self.world: self.add_block(location, self.world[location]) def initial_render(self): """ Make all blocks visually current by emptying the queue without breaks. This method is called once, when the world is first loaded. """ while self.queue: self.dequeue() def get_nearby_chunks(self, location): """ Return a set containing the chunk locations within range of a given chunk. """ x, z = location x_range = range(-CHUNK_DISTANCE, CHUNK_DISTANCE + 1) z_range = range(-CHUNK_DISTANCE, CHUNK_DISTANCE + 1) chunks = set() for dx in x_range: for dz in z_range: # Only include chunks within a certain Euclidean distance if math.sqrt(dx ** 2 + dz ** 2) > CHUNK_DISTANCE: continue chunks.add((x + dx, z + dz)) return chunks def update_chunk_location(self, before, after): """ Ensure that the proper adjacent chunks are visible. """ # Show all chunks in range when none are currently loaded. # This happens once when the program starts. if before is None: chunks = self.get_nearby_chunks(after) for chunk in chunks: self.show_chunk(chunk) return current = self.get_nearby_chunks(before) updated = self.get_nearby_chunks(after) shown = updated - current hidden = current - updated for chunk in shown: self.show_chunk(chunk) for chunk in hidden: self.hide_chunk(chunk) def show_chunk(self, chunk_location): """ Show all blocks contained within a given chunk. """ for position in self.chunks.get(chunk_location, []): if position not in self.visible and self.check_exposed(position): self.enqueue(self.show_block, position) def hide_chunk(self, chunk_location): """ Hide all blocks contained within a given chunk. """ for position in self.chunks.get(chunk_location, []): if position in self.visible: self.visible.pop(position) self.enqueue(self.hide_block, position) def add_block(self, position, block_id): """ Place a block at a given set of coordinates. """ # Place the block in the world self.world[position] = block_id # Register the block in the proper chunk self.chunks.setdefault(get_chunk(position, CHUNK_SIZE), []).append(position) def delete_block(self, position): """ Remove a block from a given set of coordinates. """ pass def show_block(self, position): """ Add a block to the batch to be rendered by OpenGL. The block will continue being rendered until it's hidden or deleted. """ # Add to record of visible blocks self.visible[position] = self.world[position] # Find the texture coordinates for the block texture_location = BLOCKS[self.world[position]]['texture'] # Convert cube coordinates and texture position to OpenGl vertices vertex_data = cube_vertices(position, 1) texture_data = texture_map(*texture_location) # Add the cube to the batch, mapping texture vertices to cube vertices. self.vertices[position] = self.batch.add(24, GL_QUADS, self.group, ('v3f/static', vertex_data), ('t2f/static', texture_data)) def hide_block(self, position): """ Stop a block from being rendered. """ # Remove from the batch by deleting the vertex list self.vertices.pop(position).delete() def check_exposed(self, position): """ Return true if any of the faces of a block are exposed, otherwise false. """ x, y, z = position for dx, dy, dz in DIRECTIONS: if (x + dx, y + dy, z + dz) not in self.world: return True return False def enqueue(self, func, *args): """ Add a function to the queue to be called in the program's update loop. """ self.queue.append((func, args)) def dequeue(self): """ Call the function at the top of the queue. """ func, args = self.queue.popleft() func(*args) def process_queue(self): """ Call as many functions in the queue as possible within one tick of the game loop. """ start_time = time.clock() while self.queue and time.clock() - start_time < 1.0 / TICKS: self.dequeue()
def cif_cabinet(x, y, z): batch = Batch() bottom_left = texture.textures['cif_cabinet_lower_left']['master_coordinates'] bottom = texture.textures['cif_cabinet_bottom']['master_coordinates'] bottom_right = texture.textures['cif_cabinet_lower_right']['master_coordinates'] left = texture.textures['cif_cabinet_left']['master_coordinates'] right = texture.textures['cif_cabinet_right']['master_coordinates'] top_left = texture.textures['cif_cabinet_upper_left']['master_coordinates'] top = texture.textures['cif_cabinet_top']['master_coordinates'] top_right = texture.textures['cif_cabinet_upper_right']['master_coordinates'] base = texture.textures['cif_cabinet_base']['master_coordinates'] line = texture.textures['cif_cabinet_line']['master_coordinates'] handles = texture.textures['cif_cabinet_handles']['master_coordinates'] tiles = [ # Front side plane_tile.PlaneTile((x, y, z), side='right', textures={'right': bottom_left}), plane_tile.PlaneTile((x, y, z+1), side='right', textures={'right': bottom}), plane_tile.PlaneTile((x, y, z+2), side='right', textures={'right': bottom_right}), plane_tile.PlaneTile((x, y+1, z), side='right', textures={'right': left}), plane_tile.PlaneTile((x, y+1, z+1), side='right', textures={'right': line}), plane_tile.PlaneTile((x, y+1, z+2), side='right', textures={'right': right}), plane_tile.PlaneTile((x, y+2, z), side='right', textures={'right': left}), plane_tile.PlaneTile((x, y+2, z+1), side='right', textures={'right': handles}), plane_tile.PlaneTile((x, y+2, z+2), side='right', textures={'right': right}), plane_tile.PlaneTile((x, y+3, z), side='right', textures={'right': left}), plane_tile.PlaneTile((x, y+3, z+1), side='right', textures={'right': line}), plane_tile.PlaneTile((x, y+3, z+2), side='right', textures={'right': right}), plane_tile.PlaneTile((x, y+4, z), side='right', textures={'right': top_left}), plane_tile.PlaneTile((x, y+4, z+1), side='right', textures={'right': top}), plane_tile.PlaneTile((x, y+4, z+2), side='right', textures={'right': top_right}), # Left side plane_tile.PlaneTile((x+1, y, z), side='back', textures={'back': bottom_left}), plane_tile.PlaneTile((x+1, y+1, z), side='back', textures={'back': left}), plane_tile.PlaneTile((x+1, y+2, z), side='back', textures={'back': left}), plane_tile.PlaneTile((x+1, y+3, z), side='back', textures={'back': left}), plane_tile.PlaneTile((x+1, y+4, z), side='back', textures={'back': top_left}), plane_tile.PlaneTile((x+2, y, z), side='back', textures={'back': bottom_right}), plane_tile.PlaneTile((x+2, y+1, z), side='back', textures={'back': right}), plane_tile.PlaneTile((x+2, y+2, z), side='back', textures={'back': right}), plane_tile.PlaneTile((x+2, y+3, z), side='back', textures={'back': right}), plane_tile.PlaneTile((x+2, y+4, z), side='back', textures={'back': top_right}), # Right side plane_tile.PlaneTile((x+1, y, z+2), side='front', textures={'front': bottom_right}), plane_tile.PlaneTile((x+1, y+1, z+2), side='front', textures={'front': right}), plane_tile.PlaneTile((x+1, y+2, z+2), side='front', textures={'front': right}), plane_tile.PlaneTile((x+1, y+3, z+2), side='front', textures={'front': right}), plane_tile.PlaneTile((x+1, y+4, z+2), side='front', textures={'front': top_right}), plane_tile.PlaneTile((x+2, y, z+2), side='front', textures={'front': bottom_left}), plane_tile.PlaneTile((x+2, y+1, z+2), side='front', textures={'front': left}), plane_tile.PlaneTile((x+2, y+2, z+2), side='front', textures={'front': left}), plane_tile.PlaneTile((x+2, y+3, z+2), side='front', textures={'front': left}), plane_tile.PlaneTile((x+2, y+4, z+2), side='front', textures={'front': top_left}), ] for tile in tiles: vertex_list = tile.get_vertex_list() texture_list = tile.get_texture_list() vertices = len(vertex_list) / 3 batch.add(vertices, GL_QUADS, texture.textures['master']['group'], ('v3f/static', vertex_list), ('t2f/static', texture_list)) return batch
def initialise(): """Initialises the cube render objects. """ global batch batch = Batch() batch.add( 24, GL_QUADS, None, ('v3f', (1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0)), ( 'c3f', ( # green 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, # orange 1.0, 0.5, 0.0, 1.0, 0.5, 0.0, 1.0, 0.5, 0.0, 1.0, 0.5, 0.0, # blue 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, # violet 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, # yellow 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, # red 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, )))
def allocate_verts(cls, count: int, batch: Batch, group: Optional[Group] = None) -> VertexList: return batch.add(count * cls.vert_count, cls.gl_mode.gl_mode, group, "v2f/stream", "c4B/stream")
class Shape(object): ''' Shape is a mixed list of: - other Shapes - base elements - list of vertices - RGBA color - single OpenGL prim: TRIANGLE_STRIP, LINE_STRIP, LINE , POINT ''' new_id = count().next def __init__(self, **kwargs): self._id = Shape.new_id() for key in kwargs: setattr(self,key,kwargs[key]) self.verts=[] self.shapes=[] self.build() if not hasattr(self,'color'): self.color = red if not hasattr(self,'peg'): # peg must be tuple (x, y, a) self.peg = DOCKED if not hasattr(self,'vel'): # peg must be tuple (vx, vy, av) self.vel = IDLE if not hasattr (self,'pivot'): self.pivot=(0,0) # or pivot at AABB center ?? # TODO self.sort() self.aabb=self.get_aabb() self.flat_verts = None self.batch=None print "::" print ":: new shape ::::::::::::::::::::::::::::::::::::::::::::::::::" print "::" dumpObj(self) def build(self): ''' if this method isn't overridden the new element is either - a list of existing shapes - epmty ''' pass def get_aabb(self): # POSSIBLE OPTIMIZATION # TODO _allx=[0] _ally=[0] for v in self.verts: _allx.append(v[0]) _ally.append(v[1]) lox=min(_allx) loy=min(_ally) hix=max(_allx) hiy=max(_ally) return (AABB(lox,loy,hix,hiy)) def copy(self): ssh=Shape(verts=self.verts, color=self.color,\ primtype=self.primtype, peg=self.peg, vel=self.vel) return(ssh) def add(self,*args): ''' Add items to the group list ''' for sh in args: self.shapes.append(sh) #add items print "::" print "GROUPED SHAPES" print "::" self.sort() def sort(self): if not self.shapes ==[]: print 'REORDERING ACTORS' for s in self.shapes: print s._id, print self.shapes = sorted(self.shapes, key=lambda sh: sh.peg.z, reverse=True) print 'NEW ORDER' for s in self.shapes: print s._id, def get_instances(self,root): if root.shapes==[]: yield root else: glPushMatrix() glTranslatef(root.peg.x, root.peg.y, 0) glRotatef(root.peg.angle, 0, 0, 1) for element in root.shapes: for e in self.get_instances(element): yield e glPopMatrix() def get_flat_verts(self): if self.flat_verts is None: self.flat_verts = \ list(self.verts[0]) + \ [x for x in chain(*self.verts)] + \ list(self.verts[-1]) return self.flat_verts def get_batch(self): self.batch = Batch() flatverts = self.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add( numverts, self.primtype, None, ('v2f/static', flatverts), ('c4B/static', self.color * numverts) ) def paint(self): print print 'paint', for sh in self.get_instances(self): if sh.batch is None: sh.get_batch() glPushMatrix() glTranslatef(sh.peg.x, sh.peg.y, 0) glRotatef(sh.peg.angle, 0, 0, 1) sh.batch.draw() glPopMatrix() print sh._id,
class Shape(object): ''' Shape is a mixed list of: - other Shapes - base elements - list of vertices - RGBA color - single OpenGL prim: TRIANGLE_STRIP, LINE_STRIP, LINE , POINT ''' shapes=[] def __init__(self, **kwargs): for key in kwargs: setattr(self,key,kwargs[key]) if not hasattr(self,'visible'): self.visible=True if not hasattr(self,'color'): self.color = red if not hasattr(self,'peg'): # peg must be tuple (x, y, a) self.peg = DOCKED if not hasattr(self,'vel'): # peg must be tuple (vx, vy, av) self.vel = IDLE if not hasattr (self,'pivot'): self.pivot=(0,0) # or pivot at AABB center ?? if not hasattr (self,'shapes'): self.shapes=[] if not hasattr (self,'verts'): self.verts=[] self.sort() self.flat_verts = None self.batch=None print ":: new shape", self.id Shape.shapes.append(self) dumpObj(self) def group(self,*args): ''' Group items to group list ''' for sh in args: self.shapes.append(sh) #add items print ":: GROUPED SHAPES" self.sort() def sort(self): if not self.shapes==[]: for s in self.shapes: print s.id, print self.shapes = sorted(self.shapes, key=lambda sh: sh.peg.z, reverse=True) print 'NEW ORDER', for s in self.shapes: print s.id, def get_flat_verts(self): if self.flat_verts is None: self.flat_verts = \ list(self.verts[0]) + \ [x for x in chain(*self.verts)] + \ list(self.verts[-1]) return self.flat_verts def get_batch(self): self.batch = Batch() flatverts = self.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add( numverts, self.primtype, None, ('v2f/static', flatverts), ('c4B/static', self.color * numverts) ) def yeld_simple_shapes(self,root): if root.verts==[]: #root is a set of shapes glPushMatrix() glTranslatef(root.peg.x, root.peg.y, 0) glRotatef(root.peg.angle, 0, 0, 1) for element in root.shapes: for e in self.yeld_simple_shapes(element): yield e glPopMatrix() else: #root is a shape itself yield root def yeld_verts(self): for sh in self.yeld_simple_shapes(self): for v in sh.verts: yield v def offset(self, dx, dy): ''' offset will change the value of all the shapes vertexes ''' newverts = [(v[0] + dx, v[1] + dy) for v in self.yeld_verts()] self.verts=newverts def gl_output(self): for sh in self.yeld_simple_shapes(self): if sh.batch is None: sh.get_batch() glPushMatrix() glTranslatef(sh.peg.x, sh.peg.y, 0) glRotatef(sh.peg.angle, 0, 0, 1) sh.batch.draw() print ('.'), glPopMatrix() @classmethod def draw(cls): visible_shapes = ifilter(lambda s: s.visible, cls.shapes) for sh in visible_shapes: cls.gl_output(sh)
class Drawing: def __init__(self, width, height, background=[255, 255, 255]): self.width = width self.height = height self.triangles = [] self.batch = Batch() self.bg_colour = background has_fbo = gl.gl_info.have_extension('GL_EXT_framebuffer_object') #setup a framebuffer self.fb = gl.GLuint() gl.glGenFramebuffersEXT(1, ctypes.byref(self.fb)) gl.glBindFramebufferEXT(gl.GL_FRAMEBUFFER_EXT, self.fb) #allocate a texture for the fb to render to tex = image.Texture.create_for_size(gl.GL_TEXTURE_2D, width, height, gl.GL_RGBA) gl.glBindTexture(gl.GL_TEXTURE_2D, tex.id) gl.glFramebufferTexture2DEXT(gl.GL_FRAMEBUFFER_EXT, gl.GL_COLOR_ATTACHMENT0_EXT, gl.GL_TEXTURE_2D, tex.id, 0) status = gl.glCheckFramebufferStatusEXT(gl.GL_FRAMEBUFFER_EXT) assert status == gl.GL_FRAMEBUFFER_COMPLETE_EXT gl.glBindFramebufferEXT(gl.GL_FRAMEBUFFER_EXT, 0) self.bg = self.batch.add( 6, gl.GL_TRIANGLES, None, ("v2i/static", (0, 0, 0, height, width, height, width, height, width, 0, 0, 0)), ("c3B/static", background * 6)) def clone(self): global group d = Drawing(self.width, self.height, self.bg_colour) bufferlength = len(self.triangles) d.vertex_list = d.batch.add(bufferlength * 3, gl.GL_TRIANGLES, group, ("v2i/stream", [0] * bufferlength * 6), ("c4B/stream", [0] * bufferlength * 12)) d.triangles = [t.clone() for t in self.triangles] d.refresh_batch() return d def mutate(self, num_mutations): triangles = self.triangles for i in xrange(0, num_mutations): e = randint(0, 2) if e == 0: choice = randint(0, len(triangles) - 1) triangles[choice].recolor_self_delta(5) self.update_index(choice) elif e == 1: choice = randint(0, len(triangles) - 1) triangles[choice].reshape_delta(self.width, self.height, 25) self.update_index(choice) elif e == 2: c1 = randint(0, len(triangles) - 1) c2 = clamp(c1 + randint(-5, 5), 0, len(triangles) - 1) triangles[c1], triangles[c2] = triangles[c2], triangles[c1] self.update_index(c1) self.update_index(c2) def update_index(self, i): vl = self.vertex_list t = self.triangles[i] i1 = i * 6 vl.vertices[i1:i1 + 6] = t.serialize_points() i1 *= 2 vl.colors[i1:i1 + 12] = t.serialize_color() * 3 def draw(self): gl.glBindFramebufferEXT(gl.GL_FRAMEBUFFER_EXT, self.fb) self.batch.draw() gl.glBindFramebufferEXT(gl.GL_FRAMEBUFFER_EXT, 0) def refresh_batch(self): for i in xrange(0, len(self.triangles)): self.update_index(i) def generate(self, number_triangles): vertices = [] colors = [] for i in xrange(0, number_triangles): t = Triangle() t.generate(self.width, self.height) self.triangles.append(t) vertices.extend(t.serialize_points()) colors.extend(t.serialize_color() * 3) self.vertex_list = self.batch.add(number_triangles * 3, gl.GL_TRIANGLES, None, ("v2i/stream", vertices), ("c4B/stream", colors)) self.refresh_batch() def svg_import(self, svg_file): """ Import the drawing from an SVG file. """ xml = open(svg_file).read() soup = BeautifulStoneSoup(xml).svg # Width and Height w = int(soup['width'].replace('px', '')) h = int(soup['height'].replace('px', '')) if w != self.width or h != self.height: raise Exception("Image dimensions don't match.") # two clockwise round triangles make a square self.bg.vertices = [0, 0, 0, h, w, h, w, h, w, 0, 0, 0] # Background colours try: name, value = soup.rect['style'].split(':') except ValueError: pass if name == 'fill': self.bg_colour[0] = int(value[1:3], 16) self.bg_colour[1] = int(value[3:5], 16) self.bg_colour[2] = int(value[5:7], 16) self.bg.colors = self.bg_colour * 6 # Polygons polygons = soup.findAll('polygon') vertices = [] colors = [] for p in polygons: T = Triangle() T.svg_soup_import(p, self.height) self.triangles.append(T) vertices.extend(T.serialize_points()) colors.extend(T.serialize_color() * 3) self.vertex_list = self.batch.add( len(polygons) * 3, gl.GL_TRIANGLES, XTranslationGroup(self.width * 2, 1), ("v2i/stream", vertices), ("c4B/stream", colors)) self.refresh_batch() def svg_export(self, image_file, svg_file): """ Export the drawing to an SVG file. """ f = open(svg_file, "w") f.write('''<?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="%dpx" height="%dpx" viewport="0 0 %d %d" version="1.1" xmlns="http://www.w3.org/2000/svg">''' % (self.width, self.height, self.width, self.height)) f.write( '\n\t<rect width="100%%" height="100%%" style="fill:#%02x%02x%02x;" />' % (self.bg_colour[0], self.bg_colour[1], self.bg_colour[2])) for t in self.triangles: f.write( '''\n\t<polygon points="%d,%d %d,%d %d,%d" style="fill:#%02x%02x%02x; fill-opacity:%f;" />''' % (t.points[0][0], self.height - t.points[0][1], t.points[1][0], self.height - t.points[1][1], t.points[2][0], self.height - t.points[2][1], t.color[0], t.color[1], t.color[2], t.color[3] / 255.0)) f.write("\n</svg>") f.close()
class Composite(object): ''' a list of shapes _ Composites have shapes and a peg" ''' _instances = set() def __init__(self, *args, **kwargs): self.shapes = [] self.batch = None if args: self.add_items(args) for i in kwargs: setattr(self,i,kwargs[i]) if not hasattr(self,'peg'): # peg must be tuple (x, y, a) self.peg = DOCKED if not hasattr(self,'vel'): # peg must be tuple (vx, vy, av) self.vel = IDLE self._instances.add(weakref.ref(self)) print "::" print ":: new C shape ::::::::::::::::::::::::::::::::::::::::::::::::" print "::" dumpObj(self) @classmethod def get_instances(cls): dead = set() for ref in cls._instances: obj = ref() if obj is not None: yield obj else: dead.add(ref) cls._instances -= dead def add_items(self, items): ''' Add a list containing shapes and shapes ''' for item in items: if isinstance(item, Composite): #item is a C-shape for ssh in item.shapes: #decompose item into shapes ssh=ssh.copy() #mke a copy move it at new pos ssh.verts = offset(ssh.verts, item.peg.x+ssh.peg.x, \ item.peg.y+ssh.peg.y) self.shapes.append(ssh) #add it ssh.peg = DOCKED ssh.vel = IDLE elif isinstance(item, Shape): ssh=item.copy() print ssh.verts, ssh.peg.x, ssh.peg.y ssh.verts = offset(ssh.verts, ssh.peg.x, ssh.peg.y) print ssh.verts self.shapes.append(ssh) # item is a shape, add a copy of it ssh.peg = DOCKED ssh.vel = IDLE else: pass def get_batch(self): print ':: getting C-Shape batch' print ':: self batch is :', self.batch if self.batch is None: self.batch = Batch() print ':: new batch :' for ssh in self.shapes: print ':: shape :', ssh flatverts = ssh.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add( numverts, ssh.primtype, None, ('v2f/static', flatverts), ('c4B/static', ssh.color * numverts) ) return self.batch def paint(self): print ':: displaying C-Shape :', self batch = self.get_batch() glPushMatrix() glTranslatef(self.peg.x, self.peg.y, 0) glRotatef(self.peg.angle, 0, 0, 1) batch.draw() # OPTIMISATION : cs.batch.draw() directement avec batch déjà à jour TODO glPopMatrix() print ''
class Shape(object): ''' Shape is a mixed list of: - other Shapes - base elements - list of vertices - RGBA color - single OpenGL prim: TRIANGLE_STRIP, LINE_STRIP, LINE , POINT ''' shapes = [] def __init__(self, **kwargs): for key in kwargs: setattr(self, key, kwargs[key]) if not hasattr(self, 'visible'): self.visible = True if not hasattr(self, 'color'): self.color = red if not hasattr(self, 'peg'): # peg must be tuple (x, y, a) self.peg = DOCKED if not hasattr(self, 'vel'): # peg must be tuple (vx, vy, av) self.vel = IDLE if not hasattr(self, 'pivot'): self.pivot = (0, 0) # or pivot at AABB center ?? if not hasattr(self, 'shapes'): self.shapes = [] if not hasattr(self, 'verts'): self.verts = [] self.sort() self.flat_verts = None self.batch = None print ":: new shape", self.id Shape.shapes.append(self) dumpObj(self) def group(self, *args): ''' Group items to group list ''' for sh in args: self.shapes.append(sh) #add items print ":: GROUPED SHAPES" self.sort() def sort(self): if not self.shapes == []: for s in self.shapes: print s.id, print self.shapes = sorted(self.shapes, key=lambda sh: sh.peg.z, reverse=True) print 'NEW ORDER', for s in self.shapes: print s.id, def get_flat_verts(self): if self.flat_verts is None: self.flat_verts = \ list(self.verts[0]) + \ [x for x in chain(*self.verts)] + \ list(self.verts[-1]) return self.flat_verts def get_batch(self): self.batch = Batch() flatverts = self.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add(numverts, self.primtype, None, ('v2f/static', flatverts), ('c4B/static', self.color * numverts)) def yeld_simple_shapes(self, root): if root.verts == []: #root is a set of shapes glPushMatrix() glTranslatef(root.peg.x, root.peg.y, 0) glRotatef(root.peg.angle, 0, 0, 1) for element in root.shapes: for e in self.yeld_simple_shapes(element): yield e glPopMatrix() else: #root is a shape itself yield root def yeld_verts(self): for sh in self.yeld_simple_shapes(self): for v in sh.verts: yield v def offset(self, dx, dy): ''' offset will change the value of all the shapes vertexes ''' newverts = [(v[0] + dx, v[1] + dy) for v in self.yeld_verts()] self.verts = newverts def gl_output(self): for sh in self.yeld_simple_shapes(self): if sh.batch is None: sh.get_batch() glPushMatrix() glTranslatef(sh.peg.x, sh.peg.y, 0) glRotatef(sh.peg.angle, 0, 0, 1) sh.batch.draw() print('.'), glPopMatrix() @classmethod def draw(cls): visible_shapes = ifilter(lambda s: s.visible, cls.shapes) for sh in visible_shapes: cls.gl_output(sh)
class Shape(object): ''' Shapes have Vertices, a single color, and a single OpenGL primitive ''' _instances = set() def __init__(self, **kwargs): for key in kwargs: setattr(self,key,kwargs[key]) if not hasattr(self,'peg'): # peg must be tuple (x, y, a) self.peg = DOCKED if not hasattr(self,'vel'): # peg must be tuple (vx, vy, av) self.vel = IDLE self.build() self.aabb=get_aabb(self.verts) self.flat_verts = None self.batch=None self._instances.add(weakref.ref(self)) print "::" print ":: new shape ::::::::::::::::::::::::::::::::::::::::::::::::::" print "::" dumpObj(self) @classmethod def get_instances(cls): dead = set() for ref in cls._instances: obj = ref() if obj is not None: yield obj else: dead.add(ref) cls._instances -= dead def copy(self): ssh=Shape(verts=self.verts, color=self.color,\ primtype=self.primtype, peg=self.peg, vel=self.vel) return(ssh) def get_flat_verts(self): if self.flat_verts is None: self.flat_verts = \ list(self.verts[0]) + \ [x for x in chain(*self.verts)] + \ list(self.verts[-1]) return self.flat_verts def get_batch(self): if self.batch is None: self.batch = Batch() flatverts = self.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add( numverts, self.primtype, None, ('v2f/static', flatverts), ('c4B/static', self.color * numverts) ) return self.batch def paint(self): batch = self.get_batch() glPushMatrix() glTranslatef(self.peg.x, self.peg.y, 0) glRotatef(self.peg.angle, 0, 0, 1) batch.draw() glPopMatrix() def build(self): pass
class Shape(object): ''' A list of primitives ''' def __init__(self, items=None, posx=0.0, posy=0.0, angle=0.0, vx=0.0, vy=0.0, va=0.0, drawable=True): self.primitives = [] self.posx = posx*1.0 self.posy = posy*1.0 self.angle = angle*1.0 self.vx=vx*1.0 self.vy=vy*1.0 self.va=va*1.0 # angular velocity self.drawable=drawable self.batch = None Field.display_list.append(self) if items: self.add_items(items) (self.minx,self.miny,self.maxx,self.maxy) = self.get_aabb() else: self.minx.self.maxx,self.miny,self.maxy = 0,0,0,0 self.drawable = False def add_items(self, items): "Add a list of primitives and shapes" for item in items: if isinstance(item, Shape): self.add_shape(item) else: self.primitives.append(item) def add_shape(self, other): "Add the primitives from a given shape" for prim in other.primitives: self.primitives.append(prim) def get_batch(self): if self.batch is None: self.batch = Batch() for prim in self.primitives: flatverts = prim.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add( numverts, prim.primtype, None, ('v2f/static', flatverts), ('c3B/static', prim.color * numverts) ) return self.batch def transform(self,M): """ applies matrix M to all self primitives """ for prim in self.primitives: prim.transform(M) def get_aabb(self): aabb = namedtuple('AABB',['xmin','xmax','ymin','ymax']) _allx=[] _ally=[] for prim in self.primitives: for v in prim.verts: _allx.append(v[0]) _ally.append(v[1]) minx=min(_allx) miny=min(_ally) maxx=max(_allx) maxy=max(_ally) box = (minx,miny,maxx,maxy) return (box) def paint(self): if self.drawable: glPushMatrix() glTranslatef(self.posx, self.posy, 0) glRotatef(self.angle, 0, 0, 1) batch = self.get_batch() batch.draw() glPopMatrix() else: print 'cell', self, 'is not drawable'
class Swept: def __init__(self, data, slices, rings): self.type = data.type self.sections = data.sections self.scales = CMSpline(data.scales, None, False) self.translates = CMSpline(data.translates, None, False) self.orients = CMSpline(data.orients, Quaternion, False) self.curves = [] self.slices = slices self.rings = rings self.positions = [] # loops for i in range(len(self.scales.pieces)): self.curves.insert(i, self.type(data.curves[i],None, True)) # positions print "making positions" for i in range(len(self.scales.pieces)): scale = n.dot(BezBase, self.scales.pieces[i]) translate = n.dot(BezBase, self.translates.pieces[i]) rotate = self.orients.pieces[i] for j in n.linspace(0, 1, self.rings, endpoint=False): mono = n.array([j**3, j**2, j, 1], dtype=GLfloat).reshape((1,4)) scale_ = n.dot(mono, scale) translate_ = n.dot(mono, translate) s0 = Quaternion.slerp(j, rotate[0], rotate[1]) s1 = Quaternion.slerp(j, rotate[1], rotate[2]) s2 = Quaternion.slerp(j, rotate[2], rotate[3]) s3 = Quaternion.slerp(j, s0, s1) s4 = Quaternion.slerp(j, s1, s2) rotate_ = Quaternion.slerp(j, s3, s4) poses = [] for l in self.curves[i].pieces: for k in n.linspace(0, 1, self.slices, endpoint=False): mono_ = n.array([k**3, k**2, k, 1], dtype=GLfloat) pos = n.dot(n.dot(mono_, self.type.base), l) pos = scale_[0]*pos pos = (rotate_*Quaternion(0, *pos)*rotate_.inv()).arr pos = pos + translate_ poses.append(*pos) self.positions.append(poses) # normals print "making normals" self.normals = [] norms = [] l = len(self.positions[0]) center = sum(self.positions[0])/l for i in range(l): v1 = self.positions[0][(i+1)%l] - self.positions[0][i] v2 = self.positions[1][i] - self.positions[0][i] v3 = self.positions[0][i-1] - self.positions[0][i] n1 = Vec3(*n.cross(v1, v2)).normalized() n2 = Vec3(*n.cross(v2, v3)).normalized() norm = (n1+n2).normalized().arr if n.dot(self.positions[0][i] - center, norm) < 0: norm = -norm norms.append(norm) self.normals.append(norms) for i in range(1, len(self.positions) - 1): norms = [] center = sum(self.positions[i])/l for j in range(l): v1 = self.positions[i][(j+1)%l] - self.positions[i][j] v2 = self.positions[i+1][j] - self.positions[i][j] v3 = self.positions[i][j-1] - self.positions[i][j] v4 = self.positions[i-1][j] - self.positions[i][j] n1 = Vec3(*n.cross(v1, v2)).normalized() n2 = Vec3(*n.cross(v2, v3)).normalized() n3 = Vec3(*n.cross(v3, v4)).normalized() n4 = Vec3(*n.cross(v4, v1)).normalized() norm = (n1+n2+n3+n4).normalized().arr if n.dot(self.positions[i][j] - center, norm) < 0: norm = -norm norms.append(norm) self.normals.append(norms) norms = [] center = sum(self.positions[-1])/l for i in range(l): v1 = self.positions[-1][i-1] - self.positions[-1][i] v2 = self.positions[-2][i] - self.positions[-1][i] v3 = self.positions[-1][(i+1)%l] - self.positions[-1][i] n1 = Vec3(*n.cross(v1, v2)).normalized() n2 = Vec3(*n.cross(v2, v3)).normalized() norm = (n1+n2).normalized().arr if n.dot(self.positions[-1][i] - center, norm) < 0: norm = -norm norms.append(norm) self.normals.append(norms) # making vertexes gl print "batching" self.batch = Batch() for i in range(1, len(self.positions)): poses = reduce(lambda x,y:x+y, map(lambda x,y:list(x)+list(y), self.positions[i-1], self.positions[i])) poses += poses[0:6] norms = reduce(lambda x,y:x+y, map(lambda x,y:list(x)+list(y), self.normals[i-1], self.normals[i])) norms += norms[0:6] self.batch.add(len(poses)/3, GL_TRIANGLE_STRIP, None, ('v3f/static', poses),('n3f/static', norms)) def draw(self): self.batch.draw()
class Gl_Prim(object): """ Stores a list of vertices, a single color, and a primitive type Intended to be rendered as a single OpenGL primitive """ def __init__(self, verts, color, primtype=GL_TRIANGLE_STRIP): global setup self.verts = verts self.color = color self.primtype = primtype self.vertex_list = None self.flat_verts = None self.batch = None self.peg = dk self.aabb = self.get_aabb() setup.append(self) def offset(self, vx, vy): newverts = [(v[0] + vx, v[1] + vy) for v in self.verts] return Gl_Prim(newverts, self.color, primtype=self.primtype) def transform(self,M): """ applies matrix M transformation to all self vertexes """ newverts = [ (M[0]*v[0]+M[1]*v[1]+M[2], M[3]*v[0]+M[4]*v[1]+M[5]) for v in self.verts] return Gl_Prim(newverts, self.color, primtype=self.primtype) def peg_to(self, p): self.peg = p def get_aabb(self): _allx=[] _ally=[] for v in self.verts: _allx.append(v[0]) _ally.append(v[1]) lox=min(_allx) loy=min(_ally) hix=max(_allx) hiy=max(_ally) return (AABB(lox,loy,hix,hiy)) def get_flat_verts(self): if self.flat_verts is None: self.flat_verts = \ list(self.verts[0]) + \ [x for x in chain(*self.verts)] + \ list(self.verts[-1]) return self.flat_verts def get_batch(self): if self.batch is None: self.batch = Batch() flatverts = self.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add( numverts, self.primtype, None, ('v2f/static', flatverts), ('c4B/static', self.color * numverts) ) return self.batch def paint(self, peg): glPushMatrix() glTranslatef(peg.x, peg.y, 0) glRotatef(peg.angle, 0, 0, 1) batch = self.get_batch() batch.draw() glPopMatrix()
class Shape(object): """ Stores a list of vertices, a single color, and a primitive type Intended to be rendered as a single OpenGL primitive """ def __init__(self, name, **kwargs): global lmnts if name in lmnts: raise ValueError('duplicate shape name', name) exit(1) self.name = name lmnts[self.name] = self for i in kwargs: setattr(self, i, kwargs[i]) self.build() self.aabb = self.get_aabb() self.flat_verts = None self.batch = None def offset(self, vx, vy): newverts = [(v[0] + vx, v[1] + vy) for v in self.verts] self.verts = newverts def transform(self, M): """ applies matrix M transformation to all self vertexes """ newverts = [(M[0] * v[0] + M[1] * v[1] + M[2], M[3] * v[0] + M[4] * v[1] + M[5]) for v in self.verts] return Shape(newverts, self.color, primtype=self.primtype) #TODO replace shape verts only def get_aabb(self): _allx = [] _ally = [] for v in self.verts: _allx.append(v[0]) _ally.append(v[1]) lox = min(_allx) loy = min(_ally) hix = max(_allx) hiy = max(_ally) return (AABB(lox, loy, hix, hiy)) def get_flat_verts(self): if self.flat_verts is None: self.flat_verts = \ list(self.verts[0]) + \ [x for x in chain(*self.verts)] + \ list(self.verts[-1]) return self.flat_verts def get_batch(self): if self.batch is None: self.batch = Batch() flatverts = self.get_flat_verts() numverts = len(flatverts) / 2 self.batch.add(numverts, self.primtype, None, ('v2f/static', flatverts), ('c4B/static', self.color * numverts)) return self.batch def paint(self, peg): print 'painting', self, '@', peg batch = self.get_batch() glPushMatrix() glTranslatef(peg.x, peg.y, 0) glRotatef(peg.angle, 0, 0, 1) batch.draw() glPopMatrix()
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 Map(object): """ Map is an object to describe the virtual world. For `no_collision` task, the `Map` contains a floor and other optional obstacle walls. Drone is rendered as 3D model with flighting pose. For `velocity_control` task, the `Map` ONLY contains a 3D drone model. Moreover, the velocity vector of the drone is shown with an orange arrow; the expected velocity vector of the drone is shown with a yellow arrow. Args: drone_3d_model (str): path to 3D STL model of the drone. horizon_view_size (int): number of blocks to show in horizon view. init_drone_z (float): the initial height of the drone. task (str): name of the task setting. Currently, support `no_collision` and `velocity_control`. """ def __init__(self, drone_3d_model, horizon_view_size=8, init_drone_z=5, task='no_collision', debug_mode=False): self.task = task self.debug_mode = debug_mode # When increase this, show more blocks in current view window self.horizon_view_size = horizon_view_size # A Batch is a collection of vertex lists for batched rendering self.batch = Batch() # Manages an OpenGL texture self.group = TextureGroup(image.load(TEXTURE_PATH).get_texture()) # A mapping from position to the texture for whole, global map self.whole_map = dict() # Same as `whole_map` but only contains the positions to show self.partial_map = dict() # A mapping from position to a pyglet `VertextList` in `partial_map` self._partial_map = dict() # A mapping from sector to a list of positions (contiguous sub-region) # using sectors for fast rendering self.sectors = dict() # Use deque to populate calling of `_show_block` and `_hide_block` self.queue = deque() # A graphics batch to draw drone 3D model self.drone_batch = pyglet.graphics.Batch() # Load drone triangular mesh and scene self.drone_name = os.path.basename(drone_3d_model) self.drone_mesh = trimesh.load(drone_3d_model) self.drone_scene = self.drone_mesh.scene() # Drawer stores drone scene geometry as vertex list in its model space self.drone_drawer = None # Store drone geometry hashes for easy retrival self.drone_vertex_list_hash = '' # Store drone geometry rendering mode, default gl.GL_TRIANGLES self.drone_vertex_list_mode = gl.GL_TRIANGLES # Store drone geometry texture self.drone_texture = None black = np.array([0, 0, 0, 255], dtype=np.uint8) red = np.array([255, 0, 0, 255], dtype=np.uint8) green = np.array([0, 255, 0, 255], dtype=np.uint8) blue = np.array([0, 0, 255, 255], dtype=np.uint8) for i, facet in enumerate(self.drone_mesh.facets): if i < 30: self.drone_mesh.visual.face_colors[facet] = black elif i < 42: self.drone_mesh.visual.face_colors[facet] = red elif i < 54: self.drone_mesh.visual.face_colors[facet] = green elif i < 66: self.drone_mesh.visual.face_colors[facet] = blue else: self.drone_mesh.visual.face_colors[facet] = black # Mark positions of bounding wall and obstacles in the map self._initialize(init_drone_z) def _initialize(self, init_drone_z): if self.task in ['no_collision', 'hovering_control']: h = w = 100 for y in range(0, h): for x in range(0, w): # Pave the floor self._add_block((x, y, 0), TILE, immediate=False) elif self.task == 'velocity_control': h = w = 0 self.drone_pos = [h // 2, w // 2, init_drone_z] self._add_drone() if self.task == 'velocity_control': self.drone_velocity_drawer = self._add_drone_velocity( np.array([0.0, 0.0, 1.0]), color=[255, 95, 63]) # orange self.drone_expected_velocity_drawer = self._add_drone_velocity( np.array([0.0, 0.0, 1.0]), color=[240, 210, 90]) # yellow def _is_exposed(self, position): x, y, z = position for dx, dy, dz in FACES: if (x + dx, y + dy, z + dz) not in self.whole_map: # At least one face is not covered by another cube block. return True return False def _add_drone(self): """ Add the drone 3D model in its own model space. """ for name, geom in self.drone_scene.geometry.items(): if geom.is_empty: continue if geometry_hash(geom) == self.drone_vertex_list_hash: continue if name == self.drone_name: args = rendering.convert_to_vertexlist(geom, smooth=True) self.drone_drawer = self.drone_batch.add_indexed(*args) self.drone_vertex_list_hash = geometry_hash(geom) self.drone_vertex_list_mode = args[1] try: assert len(geom.visual.uv) == len(geom.vertices) has_texture = True except BaseException: has_texture = False if has_texture: self.drone_texture = rendering.material_to_texture( geom.visual.material) def _add_drone_velocity(self, init_velocity_vector, radius=0.008, color=[255, 0, 0]): """ Add the drone velocity vector as a cylinder into drone drawer batch. """ translation = np.eye(4) translation[:3, 3] = [0, 0, 0.5] height = np.linalg.norm(init_velocity_vector) transform_z_axis = init_velocity_vector / height transform = np.eye(4) transform[:3, 2] = transform_z_axis transform = np.dot(translation, transform) velocity_axis = trimesh.creation.cylinder(radius=radius, height=height, transform=transform) velocity_axis.visual.face_colors = color axis_origin = trimesh.creation.uv_sphere(radius=radius * 5, count=[10, 10]) axis_origin.visual.face_colors = color merge = trimesh.util.concatenate([axis_origin, velocity_axis]) args = rendering.convert_to_vertexlist(merge) drawer = self.drone_batch.add_indexed(*args) return drawer def _add_block(self, position, texture, immediate=True): """ Add a block with the given `texture` and `position` to the world. Note that block is a 1x1x1 cube and its position is its centroid. Args: position (tuple): The (x, y, z) position of the block to add. texture (list): The coordinates of the texture squares, e.g. TILE. immediate (bool): Whether or not to draw the block immediately. """ if position in self.whole_map: # Not called for current static map assert False, 'Duplicated block!' self._remove_block(position, immediate) self.whole_map[position] = texture self.sectors.setdefault(sectorize(position), []).append(position) if immediate: if self._is_exposed(position): self.show_block(position) self._check_neighbors(position) def _remove_block(self, position, immediate=True): """ Remove the block at the given `position`. Args: position (tuple): The (x, y, z) position of the block to remove. immediate (bool): Whether or not to remove the block immediately. """ del self.whole_map[position] self.sectors[sectorize(position)].remove(position) if immediate: if position in self.partial_map: self.hide_block(position) self._check_neighbors(position) def _check_neighbors(self, position): x, y, z = position for dx, dy, dz in FACES: pos = (x + dx, y + dy, z + dz) if pos not in self.whole_map: continue if self._is_exposed(pos): if pos not in self.partial_map: self.show_block(pos) else: if pos in self.partial_map: self.hide_block(pos) def _show_block(self, position, texture): vertex_data = cube_vertices(position, 0.5) # 12x6=72 texture_data = list(texture) # 8x6=48 vertex_count = len(vertex_data) // 3 # 24 attributes = [('v3f/static', vertex_data), ('t2f/static', texture_data)] self._partial_map[position] = self.batch.add(vertex_count, gl.GL_QUADS, self.group, *attributes) def _hide_block(self, position): self._partial_map.pop(position).delete() def _enqueue(self, func, *args): self.queue.append((func, args)) def _dequeue(self): func, args = self.queue.popleft() func(*args) def _get_velocity_transform(self, velocity, position): height = np.linalg.norm(velocity) transform = np.eye(4) # Translation x, z, y = position transform[:3, 3] = [x, y, z] # Rescale transform[2, 2] = height # Rotate rotation = np.eye(4) rotation[:3, 2] = velocity / height return np.dot(transform, rotation) def show_drone(self, position, rotation): """ Show the drone 3D model with corresponding translation and rotation. """ # Get the transform matrix for drone 3D model x, z, y = position transform = np.eye(4) transform[:3, 3] = [x, y, z] # NOTE: change the view size of drone 3D model transform[0, 0] = 2.5 transform[1, 1] = 2.5 transform[2, 2] = 2.5 # Match drone model space x-y-z to openGL x-z-y # TODO: read the config.json and match the propeller positions model_space_transform = rotation_transform_mat(-np.pi / 2, 'roll') transform = np.dot(transform, model_space_transform) yaw, pitch, roll = rotation if self.debug_mode: # NOTE: manually set values to debug rotation, # it's useful when input act is in form [c, c, c, c]. yaw = np.pi / 2 # pitch = np.pi / 2 # roll = np.pi / 2 transform = np.dot(transform, rotation_transform_mat(yaw, 'yaw')) transform = np.dot(transform, rotation_transform_mat(pitch, 'pitch')) transform = np.dot(transform, rotation_transform_mat(roll, 'roll')) # Add a new matrix to the model stack to transform the model gl.glPushMatrix() gl.glMultMatrixf(rendering.matrix_to_gl(transform)) # Enable the target texture if self.drone_texture is not None: gl.glEnable(self.drone_texture.target) gl.glBindTexture(self.drone_texture.target, self.drone_texture.id) # Draw the mesh with its transform applied self.drone_drawer.draw(mode=self.drone_vertex_list_mode) gl.glPopMatrix() # Disable texture after using if self.drone_texture is not None: gl.glDisable(self.drone_texture.target) def show_velocity(self, position, velocity, expected_velocity=None): """ Show velocity vector as a thin cylinder arrow. """ if not hasattr(self, 'drone_velocity_drawer'): return transform = self._get_velocity_transform(velocity, position) gl.glPushMatrix() gl.glMultMatrixf(rendering.matrix_to_gl(transform)) self.drone_velocity_drawer.draw(mode=self.drone_vertex_list_mode) gl.glPopMatrix() if expected_velocity is not None and \ hasattr(self, 'drone_expected_velocity_drawer'): transform = self._get_velocity_transform(expected_velocity, position) gl.glPushMatrix() gl.glMultMatrixf(rendering.matrix_to_gl(transform)) self.drone_expected_velocity_drawer.draw( mode=self.drone_vertex_list_mode) gl.glPopMatrix() def show_block(self, position, immediate=True): texture = self.whole_map[position] self.partial_map[position] = texture if immediate: self._show_block(position, texture) else: self._enqueue(self._show_block, position, texture) def hide_block(self, position, immediate=True): self.partial_map.pop(position) if immediate: self._hide_block(position) else: self._enqueue(self._hide_block, position) def show_sector(self, sector): for position in self.sectors.get(sector, []): if position not in self.partial_map and self._is_exposed(position): self.show_block(position, immediate=False) def hide_sector(self, sector): for position in self.sectors.get(sector, []): if position in self.partial_map: self.hide_block(position, immediate=False) def change_sectors(self, before, after): """ Find the changed sectors and trigger show or hide operations """ # TODO: adjust the sector set when add extra view perspective # relative to the drone. # FIXME: when the drone flies high, the green floor immediately # disappear before_set, after_set = set(), set() pad = self.horizon_view_size // 2 for dx in range(-pad, pad + 1): for dy in range(-pad, pad + 1): dz = 0 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 process_queue(self): # NOTE: no scheduled interval timer, we render by manually calling # `RenderWindow.view()`. So we process queue without time contrains. # In other words, it's a copy of `process_entire_queue()` while self.queue: self._dequeue() def process_entire_queue(self): while self.queue: self._dequeue()