From 13e247bea36a9b9bdb7d3aea52828893598e795a Mon Sep 17 00:00:00 2001
From: Quantum <quantum2048@gmail.com>
Date: Fri, 24 Aug 2018 15:20:24 -0400
Subject: [PATCH] Use dedicated loader window to perform loading.

This paves the way to use OpenGL core profile in the future while still using legacy to draw the simple loader.
---
 punyverse/glgeom.py          |  14 ++++-
 punyverse/loader.py          |  76 ++++++++++++++++++++++
 punyverse/main.py            |  23 +++++--
 punyverse/texture.py         |  34 ++++------
 punyverse/{game.py => ui.py} | 119 ++++++-----------------------------
 punyverse/world.py           |   2 +-
 6 files changed, 140 insertions(+), 128 deletions(-)
 create mode 100644 punyverse/loader.py
 rename punyverse/{game.py => ui.py} (75%)

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?