def __init__(self, textures, shader, camera=None, light=None, w=1.0, h=1.0, name="", x=0.0, y=0.0, z=20.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """Uses standard constructor for Shape. Arguments: *textures* must be a two dimensional list of lists of textures or strings (which must be the path/names of image files) The array runs left to right then down so six textures in spreadsheet notation would be [[R1C1, R1C2], [R2C1, R2C2], [R3C1, R3C2]] *shader* shader to use Extra keyword arguments: *w* Width. *h* Height. """ try: nh = len(textures) nw = len(textures[0]) super(MultiSprite, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) self.width = w self.height = h ww = w / 2.0 / nw hh = h / 2.0 / nh self.buf = [] for i in range(nw): for j in range(nh): offw = w * ((1.0 - nw) / 2.0 + i) / nw offh = -h * ((1.0 - nh) / 2.0 + j) / nh verts = ((-ww + offw, hh + offh, 0.0), (ww + offw, hh + offh, 0.0), (ww + offw, -hh + offh, 0.0), (-ww + offw, -hh + offh, 0.0)) norms = ((0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1)) texcoords = ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0 , 1.0)) inds = ((0, 1, 3), (1, 2, 3)) thisbuf = Buffer(self, verts, texcoords, inds, norms) if not isinstance(textures[j][i], Texture): # i.e. can load from file name textures[j][i] = Texture(textures[j][i]) thisbuf.textures = [textures[j][i]] self.buf.append(thisbuf) self.set_shader(shader) self.set_2d_size() # method in Shape, default full window size except IndexError: LOGGER.error('Must supply a list of lists of Textures or strings')
def __init__(self, camera=None, light=None, name="", z=0.1): """Uses standard constructor for Shape but only one position variable is available as Keyword argument: *z* The depth that the shape will be constructed as an actual z offset distance in the vertices. As the Canvas is intended for use with 2d shaders there is no way to change its location as no matrix multiplication will happen in the vertex shader. """ super(Canvas, self).__init__(camera, light, name, x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0) self.ttype = GL_TRIANGLES self.verts = [] self.norms = [] self.texcoords = [] self.inds = [] self.depth = z ww = 20.0 hh = 20.0 self.verts = ((-ww, -hh, z), (0.0, hh, z), (ww, -hh, z)) self.norms = ((0, 0, -1), (0, 0, -1), (0, 0, -1)) self.texcoords = ((0.0, 0.0), (0.5, 1.0), (1.0, 0.0)) self.inds = ((0, 1, 2), ) #python quirk: comma for tuple with only one val self.buf = [] self.buf.append(Buffer(self, self.verts, self.texcoords, self.inds, self.norms))
def __init__(self, camera=None, light=None, w=1.0, h=1.0, name="", x=0.0, y=0.0, z=20.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0, flip=False): """Uses standard constructor for Shape. Extra Keyword arguments: *w* Width. *h* Height. *flip* If set to True then the Sprite is flipped vertically (top to bottom) """ super(Sprite, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) self.width = w self.height = h self.ttype = GL_TRIANGLES self.verts = [] self.norms = [] self.texcoords = [] self.inds = [] ww = w / 2.0 hh = h / 2.0 if not flip else -h / 2.0 self.verts = ((-ww, hh, 0.0), (ww, hh, 0.0), (ww, -hh, 0.0), (-ww, -hh, 0.0)) self.norms = ((0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1)) self.texcoords = ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0 , 1.0)) self.inds = ((0, 1, 3), (1, 2, 3)) if not flip else ((3, 2, 0), (2, 1, 0)) self.buf = [] self.buf.append(Buffer(self, self.verts, self.texcoords, self.inds, self.norms))
def __init__(self, camera=None, light=None, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape""" super(MergeShape, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) LOGGER.info("Creating Merge Shape ...") self.buf = [ Buffer(self, [], [], [], []) ] # create single empty buffer in case draw() before first merge() self.childModel = None #unused but asked for by pickle
def __init__(self, camera=None, light=None, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape""" super(MergeShape, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) LOGGER.info("Creating Merge Shape ...") self.buf = [ Buffer(self, [], [], [], []) ] # create single empty buffer in case draw() before first merge() self.billboard_array = [ np.zeros((0, 6), dtype='float32') ] # list of arrays holding x0, z0, x, z, nx, nz for each vert self.childModel = None #unused but asked for by pickle
def __init__(self, camera=None, light=None, name="", corners=((-1.0, -0.57735, -0.57735), (1.0, -0.57735, -0.57735), (0.0, -0.57735, 1.15470), (0.0, 1.15470, 0.0)), x=0.0, y=0.0, z=0.0, sx=1.0, sy=1.0, sz=1.0, rx=0.0, ry=0.0, rz=0.0, cx=0.0, cy=0.0, cz=0.0): """Uses standard constructor for Shape with ability to position corners. The uv mapping is taken from four equilateral triangles arranged on a square forming an upwards pointing arrow ^. Starting at the left bottom corner of the image the first three triangles are unwrapped from around the top of the tetrahedron and the right bottom triangle is the base (if the corners are arranged as per the default) Keyword argument: *corners* A tuple of four (xyz) tuples defining the corners """ super(Tetrahedron, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) c = corners # alias verts = (c[0], c[3], c[1], c[2], c[3], c[0], c[1], c[3], c[2], c[0], c[1], c[2]) texcoords = ((0.0, 0.0), (0.0, 0.57735), (0.5, 0.288675), (0.0, 0.57735), (0.5, 0.866025), (0.5, 0.288675), (0.5, 0.866025), (1.0, 0.57735), (0.5, 0.288675), (0.5, 0.288675), (1.0, 0.57735), (1.0, 0.0)) inds = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11)) self.buf = [Buffer(self, verts, texcoords, inds, normals=None, smooth=False)]
def __init__(self, camera=None, light=None, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape""" super(MergeShape, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print "Creating Merge Shape ..." self.vertices = [] self.normals = [] self.tex_coords = [] self.indices = [] #stores all indices for single render self.buf = [] self.buf.append( Buffer(self, self.vertices, self.tex_coords, self.indices, self.normals))
def __init__(self, camera=None, light=None, w=1.0, h=1.0, name="", x=0.0, y=0.0, z=20.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0, flip=True): """Uses standard constructor for Shape. Extra Keyword arguments: *w* Width. *h* Height. *flip* Should the image be flipped over """ #super(FlipSprite, self).super(Sprite, self). sprt = super(FlipSprite, self) super(Sprite, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) self.width = w self.height = h self.ttype = GL_TRIANGLES self.verts = [] self.norms = [] self.texcoords = [] self.inds = [] ww = w / 2.0 ###... hh = h / 2.0 if not flip else -h / 2.0 # hh = h / 2.0 self.verts = ((-ww, hh, 0.0), (ww, hh, 0.0), (ww, -hh, 0.0), (-ww, -hh, 0.0)) self.norms = ((0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1)) self.texcoords = ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)) ###... self.inds = ((0, 1, 3), (1, 2, 3)) if not flip else ((3, 2, 0), (2, 1, 0)) # self.inds = ((0, 1, 3), (1, 2, 3)) self.buf = [] self.buf.append( Buffer(self, self.verts, self.texcoords, self.inds, self.norms))
def __init__(self, camera=None, light=None, radius=1, sides=12, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape extra Keyword arguments: *radius* Radius of disk. *sides* Number of sides to polygon representing disk. """ super(Disk, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print("Creating disk ...") self.verts = [] self.norms = [] self.inds = [] self.texcoords = [] self.ttype = GL_TRIANGLES self.sides = sides st = 2 * pi / sides for j in range(-1, 1): self.verts.append((0.0, -0.1 * j, 0.0)) self.norms.append((0.0, -j, 0.0)) self.texcoords.append((0.5, 0.5)) for r in range(sides + 1): ca, sa = Utility.from_polar_rad(r * st) self.verts.append((radius * sa, 0.0, radius * ca)) self.norms.append((0.0, -j - 0.1 * j, 0.0)) self.texcoords.append((sa * 0.5 + 0.5, ca * 0.5 + 0.5)) if j == -1: v0, v1, v2 = 0, 1, 2 else: v0, v1, v2 = sides + 2, sides + 4, sides + 3 # i.e. reverse direction to show on back for r in range(sides): self.inds.append((v0, r + v1, r + v2)) self.but = [] self.buf.append( Buffer(self, self.verts, self.texcoords, self.inds, self.norms))
def __init__(self, camera=None, light=None, w=1.0, h=1.0, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape extra Keyword arguments: *w* width *h* height """ super(Plane, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print("Creating plane ...") self.width = w self.height = h self.ttype = GL_TRIANGLES self.verts = [] self.norms = [] self.texcoords = [] self.inds = [] ww = w / 2.0 hh = h / 2.0 self.verts = ((-ww, hh, 0.0), (ww, hh, 0.0), (ww, -hh, 0.0), (-ww, -hh, 0.0), (-ww, hh, 0.0), (ww, hh, 0.0), (ww, -hh, 0.0), (-ww, -hh, 0.0)) self.norms = ((0.0, 0.0, -1), (0.0, 0.0, -1), (0.0, 0.0, -1), (0.0, 0.0, -1), (0.0, 0.0, 1), (0.0, 0.0, 1), (0.0, 0.0, 1), (0.0, 0.0, 1)) self.texcoords = ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0), (0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)) self.inds = ((0, 1, 3), (1, 2, 3), (5, 4, 7), (6, 5, 7)) self.buf = [] self.buf.append( Buffer(self, self.verts, self.texcoords, self.inds, self.norms))
def __init__(self, camera=None, light=None, inner=0.5, outer=1, sides=12, name="", start=0.0, end=60, x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """Slice with outer and inner radius. NB this is in the xy plane rather than the xz plane that Disk produces and is only one sided. It uses standard constructor for Shape extra with the following Keyword arguments: *inner* Inner radius of slice. *outer* Outer radius. *sides* Number of sides on outside and inside of polygon. *start* Angle in degrees clockwise from vertical up, to start slice. *end* Angle to end slice. """ super(Slice, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) LOGGER.debug("Creating slice ...") self.inner = inner self.outer = outer self.start = start self.end = end self.n = sides + 1 (verts, texcoords) = self.make_verts() norms = np.zeros_like(verts) norms[:, 2] = -1.0 inds = np.array([[2 * i + u, 2 * i + v, 2 * i + w] for i in range(0, sides) for (u, v, w) in [(0, 1, 3), (3, 2, 0)]], dtype=int) verts[:, 2] = z self.buf = [Buffer(self, verts, texcoords, inds, norms)]
def __init__(self, camera=None, light=None, vertices=[], material=(1.0, 1.0, 1.0), line_width=1, closed=False, name="", x=0.0, y=0.0, z=0.0, sx=1.0, sy=1.0, sz=1.0, rx=0.0, ry=0.0, rz=0.0, cx=0.0, cy=0.0, cz=0.0, strip=True): """uses standard constructor for Shape extra Keyword arguments: *vertices* array of tuples [(x0,y0,z0),(x1,y1,z1)..] *material* tuple (r,g,b) *line_width* set to 1 if absent or set to a value less than 1 *closed* joins up last leg i.e. for polygons *strip* use GL_LINE_STRIP otherwise GL_LINES - needs pairs for line ends """ super(Lines, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) LOGGER.info("Creating Lines ...") n_v = len(vertices) indices = [[a, a + 1, a + 2] for a in range(0, n_v, 3)] for i in range(1, 3): last = indices[-1] if last[i] >= n_v: last[i] = n_v - 1 self.buf = [Buffer(self, vertices, [], indices, [], smooth=False)] if line_width < 1: self.set_line_width(1, closed) else: self.set_line_width(line_width=line_width, closed=closed, strip=strip) self.set_material(material)
def __init__(self, camera=None, light=None, vertices=[], material=(1.0, 1.0, 1.0), point_size=1, name="", x=0.0, y=0.0, z=0.0, sx=1.0, sy=1.0, sz=1.0, rx=0.0, ry=0.0, rz=0.0, cx=0.0, cy=0.0, cz=0.0, normals=[], tex_coords=[]): """uses standard constructor for Shape extra Keyword arguments: *vertices* array of tuples [(x0,y0,z0),(x1,y1,z1)..] *material* tuple (r,g,b) *point_size* set to 1 if absent or set to a value less than 1 """ super(Points, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) LOGGER.info("Creating Points ...") n_v = len(vertices) indices = [[a, a + 1, a + 2] for a in range(0, n_v, 3)] for i in range(1, 3): last = indices[-1] if last[i] >= n_v: last[i] = n_v - 1 if normals == [] and tex_coords == []: normals = [[0.0, 0.0, 0.0] for i in range(n_v)] #used for rgb in mat_pointsprite tex_coords = [[1.0, 0.0] for i in range(n_v) ] # [:,0] used for alpha in mat_pointsprite self.buf = [ Buffer(self, vertices, tex_coords, indices, normals, smooth=False) ] if point_size < 1: self.set_point_size(1) else: self.set_point_size(point_size) self.set_material(material)
def merge(self, bufr, x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0): """merge the vertices, normals etc from this Buffer with those already there the position, rotation, scale, offset are set according to the origin of the MergeShape. If bufr is not a Buffer then it will be treated as if it is a Shape and its first Buffer object will be merged. Argument additional to standard Shape: *bufr* Buffer object or Shape with a member buf[0] that is a Buffer object. OR an array or tuple where each element is an array or tuple with the required arguments i.e. [[bufr1, x1, y1, z1, rx1, ry1....], [bufr2, x2, y2...],[bufr3, x3, y3...]] this latter is a more efficient way of building a MergeShape from lots of elements. If multiple Buffers are passed in this way then the subsequent arguments (x,y,z etc) will be ignored. """ if not isinstance(bufr, list) and not isinstance(bufr, tuple): buflist = [[bufr, x, y, z, rx, ry, rz, sx, sy, sz]] else: buflist = bufr for b in buflist: if not(type(b[0]) is Buffer): bufr = b[0].buf[0] else: bufr = b[0] #assert shape.ttype == GL_TRIANGLES # this is always true of Buffer objects assert len(bufr.vertices) == len(bufr.normals) if VERBOSE: print("Merging", bufr.name) original_vertex_count = len(self.vertices) for v in range(0, len(bufr.vertices)): # Scale, offset and store vertices vx, vy, vz = rotate_vec(b[4], b[5], b[6], bufr.vertices[v]) self.vertices.append((vx * b[7] + b[1], vy * b[8] + b[2], vz * b[9] + b[3])) # Rotate normals self.normals.append(rotate_vec(b[4], b[5], b[6], bufr.normals[v])) self.tex_coords.extend(bufr.tex_coords) ctypes.restype = ctypes.c_short # TODO: remove this side-effect. indices = [(i[0] + original_vertex_count, i[1] + original_vertex_count, i[2] + original_vertex_count) for i in bufr.indices] self.indices.extend(indices) self.buf = [] self.buf.append(Buffer(self, self.vertices, self.tex_coords, self.indices, self.normals))
def __init__(self, camera=None, light=None, vertices=[], material=(1.0, 1.0, 1.0), point_size=1, name="", x=0.0, y=0.0, z=0.0, sx=1.0, sy=1.0, sz=1.0, rx=0.0, ry=0.0, rz=0.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape extra Keyword arguments: *vertices* array of tuples [(x0,y0,z0),(x1,y1,z1)..] *material* tuple (r,g,b) *point_size* set to 1 if absent or set to a value less than 1 """ super(Points, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print("Creating Points ...") self.vertices = vertices self.normals = [] n_v = len(vertices) self.indices = [(a, a + 1, a + 2) for a in range(0, n_v, 3)] self.tex_coords = [] self.buf = [ Buffer(self, self.vertices, self.tex_coords, self.indices, self.normals, smooth=False) ] if point_size < 1: self.set_point_size(1) else: self.set_point_size(point_size) self.set_material(material)
def merge(self, bufr, x, y, z, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """merge the vertices, normals etc from this Buffer with those already there the position, rotation, scale, offset are set according to the origin of the MergeShape. If bufr is not a Buffer then it will be treated as if it is a Shape and its first Buffer object will be merged. Argument additional to standard Shape: *bufr* Buffer object or Shape with a member buf[0] that is a Buffer object. """ if not(type(bufr) is Buffer): bufr = bufr.buf[0] #assert shape.ttype == GL_TRIANGLES # this is always true of Buffer objects assert len(bufr.vertices) == len(bufr.normals) if VERBOSE: print "Merging", bufr.name vertices = [] normals = [] original_vertex_count = len(self.vertices) for v in range(0, len(bufr.vertices)): def rotate_slice(array): vec = array[v] if rz: vec = rotate_vec_z(rz, vec) if rx: vec = rotate_vec_x(rx, vec) if ry: vec = rotate_vec_y(ry, vec) return vec # Scale, offset and store vertices vx, vy, vz = rotate_slice(bufr.vertices) self.vertices.append((vx * sx + x, vy * sy + y, vz * sz + z)) # Rotate normals self.normals.append(rotate_slice(bufr.normals)) self.tex_coords.extend(bufr.tex_coords) ctypes.restype = ctypes.c_short # TODO: remove this side-effect. indices = [(i[0] + original_vertex_count, i[1] + original_vertex_count, i[2] + original_vertex_count) for i in bufr.indices] self.indices.extend(indices) self.buf = [] self.buf.append(Buffer(self, self.vertices, self.tex_coords, self.indices, self.normals))
def __init__(self, camera=None, light=None, radius=1, sides=12, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape extra Keyword arguments: *radius* Radius of disk. *sides* Number of sides to polygon representing disk. """ super(RoundCorner, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) verts = [] norms = [] inds = [] texcoords = [] self.sides = sides st = (pi / 2) / sides for j in range(-1, 1): verts.append((0.0, -0.1 * j, 0.0)) norms.append((0.0, -j, 0.0)) texcoords.append((0.5, 0.5)) for r in range(0, sides + 1): ca, sa = Utility.from_polar_rad(r * st) verts.append((radius * sa, 0.0, radius * ca)) norms.append((0.0, -j - 0.1 * j, 0.0)) texcoords.append((sa * 0.5 + 0.5, ca * 0.5 + 0.5)) if j == -1: v0, v1, v2 = 0, 1, 2 else: v0, v1, v2 = sides + 2, sides + 4, sides + 3 # i.e. reverse direction to show on back for r in range(sides): inds.append((v0, r + v1, r + v2)) self.buf = [Buffer(self, verts, texcoords, inds, norms)]
def __init__(self, camera=None, light=None, font=None, string=None, x=0.0, y=0.0, z=1.0, sx=DEFAULT_FONT_SCALE, sy=DEFAULT_FONT_SCALE, is_3d=True, size=DEFAULT_FONT_DOT_SIZE, rx=0.0, ry=0.0, rz=0.0): """Standard Shape constructor without the facility to change z scale or any of the offset values. Additional keyword arguments: *font* Font or Ttffont class object. *string* of ASCI characters in range(32, 128) """ if not is_3d: sy = sx = size / DOTS_PER_INCH super(String, self).__init__(camera, light, "", x, y, z, rx, ry, rz, sx, sy, 1.0, 0.0, 0.0, 0.0) if VERBOSE: print "Creating string ..." self.verts = [] self.texcoords = [] self.norms = [] self.inds = [] temp_verts = [] xoff = 0.0 maxh = 0.0 #TODO cope with \n characters to give multi line strings for i, c in enumerate(string): v = ord(c) - 32 w, h, texc, verts = font.ch[v] if v >= 0: for j in verts: temp_verts.append((j[0]+xoff, j[1], j[2])) xoff += w if h > maxh: maxh = h for j in texc: self.texcoords.append(j) for j in [(0.0,0.0,1.0), (0.0,0.0,1.0), (0.0,0.0,1.0), (0.0,0.0,1.0)]: self.norms.append(j) stv = 4 * i for j in [(stv, stv + 2, stv + 1), (stv, stv + 3, stv + 2)]: self.inds.append(j) for j in temp_verts: self.verts.append((j[0] - xoff/2.0, j[1] + maxh/2.0, j[2])) self.buf = [] self.buf.append(Buffer(self, self.verts, self.texcoords, self.inds, self.norms)) self.buf[0].textures = [font] self.buf[0].unib[1] = -1.0
def __init__(self, camera=None, light=None, w=1.0, h=1.0, name="", x=0.0, y=0.0, z=20.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """Uses standard constructor for Shape. Extra Keyword arguments: *w* Width. *h* Height. """ super(Sprite, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) self.width = w self.height = h self.ttype = GL_TRIANGLES self.verts = [] self.norms = [] self.texcoords = [] self.inds = [] ww = w / 2.0 hh = h / 2.0 self.verts = ((-ww, hh, 0.0), (ww, hh, 0.0), (ww, -hh, 0.0), (-ww, -hh, 0.0)) self.norms = ((0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1)) self.texcoords = ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)) self.inds = ((0, 1, 3), (1, 2, 3)) self.buf = [] self.buf.append( Buffer(self, self.verts, self.texcoords, self.inds, self.norms))
def __init__(self, camera=None, light=None, w=1.0, h=1.0, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape extra Keyword arguments: *w* width *h* height """ super(Plane, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) LOGGER.info("Creating plane ...") self.width = w self.height = h ww = w / 2.0 hh = h / 2.0 verts = ((-ww, hh, 0.0), (ww, hh, 0.0), (ww, -hh, 0.0), (-ww, -hh, 0.0), (-ww, hh, 0.0), (ww, hh, 0.0), (ww, -hh, 0.0), (-ww, -hh, 0.0)) norms = ((0.0, 0.0, -1), (0.0, 0.0, -1), (0.0, 0.0, -1), (0.0, 0.0, -1), (0.0, 0.0, 1), (0.0, 0.0, 1), (0.0, 0.0, 1), (0.0, 0.0, 1)) texcoords = ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0), (0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)) inds = ((3, 0, 1), (1, 2, 3), (7, 6, 5), (5, 4, 7)) self.buf = [Buffer(self, verts, texcoords, inds, norms)]
def __init__(self, camera=None, light=None, name="", corners=((-0.5, -0.28868), (0.0, 0.57735), (0.5, -0.28868)), x=0.0, y=0.0, z=0.0, sx=1.0, sy=1.0, sz=1.0, rx=0.0, ry=0.0, rz=0.0, cx=0.0, cy=0.0, cz=0.0): """Uses standard constructor for Shape with ability to position corners. The corners must be arranged clockwise (for the Triangle to face -z direction) The uv mapping is taken from an equilateral triangles base 0,0 to 1,0 peak at 0.5, 0.86603 Keyword argument: *corners* A tuple of three (xy) tuples defining the corners """ super(Triangle, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) self.ttype = GL_TRIANGLES self.verts = [] self.norms = [] self.texcoords = [] self.inds = [] c = corners # alias for convenience self.verts = ((c[0][0], c[0][1], 0.0), (c[1][0], c[1][1], 0.0), (c[2][0], c[2][1], 0.0)) self.norms = ((0, 0, -1), (0, 0, -1), (0, 0, -1)) self.texcoords = ((0.0, 0.0), (0.5, 0.86603), (1.0, 0.0)) self.inds = ((0, 1, 2), ) #python quirk: comma for tuple with only one val self.buf = [] self.buf.append( Buffer(self, self.verts, self.texcoords, self.inds, self.norms))
def __init__(self, camera=None, light=None, name="", corners=((-0.5, -0.28868), (0.0, 0.57735), (0.5, -0.28868)), x=0.0, y=0.0, z=0.0, sx=1.0, sy=1.0, sz=1.0, rx=0.0, ry=0.0, rz=0.0, cx=0.0, cy=0.0, cz=0.0): """Uses standard constructor for Shape with ability to position corners. The corners must be arranged clockwise (for the Triangle to face -z direction) Keyword argument: *corners* A tuple of three (xy) tuples defining the corners """ super(Triangle, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) c = corners # alias for convenience verts = ((c[0][0], c[0][1], 0.0), (c[1][0], c[1][1], 0.0), (c[2][0], c[2][1], 0.0)) norms = ((0, 0, -1), (0, 0, -1), (0, 0, -1)) maxx = max(v[0] for v in corners) # u,v to match vertex locations minx = min(v[0] for v in corners) xscale = 1.0 / (maxx - minx) maxy = max(v[1] for v in corners) miny = min(v[1] for v in corners) yscale = 1.0 / (maxy - miny) texcoords = [((v[0] - minx) * xscale, 1.0 - (v[1] - miny) * yscale) for v in verts] inds = ((0, 1, 2), ) #python quirk: comma for tuple with only one val self.buf = [Buffer(self, verts, texcoords, inds, norms)]
def __init__(self, camera=None, light=None, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0): """uses standard constructor for Shape""" super(MergeShape, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) LOGGER.info("Creating Merge Shape ...") self.vertices = [] self.normals = [] self.tex_coords = [] self.indices = [] #stores all indices for single render self.buf = [] self.buf.append(Buffer(self, self.vertices, self.tex_coords, self.indices, self.normals)) self.childModel = None #unused but asked for by pickle
def create_mesh_buffer(self): ww = self.width / 2.0 hh = self.height / 2.0 nx, ny = self.mesh_size verts = [] texcoords = [] inds = [] for x in range(nx + 1): for y in range(ny + 1): coords = (-ww + self.width / nx * x, hh - self.height / ny * y, 0.0) verts.append(coords) texcoords.append(((coords[0] + ww) / self.width, -(coords[1] - hh) / self.height)) for y in range(ny): for x in range(nx): a = y + (ny + 1) * x b = a + 1 c = y + (ny + 1) * (x + 1) inds.append((a, b, c)) a = y + (ny + 1) * (x + 1) # b = b c = a + 1 inds.append((a, b, c)) # for wireframe mode topright = nx * (ny + 1) bottomright = (nx + 1) * (ny + 1) - 1 inds.append((bottomright, topright, topright)) new_buffer = Buffer(self, verts, texcoords, inds, None) if self.buf: old_buffer = self.buf[0] new_buffer.set_material(old_buffer.unib[3:6]) new_buffer.set_textures(old_buffer.textures) new_buffer.shader = old_buffer.shader self.buf = [new_buffer] if self.mesh_debug: self.set_mesh_debug(self.mesh_debug)
def _lathe(self, path, sides=12, rise=0.0, loops=1.0): """Returns a Buffer object by rotating the points defined in path. Arguments: *path* An array of points [(x0, y0), (x1, y1) ...] to rotate around the y axis. Keyword arguments: *sides* Number of sides to divide each rotation into. *rise* Amount to increment the path y values for each rotation (ie helix) *loops* Number of times to rotate the path by 360 (ie helix). """ self.sides = sides s = len(path) rl = int(self.sides * loops) pn = 0 pp = 0 tcx = 1.0 / self.sides pr = (pi / self.sides) * 2.0 rdiv = rise / rl # Find largest and smallest y of the path used for stretching the texture miny = path[0][1] maxy = path[s - 1][1] for p in range(s): if path[p][1] < miny: miny = path[p][1] if path[p][1] > maxy: maxy = path[p][1] verts = [] norms = [] idx = [] tex_coords = [] opx = path[0][0] opy = path[0][1] for p in range(s): px = path[p][0] * 1.0 py = path[p][1] * 1.0 tcy = 1.0 - ((py - miny) / (maxy - miny)) # Normalized 2D vector between path points dx, dy = Utility.vec_normal(Utility.vec_sub((px, py), (opx, opy))) for r in range(0, rl): sinr = sin(pr * r) cosr = cos(pr * r) verts.append((px * sinr, py, px * cosr)) norms.append((-sinr * dy, dx, -cosr * dy)) tex_coords.append((1.0 - tcx * r, tcy)) py += rdiv # Last path profile (tidies texture coords). verts.append((0, py, px)) norms.append((0, dx, -dy)) tex_coords.append((0, tcy)) if p < s - 1: pn += (rl + 1) for r in range(rl): idx.append((pp + r + 1, pp + r, pn + r)) idx.append((pn + r, pn + r + 1, pp + r + 1)) pp += (rl + 1) opx = px opy = py return Buffer(self, verts, tex_coords, idx, norms)
def __init__(self, mapfile, camera=None, light=None, width=100.0, depth=100.0, height=10.0, divx=0, divy=0, ntiles=1.0, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0, smooth=True, cubic=False): """uses standard constructor for Shape Arguments: *mapfile* Greyscale image path/file, string. If multiple bytes per pixel only the first one will be used for elevation. jpg files will create slight errors that will cause mis-matching of edges for tiled maps (i.e. use png for these) NB also see divx, divy below i.e. div=64x64 requires image 65x65 pixels Keyword arguments: *width, depth, height* Of the map in world units. *divx, divy* Number of divisions into which the map will be divided. to create vertices (vertices += 1) NB if you want to create a map with 64x64 divisions there will be 65x65 vertices in each direction so the mapfile (above) needs to 65x65 pixels in order to specify elevations precisely and avoid resizing errors. *ntiles* Number of repeats for tiling the texture image. *smooth* Calculate normals with averaging rather than pointing straight up, slightly faster if false. """ super(ElevationMap, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) divx += 1 # one more vertex in each direction than number of divisions divy += 1 if divx > 200 or divy > 200: print("... Map size can't be bigger than 199x199 divisions") divx = 200 divy = 200 #print(type(mapfile), type("")) try: if '' + mapfile == mapfile: #HORRIBLE. Only way to cope with python2v3 if mapfile[0] != '/': for p in sys.path: if os.path.isfile( p + '/' + mapfile ): # this could theoretically get different files with same name mapfile = p + '/' + mapfile break if VERBOSE: print("Loading height map ...", mapfile) im = Image.open(mapfile) else: im = mapfile #allow image files to be passed as mapfile except: im = mapfile ix, iy = im.size if (ix > 200 and divx == 0) or (ix != divx and iy != divy): if divx == 0: divx = 200 divy = 200 im = im.resize((divx, divy), Image.ANTIALIAS) ix, iy = im.size im = im.transpose(Image.FLIP_TOP_BOTTOM) im = im.transpose(Image.FLIP_LEFT_RIGHT) self.pixels = im.load() self.width = width self.depth = depth self.height = height self.ix = ix self.iy = iy self.ttype = GL_TRIANGLE_STRIP if VERBOSE: print("Creating Elevation Map ...", ix, iy) wh = width * 0.5 hh = depth * 0.5 ws = width / (ix - 1.0) hs = depth / (iy - 1.0) ht = height / 255.0 tx = 1.0 * ntiles / ix ty = 1.0 * ntiles / iy verts = [] norms = [] tex_coords = [] idx = [] for y in xrange(0, iy): for x in xrange(0, ix): pxl = self.pixels[x, y] hgt = pxl[0] if type(pxl) is tuple else pxl hgt *= ht this_x = -wh + x * ws this_z = -hh + y * hs if cubic: """ this is a bit experimental. It tries to make the map either zero or height high. Vertices are moved 'under' adjacent ones if there is a step to make vertical walls. Goes wrong in places - mainly because it doesn't check diagonals """ if hgt > height / 2: hgt = height else: hgt = 0.0 if hgt == 0 and y > 0 and y < iy - 1 and x > 0 and x < ix - 1: if self.pixels[x - 1, y] > 127: this_x = -wh + (x - 1) * ws elif self.pixels[x + 1, y] > 127: this_x = -wh + (x + 1) * ws elif self.pixels[x, y - 1] > 127: this_z = -hh + (y - 1) * hs elif self.pixels[x, y + 1] > 127: this_z = -hh + (y + 1) * hs elif self.pixels[x - 1, y - 1] > 127: this_x = -wh + (x - 1) * ws this_z = -hh + (y - 1) * hs elif self.pixels[x - 1, y + 1] > 127: this_x = -wh + (x - 1) * ws this_z = -hh + (y + 1) * hs elif self.pixels[x + 1, y - 1] > 127: this_x = -wh + (x + 1) * ws this_z = -hh + (y - 1) * hs elif self.pixels[x + 1, y + 1] > 127: this_x = -wh + (x + 1) * ws this_z = -hh + (y + 1) * hs verts.append((this_x, hgt, this_z)) tex_coords.append(((ix - x) * tx, (iy - y) * ty)) s = 0 #create one long triangle_strip by alternating X directions for y in range(0, iy - 1): for x in range(0, ix - 1): i = (y * ix) + x idx.append((i, i + ix, i + ix + 1)) idx.append((i + ix + 1, i + 1, i)) s += 2 self.buf = [] self.buf.append(Buffer(self, verts, tex_coords, idx, None, smooth))
def groupDrill(gp, np): structVList = {} offsetVList = {} structPList = [] offset = 0 #numv = 0 #numi = 0 for x in gp: if len(x) == 0: continue if ("<Group>" in x[0]): if len(x[1]) > 0: nextnp = np+x[1] else: nextnp = np+str(randint(10000, 99999)) groupDrill(x[3], nextnp) else: #build vertex, polygon, normal, triangles, UVs etc etc if "<VertexPool>" in x[0]: vp = x[1] structVList[vp] = [] offsetVList[vp] = offset for v in x[3]: #if "<Vertex>" in v[0]: #try with this test first! coords = [float(n) for n in v[2].strip().split()] # before first < error if no coords! # texture mapping UVcoords = [] normal = [] for u in v[3]: if "<UV>" in u[0]: UVcoords = [float(n) for n in u[2].strip().split()] #TODO get UVtangent and UVbinormal out of UVset (and use them!) # if ("<Tangent>" in vList[v]): UVtangent = [float(n) for n in (extBracket("<Tangent>", vList[v]))[0].split()] # not sure how to use this at this stage #else: UVtangent = [] # if ("<Binormal>" in vList[v]): UVbinormal = [float(n) for n in (extBracket("<Binormal>", vList[v]))[0].split()] # not sure how to use this at this stage #else: UVbinormal = [] # normals, used in 'smoothing' edges between polygons if "<Normal>" in u[0]: normal = [float(n) for n in u[2].strip().split()] vInt = int(v[1]) while (len(structVList[vp]) < (vInt+1)): structVList[vp].append("") structVList[vp][vInt] = (vertex(coords, UVcoords, normal)) offset += 1 # # now go through splitting out the Polygons from this Group same level as vertexGroup if "<Polygon>" in x[0]: normal = [] rgba = [] MRef = "" TRef = "" for p in x[3]: if ("<Normal>" in p[0]): normal = [float(n) for n in p[2].strip().split()] if ("<RGBA>" in p[0]): rgba = [float(n) for n in p[2].strip().split()] if ("<MRef>" in p[0]): MRef = p[2].strip() if ("<TRef>" in p[0]): TRef = p[2].strip() if ("<VertexRef>" in p[0]): vref = [] for n in p[2].strip().split(): vref.append(int(n)) #numv += 1 #numi += 3 #numi -= 6 # number of corners of triangle = (n-2)*3 where n is the number of corners of face vpKey = p[3][0][2].strip() # ought to do a for r in p[3]; if "Ref in... # add to list #while (len(structPList) < (p+1)): structPList.append("") # structPList.append(polygon(normal, rgba, MRef, TRef, vref, vpKey)) # now go through the polygons in order of vertexPool+id, trying to ensure that the polygon arrays in each group are built in the order of vertexPool names # only cope with one material and one texture per group #numv -= 1 #numi -= 1 g_vertices = [] g_normals = [] g_tex_coords = [] g_indices = [] nv = 0 # vertex counter in this material #ni = 0 # triangle vertex count in this material gMRef = "" gTRef = "" nP = len(structPList) for p in xrange(nP): if (len(structPList[p].MRef) > 0): gMRef = structPList[p].MRef else: gMRef = "" if (len(structPList[p].TRef) > 0): gTRef = structPList[p].TRef else: gTRef = "" vpKey = structPList[p].vpKey vref = structPList[p].vref startV = nv for j in vref: if (len(structVList[vpKey][j].normal) > 0): model.vNormal = True else: model.vNormal = False if model.coordinateSystem == "z-up": thisV = [structVList[vpKey][j].coords[1], structVList[vpKey][j].coords[2], -structVList[vpKey][j].coords[0]] if model.vNormal: thisN = [structVList[vpKey][j].normal[1], structVList[vpKey][j].normal[2], -structVList[vpKey][j].normal[0]] else: thisV = [structVList[vpKey][j].coords[0], structVList[vpKey][j].coords[1], -structVList[vpKey][j].coords[2]] if model.vNormal: thisN = [structVList[vpKey][j].normal[0], structVList[vpKey][j].normal[1], -structVList[vpKey][j].normal[2]] g_vertices.append(thisV) if model.vNormal: nml = thisN else: nml = structPList[p].normal g_normals.append(nml) uvc = structVList[vpKey][j].UVcoords if (len(uvc) == 2): g_tex_coords.append(uvc) else: g_tex_coords.append([0.0, 0.0]) nv += 1 n = nv - startV - 1 for j in range(1, n): g_indices.append((startV, startV + j + 1, startV + j)) ilen = len(g_vertices) if ilen > 0: model.buf.append(Buffer(model, g_vertices, g_tex_coords, g_indices, g_normals)) n = len(model.buf) - 1 model.vGroup[np] = n model.buf[n].indicesLen = ilen model.buf[n].material = (0.0, 0.0, 0.0, 0.0) model.buf[n].ttype = GL_TRIANGLES # load the texture file TODO check if same as previously loaded files (for other loadModel()s) if (gTRef in model.textureList): model.buf[model.vGroup[np]].textures = [model.textureList[gTRef]["texID"]] model.buf[model.vGroup[np]].texFile = model.textureList[gTRef]["filename"] else: model.buf[model.vGroup[np]].textures = [] model.buf[model.vGroup[np]].texFile = None #TODO don't create this array if texture being used but should be able to combine if (gMRef in model.materialList): redVal = float(model.materialList[gMRef]["diffr"]) grnVal = float(model.materialList[gMRef]["diffg"]) bluVal = float(model.materialList[gMRef]["diffb"]) model.buf[model.vGroup[np]].material = (redVal, grnVal, bluVal, 1.0) model.buf[model.vGroup[np]].unib[3:6] = [redVal, grnVal, bluVal] else: model.buf[model.vGroup[np]].material = (0.0, 0.0, 0.0, 0.0)
def __init__(self, camera=None, light=None, w=1.0, h=1.0, d=1.0, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, cx=0.0, cy=0.0, cz=0.0, tw=1.0, th=1.0, td=1.0): """uses standard constructor for Shape extra Keyword arguments: *w* width *h* height *d* depth *tw* scale width *th* scale height *td* scale depth The scale factors are the multiple of the texture to show along that dimension. For no distortion of the image the scale factors need to be proportional to the relative dimension. """ super(Cuboid, self).__init__(camera, light, name, x, y, z, rx, ry, rz, 1.0, 1.0, 1.0, cx, cy, cz) if VERBOSE: print("Creating cuboid ...") self.width = w self.height = h self.depth = d self.ssize = 36 self.ttype = GL_TRIANGLES ww = w / 2.0 hh = h / 2.0 dd = d / 2.0 #cuboid data - faces are separated out for texturing.. self.vertices = ((-ww, hh, dd), (ww, hh, dd), (ww, -hh, dd), (-ww, -hh, dd), (ww, hh, dd), (ww, hh, -dd), (ww, -hh, -dd), (ww, -hh, dd), (-ww, hh, dd), (-ww, hh, -dd), (ww, hh, -dd), (ww, hh, dd), (ww, -hh, dd), (ww, -hh, -dd), (-ww, -hh, -dd), (-ww, -hh, dd), (-ww, -hh, dd), (-ww, -hh, -dd), (-ww, hh, -dd), (-ww, hh, dd), (-ww, hh, -dd), (ww, hh, -dd), (ww, -hh, -dd), (-ww, -hh, -dd)) self.normals = ((0.0, 0.0, 1), (0.0, 0.0, 1), (0.0, 0.0, 1), (0.0, 0.0, 1), (1, 0.0, 0), (1, 0.0, 0), (1, 0.0, 0), (1, 0.0, 0), (0.0, 1, 0), (0.0, 1, 0), (0.0, 1, 0), (0.0, 1, 0), (0.0, -1, 0), (0, -1, 0), (0.0, -1, 0), (0.0, -1, 0), (-1, 0.0, 0), (-1, 0.0, 0), (-1, 0.0, 0), (-1, 0.0, 0), (0.0, 0.0, -1), (0.0, 0.0, -1), (0.0, 0.0, -1), (0.0, 0.0, -1)) self.indices = ((1, 0, 3), (1, 3, 2), (5, 4, 7), (5, 7, 6), (9, 8, 11), (9, 11, 10), (13, 12, 15), (13, 15, 14), (19, 18, 17), (19, 17, 16), (20, 21, 22), (20, 22, 23)) #texture scales (each set to 1 would stretch it over face) tw = tw / 2.0 th = th / 2.0 td = td / 2.0 self.tex_coords = ( (0.5 + tw, 0.5 - th), (0.5 - tw, 0.5 - th), (0.5 - tw, 0.5 + th), (0.5 + tw, 0.5 + th), #tw x th (0.5 + td, 0.5 - th), (0.5 - td, 0.5 - th), (0.5 - td, 0.5 + th), (0.5 + td, 0.5 + th), # td x th (0.5 - tw, 0.5 - th), (0.5 + tw, 0.5 - th), (0.5 + tw, 0.5 + th), (0.5 - tw, 0.5 + th), # tw x th (0.5 + tw, 0.5 + td), (0.5 - tw, 0.5 + td), (0.5 - tw, 0.5 - td), (0.5 + tw, 0.5 - td), # tw x td (0.5 - td, 0.5 + th), (0.5 + td, 0.5 + th), (0.5 + td, 0.5 - th), (0.5 - td, 0.5 - th), # td x th (0.5 - tw, 0.5 - th), (0.5 + tw, 0.5 - th), (0.5 + tw, 0.5 + th), (0.5 - tw, 0.5 + th)) # tw x th self.buf = [] self.buf.append( Buffer(self, self.vertices, self.tex_coords, self.indices, self.normals))
def __init__(self, camera=None, light=None, font=None, string=None, x=0.0, y=0.0, z=1.0, sx=DEFAULT_FONT_SCALE, sy=DEFAULT_FONT_SCALE, is_3d=True, size=DEFAULT_FONT_SIZE, rx=0.0, ry=0.0, rz=0.0, justify="C"): """Standard Shape constructor without the facility to change z scale or any of the offset values. Additional keyword arguments: *font* Pngfont or Font class object. *string* of ASCI characters in range(32, 128) plus 10 = \n Line Feed *sx, sy* These change the actual vertex positions of the shape rather than being used as scaling factors. This is to avoid distortion when the string is drawn using an orthographic camera *is_3d* alters the values of sx and sy to give reasonable sizes with 2D or 3D drawing *size* approximate size of the characters in inches - obviously for 3D drawing of strings this will depend on camera fov, display size and how far away the string is placed *justify* default C for central, can be R for right or L for left """ if not is_3d: sy = sx = size * 4.0 super(String, self).__init__(camera, light, "", x, y, z, rx, ry, rz, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0) if VERBOSE: print("Creating string ...") self.verts = [] self.texcoords = [] self.norms = [] self.inds = [] temp_verts = [] xoff = 0.0 yoff = 0.0 lines = 0 if not isinstance(string, text_type): string = string.decode('utf-8') nlines = string.count("\n") + 1 def make_verts(): #local function to justify each line if justify.upper() == "C": cx = xoff / 2.0 elif justify.upper() == "L": cx = 0.0 else: cx = xoff for j in temp_verts: self.verts.append([ (j[0] - cx) * sx, (j[1] + nlines * font.height * GAP / 2.0 - yoff) * sy, j[2] ]) default = font.glyph_table.get(unichr(0), None) for i, c in enumerate(string): if c == '\n': make_verts() yoff += font.height * GAP xoff = 0.0 temp_verts = [] lines += 1 continue #don't attempt to draw this character! glyph = font.glyph_table.get(c, default) if not glyph: continue w, h, texc, verts = glyph[0:4] for j in verts: temp_verts.append((j[0] + xoff, j[1], j[2])) xoff += w for j in texc: self.texcoords.append(j) self.norms.extend(_NORMALS) # Take Into account unprinted \n characters stv = 4 * (i - lines) self.inds.extend([[stv, stv + 2, stv + 1], [stv, stv + 3, stv + 2]]) make_verts() self.buf = [] self.buf.append( Buffer(self, self.verts, self.texcoords, self.inds, self.norms)) self.buf[0].textures = [font] self.buf[0].unib[1] = -1.0 self.string = string #for later use in quick_change() method self.maxlen = len(string) self.font = font
def __init__(self, mapfile, camera=None, light=None, width=100.0, depth=100.0, height=10.0, divx=0, divy=0, ntiles=1.0, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0, smooth=True, cubic=False, texmap=None): """uses standard constructor for Shape Arguments: *mapfile* Greyscale image path/file, string. If multiple bytes per pixel only the first one will be used for elevation. jpg files will create slight errors that will cause mis-matching of edges for tiled maps (i.e. use png for these) NB also see divx, divy below i.e. div=64x64 requires image 65x65 pixels Keyword arguments: *width, depth, height* Of the map in world units. *divx, divy* Number of divisions into which the map will be divided. to create vertices (vertices += 1) NB if you want to create a map with 64x64 divisions there will be 65x65 vertices in each direction so the mapfile (above) needs to 65x65 pixels in order to specify elevations precisely and avoid resizing errors. *ntiles* Number of repeats for tiling the texture image. *smooth* Calculate normals with averaging rather than pointing straight up, slightly faster if false. *texmap* Image file path or PIL.Image to be used to represent each of four textures and normals using the uv_elev_map shader. The image is converted to greyscale and apportioned between darkest (first and second entries in Buffer.textures list) and lightest (seventh and eighth entries). The resulting 0.0, 1.0, 2.0 or 3.0 is added to the uv texture coordinate i.e. Buffer.array_buffer[:,6:8] """ super(ElevationMap, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) divx += 1 # one more vertex in each direction than number of divisions divy += 1 if divx > 200 or divy > 200: LOGGER.warning("... Map size can't be bigger than 199x199 divisions") divx = 200 divy = 200 #print(type(mapfile), type("")) if PIL_OK: try: if '' + mapfile == mapfile: #HORRIBLE. Only way to cope with python2v3 mapfile = file_pathify(mapfile) LOGGER.debug("Loading height map ...%s", mapfile) im = Image.open(mapfile) else: im = mapfile #allow image files to be passed as mapfile except: im = mapfile ix, iy = im.size if (ix > 200 and divx == 0) or (ix != divx and iy != divy): if divx == 0: divx = 200 divy = 200 im = im.resize((divx, divy), Image.ANTIALIAS) ix, iy = im.size im = im.convert('L') im = im.transpose(Image.FLIP_TOP_BOTTOM) im = im.transpose(Image.FLIP_LEFT_RIGHT) self.pixels = im.load() if texmap is not None: try: texmap = file_pathify(texmap) tm = Image.open(texmap) except: tm = texmap tm = tm.convert('L') tm = tm.resize((ix, iy)) tm = np.array(tm) tm = np.floor(tm * 3.99 / (tm.max() - tm.min())) # set to 0.0, 1.0, 2.0, 3.0 tm = tm[::-1,::-1].T # effectively reflect and rotate to match mapping of elevation else: ''' images saved as compressed numpy npz file. No resizing so needs to be right size.''' mapfile = file_pathify(mapfile) self.pixels = np.load(mapfile)['arr_0'][::-1,::-1] # has to be saved with default key ix, iy = self.pixels.shape[:2] self.width = width self.depth = depth self.height = height self.ix = ix self.iy = iy self.ht_y = 0.0 self.ht_n = np.array([0.0, 1.0, 0.0]) LOGGER.debug("Creating Elevation Map ...%d x %d", ix, iy) self.wh = width * 0.5 self.hh = depth * 0.5 self.ws = width / (ix - 1.0) self.hs = depth / (iy - 1.0) self.ht = height / 255.0 tx = 1.0 * ntiles / ix ty = 1.0 * ntiles / iy verts = [] tex_coords = [] idx = [] for y in range(iy): for x in range(ix): hgt = self.pixels[x, y] * self.ht verts.append((-self.wh + x * self.ws, hgt, -self.hh + y * self.hs)) if texmap is not None: tex_n = tm[x, y] else: tex_n = 0.0 tex_coords.append((tex_n + (ix - x) * tx, (iy - y) * ty)) s = 0 #create one long triangle_strip by alternating X directions for y in range(0, iy-1): for x in range(0, ix-1): i = (y * ix)+x idx.append((i, i+ix, i+ix+1)) idx.append((i+ix+1, i+1, i)) s += 2 self.buf = [Buffer(self, verts, tex_coords, idx, None, smooth)]
def __init__(self, mapfile, camera=None, light=None, width=100.0, depth=100.0, height=10.0, divx=0, divy=0, ntiles=1.0, name="", x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, cx=0.0, cy=0.0, cz=0.0, smooth=True, cubic=False): """uses standard constructor for Shape Arguments: *mapfile* Greyscale image path/file, string. Keyword arguments: *width, depth, height* Of the map in world units. *divx, divy* Number of divisions into which the map will be divided. to create vertices *ntiles* Number of repeats for tiling the texture image. *smooth* Calculate normals with averaging rather than pointing straight up, slightly faster if false. """ super(ElevationMap, self).__init__(camera, light, name, x, y, z, rx, ry, rz, sx, sy, sz, cx, cy, cz) if VERBOSE: print "Loading height map ...", mapfile if divx > 200 or divy > 200: print "... Map size can't be bigger than 200x200 divisions" divx = 200 divy = 200 im = Image.open(mapfile) im = PIL.ImageOps.invert(im) ix, iy = im.size if (ix > 200 and divx == 0) or (divx > 0): if divx == 0: divx = 200 divy = 200 im = im.resize((divx, divy), Image.ANTIALIAS) ix, iy = im.size if not im.mode == "P": im = im.convert('P', palette=Image.ADAPTIVE) im = im.transpose(Image.FLIP_TOP_BOTTOM) im = im.transpose(Image.FLIP_LEFT_RIGHT) self.pixels = im.load() self.width = width self.depth = depth self.height = height self.ix = ix self.iy = iy self.ttype = GL_TRIANGLE_STRIP if VERBOSE: print "Creating Elevation Map ...", ix, iy wh = width * 0.5 hh = depth * 0.5 ws = width / ix hs = depth / iy ht = height / 255.0 tx = 1.0 * ntiles / ix ty = 1.0 * ntiles / iy verts = [] norms = [] tex_coords = [] idx = [] for y in xrange(0, iy): for x in xrange(0, ix): hgt = (self.pixels[x, y]) * ht this_x = -wh + x * ws this_z = -hh + y * hs if cubic: """ this is a bit experimental. It tries to make the map either zero or height high. Vertices are moved 'under' adjacent ones if there is a step to make vertical walls. Goes wrong in places - mainly because it doesn't check diagonals """ if hgt > height / 2: hgt = height else: hgt = 0.0 if hgt == 0 and y > 0 and y < iy - 1 and x > 0 and x < ix - 1: if self.pixels[x - 1, y] > 127: this_x = -wh + (x - 1) * ws elif self.pixels[x + 1, y] > 127: this_x = -wh + (x + 1) * ws elif self.pixels[x, y - 1] > 127: this_z = -hh + (y - 1) * hs elif self.pixels[x, y + 1] > 127: this_z = -hh + (y + 1) * hs elif self.pixels[x - 1, y - 1] > 127: this_x = -wh + (x - 1) * ws this_z = -hh + (y - 1) * hs elif self.pixels[x - 1, y + 1] > 127: this_x = -wh + (x - 1) * ws this_z = -hh + (y + 1) * hs elif self.pixels[x + 1, y - 1] > 127: this_x = -wh + (x + 1) * ws this_z = -hh + (y - 1) * hs elif self.pixels[x + 1, y + 1] > 127: this_x = -wh + (x + 1) * ws this_z = -hh + (y + 1) * hs verts.append((this_x, hgt, this_z)) tex_coords.append(((ix - x) * tx, (iy - y) * ty)) s = 0 #create one long triangle_strip by alternating X directions for y in range(0, iy - 1): for x in range(0, ix - 1): i = (y * ix) + x idx.append((i, i + ix, i + ix + 1)) idx.append((i + ix + 1, i + 1, i)) s += 2 self.buf = [] self.buf.append(Buffer(self, verts, tex_coords, idx, None, smooth))
def merge(self, bufr, x=0.0, y=0.0, z=0.0, rx=0.0, ry=0.0, rz=0.0, sx=1.0, sy=1.0, sz=1.0, bufnum=0): """merge the vertices, normals etc from this Buffer with those already there the position, rotation, scale, offset are set according to the origin of the MergeShape. If bufr is not a Buffer then it will be treated as if it is a Shape and its first Buffer object will be merged. Argument additional to standard Shape: *bufr* Buffer object or Shape with a member buf[0] that is a Buffer object. OR an array or tuple where each element is an array or tuple with the required arguments i.e. [[bufr1, x1, y1, z1, rx1, ry1....], [bufr2, x2, y2...],[bufr3, x3, y3...]] this latter is a more efficient way of building a MergeShape from lots of elements. If multiple Buffers are passed in this way then the subsequent arguments (x,y,z etc) will be ignored. *x, y, z, rx, ry, rz, sx, sy, sz* Position rotation scale if merging a single Buffer *bufnum* Specify the index of Buffer to use. This allows a MergeShape to contain multiple Buffers each with potentially different shader, material, textures, draw_method and unib """ if not isinstance(bufr, list) and not isinstance(bufr, tuple): buflist = [[bufr, x, y, z, rx, ry, rz, sx, sy, sz, bufnum]] else: buflist = bufr # existing array and element buffers to add to (as well as other draw relevant values) vertices = [] # will hold a list of ndarrays - one for each Buffer normals = [] tex_coords = [] indices = [] shader_list = [] material_list = [] textures_list = [] draw_method_list = [] unib_list = [] for b in self.buf: # first of all collect info from pre-existing Buffers buf = b.array_buffer# alias to tidy code vertices.append(buf[:,0:3] if len(buf) > 0 else buf) normals.append(buf[:,3:6] if len(buf) > 0 else buf) tex_coords.append(buf[:,6:8] if len(buf) > 0 else buf) #TODO this will only cope with N_BYTES == 32 indices.append(b.element_array_buffer[:]) shader_list.append(b.shader) material_list.append(b.material[:]) textures_list.append(b.textures[:]) draw_method_list.append(b.draw_method) unib_list.append(b.unib[:]) for b in buflist: if len(b) < 11: b.append(0) if b[10] >= len(vertices): #add buffers if needed for i in range(b[10] - len(vertices) + 1): vertices.append(np.zeros((0, 3), dtype='float32')) tex_coords.append(np.zeros((0, 2), dtype='float32')) indices.append(np.zeros((0, 3), dtype='float32')) normals.append(np.zeros((0, 3), dtype='float32')) shader_list.append(None) material_list.append(()) textures_list.append([]) draw_method_list.append(None) unib_list.append([]) if not(type(b[0]) is Buffer): #deal with being passed a Shape bufr = b[0].buf[0] else: bufr = b[0] n = len(bufr.array_buffer) LOGGER.info("Merging Buffer %s", bufr) original_vertex_count = len(vertices[b[10]]) vrot = rotate_vec(b[4], b[5], b[6], np.array(bufr.array_buffer[:,0:3])) vrot[:,0] = vrot[:,0] * b[7] + b[1] vrot[:,1] = vrot[:,1] * b[8] + b[2] vrot[:,2] = vrot[:,2] * b[9] + b[3] if bufr.array_buffer.shape[1] >= 6: nrot = rotate_vec(b[4], b[5], b[6], np.array(bufr.array_buffer[:,3:6])) else: nrot = np.zeros((n, 3)) vertices[b[10]] = np.append(vertices[b[10]], vrot) normals[b[10]] = np.append(normals[b[10]], nrot) if bufr.array_buffer.shape[1] == 8: tex_coords[b[10]] = np.append(tex_coords[b[10]], bufr.array_buffer[:,6:8]) else: tex_coords[b[10]] = np.append(tex_coords[b[10]], np.zeros((n, 2))) n = int(len(vertices[b[10]]) / 3) vertices[b[10]].shape = (n, 3) normals[b[10]].shape = (n, 3) tex_coords[b[10]].shape = (n, 2) #ctypes.restype = ctypes.c_short # TODO: remove this side-effect. faces = bufr.element_array_buffer + original_vertex_count indices[b[10]] = np.append(indices[b[10]], faces) n = int(len(indices[b[10]]) / 3) indices[b[10]].shape = (n, 3) shader_list[b[10]] = bufr.shader material_list[b[10]] = bufr.material[:] textures_list[b[10]] = bufr.textures[:] draw_method_list[b[10]] = bufr.draw_method unib_list[b[10]] = bufr.unib[:] self.buf = [] for i in range(len(vertices)): buf = Buffer(self, vertices[i], tex_coords[i], indices[i], normals[i]) # add back Buffer details from lists buf.shader = shader_list[i] buf.material = material_list[i] buf.textures = textures_list[i] buf.draw_method = draw_method_list[i] for j in range(len(unib_list[i])): # have to change elements in ctypes array buf.unib[j] = unib_list[i][j] self.buf.append(buf)