From 825e2ecf6ad0b1a65ff64188f49cbc6f1c5331ae Mon Sep 17 00:00:00 2001 From: Quantum Date: Fri, 24 Aug 2018 19:37:50 -0400 Subject: [PATCH] Use VBOs for spheres. --- punyverse/entity.py | 57 +++++++++++++++++-------- punyverse/glgeom.py | 99 +++++++++++++++++++++++++++----------------- punyverse/world.json | 2 +- 3 files changed, 101 insertions(+), 57 deletions(-) diff --git a/punyverse/entity.py b/punyverse/entity.py index 5f0d4b8..6cfb67c 100644 --- a/punyverse/entity.py +++ b/punyverse/entity.py @@ -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: diff --git a/punyverse/glgeom.py b/punyverse/glgeom.py index 79ec413..45d9bbe 100644 --- a/punyverse/glgeom.py +++ b/punyverse/glgeom.py @@ -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): diff --git a/punyverse/world.json b/punyverse/world.json index 2b73f48..c68a76e 100644 --- a/punyverse/world.json +++ b/punyverse/world.json @@ -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": {