OpenGL Intellivision Man
Not long ago I set out to turn an animated gif I found of the Intellivision Running Man into an OpenGL generated video clip. My goal was to shrink the images into tiny bitmaps, a.k.a. 8-bit, and then use the resulting bitmap data as positioning information within a 3D space. Another way of saying it: I wanted to render each pixel from the original images as a 3D block. Jump to the video to get a better idea about what I mean:
http://www.youtube.com/watch?v=E1rXt0l2pxY
Along the way I took a few turns. I never did reduce the image data to its smallest possible representation. I did reduce the image size for each image to 24x24 pixels and I converted the image data to grayscale. Using the handy PIL library, I was able to pull in the bitmap file data as a tuple of pixel values. I used each "pixel" to offset the drawing position in pyglet's on_draw handler.
The code I used to render the animation is a hack of an example found here:
http://code.google.com/p/pyglet-hene/source/browse/trunk/
This is a fairly ugly hack, meaning I didn't work to make the code beautiful. It's just the original code, chopped, altered and enhanced as needed to accomplish what I wanted to see rendered on the screen.
Though not provided here, to run this code an image directory needs to be provided named "data" containing a series of bitmaps. My directory contains this:
If you are so inclined to try your own little experiment, find a favorite animated gif on the web, extract each frame, reduce size, convert to grayscale and drop your own images in a directory similar to the one I created here. Your animation result should be similar to what I've produced here.
And here's the complete source:
http://www.youtube.com/watch?v=E1rXt0l2pxY
Along the way I took a few turns. I never did reduce the image data to its smallest possible representation. I did reduce the image size for each image to 24x24 pixels and I converted the image data to grayscale. Using the handy PIL library, I was able to pull in the bitmap file data as a tuple of pixel values. I used each "pixel" to offset the drawing position in pyglet's on_draw handler.
The code I used to render the animation is a hack of an example found here:
http://code.google.com/p/pyglet-hene/source/browse/trunk/
This is a fairly ugly hack, meaning I didn't work to make the code beautiful. It's just the original code, chopped, altered and enhanced as needed to accomplish what I wanted to see rendered on the screen.
Though not provided here, to run this code an image directory needs to be provided named "data" containing a series of bitmaps. My directory contains this:
ls -ltr
total 32
-rw-r--r-- 1 dvenable dvenable 1654 2010-07-20 14:06 9.bmp
-rw-r--r-- 1 dvenable dvenable 1654 2010-07-20 14:06 8.bmp
-rw-r--r-- 1 dvenable dvenable 1654 2010-07-20 14:06 6.bmp
-rw-r--r-- 1 dvenable dvenable 1654 2010-07-20 14:06 5.bmp
-rw-r--r-- 1 dvenable dvenable 1654 2010-07-20 14:06 4.bmp
-rw-r--r-- 1 dvenable dvenable 1654 2010-07-20 14:06 3.bmp
-rw-r--r-- 1 dvenable dvenable 1654 2010-07-20 14:06 2.bmp
-rw-r--r-- 1 dvenable dvenable 1654 2010-07-20 14:06 1.bmp
If you are so inclined to try your own little experiment, find a favorite animated gif on the web, extract each frame, reduce size, convert to grayscale and drop your own images in a directory similar to the one I created here. Your animation result should be similar to what I've produced here.
And here's the complete source:
from pyglet.gl import *
import pyglet
from pyglet.window import *
from pyglet import image
import os
from PIL import Image
import glob
from math import sin, cos
window = pyglet.window.Window(width=640, height=480, resizable=True)
y=0.0
x=-10.0
z=10.0
xspeed = 0.5
yspeed = 0.0
lx=ly=0
lz=-2
angle=ratio=0.0
boxcol = [ [1.0, 0.0, 0.0], # bright: red
[1.0, 0.5, 0.0], # orange
[1.0, 1.0, 0.0], # yellow
[0.0, 1.0, 0.0], # green
[0.0, 1.0, 1.0], # blue
]
# Dark: red, orange, yellow, green ,blue
topcol =[ [0.6, 0.0, 0.0],
[0.6, 0.25, 0.0],
[0.6, 0.6, 0.0],
[0.0, 0.6 ,0.0],
[0.0, 0.6, 0.6]]
box = None # display list storage
top = None #display list storage
yloop = None # loop for y axis
xloop = None # loop for x axies
bmpdata = None
nextimg = 0
files = None
def load_image_data():
global bmpdata, bmpdatalen, files
files = glob.glob('data/*.bmp')
files.sort()
bmpdata = map(lambda x: Image.open(x).getdata(), files.__iter__())
bmpdatalen = len(bmpdata)
def build_lists():
global box, top
box = glGenLists(2)
glNewList(box, GL_COMPILE) # new compiled box display list
# draw the box without the top (it will be store in display list
# and will not appear on the screen)
glBegin(GL_QUADS)
# front face
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 1.0)
glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, 1.0)
glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, 1.0)
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 1.0)
# back face
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0)
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, -1.0)
glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, -1.0)
glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, -1.0)
# right face
glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, -1.0)
glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, -1.0)
glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, 1.0)
glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 1.0)
# left face
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0)
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0)
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0)
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0)
glEnd()
glEndList() # Done building the list
top=box+1
glNewList(top, GL_COMPILE) # new compiled top display list
# Top face
glBegin(GL_QUADS)
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0)
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 1.0)
glTexCoord2f(1.0, 0.0); glVertex3f(1.0, 1.0, 1.0)
glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, -1.0)
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0)
glTexCoord2f(0.0, 1.0); glVertex3f(1.0, -1.0, -1.0)
glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 1.0)
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0)
glEnd()
glEndList()
def load_gl_textures():
# load bitmaps and convert to textures
global texture, texture_file, texture_surf
#texture_file = os.path.join('data', 'cube.bmp')
texture_file = files[nextimg]
texture_surf = image.load(texture_file)
texture = texture_surf.get_texture()
glBindTexture(GL_TEXTURE_2D, texture.id)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
def init():
"""
Pyglet oftentimes calls this setup()
"""
glEnable(GL_TEXTURE_2D)
load_image_data()
load_gl_textures()
build_lists()
glShadeModel(GL_SMOOTH) # Enables smooth shading
glClearColor(0.0, 0.0, 0.0, 0.0) #Black background
glClearDepth(1.0) # Depth buffer setup
glEnable(GL_DEPTH_TEST) # Enables depth testing
glDepthFunc(GL_LEQUAL) # The type of depth test to do
glEnable(GL_LIGHT0) # quick and dirty lighting
#glEnable(GL_LIGHTING) # enable lighting
glEnable(GL_COLOR_MATERIAL) # enable coloring
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) # Really nice perspective calculations
@window.event
def on_draw():
global nextimg, bmpdata, x, y, z, lx, ly, lz
# Here we do all the drawing
glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT)
# Select the texture
load_gl_textures()
#glBindTexture(GL_TEXTURE_2D, texture.id)
xloop = 1
yloop = 1
mandata = bmpdata[nextimg]
for idx in range (0, len(mandata)):
if (idx+1) % 24 == 0:
yloop += 1
xloop = 1
else:
xloop += 1
if mandata[idx] < 100:
glLoadIdentity() # reset our view
gluLookAt(x,y,z, x+lx , y+ly, z+lz, 0.0, 1.0, 0.0)
glTranslatef( xloop*1.8 - 30 ,
28 - yloop*2.4 ,
-60.0)
glColor3f(*boxcol[xloop % 4]) # select a box color
glCallList(box) # draw the box
glColor3f(*topcol[1])
glCallList(top) # draw the top
return pyglet.event.EVENT_HANDLED
def moveMeFlat(direction):
global x, z, y, lx, lz, ly
x = x - direction*(lx)*0.75;
y = y + direction*(ly)*0.5;
z = z + direction*(lz)*0.5;
def orientMe(ang):
global lx, lz
lx = sin(ang)
lz = -cos(ang)
def update(dt):
global z, angle
angle +=0.005
orientMe(angle)
moveMeFlat(0.5)
def update2(dt):
global nextimg
if nextimg < bmpdatalen-1:
nextimg += 1
else:
nextimg = 0
pyglet.clock.schedule_interval(update2, .1)
pyglet.clock.schedule(update)
@window.event
def on_resize(width, height):
if height==0:
height = 1
glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
# Calculate the aspect ratio of the window
gluPerspective(45.0, 1.0*width/height, 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
return pyglet.event.EVENT_HANDLED
init()
pyglet.app.run()
Comments