From 55ee2ee3d29d1a32dc387eb0c9ebc0339119e5a2 Mon Sep 17 00:00:00 2001 From: Quantum Date: Fri, 13 Dec 2013 18:01:52 -0500 Subject: [PATCH] Added loading screen. --- punyverse/game.py | 89 +++++++++++++++++++++++++++++++++++++++------ punyverse/glgeom.py | 48 +++++++++++++++++++++++- punyverse/world.py | 52 +++++++++++++++++++------- 3 files changed, 162 insertions(+), 27 deletions(-) diff --git a/punyverse/game.py b/punyverse/game.py index dd51fe1..3e1d654 100644 --- a/punyverse/game.py +++ b/punyverse/game.py @@ -6,7 +6,7 @@ import time import random from punyverse.camera import Camera -from punyverse.world import load_world +from punyverse.world import World from punyverse.glgeom import * from punyverse.entity import Asteroid from punyverse import texture @@ -42,10 +42,33 @@ class Applet(pyglet.window.Window): super(Applet, self).__init__(*args, **kwargs) texture.init() + self.loaded = False + self.__load_started = False + self._loading_phase = pyglet.text.Label(font_name='Consolas', font_size=20, x=10, y=self.height - 100, + color=(255, 255, 255, 255), width=self.width - 20, halign='center', + multiline=True, text='Punyverse is starting...') + self._loading_label = pyglet.text.Label(font_name='Consolas', font_size=16, x=10, y=self.height - 200, + color=(255, 255, 255, 255), width=self.width - 20, halign='center', + multiline=True) + pyglet.clock.schedule_once(self.load, 0) + + def load(self, *args, **kwargs): + if self.loaded or self.__load_started: + return + + self.__load_started = True + + def callback(phase, message, progress): + self.draw_loading(phase, message, progress) + self.flip() + self.dispatch_events() + start = clock() self.fps = 0 - self.world = load_world('world.json') - print 'Initializing game...' + self.world = World('world.json', callback) + phase = 'Initializing game...' + print phase + callback(phase, '', 0) self.speed = INITIAL_SPEED self.keys = set() self.info = True @@ -70,7 +93,6 @@ class Applet(pyglet.window.Window): self.__time_per_second_cache = None self.__time_per_second_value = None self.__time_accumulate = 0 - pyglet.clock.schedule(self.update) def speed_incrementer(object, increment): def incrementer(): @@ -160,26 +182,35 @@ class Applet(pyglet.window.Window): glLightfv(GL_LIGHT0, GL_POSITION, fv4(.5, .5, 1, 0)) glLightfv(GL_LIGHT0, GL_SPECULAR, fv4(.5, .5, 1, 1)) - glLightfv(GL_LIGHT0, GL_DIFFUSE, fv4(1, 1, 1, 1)) + glLightfv(GL_LIGHT0, GL_DIFFUSE, fv4(1, 1, 1, 1)) glLightfv(GL_LIGHT1, GL_POSITION, fv4(1, 0, .5, 0)) - glLightfv(GL_LIGHT1, GL_DIFFUSE, fv4(.5, .5, .5, 1)) + glLightfv(GL_LIGHT1, GL_DIFFUSE, fv4(.5, .5, .5, 1)) glLightfv(GL_LIGHT1, GL_SPECULAR, fv4(1, 1, 1, 1)) - print 'Loading asteroids...' - self.asteroid_ids = [model_list(load_model(r'asteroids/01.obj'), 5, 5, 5, (0, 0, 0)), - model_list(load_model(r'asteroids/02.obj'), 5, 5, 5, (0, 0, 0)), - model_list(load_model(r'asteroids/03.obj'), 5, 5, 5, (0, 0, 0)), - ] + phase = 'Loading asteroids...' + print phase + + def load_asteroids(files): + for id, file in enumerate(files): + callback(phase, 'Loading %s...' % file, float(id) / len(files)) + yield model_list(load_model(file), 5, 5, 5, (0, 0, 0)) + + self.asteroid_ids = list(load_asteroids([r'asteroids/01.obj', r'asteroids/02.obj', r'asteroids/03.obj'])) c = self.cam c.x, c.y, c.z = self.world.start c.pitch, c.yaw, c.roll = self.world.direction - print 'Updating entities...' + phase = 'Updating entities...' + print phase + callback(phase, '', 0) for entity in self.world.tracker: entity.update() 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 def set_exclusive_mouse(self, exclusive): super(Applet, self).set_exclusive_mouse(exclusive) @@ -196,6 +227,9 @@ class Applet(pyglet.window.Window): direction=(dx, dy, dz))) def on_mouse_press(self, x, y, button, modifiers): + if not self.loaded: + return + if not self.exclusive: self.set_exclusive_mouse(True) else: @@ -203,10 +237,16 @@ 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.cam.mouse_move(dx * MOUSE_SENSITIVITY, dy * MOUSE_SENSITIVITY) def on_key_press(self, symbol, modifiers): + 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]() @@ -214,10 +254,16 @@ class Applet(pyglet.window.Window): self.keys.add(symbol) def on_key_release(self, symbol, modifiers): + if not self.loaded: + return + if symbol in self.keys: self.keys.remove(symbol) 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) @@ -228,6 +274,9 @@ 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.speed += scroll_y * 50 + scroll_x * 500 def get_time_per_second(self): @@ -267,9 +316,25 @@ class Applet(pyglet.window.Window): else: self.__time_accumulate += delta + 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 - 300, self.width - 20, 50, progress) + def on_draw(self, glMatrix=GLfloat * 16): + if not self.loaded: + return self.draw_loading() + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() + c = self.cam x, y, z = c.x, c.y, c.z diff --git a/punyverse/glgeom.py b/punyverse/glgeom.py index 1ae46fd..350b1f2 100644 --- a/punyverse/glgeom.py +++ b/punyverse/glgeom.py @@ -5,7 +5,18 @@ from random import random, gauss, choice TWOPI = pi * 2 __all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'disk', 'sphere', 'colourball', 'torus', 'belt', - 'flare', 'normal_sphere'] + 'flare', 'normal_sphere', 'glSection', 'progress_bar'] + + +class glSection(object): + def __init__(self, type): + self.type = type + + def __enter__(self): + glBegin(self.type) + + def __exit__(self, exc_type, exc_val, exc_tb): + glEnd() def compile(pointer, *args, **kwargs): @@ -289,4 +300,37 @@ except ImportError: glVertex3f(x1 * r, y1 * r, z) glEnd() - glPopAttrib() \ No newline at end of file + glPopAttrib() + + +def progress_bar(x, y, width, height, filled): + x1 = x + x2 = x + width + y1 = y + y2 = y - height + y3 = 0.65 * y1 + 0.35 * y2 + y4 = 0.25 * y1 + 0.75 * y2 + + glColor3f(0.6, 0.6, 0.6) + with glSection(GL_LINE_LOOP): + glVertex2f(x1, y1) + glVertex2f(x1, y2) + glVertex2f(x2, y2) + glVertex2f(x2, y1) + + x1 += 1 + y1 -= 1 + x2 = x + width * filled - 1 + + with glSection(GL_TRIANGLE_STRIP): + glColor3f(0.81, 1, 0.82) + glVertex2f(x1, y1) + glVertex2f(x2, y1) + glColor3f(0, 0.83, 0.16) + glVertex2f(x1, y3) + glVertex2f(x2, y3) + glVertex2f(x1, y4) + glVertex2f(x2, y4) + glColor3f(0.37, 0.92, 0.43) + glVertex2f(x1, y2) + glVertex2f(x2, y2) \ No newline at end of file diff --git a/punyverse/world.py b/punyverse/world.py index 93af638..c912819 100644 --- a/punyverse/world.py +++ b/punyverse/world.py @@ -73,23 +73,35 @@ class World(object): self.tick = 0 self.callback = callback + self._phase = 'Parsing configuration...' self._parse(file) del self.callback # So it can't be used after loading finishes def _eval(self, value): - return eval(str(value), {'__builtins__': None}, self.context) + return eval(str(value), {'__builtins__': None}, self._context) def _parse(self, file): - self.callback('Loading configuration file...', 0) + self.callback(self._phase, 'Loading configuration file...', 0) with open(os.path.join(os.path.dirname(__file__), file)) as f: 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._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} tick = root.get('tick', 4320) # How many second is a tick? self.tick_length = tick + # Need to know how many objects are being loaded + self._objects = 0 + self._current_object = 0 + + def count_objects(bodies): + for body in bodies.itervalues(): + self._objects += 1 + count_objects(body.get('satellites', {})) + count_objects(root['bodies']) + print self._objects, 'objects to be loaded...' + if 'start' in root: info = root['start'] x = self._eval(info.get('x', 0)) @@ -102,11 +114,21 @@ class World(object): self.direction = (pitch, yaw, roll) for planet, info in root['bodies'].iteritems(): - print 'Loading %s.' % planet + message = 'Loading %s.' % planet + print message + self.callback('Loading objects (%d of %d)...' % (self._current_object, self._objects), + message, float(self._current_object) / self._objects) self._body(planet, info) + self._current_object += 1 - for name, info in root['belts'].iteritems(): - print 'Loading %s.' % name + if 'belts' in root: + self._phase = 'Loading belts...' + self._current_object = 0 + for name, info in root['belts'].iteritems(): + message = 'Loading %s.' % name + print message + self.callback(self._phase, message, float(self._current_object) / len(root['belts'])) + self._belt(name, info) def _belt(self, name, info): x = self._eval(info.get('x', 0)) @@ -143,9 +165,9 @@ class World(object): yaw = self._eval(info.get('yaw', 0)) roll = self._eval(info.get('roll', 0)) rotation = self._eval(info.get('rotation', 86400)) - radius = self._eval(info.get('radius', self.length)) / self.length + radius = self._eval(info.get('radius', self._length)) / self._length background = info.get('background', False) - orbit_distance = self._eval(info.get('orbit_distance', self.au)) + orbit_distance = self._eval(info.get('orbit_distance', self._au)) division = info.get('division', max(min(int(radius / 8), 60), 10)) if 'texture' in info: @@ -183,7 +205,7 @@ class World(object): speed = info.get('orbit_speed', 1) type = Satellite params.update(parent=parent, orbit_speed=speed, - distance=distance / self.length, eccentricity=info.get('eccentricity', 0), + distance=distance / self._length, eccentricity=info.get('eccentricity', 0), inclination=info.get('inclination', 0), longitude=info.get('longitude', 0), argument=info.get('argument', 0)) @@ -240,5 +262,9 @@ class World(object): (pitch, yaw, roll), **params)) for satellite, info in info.get('satellites', {}).iteritems(): - print 'Loading %s, satellite of %s.' % (satellite, name) - self._body(satellite, info, object) \ No newline at end of file + message = 'Loading %s, satellite of %s.' % (satellite, name) + print message + self.callback('Loading objects (%d of %d)...' % (self._current_object, self._objects), + message, float(self._current_object) / self._objects) + self._body(satellite, info, object) + self._current_object += 1 \ No newline at end of file