update pyglet

fix crash
This commit is contained in:
shenjackyuanjie 2022-03-22 23:20:07 +08:00
parent dc8ee0b4a5
commit 8c96116afb
14 changed files with 395 additions and 306 deletions

9
DR.py
View File

@ -38,14 +38,17 @@ if __name__ == '__main__':
print(sys.path)
print(hi)
DEBUGGING = False
DEBUGGING = True
from Difficult_Rocket.api.Exp import *
from Difficult_Rocket.crash import crash
try:
from Difficult_Rocket import main
game = main.Game()
from libs.pyglet.gl import glClearColor
glClearColor(0, 0, 0, 0)
# from libs.pyglet.gl import glClearColor
# glClearColor(0, 0, 0, 0)
cprofile = False
if cprofile:
cProfile.run('game.start()', sort='calls')

View File

@ -173,7 +173,7 @@ class ClientWindow(Window):
def start_game(self) -> None:
self.run_input = True
self.read_input()
# self.read_input()
pyglet.app.run()
@new_thread('window read_input', daemon=True)
@ -193,11 +193,11 @@ class ClientWindow(Window):
@new_thread('window save_info')
def save_info(self):
print('save_info start')
config_file = tools.load_file('./config/config.toml')
self.logger.info('save_info start')
config_file = tools.load_file('./configs/main.toml')
config_file['window']['width'] = self.width
config_file['window']['height'] = self.height
toml.dump(config_file, open('./config/config.toml', 'w'))
toml.dump(config_file, open('./configs/main.toml', 'w'))
"""
draws and some event
@ -211,9 +211,10 @@ class ClientWindow(Window):
def FPS_update(self, tick: Decimal):
now_FPS = pyglet.clock.get_fps()
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}] {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))
self.clear()
self.draw_batch()

View File

@ -70,7 +70,7 @@ class Game:
# @new_thread('main')
def _start(self):
self.server.run()
threaded = True
threaded = False
if threaded:
try:
game_process = multiprocessing.Process(target=self.client.start(), name='pyglet app')

View File

@ -2,9 +2,9 @@
fps = 60
version = "0.6.1"
language = "zh-CN"
date_fmt = '%Y-%m-%d %H-%M-%S'
date_fmt = "%Y-%m-%d %H-%M-%S"
write_py_v = "3.8.10"
fonts_folder = 'libs/fonts'
fonts_folder = "libs/fonts"
[window]
style = "None"
@ -18,4 +18,3 @@ full_screen = false
[window.default]
width = 1024
height = 768

View File

@ -452,3 +452,6 @@ class Font:
glyphs = glyph_buffer
return glyphs
def __repr__(self):
return f"{self.__class__.__name__}('{self.name}')"

View File

@ -170,7 +170,7 @@ from pyglet.graphics.vertexbuffer import BufferObject
_debug_graphics_batch = pyglet.options['debug_graphics_batch']
def draw(size, mode, **kwargs):
def draw(size, mode, **data):
"""Draw a primitive immediately.
:Parameters:
@ -194,7 +194,7 @@ def draw(size, mode, **kwargs):
program.use()
buffers = []
for name, (fmt, array) in kwargs.items():
for name, (fmt, array) in data.items():
location = program.attributes[name]['location']
count = program.attributes[name]['count']
gl_type = vertexdomain._gl_types[fmt[0]]
@ -340,6 +340,8 @@ class Batch:
self._draw_list = []
self._draw_list_dirty = False
self._context = pyglet.gl.current_context
def invalidate(self):
"""Force the batch to update the draw list.
@ -554,7 +556,7 @@ class Group:
"""Group of common OpenGL state.
Before a VertexList is rendered, its Group's OpenGL state is set.
This can including binding textures, or setting any other parameters.
This includes binding textures, shaders, or setting any other parameters.
"""
def __init__(self, order=0, parent=None):
"""Create a Group.
@ -565,6 +567,8 @@ class Group:
`parent` : `~pyglet.graphics.Group`
Group to contain this Group; its state will be set before this
Group's state.
:Ivariables:
`visible` : bool
Determines whether this Group is visible in any of the Batches
it is assigned to. If False, objects in this Group will not

View File

@ -28,41 +28,39 @@ _uniform_getters = {
}
_uniform_setters = {
# uniform type: (gl_type, setter, length, count)
GL_BOOL: (GLint, glUniform1iv, 1, 1),
GL_BOOL_VEC2: (GLint, glUniform1iv, 2, 1),
GL_BOOL_VEC3: (GLint, glUniform1iv, 3, 1),
GL_BOOL_VEC4: (GLint, glUniform1iv, 4, 1),
# uniform: gl_type, legacy_setter, setter, length, count
GL_BOOL: (GLint, glUniform1iv, glProgramUniform1iv, 1, 1),
GL_BOOL_VEC2: (GLint, glUniform1iv, glProgramUniform1iv, 2, 1),
GL_BOOL_VEC3: (GLint, glUniform1iv, glProgramUniform1iv, 3, 1),
GL_BOOL_VEC4: (GLint, glUniform1iv, glProgramUniform1iv, 4, 1),
GL_INT: (GLint, glUniform1iv, 1, 1),
GL_INT_VEC2: (GLint, glUniform2iv, 2, 1),
GL_INT_VEC3: (GLint, glUniform3iv, 3, 1),
GL_INT_VEC4: (GLint, glUniform4iv, 4, 1),
GL_INT: (GLint, glUniform1iv, glProgramUniform1iv, 1, 1),
GL_INT_VEC2: (GLint, glUniform2iv, glProgramUniform2iv, 2, 1),
GL_INT_VEC3: (GLint, glUniform3iv, glProgramUniform3iv, 3, 1),
GL_INT_VEC4: (GLint, glUniform4iv, glProgramUniform4iv, 4, 1),
GL_FLOAT: (GLfloat, glUniform1fv, 1, 1),
GL_FLOAT_VEC2: (GLfloat, glUniform2fv, 2, 1),
GL_FLOAT_VEC3: (GLfloat, glUniform3fv, 3, 1),
GL_FLOAT_VEC4: (GLfloat, glUniform4fv, 4, 1),
GL_FLOAT: (GLfloat, glUniform1fv, glProgramUniform1fv, 1, 1),
GL_FLOAT_VEC2: (GLfloat, glUniform2fv, glProgramUniform2fv, 2, 1),
GL_FLOAT_VEC3: (GLfloat, glUniform3fv, glProgramUniform3fv, 3, 1),
GL_FLOAT_VEC4: (GLfloat, glUniform4fv, glProgramUniform4fv, 4, 1),
GL_SAMPLER_1D: (GLint, glUniform1iv, 1, 1),
GL_SAMPLER_2D: (GLint, glUniform1iv, 1, 1),
GL_SAMPLER_2D_ARRAY: (GLint, glUniform1iv, 1, 1),
GL_SAMPLER_1D: (GLint, glUniform1iv, glProgramUniform1iv, 1, 1),
GL_SAMPLER_2D: (GLint, glUniform1iv, glProgramUniform1iv, 1, 1),
GL_SAMPLER_2D_ARRAY: (GLint, glUniform1iv, glProgramUniform1iv, 1, 1),
GL_SAMPLER_3D: (GLint, glUniform1iv, 1, 1),
GL_SAMPLER_3D: (GLint, glUniform1iv, glProgramUniform1iv, 1, 1),
GL_FLOAT_MAT2: (GLfloat, glUniformMatrix2fv, 4, 1),
GL_FLOAT_MAT3: (GLfloat, glUniformMatrix3fv, 6, 1),
GL_FLOAT_MAT4: (GLfloat, glUniformMatrix4fv, 16, 1),
GL_FLOAT_MAT2: (GLfloat, glUniformMatrix2fv, glProgramUniformMatrix2fv, 4, 1),
GL_FLOAT_MAT3: (GLfloat, glUniformMatrix3fv, glProgramUniformMatrix3fv, 6, 1),
GL_FLOAT_MAT4: (GLfloat, glUniformMatrix4fv, glProgramUniformMatrix4fv, 16, 1),
# TODO: test/implement these:
# GL_FLOAT_MAT2x3: glUniformMatrix2x3fv,
# GL_FLOAT_MAT2x4: glUniformMatrix2x4fv,
#
# GL_FLOAT_MAT3x2: glUniformMatrix3x2fv,
# GL_FLOAT_MAT3x4: glUniformMatrix3x4fv,
#
# GL_FLOAT_MAT4x2: glUniformMatrix4x2fv,
# GL_FLOAT_MAT4x3: glUniformMatrix4x3fv,
# GL_FLOAT_MAT2x3: glUniformMatrix2x3fv, glProgramUniformMatrix2x3fv,
# GL_FLOAT_MAT2x4: glUniformMatrix2x4fv, glProgramUniformMatrix2x4fv,
# GL_FLOAT_MAT3x2: glUniformMatrix3x2fv, glProgramUniformMatrix3x2fv,
# GL_FLOAT_MAT3x4: glUniformMatrix3x4fv, glProgramUniformMatrix3x4fv,
# GL_FLOAT_MAT4x2: glUniformMatrix4x2fv, glProgramUniformMatrix4x2fv,
# GL_FLOAT_MAT4x3: glUniformMatrix4x3fv, glProgramUniformMatrix4x3fv,
}
_attribute_types = {
@ -96,11 +94,16 @@ _attribute_types = {
class _Uniform:
__slots__ = 'program', 'name', 'type', 'location', 'length', 'count', 'get', 'set'
def __init__(self, program, name, uniform_type, gl_type, location, length, count, gl_setter, gl_getter):
def __init__(self, program, name, uniform_type, location, dsa):
self.program = program
self.name = name
self.type = uniform_type
self.location = location
gl_type, gl_setter_legacy, gl_setter_dsa, length, count = _uniform_setters[uniform_type]
gl_setter = gl_setter_dsa if dsa else gl_setter_legacy
gl_getter = _uniform_getters[gl_type]
self.length = length
self.count = count
@ -112,41 +115,62 @@ class _Uniform:
ptr = cast(c_array, POINTER(gl_type))
self.get = self._create_getter_func(program, location, gl_getter, c_array, length)
self.set = self._create_setter_func(location, gl_setter, c_array, length, count, ptr, is_matrix)
self.set = self._create_setter_func(program, location, gl_setter, c_array, length, count, ptr, is_matrix, dsa)
@staticmethod
def _create_getter_func(program_id, location, gl_getter, c_array, length):
def _create_getter_func(program, location, gl_getter, c_array, length):
"""Factory function for creating simplified Uniform getters"""
if length == 1:
def getter_func():
gl_getter(program_id, location, c_array)
gl_getter(program, location, c_array)
return c_array[0]
else:
def getter_func():
gl_getter(program_id, location, c_array)
gl_getter(program, location, c_array)
return c_array[:]
return getter_func
@staticmethod
def _create_setter_func(location, gl_setter, c_array, length, count, ptr, is_matrix):
def _create_setter_func(program, location, gl_setter, c_array, length, count, ptr, is_matrix, dsa):
"""Factory function for creating simplified Uniform setters"""
if dsa: # Bindless updates:
if is_matrix:
def setter_func(value):
c_array[:] = value
gl_setter(location, count, GL_FALSE, ptr)
gl_setter(program, location, count, GL_FALSE, ptr)
elif length == 1 and count == 1:
def setter_func(value):
c_array[0] = value
gl_setter(program, location, count, ptr)
elif length > 1 and count == 1:
def setter_func(values):
c_array[:] = values
gl_setter(program, location, count, ptr)
else:
raise NotImplementedError("Uniform type not yet supported.")
return setter_func
else:
if is_matrix:
def setter_func(value):
glUseProgram(program)
c_array[:] = value
gl_setter(location, count, GL_FALSE, ptr)
elif length == 1 and count == 1:
def setter_func(value):
glUseProgram(program)
c_array[0] = value
gl_setter(location, count, ptr)
elif length > 1 and count == 1:
def setter_func(values):
glUseProgram(program)
c_array[:] = values
gl_setter(location, count, ptr)
else:
raise NotImplementedError("Uniform type not yet supported.")
@ -167,9 +191,9 @@ class Shader:
:Parameters:
`source_string` : str
A string containing the Shader code.
A string containing the Shader source code.
`shader_type` : str
The Shader type, such as "vertex" or "fragment".
The Shader type, such as "vertex", "fragment", "geometry", etc.
"""
self._id = None
@ -228,7 +252,7 @@ class Shader:
class ShaderProgram:
"""OpenGL Shader Program"""
__slots__ = '_id', '_context', '_active', '_attributes', '_uniforms', '_uniform_blocks', '__weakref__'
__slots__ = '_id', '_context', '_attributes', '_uniforms', '_uniform_blocks', '__weakref__', '_dsa'
def __init__(self, *shaders):
"""Create an OpenGL ShaderProgram, from multiple Shaders.
@ -242,15 +266,13 @@ class ShaderProgram:
assert shaders, "At least one Shader object is required."
self._id = self._link_program(shaders)
self._context = pyglet.gl.current_context
self._active = False
self._attributes = {}
self._uniforms = {}
self._uniform_blocks = {}
# Query if Direct State Access is available:
self._dsa = gl_info.have_version(4, 1) or gl_info.have_extension("GL_ARB_separate_shader_objects")
self._introspect_attributes()
self._introspect_uniforms()
self._introspect_uniform_blocks()
self._attributes = self._introspect_attributes()
self._uniforms = self._introspect_uniforms()
self._uniform_blocks = self._introspect_uniform_blocks()
if _debug_gl_shaders:
print(self._get_program_log())
@ -259,10 +281,6 @@ class ShaderProgram:
def id(self):
return self._id
@property
def is_active(self):
return self._active
@property
def attributes(self):
return self._attributes
@ -275,10 +293,6 @@ class ShaderProgram:
def uniform_blocks(self):
return self._uniform_blocks
@property
def formats(self):
return tuple(f"{atr.name}{atr.count}{atr.format}" for atr in self._attributes.values())
def _get_program_log(self):
result = c_int(0)
glGetProgramiv(self._id, GL_INFO_LOG_LENGTH, byref(result))
@ -302,11 +316,9 @@ class ShaderProgram:
def use(self):
glUseProgram(self._id)
self._active = True
def stop(self):
glUseProgram(0)
self._active = False
__enter__ = use
bind = use
@ -314,7 +326,6 @@ class ShaderProgram:
def __exit__(self, *_):
glUseProgram(0)
self._active = False
def __del__(self):
try:
@ -325,13 +336,12 @@ class ShaderProgram:
pass
def __setitem__(self, key, value):
if not self._active:
raise Exception("Shader Program is not active.")
try:
uniform = self._uniforms[key]
except KeyError:
raise Exception("Uniform with the name `{0}` was not found.".format(key))
raise Exception(f"A Uniform with the name `{key}` was not found.\n"
f"The spelling may be incorrect, or if not in use it "
f"may have been optimized out by the OpenGL driver.")
try:
uniform.set(value)
@ -363,53 +373,46 @@ class ShaderProgram:
loc = glGetAttribLocation(program, create_string_buffer(a_name.encode('utf-8')))
count, fmt = _attribute_types[a_type]
attributes[a_name] = dict(type=a_type, size=a_size, location=loc, count=count, format=fmt)
self._attributes = attributes
if _debug_gl_shaders:
for attribute in attributes.values():
print(f"Found attribute: {attribute}")
return attributes
def _introspect_uniforms(self):
prg_id = self._id
program = self._id
uniforms = {}
for index in range(self._get_number(GL_ACTIVE_UNIFORMS)):
u_name, u_type, u_size = self._query_uniform(index)
loc = glGetUniformLocation(prg_id, create_string_buffer(u_name.encode('utf-8')))
loc = glGetUniformLocation(program, create_string_buffer(u_name.encode('utf-8')))
if loc == -1: # Skip uniforms that may be inside a Uniform Block
continue
try:
gl_type, gl_setter, length, count = _uniform_setters[u_type]
gl_getter = _uniform_getters[gl_type]
uniforms[u_name] = _Uniform(program, u_name, u_type, loc, self._dsa)
if _debug_gl_shaders:
print("Found uniform: {0}, type: {1}, size: {2}, location: {3}, length: {4},"
" count: {5}".format(u_name, u_type, u_size, loc, length, count))
for uniform in self._uniforms.values():
print(f"Found uniform: {uniform}")
except KeyError:
raise GLException("Unsupported Uniform type {0}".format(u_type))
self._uniforms[u_name] = _Uniform(prg_id, u_name, u_type, gl_type, loc, length, count, gl_setter, gl_getter)
return uniforms
def _introspect_uniform_blocks(self):
p_id = self._id
program = self._id
uniform_blocks = {}
for index in range(self._get_number(GL_ACTIVE_UNIFORM_BLOCKS)):
name = self._get_uniform_block_name(index)
uniform_blocks[name] = {}
num_active = GLint()
block_data_size = GLint()
glGetActiveUniformBlockiv(p_id, index, GL_UNIFORM_BLOCK_DATA_SIZE, block_data_size)
glGetActiveUniformBlockiv(p_id, index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, num_active)
glGetActiveUniformBlockiv(program, index, GL_UNIFORM_BLOCK_DATA_SIZE, block_data_size)
glGetActiveUniformBlockiv(program, index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, num_active)
indices = (GLuint * num_active.value)()
indices_ptr = cast(addressof(indices), POINTER(GLint))
glGetActiveUniformBlockiv(p_id, index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, indices_ptr)
glGetActiveUniformBlockiv(program, index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, indices_ptr)
uniforms = {}
for i in range(num_active.value):
uniform_name, u_type, u_size = self._query_uniform(indices[i])
@ -420,11 +423,12 @@ class ShaderProgram:
except ValueError:
pass
gl_type, _, length, _ = _uniform_setters[u_type]
gl_type, _, _, length, _ = _uniform_setters[u_type]
uniforms[i] = (uniform_name, gl_type, length)
uniform_blocks[name][i] = (uniform_name, gl_type, length)
uniform_blocks[name] = UniformBlock(self, name, index, block_data_size.value, uniforms)
self._uniform_blocks[name] = UniformBlock(self, name, index, block_data_size.value, uniform_blocks[name])
return uniform_blocks
def _get_uniform_block_name(self, index):
buf_size = 128
@ -484,10 +488,10 @@ class ShaderProgram:
try:
if isinstance(fmt, tuple):
fmt, array = fmt
initial_arrays.append((attributes[name]['location'], array))
initial_arrays.append((name, array))
attributes[name] = {**attributes[name], **{'format': fmt}}
except KeyError:
raise ShaderException(f"\nThe attribute `{name}` doesn't exist. Valid names: \n{attributes.keys()}")
raise ShaderException(f"\nThe attribute `{name}` doesn't exist. Valid names: \n{list(attributes)}")
batch = batch or pyglet.graphics.get_default_batch()
domain = batch.get_domain(False, mode, group, self._id, attributes)
@ -495,8 +499,8 @@ class ShaderProgram:
# Create vertex list and initialize
vlist = domain.create(count)
for index, array in initial_arrays:
vlist.set_attribute_data(index, array)
for name, array in initial_arrays:
vlist.set_attribute_data(name, array)
return vlist
@ -528,7 +532,7 @@ class ShaderProgram:
try:
if isinstance(fmt, tuple):
fmt, array = fmt
initial_arrays.append((attributes[name]['location'], array))
initial_arrays.append((name, array))
attributes[name] = {**attributes[name], **{'format': fmt}}
except KeyError:
raise ShaderException(f"\nThe attribute `{name}` doesn't exist. Valid names: \n{list(attributes)}")
@ -541,8 +545,8 @@ class ShaderProgram:
start = vlist.start
vlist.indices = [i + start for i in indices]
for index, array in initial_arrays:
vlist.set_attribute_data(index, array)
for name, array in initial_arrays:
vlist.set_attribute_data(name, array)
return vlist
@ -578,7 +582,6 @@ class UniformBufferObject:
self.view = self._introspect_uniforms()
self._view_ptr = pointer(self.view)
self.index = index
# glUniformBlockBinding(self.block.program.id, self.block.index, self.index)
@property
def id(self):

View File

@ -329,8 +329,8 @@ class VertexList:
for version in self._cache_versions:
self._cache_versions[version] = None
def set_attribute_data(self, i, data):
attribute = self.domain.attributes[i]
def set_attribute_data(self, name, data):
attribute = self.domain.attribute_names[name]
attribute.set_region(attribute.buffer, self.start, self.count, data)
def __getattr__(self, name):

View File

@ -81,15 +81,15 @@ class _Relation:
def _map_pair(raw_relation):
inverted = False
relation_string = raw_relation.split(":")[1]
if relation_string.startswith("+"):
relation_string = relation_string[1:]
if "+" in relation_string:
relation_string = relation_string.strip('+')
inverted = False
elif relation_string.startswith("-"):
relation_string = relation_string[1:]
elif "-" in relation_string:
relation_string = relation_string.strip('-')
inverted = True
if "~" in relation_string:
# TODO: handle this
return None
relation_string = relation_string.strip('~')
inverted = True
if relation_string.startswith("b"): # Button
return _Relation("button", int(relation_string[1:]), inverted)
elif relation_string.startswith("a"): # Axis
@ -189,7 +189,6 @@ def add_mappings_from_file(filename) -> None:
`filename` : str
A file path.
"""
assert os.path.exists(filename), f"Invalid path: {filename}"
with open(filename) as f:
add_mappings_from_string(f.read())

View File

@ -34,11 +34,15 @@
# ----------------------------------------------------------------------------
import os
import errno
import fcntl
import struct
import ctypes
from ctypes import c_uint16 as _u16
from ctypes import c_int16 as _s16
from ctypes import c_uint32 as _u32
from ctypes import c_int32 as _s32
from ctypes import c_int64 as _s64
import pyglet
from pyglet.app.xlib import XlibSelectDevice
@ -47,18 +51,11 @@ from .base import DeviceOpenException
from .evdev_constants import *
from .controller import get_mapping
c = pyglet.lib.load_library('c')
_IOC_NRBITS = 8
_IOC_TYPEBITS = 8
_IOC_SIZEBITS = 14
_IOC_DIRBITS = 2
_IOC_NRMASK = ((1 << _IOC_NRBITS) - 1)
_IOC_TYPEMASK = ((1 << _IOC_TYPEBITS) - 1)
_IOC_SIZEMASK = ((1 << _IOC_SIZEBITS) - 1)
_IOC_DIRMASK = ((1 << _IOC_DIRBITS) - 1)
_IOC_NRSHIFT = 0
_IOC_TYPESHIFT = (_IOC_NRSHIFT + _IOC_NRBITS)
_IOC_SIZESHIFT = (_IOC_TYPESHIFT + _IOC_TYPEBITS)
@ -81,9 +78,7 @@ def _IOR(type, nr, struct):
def f(fileno):
buffer = struct()
if c.ioctl(fileno, request, ctypes.byref(buffer)) < 0:
err = ctypes.c_int.in_dll(c, 'errno').value
raise OSError(err, errno.errorcode[err])
fcntl.ioctl(fileno, request, buffer)
return buffer
return f
@ -92,9 +87,7 @@ def _IOR(type, nr, struct):
def _IOR_len(type, nr):
def f(fileno, buffer):
request = _IOC(_IOC_READ, ord(type), nr, ctypes.sizeof(buffer))
if c.ioctl(fileno, request, ctypes.byref(buffer)) < 0:
err = ctypes.c_int.in_dll(c, 'errno').value
raise OSError(err, errno.errorcode[err])
fcntl.ioctl(fileno, request, buffer)
return buffer
return f
@ -103,56 +96,154 @@ def _IOR_len(type, nr):
def _IOR_str(type, nr):
g = _IOR_len(type, nr)
def f(fileno, len=256):
return g(fileno, ctypes.create_string_buffer(len)).value
def f(fileno, length=256):
return g(fileno, ctypes.create_string_buffer(length)).value
return f
time_t = ctypes.c_long
suseconds_t = ctypes.c_long
def _IOW(type, nr):
def f(fileno, buffer):
request = _IOC(_IOC_WRITE, ord(type), nr, ctypes.sizeof(buffer))
fcntl.ioctl(fileno, request, buffer)
return f
class timeval(ctypes.Structure):
# Structures from /linux/blob/master/include/uapi/linux/input.h
class Timeval(ctypes.Structure):
_fields_ = (
('tv_sec', time_t),
('tv_usec', suseconds_t)
('tv_sec', _s64),
('tv_usec', _s64)
)
class input_event(ctypes.Structure):
class InputEvent(ctypes.Structure):
_fields_ = (
('time', timeval),
('type', ctypes.c_uint16),
('code', ctypes.c_uint16),
('value', ctypes.c_int32)
('time', Timeval),
('type', _u16),
('code', _u16),
('value', _s32)
)
class input_id(ctypes.Structure):
class InputID(ctypes.Structure):
_fields_ = (
('bustype', ctypes.c_uint16),
('vendor', ctypes.c_uint16),
('product', ctypes.c_uint16),
('version', ctypes.c_uint16),
('bustype', _u16),
('vendor', _u16),
('product', _u16),
('version', _u16),
)
class input_absinfo(ctypes.Structure):
class InputABSInfo(ctypes.Structure):
_fields_ = (
('value', ctypes.c_int32),
('minimum', ctypes.c_int32),
('maximum', ctypes.c_int32),
('fuzz', ctypes.c_int32),
('flat', ctypes.c_int32),
('value', _s32),
('minimum', _s32),
('maximum', _s32),
('fuzz', _s32),
('flat', _s32),
)
class FFReplay(ctypes.Structure):
_fields_ = (
('length', _u16),
('delay', _u16)
)
class FFTrigger(ctypes.Structure):
_fields_ = (
('button', _u16),
('interval', _u16)
)
class FFEnvelope(ctypes.Structure):
_fields_ = [
('attack_length', _u16),
('attack_level', _u16),
('fade_length', _u16),
('fade_level', _u16),
]
class FFConstantEffect(ctypes.Structure):
_fields_ = [
('level', _s16),
('ff_envelope', FFEnvelope),
]
class FFRampEffect(ctypes.Structure):
_fields_ = [
('start_level', _s16),
('end_level', _s16),
('ff_envelope', FFEnvelope),
]
class FFConditionEffect(ctypes.Structure):
_fields_ = [
('right_saturation', _u16),
('left_saturation', _u16),
('right_coeff', _s16),
('left_coeff', _s16),
('deadband', _u16),
('center', _s16),
]
class FFPeriodicEffect(ctypes.Structure):
_fields_ = [
('waveform', _u16),
('period', _u16),
('magnitude', _s16),
('offset', _s16),
('phase', _u16),
('envelope', FFEnvelope),
('custom_len', _u32),
('custom_data', ctypes.POINTER(_s16)),
]
class FFRumbleEffect(ctypes.Structure):
_fields_ = (
('strong_magnitude', _u16),
('weak_magnitude', _u16)
)
class FFEffectType(ctypes.Union):
_fields_ = (
('ff_constant_effect', FFConstantEffect),
('ff_ramp_effect', FFRampEffect),
('ff_periodic_effect', FFPeriodicEffect),
('ff_condition_effect', FFConditionEffect * 2),
('ff_rumble_effect', FFRumbleEffect),
)
class FFEvent(ctypes.Structure):
_fields_ = (
('type', _u16),
('id', _s16),
('direction', _u16),
('ff_trigger', FFTrigger),
('ff_replay', FFReplay),
('u', FFEffectType)
)
EVIOCGVERSION = _IOR('E', 0x01, ctypes.c_int)
EVIOCGID = _IOR('E', 0x02, input_id)
EVIOCGID = _IOR('E', 0x02, InputID)
EVIOCGNAME = _IOR_str('E', 0x06)
EVIOCGPHYS = _IOR_str('E', 0x07)
EVIOCGUNIQ = _IOR_str('E', 0x08)
EVIOCSFF = _IOW('E', 0x80)
def EVIOCGBIT(fileno, ev, buffer):
@ -160,14 +251,14 @@ def EVIOCGBIT(fileno, ev, buffer):
def EVIOCGABS(fileno, abs):
buffer = input_absinfo()
buffer = InputABSInfo()
return _IOR_len('E', 0x40 + abs)(fileno, buffer)
def get_set_bits(bytes):
def get_set_bits(bytestring):
bits = set()
j = 0
for byte in bytes:
for byte in bytestring:
for i in range(8):
if byte & 1:
bits.add(j + i)
@ -208,7 +299,6 @@ def _create_control(fileno, event_type, event_code):
maximum = absinfo.maximum
control = AbsoluteAxis(name, minimum, maximum, raw_name)
control.value = value
if name == 'hat_y':
control.inverted = True
elif event_type == EV_REL:
@ -235,6 +325,7 @@ event_types = {
EV_MSC: MSC_MAX,
EV_LED: LED_MAX,
EV_SND: SND_MAX,
EV_FF: FF_MAX,
}
@ -273,6 +364,7 @@ class EvdevDevice(XlibSelectDevice, Device):
self.controls = []
self.control_map = {}
self.ff_types = []
event_types_bits = (ctypes.c_byte * 4)()
EVIOCGBIT(fileno, 0, event_types_bits)
@ -283,6 +375,9 @@ class EvdevDevice(XlibSelectDevice, Device):
nbytes = max_code // 8 + 1
event_codes_bits = (ctypes.c_byte * nbytes)()
EVIOCGBIT(fileno, event_type, event_codes_bits)
if event_type == EV_FF:
self.ff_types.extend(get_set_bits(event_codes_bits))
else:
for event_code in get_set_bits(event_codes_bits):
control = _create_control(fileno, event_type, event_code)
if control:
@ -308,7 +403,7 @@ class EvdevDevice(XlibSelectDevice, Device):
hex_product, shifted_product, hex_version, shifted_version)
def open(self, window=None, exclusive=False):
super(EvdevDevice, self).open(window, exclusive)
super().open(window, exclusive)
try:
self._fileno = os.open(self._filename, os.O_RDWR | os.O_NONBLOCK)
@ -318,7 +413,7 @@ class EvdevDevice(XlibSelectDevice, Device):
pyglet.app.platform_event_loop.select_devices.add(self)
def close(self):
super(EvdevDevice, self).close()
super().close()
if not self._fileno:
return
@ -332,33 +427,8 @@ class EvdevDevice(XlibSelectDevice, Device):
# Force Feedback methods
def supports_ff(self):
try:
self._fileno = os.open(self._filename, os.O_RDWR | os.O_NONBLOCK)
self.ff_create_effect(0, 0, 0)
os.close(self._fileno)
return True
except OSError:
os.close(self._fileno)
return False
def ff_create_effect(self, weak, strong, duration, effect=-1):
weak = int(max(min(1, weak), 0) * 0xFFFF) # Clamp range from 0-1, convert to 16bit
strong = int(max(min(1, strong), 0) * 0xFFFF) # Clamp range from 0-1, convert to 16bit
duration = int(duration * 1000)
effect = bytearray(struct.pack('HhHHHHHxHH', FF_RUMBLE, effect, 0, 0, 0, duration, 0, strong, weak))
view = memoryview(effect).cast('h')
fcntl.ioctl(self._fileno, 0x40304580, view, True)
return view[1] # effect ID
def ff_play(self, effect):
ev_play = struct.pack('LLHHi', 0, 0, EV_FF, effect, 1)
os.write(self._fileno, ev_play)
def ff_stop(self, effect):
ev_stop = struct.pack('LLHHi', 0, 0, EV_FF, effect, 0)
os.write(self._fileno, ev_stop)
def ff_upload_effect(self, structure):
os.write(self._fileno, structure)
# XlibSelectDevice interface
@ -372,12 +442,14 @@ class EvdevDevice(XlibSelectDevice, Device):
if not self._fileno:
return
events = (input_event * 64)()
bytes_read = c.read(self._fileno, events, ctypes.sizeof(events))
if bytes_read < 0:
try:
events = (InputEvent * 64)()
bytes_read = os.readv(self._fileno, events)
except OSError:
self.close()
return
n_events = bytes_read // ctypes.sizeof(input_event)
n_events = bytes_read // ctypes.sizeof(InputEvent)
for event in events[:n_events]:
try:
control = self.control_map[(event.type, event.code)]
@ -386,32 +458,54 @@ class EvdevDevice(XlibSelectDevice, Device):
pass
class EvdevController(Controller):
_rumble_weak = -1
_rumble_strong = -1
class FFController(Controller):
"""Controller that supports force-feedback"""
_fileno = None
_weak_effect = None
_play_weak_event = None
_stop_weak_event = None
_strong_effect = None
_play_strong_event = None
_stop_strong_event = None
def open(self, window=None, exclusive=False):
super().open(window, exclusive)
# Create Force Feedback effects when the device is opened:
self._rumble_weak = self.device.ff_create_effect(0, 0, 0)
self._rumble_strong = self.device.ff_create_effect(0, 0, 0)
self._fileno = self.device.fileno()
# Create Force Feedback effects & events when opened:
# https://www.kernel.org/doc/html/latest/input/ff.html
self._weak_effect = self._create_effect()
self._play_weak_event = InputEvent(Timeval(), EV_FF, self._weak_effect.id, 1)
self._stop_weak_event = InputEvent(Timeval(), EV_FF, self._weak_effect.id, 0)
self._strong_effect = self._create_effect()
self._play_strong_event = InputEvent(Timeval(), EV_FF, self._strong_effect.id, 1)
self._stop_strong_event = InputEvent(Timeval(), EV_FF, self._strong_effect.id, 0)
def _create_effect(self):
event = FFEvent(FF_RUMBLE, -1)
EVIOCSFF(self._fileno, event)
return event
def rumble_play_weak(self, strength=1.0, duration=0.5):
effect = self.device.ff_create_effect(strength, 0, duration, self._rumble_weak)
self.device.ff_play(effect)
effect = self._weak_effect
effect.u.ff_rumble_effect.weak_magnitude = int(max(min(1.0, strength), 0) * 0xFFFF)
effect.ff_replay.length = int(duration * 1000)
EVIOCSFF(self._fileno, effect)
self.device.ff_upload_effect(self._play_weak_event)
def rumble_play_strong(self, strength=1.0, duration=0.5):
effect = self.device.ff_create_effect(0, strength, duration, self._rumble_strong)
self.device.ff_play(effect)
effect = self._strong_effect
effect.u.ff_rumble_effect.strong_magnitude = int(max(min(1.0, strength), 0) * 0xFFFF)
effect.ff_replay.length = int(duration * 1000)
EVIOCSFF(self._fileno, effect)
self.device.ff_upload_effect(self._play_strong_event)
def rumble_stop_weak(self):
"""Stop playing rumble effects on the weak motor."""
self.device.ff_stop(self._rumble_weak)
self.device.ff_upload_effect(self._stop_weak_event)
def rumble_stop_strong(self):
"""Stop playing rumble effects on the strong motor."""
self.device.ff_stop(self._rumble_strong)
self.device.ff_upload_effect(self._stop_strong_event)
def get_devices(display=None):
@ -470,8 +564,8 @@ def _create_controller(device):
if have_button is False or mapping is None:
return
if device.supports_ff():
return EvdevController(device, mapping)
if FF_RUMBLE in device.ff_types:
return FFController(device, mapping)
else:
return Controller(device, mapping)

View File

@ -36,14 +36,19 @@
"""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.
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.
: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.
"""
import math as _math
import warnings as _warnings
from operator import mul as _mul
@ -52,7 +57,7 @@ def clamp(num, min_val, max_val):
class Vec2(tuple):
"""A two dimensional vector represented as an X Y coordinate pair.
"""A two-dimensional vector represented as an X Y coordinate pair.
:parameters:
`x` : int or float :
@ -60,7 +65,8 @@ class Vec2(tuple):
`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 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
"""
@ -148,7 +154,8 @@ class Vec2(tuple):
return self.__add__(other)
def from_magnitude(self, magnitude):
"""Create a new Vector of the given magnitude by normalizing, then scaling the vector. The heading remains unchanged.
"""Create a new Vector of the given magnitude by normalizing,
then scaling the vector. The heading remains unchanged.
:parameters:
`magnitude` : int or float :
@ -172,18 +179,18 @@ class Vec2(tuple):
mag = self.__abs__()
return Vec2(mag * _math.cos(heading), mag * _math.sin(heading))
def limit(self, max):
def limit(self, maximum):
"""Limit the magnitude of the vector to the value used for the max parameter.
:parameters:
`max` : int or float :
`maximum` : int or float :
The maximum magnitude for the vector.
:returns: Either self or a new vector with the maximum magnitude.
:rtype: Vec2
"""
if self[0] ** 2 + self[1] ** 2 > max * max:
return self.from_magnitude(max)
if self[0] ** 2 + self[1] ** 2 > maximum * maximum:
return self.from_magnitude(maximum)
return self
def lerp(self, other, alpha):
@ -291,7 +298,7 @@ class Vec2(tuple):
class Vec3(tuple):
"""A three dimensional vector represented as a X Y Z coordinates.
"""A three-dimensional vector represented as X Y Z coordinates.
:parameters:
`x` : int or float :
@ -301,7 +308,8 @@ class Vec3(tuple):
`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.
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
"""
@ -374,7 +382,8 @@ class Vec3(tuple):
return self.__add__(other)
def from_magnitude(self, magnitude):
"""Create a new Vector of the given magnitude by normalizing, then scaling the vector. The rotation remains unchanged.
"""Create a new Vector of the given magnitude by normalizing,
then scaling the vector. The rotation remains unchanged.
:parameters:
`magnitude` : int or float :
@ -385,17 +394,17 @@ class Vec3(tuple):
"""
return self.normalize().scale(magnitude)
def limit(self, max):
def limit(self, maximum):
"""Limit the magnitude of the vector to the value used for the max parameter.
:parameters:
`max` : int or float :
`maximum` : int or float :
The maximum magnitude for the vector.
:returns: Either self or a new vector with the maximum magnitude.
:rtype: Vec3
"""
if self[0] ** 2 + self[1] ** 2 + self[2] **2 > max * max * max:
if self[0] ** 2 + self[1] ** 2 + self[2] ** 2 > maximum * maximum * maximum:
return self.from_magnitude(max)
return self
@ -829,7 +838,7 @@ class Mat4(tuple):
return Mat4(temp)
def translate(self, vector: Vec3) -> 'Mat4':
"""Get a translate Matrix along x, y, and z axis."""
"""Get a translation Matrix along x, y, and z axis."""
return Mat4(self) @ Mat4((1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, *vector, 1))
def rotate(self, angle: float, vector: Vec3) -> 'Mat4':

View File

@ -219,7 +219,7 @@ class SynthesisSource(Source):
def _generate_data(self, num_bytes):
"""Generate `num_bytes` bytes of data.
Return data as ctypes array or string.
Return data as ctypes array or bytes.
"""
raise NotImplementedError('abstract')
@ -451,6 +451,8 @@ class Encoder(SynthesisSource):
def __init__(self, duration, generator, **kwargs):
super().__init__(duration, **kwargs)
self._generator = generator
self._total = int(duration * self.audio_format.sample_rate)
self._consumed = 0
def _generate_data(self, num_bytes):
envelope = self._envelope_generator

View File

@ -81,7 +81,7 @@ instance when loading the Model::
.. versionadded:: 1.4
"""
from math import radians
from io import BytesIO
import pyglet
@ -190,8 +190,7 @@ class Model:
self.vertex_lists = vertex_lists
self.groups = groups
self._batch = batch
self._rotation = Vec3()
self._translation = Vec3()
self._modelview_matrix = Mat4()
@property
def batch(self):
@ -220,24 +219,14 @@ class Model:
self._batch = batch
@property
def rotation(self):
return self._rotation
def matrix(self):
return self._modelview_matrix
@rotation.setter
def rotation(self, values):
self._rotation = values
@matrix.setter
def matrix(self, matrix):
self._modelview_matrix = matrix
for group in self.groups:
group.rotation = values
@property
def translation(self):
return self._translation
@translation.setter
def translation(self, values):
self._translation = values
for group in self.groups:
group.translation = values
group.matrix = matrix
def draw(self):
"""Draw the model.
@ -274,28 +263,11 @@ class Material:
class BaseMaterialGroup(graphics.ShaderGroup):
default_vert_src = None
default_frag_src = None
matrix = Mat4()
def __init__(self, material, program, order=0, parent=None):
super().__init__(program, order, parent)
self.material = material
self.rotation = Vec3()
self.translation = Vec3()
def set_modelview_matrix(self):
# NOTE: Matrix operations can be optimized later with transform feedback
view = Mat4()
view = view.rotate(radians(self.rotation[2]), Vec3(0, 0, 1))
view = view.rotate(radians(self.rotation[1]), Vec3(0, 1, 0))
view = view.rotate(radians(self.rotation[0]), Vec3(1, 0, 0))
view = view.translate(self.translation)
# TODO: separate the projection block, and remove this hack
block = self.program.uniform_blocks['WindowBlock']
ubo = block.create_ubo(0)
with ubo as window_block:
window_block.projection[:] = pyglet.math.Mat4.perspective_projection(0, 720, 0, 480, z_near=0.1, z_far=255)
window_block.view[:] = view
class TexturedMaterialGroup(BaseMaterialGroup):
@ -316,12 +288,13 @@ class TexturedMaterialGroup(BaseMaterialGroup):
mat4 view;
} window;
uniform mat4 model;
void main()
{
vec4 pos = window.view * vec4(vertices, 1.0);
vec4 pos = window.view * model * vec4(vertices, 1.0);
gl_Position = window.projection * pos;
mat3 normal_matrix = transpose(inverse(mat3(window.view)));
mat3 normal_matrix = transpose(inverse(mat3(model)));
vertex_position = pos.xyz;
vertex_colors = colors;
@ -345,15 +318,15 @@ class TexturedMaterialGroup(BaseMaterialGroup):
}
"""
def __init__(self, material, texture):
super().__init__(material, get_default_textured_shader())
def __init__(self, material, program, texture, order=0, parent=None):
super().__init__(material, program, order, parent)
self.texture = texture
def set_state(self):
gl.glActiveTexture(gl.GL_TEXTURE0)
gl.glBindTexture(self.texture.target, self.texture.id)
self.program.use()
self.set_modelview_matrix()
self.program['model'] = self.matrix
def unset_state(self):
gl.glBindTexture(self.texture.target, 0)
@ -387,11 +360,13 @@ class MaterialGroup(BaseMaterialGroup):
mat4 view;
} window;
uniform mat4 model;
void main()
{
vec4 pos = window.view * vec4(vertices, 1.0);
vec4 pos = window.view * model * vec4(vertices, 1.0);
gl_Position = window.projection * pos;
mat3 normal_matrix = transpose(inverse(mat3(window.view)));
mat3 normal_matrix = transpose(inverse(mat3(model)));
vertex_position = pos.xyz;
vertex_colors = colors;
@ -411,12 +386,9 @@ class MaterialGroup(BaseMaterialGroup):
}
"""
def __init__(self, material):
super().__init__(material, get_default_shader())
def set_state(self):
self.program.use()
self.set_modelview_matrix()
self.program['model'] = self.matrix
add_default_model_codecs()

View File

@ -245,17 +245,17 @@ class OBJModelDecoder(ModelDecoder):
material = mesh.material
count = len(mesh.vertices) // 3
if material.texture_name:
texture = pyglet.resource.texture(material.texture_name)
group = TexturedMaterialGroup(material, texture)
program = pyglet.model.get_default_textured_shader()
texture = pyglet.resource.texture(material.texture_name)
group = TexturedMaterialGroup(material, program, texture)
vertex_lists.append(program.vertex_list(count, GL_TRIANGLES, batch, group,
vertices=('f', mesh.vertices),
normals=('f', mesh.normals),
tex_coords=('f', mesh.tex_coords),
colors=('f', material.diffuse * count)))
else:
group = MaterialGroup(material)
program = pyglet.model.get_default_shader()
group = MaterialGroup(material, program)
vertex_lists.append(program.vertex_list(count, GL_TRIANGLES, batch, group,
vertices=('f', mesh.vertices),
normals=('f', mesh.normals),