punyverse/punyverse/glgeom.py
2018-08-25 00:06:33 -04:00

288 lines
8 KiB
Python

from array import array
from ctypes import c_int, c_float, byref, cast, POINTER, c_uint, c_short, c_ushort
from math import *
from random import random, gauss, choice
from pyglet.gl import *
# noinspection PyUnresolvedReferences
from six.moves import range
TWOPI = pi * 2
__all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'Sphere', 'belt',
'glSection', 'glMatrix', 'glRestore', 'progress_bar']
class glContext(object):
def __init__(self, context):
self.new_context = context
def __enter__(self):
self.old_context = get_current_context()
self.new_context.set_current()
def __exit__(self, exc_type, exc_val, exc_tb):
self.old_context.set_current()
class glSection(object):
def __init__(self, type):
self.type = type
def __enter__(self):
glBegin(self.type)
def __exit__(self, exc_type, exc_val, exc_tb):
glEnd()
class glRestore(object):
def __init__(self, flags):
self.flags = flags
def __enter__(self):
glPushAttrib(self.flags)
def __exit__(self, exc_type, exc_val, exc_tb):
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
self.rotation = rotation
def __enter__(self):
glPushMatrix()
if self.location:
glTranslatef(*self.location)
if self.rotation:
pitch, yaw, roll = self.rotation
glRotatef(pitch, 1, 0, 0)
glRotatef(yaw, 0, 1, 0)
glRotatef(roll, 0, 0, 1)
def __exit__(self, exc_type, exc_val, exc_tb):
glPopMatrix()
def array_to_ctypes(arr):
return cast(arr.buffer_info()[0], POINTER({
'f': c_float,
'i': c_int,
'I': c_uint,
'h': c_short,
'H': c_ushort,
}[arr.typecode]))
def array_to_gl_buffer(buffer, array_type='f'):
vbo = c_uint()
glGenBuffers(1, byref(vbo))
glBindBuffer(GL_ARRAY_BUFFER, vbo.value)
buffer = array(array_type, buffer)
glBufferData(GL_ARRAY_BUFFER, buffer.itemsize * len(buffer), array_to_ctypes(buffer), GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, 0)
return vbo.value
def compile(pointer, *args, **kwargs):
display = glGenLists(1)
glNewList(display, GL_COMPILE)
pointer(*args, **kwargs)
glEndList()
return display
def ortho(width, height):
glDisable(GL_LIGHTING)
glDisable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadIdentity()
glOrtho(0, width, 0, height, -1, 1)
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
glLoadIdentity()
def frustrum():
glMatrixMode(GL_PROJECTION)
glPopMatrix()
glMatrixMode(GL_MODELVIEW)
glPopMatrix()
glEnable(GL_LIGHTING)
glEnable(GL_DEPTH_TEST)
def crosshair(size, coords):
cx, cy = coords
with glSection(GL_LINES):
glVertex2f(cx - size, cy)
glVertex2f(cx + size, cy)
glVertex2f(cx, cy - size)
glVertex2f(cx, cy + size)
def circle(r, seg, coords):
cx, cy = coords
with glSection(GL_LINE_LOOP):
for i in range(seg):
theta = TWOPI * i / seg
glVertex2f(cx + cos(theta) * r, cy + sin(theta) * r)
class Disk(object):
def __init__(self, rinner, router, segs):
res = segs * 5
delta = 2 * pi / res
self.vertex_count = (res + 1) * 2
# Need padding to make the last vertex render correctly... why?
buffer = self.vertex_count * 3 * [0]
for i in range(res):
theta = delta * i
x, y = cos(theta), sin(theta)
buffer[6*i:6*i+6] = [rinner * x, rinner * y, 0, router * x, router * y, 1]
buffer[6*res:6*res+6] = buffer[:6]
self.vbo = array_to_gl_buffer(buffer)
def draw(self):
with glRestoreClient(GL_CLIENT_VERTEX_ARRAY_BIT):
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glVertexPointer(3, GL_FLOAT, 12, 0)
glTexCoordPointer(1, GL_FLOAT, 12, 8)
glDrawArrays(GL_TRIANGLE_STRIP, 0, self.vertex_count)
glBindBuffer(GL_ARRAY_BUFFER, 0)
class Sphere(object):
def __init__(self, r, lats, longs):
tau = pi * 2
phi_div = tau / longs
theta_div = pi / lats
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
self.vbo = array_to_gl_buffer(buffer)
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)
class OrbitVBO(object):
def __init__(self, orbit):
buffer = 360 * 3 * [0]
for theta in range(360):
x, z, y = orbit.orbit(theta)
buffer[3*theta:3*theta+3] = [x, y, z]
self.vbo = array_to_gl_buffer(buffer)
def draw(self):
with glRestoreClient(GL_CLIENT_VERTEX_ARRAY_BIT):
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(3, GL_FLOAT, 12, 0)
glDrawArrays(GL_LINE_LOOP, 0, 360)
glBindBuffer(GL_ARRAY_BUFFER, 0)
def close(self):
if self.vbo is not None:
vbo = c_uint(self.vbo)
glDeleteBuffers(1, byref(vbo))
self.vbo = None
def __del__(self):
self.close()
def belt(radius, cross, object, count):
for i in range(count):
theta = TWOPI * random()
r = gauss(radius, cross)
x, y, z = cos(theta) * r, gauss(0, cross), sin(theta) * r
with glMatrix():
glTranslatef(x, y, z)
scale = gauss(1, 0.5)
if scale < 0:
scale = 1
glScalef(scale, scale, scale)
choice(object).draw()
def progress_bar(x, y, width, height, filled):
with glRestore(GL_ENABLE_BIT):
glDisable(GL_TEXTURE_2D)
glDisable(GL_BLEND)
x1 = x
x2 = x + width
y1 = y
y2 = y - height
y3 = 0.65 * y1 + 0.35 * y2
y4 = 0.25 * y1 + 0.75 * y2
glColor3f(0.6, 0.6, 0.6)
with glSection(GL_LINE_LOOP):
glVertex2f(x1, y1)
glVertex2f(x1, y2)
glVertex2f(x2, y2)
glVertex2f(x2, y1)
x1 += 1
y1 -= 1
x2 = x + width * filled - 1
with glSection(GL_TRIANGLE_STRIP):
glColor3f(0.81, 1, 0.82)
glVertex2f(x1, y1)
glVertex2f(x2, y1)
glColor3f(0, 0.83, 0.16)
glVertex2f(x1, y3)
glVertex2f(x2, y3)
glVertex2f(x1, y4)
glVertex2f(x2, y4)
glColor3f(0.37, 0.92, 0.43)
glVertex2f(x1, y2)
glVertex2f(x2, y2)