This commit is contained in:
shenjack 2022-11-08 20:18:01 +08:00
parent 76e4b4a1cf
commit ccaaf3b7fa
18 changed files with 327 additions and 405 deletions

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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']

View File

@ -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:

View 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
View 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\'

View File

@ -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\

View File

@ -163,6 +163,9 @@ class EventLoop(event.EventDispatcher):
Developers are discouraged from overriding this method, as the
implementation is platform-specific.
"""
if not interval:
self.clock.schedule(self._redraw_windows)
else:
self.clock.schedule_interval(self._redraw_windows, interval)
self.has_exit = False

View File

@ -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,14 +56,14 @@ 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):
@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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'

View File

@ -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})"

View File

@ -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)

View File

@ -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])

View File

@ -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',
@ -456,7 +456,8 @@ class CachedFileHandler(StreamHandlerTemplate):
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:
...
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: