from OpenGL.GL import *
from OpenGL.GLUT import *
import weakref
keymap = {GLUT_KEY_F1:'f1',
GLUT_KEY_F2:'f2',
GLUT_KEY_F3:'f3',
GLUT_KEY_F4:'f4',
GLUT_KEY_F5:'f5',
GLUT_KEY_F6:'f6',
GLUT_KEY_F7:'f7',
GLUT_KEY_F8:'f8',
GLUT_KEY_F9:'f9',
GLUT_KEY_F10:'f10',
GLUT_KEY_F11:'f11',
GLUT_KEY_F12:'f12',
GLUT_KEY_LEFT:'left',
GLUT_KEY_UP:'up',
GLUT_KEY_RIGHT:'right',
GLUT_KEY_DOWN:'down',
GLUT_KEY_PAGE_UP:'page up',
GLUT_KEY_PAGE_DOWN:'page down',
GLUT_KEY_HOME:'home',
GLUT_KEY_END:'end',
GLUT_KEY_INSERT:'insert'
}
[docs]class GLUTWindow:
"""A GLUT window. Should not be used directly.
Attributes:
name (str): title of the window (only has an effect before calling
run())
index (int): the GLUT index
width, height (int): width/height of the window (only has an effect
before calling run(), and these are updated when the user resizes
the window.
glutInitialized (bool): true if GLUT has been initialized
"""
def __init__(self,name):
"""Note: must be called after GLUT is initialized."""
self.name = name
self.program = None
self.width = 640
self.height = 480
self.lastx = 0
self.lasty = 0
self.initialized = False
self.glutWindowID = None
self.modifierList = []
[docs] def initialize(self):
assert self.program is not None, "program member needs to be set"
assert not self.initialized,"initialized twice?"
program = self.program
#glutInitWindowPosition (0,0);
glutInitWindowSize (self.width, self.height);
print("Calling glutCreateWindow",self.name)
self.glutWindowID = glutCreateWindow (self.name)
program.view.x = 0
program.view.y = 0
def glutsafe(func,update_modifiers=False):
def safefunc(*args):
if update_modifiers:
self._updateModifiers()
try:
return func(*args)
except Exception as e:
import traceback
traceback.print_exc()
glutLeaveMainLoop()
return
return safefunc
# set window callbacks
glutReshapeFunc (glutsafe(self._reshapefunc))
glutKeyboardFunc (glutsafe(program.keyboardfunc,update_modifiers=True))
glutKeyboardUpFunc (glutsafe(program.keyboardupfunc,update_modifiers=True))
glutSpecialFunc (glutsafe(self._specialfunc,update_modifiers=True))
glutSpecialUpFunc (glutsafe(self._specialupfunc,update_modifiers=True))
glutMotionFunc (glutsafe(self._motionfunc,update_modifiers=False))
glutPassiveMotionFunc (glutsafe(self._motionfunc,update_modifiers=False))
glutMouseFunc (glutsafe(self._mousefunc,update_modifiers=True))
glutDisplayFunc (glutsafe(self._displayfunc))
glutIdleFunc(glutsafe(program.idlefunc))
if bool(glutCloseFunc):
glutCloseFunc(glutsafe(self._closefunc))
#init function
self.program.initialize()
glEnable(GL_MULTISAMPLE)
glutPostRedisplay()
self.initialized = True
print("GLUTWindow",self.name,"Initialized")
[docs] def add_action(self,*args):
pass
[docs] def setProgram(self,program):
from ..glprogram import GLProgram
assert isinstance(program,GLProgram)
if hasattr(program,'name'):
self.name = program.name
if self.initialized:
glutSetWindowTitle(program.name)
self.program = program
program.window = weakref.proxy(self)
if self.initialized:
program.initialize()
program.reshapefunc(self.width,self.height)
self.idlesleep(0)
else:
self.reshape(program.view.w,program.view.h)
[docs] def modifiers(self):
"""Call this to retrieve modifiers. Called by frontend."""
return self.modifierList
[docs] def refresh(self):
"""Call this to redraw the screen on the next event loop. Called by frontend."""
glutPostRedisplay()
[docs] def idlesleep(self,duration=float('inf')):
"""Sleeps the idle callback for t seconds. If t is not provided,
the idle callback is slept forever. Called by frontend."""
if duration==0:
glutIdleFunc(self.program.idlefunc);
else:
glutIdleFunc(None);
if duration!=float('inf'):
glutTimerFunc(int(duration*1000),lambda x:glutIdleFunc(self.program.idlefunc),0);
[docs] def reshape(self,w,h):
"""Resizes the GL window. Called by frontend."""
self.width,self.height = w,h
if self.initialized:
glutReshapeWindow(self.width,self.height)
[docs] def draw_text(self,point,text,size=12,color=None):
"""Renders text at the given point (may be 2d or 3d). Frontend should call this to draw text
in either the display or display_screen method.
Args:
point (list of floats): either a 2d or 3d point at which to draw the text
text (str): the text to draw
size (int, optional): if given, it renders a font in the given size.
color (list of 3 or 4 floats) if given, then an RGB or RGBA color value.
"""
import ctypes
if size <= 10:
font = GLUT_BITMAP_HELVETICA_10
elif size <= 12:
font = GLUT_BITMAP_HELVETICA_12
elif size <= 13:
font = GLUT_BITMAP_8_BY_13
elif size <= 16:
font = GLUT_BITMAP_9_BY_15
elif size <= 21:
font = GLUT_BITMAP_HELVETICA_12
else:
font = GLUT_TIMES_NEW_ROMAN_24
if color is None:
glColor3f(0,0,0)
elif len(color)==3:
glColor3f(color[0],color[1],color[2])
else:
glColor4f(color[0],color[1],color[2],color[3])
if len(point)==3:
glRasterPos3f(*point)
else:
glRasterPos2f(*point)
for c in text:
glutBitmapCharacter(font, ctypes.c_int( ord(c) ))
[docs] def close(self):
if self.index is not None:
self._closefunc()
glutDestroyWindow(self.index)
self.index = None
def _updateModifiers(self):
m = []
modifiers = glutGetModifiers()
if modifiers & GLUT_ACTIVE_CTRL:
m.append('ctrl')
if modifiers & GLUT_ACTIVE_SHIFT:
m.append('shift')
if modifiers & GLUT_ACTIVE_ALT:
m.append('alt')
self.modifierList = m
def _reshapefunc(self,w,h):
"""Internal use"""
self.width = w
self.height = h
self.program.reshapefunc(w,h)
glutPostRedisplay()
def _motionfunc(self,x,y):
"""Internal use"""
dx = x - self.lastx
dy = y - self.lasty
self.program.motionfunc(x,y,dx,dy)
self.lastx = x
self.lasty = y
def _mousefunc(self,button,state,x,y):
"""Internal use"""
self.program.mousefunc(button,state,x,y)
self.lastx = x
self.lasty = y
def _specialfunc(self,c,x,y):
if c in keymap:
self.program.keyboardfunc(keymap[c],x,y)
def _specialupfunc(self,c,x,y):
if c in keymap:
self.program.keyboardupfunc(keymap[c],x,y)
def _displayfunc(self):
"""Internal use."""
if self.width == 0 or self.height == 0:
#hidden?
print("GLProgram.displayfunc called on hidden window?")
return
self.program.displayfunc()
glutSwapBuffers ()
def _closefunc(self):
self.program.closefunc()
self.program.window = None
[docs]class GLUTBackend:
"""A basic OpenGL program using GLUT. Set up your GLProgramInterface class,
call addPlugin(plugin), then call run() to start the GLUT main loop.
NOTE: the run() call may not return depending on your GLUT system.
For more control over windowing, you can use the createWindow function to
construct new windows and addPlugin to add plugins to those windows.
IMPORTANT NOTE: only one window may be created for a given world. If you want to
use multiple windows, then a new world should be loaded for each world.
"""
def __init__(self):
self.glutInitialized = False
self.inGlutLoop = False
self.windows = []
[docs] def initialize(self,program_name):
if self.glutInitialized == False:
glutInit ([])
if bool(glutSetOption):
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE,GLUT_ACTION_GLUTMAINLOOP_RETURNS)
glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE)
self.glutInitialized = True
[docs] def createWindow(self,name):
self.initialize(name)
print("GLUTBackend.createWindow",name)
w = GLUTWindow(name)
self.windows.append(w)
return w
[docs] def run(self):
"""Starts the main loop. NOTE: if freeglut is not installed, this
will not return."""
# Initialize Glut
assert len(self.windows) >= 1,"Need to define at least one GL interface"
print("GLUTBackend: run with windows",[w.name for w in self.windows])
for w in self.windows:
if not w.initialized:
w.initialize()
else:
print(" Warning, window",w.name,"already initialized before run()?")
self.inGlutLoop = True
glutMainLoop ()