def __init__(self, tkwin=None): """ Constructs a raw Display. Use pi3d.Display.create to create an initialized Display. *tkwin* An optional Tk window. """ if Display.INSTANCE: assert ALLOW_MULTIPLE_DISPLAYS LOGGER.warning('A second instance of Display was created') else: Display.INSTANCE = self self.tkwin = tkwin self.sprites = [] self.sprites_to_load = set() self.sprites_to_unload = set() self.opengl = DisplayOpenGL() self.max_width, self.max_height = self.opengl.width, self.opengl.height self.first_time = True self.is_running = True self.lock = threading.RLock() LOGGER.debug(STARTUP_MESSAGE)
def __init__(self, tkwin=None, use_pygame=False): """ Constructs a raw Display. Use pi3d.Display.create to create an initialized Display. *tkwin* An optional Tk window. *use_pygame* Flag to opt for pygame """ if Display.INSTANCE is not None: assert ALLOW_MULTIPLE_DISPLAYS LOGGER.warning('A second instance of Display was created') else: Display.INSTANCE = self self.tkwin = tkwin if pi3d.PLATFORM == pi3d.PLATFORM_PI: use_pygame = False elif use_pygame or pi3d.PLATFORM == pi3d.PLATFORM_WINDOWS: try: import pygame use_pygame = True # for Windows except ImportError: LOGGER.warning('Do you need to install pygame?') use_pygame = False pi3d.USE_PYGAME = use_pygame self.sprites = [] self.sprites_to_load = set() self.sprites_to_unload = set() self.tidy_needed = False self.textures_dict = {} self.vbufs_dict = {} self.ebufs_dict = {} self.last_shader = None self.last_textures = [ None, None, None ] # if more than 3 used this will break in Buffer.draw() self.external_mouse = None if (pi3d.PLATFORM != pi3d.PLATFORM_PI and pi3d.PLATFORM != pi3d.PLATFORM_ANDROID and not pi3d.USE_PYGAME): self.event_list = [] self.ev = xlib.XEvent() elif pi3d.PLATFORM == pi3d.PLATFORM_ANDROID: self.android = Pi3dApp() self.opengl = DisplayOpenGL() self.max_width, self.max_height = self.opengl.width, self.opengl.height self.first_time = True self.is_running = True self.lock = threading.RLock() LOGGER.debug(pi3d.STARTUP_MESSAGE)
def __init__(self, tkwin=None): """ Constructs a raw Display. Use pi3d.Display.create to create an initialized Display. *tkwin* An optional Tk window. """ if Display.INSTANCE is not None: assert ALLOW_MULTIPLE_DISPLAYS LOGGER.warning('A second instance of Display was created') else: Display.INSTANCE = self self.tkwin = tkwin self.sprites = [] self.sprites_to_load = set() self.sprites_to_unload = set() self.tidy_needed = False self.textures_dict = {} self.vbufs_dict = {} self.ebufs_dict = {} self.last_shader = None self.last_textures = [ None, None, None ] # if more than 3 used this will break in Buffer.draw() self.external_mouse = None if (PLATFORM != PLATFORM_PI and PLATFORM != PLATFORM_ANDROID and PLATFORM != PLATFORM_WINDOWS): self.event_list = [] self.ev = xlib.XEvent() elif PLATFORM == PLATFORM_ANDROID: self.android = Pi3dApp() self.opengl = DisplayOpenGL() self.max_width, self.max_height = self.opengl.width, self.opengl.height self.first_time = True self.is_running = True self.lock = threading.RLock() LOGGER.debug(STARTUP_MESSAGE)
def __init__(self, tkwin=None, use_pygame=False): """ Constructs a raw Display. Use pi3d.Display.create to create an initialized Display. *tkwin* An optional Tk window. *use_pygame* Flag to opt for pygame """ if Display.INSTANCE is not None: assert ALLOW_MULTIPLE_DISPLAYS LOGGER.warning('A second instance of Display was created') else: Display.INSTANCE = self self.tkwin = tkwin if pi3d.PLATFORM == pi3d.PLATFORM_PI: use_pygame = False elif use_pygame or pi3d.PLATFORM == pi3d.PLATFORM_WINDOWS: try: import pygame use_pygame = True # for Windows except ImportError: LOGGER.warning('Do you need to install pygame?') use_pygame = False pi3d.USE_PYGAME = use_pygame self.sprites = [] self.sprites_to_load = set() self.sprites_to_unload = set() self.tidy_needed = False self.textures_dict = {} self.vbufs_dict = {} self.ebufs_dict = {} self.last_shader = None self.last_textures = [None, None, None] # if more than 3 used this will break in Buffer.draw() self.external_mouse = None self.offscreen_tex = False # used in Buffer.draw() to force reload of textures if (pi3d.PLATFORM != pi3d.PLATFORM_PI and pi3d.PLATFORM != pi3d.PLATFORM_ANDROID and not pi3d.USE_PYGAME): self.event_list = [] self.ev = xlib.XEvent() elif pi3d.PLATFORM == pi3d.PLATFORM_ANDROID: self.android = Pi3dApp() self.opengl = DisplayOpenGL() self.max_width, self.max_height = self.opengl.width, self.opengl.height self.first_time = True self.is_running = True self.lock = threading.RLock() LOGGER.debug(pi3d.STARTUP_MESSAGE)
def __init__(self, tkwin=None): """ Constructs a raw Display. Use pi3d.Display.create to create an initialized Display. *tkwin* An optional Tk window. """ if Display.INSTANCE is not None: assert ALLOW_MULTIPLE_DISPLAYS LOGGER.warning('A second instance of Display was created') else: Display.INSTANCE = self self.tkwin = tkwin self.sprites = [] self.sprites_to_load = set() self.sprites_to_unload = set() self.tidy_needed = False self.textures_dict = {} self.vbufs_dict = {} self.ebufs_dict = {} self.last_shader = None self.last_textures = [None, None, None] # if more than 3 used this will break in Buffer.draw() self.external_mouse = None if (PLATFORM != PLATFORM_PI and PLATFORM != PLATFORM_ANDROID and PLATFORM != PLATFORM_WINDOWS): self.event_list = [] self.ev = xlib.XEvent() elif PLATFORM == PLATFORM_ANDROID: self.android = Pi3dApp() self.opengl = DisplayOpenGL() self.max_width, self.max_height = self.opengl.width, self.opengl.height self.first_time = True self.is_running = True self.lock = threading.RLock() LOGGER.debug(STARTUP_MESSAGE)
def __init__(self, tkwin=None): """ Constructs a raw Display. Use pi3d.Display.create to create an initialized Display. *tkwin* An optional Tk window. """ if Display.INSTANCE: assert ALLOW_MULTIPLE_DISPLAYS LOGGER.warning('A second instance of Display was created') else: Display.INSTANCE = self self.tkwin = tkwin self.sprites = [] self.sprites_to_load = set() self.sprites_to_unload = set() self.tidy_needed = False self.textures_dict = {} self.vbufs_dict = {} self.ebufs_dict = {} self.external_mouse = None if PLATFORM != PLATFORM_PI: self.event_list = [] self.ev = xlib.XEvent() self.opengl = DisplayOpenGL() self.max_width, self.max_height = self.opengl.width, self.opengl.height self.first_time = True self.is_running = True self.lock = threading.RLock() LOGGER.debug(STARTUP_MESSAGE)
class Display(object): """This is the central control object of the pi3d system and an instance must be created before some of the other class methods are called. """ INSTANCE = None """The current unique instance of Display.""" def __init__(self, tkwin=None, use_pygame=False): """ Constructs a raw Display. Use pi3d.Display.create to create an initialized Display. *tkwin* An optional Tk window. *use_pygame* Flag to opt for pygame """ if Display.INSTANCE is not None: assert ALLOW_MULTIPLE_DISPLAYS LOGGER.warning('A second instance of Display was created') else: Display.INSTANCE = self self.tkwin = tkwin if PLATFORM == PLATFORM_PI: use_pygame = False elif use_pygame or PLATFORM == PLATFORM_WINDOWS: try: import pygame use_pygame = True # for Windows except ImportError: LOGGER.warning('Do you need to install pygame?') use_pygame = False pi3d.USE_PYGAME = use_pygame self.sprites = [] self.sprites_to_load = set() self.sprites_to_unload = set() self.tidy_needed = False self.textures_dict = {} self.vbufs_dict = {} self.ebufs_dict = {} self.last_shader = None self.last_textures = [None for _ in range(8) ] # 8 is max no. texture2D on broadcom GPU self.external_mouse = None self.offscreen_tex = False # used in Buffer.draw() to force reload of textures if (PLATFORM != PLATFORM_PI and PLATFORM != PLATFORM_ANDROID and not pi3d.USE_PYGAME): self.event_list = [] self.ev = xlib.XEvent() elif PLATFORM == PLATFORM_ANDROID: self.android = Pi3dApp() self.opengl = DisplayOpenGL() self.max_width, self.max_height = self.opengl.width, self.opengl.height self.first_time = True self.is_running = True self.lock = threading.RLock() LOGGER.debug(STARTUP_MESSAGE) def loop_running(self): """*loop_running* is the main event loop for the Display. Most pi3d code will look something like this:: DISPLAY = Display.create() # Initialize objects and variables here. # ... while DISPLAY.loop_running(): # Update the frame, using DISPLAY.time for the current time. # ... # Check for quit, then call DISPLAY.stop. if some_quit_condition(): DISPLAY.stop() ``Display.loop_running()`` **must** be called on the main Python thread, or else white screens and program crashes are likely. The Display loop can run in two different modes - *free* or *framed*. If ``DISPLAY.frames_per_second`` is empty or 0 then the loop runs *free* - when it finishes one frame, it immediately starts working on the next frame. If ``Display.frames_per_second`` is a positive number then the Display is *framed* - when the Display finishes one frame before the next frame_time, it waits till the next frame starts. A free Display gives the highest frame rate, but it will also consume more CPU, to the detriment of other threads or other programs. There is also the significant drawback that the framerate will fluctuate as the numbers of CPU cycles consumed per loop, resulting in jerky motion and animations. A framed Display has a consistent if smaller number of frames, and also allows for potentially much smoother motion and animation. The ability to throttle down the number of frames to conserve CPU cycles is essential for programs with other important threads like audio. ``Display.frames_per_second`` can be set at construction in ``Display.create`` or changed on-the-fly during the execution of the program. If ``Display.frames_per_second`` is set too high, the Display doesn't attempt to "catch up" but simply runs freely. """ if self.is_running: if self.first_time: self.time = time.time() self.first_time = False else: self._loop_end() # Finish the previous loop. self._loop_begin() else: self._loop_end() self.destroy() return self.is_running def resize(self, x=0, y=0, w=0, h=0): """Reshape the window with the given coordinates.""" if w <= 0: w = self.max_width if h <= 0: h = self.max_height self.width = w self.height = h self.left = x self.top = y self.right = x + w self.bottom = y + h self.opengl.resize(x, y, w, h, self.layer) def change_layer(self, layer=0): self.layer = layer self.opengl.change_layer(layer) def add_sprites(self, *sprites): """Add one or more sprites to this Display.""" with self.lock: self.sprites_to_load.update(sprites) def remove_sprites(self, *sprites): """Remove one or more sprites from this Display.""" with self.lock: self.sprites_to_unload.update(sprites) def stop(self): """Stop the Display.""" self.is_running = False def destroy(self): """Destroy the current Display and reset Display.INSTANCE.""" self._tidy() self.stop() try: self.opengl.destroy(self) except: pass if self.external_mouse: try: self.external_mouse.stop() except: pass try: self.mouse.stop() except: pass try: self.tkwin.destroy() except: pass Display.INSTANCE = None try: import pygame # NB seems to be needed on some setups (64 bit anaconda windows!) pygame.quit() except: pass def clear(self): """Clear the Display.""" # opengles.glBindFramebuffer(GL_FRAMEBUFFER,0) opengles.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) def set_background(self, r, g, b, alpha): """Set the Display background. **NB the actual drawing of the background happens during the rendering of the framebuffer by the shader so if no draw() is done by anything during each Display loop the screen will remain black** If you want to see just the background you will have to draw() something out of view (i.e. behind) the Camera. *r, g, b* Color values for the display *alpha* Opacity of the color. An alpha of 0 means a transparent background, an alpha of 1 means full opaque. """ opengles.glClearColor(c_float(r), c_float(g), c_float(b), c_float(alpha)) opengles.glColorMask(1, 1, 1, int(alpha < 1.0)) # Switches off alpha blending with desktop (is there a bug in the driver?) def mouse_position(self): """The current mouse position as a tuple.""" # TODO: add: Now deprecated in favor of pi3d.events if self.mouse: return self.mouse.position() elif self.tkwin: return self.tkwin.winfo_pointerxy() else: return -1, -1 def _loop_begin(self): # TODO(rec): check if the window was resized and resize it, removing # code from MegaStation to here. if pi3d.USE_PYGAME: import pygame # although done in __init__ ...python namespaces aarg!!! if pygame.event.get(pygame.QUIT): self.destroy() elif PLATFORM != PLATFORM_PI and PLATFORM != PLATFORM_ANDROID: n = xlib.XEventsQueued(self.opengl.d, xlib.QueuedAfterFlush) for _ in range(n): xlib.XNextEvent(self.opengl.d, self.ev) if self.ev.type == KeyPress or self.ev.type == KeyRelease: self.event_list.append(self.ev) elif self.ev.type == ClientMessage: if (self.ev.xclient.data.l[0] == self.opengl.WM_DELETE_WINDOW.value): self.destroy() self.clear() with self.lock: self.sprites_to_load, to_load = set(), self.sprites_to_load self.sprites.extend(to_load) self._for_each_sprite(lambda s: s.load_opengl(), to_load) if MARK_CAMERA_CLEAN_ON_EACH_LOOP: from pi3d.Camera import Camera #camera = Camera.instance() #if camera is not None: # camera.was_moved = False cameras = Camera.all_instances() if cameras is not None: for camera in cameras: camera.was_moved = False if self.tidy_needed: self._tidy() def _tidy(self): to_del = [] for i in self.textures_dict: tex = self.textures_dict[i] if tex[1] == 1: opengles.glDeleteTextures(1, byref(tex[0])) to_del.append(i) for i in to_del: del self.textures_dict[i] to_del = [] for i in self.vbufs_dict: vbuf = self.vbufs_dict[i] if vbuf[1] == 1: opengles.glDeleteBuffers(1, byref(vbuf[0])) to_del.append(i) for i in to_del: del self.vbufs_dict[i] to_del = [] for i in self.ebufs_dict: ebuf = self.ebufs_dict[i] if ebuf[1] == 1: opengles.glDeleteBuffers(1, byref(ebuf[0])) to_del.append(i) for i in to_del: del self.ebufs_dict[i] self.tidy_needed = False def _loop_end(self): if pi3d.USE_PYGAME: import pygame pygame.event.clear() with self.lock: self.sprites_to_unload, to_unload = set(), self.sprites_to_unload if to_unload: self.sprites = [s for s in self.sprites if s not in to_unload] t = time.time() self._for_each_sprite(lambda s: s.repaint(t)) self.swap_buffers() for sprite in to_unload: sprite.unload_opengl() if getattr(self, 'frames_per_second', 0): self.time += 1.0 / self.frames_per_second delta = self.time - time.time() if delta > 0: time.sleep(delta) def _for_each_sprite(self, function, sprites=None): if sprites is None: sprites = self.sprites for s in sprites: try: function(s) except: LOGGER.error(traceback.format_exc()) if RAISE_EXCEPTIONS: raise def __del__(self): try: self.destroy() except: pass # often something has already been deleted leading to various None objects def swap_buffers(self): self.opengl.swap_buffers()
class Display(object): """This is the central control object of the pi3d system and an instance must be created before some of the other class methods are called. """ INSTANCE = None """The current unique instance of Display.""" def __init__(self, tkwin=None): """ Constructs a raw Display. Use pi3d.Display.create to create an initialized Display. *tkwin* An optional Tk window. """ if Display.INSTANCE: assert ALLOW_MULTIPLE_DISPLAYS LOGGER.warning('A second instance of Display was created') else: Display.INSTANCE = self self.tkwin = tkwin self.sprites = [] self.sprites_to_load = set() self.sprites_to_unload = set() self.opengl = DisplayOpenGL() self.max_width, self.max_height = self.opengl.width, self.opengl.height self.first_time = True self.is_running = True self.lock = threading.RLock() LOGGER.debug(STARTUP_MESSAGE) def loop_running(self): """*loop_running* is the main event loop for the Display. Most pi3d code will look something like this:: DISPLAY = Display.create() # Initialize objects and variables here. # ... while DISPLAY.loop_running(): # Update the frame, using DISPLAY.time for the current time. # ... # Check for quit, then call DISPLAY.stop. if some_quit_condition(): DISPLAY.stop() ``Display.loop_running()`` **must** be called on the main Python thread, or else white screens and program crashes are likely. The Display loop can run in two different modes - *free* or *framed*. If ``DISPLAY.frames_per_second`` is empty or 0 then the loop runs *free* - when it finishes one frame, it immediately starts working on the next frame. If ``Display.frames_per_second`` is a positive number then the Display is *framed* - when the Display finishes one frame before the next frame_time, it waits till the next frame starts. A free Display gives the highest frame rate, but it will also consume more CPU, to the detriment of other threads or other programs. There is also the significant drawback that the framerate will fluctuate as the numbers of CPU cycles consumed per loop, resulting in jerky motion and animations. A framed Display has a consistent if smaller number of frames, and also allows for potentially much smoother motion and animation. The ability to throttle down the number of frames to conserve CPU cycles is essential for programs with other important threads like audio. ``Display.frames_per_second`` can be set at construction in ``Display.create`` or changed on-the-fly during the execution of the program. If ``Display.frames_per_second`` is set too high, the Display doesn't attempt to "catch up" but simply runs freely. """ if self.is_running: if self.first_time: self.time = time.time() self.first_time = False else: self._loop_end() # Finish the previous loop. self._loop_begin() else: self._loop_end() self.destroy() return self.is_running def resize(self, x=0, y=0, w=0, h=0): """Reshape the window with the given coordinates.""" if w <= 0: w = display.max_width if h <= 0: h = display.max_height self.width = w self.height = h self.left = x self.top = y self.right = x + w self.bottom = y + h self.opengl.resize(x, y, w, h) def add_sprites(self, *sprites): """Add one or more sprites to this Display.""" with self.lock: self.sprites_to_load.update(sprites) def remove_sprites(self, *sprites): """Remove one or more sprites from this Display.""" with self.lock: self.sprites_to_unload.update(sprites) def stop(self): """Stop the Display.""" self.is_running = False def destroy(self): """Destroy the current Display and reset Display.INSTANCE.""" self.stop() try: self.opengl.destroy() except: pass try: self.mouse.stop() except: pass try: self.tkwin.destroy() except: pass Display.INSTANCE = None def clear(self): """Clear the Display.""" # opengles.glBindFramebuffer(GL_FRAMEBUFFER,0) opengles.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) def set_background(self, r, g, b, alpha): """Set the Display background. **NB the actual drawing of the background happens during the rendering of the framebuffer by the shader so if no draw() is done by anything during each Display loop the screen will remain black** If you want to see just the background you will have to draw() something out of view (i.e. behind) the Camera. *r, g, b* Color values for the display *alpha* Opacity of the color. An alpha of 0 means a transparent background, an alpha of 1 means full opaque. """ opengles.glClearColor(c_float(r), c_float(g), c_float(b), c_float(alpha)) opengles.glColorMask(1, 1, 1, int(alpha < 1.0)) # Switches off alpha blending with desktop (is there a bug in the driver?) def mouse_position(self): """The current mouse position as a tuple.""" # TODO: add: Now deprecated in favor of pi3d.events if self.mouse: return self.mouse.position() elif self.tkwin: return self.tkwin.winfo_pointerxy() else: return -1, -1 def _loop_begin(self): # TODO(rec): check if the window was resized and resize it, removing # code from MegaStation to here. self.clear() with self.lock: self.sprites_to_load, to_load = set(), self.sprites_to_load self.sprites.extend(to_load) self._for_each_sprite(lambda s: s.load_opengl(), to_load) if MARK_CAMERA_CLEAN_ON_EACH_LOOP: from pi3d.Camera import Camera camera = Camera.instance() if camera: camera.was_moved = False def _loop_end(self): with self.lock: self.sprites_to_unload, to_unload = set(), self.sprites_to_unload if to_unload: self.sprites = [s for s in self.sprites if s not in to_unload] t = time.time() self._for_each_sprite(lambda s: s.repaint(t)) self.swap_buffers() for sprite in to_unload: sprite.unload_opengl() if getattr(self, 'frames_per_second', 0): self.time += 1.0 / self.frames_per_second delta = self.time - time.time() if delta > 0: time.sleep(delta) def _for_each_sprite(self, function, sprites=None): if sprites is None: sprites = self.sprites for s in sprites: try: function(s) except: LOGGER.error(traceback.format_exc()) if RAISE_EXCEPTIONS: raise def __del__(self): self.destroy() def swap_buffers(self): self.opengl.swap_buffers()
class Display(object): """This is the central control object of the pi3d system and an instance must be created before some of the other class methods are called. """ INSTANCE = None """The current unique instance of Display.""" def __init__(self, tkwin=None, use_pygame=False): """ Constructs a raw Display. Use pi3d.Display.create to create an initialized Display. *tkwin* An optional Tk window. *use_pygame* Flag to opt for pygame """ if Display.INSTANCE is not None: assert ALLOW_MULTIPLE_DISPLAYS LOGGER.warning('A second instance of Display was created') else: Display.INSTANCE = self self.tkwin = tkwin if pi3d.PLATFORM == pi3d.PLATFORM_PI: use_pygame = False elif use_pygame or pi3d.PLATFORM == pi3d.PLATFORM_WINDOWS: try: import pygame use_pygame = True # for Windows except ImportError: LOGGER.warning('Do you need to install pygame?') use_pygame = False pi3d.USE_PYGAME = use_pygame self.sprites = [] self.sprites_to_load = set() self.sprites_to_unload = set() self.tidy_needed = False self.textures_dict = {} self.vbufs_dict = {} self.ebufs_dict = {} self.last_shader = None self.last_textures = [None, None, None] # if more than 3 used this will break in Buffer.draw() self.external_mouse = None if (pi3d.PLATFORM != pi3d.PLATFORM_PI and pi3d.PLATFORM != pi3d.PLATFORM_ANDROID and not pi3d.USE_PYGAME): self.event_list = [] self.ev = xlib.XEvent() elif pi3d.PLATFORM == pi3d.PLATFORM_ANDROID: self.android = Pi3dApp() self.opengl = DisplayOpenGL() self.max_width, self.max_height = self.opengl.width, self.opengl.height self.first_time = True self.is_running = True self.lock = threading.RLock() LOGGER.debug(pi3d.STARTUP_MESSAGE) def loop_running(self): """*loop_running* is the main event loop for the Display. Most pi3d code will look something like this:: DISPLAY = Display.create() # Initialize objects and variables here. # ... while DISPLAY.loop_running(): # Update the frame, using DISPLAY.time for the current time. # ... # Check for quit, then call DISPLAY.stop. if some_quit_condition(): DISPLAY.stop() ``Display.loop_running()`` **must** be called on the main Python thread, or else white screens and program crashes are likely. The Display loop can run in two different modes - *free* or *framed*. If ``DISPLAY.frames_per_second`` is empty or 0 then the loop runs *free* - when it finishes one frame, it immediately starts working on the next frame. If ``Display.frames_per_second`` is a positive number then the Display is *framed* - when the Display finishes one frame before the next frame_time, it waits till the next frame starts. A free Display gives the highest frame rate, but it will also consume more CPU, to the detriment of other threads or other programs. There is also the significant drawback that the framerate will fluctuate as the numbers of CPU cycles consumed per loop, resulting in jerky motion and animations. A framed Display has a consistent if smaller number of frames, and also allows for potentially much smoother motion and animation. The ability to throttle down the number of frames to conserve CPU cycles is essential for programs with other important threads like audio. ``Display.frames_per_second`` can be set at construction in ``Display.create`` or changed on-the-fly during the execution of the program. If ``Display.frames_per_second`` is set too high, the Display doesn't attempt to "catch up" but simply runs freely. """ if self.is_running: if self.first_time: self.time = time.time() self.first_time = False else: self._loop_end() # Finish the previous loop. self._loop_begin() else: self._loop_end() self.destroy() return self.is_running def resize(self, x=0, y=0, w=0, h=0): """Reshape the window with the given coordinates.""" if w <= 0: w = self.max_width if h <= 0: h = self.max_height self.width = w self.height = h self.left = x self.top = y self.right = x + w self.bottom = y + h self.opengl.resize(x, y, w, h) def add_sprites(self, *sprites): """Add one or more sprites to this Display.""" with self.lock: self.sprites_to_load.update(sprites) def remove_sprites(self, *sprites): """Remove one or more sprites from this Display.""" with self.lock: self.sprites_to_unload.update(sprites) def stop(self): """Stop the Display.""" self.is_running = False def destroy(self): """Destroy the current Display and reset Display.INSTANCE.""" self._tidy() self.stop() try: self.opengl.destroy(self) except: pass if self.external_mouse: try: self.external_mouse.stop() except: pass_ try: self.mouse.stop() except: pass try: self.tkwin.destroy() except: pass Display.INSTANCE = None try: import pygame # NB seems to be needed on some setups (64 bit anaconda windows!) pygame.quit() except: pass def clear(self): """Clear the Display.""" # opengles.glBindFramebuffer(GL_FRAMEBUFFER,0) pi3d.opengles.glClear(pi3d.GL_COLOR_BUFFER_BIT | pi3d.GL_DEPTH_BUFFER_BIT) def set_background(self, r, g, b, alpha): """Set the Display background. **NB the actual drawing of the background happens during the rendering of the framebuffer by the shader so if no draw() is done by anything during each Display loop the screen will remain black** If you want to see just the background you will have to draw() something out of view (i.e. behind) the Camera. *r, g, b* Color values for the display *alpha* Opacity of the color. An alpha of 0 means a transparent background, an alpha of 1 means full opaque. """ pi3d.opengles.glClearColor(c_float(r), c_float(g), c_float(b), c_float(alpha)) pi3d.opengles.glColorMask(1, 1, 1, int(alpha < 1.0)) # Switches off alpha blending with desktop (is there a bug in the driver?) def mouse_position(self): """The current mouse position as a tuple.""" # TODO: add: Now deprecated in favor of pi3d.events if self.mouse: return self.mouse.position() elif self.tkwin: return self.tkwin.winfo_pointerxy() else: return -1, -1 def _loop_begin(self): # TODO(rec): check if the window was resized and resize it, removing # code from MegaStation to here. if pi3d.USE_PYGAME: import pygame # although done in __init__ ...python namespaces aarg!!! if pygame.event.get(pygame.QUIT): self.destroy() elif pi3d.PLATFORM != pi3d.PLATFORM_PI and pi3d.PLATFORM != pi3d.PLATFORM_ANDROID: n = xlib.XEventsQueued(self.opengl.d, xlib.QueuedAfterFlush) for i in range(n): if xlib.XCheckMaskEvent(self.opengl.d, KeyPressMask, self.ev): self.event_list.append(self.ev) else: xlib.XNextEvent(self.opengl.d, self.ev) if self.ev.type == ClientMessage: if (self.ev.xclient.data.l[0] == self.opengl.WM_DELETE_WINDOW.value): self.destroy() self.clear() with self.lock: self.sprites_to_load, to_load = set(), self.sprites_to_load self.sprites.extend(to_load) self._for_each_sprite(lambda s: s.load_opengl(), to_load) if MARK_CAMERA_CLEAN_ON_EACH_LOOP: from pi3d.Camera import Camera camera = Camera.instance() if camera is not None: camera.was_moved = False if self.tidy_needed: self._tidy() def _tidy(self): to_del = [] for i in self.textures_dict: tex = self.textures_dict[i] LOGGER.debug('tex0=%s tex1=%s',tex[0], tex[1]) if tex[1] == 1: pi3d.opengles.glDeleteTextures(1, byref(tex[0])) to_del.append(i) for i in to_del: del self.textures_dict[i] to_del = [] for i in self.vbufs_dict: vbuf = self.vbufs_dict[i] if vbuf[1] == 1: pi3d.opengles.glDeleteBuffers(1, byref(vbuf[0])) to_del.append(i) for i in to_del: del self.vbufs_dict[i] to_del = [] for i in self.ebufs_dict: ebuf = self.ebufs_dict[i] if ebuf[1] == 1: pi3d.opengles.glDeleteBuffers(1, byref(ebuf[0])) to_del.append(i) for i in to_del: del self.ebufs_dict[i] self.tidy_needed = False def _loop_end(self): if pi3d.USE_PYGAME: import pygame pygame.event.clear() with self.lock: self.sprites_to_unload, to_unload = set(), self.sprites_to_unload if to_unload: self.sprites = [s for s in self.sprites if s not in to_unload] t = time.time() self._for_each_sprite(lambda s: s.repaint(t)) self.swap_buffers() for sprite in to_unload: sprite.unload_opengl() if getattr(self, 'frames_per_second', 0): self.time += 1.0 / self.frames_per_second delta = self.time - time.time() if delta > 0: time.sleep(delta) def _for_each_sprite(self, function, sprites=None): if sprites is None: sprites = self.sprites for s in sprites: try: function(s) except: LOGGER.error(traceback.format_exc()) if RAISE_EXCEPTIONS: raise def __del__(self): self.destroy() def swap_buffers(self): self.opengl.swap_buffers()