completely overhauled models framework... now loads Model, Mesh, Material, and Texture (plus TextureFile) fixed a number of bugs/inflexibilities in gamedata xml parser refactored the various modules to use "init" functions to better control when they get loaded (ie, before or after OpenGL init) which it turns out is *very important*, because if the shaders and textures get loaded before OpenGL does, they don't work. go figure.
392 lines
9.9 KiB
Python
Executable file
392 lines
9.9 KiB
Python
Executable file
import enums
|
|
import files
|
|
import gamedata
|
|
import pygame
|
|
from OpenGL.GL import *
|
|
from py3dutil import vect
|
|
|
|
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):
|
|
pass
|
|
|
|
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()
|
|
|
|
def load_textypes(self):
|
|
self.textypes = {}
|
|
self.textypes[enums.tt.diffuse] = TextureType(GL_TEXTURE0, GL_TEXTURE_2D, GL_CLAMP, GL_CLAMP, GL_NEAREST, GL_NEAREST)
|
|
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 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']:
|
|
mesh = SpriteMesh.singleton()
|
|
elif meshdata['centered_sprite']:
|
|
mesh = SpriteMeshCentered.singleton()
|
|
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
|
|
|
|
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):
|
|
glActiveTexture(self.texunit)
|
|
glTexParameterf(self.texdim, GL_TEXTURE_WRAP_S, self.wrap_s)
|
|
glTexParameterf(self.texdim, GL_TEXTURE_WRAP_T, self.wrap_t)
|
|
glTexParameterf(self.texdim, GL_TEXTURE_MAG_FILTER, self.mag)
|
|
glTexParameterf(self.texdim, GL_TEXTURE_MIN_FILTER, self.min)
|
|
|
|
def get_dimension(self):
|
|
return self.texdim
|
|
|
|
def activate(self):
|
|
glActiveTexture(self.texunit)
|
|
|
|
|
|
|
|
|
|
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.initialize()
|
|
tex = self.textures[tt]
|
|
tex.bind()
|
|
|
|
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
|
|
|
|
print self.textures
|
|
|
|
|
|
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) / float(tf.w)
|
|
dest_w = (float(self.x + self.w) / float(tf.w)) - dest_x
|
|
dest_y = float(self.y) / float(tf.h)
|
|
dest_h = (float(self.y + self.h) / 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
|
|
|
|
def bind(self):
|
|
glBindTexture(GL_TEXTURE_2D, self.texid)
|
|
|
|
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" % (texid,)
|
|
|
|
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 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):
|
|
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):
|
|
_SINGLETON = None
|
|
|
|
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), (1.0, 1.0, 1.0)),
|
|
Vertex(vect(0.0, 1.0, 0.0), vect(0.0, 0.0, 1.0), (0.0, 1.0), (1.0, 1.0, 1.0)),
|
|
Vertex(vect(1.0, 0.0, 0.0), vect(0.0, 0.0, 1.0), (1.0, 0.0), (1.0, 1.0, 1.0)),
|
|
Vertex(vect(1.0, 0.0, 0.0), vect(0.0, 0.0, 1.0), (1.0, 0.0), (1.0, 1.0, 1.0)),
|
|
Vertex(vect(0.0, 1.0, 0.0), vect(0.0, 0.0, 1.0), (0.0, 1.0), (1.0, 1.0, 1.0)),
|
|
Vertex(vect(1.0, 1.0, 0.0), vect(0.0, 0.0, 1.0), (1.0, 1.0), (1.0, 1.0, 1.0)),
|
|
]
|
|
|
|
@classmethod
|
|
def return_singleton(cls):
|
|
return cls._SINGLETON
|
|
|
|
@classmethod
|
|
def singleton(cls):
|
|
cls._SINGLETON = cls()
|
|
cls.singleton = cls.return_singleton
|
|
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), (1.0, 1.0, 1.0)),
|
|
Vertex(vect( 0.5, -0.5, 0.0), vect(0.0, 0.0, 1.0), (1.0, 0.0), (1.0, 1.0, 1.0)),
|
|
Vertex(vect(-0.5, 0.5, 0.0), vect(0.0, 0.0, 1.0), (0.0, 1.0), (1.0, 1.0, 1.0)),
|
|
Vertex(vect( 0.5, -0.5, 0.0), vect(0.0, 0.0, 1.0), (1.0, 0.0), (1.0, 1.0, 1.0)),
|
|
Vertex(vect(-0.5, 0.5, 0.0), vect(0.0, 0.0, 1.0), (0.0, 1.0), (1.0, 1.0, 1.0)),
|
|
Vertex(vect( 0.5, 0.5, 0.0), vect(0.0, 0.0, 1.0), (1.0, 1.0), (1.0, 1.0, 1.0)),
|
|
]
|
|
|
|
|
|
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 RenderLayer(object):
|
|
def __init__(self, layernum):
|
|
self.id = layernum
|
|
self.mesh = None
|
|
self.material = None
|
|
self.color = [1.0, 1.0, 1.0, 1.0]
|
|
self.scale = [1.0, 1.0, 1.0]
|
|
|
|
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()
|
|
glTranslatef(500.0, 300.0, 0.0)
|
|
glScalef(*self.scale)
|
|
self.mesh.draw(self.material.texcoords)
|
|
glPopMatrix()
|
|
|
|
|
|
class Sprite(object):
|
|
def __init__(self):
|
|
Model.__init__(self)
|
|
vl = [Vertex() for x in xrange(4)]
|
|
|
|
normal = vect(0.0, 0.0, 1.0)
|
|
white = (1.0, 1.0, 1.0)
|
|
for v in vl:
|
|
v.n = normal
|
|
v.c = white
|
|
|
|
tl, bl, tr, br = vl
|
|
|
|
tl.v = vect(-0.5, -0.5, 0.0); tl.t = (0.0, 0.0)
|
|
bl.v = vect(-0.5, 0.5, 0.0); bl.t = (0.0, 1.0)
|
|
tr.v = vect( 0.5, -0.5, 0.0); tr.t = (1.0, 0.0)
|
|
br.v = vect( 0.5, 0.5, 0.0); br.t = (1.0, 1.0)
|
|
|
|
self.corners = vl
|
|
self.vertexes = [tl, bl, tr, bl, tr, br]
|
|
|
|
def set_texture(self):
|
|
pass
|
|
|
|
def init():
|
|
global mgr
|
|
mgr = Model_Manager()
|
|
mgr.load()
|
|
|
|
mgr = None
|