diff --git a/Difficult_Rocket/__init__.py b/Difficult_Rocket/__init__.py index a513fc7..d8efed0 100644 --- a/Difficult_Rocket/__init__.py +++ b/Difficult_Rocket/__init__.py @@ -11,7 +11,9 @@ github: @shenjackyuanjie gitee: @shenjackyuanjie """ -from typing import Any, Dict, Callable, Union, Tuple, List +from typing import Any, Dict, Callable, Union, Tuple, List, get_type_hints, Type + +from Difficult_Rocket.utils.typings import Options from libs.MCDR.version import Version @@ -19,71 +21,6 @@ game_version = Version("0.6.3") __version__ = game_version -class OptionNameNotDefined(Exception): - """向初始化的 option 里添加了一个不存在于选项里的选项""" - - -class Options: - """ - Difficult Rocket 的游戏配置的存储基类 - """ - __options: Dict[str, Union[Callable, object]] = {} - cached_options: Dict[str, Union[str, Any]] = {} - - def __init__(self, **kwargs): - for option, value in kwargs.items(): - if option not in self.option(): - raise OptionNameNotDefined(f"option: {option} with value: {value} is not defined") - setattr(self, option, value) - self.flush_option() - - def option(self) -> Dict[str, Any]: - """ - 获取配置类的所有配置 - :return: 自己的所有配置 - """ - values = {} - for ann in self.__annotations__: # 获取类型注释 - values[ann] = getattr(self, ann, None) if getattr(self, ann, None) else self.__annotations__[ann] - - for option, a_fun in self.__options.items(): # 获取额外内容 - values[option] = a_fun - - for option, a_fun in values.items(): # 检查是否为 property - if a_fun is bool and getattr(self, option, None) is not None: - values[option] = False - if isinstance(a_fun, property): - values[option] = getattr(self, option) - return values - - def flush_option(self) -> Dict[str, Any]: - """ - 刷新缓存 options 的内容 - :return: 刷新过的 options - """ - self.cached_options = self.option() - return self.cached_options - - def option_with_len(self) -> List[Union[List[Tuple[str, Any, Any]], int, Any]]: - options = self.flush_option() - max_len_key = 1 - max_len_value = 1 - max_len_value_t = 1 - option_list = [] - for key, value in options.items(): - value_t = type(value) if not isinstance(type(value), type(value)) else value - # value_t = type(value) if type(value) is not type(type(value)) else value - max_len_key = max(max_len_key, len(key)) - max_len_value = max(max_len_value, len(str(value))) - max_len_value_t = max(max_len_value_t, len(str(value_t))) - option_list.append((key, value, value_t)) - return [option_list, max_len_key, max_len_value, max_len_value_t] - - @classmethod - def add_option(cls, name, value: Union[Callable, object]) -> Dict: - cls.__options[name] = value - return cls.__options - class _DR_option(Options): """ @@ -91,9 +28,9 @@ class _DR_option(Options): """ # runtime options InputBox_use_TextEntry: bool = False - use_local_logging: bool = False record_threads: bool = True use_cProfile: bool = False + use_local_logging: bool = False # tests playing: bool = False @@ -139,9 +76,16 @@ class _DR_runtime(Options): DR_option = _DR_option() DR_runtime = _DR_runtime() +print(DR_option.option()) +print(_DR_option.option()) +_DR_option.use_cProfile = True +print(_DR_option.option()) +# print(_DR_option.__dict__) +# print(DR_option.__dir__()) +# print(_DR_option.__dir__) if DR_option.playing: - from .utils import new_thread + from Difficult_Rocket.utils import new_thread def think_it(something): diff --git a/Difficult_Rocket/client/__init__.py b/Difficult_Rocket/client/__init__.py index 8b14377..0c04e81 100644 --- a/Difficult_Rocket/client/__init__.py +++ b/Difficult_Rocket/client/__init__.py @@ -23,13 +23,13 @@ from decimal import Decimal # Difficult_Rocket function from Difficult_Rocket import Options, DR_runtime from Difficult_Rocket.command import line, tree -from Difficult_Rocket.utils import new_thread from Difficult_Rocket.utils.translate import tr -from Difficult_Rocket.client.guis.widgets import InputBox # from Difficult_Rocket.client.screen import DRScreen # from Difficult_Rocket.client.screen import DRDEBUGScreen from Difficult_Rocket.utils import tools, translate +from Difficult_Rocket.utils.new_thread import new_thread from Difficult_Rocket.client.fps.fps_log import FpsLogger +from Difficult_Rocket.client.guis.widgets import InputBox from Difficult_Rocket.exception.command import CommandError import tomlkit diff --git a/Difficult_Rocket/command/line.py b/Difficult_Rocket/command/line.py index 09d77dd..94083f5 100644 --- a/Difficult_Rocket/command/line.py +++ b/Difficult_Rocket/command/line.py @@ -18,8 +18,9 @@ from typing import Union from decimal import Decimal # from DR +from Difficult_Rocket.utils import translate from Difficult_Rocket.command.api import CommandText -from Difficult_Rocket.utils import new_thread, translate +from Difficult_Rocket.utils.new_thread import new_thread # from pyglet import pyglet diff --git a/Difficult_Rocket/utils/__init__.py b/Difficult_Rocket/utils/__init__.py index 8a1fe49..cb59389 100644 --- a/Difficult_Rocket/utils/__init__.py +++ b/Difficult_Rocket/utils/__init__.py @@ -11,6 +11,11 @@ github: @shenjackyuanjie gitee: @shenjackyuanjie """ -from .new_thread import new_thread +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .new_thread import new_thread + from .typings import get_type_hints_ + +__all__ = ['new_thread', 'get_type_hints_'] -__all__ = ['new_thread'] diff --git a/Difficult_Rocket/utils/tools.py b/Difficult_Rocket/utils/tools.py index ab30129..feb58cc 100644 --- a/Difficult_Rocket/utils/tools.py +++ b/Difficult_Rocket/utils/tools.py @@ -21,7 +21,7 @@ import configparser from typing import Union from xml.dom.minidom import parse -from libs import toml +import tomlkit from Difficult_Rocket.exception.unsupport import NoMoreJson5 @@ -50,7 +50,8 @@ def load_file(file_name: str, stack: Union[str, list, dict] = None, raise_error: if stack: get_file = get_file[stack] elif f_type == 'toml': - get_file = toml.load(file_name) + with open(file_name, mode='r', encoding='utf-8') as file: + get_file = tomlkit.load(file) elif f_type == 'json5': raise NoMoreJson5("我说什么也不用json5了!喵的") except Exception as exp: diff --git a/Difficult_Rocket/utils/typings.py b/Difficult_Rocket/utils/typings.py new file mode 100644 index 0000000..bc73092 --- /dev/null +++ b/Difficult_Rocket/utils/typings.py @@ -0,0 +1,84 @@ +# ------------------------------- +# Difficult Rocket +# Copyright © 2021-2022 by shenjackyuanjie 3695888@qq.com +# All rights reserved +# ------------------------------- + +from typing import get_type_hints, Type, List, Union, Dict, Any, Callable, Tuple + + +def get_type_hints_(cls: Type): + try: + return get_type_hints(cls) + except ValueError: + return get_type_hints(cls, globalns={}) + + +class OptionNameNotDefined(Exception): + """向初始化的 option 里添加了一个不存在于选项里的选项""" + + +class Options: + """ + Difficult Rocket 的游戏配置的存储基类 + """ + __options: Dict[str, Union[Callable, object]] = {} + cached_options: Dict[str, Union[str, Any]] = {} + + def __init__(self, **kwargs): + for option, value in kwargs.items(): + if option not in self.option(): + raise OptionNameNotDefined(f"option: {option} with value: {value} is not defined") + setattr(self, option, value) + self.flush_option() + + @classmethod + def option(cls) -> Dict[str, Any]: + """ + 获取配置类的所有配置 + :return: 自己的所有配置 + """ + values = {} + for ann in cls.__annotations__: # 获取类型注释 + values[ann] = getattr(cls, ann, None) + if values[ann] is None: + values[ann] = cls.__annotations__[ann] + + for option, a_fun in cls.__options.items(): # 获取额外内容 + values[option] = a_fun + + for option, a_fun in values.items(): # 检查是否为 property + if a_fun is bool and getattr(cls, option, None) is not None: + values[option] = False + if isinstance(a_fun, property): + values[option] = getattr(cls, option) + return values + + @classmethod + def flush_option(cls) -> Dict[str, Any]: + """ + 刷新缓存 options 的内容 + :return: 刷新过的 options + """ + cls.cached_options = cls.option() + return cls.cached_options + + @classmethod + def option_with_len(cls) -> List[Union[List[Tuple[str, Any, Any]], int, Any]]: + options = cls.flush_option() + max_len_key = 1 + max_len_value = 1 + max_len_value_t = 1 + option_list = [] + for key, value in options.items(): + value_t = type(value) if not isinstance(value, Type) else value + max_len_key = max(max_len_key, len(key)) + max_len_value = max(max_len_value, len(str(value))) + max_len_value_t = max(max_len_value_t, len(str(value_t))) + option_list.append((key, value, value_t)) + return [option_list, max_len_key, max_len_value, max_len_value_t] + + @classmethod + def add_option(cls, name, value: Union[Callable, object]) -> Dict: + cls.__options[name] = value + return cls.__options diff --git a/compile.ps1 b/compile.ps1 new file mode 100644 index 0000000..90472b6 --- /dev/null +++ b/compile.ps1 @@ -0,0 +1,6 @@ +allPython.ps1 -m compileall 'libs\' +allPython.ps1 -m compileall 'Difficult_Rocket\' +allPython.ps1 -O -m compileall 'libs\' +allPython.ps1 -O -m compileall 'Difficult_Rocket\' +allPython.ps1 -OO -m compileall 'libs\' +allPython.ps1 -OO -m compileall 'Difficult_Rocket\' \ No newline at end of file diff --git a/complie.cmd b/complie.cmd deleted file mode 100644 index 79f3dc9..0000000 --- a/complie.cmd +++ /dev/null @@ -1,4 +0,0 @@ -python3.8 -O -m compileall .\libs\ -python3.8 -O -m compileall .\Difficult_Rocket\ -python3.10 -O -m compileall .\libs\ -python3.10 -O -m compileall .\Difficult_Rocket\ \ No newline at end of file diff --git a/libs/MCDR/serializer.py b/libs/MCDR/serializer.py index b7358d9..84a88db 100644 --- a/libs/MCDR/serializer.py +++ b/libs/MCDR/serializer.py @@ -40,7 +40,7 @@ def _get_origin(cls: Type): def _get_args(cls: Type) -> tuple: return getattr(cls, '__args__', ()) - + _BASIC_CLASSES = (type(None), bool, int, float, str, list, dict, Version) diff --git a/libs/pyglet/app/base.py b/libs/pyglet/app/base.py index 2a5dee0..92123e8 100644 --- a/libs/pyglet/app/base.py +++ b/libs/pyglet/app/base.py @@ -163,7 +163,10 @@ class EventLoop(event.EventDispatcher): Developers are discouraged from overriding this method, as the implementation is platform-specific. """ - self.clock.schedule_interval(self._redraw_windows, interval) + if not interval: + self.clock.schedule(self._redraw_windows) + else: + self.clock.schedule_interval(self._redraw_windows, interval) self.has_exit = False diff --git a/libs/pyglet/clock.py b/libs/pyglet/clock.py index 01fe7bc..009d7ec 100644 --- a/libs/pyglet/clock.py +++ b/libs/pyglet/clock.py @@ -35,26 +35,13 @@ """Precise framerate calculation function scheduling. -Measuring time -============== +The :py:mod:`~pyglet.clock` module allows you to schedule functions +to run periodically, or for one-shot future execution. pyglet's default +event loop (:py:func:`~pyglet.app.run`) keeps an internal instance of +a :py:class:`~pyglet.clock.Clock`, which is ticked automatically. -The `tick` and `get_frequency` functions can be used in conjunction to fulfil most -games' basic requirements:: - - from pyglet import clock - while True: - dt = clock.tick() - # ... update and render ... - print(f"FPS is {clock.get_frequency()}") - -The ``dt`` value returned gives the number of seconds (as a float) since the -last "tick". - -The `get_frequency` function averages the framerate over a sliding window of -approximately 1 second. (You can calculate the instantaneous framerate by -taking the reciprocal of ``dt``). - -Always remember to `tick` the clock! +..note:: Some internal modules will schedule items on the clock. If you + are using a custom event loop, always remember to `tick` the clock! Scheduling ========== @@ -69,18 +56,18 @@ You can schedule a function to be called every time the clock is ticked:: The `schedule_interval` method causes a function to be called every "n" seconds:: - clock.schedule_interval(callback, .5) # called twice a second + clock.schedule_interval(callback, 0.5) # called twice a second The `schedule_once` method causes a function to be called once "n" seconds in the future:: clock.schedule_once(callback, 5) # called in 5 seconds -All of the `schedule` methods will pass on any additional args or keyword args +All the `schedule` methods will pass on any additional args or keyword args you specify to the callback function:: def move(dt, velocity, sprite): - sprite.position += dt * velocity + sprite.position += dt * velocity clock.schedule(move, velocity=5.0, sprite=alien) @@ -96,14 +83,14 @@ The clock functions are all relayed to an instance of :py:class:`~pyglet.clock.Clock` which is initialised with the module. You can get this instance to use directly:: - clk = clock.get_default() + clk = pyglet.clock.get_default() You can also replace the default clock with your own: - myclk = clock.Clock() - clock.set_default(myclk) + myclk = pyglet.clock.Clock() + pyglet.clock.set_default(myclk) -Each clock maintains its own set of scheduled functions and FPS +Each clock maintains its own set of scheduled functions and frequency measurement. Each clock must be "ticked" separately. Multiple and derived clocks potentially allow you to separate "game-time" and @@ -113,6 +100,7 @@ of the system clock. import time as _time +from typing import Callable from heapq import heappop as _heappop from heapq import heappush as _heappush from heapq import heappushpop as _heappushpop @@ -164,14 +152,11 @@ class Clock: def __init__(self, time_function=_time.perf_counter): """Initialise a Clock, with optional custom time function. - :Parameters: - `time_function` : function - Function to return the elapsed time of the application, - in seconds. Defaults to time.time, but can be replaced - to allow for easy time dilation effects or game pausing. - + You can provide a custom time function to return the elapsed + time of the application, in seconds. Defaults to time.perf_counter, + but can be replaced to allow for easy time dilation effects or game + pausing. """ - super(Clock, self).__init__() self.time = time_function self.next_ts = self.time() self.last_ts = None @@ -263,7 +248,7 @@ class Clock: else: item = _heappushpop(interval_items, item) - # a scheduled function may try and unschedule itself + # a scheduled function may try to unschedule itself, # so we need to keep a reference to the current # item no longer on heap to be able to check self._current_interval_item = item @@ -288,15 +273,15 @@ class Clock: # test the schedule for the next execution if item.next_ts <= now: - # the scheduled time of this item has already passed - # so it must be rescheduled + # the scheduled time of this item has already + # passed, so it must be rescheduled if now - item.next_ts < 0.05: # missed execution time by 'reasonable' amount, so # reschedule at normal interval item.next_ts = now + item.interval else: # missed by significant amount, now many events have - # likely missed execution. do a soft reschedule to + # likely missed execution. do a soft re-schedule to # avoid lumping many events together. # in this case, the next dt will not be accurate item.next_ts = get_soft_next_ts(now, item.interval) @@ -400,6 +385,7 @@ class Clock: return last_ts def _get_soft_next_ts(self, last_ts, interval): + def taken(ts, e): """Check if `ts` has already got an item scheduled nearby.""" # TODO this function is slow and called very often. @@ -412,9 +398,9 @@ class Clock: return False - # sorted list is required required to produce expected results + # sorted list is required to produce expected results # taken() will iterate through the heap, expecting it to be sorted - # and will not always catch smallest value, so sort here. + # and will not always catch the smallest value, so sort here. # do not remove the sort key...it is faster than relaying comparisons # NOTE: do not rewrite as popping from heap, as that is super slow! self._schedule_interval_items.sort(key=_attrgetter('next_ts')) @@ -512,7 +498,7 @@ class Clock: This method is similar to `schedule_interval`, except that the clock will move the interval out of phase with other scheduled - functions so as to distribute CPU more load evenly over time. + functions in order to distribute CPU load more evenly. This is useful for functions that need to be called regularly, but not relative to the initial start time. :py:mod:`pyglet.media` @@ -572,14 +558,10 @@ class Clock: _default = Clock() -def set_default(default): +def set_default(default) -> None: """Set the default clock to use for all module-level functions. - By default an instance of :py:class:`~pyglet.clock.Clock` is used. - - :Parameters: - `default` : `Clock` - The default clock to use. + By default, an instance of :py:class:`~pyglet.clock.Clock` is used. """ global _default _default = default @@ -590,17 +572,16 @@ def get_default(): Return the :py:class:`~pyglet.clock.Clock` instance that is used by all module-level clock functions. - - :rtype: `Clock` - :return: The default clock. """ return _default -def tick(poll=False): +def tick(poll: bool = False) -> float: """Signify that one frame has passed on the default clock. - This will call any scheduled functions that have elapsed. + This will call any scheduled functions that have elapsed, + and return the elapsed seconds since the last tick. The + return value will be 0.0 if this is the first tick. :Parameters: `poll` : bool @@ -610,17 +591,16 @@ def tick(poll=False): only. Since pyglet 1.1. - - :rtype: float - :return: The number of seconds since the last "tick", or 0 if this was the - first frame. """ return _default.tick(poll) -def get_sleep_time(sleep_idle): +def get_sleep_time(sleep_idle: bool) -> float: """Get the time until the next item is scheduled on the default clock. + Returns the time until the next scheduled event in seconds, or + ``None`` if there is no event scheduled. + See `Clock.get_sleep_time` for details. :Parameters: @@ -628,17 +608,11 @@ def get_sleep_time(sleep_idle): If True, the application intends to sleep through its idle time; otherwise it will continue ticking at the maximum frame rate allowed. - - :rtype: float - :return: Time until the next scheduled event in seconds, or ``None`` - if there is no event scheduled. - - .. versionadded:: 1.1 """ return _default.get_sleep_time(sleep_idle) -def get_frequency(): +def get_frequency() -> float: """Get the average clock update frequency. The result is the sliding average of the last "n" updates, @@ -646,65 +620,43 @@ def get_frequency(): second. This is the internal clock update rate, **not** the Window redraw rate. Platform events, such as moving the mouse rapidly, will cause the clock to refresh more often. - - :rtype: float - :return: The measured updates per second. """ return _default.get_frequency() -def schedule(func, *args, **kwargs): +def schedule(func: Callable, *args, **kwargs) -> None: """Schedule 'func' to be called every frame on the default clock. The arguments passed to func are ``dt``, followed by any ``*args`` and ``**kwargs`` given here. - - :Parameters: - `func` : callable - The function to call each frame. """ _default.schedule(func, *args, **kwargs) -def schedule_interval(func, interval, *args, **kwargs): - """Schedule ``func`` on the default clock every interval seconds. +def schedule_interval(func: Callable, interval: float, *args, **kwargs) -> None: + """Schedule ``func`` on the default clock every ``interval`` seconds. The arguments passed to ``func`` are ``dt`` (time since last function call), followed by any ``*args`` and ``**kwargs`` given here. - - :Parameters: - `func` : callable - The function to call when the timer lapses. - `interval` : float - The number of seconds to wait between each call. """ _default.schedule_interval(func, interval, *args, **kwargs) -def schedule_interval_soft(func, interval, *args, **kwargs): +def schedule_interval_soft(func: Callable, interval: float, *args, **kwargs) -> None: """Schedule ``func`` on the default clock every interval seconds. The clock will move the interval out of phase with other scheduled - functions so as to distribute CPU more load evenly over time. + functions in order to distribute CPU load more evenly. The arguments passed to ``func`` are ``dt`` (time since last function call), followed by any ``*args`` and ``**kwargs`` given here. :see: `Clock.schedule_interval_soft` - - .. versionadded:: 1.1 - - :Parameters: - `func` : callable - The function to call when the timer lapses. - `interval` : float - The number of seconds to wait between each call. - """ _default.schedule_interval_soft(func, interval, *args, **kwargs) -def schedule_once(func, delay, *args, **kwargs): +def schedule_once(func: Callable, delay: float, *args, **kwargs) -> None: """Schedule ``func`` to be called once after ``delay`` seconds. This function uses the default clock. ``delay`` can be a float. The @@ -713,23 +665,13 @@ def schedule_once(func, delay, *args, **kwargs): If no default clock is set, the func is queued and will be scheduled on the default clock as soon as it is created. - - :Parameters: - `func` : callable - The function to call when the timer lapses. - `delay` : float - The number of seconds to wait before the timer lapses. """ _default.schedule_once(func, delay, *args, **kwargs) -def unschedule(func): +def unschedule(func: Callable) -> None: """Remove ``func`` from the default clock's schedule. No error is raised if the ``func`` was never scheduled. - - :Parameters: - `func` : callable - The function to remove from the schedule. """ _default.unschedule(func) diff --git a/libs/pyglet/font/directwrite.py b/libs/pyglet/font/directwrite.py index 0c84a77..7a90205 100644 --- a/libs/pyglet/font/directwrite.py +++ b/libs/pyglet/font/directwrite.py @@ -1550,7 +1550,7 @@ if not wic_decoder: class DirectWriteGlyphRenderer(base.GlyphRenderer): antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT - draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT + draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT if WINDOWS_8_1_OR_GREATER else D2D1_DRAW_TEXT_OPTIONS_NONE measuring_mode = DWRITE_MEASURING_MODE_NATURAL def __init__(self, font): diff --git a/libs/pyglet/graphics/__init__.py b/libs/pyglet/graphics/__init__.py index 498aee8..506bb62 100644 --- a/libs/pyglet/graphics/__init__.py +++ b/libs/pyglet/graphics/__init__.py @@ -91,46 +91,13 @@ possible. If the drawing of sprites or text objects need to be interleaved with other drawing that does not use the graphics API, multiple batches will be required. -Data item parameters -==================== - -Many of the functions and methods in this module accept any number of ``data`` -parameters as their final parameters. In the documentation these are notated -as ``*data`` in the formal parameter list. - -A data parameter describes a vertex attribute format and an optional sequence -to initialise that attribute. Examples of common attribute formats are: - -``"v3f"`` - Vertex position, specified as three floats. -``"c4B"`` - Vertex color, specified as four unsigned bytes. -``"t2f"`` - Texture coordinate, specified as two floats. - -See `pyglet.graphics.vertexattribute` for the complete syntax of the vertex -format string. - -When no initial data is to be given, the data item is just the format string. -For example, the following creates a 2 element vertex list with position and -color attributes:: - - vertex_list = pyglet.graphics.vertex_list(2, 'v2f', 'c4B') - -When initial data is required, wrap the format string and the initial data in -a tuple, for example:: - - vertex_list = pyglet.graphics.vertex_list(2, - ('v2f', (0.0, 1.0, 1.0, 0.0)), - ('c4B', (255, 255, 255, 255) * 2)) - Drawing modes ============= Methods in this module that accept a ``mode`` parameter will accept any value in the OpenGL drawing mode enumeration: ``GL_POINTS``, ``GL_LINE_STRIP``, ``GL_LINE_LOOP``, ``GL_LINES``, ``GL_TRIANGLE_STRIP``, ``GL_TRIANGLE_FAN``, -``GL_TRIANGLES``, ``GL_QUAD_STRIP``, ``GL_QUADS``, and ``GL_POLYGON``. +``GL_TRIANGLES``, and ``GL_POLYGON``. :: @@ -140,8 +107,8 @@ However, because of the way the graphics API renders multiple primitives with shared state, ``GL_POLYGON``, ``GL_LINE_LOOP`` and ``GL_TRIANGLE_FAN`` cannot be used --- the results are undefined. -When using ``GL_LINE_STRIP``, ``GL_TRIANGLE_STRIP`` or ``GL_QUAD_STRIP`` care -must be taken to insert degenerate vertices at the beginning and end of each +When using ``GL_LINE_STRIP`` or ``GL_TRIANGLE_STRIP``, care must be taken to +insert degenerate vertices at the beginning and end of each vertex list. For example, given the vertex list:: A, B, C, D @@ -163,7 +130,7 @@ import weakref import pyglet from pyglet.gl import * -from pyglet.graphics import vertexattribute, vertexdomain +from pyglet.graphics import shader, vertexdomain from pyglet.graphics.vertexarray import VertexArray from pyglet.graphics.vertexbuffer import BufferObject @@ -199,7 +166,7 @@ def draw(size, mode, **data): count = program.attributes[name]['count'] gl_type = vertexdomain._gl_types[fmt[0]] normalize = 'n' in fmt - attribute = vertexattribute.VertexAttribute(name, location, count, gl_type, normalize) + attribute = shader.Attribute(name, location, count, gl_type, normalize) assert size == len(array) // attribute.count, 'Data for %s is incorrect length' % fmt buffer = BufferObject(size * attribute.stride) @@ -249,7 +216,7 @@ def draw_indexed(size, mode, indices, **data): count = program.attributes[name]['count'] gl_type = vertexdomain._gl_types[fmt[0]] normalize = 'n' in fmt - attribute = vertexattribute.VertexAttribute(name, location, count, gl_type, normalize) + attribute = shader.Attribute(name, location, count, gl_type, normalize) assert size == len(array) // attribute.count, 'Data for %s is incorrect length' % fmt buffer = BufferObject(size * attribute.stride) diff --git a/libs/pyglet/graphics/shader.py b/libs/pyglet/graphics/shader.py index 80d40b1..899b816 100644 --- a/libs/pyglet/graphics/shader.py +++ b/libs/pyglet/graphics/shader.py @@ -14,6 +14,17 @@ class ShaderException(BaseException): pass +_c_types = { + GL_BYTE: c_byte, + GL_UNSIGNED_BYTE: c_ubyte, + GL_SHORT: c_short, + GL_UNSIGNED_SHORT: c_ushort, + GL_INT: c_int, + GL_UNSIGNED_INT: c_uint, + GL_FLOAT: c_float, + GL_DOUBLE: c_double, +} + # TODO: test other shader types, and update if necessary. _shader_types = { 'vertex': GL_VERTEX_SHADER, @@ -91,6 +102,102 @@ _attribute_types = { } +class Attribute: + """Abstract accessor for an attribute in a mapped buffer.""" + + def __init__(self, name, location, count, gl_type, normalize): + """Create the attribute accessor. + + :Parameters: + `name` : str + Name of the vertex attribute. + `location` : int + Location (index) of the vertex attribute. + `count` : int + Number of components in the attribute. + `gl_type` : int + OpenGL type enumerant; for example, ``GL_FLOAT`` + `normalize`: bool + True if OpenGL should normalize the values + + """ + self.name = name + self.location = location + self.count = count + + self.gl_type = gl_type + self.c_type = _c_types[gl_type] + self.normalize = normalize + + self.align = sizeof(self.c_type) + self.size = count * self.align + self.stride = self.size + + def enable(self): + """Enable the attribute.""" + glEnableVertexAttribArray(self.location) + + def set_pointer(self, ptr): + """Setup this attribute to point to the currently bound buffer at + the given offset. + + ``offset`` should be based on the currently bound buffer's ``ptr`` + member. + + :Parameters: + `offset` : int + Pointer offset to the currently bound buffer for this + attribute. + + """ + glVertexAttribPointer(self.location, self.count, self.gl_type, self.normalize, self.stride, ptr) + + def get_region(self, buffer, start, count): + """Map a buffer region using this attribute as an accessor. + + The returned region consists of a contiguous array of component + data elements. For example, if this attribute uses 3 floats per + vertex, and the `count` parameter is 4, the number of floats mapped + will be ``3 * 4 = 12``. + + :Parameters: + `buffer` : `AbstractMappable` + The buffer to map. + `start` : int + Offset of the first vertex to map. + `count` : int + Number of vertices to map + + :rtype: `AbstractBufferRegion` + """ + byte_start = self.stride * start + byte_size = self.stride * count + array_count = self.count * count + ptr_type = POINTER(self.c_type * array_count) + return buffer.get_region(byte_start, byte_size, ptr_type) + + def set_region(self, buffer, start, count, data): + """Set the data over a region of the buffer. + + :Parameters: + `buffer` : AbstractMappable` + The buffer to modify. + `start` : int + Offset of the first vertex to set. + `count` : int + Number of vertices to set. + `data` : A sequence of data components. + """ + byte_start = self.stride * start + byte_size = self.stride * count + array_count = self.count * count + data = (self.c_type * array_count)(*data) + buffer.set_data_region(data, byte_start, byte_size) + + def __repr__(self): + return f"Attribute(name='{self.name}', location={self.location}, count={self.count})" + + class _Uniform: __slots__ = 'program', 'name', 'type', 'location', 'length', 'count', 'get', 'set' diff --git a/libs/pyglet/graphics/vertexattribute.py b/libs/pyglet/graphics/vertexattribute.py deleted file mode 100644 index 8197cee..0000000 --- a/libs/pyglet/graphics/vertexattribute.py +++ /dev/null @@ -1,148 +0,0 @@ -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# Copyright (c) 2008-2022 pyglet contributors -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- - -import ctypes - -from pyglet.gl import * - - -_c_types = { - GL_BYTE: ctypes.c_byte, - GL_UNSIGNED_BYTE: ctypes.c_ubyte, - GL_SHORT: ctypes.c_short, - GL_UNSIGNED_SHORT: ctypes.c_ushort, - GL_INT: ctypes.c_int, - GL_UNSIGNED_INT: ctypes.c_uint, - GL_FLOAT: ctypes.c_float, - GL_DOUBLE: ctypes.c_double, -} - - -class VertexAttribute: - """Abstract accessor for an attribute in a mapped buffer.""" - - def __init__(self, name, location, count, gl_type, normalize): - """Create the attribute accessor. - - :Parameters: - `name` : str - Name of the vertex attribute. - `location` : int - Location (index) of the vertex attribute. - `count` : int - Number of components in the attribute. - `gl_type` : int - OpenGL type enumerant; for example, ``GL_FLOAT`` - `normalize`: bool - True if OpenGL should normalize the values - - """ - self.name = name - self.location = location - self.count = count - - self.gl_type = gl_type - self.c_type = _c_types[gl_type] - self.normalize = normalize - - self.align = ctypes.sizeof(self.c_type) - self.size = count * self.align - self.stride = self.size - - def enable(self): - """Enable the attribute.""" - glEnableVertexAttribArray(self.location) - - def set_pointer(self, pointer): - """Setup this attribute to point to the currently bound buffer at - the given offset. - - ``offset`` should be based on the currently bound buffer's ``ptr`` - member. - - :Parameters: - `offset` : int - Pointer offset to the currently bound buffer for this - attribute. - - """ - glVertexAttribPointer(self.location, self.count, self.gl_type, self.normalize, self.stride, pointer) - - def get_region(self, buffer, start, count): - """Map a buffer region using this attribute as an accessor. - - The returned region consists of a contiguous array of component - data elements. For example, if this attribute uses 3 floats per - vertex, and the `count` parameter is 4, the number of floats mapped - will be ``3 * 4 = 12``. - - :Parameters: - `buffer` : `AbstractMappable` - The buffer to map. - `start` : int - Offset of the first vertex to map. - `count` : int - Number of vertices to map - - :rtype: `AbstractBufferRegion` - """ - byte_start = self.stride * start - byte_size = self.stride * count - array_count = self.count * count - ptr_type = ctypes.POINTER(self.c_type * array_count) - return buffer.get_region(byte_start, byte_size, ptr_type) - - def set_region(self, buffer, start, count, data): - """Set the data over a region of the buffer. - - :Parameters: - `buffer` : AbstractMappable` - The buffer to modify. - `start` : int - Offset of the first vertex to set. - `count` : int - Number of vertices to set. - `data` : sequence - Sequence of data components. - - """ - byte_start = self.stride * start - byte_size = self.stride * count - array_count = self.count * count - data = (self.c_type * array_count)(*data) - buffer.set_data_region(data, byte_start, byte_size) - - def __repr__(self): - return f"VertexAttribute(name='{self.name}', location={self.location}, count={self.count})" diff --git a/libs/pyglet/graphics/vertexdomain.py b/libs/pyglet/graphics/vertexdomain.py index e2feb0f..1eeda44 100644 --- a/libs/pyglet/graphics/vertexdomain.py +++ b/libs/pyglet/graphics/vertexdomain.py @@ -61,7 +61,7 @@ import ctypes import pyglet from pyglet.gl import * -from pyglet.graphics import allocation, vertexattribute, vertexarray +from pyglet.graphics import allocation, shader, vertexarray from pyglet.graphics.vertexbuffer import BufferObject, MappableBufferObject @@ -125,7 +125,7 @@ class VertexDomain: count = meta['count'] gl_type = _gl_types[meta['format'][0]] normalize = 'n' in meta['format'] - attribute = vertexattribute.VertexAttribute(name, location, count, gl_type, normalize) + attribute = shader.Attribute(name, location, count, gl_type, normalize) self.attributes.append(attribute) # Create buffer: @@ -370,7 +370,7 @@ class IndexedVertexDomain(VertexDomain): self.index_allocator = allocation.Allocator(self._initial_index_count) self.index_gl_type = index_gl_type - self.index_c_type = vertexattribute._c_types[index_gl_type] + self.index_c_type = shader._c_types[index_gl_type] self.index_element_size = ctypes.sizeof(self.index_c_type) self.index_buffer = BufferObject(self.index_allocator.capacity * self.index_element_size) diff --git a/libs/pyglet/math.py b/libs/pyglet/math.py index eaff5c7..2c89d1c 100644 --- a/libs/pyglet/math.py +++ b/libs/pyglet/math.py @@ -818,6 +818,31 @@ class Mat4(tuple): 0, 0, q, -1, 0, 0, qn, 0)) + @classmethod + def from_rotation(cls, angle: float, vector: Vec3) -> Mat4: + """Create a rotation matrix from an angle and Vec3. + + :Parameters: + `angle` : A `float` : + The angle as a float. + `vector` : A `Vec3`, or 3 component tuple of float or int : + Vec3 or tuple with x, y and z translation values + """ + return cls().rotate(angle, vector) + + @classmethod + def from_scale(cls: type[Mat4T], vector: Vec3) -> Mat4T: + """Create a scale matrix from a Vec3. + + :Parameters: + `vector` : A `Vec3`, or 3 component tuple of float or int + Vec3 or tuple with x, y and z scale values + """ + return cls((vector[0], 0.0, 0.0, 0.0, + 0.0, vector[1], 0.0, 0.0, + 0.0, 0.0, vector[2], 0.0, + 0.0, 0.0, 0.0, 1.0)) + @classmethod def from_translation(cls: type[Mat4T], vector: Vec3) -> Mat4T: """Create a translation matrix from a Vec3. @@ -831,18 +856,6 @@ class Mat4(tuple): 0.0, 0.0, 1.0, 0.0, vector[0], vector[1], vector[2], 1.0)) - @classmethod - def from_rotation(cls, angle: float, vector: Vec3) -> Mat4: - """Create a rotation matrix from an angle and Vec3. - - :Parameters: - `angle` : A `float` : - The angle as a float. - `vector` : A `Vec3`, or 3 component tuple of float or int : - Vec3 or tuple with x, y and z translation values - """ - return cls().rotate(angle, vector) - @classmethod def look_at_direction(cls: type[Mat4T], direction: Vec3, up: Vec3) -> Mat4T: vec_z = direction.normalize() @@ -869,18 +882,6 @@ class Mat4(tuple): """Get a specific column as a tuple.""" return self[index::4] - def scale(self, vector: Vec3) -> Mat4: - """Get a scale Matrix on x, y, or z axis.""" - temp = list(self) - temp[0] *= vector[0] - temp[5] *= vector[1] - temp[10] *= vector[2] - return Mat4(temp) - - def translate(self, vector: Vec3) -> Mat4: - """Get a translation Matrix along x, y, and z axis.""" - return self @ Mat4((1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, *vector, 1)) - def rotate(self, angle: float, vector: Vec3) -> Mat4: """Get a rotation Matrix on x, y, or z axis.""" if not all(abs(n) <= 1 for n in vector): @@ -908,6 +909,18 @@ class Mat4(tuple): return Mat4(self) @ Mat4((ra, rb, rc, 0, re, rf, rg, 0, ri, rj, rk, 0, 0, 0, 0, 1)) + def scale(self, vector: Vec3) -> Mat4: + """Get a scale Matrix on x, y, or z axis.""" + temp = list(self) + temp[0] *= vector[0] + temp[5] *= vector[1] + temp[10] *= vector[2] + return Mat4(temp) + + def translate(self, vector: Vec3) -> Mat4: + """Get a translation Matrix along x, y, and z axis.""" + return self @ Mat4((1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, *vector, 1)) + def transpose(self) -> Mat4: """Get a transpose of this Matrix.""" return Mat4(self[0::4] + self[1::4] + self[2::4] + self[3::4]) diff --git a/libs/utils/logger.py b/libs/utils/logger.py index 176d436..cfed312 100644 --- a/libs/utils/logger.py +++ b/libs/utils/logger.py @@ -7,8 +7,8 @@ # Copyright © 2021-2022 by shenjackyuanjie 3695888@qq.com # All rights reserved # ------------------------------- -import re import os +import re import time import atexit import inspect @@ -16,12 +16,13 @@ import threading from queue import Queue from time import strftime -from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, FATAL +from logging import NOTSET, DEBUG from types import FrameType -from typing import NoReturn, Optional, Type, Union, Dict, Iterable, Any, List +from typing import Optional, Type, Union, Dict, Iterable, Any, List os.system('') # print(os.path.abspath(os.curdir)) +# TODO 这个文件就是个大TODO """ 如果想要直接使用 logger 来 logging 直接调用 logger.debug() 即可 @@ -161,8 +162,7 @@ logger_configs = { LoggingLevel.INFO_t: {'info': '\033[0m'}, LoggingLevel.WARNING_t: {'info': '\033[33m'}, LoggingLevel.ERROR_t: {'info': '\033[31m'}, - LoggingLevel.FATAL_t: { - 'info': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'} + LoggingLevel.FATAL_t: {'info': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'} }, 'fancy_main_color': { # 'file_time': '\033[38;2;201;222;56m', @@ -455,8 +455,9 @@ class CachedFileHandler(StreamHandlerTemplate): if by_thread: with self.time_limit_lock: with open(file=self.file_conf.file_name, mode=self.file_conf.file_mode, - encoding=self.file_conf.file_encoding) as log_file: - ... + encoding=self.file_conf.file_encoding) as log_file: + while not self.string_queue.empty(): + log_file.write(self.string_queue.get()) def write(self, message: str, flush: Optional[bool]) -> bool: if not flush: