pyglet !
This commit is contained in:
parent
76e4b4a1cf
commit
ccaaf3b7fa
@ -11,7 +11,9 @@ github: @shenjackyuanjie
|
|||||||
gitee: @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
|
from libs.MCDR.version import Version
|
||||||
|
|
||||||
@ -19,71 +21,6 @@ game_version = Version("0.6.3")
|
|||||||
__version__ = game_version
|
__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):
|
class _DR_option(Options):
|
||||||
"""
|
"""
|
||||||
@ -91,9 +28,9 @@ class _DR_option(Options):
|
|||||||
"""
|
"""
|
||||||
# runtime options
|
# runtime options
|
||||||
InputBox_use_TextEntry: bool = False
|
InputBox_use_TextEntry: bool = False
|
||||||
use_local_logging: bool = False
|
|
||||||
record_threads: bool = True
|
record_threads: bool = True
|
||||||
use_cProfile: bool = False
|
use_cProfile: bool = False
|
||||||
|
use_local_logging: bool = False
|
||||||
|
|
||||||
# tests
|
# tests
|
||||||
playing: bool = False
|
playing: bool = False
|
||||||
@ -139,9 +76,16 @@ class _DR_runtime(Options):
|
|||||||
|
|
||||||
DR_option = _DR_option()
|
DR_option = _DR_option()
|
||||||
DR_runtime = _DR_runtime()
|
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:
|
if DR_option.playing:
|
||||||
from .utils import new_thread
|
from Difficult_Rocket.utils import new_thread
|
||||||
|
|
||||||
|
|
||||||
def think_it(something):
|
def think_it(something):
|
||||||
|
@ -23,13 +23,13 @@ from decimal import Decimal
|
|||||||
# Difficult_Rocket function
|
# Difficult_Rocket function
|
||||||
from Difficult_Rocket import Options, DR_runtime
|
from Difficult_Rocket import Options, DR_runtime
|
||||||
from Difficult_Rocket.command import line, tree
|
from Difficult_Rocket.command import line, tree
|
||||||
from Difficult_Rocket.utils import new_thread
|
|
||||||
from Difficult_Rocket.utils.translate import tr
|
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 DRScreen
|
||||||
# from Difficult_Rocket.client.screen import DRDEBUGScreen
|
# from Difficult_Rocket.client.screen import DRDEBUGScreen
|
||||||
from Difficult_Rocket.utils import tools, translate
|
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.fps.fps_log import FpsLogger
|
||||||
|
from Difficult_Rocket.client.guis.widgets import InputBox
|
||||||
from Difficult_Rocket.exception.command import CommandError
|
from Difficult_Rocket.exception.command import CommandError
|
||||||
|
|
||||||
import tomlkit
|
import tomlkit
|
||||||
|
@ -18,8 +18,9 @@ from typing import Union
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
# from DR
|
# from DR
|
||||||
|
from Difficult_Rocket.utils import translate
|
||||||
from Difficult_Rocket.command.api import CommandText
|
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
|
# from pyglet
|
||||||
import pyglet
|
import pyglet
|
||||||
|
@ -11,6 +11,11 @@ github: @shenjackyuanjie
|
|||||||
gitee: @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']
|
|
||||||
|
@ -21,7 +21,7 @@ import configparser
|
|||||||
from typing import Union
|
from typing import Union
|
||||||
from xml.dom.minidom import parse
|
from xml.dom.minidom import parse
|
||||||
|
|
||||||
from libs import toml
|
import tomlkit
|
||||||
|
|
||||||
from Difficult_Rocket.exception.unsupport import NoMoreJson5
|
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:
|
if stack:
|
||||||
get_file = get_file[stack]
|
get_file = get_file[stack]
|
||||||
elif f_type == 'toml':
|
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':
|
elif f_type == 'json5':
|
||||||
raise NoMoreJson5("我说什么也不用json5了!喵的")
|
raise NoMoreJson5("我说什么也不用json5了!喵的")
|
||||||
except Exception as exp:
|
except Exception as exp:
|
||||||
|
84
Difficult_Rocket/utils/typings.py
Normal file
84
Difficult_Rocket/utils/typings.py
Normal file
@ -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
|
6
compile.ps1
Normal file
6
compile.ps1
Normal file
@ -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\'
|
@ -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\
|
|
@ -40,7 +40,7 @@ def _get_origin(cls: Type):
|
|||||||
def _get_args(cls: Type) -> tuple:
|
def _get_args(cls: Type) -> tuple:
|
||||||
return getattr(cls, '__args__', ())
|
return getattr(cls, '__args__', ())
|
||||||
|
|
||||||
|
|
||||||
_BASIC_CLASSES = (type(None), bool, int, float, str, list, dict, Version)
|
_BASIC_CLASSES = (type(None), bool, int, float, str, list, dict, Version)
|
||||||
|
|
||||||
|
|
||||||
|
@ -163,7 +163,10 @@ class EventLoop(event.EventDispatcher):
|
|||||||
Developers are discouraged from overriding this method, as the
|
Developers are discouraged from overriding this method, as the
|
||||||
implementation is platform-specific.
|
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
|
self.has_exit = False
|
||||||
|
|
||||||
|
@ -35,26 +35,13 @@
|
|||||||
|
|
||||||
"""Precise framerate calculation function scheduling.
|
"""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
|
..note:: Some internal modules will schedule items on the clock. If you
|
||||||
games' basic requirements::
|
are using a custom event loop, always remember to `tick` the clock!
|
||||||
|
|
||||||
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!
|
|
||||||
|
|
||||||
Scheduling
|
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"
|
The `schedule_interval` method causes a function to be called every "n"
|
||||||
seconds::
|
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
|
The `schedule_once` method causes a function to be called once "n" seconds
|
||||||
in the future::
|
in the future::
|
||||||
|
|
||||||
clock.schedule_once(callback, 5) # called in 5 seconds
|
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::
|
you specify to the callback function::
|
||||||
|
|
||||||
def move(dt, velocity, sprite):
|
def move(dt, velocity, sprite):
|
||||||
sprite.position += dt * velocity
|
sprite.position += dt * velocity
|
||||||
|
|
||||||
clock.schedule(move, velocity=5.0, sprite=alien)
|
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
|
:py:class:`~pyglet.clock.Clock` which is initialised with the module. You can
|
||||||
get this instance to use directly::
|
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:
|
You can also replace the default clock with your own:
|
||||||
|
|
||||||
myclk = clock.Clock()
|
myclk = pyglet.clock.Clock()
|
||||||
clock.set_default(myclk)
|
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.
|
measurement. Each clock must be "ticked" separately.
|
||||||
|
|
||||||
Multiple and derived clocks potentially allow you to separate "game-time" and
|
Multiple and derived clocks potentially allow you to separate "game-time" and
|
||||||
@ -113,6 +100,7 @@ of the system clock.
|
|||||||
|
|
||||||
import time as _time
|
import time as _time
|
||||||
|
|
||||||
|
from typing import Callable
|
||||||
from heapq import heappop as _heappop
|
from heapq import heappop as _heappop
|
||||||
from heapq import heappush as _heappush
|
from heapq import heappush as _heappush
|
||||||
from heapq import heappushpop as _heappushpop
|
from heapq import heappushpop as _heappushpop
|
||||||
@ -164,14 +152,11 @@ class Clock:
|
|||||||
def __init__(self, time_function=_time.perf_counter):
|
def __init__(self, time_function=_time.perf_counter):
|
||||||
"""Initialise a Clock, with optional custom time function.
|
"""Initialise a Clock, with optional custom time function.
|
||||||
|
|
||||||
:Parameters:
|
You can provide a custom time function to return the elapsed
|
||||||
`time_function` : function
|
time of the application, in seconds. Defaults to time.perf_counter,
|
||||||
Function to return the elapsed time of the application,
|
but can be replaced to allow for easy time dilation effects or game
|
||||||
in seconds. Defaults to time.time, but can be replaced
|
pausing.
|
||||||
to allow for easy time dilation effects or game pausing.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
super(Clock, self).__init__()
|
|
||||||
self.time = time_function
|
self.time = time_function
|
||||||
self.next_ts = self.time()
|
self.next_ts = self.time()
|
||||||
self.last_ts = None
|
self.last_ts = None
|
||||||
@ -263,7 +248,7 @@ class Clock:
|
|||||||
else:
|
else:
|
||||||
item = _heappushpop(interval_items, item)
|
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
|
# so we need to keep a reference to the current
|
||||||
# item no longer on heap to be able to check
|
# item no longer on heap to be able to check
|
||||||
self._current_interval_item = item
|
self._current_interval_item = item
|
||||||
@ -288,15 +273,15 @@ class Clock:
|
|||||||
|
|
||||||
# test the schedule for the next execution
|
# test the schedule for the next execution
|
||||||
if item.next_ts <= now:
|
if item.next_ts <= now:
|
||||||
# the scheduled time of this item has already passed
|
# the scheduled time of this item has already
|
||||||
# so it must be rescheduled
|
# passed, so it must be rescheduled
|
||||||
if now - item.next_ts < 0.05:
|
if now - item.next_ts < 0.05:
|
||||||
# missed execution time by 'reasonable' amount, so
|
# missed execution time by 'reasonable' amount, so
|
||||||
# reschedule at normal interval
|
# reschedule at normal interval
|
||||||
item.next_ts = now + item.interval
|
item.next_ts = now + item.interval
|
||||||
else:
|
else:
|
||||||
# missed by significant amount, now many events have
|
# 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.
|
# avoid lumping many events together.
|
||||||
# in this case, the next dt will not be accurate
|
# in this case, the next dt will not be accurate
|
||||||
item.next_ts = get_soft_next_ts(now, item.interval)
|
item.next_ts = get_soft_next_ts(now, item.interval)
|
||||||
@ -400,6 +385,7 @@ class Clock:
|
|||||||
return last_ts
|
return last_ts
|
||||||
|
|
||||||
def _get_soft_next_ts(self, last_ts, interval):
|
def _get_soft_next_ts(self, last_ts, interval):
|
||||||
|
|
||||||
def taken(ts, e):
|
def taken(ts, e):
|
||||||
"""Check if `ts` has already got an item scheduled nearby."""
|
"""Check if `ts` has already got an item scheduled nearby."""
|
||||||
# TODO this function is slow and called very often.
|
# TODO this function is slow and called very often.
|
||||||
@ -412,9 +398,9 @@ class Clock:
|
|||||||
|
|
||||||
return False
|
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
|
# 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
|
# 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!
|
# NOTE: do not rewrite as popping from heap, as that is super slow!
|
||||||
self._schedule_interval_items.sort(key=_attrgetter('next_ts'))
|
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
|
This method is similar to `schedule_interval`, except that the
|
||||||
clock will move the interval out of phase with other scheduled
|
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,
|
This is useful for functions that need to be called regularly,
|
||||||
but not relative to the initial start time. :py:mod:`pyglet.media`
|
but not relative to the initial start time. :py:mod:`pyglet.media`
|
||||||
@ -572,14 +558,10 @@ class Clock:
|
|||||||
_default = Clock()
|
_default = Clock()
|
||||||
|
|
||||||
|
|
||||||
def set_default(default):
|
def set_default(default) -> None:
|
||||||
"""Set the default clock to use for all module-level functions.
|
"""Set the default clock to use for all module-level functions.
|
||||||
|
|
||||||
By default an instance of :py:class:`~pyglet.clock.Clock` is used.
|
By default, an instance of :py:class:`~pyglet.clock.Clock` is used.
|
||||||
|
|
||||||
:Parameters:
|
|
||||||
`default` : `Clock`
|
|
||||||
The default clock to use.
|
|
||||||
"""
|
"""
|
||||||
global _default
|
global _default
|
||||||
_default = default
|
_default = default
|
||||||
@ -590,17 +572,16 @@ def get_default():
|
|||||||
|
|
||||||
Return the :py:class:`~pyglet.clock.Clock` instance that is used by all
|
Return the :py:class:`~pyglet.clock.Clock` instance that is used by all
|
||||||
module-level clock functions.
|
module-level clock functions.
|
||||||
|
|
||||||
:rtype: `Clock`
|
|
||||||
:return: The default clock.
|
|
||||||
"""
|
"""
|
||||||
return _default
|
return _default
|
||||||
|
|
||||||
|
|
||||||
def tick(poll=False):
|
def tick(poll: bool = False) -> float:
|
||||||
"""Signify that one frame has passed on the default clock.
|
"""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:
|
:Parameters:
|
||||||
`poll` : bool
|
`poll` : bool
|
||||||
@ -610,17 +591,16 @@ def tick(poll=False):
|
|||||||
only.
|
only.
|
||||||
|
|
||||||
Since pyglet 1.1.
|
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)
|
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.
|
"""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.
|
See `Clock.get_sleep_time` for details.
|
||||||
|
|
||||||
:Parameters:
|
:Parameters:
|
||||||
@ -628,17 +608,11 @@ def get_sleep_time(sleep_idle):
|
|||||||
If True, the application intends to sleep through its idle
|
If True, the application intends to sleep through its idle
|
||||||
time; otherwise it will continue ticking at the maximum
|
time; otherwise it will continue ticking at the maximum
|
||||||
frame rate allowed.
|
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)
|
return _default.get_sleep_time(sleep_idle)
|
||||||
|
|
||||||
|
|
||||||
def get_frequency():
|
def get_frequency() -> float:
|
||||||
"""Get the average clock update frequency.
|
"""Get the average clock update frequency.
|
||||||
|
|
||||||
The result is the sliding average of the last "n" updates,
|
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
|
second. This is the internal clock update rate, **not** the
|
||||||
Window redraw rate. Platform events, such as moving the
|
Window redraw rate. Platform events, such as moving the
|
||||||
mouse rapidly, will cause the clock to refresh more often.
|
mouse rapidly, will cause the clock to refresh more often.
|
||||||
|
|
||||||
:rtype: float
|
|
||||||
:return: The measured updates per second.
|
|
||||||
"""
|
"""
|
||||||
return _default.get_frequency()
|
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.
|
"""Schedule 'func' to be called every frame on the default clock.
|
||||||
|
|
||||||
The arguments passed to func are ``dt``, followed by any ``*args`` and
|
The arguments passed to func are ``dt``, followed by any ``*args`` and
|
||||||
``**kwargs`` given here.
|
``**kwargs`` given here.
|
||||||
|
|
||||||
:Parameters:
|
|
||||||
`func` : callable
|
|
||||||
The function to call each frame.
|
|
||||||
"""
|
"""
|
||||||
_default.schedule(func, *args, **kwargs)
|
_default.schedule(func, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def schedule_interval(func, interval, *args, **kwargs):
|
def schedule_interval(func: Callable, interval: float, *args, **kwargs) -> None:
|
||||||
"""Schedule ``func`` on the default clock every interval seconds.
|
"""Schedule ``func`` on the default clock every ``interval`` seconds.
|
||||||
|
|
||||||
The arguments passed to ``func`` are ``dt`` (time since last function
|
The arguments passed to ``func`` are ``dt`` (time since last function
|
||||||
call), followed by any ``*args`` and ``**kwargs`` given here.
|
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)
|
_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.
|
"""Schedule ``func`` on the default clock every interval seconds.
|
||||||
|
|
||||||
The clock will move the interval out of phase with other scheduled
|
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
|
The arguments passed to ``func`` are ``dt`` (time since last function
|
||||||
call), followed by any ``*args`` and ``**kwargs`` given here.
|
call), followed by any ``*args`` and ``**kwargs`` given here.
|
||||||
|
|
||||||
:see: `Clock.schedule_interval_soft`
|
: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)
|
_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.
|
"""Schedule ``func`` to be called once after ``delay`` seconds.
|
||||||
|
|
||||||
This function uses the default clock. ``delay`` can be a float. The
|
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
|
If no default clock is set, the func is queued and will be scheduled
|
||||||
on the default clock as soon as it is created.
|
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)
|
_default.schedule_once(func, delay, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def unschedule(func):
|
def unschedule(func: Callable) -> None:
|
||||||
"""Remove ``func`` from the default clock's schedule.
|
"""Remove ``func`` from the default clock's schedule.
|
||||||
|
|
||||||
No error is raised if the ``func`` was never scheduled.
|
No error is raised if the ``func`` was never scheduled.
|
||||||
|
|
||||||
:Parameters:
|
|
||||||
`func` : callable
|
|
||||||
The function to remove from the schedule.
|
|
||||||
"""
|
"""
|
||||||
_default.unschedule(func)
|
_default.unschedule(func)
|
||||||
|
@ -1550,7 +1550,7 @@ if not wic_decoder:
|
|||||||
|
|
||||||
class DirectWriteGlyphRenderer(base.GlyphRenderer):
|
class DirectWriteGlyphRenderer(base.GlyphRenderer):
|
||||||
antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
|
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
|
measuring_mode = DWRITE_MEASURING_MODE_NATURAL
|
||||||
|
|
||||||
def __init__(self, font):
|
def __init__(self, font):
|
||||||
|
@ -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
|
with other drawing that does not use the graphics API, multiple batches will
|
||||||
be required.
|
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
|
Drawing modes
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Methods in this module that accept a ``mode`` parameter will accept any value
|
Methods in this module that accept a ``mode`` parameter will accept any value
|
||||||
in the OpenGL drawing mode enumeration: ``GL_POINTS``, ``GL_LINE_STRIP``,
|
in the OpenGL drawing mode enumeration: ``GL_POINTS``, ``GL_LINE_STRIP``,
|
||||||
``GL_LINE_LOOP``, ``GL_LINES``, ``GL_TRIANGLE_STRIP``, ``GL_TRIANGLE_FAN``,
|
``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
|
shared state, ``GL_POLYGON``, ``GL_LINE_LOOP`` and ``GL_TRIANGLE_FAN`` cannot
|
||||||
be used --- the results are undefined.
|
be used --- the results are undefined.
|
||||||
|
|
||||||
When using ``GL_LINE_STRIP``, ``GL_TRIANGLE_STRIP`` or ``GL_QUAD_STRIP`` care
|
When using ``GL_LINE_STRIP`` or ``GL_TRIANGLE_STRIP``, care must be taken to
|
||||||
must be taken to insert degenerate vertices at the beginning and end of each
|
insert degenerate vertices at the beginning and end of each
|
||||||
vertex list. For example, given the vertex list::
|
vertex list. For example, given the vertex list::
|
||||||
|
|
||||||
A, B, C, D
|
A, B, C, D
|
||||||
@ -163,7 +130,7 @@ import weakref
|
|||||||
|
|
||||||
import pyglet
|
import pyglet
|
||||||
from pyglet.gl import *
|
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.vertexarray import VertexArray
|
||||||
from pyglet.graphics.vertexbuffer import BufferObject
|
from pyglet.graphics.vertexbuffer import BufferObject
|
||||||
|
|
||||||
@ -199,7 +166,7 @@ def draw(size, mode, **data):
|
|||||||
count = program.attributes[name]['count']
|
count = program.attributes[name]['count']
|
||||||
gl_type = vertexdomain._gl_types[fmt[0]]
|
gl_type = vertexdomain._gl_types[fmt[0]]
|
||||||
normalize = 'n' in fmt
|
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
|
assert size == len(array) // attribute.count, 'Data for %s is incorrect length' % fmt
|
||||||
|
|
||||||
buffer = BufferObject(size * attribute.stride)
|
buffer = BufferObject(size * attribute.stride)
|
||||||
@ -249,7 +216,7 @@ def draw_indexed(size, mode, indices, **data):
|
|||||||
count = program.attributes[name]['count']
|
count = program.attributes[name]['count']
|
||||||
gl_type = vertexdomain._gl_types[fmt[0]]
|
gl_type = vertexdomain._gl_types[fmt[0]]
|
||||||
normalize = 'n' in fmt
|
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
|
assert size == len(array) // attribute.count, 'Data for %s is incorrect length' % fmt
|
||||||
|
|
||||||
buffer = BufferObject(size * attribute.stride)
|
buffer = BufferObject(size * attribute.stride)
|
||||||
|
@ -14,6 +14,17 @@ class ShaderException(BaseException):
|
|||||||
pass
|
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.
|
# TODO: test other shader types, and update if necessary.
|
||||||
_shader_types = {
|
_shader_types = {
|
||||||
'vertex': GL_VERTEX_SHADER,
|
'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:
|
class _Uniform:
|
||||||
__slots__ = 'program', 'name', 'type', 'location', 'length', 'count', 'get', 'set'
|
__slots__ = 'program', 'name', 'type', 'location', 'length', 'count', 'get', 'set'
|
||||||
|
|
||||||
|
@ -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})"
|
|
@ -61,7 +61,7 @@ import ctypes
|
|||||||
import pyglet
|
import pyglet
|
||||||
|
|
||||||
from pyglet.gl import *
|
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
|
from pyglet.graphics.vertexbuffer import BufferObject, MappableBufferObject
|
||||||
|
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ class VertexDomain:
|
|||||||
count = meta['count']
|
count = meta['count']
|
||||||
gl_type = _gl_types[meta['format'][0]]
|
gl_type = _gl_types[meta['format'][0]]
|
||||||
normalize = 'n' in meta['format']
|
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)
|
self.attributes.append(attribute)
|
||||||
|
|
||||||
# Create buffer:
|
# Create buffer:
|
||||||
@ -370,7 +370,7 @@ class IndexedVertexDomain(VertexDomain):
|
|||||||
self.index_allocator = allocation.Allocator(self._initial_index_count)
|
self.index_allocator = allocation.Allocator(self._initial_index_count)
|
||||||
|
|
||||||
self.index_gl_type = index_gl_type
|
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_element_size = ctypes.sizeof(self.index_c_type)
|
||||||
self.index_buffer = BufferObject(self.index_allocator.capacity * self.index_element_size)
|
self.index_buffer = BufferObject(self.index_allocator.capacity * self.index_element_size)
|
||||||
|
|
||||||
|
@ -818,6 +818,31 @@ class Mat4(tuple):
|
|||||||
0, 0, q, -1,
|
0, 0, q, -1,
|
||||||
0, 0, qn, 0))
|
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
|
@classmethod
|
||||||
def from_translation(cls: type[Mat4T], vector: Vec3) -> Mat4T:
|
def from_translation(cls: type[Mat4T], vector: Vec3) -> Mat4T:
|
||||||
"""Create a translation matrix from a Vec3.
|
"""Create a translation matrix from a Vec3.
|
||||||
@ -831,18 +856,6 @@ class Mat4(tuple):
|
|||||||
0.0, 0.0, 1.0, 0.0,
|
0.0, 0.0, 1.0, 0.0,
|
||||||
vector[0], vector[1], vector[2], 1.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
|
@classmethod
|
||||||
def look_at_direction(cls: type[Mat4T], direction: Vec3, up: Vec3) -> Mat4T:
|
def look_at_direction(cls: type[Mat4T], direction: Vec3, up: Vec3) -> Mat4T:
|
||||||
vec_z = direction.normalize()
|
vec_z = direction.normalize()
|
||||||
@ -869,18 +882,6 @@ class Mat4(tuple):
|
|||||||
"""Get a specific column as a tuple."""
|
"""Get a specific column as a tuple."""
|
||||||
return self[index::4]
|
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:
|
def rotate(self, angle: float, vector: Vec3) -> Mat4:
|
||||||
"""Get a rotation Matrix on x, y, or z axis."""
|
"""Get a rotation Matrix on x, y, or z axis."""
|
||||||
if not all(abs(n) <= 1 for n in vector):
|
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))
|
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:
|
def transpose(self) -> Mat4:
|
||||||
"""Get a transpose of this Matrix."""
|
"""Get a transpose of this Matrix."""
|
||||||
return Mat4(self[0::4] + self[1::4] + self[2::4] + self[3::4])
|
return Mat4(self[0::4] + self[1::4] + self[2::4] + self[3::4])
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
# Copyright © 2021-2022 by shenjackyuanjie 3695888@qq.com
|
# Copyright © 2021-2022 by shenjackyuanjie 3695888@qq.com
|
||||||
# All rights reserved
|
# All rights reserved
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
import re
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
import atexit
|
import atexit
|
||||||
import inspect
|
import inspect
|
||||||
@ -16,12 +16,13 @@ import threading
|
|||||||
|
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from time import strftime
|
from time import strftime
|
||||||
from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, FATAL
|
from logging import NOTSET, DEBUG
|
||||||
from types import FrameType
|
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('')
|
os.system('')
|
||||||
# print(os.path.abspath(os.curdir))
|
# print(os.path.abspath(os.curdir))
|
||||||
|
# TODO 这个文件就是个大TODO
|
||||||
"""
|
"""
|
||||||
如果想要直接使用 logger 来 logging
|
如果想要直接使用 logger 来 logging
|
||||||
直接调用 logger.debug() 即可
|
直接调用 logger.debug() 即可
|
||||||
@ -161,8 +162,7 @@ logger_configs = {
|
|||||||
LoggingLevel.INFO_t: {'info': '\033[0m'},
|
LoggingLevel.INFO_t: {'info': '\033[0m'},
|
||||||
LoggingLevel.WARNING_t: {'info': '\033[33m'},
|
LoggingLevel.WARNING_t: {'info': '\033[33m'},
|
||||||
LoggingLevel.ERROR_t: {'info': '\033[31m'},
|
LoggingLevel.ERROR_t: {'info': '\033[31m'},
|
||||||
LoggingLevel.FATAL_t: {
|
LoggingLevel.FATAL_t: {'info': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'}
|
||||||
'info': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'}
|
|
||||||
},
|
},
|
||||||
'fancy_main_color': {
|
'fancy_main_color': {
|
||||||
# 'file_time': '\033[38;2;201;222;56m',
|
# 'file_time': '\033[38;2;201;222;56m',
|
||||||
@ -455,8 +455,9 @@ class CachedFileHandler(StreamHandlerTemplate):
|
|||||||
if by_thread:
|
if by_thread:
|
||||||
with self.time_limit_lock:
|
with self.time_limit_lock:
|
||||||
with open(file=self.file_conf.file_name, mode=self.file_conf.file_mode,
|
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:
|
def write(self, message: str, flush: Optional[bool]) -> bool:
|
||||||
if not flush:
|
if not flush:
|
||||||
|
Loading…
Reference in New Issue
Block a user