To main page | 3dengine.org

Python CallList class


Python CallList class - a wrapper class to simplify Call Lists usage in Python.
Call Lists are a way to draw static geometry very fast. (More than 1M points in real-time even in Python)

How to use it

cl = CallList(random_lines) 
# replace random_lines with whatever YOUR function does the drawing
cl.draw()
cl.lazy_update()
while True: # OpenGL infinite loop
    # clear screen here
    cl.draw()
Obviously you require OpenGL context, try my python spectator code, but replace box drawing code with above call list ("fps.draw_simple_cube()" to "cl.draw()").

random_lines is the function that does the drawing.

What's that?

The idea is - generating 10000 random lines (or drawing a million points from database) takes A LOT of time in Python, but if you do it once and then call glCallList, then it's stunningly fast. Faster than C/C++ doing glBegin/glEnd each frame.

You supply a function (random_lines) and optionally it's arguments as second parameter ( cl=CallList(random_lines, (1,2,3)) )

You can mark calllist for update with either cl.lazy_update() or cl.update(). The lazy_update() can be called 1000 times for example, but will call update only once when the drawing will happen. The update() forces the update right this moment.

.lazy_update() is basically "next time when .draw() is called - call the function (random_lines) once and 'cache' the result";

When you call .draw() first time does "lazy_update" first, so no need to explicitly call "lazy_update". You need to call "lazy_update" only when you need to update the "cached" geometry.

The update/lazy_update means that "random_lines" will be called again and the new result will be cached.


Actual Class

class CallList:
	def __init__(self, fun = None, args = None): 
		self._id = glGenLists(1)
		self._valid = False
		self._update_fun = fun
		self._update_args = args

	def draw(self): 
		if self._valid:
			glCallList(self._id)		
		else:
			if self._update_fun is not None:
				self.update()

	def lazy_update(self):
		self._valid = False
	
	def update(self):
		self.start(GL_COMPILE_AND_EXECUTE)
		if self._update_args is not None:
			self._update_fun(*self._update_args)
		else:
			self._update_fun()
		self._valid = True
		self.end()
	
	def start(self, _call = GL_COMPILE): 
		glNewList(self._id, _call)
	
	def end(self): 
		glEndList()

	def __del__(self): 
		if glDeleteLists is not None: glDeleteLists(self._id,1)