diff options
-rw-r--r-- | requirements.txt | 4 | ||||
-rw-r--r-- | txtgameengine/__init__.py | 2 | ||||
-rw-r--r-- | txtgameengine/__main__.py (renamed from main.py) | 9 | ||||
-rw-r--r-- | txtgameengine/app.py | 131 | ||||
-rw-r--r-- | txtgameengine/platform.py | 67 |
5 files changed, 85 insertions, 128 deletions
diff --git a/requirements.txt b/requirements.txt index 44c7d1d..beb7232 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -vulkan -glfw +glfw~=2.1.0 +pyopengl~=3.1.5 diff --git a/txtgameengine/__init__.py b/txtgameengine/__init__.py new file mode 100644 index 0000000..ac608af --- /dev/null +++ b/txtgameengine/__init__.py @@ -0,0 +1,2 @@ +from .app import TxtGameApp +from .platform import PlatformComponent, PlatformError diff --git a/main.py b/txtgameengine/__main__.py index c33a120..bed2972 100644 --- a/main.py +++ b/txtgameengine/__main__.py @@ -1,8 +1,7 @@ import glfw -from vulkan import * from glfw.GLFW import * -from txtgameengine.app import TxtGameApp +from .app import TxtGameApp def main(): @@ -12,9 +11,9 @@ def main(): if not window: glfw.terminate() return - extensions = vkEnumerateInstanceExtensionProperties(None) - print([e.name for e in extensions]) + glfw.swap_interval(1) while not glfw.window_should_close(window): + glfw.swap_buffers(window) glfw.poll_events() glfw.destroy_window(window) glfw.terminate() @@ -26,7 +25,7 @@ class TestApp(TxtGameApp): self.requested_validation_layers += ["VK_LAYER_KHRONOS_validation"] def update(self, delta: float): - pass + super().update(delta) if __name__ == '__main__': diff --git a/txtgameengine/app.py b/txtgameengine/app.py index a8c9913..e651078 100644 --- a/txtgameengine/app.py +++ b/txtgameengine/app.py @@ -1,123 +1,8 @@ -import glfw -from vulkan import * import time +from .platform import PlatformComponent -class PlatformError(Exception): - pass - - -class QueueFamilies: - graphics_i: int - graphics: object - - -class PlatformComponent: - def __init__(self, app: 'TxtGameApp'): - self.app = app - - def init(self): - glfw.init() - self.init_vulkan() - self.init_window() - - def init_vulkan(self): - self.create_instance() - self.pick_physical_device() - self.create_logical_device() - - def is_device_suitable(self, device): - return 1 - - def pick_physical_device(self): - devices = [(d, self.is_device_suitable(d)) - for d in vkEnumeratePhysicalDevices(self.instance)] - if len(devices) == 0: - raise PlatformError("No vulkan devices available.") - device, rating = sorted(devices, key=lambda x: x[1])[-1] - if rating < 0: - raise PlatformError("No suitable devices available.") - self.physical_device = device - self.find_queue_families() - - def find_queue_families(self): - self.queues = QueueFamilies() - for i, queue_family in enumerate(vkGetPhysicalDeviceQueueFamilyProperties(self.physical_device)): - if queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT: - self.queues.graphics_i = i - - def create_logical_device(self): - queue_create_info = [VkDeviceQueueCreateInfo( - queueFamilyIndex=self.queues.graphics_i, - queueCount=1, - pQueuePriorities=[1], - flags=0, - )] - device_create = VkDeviceCreateInfo( - pQueueCreateInfos=queue_create_info, - pEnabledFeatures=vkGetPhysicalDeviceFeatures(self.physical_device), - flags=0, - ppEnabledLayerNames=self.layers, - ppEnabledExtensionNames=[], - ) - self.logical_device = vkCreateDevice( - self.physical_device, device_create, None) - self.queues.graphic = vkGetDeviceQueue( - device=self.logical_device, - queueFamilyIndex=self.queues.graphics_i, - queueIndex=0, - ) - - def create_instance(self): - app_info = VkApplicationInfo( - pApplicationName=self.app.name, - applicationVersion=VK_MAKE_VERSION(1, 0, 0), - pEngineName="TxtGameEngine", - engineVersion=VK_MAKE_VERSION(1, 0, 0), - apiVersion=VK_API_VERSION_1_0, - ) - extensions = glfw.get_required_instance_extensions() - present_layers = vkEnumerateInstanceLayerProperties() - missing_layers = set(self.app.requested_validation_layers) - \ - set(l.layerName for l in present_layers) - if missing_layers: - raise PlatformError( - "Missing validation layers: "+str(missing_layers)) - self.layers = self.app.requested_validation_layers - createInfo = VkInstanceCreateInfo( - pApplicationInfo=app_info, - flags=0, - enabledExtensionCount=len(extensions), - ppEnabledExtensionNames=extensions, - enabledLayerCount=len(self.layers), - ppEnabledLayerNames=self.layers, - ) - self.instance = vkCreateInstance(createInfo, None) - - def init_window(self): - glfw.window_hint(glfw.CLIENT_API, glfw.NO_API) - glfw.window_hint(glfw.RESIZABLE, glfw.FALSE) - self.window = glfw.create_window( - *self.app.size, self.app.name, None, None) - if not self.window: - raise PlatformError("Failed to initialize glfw window") - - @property - def should_close(self) -> bool: - return glfw.window_should_close(self.window) - - @should_close.setter - def should_close(self, val: bool): - glfw.set_window_should_close(val) - - def poll_events(self): - glfw.poll_events() - - def cleanup(self): - vkDestroyDevice(self.logical_device) - vkDestroyInstance(self.instance) - glfw.destroy_window(window) - glfw.terminate() +EPSILON = 1.e-10 class TxtGameApp: @@ -126,17 +11,21 @@ class TxtGameApp: def __init__(self, size: (int, int), name: str): self.size = size self.name = name - self.platform: self.PLATFORM_CLASS = self.PLATFORM_CLASS(self) + self.platform = self.PLATFORM_CLASS(self) self.requested_validation_layers = [] def start(self): self.platform.init() - last_update_time = time.monotonic() + self.platform.set_clear_color(1, 0, 0.75, 1) + last_update_time = self.platform.monotonic_time() while not self.platform.should_close: - update_time = time.monotonic() + update_time = self.platform.monotonic_time() self.platform.poll_events() self.update(update_time - last_update_time) + self.platform.swap_buffers() last_update_time = update_time + self.platform.cleanup() def update(self, delta: float): - raise NotImplementedError + print("Running: delta = %.4fs, approx. fps = %ds" % (delta, 1. / (delta or EPSILON))) + self.platform.clear_background() diff --git a/txtgameengine/platform.py b/txtgameengine/platform.py new file mode 100644 index 0000000..0d89213 --- /dev/null +++ b/txtgameengine/platform.py @@ -0,0 +1,67 @@ +import glfw +import typing + +from OpenGL import GL + +if typing.TYPE_CHECKING: + from .app import TxtGameApp + + +class PlatformError(Exception): + pass + + +class PlatformComponent: + def __init__(self, app: 'TxtGameApp'): + self.app = app + self.window = None + + def init(self): + glfw.init() + self.init_window() + glfw.make_context_current(self.window) + + @staticmethod + def monotonic_time(): + return glfw.get_time() + + @staticmethod + def enable_vsync(): + glfw.swap_interval(1) + + def init_window(self): + glfw.window_hint(glfw.CLIENT_API, glfw.OPENGL_API) + glfw.window_hint(glfw.RESIZABLE, glfw.FALSE) + glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3) + glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3) + self.window = glfw.create_window( + *self.app.size, self.app.name, None, None) + if not self.window: + raise PlatformError("Failed to initialize glfw window") + + @property + def should_close(self) -> bool: + return glfw.window_should_close(self.window) + + @should_close.setter + def should_close(self, val: bool): + glfw.set_window_should_close(val) + + @staticmethod + def poll_events(): + glfw.poll_events() + + def cleanup(self): + glfw.destroy_window(self.window) + glfw.terminate() + + def swap_buffers(self): + glfw.swap_buffers(self.window) + + @staticmethod + def set_clear_color(r, g, b, a): + GL.glClearColor(r, g, b, a) + + @staticmethod + def clear_background(depth_buffer=False): + GL.glClear(GL.GL_COLOR_BUFFER_BIT | (depth_buffer and GL.GL_DEPTH_BUFFER_BIT or 0)) |