import os import glob import enums import files import gamedata import pygame from OpenGL.GL import * from py3dutil import vect import fonts import collections class Vertex(object): __slots__ = [ 'v', # vertex coordinates 'n', # normal 't', # texture coordinates 'c', # color ] def __init__(self, v, n, t, c): self.v = v self.n = n self.t = t self.c = c class Model_Manager(object): def __init__(self): self.centered_sprite_mesh = None self.sprite_mesh = None def create(self, mname): return self.models[mname].instance() def get_textype(self, textype): if isinstance(textype, TextureType): return textype return self.textypes[textype] def load(self): self.load_textypes() self.load_textures() self.load_materials() self.load_models() self.load_fonts() def load_textypes(self): self.textypes = {} self.textypes[enums.tt.diffuse] = TextureType(GL_TEXTURE0, GL_TEXTURE_2D, GL_CLAMP, GL_CLAMP, GL_LINEAR, GL_LINEAR) self.textypes[enums.tt.emissive] = TextureType(GL_TEXTURE1, GL_TEXTURE_2D) self.textypes[enums.tt.specular] = TextureType(GL_TEXTURE2, GL_TEXTURE_2D) self.textypes[enums.tt.normal] = TextureType(GL_TEXTURE3, GL_TEXTURE_2D) for tt in list(self.textypes.values()): tt.initialize() def load_textures(self): texdata = gamedata.get('textures') self.texture_files = {} self.texture_ids = {} self.textures = {} self.materials = {} for texd in texdata.texture: fp = files.mgr.canonize_path(texd['file']) if not fp in self.texture_files: tf = TextureFile() tf.load(fp) self.texture_files[fp] = tf self.texture_ids[tf.id] = tf else: tf = self.texture_files[fp] to = Texture() to.load(texd, tf) self.textures[to.id] = to for matd in texdata.material: mo = Material() mo.load(matd) self.materials[mo.id] = mo def load_meshes(self): "ignored!!! meshes are contained in models.xml for now" return gamedata.get('meshes') def load_materials(self): "ignored!!! materials are contained in textures.xml for now" return gamedata.get('materials') def load_models(self): mdldata = gamedata.get('models') self.meshes = {} self.models = {} for meshdata in mdldata.mesh: if meshdata['sprite']: self.sprite_mesh = SpriteMesh() mesh = self.sprite_mesh elif meshdata['centered_sprite']: if self.centered_sprite_mesh == None: self.centered_sprite_mesh = SpriteMeshCentered() mesh = self.centered_sprite_mesh else: mesh = Mesh.factory(meshdata) mgr.meshes[meshdata['id']] = mesh for modeldata in mdldata.model: model = Model() model.load(modeldata) mgr.models[modeldata['id']] = model def load_fonts(self): self.fontlib = {} fontdir = os.path.join(files.get_basedir(), 'data', 'font') for fontfile in glob.glob(os.path.join(fontdir, '*.tfd')): fontname = os.path.splitext(os.path.split(fontfile)[1])[0] fontobj = fonts.TexFont.new(fontdir, fontname) self.fontlib[fontname] = fontobj print("Loaded font %s" % (fontname,)) def get_font(self, name): return self.fontlib[name] class TextureType(object): def __init__(self, texunit, texdim, wrap_s=GL_CLAMP, wrap_t=GL_CLAMP, mag=GL_LINEAR, min=GL_LINEAR): self.texunit = texunit self.texdim = texdim self.wrap_s = wrap_s self.wrap_t = wrap_t self.mag = mag self.min = min def initialize(self): return def apply_parameters(self): glTexParameteri(self.texdim, GL_TEXTURE_WRAP_S, self.wrap_s) glTexParameteri(self.texdim, GL_TEXTURE_WRAP_T, self.wrap_t) glTexParameteri(self.texdim, GL_TEXTURE_MAG_FILTER, self.mag) glTexParameteri(self.texdim, GL_TEXTURE_MIN_FILTER, self.min) def get_dimension(self): return self.texdim def activate(self): glActiveTexture(self.texunit) class Texture(object): def __init__(self): self.id = None self.texid = None self.uniform = False self.x = None self.y = None self.h = None self.w = None def texcoords_subset(self, u, v): tf = mgr.texture_ids[self.texid] dest_x = (float(self.x)-0.0) / float(tf.w) dest_w = ((float(self.x + self.w)+0.3) / float(tf.w)) - dest_x dest_y = (float(self.y)-0.3) / float(tf.h) dest_h = ((float(self.y + self.h)+0.0) / float(tf.h)) - dest_y return (dest_x + (u * dest_w), dest_y + (v * dest_h)) def texcoords_direct(self, u, v): return (u, v) def load(self, data, tf): self.id = data['id'] self.texid = tf.id self.uniform = data['uniform'] c = data.coords if c['x'] != -1 and c['y'] != -1 and c['h'] != -1 and c['w'] != -1: self.x = c['x'] self.y = c['y'] self.w = c['w'] self.h = c['h'] assert self.x < tf.w and self.w < tf.w and self.y < tf.h and self.h < tf.h self.texcoords = self.texcoords_subset elif c['x1'] != -1 and c['y1'] != -1 and c['x2'] != -1 and c['y2'] != -1: self.x = c['x1'] self.y = c['y1'] self.w = c['x2'] - self.x self.h = c['y2'] - self.y assert self.x < tf.w and self.w < tf.w and self.y < tf.h and self.h < tf.h self.texcoords = self.texcoords_subset else: self.x = 0 self.y = 0 self.w = tf.w self.h = tf.h self.texcoords = self.texcoords_direct #self.texcoords = self.texcoords_subset def bind(self, textype): textype.activate() glBindTexture(GL_TEXTURE_2D, self.texid) textype.apply_parameters() class TextureFile(object): def __init__(self): self.id = None self.filename = None self.h = None self.w = None def load(self, filename): self.filename = filename img = files.mgr.png(filename) texid = glGenTextures(1) print("Generated texture id %s from %s" % (texid, self.filename)) self.id = texid imgdata = pygame.image.tostring(img, "RGBA") imgr = img.get_rect() self.h = imgr.h self.w = imgr.w dimension = GL_TEXTURE_2D glBindTexture(dimension, texid) glTexImage2D(dimension, 0, GL_RGBA8, imgr.w, imgr.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgdata) #glBindTexture(dimension, 0) class Material(object): def __init__(self): self.id = None self.textures = {} self.texcoords = self.texcoords_notimpl self.texcoords_uniform = self.texcoords_notimpl def texcoords_notimpl(self, u, v): raise NotImplementedError("No textures were associated with this material.") def bind(self): for tt in enums.tt: if tt in self.textures: tto = mgr.get_textype(tt) tto.activate() tex = self.textures[tt] #print "Binding texture %s" % (tex.texid,) tex.bind(tto) def load(self, data): self.id = data['id'] prevtex = None for tex in data.texture: try: texobj = mgr.textures[tex['ref']] except KeyError: raise KeyError("""Material "%s" references invalid %s texture "%s".""" % (self.id, tex['type'], tex['ref'])) typeidx = enums.index(enums.tt, tex['type']) self.textures[typeidx] = texobj if texobj.uniform: if self.texcoords_uniform == self.texcoords_notimpl: self.texcoords_uniform = texobj.texcoords else: if prevtex: assert texobj.texcoords(0.0, 0.0) == prevtex.texcoords(0.0, 0.0) and texobj.texcoords(1.0, 1.0) == prevtex.texcoords(1.0, 1.0) if self.texcoords == self.texcoords_notimpl: self.texcoords = texobj.texcoords prevtex = texobj class Mesh(object): def __init__(self): self.vertexes = [] def set_rotation(self): glPushMatrix() glMultMatrixf(self.rot.get_matrix()) def unset_rotation(self): glPopMatrix() def draw(self, texcoord): glBegin(GL_TRIANGLES) for v in self.vertexes: self.draw_vertex(v, texcoord) glEnd() def draw_vertex(self, v, texcoord): if v.c != None: glColor3f(v.c[0], v.c[1], v.c[2]) glNormal3f(v.n.x, v.n.y, v.n.z) glTexCoord2f(*texcoord(v.t[0], v.t[1])) glVertex3f(v.v.x, v.v.y, v.v.z) class SpriteMesh(Mesh): def __init__(self): Mesh.__init__(self) self.vertexes = [ Vertex(vect(0.0, 0.0, 0.0), vect(0.0, 0.0, 1.0), (0.0, 0.0), None), Vertex(vect(0.0, 1.0, 0.0), vect(0.0, 0.0, 1.0), (0.0, 1.0), None), Vertex(vect(1.0, 0.0, 0.0), vect(0.0, 0.0, 1.0), (1.0, 0.0), None), Vertex(vect(1.0, 0.0, 0.0), vect(0.0, 0.0, 1.0), (1.0, 0.0), None), Vertex(vect(0.0, 1.0, 0.0), vect(0.0, 0.0, 1.0), (0.0, 1.0), None), Vertex(vect(1.0, 1.0, 0.0), vect(0.0, 0.0, 1.0), (1.0, 1.0), None), ] @classmethod def singleton(cls): if cls.__SINGLETON == None: cls.__SINGLETON = cls() return cls.__SINGLETON class SpriteMeshCentered(SpriteMesh): def __init__(self): SpriteMesh.__init__(self) self.vertexes = [ Vertex(vect(-0.5, -0.5, 0.0), vect(0.0, 0.0, 1.0), (0.0, 0.0), None), Vertex(vect(-0.5, 0.5, 0.0), vect(0.0, 0.0, 1.0), (0.0, 1.0), None), Vertex(vect( 0.5, -0.5, 0.0), vect(0.0, 0.0, 1.0), (1.0, 0.0), None), Vertex(vect( 0.5, -0.5, 0.0), vect(0.0, 0.0, 1.0), (1.0, 0.0), None), Vertex(vect(-0.5, 0.5, 0.0), vect(0.0, 0.0, 1.0), (0.0, 1.0), None), Vertex(vect( 0.5, 0.5, 0.0), vect(0.0, 0.0, 1.0), (1.0, 1.0), None), ] class RenderLayer(object): def __init__(self, layernum): self.id = layernum self.mesh = None self.material = None self.color = None self.scale = [1.0, 1.0, 1.0] self.displaylist = None self.anim = "idle" def load(self, layerdata): meshdata = layerdata.mesh matdata = layerdata.material self.mesh = mgr.meshes[meshdata['ref']] self.material = mgr.materials[matdata['ref']] self.color = [matdata.color['r'], matdata.color['g'], matdata.color['b'], matdata.color['a']] self.scale = [meshdata.scale['x'], meshdata.scale['y'], meshdata.scale['z']] self.scale = [x * meshdata['scale'] for x in self.scale] def render(self): self.material.bind() glPushMatrix() glScalef(*self.scale) if self.color != None: glColor4f(*self.color) if self.displaylist == None: self.displaylist = glGenLists(1) glNewList(self.displaylist, GL_COMPILE) self.mesh.draw(self.material.texcoords) glEndList() #print "Drawing mesh %s" % ( self.displaylist, ) glCallList(self.displaylist) glPopMatrix() class ParticleLayer(RenderLayer): def __init__(self, layernum): RenderLayer.__init__(self, layernum) self.particles = collections.deque() def render(self): pass class Model(object): def __init__(self): self.layers = [] def load(self, data): tmplayers = [] for rend in data.render: layernum = rend['layer'] rl = RenderLayer(layernum) rl.load(rend) tmplayers.append((layernum, rl)) for ln, rl in sorted(tmplayers): self.layers.append(rl) def instance(self): return self def render(self): for layer in self.layers: layer.render() class TextModel(Model): def __init__(self, fontname, text, color=None, scale=None): self.displaylist = None self.fontname = fontname self.text = text self.color = color self.scale = scale if self.scale == None: self.scale = (1.0, 1.0, 1.0) def load(self, data): pass def instance(self): return self def render(self): glPushMatrix() glScalef(*self.scale) if self.color != None: glColor4f(*self.color) if self.displaylist == None: self.displaylist = glGenLists(1) glNewList(self.displaylist, GL_COMPILE) font = mgr.get_font(self.fontname) font.render(self.text, self.color) glEndList() #print "Drawing mesh %s" % ( self.displaylist, ) glCallList(self.displaylist) glPopMatrix() def init(): global mgr mgr = Model_Manager() mgr.load() mgr = None