roc/models.py
cecilkorik deaa55d535 working textures!
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.
2011-06-17 19:54:49 -06:00

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