glitch-0.6/0000755000175000017500000000000011545146453010707 5ustar dafdafglitch-0.6/glitch/0000755000175000017500000000000011545146453012161 5ustar dafdafglitch-0.6/glitch/read.py0000644000175000017500000000201311544723452013441 0ustar dafdaf "Reading pixels from GPU memory." from __future__ import absolute_import import OpenGL.GL as gl import glitch class Read(glitch.Node): """Read pixels from GPU memory. After each render, the C{pixels} attribute is updated with the contents of the back buffer. """ def __init__(self, **kw): glitch.Node.__init__(self, **kw) self.width = None self.height = None self.pixels = None def render(self, ctx): glitch.Node.render(self, ctx) self.width = ctx['w'] self.height = ctx['h'] self.pixels = gl.glReadPixels( 0, 0, ctx['w'], ctx['h'], gl.GL_RGB, gl.GL_UNSIGNED_BYTE) assert len(self.pixels) == self.width * self.height * 3 def get_pixbuf(self): # mainly useful for flipping pixel coordinate system import gtk pixbuf = gtk.gdk.pixbuf_new_from_data(self.pixels, gtk.gdk.COLORSPACE_RGB, False, 8, self.width, self.height, self.width * 3) return pixbuf.flip(False) glitch-0.6/glitch/glx/0000755000175000017500000000000011545146453012753 5ustar dafdafglitch-0.6/glitch/glx/__init__.py0000644000175000017500000000007311532271477015065 0ustar dafdaf "GLX support." from glitch.glx.context import GLXContext glitch-0.6/glitch/glx/context.py0000644000175000017500000000223511532271477015014 0ustar dafdaf "Utility class for GLX context creation." import ctypes import OpenGL.GL as gl import OpenGL.GLX as glx from OpenGL.raw._GLX import Display def array(t, xs): # Thanks, ctypes. return (t * len(xs))(*xs) class GLXContext(object): """Utility class for GLX context creation. This class represents a GLX rendering context attached to the root window. """ def __init__(self): # Me, unportable? xlib = ctypes.cdll.LoadLibrary('libX11.so') display = xlib.XOpenDisplay(None) # XXX: We should probably create a separate window. self.window = xlib.XRootWindow(display, 0) self.display_ = Display.from_address(display) visual = glx.glXChooseVisual(self.display_, 0, array(ctypes.c_int, [glx.GLX_RGBA, glx.GLX_DEPTH_SIZE, 24])) self.context = glx.glXCreateContext( self.display_, visual, None, gl.GL_TRUE) def __enter__(self): assert glx.glXMakeCurrent( self.display_, self.window, self.context) != 0 def __exit__(self, type, value, traceback): # Not that we're going to be doing any X drawing, but may as well. glx.glXWaitGL() glitch-0.6/glitch/node.py0000644000175000017500000000101611532271477013457 0ustar dafdaf "The base Node class." class Node(object): def __init__(self, children=[]): """ @param children: A list of child nodes. """ self.children = list(children) def render(self, ctx): "Call C{self.draw()}, then render children." self.draw(ctx) for c in self.children: c.render(ctx) def draw(self, ctx): "Perform drawing for this node." def __repr__(self): return '%s(children=%r)' % (self.__class__.__name__, self.children) glitch-0.6/glitch/shader.py0000644000175000017500000000323111532271477014001 0ustar dafdaf "Shader support." from __future__ import with_statement import OpenGL.GL as gl import OpenGL.GL.shaders as shaders import glitch from glitch.multicontext import MultiContext class Shader(glitch.Node, MultiContext): "An OpenGL vertex and fragment shader." _context_key = 'shader' vertex = """ varying vec4 vertex_color; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; vertex_color = gl_Color; } """ fragment = """ varying vec4 vertex_color; void main() { gl_FragColor = vertex_color; } """ def __init__(self, vertex=None, fragment=None, **kw): if vertex: self.vertex = vertex if fragment: self.fragment = fragment glitch.Node.__init__(self, **kw) MultiContext.__init__(self) def compile(self, ctx): "Compile this shader." try: return shaders.compileProgram( shaders.compileShader(self.vertex, gl.GL_VERTEX_SHADER), shaders.compileShader(self.fragment, gl.GL_FRAGMENT_SHADER)) except RuntimeError, e: print e return None def set_uniforms(self, ctx, shader): "Set any uniform variables the shader uses." def _context_create(self, ctx): return self.compile(ctx) def _context_update(self, ctx, _old_shader): return self.compile(ctx) def render(self, ctx): shader = self.context_get(ctx) if shader is not None: with shader: self.set_uniforms(ctx, shader) glitch.Node.render(self, ctx) glitch-0.6/glitch/gst/0000755000175000017500000000000011545146453012756 5ustar dafdafglitch-0.6/glitch/gst/__init__.py0000644000175000017500000000021011532271477015061 0ustar dafdaf "GStreamer integration." from video import Video, VideoFileTexture from videoloop import VideoLoop from camerasrc import CameraSource glitch-0.6/glitch/gst/video.py0000644000175000017500000000605411532271477014444 0ustar dafdaf "Upload GStreamer video data as textures." import gst, gobject import OpenGL.GL as gl from glitch.texture import ApplyTexture, Texture, TexturedSquare class VideoTexture(Texture, gobject.GObject): "Abstract base class for GStreamer-based video textures." __gsignals__ = { 'new-buffer': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) } internal_format = gl.GL_RGB format = gl.GL_RGB y_flip = True def __init__(self): gobject.GObject.__init__(self) Texture.__init__(self, None, None, None) self.bin = self.make_bin() self.appsink = self.bin.get_by_name('appsink') self.appsink.props.emit_signals = True self.new_buffer_handler = \ self.appsink.connect('new-buffer', self.pull_buffer) self.buffer = None def make_bin(self): """ Called to create the GStreamer bin. The bin should have an appsink called "appsink". """ raise NotImplementedError def pull_buffer(self, appsink): if self.width is None: pad = appsink.get_pad('sink') caps = pad.get_negotiated_caps() self.width = caps[0]['width'] self.height = caps[0]['height'] self.buffer = appsink.emit('pull-buffer') self.version += 1 self.data = self.buffer.data self.emit('new-buffer', self.buffer) def stop(self): self.appsink.disconnect(self.new_buffer_handler) def upload(self, ctx): if self.buffer is not None: Texture.upload(self, ctx) class VideoFileTexture(VideoTexture): "Load a video from a file." def __init__(self, path): self.path = path VideoTexture.__init__(self) def make_bin(self): bin = gst.parse_bin_from_description(''' filesrc name=filesrc ! decodebin name=decodebin ! videoscale ! ffmpegcolorspace ! video/x-raw-rgb,bpp=24 ! appsink name=appsink ''', False) filesrc = bin.get_by_name('filesrc') filesrc.props.location = self.path return bin class Video(ApplyTexture): "Utility to show a video in a square." def __init__(self, texture, **kw): assert 'children' not in kw ApplyTexture.__init__(self, texture, **kw) self.texture = texture # XXX: Perhaps this should be TexturedRectangle. self.children = [TexturedSquare()] def _no_video(self): # XXX: Perhaps it should be possible to disable this. gl.glBegin(gl.GL_LINE_STRIP) gl.glVertex2f(1, 1) gl.glVertex2f(1, 0) gl.glVertex2f(0, 0) gl.glVertex2f(0, 1) gl.glVertex2f(1, 1) gl.glEnd() gl.glBegin(gl.GL_LINES) gl.glVertex2f(1, 1) gl.glVertex2f(0, 0) gl.glVertex2f(1, 0) gl.glVertex2f(0, 1) gl.glEnd() def render(self, ctx): if self.texture.buffer is None: self._no_video() else: ApplyTexture.render(self, ctx) glitch-0.6/glitch/gst/camerasrc.py0000644000175000017500000000363311511244525015265 0ustar dafdaffrom __future__ import with_statement import gobject import gst from glitch.fbo import BufferCamera from glitch.glx import GLXContext from glitch.read import Read class CameraSource(gst.BaseSrc): __gsttemplates__ = ( gst.PadTemplate("src", gst.PAD_SRC, gst.PAD_ALWAYS, # XXX: hardcoded width and height gst.Caps("video/x-raw-rgb,depth=24,bpp=24,width=720,height=576"))) def __init__(self, camera): gst.BaseSrc.__init__(self) self.camera = camera self.pixbuf = None self.start = 0 self.stop = gst.CLOCK_TIME_NONE self.curpos = 0 self.set_live(False) self.set_format(gst.FORMAT_BYTES) self.glx_context = GLXContext() def do_create(self, offset, size): #print (offset, size) gst.debug("offset: %r, size:%r" % (offset, size)) pad = self.get_pad('src') caps = pad.get_negotiated_caps() increment = int(gst.SECOND / max(1, float(caps[0]['framerate']))) if self.pixbuf is None: with self.glx_context: # XXX: hardcoded width and height self.camera.context['w'] = 720 self.camera.context['h'] = 576 read = Read(children=[self.camera]) bc = BufferCamera(720, 576, children=[read]) bc.render(None) self.pixbuf = read.get_pixbuf() pixels = self.pixbuf.get_pixels() assert len(pixels) == \ self.camera.context['w'] * self.camera.context['h'] * 3 buf = gst.Buffer(pixels) buf.timestamp = self.curpos buf.duration = increment self.curpos += buf.duration gst.debug("timestamp:%s" % gst.TIME_ARGS(buf.timestamp)) gst.debug("duration:%s" % gst.TIME_ARGS(buf.duration)) return (gst.FLOW_OK, buf) def do_is_seekable(self): return False gobject.type_register(CameraSource) glitch-0.6/glitch/gst/videoloop.py0000644000175000017500000000122411532271477015330 0ustar dafdaf "Play a video file in a loop." import gobject import gst from glitch.gst import VideoFileTexture class VideoLoop(VideoFileTexture): "Play a video file in a loop." def __init__(self, *args, **kwargs): VideoFileTexture.__init__(self, *args, **kwargs) appsink = self.bin.get_by_name('appsink') pad = appsink.get_static_pad('sink') pad.add_event_probe(self._event) def _event(self, pad, event): if event.type == gst.EVENT_EOS: gobject.idle_add(self._reset) return True def _reset(self): self.bin.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, 0) return False glitch-0.6/glitch/camera.py0000644000175000017500000001046211544723452013765 0ustar dafdaf "The abstract camera node." from __future__ import with_statement import ctypes import OpenGL.GL as gl import OpenGL.GLU as glu import glitch from glitch.context import Context # XXX: buggy with extreme vertical aspect ratios class Camera(glitch.Node): def __init__(self, eye=(0, 0, 5), ref=(0, 0, 0), up=(0, 1, 0), fovx=60, fovy=60, zNear=0.1, zFar=50, clear=(0, 0, 0, 0), **kwargs): glitch.Node.__init__(self, **kwargs) self.eye = list(eye) self.ref = list(ref) self.up = list(up) self.fovy = fovx self.fovx = fovy self.zNear = zNear self.zFar = zFar self.clear = clear self.context = Context() def draw(self, ctx): if ctx['w'] == 0: return gl.glClearColor(*self.clear) gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) gl.glColor(1, 1, 1) gl.glMaterialfv( gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT_AND_DIFFUSE, (1, 1, 1, 1)) if ctx.get('debug', False): gl.glLightModelfv(gl.GL_LIGHT_MODEL_AMBIENT, [.3, .3, .3, 1]) gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE) gl.glColor(0, 1, 0, 1) else: gl.glLightModelfv(gl.GL_LIGHT_MODEL_AMBIENT, [0, 0, 0, 1]) gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL) gl.glViewport(0, 0, ctx['w'], ctx['h']) self.aspect = ctx['w'] / float(ctx['h']) gl.glMatrixMode(gl.GL_MODELVIEW) gl.glLoadIdentity() glu.gluLookAt(*(self.eye + self.ref + self.up)) gl.glMatrixMode(gl.GL_PROJECTION) gl.glLoadIdentity() selection = ctx.get('selection') if selection is not None: (x, y, w, h) = selection glu.gluPickMatrix(x, y, w, h, (0, 0, ctx['w'], ctx['h'])) fovy = max(self.fovy, self.fovx/self.aspect) glu.gluPerspective(fovy, self.aspect, self.zNear, self.zFar) gl.glEnable(gl.GL_DEPTH_TEST) gl.glMatrixMode(gl.GL_MODELVIEW) def select(self, parent_ctx, x, y, width=3, height=3): with self.context.push('selection', (x, y, width, height)): gl.glSelectBuffer(1000) gl.glRenderMode(gl.GL_SELECT) self.render(None) hits = gl.glRenderMode(gl.GL_RENDER) return [ # The GL selection buffer contains the addresses of the Python node # objects on the stack when the hit occurred. Here we turn the # addresses back in to Python objects. This presumably won't work # outside CPython. ctypes.cast(int(id), ctypes.py_object).value for (_misc1, _misc2, path) in hits for id in path] def _save(self): gl.glMatrixMode(gl.GL_PROJECTION) gl.glPushMatrix() gl.glMatrixMode(gl.GL_MODELVIEW) gl.glPushMatrix() # XXX: Should we just save/restore everything? gl.glPushAttrib(gl.GL_VIEWPORT_BIT | gl.GL_TRANSFORM_BIT | gl.GL_ENABLE_BIT | gl.GL_LIGHTING_BIT | gl.GL_CURRENT_BIT) def _restore(self): gl.glPopAttrib() gl.glMatrixMode(gl.GL_PROJECTION) gl.glPopMatrix() gl.glMatrixMode(gl.GL_MODELVIEW) gl.glPopMatrix() def _all_lights_off(self, ctx): # Disable all lights in the parent scene, to prevent them affecting # the child scene. for light in ctx.get('lights', {}).values(): gl.glDisable(gl.GL_LIGHT0 + light) def render(self, parent_ctx): # If this camera is part of an existing scene, we save and restore the # parent state. if parent_ctx is not None: self._save() parent_ctx.push('lights', {}) parent_ctx.push('w', self.context['w']) parent_ctx.push('h', self.context['h']) glitch.Node.render(self, parent_ctx) parent_ctx.pop() parent_ctx.pop() parent_ctx.pop() self._restore() else: glitch.Node.render(self, self.context) def refresh(self): "Ask the camera to render whatever it renders to." raise NotImplementedError def run(self): """Display this camera, whatever that means. This might involve, for example, creating a window or starting a mainloop.""" raise NotImplementedError glitch-0.6/glitch/texture.py0000644000175000017500000001402711544723452014236 0ustar dafdaf "The abstract texture class." from __future__ import with_statement import OpenGL.GL as gl import glitch from glitch.multicontext import MultiContext class TextureBinding(object): def __init__(self, ctx, unit, texture): self.ctx = ctx self.unit = unit self.texture = texture self.bound = False def _try_bind(self): if self.texture.width is None: return None self.ctx.push('textures') # A bit annoying that we have to set this. self.ctx.setdefault('textures', {}) self.ctx['textures'][self.unit] = self.texture gl.glActiveTexture(gl.GL_TEXTURE0 + self.unit) gl.glPushAttrib(gl.GL_ENABLE_BIT | gl.GL_TEXTURE_BIT) gl.glEnable(gl.GL_TEXTURE_2D) id = self.texture.context_get(self.ctx) gl.glBindTexture(gl.GL_TEXTURE_2D, id) return id def __enter__(self): id = self._try_bind() self.bound = id is not None return id def __exit__(self, type, exception, traceback): if self.bound: gl.glActiveTexture(gl.GL_TEXTURE0 + self.unit) gl.glPopAttrib() self.ctx.pop() self.bound = False class Texture(MultiContext): "An OpenGL texture." _context_key = 'texture' internal_format = gl.GL_RGBA format = gl.GL_RGBA wrap_s = gl.GL_CLAMP_TO_EDGE wrap_t = gl.GL_CLAMP_TO_EDGE def __init__(self, width, height, data): """ @param width: Width. @param height: Height. @param data: Pixel data, in RGBA format. """ MultiContext.__init__(self) self.width = width self.height = height self.data = data # import traceback # for f in traceback.format_stack(limit=10): # for l in f.splitlines(): # print ' ' + l # print '--' def _context_create(self, ctx): id = gl.glGenTextures(1) gl.glBindTexture(gl.GL_TEXTURE_2D, id) self.set_parameters(ctx) self.upload(ctx) return id def _context_update(self, ctx, id): gl.glBindTexture(gl.GL_TEXTURE_2D, id) self.upload(ctx) return id def _context_delete(self, ctx, id): gl.glDeleteTextures(id) def bind(self, ctx, unit): """Bind this texture to a texture unit. The return value is an object supporting the C{with} protocol. E.g.:: with texture.bind(ctx, 0): # Render some other nodes that should be textured. """ return TextureBinding(ctx, unit, self) def upload(self, ctx): """Upload the texture data to GPU memory. This function assumes that the texture is currently bound to the active OpenGL texture unit. """ # Target, level, internal format, width, height, border, format, type, # data. gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, self.internal_format, self.width, self.height, 0, self.format, gl.GL_UNSIGNED_BYTE, self.data) def set_parameters(self, ctx): """Set texture parameters such as wrapping and filtering. This function assumes that the texture is currently bound to the active OpenGL texture unit. """ gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, self.wrap_s) gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, self.wrap_t) gl.glTexParameteri( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR) gl.glTexParameteri( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR) class ApplyTexture(glitch.Node): "Bind a texture to a texture unit." def __init__(self, texture, unit=0, **kw): glitch.Node.__init__(self, **kw) self.texture = texture self.unit = unit def __repr__(self): return '%s(%r, %r, children=%r)' % ( self.__class__.__name__, self.texture, self.unit, self.children) def render(self, ctx): with self.texture.bind(ctx, self.unit): glitch.Node.render(self, ctx) class TexturedSquare(glitch.Node): "Draw a square with texture coordinates from (0, 0, 0) to (1, 1, 0)." def __init__(self, x_max=1, y_max=1, **kw): glitch.Node.__init__(self, **kw) self.x_max = x_max self.y_max = y_max def draw_rect(self, ctx, x1, y1, x2, y2): # Draw a rectangle with a texture coordinate for each bound # texture for each rectangle coordinate, taking into account # each texture's vertical orientation. rect = [(x1, y1), (x1, y2), (x2, y2), (x2, y1)] tex_rect = [ (0.0, 0.0), (0.0, self.y_max), (self.x_max, self.y_max), (self.x_max, 0.0)] tex_rect_flipped = [(x, self.y_max - y) for (x, y) in tex_rect] textures = ctx.get('textures', {}) tex_coords = [] for (unit, texture) in textures.iteritems(): tex_coords.append((unit, tex_rect_flipped if getattr(texture, 'y_flip', False) else tex_rect)) gl.glNormal3f(0, 0, 1) gl.glBegin(gl.GL_QUADS) for (i, vertex) in enumerate(rect): for (unit, tc) in tex_coords: gl.glMultiTexCoord2f(unit, *(tc[i])) gl.glVertex2f(*vertex) gl.glEnd() def draw(self, ctx): self.draw_rect(ctx, 0, 0, 1, 1) class TexturedRectangle(TexturedSquare): "Like TextureSquare, but maintains aspect ratio." def draw(self, ctx): textures = ctx.get('textures', {}) units = sorted(textures.keys()) if units: texture = textures[units[0]] (tw, th) = (texture.width, texture.height) else: (tw, th) = (1, 1) if tw > th: h = float(th) / tw / 2 (x1, x2) = (0, 1) (y1, y2) = (0.5 - h, 0.5 + h) else: w = float(tw) / th / 2 (x1, x2) = (0.5 - w, 0.5 + w) (y1, y2) = (0, 1) self.draw_rect(ctx, x1, y1, x2, y2) glitch-0.6/glitch/__init__.py0000644000175000017500000000207111532271477014273 0ustar dafdaf """ Glitch, an OpenGL rendering framework. Glitch renders using a tree of L{Node}s which share state using a L{Context}. The root of the tree is a special type of node called a L{Camera}. Texturing is accomplished using L{Texture} and its associated classes, and shading is accomplished using the L{Shader} node. Glitch is organised into a small core, with seperate packages integrating with other software (e.g. L{glitch.gtk} and L{glitch.glut} for windowing and input). Since Glitch leans towards being small and limited rather than all-inclusive, it is not rare for programs using Glitch to include specialised nodes of their own, and often those will need to call OpenGL directly. However, there is a collection of useful nodes (of varying levels of quality) in the L{glitch.limbo} package. """ from glitch.node import Node from glitch.color import Color from glitch.fbo import CameraTexture from glitch.transform import (Translate, Scale, Rotate) from glitch.shader import Shader from glitch.texture import ( Texture, ApplyTexture, TexturedRectangle, TexturedSquare) glitch-0.6/glitch/gtk/0000755000175000017500000000000011545146453012746 5ustar dafdafglitch-0.6/glitch/gtk/gtkmovingcamera.py0000644000175000017500000001441311511244525016471 0ustar dafdaf# encoding: utf8 import string import time from math import acos, cos, pi, sin, sqrt import gtk from glitch.gtk import GtkCamera bindings = { 'w': 'forward', 'a': 'left', 's': 'back', 'd': 'right', 'Up': 'forward', 'Down': 'back', 'Left': 'left', 'Right': 'right', 'F11': 'toggle-lighting', 'F12': 'toggle-debug', 'W': 'turn-up', 'A': 'turn-left', 'S': 'turn-down', 'D': 'turn-right', 'Shift-Left': 'turn-left', 'Shift-Right': 'turn-right', 'Shift-Up': 'turn-up', 'Shift-Down': 'turn-down', 'Page_Up': 'go-up', 'Page_Down': 'go-down', } class GtkMovingCamera(GtkCamera): def __init__(self, grab_mouse=False, **kw): GtkCamera.__init__(self, **kw) self.have_grab = False self.last_event_time = None self.add_events(gtk.gdk.POINTER_MOTION_MASK) self.connect('grab-broken-event', self.grab_broken) self.connect('key-press-event', self.key_pressed) if grab_mouse: self.connect('motion-notify-event', self.motion_notify) self.connect('visibility-notify-event', self.visibility) def visibility(self, _widget, event): if not self.have_grab: result = self.grab() if result == gtk.gdk.GRAB_SUCCESS: self.have_grab = True def grab_broken(self, _widget, event): self.have_grab = False def grab(self): window = self.window pixmap = gtk.gdk.Pixmap(window, 1, 1, 1) color = gtk.gdk.Color() blank_cursor = gtk.gdk.Cursor(pixmap, pixmap, color, color, 0, 0) return gtk.gdk.pointer_grab( window, False, gtk.gdk.POINTER_MOTION_MASK, None, blank_cursor, 0) def get_orientation(self): (ex, ey, ez) = self.eye (rx, ry, rz) = self.ref r1 = sqrt((ex - rx) ** 2 + (ez - rz) ** 2) yaw = acos((ez - rz) / r1) if ex - rx < 0: yaw *= -1 r2 = sqrt((ex - rz) ** 2 + (ez - rz) ** 2 + (ey - ry) ** 2) pitch = acos((ey - ry) / r2) return (yaw, pitch) def set_orientation(self, yaw, pitch): (ex, ey, ez) = self.eye (rx, ry, rz) = self.ref r1 = sqrt((ex - rx) ** 2 + (ez - rz) ** 2) r2 = sqrt((ex - rz) ** 2 + (ey - ry) ** 2 + (ez - rz) ** 2) self.ref[0] = ex - r1 * sin(yaw) self.ref[1] = ey - r2 * cos(pitch) self.ref[2] = ez - r1 * cos(yaw) def key_pressed(self, _widget, event): name = gtk.gdk.keyval_name(event.keyval) if name not in string.uppercase and event.state & gtk.gdk.SHIFT_MASK: name = 'Shift-' + name slow = False if event.state & gtk.gdk.CONTROL_MASK: slow = True if name in bindings: action = bindings[name].replace('-', '_') method = getattr(self, action) method(slow) def toggle_lighting(self, *args): self.ctx['lighting'] = not self.ctx['lighting'] print 'lighting = %r' % self.ctx['lighting'] self.refresh() def toggle_debug(self, *args): self.ctx['debug'] = not self.ctx['debug'] print 'debug = %r' % self.ctx['debug'] self.refresh() def forward(self, slow): (yaw, pitch) = self.get_orientation() self.eye[0] -= (0.04 if slow else 0.4) * sin(yaw) self.eye[2] -= (0.04 if slow else 0.4) * cos(yaw) self.ref[0] -= (0.04 if slow else 0.4) * sin(yaw) self.ref[2] -= (0.04 if slow else 0.4) * cos(yaw) self.refresh() def back(self, slow): (yaw, pitch) = self.get_orientation() self.eye[0] += (0.04 if slow else 0.4) * sin(yaw) self.eye[2] += (0.04 if slow else 0.4) * cos(yaw) self.ref[0] += (0.04 if slow else 0.4) * sin(yaw) self.ref[2] += (0.04 if slow else 0.4) * cos(yaw) self.refresh() def left(self, slow): (yaw, pitch) = self.get_orientation() self.eye[0] -= (0.04 if slow else 0.4) * sin(yaw + pi / 2) self.eye[2] -= (0.04 if slow else 0.4) * cos(yaw + pi / 2) self.ref[0] -= (0.04 if slow else 0.4) * sin(yaw + pi / 2) self.ref[2] -= (0.04 if slow else 0.4) * cos(yaw + pi / 2) self.refresh() def right(self, slow): (yaw, pitch) = self.get_orientation() self.eye[0] += (0.04 if slow else 0.4) * sin(yaw + pi / 2) self.eye[2] += (0.04 if slow else 0.4) * cos(yaw + pi / 2) self.ref[0] += (0.04 if slow else 0.4) * sin(yaw + pi / 2) self.ref[2] += (0.04 if slow else 0.4) * cos(yaw + pi / 2) self.refresh() def turn_left(self, slow): self.turn(-(0.01 if slow else 0.1), 0) def turn_right(self, slow): self.turn((0.01 if slow else 0.1), 0) def turn_up(self, slow): self.turn(0, -(0.01 if slow else 0.1)) def turn_down(self, slow): self.turn(0, (0.01 if slow else 0.1)) def go_up(self, slow): self.eye[1] += (0.01 if slow else 0.1) self.ref[1] += (0.01 if slow else 0.1) self.refresh() def go_down(self, slow): self.eye[1] -= (0.01 if slow else 0.1) self.ref[1] -= (0.01 if slow else 0.1) self.refresh() def turn(self, d_yaw, d_pitch): (ex, ey, ez) = self.eye (yaw, pitch) = self.get_orientation() self.set_orientation(yaw - d_yaw, pitch - d_pitch) self.refresh() def motion_notify(self, _widget, event): t = time.time() if (self.last_event_time is not None and t - self.last_event_time < 0.05): return False display = gtk.gdk.display_get_default() screen = display.get_default_screen() (w, h) = (screen.get_width(), screen.get_height()) if self.last_event_time is not None: d_yaw = (event.x_root - w / 2) / 100.0 d_pitch = (event.y_root - h / 2) / 100.0 if (d_yaw, d_pitch) != (0, 0): self.turn(d_yaw, d_pitch) display.warp_pointer(screen, w / 2, h / 2) self.last_event_time = t return True def make_window(self, *args, **kwargs): window = GtkCamera.make_window(self, *args, **kwargs) self.grab_add() return window glitch-0.6/glitch/gtk/gtkcamera.py0000644000175000017500000000342311532271477015261 0ustar dafdaf from __future__ import with_statement import gtk, gobject import gtk.gtkgl import gtk.gdkgl gobject.threads_init() from glitch.camera import Camera class GtkCamera(Camera, gtk.gtkgl.widget.DrawingArea): "A camera that is also a C{gtk.Widget}." mode = ( gtk.gdkgl.MODE_ACCUM | gtk.gdkgl.MODE_RGB | gtk.gdkgl.MODE_DEPTH | gtk.gdkgl.MODE_DOUBLE | gtk.gdkgl.MODE_STENCIL) def __init__(self, **kwargs): glconfig = gtk.gdkgl.Config(mode=self.mode) gtk.gtkgl.widget.DrawingArea.__init__(self, glconfig) Camera.__init__(self, **kwargs) self.connect('expose-event', self.expose) def __enter__(self): context = gtk.gtkgl.widget_get_gl_context(self) drawable = self.get_gl_drawable() if not drawable.gl_begin(context): raise RuntimeError("couldn't begin GL drawing") return drawable.get_size() def __exit__(self, type, exception, traceback): drawable = self.get_gl_drawable() drawable.gl_end() drawable.swap_buffers() def expose(self, widget, e): with self as (w, h): (self.context['w'], self.context['h']) = (w, h) Camera.render(self, None) def refresh(self): self.queue_draw() def refresh_now(self): self.refresh() while gtk.events_pending(): gtk.main_iteration() def make_window(self, fullscreen=False): w = gtk.Window() w.connect('destroy', lambda w: gtk.main_quit()) w.add(self) if fullscreen: w.fullscreen() return w def run(self,fullscreen=False): w = self.make_window(fullscreen) w.show_all() try: gtk.main() except KeyboardInterrupt: pass glitch-0.6/glitch/gtk/reload.py0000644000175000017500000000474711532271477014603 0ustar dafdaf import sys import weakref import gio def load_module(path): source = file(path).read() code = compile(source, path, 'exec') globals_dict = {} exec code in globals_dict return globals_dict class Reload(object): """Updates instances when class source code changes.""" _reload_monitors = {} _reload_instances = {} _rate_limit = None def __init__(self): class_name = self.__class__.__name__ module_name = self.__module__ #print class_name if class_name not in self._reload_monitors: module_path = sys.modules[module_name].__file__ self._reload_monitors[class_name] = \ self._create_monitor(module_path) instances = self._reload_instances.setdefault( class_name, weakref.WeakKeyDictionary()) instances[self] = True def _create_monitor(self, path): monitor = gio.File(path).monitor() monitor.connect('changed', self._file_changed) return monitor def _update_instance(self, cls, instance): instance.__class__ = cls def _update_class(self, class_name, cls): for instance in self._reload_instances[class_name].keys(): self._update_instance(cls, instance) def _file_changed(self, monitor, file, _other_file, event_type): if event_type != gio.FILE_MONITOR_EVENT_CHANGES_DONE_HINT: return self._reload() def _reload(self): class_name = self.__class__.__name__ module_name = self.__module__ module_path = sys.modules[module_name].__file__ try: globals_dict = load_module(module_path) except Exception, e: # XXX: Should use a logger for this. print repr(e) return cls = globals_dict.get(class_name) if cls is None: # The class we're trying to reload is no longer defined in # the file that it originally came from. Not much we can # do about that. raise Warning('Class missing on reload', file.get_path(), class_name) return # Setting this means that repr() gives consistent results. cls.__module__ = module_name self._update_class(class_name, cls) # It seems that if this file gets loaded, this class gets # re-evaluated and hence the variables get reset. Since we # don't handle this gracefully, check it doesn't happen. assert self._reload_instances glitch-0.6/glitch/gtk/__init__.py0000644000175000017500000000017011532271477015056 0ustar dafdaf "Gtk+ integration." from glitch.gtk.gtkcamera import GtkCamera from glitch.gtk.gtkmovingcamera import GtkMovingCamera glitch-0.6/glitch/PIL/0000755000175000017500000000000011545146453012605 5ustar dafdafglitch-0.6/glitch/PIL/__init__.py0000644000175000017500000000020111532271477014710 0ustar dafdaf "Python Imaging Library integration." from image import ImageTexture, ImageSequenceTexture from imagecamera import ImageCamera glitch-0.6/glitch/PIL/imagecamera.py0000644000175000017500000000251511544723452015414 0ustar dafdaf from __future__ import with_statement "A camera that renders to an image file." import Image from glitch.camera import Camera from glitch.read import Read from glitch.fbo import BufferCamera from glitch.glx import GLXContext class ImageCamera(Camera): "A camera that renders to an image file." def __init__(self, width, height, out, *args, **kwargs): self.out = out Camera.__init__(self, *args, **kwargs) self.context['w'] = width self.context['h'] = height self.context['frame'] = 0 self.glx_context = GLXContext() def render(self, parent_ctx=None): with self.glx_context: read = Read(children=self.children) bc = BufferCamera( # XXX: Nicer way to pass values through? eye=self.eye, ref=self.ref, up=self.up, fovy=self.fovy, fovx=self.fovx, zNear=self.zNear, zFar=self.zFar, clear=self.clear, width=self.context['w'], height=self.context['h'], children=[read]) bc.render(None) im = Image.fromstring('RGB', (self.context['w'], self.context['h']), read.get_pixbuf().get_pixels()) # XXX: Return the image somehow instead of saving it. im.save(self.out % (self.context['frame'])) self.context['frame'] += 1 glitch-0.6/glitch/PIL/image.py0000644000175000017500000000275511532271477014253 0ustar dafdaf "PIL image texture." import PIL.Image from glitch.texture import Texture class ImageTexture(Texture): "PIL image texture." def __init__(self, image): (width, height) = image.size if image.mode == 'RGBA': data = image.tostring("raw", "RGBA", 0, -1) elif image.mode == 'RGBX' or image.mode == 'RGB': data = image.tostring("raw", "RGBX", 0, -1) else: raise ValueError Texture.__init__(self, width, height, data) @classmethod def from_file(cls, path): return cls(PIL.Image.open(path)) def set_from_image(self, image): (self.width, self.height) = image.size self.data = image.tostring("raw", "RGBX", 0, -1) def load_file(self, path): image = PIL.Image.open(path) self.set_from_image(image) class ImageSequenceTexture(ImageTexture): """A sequence of images. When rendered, this loads the current image based on the context value C{frame}. """ def __init__(self, pattern): """pattern should be in the form `image-%06d.jpg' to refer to a sequence of images like image-000001.jpg""" self.pattern = pattern self.version = 1 self.load_file(pattern % (1)) # XXX avoid forced initialization? def upload(self, ctx): self.load_file(self.pattern % (ctx['frame'])) ImageTexture.upload(self, ctx) def update(self, ctx): self.version = ctx['frame'] ImageTexture.update(self, ctx) glitch-0.6/glitch/limbo/0000755000175000017500000000000011545146453013263 5ustar dafdafglitch-0.6/glitch/limbo/particles.py0000644000175000017500000000272011532271477015625 0ustar dafdaf "Particle systems using numpy." from __future__ import with_statement import OpenGL.GL as gl import OpenGL.arrays.vbo as vbo import glitch class ParticleSystem(glitch.Node): def __init__(self, size, **kw): glitch.Node.__init__(self, **kw) self.size = size # make_particles() should return a numpy array has the shape (size, # 10), where 10 is the number of fields (x, y, z, dx, dy, dz, ddz, # ddy, ddz, ttl). self.particles = self.make_particles(size) assert self.particles is not None def step(self, amount): a = self.particles a[:,0:3] += amount * a[:,3:6] self.accelerate(amount) a[:,9] -= amount # replace dead particles dead = a[:,9] < 0 a[dead,:] = self.make_particles(len(a[dead,:])) def accelerate(self, amount): a = self.particles a[:,3:6] += amount * a[:,6:9] def draw(self, ctx): # In principle, we should just have one VBO and update it on each # draw, but it doesn't seem to be any faster in practice. buffer = vbo.VBO(self.particles) buffer.bind() gl.glEnableClientState(gl.GL_VERTEX_ARRAY) # Three coordinates per vertex, with a stride of sizeof(float) * the # number of fields. gl.glVertexPointer(3, gl.GL_FLOAT, 40, buffer) gl.glDrawArrays(gl.GL_POINTS, 0, self.size) gl.glDisableClientState(gl.GL_VERTEX_ARRAY) buffer.unbind() glitch-0.6/glitch/limbo/lights.py0000644000175000017500000001055511544723452015134 0ustar dafdaf "Lighting." from __future__ import with_statement import weakref import OpenGL.GL as gl import OpenGL.GLU as glu import glitch class LightSwitch(glitch.Node): def __init__(self, lights=True, **kw): glitch.Node.__init__(self, **kw) self.lights = lights def render(self, ctx): with ctx.push('lighting', True): gl.glPushAttrib(gl.GL_ENABLE_BIT) if self.lights: gl.glEnable(gl.GL_LIGHTING) else: gl.glDisable(gl.GL_LIGHTING) glitch.Node.render(self, ctx) gl.glPopAttrib() class Light(glitch.Node): def __init__(self, x=0, y=0, z=0, intensity=1, attenuation=None, **kw): glitch.Node.__init__(self, **kw) self.i = intensity self.attenuation = attenuation (self.x, self.y, self.z) = (x, y, z) def get_light(self, ctx): lights = ctx.setdefault('lights', weakref.WeakKeyDictionary()) if self not in lights: n_lights = gl.glGetInteger(gl.GL_MAX_LIGHTS) v = lights.values() # Avoid using light 0 as it is a special case. for i in xrange(1, n_lights): if i not in v: lights[self] = i break return gl.GL_LIGHT0 + lights[self] def render(self, ctx): if not ctx.get('lighting', True): glitch.Node.render(self, ctx) return gl_light = self.get_light(ctx) # Treat as positional (rather than directional) by making w # nonzero. Confusing! gl.glLightfv(gl_light, gl.GL_POSITION, [self.x, self.y, self.z, 1.]) if self.attenuation is not None: (constant, linear, quadratic) = self.attenuation gl.glLightf(gl_light, gl.GL_CONSTANT_ATTENUATION, constant) gl.glLightf(gl_light, gl.GL_LINEAR_ATTENUATION, linear) gl.glLightf(gl_light, gl.GL_QUADRATIC_ATTENUATION, quadratic) gl.glEnable(gl_light) glitch.Node.render(self, ctx) gl.glDisable(gl_light) def draw(self, ctx): if ctx.get('debug', False): gl.glPushAttrib(gl.GL_LIGHTING_BIT | gl.GL_CURRENT_BIT) gl.glDisable(gl.GL_LIGHTING) gl.glColor(1, 1, 0.3) gl.glMatrixMode(gl.GL_MODELVIEW) gl.glTranslate(self.x, self.y, self.z) quad = glu.gluNewQuadric() glu.gluSphere(quad, 0.1, 15, 15) gl.glTranslate(-self.x, -self.y, -self.z) glu.gluDeleteQuadric(quad) gl.glPopAttrib() class AmbientLight(Light): def draw(self, ctx): Light.draw(self, ctx) gl_light = self.get_light(ctx) gl.glLightfv(gl_light, gl.GL_AMBIENT, [self.i, self.i, self.i, 1]) #[self.r, self.g, self.b, self.a]) class DiffuseLight(Light): def draw(self, ctx): Light.draw(self, ctx) gl_light = self.get_light(ctx) gl.glLightfv(gl_light, gl.GL_DIFFUSE, [self.i, self.i, self.i, 1]) class ConeLight(Light): "Draw and enable a light at the origin directed towards negative-Z axis" def __init__(self, cutoff=45.0, dx=0, dy=0, dz=-1, exp=0, **kwargs): Light.__init__(self, **kwargs) self.cutoff = cutoff (self.dx, self.dy, self.dz) = (dx, dy, dz) self.exp = exp # def draw(self, ctx): # if ctx.get('debug', False): # # The GLUT cone starts at the circular base; to model a light we # # need to start from the tip. Draw the tip at (0, 0, 0), with # # light direction along the -z axis, which is where we point # # GL_SPOT_DIRECTION. # gl.glColor(1, 1, 0.3) # q = glu.gluNewQuadric() # gl.glMatrixMode(gl.GL_MODELVIEW) # gl.glPushMatrix() # gl.glTranslatef(self.x, self.y, self.z-1.) # radius = tan(pi * (self.cutoff / 180.)) # glut.glutWireCone(radius, 0.3, 7, 7) # glu.gluDeleteQuadric(q) # gl.glPopMatrix() def render(self, ctx): gl_light = self.get_light(ctx) gl.glLightfv(gl_light, gl.GL_DIFFUSE, [self.i, self.i, self.i, 1.0]) gl.glLightfv(gl_light, gl.GL_SPOT_CUTOFF, self.cutoff) gl.glLightfv(gl_light, gl.GL_SPOT_EXPONENT, self.exp) gl.glLightfv(gl_light, gl.GL_SPOT_DIRECTION, [self.dx, self.dy, self.dz]) Light.render(self, ctx) glitch-0.6/glitch/limbo/cache.py0000644000175000017500000000117711511244525014676 0ustar dafdaf import OpenGL.GL as gl import glitch class Cached(glitch.Node): def _make_display_list(self, ctx): id = gl.glGenLists(1) gl.glNewList(id, gl.GL_COMPILE) self.draw(ctx) gl.glEndList() return id def _cached_draw(self, ctx, key): lists = ctx.setdefault('display_lists', {}) if key in lists: list_id = lists[key] else: list_id = lists[key] = self._make_display_list(ctx) gl.glCallList(list_id) def render(self, ctx): self._cached_draw(ctx, key=self.__class__) for c in self.children: c.render(ctx) glitch-0.6/glitch/limbo/objects.py0000644000175000017500000001377711544723452015304 0ustar dafdaf import OpenGL.GL as gl import OpenGL.GLU as glu import OpenGL.GLUT as glut import glitch import glitch.limbo.lights from glitch.limbo.material import Material glut.glutInit() class Teapot(glitch.Node): def __init__(self, size=1, **kw): glitch.Node.__init__(self, **kw) self.size = size def draw(self, ctx): glut.glutSolidTeapot(self.size) class Square(glitch.Node): "A unit square on the XY plane." def draw(self, ctx): gl.glBegin(gl.GL_QUADS) gl.glNormal3f(0, 0, -1) gl.glVertex3f(0, 0, 0) gl.glVertex3f(1, 0, 0) gl.glVertex3f(1, 1, 0) gl.glVertex3f(0, 1, 0) gl.glEnd() class Grid(glitch.Node): "A 10x10 grid in a unit square on the XY plane." def draw(self, ctx): gl.glBegin(gl.GL_LINES) for x in xrange(11): gl.glVertex3f(0.1 * x, 0, 0) gl.glVertex3f(0.1 * x, 1, 0) for y in xrange(11): gl.glVertex3f(0, 0.1 * y, 0) gl.glVertex3f(1, 0.1 * y, 0) gl.glEnd() class Mesh(glitch.Node): "Like a Grid, but can be textured & illuminated" def draw(self, ctx): num_subdivisions = 20 step_size = 1.0 / num_subdivisions tex_width= 1.0 tex_height= 1.0 for sy in range(num_subdivisions): gl.glBegin(gl.GL_TRIANGLE_STRIP) gl.glNormal3f(0., 0., 1.) y1 = sy * step_size y2 = y1 + step_size for sx in range(num_subdivisions + 1): x1 = sx * step_size # XXX: texture upside down to match gstreamer expectations (?) gl.glTexCoord2f(x1 * tex_width, tex_height - y1 * tex_height) gl.glVertex3f(x1, y1, 0.0) gl.glTexCoord2f(x1 * tex_width, tex_height - y2 * tex_height) gl.glVertex3f(x1, y2, 0.0) gl.glEnd() class Cube(glitch.Node): def __init__(self, size=1, **kw): glitch.Node.__init__(self, **kw) self.size = size def draw(self, ctx): # XXX: This is centered around the origin. This is inconsistent with # other Glitch objects which fill the unit cube from (0, 0, 0) to (1, # 1, 1). glut.glutSolidCube(self.size) class Sphere(glitch.Node): def __init__(self, radius=0.5, orientation=glu.GLU_OUTSIDE, **kw): glitch.Node.__init__(self, **kw) self.radius = radius self.orientation = orientation def draw(self, ctx): quad = glu.gluNewQuadric() glu.gluQuadricOrientation(quad, self.orientation) # XXX: This is centered around the origin. This is inconsistent with # other Glitch objects which fill the unit cube from (0, 0, 0) to (1, # 1, 1). glu.gluSphere(quad, self.radius, 32, 32) glu.gluDeleteQuadric(quad) class Axes(glitch.Node): def axis(self): gl.glPushMatrix() axis = glu.gluNewQuadric() glu.gluQuadricDrawStyle(axis, glu.GLU_FILL) glu.gluQuadricNormals(axis, glu.GLU_SMOOTH) glu.gluQuadricOrientation(axis, glu.GLU_OUTSIDE) glu.gluQuadricTexture(axis, glu.GLU_FALSE) glu.gluCylinder(axis, 0.05, 0.05, 1, 10, 1) gl.glPushMatrix() gl.glTranslate(0, 0, 1) glu.gluCylinder(axis, 0.07, 0, 0.1, 10, 1) gl.glPopMatrix() glu.gluDeleteQuadric(axis) gl.glPopMatrix() def axes(self): gl.glPushMatrix() gl.glRotate(90, 0, 1, 0) gl.glColor3f(1.0, 0, 0) gl.glMaterial(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT, [1.0, 0.0, 0.0, 0.8]) self.axis() gl.glPopMatrix() gl.glPushMatrix() gl.glRotate(-90, 1, 0, 0) gl.glColor3f(0, 1.0, 0) gl.glMaterial(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT, [0.0, 1.0, 0.0, 0.8]) self.axis() gl.glPopMatrix() gl.glColor3f(0, 0, 1.0) gl.glMaterial(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT, [0.0, 0.0, 1.0, 0.8]) self.axis() def draw(self, ctx): gl.glPushAttrib(gl.GL_LIGHTING_BIT) gl.glDisable(gl.GL_LIGHTING) self.axes() gl.glPopAttrib() def make_test_scene(): """ Generate a scene with a ground, a sky, and three objects. @rtype: A list of nodes. @return: The nodes in the scene. """ sky = Material(0.6, 0.7, 1.0, children=[ Sphere(radius=25, orientation=glu.GLU_INSIDE)]) ground = glitch.Translate(-30, -1, 30, children=[ glitch.Scale(60, 1, 60, children=[ glitch.Rotate(90, -1, children=[ Material(0.7, 0.6, 0.5, children=[Mesh()])])])]) teapot = glitch.Translate(-2, children=[ Material(g=1, children=[Teapot(size=0.5)])]) cube = glitch.Translate(x=2, children=[ Material(g=1, children=[Cube()])]) sphere = glitch.Translate(z=-2, children=[ Material(g=1, children=[Sphere()])]) return glitch.Node([sky, ground, teapot, cube, sphere]) def make_lit_test_scene(): """ As L{make_test_scene}, but with lighting. @rtype: L{Node} @return: A node containing the lit scene. """ return glitch.limbo.lights.LightSwitch(children=[ glitch.limbo.lights.AmbientLight(intensity=0.3, children=[ glitch.limbo.lights.DiffuseLight(intensity=0.4, children=[ make_test_scene()])])]) class ColorfulTriangle(glitch.Node): def draw(self, ctx): gl.glBegin(gl.GL_TRIANGLES) gl.glColor3f(1, 0, 0) gl.glVertex(-1, 0, 0) gl.glColor3f(0, 1, 0) gl.glVertex(0, 1, 0) gl.glColor3f(0, 0, 1) gl.glVertex(1, 0, 0) gl.glEnd() class Cylinder(glitch.Node): def __init__(self, base=0.1, top=0.1, height=1.0, slices=20, stacks=1, **kw): glitch.Node.__init__(self, **kw) (self.base, self.top, self.height, self.slices, self.stacks) = (base, top, height, slices, stacks) def draw(self, ctx): quad = glu.gluNewQuadric() glu.gluCylinder(quad, self.base, self.top, self.height, self.slices, self.stacks) glu.gluDeleteQuadric(quad) glitch-0.6/glitch/limbo/blend.py0000644000175000017500000000103011532271477014714 0ustar dafdaf "Source/destination blending using C{glBlendFunc}." import OpenGL.GL as gl import glitch class Blend(glitch.Node): def __init__( self, src=gl.GL_SRC_ALPHA, dst=gl.GL_ONE_MINUS_SRC_ALPHA, **kw): glitch.Node.__init__(self, **kw) self.src = src self.dst = dst def render(self, ctx): gl.glPushAttrib(gl.GL_COLOR_BUFFER_BIT | gl.GL_ENABLE_BIT) gl.glBlendFunc(self.src, self.dst) gl.glEnable(gl.GL_BLEND) glitch.Node.render(self, ctx) gl.glPopAttrib() glitch-0.6/glitch/limbo/point.py0000644000175000017500000000405611532271477014774 0ustar dafdaf "Point/vector arithmetic." from math import sqrt class Point(object): "A point in 3D space." __slots__ = ('px', 'py', 'pz') def __init__(self, x, y, z): self.px = x self.py = y self.pz = z def __add__(self, vec): "Add a vector to this point, returning another point." return Point( self.px + vec.vx, self.py + vec.vy, self.pz + vec.vz) def __sub__(self, other): "Subtract another point from this one, returning a vector." return Vector( self.px - other.px, self.py - other.py, self.pz - other.pz) def __iter__(self): return iter((self.px, self.py, self.pz)) def __repr__(self): return '' % (self.px, self.py, self.pz) class Vector(object): "A vector in 3D space." __slots__ = ('vx', 'vy', 'vz') def __init__(self, x, y, z): self.vx = x self.vy = y self.vz = z def __iter__(self): return iter((self.vx, self.vy, self.vz)) def __neg__(self): return Vector(-self.vx, -self.vy, -self.vz) def __add__(self, other): return Vector( self.vx + other.vx, self.vy + other.vy, self.vz + other.vz) def __mul__(self, n): return Vector(self.vx * n, self.vy * n, self.vz * n) def __div__(self, n): return Vector(self.vx / n, self.vy / n, self.vz / n) def length(self): "Compute this vector's length." return sqrt(self.vx ** 2 + self.vy ** 2 + self.vz ** 2) def normalize(self): "Convert this vector to a unit vector." return self / self.length() def __repr__(self): return '' % (self.vx, self.vy, self.vz) def cross(a, b): "Compute cross product of two vectors." return Vector( a.vy * b.vz - a.vz * b.vy, a.vz * b.vx - a.vx * b.vz, a.vx * b.vy - a.vy * b.vx) def normal(v1, v2, v3): "Compute normal given three vectors." return cross(v2 - v1, v3 - v2) glitch-0.6/glitch/limbo/fog.py0000644000175000017500000000151511532271477014413 0ustar dafdaf "Fog." import OpenGL.GL as gl import glitch class Fog(glitch.Node): "Set OpenGL fog parameters." def __init__(self, r=0.2, g=0.2, b=0.2, a=0, density=0.3, start=0, end=1, **kw): glitch.Node.__init__(self, **kw) (self.r, self.g, self.b, self.a) = (r, g, b, a) self.density = density (self.start, self.end) = start, end def render(self, ctx): gl.glPushAttrib(gl.GL_ENABLE_BIT) gl.glEnable(gl.GL_FOG) gl.glFogi(gl.GL_FOG_MODE, gl.GL_EXP2) gl.glFogfv(gl.GL_FOG_COLOR, [self.r, self.g, self.b, self.a]) gl.glFogf(gl.GL_FOG_DENSITY, self.density) gl.glFogf(gl.GL_FOG_START, self.start) gl.glFogf(gl.GL_FOG_END, self.end) gl.glHint(gl.GL_FOG_HINT, gl.GL_NICEST) glitch.Node.render(self, ctx) gl.glPopAttrib() glitch-0.6/glitch/limbo/shaderprogram.py0000644000175000017500000000644411544723452016502 0ustar dafdaf# Experimental facilities for programmatically creating and # compositing shaders # Currently broken class ShaderProgram(object): def __init__(self, varying={}, uniform={}, frag="", vert=""): self.varying, self.uniform, self.frag, self.vert = (varying, uniform, frag, vert) def get_varying(self): return "\n".join([ "varying %s %s;" % (typ, ",".join(varlist)) for (typ, varlist) in self.varying.items()]) def get_uniform(self): return "\n".join([ "uniform %s %s;" % (typ, ",".join(varlist)) for (typ, varlist) in self.uniform.items()]) def get_fragshader_fn(self, name="get_fragment_color"): return "vec4 %s(){\n%s\n}" % (name, self.frag) def get_frag(self): return "%s\n%s\n%s\nvoid main() {\n%s\n}" % (self.get_varying(), self.get_uniform(), self.get_fragshader_fn("get_fragment_color"), "gl_FragColor = get_fragment_color();") def get_vert(self): return "%s\n%s\n\nvoid main() {\n%s\n}" % (self.get_varying(), self.get_uniform(), self.vert) class MultiShaderProgram(ShaderProgram): """creates a new shader by additively composing several""" def __init__(self, shaderprograms): self.progs = shaderprograms self.varying = {} for p in self.progs: for (k, v) in p.varying.items(): if self.varying.has_key(k): self.varying[k].extend(v) else: self.varying[k] = v self.uniform = {} for p in self.progs: for (k, v) in p.uniform.items(): if self.uniform.has_key(k): self.uniform[k].extend(v) else: self.uniform[k] = v self.vert = "\n".join(X.vert for X in self.progs) print self.varying print self.uniform print self.vert def mix_fn(self): return "return %s;" % (" + ".join([X+"()" for X in self.names])) def get_fragshader_fn(self, name="asp_frag_color"): acc = "" self.names = [] for i, p in enumerate(self.progs): tname = "%s_%d" % (name, i) self.names.append(tname) acc += p.get_fragshader_fn(tname) + '\n' acc += 'vec4 %s() {\n%s\n}' % (name, self.mix_fn()) return acc class AdditiveShaderProgram(MultiShaderProgram): pass class MultiplyShaderProgram(MultiShaderProgram): def mix_fn(self): return "return %s;" % (" * ".join([X+"()" for X in self.names])) class SubtractShaderProgram(MultiShaderProgram): def mix_fn(self): return "return %s() - (%s);" % (self.names[0], " + ".join([X+"()" for X in self.names[1:]])) if __name__=='__main__': varying = {'vec4': ['vertex_color']} vert = """gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; vertex_color = gl_Color;""" frag = """return vertex_color ;""" p = ShaderProgram(varying=varying, vert=vert, frag=frag) sp = AdditiveShaderProgram([p,p,p,p,p]) import glitch from glitch.limbo.objects import ColorfulTriangle shadernode = glitch.Shader(children=[ColorfulTriangle()]) shadernode.vertex = sp.get_vert() shadernode.fragment = sp.get_frag() import glitch.gtk camera = glitch.gtk.GtkCamera( children=[shadernode]) camera.run() glitch-0.6/glitch/limbo/__init__.py0000644000175000017500000000003711532271477015375 0ustar dafdaf"Miscellaneous useful things." glitch-0.6/glitch/limbo/unitview.py0000644000175000017500000000136511544723452015513 0ustar dafdaf import OpenGL.GL as gl import glitch import glitch.gtk class UnitView(glitch.Node): """Unit view. Sets the model view and projection matrices so that (0, 0, z) is the bottom left corner of the screen and (1, 1, z) is the top right. """ def render(self, ctx): gl.glMatrixMode(gl.GL_MODELVIEW) gl.glPushMatrix() gl.glLoadIdentity() gl.glMatrixMode(gl.GL_PROJECTION) gl.glPushMatrix() gl.glLoadIdentity() tmp = glitch.Translate(x=-1, y=-1, z=0, children=[ glitch.Scale(x=2, y=2, children=self.children)]) tmp.render(ctx) gl.glMatrixMode(gl.GL_PROJECTION) gl.glPopMatrix() gl.glMatrixMode(gl.GL_MODELVIEW) gl.glPopMatrix() glitch-0.6/glitch/limbo/lines.py0000644000175000017500000000113011532271477014743 0ustar dafdaf "Line drawing." import OpenGL.GL as gl import glitch class Lines(glitch.Node): def __init__(self, vs, **kw): glitch.Node.__init__(self, **kw) self.vs = vs def draw(self, ctx): gl.glBegin(gl.GL_LINES) for (x, y, z) in self.vs: gl.glVertex(x, y, z) gl.glEnd() class LineStrip(glitch.Node): def __init__(self, vs, **kw): glitch.Node.__init__(self, **kw) self.vs = vs def draw(self, ctx): gl.glBegin(gl.GL_LINE_STRIP) for (x, y, z) in self.vs: gl.glVertex(x, y, z) gl.glEnd() glitch-0.6/glitch/limbo/mesh.py0000644000175000017500000000323211511244525014561 0ustar dafdaf import OpenGL.GL as gl import glitch from glitch.limbo.point import Point, normal class Mesh(glitch.Node): def __init__(self): glitch.Node.__init__(self) self.vertices = [] self.edges = [] def draw(self, ctx): if ctx.get('debug', False): gl.glPushAttrib(gl.GL_POLYGON_BIT | gl.GL_LIGHTING_BIT) gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE) gl.glBegin(gl.GL_TRIANGLES) for (i1, i2, i3) in self.faces: (v1, v2, v3) = ( Point(*self.vertices[i1]), Point(*self.vertices[i2]), Point(*self.vertices[i3])) # XXX: Precalculate normals. n = normal(v1, v2, v3) gl.glNormal(*n.normalize()) gl.glVertex(*v1) gl.glVertex(*v2) gl.glVertex(*v3) gl.glEnd() if ctx.get('debug', False): # Draw normals. gl.glBegin(gl.GL_LINES) gl.glMaterial( gl.GL_FRONT, gl.GL_AMBIENT_AND_DIFFUSE, (0, 1, 0, 1)), for (i1, i2, i3) in self.faces: (v1, v2, v3) = ( Point(*self.vertices[i1]), Point(*self.vertices[i2]), Point(*self.vertices[i3])) n = normal(v1, v2, v3) p1 = Point( (float(v1.px) + v2.px + v3.px) / 3, (float(v1.py) + v2.py + v3.py) / 3, (float(v1.pz) + v2.pz + v3.pz) / 3) p2 = p1 + n.normalize() / 10 gl.glVertex(*p1) gl.glVertex(*p2) gl.glEnd() gl.glPopAttrib() glitch-0.6/glitch/limbo/spread.py0000644000175000017500000000070211511244525015102 0ustar dafdaf import OpenGL.GL as gl import glitch class Spread(glitch.Node): def __init__(self, x=0, y=0, z=0, **kw): glitch.Node.__init__(self, **kw) self.x = x self.y = y self.z = z def render(self, ctx): gl.glMatrixMode(gl.GL_MODELVIEW) gl.glPushMatrix() for child in self.children: child.render(ctx) gl.glTranslate(self.x, self.y, self.z) gl.glPopMatrix() glitch-0.6/glitch/limbo/vertices.py0000644000175000017500000000204411544711372015456 0ustar dafdaf """Draw vertices from numpy arrays.""" import OpenGL.GL as gl import OpenGL.arrays.vbo as vbo import glitch class DrawVertexArray(glitch.Node): def __init__(self, mode, array): glitch.Node.__init__(self) self.mode = mode self.array = array assert self.array.shape[1] == 2 if mode == gl.GL_LINES: assert self.array.shape[0] % 2 == 0 # This is 2 * sizeof(float). self.stride = 8 elif mode == gl.GL_TRIANGLES: assert self.array.shape[0] % 3 == 0 self.stride = 8 else: raise ValueError def draw(self, ctx): buffer = vbo.VBO(self.array) buffer.bind() gl.glEnableClientState(gl.GL_VERTEX_ARRAY) # Three coordinates per vertex, with a stride of sizeof(float) * the # number of fields. gl.glVertexPointer(2, gl.GL_FLOAT, self.stride, buffer) gl.glDrawArrays(self.mode, 0, self.array.shape[0]) gl.glDisableClientState(gl.GL_VERTEX_ARRAY) buffer.unbind() glitch-0.6/glitch/limbo/material.py0000644000175000017500000000077611511244525015435 0ustar dafdaf import OpenGL.GL as gl import glitch class Material(glitch.Node): def __init__(self, r=0, g=0, b=0, a=1, **kw): glitch.Node.__init__(self, **kw) (self.r, self.g, self.b, self.a) = (r, g, b, a) def render(self, ctx): gl.glPushAttrib(gl.GL_LIGHTING_BIT) glitch.Node.render(self, ctx) gl.glPopAttrib() def draw(self, ctx): gl.glMaterialfv( gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT_AND_DIFFUSE, (self.r, self.g, self.b, self.a)) glitch-0.6/glitch/multicontext.py0000644000175000017500000000305211533631766015275 0ustar dafdaf "Base class for objects that have per-context state." class CacheEntry(object): def __init__(self, version, data): self.version = version self.data = data class MultiContext(object): "Base class for objects that have per-context state." def __init__(self): self.version = 0 def _context_create(self, ctx): "Create per-context state." raise NotImplementedError() def _context_update(self, ctx, old_data): "Update per-context state." raise NotImplementedError() def _context_delete(self, ctx, obj): "Delete per-context state." raise NotImplementedError() def context_delete(self, ctx): "Remove this object from the given context." cache = ctx.setdefault(self._context_key, {}) entry = cache.get(self) if entry is not None: self._context_delete(ctx, entry.data) del cache[self] def context_get(self, ctx): """Get the instantiation of this object in the given context. This instantiates the object in the context if necessary. """ cache = ctx.setdefault(self._context_key, {}) entry = cache.get(self) if entry is not None: if entry.version < self.version: data = self._context_update(ctx, entry.data) entry.version = self.version entry.data = data else: data = self._context_create(ctx) entry = cache[self] = CacheEntry(self.version, data) return entry.data glitch-0.6/glitch/cairo/0000755000175000017500000000000011545146453013256 5ustar dafdafglitch-0.6/glitch/cairo/text.py0000644000175000017500000000426111532271477014620 0ustar dafdaf "Text rendering using Cairo and Pango." import cairo import pango import pangocairo from glitch.cairo.draw import CairoTexture class Text(CairoTexture): "Text rendering using Cairo and Pango." def __init__(self, text, font_spec='Sans 64', bg=(0, 0, 0, 0), fg=(1, 1, 1, 1), **kw): """ @param text: The text to render. @param font_spec: A Pango font description string. @param bg: The background color, as an ARGB tuple. @param fg: The foreground color, as an ARGB tuple. """ self.text = text self.font_spec = font_spec self.bg = bg self.fg = fg (width, height) = self._measure_fake() CairoTexture.__init__(self, width, height, **kw) def make_font_description(self): return pango.FontDescription(self.font_spec) def _measure_fake(self): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1) cr = cairo.Context(surface) return self.measure(cr) def measure(self, cr): pcr = pangocairo.CairoContext(cr) layout = self.make_layout(pcr) (logical, inked) = layout.get_pixel_extents() (ix, iy, iw, ih) = inked return (iw, ih) def make_layout(self, pcr): layout = pcr.create_layout() desc = self.make_font_description() layout.set_font_description(desc) layout.set_text(self.text) return layout def _clear(self, cr): cr.save() cr.set_operator(cairo.OPERATOR_SOURCE) cr.rectangle(0, 0, self.width, self.height) cr.set_source_rgba(*self.bg) cr.fill() cr.restore() def _debug(self, cr): cr.set_source_rgb(0.6, 0.6, 0.6) cr.rectangle(0, 0, self.width, self.height) cr.stroke() # XXX: This should be at the baseline, not at the bottom. cr.move_to(0, self.height) cr.rel_line_to(self.width, 0) cr.set_source_rgb(1, 0, 0) cr.stroke() def draw_cairo(self, cr): self._clear(cr) #self._debug(cr) pcr = pangocairo.CairoContext(cr) layout = self.make_layout(pcr) cr.set_source_rgba(*self.fg) pcr.show_layout(layout) glitch-0.6/glitch/cairo/draw.py0000644000175000017500000000217611532271477014574 0ustar dafdaf "A texture using Cairo for 2D drawing." import cairo import OpenGL.GL as gl from glitch.texture import Texture class CairoTexture(Texture): "A texture using Cairo for 2D drawing." y_flip = True def __init__(self, width, height): # Data is created later. Texture.__init__(self, width, height, None) self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) # XXX: This assumes little-endian. self.format = gl.GL_BGRA def draw_cairo(self, cr): """Called when the texture needs to be rendered. @param cr: The Cairo context to draw using. """ cr.set_source_rgba(0, 0, 0, 0) cr.paint() cr.set_source_rgb(1, 0, 0) cr.move_to(0, 0) cr.line_to(self.width, 0) cr.line_to(self.width, self.height) cr.line_to(0, self.height) cr.line_to(0, 0) cr.set_line_width(20) cr.stroke() def upload(self, ctx): cr = cairo.Context(self.surface) self.draw_cairo(cr) # XXX: Eww. self.data = str(self.surface.get_data()) Texture.upload(self, ctx) glitch-0.6/glitch/cairo/__init__.py0000644000175000017500000000016411532271477015371 0ustar dafdaf "Cairo integration for 2D drawing." from glitch.cairo.draw import CairoTexture from glitch.cairo.text import Text glitch-0.6/glitch/selectable.py0000644000175000017500000000043511532271477014641 0ustar dafdaf import OpenGL.GL as gl from glitch.node import Node class Selectable(Node): def render(self, ctx): if 'selection' in ctx: gl.glPushName(id(self)) Node.render(self, ctx) gl.glPopName() else: Node.render(self, ctx) glitch-0.6/glitch/fbo.py0000644000175000017500000001420111544723452013276 0ustar dafdaf "Framebuffer objects, for offscreen rendering." from __future__ import with_statement import OpenGL.GL as gl import OpenGL.GL.EXT.framebuffer_object as fbo from glitch.camera import Camera from glitch.texture import Texture from glitch.multicontext import MultiContext class RenderBuffer(MultiContext): "An abstract OpenGL render buffer." _context_key = 'render_buffer' def __init__(self, width, height): MultiContext.__init__(self) self.width = width self.height = height def _context_create(self, ctx): return fbo.glGenRenderbuffersEXT(1) def _context_delete(self, ctx, id): fbo.glDeleteRenderbuffersEXT(1, [id]) def bind(self, ctx): id = self.context_get(ctx) fbo.glBindRenderbufferEXT(gl.GL_RENDERBUFFER_EXT, id) #assert fbo.glIsRenderbufferEXT(id) return id def unbind(self, ctx): fbo.glBindRenderbufferEXT(gl.GL_RENDERBUFFER_EXT, 0) class DepthRenderBuffer(RenderBuffer): "An OpenGL depth render buffer." def _context_create(self, ctx): id = RenderBuffer._context_create(self, ctx) fbo.glBindRenderbufferEXT(gl.GL_RENDERBUFFER_EXT, id) fbo.glRenderbufferStorageEXT(gl.GL_RENDERBUFFER_EXT, gl.GL_DEPTH_COMPONENT, self.width, self.height) return id class ColorRenderBuffer(RenderBuffer): "An OpenGL color render buffer." format = gl.GL_RGBA8 def _context_create(self, ctx): id = RenderBuffer._context_create(self, ctx) fbo.glBindRenderbufferEXT(gl.GL_RENDERBUFFER_EXT, id) fbo.glRenderbufferStorageEXT(gl.GL_RENDERBUFFER_EXT, self.format, self.width, self.height) return id class FrameBuffer(MultiContext): "An OpenGL frame buffer." _context_key = 'framebuffer' def __init__(self, color_buffer, depth_buffer=None): MultiContext.__init__(self) self.color_buffer = color_buffer self.depth_buffer = depth_buffer def _context_create(self, ctx): id = fbo.glGenFramebuffersEXT(1) fbo.glBindFramebufferEXT(gl.GL_FRAMEBUFFER_EXT, id) if isinstance(self.color_buffer, Texture): with self.color_buffer.bind(ctx, 0) as color_buffer_id: # Attach texture to render to. fbo.glFramebufferTexture2DEXT(gl.GL_FRAMEBUFFER_EXT, gl.GL_COLOR_ATTACHMENT0_EXT, gl.GL_TEXTURE_2D, color_buffer_id, 0) else: color_buffer_id = self.color_buffer.bind(ctx) # Attach color buffer to render to. fbo.glFramebufferRenderbufferEXT(gl.GL_FRAMEBUFFER_EXT, gl.GL_COLOR_ATTACHMENT0_EXT, gl.GL_RENDERBUFFER_EXT, color_buffer_id) if self.depth_buffer is not None: depth_buffer_id = self.depth_buffer.bind(ctx) #self.depth_buffer.unbind(ctx) fbo.glFramebufferRenderbufferEXT(gl.GL_FRAMEBUFFER_EXT, gl.GL_DEPTH_ATTACHMENT_EXT, gl.GL_RENDERBUFFER_EXT, depth_buffer_id) self._check_status() return id def _context_delete(self, ctx, id): fbo.glDeleteFramebuffersEXT([id]) def bind(self, ctx): # XXX: Put this on a stack. self.previous_binding = gl.glGetIntegerv( fbo.GL_FRAMEBUFFER_BINDING_EXT) id = self.context_get(ctx) fbo.glBindFramebufferEXT(gl.GL_FRAMEBUFFER_EXT, id) def _check_status(self): status = fbo.glCheckFramebufferStatusEXT(gl.GL_FRAMEBUFFER_EXT) if status != fbo.GL_FRAMEBUFFER_COMPLETE_EXT: errors = dict((int(getattr(fbo, name)), getattr(fbo, name)) for name in dir(fbo) if 'INCOMPLETE' in name) raise RuntimeError(errors[status]) def unbind(self, ctx): fbo.glBindFramebufferEXT(gl.GL_FRAMEBUFFER_EXT, self.previous_binding) class CameraTexture(Texture): """Render a scene to a texture. A CameraTexture behaves like a Camera, in that it takes the same parameters as a Camera, and it behaves like a Texture, in that it can be passed to ApplyTexture. """ _context_key = 'camera_texture' def __init__(self, width, height, **kw): Texture.__init__(self, width, height, None) # XXX: Should probably just take a camera as an argument, or have a # texture as a property. self.camera = Camera(**kw) def _context_create(self, ctx): depth_buffer = DepthRenderBuffer(self.width, self.height) # Data is None: texture will be allocated but no texels loaded. texture = Texture(self.width, self.height, None) with texture.bind(ctx, 0): pass fbo = FrameBuffer(texture, depth_buffer) self._context_update(ctx, fbo) return fbo def _context_update(self, ctx, fbo): fbo.bind(ctx) self.camera.context['w'] = self.width self.camera.context['h'] = self.height self.camera.render(ctx) fbo.unbind(ctx) return fbo def _context_delete(self, ctx, fbo): fbo.color_buffer.context_delete(ctx) fbo.depth_buffer.context_delete(ctx) fbo.context_delete(ctx) def bind(self, ctx, unit): fbo = self.context_get(ctx) return fbo.color_buffer.bind(ctx, unit) def refresh(self): self.version += 1 class BufferCamera(Camera, MultiContext): "A camera that renders to a ColorRenderBuffer." _context_key = 'buffer_camera' def __init__(self, width, height, **kw): self.width = width self.height = height MultiContext.__init__(self) Camera.__init__(self, **kw) def _context_create(self, ctx): color_buffer = ColorRenderBuffer(self.width, self.height) depth_buffer = DepthRenderBuffer(self.width, self.height) fbo = FrameBuffer(color_buffer, depth_buffer) self._context_update(self, ctx, fbo) return fbo def _context_update(self, ctx, fbo): fbo.bind(ctx) self.context['w'] = self.width self.context['h'] = self.height Camera.render(self, ctx) fbo.unbind(ctx) return fbo def render(self, parent_ctx=None): self.context_get(self.context) glitch-0.6/glitch/glut/0000755000175000017500000000000011545146453013134 5ustar dafdafglitch-0.6/glitch/glut/camera.py0000644000175000017500000000162111511244525014726 0ustar dafdaf import OpenGL.GLUT as glut glut.glutInit() from glitch.camera import Camera class GLUTCamera(Camera): def __init__(self, title="glitch", fullscreen=False, **kw): Camera.__init__(self, **kw) self.window = glut.glutCreateWindow(title) self.width = 0 self.height = 0 if fullscreen: glut.glutFullScreen() glut.glutDisplayFunc(lambda: self.render(None)) glut.glutReshapeFunc(self._reshape) def _reshape(self, width, height): self.width = width self.height = height self.refresh() def render(self, parent_ctx): self.context['w'] = self.width self.context['h'] = self.height Camera.render(self, parent_ctx) glut.glutSwapBuffers() def refresh(self): glut.glutSetWindow(self.window) glut.glutPostRedisplay() def run(self): glut.glutMainLoop() glitch-0.6/glitch/glut/__init__.py0000644000175000017500000000011611532271477015244 0ustar dafdaf "GL Utility Toolkit integration." from glitch.glut.camera import GLUTCamera glitch-0.6/glitch/context.py0000644000175000017500000000272611544723452014225 0ustar dafdaf "The rendering context object." import copy _no_value = object() class Context(dict): """The rendering context object. The rendering context is used by a node to communicate state to its children. It acts like a dictionary, in that values are stored and retrieved by keys, and like a stack, in that its state can be saved and restored. """ def __init__(self, init=None): self.stack = [] if init is not None: dict.__init__(self, init) else: dict.__init__(self) def push(self, key, value=_no_value): """Save the current state, then save a value. @return: A value that pops the current state on the exit of a C{with} statement. """ if key in self: old = copy.copy(self[key]) def restore(): self[key] = old else: def restore(): if key in self: del self[key] self.stack.append(restore) if value is not _no_value: self[key] = value class Push(object): def __enter__(self_): pass def __exit__(self_, type, exc, tb): self.pop() return Push() def pop(self): "Pop the current state, undoing any changes since the last push." restore = self.stack.pop() restore() def __repr__(self): return 'Context(%s)' % dict.__repr__(self) glitch-0.6/glitch/transform.py0000644000175000017500000000310211532271477014543 0ustar dafdaf "Modelview matrix transformations." import OpenGL.GL as gl import glitch class Transform(glitch.Node): "A transformation of the modelview matrix." def render(self, ctx): gl.glMatrixMode(gl.GL_MODELVIEW) gl.glPushMatrix() glitch.Node.render(self, ctx) gl.glMatrixMode(gl.GL_MODELVIEW) gl.glPopMatrix() class Translate(Transform): "A translation." def __init__(self, x=0, y=0, z=0, **kwargs): """ @param x: X translation. @param y: Y translation. @param z: Z translation. """ Transform.__init__(self, **kwargs) self.x=x self.y=y self.z=z def draw(self, ctx): gl.glTranslate(self.x, self.y, self.z) class Scale(Transform): "A scaling." def __init__(self, x=1, y=1, z=1, **kwargs): """ @param x: X dimension scale. @param y: Y dimension scale. @param z: Z dimension scale. """ Transform.__init__(self, **kwargs) self.x=x self.y=y self.z=z def draw(self, ctx): gl.glScale(self.x, self.y, self.z) class Rotate(Transform): "A rotation." def __init__(self, angle=0, x=0, y=0, z=0, **kwargs): """ @param angle: The rotation angle. @param x: X magnitude. @param y: Y magnitude. @param z: Z magnitude. """ Transform.__init__(self, **kwargs) self.angle=angle self.x=x self.y=y self.z=z def draw(self, ctx): gl.glRotate(self.angle, self.x, self.y, self.z) glitch-0.6/glitch/color.py0000644000175000017500000000110011532271477013642 0ustar dafdaf "Color." import OpenGL.GL as gl import glitch class Color(glitch.Node): "Change the OpenGL color." def __init__(self, r=0, g=0, b=0, a=1, **kwargs): glitch.Node.__init__(self, **kwargs) self.r = r self.g = g self.b = b self.a = a def render(self, ctx): gl.glPushAttrib(gl.GL_CURRENT_BIT) glitch.Node.render(self, ctx) gl.glPopAttrib() def draw(self, ctx): if ctx.get('debug'): gl.glColor(0, 1, 0, 1) else: gl.glColor(self.r, self.g, self.b, self.a) glitch-0.6/COPYING0000644000175000017500000010451311511244525011736 0ustar dafdaf GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . glitch-0.6/interactive/0000755000175000017500000000000011545146453013224 5ustar dafdafglitch-0.6/interactive/live.py0000644000175000017500000000621611511244525014532 0ustar dafdaf from __future__ import with_statement import gobject import gtk import gtksourceview2 from glitch.gtk import GtkCamera create = """ import OpenGL.GL as gl import glitch from glitch.limbo.lights import ( DiffuseLight, LightSwitch) from glitch.limbo.objects import ( Cube, Sphere) from glitch.limbo.material import Material c = Material(r=1, children=[Cube()]) translate = glitch.Translate(children=[c]) camera.children = [LightSwitch(children=[ DiffuseLight(y=2, z=2, children=[translate])])] """ update = """ from math import sin translate.x = 2 * sin(t/10.0) camera.refresh() """ error_color = gtk.gdk.Color(1.0, 0.7, 0.7) white = gtk.gdk.Color(1.0, 1.0, 1.0) update_code = None t = 0 def make_srcview(initial_text): buffer = gtksourceview2.Buffer() buffer.set_language(python) buffer.set_text(initial_text) srcview = gtksourceview2.View(buffer) srcview.set_size_request(400, -1) frame = gtk.Frame() frame.props.shadow_type = gtk.SHADOW_IN frame.add(srcview) return (frame, srcview, buffer) def view_compile(view, name): buffer = view.props.buffer source = buffer.get_text(*buffer.get_bounds()) try: code = compile(source, name, 'exec') except Exception, e: view.modify_base(gtk.STATE_NORMAL, error_color) raise e else: view.modify_base(gtk.STATE_NORMAL, white) return code def change_timeout(view): global change_timeout_id global update_code print 'compiling...', with camera: try: if view == srcview1: code = view_compile(view, 'create') else: update_code = view_compile(view, 'update') except Exception, e: print print e else: print 'ok' if view == srcview1: try: exec code in globals() except Exception, e: print e change_timeout_id = None def tick(): global t t += 1 try: exec update_code in globals() except Exception, e: print e return True def buffer_changed(buffer, view): global change_timeout_id if change_timeout_id is not None: gobject.source_remove(change_timeout_id) change_timeout_id = gobject.timeout_add(1000, change_timeout, view) if __name__ == '__main__': change_timeout_id = None mgr = gtksourceview2.LanguageManager() python = mgr.get_language('python') w = gtk.Window() w.connect('destroy', lambda w: gtk.main_quit()) hbox = gtk.HBox() vbox = gtk.VBox(spacing=1) (srcview1_frame, srcview1, buffer1) = make_srcview(create) (srcview2_frame, srcview2, buffer2) = make_srcview(update) buffer1.connect('changed', buffer_changed, srcview1) buffer2.connect('changed', buffer_changed, srcview2) camera = GtkCamera(eye=[1, 0, 3]) vbox.pack_start(srcview1_frame, True) vbox.pack_start(srcview2_frame, True) hbox.pack_start(vbox, False) hbox.pack_start(camera, True) w.add(hbox) w.show_all() exec create update_code = view_compile(srcview2, 'update') gobject.timeout_add(1000 / 30, tick) gtk.main() glitch-0.6/interactive/start.py0000644000175000017500000000755111544723452014742 0ustar dafdafimport os, gtk, gtksourceview2 import glitch from glitch import * _default="""import glitch, glitch.gtk from glitch.limbo.objects import Square cam=glitch.gtk.GtkCamera(children=[ Square()]) cam.run() """ class Start(gtk.Window): def __init__(self, *args, **kwargs): gtk.Window.__init__(self) self.set_title("The Dagger") self.glue = gtk.VBox() self.add(self.glue) self.srcview = self._srcview() sw = gtk.ScrolledWindow() sw.add(self.srcview) toolbar=self._toolbar() self.nodes=self._nodes() hp= gtk.HPaned() hp.add1(self.nodes) hp.add2(sw) self.glue.pack_start(toolbar, False) self.glue.pack_start(hp) def do_run(self, *args): src= self.srcview.get_buffer().get_text(*self.srcview.get_buffer().get_bounds()) exec(src, {}) def do_open(self, *args): f=gtk.FileChooserDialog(action=gtk.FILE_CHOOSER_ACTION_OPEN,buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) response = f.run() if response == gtk.RESPONSE_OK: self.outfile=f.get_filename() self.srcview.get_buffer().set_text(open(self.outfile).read()) f.destroy() def do_save(self, *args): if not hasattr(self, "outfile"): f=gtk.FileChooserDialog(action=gtk.FILE_CHOOSER_ACTION_SAVE,buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) response = f.run() if response == gtk.RESPONSE_OK: self.outfile=f.get_filename() self.do_save() f.destroy() else: src= self.srcview.get_buffer().get_text(*self.srcview.get_buffer().get_bounds()) open(self.outfile, 'w').write(src) def _srcview(self): mgr = gtksourceview2.LanguageManager() python = mgr.get_language('python') buffer = gtksourceview2.Buffer() buffer.set_language(python) buffer.set_text(_default) return gtksourceview2.View(buffer) def _nodes(self): vp = gtk.VPaned() ls = gtk.ListStore(str) for node in dir(glitch): if isinstance(getattr(glitch, node), type): ls.append([node]) tv = gtk.TreeView(ls) renderer = gtk.CellRendererText() col= gtk.TreeViewColumn("Nodes", renderer) col.add_attribute(renderer, "text", 0) mgr = gtksourceview2.LanguageManager() python = mgr.get_language('python') docbuffer=gtksourceview2.Buffer() docbuffer.set_language(python) docs=gtksourceview2.View(docbuffer) def on_change(tv): (model, iter)=tv.get_selection().get_selected() sel= model.get_value(iter, 0) cls=eval(sel, globals()) mod=eval(cls.__module__, globals()) f=mod.__file__.replace('pyc', 'py') docbuffer.set_text(open(f).read()) tv.connect('cursor-changed', on_change) tv.append_column(col) sc_tv=gtk.ScrolledWindow() sc_tv.set_size_request(200,400) sc_tv.add(tv) sc_docs=gtk.ScrolledWindow() sc_docs.add(docs) vp.add1(sc_tv) vp.add2(sc_docs) return vp def _toolbar(self): toolbar = gtk.Toolbar() items = [("execute", gtk.STOCK_EXECUTE, self.do_run), ("save", gtk.STOCK_SAVE, self.do_save), ("open", gtk.STOCK_OPEN, self.do_open), ("close", gtk.STOCK_CLOSE, lambda w: gtk.main_quit())] for (label, icon, fn) in items: b = gtk.ToolButton(icon) b.set_label(label) toolbar.insert(b, 0) b.connect("clicked", fn) return toolbar if __name__=='__main__': s = Start() s.show_all() s.connect('destroy', lambda w: gtk.main_quit()) # todo: save? gtk.main() glitch-0.6/interactive/shader-editor.py0000644000175000017500000000670011511244525016323 0ustar dafdaf import gobject import gtk import gtksourceview2 import OpenGL.GL as gl import glitch, glitch.gtk from glitch.limbo.objects import ColorfulTriangle import OpenGL.GL.shaders as shaders vertex = """ varying vec4 vertex_color; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; vertex_color = gl_Color; } """ fragment = """ varying vec4 vertex_color; void main() { gl_FragColor = vertex_color; } """ error_color = gtk.gdk.Color(1.0, 0.7, 0.7) white = gtk.gdk.Color(1.0, 1.0, 1.0) class InteractiveShadedNode(glitch.Shader): def compile(self, ctx): print 'compiling...', try: vshader = shaders.compileShader(self.vertex, gl.GL_VERTEX_SHADER) except Exception, e: print print e srcview1.modify_base(gtk.STATE_NORMAL, error_color) return ctx['shaders'][self][1] else: srcview1.modify_base(gtk.STATE_NORMAL, white) try: fshader = shaders.compileShader(self.fragment, gl.GL_FRAGMENT_SHADER) except Exception, e: print print e srcview2.modify_base(gtk.STATE_NORMAL, error_color) return ctx['shaders'][self][1] else: srcview2.modify_base(gtk.STATE_NORMAL, white) print 'ok' program = shaders.compileProgram(vshader, fshader) return program def make_srcview(initial_text): buffer = gtksourceview2.Buffer() buffer.set_language(glsl) buffer.set_text(initial_text) srcview = gtksourceview2.View(buffer) srcview.set_size_request(400, -1) frame = gtk.Frame() frame.props.shadow_type = gtk.SHADOW_IN frame.add(srcview) return (frame, srcview, buffer) def change_timeout(vertex_buffer, fragment_buffer, camera, obj): global change_timeout_id obj.vertex = vertex_buffer.get_text(*vertex_buffer.get_bounds()) obj.fragment = fragment_buffer.get_text(*fragment_buffer.get_bounds()) obj.version += 1 camera.refresh() change_timeout_id = None def vertex_changed(buffer, fragment_buffer, camera, obj): global change_timeout_id if change_timeout_id is not None: gobject.source_remove(change_timeout_id) change_timeout_id = gobject.timeout_add(1000, change_timeout, buffer, fragment_buffer, camera, obj) def fragment_changed(buffer, vertex_buffer, camera, obj): global change_timeout_id if change_timeout_id is not None: gobject.source_remove(change_timeout_id) change_timeout_id = gobject.timeout_add(1000, change_timeout, vertex_buffer, buffer, camera, obj) if __name__ == '__main__': change_timeout_id = None mgr = gtksourceview2.LanguageManager() glsl = mgr.get_language('glsl') w = gtk.Window() w.connect('destroy', lambda w: gtk.main_quit()) hbox = gtk.HBox() vbox = gtk.VBox() vbox.set_spacing(1) (srcview1_frame, srcview1, buffer1) = make_srcview(vertex) (srcview2_frame, srcview2, buffer2) = make_srcview(fragment) obj = InteractiveShadedNode(children=[ColorfulTriangle()]) camera = glitch.gtk.GtkCamera(eye=[1, 0, 3], children=[obj]) buffer1.connect('changed', vertex_changed, buffer2, camera, obj) buffer2.connect('changed', fragment_changed, buffer1, camera, obj) vbox.pack_start(srcview1_frame, True) vbox.pack_start(srcview2_frame, True) hbox.pack_start(vbox, False) hbox.pack_start(camera, True) w.add(hbox) w.show_all() gtk.main() glitch-0.6/examples/0000755000175000017500000000000011545146453012525 5ustar dafdafglitch-0.6/examples/particles.py0000644000175000017500000000455611544723452015076 0ustar dafdaf from __future__ import with_statement import numpy import gobject import OpenGL.GL as gl import glitch, glitch.gtk from glitch.limbo.lights import LightSwitch, AmbientLight from glitch.limbo.material import Material from glitch.limbo.particles import ParticleSystem class Fountain(ParticleSystem): def make_particles(self, n): a = numpy.ndarray(shape=(n, 10), dtype=numpy.float32) # particles start at origin a[:,0:3] = 0 # velocities a[:,3] = numpy.random.normal(0, 0.02, n) * 4 a[:,4] = numpy.random.uniform(0.1, 0.3, n) * 4 a[:,5] = numpy.random.normal(0, 0.02, n) * 4 # accelerations a[:,6] = 0 a[:,7] = -0.1 a[:,8] = 0 # time to live a[:,9] = numpy.random.uniform(1, 20, n) return a class Fire(ParticleSystem): def make_particles(self, n): a = numpy.ndarray(shape=(n, 10), dtype=numpy.float32) # position a[:,0] = numpy.random.uniform(-0.4, 0.4) a[:,1] = 0 a[:,2] = numpy.random.uniform(-0.4, 0.4) # velocities a[:,3] = numpy.random.normal(0, 0.04, n) * 4 a[:,4] = numpy.random.uniform(0.1, 0.2, n) * 1 a[:,5] = numpy.random.normal(0, 0.04, n) * 4 # accelerations a[:,6] = 0.9 a[:,7] = 1.2 a[:,8] = 0.9 # time to live a[:,9] = numpy.random.uniform(1, 10, n) return a def accelerate(self, amount): a = self.particles a[:,3:6] *= a[:,6:9] ** amount def tick(): fountain.step(0.1) fire.step(0.1) camera.refresh() return True def set_point_size(widget): with camera: gl.glPointSize(2) if __name__ == '__main__': fountain = Fountain(1000) fire = Fire(1000) camera = glitch.gtk.GtkCamera(ref=[0, 4, 0], eye=[0, 4, 10], children=[ LightSwitch(children=[ AmbientLight(children=[ glitch.Translate(-4, children=[ glitch.Scale(0.7, 0.7, 0.7, children=[ Material(0.3, 0.5, 1.0, children=[fountain])]), glitch.Translate(4, children=[ glitch.Scale(0.7, 0.7, 0.7, children=[ Material(1.0, 0.3, 0.3, children=[fire])])])])])])]) camera.connect_after('realize', set_point_size) gobject.timeout_add(1000/30, tick) camera.run() glitch-0.6/examples/shader.py0000644000175000017500000000036611511244525014342 0ustar dafdafimport glitch, glitch.glut, glitch.limbo.objects if __name__ == '__main__': camera = glitch.glut.GLUTCamera(eye=[0, 0, 5], ref=[0, 0, 0], children=[glitch.Shader(children=[glitch.limbo.objects.ColorfulTriangle()])]) camera.run() glitch-0.6/examples/multiwin.py0000644000175000017500000000172311511244525014742 0ustar dafdaf import gobject import gtk import glitch, glitch.gtk from glitch.limbo.lights import LightSwitch, AmbientLight from glitch.limbo.material import Material from glitch.limbo.objects import Cube def tick(): global theta theta = (theta + 5) % 360 rotate.angle = theta view1.refresh() view2.refresh() return True if __name__ == '__main__': theta = 0 rotate = glitch.Rotate(y=1, children=[Cube()]) scene = LightSwitch(children=[ AmbientLight(2, 2, 2, children=[ Material(1.0, 0.5, 0.5, children=[rotate])])]) view1 = glitch.gtk.GtkCamera(eye=[0, 0, 2], children=[scene]) view2 = glitch.gtk.GtkCamera(eye=[0, 2, 0.01], children=[scene]) w1 = gtk.Window() w1.add(view1) w1.show_all() w2 = gtk.Window() w2.add(view2) w2.show_all() gobject.timeout_add(1000/30, tick) w1.connect('destroy', lambda w: gtk.main_quit()) w2.connect('destroy', lambda w: gtk.main_quit()) gtk.main() glitch-0.6/examples/cube.py0000644000175000017500000000062411511244525014007 0ustar dafdaf import glitch, glitch.glut from glitch.limbo.lights import LightSwitch, DiffuseLight from glitch.limbo.material import Material from glitch.limbo.objects import Cube camera = glitch.glut.GLUTCamera(eye=[-2, 2, 2], fovx=30, fovy=30, children=[ LightSwitch(children=[ DiffuseLight(x=-4, y=5, z=3, children=[ Material(g=0.6, children=[ Cube()])])])]) camera.run() glitch-0.6/examples/glx_offscreen.py0000644000175000017500000000162011544723452015721 0ustar dafdaf from __future__ import with_statement import glitch import glitch.limbo.objects from glitch.fbo import BufferCamera from glitch.glx import GLXContext from glitch.read import Read class Shader(glitch.Shader): vertex = """ varying vec4 pos; void main() { pos = gl_Vertex; gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; } """ fragment = """ varying vec4 pos; void main() { gl_FragColor = vec4(pos.x, 0.5, pos.y, 1); } """ if __name__ == '__main__': width = 320 height = 200 read = Read(children=[Shader(children=[glitch.limbo.objects.Square()])]) camera = BufferCamera( width, height, eye=[0.5, 0.5, 1], ref=[0.5, 0.5, 0], children=[read]) context = GLXContext() with context: camera.render() pb = read.get_pixbuf() pb.save("glx.png", "png") glitch-0.6/examples/gst/0000755000175000017500000000000011545146453013322 5ustar dafdafglitch-0.6/examples/gst/video.py0000644000175000017500000000071011511244525014770 0ustar dafdafimport sys import gst from glitch.gst import Video, VideoFileTexture import glitch, glitch.gtk if __name__ == '__main__': v = VideoFileTexture(path=sys.argv[1]) pipeline = gst.Pipeline() pipeline.add(v.bin) pipeline.set_state(gst.STATE_PLAYING) camera = glitch.gtk.GtkCamera(eye=[0.5, 0.5, 1], ref=[0.5, 0.5, 0], children=[Video(v)]) v.connect('new-buffer', lambda *args: camera.refresh()) camera.run() v.stop() glitch-0.6/examples/gst/videoblend.py0000644000175000017500000000314311511244525016000 0ustar dafdafimport sys import gobject import gst import OpenGL.GL.shaders as shaders gobject.threads_init() import glitch, glitch.gtk from glitch.gst import VideoLoop import OpenGL.GL.shaders as shaders class VideoBlend(glitch.Shader): vertex = """ void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = gl_MultiTexCoord0; } """ fragment = """ uniform sampler2D video1; uniform sampler2D video2; void main() { gl_FragColor = texture2D(video1, gl_TexCoord[0].st) / 2.0 + texture2D(video2, gl_TexCoord[0].st) / 2.0; } """ def set_uniforms(self, ctx, shader): loc = shaders.glGetUniformLocation(shader, 'video1') shaders.glUniform1i(loc, 0) loc = shaders.glGetUniformLocation(shader, 'video2') shaders.glUniform1i(loc, 1) if __name__ == '__main__': loop1 = VideoLoop(sys.argv[1]) loop2 = VideoLoop(sys.argv[2]) pipeline = gst.Pipeline() pipeline.add(loop1.bin) pipeline.add(loop2.bin) pipeline.set_state(gst.STATE_PLAYING) camera = glitch.gtk.GtkCamera(eye=[0.5, 0.5, 1], ref=[0.5, 0.5, 0], children=[ glitch.ApplyTexture(loop1, unit=0, children=[ glitch.ApplyTexture(loop2, unit=1, children=[ VideoBlend(children=[ glitch.TexturedRectangle()])])])]) loop1.connect('new-buffer', lambda *args: camera.refresh()) loop2.connect('new-buffer', lambda *args: camera.refresh()) camera.run() pipeline.set_state(gst.STATE_NULL) glitch-0.6/examples/gst/camerasrc.py0000644000175000017500000000133011532271477015632 0ustar dafdafimport gobject import gst import gtk from glitch.camera import Camera from glitch.gst.camerasrc import CameraSource from glitch.limbo.objects import make_lit_test_scene if __name__ == '__main__': gobject.threads_init() gtk.gdk.threads_init() camera = Camera(eye=[0, 2, 3], children=[make_lit_test_scene()]) src = CameraSource(camera) sink = gst.parse_bin_from_description( 'ffmpegcolorspace ! autovideosink', True) pipeline = gst.Pipeline() pipeline.add(src, sink) gst.element_link_many(src, sink) pipeline.set_state(gst.STATE_PLAYING) loop = gobject.MainLoop() try: loop.run() except KeyboardInterrupt: pass pipeline.set_state(gst.STATE_NULL) glitch-0.6/examples/gst/videoloop.py0000644000175000017500000000077011511244525015670 0ustar dafdaf import sys import gst import glitch, glitch.gtk from glitch.gst import VideoLoop if __name__ == '__main__': v = VideoLoop(path=sys.argv[1]) pipeline = gst.Pipeline() pipeline.add(v.bin) pipeline.set_state(gst.STATE_PLAYING) camera = glitch.gtk.GtkCamera(eye=[0.5, 0.5, 1], ref=[0.5, 0.5, 0], children=[ glitch.ApplyTexture(v, children=[glitch.TexturedRectangle()])]) v.connect('new-buffer', lambda *args: camera.refresh()) camera.run() v.stop() glitch-0.6/examples/objects.py0000644000175000017500000000036711532271477014537 0ustar dafdaf import glitch, glitch.glut from glitch.limbo.objects import make_lit_test_scene import glitch.limbo.lights if __name__ == '__main__': camera = glitch.glut.GLUTCamera(eye=[0, 1, 3], children=[make_lit_test_scene()]) camera.run() glitch-0.6/examples/select.py0000644000175000017500000000417311544723452014362 0ustar dafdaf from __future__ import with_statement import gtk import glitch, glitch.gtk from glitch.selectable import Selectable from glitch.limbo.lights import LightSwitch, AmbientLight, DiffuseLight from glitch.limbo.material import Material from glitch.limbo.objects import Cube, Sphere, Teapot def has_descendent(x, y): if y in x.children: return True else: for child in x.children: if has_descendent(child, y): return True return False def on_click(window, event, camera): global selected (x, y) = event.get_coords() old_selected = selected selected = None # XXX: This might cause a buffer swap that's redundant. with camera as (w, h): hits = camera.select(camera.context, x, (h - y)) if hits: for o in [teapot, cube, sphere]: if has_descendent(o, hits[-1]): selected = o if selected != old_selected: if old_selected is not None: (old_selected.r, old_selected.g, old_selected.b) = \ (0.2, 0.2, 0.3) if selected is not None: (selected.r, selected.g, selected.b) = (0.2, 0.2, 1.0) camera.refresh_now() if __name__=='__main__': selected = None teapot = Material(0.2, 0.2, 0.3, children=[ glitch.Translate(-2, children=[ Selectable(children=[Teapot(size=0.5)])])]) cube = Material(0.2, 0.2, 0.3, children=[ glitch.Translate(x=2, children=[ Selectable(children=[Cube()])])]) sphere = Material(0.2, 0.2, 0.3, children=[ glitch.Translate(z=-2, children=[ Selectable(children=[Sphere()])])]) camera = glitch.gtk.GtkCamera(eye=[0, 1, 3], children=[ LightSwitch(children=[ AmbientLight(intensity=0.3, children=[ DiffuseLight(intensity=0.4, children= [teapot, cube, sphere])])])]) w = camera.make_window() w.show_all() camera.add_events( gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK) camera.connect('motion-notify-event', on_click, camera) gtk.main() glitch-0.6/examples/multiview.py0000644000175000017500000000215511511244525015117 0ustar dafdaf import gobject import gtk import glitch, glitch.gtk from glitch.limbo.lights import LightSwitch, AmbientLight from glitch.limbo.material import Material from glitch.limbo.objects import Cube def tick(): global theta theta = (theta + 5) % 360 rotate.angle = theta view1.refresh() view2.refresh() view3.refresh() return True if __name__ == '__main__': theta = 0 rotate = glitch.Rotate(y=1, children=[Cube()]) scene = LightSwitch(children=[ AmbientLight(2, 2, 2, children=[ Material(1.0, 0.5, 0.5, children=[rotate])])]) w = gtk.Window() box = gtk.HBox() box.props.spacing = 1 # XXX: views don't share textures view1 = glitch.gtk.GtkCamera(eye=[0, 0, 2], children=[scene]) view2 = glitch.gtk.GtkCamera(eye=[0, 2, 0.01], children=[scene]) view3 = glitch.gtk.GtkCamera(eye=[1.5, 1.5, 1.5], children=[scene]) w.add(box) box.pack_start(view1, True) box.pack_start(view2, True) box.pack_start(view3, True) w.show_all() gobject.timeout_add(1000/30, tick) w.connect('destroy', lambda w: gtk.main_quit()) gtk.main() glitch-0.6/examples/sin.py0000644000175000017500000000167511511244525013671 0ustar dafdaf from math import sin import gobject import glitch, glitch.gtk from glitch.limbo.lights import LightSwitch, AmbientLight, DiffuseLight from glitch.limbo.material import Material from glitch.limbo.objects import Cube from glitch.limbo.spread import Spread def tick(): global t for i, cube in enumerate(cubes): cube.y = 0.1 + 3 * sin((-t + (i * 5.0)) / 15.0) t += 1 camera.refresh() return True if __name__ == '__main__': cubes = [glitch.Scale(children=[ Material(r=1, children=[ Cube()])]) for x in xrange(10)] camera = glitch.gtk.GtkCamera(eye=[0, 2, 6], ref=[0, 0, -1], children=[ LightSwitch(children=[ AmbientLight(intensity=0.4, children=[ DiffuseLight(y=5, children=[ glitch.Translate(x=-5, children=[ Spread(x=1.1, children=cubes)])])])])]) t = 0 gobject.timeout_add(1000/30, tick) camera.run() glitch-0.6/examples/fog.py0000644000175000017500000000035311532271477013654 0ustar dafdaf import glitch, glitch.glut from glitch.limbo.fog import Fog from glitch.limbo.objects import make_lit_test_scene camera = glitch.glut.GLUTCamera(eye=[0, 2, 3], children=[ Fog(children=[make_lit_test_scene()])]) camera.run() glitch-0.6/examples/bubbles.py0000644000175000017500000000300611532271477014515 0ustar dafdaf import gobject import numpy import glitch import glitch.gtk from glitch.limbo.lights import LightSwitch, DiffuseLight from glitch.limbo.objects import Sphere from glitch.limbo.particles import ParticleSystem from glitch.limbo.material import Material class Bubbles(ParticleSystem): def make_particles(self, n): a = numpy.ndarray(shape=(n, 11), dtype=numpy.float32) # position a[:,0] = numpy.random.uniform(-0.6, 0.6, n) a[:,1] = -1 a[:,2] = numpy.random.uniform(-0.6, 0.6, n) # size a[:,10] = numpy.random.uniform(1, 10, n) # velocities a[:,3] = 0 a[:,4] = 1 / a[:,10] a[:,5] = 0 # accelerations a[:,6] = 0 a[:,7] = 0 a[:,8] = 0 # time to live a[:,9] = numpy.random.uniform(5, 15, n) return a def draw(self, ctx): for (x, y, z, _dz, _dy, _dz, _ddx, _ddy, _ddz, _ttl, size) in self.particles: tmp = glitch.Translate(x=x, y=y, z=z, children=[ Sphere(radius=size / 100)]) tmp.render(ctx) def tick(): bubbles.step(0.2) camera.refresh() return True if __name__ == '__main__': bubbles = Bubbles(100) camera = glitch.gtk.GtkCamera(ref=[0, 1, 0], eye=[0, 1, 1], children=[ LightSwitch(children=[ DiffuseLight(children=[ Material(0.3, 0.5, 1.0, children=[bubbles])])])]) gobject.timeout_add(1000/30, lambda: bubbles.step(0.2) and camera.refresh() and True) camera.run() glitch-0.6/examples/unitview.py0000644000175000017500000000174611532271477014762 0ustar dafdaf import glitch import glitch.glut import glitch.limbo.lights import glitch.limbo.objects import glitch.limbo.unitview class Shader(glitch.Shader): vertex = """ varying vec4 vertex; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = gl_MultiTexCoord0; vertex = gl_Vertex; } """ fragment = """ varying vec4 vertex; uniform sampler2D texture; void main() { gl_FragColor = vertex * texture2D(texture, gl_TexCoord[0].st); } """ if __name__ == '__main__': camera = glitch.glut.GLUTCamera(children=[ glitch.limbo.unitview.UnitView(children=[ glitch.ApplyTexture( glitch.CameraTexture(800, 600, eye=[0, 1, 3], children=[ glitch.limbo.objects.make_lit_test_scene()]), children=[Shader(children=[ glitch.TexturedSquare()])])])]) camera.run() glitch-0.6/examples/cameratexture.py0000644000175000017500000000221411532271477015750 0ustar dafdaf import glitch, glitch.glut from glitch.limbo.lights import LightSwitch, AmbientLight, DiffuseLight from glitch.limbo.objects import make_test_scene def make_scene(children): return LightSwitch(children=[ AmbientLight(intensity=0.4, children=[ DiffuseLight(intensity=0.4, children=children)])]) if __name__ == '__main__': texture1 = glitch.CameraTexture(800, 600, eye=[0, 0.5, 3], children=[make_scene([make_test_scene()])]) texture2 = glitch.CameraTexture(800, 600, eye=[0, 0.5, 3], children=[ make_scene([ make_test_scene(), glitch.Translate(-1.5, -1.5, z=-1.4, children=[ glitch.Scale(3, 3, children=[ glitch.ApplyTexture(texture1, children=[glitch.TexturedRectangle()])])])])]) camera = glitch.glut.GLUTCamera(eye=[0, 0.5, 3], children=[ make_scene([make_test_scene(), glitch.Translate(-1.5, -1.5, z=-1.4, children=[ glitch.Scale(3, 3, children=[ glitch.ApplyTexture(texture2, children=[glitch.TexturedRectangle()])])])])]) camera.run() glitch-0.6/examples/glut.py0000644000175000017500000000022011511244525014034 0ustar dafdaf from glitch.glut import GLUTCamera from glitch.limbo.objects import Cube camera = GLUTCamera(fullscreen=True, children=[Cube()]) camera.run() glitch-0.6/examples/gtk/0000755000175000017500000000000011545146453013312 5ustar dafdafglitch-0.6/examples/gtk/gtkmovingcamera.py0000644000175000017500000000031111532271477017036 0ustar dafdaf import glitch, glitch.gtk from glitch.limbo.objects import make_lit_test_scene camera = glitch.gtk.GtkMovingCamera(eye=[0, 1, 2], ref=[0, 1, 0.5], children=[make_lit_test_scene()]) camera.run() glitch-0.6/examples/PIL/0000755000175000017500000000000011545146453013151 5ustar dafdafglitch-0.6/examples/PIL/imagecamera.py0000644000175000017500000000037611532271477015765 0ustar dafdaf from glitch.limbo.objects import make_lit_test_scene from glitch.PIL import ImageCamera camera = ImageCamera(640,480,'out_%06d.jpg',eye=[0, 2, 3], children=[ make_lit_test_scene()]) for i in range(10): camera.render() camera.eye[2] -= 0.1 glitch-0.6/examples/PIL/image_sequence.py0000644000175000017500000000073111511244525016466 0ustar dafdaf import glitch from glitch.PIL.image import ImageSequenceTexture from glitch.PIL.imagecamera import ImageCamera if __name__ == '__main__': texture = ImageSequenceTexture("/path/to/image/sequence-%06d.jpg") camera = ImageCamera(640, 360, "out-%06d.jpg", eye=[.5,.5,1], ref=[.5,.5,0],children=[ glitch.ApplyTexture(texture, children=[ glitch.TexturedRectangle()])]) for i in range(1, 50): camera.render({'frame':i}) glitch-0.6/examples/PIL/animate.py0000644000175000017500000000263211532271477015145 0ustar dafdaf import time from math import cos, sin import OpenGL.GLUT as glut import PIL.ImageDraw import glitch import glitch.glut import glitch.PIL white = (255, 255, 255) black = (0, 0, 0) class Animate(glitch.PIL.ImageTexture): def __init__(self, width, height): self.version = 0 self.width = width self.height = height image = self._make_image() glitch.PIL.ImageTexture.__init__(self, image) def upload(self, ctx): self.set_from_image(self._make_image()) glitch.PIL.ImageTexture.upload(self, ctx) class Bounce(Animate): def _draw_image(self, draw): size = self.width r = size / 60.0 x = (sin(-self.version * 4.0) / 2.4 + 0.5) * size y = (cos(self.version * 3.3) / 2.4 + 0.5) * size draw.ellipse(((x - r, y - r), (x + r, y + r)), white) def _make_image(self): image = PIL.Image.new('RGB', (self.width, self.height), black) draw = PIL.ImageDraw.Draw(image) self._draw_image(draw) return image def timeout(_ignored=None): bounce.version = time.time() - start glut.glutPostRedisplay() glut.glutTimerFunc(1000 / 30, timeout, None) start = time.time() bounce = Bounce(600, 600) camera = glitch.glut.GLUTCamera( eye=[0.5, 0.5, 1], ref=[0.5, 0.5, 0], children=[ glitch.ApplyTexture(bounce, children=[ glitch.TexturedSquare()])]) timeout() camera.run() glitch-0.6/examples/PIL/image.py0000644000175000017500000000050111511244525014571 0ustar dafdaf import sys import glitch, glitch.PIL, glitch.glut if __name__ == '__main__': texture = glitch.PIL.ImageTexture.from_file(sys.argv[1]) camera = glitch.glut.GLUTCamera(eye=[0.5, 0.5, 1], ref=[0.5, 0.5, 0], children=[ glitch.ApplyTexture(texture, children=[glitch.TexturedRectangle()])]) camera.run() glitch-0.6/examples/PIL/headless_shader.py0000644000175000017500000000041411511244525016630 0ustar dafdafimport glitch from glitch.PIL import ImageCamera from glitch.limbo.objects import ColorfulTriangle camera = ImageCamera(640, 480, "example_%d.jpg", eye=[0, 0, 5], ref=[0, 0, 0], children=[glitch.Shader(children=[ColorfulTriangle()])]) camera.render({'frame':0}) glitch-0.6/examples/tree.py0000644000175000017500000000207711511244525014034 0ustar dafdaf import OpenGL.GL as gl import numpy import glitch, glitch.glut from glitch.limbo.objects import Grid class Tree(glitch.Node): def draw(self, ctx): gl.glBegin(gl.GL_LINES) gl.glVertex3f(0, 0, 0) gl.glVertex3f(0, 1, 0) gl.glEnd() # XXX: Not a very pretty tree. Use an L-system? def make_tree(depth): if depth > 0: children = [ glitch.Translate(y=1, children=[ glitch.Scale(scale, scale, scale, children=[make_tree(depth - 1)])]) for (i, scale) in zip(xrange(4), numpy.random.uniform(0.7, 0.8, 4))] else: children = [] rotation = numpy.random.uniform(-35, 55) / (depth + 1) tree = glitch.Rotate(rotation, 0.5, 0, 0.5, children=[ Tree(children=children)]) return tree if __name__ == '__main__': grid = glitch.Rotate(90, 1, children=[Grid()]) tree = glitch.Scale(0.7, 0.3, 0.7, children=[make_tree(4)]) camera = glitch.glut.GLUTCamera(eye=[2, 1, 2], ref=[0, 0.5, 0], children=[grid, tree]) camera.run() glitch-0.6/examples/square.py0000644000175000017500000000061411513125301014360 0ustar dafdaf import glitch, glitch.glut import OpenGL.GL as gl class Square(glitch.Node): def draw(self, ctx): gl.glColor3f(0, 1.0, 0) gl.glBegin(gl.GL_QUADS) gl.glVertex3f(0, 0, 0) gl.glVertex3f(1, 0, 0) gl.glVertex3f(1, 1, 0) gl.glVertex3f(0, 1, 0) gl.glEnd() square = Square() camera = glitch.glut.GLUTCamera(children=[square]) camera.run() glitch-0.6/examples/cairo/0000755000175000017500000000000011545146453013622 5ustar dafdafglitch-0.6/examples/cairo/text.py0000644000175000017500000000037111511244525015151 0ustar dafdaf import glitch, glitch.glut from glitch.cairo import Text camera = glitch.glut.GLUTCamera(eye=[0.5, 0.5, 1], ref=[0.5, 0.5, 0], children=[ glitch.ApplyTexture(Text('Hello World!'), children=[glitch.TexturedRectangle()])]) camera.run() glitch-0.6/examples/cairo/slide.py0000644000175000017500000000616011511244525015267 0ustar dafdaf import math import cairo import gobject import gtk import gtk.gdk import glitch, glitch.gtk from glitch.cairo import CairoTexture from glitch.limbo.spread import Spread class Slide(CairoTexture): def __init__(self, text, **kw): CairoTexture.__init__(self, 800, 600, **kw) self.text = text def draw_cairo(self, cr): cr.set_source_rgba(1, 1, 1, 0.95) cr.paint() cr.set_source_rgba(0, 0, 0, 0.95) cr.move_to(0, 100) cr.set_font_size(64) cr.select_font_face( "Sans", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) cr.show_text(self.text) def sigmoid(t): return 1 / (1 + math.exp(-t)) class Animation(object): def __init__(self, duration, steps, apply): self.duration = duration self.steps = steps self.apply = apply self.done = lambda: None self.time = 0 self.timeout_id = None def start(self): if self.timeout_id is not None: raise RuntimeError interval = int(self.duration / float(self.steps) * 100) self.timeout_id = gobject.timeout_add(interval, self.tick) def tick(self): self.apply(self.time / float(self.steps)) self.time += 1 if self.time == self.steps: self.timeout_id = None self.done() return False else: return True def animate_move_x(obj, destination): source = obj.x def apply(time): obj.x = source + sigmoid(10 * (time - 0.5)) * (destination - source) return Animation(0.6, 12, apply) def key_press(w, ev): global current_slide name = gtk.gdk.keyval_name(ev.keyval) new_slide = current_slide if name == 'Left': new_slide = max(0, current_slide - 1) elif name == 'Right': new_slide = min(len(slides.children) - 1, current_slide + 1) if new_slide != current_slide and not animations: current_slide = new_slide a = animate_move_x(translate, -slides.x * current_slide) old_apply = a.apply def apply_and_refresh(time): old_apply(time) camera.refresh() def done(): animations.remove(a) a.apply = apply_and_refresh a.done = done animations.append(a) a.start() if __name__ == '__main__': current_slide = 0 animations = [] slides = Spread(x=1.3, children=[ glitch.ApplyTexture(Slide(text='EENIE'), children=[glitch.TexturedRectangle()]), glitch.ApplyTexture(Slide(text='MEANIE'), children=[glitch.TexturedRectangle()]), glitch.ApplyTexture(Slide(text='MYNIE'), children=[glitch.TexturedRectangle()]), glitch.ApplyTexture(Slide(text='MOE'), children=[glitch.TexturedRectangle()])]) translate = glitch.Translate(children=[slides]) camera = glitch.gtk.GtkCamera(eye=[0.5, 0.5, 0.7], ref=[0.5, 0.5, 0], zNear=0.1, zFar=10, children=[translate]) w = gtk.Window() w.connect('destroy', lambda w: gtk.main_quit()) w.connect('key-press-event', key_press) w.add(camera) w.show_all() gtk.main() glitch-0.6/examples/cairo/draw.py0000644000175000017500000000327711511244525015132 0ustar dafdaf import math import glitch, glitch.glut from glitch.limbo.blend import Blend from glitch.cairo.draw import CairoTexture class Chequer(CairoTexture): def draw_cairo(self, cr): w = self.width / 10 h = self.height / 10 for y in xrange(10): for x in xrange(10): if (x + y) % 2 == 0: cr.set_source_rgba(0.5, 0.5, 0.5, 0.7) else: cr.set_source_rgba(0.8, 0.8, 0.8, 0.7) cr.move_to(x * w, y * h) cr.rel_line_to(w, 0) cr.rel_line_to(0, h) cr.rel_line_to(-w, 0) cr.rel_line_to(0, -0) cr.fill() class Circles(CairoTexture): def draw_cairo(self, cr): c1 = (1.0, 0.7, 0.7) c2 = (1.0, 1.0, 0.7) c3 = (0.7, 1.0, 1.0) rmax = int(max(self.width, self.height) / math.sqrt(2)) for i, r in enumerate(xrange(rmax, 0, -int(rmax / 5))): cr.set_source_rgb(*(c1, c2, c3)[i % 3]) cr.arc(self.width / 2, self.height / 2, r, 0, 2 * math.pi) cr.fill() if __name__ == '__main__': o1 = glitch.Translate(-0.25, 0, 0, children=[ glitch.Rotate(20, 0, 1, 0, children=[ glitch.ApplyTexture(Chequer(400, 400), children=[ glitch.TexturedSquare()])])]) o2 = glitch.Translate(0.25, 0, -0.5, children=[ glitch.Rotate(20, 0, -1, 0, children=[ glitch.ApplyTexture(Circles(400, 400), children=[ glitch.TexturedSquare()])])]) # XXX: Scene manually sorted farthest first. camera = glitch.glut.GLUTCamera(eye=[0, 0.75, 2.0], children=[ Blend(children=[o2, o1])]) camera.run() glitch-0.6/examples/hyggelig.py0000644000175000017500000000232611511244525014671 0ustar dafdafimport glitch, glitch.gtk from glitch.cairo import Text from glitch.limbo.blend import Blend from glitch.PIL import ImageTexture from PIL import Image import urllib2, simplejson, StringIO def get_flickr_photos(tags): photo_json = urllib2.urlopen('http://api.flickr.com/services/feeds/photos_public.gne?tags=%s&tagmode=any&format=json' %(tags)).read() photo_json = photo_json[len('jsonFlickrFeed('):-1] return simplejson.loads(photo_json)['items'] def get_photo_texture(photo): im = Image.open(StringIO.StringIO(urllib2.urlopen(photo['media']['m']).read())) return ImageTexture(im) photos = get_flickr_photos('hyggelig,hygge') # TODO: download images in background? photo_nodes = [glitch.Translate(x=i, children=[ glitch.ApplyTexture(get_photo_texture(photo), children=[ glitch.TexturedRectangle()])]) for i, photo in enumerate(photos)] camera = glitch.gtk.GtkMovingCamera(eye=[1.5,0.5,2.5], ref=[1.5,0.5,0], children=[ Blend(children=photo_nodes + [ glitch.Translate(y=-0.75,z=0.1,children=[ glitch.ApplyTexture(Text('glitch 0.5: hyggelig'),children=[ glitch.TexturedRectangle()])])])]) camera.run() glitch-0.6/examples/conelight.py0000644000175000017500000000060611532271477015056 0ustar dafdaf import glitch, glitch.glut from glitch.limbo.lights import LightSwitch, ConeLight from glitch.limbo.objects import make_test_scene if __name__ == '__main__': camera = glitch.glut.GLUTCamera(eye=[0, 2, 3], children=[ LightSwitch(children=[ ConeLight(y=1.8, dy=-1, dz=0, cutoff=45., intensity=1.0, children=[make_test_scene()])])]) camera.run() glitch-0.6/examples/mesh.py0000644000175000017500000000510311511244525014022 0ustar dafdaf from math import sqrt, pi, sin, cos import glitch, glitch.glut from glitch.limbo.lights import LightSwitch, AmbientLight, DiffuseLight from glitch.limbo.objects import Grid from glitch.limbo.material import Material from glitch.limbo.mesh import Mesh from glitch.limbo.spread import Spread def make_tetrahedron(): r3 = sqrt(3) mesh = Mesh() mesh.vertices = [(0, 0, 0), (0.5, 0, r3/2), (1, 0, 0), (0.5, r3/2, r3/6)] mesh.faces = [(0, 2, 1), (1, 3, 0), (0, 3, 2), (1, 2, 3)] return mesh def make_cube(): mesh = Mesh() mesh.vertices = [ (0., 0., 0.), (0., 0., 1.), (0., 1., 0.), (0., 1., 1.), (1., 0., 0.), (1., 0., 1.), (1., 1., 0.), (1., 1., 1.)] mesh.faces = [ (1, 3, 2), (2, 0, 1), # x = 0 (4, 6, 7), (7, 5, 4), # x = 1 (0, 4, 5), (5, 1, 0), # y = 0 (7, 6, 2), (2, 3, 7), # y = 1 (0, 2, 6), (6, 4, 0), # z = 0 (5, 7, 3), (3, 1, 5), # z = 1 ] return mesh def make_sphere(): # Number of slices. m = 20 # Number of wedges. n = 10 r = 0.5 vertices = [] faces = [] for i in xrange(1, n): s = i * pi / n r_ = r * sin(s) vertices.extend([ (r + r_ * sin(t * 2 * pi / m), r * (1 - cos(s)), r + r_ * cos(t * 2 * pi / m)) for t in xrange(m)]) for i in xrange(n - 2): for j in xrange(m): faces.append( (m * i + j, m * i + (j + 1) % m, m * (i + 1) + j)) faces.append( (m * i + (j + 1) % m, m * (i + 1) + (j + 1) % m, m * (i + 1) + j)) l = len(vertices) p0 = (0.5, 0, 0.5) p1 = (0.5, 1, 0.5) vertices.extend([p0, p1]) for i in xrange(m): faces.append((l, (i + 1) % m, i)) faces.append((l + 1, l - m + i, l - m + (i + 1) % m)) mesh = Mesh() mesh.vertices = vertices mesh.faces = faces return mesh if __name__ == '__main__': mesh1 = make_tetrahedron() mesh2 = make_cube() mesh3 = make_sphere() camera = glitch.glut.GLUTCamera(eye=[0, 4, 5], ref=[2.5, 0, 2.5], children=[ glitch.Rotate(90, x=3, children=[ glitch.Scale(5, 5, 1, children=[Grid()])]), LightSwitch(children=[ AmbientLight(intensity=0.05, x=-1, z=4, y=2, children=[ DiffuseLight(x=-1, z=4, y=2, children=[ Material(r=0.9, g=0.5, b=0.5, children=[ Spread(x=2, z=2, children=[mesh1, mesh2, mesh3]) ])])])])]) camera.run() glitch-0.6/examples/accumulate.py0000644000175000017500000000140011532271477015216 0ustar dafdaf import OpenGL.GL as gl import glitch, glitch.gtk from glitch.limbo.objects import make_lit_test_scene class Accumulate(glitch.Node): def render(self, ctx): gl.glClear(gl.GL_ACCUM_BUFFER_BIT) for child in self.children: gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) child.render(ctx) gl.glAccum(gl.GL_ACCUM, 1.0 / len(self.children)) gl.glAccum(gl.GL_RETURN, 1.0) if __name__ == '__main__': scene = make_lit_test_scene() scene_x = glitch.Rotate(0.5, 0, 1, 0, children=[scene]) scene_y = glitch.Rotate(0.5, 1, 0, 0, children=[scene]) camera = glitch.gtk.GtkMovingCamera(eye=[0, 2, 3], children=[ Accumulate(children=[scene, scene_x, scene_y])]) camera.run() glitch-0.6/examples/cubes.py0000644000175000017500000000357611511244525014203 0ustar dafdaf import math import gobject import gtk import glitch, glitch.gtk from glitch.limbo.lights import LightSwitch, DiffuseLight from glitch.limbo.material import Material from glitch.limbo.objects import Cube def tick(): if paused: return True global time theta = (3 * time) % 360 theta2 = (time) % 360 time += 1 rotate.angle = theta camera.eye[0] = 3 * math.sin(theta2 * (math.pi / 180)) camera.eye[2] = 3 * math.cos(theta2 * (math.pi / 180)) camera.refresh() return True def key_press(w, ev): global paused paused = True def key_release(w, ev): global paused paused = False if __name__ == '__main__': """ camera | light / | \ material material material | | | trans trans trans \ | / rotate | cube """ cube = Cube() rotate = glitch.Rotate(y=1, children=[cube]) (trans1, trans2, trans3) = ( glitch.Translate(0, 0, math.sqrt(3)/2, children=[rotate]), glitch.Translate(-1, 0, -math.sqrt(3)/2, children=[rotate]), glitch.Translate(1, 0, -math.sqrt(3)/2, children=[rotate])) (mat1, mat2, mat3) = ( Material(1, 0, 0, children=[trans1]), Material(0, 1, 0, children=[trans2]), Material(0, 0, 1, children=[trans3])) time = 0 paused = False camera = glitch.gtk.GtkCamera(eye=[0, 1, 3], children=[ LightSwitch(children=[ DiffuseLight(y=2, children=[mat1, mat2, mat3])])]) w = gtk.Window() w.add(camera) w.connect('key-press-event', key_press) w.connect('key-release-event', key_release) w.show_all() gobject.timeout_add(30, tick) if '__IP' not in dir(): w.connect('destroy', lambda w: gtk.main_quit()) gtk.main() glitch-0.6/examples/vertices.py0000644000175000017500000000305311532271477014725 0ustar dafdaf import numpy import OpenGL.GL as gl import glitch.glut from glitch.limbo.vertices import DrawVertexArray # Lines. # The number of lines in each dimension. nlines = 7 # We need four times as many points: two coordinates per line, two # sets of lines. lines = numpy.ndarray(shape=(nlines * 4, 2), dtype=numpy.float32) indices = numpy.arange(nlines * 4) # Horizontal / X. lines[(indices >= nlines * 2) & (indices % 2 == 0), 0] = 0.0 lines[(indices >= nlines * 2) & (indices % 2 == 1), 0] = 1.0 # Horizontal / Y. ys = (numpy.arange(nlines * 2) // 2) * (1.0 / (nlines - 1)) lines[nlines * 2 :, 1] = 0.1 + 0.8 * ys # Vertical / X. xs = (numpy.arange(nlines * 2) // 2) * (1.0 / (nlines - 1)) lines[: nlines * 2, 0] = 0.1 + 0.8 * xs # Vertical / Y. lines[(indices < nlines * 2) & (indices % 2 == 0), 1] = 0.0 lines[(indices < nlines * 2) & (indices % 2 == 1), 1] = 1.0 # Triangles. ntriangles = 5 triangles = numpy.ndarray(shape=(ntriangles * 3, 2), dtype=numpy.float32) nums = numpy.arange(ntriangles) indices = numpy.arange(ntriangles * 3) triangles[indices % 3 == 0, :] = numpy.tile(nums, (2, 1)).T triangles[indices % 3 == 1, :] = numpy.tile(nums, (2, 1)).T + 1 triangles[indices % 3 == 2, 0] = nums + 1 triangles[indices % 3 == 2, 1] = nums triangles[:, :] = triangles[:, :] / float(ntriangles) print 'lines:' print lines print print 'triangles:' print triangles camera = glitch.glut.GLUTCamera( eye=[0.5, 0.5, 1], ref=[0.5, 0.5, 0], children=[ DrawVertexArray(gl.GL_LINES, lines), DrawVertexArray(gl.GL_TRIANGLES, triangles)]) camera.run() glitch-0.6/examples/blur.py0000644000175000017500000000374311511244525014042 0ustar dafdafimport glitch, glitch.glut import glitch.PIL.image as image import OpenGL.GL.shaders as shaders class HorizontalBlur(glitch.Shader): vertex = """ void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = gl_MultiTexCoord0; } """ fragment = """ uniform sampler2D tex; uniform int width; uniform int height; void main () { float tx = gl_TexCoord[0].x; float ty = gl_TexCoord[0].y; float step = 1.0 / float(width); gl_FragColor = 0.05 * texture2D(tex, vec2(tx - 4.0 * step, ty)) + 0.09 * texture2D(tex, vec2(tx - 3.0 * step, ty)) + 0.12 * texture2D(tex, vec2(tx - 2.0 * step, ty)) + 0.15 * texture2D(tex, vec2(tx - 1.0 * step, ty)) + 0.16 * texture2D(tex, vec2(tx - 0.0 * step, ty)) + 0.15 * texture2D(tex, vec2(tx + 1.0 * step, ty)) + 0.12 * texture2D(tex, vec2(tx + 2.0 * step, ty)) + 0.09 * texture2D(tex, vec2(tx + 3.0 * step, ty)) + 0.05 * texture2D(tex, vec2(tx + 4.0 * step, ty)); } """ def set_uniforms(self, ctx, shader): # XXX: Assumes texture unit 0. loc = shaders.glGetUniformLocation(shader, 'tex') shaders.glUniform1i(loc, 0) (w, h) = ctx['texture_size'] loc = shaders.glGetUniformLocation(shader, 'width') shaders.glUniform1i(loc, w) loc = shaders.glGetUniformLocation(shader, 'height') shaders.glUniform1i(loc, h) if __name__ == '__main__': import sys texture = image.ImageTexture.from_file(sys.argv[1]) camera = glitch.glut.GLUTCamera(eye=[0, 0, 1], children=[ glitch.Translate(-0.5, -0.5, children=[ glitch.ApplyTexture(texture, children=[ HorizontalBlur(children=[ glitch.TexturedRectangle()])])])]) camera.run() glitch-0.6/PKG-INFO0000644000175000017500000000041411545146453012003 0ustar dafdafMetadata-Version: 1.0 Name: glitch Version: 0.6 Summary: Glitch GL Convenience Library Home-page: http://glitch.rhydd.org Author: Dafydd Harries and Robert M Ochshorn Author-email: mail@rmozone.com,daf@rhydd.org License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN glitch-0.6/test/0000755000175000017500000000000011545146453011666 5ustar dafdafglitch-0.6/test/test_core.py0000644000175000017500000002647011544723452014237 0ustar dafdaf import difflib import sys import unittest import glitch from glitch.camera import Camera from glitch.context import Context class Add(object): def __init__(self, a, b): self.a = a self.b = b def __repr__(self): return '%r + %r' % (self.a, self.b) def __add__(self, other): return Add(self, other) class Or(object): def __init__(self, a, b): self.a = a self.b = b def __repr__(self): return '%r | %r' % (self.a, self.b) def __or__(self, other): return Or(self, other) class FakeAttr(object): def __init__(self, obj, log, name): self.obj = obj self.log = log self.name = name def __call__(self, *args): self.log.append( '%s(%s)' % (self.name, ', '.join(map(repr, args)))) def __repr__(self): return self.name def __add__(self, other): return Add(self, other) def __or__(self, other): return Or(self, other) def __cmp__(self, other): return cmp( (self.obj, self.name), (other.obj, other.name)) class WrappedMethod(FakeAttr): def __init__(self, obj, log, name, impl): FakeAttr.__init__(self, obj, log, name) self.impl = impl def __call__(self, *args): FakeAttr.__call__(self, *args) return self.impl(*args) class FakeModule(object): def __init__(self, log): self.log = log def __getattribute__(self, name): d = object.__getattribute__(self, '__dict__') log = d['log'] try: v = object.__getattribute__(self, name) except AttributeError: v = None if v is not None: if hasattr(v, '__call__'): return WrappedMethod(self, log, name, v) else: return v return FakeAttr(self, log, name) class FakeGL(FakeModule): def __init__(self, *args): FakeModule.__init__(self, *args) self.next_texture_id = 1 def glGenTextures(self, length): id = self.next_texture_id self.next_texture_id += 1 return id class FakeProgram(object): def __init__(self, log, id): self.log = log self.id = id def __enter__(self): self.log.append('program %d: enter' % self.id) def __exit__(self, type, exc, tb): self.log.append('program %d: exit' % self.id) class FakeShaders(FakeModule): def __init__(self, *args): FakeModule.__init__(self, *args) self.next_shader_id = 1 self.next_program_id = 1 def compileShader(self, code, type): id = self.next_shader_id self.next_shader_id += 1 return 'shader%d' % id def compileProgram(self, vshader, fshader): id = self.next_program_id self.next_program_id += 1 return FakeProgram(self.log, id) class LoggingNode(glitch.Node): def __init__(self, name, log, **kw): glitch.Node.__init__(self, **kw) self.name = name self.log = log def render(self, ctx): self.log.append('%s: begin' % self.name) glitch.Node.render(self, ctx) self.log.append('%s: end' % self.name) def draw(self, ctx): self.log.append('%s: draw' % self.name) class GlitchTest(unittest.TestCase): def assertStringsEqual(self, expected, other): if expected != other: raise AssertionError( list(difflib.unified_diff(expected, other))) def setUp(self): self.log = [] self.ctx = Context() self.fake_gl = FakeGL(self.log) self.fake_shaders = FakeShaders(self.log) self.undo = [] # Dependency injection, Python style. for (name, module) in sys.modules.iteritems(): if name.startswith('glitch.'): if hasattr(module, 'gl'): self.undo.append((module, 'gl', module.gl)) module.gl = self.fake_gl if hasattr(module, 'shaders'): self.undo.append((module, 'shaders', module.shaders)) module.shaders = self.fake_shaders def tearDown(self): for (module, name, value) in self.undo: setattr(module, name, value) def test_recurse(self): node = LoggingNode('a', self.log, children=[ LoggingNode('b', self.log), LoggingNode('c', self.log)]) node.render(self.ctx) self.assertStringsEqual([ 'a: begin', 'a: draw', 'b: begin', 'b: draw', 'b: end', 'c: begin', 'c: draw', 'c: end', 'a: end' ], self.log) def test_translate(self): node = glitch.Translate(x=1, children=[LoggingNode('a', self.log)]) node.render(self.ctx) self.assertStringsEqual([ 'glMatrixMode(GL_MODELVIEW)', 'glPushMatrix()', 'glTranslate(1, 0, 0)', 'a: begin', 'a: draw', 'a: end', 'glMatrixMode(GL_MODELVIEW)', 'glPopMatrix()' ], self.log) def test_scale(self): node = glitch.Scale(x=2, children=[LoggingNode('a', self.log)]) node.render(self.ctx) self.assertStringsEqual([ 'glMatrixMode(GL_MODELVIEW)', 'glPushMatrix()', 'glScale(2, 1, 1)', 'a: begin', 'a: draw', 'a: end', 'glMatrixMode(GL_MODELVIEW)', 'glPopMatrix()' ], self.log) def test_rotate(self): node = glitch.Rotate(angle=90, x=1, children=[LoggingNode('a', self.log)]) node.render(self.ctx) self.assertStringsEqual([ 'glMatrixMode(GL_MODELVIEW)', 'glPushMatrix()', 'glRotate(90, 1, 0, 0)', 'a: begin', 'a: draw', 'a: end', 'glMatrixMode(GL_MODELVIEW)', 'glPopMatrix()' ], self.log) def test_color(self): node = glitch.Color(r=1, g=2, b=3, a=4, children=[LoggingNode('a', self.log)]) node.render(self.ctx) self.assertStringsEqual([ 'glPushAttrib(GL_CURRENT_BIT)', 'glColor(1, 2, 3, 4)', 'a: begin', 'a: draw', 'a: end', 'glPopAttrib()' ], self.log) def test_camera(self): camera = Camera( children=[LoggingNode('a', self.log)]) camera.context['w'] = 10 camera.context['h'] = 10 camera.render(None) self.assertStringsEqual([ 'glClearColor(0, 0, 0, 0)', 'glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)', 'glColor(1, 1, 1)', 'glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ' '(1, 1, 1, 1))', 'glLightModelfv(GL_LIGHT_MODEL_AMBIENT, [0, 0, 0, 1])', 'glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)', 'glViewport(0, 0, 10, 10)', 'glMatrixMode(GL_MODELVIEW)', 'glLoadIdentity()', 'glMatrixMode(GL_PROJECTION)', 'glLoadIdentity()', 'glEnable(GL_DEPTH_TEST)', 'glMatrixMode(GL_MODELVIEW)', 'a: begin', 'a: draw', 'a: end', ], self.log) def test_camera_with_parent(self): camera = Camera( children=[LoggingNode('a', self.log)]) camera.context['w'] = 20 camera.context['h'] = 20 self.ctx['w'] = 10 self.ctx['h'] = 10 camera.render(self.ctx) self.assertStringsEqual([ # Save. 'glMatrixMode(GL_PROJECTION)', 'glPushMatrix()', 'glMatrixMode(GL_MODELVIEW)', 'glPushMatrix()', 'glPushAttrib(GL_VIEWPORT_BIT | GL_TRANSFORM_BIT | ' 'GL_ENABLE_BIT | GL_LIGHTING_BIT | GL_CURRENT_BIT)', # Draw. 'glClearColor(0, 0, 0, 0)', 'glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)', 'glColor(1, 1, 1)', 'glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ' '(1, 1, 1, 1))', 'glLightModelfv(GL_LIGHT_MODEL_AMBIENT, [0, 0, 0, 1])', 'glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)', 'glViewport(0, 0, 20, 20)', 'glMatrixMode(GL_MODELVIEW)', 'glLoadIdentity()', 'glMatrixMode(GL_PROJECTION)', 'glLoadIdentity()', 'glEnable(GL_DEPTH_TEST)', 'glMatrixMode(GL_MODELVIEW)', 'a: begin', 'a: draw', 'a: end', # Restore. 'glPopAttrib()', 'glMatrixMode(GL_PROJECTION)', 'glPopMatrix()', 'glMatrixMode(GL_MODELVIEW)', 'glPopMatrix()' ], self.log) def test_texture(self): texture = glitch.Texture(20, 20, 'abcdef') node = glitch.ApplyTexture(texture, children=[LoggingNode('a', self.log)]) node.render(self.ctx) node.render(self.ctx) self.assertStringsEqual([ # First render. 'glActiveTexture(GL_TEXTURE0 + 0)', 'glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT)', 'glEnable(GL_TEXTURE_2D)', 'glGenTextures(1)', 'glBindTexture(GL_TEXTURE_2D, 1)', 'glTexParameterf(' 'GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)', 'glTexParameterf(' 'GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)', 'glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)', 'glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)', "glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 20, 20, 0, GL_RGBA, " "GL_UNSIGNED_BYTE, 'abcdef')", 'glBindTexture(GL_TEXTURE_2D, 1)', 'a: begin', 'a: draw', 'a: end', 'glActiveTexture(GL_TEXTURE0 + 0)', 'glPopAttrib()', # Second render (no parameters/upload). 'glActiveTexture(GL_TEXTURE0 + 0)', 'glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT)', 'glEnable(GL_TEXTURE_2D)', 'glBindTexture(GL_TEXTURE_2D, 1)', 'a: begin', 'a: draw', 'a: end', 'glActiveTexture(GL_TEXTURE0 + 0)', 'glPopAttrib()' ], self.log) self.log[:] = [] assert texture in self.ctx['texture'] texture.context_delete(self.ctx) assert texture not in self.ctx['texture'] self.assertEquals(['glDeleteTextures(1)'], self.log) def test_shader(self): node = glitch.Shader(vertex='vertex', fragment='fragment', children=[LoggingNode('a', self.log)]) node.render(self.ctx) node.render(self.ctx) print dir(self) self.assertStringsEqual([ # First render. "compileShader('vertex', GL_VERTEX_SHADER)", "compileShader('fragment', GL_FRAGMENT_SHADER)", "compileProgram('shader1', 'shader2')", 'program 1: enter', 'a: begin', 'a: draw', 'a: end', 'program 1: exit', # Second render. 'program 1: enter', 'a: begin', 'a: draw', 'a: end', 'program 1: exit', ], self.log) glitch-0.6/test/test_context.py0000644000175000017500000000371011544723452014763 0ustar dafdaf def test_context_repr(): """ Context: Representation. >>> from glitch.context import Context >>> c = Context() >>> c Context({}) >>> c = Context({'foo': 1}) >>> c Context({'foo': 1}) """ def test_context_get_set(): """ Context: Getting and setting values. >>> from glitch.context import Context >>> c = Context({'foo': 1}) >>> c['foo'] 1 >>> c['foo'] = 3 >>> c['foo'] 3 """ def test_context_push_pop(): """ Context: Pushing and popping. >>> from glitch.context import Context >>> c = Context() >>> c['bar'] = 2 >>> c.push('bar') # doctest: +ELLIPSIS <...> >>> c['bar'] 2 >>> c['bar'] = 4 >>> c['bar'] 4 >>> c.pop() >>> c['bar'] 2 Pushing with value. >>> c.push('bar', 4) # doctest: +ELLIPSIS <...> >>> c['bar'] 4 >>> c.pop() >>> c['bar'] 2 """ def test_context_push_absent(): """ Context: Pushing an absent value. >>> from glitch.context import Context >>> c = Context() >>> c.push('baz') # doctest: +ELLIPSIS <...> >>> c['baz'] = 5 >>> c['baz'] 5 >>> c.pop() >>> 'baz' in c False Pushing a value that doesn't get set before getting popped. >>> c.push('quux') # doctest: +ELLIPSIS <...> >>> c.pop() """ def test_context_with(): """ Context: Using with statement. >>> from __future__ import with_statement >>> from glitch.context import Context >>> c = Context() >>> with c.push('quux'): ... c['quux'] = 2 ... >>> 'quux' in c False """ def test_context_copy(): """ Context: Pushing mutable values. >>> from __future__ import with_statement >>> from glitch.context import Context >>> c = Context() >>> c['x'] = {'a': 1, 'b': 2} >>> with c.push('x'): ... c['x']['c'] = 3 ... >>> sorted(c['x'].items()) [('a', 1), ('b', 2)] """ glitch-0.6/test/test_reload.py0000644000175000017500000000436611544723452014555 0ustar dafdaf from __future__ import with_statement import os import sys import tempfile import unittest import gio import gobject def unsplitlines(ls): return ''.join([l + '\n' for l in ls]) def code_literal(s): lines = s.splitlines() i = 1 while True: prefixes = [line[:i] for line in lines if line != ''] if not all([prefix == prefixes[0] for prefix in prefixes[1:]]): break i += 1 return unsplitlines([s[i - 1:] for s in lines]) class ReloadTest(unittest.TestCase): def setUp(self): (_, self.tmp_path) = tempfile.mkstemp(suffix='.py') sys.path.insert(0, tempfile.gettempdir()) # Avoid generating .pyc files in the temporary directory. if hasattr(sys, 'dont_write_bytecode'): self.dont_write_bytecode = sys.dont_write_bytecode sys.dont_write_bytecode = True def tearDown(self): os.unlink(self.tmp_path) sys.path.pop(0) if hasattr(sys, 'dont_write_bytecode'): sys.dont_write_bytecode = self.dont_write_bytecode def wait_for_file_change(self, path): # Wait for the GFileMonitor to notice that the file has # changed. Annoyingly, this takes about a second, and setting # the rate-limit property doesn't seem to affect it. loop = gobject.MainLoop() monitor = gio.File(path).monitor() monitor.connect('changed', lambda *x: loop.quit()) loop.run() def test(self): code1 = code_literal(""" from glitch.gtk.reload import Reload class Foo(Reload): def foo(self): return 42 """) code_lines = code1.splitlines() code_lines[-2] = " return 43" code2 = unsplitlines(code_lines) with file(self.tmp_path, 'w') as fh: fh.write(code1) mod_name = os.path.splitext(os.path.basename(self.tmp_path))[0] mod = __import__(mod_name) mod.Foo._rate_limit = 1 foo = mod.Foo() self.assertEquals(42, foo.foo()) with file(self.tmp_path, 'w') as fh: fh.write(code2) self.wait_for_file_change(self.tmp_path) self.assertEquals(43, foo.foo()) if __name__ == '__main__': unittest.main() glitch-0.6/README0000644000175000017500000000006611544723452011570 0ustar dafdaf ## Dependencies - Python >= 2.5 - PyOpenGL >= 3.0 glitch-0.6/setup.py0000644000175000017500000000155311545146430012420 0ustar dafdaf import distutils.core import distutils.cmd import subprocess #from distutils.core import setup class make_docs(distutils.cmd.Command): "build API documentation" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): subprocess.call(['pydoctor', '--project-name=glitch', '--resolve-aliases', 'glitch/']) distutils.core.setup(name='glitch', cmdclass={'make_docs': make_docs}, version='0.6', url='http://glitch.rhydd.org', description='Glitch GL Convenience Library', author='Dafydd Harries and Robert M Ochshorn', author_email='mail@rmozone.com,daf@rhydd.org', packages=[ 'glitch', 'glitch.cairo', 'glitch.gst', 'glitch.glut', 'glitch.glx', 'glitch.gtk', 'glitch.limbo', 'glitch.PIL'], )