444 lines
11 KiB
Python
Executable file
444 lines
11 KiB
Python
Executable file
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
|