Okay, for your amusement, a BCX/BSX viewer in Python:
import sys, struct, array, math, os, zlib
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL.ARB.texture_rectangle import *
from OpenGL.GL.ARB.multitexture import *
import pygame
from pygame.locals import *
bcxMap = {}
normals = []
def vecDot(a,b):
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]
def vecNormal(a):
lenSqr = vecDot(a,a)
if lenSqr > 0.0:
ool = 1.0 / math.sqrt(lenSqr)
return (a[0] * ool, a[1] * ool, a[2] * ool)
else:
return (0,0,0)
def loadLzs(name):
# read compressed data
f = open(name, "rb")
buf = f.read()
f.close()
(size,) = struct.unpack_from("<I", buf, 0)
if size + 4 != len(buf):
del buf
return None
ibuf = array.array("B", buf)
obuf = array.array("B")
# decompress;
iofs = 4
cmd = 0
bit = 0
while iofs < len(ibuf):
if bit == 0:
cmd = ibuf[iofs]
bit = 8
iofs += 1
if cmd & 1:
obuf.append(ibuf[iofs])
iofs += 1
else:
a = ibuf[iofs]
iofs += 1
b = ibuf[iofs]
iofs += 1
o = a | ((b & 0xF0) << 4)
l = (b & 0xF) + 3;
rofs = len(obuf) - ((len(obuf) - 18 - o) & 0xFFF)
for j in xrange(l):
if rofs < 0:
obuf.append(0)
else:
obuf.append(obuf[rofs])
rofs += 1
cmd >>= 1
bit -= 1
return obuf.tostring()
def loadFieldBin(name):
# read compressed data
f = open(name, "rb")
buf = f.read()
f.close()
(size,) = struct.unpack_from("<I", buf, 0)
# decompress
data = zlib.decompress(buf[8:], 16 | 15)
if len(data) != size:
del buf
del data
return None
return data
def readModel(data, num_bones, offset_skeleton, num_parts, offset_parts):
model = {}
skeleton = []
for i in xrange(num_bones):
(length, parent, has_mesh) = struct.unpack_from("<hbB", data, offset_skeleton + i * 4)
skeleton.append((length, parent, has_mesh != 0))
model["skeleton"] = skeleton
parts = []
for i in xrange(num_parts):
part = {}
(u1, bone_index, num_vertex, num_texcoord, num_quad_color_tex, num_tri_color_tex, num_quad_mono_tex, num_tri_mono_tex, num_tri_mono, num_quad_mono, num_tri_color, num_quad_color, u3, offset_poly, offset_texcoord, offset_flags, offset_control, u4, offset_vertex) = struct.unpack_from("<BBBBBBBBBBBBHHHHHHH", data, offset_parts + i * 0x20)
# print (u1, bone_index, num_vertex, num_texcoord, num_quad_color_tex, num_tri_color_tex, num_quad_mono_tex, num_tri_mono_tex, num_tri_mono, num_quad_mono, num_tri_color, num_quad_color, u3, offset_poly, offset_texcoord, offset_flags, offset_control, u4, offset_vertex)
#print "offset_vertex %x %x %x" % (offset_vertex, num_vertex, offset_vertex + 4 + num_vertex * 8)
#print "offset_texcoord %x %x %x" % (offset_vertex + offset_texcoord, num_texcoord, offset_vertex + offset_texcoord + num_texcoord * 2)
part["bone_index"] = bone_index
# print (u1, u3, u4)
vertices = []
for j in xrange(num_vertex):
(x, y, z) = struct.unpack_from("<hhh", data, offset_vertex + 4 + j * 8)
vertices.append((x,y,z))
part["vertices"] = vertices
texcoords = []
for j in xrange(num_texcoord):
(u, v) = struct.unpack_from("<BB", data, offset_vertex + offset_texcoord + j * 2)
texcoords.append((u,v))
part["texcoords"] = texcoords
cur_poly = offset_vertex + offset_poly
cur_flags = offset_vertex + offset_flags
cur_control = offset_vertex + offset_control
# print "u4 : %4.4x" % (offset_vertex + u4)
# print "cur_poly : %4.4x" % (cur_poly)
# print "cur_flags : %4.4x" % (cur_flags)
# print "cur_control : %4.4x" % (cur_control)
quad_color_tex = []
for j in xrange(num_quad_color_tex):
control = struct.unpack_from("<B", data, cur_control)
cur_control += 1
(av, bv, cv, dv, ar, ag, ab, an, br, bg, bb, bn, cr, cg, cb, cn, dr, dg, db, dn, at, bt, ct, dt) = struct.unpack_from("<BBBBBBBBBBBBBBBBBBBBBBBB", data, cur_poly)
poly = ((av, at, (ar, ag, ab), an), (bv, bt, (br, bg, bb), bn), (dv, dt, (dr, dg, db), dn), (cv, ct, (cr, cg, cb), cn))
# print "quad_color_tex %i %i %i %i / %i %i %i %i" % (av, bv, cv, dv, an, bn, cn, dn)
quad_color_tex.append(poly)
cur_poly += 0x18
part["quad_color_tex"] = quad_color_tex
tri_color_tex = []
for j in xrange(num_tri_color_tex):
control = struct.unpack_from("<B", data, cur_control)
cur_control += 1
(av, bv, cv, xv, ar, ag, ab, an, br, bg, bb, bn, cr, cg, cb, cn, at, bt, ct, xt) = struct.unpack_from("<BBBBBBBBBBBBBBBBBBBB", data, cur_poly)
poly = ((av, at, (ar, ag, ab), an), (bv, bt, (br, bg, bb), bn), (cv, ct, (cr, cg, cb), cn), xv, xt)
# print "tri_color_tex %i %i %i / %x %x %x" % (av, bv, cv, an, bn, cn)
tri_color_tex.append(poly)
cur_poly += 0x14
part["tri_color_tex"] = tri_color_tex
quad_mono_tex = []
for j in xrange(num_quad_mono_tex):
control = struct.unpack_from("<B", data, cur_control)
cur_control += 1
(av, bv, cv, dv, r, g, b, n, at, bt, ct, dt) = struct.unpack_from("<BBBBBBBBBBBB", data, cur_poly)
# print "quad_mono_tex", x
poly = ((av, at), (bv, bt), (dv, dt), (cv, ct), (r, g, b), n)
quad_mono_tex.append(poly)
cur_poly += 0x0C
part["quad_mono_tex"] = quad_mono_tex
tri_mono_tex = []
for j in xrange(num_tri_mono_tex):
control = struct.unpack_from("<B", data, cur_control)
cur_control += 1
(av, bv, cv, xv, r, g, b, n, at, bt, ct, xt) = struct.unpack_from("<BBBBBBBBBBBB", data, cur_poly)
poly = ((av, at), (bv, bt), (cv, ct), (r, g, b), n, xv, xt)
# print "tri_mono_tex", x, xv, xt
tri_mono_tex.append(poly)
cur_poly += 0x0C
part["tri_mono_tex"] = tri_mono_tex
tri_mono = []
for j in xrange(num_tri_mono):
(av, bv, cv, xv, r, g, b, n) = struct.unpack_from("<BBBBBBBB", data, cur_poly)
poly = ((av,), (bv,), (cv,), (r, g, b), n, xv)
# print "tri_mono", x, xv
tri_mono.append(poly)
cur_poly += 8
part["tri_mono"] = tri_mono
quad_mono = []
for j in xrange(num_quad_mono):
(av, bv, cv, dv, r, g, b, n) = struct.unpack_from("<BBBBBBBB", data, cur_poly)
poly = ((av,), (bv,), (dv,), (cv,), (r, g, b), n)
# print "quad_mono", x
quad_mono.append(poly)
cur_poly += 8
part["quad_mono"] = quad_mono
tri_color = []
for j in xrange(num_tri_color):
(av, bv, cv, xv, ar, ag, ab, an, br, bg, bb, bn, cr, cg, cb, cn) = struct.unpack_from("<BBBBBBBBBBBBBBBB", data, cur_poly)
poly = ((av,(ar, ag, ab), an), (bv, (br, bg, bb), bn), (cv, (cr, cg, cb), cn), xv)
# print "tri_color %i %i %i / %i %i %i %i / %i" % (av, bv, cv, an, bn, cn, xv, len(vertices))
tri_color.append(poly)
cur_poly += 0x10
part["tri_color"] = tri_color
quad_color = []
for j in xrange(num_quad_color):
(av, bv, cv, dv, ar, ag, ab, an, br, bg, bb, bn, cr, cg, cb, cn, dr, dg, db, dn) = struct.unpack_from("<BBBBBBBBBBBBBBBBBBBB", data, cur_poly)
poly = ((av,(ar, ag, ab), an), (bv, (br, bg, bb), bn), (dv, (dr, dg, db), dn), (cv, (cr, cg, cb), cn))
# print "quad_color %i %i %i %i / %x %x %x %x" % (av, bv, cv, dv, an, bn, cn, dn)
quad_color.append(poly)
cur_poly += 0x14
part["quad_color"] = quad_color
# print "end_poly : %4.4x" % (cur_poly)
# print "end_flags : %4.4x" % (cur_flags)
# print "end_control : %4.4x" % (cur_control)
parts.append(part)
model["parts"] = parts
return model
def readAnimations(data, num_animations, offset_animations):
animations = []
for i in xrange(num_animations):
(num_frames, num_channel, num_frames_translation, num_static_translation, num_frames_rotation, offset_frames_translation, offset_static_translation, offset_frames_rotation, offset_data) = struct.unpack_from("<HBBBBHHHI", data, offset_animations + i * 0x10)
offset_data &= 0x7FFFFFFF
# print "animation:", (offset_animations + i * 0x10, num_frames, num_channel, num_frames_translation, num_static_translation, num_frames_rotation, offset_frames_translation, offset_static_translation, offset_frames_rotation, offset_data)
animation = []
for k in xrange(num_channel):
(flag, rx, ry, rz, tx, ty, tz) = struct.unpack_from("<BBBBBBB", data, offset_data + 0x04 + k * 8)
channel = []
for j in xrange(num_frames):
# rotation
if flag & 0x01:
rotation_x = 360.0 * float(ord(data[offset_data + offset_frames_rotation + rx * num_frames + j])) / 255.0
else:
rotation_x = 360.0 * rx / 255.0
if flag & 0x02:
rotation_y = 360.0 * float(ord(data[offset_data + offset_frames_rotation + ry * num_frames + j])) / 255.0
else:
rotation_y = 360.0 * ry / 255.0
if flag & 0x04:
rotation_z = 360.0 * float(ord(data[offset_data + offset_frames_rotation + rz * num_frames + j])) / 255.0
else:
rotation_z = 360.0 * rz / 255.0
# translation
if flag & 0x10:
(translation_x,) = struct.unpack_from("<h", data, offset_data + offset_frames_translation + tx * num_frames * 2 + j * 2)
elif tx != 0xFF:
(translation_x,) = struct.unpack_from("<h", data, offset_data + offset_static_translation + tx * 2)
else:
translation_x = 0
if flag & 0x20:
(translation_y,) = struct.unpack_from("<h", data, offset_data + offset_frames_translation + ty * num_frames * 2 + j * 2);
elif ty != 0xFF:
(translation_y,) = struct.unpack_from("<h", data, offset_data + offset_static_translation + ty * 2)
else:
translation_y = 0
if flag & 0x40:
(translation_z,) = struct.unpack_from("<h", data, offset_data + offset_frames_translation + tz * num_frames * 2 + j * 2)
elif tz != 0xFF:
(translation_z,) = struct.unpack_from("<h", data, offset_data + offset_static_translation + tz * 2)
else:
translation_z = 0
channel.append(((translation_x, translation_y, translation_z), (rotation_x, rotation_y, rotation_z)))
animation.append(channel)
animations.append(animation)
return animations
def loadBcx(name):
data = loadLzs(name)
(size, header_offset) = struct.unpack_from("<II", data, 0)
assert(size == len(data))
(u1, num_bones, num_parts, num_animations, u2, u3, u4, offset_skeleton) = struct.unpack_from("<HBBB19sHHI", data, header_offset)
offset_skeleton &= 0x7FFFFFFF
offset_parts = offset_skeleton + num_bones * 4
offset_animations = offset_parts + num_parts * 32
# print "header", (header_offset, u1, num_bones, num_parts, num_animations, u2, u3, u4, offset_skeleton, offset_parts, offset_animations)
object = {}
object["model"] = readModel(data, num_bones, offset_skeleton, num_parts, offset_parts)
object["animations"] = readAnimations(data, num_animations, offset_animations)
return object
def loadBsx(name):
# get compressed file
data = loadLzs(name)
(size, header_offset) = struct.unpack_from("<II", data, 0)
assert(size == len(data))
# decode individual objects
object_list = []
(u1, num_models) = struct.unpack_from("<II", data, header_offset)
for i in xrange(num_models):
model_offset = header_offset + 0x10 + i * 0x30
(model_id,u2,u3,offset_skeleton,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15,u16,u17,u18,u19,u20,num_bones,u22,u23,u24,u25,u26,u27,u28,u29,u30,u31,u32,num_parts,u34,u35,u36,u37,u38,u39,u40,u41,u42,u43,u44,num_animations) = struct.unpack_from("<HBBHBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", data, model_offset)
# print (model_id,u2,u3,offset_skeleton,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15,u16,u17,u18,u19,u20,num_bones,u22,u23,u24,u25,u26,u27,u28,u29,u30,u31,u32,num_parts,u34,u35,u36,u37,u38,u39,u40,u41,u42,u43,u44,num_animations)
offset_skeleton += model_offset
offset_parts = offset_skeleton + num_bones * 4
offset_animations = offset_parts + num_parts * 32
if model_id == 0x0100:
object = loadBcx(bcxMap["cloud"])
elif model_id == 0x0302:
object = loadBcx(bcxMap["ballet"])
elif model_id == 0x0403:
object = loadBcx(bcxMap["tifa"])
elif model_id == 0x0605:
object = loadBcx(bcxMap["cid"])
elif model_id == 0x0706:
object = loadBcx(bcxMap["yufi"])
elif num_parts > 0:
object = {}
object["model"] = readModel(data, num_bones, offset_skeleton, num_parts, offset_parts)
object["animations"] = []
else:
print "unknown model id: %x" % model_id
object["animations"].extend(readAnimations(data, num_animations, offset_animations))
object_list.append(object)
return object_list
class OpenGLObject:
def __init__(self, object):
self.object = object
self.time = 0
self.animation = 0
self.draw_normals = False
self.draw_skeleton = True
self.part_map = {}
model = object["model"]
for idx, part in enumerate(model["parts"]):
self.part_map[part["bone_index"]] = idx
def nextAnim(self):
self.animation = (self.animation + 1) % len(self.object["animations"])
self.time = 0
def drawPart(self, part):
glEnable(GL_TEXTURE_2D)
glBegin(GL_TRIANGLES)
for tri in part["tri_color_tex"]:
for i in xrange(3):
pos = part["vertices"][tri[i][0]]
texcoord = part["texcoords"][tri[i][1]]
col = tri[i][2]
glTexCoord2i(texcoord[0], texcoord[1])
glColor3ub(col[0], col[1], col[2])
glVertex3i(pos[0], pos[1], pos[2])
for tri in part["tri_mono_tex"]:
col = tri[3]
glColor3ub(col[0], col[1], col[2])
for i in xrange(3):
pos = part["vertices"][tri[i][0]]
texcoord = part["texcoords"][tri[i][1]]
glTexCoord2i(texcoord[0], texcoord[1])
glColor3ub(col[0], col[1], col[2])
glVertex3i(pos[0], pos[1], pos[2])
glEnd()
glBegin(GL_QUADS)
for quad in part["quad_color_tex"]:
for i in xrange(4):
pos = part["vertices"][quad[i][0]]
texcoord = part["texcoords"][quad[i][1]]
col = quad[i][2]
glTexCoord2i(texcoord[0], texcoord[1])
glColor3ub(col[0], col[1], col[2])
glVertex3i(pos[0], pos[1], pos[2])
for quad in part["quad_mono_tex"]:
col = quad[4]
glColor3ub(col[0], col[1], col[2])
for i in xrange(4):
pos = part["vertices"][quad[i][0]]
texcoord = part["texcoords"][quad[i][1]]
glTexCoord2i(texcoord[0], texcoord[1])
glVertex3i(pos[0], pos[1], pos[2])
glEnd()
glDisable(GL_TEXTURE_2D)
glBegin(GL_TRIANGLES)
for tri in part["tri_color"]:
for i in xrange(3):
pos = part["vertices"][tri[i][0]]
col = tri[i][1]
glColor3ub(col[0], col[1], col[2])
glVertex3i(pos[0], pos[1], pos[2])
for tri in part["tri_mono"]:
col = tri[3]
glColor3ub(col[0], col[1], col[2])
for i in xrange(3):
pos = part["vertices"][tri[i][0]]
glVertex3i(pos[0], pos[1], pos[2])
glEnd()
glBegin(GL_QUADS)
for quad in part["quad_color"]:
for i in xrange(4):
pos = part["vertices"][quad[i][0]]
col = quad[i][1]
glColor3ub(col[0], col[1], col[2])
glVertex3i(pos[0], pos[1], pos[2])
for quad in part["quad_mono"]:
col = quad[4]
glColor3ub(col[0], col[1], col[2])
for i in xrange(4):
pos = part["vertices"][quad[i][0]]
glVertex3i(pos[0], pos[1], pos[2])
glEnd()
# draw normals (I'm not sure about the mono primitives. Maybe they're not lit at all?)
if self.draw_normals:
global normals
glBegin(GL_LINES)
glColor3f(1,1,0)
for quad in part["quad_mono_tex"]:
for i in xrange(4):
pos = part["vertices"][quad[i][0]]
nrm = normals[quad[5]]
glVertex3i(pos[0], pos[1], pos[2])
glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
for tri in part["tri_mono_tex"]:
for i in xrange(3):
pos = part["vertices"][tri[i][0]]
nrm = normals[tri[4]]
glVertex3i(pos[0], pos[1], pos[2])
glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
for quad in part["quad_mono"]:
for i in xrange(4):
pos = part["vertices"][quad[i][0]]
nrm = normals[quad[5]]
glVertex3i(pos[0], pos[1], pos[2])
glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
for tri in part["tri_mono"]:
for i in xrange(3):
pos = part["vertices"][tri[i][0]]
nrm = normals[tri[4]]
glVertex3i(pos[0], pos[1], pos[2])
glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
for quad in part["quad_color_tex"]:
for i in xrange(4):
pos = part["vertices"][quad[i][0]]
nrm = normals[quad[i][3]]
glVertex3i(pos[0], pos[1], pos[2])
glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
for tri in part["tri_color_tex"]:
for i in xrange(3):
pos = part["vertices"][tri[i][0]]
nrm = normals[tri[i][3]]
glVertex3i(pos[0], pos[1], pos[2])
glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
for quad in part["quad_color"]:
for i in xrange(4):
pos = part["vertices"][quad[i][0]]
nrm = normals[quad[i][2]]
glVertex3i(pos[0], pos[1], pos[2])
glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
for tri in part["tri_color"]:
for i in xrange(3):
pos = part["vertices"][tri[i][0]]
nrm = normals[tri[i][2]]
glVertex3i(pos[0], pos[1], pos[2])
glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
glEnd()
def draw(self):
glMatrixMode(GL_MODELVIEW)
glRotatef(180, 1, 0, 0)
model = self.object["model"]
animation = self.object["animations"][self.animation]
skeleton = model["skeleton"]
self.time = (self.time + 1) % len(animation[0])
parent = [-1]
for idx, bone in enumerate(model["skeleton"]):
((translation_x, translation_y, translation_z), (rotation_x, rotation_y, rotation_z)) = animation[idx][self.time]
while parent[-1] != bone[1]:
parent.pop()
glPopMatrix()
parent.append(idx)
glPushMatrix()
if self.draw_skeleton:
glBegin(GL_LINES)
glColor3f(1.0, 0.0, 1.0)
glVertex3i(0, 0, 0)
glVertex3i(0, 0, bone[0])
glColor3f(1.0, 0.0, 0.0)
glVertex3i(0, 0, 0)
glVertex3i(50, 0, 0)
glColor3f(0.0, 1.0, 0.0)
glVertex3i(0, 0, 0)
glVertex3i(0, 50, 0)
glColor3f(0.0, 0.0, 1.0)
glVertex3i(0, 0, 0)
glVertex3i(0, 0, 50)
glEnd()
glTranslatef(0, 0, bone[0])
glRotatef(rotation_y, 0, 1, 0)
glRotatef(rotation_x, 1, 0, 0)
glRotatef(rotation_z, 0, 0, 1)
glTranslatef(translation_x, translation_y, translation_z)
if bone[2]:
self.drawPart(model["parts"][self.part_map[idx]])
while parent[-1] != -1:
parent.pop()
glPopMatrix()
def main(*argv):
pygame.init()
screen = pygame.display.set_mode((640, 480), HWSURFACE|DOUBLEBUF|OPENGL)
glViewport(0, 0, 640, 480)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
kFOVy = 0.57735
kZNear = 1000.0
kZFar = 3000.0
aspect = (640.0 / 480.0) * kZNear * kFOVy
glFrustum(-aspect, aspect, -(kZNear * kFOVy), (kZNear * kFOVy), kZNear, kZFar)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LEQUAL)
glDisable(GL_CULL_FACE)
glDisable(GL_LIGHTING)
glDisable(GL_BLEND)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
filePath = os.path.abspath(argv[0])
# get list of BCX, which should be in the same dir as the .bsx
# (This is done this way to support operating sytems with a case-sensitive file systems.)
global bcxMap
bcxMap = {}
fieldInfo = {}
dir = os.path.dirname(filePath)
for path in os.listdir(dir):
file = os.path.splitext(path)
if file[1].lower() == ".bcx":
bcxMap[file[0].lower()] = os.path.join(dir, path)
elif path.lower() == "field.bin":
fieldInfo["field.bin"] = os.path.join(dir, path)
elif path.lower() == "field.tdb":
fieldInfo["field.tdb"] = os.path.join(dir, path)
# get normals
fieldBin = loadFieldBin(fieldInfo["field.bin"])
global normals
normals = []
for i in xrange(240):
n = struct.unpack_from("<hhh", fieldBin, 0x3f520 + i * 8)
normals.append(vecNormal(n))
# load BCX or BSX file
if os.path.splitext(os.path.basename(filePath))[1].lower() == ".bcx":
data = [loadBcx(filePath)]
else:
data = loadBsx(filePath)
clock = pygame.time.Clock()
key_up = key_down = key_left = key_right = False
rot_x = 0.0
rot_y = 0.0
cur_model = 0
object = OpenGLObject(data[cur_model])
wireframe = True
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
elif event.type == KEYDOWN:
if event.key == pygame.K_UP:
key_up = True
elif event.key == pygame.K_DOWN:
key_down = True
elif event.key == pygame.K_LEFT:
key_left = True
elif event.key == pygame.K_RIGHT:
key_right = True
elif event.key == pygame.K_ESCAPE:
exit()
elif event.key == pygame.K_a:
object.nextAnim()
elif event.key == pygame.K_n:
object.draw_normals = not object.draw_normals
elif event.key == pygame.K_s:
object.draw_skeleton = not object.draw_skeleton
elif event.key == pygame.K_m:
cur_model = (cur_model + 1) % len(data)
del object
object = OpenGLObject(data[cur_model])
elif event.key == pygame.K_w:
wireframe = not wireframe
if wireframe:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
else:
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
elif event.type == KEYUP:
if event.key == pygame.K_UP:
key_up = False
elif event.key == pygame.K_DOWN:
key_down = False
elif event.key == pygame.K_LEFT:
key_left = False
elif event.key == pygame.K_RIGHT:
key_right = False
if key_up:
rot_x += 1.0
elif key_down:
rot_x -= 1.0
if key_left:
rot_y += 1.0
elif key_right:
rot_y -= 1.0
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glFrustum(-aspect, aspect, -(kZNear * kFOVy), (kZNear * kFOVy), kZNear, 100000.0)
glTranslatef(0, 0, -2000)
glRotatef(rot_y * 360.0 / 256.0, 0.0, 1.0, 0.0)
glRotatef(rot_x * 360.0 / 256.0, 1.0, 0.0, 0.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
object.draw()
clock.tick(60)
pygame.display.flip()
if __name__ == "__main__":
main(*sys.argv[1:])
You'll need pygame and the python-opengl bindings. Run with a BCX or BSX file as argument, w toggles wireframe, n toggles normals, s toggles the skeleton, m cycles through models and a cycles through animations.
It should be quite easy to take it from here and make an importer for Blender, but if you're after efficient rendering or animation then Akari's solution in the q-gears source is more efficient.