diff --git a/punyverse/glgeom.py b/punyverse/glgeom.py index 7590922..79ec413 100644 --- a/punyverse/glgeom.py +++ b/punyverse/glgeom.py @@ -1,9 +1,9 @@ from math import * from random import random, gauss, choice +from pyglet.gl import * # noinspection PyUnresolvedReferences from six.moves import range -from pyglet.gl import * TWOPI = pi * 2 @@ -11,6 +11,18 @@ __all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'disk', 'spher 'flare', 'glSection', 'glMatrix', 'glRestore', 'progress_bar'] +class glContext(object): + def __init__(self, context): + self.new_context = context + + def __enter__(self): + self.old_context = get_current_context() + self.new_context.set_current() + + def __exit__(self, exc_type, exc_val, exc_tb): + self.old_context.set_current() + + class glSection(object): def __init__(self, type): self.type = type diff --git a/punyverse/loader.py b/punyverse/loader.py new file mode 100644 index 0000000..14c18fc --- /dev/null +++ b/punyverse/loader.py @@ -0,0 +1,76 @@ +import time + +import pyglet +from pyglet.gl import * +from six.moves import zip_longest + +from punyverse.glgeom import glContext, progress_bar +from punyverse.world import World + + +class LoaderWindow(pyglet.window.Window): + def __init__(self, *args, **kwargs): + super(LoaderWindow, self).__init__(*args, **kwargs) + + self.loading_phase = pyglet.text.Label( + font_name='Consolas', font_size=20, x=10, y=self.height - 50, + color=(255, 255, 255, 255), width=self.width - 20, align='center', + multiline=True, text='Punyverse is starting...' + ) + self.loading_label = pyglet.text.Label( + font_name='Consolas', font_size=16, x=10, y=self.height - 120, + color=(255, 255, 255, 255), width=self.width - 20, align='center', + multiline=True + ) + self.info_label = pyglet.text.Label( + font_name='Consolas', font_size=13, x=10, y=self.height - 220, + color=(255, 255, 255, 255), width=self.width - 20, + multiline=True + ) + self.progress = 0 + + self._main_context = None + + def set_main_context(self, context): + self._main_context = context + + info = [' %-22s %s' % (key + ':', value) + for key, value in context.config.get_gl_attributes()] + info = ['%-30s %-30s' % group for group in + zip_longest(info[::2], info[1::2], fillvalue='')] + + with glContext(context): + self.info_label.text = '\n'.join([ + 'Graphics Vendor: ' + gl_info.get_vendor(), + 'Graphics Version: ' + gl_info.get_version(), + 'Graphics Renderer: ' + gl_info.get_renderer(), + ]) + '\n\n' + 'OpenGL configuration:\n' + '\n'.join(info) + + def _load_callback(self, phase, message, progress): + print(message) + with glContext(self.context): + self.loading_phase.text = phase + self.loading_label.text = message + self.progress = progress + + self.on_draw() + self.flip() + self.dispatch_events() + + def load(self): + start = time.clock() + with glContext(self._main_context): + world = World('world.json', self._load_callback) + print('Loaded in %s seconds.' % (time.clock() - start)) + return world + + def on_draw(self): + glClear(GL_COLOR_BUFFER_BIT) + glLoadIdentity() + self.loading_phase.draw() + self.loading_label.draw() + progress_bar(10, self.height - 140, self.width - 20, 50, self.progress) + self.info_label.draw() + + def main_is_initializing(self): + self._load_callback('Loading main window...', '', 0) diff --git a/punyverse/main.py b/punyverse/main.py index ac4fafa..cd243ba 100644 --- a/punyverse/main.py +++ b/punyverse/main.py @@ -2,11 +2,11 @@ import argparse import pyglet -from punyverse import game +from punyverse.loader import LoaderWindow +from punyverse.ui import Punyverse INITIAL_WIN_HEIGHT = 540 INITIAL_WIN_WIDTH = 700 -WIN_TITLE = 'Punyverse' def main(): @@ -24,10 +24,13 @@ def main(): args = parser.parse_args() pyglet.options['shadow_window'] = False + loader = LoaderWindow(width=INITIAL_WIN_WIDTH, height=INITIAL_WIN_HEIGHT, + caption='Punyverse is loading...') template = pyglet.gl.Config(depth_size=args.depth, double_buffer=True, sample_buffers=args.multisample > 1, - samples=args.multisample) + samples=args.multisample, + major_version=3) platform = pyglet.window.get_platform() display = platform.get_default_display() @@ -42,9 +45,17 @@ def main(): for key in config._attribute_names: print(' %-22s %s' % (key + ':', getattr(config, key))) - game.Applet(width=INITIAL_WIN_WIDTH, height=INITIAL_WIN_HEIGHT, - caption=WIN_TITLE, resizable=True, vsync=args.vsync, - config=config) + punyverse = Punyverse(width=INITIAL_WIN_WIDTH, height=INITIAL_WIN_HEIGHT, + caption='Punyverse', resizable=True, vsync=args.vsync, + config=config, visible=False) + + loader.set_main_context(punyverse.context) + world = loader.load() + punyverse.context.set_current() + punyverse.initialize(world) + loader.close() + punyverse.set_visible(True) + pyglet.app.run() diff --git a/punyverse/texture.py b/punyverse/texture.py index a5640de..616c66d 100644 --- a/punyverse/texture.py +++ b/punyverse/texture.py @@ -17,6 +17,7 @@ except ImportError: import warnings warnings.warn('Compile _glgeom.c, or double the start up time.') + def bgr_to_rgb(source, width, height, alpha=False): length = len(source) depth = length // (width * height) @@ -47,25 +48,9 @@ __all__ = ['load_texture', 'load_clouds', 'load_image', 'get_best_texture'] id = 0 cache = {} -max_texture = None -power_of_two = None -bgra = False - -def init(): - global max_texture, power_of_two, bgra - - if max_texture is None: - buf = c_int() - glGetIntegerv(GL_MAX_TEXTURE_SIZE, byref(buf)) - max_texture = buf.value - bgra = gl_info.have_extension('GL_EXT_bgra') - - if power_of_two is None: - power_of_two = gl_info.have_version(2) or gl_info.have_extension('GL_ARB_texture_non_power_of_two') - - -is_power2 = lambda num: num != 0 and ((num & (num - 1)) == 0) +def is_power2(num): + return num != 0 and ((num & (num - 1)) == 0) def image_info(data): @@ -126,12 +111,19 @@ def image_info(data): return content_type, width, height +def max_texture_size(): + buf = c_int() + glGetIntegerv(GL_MAX_TEXTURE_SIZE, byref(buf)) + return buf.value + + def check_size(width, height): - init() + max_texture = max_texture_size() + if width > max_texture or height > max_texture: print('too large') raise ValueError('Texture too large') - elif not power_of_two: + elif not gl_info.have_version(2) and not gl_info.have_extension('GL_ARB_texture_non_power_of_two'): if not is_power2(width) or not is_power2(height): print('not power of two') raise ValueError('Texture not power of two') @@ -165,7 +157,7 @@ def load_image(file, path): # Flip from BGR to RGB if raw.format in ('BGR', 'BGRA'): - if bgra: + if gl_info.have_extension('GL_EXT_bgra'): mode = GL_BGRA if 'A' in raw.format else GL_BGR texture = raw.data else: diff --git a/punyverse/game.py b/punyverse/ui.py similarity index 75% rename from punyverse/game.py rename to punyverse/ui.py index c1e4f76..fd6db2a 100644 --- a/punyverse/game.py +++ b/punyverse/ui.py @@ -3,29 +3,19 @@ import os import time from math import hypot from operator import attrgetter -from time import clock +import pyglet import six +from pyglet.gl import * +from pyglet.window import key, mouse -from punyverse import texture from punyverse.glgeom import * -from punyverse.world import World try: from punyverse._model import model_list, load_model except ImportError: from punyverse.model import model_list, load_model -try: - from itertools import zip_longest -except ImportError: - from itertools import izip_longest as zip_longest - -from pyglet.gl import * -from pyglet.window import key, mouse - -import pyglet - MOUSE_SENSITIVITY = 0.3 # Mouse sensitivity, 0..1, none...hyperspeed MAX_DELTA = 5 @@ -40,55 +30,11 @@ def entity_distance(x0, y0, z0): return distance -class Applet(pyglet.window.Window): +class Punyverse(pyglet.window.Window): def __init__(self, *args, **kwargs): - super(Applet, self).__init__(*args, **kwargs) - texture.init() + super(Punyverse, self).__init__(*args, **kwargs) - if hasattr(self.config, '_attribute_names'): - info = [' %-22s %s' % (key + ':', value) - for key, value in self.config.get_gl_attributes()] - info = ['%-30s %-30s' % group for group in - zip_longest(info[::2], info[1::2], fillvalue='')] - info = 'OpenGL configuration:\n' + '\n'.join(info) - else: - info = 'Unknown OpenGL configuration' - - info = '\n'.join([ - 'Graphics Vendor: ' + gl_info.get_vendor(), - 'Graphics Version: ' + gl_info.get_version(), - 'Graphics Renderer: ' + gl_info.get_renderer(), - ]) + '\n\n' + info - - self.loaded = False - self._loading_phase = pyglet.text.Label( - font_name='Consolas', font_size=20, x=10, y=self.height - 50, - color=(255, 255, 255, 255), width=self.width - 20, align='center', - multiline=True, text='Punyverse is starting...' - ) - self._loading_label = pyglet.text.Label( - font_name='Consolas', font_size=16, x=10, y=self.height - 120, - color=(255, 255, 255, 255), width=self.width - 20, align='center', - multiline=True - ) - self._info_label = pyglet.text.Label( - font_name='Consolas', font_size=13, x=10, y=self.height - 220, - color=(255, 255, 255, 255), width=self.width - 20, - multiline=True, text=info - ) - pyglet.clock.schedule_once(self.load, 0) - - def _load_callback(self, phase, message, progress): - print(message) - self.draw_loading(phase, message, progress) - self.flip() - self.dispatch_events() - - def load(self, *args, **kwargs): - start = clock() self.fps = 0 - self.world = World('world.json', self._load_callback) - self._load_callback('Initializing game...', '', 0) self.info = True self.debug = False self.orbit = True @@ -109,6 +55,19 @@ class Applet(pyglet.window.Window): 630720000, 1576800000, 3153600000, # 20, 50, 100 years ] + self.key_handler = {} + self.mouse_press_handler = {} + + self.label = pyglet.text.Label('', font_name='Consolas', font_size=12, x=10, y=self.height - 20, + color=(255,) * 4, multiline=True, width=600) + self.exclusive = False + self.modifiers = 0 + + self.world = None + + def initialize(self, world): + self.world = world + def speed_incrementer(object, increment): def incrementer(): object.speed += increment @@ -163,11 +122,6 @@ class Applet(pyglet.window.Window): mouse.RIGHT: attribute_toggler(self, 'moving'), } - self.label = pyglet.text.Label('', font_name='Consolas', font_size=12, x=10, y=self.height - 20, - color=(255,) * 4, multiline=True, width=600) - - self.exclusive = False - glClearColor(0, 0, 0, 1) glClearDepth(1.0) @@ -193,8 +147,6 @@ class Applet(pyglet.window.Window): glLightfv(GL_LIGHT1, GL_DIFFUSE, fv4(.5, .5, .5, 1)) glLightfv(GL_LIGHT1, GL_SPECULAR, fv4(1, 1, 1, 1)) - print('Loaded in %s seconds.' % (clock() - start)) - self.loaded = True pyglet.clock.schedule(self.update) self.on_resize(self.width, self.height) # On resize handler does nothing unless it's loaded @@ -225,13 +177,11 @@ class Applet(pyglet.window.Window): image.save(os.path.expanduser('~/punyverse.png')) def set_exclusive_mouse(self, exclusive): - super(Applet, self).set_exclusive_mouse(exclusive) + super(Punyverse, self).set_exclusive_mouse(exclusive) self.exclusive = exclusive def on_mouse_press(self, x, y, button, modifiers): self.modifiers = modifiers - if not self.loaded: - return if not self.exclusive: self.set_exclusive_mouse(True) @@ -240,9 +190,6 @@ class Applet(pyglet.window.Window): self.mouse_press_handler[button]() def on_mouse_motion(self, x, y, dx, dy): - if not self.loaded: - return - if self.exclusive: # Only handle camera movement if mouse is grabbed self.world.cam.mouse_move(dx * MOUSE_SENSITIVITY, dy * MOUSE_SENSITIVITY) @@ -250,8 +197,7 @@ class Applet(pyglet.window.Window): self.modifiers = modifiers if symbol == key.Q: self.screenshot() - if not self.loaded: - return + if self.exclusive: # Only handle keyboard input if mouse is grabbed if symbol in self.key_handler: self.key_handler[symbol]() @@ -261,18 +207,12 @@ class Applet(pyglet.window.Window): self.world.cam.roll_right = True def on_key_release(self, symbol, modifiers): - if not self.loaded: - return - if symbol == key.A: self.world.cam.roll_left = False elif symbol == key.S: self.world.cam.roll_right = False def on_resize(self, width, height): - if not self.loaded: - return super(Applet, self).on_resize(width, height) - height = max(height, 1) # Prevent / by 0 self.label.y = height - 20 glViewport(0, 0, width, height) @@ -283,9 +223,6 @@ class Applet(pyglet.window.Window): glMatrixMode(GL_MODELVIEW) def on_mouse_scroll(self, x, y, scroll_x, scroll_y): - if not self.loaded: - return - self.world.cam.speed += scroll_y * 50 + scroll_x * 500 def get_time_per_second(self): @@ -302,23 +239,7 @@ class Applet(pyglet.window.Window): def update(self, dt): self.world.update(dt, move=self.exclusive and self.moving, tick=self.running) - def draw_loading(self, phase=None, message=None, progress=None): - glClear(GL_COLOR_BUFFER_BIT) - glLoadIdentity() - if phase is not None: - self._loading_phase.text = phase - if message is not None: - self._loading_label.text = message - self._loading_phase.draw() - self._loading_label.draw() - if progress is not None: - progress_bar(10, self.height - 140, self.width - 20, 50, progress) - self._info_label.draw() - def on_draw(self): - if not self.loaded: - return self.draw_loading() - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() diff --git a/punyverse/world.py b/punyverse/world.py index ea1cc54..09c8c74 100644 --- a/punyverse/world.py +++ b/punyverse/world.py @@ -57,7 +57,7 @@ class World(object): root = json.load(f, object_pairs_hook=OrderedDict) self._au = root.get('au', 2000) self._length = root.get('length', 4320) - self._context = {'AU': self._au, 'TEXTURE': texture.max_texture, 'KM': 1.0 / self._length} + self._context = {'AU': self._au, 'TEXTURE': texture.max_texture_size(), 'KM': 1.0 / self._length} self.tick_length = root.get('tick', 4320) # How many second is a tick?