roc/models.py

500 lines
13 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.noaa] = TextureType(GL_TEXTURE0, GL_TEXTURE_2D, GL_REPEAT, GL_REPEAT, 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 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']:
if self.sprite_mesh == None:
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
self.texcoords = 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
def get_base_texture(self):
if enums.tt.noaa in self.textures:
t = self.textures[enums.tt.noaa]
else:
t = self.textures[enums.tt.diffuse]
return t
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),
]
@staticmethod
def factory(meshdata):
s = SpriteMeshCentered()
s.scale
return s
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"
self.rotations = None
self.tile = False
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]
self.rotations = [meshdata.rot['x'], meshdata.rot['y'], meshdata.rot['z']]
if meshdata['scale_to_tex']:
t = self.material.get_base_texture()
self.scale = [float(t.w), float(t.h), 1.0]
def get_base_texture(self):
if enums.tt.noaa in self.material.textures:
t = self.material.textures[enums.tt.noaa]
else:
t = self.material.textures[enums.tt.diffuse]
return t
def render(self):
self.material.bind()
glPushMatrix()
glScalef(*self.scale)
if self.color != None:
glColor4f(*self.color)
glRotatef(self.rotations[0], 1.0, 0.0, 0.0)
glRotatef(self.rotations[1], 0.0, 1.0, 0.0)
glRotatef(self.rotations[2], 0.0, 0.0, 1.0)
if self.displaylist == None:
self.displaylist = glGenLists(1)
glNewList(self.displaylist, GL_COMPILE)
if self.tile:
tcfunc = self.tile_texcoords
else:
tcfunc = self.material.texcoords
self.mesh.draw(tcfunc)
glEndList()
#print "Drawing mesh %s" % ( self.displaylist, )
glCallList(self.displaylist)
glPopMatrix()
def tile_texcoords(self, u, v):
t = self.material.get_base_texture()
u = u * self.scale[0] / t.w
v = v * self.scale[1] / t.h
return self.material.texcoords(u, v)
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 get_size(self):
if not self.layers:
return (0.0, 0.0, 0.0)
for layer in self.layers:
return [layer.scale[i] for i in range(0, len(layer.scale))]
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):
Model.__init__(self)
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 get_size(self):
font = mgr.get_font(self.fontname)
return font.get_size(self.text)
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