Compare commits
12 Commits
364924a7b7
...
dd7038272c
Author | SHA1 | Date | |
---|---|---|---|
dd7038272c | |||
f1172c30be | |||
6c68da7edf | |||
951784cf64 | |||
6887660ee1 | |||
3df29bee64 | |||
b70705fb67 | |||
ce0fe298e6 | |||
0c5647fee2 | |||
253e3b62fb | |||
640cc27e34 | |||
c14786e216 |
@ -15,7 +15,7 @@ import traceback
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Callable, Dict, List, TYPE_CHECKING, Optional, Type
|
from typing import Callable, Dict, List, TYPE_CHECKING, Type
|
||||||
|
|
||||||
# third function
|
# third function
|
||||||
import rtoml
|
import rtoml
|
||||||
@ -71,6 +71,9 @@ class ClientOption(Options):
|
|||||||
|
|
||||||
|
|
||||||
class Client:
|
class Client:
|
||||||
|
"""
|
||||||
|
客户端
|
||||||
|
"""
|
||||||
def __init__(self, game: "Game", net_mode='local'):
|
def __init__(self, game: "Game", net_mode='local'):
|
||||||
start_time = time.time_ns()
|
start_time = time.time_ns()
|
||||||
# logging
|
# logging
|
||||||
@ -95,6 +98,9 @@ class Client:
|
|||||||
self.logger.debug(tr().client.setup.use_time_ns().format(self.use_time))
|
self.logger.debug(tr().client.setup.use_time_ns().format(self.use_time))
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
"""
|
||||||
|
启动客户端
|
||||||
|
"""
|
||||||
DR_runtime.running = True
|
DR_runtime.running = True
|
||||||
self.window.start_game() # 游戏启动
|
self.window.start_game() # 游戏启动
|
||||||
# TODO 写一下服务端启动相关,还是需要服务端啊
|
# TODO 写一下服务端启动相关,还是需要服务端啊
|
||||||
@ -104,6 +110,11 @@ class Client:
|
|||||||
|
|
||||||
|
|
||||||
def pyglet_load_fonts_folder(folder) -> None:
|
def pyglet_load_fonts_folder(folder) -> None:
|
||||||
|
"""
|
||||||
|
递归加载字体文件夹
|
||||||
|
:param folder:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
font_path = Path(folder)
|
font_path = Path(folder)
|
||||||
if not font_path.exists():
|
if not font_path.exists():
|
||||||
font_path.mkdir(parents=True)
|
font_path.mkdir(parents=True)
|
||||||
|
@ -333,10 +333,10 @@ class _ModuleProxy:
|
|||||||
# Lazily load all modules, except if performing
|
# Lazily load all modules, except if performing
|
||||||
# type checking or code inspection.
|
# type checking or code inspection.
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from . import types
|
|
||||||
from . import app
|
from . import app
|
||||||
from . import canvas
|
from . import canvas
|
||||||
from . import clock
|
from . import clock
|
||||||
|
from . import customtypes
|
||||||
from . import event
|
from . import event
|
||||||
from . import font
|
from . import font
|
||||||
from . import gl
|
from . import gl
|
||||||
@ -354,10 +354,10 @@ if TYPE_CHECKING:
|
|||||||
from . import text
|
from . import text
|
||||||
from . import window
|
from . import window
|
||||||
else:
|
else:
|
||||||
types = _ModuleProxy('types')
|
|
||||||
app = _ModuleProxy('app')
|
app = _ModuleProxy('app')
|
||||||
canvas = _ModuleProxy('canvas')
|
canvas = _ModuleProxy('canvas')
|
||||||
clock = _ModuleProxy('clock')
|
clock = _ModuleProxy('clock')
|
||||||
|
customtypes = _ModuleProxy('customtypes')
|
||||||
event = _ModuleProxy('event')
|
event = _ModuleProxy('event')
|
||||||
font = _ModuleProxy('font')
|
font = _ModuleProxy('font')
|
||||||
gl = _ModuleProxy('gl')
|
gl = _ModuleProxy('gl')
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
"""Holds type aliases used throughout the codebase."""
|
"""Holds type aliases used throughout the codebase."""
|
||||||
import ctypes
|
import ctypes
|
||||||
|
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Buffer"
|
"Buffer"
|
||||||
]
|
]
|
6
libs/pyglet/experimental/__init__.py
Normal file
6
libs/pyglet/experimental/__init__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
"""Experimental modules.
|
||||||
|
|
||||||
|
This package contains experimental modules, included here for wider testing
|
||||||
|
and experimentation. Anything contined within may be broken, refactored, or
|
||||||
|
removed without notice.
|
||||||
|
"""
|
804
libs/pyglet/experimental/geoshader_sprite.py
Normal file
804
libs/pyglet/experimental/geoshader_sprite.py
Normal file
@ -0,0 +1,804 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
import pyglet
|
||||||
|
|
||||||
|
from pyglet.gl import *
|
||||||
|
from pyglet import clock
|
||||||
|
from pyglet import event
|
||||||
|
from pyglet import graphics
|
||||||
|
from pyglet import image
|
||||||
|
|
||||||
|
_is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
|
||||||
|
|
||||||
|
|
||||||
|
vertex_source = """#version 150
|
||||||
|
in vec3 position;
|
||||||
|
in vec4 size;
|
||||||
|
in vec4 color;
|
||||||
|
in vec4 texture_uv;
|
||||||
|
in float rotation;
|
||||||
|
|
||||||
|
out vec4 geo_size;
|
||||||
|
out vec4 geo_color;
|
||||||
|
out vec4 geo_tex_coords;
|
||||||
|
out float geo_rotation;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(position, 1);
|
||||||
|
geo_size = size;
|
||||||
|
geo_color = color;
|
||||||
|
geo_tex_coords = texture_uv;
|
||||||
|
geo_rotation = rotation;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
geometry_source = """#version 150
|
||||||
|
// We are taking single points from the vertex shader
|
||||||
|
// and emitting 4 new vertices creating a quad/sprites
|
||||||
|
layout (points) in;
|
||||||
|
layout (triangle_strip, max_vertices = 4) out;
|
||||||
|
|
||||||
|
uniform WindowBlock
|
||||||
|
{
|
||||||
|
mat4 projection;
|
||||||
|
mat4 view;
|
||||||
|
} window;
|
||||||
|
|
||||||
|
|
||||||
|
// Since geometry shader can take multiple values from a vertex
|
||||||
|
// shader we need to define the inputs from it as arrays.
|
||||||
|
// In our instance we just take single values (points)
|
||||||
|
in vec4 geo_size[];
|
||||||
|
in vec4 geo_color[];
|
||||||
|
in vec4 geo_tex_coords[];
|
||||||
|
in float geo_rotation[];
|
||||||
|
|
||||||
|
out vec2 uv;
|
||||||
|
out vec4 frag_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
// We grab the position value from the vertex shader
|
||||||
|
vec2 center = gl_in[0].gl_Position.xy;
|
||||||
|
|
||||||
|
// Calculate the half size of the sprites for easier calculations
|
||||||
|
vec2 hsize = geo_size[0].xy / 2.0;
|
||||||
|
|
||||||
|
// Alias the Z value to save space
|
||||||
|
float z = gl_in[0].gl_Position.z;
|
||||||
|
|
||||||
|
// Convert the rotation to radians
|
||||||
|
float angle = radians(-geo_rotation[0]);
|
||||||
|
|
||||||
|
// Create a scale vector
|
||||||
|
vec2 scale = vec2(geo_size[0][2], geo_size[0][3]);
|
||||||
|
|
||||||
|
// Create a 2d rotation matrix
|
||||||
|
mat2 rot = mat2(cos(angle), sin(angle),
|
||||||
|
-sin(angle), cos(angle));
|
||||||
|
|
||||||
|
// Calculate the left, bottom, right, top:
|
||||||
|
float tl = geo_tex_coords[0].s;
|
||||||
|
float tb = geo_tex_coords[0].t;
|
||||||
|
float tr = geo_tex_coords[0].s + geo_tex_coords[0].p;
|
||||||
|
float tt = geo_tex_coords[0].t + geo_tex_coords[0].q;
|
||||||
|
|
||||||
|
// Emit a triangle strip creating a quad (4 vertices).
|
||||||
|
// Here we need to make sure the rotation is applied before we position the sprite.
|
||||||
|
// We just use hardcoded texture coordinates here. If an atlas is used we
|
||||||
|
// can pass an additional vec4 for specific texture coordinates.
|
||||||
|
// Each EmitVertex() emits values down the shader pipeline just like a single
|
||||||
|
// run of a vertex shader, but in geomtry shaders we can do it multiple times!
|
||||||
|
|
||||||
|
// Upper left
|
||||||
|
gl_Position = window.projection * window.view * vec4(rot * vec2(-hsize.x, hsize.y) * scale + center, z, 1.0);
|
||||||
|
uv = vec2(tl, tt);
|
||||||
|
frag_color = geo_color[0];
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
// lower left
|
||||||
|
gl_Position = window.projection * window.view * vec4(rot * vec2(-hsize.x, -hsize.y) * scale + center, z, 1.0);
|
||||||
|
uv = vec2(tl, tb);
|
||||||
|
frag_color = geo_color[0];
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
// upper right
|
||||||
|
gl_Position = window.projection * window.view * vec4(rot * vec2(hsize.x, hsize.y) * scale + center, z, 1.0);
|
||||||
|
uv = vec2(tr, tt);
|
||||||
|
frag_color = geo_color[0];
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
// lower right
|
||||||
|
gl_Position = window.projection * window.view * vec4(rot * vec2(hsize.x, -hsize.y) * scale + center, z, 1.0);
|
||||||
|
uv = vec2(tr, tb);
|
||||||
|
frag_color = geo_color[0];
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
// We are done with this triangle strip now
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
fragment_source = """#version 150
|
||||||
|
in vec2 uv;
|
||||||
|
in vec4 frag_color;
|
||||||
|
out vec4 final_color;
|
||||||
|
|
||||||
|
uniform sampler2D sprite_texture;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final_color = texture(sprite_texture, uv) * frag_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
fragment_array_source = """#version 150 core
|
||||||
|
in vec2 uv;
|
||||||
|
in vec4 frag_color;
|
||||||
|
|
||||||
|
out vec4 final_colors;
|
||||||
|
|
||||||
|
uniform sampler2DArray sprite_texture;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
final_colors = texture(sprite_texture, uv) * frag_color;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_shader():
|
||||||
|
try:
|
||||||
|
return pyglet.gl.current_context.pyglet_sprite_default_shader
|
||||||
|
except AttributeError:
|
||||||
|
vert_shader = graphics.shader.Shader(vertex_source, 'vertex')
|
||||||
|
geom_shader = graphics.shader.Shader(geometry_source, 'geometry')
|
||||||
|
frag_shader = graphics.shader.Shader(fragment_source, 'fragment')
|
||||||
|
default_shader_program = graphics.shader.ShaderProgram(vert_shader, geom_shader, frag_shader)
|
||||||
|
pyglet.gl.current_context.pyglet_sprite_default_shader = default_shader_program
|
||||||
|
return pyglet.gl.current_context.pyglet_sprite_default_shader
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_array_shader():
|
||||||
|
try:
|
||||||
|
return pyglet.gl.current_context.pyglet_sprite_default_array_shader
|
||||||
|
except AttributeError:
|
||||||
|
vert_shader = graphics.shader.Shader(vertex_source, 'vertex')
|
||||||
|
geom_shader = graphics.shader.Shader(geometry_source, 'geometry')
|
||||||
|
frag_shader = graphics.shader.Shader(fragment_array_source, 'fragment')
|
||||||
|
default_shader_program = graphics.shader.ShaderProgram(vert_shader, geom_shader, frag_shader)
|
||||||
|
pyglet.gl.current_context.pyglet_sprite_default_array_shader = default_shader_program
|
||||||
|
return pyglet.gl.current_context.pyglet_sprite_default_array_shader
|
||||||
|
|
||||||
|
|
||||||
|
class SpriteGroup(graphics.Group):
|
||||||
|
"""Shared sprite rendering group.
|
||||||
|
|
||||||
|
The group is automatically coalesced with other sprite groups sharing the
|
||||||
|
same parent group, texture and blend parameters.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, texture, blend_src, blend_dest, program, parent=None):
|
||||||
|
"""Create a sprite group.
|
||||||
|
|
||||||
|
The group is created internally when a :py:class:`~pyglet.sprite.Sprite`
|
||||||
|
is created; applications usually do not need to explicitly create it.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`texture` : `~pyglet.image.Texture`
|
||||||
|
The (top-level) texture containing the sprite image.
|
||||||
|
`blend_src` : int
|
||||||
|
OpenGL blend source mode; for example,
|
||||||
|
``GL_SRC_ALPHA``.
|
||||||
|
`blend_dest` : int
|
||||||
|
OpenGL blend destination mode; for example,
|
||||||
|
``GL_ONE_MINUS_SRC_ALPHA``.
|
||||||
|
`program` : `~pyglet.graphics.shader.ShaderProgram`
|
||||||
|
A custom ShaderProgram.
|
||||||
|
`order` : int
|
||||||
|
Change the order to render above or below other Groups.
|
||||||
|
`parent` : `~pyglet.graphics.Group`
|
||||||
|
Optional parent group.
|
||||||
|
"""
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
self.texture = texture
|
||||||
|
self.blend_src = blend_src
|
||||||
|
self.blend_dest = blend_dest
|
||||||
|
self.program = program
|
||||||
|
|
||||||
|
def set_state(self):
|
||||||
|
self.program.use()
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0)
|
||||||
|
glBindTexture(self.texture.target, self.texture.id)
|
||||||
|
|
||||||
|
glEnable(GL_BLEND)
|
||||||
|
glBlendFunc(self.blend_src, self.blend_dest)
|
||||||
|
|
||||||
|
def unset_state(self):
|
||||||
|
glDisable(GL_BLEND)
|
||||||
|
self.program.stop()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "{0}({1})".format(self.__class__.__name__, self.texture)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return (other.__class__ is self.__class__ and
|
||||||
|
self.program is other.program and
|
||||||
|
self.parent == other.parent and
|
||||||
|
self.texture.target == other.texture.target and
|
||||||
|
self.texture.id == other.texture.id and
|
||||||
|
self.blend_src == other.blend_src and
|
||||||
|
self.blend_dest == other.blend_dest)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.program, self.parent,
|
||||||
|
self.texture.id, self.texture.target,
|
||||||
|
self.blend_src, self.blend_dest))
|
||||||
|
|
||||||
|
|
||||||
|
class Sprite(event.EventDispatcher):
|
||||||
|
|
||||||
|
_batch = None
|
||||||
|
_animation = None
|
||||||
|
_frame_index = 0
|
||||||
|
_paused = False
|
||||||
|
_rotation = 0
|
||||||
|
_rgba = [255, 255, 255, 255]
|
||||||
|
_scale = 1.0
|
||||||
|
_scale_x = 1.0
|
||||||
|
_scale_y = 1.0
|
||||||
|
_visible = True
|
||||||
|
_vertex_list = None
|
||||||
|
group_class = SpriteGroup
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
img, x=0, y=0, z=0,
|
||||||
|
blend_src=GL_SRC_ALPHA, blend_dest=GL_ONE_MINUS_SRC_ALPHA,
|
||||||
|
batch=None, group=None, subpixel=False, program=None):
|
||||||
|
"""Create a sprite.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`img` : `~pyglet.image.AbstractImage` or `~pyglet.image.Animation`
|
||||||
|
Image or animation to display.
|
||||||
|
`x` : int
|
||||||
|
X coordinate of the sprite.
|
||||||
|
`y` : int
|
||||||
|
Y coordinate of the sprite.
|
||||||
|
`z` : int
|
||||||
|
Z coordinate of the sprite.
|
||||||
|
`blend_src` : int
|
||||||
|
OpenGL blend source mode. The default is suitable for
|
||||||
|
compositing sprites drawn from back-to-front.
|
||||||
|
`blend_dest` : int
|
||||||
|
OpenGL blend destination mode. The default is suitable for
|
||||||
|
compositing sprites drawn from back-to-front.
|
||||||
|
`batch` : `~pyglet.graphics.Batch`
|
||||||
|
Optional batch to add the sprite to.
|
||||||
|
`group` : `~pyglet.graphics.Group`
|
||||||
|
Optional parent group of the sprite.
|
||||||
|
`subpixel` : bool
|
||||||
|
Allow floating-point coordinates for the sprite. By default,
|
||||||
|
coordinates are restricted to integer values.
|
||||||
|
`program` : `~pyglet.graphics.shader.ShaderProgram`
|
||||||
|
A custom shader to use. This shader program must contain the
|
||||||
|
exact same attribute names and types as the default shader.
|
||||||
|
The class methods and properties depend on this, and will
|
||||||
|
crash otherwise.
|
||||||
|
"""
|
||||||
|
self._x = x
|
||||||
|
self._y = y
|
||||||
|
self._z = z
|
||||||
|
self._img = img
|
||||||
|
|
||||||
|
if isinstance(img, image.Animation):
|
||||||
|
self._animation = img
|
||||||
|
self._texture = img.frames[0].image.get_texture()
|
||||||
|
self._next_dt = img.frames[0].duration
|
||||||
|
if self._next_dt:
|
||||||
|
clock.schedule_once(self._animate, self._next_dt)
|
||||||
|
else:
|
||||||
|
self._texture = img.get_texture()
|
||||||
|
|
||||||
|
if not program:
|
||||||
|
if isinstance(img, image.TextureArrayRegion):
|
||||||
|
self._program = get_default_array_shader()
|
||||||
|
else:
|
||||||
|
self._program = get_default_shader()
|
||||||
|
else:
|
||||||
|
self._program = program
|
||||||
|
|
||||||
|
self._batch = batch or graphics.get_default_batch()
|
||||||
|
self._user_group = group
|
||||||
|
self._group = self.group_class(self._texture, blend_src, blend_dest, self.program, group)
|
||||||
|
self._subpixel = subpixel
|
||||||
|
|
||||||
|
self._create_vertex_list()
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
texture = self._texture
|
||||||
|
self._vertex_list = self.program.vertex_list(
|
||||||
|
1, GL_POINTS, self._batch, self._group,
|
||||||
|
position=('f', (self._x, self._y, self._z)),
|
||||||
|
size=('f', (texture.width, texture.height, 1, 1)),
|
||||||
|
color=('Bn', self._rgba),
|
||||||
|
texture_uv=('f', texture.uv),
|
||||||
|
rotation=('f', (self._rotation,)))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def program(self):
|
||||||
|
return self._program
|
||||||
|
|
||||||
|
@program.setter
|
||||||
|
def program(self, program):
|
||||||
|
if self._program == program:
|
||||||
|
return
|
||||||
|
self._group = self.group_class(self._texture,
|
||||||
|
self._group.blend_src,
|
||||||
|
self._group.blend_dest,
|
||||||
|
program,
|
||||||
|
self._user_group)
|
||||||
|
self._batch.migrate(self._vertex_list, GL_POINTS, self._group, self._batch)
|
||||||
|
self._program = program
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
"""Force immediate removal of the sprite from video memory.
|
||||||
|
|
||||||
|
This is often necessary when using batches, as the Python garbage
|
||||||
|
collector will not necessarily call the finalizer as soon as the
|
||||||
|
sprite is garbage.
|
||||||
|
"""
|
||||||
|
if self._animation:
|
||||||
|
clock.unschedule(self._animate)
|
||||||
|
self._vertex_list.delete()
|
||||||
|
self._vertex_list = None
|
||||||
|
self._texture = None
|
||||||
|
self._group = None
|
||||||
|
|
||||||
|
def _animate(self, dt):
|
||||||
|
self._frame_index += 1
|
||||||
|
if self._frame_index >= len(self._animation.frames):
|
||||||
|
self._frame_index = 0
|
||||||
|
self.dispatch_event('on_animation_end')
|
||||||
|
if self._vertex_list is None:
|
||||||
|
return # Deleted in event handler.
|
||||||
|
|
||||||
|
frame = self._animation.frames[self._frame_index]
|
||||||
|
self._set_texture(frame.image.get_texture())
|
||||||
|
|
||||||
|
if frame.duration is not None:
|
||||||
|
duration = frame.duration - (self._next_dt - dt)
|
||||||
|
duration = min(max(0, duration), frame.duration)
|
||||||
|
clock.schedule_once(self._animate, duration)
|
||||||
|
self._next_dt = duration
|
||||||
|
else:
|
||||||
|
self.dispatch_event('on_animation_end')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def batch(self):
|
||||||
|
"""Graphics batch.
|
||||||
|
|
||||||
|
The sprite can be migrated from one batch to another, or removed from
|
||||||
|
its batch (for individual drawing). Note that this can be an expensive
|
||||||
|
operation.
|
||||||
|
|
||||||
|
:type: :py:class:`pyglet.graphics.Batch`
|
||||||
|
"""
|
||||||
|
return self._batch
|
||||||
|
|
||||||
|
@batch.setter
|
||||||
|
def batch(self, batch):
|
||||||
|
if self._batch == batch:
|
||||||
|
return
|
||||||
|
|
||||||
|
if batch is not None and self._batch is not None:
|
||||||
|
self._batch.migrate(self._vertex_list, GL_POINTS, self._group, batch)
|
||||||
|
self._batch = batch
|
||||||
|
else:
|
||||||
|
self._vertex_list.delete()
|
||||||
|
self._batch = batch
|
||||||
|
self._create_vertex_list()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def group(self):
|
||||||
|
"""Parent graphics group.
|
||||||
|
|
||||||
|
The sprite can change its rendering group, however this can be an
|
||||||
|
expensive operation.
|
||||||
|
|
||||||
|
:type: :py:class:`pyglet.graphics.Group`
|
||||||
|
"""
|
||||||
|
return self._group.parent
|
||||||
|
|
||||||
|
@group.setter
|
||||||
|
def group(self, group):
|
||||||
|
if self._group.parent == group:
|
||||||
|
return
|
||||||
|
self._group = self.group_class(self._texture,
|
||||||
|
self._group.blend_src,
|
||||||
|
self._group.blend_dest,
|
||||||
|
self._group.program,
|
||||||
|
group)
|
||||||
|
self._batch.migrate(self._vertex_list, GL_POINTS, self._group, self._batch)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def image(self):
|
||||||
|
"""Image or animation to display.
|
||||||
|
|
||||||
|
:type: :py:class:`~pyglet.image.AbstractImage` or
|
||||||
|
:py:class:`~pyglet.image.Animation`
|
||||||
|
"""
|
||||||
|
if self._animation:
|
||||||
|
return self._animation
|
||||||
|
return self._texture
|
||||||
|
|
||||||
|
@image.setter
|
||||||
|
def image(self, img):
|
||||||
|
if self._animation is not None:
|
||||||
|
clock.unschedule(self._animate)
|
||||||
|
self._animation = None
|
||||||
|
|
||||||
|
if isinstance(img, image.Animation):
|
||||||
|
self._animation = img
|
||||||
|
self._frame_index = 0
|
||||||
|
self._set_texture(img.frames[0].image.get_texture())
|
||||||
|
self._next_dt = img.frames[0].duration
|
||||||
|
if self._next_dt:
|
||||||
|
clock.schedule_once(self._animate, self._next_dt)
|
||||||
|
else:
|
||||||
|
self._set_texture(img.get_texture())
|
||||||
|
|
||||||
|
def _set_texture(self, texture):
|
||||||
|
if texture.id is not self._texture.id:
|
||||||
|
self._group = self._group.__class__(texture,
|
||||||
|
self._group.blend_src,
|
||||||
|
self._group.blend_dest,
|
||||||
|
self._group.program,
|
||||||
|
self._group.parent)
|
||||||
|
self._vertex_list.delete()
|
||||||
|
self._texture = texture
|
||||||
|
self._create_vertex_list()
|
||||||
|
else:
|
||||||
|
self._vertex_list.texture_uv[:] = texture.uv
|
||||||
|
self._texture = texture
|
||||||
|
|
||||||
|
@property
|
||||||
|
def position(self):
|
||||||
|
"""The (x, y, z) coordinates of the sprite, as a tuple.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`x` : int
|
||||||
|
X coordinate of the sprite.
|
||||||
|
`y` : int
|
||||||
|
Y coordinate of the sprite.
|
||||||
|
`z` : int
|
||||||
|
Z coordinate of the sprite.
|
||||||
|
"""
|
||||||
|
return self._x, self._y, self._z
|
||||||
|
|
||||||
|
@position.setter
|
||||||
|
def position(self, position):
|
||||||
|
self._x, self._y, self._z = position
|
||||||
|
self._vertex_list.position[:] = position
|
||||||
|
|
||||||
|
@property
|
||||||
|
def x(self):
|
||||||
|
"""X coordinate of the sprite.
|
||||||
|
|
||||||
|
:type: int
|
||||||
|
"""
|
||||||
|
return self._x
|
||||||
|
|
||||||
|
@x.setter
|
||||||
|
def x(self, x):
|
||||||
|
self._x = x
|
||||||
|
self._vertex_list.position[:] = x, self._y, self._z
|
||||||
|
|
||||||
|
@property
|
||||||
|
def y(self):
|
||||||
|
"""Y coordinate of the sprite.
|
||||||
|
|
||||||
|
:type: int
|
||||||
|
"""
|
||||||
|
return self._y
|
||||||
|
|
||||||
|
@y.setter
|
||||||
|
def y(self, y):
|
||||||
|
self._y = y
|
||||||
|
self._vertex_list.position[:] = self._x, y, self._z
|
||||||
|
|
||||||
|
@property
|
||||||
|
def z(self):
|
||||||
|
"""Z coordinate of the sprite.
|
||||||
|
|
||||||
|
:type: int
|
||||||
|
"""
|
||||||
|
return self._z
|
||||||
|
|
||||||
|
@z.setter
|
||||||
|
def z(self, z):
|
||||||
|
self._z = z
|
||||||
|
self._vertex_list.position[:] = self._x, self._y, z
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rotation(self):
|
||||||
|
"""Clockwise rotation of the sprite, in degrees.
|
||||||
|
|
||||||
|
The sprite image will be rotated about its image's (anchor_x, anchor_y)
|
||||||
|
position.
|
||||||
|
|
||||||
|
:type: float
|
||||||
|
"""
|
||||||
|
return self._rotation
|
||||||
|
|
||||||
|
@rotation.setter
|
||||||
|
def rotation(self, rotation):
|
||||||
|
self._rotation = rotation
|
||||||
|
self._vertex_list.rotation[0] = self._rotation
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scale(self):
|
||||||
|
"""Base Scaling factor.
|
||||||
|
|
||||||
|
A scaling factor of 1 (the default) has no effect. A scale of 2 will
|
||||||
|
draw the sprite at twice the native size of its image.
|
||||||
|
|
||||||
|
:type: float
|
||||||
|
"""
|
||||||
|
return self._scale
|
||||||
|
|
||||||
|
@scale.setter
|
||||||
|
def scale(self, scale):
|
||||||
|
self._scale = scale
|
||||||
|
self._vertex_list.scale[:] = scale * self._scale_x, scale * self._scale_y
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scale_x(self):
|
||||||
|
"""Horizontal scaling factor.
|
||||||
|
|
||||||
|
A scaling factor of 1 (the default) has no effect. A scale of 2 will
|
||||||
|
draw the sprite at twice the native width of its image.
|
||||||
|
|
||||||
|
:type: float
|
||||||
|
"""
|
||||||
|
return self._scale_x
|
||||||
|
|
||||||
|
@scale_x.setter
|
||||||
|
def scale_x(self, scale_x):
|
||||||
|
self._scale_x = scale_x
|
||||||
|
self._vertex_list.scale[:] = self._scale * scale_x, self._scale * self._scale_y
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scale_y(self):
|
||||||
|
"""Vertical scaling factor.
|
||||||
|
|
||||||
|
A scaling factor of 1 (the default) has no effect. A scale of 2 will
|
||||||
|
draw the sprite at twice the native height of its image.
|
||||||
|
|
||||||
|
:type: float
|
||||||
|
"""
|
||||||
|
return self._scale_y
|
||||||
|
|
||||||
|
@scale_y.setter
|
||||||
|
def scale_y(self, scale_y):
|
||||||
|
self._scale_y = scale_y
|
||||||
|
self._vertex_list.scale[:] = self._scale * self._scale_x, self._scale * scale_y
|
||||||
|
|
||||||
|
def update(self, x=None, y=None, z=None, rotation=None, scale=None, scale_x=None, scale_y=None):
|
||||||
|
"""Simultaneously change the position, rotation or scale.
|
||||||
|
|
||||||
|
This method is provided for convenience. There is not much
|
||||||
|
performance benefit to updating multiple Sprite attributes at once.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`x` : int
|
||||||
|
X coordinate of the sprite.
|
||||||
|
`y` : int
|
||||||
|
Y coordinate of the sprite.
|
||||||
|
`z` : int
|
||||||
|
Z coordinate of the sprite.
|
||||||
|
`rotation` : float
|
||||||
|
Clockwise rotation of the sprite, in degrees.
|
||||||
|
`scale` : float
|
||||||
|
Scaling factor.
|
||||||
|
`scale_x` : float
|
||||||
|
Horizontal scaling factor.
|
||||||
|
`scale_y` : float
|
||||||
|
Vertical scaling factor.
|
||||||
|
"""
|
||||||
|
|
||||||
|
translations_outdated = False
|
||||||
|
|
||||||
|
# only bother updating if the translation actually changed
|
||||||
|
if x is not None:
|
||||||
|
self._x = x
|
||||||
|
translations_outdated = True
|
||||||
|
if y is not None:
|
||||||
|
self._y = y
|
||||||
|
translations_outdated = True
|
||||||
|
if z is not None:
|
||||||
|
self._z = z
|
||||||
|
translations_outdated = True
|
||||||
|
|
||||||
|
if translations_outdated:
|
||||||
|
self._vertex_list.position[:] = (self._x, self._y, self._z)
|
||||||
|
|
||||||
|
if rotation is not None and rotation != self._rotation:
|
||||||
|
self._rotation = rotation
|
||||||
|
self._vertex_list.rotation[:] = rotation
|
||||||
|
|
||||||
|
scales_outdated = False
|
||||||
|
|
||||||
|
# only bother updating if the scale actually changed
|
||||||
|
if scale is not None:
|
||||||
|
self._scale = scale
|
||||||
|
scales_outdated = True
|
||||||
|
if scale_x is not None:
|
||||||
|
self._scale_x = scale_x
|
||||||
|
scales_outdated = True
|
||||||
|
if scale_y is not None:
|
||||||
|
self._scale_y = scale_y
|
||||||
|
scales_outdated = True
|
||||||
|
|
||||||
|
if scales_outdated:
|
||||||
|
self._vertex_list.scale[:] = self._scale * self._scale_x, self._scale * self._scale_y
|
||||||
|
|
||||||
|
@property
|
||||||
|
def width(self):
|
||||||
|
"""Scaled width of the sprite.
|
||||||
|
|
||||||
|
Invariant under rotation.
|
||||||
|
|
||||||
|
:type: int
|
||||||
|
"""
|
||||||
|
w = self._texture.width * abs(self._scale_x) * abs(self._scale)
|
||||||
|
return w if self._subpixel else int(w)
|
||||||
|
|
||||||
|
@width.setter
|
||||||
|
def width(self, width):
|
||||||
|
self.scale_x = width / (self._texture.width * abs(self._scale))
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def height(self):
|
||||||
|
"""Scaled height of the sprite.
|
||||||
|
|
||||||
|
Invariant under rotation.
|
||||||
|
|
||||||
|
:type: int
|
||||||
|
"""
|
||||||
|
h = self._texture.height * abs(self._scale_y) * abs(self._scale)
|
||||||
|
return h if self._subpixel else int(h)
|
||||||
|
|
||||||
|
@height.setter
|
||||||
|
def height(self, height):
|
||||||
|
self.scale_y = height / (self._texture.height * abs(self._scale))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def opacity(self):
|
||||||
|
"""Blend opacity.
|
||||||
|
|
||||||
|
This property sets the alpha component of the colour of the sprite's
|
||||||
|
vertices. With the default blend mode (see the constructor), this
|
||||||
|
allows the sprite to be drawn with fractional opacity, blending with the
|
||||||
|
background.
|
||||||
|
|
||||||
|
An opacity of 255 (the default) has no effect. An opacity of 128 will
|
||||||
|
make the sprite appear translucent.
|
||||||
|
|
||||||
|
:type: int
|
||||||
|
"""
|
||||||
|
return self._rgba[3]
|
||||||
|
|
||||||
|
@opacity.setter
|
||||||
|
def opacity(self, opacity):
|
||||||
|
self._rgba[3] = opacity
|
||||||
|
self._vertex_list.color[:] = self._rgba
|
||||||
|
|
||||||
|
@property
|
||||||
|
def color(self):
|
||||||
|
"""Blend color.
|
||||||
|
|
||||||
|
This property sets the color of the sprite's vertices. This allows the
|
||||||
|
sprite to be drawn with a color tint.
|
||||||
|
|
||||||
|
The color is specified as an RGB tuple of integers '(red, green, blue)'.
|
||||||
|
Each color component must be in the range 0 (dark) to 255 (saturated).
|
||||||
|
|
||||||
|
:type: (int, int, int)
|
||||||
|
"""
|
||||||
|
return self._rgba[:3]
|
||||||
|
|
||||||
|
@color.setter
|
||||||
|
def color(self, rgb):
|
||||||
|
self._rgba[:3] = list(map(int, rgb))
|
||||||
|
self._vertex_list.color[:] = self._rgba
|
||||||
|
|
||||||
|
@property
|
||||||
|
def visible(self):
|
||||||
|
"""True if the sprite will be drawn.
|
||||||
|
|
||||||
|
:type: bool
|
||||||
|
"""
|
||||||
|
return self._visible
|
||||||
|
|
||||||
|
@visible.setter
|
||||||
|
def visible(self, visible):
|
||||||
|
self._visible = visible
|
||||||
|
self._vertex_list.texture_uv[:] = (0, 0, 0, 0) if not visible else self._texture.uv
|
||||||
|
|
||||||
|
@property
|
||||||
|
def paused(self):
|
||||||
|
"""Pause/resume the Sprite's Animation
|
||||||
|
|
||||||
|
If `Sprite.image` is an Animation, you can pause or resume
|
||||||
|
the animation by setting this property to True or False.
|
||||||
|
If not an Animation, this has no effect.
|
||||||
|
|
||||||
|
:type: bool
|
||||||
|
"""
|
||||||
|
return self._paused
|
||||||
|
|
||||||
|
@paused.setter
|
||||||
|
def paused(self, pause):
|
||||||
|
if not hasattr(self, '_animation') or pause == self._paused:
|
||||||
|
return
|
||||||
|
if pause is True:
|
||||||
|
clock.unschedule(self._animate)
|
||||||
|
else:
|
||||||
|
frame = self._animation.frames[self._frame_index]
|
||||||
|
self._next_dt = frame.duration
|
||||||
|
if self._next_dt:
|
||||||
|
clock.schedule_once(self._animate, self._next_dt)
|
||||||
|
self._paused = pause
|
||||||
|
|
||||||
|
@property
|
||||||
|
def frame_index(self):
|
||||||
|
"""The current Animation frame.
|
||||||
|
|
||||||
|
If the `Sprite.image` is an `Animation`,
|
||||||
|
you can query or set the current frame.
|
||||||
|
If not an Animation, this will always
|
||||||
|
be 0.
|
||||||
|
|
||||||
|
:type: int
|
||||||
|
"""
|
||||||
|
return self._frame_index
|
||||||
|
|
||||||
|
@frame_index.setter
|
||||||
|
def frame_index(self, index):
|
||||||
|
# Bound to available number of frames
|
||||||
|
if self._animation is None:
|
||||||
|
return
|
||||||
|
self._frame_index = max(0, min(index, len(self._animation.frames)-1))
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
"""Draw the sprite at its current position.
|
||||||
|
|
||||||
|
See the module documentation for hints on drawing multiple sprites
|
||||||
|
efficiently.
|
||||||
|
"""
|
||||||
|
self._group.set_state_recursive()
|
||||||
|
self._vertex_list.draw(GL_POINTS)
|
||||||
|
self._group.unset_state_recursive()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
try:
|
||||||
|
if self._vertex_list is not None:
|
||||||
|
self._vertex_list.delete()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if _is_pyglet_doc_run:
|
||||||
|
def on_animation_end(self):
|
||||||
|
"""The sprite animation reached the final frame.
|
||||||
|
|
||||||
|
The event is triggered only if the sprite has an animation, not an
|
||||||
|
image. For looping animations, the event is triggered each time
|
||||||
|
the animation loops.
|
||||||
|
|
||||||
|
:event:
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
Sprite.register_event_type('on_animation_end')
|
@ -68,10 +68,10 @@ class Frame:
|
|||||||
widget.on_mouse_drag(x, y, dx, dy, buttons, modifiers)
|
widget.on_mouse_drag(x, y, dx, dy, buttons, modifiers)
|
||||||
self._mouse_pos = x, y
|
self._mouse_pos = x, y
|
||||||
|
|
||||||
def on_mouse_scroll(self, x, y, index, direction):
|
def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
|
||||||
"""Pass the event to any widgets within range of the mouse"""
|
"""Pass the event to any widgets within range of the mouse"""
|
||||||
for widget in self._cells.get(self._hash(x, y), set()):
|
for widget in self._cells.get(self._hash(x, y), set()):
|
||||||
widget.on_mouse_scroll(x, y, index, direction)
|
widget.on_mouse_scroll(x, y, scroll_x, scroll_y)
|
||||||
|
|
||||||
def on_mouse_motion(self, x, y, dx, dy):
|
def on_mouse_motion(self, x, y, dx, dy):
|
||||||
"""Pass the event to any widgets within range of the mouse"""
|
"""Pass the event to any widgets within range of the mouse"""
|
||||||
|
@ -122,7 +122,7 @@ class WidgetBase(EventDispatcher):
|
|||||||
def on_mouse_motion(self, x, y, dx, dy):
|
def on_mouse_motion(self, x, y, dx, dy):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_mouse_scroll(self, x, y, mouse, direction):
|
def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_text(self, text):
|
def on_text(self, text):
|
||||||
@ -348,11 +348,11 @@ class Slider(WidgetBase):
|
|||||||
if self._in_update:
|
if self._in_update:
|
||||||
self._update_knob(x)
|
self._update_knob(x)
|
||||||
|
|
||||||
def on_mouse_scroll(self, x, y, mouse, direction):
|
def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
|
||||||
if not self.enabled:
|
if not self.enabled:
|
||||||
return
|
return
|
||||||
if self._check_hit(x, y):
|
if self._check_hit(x, y):
|
||||||
self._update_knob(self._knob_spr.x + self._half_knob_width + direction)
|
self._update_knob(self._knob_spr.x + self._half_knob_width + scroll_y)
|
||||||
|
|
||||||
def on_mouse_release(self, x, y, buttons, modifiers):
|
def on_mouse_release(self, x, y, buttons, modifiers):
|
||||||
if not self.enabled:
|
if not self.enabled:
|
||||||
|
@ -1427,6 +1427,12 @@ class Texture(AbstractImage):
|
|||||||
order = self.tex_coords_order
|
order = self.tex_coords_order
|
||||||
self.tex_coords_order = (order[bl], order[br], order[tr], order[tl])
|
self.tex_coords_order = (order[bl], order[br], order[tr], order[tl])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uv(self):
|
||||||
|
"""Tuple containing the left, bottom, right, top 2D texture coordinates."""
|
||||||
|
tex_coords = self.tex_coords
|
||||||
|
return tex_coords[0], tex_coords[1], tex_coords[3], tex_coords[7]
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "{}(id={}, size={}x{})".format(self.__class__.__name__, self.id, self.width, self.height)
|
return "{}(id={}, size={}x{})".format(self.__class__.__name__, self.id, self.width, self.height)
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ class Allocator:
|
|||||||
class TextureAtlas:
|
class TextureAtlas:
|
||||||
"""Collection of images within a texture."""
|
"""Collection of images within a texture."""
|
||||||
|
|
||||||
def __init__(self, width: int=2048, height: int=2048) -> None:
|
def __init__(self, width: int = 2048, height: int = 2048) -> None:
|
||||||
"""Create a texture atlas of the given size.
|
"""Create a texture atlas of the given size.
|
||||||
|
|
||||||
:Parameters:
|
:Parameters:
|
||||||
@ -252,7 +252,7 @@ class TextureArrayBin:
|
|||||||
ones as necessary as the depth is exceeded.
|
ones as necessary as the depth is exceeded.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, texture_width: int=2048, texture_height: int=2048, max_depth: Optional[int]=None) -> None:
|
def __init__(self, texture_width: int = 2048, texture_height: int = 2048, max_depth: Optional[int] = None) -> None:
|
||||||
max_texture_size = pyglet.image.get_max_texture_size()
|
max_texture_size = pyglet.image.get_max_texture_size()
|
||||||
self.max_depth = max_depth or pyglet.image.get_max_array_texture_layers()
|
self.max_depth = max_depth or pyglet.image.get_max_array_texture_layers()
|
||||||
self.texture_width = min(texture_width, max_texture_size)
|
self.texture_width = min(texture_width, max_texture_size)
|
||||||
|
@ -9,9 +9,9 @@ for creating orthographic and perspective projection matrixes.
|
|||||||
Matrices behave just like they do in GLSL: they are specified in column-major
|
Matrices behave just like they do in GLSL: they are specified in column-major
|
||||||
order and multiply on the left of vectors, which are treated as columns.
|
order and multiply on the left of vectors, which are treated as columns.
|
||||||
|
|
||||||
:note: For performance, Matrixes subclass the `tuple` type. They
|
.. note:: For performance reasons, Matrix types subclass `tuple`. They are
|
||||||
are therefore immutable - all operations return a new object;
|
therefore immutable. All operations return a new object; the object
|
||||||
the object is not updated in-place.
|
is not updated in-place.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@ -719,29 +719,23 @@ class Mat3(tuple):
|
|||||||
|
|
||||||
|
|
||||||
class Mat4(tuple):
|
class Mat4(tuple):
|
||||||
"""A 4x4 Matrix class
|
|
||||||
|
|
||||||
`Mat4` is an immutable 4x4 Matrix, including most common
|
|
||||||
operators. Matrix multiplication must be performed using
|
|
||||||
the "@" operator.
|
|
||||||
Class methods are available for creating orthogonal
|
|
||||||
and perspective projections matrixes.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __new__(cls, values: _Iterable[float] = (1.0, 0.0, 0.0, 0.0,
|
def __new__(cls, values: _Iterable[float] = (1.0, 0.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 0.0, 0.0,
|
0.0, 1.0, 0.0, 0.0,
|
||||||
0.0, 0.0, 1.0, 0.0,
|
0.0, 0.0, 1.0, 0.0,
|
||||||
0.0, 0.0, 0.0, 1.0,)) -> Mat4:
|
0.0, 0.0, 0.0, 1.0,)) -> Mat4:
|
||||||
"""Create a 4x4 Matrix
|
"""Create a 4x4 Matrix.
|
||||||
|
|
||||||
|
`Mat4` is an immutable 4x4 Matrix, which includs most common
|
||||||
|
operators. This includes class methods for creating orthogonal
|
||||||
|
and perspective projection matrixes, to be used by OpenGL.
|
||||||
|
|
||||||
A Matrix can be created with a list or tuple of 16 values.
|
A Matrix can be created with a list or tuple of 16 values.
|
||||||
If no values are provided, an "identity matrix" will be created
|
If no values are provided, an "identity matrix" will be created
|
||||||
(1.0 on the main diagonal). Matrix objects are immutable, so
|
(1.0 on the main diagonal). Matrix objects are immutable, so
|
||||||
all operations return a new Mat4 object.
|
all operations return a new Mat4 object.
|
||||||
|
|
||||||
:Parameters:
|
.. note:: Matrix multiplication is performed using the "@" operator.
|
||||||
`values` : tuple of float or int
|
|
||||||
A tuple or list containing 16 floats or ints.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
new = super().__new__(Mat4, values)
|
new = super().__new__(Mat4, values)
|
||||||
@ -749,19 +743,12 @@ class Mat4(tuple):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def orthogonal_projection(
|
def orthogonal_projection(cls: type[Mat4T], left: float, right: float, bottom: float, top: float, z_near: float, z_far: float) -> Mat4T:
|
||||||
cls: type[Mat4T],
|
|
||||||
left: float,
|
|
||||||
right: float,
|
|
||||||
bottom: float,
|
|
||||||
top: float,
|
|
||||||
z_near: float,
|
|
||||||
z_far: float
|
|
||||||
) -> Mat4T:
|
|
||||||
"""Create a Mat4 orthographic projection matrix for use with OpenGL.
|
"""Create a Mat4 orthographic projection matrix for use with OpenGL.
|
||||||
|
|
||||||
This matrix doesn't actually perform the projection; it transforms the
|
Given left, right, bottom, top values, and near/far z planes,
|
||||||
space so that OpenGL's vertex processing performs it.
|
create a 4x4 Projection Matrix. This is useful for setting
|
||||||
|
:py:attr:`~pyglet.window.Window.projection`.
|
||||||
"""
|
"""
|
||||||
width = right - left
|
width = right - left
|
||||||
height = top - bottom
|
height = top - bottom
|
||||||
@ -781,24 +768,12 @@ class Mat4(tuple):
|
|||||||
tx, ty, tz, 1.0))
|
tx, ty, tz, 1.0))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def perspective_projection(
|
def perspective_projection(cls: type[Mat4T], aspect: float, z_near: float, z_far: float, fov: float = 60) -> Mat4T:
|
||||||
cls: type[Mat4T],
|
"""Create a Mat4 perspective projection matrix for use with OpenGL.
|
||||||
aspect: float,
|
|
||||||
z_near: float,
|
|
||||||
z_far: float,
|
|
||||||
fov: float = 60
|
|
||||||
) -> Mat4T:
|
|
||||||
"""
|
|
||||||
Create a Mat4 perspective projection matrix for use with OpenGL.
|
|
||||||
|
|
||||||
This matrix doesn't actually perform the projection; it transforms the
|
Given a desired aspect ratio, near/far planes, and fov (field of view),
|
||||||
space so that OpenGL's vertex processing performs it.
|
create a 4x4 Projection Matrix. This is useful for setting
|
||||||
|
:py:attr:`~pyglet.window.Window.projection`.
|
||||||
: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)
|
xy_max = z_near * _math.tan(fov * _math.pi / 360)
|
||||||
y_min = -xy_max
|
y_min = -xy_max
|
||||||
|
@ -139,6 +139,7 @@ def parse_obj_file(filename, file=None):
|
|||||||
|
|
||||||
elif values[0] == 'o':
|
elif values[0] == 'o':
|
||||||
mesh = Mesh(name=values[1])
|
mesh = Mesh(name=values[1])
|
||||||
|
mesh.material = default_material
|
||||||
mesh_list.append(mesh)
|
mesh_list.append(mesh)
|
||||||
|
|
||||||
elif values[0] == 'f':
|
elif values[0] == 'f':
|
||||||
|
@ -1792,7 +1792,7 @@ class Polygon(ShapeBase):
|
|||||||
:Parameters:
|
:Parameters:
|
||||||
`coordinates` : List[[int, int]]
|
`coordinates` : List[[int, int]]
|
||||||
The coordinates for each point in the polygon.
|
The coordinates for each point in the polygon.
|
||||||
`color` : (int, int, int)
|
`color` : (int, int, int, int)
|
||||||
The RGB or RGBA color of the polygon, specified as a
|
The RGB or RGBA color of the polygon, specified as a
|
||||||
tuple of 3 or 4 ints in the range of 0-255. RGB colors
|
tuple of 3 or 4 ints in the range of 0-255. RGB colors
|
||||||
will be treated as having an opacity of 255.
|
will be treated as having an opacity of 255.
|
||||||
|
@ -284,11 +284,7 @@ pub mod part_list {
|
|||||||
explosion_power: Some(0u32),
|
explosion_power: Some(0u32),
|
||||||
explosion_size: Some(0u32),
|
explosion_size: Some(0u32),
|
||||||
});
|
});
|
||||||
let attach_points: Option<Vec<AttachPoint>> = if let Some(attach_points) = &self.attach_points {
|
let attach_points: Option<Vec<AttachPoint>> = self.attach_points.as_ref().map(|attach_points| attach_points.unzip());
|
||||||
Some(attach_points.unzip())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
SR1PartType {
|
SR1PartType {
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
@ -591,7 +587,12 @@ pub mod ship {
|
|||||||
impl RawShip {
|
impl RawShip {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_file(path: String) -> Option<RawShip> {
|
pub fn from_file(path: String) -> Option<RawShip> {
|
||||||
let ship_file = fs::read_to_string(path).unwrap();
|
let ship_file = fs::read_to_string(path); // for encoding error
|
||||||
|
if let Err(e) = ship_file {
|
||||||
|
println!("ERROR!\n{:?}\n----------", e);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let ship_file = ship_file.unwrap();
|
||||||
let ship = from_str(&ship_file);
|
let ship = from_str(&ship_file);
|
||||||
match ship {
|
match ship {
|
||||||
Ok(ship) => Some(ship),
|
Ok(ship) => Some(ship),
|
||||||
|
@ -4,16 +4,12 @@
|
|||||||
# All rights reserved
|
# All rights reserved
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
# import math
|
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from defusedxml.ElementTree import parse
|
|
||||||
from xml.etree.ElementTree import Element, ElementTree
|
|
||||||
from typing import List, TYPE_CHECKING, Union, Dict, Optional, Generator, Tuple
|
from typing import List, TYPE_CHECKING, Union, Dict, Optional, Generator, Tuple
|
||||||
|
|
||||||
from pyglet.math import Vec4
|
from pyglet.math import Vec4
|
||||||
@ -23,8 +19,6 @@ from pyglet.sprite import Sprite
|
|||||||
from pyglet.graphics import Batch, Group
|
from pyglet.graphics import Batch, Group
|
||||||
from pyglet.shapes import Line, Rectangle
|
from pyglet.shapes import Line, Rectangle
|
||||||
from pyglet.image import Framebuffer, Texture
|
from pyglet.image import Framebuffer, Texture
|
||||||
# pyglet OpenGL
|
|
||||||
from pyglet.gl import glViewport
|
|
||||||
|
|
||||||
from . import DR_mod_runtime
|
from . import DR_mod_runtime
|
||||||
|
|
||||||
@ -35,7 +29,7 @@ from Difficult_Rocket.api.camera import CenterCamera
|
|||||||
from Difficult_Rocket.api.types import Fonts, Options
|
from Difficult_Rocket.api.types import Fonts, Options
|
||||||
from Difficult_Rocket.command.line import CommandText
|
from Difficult_Rocket.command.line import CommandText
|
||||||
from Difficult_Rocket.client.screen import BaseScreen
|
from Difficult_Rocket.client.screen import BaseScreen
|
||||||
from .types import SR1Textures, SR1PartTexture, SR1PartData, SR1Rotation, xml_bool
|
from .types import SR1Textures, SR1Rotation
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from Difficult_Rocket.client import ClientWindow
|
from Difficult_Rocket.client import ClientWindow
|
||||||
@ -78,7 +72,7 @@ class SR1ShipRender(BaseScreen):
|
|||||||
load_start_time = time.time_ns()
|
load_start_time = time.time_ns()
|
||||||
# status
|
# status
|
||||||
self.status = SR1ShipRenderStatus()
|
self.status = SR1ShipRenderStatus()
|
||||||
|
|
||||||
self.dx = 0
|
self.dx = 0
|
||||||
self.dy = 0
|
self.dy = 0
|
||||||
self.width = main_window.width - 100
|
self.width = main_window.width - 100
|
||||||
@ -184,25 +178,23 @@ class SR1ShipRender(BaseScreen):
|
|||||||
|
|
||||||
batch.append(part_sprite)
|
batch.append(part_sprite)
|
||||||
line_box_group = Group(6, parent=self.part_group)
|
line_box_group = Group(6, parent=self.part_group)
|
||||||
part_debug_box = self.rust_ship.get_part_box(p_id)
|
part_box = self.rust_ship.get_part_box(p_id)
|
||||||
if part_debug_box:
|
if part_box:
|
||||||
# 线框
|
# 线框
|
||||||
part_line_box = []
|
part_line_box = []
|
||||||
width = 4
|
width = 4
|
||||||
color = (random.randrange(0, 255), random.randrange(0, 255), random.randrange(0, 255),
|
color = (random.randrange(0, 255), random.randrange(0, 255), random.randrange(0, 255),
|
||||||
random.randrange(100, 200))
|
random.randrange(100, 200))
|
||||||
part_line_box.append(Line(x=part_debug_box[0][0] * 30, y=part_debug_box[0][1] * 30,
|
(x, y), (x2, y2) = part_box
|
||||||
x2=part_debug_box[0][0] * 30, y2=part_debug_box[1][1] * 30,
|
part_line_box.append(Line(x=x * 30, y=y * 30, x2=x * 30, y2=y2 * 30,
|
||||||
batch=self.main_batch, width=width, color=color, group=line_box_group))
|
batch=self.main_batch, width=width, color=color, group=line_box_group))
|
||||||
part_line_box.append(Line(x=part_debug_box[0][0] * 30, y=part_debug_box[1][1] * 30,
|
part_line_box.append(Line(x=x * 30, y=y2 * 30, x2=x2 * 30, y2=y2 * 30,
|
||||||
x2=part_debug_box[1][0] * 30, y2=part_debug_box[1][1] * 30,
|
|
||||||
batch=self.main_batch, width=width, color=color, group=line_box_group))
|
batch=self.main_batch, width=width, color=color, group=line_box_group))
|
||||||
part_line_box.append(Line(x=part_debug_box[1][0] * 30, y=part_debug_box[1][1] * 30,
|
part_line_box.append(Line(x=x2 * 30, y=y2 * 30, x2=x2 * 30, y2=y * 30,
|
||||||
x2=part_debug_box[1][0] * 30, y2=part_debug_box[0][1] * 30,
|
|
||||||
batch=self.main_batch, width=width, color=color, group=line_box_group))
|
batch=self.main_batch, width=width, color=color, group=line_box_group))
|
||||||
part_line_box.append(Line(x=part_debug_box[1][0] * 30, y=part_debug_box[0][1] * 30,
|
part_line_box.append(Line(x=x2 * 30, y=y * 30, x2=x * 30, y2=y * 30,
|
||||||
x2=part_debug_box[0][0] * 30, y2=part_debug_box[0][1] * 30,
|
|
||||||
batch=self.main_batch, width=width, color=color, group=line_box_group))
|
batch=self.main_batch, width=width, color=color, group=line_box_group))
|
||||||
|
# 直接用循环得了
|
||||||
self.part_line_box[p_id] = part_line_box
|
self.part_line_box[p_id] = part_line_box
|
||||||
self.parts_sprite[p_id] = batch
|
self.parts_sprite[p_id] = batch
|
||||||
count += 1
|
count += 1
|
||||||
@ -291,11 +283,9 @@ class SR1ShipRender(BaseScreen):
|
|||||||
self.buffer.bind()
|
self.buffer.bind()
|
||||||
window.clear()
|
window.clear()
|
||||||
with self.camera:
|
with self.camera:
|
||||||
# glViewport(int(self.camera.dx), int(self.camera.dy), window.width // 2, window.height // 2)
|
|
||||||
self.main_batch.draw()
|
self.main_batch.draw()
|
||||||
# glViewport(0, 0, window.width, window.height)
|
|
||||||
self.buffer.unbind()
|
self.buffer.unbind()
|
||||||
self.render_texture.blit(x=0, y=0, z=0, width=self.width, height=self.height)
|
self.render_texture.blit(x=self.dx, y=self.dy, z=0, width=self.width, height=self.height)
|
||||||
|
|
||||||
def on_draw(self, window: "ClientWindow"):
|
def on_draw(self, window: "ClientWindow"):
|
||||||
if self.status.draw_call:
|
if self.status.draw_call:
|
||||||
@ -318,32 +308,42 @@ class SR1ShipRender(BaseScreen):
|
|||||||
return
|
return
|
||||||
self.render_d_line.x2 = width // 2
|
self.render_d_line.x2 = width // 2
|
||||||
self.render_d_line.y2 = height // 2
|
self.render_d_line.y2 = height // 2
|
||||||
self.size = width - 100, height - 100
|
|
||||||
|
|
||||||
def on_mouse_scroll(self, x: int, y: int, scroll_x: int, scroll_y: int, window: "ClientWindow"):
|
def on_mouse_scroll(self, x: int, y: int, scroll_x: int, scroll_y: int, window: "ClientWindow"):
|
||||||
if not self.status.draw_done:
|
if not self.status.draw_done:
|
||||||
return
|
return
|
||||||
mouse_dx = x - (window.width / 2)
|
if self.status.focus:
|
||||||
mouse_dy = y - (window.height / 2)
|
mouse_dx = x - (window.width / 2)
|
||||||
# 鼠标缩放位置相对于屏幕中心的位置
|
mouse_dy = y - (window.height / 2)
|
||||||
mouse_dx_d = mouse_dx - self.camera.dx
|
# 鼠标缩放位置相对于屏幕中心的位置
|
||||||
mouse_dy_d = mouse_dy - self.camera.dy
|
mouse_dx_d = mouse_dx - self.camera.dx
|
||||||
# 鼠标相对偏移量的偏移量
|
mouse_dy_d = mouse_dy - self.camera.dy
|
||||||
if scroll_y == 0:
|
# 鼠标相对偏移量的偏移量
|
||||||
zoom_d = 1
|
if scroll_y == 0:
|
||||||
else:
|
zoom_d = 1
|
||||||
zoom_d = ((2 ** scroll_y) - 1) * 0.5 + 1
|
|
||||||
# 缩放的变换量
|
|
||||||
if not (self.camera.zoom == 10 and scroll_y > 0):
|
|
||||||
if self.camera.zoom * zoom_d >= 10:
|
|
||||||
zoom_d = 10 / self.camera.zoom
|
|
||||||
self.camera.zoom = 10
|
|
||||||
else:
|
else:
|
||||||
self.camera.zoom *= zoom_d
|
zoom_d = ((2 ** scroll_y) - 1) * 0.5 + 1
|
||||||
mouse_dx_d *= (1 - zoom_d)
|
# 缩放的变换量
|
||||||
mouse_dy_d *= (1 - zoom_d)
|
if not (self.camera.zoom == 10 and scroll_y > 0):
|
||||||
self.camera.dx += mouse_dx_d
|
if self.camera.zoom * zoom_d >= 10:
|
||||||
self.camera.dy += mouse_dy_d
|
zoom_d = 10 / self.camera.zoom
|
||||||
|
self.camera.zoom = 10
|
||||||
|
else:
|
||||||
|
self.camera.zoom *= zoom_d
|
||||||
|
mouse_dx_d *= (1 - zoom_d)
|
||||||
|
mouse_dy_d *= (1 - zoom_d)
|
||||||
|
self.camera.dx += mouse_dx_d
|
||||||
|
self.camera.dy += mouse_dy_d
|
||||||
|
elif self.status.moving:
|
||||||
|
# 如果是在移动整体渲染位置
|
||||||
|
size_x, size_y = self.size
|
||||||
|
size_x += round(scroll_y) * 10
|
||||||
|
size_y += round(scroll_y) * 10
|
||||||
|
if size_x < 10:
|
||||||
|
size_x = 10
|
||||||
|
if size_y < 10:
|
||||||
|
size_y = 10
|
||||||
|
self.size = size_x, size_y
|
||||||
|
|
||||||
def on_command(self, command: CommandText, window: "ClientWindow"):
|
def on_command(self, command: CommandText, window: "ClientWindow"):
|
||||||
""" 解析命令 """
|
""" 解析命令 """
|
||||||
@ -427,7 +427,6 @@ class SR1ShipRender(BaseScreen):
|
|||||||
int(part_data[part][index][1].x * 60 + img_center[0]),
|
int(part_data[part][index][1].x * 60 + img_center[0]),
|
||||||
int(-part_data[part][index][1].y * 60 + img_center[1])))
|
int(-part_data[part][index][1].y * 60 + img_center[1])))
|
||||||
|
|
||||||
img.show("???")
|
|
||||||
img.save(f'test{time.time()}.png', 'PNG')
|
img.save(f'test{time.time()}.png', 'PNG')
|
||||||
|
|
||||||
elif command.find('test'):
|
elif command.find('test'):
|
||||||
@ -438,15 +437,16 @@ class SR1ShipRender(BaseScreen):
|
|||||||
return
|
return
|
||||||
logger.info(sr_tr().sr1.ship.save.start().format(self.rust_ship))
|
logger.info(sr_tr().sr1.ship.save.start().format(self.rust_ship))
|
||||||
self.rust_ship.save('./test-save.xml')
|
self.rust_ship.save('./test-save.xml')
|
||||||
elif command.find('render'):
|
|
||||||
glViewport(0, 0, 1000, 1000)
|
|
||||||
|
|
||||||
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int, window: "ClientWindow"):
|
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int, window: "ClientWindow"):
|
||||||
if not self.status.focus:
|
if self.status.focus:
|
||||||
return
|
self.camera.dx += dx
|
||||||
self.camera.dx += dx
|
self.camera.dy += dy
|
||||||
self.camera.dy += dy
|
self.status.update_call = True
|
||||||
self.status.update_call = True
|
elif self.status.moving:
|
||||||
|
# 如果是在移动整体渲染位置
|
||||||
|
self.dx += dx
|
||||||
|
self.dy += dy
|
||||||
|
|
||||||
def on_file_drop(self, x: int, y: int, paths: List[str], window: "ClientWindow"):
|
def on_file_drop(self, x: int, y: int, paths: List[str], window: "ClientWindow"):
|
||||||
if len(paths) > 1:
|
if len(paths) > 1:
|
||||||
|
@ -15,22 +15,6 @@ from pyglet.image import load, AbstractImage
|
|||||||
from Difficult_Rocket.utils.options import Options
|
from Difficult_Rocket.utils.options import Options
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class SR1PartData:
|
|
||||||
x: float
|
|
||||||
y: float
|
|
||||||
id: int
|
|
||||||
p_type: str
|
|
||||||
active: bool
|
|
||||||
angle: float
|
|
||||||
angle_v: float
|
|
||||||
editor_angle: int
|
|
||||||
flip_x: bool
|
|
||||||
flip_y: bool
|
|
||||||
explode: bool
|
|
||||||
textures: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class SR1Textures(Options):
|
class SR1Textures(Options):
|
||||||
""" 存储 sr1 的材质 img """
|
""" 存储 sr1 的材质 img """
|
||||||
def load_file(self, **kwargs):
|
def load_file(self, **kwargs):
|
||||||
@ -146,14 +130,4 @@ class SR1Rotation(Options):
|
|||||||
if radian in cls.radian_angle_map:
|
if radian in cls.radian_angle_map:
|
||||||
return cls.radian_angle_map[radian]
|
return cls.radian_angle_map[radian]
|
||||||
else:
|
else:
|
||||||
return (radian / math.pi) * 180
|
return (radian / math.pi) * 180
|
||||||
|
|
||||||
|
|
||||||
def xml_bool(bool_like: Union[str, int, bool, None]) -> bool:
|
|
||||||
if bool_like is None:
|
|
||||||
return False
|
|
||||||
if isinstance(bool_like, bool):
|
|
||||||
return bool_like
|
|
||||||
if isinstance(bool_like, int):
|
|
||||||
return bool_like != 0
|
|
||||||
return False if bool_like == '0' else bool_like.lower() != 'false'
|
|
Loading…
Reference in New Issue
Block a user