punyverse/punyverse/model.py
Quantum a7ffda822e Added launcher generation and fixed all imports.
Signed-off-by: Xiaomao Chen <xiaomao5@live.com>
2013-10-29 18:52:35 -04:00

288 lines
8 KiB
Python

from time import clock
import os.path
from pyglet.gl import *
from punyverse.texture import load_texture
FACE_TRIANGLES = 0
FACE_QUADS = 1
def model_list(model, sx=1, sy=1, sz=1, rotation=(0, 0, 0)):
for m, text in model.materials.iteritems():
if text.texture:
load_texture(os.path.join(model.root, text.texture))
display = glGenLists(1)
glNewList(display, GL_COMPILE)
glPushMatrix()
glPushAttrib(GL_CURRENT_BIT)
pitch, yaw, roll = rotation
glPushAttrib(GL_TRANSFORM_BIT)
glRotatef(pitch, 1, 0, 0)
glRotatef(yaw, 0, 1, 0)
glRotatef(roll, 0, 0, 1)
glPopAttrib()
vertices = model.vertices
textures = model.textures
normals = model.normals
for g in model.groups:
material = g.material
tex_id = load_texture(os.path.join(model.root, material.texture)) if (material and material.texture) else 0
if tex_id:
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, tex_id)
else:
glBindTexture(GL_TEXTURE_2D, 0)
glDisable(GL_TEXTURE_2D)
if material:
fv4 = GLfloat * 4
if material.Ka:
kx, ky, kz = material.Ka
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, fv4(kx, ky, kz, 1))
if material.Kd:
kx, ky, kz = material.Kd
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fv4(kx, ky, kz, 1))
if material.Ks:
kx, ky, kz = material.Ks
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(kx, ky, kz, 1))
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material.shininess)
type = -1
def point(f, vertices, normals, textures, n):
if f.norms:
glNormal3f(*normals[f.norms[n]])
if tex_id:
glTexCoord2f(*textures[f.texs[n]][:2])
x, y, z = vertices[f.verts[n]]
glVertex3f(x * sx, y * sy, z * sz)
for f in g.faces:
if type != f.type:
if type != -1:
glEnd()
glBegin(GL_TRIANGLES)
type = f.type
point(f, vertices, normals, textures, 0)
point(f, vertices, normals, textures, 1)
point(f, vertices, normals, textures, 2)
if type == FACE_QUADS:
point(f, vertices, normals, textures, 2)
point(f, vertices, normals, textures, 3)
point(f, vertices, normals, textures, 0)
glEnd()
glPopAttrib()
glPopMatrix()
glEndList()
return display
def load_model(path):
path = os.path.join(os.path.dirname(__file__), "assets", "models", path)
root = os.path.dirname(path)
vertices = []
normals = []
textures = []
groups = []
materials = {}
# Allez au diable, Python
current_group = [None]
current_material = [None]
def dispatch(p, commands):
with open(p, 'r') as file:
for line in file:
line = line.strip()
if not line or line[0] == '#':
continue # Empty or comment
words = line.split()
type = words[0]
if type in commands:
commands[type](words)
def newmtl(words):
material = Material(words[1])
materials[words[1]] = material
current_material[0] = material
def Ka(words):
current_material[0].Ka = (float(words[1]), float(words[2]), float(words[3]))
def Kd(words):
current_material[0].Kd = (float(words[1]), float(words[2]), float(words[3]))
def Ks(words):
current_material[0].Ks = (float(words[1]), float(words[2]), float(words[3]))
def Ns(words):
current_material[0].shininess = min(float(words[1]), 125) # Seems to sometimes be > 125. TODO: find out why
def map_Kd(words):
current_material[0].texture = words[-1]
def v(words):
vertices.append((float(words[1]), float(words[2]), float(words[3])))
def vn(words):
normals.append((float(words[1]), float(words[2]), float(words[3])))
def vt(words):
l = len(words)
x, y, z = 0, 0, 0
if l >= 2:
x = float(words[1])
if l >= 3:
# OBJ origin is at upper left, OpenGL origin is at lower left
y = 1 - float(words[2])
if l >= 4:
z = float(words[3])
textures.append((x, y, z))
def f(words):
l = len(words)
type = -1
face_vertices = []
face_normals = []
face_textures = []
vertex_count = l - 1
if vertex_count == 3:
type = FACE_TRIANGLES
else:
type = FACE_QUADS
raw_faces = []
current_value = -1
vindices = []
nindices = []
tindices = []
for i in xrange(1, vertex_count + 1):
raw_faces = words[i].split('/')
l = len(raw_faces)
current_value = int(raw_faces[0])
vindices.append(current_value - 1)
face_vertices.append(vertices[current_value - 1])
if l == 1:
continue
if l >= 2 and raw_faces[1]:
current_value = int(raw_faces[1])
if current_value <= len(textures):
tindices.append(current_value - 1)
face_textures.append(textures[current_value - 1])
if l >= 3 and raw_faces[2]:
current_value = int(raw_faces[2])
nindices.append(current_value - 1)
face_normals.append(normals[current_value - 1])
if not groups:
group = Group()
groups.append(group)
else:
group = groups[-1]
group.vertices += face_vertices
group.normals += face_normals
group.textures += face_textures
idx_count = group.idx_count
group.indices += (idx_count + 1, idx_count + 2, idx_count + 3)
group.idx_count += 3
group.faces.append(Face(type, vindices, nindices, tindices, face_vertices, face_normals, face_textures))
mtllib = lambda words: dispatch(os.path.join(root, words[1]), obj)
def usemtl(words):
mat = words[1]
if mat in materials:
groups[-1].material = materials[mat]
else:
print "Warning: material %s undefined." % mat
g = lambda words: groups.append(Group(words[1]))
obj = {
'v': v,
'vn': vn,
'vt': vt,
'f': f,
'mtllib': mtllib,
'usemtl': usemtl,
'g': g,
'o': g, # TODO: o is not really g
'newmtl': newmtl,
'Ka': Ka,
'Kd': Kd,
'Ks': Ks,
'Ns': Ns,
'map_Kd': map_Kd
}
dispatch(path, obj)
return WavefrontObject(root, vertices, normals, textures, groups, materials)
class WavefrontObject(object):
def __init__(self, root, vertices=None, normals=None, textures=None, groups=None, materials=None):
self.root = root
self.vertices = vertices or []
self.normals = normals or []
self.textures = textures or []
self.groups = groups or []
self.materials = materials or []
class Group(object):
def __init__(self, name=None):
if not name:
name = clock()
self.name = name
self.material = None
self.faces = []
self.indices = []
self.vertices = []
self.normals = []
self.textures = []
self.idx_count = 0
class Face(object):
def __init__(self, type, verts, norms, texs, vertices, normals, textures):
self.type = type
self.verts = verts
self.norms = norms
self.texs = texs
self.vertices = vertices
self.normals = normals
self.textures = textures
class Material(object):
def __init__(self, name, texture=None, Ka=0, Kd=0, Ks=0, shininess=0.0):
self.name = name
self.texture = texture
self.Ka = Ka
self.Kd = Kd
self.Ks = Ks
self.shininess = shininess