Example #1
0
	def __init__(self):
		BasicApplication.__init__(self)
		# initialize timer
		self.timer = Timer(self.OnTimer)
		# initialize state
		self.scene = Scene()
		self.scene.modified = False
		self.camera = Camera(Vector3f(4), Euler(DegToRad(45), DegToRad(-45)))
		self.selectedCamera = None
		# initialize window
		self.window = Window(self)
		self.window.canvas.OnRender = self.OnRender
Example #2
0
	def __init__(self):
		BasicApplication.__init__(self)
		# initialize timer
		self.timer = Timer(self.OnTimer)
		# initialize state
		self.scene = Scene()
		self.scene.modified = False
		self.camera = ZoomCamera()
		self.selected = []
		self.mode = self.SELECT_MODE
		# initialize window
		self.window = Window(self)
		self.window.canvas.OnRender = self.OnRender
Example #3
0
class Application(BasicApplication):
	# modes
	SELECT_MODE         = 0
	MOVE_MODE           = 1
	MOVE_X_MODE         = 2
	MOVE_Y_MODE         = 3
	MOVE_Z_MODE         = 4
	ROTATE_X_MODE       = 6
	ROTATE_Y_MODE       = 7
	ROTATE_Z_MODE       = 8
	SCALE_MODE          = 9
	SCALE_X_MODE        = 10
	SCALE_Y_MODE        = 11
	SCALE_Z_MODE        = 12
	SCALE_RADIAL_X_MODE = 13
	SCALE_RADIAL_Y_MODE = 14
	SCALE_RADIAL_Z_MODE = 15

	def __init__(self):
		BasicApplication.__init__(self)
		# initialize timer
		self.timer = Timer(self.OnTimer)
		# initialize state
		self.scene = Scene()
		self.scene.modified = False
		self.camera = ZoomCamera()
		self.selected = []
		self.mode = self.SELECT_MODE
		# initialize window
		self.window = Window(self)
		self.window.canvas.OnRender = self.OnRender

	def Update(self, deltaTime):
		if self.window.active:
			# update camera position
			translation = Vector3f()
			if wx.GetKeyState(ord('A')): translation.x -= 1
			if wx.GetKeyState(ord('D')): translation.x += 1
			if wx.GetKeyState(ord('Q')): translation.y -= 1
			if wx.GetKeyState(ord('E')): translation.y += 1
			if wx.GetKeyState(ord('W')): translation.z -= 1
			if wx.GetKeyState(ord('S')): translation.z += 1
			if any(translation):
				if wx.GetKeyState(wx.WXK_CONTROL): translation *= .25
				if wx.GetKeyState(wx.WXK_SHIFT): translation *= 4
				translation.x, translation.z = (
					Euler(self.camera.orientation.yaw, 0, 0) *
					Vector3f(translation.x, 0, translation.z)).Swizzle(0, 2)
				self.camera.position += (translation *
					prefs['camera.translation.speed'] * deltaTime)
				self.window.canvas.Refresh()

	def AskSave(self):
		if self.scene.modified:
			result = ShowSaveChangesDialog(self.window)
			if result: self.SaveScene()
			elif result is None: return False
		return True

	def Exit(self):
		if not self.Destroy(): return
		self.window.Close()

	def Destroy(self, ask = True):
		if ask and not self.AskSave(): return False
		self.timer.Stop()
		return True

	# rendering
	def Render(self):
		SetCameraMatrix(self.camera)
		DrawScene(self.scene, self.selected,
			(1., .5, .5) if self.mode == self.SELECT_MODE else (1., .75, .5))
		# draw wireframe
		if prefs['render.wireframe']:
			glPushAttrib(GL_DEPTH_BUFFER_BIT)
			glDepthMask(False)
			DrawSceneWireframe(self.scene)
			glPopAttrib()
		# draw world axes
		if prefs['render.axes']:
			self.DrawAxes()
	def DrawAxes(self):
		glTranslate(*self.camera.position)
		glPushAttrib(GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT)
		glDepthMask(False)
		glDepthFunc(GL_GEQUAL)
		glBegin(GL_LINES)
		axes = (
			Vector3f(1, 0, 0),
			Vector3f(0, 1, 0),
			Vector3f(0, 0, 1))
		for axis in axes:
			glColor((axis * 0.5).tolist())
			glVertex(0, 0, 0)
			glVertex((axis * 0.5).tolist())
		glEnd()
		glDepthMask(True)
		glDepthFunc(GL_LESS)
		glBegin(GL_LINES)
		for axis in axes:
			glColor(axis.tolist())
			glVertex(0, 0, 0)
			glVertex((axis * 0.5).tolist())
		glEnd()
		glPopAttrib()

	# selection
	def RenderSelect(self, cursorPosition):
		SetCameraMatrixForSelect(self.camera, cursorPosition)
		# initialize selection mode
		glSelectBuffer(256)
		glRenderMode(GL_SELECT)
		glInitNames()
		# draw scene
		DrawSceneForSelect(self.scene)
		glFlush()
		return glRenderMode(GL_RENDER)
	def Select(self, cursorPosition):
		nameStack = self.RenderSelect(cursorPosition)
		if nameStack:
			nearest = (None, 1.)
			for near, far, names in nameStack:
				if near < nearest[1]:
					nearest = (names, near)
			if nearest[0]:
				names = nearest[0]
				return self.scene.forms[names[0]]
		return None
	def UpdateSelection(self, cursorPosition, shiftDown, ctrlDown):
		selection = self.Select(cursorPosition)
		if selection:
			if shiftDown:
				if ctrlDown:
					if selection in self.selected:
						self.selected.remove(selection)
						self.window.canvas.Refresh()
				elif selection not in self.selected:
					self.selected.append(selection)
					self.window.canvas.Refresh()
			elif [selection] != self.selected:
				self.selected = [selection]
				self.window.canvas.Refresh()
		elif not shiftDown and self.selected:
			self.selected = []
			self.window.canvas.Refresh()

	# mode functions
	def SetMode(self, mode):
		self.mode = mode
		self.window.canvas.Refresh()
	def BakeMode(self):
		if self.mode != self.SELECT_MODE:
			for sel in self.selected:
				sel.BakeTransformation()
			self.SetMode(self.SELECT_MODE)
			self.scene.modified = True
	def ResetMode(self):
		if self.mode != self.SELECT_MODE:
			for sel in self.selected:
				sel.ResetTransformation()
			self.SetMode(self.SELECT_MODE)
	def ModeOnCursorMotion(self, motion):
		if self.mode == self.MOVE_MODE:
			translation = (
				Euler(self.camera.orientation.yaw, 0, 0) *
				Vector3f(motion.x, 0, motion.y) *
				prefs['tool.translation.speed'])
			for sel in self.selected:
				sel.translation += translation
		else:
			translation = motion.x * prefs['tool.translation.speed']
			rotation = motion.x * prefs['tool.rotation.speed']
			scale = motion.x * prefs['tool.scale.speed']
			if self.mode == self.MOVE_X_MODE:
				for sel in self.selected:
					sel.translation.x += translation
			elif self.mode == self.MOVE_Y_MODE:
				for sel in self.selected:
					sel.translation.y += translation
			elif self.mode == self.MOVE_Z_MODE:
				for sel in self.selected:
					sel.translation.z += translation
			elif self.mode == self.ROTATE_X_MODE:
				for sel in self.selected:
					sel.rotation.pitch += rotation
			elif self.mode == self.ROTATE_Y_MODE:
				for sel in self.selected:
					sel.rotation.yaw += rotation
			elif self.mode == self.ROTATE_Z_MODE:
				for sel in self.selected:
					sel.rotation.roll += rotation
			elif self.mode == self.SCALE_MODE:
				for sel in self.selected:
					sel.rescale += scale
			elif self.mode == self.SCALE_X_MODE:
				for sel in self.selected:
					sel.rescale.x += scale
			elif self.mode == self.SCALE_Y_MODE:
				for sel in self.selected:
					sel.rescale.y += scale
			elif self.mode == self.SCALE_Z_MODE:
				for sel in self.selected:
					sel.rescale.z += scale
			elif self.mode == self.SCALE_RADIAL_X_MODE:
				for sel in self.selected:
					sel.rescale.y += scale
					sel.rescale.z += scale
			elif self.mode == self.SCALE_RADIAL_Y_MODE:
				for sel in self.selected:
					sel.rescale.x += scale
					sel.rescale.z += scale
			elif self.mode == self.SCALE_RADIAL_Z_MODE:
				for sel in self.selected:
					sel.rescale.x += scale
					sel.rescale.y += scale
		self.window.canvas.Refresh()

	# camera modifiers
	def RotateCamera(self, cursorTranslation):
		cursorTranslation *= prefs['camera.rotation.speed']
		rotation = Euler([RevToRad(c) for c in -cursorTranslation])
		self.camera.orientation *= rotation
		self.window.canvas.Refresh()
	def GotoSelection(self):
		if self.selected:
			self.camera.position = \
				sum([sel.position for sel in self.selected]) / len(self.selected)
			self.window.canvas.Refresh()

	# scene modifiers
	def NewForm(self):
		path = ShowOpenModelDialog(self.window,
			os.path.dirname(prefs['file.model.last']) + os.path.sep)
		if path:
			model = Model().FromFile(path)
			name = os.path.splitext(os.path.basename(path))[0]
			form = self.scene.Form(name, model)
			form.position = self.camera.position
			form.orientation.yaw = self.camera.orientation.yaw
			self.scene.forms.append(form)
			self.scene.modified = True
			self.window.canvas.Refresh()
			prefs['file.model.last'] = path

	# absolute positioning
	def PutOnTrack(self):
		# FIXME: implement
		self.window.canvas.Refresh()
	def PutOnGround(self):
		print 'working...'
		for sel in self.selected:
			if sel.name:
				print '  %s...' % sel.name
			nearest = (NegInf, None)
			for form in self.scene.forms:
				if form.model and form not in self.selected:
					for part in form.model.parts:
						vertices = [form.matrix * vertex.co for vertex in part.mesh.vertices]
						for face in part.mesh.faces:
							if IsPointInTriangle(
								sel.position.Swizzle(0, 2),
								vertices[face.indices[0]].Swizzle(0, 2),
								vertices[face.indices[1]].Swizzle(0, 2),
								vertices[face.indices[2]].Swizzle(0, 2)):
								mu = Barycentric(
									sel.position.Swizzle(0, 2),
									vertices[face.indices[0]].Swizzle(0, 2),
									vertices[face.indices[1]].Swizzle(0, 2),
									vertices[face.indices[2]].Swizzle(0, 2))
								height = Bilerp(
									vertices[face.indices[0]].y,
									vertices[face.indices[1]].y,
									vertices[face.indices[2]].y, *mu)
								if (height <= sel.position.y + .001 and
									height > nearest[0]):
									normal = Bilerp(
										Orientation.GetMatrix(form) * part.mesh.vertices[face.indices[0]].no,
										Orientation.GetMatrix(form) * part.mesh.vertices[face.indices[1]].no,
										Orientation.GetMatrix(form) * part.mesh.vertices[face.indices[2]].no, *mu).Norm()
									nearest = (height, normal)
			if nearest[1]:
				sel.position.y = nearest[0]
				# FIXME: this implementation doesn't work
#				sel.orientation = EulerBetween(Vector3f(0, 1, 0), normal)
		self.window.canvas.Refresh()
		print 'done'

	# selection modifiers
	def DeleteSelection(self):
		if self.selected:
			for sel in self.selected:
				self.scene.forms.remove(sel)
			self.selected = []
			self.window.canvas.Refresh()
	def ToggleSelection(self):
		for form in self.scene.forms:
			if form in self.selected:
				self.selected = []
				break
		else:
			self.selected = self.scene.forms
		self.window.canvas.Refresh()

	# file menu functions
	def NewScene(self):
		if not self.AskSave(): return
		self.scene.__init__()
		self.scene.modified = False
		self.selected = []
		self.window.canvas.Refresh()
	def OpenScene(self):
		if not self.AskSave(): return
		path = ShowOpenSceneDialog(self.window,
			os.path.dirname(prefs['file.scene.last']) + os.path.sep)
		if path:
			self.scene.FromFile(path)
			self.scene.modified = False
			self.selected = []
			self.window.canvas.Refresh()
			prefs['file.scene.last'] = path
			print 'scene loaded from %s' % self.scene.path
	def SaveScene(self):
		if self.scene.modified:
			if self.scene.absPath:
				self.scene.ToFile()
				self.scene.modified = False
				print 'scene saved as %s' % self.scene.path
			else:
				self.SaveSceneAs()
	def SaveSceneAs(self):
		scenePath = ShowSaveSceneDialog(self.window, self.scene.absPath)
		if scenePath:
			self.scene.ToFile(scenePath)
			self.scene.modified = False
			print 'scene saved as %s' % self.scene.path
	def RevertScene(self):
		if self.scene.absPath:
			self.scene.FromFile()
			self.scene.modified = False
			self.selected = []
			self.window.canvas.Refresh()
			print 'scene reverted from %s' % self.scene.path
	def ChangeRootDir(self):
		# FIXME: this should be a standard dialog
		# FIXME: we will have to reload all resources if root is changed
		pass
		'''
		dlg = wx.DirDialog(self, 'Choose a root directory for loading resources', prefs['res.root'], wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST)
		if dlg.ShowModal() == wx.ID_OK:
			prefs['res.root'] = dlg.GetPath()
		'''
	def ShowPreferences(self):
		pass # FIXME: show preference dialog

	# event handlers
	def OnRender(self):
		self.timer.Update()
	def OnTimer(self):
		self.Update(self.timer.delta)
Example #4
0
class Application(BasicApplication):
	# selection constraints
	selectFaceConstraint     = 1
	deselectFaceConstraint   = 2
	selectCameraConstraint   = 3
	deselectCameraConstraint = 4

	def __init__(self):
		BasicApplication.__init__(self)
		# initialize timer
		self.timer = Timer(self.OnTimer)
		# initialize state
		self.scene = Scene()
		self.scene.modified = False
		self.camera = Camera(Vector3f(4), Euler(DegToRad(45), DegToRad(-45)))
		self.selectedCamera = None
		# initialize window
		self.window = Window(self)
		self.window.canvas.OnRender = self.OnRender

	def Update(self, deltaTime):
		if self.window.active:
			# update camera position
			translation = Vector3f()
			if wx.GetKeyState(ord('A')): translation.x -= 1
			if wx.GetKeyState(ord('D')): translation.x += 1
			if wx.GetKeyState(ord('Q')): translation.y -= 1
			if wx.GetKeyState(ord('E')): translation.y += 1
			if wx.GetKeyState(ord('W')): translation.z -= 1
			if wx.GetKeyState(ord('S')): translation.z += 1
			if any(translation):
				if wx.GetKeyState(wx.WXK_CONTROL): translation *= .25;
				if wx.GetKeyState(wx.WXK_SHIFT): translation *= 4;
				self.camera.position += (
					self.camera.orientation * translation *
					prefs['camera.translation.speed'] * deltaTime)
				self.window.canvas.Refresh()

	def AskSave(self):
		if (self.scene.modified or
			self.scene.cameraSet and self.scene.cameraSet.modified):
			result = ShowSaveChangesDialog(self.window)
			if result: self.Save()
			elif result is None: return False
		return True

	def Exit(self):
		if not self.Destroy(): return
		self.window.Close()

	def Destroy(self, ask = True):
		if ask and not self.AskSave(): return False
		self.timer.Stop()
		return True

	# rendering
	def RenderEdit(self):
		glPushAttrib(GL_VIEWPORT)
		viewport = glGetIntegerv(GL_VIEWPORT)
		glViewport(
			viewport[0],     viewport[1],
			viewport[2] / 2, viewport[3])
		SetCameraMatrix(self.camera)
		# draw track
		if self.scene.track:
			DrawTrack(self.scene.track, (
				[face for i, face in enumerate(self.scene.track.faces)
				if self.selectedCamera in self.scene.cameraSet.trackFaces[i].cameras]
				if self.scene.cameraSet else ()))
		# draw cameras
		if self.scene.cameraSet:
			DrawCameras(self.scene.cameraSet.cameras, (self.selectedCamera,))
		glPopAttrib()
	def RenderView(self):
		glPushAttrib(GL_VIEWPORT)
		viewport = glGetIntegerv(GL_VIEWPORT)
		glViewport(
			viewport[0] + viewport[2] / 2, viewport[1],
			viewport[2] - viewport[2] / 2, viewport[3])
		SetCameraMatrix(self.selectedCamera or self.camera)
		# draw scene
		DrawScene(self.scene)
		# draw track
		if self.scene.track:
			DrawTrack(self.scene.track, (
				[face for i, face in enumerate(self.scene.track.faces)
				if self.selectedCamera in self.scene.cameraSet.trackFaces[i].cameras]
				if self.scene.cameraSet else ()))
		glPopAttrib()

	# selection
	def RenderSelect(self, cursorPosition):
		glPushAttrib(GL_VIEWPORT)
		viewport = glGetIntegerv(GL_VIEWPORT)
		glViewport(
			viewport[0],     viewport[1],
			viewport[2] / 2, viewport[3])
		SetCameraMatrixForSelect(self.camera, cursorPosition)
		# initialize selection mode
		glSelectBuffer(256)
		glRenderMode(GL_SELECT)
		glInitNames()
		# draw track
		if self.scene.track:
			glPushName(0)
			DrawTrackForSelect(self.scene.track)
			glPopName()
		# draw cameras
		if self.scene.cameraSet:
			glPushName(1)
			DrawCamerasForSelect(self.scene.cameraSet.cameras)
			glPopName()
		glPopAttrib()
		glFlush()
		return glRenderMode(GL_RENDER)
	def Select(self, cursorPosition):
		nameStack = self.RenderSelect(cursorPosition)
		if nameStack:
			nearest = (None, 1.)
			for near, far, names in nameStack:
				if near < nearest[1]:
					nearest = (names, near)
			if nearest[0]:
				names = nearest[0]
				if names[0] == 0: return self.scene.track.faces[names[1]]
				if names[0] == 1: return self.scene.cameraSet.cameras[names[1]]
				assert not 'invalid name in stack'
		return None
	def UpdateSelection(self, cursorPosition, reset = False):
		if reset: self.__selectConstraint = None
		cursorPosition = Vector2i(cursorPosition)
		if cursorPosition.x < self.window.canvas.Size[0] / 2:
			selection = self.Select(cursorPosition)
			if isinstance(selection, Track.Face):
				if self.selectedCamera:
					i = self.scene.track.faces.index(selection)
					cameras = self.scene.cameraSet.trackFaces[i].cameras
					if self.selectedCamera not in cameras:
						if (not self.__selectConstraint or
							self.__selectConstraint is self.selectFaceConstraint):
							self.__selectConstraint = self.selectFaceConstraint
							cameras.append(self.selectedCamera)
							self.scene.cameraSet.modified = True
							self.window.canvas.Refresh()
					else:
						if (not self.__selectConstraint or
							self.__selectConstraint is self.deselectFaceConstraint):
							self.__selectConstraint = self.deselectFaceConstraint
							cameras.remove(self.selectedCamera)
							self.scene.cameraSet.modified = True
							self.window.canvas.Refresh()
			elif isinstance(selection, CameraSet.Camera):
				if (not self.__selectConstraint or
					self.__selectConstraint is self.selectCameraConstraint):
					self.__selectConstraint = self.selectCameraConstraint
					if selection is not self.selectedCamera:
						self.selectedCamera = selection
						self.window.canvas.Refresh()
			elif selection is None:
				if (not self.__selectConstraint or
					self.__selectConstraint is self.deselectCameraConstraint):
					self.__selectConstraint = self.deselectCameraConstraint
					if self.selectedCamera:
						self.selectedCamera = None
						self.window.canvas.Refresh()

	# camera modifiers
	def RotateCamera(self, cursorTranslation):
		cursorTranslation *= prefs['camera.rotation.speed'] * .001
		rotation = Euler([RevToRad(c) for c in -cursorTranslation])
		self.camera.orientation *= rotation
		self.window.canvas.Refresh()

	# camera set modifiers
	def NewCamera(self):
		if not self.scene.cameraSet:
			MakeCameraSet(self.scene)
		camera = CameraSet.Camera()
		camera.position = self.camera.position
		camera.orientation = Quaternion(self.camera.orientation)
		camera.fov = self.camera.fov
		self.scene.cameraSet.cameras.append(camera)
		self.selectedCamera = camera
		self.scene.cameraSet.modified = True
		self.window.canvas.Refresh()
	def DeleteSelectedCamera(self):
		assert self.selectedCamera
		self.ClearFaceSelection()
		self.scene.cameraSet.cameras.remove(self.selectedCamera)
		self.scene.cameraSet.modified = True
		self.selectedCamera = None
		self.window.canvas.Refresh()
	def RepositionSelectedCamera(self):
		assert self.selectedCamera
		self.selectedCamera.position = self.camera.position
		self.selectedCamera.orientation = Quaternion(self.camera.orientation)
		self.scene.cameraSet.modified = True
		self.window.canvas.Refresh()
	def ShowSelectedCameraProperties(self):
		if CameraPropertiesDialog(self.window.canvas, self.selectedCamera).ShowModal():
			self.scene.cameraSet.modified = True
			self.window.canvas.Refresh()

	# track face selection modifiers
	def ClearFaceSelection(self):
		assert self.selectedCamera
		for face in self.scene.cameraSet.trackFaces:
			if self.selectedCamera in face.cameras:
				face.cameras.remove(self.selectedCamera)
		self.scene.cameraSet.modified = True
		self.window.canvas.Refresh()
	def ToggleFaceSelection(self):
		assert self.selectedCamera
		for face in self.scene.cameraSet.trackFaces:
			if self.selectedCamera in face.cameras:
				self.ClearFaceSelection()
				return
		for face in self.scene.cameraSet.trackFaces:
			face.cameras.append(self.selectedCamera)
		self.scene.cameraSet.modified = True
		self.window.canvas.Refresh()

	# file menu functions
	def NewScene(self):
		if not self.AskSave(): return
		self.scene.__init__()
		self.scene.modified = False
		self.selectedCamera = None
		self.window.canvas.Refresh()
	def OpenScene(self):
		if not self.AskSave(): return
		path = ShowOpenSceneDialog(self.window,
			os.path.dirname(prefs['file.scene.last']) + os.path.sep)
		if path:
			self.scene.FromFile(path)
			self.scene.modified = False
			if self.scene.cameraSet:
				self.scene.cameraSet.modified = False
				if self.scene.track:
					BindCameraSet(self.scene.cameraSet, self.scene.track)
			self.selectedCamera = None
			self.window.canvas.Refresh()
			prefs['file.scene.last'] = path
			print 'scene loaded from %s' % self.scene.path
	def Save(self):
		# request scene path
		if self.scene.modified and not self.scene.absPath:
			scenePath = ShowSaveSceneDialog(self.window)
			if not scenePath: return
		# request camera set path
		if self.scene.cameraSet and self.scene.cameraSet.modified and not self.scene.cameraSet.absPath:
			cameraSetPath = ShowSaveCameraSetDialog(self.window,
				(self.scene.absPath or scenePath).replace('.scene', '.cam'))
			if not cameraSetPath: return
			# NOTE: must set path here so that scene will know about it
			self.scene.cameraSet.SetPath(cameraSetPath)
		# save scene
		if self.scene.modified:
			self.scene.ToFile(self.scene.absPath or scenePath)
			self.scene.modified = False
			print 'scene saved as %s' % self.scene.path
		# save camera set
		if self.scene.cameraSet and self.scene.cameraSet.modified:
			self.scene.cameraSet.ToFile()
			self.scene.cameraSet.modified = False
			print 'camera set saved as %s' % self.scene.cameraSet.path
	def SaveAs(self):
		# request scene path
		scenePath = ShowSaveSceneDialog(self.window, self.scene.absPath)
		# request camera set path
		if self.scene.cameraSet:
			cameraSetPath = ShowSaveCameraSetDialog(self.window, self.cameraSet.absPath)
			if cameraSetPath:
				# NOTE: must set path here so that scene will know about it
				self.scene.cameraSet.SetPath(cameraSetPath)
		# save scene
		if scenePath:
			self.scene.ToFile(scenePath)
			self.scene.modified = False
			print 'scene saved as %s' % self.scene.path
		# save camera set
		if self.scene.cameraSet and cameraSetPath:
			self.scene.cameraSet.ToFile()
			self.scene.cameraSet.modified = False
			print 'camera set saved as %s' % self.scene.cameraSet.path
	def Revert(self):
		if self.scene.absPath:
			self.scene.FromFile()
			self.scene.modified = False
			if self.scene.cameraSet:
				self.scene.cameraSet.modified = False
			self.selectedCamera = None
			self.window.canvas.Refresh()
			print 'scene reverted from %s' % self.scene.path

	# event handlers
	def OnRender(self):
		self.timer.Update()
	def OnTimer(self):
		self.Update(self.timer.delta)