ruaaa update!
This commit is contained in:
parent
2d2e2158a7
commit
9288423d54
@ -11,13 +11,8 @@ github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
from .command import CommandParseError, CommandQuotationMarkError
|
||||
|
||||
__all__ = ['TexturesError',
|
||||
'LanguageError',
|
||||
'CommandError',
|
||||
'CommandParseError',
|
||||
'CommandQuotationMarkError',
|
||||
'TestError']
|
||||
|
||||
|
||||
@ -31,11 +26,6 @@ class TexturesError(Error):
|
||||
pass
|
||||
|
||||
|
||||
class CommandError(Error):
|
||||
"""命令解析相关 error"""
|
||||
pass
|
||||
|
||||
|
||||
class LanguageError(Error):
|
||||
"""语言相关 error"""
|
||||
pass
|
||||
|
@ -11,16 +11,22 @@ github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
from . import CommandError
|
||||
from . import Error
|
||||
|
||||
|
||||
class CommandError(Error):
|
||||
"""命令解析相关 error"""
|
||||
|
||||
|
||||
class CommandParseError(CommandError):
|
||||
"""命令解析时出现错误"""
|
||||
pass
|
||||
|
||||
|
||||
class CommandQuotationMarkError(CommandParseError):
|
||||
class CommandQuotationMarkPositionError(CommandParseError):
|
||||
"""命令中,引号位置不正确
|
||||
例如: /command "aabcc "awdawd
|
||||
"""
|
||||
pass
|
||||
例如: /command "aabcc "awdawd"""
|
||||
|
||||
|
||||
class CommandQuotationMarkMissing(CommandParseError):
|
||||
"""命令中引号缺失
|
||||
例如: /command "aawwdawda awdaw """
|
||||
|
@ -15,18 +15,20 @@ gitee: @shenjackyuanjie
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import ctypes
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
# Difficult_Rocket function
|
||||
from Difficult_Rocket.api.Exp import *
|
||||
from Difficult_Rocket.command import line, tree
|
||||
from Difficult_Rocket.api import new_thread
|
||||
from Difficult_Rocket.utils.translate import tr
|
||||
from Difficult_Rocket.guis.widgets import InputBox
|
||||
# from Difficult_Rocket.client.screen import DRScreen
|
||||
from Difficult_Rocket.utils import tools, translate
|
||||
from Difficult_Rocket.api.Exp.command import CommandError
|
||||
from Difficult_Rocket.client.fps.fps_log import FpsLogger
|
||||
|
||||
# libs function
|
||||
@ -115,6 +117,7 @@ class ClientWindow(Window):
|
||||
# frame
|
||||
self.frame = pyglet.gui.Frame(self, order=20)
|
||||
self.M_frame = pyglet.gui.MovableFrame(self, modifier=key.LCTRL)
|
||||
# self.DRscreen = DRScreen(self)
|
||||
# setup
|
||||
self.setup()
|
||||
# 命令显示
|
||||
|
@ -7,19 +7,21 @@
|
||||
import ctypes
|
||||
|
||||
from Difficult_Rocket.client import ClientWindow
|
||||
from Difficult_Rocket.command.tree import CommandTree
|
||||
|
||||
|
||||
class BaseScreen:
|
||||
def __init__(self, main_window: ClientWindow):
|
||||
self.main_window_pointer = ctypes.pointer(main_window)
|
||||
def __init__(self, main_window: "ClientWindow"):
|
||||
self.command_tree = None
|
||||
self.create_command_tree()
|
||||
|
||||
def update(self, tick: float):
|
||||
pass
|
||||
|
||||
def create_command_tree(self):
|
||||
pass
|
||||
self.command_tree = CommandTree({})
|
||||
|
||||
|
||||
class DRScreen(BaseScreen):
|
||||
def __init__(self, main_window: ClientWindow):
|
||||
def __init__(self, main_window: "ClientWindow"):
|
||||
super().__init__(main_window)
|
||||
|
@ -14,10 +14,10 @@ gitee: @shenjackyuanjie
|
||||
# system function
|
||||
import re
|
||||
|
||||
from typing import Union
|
||||
from typing import Union, Optional, Type, Tuple
|
||||
|
||||
# DR
|
||||
from Difficult_Rocket.api.Exp import CommandQuotationMarkError
|
||||
from Difficult_Rocket.api.Exp.command import *
|
||||
|
||||
search_re = re.compile(r'(?<!\\)"')
|
||||
|
||||
@ -36,15 +36,16 @@ class CommandText:
|
||||
self.command_tree = {}
|
||||
tree_list = text.split(' ')
|
||||
|
||||
pass_node = False
|
||||
for node in tree_list:
|
||||
if node[0] == "\"" and len(node) > 1: # |"xxxxx|
|
||||
if pass_node: # |"xxxx "xxxx|
|
||||
self.error = CommandQuotationMarkError
|
||||
pass_node = True
|
||||
first_node = tree_list.index(node)
|
||||
if node[-1] == "\"" and len(node) > 1: # |xxxxxx"|
|
||||
pass_node = False
|
||||
self.tree_node = tree_list
|
||||
|
||||
@staticmethod
|
||||
def parse_command(raw_command: Union[str, "CommandText"]) -> Tuple[list, Optional[Type[CommandParseError]]]:
|
||||
spilt_list = str(raw_command).split(" ")
|
||||
|
||||
for spilted in spilt_list:
|
||||
pass
|
||||
|
||||
return spilt_list, CommandQuotationMarkPositionError
|
||||
|
||||
def find(self, text: str) -> Union[str, bool]:
|
||||
finding = re.match(text, self.text)
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
- 将 `api/Exp` 改为文件夹 `api/Exp/__init__.py`
|
||||
- 再次重写了 `client.load_fonts()` 现在改为直接加载单独的 `pyglet_load_fonts_folder()`
|
||||
- 更新了 `command/` 里的一大堆东西
|
||||
- 退钱!开摆!
|
||||
|
||||
|
||||
|
||||
|
@ -200,7 +200,7 @@ class GlyphTextureAtlas(image.atlas.TextureAtlas):
|
||||
texture_class = GlyphTexture
|
||||
|
||||
def __init__(self, width=2048, height=2048, fmt=GL_RGBA, min_filter=GL_LINEAR, mag_filter=GL_LINEAR):
|
||||
self.texture = self.texture_class.create(width, height, GL_TEXTURE_2D, fmt, min_filter, mag_filter)
|
||||
self.texture = self.texture_class.create(width, height, GL_TEXTURE_2D, fmt, min_filter, mag_filter, fmt=fmt)
|
||||
self.allocator = image.atlas.Allocator(width, height)
|
||||
|
||||
|
||||
@ -306,7 +306,7 @@ class Font:
|
||||
"""
|
||||
return True
|
||||
|
||||
def create_glyph(self, image):
|
||||
def create_glyph(self, image, fmt=None):
|
||||
"""Create a glyph using the given image.
|
||||
|
||||
This is used internally by `Font` subclasses to add glyph data
|
||||
@ -320,6 +320,8 @@ class Font:
|
||||
:Parameters:
|
||||
`image` : `pyglet.image.AbstractImage`
|
||||
The image to write to the font texture.
|
||||
`fmt` : `int`
|
||||
Override for the format and internalformat of the atlas texture
|
||||
|
||||
:rtype: `Glyph`
|
||||
"""
|
||||
@ -329,7 +331,7 @@ class Font:
|
||||
self.texture_bin = GlyphTextureBin(self.texture_width, self.texture_height)
|
||||
|
||||
glyph = self.texture_bin.add(
|
||||
image, self.texture_internalformat, self.texture_min_filter, self.texture_mag_filter, border=1)
|
||||
image, fmt or self.texture_internalformat, self.texture_min_filter, self.texture_mag_filter, border=1)
|
||||
|
||||
return glyph
|
||||
|
||||
|
@ -32,10 +32,17 @@
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ----------------------------------------------------------------------------
|
||||
from enum import Enum
|
||||
|
||||
from pyglet import gl
|
||||
from pyglet.gl import gl_info
|
||||
|
||||
|
||||
class OpenGLAPI(Enum):
|
||||
OPENGL = 1
|
||||
OPENGL_ES = 2
|
||||
|
||||
|
||||
class Config:
|
||||
"""Graphics configuration.
|
||||
|
||||
@ -106,12 +113,14 @@ class Config:
|
||||
'major_version',
|
||||
'minor_version',
|
||||
'forward_compatible',
|
||||
'opengl_api',
|
||||
'debug'
|
||||
]
|
||||
|
||||
major_version = None
|
||||
minor_version = None
|
||||
forward_compatible = None
|
||||
opengl_api = None
|
||||
debug = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@ -211,6 +220,7 @@ class CanvasConfig(Config):
|
||||
self.major_version = base_config.major_version
|
||||
self.minor_version = base_config.minor_version
|
||||
self.forward_compatible = base_config.forward_compatible
|
||||
self.opengl_api = base_config.opengl_api
|
||||
self.debug = base_config.debug
|
||||
|
||||
def compatible(self, canvas):
|
||||
|
@ -56,10 +56,11 @@ context::
|
||||
|
||||
"""
|
||||
|
||||
from ctypes import c_char_p, cast
|
||||
from ctypes import c_char_p, cast, c_int
|
||||
import warnings
|
||||
|
||||
from pyglet.gl.gl import GL_EXTENSIONS, GL_RENDERER, GL_VENDOR, GL_VERSION
|
||||
from pyglet.gl.gl import (GL_EXTENSIONS, GL_RENDERER, GL_VENDOR,
|
||||
GL_VERSION, GL_MAJOR_VERSION, GL_MINOR_VERSION)
|
||||
from pyglet.util import asstr
|
||||
|
||||
|
||||
@ -92,7 +93,16 @@ class GLInfo:
|
||||
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)
|
||||
@ -132,7 +142,17 @@ class GLInfo:
|
||||
def get_version(self):
|
||||
"""Get the current OpenGL version.
|
||||
|
||||
:return: the OpenGL version
|
||||
:return: The major and minor version as a tuple
|
||||
:rtype: tuple
|
||||
"""
|
||||
if not self.have_context:
|
||||
warnings.warn('No GL context created yet.')
|
||||
return self.major_version, self.minor_version
|
||||
|
||||
def get_version_string(self):
|
||||
"""Get the current OpenGL version string.
|
||||
|
||||
:return: The OpenGL version string
|
||||
:rtype: str
|
||||
"""
|
||||
if not self.have_context:
|
||||
@ -154,13 +174,12 @@ class GLInfo:
|
||||
|
||||
if not self.have_context:
|
||||
warnings.warn('No GL context created yet.')
|
||||
if not self.version or 'None' in self.version:
|
||||
if not self.major_version and not self.minor_version:
|
||||
return False
|
||||
ver = '%s.0' % self.version.split(' ', 1)[0]
|
||||
imajor, iminor = [int(v) for v in ver.split('.', 3)[:2]]
|
||||
return (imajor > major or
|
||||
(imajor == major and iminor >= minor) or
|
||||
(imajor == major and iminor == minor))
|
||||
|
||||
return (self.major_version > major or
|
||||
(self.major_version == major and self.minor_version >= minor) or
|
||||
(self.major_version == major and self.minor_version == minor))
|
||||
|
||||
def get_renderer(self):
|
||||
"""Determine the renderer string of the OpenGL context.
|
||||
@ -180,6 +199,16 @@ class GLInfo:
|
||||
warnings.warn('No GL context created yet.')
|
||||
return self.vendor
|
||||
|
||||
def get_opengl_api(self):
|
||||
"""Determine the OpenGL API version.
|
||||
Usually ``gl`` or ``gles``.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
if not self.have_context:
|
||||
warnings.warn('No GL context created yet.')
|
||||
return self.opengl_api
|
||||
|
||||
|
||||
# Single instance useful for apps with only a single context
|
||||
# (or all contexts have the same GL driver, a common case).
|
||||
@ -190,10 +219,11 @@ 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
|
||||
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.
|
||||
|
@ -36,12 +36,12 @@
|
||||
import warnings
|
||||
from ctypes import *
|
||||
|
||||
from .base import Config, CanvasConfig, Context
|
||||
from pyglet import gl
|
||||
from pyglet.canvas.headless import HeadlessCanvas
|
||||
from pyglet.libs.egl import egl
|
||||
from pyglet.libs.egl.egl import *
|
||||
from pyglet import gl
|
||||
|
||||
from .base import CanvasConfig, Config, Context
|
||||
|
||||
_fake_gl_attributes = {
|
||||
'double_buffer': 0,
|
||||
@ -69,7 +69,10 @@ class HeadlessConfig(Config):
|
||||
if attr and value is not None:
|
||||
attrs.extend([attr, int(value)])
|
||||
attrs.extend([EGL_SURFACE_TYPE, EGL_PBUFFER_BIT])
|
||||
attrs.extend([EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT])
|
||||
if self.opengl_api == "gl":
|
||||
attrs.extend([EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT])
|
||||
elif self.opengl_api == "gles":
|
||||
attrs.extend([EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT])
|
||||
attrs.extend([EGL_NONE])
|
||||
attrs_list = (egl.EGLint * len(attrs))(*attrs)
|
||||
|
||||
@ -139,7 +142,10 @@ class HeadlessContext(Context):
|
||||
else:
|
||||
share_context = None
|
||||
|
||||
egl.eglBindAPI(egl.EGL_OPENGL_API)
|
||||
if self.config.opengl_api == "gl":
|
||||
egl.eglBindAPI(egl.EGL_OPENGL_API)
|
||||
elif self.config.opengl_api == "gles":
|
||||
egl.eglBindAPI(egl.EGL_OPENGL_ES_API)
|
||||
return egl.eglCreateContext(self.config.canvas.display._display_connection,
|
||||
self.config._egl_config, share_context,
|
||||
self.config._context_attrib_array)
|
||||
|
@ -36,7 +36,7 @@
|
||||
import warnings
|
||||
from ctypes import *
|
||||
|
||||
from .base import Config, CanvasConfig, Context
|
||||
from .base import Config, CanvasConfig, Context, OpenGLAPI
|
||||
from pyglet.canvas.xlib import XlibCanvas
|
||||
from pyglet.gl import glx
|
||||
from pyglet.gl import glxext_arb
|
||||
@ -390,11 +390,18 @@ class XlibContextARB(XlibContext13):
|
||||
if self.config.minor_version is not None:
|
||||
attribs.extend([glxext_arb.GLX_CONTEXT_MINOR_VERSION_ARB,
|
||||
self.config.minor_version])
|
||||
|
||||
if self.config.opengl_api == "gl":
|
||||
attribs.extend([glxext_arb.GLX_CONTEXT_PROFILE_MASK_ARB, glxext_arb.GLX_CONTEXT_CORE_PROFILE_BIT_ARB])
|
||||
elif self.config.opengl_api == "gles":
|
||||
attribs.extend([glxext_arb.GLX_CONTEXT_PROFILE_MASK_ARB, glxext_arb.GLX_CONTEXT_ES2_PROFILE_BIT_EXT])
|
||||
|
||||
flags = 0
|
||||
if self.config.forward_compatible:
|
||||
flags |= glxext_arb.GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
|
||||
if self.config.debug:
|
||||
flags |= glxext_arb.GLX_CONTEXT_DEBUG_BIT_ARB
|
||||
|
||||
if flags:
|
||||
attribs.extend([glxext_arb.GLX_CONTEXT_FLAGS_ARB, flags])
|
||||
attribs.append(0)
|
||||
|
@ -1,3 +1,4 @@
|
||||
from typing import Dict, List
|
||||
from ctypes import *
|
||||
from weakref import proxy
|
||||
|
||||
@ -201,7 +202,7 @@ class Shader:
|
||||
raise TypeError("The `shader_type` '{}' is not yet supported".format(shader_type))
|
||||
self.type = shader_type
|
||||
|
||||
source_string = source_string.strip()
|
||||
source_string = ShaderSource(source_string, _shader_types[shader_type]).validate()
|
||||
shader_source_utf8 = source_string.encode("utf8")
|
||||
source_buffer_pointer = cast(c_char_p(shader_source_utf8), POINTER(c_char))
|
||||
source_length = c_int(len(shader_source_utf8))
|
||||
@ -697,3 +698,61 @@ 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}"
|
||||
)
|
||||
)
|
||||
|
@ -241,7 +241,7 @@ class BufferObject(AbstractBuffer):
|
||||
temp = (ctypes.c_byte * size)()
|
||||
|
||||
glBindBuffer(self.target, self.id)
|
||||
data = glMapBuffer(self.target, GL_READ_ONLY)
|
||||
data = glMapBufferRange(self.target, 0, self.size, GL_MAP_READ_BIT)
|
||||
ctypes.memmove(temp, data, min(size, self.size))
|
||||
glUnmapBuffer(self.target)
|
||||
|
||||
|
@ -210,7 +210,7 @@ class PushButton(WidgetBase):
|
||||
self._pressed = False
|
||||
|
||||
def _update_position(self):
|
||||
self._sprite.position = self._x, self._y
|
||||
self._sprite.position = self._x, self._y, 0
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
|
@ -1249,7 +1249,7 @@ class Texture(AbstractImage):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def create(cls, width, height, target=GL_TEXTURE_2D, internalformat=GL_RGBA, min_filter=None, mag_filter=None):
|
||||
def create(cls, width, height, target=GL_TEXTURE_2D, internalformat=GL_RGBA8, min_filter=None, mag_filter=None, fmt=GL_RGBA):
|
||||
"""Create a Texture
|
||||
|
||||
Create a Texture with the specified dimentions, target and format.
|
||||
@ -1264,11 +1264,16 @@ class Texture(AbstractImage):
|
||||
GL constant giving texture target to use, typically ``GL_TEXTURE_2D``.
|
||||
`internalformat` : int
|
||||
GL constant giving internal format of texture; for example, ``GL_RGBA``.
|
||||
The internal format decides how the texture data will be stored internally.
|
||||
If ``None``, the texture will be created but not initialized.
|
||||
`min_filter` : int
|
||||
The minifaction filter used for this texture, commonly ``GL_LINEAR`` or ``GL_NEAREST``
|
||||
`mag_filter` : int
|
||||
The magnification filter used for this texture, commonly ``GL_LINEAR`` or ``GL_NEAREST``
|
||||
`fmt` : int
|
||||
GL constant giving format of texture; for example, ``GL_RGBA``.
|
||||
The format specifies what format the pixel data we're expecting to write
|
||||
to the texture and should ideally be the same as for internal format.
|
||||
|
||||
:rtype: :py:class:`~pyglet.image.Texture`
|
||||
"""
|
||||
@ -1287,7 +1292,8 @@ class Texture(AbstractImage):
|
||||
internalformat,
|
||||
width, height,
|
||||
0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
fmt,
|
||||
GL_UNSIGNED_BYTE,
|
||||
blank)
|
||||
glFlush()
|
||||
|
||||
@ -1320,9 +1326,22 @@ class Texture(AbstractImage):
|
||||
fmt = 'RGBA'
|
||||
gl_format = GL_RGBA
|
||||
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1)
|
||||
buf = (GLubyte * (self.width * self.height * self.images * len(fmt)))()
|
||||
glGetTexImage(self.target, self.level, gl_format, GL_UNSIGNED_BYTE, buf)
|
||||
|
||||
# TODO: Clean up this temporary hack
|
||||
if gl.current_context.get_info().get_opengl_api() == "gles":
|
||||
fbo = c_uint()
|
||||
glGenFramebuffers(1, fbo)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo.value)
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1)
|
||||
glCheckFramebufferStatus(GL_FRAMEBUFFER)
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.id, self.level)
|
||||
glReadPixels(0, 0, self.width, self.height, gl_format, GL_UNSIGNED_BYTE, buf)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0)
|
||||
glDeleteFramebuffers(1, fbo)
|
||||
else:
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1)
|
||||
glGetTexImage(self.target, self.level, gl_format, GL_UNSIGNED_BYTE, buf)
|
||||
|
||||
data = ImageData(self.width, self.height, fmt, buf)
|
||||
if self.images > 1:
|
||||
|
@ -211,4 +211,4 @@ else:
|
||||
from .darwin_hid import get_joysticks
|
||||
from .darwin_hid import get_apple_remote
|
||||
from .darwin_hid import get_controllers
|
||||
# TODO: create ControllerManager for OSX
|
||||
from .darwin_hid import DarwinControllerManager as ControllerManager
|
||||
|
@ -39,12 +39,13 @@ from ctypes import CFUNCTYPE, byref, c_void_p, c_int, c_ubyte, c_bool, c_uint32,
|
||||
|
||||
from .controller import get_mapping
|
||||
from .base import Device, AbsoluteAxis, RelativeAxis, Button
|
||||
from .base import Joystick, Controller, AppleRemote
|
||||
from .base import Joystick, Controller, AppleRemote, ControllerManager
|
||||
|
||||
from pyglet.libs.darwin.cocoapy import CFSTR, CFIndex, CFTypeID, known_cftypes
|
||||
from pyglet.libs.darwin.cocoapy import kCFRunLoopDefaultMode, CFAllocatorRef, cf
|
||||
from pyglet.libs.darwin.cocoapy import cfset_to_set, cftype_to_value, cfarray_to_list
|
||||
from pyglet.lib import load_library
|
||||
from pyglet.event import EventDispatcher
|
||||
|
||||
|
||||
__LP64__ = (sys.maxsize > 2 ** 32)
|
||||
@ -465,7 +466,7 @@ class HIDDeviceElement:
|
||||
self.physicalMax = iokit.IOHIDElementGetPhysicalMax(elementRef)
|
||||
|
||||
|
||||
class HIDManager:
|
||||
class HIDManager(EventDispatcher):
|
||||
def __init__(self):
|
||||
# Create the HID Manager.
|
||||
self.managerRef = c_void_p(iokit.IOHIDManagerCreate(None, kIOHIDOptionsTypeNone))
|
||||
@ -521,6 +522,10 @@ class HIDManager:
|
||||
return matching_callback
|
||||
|
||||
|
||||
HIDManager.register_event_type('on_connect')
|
||||
HIDManager.register_event_type('on_disconnect')
|
||||
|
||||
|
||||
######################################################################
|
||||
# Add conversion methods for IOHIDDevices and IOHIDDeviceElements
|
||||
# to the list of known types used by cftype_to_value.
|
||||
@ -565,11 +570,12 @@ _button_names = {
|
||||
|
||||
class PygletDevice(Device):
|
||||
def __init__(self, display, device, manager):
|
||||
super(PygletDevice, self).__init__(display=display, name=device.product)
|
||||
super().__init__(display=display, name=device.product)
|
||||
self.device = device
|
||||
self.device_identifier = self.device.unique_identifier()
|
||||
self.device.add_value_observer(self)
|
||||
self.device.add_removal_observer(self)
|
||||
self._manager = manager
|
||||
manager.matching_observers.add(self)
|
||||
self._create_controls()
|
||||
self._is_open = False
|
||||
@ -619,6 +625,7 @@ class PygletDevice(Device):
|
||||
# Set device to None, but Keep self._controls around
|
||||
# in case device is plugged back in.
|
||||
self.device = None
|
||||
self._manager.dispatch_event('on_disconnect', self)
|
||||
|
||||
def device_discovered(self, hid_device):
|
||||
# Called by HID manager when new device is found.
|
||||
@ -632,6 +639,7 @@ class PygletDevice(Device):
|
||||
if self._is_open:
|
||||
self.device.open(self._is_exclusive)
|
||||
self.device.schedule_with_run_loop()
|
||||
self._manager.dispatch_event('on_connect', self)
|
||||
|
||||
def device_value_changed(self, hid_device, hid_value):
|
||||
# Called by device when input value changes.
|
||||
@ -671,32 +679,55 @@ class PygletDevice(Device):
|
||||
######################################################################
|
||||
|
||||
|
||||
_manager = HIDManager()
|
||||
_hid_manager = HIDManager()
|
||||
|
||||
|
||||
class DarwinControllerManager(ControllerManager):
|
||||
|
||||
def __init__(self, display=None):
|
||||
self._display = display
|
||||
self._controllers = {}
|
||||
|
||||
for device in _hid_manager.devices:
|
||||
controller = _create_controller(device, display)
|
||||
if controller:
|
||||
self._controllers[device] = controller
|
||||
|
||||
@_hid_manager.event
|
||||
def on_connect(hiddevice):
|
||||
self.dispatch_event('on_connect', self._controllers[hiddevice])
|
||||
|
||||
@_hid_manager.event
|
||||
def on_disconnect(hiddevice):
|
||||
self.dispatch_event('on_disconnect', self._controllers[hiddevice])
|
||||
|
||||
def get_controllers(self):
|
||||
pass
|
||||
|
||||
|
||||
def get_devices(display=None):
|
||||
return [PygletDevice(display, device, _manager) for device in _manager.devices]
|
||||
return [PygletDevice(display, device, _hid_manager) for device in _hid_manager.devices]
|
||||
|
||||
|
||||
def get_joysticks(display=None):
|
||||
return [Joystick(PygletDevice(display, device, _manager)) for device in _manager.devices
|
||||
return [Joystick(PygletDevice(display, device, _hid_manager)) for device in _hid_manager.devices
|
||||
if device.is_joystick() or device.is_gamepad() or device.is_multi_axis()]
|
||||
|
||||
|
||||
def get_apple_remote(display=None):
|
||||
for device in _manager.devices:
|
||||
for device in _hid_manager.devices:
|
||||
if device.product == 'Apple IR':
|
||||
return AppleRemote(PygletDevice(display, device, _manager))
|
||||
return AppleRemote(PygletDevice(display, device, _hid_manager))
|
||||
|
||||
|
||||
def _create_controller(device, display):
|
||||
mapping = get_mapping(device.get_guid())
|
||||
if not mapping:
|
||||
return
|
||||
return Controller(PygletDevice(display, device, _manager), mapping)
|
||||
return Controller(PygletDevice(display, device, _hid_manager), mapping)
|
||||
|
||||
|
||||
def get_controllers(display=None):
|
||||
return [controller for controller in
|
||||
[_create_controller(device, display) for device in _manager.devices]
|
||||
[_create_controller(device, display) for device in _hid_manager.devices]
|
||||
if controller is not None]
|
||||
|
@ -56,7 +56,7 @@ class FlatEnvelope(_Envelope):
|
||||
:Parameters:
|
||||
`amplitude` : float
|
||||
The amplitude (volume) of the wave, from 0.0 to 1.0.
|
||||
Values outside of this range will be clamped.
|
||||
Values outside this range will be clamped.
|
||||
"""
|
||||
|
||||
def __init__(self, amplitude=0.5):
|
||||
@ -77,7 +77,7 @@ class LinearDecayEnvelope(_Envelope):
|
||||
:Parameters:
|
||||
`peak` : float
|
||||
The Initial peak value of the envelope, from 0.0 to 1.0.
|
||||
Values outside of this range will be clamped.
|
||||
Values outside this range will be clamped.
|
||||
"""
|
||||
|
||||
def __init__(self, peak=1.0):
|
||||
@ -176,6 +176,42 @@ class TremoloEnvelope(_Envelope):
|
||||
yield 0
|
||||
|
||||
|
||||
# Waveform generators
|
||||
|
||||
def sine_generator(frequency, sample_rate, offset=0):
|
||||
step = 2 * math.pi * frequency
|
||||
i = offset
|
||||
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 pulse_generator(frequency, sample_rate, offset=0, duty_cycle=50):
|
||||
period_length = int(sample_rate / frequency)
|
||||
duty_cycle = int(duty_cycle * period_length / 100)
|
||||
i = offset
|
||||
while True:
|
||||
yield int(i % period_length < duty_cycle) * 2 - 1
|
||||
i += 1
|
||||
|
||||
|
||||
# Source classes:
|
||||
|
||||
class SynthesisSource(Source):
|
||||
@ -235,6 +271,73 @@ class SynthesisSource(Source):
|
||||
self._envelope_generator = self.envelope.get_generator(self._sample_rate, self._duration)
|
||||
|
||||
|
||||
class _SynthesisSource(Source):
|
||||
"""Base class for synthesized waveforms.
|
||||
|
||||
:Parameters:
|
||||
`generator` : generator
|
||||
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.
|
||||
`frequency` : float
|
||||
The frequency, in Hz, of the waveform you wish to generate.
|
||||
`sample_rate` : int
|
||||
Audio samples per second. (CD quality is 44100).
|
||||
`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)
|
||||
self._frequency = frequency
|
||||
self.envelope = envelope or FlatEnvelope(amplitude=1.0)
|
||||
self._envelope_generator = self.envelope.get_generator(sample_rate, 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)
|
||||
# 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):
|
||||
samples = num_bytes >> 1
|
||||
amplitude = 32767
|
||||
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)
|
||||
|
||||
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)
|
||||
self._generator = self._generator_function(self._frequency, self._sample_rate, self._offset)
|
||||
|
||||
|
||||
class Silence(SynthesisSource):
|
||||
"""A silent waveform."""
|
||||
|
||||
@ -246,34 +349,20 @@ class WhiteNoise(SynthesisSource):
|
||||
"""A white noise, random waveform."""
|
||||
|
||||
def _generate_data(self, num_bytes):
|
||||
# TODO; use envelope
|
||||
return os.urandom(num_bytes)
|
||||
|
||||
|
||||
class Sine(SynthesisSource):
|
||||
"""A sinusoid (sine) waveform.
|
||||
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)
|
||||
|
||||
: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
|
||||
amplitude = 32767
|
||||
data = (ctypes.c_short * samples)()
|
||||
step = self.frequency * (math.pi * 2) / self.audio_format.sample_rate
|
||||
envelope = self._envelope_generator
|
||||
for i in range(samples):
|
||||
data[i] = int(math.sin(step * i) * amplitude * next(envelope))
|
||||
return bytes(data)
|
||||
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)
|
||||
|
||||
|
||||
class Triangle(SynthesisSource):
|
||||
@ -344,88 +433,10 @@ class Sawtooth(SynthesisSource):
|
||||
return bytes(data)
|
||||
|
||||
|
||||
class Square(SynthesisSource):
|
||||
"""A square (pulse) 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
|
||||
amplitude = 32767
|
||||
value = 1
|
||||
count = 0
|
||||
data = (ctypes.c_short * samples)()
|
||||
half_period = self.audio_format.sample_rate / self.frequency / 2
|
||||
envelope = self._envelope_generator
|
||||
for i in range(samples):
|
||||
if count >= half_period:
|
||||
value = -value
|
||||
count %= half_period
|
||||
count += 1
|
||||
data[i] = int(value * amplitude * next(envelope))
|
||||
return bytes(data)
|
||||
|
||||
|
||||
class SimpleFM(SynthesisSource):
|
||||
"""A simple FM waveform.
|
||||
|
||||
This is a simplistic frequency modulated waveform, based on the
|
||||
concepts by John Chowning. Basic sine waves are used for both
|
||||
frequency carrier and modulator inputs, of which the frequencies can
|
||||
be provided. The modulation index, or amplitude, can also be adjusted.
|
||||
|
||||
:Parameters:
|
||||
`duration` : float
|
||||
The length, in seconds, of audio that you wish to generate.
|
||||
`carrier` : int
|
||||
The carrier frequency, in Hz.
|
||||
`modulator` : int
|
||||
The modulator frequency, in Hz.
|
||||
`mod_index` : int
|
||||
The modulation index.
|
||||
`sample_rate` : int
|
||||
Audio samples per second. (CD quality is 44100).
|
||||
"""
|
||||
|
||||
def __init__(self, duration, carrier=440, modulator=440, mod_index=1, **kwargs):
|
||||
super().__init__(duration, **kwargs)
|
||||
self.carrier = carrier
|
||||
self.modulator = modulator
|
||||
self.mod_index = mod_index
|
||||
|
||||
def _generate_data(self, num_bytes):
|
||||
samples = num_bytes >> 1
|
||||
amplitude = 32767
|
||||
c_step = 2 * math.pi * self.carrier
|
||||
m_step = 2 * math.pi * self.modulator
|
||||
m_index = self.mod_index
|
||||
sample_rate = self._sample_rate
|
||||
envelope = self._envelope_generator
|
||||
sin = math.sin
|
||||
# FM equation: sin((2 * pi * carrier) + sin(2 * pi * modulator))
|
||||
data = []
|
||||
for i in range(samples):
|
||||
increment = i / sample_rate
|
||||
data.append(int(sin(c_step * increment + m_index * sin(m_step * increment)) * amplitude * next(envelope)))
|
||||
return struct.pack(str(samples) + 'h', *data)
|
||||
|
||||
|
||||
#############################################
|
||||
# Experimental multi-operator FM synthesis:
|
||||
#############################################
|
||||
|
||||
|
||||
def 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))
|
||||
@ -443,20 +454,20 @@ def operator(samplerate=44800, frequency=440, index=1, modulator=None, envelope=
|
||||
i += 1
|
||||
|
||||
|
||||
def composite_generator(*operators):
|
||||
def composite_operator(*operators):
|
||||
return (sum(samples) / len(samples) for samples in zip(*operators))
|
||||
|
||||
|
||||
class Encoder(SynthesisSource):
|
||||
def __init__(self, duration, generator, **kwargs):
|
||||
def __init__(self, duration, operator, **kwargs):
|
||||
super().__init__(duration, **kwargs)
|
||||
self._generator = generator
|
||||
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._generator
|
||||
generator = self._operator
|
||||
|
||||
samples = num_bytes >> 1
|
||||
amplitude = 32767
|
||||
|
@ -564,7 +564,7 @@ layout_fragment_source = """#version 330 core
|
||||
out vec4 final_colors;
|
||||
|
||||
uniform sampler2D text;
|
||||
uniform bool scissor = false;
|
||||
uniform bool scissor;
|
||||
uniform vec4 scissor_area;
|
||||
|
||||
void main()
|
||||
@ -608,7 +608,7 @@ decoration_fragment_source = """#version 330 core
|
||||
|
||||
out vec4 final_colors;
|
||||
|
||||
uniform bool scissor = false;
|
||||
uniform bool scissor;
|
||||
uniform vec4 scissor_area;
|
||||
|
||||
void main()
|
||||
@ -628,10 +628,10 @@ def get_default_layout_shader():
|
||||
try:
|
||||
return pyglet.gl.current_context.pyglet_text_layout_shader
|
||||
except AttributeError:
|
||||
_default_vert_shader = shader.Shader(layout_vertex_source, 'vertex')
|
||||
_default_frag_shader = shader.Shader(layout_fragment_source, 'fragment')
|
||||
default_shader_program = shader.ShaderProgram(_default_vert_shader, _default_frag_shader)
|
||||
pyglet.gl.current_context.pyglet_text_layout_shader = default_shader_program
|
||||
pyglet.gl.current_context.pyglet_text_layout_shader = shader.ShaderProgram(
|
||||
shader.Shader(layout_vertex_source, 'vertex'),
|
||||
shader.Shader(layout_fragment_source, 'fragment'),
|
||||
)
|
||||
return pyglet.gl.current_context.pyglet_text_layout_shader
|
||||
|
||||
|
||||
@ -639,10 +639,10 @@ def get_default_decoration_shader():
|
||||
try:
|
||||
return pyglet.gl.current_context.pyglet_text_decoration_shader
|
||||
except AttributeError:
|
||||
_default_vert_shader = shader.Shader(decoration_vertex_source, 'vertex')
|
||||
_default_frag_shader = shader.Shader(decoration_fragment_source, 'fragment')
|
||||
default_shader_program = shader.ShaderProgram(_default_vert_shader, _default_frag_shader)
|
||||
pyglet.gl.current_context.pyglet_text_decoration_shader = default_shader_program
|
||||
pyglet.gl.current_context.pyglet_text_decoration_shader = shader.ShaderProgram(
|
||||
shader.Shader(decoration_vertex_source, 'vertex'),
|
||||
shader.Shader(decoration_fragment_source, 'fragment'),
|
||||
)
|
||||
return pyglet.gl.current_context.pyglet_text_decoration_shader
|
||||
|
||||
|
||||
|
@ -456,12 +456,14 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
|
||||
}
|
||||
"""
|
||||
_default_fragment_source = """#version 150 core
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
fragColor = vec4(1.0);
|
||||
}
|
||||
out vec4 color;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
width=None,
|
||||
height=None,
|
||||
@ -619,8 +621,8 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
|
||||
def _create_projection(self):
|
||||
self._default_program = shader.ShaderProgram(
|
||||
shader.Shader(self._default_vertex_source, 'vertex'),
|
||||
shader.Shader(self._default_fragment_source, 'fragment'),
|
||||
)
|
||||
shader.Shader(self._default_fragment_source, 'fragment'))
|
||||
|
||||
self.ubo = self._default_program.uniform_blocks['WindowBlock'].create_ubo()
|
||||
|
||||
self._viewport = 0, 0, *self.get_framebuffer_size()
|
||||
|
Loading…
Reference in New Issue
Block a user