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 # noinspection PyUnresolvedReferences
from six.moves import range 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.orbit import KeplerOrbit
from punyverse.texture import get_best_texture, load_clouds 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)) super(Sky, self).__init__('Sky', (0, 0, 0), (pitch, yaw, roll))
self.world = world self.world = world
texture = get_best_texture(info['texture']) self.texture = get_best_texture(info['texture'])
division = info.get('division', 30) division = info.get('division', 30)
self.sky_id = compile(sphere, info.get('radius', 1000000), division, division, texture, self.sphere = Sphere(info.get('radius', 1000000), division, division)
inside=True, lighting=False)
def draw(self, options): def draw(self, options):
cam = self.world.cam cam = self.world.cam
with glMatrix((-cam.x, -cam.y, -cam.z), self.rotation), glRestore(GL_CURRENT_BIT): with glMatrix((-cam.x, -cam.y, -cam.z), self.rotation), glRestore(GL_TEXTURE_BIT | GL_ENABLE_BIT):
glCallList(self.sky_id) 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): class Body(Entity):
@ -258,11 +263,11 @@ class SphericalBody(Body):
division = info.get('division', max(min(int(self.radius / 8), 60), 10)) division = info.get('division', max(min(int(self.radius / 8), 60), 10))
self.light_source = info.get('light_source', False) self.light_source = info.get('light_source', False)
texture = get_best_texture(info['texture']) self.texture = get_best_texture(info['texture'])
self.sphere_id = compile(sphere, self.radius, division, division, texture) self.sphere = Sphere(self.radius, division, division)
self.atmosphere_id = 0 self.atmosphere_id = 0
self.cloudmap_id = 0 self.clouds = None
self.corona_id = 0 self.corona_id = 0
self.ring_id = 0 self.ring_id = 0
@ -273,8 +278,8 @@ class SphericalBody(Body):
cloud_texture = atmosphere_data.get('cloud_texture', None) cloud_texture = atmosphere_data.get('cloud_texture', None)
corona_texture = atmosphere_data.get('corona_texture', None) corona_texture = atmosphere_data.get('corona_texture', None)
if cloud_texture is not None: if cloud_texture is not None:
cloud_texture = get_best_texture(cloud_texture, loader=load_clouds) self.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.clouds = Sphere(self.radius + 2, division, division)
if corona_texture is not None: if corona_texture is not None:
corona = get_best_texture(corona_texture, clamp=True) corona = get_best_texture(corona_texture, clamp=True)
@ -301,11 +306,23 @@ class SphericalBody(Body):
self.ring_id = compile(disk, distance, distance + size, 30, self.ring_id = compile(disk, distance, distance + size, 30,
get_best_texture(info['ring'].get('texture', None), clamp=True)) get_best_texture(info['ring'].get('texture', None), clamp=True))
def _draw_sphere(self): def _draw_sphere(self, fv4=GLfloat * 4):
with glMatrix(self.location, self.rotation), glRestore(GL_CURRENT_BIT | GL_ENABLE_BIT): 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: if self.light_source:
glDisable(GL_LIGHTING) 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): def _draw_atmosphere(self, glMatrixBuffer=GLfloat * 16):
with glMatrix(self.location), glRestore(GL_ENABLE_BIT | GL_CURRENT_BIT): with glMatrix(self.location), glRestore(GL_ENABLE_BIT | GL_CURRENT_BIT):
@ -326,10 +343,16 @@ class SphericalBody(Body):
glCallList(self.corona_id) glCallList(self.corona_id)
def _draw_clouds(self): 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_BLEND)
glEnable(GL_ALPHA_TEST) 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): def _draw_rings(self):
with glMatrix(self.location, self.ring_rotation), glRestore(GL_CURRENT_BIT): 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): if options.atmosphere and (self.atmosphere_id or self.corona_id):
self._draw_atmosphere() self._draw_atmosphere()
if options.cloud and self.cloudmap_id: if options.cloud and self.clouds:
self._draw_clouds() self._draw_clouds()
if self.ring_id: 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 math import *
from random import random, gauss, choice from random import random, gauss, choice
@ -7,7 +9,7 @@ from six.moves import range
TWOPI = pi * 2 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'] 'flare', 'glSection', 'glMatrix', 'glRestore', 'progress_bar']
@ -45,6 +47,17 @@ class glRestore(object):
glPopAttrib() 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): class glMatrix(object):
def __init__(self, location=None, rotation=None): def __init__(self, location=None, rotation=None):
self.location = location self.location = location
@ -169,49 +182,57 @@ def flare(rinner, router, res, prob, tex):
last_y = y last_y = y
def sphere(r, lats, longs, tex, lighting=True, inside=False, fv4=GLfloat * 4): def array_to_ctypes(arr):
with glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT): return cast(arr.buffer_info()[0], POINTER({
sphere = gluNewQuadric() 'f': c_float,
gluQuadricDrawStyle(sphere, GLU_FILL) 'i': c_int,
gluQuadricTexture(sphere, True) 'I': c_uint,
if lighting: }[arr.typecode]))
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 colourball(r, lats, longs, colour, fv4=GLfloat * 4): class Sphere(object):
""" def __init__(self, r, lats, longs):
Sphere function from the OpenGL red book. tau = pi * 2
""" phi_div = tau / longs
with glRestore(GL_ENABLE_BIT): theta_div = pi / lats
sphere = gluNewQuadric()
glDisable(GL_BLEND) self.vertex_count = (lats + 1) * (longs + 1) * 2
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(*colour)) buffer = self.vertex_count * 8 * [0]
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 1)) index = 0
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125) for i in range(longs + 1):
glEnable(GL_CULL_FACE) phi1, phi2 = i * phi_div, (i + 1) * phi_div
glCullFace(GL_BACK) 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) def draw(self):
gluDeleteQuadric(sphere) 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): def belt(radius, cross, object, count):

View file

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