Use VBOs for spheres.

This commit is contained in:
Quantum 2018-08-24 19:37:50 -04:00
parent 338b7fb66c
commit 825e2ecf6a
3 changed files with 101 additions and 57 deletions

View file

@ -5,7 +5,7 @@ from pyglet.gl import *
# noinspection PyUnresolvedReferences
from six.moves import range
from punyverse.glgeom import compile, sphere, flare, disk, glMatrix, glRestore, belt
from punyverse.glgeom import compile, flare, disk, glMatrix, glRestore, belt, Sphere
from punyverse.orbit import KeplerOrbit
from punyverse.texture import get_best_texture, load_clouds
@ -118,15 +118,20 @@ class Sky(Entity):
super(Sky, self).__init__('Sky', (0, 0, 0), (pitch, yaw, roll))
self.world = world
texture = get_best_texture(info['texture'])
self.texture = get_best_texture(info['texture'])
division = info.get('division', 30)
self.sky_id = compile(sphere, info.get('radius', 1000000), division, division, texture,
inside=True, lighting=False)
self.sphere = Sphere(info.get('radius', 1000000), division, division)
def draw(self, options):
cam = self.world.cam
with glMatrix((-cam.x, -cam.y, -cam.z), self.rotation), glRestore(GL_CURRENT_BIT):
glCallList(self.sky_id)
with glMatrix((-cam.x, -cam.y, -cam.z), self.rotation), glRestore(GL_TEXTURE_BIT | GL_ENABLE_BIT):
glEnable(GL_CULL_FACE)
glEnable(GL_TEXTURE_2D)
glDisable(GL_LIGHTING)
glCullFace(GL_FRONT)
glBindTexture(GL_TEXTURE_2D, self.texture)
self.sphere.draw()
class Body(Entity):
@ -258,11 +263,11 @@ class SphericalBody(Body):
division = info.get('division', max(min(int(self.radius / 8), 60), 10))
self.light_source = info.get('light_source', False)
texture = get_best_texture(info['texture'])
self.sphere_id = compile(sphere, self.radius, division, division, texture)
self.texture = get_best_texture(info['texture'])
self.sphere = Sphere(self.radius, division, division)
self.atmosphere_id = 0
self.cloudmap_id = 0
self.clouds = None
self.corona_id = 0
self.ring_id = 0
@ -273,8 +278,8 @@ class SphericalBody(Body):
cloud_texture = atmosphere_data.get('cloud_texture', None)
corona_texture = atmosphere_data.get('corona_texture', None)
if cloud_texture is not None:
cloud_texture = get_best_texture(cloud_texture, loader=load_clouds)
self.cloudmap_id = compile(sphere, self.radius + 2, division, division, cloud_texture, lighting=False)
self.cloud_texture = get_best_texture(cloud_texture, loader=load_clouds)
self.clouds = Sphere(self.radius + 2, division, division)
if corona_texture is not None:
corona = get_best_texture(corona_texture, clamp=True)
@ -301,11 +306,23 @@ class SphericalBody(Body):
self.ring_id = compile(disk, distance, distance + size, 30,
get_best_texture(info['ring'].get('texture', None), clamp=True))
def _draw_sphere(self):
with glMatrix(self.location, self.rotation), glRestore(GL_CURRENT_BIT | GL_ENABLE_BIT):
def _draw_sphere(self, fv4=GLfloat * 4):
with glMatrix(self.location, self.rotation), glRestore(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT):
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, self.texture)
if self.light_source:
glDisable(GL_LIGHTING)
glCallList(self.sphere_id)
else:
glDisable(GL_BLEND)
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(1, 1, 1, 0))
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 0))
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
self.sphere.draw()
def _draw_atmosphere(self, glMatrixBuffer=GLfloat * 16):
with glMatrix(self.location), glRestore(GL_ENABLE_BIT | GL_CURRENT_BIT):
@ -326,10 +343,16 @@ class SphericalBody(Body):
glCallList(self.corona_id)
def _draw_clouds(self):
with glMatrix(self.location, self.rotation), glRestore(GL_ENABLE_BIT | GL_CURRENT_BIT):
with glMatrix(self.location, self.rotation), glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT):
glEnable(GL_BLEND)
glEnable(GL_ALPHA_TEST)
glCallList(self.cloudmap_id)
glEnable(GL_CULL_FACE)
glDisable(GL_LIGHTING)
glEnable(GL_TEXTURE_2D)
glCullFace(GL_BACK)
glBindTexture(GL_TEXTURE_2D, self.cloud_texture)
self.clouds.draw()
def _draw_rings(self):
with glMatrix(self.location, self.ring_rotation), glRestore(GL_CURRENT_BIT):
@ -341,7 +364,7 @@ class SphericalBody(Body):
if options.atmosphere and (self.atmosphere_id or self.corona_id):
self._draw_atmosphere()
if options.cloud and self.cloudmap_id:
if options.cloud and self.clouds:
self._draw_clouds()
if self.ring_id:

View file

@ -1,3 +1,5 @@
from array import array
from ctypes import c_int, c_float, byref, cast, POINTER, c_uint
from math import *
from random import random, gauss, choice
@ -7,7 +9,7 @@ from six.moves import range
TWOPI = pi * 2
__all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'disk', 'sphere', 'colourball', 'belt',
__all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'disk', 'Sphere', 'belt',
'flare', 'glSection', 'glMatrix', 'glRestore', 'progress_bar']
@ -45,6 +47,17 @@ class glRestore(object):
glPopAttrib()
class glRestoreClient(object):
def __init__(self, flags):
self.flags = flags
def __enter__(self):
glPushClientAttrib(self.flags)
def __exit__(self, exc_type, exc_val, exc_tb):
glPopClientAttrib()
class glMatrix(object):
def __init__(self, location=None, rotation=None):
self.location = location
@ -169,49 +182,57 @@ def flare(rinner, router, res, prob, tex):
last_y = y
def sphere(r, lats, longs, tex, lighting=True, inside=False, fv4=GLfloat * 4):
with glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT):
sphere = gluNewQuadric()
gluQuadricDrawStyle(sphere, GLU_FILL)
gluQuadricTexture(sphere, True)
if lighting:
gluQuadricNormals(sphere, GLU_SMOOTH)
glEnable(GL_CULL_FACE)
glCullFace(GL_FRONT if inside else GL_BACK)
glEnable(GL_TEXTURE_2D)
if lighting:
glDisable(GL_BLEND)
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(1, 1, 1, 0))
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 0))
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
else:
glDisable(GL_LIGHTING)
glBindTexture(GL_TEXTURE_2D, tex)
gluSphere(sphere, r, lats, longs)
gluDeleteQuadric(sphere)
def array_to_ctypes(arr):
return cast(arr.buffer_info()[0], POINTER({
'f': c_float,
'i': c_int,
'I': c_uint,
}[arr.typecode]))
def colourball(r, lats, longs, colour, fv4=GLfloat * 4):
"""
Sphere function from the OpenGL red book.
"""
with glRestore(GL_ENABLE_BIT):
sphere = gluNewQuadric()
class Sphere(object):
def __init__(self, r, lats, longs):
tau = pi * 2
phi_div = tau / longs
theta_div = pi / lats
glDisable(GL_BLEND)
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(*colour))
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 1))
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
self.vertex_count = (lats + 1) * (longs + 1) * 2
buffer = self.vertex_count * 8 * [0]
index = 0
for i in range(longs + 1):
phi1, phi2 = i * phi_div, (i + 1) * phi_div
for j in range(lats + 1):
theta = j * theta_div
sine = sin(theta)
dz = cos(theta)
t = 1 - theta / pi
dx1 = sine * cos(phi2)
dy1 = sine * sin(phi2)
dx2 = sine * cos(phi1)
dy2 = sine * sin(phi1)
buffer[index:index + 16] = [r * dx1, r * dy1, r * dz, dx1, dy1, dz, phi2 / tau, t,
r * dx2, r * dy2, r * dz, dx2, dy2, dz, phi1 / tau, t]
index += 16
gluSphere(sphere, r, lats, longs)
vbo = c_uint()
glGenBuffers(1, byref(vbo))
self.vbo = vbo.value
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
buffer = array('f', buffer)
glBufferData(GL_ARRAY_BUFFER, buffer.itemsize * len(buffer), array_to_ctypes(buffer), GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glEnable(GL_BLEND)
gluDeleteQuadric(sphere)
def draw(self):
with glRestoreClient(GL_CLIENT_VERTEX_ARRAY_BIT):
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_NORMAL_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glVertexPointer(3, GL_FLOAT, 32, 0)
glNormalPointer(GL_FLOAT, 32, 3 * 4)
glTexCoordPointer(3, GL_FLOAT, 32, 6 * 4)
glDrawArrays(GL_TRIANGLE_STRIP, 0, self.vertex_count)
glBindBuffer(GL_ARRAY_BUFFER, 0)
def belt(radius, cross, object, count):

View file

@ -306,7 +306,7 @@
"division": 30,
"pitch": 90,
"yaw": 30,
"roll": 180
"roll": -90
},
"asteroids": ["asteroids/01.obj", "asteroids/02.obj", "asteroids/03.obj"],
"start": {