mirror of
https://github.com/quantum5/punyverse.git
synced 2025-04-24 13:11:57 -04:00
Backported all of Cython model loader to Python.
Signed-off-by: Xiaomao Chen <xiaomao5@live.com>
This commit is contained in:
parent
e9403fe746
commit
35d77c2bc0
|
@ -328,8 +328,6 @@ cpdef int model_list(WavefrontObject model, float sx=1, float sy=1, float sz=1,
|
|||
glDisable(GL_TEXTURE_2D)
|
||||
|
||||
if g.material is not None:
|
||||
if g.material.texture is not None:
|
||||
tex_id = load_texture(os.path.join(model.root, g.material.texture))
|
||||
if g.material.Ka:
|
||||
kx, ky, kz = g.material.Ka
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [kx, ky, kz, 1])
|
||||
|
|
|
@ -1,14 +1,246 @@
|
|||
from time import clock
|
||||
import os.path
|
||||
from punyverse.texture import load_texture
|
||||
|
||||
from pyglet.gl import *
|
||||
from uuid import uuid4
|
||||
import os
|
||||
import gzip
|
||||
import bz2
|
||||
import zipfile
|
||||
|
||||
|
||||
def zip_open(file):
|
||||
zip = zipfile.ZipFile(file)
|
||||
return zip.open(zip.namelist()[0])
|
||||
|
||||
openers = {
|
||||
'gz': gzip.open,
|
||||
'bz2': bz2.BZ2File,
|
||||
'zip': zip_open,
|
||||
}
|
||||
|
||||
from punyverse.texture import load_texture
|
||||
|
||||
FACE_TRIANGLES = 0
|
||||
FACE_QUADS = 1
|
||||
|
||||
|
||||
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, 0, 0), Kd=(0, 0, 0), Ks=(0, 0, 0), shininess=0.0):
|
||||
self.name = name
|
||||
self.texture = texture
|
||||
self.Ka = Ka
|
||||
self.Kd = Kd
|
||||
self.Ks = Ks
|
||||
self.shininess = shininess
|
||||
|
||||
|
||||
class Group(object):
|
||||
def __init__(self, name=None):
|
||||
if name is None:
|
||||
self.name = str(uuid4())
|
||||
else:
|
||||
self.name = name
|
||||
self.min = ()
|
||||
self.material = None
|
||||
self.faces = []
|
||||
self.indices = []
|
||||
self.vertices = []
|
||||
self.normals = []
|
||||
self.textures = []
|
||||
self.idx_count = 0
|
||||
|
||||
def pack(self):
|
||||
min_x, min_y, min_z = 0, 0, 0
|
||||
for face in self.faces:
|
||||
for x, y, z in face.vertices:
|
||||
min_x = max(min_x, abs(x))
|
||||
min_y = max(min_y, abs(y))
|
||||
min_z = max(min_x, abs(z))
|
||||
self.min = (min_x, min_y, min_z)
|
||||
|
||||
|
||||
class WavefrontObject(object):
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.root = os.path.abspath(os.path.dirname(path))
|
||||
self.vertices = []
|
||||
self.normals = []
|
||||
self.textures = []
|
||||
self.groups = []
|
||||
self.materials = {}
|
||||
|
||||
self.perform_io(self.path)
|
||||
|
||||
def new_material(self, words):
|
||||
material = Material(words[1])
|
||||
self.materials[words[1]] = material
|
||||
self.current_material = material
|
||||
|
||||
def Ka(self, words):
|
||||
self.current_material.Ka = (float(words[1]), float(words[2]), float(words[3]))
|
||||
|
||||
def Kd(self, words):
|
||||
self.current_material.Kd = (float(words[1]), float(words[2]), float(words[3]))
|
||||
|
||||
def Ks(self, words):
|
||||
self.current_material.Ks = (float(words[1]), float(words[2]), float(words[3]))
|
||||
|
||||
def material_shininess(self, words):
|
||||
self.current_material.shininess = min(float(words[1]), 125)
|
||||
|
||||
def material_texture(self, words):
|
||||
self.current_material.texture = words[-1]
|
||||
|
||||
def vertex(self, words):
|
||||
self.vertices.append((float(words[1]), float(words[2]), float(words[3])))
|
||||
|
||||
def normal(self, words):
|
||||
self.normals.append((float(words[1]), float(words[2]), float(words[3])))
|
||||
|
||||
def texture(self, 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])
|
||||
self.textures.append((x, y, z))
|
||||
|
||||
def face(self, words):
|
||||
l = len(words)
|
||||
type = -1
|
||||
vertex_count = l - 1
|
||||
face_vertices = []
|
||||
face_normals = []
|
||||
face_textures = []
|
||||
|
||||
if vertex_count == 3:
|
||||
type = FACE_TRIANGLES
|
||||
else:
|
||||
type = FACE_QUADS
|
||||
|
||||
current_value = -1
|
||||
texture_len = len(self.textures)
|
||||
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(self.vertices[current_value - 1])
|
||||
|
||||
if l == 1:
|
||||
continue
|
||||
if l >= 2 and raw_faces[1]:
|
||||
current_value = int(raw_faces[1])
|
||||
if current_value <= texture_len:
|
||||
tindices.append(current_value - 1)
|
||||
face_textures.append(self.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(self.normals[current_value - 1])
|
||||
|
||||
if self.current_group is None:
|
||||
self.current_group = group = Group()
|
||||
self.groups.append(group)
|
||||
else:
|
||||
group = self.current_group
|
||||
|
||||
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))
|
||||
|
||||
def material(self, words):
|
||||
self.perform_io(os.path.join(self.root, words[1]))
|
||||
|
||||
def use_material(self, words):
|
||||
mat = words[1]
|
||||
try:
|
||||
self.current_group.material = self.materials[mat]
|
||||
except KeyError:
|
||||
print "Warning: material %s undefined, only %s defined." % (mat, self.materials)
|
||||
except AttributeError:
|
||||
print "Warning: no group"
|
||||
|
||||
def group(self, words):
|
||||
name = words[1]
|
||||
group = Group(name)
|
||||
|
||||
if self.groups:
|
||||
self.current_group.pack()
|
||||
self.groups.append(group)
|
||||
self.current_group = group
|
||||
|
||||
def perform_io(self, file):
|
||||
ext = os.path.splitext(file)[1].lstrip('.')
|
||||
reader = openers.get(ext, open)(file)
|
||||
|
||||
dispatcher = {
|
||||
'v': self.vertex,
|
||||
'vn': self.normal,
|
||||
'vt': self.texture,
|
||||
'f': self.face,
|
||||
'mtllib': self.material,
|
||||
'usemtl': self.use_material,
|
||||
'g': self.group,
|
||||
'o': self.group,
|
||||
'newmtl': self.new_material,
|
||||
'Ka': self.Ka,
|
||||
'Kd': self.Kd,
|
||||
'Ks': self.Ks,
|
||||
'Ns': self.material_shininess,
|
||||
'map_Kd': self.material_texture,
|
||||
}
|
||||
default = lambda words: None
|
||||
|
||||
with reader:
|
||||
for buf in reader:
|
||||
if not buf or buf.startswith(('\r', '\n', '#')):
|
||||
continue # Empty or comment
|
||||
words = buf.split()
|
||||
type = words[0]
|
||||
|
||||
dispatcher.get(type, default)(words)
|
||||
return True
|
||||
|
||||
import sys
|
||||
if hasattr(sys, 'frozen'):
|
||||
model_base = os.path.dirname(sys.executable)
|
||||
else:
|
||||
model_base = os.path.join(os.path.dirname(__file__), 'assets', 'models')
|
||||
|
||||
|
||||
def load_model(path):
|
||||
if not os.path.isabs(path):
|
||||
path = os.path.join(model_base, path)
|
||||
if not isinstance(path, unicode):
|
||||
path = path.decode('mbcs')
|
||||
return WavefrontObject(path)
|
||||
|
||||
|
||||
def model_list(model, sx=1, sy=1, sz=1, rotation=(0, 0, 0)):
|
||||
for m, text in model.materials.iteritems():
|
||||
if text.texture:
|
||||
|
@ -83,206 +315,14 @@ def model_list(model, sx=1, sy=1, sz=1, rotation=(0, 0, 0)):
|
|||
point(f, vertices, normals, textures, 2)
|
||||
point(f, vertices, normals, textures, 3)
|
||||
point(f, vertices, normals, textures, 0)
|
||||
glEnd()
|
||||
glEnd()
|
||||
|
||||
if tex_id:
|
||||
glBindTexture(GL_TEXTURE_2D, 0)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
|
||||
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
|
||||
return display
|
Loading…
Reference in a new issue