Hi all,
I've been interested in using numpy arrays with the pyglet.graphics
api. One of my motivations to use numpy arrays over the ctypes c
arrays is the nice item access that comes with numpy arrays.
The unobtrusive approach is just to just use a numpy array that
references the same memory of the c arrays returned by vertex list
properties (I found some useful discussion related to this at
http://projects.scipy.org/pipermail/numpy-discussion/2006-July/009438.html
that uses the buffer interface to share the memory). This works just
fine, although then you'll need to do some extra work to shape the
array to reflex the vertex/attribute counts, and then update it as the
properties of the vertex list change (resizing, allocation, etc).
I've attached some code (and example usage) to use numpy arrays with
pyglet.graphics. Essentially the 'as_numpy_array' function is all you
need to manipulate pyglets vertex data as numpy arrays. But based on
Alex's suggestion
(http://groups.google.com/group/pyglet-users/msg/92a658460c2547d8),
this sketch implements versions of the relevant classes in
pyglet.graphics. I implemented simple subclasses of the classes in
pyglet.graphics.vertexbuffer to return numpy array views of the
allocated data, as well as a subclass of
pyglet.graphics.vertexdomain.VertexList whose vertexattribute property
descriptors reshape and transpose their numpy arrays to reflect the
vertex/attribute counts, i.e for 4 vertices with the format 'v3f':
((x1, x2, x3, x4),
(y1, y2, y3, y4),
(z1, z2, z3, z4))
Using these classes with pyglet.graphics in the normal way (i.e
pyglet.graphics.vertex_list, pyglet.graphics.Batch.add/add_indexed)
involves hacking the module by replacing some of its classes and
functions (see the 'install' function), although hooks in
pyglet.graphics some time in the future would be nice :)
-tamas
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"pyglet-users" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/pyglet-users?hl=en
-~----------~----~----~----~------~----~------~--~---
"""
Numpy arrays with pyglet's vertex lists.
"""
import numpy
import ctypes
from pyglet import gl
from pyglet.graphics import vertexbuffer, vertexdomain
_installed = False
_VertexList = vertexdomain.VertexList
ctypes.pythonapi.PyBuffer_FromReadWriteMemory.restype = ctypes.py_object
def install():
"""
Globally hooks in numpy arrays for pyglet.graphics.
"""
global _installed
if _installed:
return
vertexdomain.VertexList = VertexList
vertexbuffer.create_buffer = _with_numpy(
vertexbuffer.create_buffer)
vertexbuffer.create_mappable_buffer = _with_numpy(
vertexbuffer.create_mappable_buffer)
def as_numpy_array(c_array):
"""
Return a view of the c array as a numpy array, with the
underlying memory shared.
"""
return numpy.frombuffer(
ctypes.pythonapi.PyBuffer_FromReadWriteMemory(
c_array, ctypes.sizeof(c_array._type_) * len(c_array)),
dtype=c_array._type_)
def _with_numpy(func):
"""
Decorates the create_buffer and create_mappable_buffer in
pyglet.grpahics.vertexbuffer that reinstantiates their returned
vertex buffers with their equivalents in this module.
"""
def new_func(
size,
target=gl.GL_ARRAY_BUFFER,
usage=gl.GL_DYNAMIC_DRAW,
vbo=True):
a = func(size, target, usage, vbo)
if isinstance(a, vertexbuffer.VertexBufferObject):
return NumpyVertexBufferObject(size, target, usage)
elif isinstance(a, vertexbuffer.MappableVertexBufferObject):
return NumpyMappableVertexBufferObject(size, target, usage)
else:
return NumpyVertexArray(size)
new_func.__name__ = func.__name__
new_func.__dict__ = func.__dict__
new_func.__doc__ = func.__doc__
new_func._decorated_func = func
return new_func
class NumpyVertexArray(vertexbuffer.VertexArray):
def get_region(self, start, size, ptr_type):
region = super(NumpyVertexArray, self).get_region(
start, size, ptr_type)
region.array = as_numpy_array(region.array)
return region
class NumpyVertexBufferObject(vertexbuffer.VertexBufferObject):
def get_region(self, start, size, ptr_type):
region = super(NumpyVertexBufferObject, self).get_region(
start, size, ptr_type)
region.array = as_numpy_array(region.array)
return region
class NumpyMappableVertexBufferObject(vertexbuffer.VertexBufferObject):
def get_region(self, start, size, ptr_type):
region = super(NumpyMappableVertexBufferObject, self).get_region(
start, size, ptr_type)
region.array = as_numpy_array(region.array)
return region
class VertexList(_VertexList):
def _get_shaped_array(self, attribute_name):
"""
Shapes and transposes the arrays retrieved by the
vertexattribute property descriptors to reflect the
vertex/attribute counts.
"""
array = getattr(super(VertexList, self), '_get_%s' % attribute_name)()
count = self.domain.attribute_names[attribute_name].count
if len(array.shape) == 1:
array.shape = (len(array) / count, count)
array = array.transpose()
getattr(self, '_%s_cache' % attribute_name).array = array
return array
def _get_colors(self):
return self._get_shaped_array('colors')
def _get_fog_coords(self):
return self._get_shaped_array('fog_coords')
def _get_edge_flags(self):
return self._get_shaped_array('edge_flags')
def _get_normals(self):
return self._get_shaped_array('normals')
def _get_secondary_colors(self):
return self._get_shaped_array('secondary_colors')
def _get_tex_coords(self):
return self._get_shaped_array('tex_coords')
def _get_vertices(self):
return self._get_shaped_array('vertices')
colors = property(
_get_colors, _VertexList._set_colors,
doc=_VertexList.colors.__doc__)
fog_coords = property(
_get_fog_coords, _VertexList._set_fog_coords,
doc=_VertexList.fog_coords.__doc__)
edge_flags = property(
_get_edge_flags, _VertexList._set_edge_flags,
doc=_VertexList.edge_flags.__doc__)
normals = property(
_get_normals, _VertexList._set_normals,
doc=_VertexList.normals.__doc__)
tex_coords = property(
_get_tex_coords, _VertexList._set_tex_coords,
doc=_VertexList.tex_coords.__doc__)
vertices = property(
_get_vertices, _VertexList._set_vertices,
doc=_VertexList.vertices.__doc__)
import sys
import random
import numpy
from numpy import sin, cos
import pyglet
from pyglet import gl
import nverts
# first, an example without hacking pyglet.graphics
vlist = pyglet.graphics.vertex_list(4, 'v2i')
nv = nverts.as_numpy_array(vlist.vertices)
nv.shape = (4, 2)
nv = nv.transpose()
# nv is now in the form:
# ((x1, x2, x3, x4),
# (y1, y2, x3, x4))
# so using 'v4f' or similar attributes will allow you
# to apply the dot product of matrix transformations
# to the vertices
vlist.delete()
del nv
# integrating numpy arrays into python.graphics
nverts.install()
window = pyglet.window.Window(width=256, height=256)
# geometry
batch = pyglet.graphics.Batch()
quad = numpy.array(
((-1, -1, 1, 1),
(-1, 1, 1, -1),
( 0, 0, 0, 0),
( 1, 1, 1, 1)),
dtype=gl.GLfloat)
scale = numpy.array(
((10, 0 , 0 , 0),
(0 , 10, 0 , 0),
(0 , 0 , 10, 0),
(0 , 0 , 0 , 1)),
dtype=gl.GLfloat)
quad = numpy.dot(scale, quad)
color = numpy.array(
((255, 255, 255, 255),
(255, 255, 255, 255),
(255, 255, 255, 255),
(255, 255, 255, 255)),
dtype='uint8')
quad = batch.add(
4, gl.GL_LINE_LOOP, None,
('v4f', quad.transpose().flat),
('c4B', color.transpose().flat))
@window.event
def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
rotate_x = numpy.array(
((1., 0. , 0. , 0.),
(0., cos(-dy*.01), -sin(-dy*.01), 0.),
(0., sin(-dy*.01), cos(-dy*.01) , 0.),
(0., 0. , 0. , 1.)),
dtype=gl.GLfloat)
rotate_y = numpy.array(
((cos(dx*.01) , 0., sin(dx*.01), 0.),
(0. , 1., 0. , 0.),
(-sin(dx*.01), 0., cos(dx*.01), 0.),
(0. , 0., 0. , 1.)),
dtype=gl.GLfloat)
transform = numpy.dot(rotate_x, rotate_y)
quad.vertices[:] = numpy.dot(transform, quad.vertices)
# colors
target_color = (255, 255, 255)
def pick_new_color(dt):
global target_color
target_color = (
random.choice(range(256)),
random.choice(range(256)),
random.choice(range(256)))
def adjust_color(dt):
global target_color
actual_r = quad.colors[0, 0]
actual_g = quad.colors[1, 0]
actual_b = quad.colors[2, 0]
quad.colors[0] = int(actual_r - (actual_r - target_color[0]) / 10.)
quad.colors[1] = int(actual_g - (actual_g - target_color[1]) / 10.)
quad.colors[2] = int(actual_b - (actual_b - target_color[2]) / 10.)
pyglet.clock.schedule_interval(pick_new_color, 1)
pyglet.clock.schedule_interval(adjust_color, 1. / 10.)
# main app
@window.event
def on_draw():
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
gl.glMatrixMode(gl.GL_PROJECTION)
gl.glLoadIdentity()
gl.gluPerspective(60., float(window.width) / window.height, .1, 8192)
gl.glTranslatef(0., 0., -50.)
gl.glMatrixMode(gl.GL_MODELVIEW)
gl.glLineWidth(3.)
batch.draw()
@window.event
def on_key_press(symbol, modifiers):
if symbol == pyglet.window.key.ESCAPE:
sys.exit()
try:
pyglet.app.run()
except KeyboardInterrupt:
sys.exit()