update pyglet and add more
This commit is contained in:
parent
9288423d54
commit
e2b0109216
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -1,2 +0,0 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
@ -18,19 +18,17 @@ __all__ = ['TexturesError',
|
||||
|
||||
class Error(Exception):
|
||||
"""基础 Exception"""
|
||||
pass
|
||||
def __bool__(self):
|
||||
return False
|
||||
|
||||
|
||||
class TexturesError(Error):
|
||||
"""材质相关 error"""
|
||||
pass
|
||||
|
||||
|
||||
class LanguageError(Error):
|
||||
"""语言相关 error"""
|
||||
pass
|
||||
|
||||
|
||||
class TestError(Error):
|
||||
"""就像名字一样 用于测试的 error"""
|
||||
pass
|
||||
|
@ -22,11 +22,26 @@ class CommandParseError(CommandError):
|
||||
"""命令解析时出现错误"""
|
||||
|
||||
|
||||
class CommandQuotationMarkPositionError(CommandParseError):
|
||||
# QMark -> Quotation marks
|
||||
# Pos -> Position
|
||||
|
||||
class CommandQMarkPosError(CommandParseError):
|
||||
"""命令中,引号位置不正确
|
||||
例如: /command "aabcc "awdawd"""
|
||||
|
||||
|
||||
class CommandQuotationMarkMissing(CommandParseError):
|
||||
class CommandQMarkMissing(CommandParseError):
|
||||
"""命令中引号缺失
|
||||
例如: /command "aawwdawda awdaw """
|
||||
|
||||
|
||||
class CommandQMarkPreMissing(CommandQMarkMissing):
|
||||
"""命令中 前面的引号缺失
|
||||
例如: /command aaaa" aaaaaa"""
|
||||
suf_qmark_pos = None
|
||||
|
||||
|
||||
class CommandQMarkSufMissing(CommandQMarkMissing):
|
||||
"""命令中 后面的引号缺失(引号未闭合)
|
||||
例如: /command "aaaawaa some command"""
|
||||
pre_qmark_pos = None
|
||||
|
@ -184,6 +184,7 @@ class ClientWindow(Window):
|
||||
config_file['window']['width'] = self.width
|
||||
config_file['window']['height'] = self.height
|
||||
toml.dump(config_file, open('./configs/main.toml', 'w'))
|
||||
self.logger.info('save_info end')
|
||||
|
||||
"""
|
||||
draws and some event
|
||||
@ -195,9 +196,10 @@ class ClientWindow(Window):
|
||||
self.fps_log.update_tick(Decimal(tick))
|
||||
|
||||
def FPS_update(self, tick: Decimal):
|
||||
now_FPS = pyglet.clock.get_fps()
|
||||
now_FPS = pyglet.clock.get_frequency()
|
||||
self.fps_log.update_tick(tick)
|
||||
self.fps_label.text = f'FPS: {self.fps_log.fps: >5.1f}({self.fps_log.middle_fps: >5.1f})[{now_FPS}]\n {self.fps_log.max_fps: >7.1f} {self.fps_log.min_fps:>5.1f}'
|
||||
|
||||
self.fps_label.text = f'FPS: {self.fps_log.fps: >5.1f}({self.fps_log.middle_fps: >5.1f})[{now_FPS: >.7f}]\n {self.fps_log.max_fps: >7.1f} {self.fps_log.min_fps:>5.1f}'
|
||||
|
||||
def on_draw(self, *dt):
|
||||
# self.logger.debug('on_draw call dt: {}'.format(dt))
|
||||
|
@ -17,8 +17,7 @@ import statistics
|
||||
from typing import Union
|
||||
from decimal import Decimal
|
||||
|
||||
from libs.pyglet import clock
|
||||
|
||||
from libs.pyglet.clock import get_frequency
|
||||
|
||||
class FpsLogger:
|
||||
def __init__(self,
|
||||
@ -36,7 +35,7 @@ class FpsLogger:
|
||||
|
||||
def update_tick(self,
|
||||
tick: Decimal):
|
||||
now_fps = clock.get_fps()
|
||||
now_fps = get_frequency()
|
||||
if now_fps != 0:
|
||||
self.fps_list.append(now_fps)
|
||||
else:
|
||||
|
@ -39,13 +39,22 @@ class CommandText:
|
||||
self.tree_node = tree_list
|
||||
|
||||
@staticmethod
|
||||
def parse_command(raw_command: Union[str, "CommandText"]) -> Tuple[list, Optional[Type[CommandParseError]]]:
|
||||
def parse_command(raw_command: Union[str, "CommandText"]) -> Tuple[list, Union[Type[CommandParseError], type(True)]]:
|
||||
spilt_list = str(raw_command).split(" ")
|
||||
|
||||
for spilted in spilt_list:
|
||||
pass
|
||||
spilts = [None, None]
|
||||
for spited in spilt_list:
|
||||
if len(spited) > 1:
|
||||
if spited[0] == "\"": # 开头有一个 "
|
||||
if spilts[0] is None: # 如果没有标记一个字符串开头
|
||||
pass
|
||||
else: # 已经标记了一个字符串开头
|
||||
return spilt_list, CommandQMarkPosError
|
||||
if spited[-1] == "\"" and spited[-2] != "\\": # 末尾有一个没有被转义的 "
|
||||
|
||||
return spilt_list, CommandQuotationMarkPositionError
|
||||
...
|
||||
|
||||
return spilt_list, True
|
||||
|
||||
def find(self, text: str) -> Union[str, bool]:
|
||||
finding = re.match(text, self.text)
|
||||
|
2
desktop.ini
Normal file
2
desktop.ini
Normal file
@ -0,0 +1,2 @@
|
||||
[.ShellClassInfo]
|
||||
IconResource=C:\WINDOWS\System32\SHELL32.dll,11
|
@ -44,7 +44,7 @@ import sys
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
#: The release version
|
||||
version = '2.0.dev14'
|
||||
version = '2.0.dev18'
|
||||
__version__ = version
|
||||
|
||||
if sys.version_info < (3, 6):
|
||||
@ -360,7 +360,6 @@ if TYPE_CHECKING:
|
||||
from . import app
|
||||
from . import canvas
|
||||
from . import clock
|
||||
from . import com
|
||||
from . import event
|
||||
from . import font
|
||||
from . import gl
|
||||
@ -381,7 +380,6 @@ else:
|
||||
app = _ModuleProxy('app')
|
||||
canvas = _ModuleProxy('canvas')
|
||||
clock = _ModuleProxy('clock')
|
||||
com = _ModuleProxy('com')
|
||||
event = _ModuleProxy('event')
|
||||
font = _ModuleProxy('font')
|
||||
gl = _ModuleProxy('gl')
|
||||
|
@ -369,7 +369,7 @@ class Clock:
|
||||
|
||||
return None
|
||||
|
||||
def get_fps(self):
|
||||
def get_frequency(self):
|
||||
"""Get the average clock update frequency of recent history.
|
||||
|
||||
The result is the average of a sliding window of the last "n" updates,
|
||||
@ -638,7 +638,7 @@ def get_sleep_time(sleep_idle):
|
||||
return _default.get_sleep_time(sleep_idle)
|
||||
|
||||
|
||||
def get_fps():
|
||||
def get_frequency():
|
||||
"""Get the average clock update frequency.
|
||||
|
||||
The result is the sliding average of the last "n" updates,
|
||||
@ -650,7 +650,7 @@ def get_fps():
|
||||
:rtype: float
|
||||
:return: The measured updates per second.
|
||||
"""
|
||||
return _default.get_fps()
|
||||
return _default.get_frequency()
|
||||
|
||||
|
||||
def schedule(func, *args, **kwargs):
|
||||
|
@ -9,7 +9,7 @@ from pyglet.image.codecs.wic import IWICBitmap, GUID_WICPixelFormat32bppBGR, WIC
|
||||
from pyglet import image
|
||||
import ctypes
|
||||
import math
|
||||
from pyglet import com
|
||||
from pyglet.libs.win32 import com
|
||||
from pyglet.libs.win32 import _kernel32 as kernel32
|
||||
from pyglet.libs.win32 import _ole32 as ole32
|
||||
from pyglet.libs.win32.constants import *
|
||||
|
@ -227,7 +227,7 @@ class Win32Font(base.Font):
|
||||
super(Win32Font, self).__init__()
|
||||
|
||||
self.logfont = self.get_logfont(name, size, bold, italic, dpi)
|
||||
self.hfont = gdi32.CreateFontIndirectA(byref(self.logfont))
|
||||
self.hfont = gdi32.CreateFontIndirectW(byref(self.logfont))
|
||||
|
||||
# Create a dummy DC for coordinate mapping
|
||||
dc = user32.GetDC(0)
|
||||
@ -250,7 +250,7 @@ class Win32Font(base.Font):
|
||||
dpi = 96
|
||||
logpixelsy = dpi
|
||||
|
||||
logfont = LOGFONT()
|
||||
logfont = LOGFONTW()
|
||||
# Conversion of point size to device pixels
|
||||
logfont.lfHeight = int(-size * logpixelsy // 72)
|
||||
if bold:
|
||||
@ -258,7 +258,7 @@ class Win32Font(base.Font):
|
||||
else:
|
||||
logfont.lfWeight = FW_NORMAL
|
||||
logfont.lfItalic = italic
|
||||
logfont.lfFaceName = asbytes(name)
|
||||
logfont.lfFaceName = name
|
||||
logfont.lfQuality = ANTIALIASED_QUALITY
|
||||
user32.ReleaseDC(0, dc)
|
||||
return logfont
|
||||
@ -351,30 +351,29 @@ class GDIPlusGlyphRenderer(Win32GlyphRenderer):
|
||||
self._bitmap_height = height
|
||||
|
||||
def render(self, text):
|
||||
|
||||
ch = ctypes.create_unicode_buffer(text)
|
||||
len_ch = len(text)
|
||||
|
||||
# Layout rectangle; not clipped against so not terribly important.
|
||||
width = 10000
|
||||
height = self._bitmap_height
|
||||
rect = Rectf(0, self._bitmap_height
|
||||
- self.font.ascent + self.font.descent,
|
||||
rect = Rectf(0, self._bitmap_height
|
||||
- self.font.ascent + self.font.descent,
|
||||
width, height)
|
||||
|
||||
# Set up GenericTypographic with 1 character measure range
|
||||
generic = ctypes.c_void_p()
|
||||
gdiplus.GdipStringFormatGetGenericTypographic(ctypes.byref(generic))
|
||||
format = ctypes.c_void_p()
|
||||
gdiplus.GdipCloneStringFormat(generic, ctypes.byref(format))
|
||||
fmt = ctypes.c_void_p()
|
||||
gdiplus.GdipCloneStringFormat(generic, ctypes.byref(fmt))
|
||||
gdiplus.GdipDeleteStringFormat(generic)
|
||||
|
||||
# Measure advance
|
||||
|
||||
# --- Measure advance
|
||||
|
||||
# XXX HACK HACK HACK
|
||||
# Windows GDI+ is a filthy broken toy. No way to measure the bounding
|
||||
# box of a string, or to obtain LSB. What a joke.
|
||||
#
|
||||
#
|
||||
# For historical note, GDI cannot be used because it cannot composite
|
||||
# into a bitmap with alpha.
|
||||
#
|
||||
@ -382,7 +381,7 @@ class GDIPlusGlyphRenderer(Win32GlyphRenderer):
|
||||
# supporting accurate text measurement with alpha composition in .NET
|
||||
# 2.0 (WinForms) via the TextRenderer class; this has no C interface
|
||||
# though, so we're entirely screwed.
|
||||
#
|
||||
#
|
||||
# So anyway, we first try to get the width with GdipMeasureString.
|
||||
# Then if it's a TrueType font, we use GetCharABCWidthsW to get the
|
||||
# correct LSB. If it's a negative LSB, we move the layoutRect `rect`
|
||||
@ -391,65 +390,77 @@ class GDIPlusGlyphRenderer(Win32GlyphRenderer):
|
||||
# space and we don't pass the LSB info to the Glyph.set_bearings
|
||||
|
||||
bbox = Rectf()
|
||||
flags = (StringFormatFlagsMeasureTrailingSpaces |
|
||||
StringFormatFlagsNoClip |
|
||||
flags = (StringFormatFlagsMeasureTrailingSpaces |
|
||||
StringFormatFlagsNoClip |
|
||||
StringFormatFlagsNoFitBlackBox)
|
||||
gdiplus.GdipSetStringFormatFlags(format, flags)
|
||||
gdiplus.GdipMeasureString(self._graphics,
|
||||
ch,
|
||||
gdiplus.GdipSetStringFormatFlags(fmt, flags)
|
||||
gdiplus.GdipMeasureString(self._graphics,
|
||||
ch,
|
||||
len_ch,
|
||||
self.font._gdipfont,
|
||||
ctypes.byref(rect),
|
||||
format,
|
||||
self.font._gdipfont,
|
||||
ctypes.byref(rect),
|
||||
fmt,
|
||||
ctypes.byref(bbox),
|
||||
None,
|
||||
None,
|
||||
None)
|
||||
lsb = 0
|
||||
advance = int(math.ceil(bbox.width))
|
||||
width = advance
|
||||
|
||||
# This hack bumps up the width if the font is italic;
|
||||
# this compensates for some common fonts. It's also a stupid
|
||||
# waste of texture memory.
|
||||
if self.font.italic:
|
||||
width += width // 2
|
||||
# Do not enlarge more than the _rect width.
|
||||
width = min(width, self._rect.Width)
|
||||
|
||||
# We only care about the advance from this whole thing.
|
||||
advance = int(math.ceil(bbox.width))
|
||||
|
||||
# GDI functions only work for a single character so we transform
|
||||
# grapheme \r\n into \r
|
||||
if text == '\r\n':
|
||||
text = '\r'
|
||||
|
||||
abc = ABC()
|
||||
# Check if ttf font.
|
||||
if gdi32.GetCharABCWidthsW(self._dc,
|
||||
ord(text), ord(text), byref(abc)):
|
||||
|
||||
lsb = abc.abcA
|
||||
width = abc.abcB
|
||||
if lsb < 0:
|
||||
# Negative LSB: we shift the layout rect to the right
|
||||
# Otherwise we will cut the left part of the glyph
|
||||
rect.x = -lsb
|
||||
width -= lsb
|
||||
else:
|
||||
width += lsb
|
||||
|
||||
# XXX END HACK HACK HACK
|
||||
|
||||
abc = ABC()
|
||||
width = 0
|
||||
lsb = 0
|
||||
ttf_font = True
|
||||
# Use GDI to get code points for the text passed. This is almost always 1.
|
||||
# For special unicode characters it may be comprised of 2+ codepoints. Get the width/lsb of each.
|
||||
# Function only works on TTF fonts.
|
||||
for codepoint in [ord(c) for c in text]:
|
||||
if gdi32.GetCharABCWidthsW(self._dc, codepoint, codepoint, byref(abc)):
|
||||
lsb += abc.abcA
|
||||
width += abc.abcB
|
||||
|
||||
if lsb < 0:
|
||||
# Negative LSB: we shift the layout rect to the right
|
||||
# Otherwise we will cut the left part of the glyph
|
||||
rect.x = -lsb
|
||||
width -= lsb
|
||||
else:
|
||||
width += lsb
|
||||
else:
|
||||
ttf_font = False
|
||||
break
|
||||
|
||||
# Almost always a TTF font. Haven't seen a modern font that GetCharABCWidthsW fails on.
|
||||
# For safety, just use the advance as the width.
|
||||
if not ttf_font:
|
||||
width = advance
|
||||
|
||||
# This hack bumps up the width if the font is italic;
|
||||
# this compensates for some common fonts. It's also a stupid
|
||||
# waste of texture memory.
|
||||
if self.font.italic:
|
||||
width += width // 2
|
||||
# Do not enlarge more than the _rect width.
|
||||
width = min(width, self._rect.Width)
|
||||
|
||||
# Draw character to bitmap
|
||||
|
||||
gdiplus.GdipGraphicsClear(self._graphics, 0x00000000)
|
||||
gdiplus.GdipDrawString(self._graphics,
|
||||
ch,
|
||||
len_ch,
|
||||
self.font._gdipfont,
|
||||
ctypes.byref(rect),
|
||||
format,
|
||||
fmt,
|
||||
self._brush)
|
||||
gdiplus.GdipFlush(self._graphics, 1)
|
||||
gdiplus.GdipDeleteStringFormat(format)
|
||||
gdiplus.GdipDeleteStringFormat(fmt)
|
||||
|
||||
bitmap_data = BitmapData()
|
||||
gdiplus.GdipBitmapLockBits(self._bitmap,
|
||||
|
@ -153,6 +153,7 @@ but the same code using GDI+ rendered 16,600 glyphs per second.
|
||||
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
from pyglet.libs.win32 import LOGFONT, LOGFONTW
|
||||
|
||||
user32 = ctypes.windll.user32
|
||||
gdi32 = ctypes.windll.gdi32
|
||||
@ -209,62 +210,6 @@ FF_SCRIPT = 4 # handwritten
|
||||
FF_DECORATIVE = 5 # novelty
|
||||
|
||||
|
||||
class LOGFONT(ctypes.Structure):
|
||||
# EnumFontFamiliesEx examines only 3 fields:
|
||||
# - lfCharSet
|
||||
# - lfFaceName - empty string enumerates one font in each available
|
||||
# typeface name, valid typeface name gets all fonts
|
||||
# with that name
|
||||
# - lfPitchAndFamily - must be set to 0 [ ]
|
||||
_fields_ = [
|
||||
('lfHeight', wintypes.LONG),
|
||||
# value > 0 specifies the largest size of *char cell* to match
|
||||
# char cell = char height + internal leading
|
||||
# value = 0 makes matched use default height for search
|
||||
# value < 0 specifies the largest size of *char height* to match
|
||||
('lfWidth', wintypes.LONG),
|
||||
# average width also in *logical units*, which are pixels in
|
||||
# default _mapping mode_ (MM_TEXT) for device
|
||||
('lfEscapement', wintypes.LONG),
|
||||
# string baseline rotation in tenths of degrees
|
||||
('lfOrientation', wintypes.LONG),
|
||||
# character rotation in tenths of degrees
|
||||
('lfWeight', wintypes.LONG),
|
||||
# 0 through 1000 400 is normal, 700 is bold, 0 is default
|
||||
('lfItalic', BYTE),
|
||||
('lfUnderline', BYTE),
|
||||
('lfStrikeOut', BYTE),
|
||||
('lfCharSet', BYTE),
|
||||
# ANSI_CHARSET, BALTIC_CHARSET, ... - see *_CHARSET constants above
|
||||
('lfOutPrecision', BYTE),
|
||||
# many constants how the output must match height, width, pitch etc.
|
||||
# OUT_DEFAULT_PRECIS
|
||||
# [ ] TODO
|
||||
('lfClipPrecision', BYTE),
|
||||
# how to clip characters, no useful properties, leave default value
|
||||
# CLIP_DEFAULT_PRECIS
|
||||
('lfQuality', BYTE),
|
||||
# ANTIALIASED_QUALITY
|
||||
# CLEARTYPE_QUALITY
|
||||
# DEFAULT_QUALITY
|
||||
# DRAFT_QUALITY
|
||||
# NONANTIALIASED_QUALITY
|
||||
# PROOF_QUALITY
|
||||
('lfPitchAndFamily', BYTE),
|
||||
# DEFAULT_PITCH
|
||||
# FIXED_PITCH - authoritative for monospace
|
||||
# VARIABLE_PITCH
|
||||
# stacked with any of
|
||||
# FF_DECORATIVE - novelty
|
||||
# FF_DONTCARE - default font
|
||||
# FF_MODERN - stroke width ('pen width') near constant
|
||||
# FF_ROMAN - proportional (variable char width) with serifs
|
||||
# FF_SCRIPT - handwritten
|
||||
# FF_SWISS - proportional without serifs
|
||||
('lfFaceName', TCHAR * 32)]
|
||||
# typeface name of the font - null-terminated string
|
||||
|
||||
|
||||
class FONTSIGNATURE(ctypes.Structure):
|
||||
# supported code pages and Unicode subranges for the font
|
||||
# needed for NEWTEXTMETRICEX structure
|
||||
@ -302,6 +247,32 @@ class NEWTEXTMETRIC(ctypes.Structure):
|
||||
('ntmCellHeight', wintypes.UINT),
|
||||
('ntmAvgWidth', wintypes.UINT)]
|
||||
|
||||
class NEWTEXTMETRICW(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('tmHeight', wintypes.LONG),
|
||||
('tmAscent', wintypes.LONG),
|
||||
('tmDescent', wintypes.LONG),
|
||||
('tmInternalLeading', wintypes.LONG),
|
||||
('tmExternalLeading', wintypes.LONG),
|
||||
('tmAveCharWidth', wintypes.LONG),
|
||||
('tmMaxCharWidth', wintypes.LONG),
|
||||
('tmWeight', wintypes.LONG),
|
||||
('tmOverhang', wintypes.LONG),
|
||||
('tmDigitizedAspectX', wintypes.LONG),
|
||||
('tmDigitizedAspectY', wintypes.LONG),
|
||||
('mFirstChar', wintypes.WCHAR),
|
||||
('mLastChar', wintypes.WCHAR),
|
||||
('mDefaultChar', wintypes.WCHAR),
|
||||
('mBreakChar', wintypes.WCHAR),
|
||||
('tmItalic', BYTE),
|
||||
('tmUnderlined', BYTE),
|
||||
('tmStruckOut', BYTE),
|
||||
('tmPitchAndFamily', BYTE),
|
||||
('tmCharSet', BYTE),
|
||||
('tmFlags', wintypes.DWORD),
|
||||
('ntmSizeEM', wintypes.UINT),
|
||||
('ntmCellHeight', wintypes.UINT),
|
||||
('ntmAvgWidth', wintypes.UINT)]
|
||||
|
||||
class NEWTEXTMETRICEX(ctypes.Structure):
|
||||
# physical font attributes for True Type fonts
|
||||
@ -310,6 +281,10 @@ class NEWTEXTMETRICEX(ctypes.Structure):
|
||||
('ntmTm', NEWTEXTMETRIC),
|
||||
('ntmFontSig', FONTSIGNATURE)]
|
||||
|
||||
class NEWTEXTMETRICEXW(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('ntmTm', NEWTEXTMETRICW),
|
||||
('ntmFontSig', FONTSIGNATURE)]
|
||||
|
||||
# type for a function that is called by the system for
|
||||
# each font during execution of EnumFontFamiliesEx
|
||||
@ -324,6 +299,15 @@ FONTENUMPROC = ctypes.WINFUNCTYPE(
|
||||
wintypes.LPARAM
|
||||
)
|
||||
|
||||
FONTENUMPROCW = ctypes.WINFUNCTYPE(
|
||||
ctypes.c_int, # return non-0 to continue enumeration, 0 to stop
|
||||
ctypes.POINTER(LOGFONTW),
|
||||
ctypes.POINTER(NEWTEXTMETRICEXW),
|
||||
wintypes.DWORD,
|
||||
wintypes.LPARAM
|
||||
)
|
||||
|
||||
|
||||
# When running 64 bit windows, some types are not 32 bit, so Python/ctypes guesses wrong
|
||||
gdi32.EnumFontFamiliesExA.argtypes = [
|
||||
wintypes.HDC,
|
||||
@ -333,6 +317,13 @@ gdi32.EnumFontFamiliesExA.argtypes = [
|
||||
wintypes.DWORD]
|
||||
|
||||
|
||||
gdi32.EnumFontFamiliesExW.argtypes = [
|
||||
wintypes.HDC,
|
||||
ctypes.POINTER(LOGFONTW),
|
||||
FONTENUMPROCW,
|
||||
wintypes.LPARAM,
|
||||
wintypes.DWORD]
|
||||
|
||||
def _enum_font_names(logfont, textmetricex, fonttype, param):
|
||||
"""callback function to be executed during EnumFontFamiliesEx
|
||||
call for each font name. it stores names in global variable
|
||||
@ -340,7 +331,7 @@ def _enum_font_names(logfont, textmetricex, fonttype, param):
|
||||
global FONTDB
|
||||
|
||||
lf = logfont.contents
|
||||
name = lf.lfFaceName.decode('utf-8')
|
||||
name = lf.lfFaceName
|
||||
|
||||
# detect font type (vector|raster) and format (ttf)
|
||||
# [ ] use Windows constant TRUETYPE_FONTTYPE
|
||||
@ -406,7 +397,7 @@ def _enum_font_names(logfont, textmetricex, fonttype, param):
|
||||
return 1 # non-0 to continue enumeration
|
||||
|
||||
|
||||
enum_font_names = FONTENUMPROC(_enum_font_names)
|
||||
enum_font_names = FONTENUMPROCW(_enum_font_names)
|
||||
|
||||
|
||||
# --- /define
|
||||
@ -445,9 +436,9 @@ def query(charset=DEFAULT_CHARSET):
|
||||
# - enumerate all available charsets for a single font
|
||||
# - other params?
|
||||
|
||||
logfont = LOGFONT(0, 0, 0, 0, 0, 0, 0, 0, charset, 0, 0, 0, 0, b'\0')
|
||||
logfont = LOGFONTW(0, 0, 0, 0, 0, 0, 0, 0, charset, 0, 0, 0, 0, '')
|
||||
FONTDB = [] # clear cached FONTDB for enum_font_names callback
|
||||
res = gdi32.EnumFontFamiliesExA(
|
||||
res = gdi32.EnumFontFamiliesExW(
|
||||
hdc, # handle to device context
|
||||
ctypes.byref(logfont),
|
||||
enum_font_names, # pointer to callback function
|
||||
|
@ -55,15 +55,23 @@ context::
|
||||
# ...
|
||||
|
||||
"""
|
||||
|
||||
from ctypes import c_char_p, cast, c_int
|
||||
import warnings
|
||||
|
||||
from pyglet.gl.gl import (GL_EXTENSIONS, GL_RENDERER, GL_VENDOR,
|
||||
GL_VERSION, GL_MAJOR_VERSION, GL_MINOR_VERSION)
|
||||
from ctypes import c_char_p, cast
|
||||
|
||||
from pyglet.gl.gl import GL_EXTENSIONS, GL_RENDERER, GL_VENDOR, GL_VERSION
|
||||
from pyglet.gl.gl import GL_MAJOR_VERSION, GL_MINOR_VERSION, GLint
|
||||
from pyglet.gl.lib import GLException
|
||||
from pyglet.util import asstr
|
||||
|
||||
|
||||
def _get_number(parameter):
|
||||
from pyglet.gl.gl import glGetIntegerv
|
||||
number = GLint()
|
||||
glGetIntegerv(parameter, number)
|
||||
return number.value
|
||||
|
||||
|
||||
class GLInfo:
|
||||
"""Information interface for a single GL context.
|
||||
|
||||
@ -74,10 +82,13 @@ class GLInfo:
|
||||
If you are using more than one context, you must call `set_active_context`
|
||||
when the context is active for this `GLInfo` instance.
|
||||
"""
|
||||
have_context = False
|
||||
version = '0.0'
|
||||
_have_context = False
|
||||
vendor = ''
|
||||
renderer = ''
|
||||
version = '0.0'
|
||||
major_version = 0
|
||||
minor_version = 0
|
||||
opengl_api = 'gl'
|
||||
extensions = set()
|
||||
|
||||
_have_info = False
|
||||
@ -87,33 +98,35 @@ class GLInfo:
|
||||
|
||||
This method is called automatically for the default context.
|
||||
"""
|
||||
from pyglet.gl.gl import GLint, glGetIntegerv, glGetString, glGetStringi, GL_NUM_EXTENSIONS
|
||||
from pyglet.gl.gl import glGetString, glGetStringi, GL_NUM_EXTENSIONS
|
||||
|
||||
self.have_context = True
|
||||
self._have_context = True
|
||||
if not self._have_info:
|
||||
self.vendor = asstr(cast(glGetString(GL_VENDOR), c_char_p).value)
|
||||
self.renderer = asstr(cast(glGetString(GL_RENDERER), c_char_p).value)
|
||||
major_version = c_int()
|
||||
glGetIntegerv(GL_MAJOR_VERSION, major_version)
|
||||
self.major_version = major_version.value
|
||||
minor_version = c_int()
|
||||
glGetIntegerv(GL_MINOR_VERSION, minor_version)
|
||||
self.minor_version = minor_version.value
|
||||
self.version = asstr(cast(glGetString(GL_VERSION), c_char_p).value)
|
||||
# NOTE: The version string requirements for gles is a lot stricter
|
||||
# so using this to rely on detecting the API is not too unreasonable
|
||||
self.opengl_api = "gles" if "opengl es" in self.version.lower() else "gl"
|
||||
num_extensions = GLint()
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, num_extensions)
|
||||
self.extensions = (asstr(cast(glGetStringi(GL_EXTENSIONS, i), c_char_p).value)
|
||||
for i in range(num_extensions.value))
|
||||
self.extensions = set(self.extensions)
|
||||
|
||||
try:
|
||||
self.major_version = _get_number(GL_MAJOR_VERSION)
|
||||
self.minor_version = _get_number(GL_MINOR_VERSION)
|
||||
num_ext = _get_number(GL_NUM_EXTENSIONS)
|
||||
self.extensions = (asstr(cast(glGetStringi(GL_EXTENSIONS, i), c_char_p).value) for i in range(num_ext))
|
||||
self.extensions = set(self.extensions)
|
||||
except GLException:
|
||||
pass # GL3 is likely not available
|
||||
|
||||
self._have_info = True
|
||||
|
||||
def remove_active_context(self):
|
||||
self.have_context = False
|
||||
self._have_context = False
|
||||
self._have_info = False
|
||||
|
||||
def have_context(self):
|
||||
return self._have_context
|
||||
|
||||
def have_extension(self, extension):
|
||||
"""Determine if an OpenGL extension is available.
|
||||
|
||||
@ -125,7 +138,7 @@ class GLInfo:
|
||||
:return: True if the extension is provided by the driver.
|
||||
:rtype: bool
|
||||
"""
|
||||
if not self.have_context:
|
||||
if not self._have_context:
|
||||
warnings.warn('No GL context created yet.')
|
||||
return extension in self.extensions
|
||||
|
||||
@ -135,7 +148,7 @@ class GLInfo:
|
||||
:return: a list of the available extensions.
|
||||
:rtype: list of str
|
||||
"""
|
||||
if not self.have_context:
|
||||
if not self._have_context:
|
||||
warnings.warn('No GL context created yet.')
|
||||
return self.extensions
|
||||
|
||||
@ -145,7 +158,7 @@ class GLInfo:
|
||||
:return: The major and minor version as a tuple
|
||||
:rtype: tuple
|
||||
"""
|
||||
if not self.have_context:
|
||||
if not self._have_context:
|
||||
warnings.warn('No GL context created yet.')
|
||||
return self.major_version, self.minor_version
|
||||
|
||||
@ -155,7 +168,7 @@ class GLInfo:
|
||||
:return: The OpenGL version string
|
||||
:rtype: str
|
||||
"""
|
||||
if not self.have_context:
|
||||
if not self._have_context:
|
||||
warnings.warn('No GL context created yet.')
|
||||
return self.version
|
||||
|
||||
@ -172,7 +185,7 @@ class GLInfo:
|
||||
:return: True if the requested or a later version is supported.
|
||||
"""
|
||||
|
||||
if not self.have_context:
|
||||
if not self._have_context:
|
||||
warnings.warn('No GL context created yet.')
|
||||
if not self.major_version and not self.minor_version:
|
||||
return False
|
||||
@ -186,7 +199,7 @@ class GLInfo:
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
if not self.have_context:
|
||||
if not self._have_context:
|
||||
warnings.warn('No GL context created yet.')
|
||||
return self.renderer
|
||||
|
||||
@ -195,7 +208,7 @@ class GLInfo:
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
if not self.have_context:
|
||||
if not self._have_context:
|
||||
warnings.warn('No GL context created yet.')
|
||||
return self.vendor
|
||||
|
||||
@ -205,7 +218,7 @@ class GLInfo:
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
if not self.have_context:
|
||||
if not self._have_context:
|
||||
warnings.warn('No GL context created yet.')
|
||||
return self.opengl_api
|
||||
|
||||
@ -214,9 +227,6 @@ class GLInfo:
|
||||
# (or all contexts have the same GL driver, a common case).
|
||||
_gl_info = GLInfo()
|
||||
|
||||
set_active_context = _gl_info.set_active_context
|
||||
remove_active_context = _gl_info.remove_active_context
|
||||
have_extension = _gl_info.have_extension
|
||||
get_extensions = _gl_info.get_extensions
|
||||
get_version = _gl_info.get_version
|
||||
get_version_string = _gl_info.get_version_string
|
||||
@ -224,10 +234,7 @@ have_version = _gl_info.have_version
|
||||
get_renderer = _gl_info.get_renderer
|
||||
get_vendor = _gl_info.get_vendor
|
||||
get_opengl_api = _gl_info.get_opengl_api
|
||||
|
||||
def have_context():
|
||||
"""Determine if a default OpenGL context has been set yet.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return _gl_info.have_context
|
||||
have_extension = _gl_info.have_extension
|
||||
have_context = _gl_info.have_context
|
||||
remove_active_context = _gl_info.remove_active_context
|
||||
set_active_context = _gl_info.set_active_context
|
||||
|
@ -40,7 +40,7 @@ Do not modify generated portions of this file.
|
||||
|
||||
from ctypes import *
|
||||
from pyglet.gl.lib import link_GLX as _link_function
|
||||
from pyglet.gl.lib import c_ptrdiff_t, c_void
|
||||
from pyglet.gl.lib import c_void
|
||||
|
||||
if not _link_function:
|
||||
raise ImportError('libGL.so is not available.')
|
||||
|
@ -41,6 +41,7 @@ Do not modify this file.
|
||||
|
||||
from ctypes import *
|
||||
from pyglet.gl.lib import link_GLX as _link_function
|
||||
from pyglet.gl.lib import c_void
|
||||
|
||||
|
||||
# BEGIN GENERATED CONTENT (do not edit below this line)
|
||||
|
@ -44,7 +44,8 @@ __all__ = ['link_GL', 'link_WGL']
|
||||
|
||||
_debug_trace = pyglet.options['debug_trace']
|
||||
|
||||
gl_lib = ctypes.windll.opengl32
|
||||
# gl_lib = ctypes.windll.opengl32
|
||||
gl_lib = pyglet.lib.load_library('opengl32')
|
||||
wgl_lib = gl_lib
|
||||
|
||||
if _debug_trace:
|
||||
|
@ -1,4 +1,3 @@
|
||||
from typing import Dict, List
|
||||
from ctypes import *
|
||||
from weakref import proxy
|
||||
|
||||
@ -181,6 +180,56 @@ class _Uniform:
|
||||
return f"Uniform('{self.name}', location={self.location}, length={self.length}, count={self.count})"
|
||||
|
||||
|
||||
class ShaderSource:
|
||||
"""GLSL source container for making source parsing simpler.
|
||||
|
||||
We support locating out attributes and applying #defines values.
|
||||
|
||||
NOTE: We do assume the source is neat enough to be parsed
|
||||
this way and don't contain several statements in one line.
|
||||
"""
|
||||
|
||||
def __init__(self, source: str, source_type: gl.GLenum):
|
||||
"""Create a shader source wrapper."""
|
||||
self._lines = source.strip().splitlines()
|
||||
self._type = source_type
|
||||
|
||||
if not self._lines:
|
||||
raise ValueError("Shader source is empty")
|
||||
|
||||
self._version = self._find_glsl_version()
|
||||
|
||||
if pyglet.gl.current_context.get_info().get_opengl_api() == "gles":
|
||||
self._lines[0] = "#version 310 es"
|
||||
self._lines.insert(1, "precision mediump float;")
|
||||
|
||||
if self._type == gl.GL_GEOMETRY_SHADER:
|
||||
self._lines.insert(1, "#extension GL_EXT_geometry_shader : require")
|
||||
|
||||
if self._type == gl.GL_COMPUTE_SHADER:
|
||||
self._lines.insert(1, "precision mediump image2D;")
|
||||
|
||||
self._version = self._find_glsl_version()
|
||||
|
||||
def validate(self) -> str:
|
||||
"""Return the validated shader source."""
|
||||
return "\n".join(self._lines)
|
||||
|
||||
def _find_glsl_version(self) -> int:
|
||||
if self._lines[0].strip().startswith("#version"):
|
||||
try:
|
||||
return int(self._lines[0].split()[1])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
source = "\n".join(f"{str(i+1).zfill(3)}: {line} " for i, line in enumerate(self._lines))
|
||||
|
||||
raise ShaderException(("Cannot find #version flag in shader source. "
|
||||
"A #version statement is required on the first line.\n"
|
||||
"------------------------------------\n"
|
||||
f"{source}"))
|
||||
|
||||
|
||||
class Shader:
|
||||
"""OpenGL Shader object"""
|
||||
|
||||
@ -247,7 +296,8 @@ class Shader:
|
||||
else:
|
||||
return f"{self.type.capitalize()} Shader '{shader_id}' compiled successfully."
|
||||
|
||||
def _get_shader_source(self, shader_id):
|
||||
@staticmethod
|
||||
def _get_shader_source(shader_id):
|
||||
"""Get the shader source from the shader object"""
|
||||
source_length = c_int(0)
|
||||
glGetShaderiv(shader_id, GL_SHADER_SOURCE_LENGTH, source_length)
|
||||
@ -698,61 +748,3 @@ class UniformBufferObject:
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}(id={1})".format(self.block.name + 'Buffer', self.buffer.id)
|
||||
|
||||
|
||||
|
||||
class ShaderSource:
|
||||
"""
|
||||
GLSL source container for making source parsing simpler.
|
||||
We support locating out attributes and applying #defines values.
|
||||
|
||||
NOTE: We do assume the source is neat enough to be parsed
|
||||
this way and don't contain several statements in one line.
|
||||
"""
|
||||
|
||||
def __init__(self, source: str, source_type: gl.GLenum):
|
||||
"""Create a shader source wrapper."""
|
||||
self._source = source.strip()
|
||||
self._type = source_type
|
||||
self._lines = self._source.split("\n") if source else []
|
||||
|
||||
if not self._lines:
|
||||
raise ValueError("Shader source is empty")
|
||||
|
||||
self._version = self._find_glsl_version()
|
||||
|
||||
if pyglet.gl.current_context.get_info().get_opengl_api() == "gles":
|
||||
self._lines[0] = "#version 310 es"
|
||||
self._lines.insert(1, "precision mediump float;")
|
||||
|
||||
if self._type == gl.GL_GEOMETRY_SHADER:
|
||||
self._lines.insert(1, "#extension GL_EXT_geometry_shader : require")
|
||||
|
||||
if self._type == gl.GL_COMPUTE_SHADER:
|
||||
self._lines.insert(1, "precision mediump image2D;")
|
||||
|
||||
self._version = self._find_glsl_version()
|
||||
|
||||
def validate(self) -> str:
|
||||
"""Return the validated shader source."""
|
||||
return "\n".join(self._lines)
|
||||
|
||||
def _find_glsl_version(self) -> int:
|
||||
if self._lines[0].strip().startswith("#version"):
|
||||
try:
|
||||
return int(self._lines[0].split()[1])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
source = "\n".join(
|
||||
f"{str(i+1).zfill(3)}: {line} " for i, line in enumerate(self._lines)
|
||||
)
|
||||
|
||||
raise ShaderException(
|
||||
(
|
||||
"Cannot find #version in shader source. "
|
||||
"A #version statement is required in the first line.\n"
|
||||
f"------------------------------------\n"
|
||||
f"{source}"
|
||||
)
|
||||
)
|
||||
|
@ -1329,7 +1329,7 @@ class Texture(AbstractImage):
|
||||
buf = (GLubyte * (self.width * self.height * self.images * len(fmt)))()
|
||||
|
||||
# TODO: Clean up this temporary hack
|
||||
if gl.current_context.get_info().get_opengl_api() == "gles":
|
||||
if pyglet.gl.current_context.get_info().get_opengl_api() == "gles":
|
||||
fbo = c_uint()
|
||||
glGenFramebuffers(1, fbo)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo.value)
|
||||
|
@ -33,7 +33,7 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
from pyglet.com import pIUnknown
|
||||
from pyglet.libs.win32.com import pIUnknown
|
||||
from pyglet.image import *
|
||||
from pyglet.image.codecs import *
|
||||
from pyglet.libs.win32.constants import *
|
||||
|
@ -399,11 +399,6 @@ class IWICImagingFactory(com.pIUnknown):
|
||||
|
||||
_factory = IWICImagingFactory()
|
||||
|
||||
try:
|
||||
ole32.CoInitializeEx(None, COINIT_MULTITHREADED)
|
||||
except OSError as err:
|
||||
warnings.warn(str(err))
|
||||
|
||||
ole32.CoCreateInstance(CLSID_WICImagingFactory,
|
||||
None,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
|
@ -94,6 +94,10 @@ def dump_pyglet():
|
||||
|
||||
def dump_window():
|
||||
"""Dump display, window, screen and default config info."""
|
||||
from pyglet.gl import gl_info
|
||||
if not gl_info.have_version(3):
|
||||
print(f"Insufficient OpenGL version: {gl_info.get_version_string()}")
|
||||
return
|
||||
import pyglet.window
|
||||
display = pyglet.canvas.get_display()
|
||||
print('display:', repr(display))
|
||||
|
@ -45,6 +45,8 @@ from ctypes import c_int32 as _s32
|
||||
from ctypes import c_int64 as _s64
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from typing import List
|
||||
|
||||
import pyglet
|
||||
|
||||
from pyglet.app.xlib import XlibSelectDevice
|
||||
@ -580,7 +582,7 @@ class EvdevControllerManager(ControllerManager, XlibSelectDevice):
|
||||
if controller:
|
||||
self.dispatch_event('on_disconnect', controller)
|
||||
|
||||
def get_controllers(self) -> list[Controller]:
|
||||
def get_controllers(self) -> List[Controller]:
|
||||
return list(self._controllers.values())
|
||||
|
||||
|
||||
|
@ -188,8 +188,6 @@ class WintabTabletCanvas(TabletCanvas):
|
||||
|
||||
self.dispatch_event('on_motion', self._current_cursor, x, y, pressure, 0., 0.)
|
||||
|
||||
print(packet.pkButtons)
|
||||
|
||||
@pyglet.window.win32.Win32EventHandler(0)
|
||||
def _event_wt_proximity(self, msg, wParam, lParam):
|
||||
if wParam != self._context:
|
||||
|
@ -4,7 +4,7 @@ import threading
|
||||
|
||||
import pyglet
|
||||
|
||||
from pyglet import com
|
||||
from pyglet.libs.win32 import com
|
||||
from pyglet.event import EventDispatcher
|
||||
from pyglet.libs.win32.types import *
|
||||
from pyglet.libs.win32 import _ole32 as ole32, _oleaut32 as oleaut32
|
||||
@ -281,8 +281,6 @@ def get_xinput_guids():
|
||||
XInput device. Returns a list of strings containing pid/vid.
|
||||
Monstrosity found at: https://docs.microsoft.com/en-us/windows/win32/xinput/xinput-and-directinput
|
||||
"""
|
||||
ole32.CoInitialize(None)
|
||||
|
||||
guids_found = []
|
||||
|
||||
locator = IWbemLocator()
|
||||
@ -340,7 +338,6 @@ def get_xinput_guids():
|
||||
guids_found.append(sdl_guid)
|
||||
|
||||
oleaut32.VariantClear(var)
|
||||
ole32.CoUninitialize()
|
||||
return guids_found
|
||||
|
||||
|
||||
|
@ -128,7 +128,7 @@ class LibraryLoader:
|
||||
|
||||
if not names:
|
||||
raise ImportError("No library name specified")
|
||||
|
||||
|
||||
platform_names = kwargs.get(self.platform, [])
|
||||
if isinstance(platform_names, str):
|
||||
platform_names = [platform_names]
|
||||
@ -145,7 +145,7 @@ class LibraryLoader:
|
||||
try:
|
||||
lib = ctypes.cdll.LoadLibrary(name)
|
||||
if _debug_lib:
|
||||
print(name)
|
||||
print(name, self.find_library(name))
|
||||
if _debug_trace:
|
||||
lib = _TraceLibrary(lib)
|
||||
return lib
|
||||
@ -225,10 +225,10 @@ class MachOLibraryLoader(LibraryLoader):
|
||||
search_path.append(os.path.join(os.environ['CONDA_PREFIX'], 'lib', libname))
|
||||
|
||||
# pyinstaller.py sets sys.frozen to True, and puts dylibs in
|
||||
# Contents/MacOS, which path pyinstaller puts in sys._MEIPASS
|
||||
if (hasattr(sys, 'frozen') and hasattr(sys, '_MEIPASS') and
|
||||
sys.frozen is True and pyglet.compat_platform == 'darwin'):
|
||||
search_path.append(os.path.join(sys._MEIPASS, libname))
|
||||
# Contents/macOS, which path pyinstaller puts in sys._MEIPASS
|
||||
if getattr(sys, 'frozen', False) and getattr(sys, '_MEIPASS', None):
|
||||
meipass = getattr(sys, '_MEIPASS')
|
||||
search_path.append(os.path.join(meipass, libname))
|
||||
|
||||
# conda support
|
||||
if os.environ.get('CONDA_PREFIX', False):
|
||||
@ -316,7 +316,7 @@ class LinuxLibraryLoader(LibraryLoader):
|
||||
|
||||
try:
|
||||
with open('/etc/ld.so.conf') as fid:
|
||||
directories.extend([dir.strip() for dir in fid])
|
||||
directories.extend([directory.strip() for directory in fid])
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
@ -354,4 +354,5 @@ elif pyglet.compat_platform.startswith('linux'):
|
||||
loader = LinuxLibraryLoader()
|
||||
else:
|
||||
loader = LibraryLoader()
|
||||
|
||||
load_library = loader.load_library
|
||||
|
@ -33,12 +33,13 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
import atexit
|
||||
import struct
|
||||
|
||||
import pyglet
|
||||
from . import com
|
||||
from . import constants
|
||||
from .types import *
|
||||
from pyglet import com
|
||||
|
||||
IS64 = struct.calcsize("P") == 8
|
||||
|
||||
@ -46,12 +47,14 @@ _debug_win32 = pyglet.options['debug_win32']
|
||||
|
||||
if _debug_win32:
|
||||
import traceback
|
||||
|
||||
_GetLastError = windll.kernel32.GetLastError
|
||||
_SetLastError = windll.kernel32.SetLastError
|
||||
_FormatMessageA = windll.kernel32.FormatMessageA
|
||||
|
||||
_log_win32 = open('debug_win32.log', 'w')
|
||||
|
||||
|
||||
|
||||
def format_error(err):
|
||||
msg = create_string_buffer(256)
|
||||
_FormatMessageA(constants.FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
@ -62,7 +65,8 @@ if _debug_win32:
|
||||
len(msg),
|
||||
c_void_p())
|
||||
return msg.value
|
||||
|
||||
|
||||
|
||||
class DebugLibrary:
|
||||
def __init__(self, lib):
|
||||
self.lib = lib
|
||||
@ -84,7 +88,6 @@ if _debug_win32:
|
||||
else:
|
||||
DebugLibrary = lambda lib: lib
|
||||
|
||||
|
||||
_gdi32 = DebugLibrary(windll.gdi32)
|
||||
_kernel32 = DebugLibrary(windll.kernel32)
|
||||
_user32 = DebugLibrary(windll.user32)
|
||||
@ -108,6 +111,8 @@ _gdi32.CreateDIBSection.restype = HBITMAP
|
||||
_gdi32.CreateDIBSection.argtypes = [HDC, c_void_p, UINT, c_void_p, HANDLE, DWORD] # POINTER(BITMAPINFO)
|
||||
_gdi32.CreateFontIndirectA.restype = HFONT
|
||||
_gdi32.CreateFontIndirectA.argtypes = [POINTER(LOGFONT)]
|
||||
_gdi32.CreateFontIndirectW.restype = HFONT
|
||||
_gdi32.CreateFontIndirectW.argtypes = [POINTER(LOGFONTW)]
|
||||
_gdi32.DeleteDC.restype = BOOL
|
||||
_gdi32.DeleteDC.argtypes = [HDC]
|
||||
_gdi32.DeleteObject.restype = BOOL
|
||||
@ -122,7 +127,7 @@ _gdi32.GetCharABCWidthsW.restype = BOOL
|
||||
_gdi32.GetCharABCWidthsW.argtypes = [HDC, UINT, UINT, POINTER(ABC)]
|
||||
_gdi32.GetCharWidth32W.restype = BOOL
|
||||
_gdi32.GetCharWidth32W.argtypes = [HDC, UINT, UINT, POINTER(INT)]
|
||||
_gdi32.GetStockObject.restype = HGDIOBJ
|
||||
_gdi32.GetStockObject.restype = HGDIOBJ
|
||||
_gdi32.GetStockObject.argtypes = [c_int]
|
||||
_gdi32.GetTextMetricsA.restype = BOOL
|
||||
_gdi32.GetTextMetricsA.argtypes = [HDC, POINTER(TEXTMETRIC)]
|
||||
@ -173,7 +178,8 @@ _user32.ClipCursor.argtypes = [LPRECT]
|
||||
_user32.CreateIconIndirect.restype = HICON
|
||||
_user32.CreateIconIndirect.argtypes = [POINTER(ICONINFO)]
|
||||
_user32.CreateWindowExW.restype = HWND
|
||||
_user32.CreateWindowExW.argtypes = [DWORD, c_wchar_p, c_wchar_p, DWORD, c_int, c_int, c_int, c_int, HWND, HMENU, HINSTANCE, LPVOID]
|
||||
_user32.CreateWindowExW.argtypes = [DWORD, c_wchar_p, c_wchar_p, DWORD, c_int, c_int, c_int, c_int, HWND, HMENU,
|
||||
HINSTANCE, LPVOID]
|
||||
_user32.DefWindowProcW.restype = LRESULT
|
||||
_user32.DefWindowProcW.argtypes = [HWND, UINT, WPARAM, LPARAM]
|
||||
_user32.DestroyWindow.restype = BOOL
|
||||
@ -191,8 +197,8 @@ _user32.GetClientRect.argtypes = [HWND, LPRECT]
|
||||
_user32.GetCursorPos.restype = BOOL
|
||||
_user32.GetCursorPos.argtypes = [LPPOINT]
|
||||
# workaround for win 64-bit, see issue #664
|
||||
_user32.GetDC.restype = c_void_p # HDC
|
||||
_user32.GetDC.argtypes = [c_void_p] # [HWND]
|
||||
_user32.GetDC.restype = c_void_p # HDC
|
||||
_user32.GetDC.argtypes = [c_void_p] # [HWND]
|
||||
_user32.GetDesktopWindow.restype = HWND
|
||||
_user32.GetDesktopWindow.argtypes = []
|
||||
_user32.GetKeyState.restype = c_short
|
||||
@ -226,8 +232,8 @@ _user32.RegisterHotKey.argtypes = [HWND, c_int, UINT, UINT]
|
||||
_user32.ReleaseCapture.restype = BOOL
|
||||
_user32.ReleaseCapture.argtypes = []
|
||||
# workaround for win 64-bit, see issue #664
|
||||
_user32.ReleaseDC.restype = c_int32 # c_int
|
||||
_user32.ReleaseDC.argtypes = [c_void_p, c_void_p] # [HWND, HDC]
|
||||
_user32.ReleaseDC.restype = c_int32 # c_int
|
||||
_user32.ReleaseDC.argtypes = [c_void_p, c_void_p] # [HWND, HDC]
|
||||
_user32.ScreenToClient.restype = BOOL
|
||||
_user32.ScreenToClient.argtypes = [HWND, LPPOINT]
|
||||
_user32.SetCapture.restype = HWND
|
||||
@ -275,13 +281,13 @@ _user32.GetRawInputData.argtypes = [HRAWINPUT, UINT, LPVOID, PUINT, UINT]
|
||||
_user32.ChangeWindowMessageFilterEx.restype = BOOL
|
||||
_user32.ChangeWindowMessageFilterEx.argtypes = [HWND, UINT, DWORD, c_void_p]
|
||||
|
||||
#dwmapi
|
||||
# dwmapi
|
||||
_dwmapi.DwmIsCompositionEnabled.restype = c_int
|
||||
_dwmapi.DwmIsCompositionEnabled.argtypes = [POINTER(INT)]
|
||||
_dwmapi.DwmFlush.restype = c_int
|
||||
_dwmapi.DwmFlush.argtypes = []
|
||||
|
||||
#_shell32
|
||||
# _shell32
|
||||
_shell32.DragAcceptFiles.restype = c_void
|
||||
_shell32.DragAcceptFiles.argtypes = [HWND, BOOL]
|
||||
_shell32.DragFinish.restype = c_void
|
||||
@ -306,9 +312,23 @@ _ole32.CoCreateInstance.argtypes = [com.REFIID, c_void_p, DWORD, com.REFIID, c_v
|
||||
_ole32.CoSetProxyBlanket.restype = HRESULT
|
||||
_ole32.CoSetProxyBlanket.argtypes = (c_void_p, DWORD, DWORD, c_void_p, DWORD, DWORD, c_void_p, DWORD)
|
||||
|
||||
|
||||
# oleaut32
|
||||
_oleaut32.VariantInit.restype = c_void_p
|
||||
_oleaut32.VariantInit.argtypes = [c_void_p]
|
||||
_oleaut32.VariantClear.restype = HRESULT
|
||||
_oleaut32.VariantClear.argtypes = [c_void_p]
|
||||
|
||||
# Initialize COM in MTA mode. Required for: WIC (DirectWrite), WMF, and XInput
|
||||
try:
|
||||
_ole32.CoInitializeEx(None, constants.COINIT_MULTITHREADED)
|
||||
except OSError as err:
|
||||
warnings.warn("Could not set COM MTA mode. Unexpected behavior may occur.")
|
||||
|
||||
|
||||
def _uninitialize():
|
||||
try:
|
||||
_ole32.CoUninitialize()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
atexit.register(_uninitialize)
|
||||
|
@ -77,7 +77,7 @@ from pyglet.util import debug_print
|
||||
_debug_com = debug_print('debug_com')
|
||||
|
||||
if sys.platform != 'win32':
|
||||
raise ImportError('pyglet.com requires a Windows build of Python')
|
||||
raise ImportError('pyglet.libs.win32.com requires a Windows build of Python')
|
||||
|
||||
|
||||
class GUID(ctypes.Structure):
|
@ -34,7 +34,7 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
import ctypes
|
||||
|
||||
from pyglet import com
|
||||
from pyglet.libs.win32 import com
|
||||
|
||||
lib = ctypes.oledll.dinput8
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
import sys
|
||||
import ctypes
|
||||
from pyglet import com
|
||||
from . import com
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import *
|
||||
|
||||
@ -269,6 +269,25 @@ class LOGFONT(Structure):
|
||||
]
|
||||
|
||||
|
||||
class LOGFONTW(Structure):
|
||||
_fields_ = [
|
||||
('lfHeight', LONG),
|
||||
('lfWidth', LONG),
|
||||
('lfEscapement', LONG),
|
||||
('lfOrientation', LONG),
|
||||
('lfWeight', LONG),
|
||||
('lfItalic', BYTE),
|
||||
('lfUnderline', BYTE),
|
||||
('lfStrikeOut', BYTE),
|
||||
('lfCharSet', BYTE),
|
||||
('lfOutPrecision', BYTE),
|
||||
('lfClipPrecision', BYTE),
|
||||
('lfQuality', BYTE),
|
||||
('lfPitchAndFamily', BYTE),
|
||||
('lfFaceName', (WCHAR * LF_FACESIZE))
|
||||
]
|
||||
|
||||
|
||||
class TRACKMOUSEEVENT(Structure):
|
||||
_fields_ = [
|
||||
('cbSize', DWORD),
|
||||
|
@ -35,15 +35,15 @@
|
||||
|
||||
"""Matrix and Vector math.
|
||||
|
||||
This module provides Vector and Matrix objects, include Vec2, Vec3, Vec4,
|
||||
Mat3, and Mat4. Most common operations are supported, and many helper
|
||||
methods are included for rotating, scaling, and transforming. The
|
||||
:py:class:`~pyglet.matrix.Mat4` includes class methods for creating
|
||||
orthographic and perspective projection matrixes.
|
||||
This module provides Vector and Matrix objects, including Vec2, Vec3,
|
||||
Vec4, Mat3, and Mat4. Most common matrix and vector operations are
|
||||
supported. Helper methods are included for rotating, scaling, and
|
||||
transforming. The :py:class:`~pyglet.matrix.Mat4` includes class methods
|
||||
for creating orthographic and perspective projection matrixes.
|
||||
|
||||
:note: For performance, these objects' subclass the `tuple` type. They
|
||||
are therefore immutable - all operations return a new object; the
|
||||
object is not updated in-place.
|
||||
:note: For performance, Matrixes subclass the `tuple` type. They
|
||||
are therefore immutable - all operations return a new object;
|
||||
the object is not updated in-place.
|
||||
"""
|
||||
|
||||
import math as _math
|
||||
@ -56,7 +56,10 @@ def clamp(num, min_val, max_val):
|
||||
return max(min(num, max_val), min_val)
|
||||
|
||||
|
||||
class Vec2(tuple):
|
||||
class Vec2:
|
||||
|
||||
__slots__ = 'x', 'y'
|
||||
|
||||
"""A two-dimensional vector represented as an X Y coordinate pair.
|
||||
|
||||
:parameters:
|
||||
@ -64,83 +67,40 @@ class Vec2(tuple):
|
||||
The X coordinate of the vector.
|
||||
`y` : int or float :
|
||||
The Y coordinate of the vector.
|
||||
|
||||
Vectors must be created with either 0 or 2 values. If no
|
||||
arguments are provided a vector with the coordinates 0, 0 is created.
|
||||
|
||||
Vectors are stored as a tuple and therefore immutable and cannot be modified directly
|
||||
"""
|
||||
|
||||
def __new__(cls, *args):
|
||||
assert len(args) in (0, 2), "0 or 2 values are required for Vec2 types."
|
||||
return super().__new__(Vec2, args or (0, 0))
|
||||
def __init__(self, x=0.0, y=0.0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
@staticmethod
|
||||
def from_polar(mag, angle):
|
||||
"""Create a new vector from the given polar coodinates.
|
||||
def __iter__(self):
|
||||
yield self.x
|
||||
yield self.y
|
||||
|
||||
:parameters:
|
||||
`mag` : int or float :
|
||||
The magnitude of the vector.
|
||||
`angle` : int or float :
|
||||
The angle of the vector in radians.
|
||||
def __len__(self):
|
||||
return 2
|
||||
|
||||
:returns: A new vector with the given angle and magnitude.
|
||||
:rtype: Vec2
|
||||
"""
|
||||
return Vec2(mag * _math.cos(angle), mag * _math.sin(angle))
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
"""The X coordinate of the vector.
|
||||
|
||||
:type: float
|
||||
"""
|
||||
return self[0]
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
"""The Y coordinate of the vector.
|
||||
|
||||
:type: float
|
||||
"""
|
||||
return self[1]
|
||||
|
||||
@property
|
||||
def heading(self):
|
||||
"""The angle of the vector in radians.
|
||||
|
||||
:type: float
|
||||
"""
|
||||
return _math.atan2(self[1], self[0])
|
||||
|
||||
@property
|
||||
def mag(self):
|
||||
"""The magnitude, or length of the vector. The distance between the coordinates and the origin.
|
||||
|
||||
Alias of abs(self).
|
||||
|
||||
:type: float
|
||||
"""
|
||||
return self.__abs__()
|
||||
def __getitem__(self, item):
|
||||
return (self.x, self.y)[item]
|
||||
|
||||
def __add__(self, other):
|
||||
return Vec2(self[0] + other[0], self[1] + other[1])
|
||||
return Vec2(self.x + other.x, self.y + other.y)
|
||||
|
||||
def __sub__(self, other):
|
||||
return Vec2(self[0] - other[0], self[1] - other[1])
|
||||
return Vec2(self.x - other.x, self.y - other.y)
|
||||
|
||||
def __mul__(self, other):
|
||||
return Vec2(self[0] * other[0], self[1] * other[1])
|
||||
return Vec2(self.x * other.x, self.y * other.y)
|
||||
|
||||
def __truediv__(self, other):
|
||||
return Vec2(self[0] / other[0], self[1] / other[1])
|
||||
return Vec2(self.x / other.x, self.y / other.y)
|
||||
|
||||
def __abs__(self):
|
||||
return _math.sqrt(self[0] ** 2 + self[1] ** 2)
|
||||
return _math.sqrt(self.x ** 2 + self.y ** 2)
|
||||
|
||||
def __neg__(self):
|
||||
return Vec2(-self[0], -self[1])
|
||||
return Vec2(-self.x, -self.y)
|
||||
|
||||
def __round__(self, ndigits=None):
|
||||
return Vec2(*(round(v, ndigits) for v in self))
|
||||
@ -153,6 +113,21 @@ class Vec2(tuple):
|
||||
else:
|
||||
return self.__add__(other)
|
||||
|
||||
@staticmethod
|
||||
def from_polar(mag, angle):
|
||||
"""Create a new vector from the given polar coodinates.
|
||||
|
||||
:parameters:
|
||||
`mag` : int or float :
|
||||
The magnitude of the vector.
|
||||
`angle` : int or float :
|
||||
The angle of the vector in radians.
|
||||
|
||||
:returns: A new vector with the given angle and magnitude.
|
||||
:rtype: Vec2
|
||||
"""
|
||||
return Vec2(mag * _math.cos(angle), mag * _math.sin(angle))
|
||||
|
||||
def from_magnitude(self, magnitude):
|
||||
"""Create a new Vector of the given magnitude by normalizing,
|
||||
then scaling the vector. The heading remains unchanged.
|
||||
@ -179,6 +154,24 @@ class Vec2(tuple):
|
||||
mag = self.__abs__()
|
||||
return Vec2(mag * _math.cos(heading), mag * _math.sin(heading))
|
||||
|
||||
@property
|
||||
def heading(self):
|
||||
"""The angle of the vector in radians.
|
||||
|
||||
:type: float
|
||||
"""
|
||||
return _math.atan2(self.y, self.x)
|
||||
|
||||
@property
|
||||
def mag(self):
|
||||
"""The magnitude, or length of the vector. The distance between the coordinates and the origin.
|
||||
|
||||
Alias of abs(self).
|
||||
|
||||
:type: float
|
||||
"""
|
||||
return self.__abs__()
|
||||
|
||||
def limit(self, maximum):
|
||||
"""Limit the magnitude of the vector to the value used for the max parameter.
|
||||
|
||||
@ -189,7 +182,7 @@ class Vec2(tuple):
|
||||
:returns: Either self or a new vector with the maximum magnitude.
|
||||
:rtype: Vec2
|
||||
"""
|
||||
if self[0] ** 2 + self[1] ** 2 > maximum * maximum:
|
||||
if self.x ** 2 + self.y ** 2 > maximum * maximum:
|
||||
return self.from_magnitude(maximum)
|
||||
return self
|
||||
|
||||
@ -207,8 +200,8 @@ class Vec2(tuple):
|
||||
:returns: A new interpolated vector.
|
||||
:rtype: Vec2
|
||||
"""
|
||||
return Vec2(self[0] + (alpha * (other[0] - self[0])),
|
||||
self[1] + (alpha * (other[1] - self[1])))
|
||||
return Vec2(self.x + (alpha * (other.x - self.x)),
|
||||
self.y + (alpha * (other.y - self.y)))
|
||||
|
||||
def scale(self, value):
|
||||
"""Multiply the vector by a scalar value.
|
||||
@ -220,7 +213,7 @@ class Vec2(tuple):
|
||||
:returns: A new vector scaled by the value.
|
||||
:rtype: Vec2
|
||||
"""
|
||||
return Vec2(self[0] * value, self[1] * value)
|
||||
return Vec2(self.x * value, self.y * value)
|
||||
|
||||
def rotate(self, angle):
|
||||
"""Create a new Vector rotated by the angle. The magnitude remains unchanged.
|
||||
@ -246,7 +239,7 @@ class Vec2(tuple):
|
||||
:returns: The distance between the two vectors.
|
||||
:rtype: float
|
||||
"""
|
||||
return _math.sqrt(((other[0] - self[0]) ** 2) + ((other[1] - self[1]) ** 2))
|
||||
return _math.sqrt(((other.x - self.x) ** 2) + ((other.y - self.y) ** 2))
|
||||
|
||||
def normalize(self):
|
||||
"""Normalize the vector to have a magnitude of 1. i.e. make it a unit vector.
|
||||
@ -256,7 +249,7 @@ class Vec2(tuple):
|
||||
"""
|
||||
d = self.__abs__()
|
||||
if d:
|
||||
return Vec2(self[0] / d, self[1] / d)
|
||||
return Vec2(self.x / d, self.y / d)
|
||||
return self
|
||||
|
||||
def clamp(self, min_val, max_val):
|
||||
@ -271,7 +264,7 @@ class Vec2(tuple):
|
||||
:returns: A new vector with clamped X and Y components.
|
||||
:rtype: Vec2
|
||||
"""
|
||||
return Vec2(clamp(self[0], min_val, max_val), clamp(self[1], min_val, max_val))
|
||||
return Vec2(clamp(self.x, min_val, max_val), clamp(self.y, min_val, max_val))
|
||||
|
||||
def dot(self, other):
|
||||
"""Calculate the dot product of this vector and another 2D vector.
|
||||
@ -283,7 +276,7 @@ class Vec2(tuple):
|
||||
:returns: The dot product of the two vectors.
|
||||
:rtype: float
|
||||
"""
|
||||
return self[0] * other[0] + self[1] * other[1]
|
||||
return self.x * other.x + self.y * other.y
|
||||
|
||||
def __getattr__(self, attrs):
|
||||
try:
|
||||
@ -294,10 +287,13 @@ class Vec2(tuple):
|
||||
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attrs}'")
|
||||
|
||||
def __repr__(self):
|
||||
return f"Vec2({self[0]}, {self[1]})"
|
||||
return f"Vec2({self.x}, {self.y})"
|
||||
|
||||
|
||||
class Vec3(tuple):
|
||||
class Vec3:
|
||||
|
||||
__slots__ = 'x', 'y', 'z'
|
||||
|
||||
"""A three-dimensional vector represented as X Y Z coordinates.
|
||||
|
||||
:parameters:
|
||||
@ -307,68 +303,52 @@ class Vec3(tuple):
|
||||
The Y coordinate of the vector.
|
||||
`z` : int or float :
|
||||
The Z coordinate of the vector.
|
||||
|
||||
3D Vectors must be created with either 0 or 3 values. If no arguments
|
||||
are provided, a vector with the coordinates 0, 0, 0 is created.
|
||||
|
||||
Vectors are stored as a tuple and therefore immutable and cannot be modified directly
|
||||
"""
|
||||
|
||||
def __new__(cls, *args):
|
||||
assert len(args) in (0, 3), "0 or 3 values are required for Vec3 types."
|
||||
return super().__new__(Vec3, args or (0, 0, 0))
|
||||
def __init__(self, x=0.0, y=0.0, z=0.0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
"""The X coordinate of the vector.
|
||||
def __iter__(self):
|
||||
yield self.x
|
||||
yield self.y
|
||||
yield self.z
|
||||
|
||||
:type: float
|
||||
"""
|
||||
return self[0]
|
||||
def __getitem__(self, item):
|
||||
return (self.x, self.y, self.z)[item]
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
"""The Y coordinate of the vector.
|
||||
def __len__(self):
|
||||
return 3
|
||||
|
||||
:type: float
|
||||
"""
|
||||
return self[1]
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
"""The Z coordinate of the vector.
|
||||
|
||||
:type: float
|
||||
"""
|
||||
return self[2]
|
||||
|
||||
@property
|
||||
def mag(self):
|
||||
"""The magnitude, or length of the vector. The distance between the coordinates and the origin.
|
||||
|
||||
Alias of abs(self).
|
||||
|
||||
:type: float
|
||||
:type: float
|
||||
"""
|
||||
return self.__abs__()
|
||||
|
||||
def __add__(self, other):
|
||||
return Vec3(self[0] + other[0], self[1] + other[1], self[2] + other[2])
|
||||
return Vec3(self.x + other.x, self.y + other.y, self.z + other.z)
|
||||
|
||||
def __sub__(self, other):
|
||||
return Vec3(self[0] - other[0], self[1] - other[1], self[2] - other[2])
|
||||
return Vec3(self.x - other.x, self.y - other.y, self.z - other.z)
|
||||
|
||||
def __mul__(self, other):
|
||||
return Vec3(self[0] * other[0], self[1] * other[1], self[2] * other[2])
|
||||
return Vec3(self.x * other.x, self.y * other.y, self.z * other.z)
|
||||
|
||||
def __truediv__(self, other):
|
||||
return Vec3(self[0] / other[0], self[1] / other[1], self[2] / other[2])
|
||||
return Vec3(self.x / other.x, self.y / other.y, self.z / other.z)
|
||||
|
||||
def __abs__(self):
|
||||
return _math.sqrt(self[0] ** 2 + self[1] ** 2 + self[2] ** 2)
|
||||
return _math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
|
||||
|
||||
def __neg__(self):
|
||||
return Vec3(-self[0], -self[1], -self[2])
|
||||
return Vec3(-self.x, -self.y, -self.z)
|
||||
|
||||
def __round__(self, ndigits=None):
|
||||
return Vec3(*(round(v, ndigits) for v in self))
|
||||
@ -404,7 +384,7 @@ class Vec3(tuple):
|
||||
:returns: Either self or a new vector with the maximum magnitude.
|
||||
:rtype: Vec3
|
||||
"""
|
||||
if self[0] ** 2 + self[1] ** 2 + self[2] ** 2 > maximum * maximum * maximum:
|
||||
if self.x ** 2 + self.y ** 2 + self.z ** 2 > maximum * maximum * maximum:
|
||||
return self.from_magnitude(max)
|
||||
return self
|
||||
|
||||
@ -418,21 +398,21 @@ class Vec3(tuple):
|
||||
:returns: The cross product of the two vectors.
|
||||
:rtype: float
|
||||
"""
|
||||
return Vec3((self[1] * other[2]) - (self[2] * other[1]),
|
||||
(self[2] * other[0]) - (self[0] * other[2]),
|
||||
(self[0] * other[1]) - (self[1] * other[0]))
|
||||
return Vec3((self.y * other.z) - (self.z * other.y),
|
||||
(self.z * other.x) - (self.x * other.z),
|
||||
(self.x * other.y) - (self.y * other.x))
|
||||
|
||||
def dot(self, other):
|
||||
"""Calculate the dot product of this vector and another 3D vector.
|
||||
|
||||
:parameters:
|
||||
:parameters:
|
||||
`other` : Vec3 :
|
||||
The other vector.
|
||||
|
||||
|
||||
:returns: The dot product of the two vectors.
|
||||
:rtype: float
|
||||
"""
|
||||
return self[0] * other[0] + self[1] * other[1] + self[2] * other[2]
|
||||
return self.x * other.x + self.y * other.y + self.z * other.z
|
||||
|
||||
def lerp(self, other, alpha):
|
||||
"""Create a new vector lineraly interpolated between this vector and another vector.
|
||||
@ -448,9 +428,9 @@ class Vec3(tuple):
|
||||
:returns: A new interpolated vector.
|
||||
:rtype: Vec3
|
||||
"""
|
||||
return Vec3(self[0] + (alpha * (other[0] - self[0])),
|
||||
self[1] + (alpha * (other[1] - self[1])),
|
||||
self[2] + (alpha * (other[2] - self[2])))
|
||||
return Vec3(self.x + (alpha * (other.x - self.x)),
|
||||
self.y + (alpha * (other.y - self.y)),
|
||||
self.z + (alpha * (other.z - self.z)))
|
||||
|
||||
def scale(self, value):
|
||||
"""Multiply the vector by a scalar value.
|
||||
@ -462,7 +442,7 @@ class Vec3(tuple):
|
||||
:returns: A new vector scaled by the value.
|
||||
:rtype: Vec3
|
||||
"""
|
||||
return Vec3(self[0] * value, self[1] * value, self[2] * value)
|
||||
return Vec3(self.x * value, self.y * value, self.z * value)
|
||||
|
||||
def distance(self, other):
|
||||
"""Calculate the distance between this vector and another 3D vector.
|
||||
@ -474,9 +454,9 @@ class Vec3(tuple):
|
||||
:returns: The distance between the two vectors.
|
||||
:rtype: float
|
||||
"""
|
||||
return _math.sqrt(((other[0] - self[0]) ** 2) +
|
||||
((other[1] - self[1]) ** 2) +
|
||||
((other[2] - self[2]) ** 2))
|
||||
return _math.sqrt(((other.x - self.x) ** 2) +
|
||||
((other.y - self.y) ** 2) +
|
||||
((other.z - self.z) ** 2))
|
||||
|
||||
def normalize(self):
|
||||
"""Normalize the vector to have a magnitude of 1. i.e. make it a unit vector.
|
||||
@ -486,7 +466,7 @@ class Vec3(tuple):
|
||||
"""
|
||||
d = self.__abs__()
|
||||
if d:
|
||||
return Vec3(self[0] / d, self[1] / d, self[2] / d)
|
||||
return Vec3(self.x / d, self.y / d, self.z / d)
|
||||
return self
|
||||
|
||||
def clamp(self, min_val, max_val):
|
||||
@ -501,9 +481,9 @@ class Vec3(tuple):
|
||||
:returns: A new vector with clamped X, Y and Z components.
|
||||
:rtype: Vec3
|
||||
"""
|
||||
return Vec3(clamp(self[0], min_val, max_val),
|
||||
clamp(self[1], min_val, max_val),
|
||||
clamp(self[2], min_val, max_val))
|
||||
return Vec3(clamp(self.x, min_val, max_val),
|
||||
clamp(self.y, min_val, max_val),
|
||||
clamp(self.z, min_val, max_val))
|
||||
|
||||
def __getattr__(self, attrs):
|
||||
try:
|
||||
@ -514,48 +494,62 @@ class Vec3(tuple):
|
||||
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attrs}'")
|
||||
|
||||
def __repr__(self):
|
||||
return f"Vec3({self[0]}, {self[1]}, {self[2]})"
|
||||
return f"Vec3({self.x}, {self.y}, {self.z})"
|
||||
|
||||
|
||||
class Vec4(tuple):
|
||||
class Vec4:
|
||||
|
||||
def __new__(cls, *args):
|
||||
assert len(args) in (0, 4), "0 or 4 values are required for Vec4 types."
|
||||
return super().__new__(Vec4, args or (0, 0, 0, 0))
|
||||
__slots__ = 'x', 'y', 'z', 'w'
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return self[0]
|
||||
"""A four-dimensional vector represented as X Y Z W coordinates.
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
return self[1]
|
||||
:parameters:
|
||||
`x` : int or float :
|
||||
The X coordinate of the vector.
|
||||
`y` : int or float :
|
||||
The Y coordinate of the vector.
|
||||
`z` : int or float :
|
||||
The Z coordinate of the vector.
|
||||
`w` : int or float :
|
||||
The W coordinate of the vector.
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
return self[2]
|
||||
"""
|
||||
|
||||
@property
|
||||
def w(self):
|
||||
return self[3]
|
||||
def __init__(self, x=0.0, y=0.0, z=0.0, w=0.0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
self.w = w
|
||||
|
||||
def __iter__(self):
|
||||
yield self.x
|
||||
yield self.y
|
||||
yield self.z
|
||||
yield self.w
|
||||
|
||||
def __getitem__(self, item):
|
||||
return (self.x, self.y, self.z, self.w)[item]
|
||||
|
||||
def __len__(self):
|
||||
return 4
|
||||
|
||||
def __add__(self, other):
|
||||
return Vec4(self[0] + other[0], self[1] + other[1], self[2] + other[2], self[3] + other[3])
|
||||
return Vec4(self.x + other.x, self.y + other.y, self.z + other.z, self.w + other.w)
|
||||
|
||||
def __sub__(self, other):
|
||||
return Vec4(self[0] - other[0], self[1] - other[1], self[2] - other[2], self[3] - other[3])
|
||||
return Vec4(self.x - other.x, self.y - other.y, self.z - other.z, self.w - other.w)
|
||||
|
||||
def __mul__(self, other):
|
||||
return Vec4(self[0] * other[0], self[1] * other[1], self[2] * other[2], self[3] * other[3])
|
||||
return Vec4(self.x * other.x, self.y * other.y, self.z * other.z, self.w * other.w)
|
||||
|
||||
def __truediv__(self, other):
|
||||
return Vec4(self[0] / other[0], self[1] / other[1], self[2] / other[2], self[3] / other[3])
|
||||
return Vec4(self.x / other.x, self.y / other.y, self.z / other.z, self.w / other.w)
|
||||
|
||||
def __abs__(self):
|
||||
return _math.sqrt(self[0] ** 2 + self[1] ** 2 + self[2] ** 2 + self[3] ** 2)
|
||||
return _math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2 + self.w ** 2)
|
||||
|
||||
def __neg__(self):
|
||||
return Vec4(-self[0], -self[1], -self[2], -self[3])
|
||||
return Vec4(-self.x, -self.y, -self.z, -self.w)
|
||||
|
||||
def __round__(self, ndigits=None):
|
||||
return Vec4(*(round(v, ndigits) for v in self))
|
||||
@ -567,34 +561,34 @@ class Vec4(tuple):
|
||||
return self.__add__(other)
|
||||
|
||||
def lerp(self, other, alpha):
|
||||
return Vec4(self[0] + (alpha * (other[0] - self[0])),
|
||||
self[1] + (alpha * (other[1] - self[1])),
|
||||
self[2] + (alpha * (other[2] - self[2])),
|
||||
self[3] + (alpha * (other[3] - self[3])))
|
||||
return Vec4(self.x + (alpha * (other.x - self.x)),
|
||||
self.y + (alpha * (other.y - self.y)),
|
||||
self.z + (alpha * (other.z - self.z)),
|
||||
self.w + (alpha * (other.w - self.w)))
|
||||
|
||||
def scale(self, value):
|
||||
return Vec4(self[0] * value, self[1] * value, self[2] * value, self[3] * value)
|
||||
return Vec4(self.x * value, self.y * value, self.z * value, self.w * value)
|
||||
|
||||
def distance(self, other):
|
||||
return _math.sqrt(((other[0] - self[0]) ** 2) +
|
||||
((other[1] - self[1]) ** 2) +
|
||||
((other[2] - self[2]) ** 2) +
|
||||
((other[3] - self[3]) ** 2))
|
||||
return _math.sqrt(((other.x - self.x) ** 2) +
|
||||
((other.y - self.y) ** 2) +
|
||||
((other.z - self.z) ** 2) +
|
||||
((other.w - self.w) ** 2))
|
||||
|
||||
def normalize(self):
|
||||
d = self.__abs__()
|
||||
if d:
|
||||
return Vec4(self[0] / d, self[1] / d, self[2] / d, self[3] / d)
|
||||
return Vec4(self.x / d, self.y / d, self.z / d, self.w / d)
|
||||
return self
|
||||
|
||||
def clamp(self, min_val, max_val):
|
||||
return Vec4(clamp(self[0], min_val, max_val),
|
||||
clamp(self[1], min_val, max_val),
|
||||
clamp(self[2], min_val, max_val),
|
||||
clamp(self[3], min_val, max_val))
|
||||
return Vec4(clamp(self.x, min_val, max_val),
|
||||
clamp(self.y, min_val, max_val),
|
||||
clamp(self.z, min_val, max_val),
|
||||
clamp(self.w, min_val, max_val))
|
||||
|
||||
def dot(self, other):
|
||||
return self[0] * other[0] + self[1] * other[1] + self[2] * other[2] + self[3] * other[3]
|
||||
return self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
|
||||
|
||||
def __getattr__(self, attrs):
|
||||
try:
|
||||
@ -605,7 +599,7 @@ class Vec4(tuple):
|
||||
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attrs}'")
|
||||
|
||||
def __repr__(self):
|
||||
return f"Vec4({self[0]}, {self[1]}, {self[2]}, {self[3]})"
|
||||
return f"Vec4({self.x}, {self.y}, {self.z}, {self.w})"
|
||||
|
||||
|
||||
class Mat3(tuple):
|
||||
@ -667,7 +661,7 @@ class Mat3(tuple):
|
||||
def __mul__(self, other):
|
||||
raise NotImplementedError("Please use the @ operator for Matrix multiplication.")
|
||||
|
||||
def __matmul__(self, other) -> 'Mat3':
|
||||
def __matmul__(self, other) -> 'Mat3' or 'Vec3':
|
||||
assert len(other) in (3, 9), "Can only multiply with Mat3 or Vec3 types"
|
||||
|
||||
if type(other) is Vec3:
|
||||
@ -754,12 +748,16 @@ class Mat4(tuple):
|
||||
tx, ty, tz, 1.0))
|
||||
|
||||
@classmethod
|
||||
def perspective_projection(cls, left, right, bottom, top, z_near, z_far, fov=60) -> 'Mat4':
|
||||
"""Create a Mat4 perspective projection matrix."""
|
||||
width = right - left
|
||||
height = top - bottom
|
||||
aspect = width / height
|
||||
def perspective_projection(cls, aspect, z_near, z_far, fov=60) -> 'Mat4':
|
||||
"""
|
||||
Create a Mat4 perspective projection matrix.
|
||||
|
||||
:Parameters:
|
||||
`aspect` : The aspect ratio as a `float`
|
||||
`z_near` : The near plane as a `float`
|
||||
`z_far` : The far plane as a `float`
|
||||
`fov` : Field of view in degrees as a `float`
|
||||
"""
|
||||
xy_max = z_near * _math.tan(fov * _math.pi / 360)
|
||||
y_min = -xy_max
|
||||
x_min = -xy_max
|
||||
@ -781,11 +779,11 @@ class Mat4(tuple):
|
||||
|
||||
@classmethod
|
||||
def from_translation(cls, vector: Vec3) -> 'Mat4':
|
||||
"""Create a translaton matrix from a Vec3.
|
||||
"""Create a translation matrix from a Vec3.
|
||||
|
||||
:Parameters:
|
||||
`vector` : A `Vec3`, or 3 component tuple of float or int
|
||||
Vec3 or tuple with x, y and z translaton values
|
||||
Vec3 or tuple with x, y and z translation values
|
||||
"""
|
||||
return cls((1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
@ -800,7 +798,7 @@ class Mat4(tuple):
|
||||
`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 translaton values
|
||||
Vec3 or tuple with x, y and z translation values
|
||||
"""
|
||||
return cls().rotate(angle, vector)
|
||||
|
||||
@ -869,7 +867,7 @@ class Mat4(tuple):
|
||||
return Mat4(self) @ Mat4((ra, rb, rc, 0, re, rf, rg, 0, ri, rj, rk, 0, 0, 0, 0, 1))
|
||||
|
||||
def transpose(self) -> 'Mat4':
|
||||
"""Get a tranpose of this Matrix."""
|
||||
"""Get a transpose of this Matrix."""
|
||||
return Mat4(self[0::4] + self[1::4] + self[2::4] + self[3::4])
|
||||
|
||||
def __add__(self, other) -> 'Mat4':
|
||||
@ -941,7 +939,7 @@ class Mat4(tuple):
|
||||
def __mul__(self, other):
|
||||
raise NotImplementedError("Please use the @ operator for Matrix multiplication.")
|
||||
|
||||
def __matmul__(self, other) -> 'Mat4':
|
||||
def __matmul__(self, other) -> 'Mat4' or 'Vec4':
|
||||
assert len(other) in (4, 16), "Can only multiply with Mat4 or Vec4 types"
|
||||
|
||||
if type(other) is Vec4:
|
||||
|
@ -36,14 +36,15 @@ import os
|
||||
import platform
|
||||
import warnings
|
||||
|
||||
from pyglet import com, image
|
||||
from pyglet.util import debug_print, DecodeException
|
||||
from pyglet import image
|
||||
from pyglet.libs.win32 import _kernel32 as kernel32
|
||||
from pyglet.libs.win32 import _ole32 as ole32
|
||||
from pyglet.libs.win32 import com
|
||||
from pyglet.libs.win32.constants import *
|
||||
from pyglet.libs.win32.types import *
|
||||
from pyglet.media import Source
|
||||
from pyglet.media.codecs import AudioFormat, AudioData, VideoFormat, MediaDecoder, StaticSource
|
||||
from pyglet.util import debug_print, DecodeException
|
||||
|
||||
_debug = debug_print('debug_media')
|
||||
|
||||
@ -827,16 +828,8 @@ class WMFSource(Source):
|
||||
|
||||
class WMFDecoder(MediaDecoder):
|
||||
def __init__(self):
|
||||
|
||||
self.ole32 = None
|
||||
self.MFShutdown = None
|
||||
|
||||
try:
|
||||
# Coinitialize supposed to be called for COMs?
|
||||
ole32.CoInitializeEx(None, COINIT_MULTITHREADED)
|
||||
except OSError as err:
|
||||
warnings.warn(str(err))
|
||||
|
||||
try:
|
||||
MFStartup(MF_VERSION, 0)
|
||||
except OSError as err:
|
||||
@ -844,7 +837,6 @@ class WMFDecoder(MediaDecoder):
|
||||
|
||||
self.extensions = self._build_decoder_extensions()
|
||||
|
||||
self.ole32 = ole32
|
||||
self.MFShutdown = MFShutdown
|
||||
|
||||
assert _debug('Windows Media Foundation: Initialized.')
|
||||
@ -884,8 +876,6 @@ class WMFDecoder(MediaDecoder):
|
||||
def __del__(self):
|
||||
if self.MFShutdown is not None:
|
||||
self.MFShutdown()
|
||||
if self.ole32 is not None:
|
||||
self.ole32.CoUninitialize()
|
||||
|
||||
|
||||
def get_decoders():
|
||||
|
@ -1,4 +1,4 @@
|
||||
from pyglet import com
|
||||
from pyglet.libs.win32 import com
|
||||
from pyglet.libs.win32 import _ole32 as ole32
|
||||
from pyglet.libs.win32.constants import CLSCTX_INPROC_SERVER
|
||||
from pyglet.libs.win32.types import *
|
||||
|
@ -92,7 +92,7 @@ class DirectSoundAudioPlayer(AbstractAudioPlayer):
|
||||
|
||||
# Up to one audio data may be buffered if too much data was received
|
||||
# from the source that could not be written immediately into the
|
||||
# buffer. See refill().
|
||||
# buffer. See _refill().
|
||||
self._audiodata_buffer = None
|
||||
|
||||
# Theoretical write and play cursors for an infinite buffer. play
|
||||
@ -106,7 +106,7 @@ class DirectSoundAudioPlayer(AbstractAudioPlayer):
|
||||
self._eos_cursor = None
|
||||
|
||||
# Indexes into DSound circular buffer. Complications ensue wrt each
|
||||
# other to avoid writing over the play cursor. See get_write_size and
|
||||
# other to avoid writing over the play cursor. See _get_write_size and
|
||||
# write().
|
||||
self._play_cursor_ring = 0
|
||||
self._write_cursor_ring = 0
|
||||
@ -126,7 +126,7 @@ class DirectSoundAudioPlayer(AbstractAudioPlayer):
|
||||
|
||||
self._ds_buffer.current_position = 0
|
||||
|
||||
self.refill(self._buffer_size)
|
||||
self._refill(self._buffer_size)
|
||||
|
||||
def __del__(self):
|
||||
# We decrease the IDirectSound refcount
|
||||
@ -167,9 +167,16 @@ class DirectSoundAudioPlayer(AbstractAudioPlayer):
|
||||
del self._events[:]
|
||||
del self._timestamps[:]
|
||||
|
||||
def refill(self, write_size):
|
||||
def refill_buffer(self):
|
||||
write_size = self._get_write_size()
|
||||
if write_size > self.min_buffer_size:
|
||||
self._refill(write_size)
|
||||
return True
|
||||
return False
|
||||
|
||||
def _refill(self, write_size):
|
||||
while write_size > 0:
|
||||
assert _debug('refill, write_size =', write_size)
|
||||
assert _debug('_refill, write_size =', write_size)
|
||||
audio_data = self._get_audiodata()
|
||||
|
||||
if audio_data is not None:
|
||||
@ -277,7 +284,7 @@ class DirectSoundAudioPlayer(AbstractAudioPlayer):
|
||||
self.stop()
|
||||
self._dispatch_new_event('on_eos')
|
||||
|
||||
def get_write_size(self):
|
||||
def _get_write_size(self):
|
||||
self.update_play_cursor()
|
||||
|
||||
play_cursor = self._play_cursor
|
||||
@ -368,8 +375,8 @@ class DirectSoundAudioPlayer(AbstractAudioPlayer):
|
||||
self._ds_buffer.cone_outside_volume = volume
|
||||
|
||||
def prefill_audio(self):
|
||||
write_size = self.get_write_size()
|
||||
self.refill(write_size)
|
||||
write_size = self._get_write_size()
|
||||
self._refill(write_size)
|
||||
|
||||
|
||||
class DirectSoundDriver(AbstractAudioDriver):
|
||||
@ -400,8 +407,9 @@ class DirectSoundDriver(AbstractAudioDriver):
|
||||
return DirectSoundListener(self._ds_listener, self._ds_driver.primary_buffer)
|
||||
|
||||
def delete(self):
|
||||
if hasattr(self, 'worker'):
|
||||
self.worker.stop()
|
||||
# Make sure the _ds_listener is deleted before the _ds_driver
|
||||
self.worker.stop()
|
||||
self._ds_listener = None
|
||||
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
import ctypes
|
||||
from pyglet import com
|
||||
from pyglet.libs.win32 import com
|
||||
|
||||
lib = ctypes.oledll.dsound
|
||||
|
||||
|
@ -158,10 +158,10 @@ class OpenALAudioPlayer(AbstractAudioPlayer):
|
||||
|
||||
# Up to one audio data may be buffered if too much data was received
|
||||
# from the source that could not be written immediately into the
|
||||
# buffer. See refill().
|
||||
# buffer. See _refill().
|
||||
self._audiodata_buffer = None
|
||||
|
||||
self.refill(self.ideal_buffer_size)
|
||||
self._refill(self.ideal_buffer_size)
|
||||
|
||||
def __del__(self):
|
||||
self.delete()
|
||||
@ -258,7 +258,7 @@ class OpenALAudioPlayer(AbstractAudioPlayer):
|
||||
_, event = self._events.pop(0)
|
||||
event._sync_dispatch_to_player(self.player)
|
||||
|
||||
def get_write_size(self):
|
||||
def _get_write_size(self):
|
||||
self._update_play_cursor()
|
||||
buffer_size = int(self._write_cursor - self._play_cursor)
|
||||
|
||||
@ -268,8 +268,15 @@ class OpenALAudioPlayer(AbstractAudioPlayer):
|
||||
assert _debug("Write size {} bytes".format(write_size))
|
||||
return write_size
|
||||
|
||||
def refill(self, write_size):
|
||||
assert _debug('refill', write_size)
|
||||
def refill_buffer(self):
|
||||
write_size = self._get_write_size()
|
||||
if write_size > self.min_buffer_size:
|
||||
self._refill(write_size)
|
||||
return True
|
||||
return False
|
||||
|
||||
def _refill(self, write_size):
|
||||
assert _debug('_refill', write_size)
|
||||
|
||||
while write_size > self.min_buffer_size:
|
||||
audio_data = self._get_audiodata()
|
||||
@ -391,5 +398,5 @@ class OpenALAudioPlayer(AbstractAudioPlayer):
|
||||
self.alsource.cone_outer_gain = cone_outer_gain
|
||||
|
||||
def prefill_audio(self):
|
||||
write_size = self.get_write_size()
|
||||
self.refill(write_size)
|
||||
write_size = self._get_write_size()
|
||||
self._refill(write_size)
|
||||
|
@ -51,9 +51,6 @@ class SilentAudioPlayer(AbstractAudioPlayer):
|
||||
def clear(self):
|
||||
pass
|
||||
|
||||
def get_write_size(self):
|
||||
pass
|
||||
|
||||
def write(self, audio_data, length):
|
||||
pass
|
||||
|
||||
|
@ -3,7 +3,7 @@ import platform
|
||||
import os
|
||||
from pyglet.libs.win32.constants import *
|
||||
from pyglet.libs.win32.types import *
|
||||
from pyglet import com
|
||||
from pyglet.libs.win32 import com
|
||||
from pyglet.util import debug_print
|
||||
|
||||
_debug = debug_print('debug_media')
|
||||
|
@ -152,10 +152,7 @@ class PlayerWorkerThread(MediaThread):
|
||||
if self.players:
|
||||
filled = False
|
||||
for player in list(self.players):
|
||||
write_size = player.get_write_size()
|
||||
if write_size > player.min_buffer_size:
|
||||
player.refill(write_size)
|
||||
filled = True
|
||||
filled = player.refill_buffer()
|
||||
if not filled:
|
||||
sleep_time = self._nap_time
|
||||
else:
|
||||
|
@ -427,6 +427,9 @@ class Player(pyglet.event.EventDispatcher):
|
||||
"""
|
||||
return self.texture
|
||||
|
||||
def refill_buffer(self):
|
||||
raise NotImplemented
|
||||
|
||||
def seek_next_frame(self):
|
||||
"""Step forwards one video frame in the current source."""
|
||||
time = self.source.get_next_video_timestamp()
|
||||
@ -634,7 +637,7 @@ class Player(pyglet.event.EventDispatcher):
|
||||
def on_driver_reset(self):
|
||||
"""The audio driver has been reset, by default this will kill the current audio player and create a new one,
|
||||
and requeue the buffers. Any buffers that may have been queued in a player will be resubmitted. It will
|
||||
continue from from the last buffers submitted, not played and may cause sync issues if using video.
|
||||
continue from the last buffers submitted, not played and may cause sync issues if using video.
|
||||
|
||||
:event:
|
||||
"""
|
||||
|
@ -33,12 +33,12 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import math
|
||||
import ctypes
|
||||
import struct
|
||||
import math as _math
|
||||
import struct as _struct
|
||||
|
||||
from .codecs.base import Source, AudioFormat, AudioData
|
||||
from random import uniform as _uniform
|
||||
|
||||
from pyglet.media.codecs.base import Source, AudioFormat, AudioData
|
||||
|
||||
|
||||
# Envelope classes:
|
||||
@ -168,9 +168,9 @@ class TremoloEnvelope(_Envelope):
|
||||
period = total_bytes / duration
|
||||
max_amplitude = self.amplitude
|
||||
min_amplitude = max(0.0, (1.0 - self.depth) * self.amplitude)
|
||||
step = (math.pi * 2) / period / self.rate
|
||||
step = (_math.pi * 2) / period / self.rate
|
||||
for i in range(total_bytes):
|
||||
value = math.sin(step * i)
|
||||
value = _math.sin(step * i)
|
||||
yield value * (max_amplitude - min_amplitude) + min_amplitude
|
||||
while True:
|
||||
yield 0
|
||||
@ -178,35 +178,51 @@ class TremoloEnvelope(_Envelope):
|
||||
|
||||
# Waveform generators
|
||||
|
||||
def sine_generator(frequency, sample_rate, offset=0):
|
||||
step = 2 * math.pi * frequency
|
||||
i = offset
|
||||
def silence_generator(frequency, sample_rate):
|
||||
while True:
|
||||
yield math.sin(step * i / sample_rate)
|
||||
yield 0
|
||||
|
||||
|
||||
def noise_generator(frequency, sample_rate):
|
||||
while True:
|
||||
yield _uniform(-1, 1)
|
||||
|
||||
|
||||
def sine_generator(frequency, sample_rate):
|
||||
step = 2 * _math.pi * frequency
|
||||
i = 0
|
||||
while True:
|
||||
yield _math.sin(step * i / sample_rate)
|
||||
i += 1
|
||||
|
||||
|
||||
# def triangle_generator(frequency, sample_rate, offset=0):
|
||||
# period_length = int(sample_rate / frequency)
|
||||
# half_period = period_length / 2
|
||||
# one_period = [1 / half_period * (half_period - abs(i - half_period) * 2 - 1) + 0.02
|
||||
# for i in range(period_length)]
|
||||
# return itertools.cycle(one_period)
|
||||
#
|
||||
#
|
||||
# def sawtooth_generator(frequency, sample_rate, offset=0):
|
||||
# i = offset
|
||||
# while True:
|
||||
# yield frequency * i * 2 - 1
|
||||
# i += 1 / sample_rate
|
||||
# if i > 1:
|
||||
# i = 0
|
||||
def triangle_generator(frequency, sample_rate):
|
||||
step = 4 * frequency / sample_rate
|
||||
value = 0
|
||||
while True:
|
||||
if value > 1:
|
||||
value = 1 - (value - 1)
|
||||
step = -step
|
||||
if value < -1:
|
||||
value = -1 - (value - -1)
|
||||
step = -step
|
||||
yield value
|
||||
value += step
|
||||
|
||||
|
||||
def pulse_generator(frequency, sample_rate, offset=0, duty_cycle=50):
|
||||
def sawtooth_generator(frequency, sample_rate):
|
||||
period_length = int(sample_rate / frequency)
|
||||
step = 2 * frequency / sample_rate
|
||||
i = 0
|
||||
while True:
|
||||
yield step * (i % period_length) - 1
|
||||
i += 1
|
||||
|
||||
|
||||
def pulse_generator(frequency, sample_rate, duty_cycle=50):
|
||||
period_length = int(sample_rate / frequency)
|
||||
duty_cycle = int(duty_cycle * period_length / 100)
|
||||
i = offset
|
||||
i = 0
|
||||
while True:
|
||||
yield int(i % period_length < duty_cycle) * 2 - 1
|
||||
i += 1
|
||||
@ -214,68 +230,11 @@ def pulse_generator(frequency, sample_rate, offset=0, duty_cycle=50):
|
||||
|
||||
# Source classes:
|
||||
|
||||
class SynthesisSource(Source):
|
||||
"""Base class for synthesized waveforms.
|
||||
|
||||
:Parameters:
|
||||
`duration` : float
|
||||
The length, in seconds, of audio that you wish to generate.
|
||||
`sample_rate` : int
|
||||
Audio samples per second. (CD quality is 44100).
|
||||
"""
|
||||
|
||||
def __init__(self, duration, sample_rate=44800, envelope=None):
|
||||
|
||||
self._duration = float(duration)
|
||||
self.audio_format = AudioFormat(channels=1, sample_size=16, sample_rate=sample_rate)
|
||||
|
||||
self._offset = 0
|
||||
self._sample_rate = sample_rate
|
||||
self._bytes_per_sample = 2
|
||||
self._bytes_per_second = self._bytes_per_sample * sample_rate
|
||||
self._max_offset = int(self._bytes_per_second * self._duration)
|
||||
self.envelope = envelope or FlatEnvelope(amplitude=1.0)
|
||||
self._envelope_generator = self.envelope.get_generator(sample_rate, duration)
|
||||
# Align to sample:
|
||||
self._max_offset &= 0xfffffffe
|
||||
|
||||
def get_audio_data(self, num_bytes, compensation_time=0.0):
|
||||
"""Return `num_bytes` bytes of audio data."""
|
||||
num_bytes = min(num_bytes, self._max_offset - self._offset)
|
||||
if num_bytes <= 0:
|
||||
return None
|
||||
|
||||
timestamp = float(self._offset) / self._bytes_per_second
|
||||
duration = float(num_bytes) / self._bytes_per_second
|
||||
data = self._generate_data(num_bytes)
|
||||
self._offset += num_bytes
|
||||
|
||||
return AudioData(data, num_bytes, timestamp, duration, [])
|
||||
|
||||
def _generate_data(self, num_bytes):
|
||||
"""Generate `num_bytes` bytes of data.
|
||||
|
||||
Return data as ctypes array or bytes.
|
||||
"""
|
||||
raise NotImplementedError('abstract')
|
||||
|
||||
def seek(self, timestamp):
|
||||
self._offset = int(timestamp * self._bytes_per_second)
|
||||
|
||||
# Bound within duration
|
||||
self._offset = min(max(self._offset, 0), self._max_offset)
|
||||
|
||||
# Align to sample
|
||||
self._offset &= 0xfffffffe
|
||||
|
||||
self._envelope_generator = self.envelope.get_generator(self._sample_rate, self._duration)
|
||||
|
||||
|
||||
class _SynthesisSource(Source):
|
||||
"""Base class for synthesized waveforms.
|
||||
|
||||
:Parameters:
|
||||
`generator` : generator
|
||||
`generator` : A non-instantiated generator object
|
||||
A waveform generator that produces a stream of numbers from (-1, 1)
|
||||
`duration` : float
|
||||
The length, in seconds, of audio that you wish to generate.
|
||||
@ -286,11 +245,11 @@ class _SynthesisSource(Source):
|
||||
`envelope` : :py:class:`pyglet.media.synthesis._Envelope`
|
||||
An optional Envelope to apply to the waveform.
|
||||
"""
|
||||
def __init__(self, generator, duration, frequency=440, sample_rate=44800, envelope=None):
|
||||
self._generator_function = generator
|
||||
self._generator = generator(frequency, sample_rate)
|
||||
self._duration = float(duration)
|
||||
def __init__(self, generator, duration, frequency, sample_rate, envelope=None):
|
||||
self._generator = generator
|
||||
self._duration = duration
|
||||
self._frequency = frequency
|
||||
|
||||
self.envelope = envelope or FlatEnvelope(amplitude=1.0)
|
||||
self._envelope_generator = self.envelope.get_generator(sample_rate, duration)
|
||||
|
||||
@ -300,7 +259,7 @@ class _SynthesisSource(Source):
|
||||
self._sample_rate = sample_rate
|
||||
self._bytes_per_sample = 2
|
||||
self._bytes_per_second = self._bytes_per_sample * sample_rate
|
||||
self._max_offset = int(self._bytes_per_second * self._duration)
|
||||
self._max_offset = int(self._bytes_per_second * duration)
|
||||
# Align to sample:
|
||||
self._max_offset &= 0xfffffffe
|
||||
|
||||
@ -312,9 +271,10 @@ class _SynthesisSource(Source):
|
||||
|
||||
timestamp = float(self._offset) / self._bytes_per_second
|
||||
duration = float(num_bytes) / self._bytes_per_second
|
||||
data = self._generate_data(num_bytes)
|
||||
self._offset += num_bytes
|
||||
|
||||
data = self._generate_data(num_bytes)
|
||||
|
||||
return AudioData(data, num_bytes, timestamp, duration, [])
|
||||
|
||||
def _generate_data(self, num_bytes):
|
||||
@ -323,7 +283,7 @@ class _SynthesisSource(Source):
|
||||
generator = self._generator
|
||||
envelope = self._envelope_generator
|
||||
data = (int(next(generator) * next(envelope) * amplitude) for _ in range(samples))
|
||||
return struct.pack(f"{samples}h", *data)
|
||||
return _struct.pack(f"{samples}h", *data)
|
||||
|
||||
def seek(self, timestamp):
|
||||
self._offset = int(timestamp * self._bytes_per_second)
|
||||
@ -333,115 +293,54 @@ class _SynthesisSource(Source):
|
||||
|
||||
# Align to sample
|
||||
self._offset &= 0xfffffffe
|
||||
|
||||
self._envelope_generator = self.envelope.get_generator(self._sample_rate, self._duration)
|
||||
self._generator = self._generator_function(self._frequency, self._sample_rate, self._offset)
|
||||
|
||||
|
||||
class Silence(SynthesisSource):
|
||||
"""A silent waveform."""
|
||||
|
||||
def _generate_data(self, num_bytes):
|
||||
return b'\0' * num_bytes
|
||||
class Silence(_SynthesisSource):
|
||||
def __init__(self, duration, frequency=440, sample_rate=44800, envelope=None):
|
||||
"""Create a Silent waveform."""
|
||||
super().__init__(silence_generator(frequency, sample_rate), duration, frequency, sample_rate, envelope)
|
||||
|
||||
|
||||
class WhiteNoise(SynthesisSource):
|
||||
"""A white noise, random waveform."""
|
||||
|
||||
def _generate_data(self, num_bytes):
|
||||
# TODO; use envelope
|
||||
return os.urandom(num_bytes)
|
||||
class WhiteNoise(_SynthesisSource):
|
||||
def __init__(self, duration, frequency=440, sample_rate=44800, envelope=None):
|
||||
"""Create a random white noise waveform."""
|
||||
super().__init__(noise_generator(frequency, sample_rate), duration, frequency, sample_rate, envelope)
|
||||
|
||||
|
||||
class Sine(_SynthesisSource):
|
||||
def __init__(self, duration, frequency=440, sample_rate=44800, envelope=None):
|
||||
"""Create a sinusoid (sine) waveform."""
|
||||
super().__init__(sine_generator, duration, frequency, sample_rate, envelope)
|
||||
super().__init__(sine_generator(frequency, sample_rate), duration, frequency, sample_rate, envelope)
|
||||
|
||||
|
||||
class Square(_SynthesisSource):
|
||||
def __init__(self, duration, frequency=440, sample_rate=44800, envelope=None):
|
||||
"""Create a Square (pulse) waveform."""
|
||||
super().__init__(pulse_generator, duration, frequency, sample_rate, envelope)
|
||||
super().__init__(pulse_generator(frequency, sample_rate), duration, frequency, sample_rate, envelope)
|
||||
|
||||
|
||||
class Triangle(SynthesisSource):
|
||||
"""A triangle waveform.
|
||||
|
||||
:Parameters:
|
||||
`duration` : float
|
||||
The length, in seconds, of audio that you wish to generate.
|
||||
`frequency` : int
|
||||
The frequency, in Hz of the waveform you wish to produce.
|
||||
`sample_rate` : int
|
||||
Audio samples per second. (CD quality is 44100).
|
||||
"""
|
||||
|
||||
def __init__(self, duration, frequency=440, **kwargs):
|
||||
super().__init__(duration, **kwargs)
|
||||
self.frequency = frequency
|
||||
|
||||
def _generate_data(self, num_bytes):
|
||||
samples = num_bytes >> 1
|
||||
value = 0
|
||||
maximum = 32767
|
||||
minimum = -32768
|
||||
data = (ctypes.c_short * samples)()
|
||||
step = (maximum - minimum) * 2 * self.frequency / self.audio_format.sample_rate
|
||||
envelope = self._envelope_generator
|
||||
for i in range(samples):
|
||||
value += step
|
||||
if value > maximum:
|
||||
value = maximum - (value - maximum)
|
||||
step = -step
|
||||
if value < minimum:
|
||||
value = minimum - (value - minimum)
|
||||
step = -step
|
||||
data[i] = int(value * next(envelope))
|
||||
return bytes(data)
|
||||
class Triangle(_SynthesisSource):
|
||||
def __init__(self, duration, frequency=440, sample_rate=44800, envelope=None):
|
||||
"""Create a Triangle waveform."""
|
||||
super().__init__(triangle_generator(frequency, sample_rate), duration, frequency, sample_rate, envelope)
|
||||
|
||||
|
||||
class Sawtooth(SynthesisSource):
|
||||
"""A sawtooth waveform.
|
||||
|
||||
:Parameters:
|
||||
`duration` : float
|
||||
The length, in seconds, of audio that you wish to generate.
|
||||
`frequency` : int
|
||||
The frequency, in Hz of the waveform you wish to produce.
|
||||
`sample_rate` : int
|
||||
Audio samples per second. (CD quality is 44100).
|
||||
"""
|
||||
|
||||
def __init__(self, duration, frequency=440, **kwargs):
|
||||
super().__init__(duration, **kwargs)
|
||||
self.frequency = frequency
|
||||
|
||||
def _generate_data(self, num_bytes):
|
||||
samples = num_bytes >> 1
|
||||
value = 0
|
||||
maximum = 32767
|
||||
minimum = -32768
|
||||
data = (ctypes.c_short * samples)()
|
||||
step = (maximum - minimum) * self.frequency / self._sample_rate
|
||||
envelope = self._envelope_generator
|
||||
for i in range(samples):
|
||||
value += step
|
||||
if value > maximum:
|
||||
value = minimum + (value % maximum)
|
||||
data[i] = int(value * next(envelope))
|
||||
return bytes(data)
|
||||
class Sawtooth(_SynthesisSource):
|
||||
def __init__(self, duration, frequency=440, sample_rate=44800, envelope=None):
|
||||
"""Create a Sawtooth waveform."""
|
||||
super().__init__(sawtooth_generator(frequency, sample_rate), duration, frequency, sample_rate, envelope)
|
||||
|
||||
|
||||
#############################################
|
||||
# Experimental multi-operator FM synthesis:
|
||||
#############################################
|
||||
|
||||
def operator(samplerate=44800, frequency=440, index=1, modulator=None, envelope=None):
|
||||
def sine_operator(samplerate=44800, frequency=440, index=1, modulator=None, envelope=None):
|
||||
# A sine generator that can be optionally modulated with another generator.
|
||||
# FM equation: sin((i * 2 * pi * carrier_frequency) + sin(i * 2 * pi * modulator_frequency))
|
||||
sin = math.sin
|
||||
step = 2 * math.pi * frequency / samplerate
|
||||
sin = _math.sin
|
||||
step = 2 * _math.pi * frequency / samplerate
|
||||
i = 0
|
||||
envelope = envelope or FlatEnvelope(1).get_generator(samplerate, duration=None)
|
||||
if modulator:
|
||||
@ -450,7 +349,7 @@ def operator(samplerate=44800, frequency=440, index=1, modulator=None, envelope=
|
||||
i += 1
|
||||
else:
|
||||
while True:
|
||||
yield math.sin(i * step) * next(envelope)
|
||||
yield _math.sin(i * step) * next(envelope)
|
||||
i += 1
|
||||
|
||||
|
||||
@ -458,20 +357,6 @@ def composite_operator(*operators):
|
||||
return (sum(samples) / len(samples) for samples in zip(*operators))
|
||||
|
||||
|
||||
class Encoder(SynthesisSource):
|
||||
def __init__(self, duration, operator, **kwargs):
|
||||
super().__init__(duration, **kwargs)
|
||||
self._operator = operator
|
||||
self._total = int(duration * self.audio_format.sample_rate)
|
||||
self._consumed = 0
|
||||
|
||||
def _generate_data(self, num_bytes):
|
||||
envelope = self._envelope_generator
|
||||
generator = self._operator
|
||||
|
||||
samples = num_bytes >> 1
|
||||
amplitude = 32767
|
||||
|
||||
data = (int(next(generator) * amplitude * next(envelope)) for i in range(samples))
|
||||
|
||||
return struct.pack(f"{samples}h", *data)
|
||||
class Encoder(_SynthesisSource):
|
||||
def __init__(self, operator, duration, frequency=440, sample_rate=44800, envelope=None):
|
||||
super().__init__(operator, duration, frequency, sample_rate, envelope)
|
||||
|
@ -37,6 +37,7 @@ import os
|
||||
import pyglet
|
||||
|
||||
from pyglet.gl import GL_TRIANGLES
|
||||
from pyglet.util import asstr
|
||||
|
||||
from .. import Model, Material, MaterialGroup, TexturedMaterialGroup
|
||||
from . import ModelDecodeException, ModelDecoder
|
||||
@ -121,17 +122,14 @@ def parse_obj_file(filename, file=None):
|
||||
|
||||
location = os.path.dirname(filename)
|
||||
|
||||
if file is None:
|
||||
with open(filename, 'r') as f:
|
||||
file_contents = f.read()
|
||||
else:
|
||||
file_contents = file.read()
|
||||
|
||||
if hasattr(file_contents, 'decode'):
|
||||
try:
|
||||
file_contents = file_contents.decode()
|
||||
except UnicodeDecodeError as e:
|
||||
raise ModelDecodeException("Unable to decode obj: {}".format(e))
|
||||
try:
|
||||
if file is None:
|
||||
with open(filename, 'r') as f:
|
||||
file_contents = f.read()
|
||||
else:
|
||||
file_contents = asstr(file.read())
|
||||
except (UnicodeDecodeError, OSError):
|
||||
raise ModelDecodeException
|
||||
|
||||
material = None
|
||||
mesh = None
|
||||
|
@ -943,6 +943,27 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
|
||||
def height(self, new_height):
|
||||
self.set_size(self.width, new_height)
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
"""The size of the window. Read-Write.
|
||||
|
||||
:type: tuple
|
||||
"""
|
||||
return self.get_size()
|
||||
|
||||
@size.setter
|
||||
def size(self, new_size):
|
||||
self.set_size(*new_size)
|
||||
|
||||
@property
|
||||
def aspect_ratio(self):
|
||||
"""The aspect ratio of the window. Read-Only.
|
||||
|
||||
:type: float
|
||||
"""
|
||||
w, h = self.get_size()
|
||||
return w / h
|
||||
|
||||
@property
|
||||
def projection(self):
|
||||
"""The OpenGL window projection matrix. Read-write.
|
||||
|
@ -56,9 +56,6 @@ class HeadlessWindow(BaseWindow):
|
||||
_egl_display_connection = None
|
||||
_egl_surface = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def _recreate(self, changes):
|
||||
pass
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user