Back to other Writings

Draw a Teapot in 19 lines of Python

Want to draw a teapot in Python quickly? It's actually pretty easy.


import pygame
from OpenGL import GLUT, GLU, GL

# Set up pygame and enable OPENGL and GLUT
pygame.init()
display = (400, 300)
pygame.display.set_mode(display, pygame.DOUBLEBUF | pygame.OPENGL)
GLUT.glutInit()

# Clock object lets us set programs FPS
clock = pygame.Clock()

# Set the perspective of the camera
GLU.gluPerspective(45, (display[0] / display[1]), 0.1, 50.0)

# Move everything back 5 units from the camera
GL.glTranslatef(0.0, 0.0, -5)

# Render loop
while True:
    # Clear the screen/buffer
    GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)

    # Get any events like key presses or mouse clicks
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            quit()
    
    # Rotate
    GL.glRotatef(1, 1, 1, 1)
    
    # Draw the teapot
    GLUT.glutSolidTeapot(.5)
    
    # Display what we just drew and wait for 16 ms 
    pygame.display.flip()
    clock.tick(60)

Pretty cool right? :)

Why being slow and limited is okay

Now if you've been in the graphics scene for even just a bit you probably know that I should have put a content warning on this. I can hear a many graphics programmers angerly typing out in their respective comment section. "Fixed function programming in 2023... The insanity! I mean it's been depericated for... *starts counting* Subtract the one... Carry the 8... The moon is waining so multiply by 5... 14 years! It's not how gpus work at all and worse of all it's limiting and slow." And I hear you. It has been depericated for 14 years and certainly outdated longer than that. It is not even remotly close to how GPU's work at all. Finally yes it is limiting and slow but I'd like to argue that it being limited and slow in terms of performance isn't necessarily a bad thing.

Now yes performance does matter and it is nice to have flexabilty but at times we may to so spoiled that we lose sight of our goals. What do I mean by that? Now if your planning to be a graphics programmer yes fixed function pipeline is pretty useless for getting a job in the industry unless you're going to be put in charge of mainting some old system but for gamedevs I'd argue that when using new graphics apis it's really easy to lose sight your goal. Let's say you don't want to use someone else's engine and you decide to write your own, which is reasonable it's nice to have control over your codebase and understand how everything works,but it is incredibly easy to get side tracked and start implementing random shaders all the while you forget your orginal goal. Which is to make the game you want to make not a render engine.

So how does old graphics apis help with this? They place limits on you and as any good artist knows limits breed creativty. It's much harder to get lost and start building a render engine instead of a game when the celling for render tech is super low and it's much easier to make art for. Finally with the rise of interest in old school 3d graphics if you make a 3d game using the old apis your game will not only get support from that crowd but you'll also be able to run on more machines which helps widen your audience.

Drawing Other shapes

Okay that was a weird aside right? Not sure who that was or what that was about but let's keep drawing! Teapots are boring right? Let's draw some other shapes.


import pygame
from OpenGL import GLUT, GLU, GL

# Set up pygame and enable OPENGL and GLUT
pygame.init()
display = (400, 300)
pygame.display.set_mode(display, pygame.DOUBLEBUF | pygame.OPENGL)
GLUT.glutInit()

# Clock object lets us set programs FPS
clock = pygame.Clock()

# Set the perspective of the camera
GLU.gluPerspective(45, (display[0] / display[1]), 0.1, 50.0)

# Move everything back 5 units from the camera
GL.glTranslatef(0.0, 0.0, -5)

# The shape to draw
current_shape = 0

# Render loop
while True:
    # Clear the screen/buffer
    GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)

    # Get any events like key presses or mouse clicks
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            quit()
        
        # If the mouse is pressed move to the next shape
        if event.type == pygame.MOUSEBUTTONUP:
            current_shape += 1
  
            if current_shape > 8:
                current_shape = 0

    # Rotate the shape
    GL.glRotatef(1, 1, 1, 1)

    # Draw one of the shapes
    if current_shape == 0:
        GLUT.glutSolidTeapot(.5)
    elif current_shape == 1:
        GLUT.glutSolidCylinder(.5, 1, 8, 5)
    elif current_shape == 2:
        GLUT.glutSolidIcosahedron()
    elif current_shape == 3:
        GLUT.glutSolidOctahedron()
    elif current_shape == 4:
        GLUT.glutSolidTetrahedron()
    elif current_shape == 5:
        GLUT.glutSolidDodecahedron()
    elif current_shape == 6:
        GLUT.glutSolidSphere(1, 16, 16)
    elif current_shape == 7:
        GLUT.glutSolidRhombicDodecahedron()
    elif current_shape == 8:
        GLUT.glutSolidTorus(.5, 1, 8, 8)
    
    # Display what we just drew and wait for 16 ms 
    pygame.display.flip()
    clock.tick(60)

Lighting and Materials WIP

Movements WIP

Drawing Sprites WIP

Drawing Meshes WIP

Audio WIP