mirror of
https://github.com/quantum5/punyverse.git
synced 2025-04-24 13:11:57 -04:00
Converted models to VBO.
Note that this actually makes things slower... at least until we move to shaders.
This commit is contained in:
parent
8b070726c3
commit
a2620fc3bc
|
@ -6,7 +6,7 @@ from pyglet.gl import *
|
||||||
from six.moves import range
|
from six.moves import range
|
||||||
|
|
||||||
from punyverse.glgeom import compile, glMatrix, glRestore, belt, Sphere, Disk
|
from punyverse.glgeom import compile, glMatrix, glRestore, belt, Sphere, Disk
|
||||||
from punyverse.model import model_list, load_model
|
from punyverse.model import load_model, WavefrontVBO
|
||||||
from punyverse.orbit import KeplerOrbit
|
from punyverse.orbit import KeplerOrbit
|
||||||
from punyverse.texture import get_best_texture, load_clouds
|
from punyverse.texture import get_best_texture, load_clouds
|
||||||
|
|
||||||
|
@ -35,9 +35,9 @@ class Entity(object):
|
||||||
|
|
||||||
|
|
||||||
class Asteroid(Entity):
|
class Asteroid(Entity):
|
||||||
def __init__(self, asteroid_id, location, direction):
|
def __init__(self, model, location, direction):
|
||||||
super(Asteroid, self).__init__('Asteroid', location, direction=direction)
|
super(Asteroid, self).__init__('Asteroid', location, direction=direction)
|
||||||
self.asteroid_id = asteroid_id
|
self.model = model
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
super(Asteroid, self).update()
|
super(Asteroid, self).update()
|
||||||
|
@ -47,8 +47,8 @@ class Asteroid(Entity):
|
||||||
self.rotation = rx + 1, ry + 1, rz + 1
|
self.rotation = rx + 1, ry + 1, rz + 1
|
||||||
|
|
||||||
def draw(self, options):
|
def draw(self, options):
|
||||||
with glMatrix(self.location, self.rotation), glRestore(GL_CURRENT_BIT):
|
with glMatrix(self.location, self.rotation):
|
||||||
glCallList(self.asteroid_id)
|
self.model.draw()
|
||||||
|
|
||||||
|
|
||||||
class AsteroidManager(object):
|
class AsteroidManager(object):
|
||||||
|
@ -60,7 +60,7 @@ class AsteroidManager(object):
|
||||||
__nonzero__ = __bool__
|
__nonzero__ = __bool__
|
||||||
|
|
||||||
def load(self, file):
|
def load(self, file):
|
||||||
self.asteroids.append(model_list(load_model(file), 5, 5, 5, (0, 0, 0)))
|
self.asteroids.append(WavefrontVBO(load_model(file), 5, 5, 5, (0, 0, 0)))
|
||||||
|
|
||||||
def new(self, location, direction):
|
def new(self, location, direction):
|
||||||
return Asteroid(random.choice(self.asteroids), location, direction)
|
return Asteroid(random.choice(self.asteroids), location, direction)
|
||||||
|
@ -85,8 +85,8 @@ class Belt(Entity):
|
||||||
if not isinstance(models, list):
|
if not isinstance(models, list):
|
||||||
models = [models]
|
models = [models]
|
||||||
|
|
||||||
objects = [model_list(load_model(model), info.get('sx', scale), info.get('sy', scale),
|
objects = [WavefrontVBO(load_model(model), info.get('sx', scale), info.get('sy', scale),
|
||||||
info.get('sz', scale), (0, 0, 0)) for model in models]
|
info.get('sz', scale), (0, 0, 0)) for model in models]
|
||||||
|
|
||||||
self.belt_id = compile(belt, radius, cross, objects, count)
|
self.belt_id = compile(belt, radius, cross, objects, count)
|
||||||
self.rotation_angle = 360.0 / rotation if rotation else 0
|
self.rotation_angle = 360.0 / rotation if rotation else 0
|
||||||
|
@ -373,9 +373,9 @@ class ModelBody(Body):
|
||||||
super(ModelBody, self).__init__(name, world, info, parent)
|
super(ModelBody, self).__init__(name, world, info, parent)
|
||||||
|
|
||||||
scale = info.get('scale', 1)
|
scale = info.get('scale', 1)
|
||||||
self.object_id = model_list(load_model(info['model']), info.get('sx', scale), info.get('sy', scale),
|
self.vbo = WavefrontVBO(load_model(info['model']), info.get('sx', scale), info.get('sy', scale),
|
||||||
info.get('sz', scale), (0, 0, 0))
|
info.get('sz', scale), (0, 0, 0))
|
||||||
|
|
||||||
def _draw(self, options):
|
def _draw(self, options):
|
||||||
with glMatrix(self.location, self.rotation), glRestore(GL_CURRENT_BIT):
|
with glMatrix(self.location, self.rotation):
|
||||||
glCallList(self.object_id)
|
self.vbo.draw()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from array import array
|
from array import array
|
||||||
from ctypes import c_int, c_float, byref, cast, POINTER, c_uint
|
from ctypes import c_int, c_float, byref, cast, POINTER, c_uint, c_short, c_ushort
|
||||||
from math import *
|
from math import *
|
||||||
from random import random, gauss, choice
|
from random import random, gauss, choice
|
||||||
|
|
||||||
|
@ -84,6 +84,8 @@ def array_to_ctypes(arr):
|
||||||
'f': c_float,
|
'f': c_float,
|
||||||
'i': c_int,
|
'i': c_int,
|
||||||
'I': c_uint,
|
'I': c_uint,
|
||||||
|
'h': c_short,
|
||||||
|
'H': c_ushort,
|
||||||
}[arr.typecode]))
|
}[arr.typecode]))
|
||||||
|
|
||||||
|
|
||||||
|
@ -219,7 +221,7 @@ def belt(radius, cross, object, count):
|
||||||
if scale < 0:
|
if scale < 0:
|
||||||
scale = 1
|
scale = 1
|
||||||
glScalef(scale, scale, scale)
|
glScalef(scale, scale, scale)
|
||||||
glCallList(choice(object))
|
choice(object).draw()
|
||||||
|
|
||||||
|
|
||||||
def progress_bar(x, y, width, height, filled):
|
def progress_bar(x, y, width, height, filled):
|
||||||
|
|
|
@ -2,13 +2,14 @@ import bz2
|
||||||
import gzip
|
import gzip
|
||||||
import os
|
import os
|
||||||
import zipfile
|
import zipfile
|
||||||
from uuid import uuid4
|
from collections import defaultdict
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from pyglet.gl import *
|
from pyglet.gl import *
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from six.moves import range, zip
|
from six.moves import range, zip
|
||||||
|
|
||||||
|
from punyverse.glgeom import array_to_gl_buffer, glRestoreClient, glMatrix, glRestore
|
||||||
from punyverse.texture import load_texture
|
from punyverse.texture import load_texture
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,15 +47,11 @@ class Material(object):
|
||||||
|
|
||||||
|
|
||||||
class Group(object):
|
class Group(object):
|
||||||
__slots__ = ('name', 'material', 'faces')
|
__slots__ = ('material', 'faces')
|
||||||
|
|
||||||
def __init__(self, name=None):
|
def __init__(self, material=None, faces=None):
|
||||||
if name is None:
|
self.material = material
|
||||||
self.name = str(uuid4())
|
self.faces = faces or []
|
||||||
else:
|
|
||||||
self.name = name
|
|
||||||
self.material = None
|
|
||||||
self.faces = []
|
|
||||||
|
|
||||||
|
|
||||||
class WavefrontObject(object):
|
class WavefrontObject(object):
|
||||||
|
@ -98,15 +95,13 @@ class WavefrontObject(object):
|
||||||
|
|
||||||
def texture(self, words):
|
def texture(self, words):
|
||||||
l = len(words)
|
l = len(words)
|
||||||
x, y, z = 0, 0, 0
|
u, v = 0, 0
|
||||||
if l >= 2:
|
if l >= 2:
|
||||||
x = float(words[1])
|
u = float(words[1])
|
||||||
if l >= 3:
|
if l >= 3:
|
||||||
# OBJ origin is at upper left, OpenGL origin is at lower left
|
# OBJ origin is at upper left, OpenGL origin is at lower left
|
||||||
y = 1 - float(words[2])
|
v = 1 - float(words[2])
|
||||||
if l >= 4:
|
self.textures.append((u, v))
|
||||||
z = float(words[3])
|
|
||||||
self.textures.append((x, y, z))
|
|
||||||
|
|
||||||
def face(self, words):
|
def face(self, words):
|
||||||
l = len(words)
|
l = len(words)
|
||||||
|
@ -153,7 +148,7 @@ class WavefrontObject(object):
|
||||||
print("Warning: no group")
|
print("Warning: no group")
|
||||||
|
|
||||||
def group(self, words):
|
def group(self, words):
|
||||||
group = Group(words[1].decode('utf-8'))
|
group = Group()
|
||||||
self.groups.append(group)
|
self.groups.append(group)
|
||||||
self.current_group = group
|
self.current_group = group
|
||||||
|
|
||||||
|
@ -201,78 +196,128 @@ def load_model(path):
|
||||||
return WavefrontObject(path)
|
return WavefrontObject(path)
|
||||||
|
|
||||||
|
|
||||||
def model_list(model, sx=1, sy=1, sz=1, rotation=(0, 0, 0)):
|
class ModelVBO(object):
|
||||||
for m, text in six.iteritems(model.materials):
|
__slots__ = ('has_normal', 'has_texture', 'data_buf', 'index_buf', 'offset_type', 'vertex_count')
|
||||||
if text.texture:
|
|
||||||
load_texture(os.path.join(model.root, text.texture))
|
|
||||||
|
|
||||||
display = glGenLists(1)
|
def draw(self):
|
||||||
|
with glRestoreClient(GL_CLIENT_VERTEX_ARRAY_BIT):
|
||||||
|
stride = (3 + self.has_normal * 3 + self.has_texture * 2) * 4
|
||||||
|
|
||||||
glNewList(display, GL_COMPILE)
|
glBindBuffer(GL_ARRAY_BUFFER, self.data_buf)
|
||||||
glPushMatrix()
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.index_buf)
|
||||||
glPushAttrib(GL_CURRENT_BIT)
|
|
||||||
|
|
||||||
pitch, yaw, roll = rotation
|
glEnableClientState(GL_VERTEX_ARRAY)
|
||||||
glPushAttrib(GL_TRANSFORM_BIT)
|
glVertexPointer(3, GL_FLOAT, stride, 0)
|
||||||
glRotatef(pitch, 1, 0, 0)
|
if self.has_normal:
|
||||||
glRotatef(yaw, 0, 1, 0)
|
glEnableClientState(GL_NORMAL_ARRAY)
|
||||||
glRotatef(roll, 0, 0, 1)
|
glNormalPointer(GL_FLOAT, stride, 3 * 4)
|
||||||
glPopAttrib()
|
if self.has_texture:
|
||||||
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
|
||||||
|
glTexCoordPointer(3, GL_FLOAT, stride, (6 if self.has_normal else 3) * 4)
|
||||||
|
|
||||||
vertices = model.vertices
|
glDrawElements(GL_TRIANGLES, self.vertex_count, self.offset_type, 0)
|
||||||
textures = model.textures
|
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||||
normals = model.normals
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
|
||||||
|
|
||||||
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
|
class WavefrontVBO(object):
|
||||||
|
def __init__(self, model, sx=1, sy=1, sz=1, rotation=(0, 0, 0)):
|
||||||
|
self._tex_cache = {}
|
||||||
|
self.vbos = []
|
||||||
|
self.rotation = rotation
|
||||||
|
self.scale = (sx, sy, sz)
|
||||||
|
|
||||||
if tex_id:
|
for m, material in six.iteritems(model.materials):
|
||||||
glEnable(GL_TEXTURE_2D)
|
if material.texture and material.texture not in self._tex_cache:
|
||||||
glBindTexture(GL_TEXTURE_2D, tex_id)
|
self._tex_cache[material.texture] = load_texture(os.path.join(model.root, material.texture))
|
||||||
else:
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0)
|
|
||||||
glDisable(GL_TEXTURE_2D)
|
|
||||||
|
|
||||||
if material:
|
vertices = model.vertices
|
||||||
fv4 = GLfloat * 4
|
textures = model.textures
|
||||||
|
normals = model.normals
|
||||||
|
|
||||||
if material.Ka:
|
for group in self.merge_groups(model):
|
||||||
kx, ky, kz = material.Ka
|
self.vbos.append((group.material, self.process_group(group, vertices, normals, textures)))
|
||||||
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)
|
|
||||||
|
|
||||||
def point(f, n, max_texture=len(textures)):
|
def draw(self, fv4=GLfloat * 4):
|
||||||
normal = f.norms[n]
|
with glMatrix(rotation=self.rotation), glRestore(GL_TEXTURE_BIT | GL_ENABLE_BIT):
|
||||||
if normal is not None:
|
for mat, vbo in self.vbos:
|
||||||
glNormal3f(*normals[normal])
|
tex_id = self._tex_cache[mat.texture] if mat and mat.texture else 0
|
||||||
texture = f.texs[n]
|
|
||||||
if texture is not None and texture < max_texture:
|
|
||||||
glTexCoord2f(*textures[texture][:2])
|
|
||||||
x, y, z = vertices[f.verts[n]]
|
|
||||||
glVertex3f(x * sx, y * sy, z * sz)
|
|
||||||
|
|
||||||
glBegin(GL_TRIANGLES)
|
if tex_id:
|
||||||
for f in g.faces:
|
glEnable(GL_TEXTURE_2D)
|
||||||
for a, b in zip(range(1, f.size), range(2, f.size)):
|
glBindTexture(GL_TEXTURE_2D, tex_id)
|
||||||
point(f, 0)
|
else:
|
||||||
point(f, a)
|
glBindTexture(GL_TEXTURE_2D, 0)
|
||||||
point(f, b)
|
glDisable(GL_TEXTURE_2D)
|
||||||
glEnd()
|
|
||||||
|
|
||||||
if tex_id:
|
if mat:
|
||||||
glBindTexture(GL_TEXTURE_2D, 0)
|
if mat.Ka:
|
||||||
glDisable(GL_TEXTURE_2D)
|
kx, ky, kz = mat.Ka
|
||||||
|
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, fv4(kx, ky, kz, 1))
|
||||||
|
if mat.Kd:
|
||||||
|
kx, ky, kz = mat.Kd
|
||||||
|
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fv4(kx, ky, kz, 1))
|
||||||
|
if mat.Ks:
|
||||||
|
kx, ky, kz = mat.Ks
|
||||||
|
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(kx, ky, kz, 1))
|
||||||
|
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, mat.shininess)
|
||||||
|
|
||||||
glPopAttrib()
|
vbo.draw()
|
||||||
glPopMatrix()
|
|
||||||
|
|
||||||
glEndList()
|
def merge_groups(self, model):
|
||||||
return display
|
by_mat = defaultdict(list)
|
||||||
|
for g in model.groups:
|
||||||
|
if g.faces:
|
||||||
|
by_mat[g.material].append(g)
|
||||||
|
|
||||||
|
groups = []
|
||||||
|
for mat, gs in six.iteritems(by_mat):
|
||||||
|
faces = []
|
||||||
|
for g in gs:
|
||||||
|
faces += g.faces
|
||||||
|
groups.append(Group(mat, faces))
|
||||||
|
return groups
|
||||||
|
|
||||||
|
def process_group(self, group, vertices, normals, textures):
|
||||||
|
sx, sy, sz = self.scale
|
||||||
|
max_texture = len(textures)
|
||||||
|
has_texture = bool(textures) and any(any(n is not None for n in f.texs) for f in group.faces)
|
||||||
|
has_normal = bool(normals) and any(any(n is not None for n in f.norms) for f in group.faces)
|
||||||
|
buffer = []
|
||||||
|
indices = []
|
||||||
|
offsets = {}
|
||||||
|
|
||||||
|
for f in group.faces:
|
||||||
|
verts = []
|
||||||
|
for v, n, t in zip(f.verts, f.norms, f.texs):
|
||||||
|
# Blender defines texture coordinates on faces even without textures.
|
||||||
|
if t is not None and t >= max_texture:
|
||||||
|
t = None
|
||||||
|
if (v, n, t) in offsets:
|
||||||
|
verts.append(offsets[v, n, t])
|
||||||
|
else:
|
||||||
|
index = len(offsets)
|
||||||
|
verts.append(index)
|
||||||
|
x, y, z = vertices[v]
|
||||||
|
item = [sx * x, sy * y, sz * z]
|
||||||
|
if has_normal:
|
||||||
|
item += [0, 0, 0] if n is None else list(normals[n])
|
||||||
|
if has_texture:
|
||||||
|
item += [0, 0] if t is None else list(textures[t])
|
||||||
|
offsets[v, n, t] = index
|
||||||
|
buffer += item
|
||||||
|
|
||||||
|
for a, b in zip(verts[1:], verts[2:]):
|
||||||
|
indices += [verts[0], a, b]
|
||||||
|
|
||||||
|
result = ModelVBO()
|
||||||
|
result.has_normal = has_normal
|
||||||
|
result.has_texture = has_texture
|
||||||
|
result.offset_type = GL_UNSIGNED_SHORT if len(offsets) < 65536 else GL_UNSIGNED_INT
|
||||||
|
result.data_buf = array_to_gl_buffer(buffer, 'f')
|
||||||
|
result.index_buf = array_to_gl_buffer(indices, {
|
||||||
|
GL_UNSIGNED_SHORT: 'H',
|
||||||
|
GL_UNSIGNED_INT: 'I',
|
||||||
|
}[result.offset_type])
|
||||||
|
result.vertex_count = len(indices)
|
||||||
|
return result
|
||||||
|
|
|
@ -291,7 +291,7 @@
|
||||||
"radius": "2.362 * AU",
|
"radius": "2.362 * AU",
|
||||||
"cross": 1000,
|
"cross": 1000,
|
||||||
"scale": 30,
|
"scale": 30,
|
||||||
"count": 1024,
|
"count": 256,
|
||||||
"rotation": 114536500
|
"rotation": 114536500
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue