Feature/py camera #35

Merged
shenjackyuanjie merged 9 commits from feature/py_camera into main 2023-06-25 15:45:54 +08:00
2 changed files with 151 additions and 29 deletions
Showing only changes of commit 67a9dddb30 - Show all commits

View File

@ -0,0 +1,123 @@
# -------------------------------
# Difficult Rocket
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
# All rights reserved
# -------------------------------
# Huge thanks to pyglet developers
from typing import Tuple, Optional
class Camera:
"""
>>> from pyglet.window import Window
>>> window = Window()
>>> camera = Camera(window)
>>> @window.event
>>> def on_draw():
>>> camera.begin()
>>> window.clear()
>>> camera.end()
"""
def __init__(self,
window,
zoom: Optional[float] = 1.0,
dx: Optional[float] = 1.0,
dy: Optional[float] = 1.0,
min_zoom: Optional[float] = 1.0,
max_zoom: Optional[float] = 1.0) -> None:
self.window = window
self.dx = dx
self.dy = dy
self.zoom = zoom
self.min_zoom = min_zoom
self.max_zoom = max_zoom
def get_view(self):
return self.window.view
@property
def position(self) -> Tuple[float, float]:
return self.dx, self.dy
@position.setter
def position(self, value: Tuple[float, float]):
self.dx, self.dy = value
@property
def zoom_level(self) -> float:
return self.zoom
@zoom_level.setter
def zoom_level(self, value: float) -> None:
self.zoom = min(max(value, self.min_zoom), self.max_zoom)
def begin(self) -> None:
view = self.window.view
x = self.window.width / self.zoom + (self.dx / self.zoom)
y = self.window.height / self.zoom + (self.dy / self.zoom)
view_matrix = view.translate((x * self.zoom, y * self.zoom, 0))
view_matrix = view_matrix.scale((self.zoom, self.zoom, 1))
self.window.view = view_matrix
def end(self) -> None:
view = self.window.view
x = self.window.width / self.zoom + (self.dx / self.zoom)
y = self.window.height / self.zoom + (self.dy / self.zoom)
view_matrix = view.scale((1.0 / self.zoom, 1.0 / self.zoom, 1))
view_matrix = view_matrix.translate((-x * self.zoom, -y * self.zoom, 0))
self.window.view = view_matrix
def __enter__(self):
self.begin()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.end()
class CenterCamera(Camera):
"""
A camera that centers the view on the center of the window
>>> from pyglet.window import Window
>>> window = Window()
>>> camera = CenterCamera(window)
>>> @window.event
>>> def on_draw():
>>> camera.begin()
>>> window.clear()
>>> camera.end()
"""
def begin(self) -> None:
view = self.window.view
x = self.window.width / 2.0 / self.zoom + (self.dx / self.zoom)
y = self.window.height / 2.0 / self.zoom + (self.dy / self.zoom)
view_matrix = view.translate((x * self.zoom, y * self.zoom, 0))
view_matrix = view_matrix.scale((self.zoom, self.zoom, 1))
self.window.view = view_matrix
def end(self) -> None:
view = self.window.view
x = self.window.width / 2.0 / self.zoom + (self.dx / self.zoom)
y = self.window.height / 2.0 / self.zoom + (self.dy / self.zoom)
view_matrix = view.scale((1.0 / self.zoom, 1.0 / self.zoom, 1))
view_matrix = view_matrix.translate((-x * self.zoom, -y * self.zoom, 0))
self.window.view = view_matrix

View File

@ -30,14 +30,14 @@ from Difficult_Rocket.utils.translate import Tr
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 Difficult_Rocket.utils.camera import CenterCamera
from .types import SR1Textures, SR1PartTexture, SR1PartData, SR1Rotation, xml_bool from .types import SR1Textures, SR1PartTexture, SR1PartData, SR1Rotation, xml_bool
if TYPE_CHECKING: if TYPE_CHECKING:
from Difficult_Rocket.client import ClientWindow from Difficult_Rocket.client import ClientWindow
if DR_mod_runtime.use_DR_rust: if DR_mod_runtime.use_DR_rust:
from .Difficult_Rocket_rs import (CenterCamera_rs, from .Difficult_Rocket_rs import (SR1PartList_rs,
SR1PartList_rs,
SR1Ship_rs, SR1Ship_rs,
load_and_save_test, load_and_save_test,
test_ship_read_and_write) test_ship_read_and_write)
@ -138,9 +138,9 @@ class SR1ShipRender(BaseScreen):
load_end_time = time.time_ns() load_end_time = time.time_ns()
logger.info(sr_tr().mod.info.setup.use_time().format( logger.info(sr_tr().mod.info.setup.use_time().format(
(load_end_time - load_start_time) / 1000000000)) (load_end_time - load_start_time) / 1000000000))
if DR_mod_runtime.use_DR_rust: self.camera = CenterCamera(main_window,
self.camera_rs = CenterCamera_rs(main_window,
min_zoom=(1 / 2) ** 10, max_zoom=10) min_zoom=(1 / 2) ** 10, max_zoom=10)
if DR_mod_runtime.use_DR_rust:
self.rust_parts = None self.rust_parts = None
self.part_list_rs = SR1PartList_rs('configs/PartList.xml', 'default_part_list') self.part_list_rs = SR1PartList_rs('configs/PartList.xml', 'default_part_list')
@ -234,10 +234,9 @@ class SR1ShipRender(BaseScreen):
self.part_data: Dict[int, SR1PartData] = {} self.part_data: Dict[int, SR1PartData] = {}
self.parts_sprite: Dict[int, Sprite] = {} self.parts_sprite: Dict[int, Sprite] = {}
self.part_line_box = {} self.part_line_box = {}
self.camera_rs.zoom = 1.0 self.camera.zoom = 1.0
if DR_mod_runtime.use_DR_rust: self.camera.dx = 0
self.camera_rs.dx = 0 self.camera.dy = 0
self.camera_rs.dy = 0
parts = self.xml_root.find('Parts') parts = self.xml_root.find('Parts')
for part_xml in parts: for part_xml in parts:
if part_xml.tag != 'Part': if part_xml.tag != 'Part':
@ -266,11 +265,11 @@ class SR1ShipRender(BaseScreen):
def update_parts(self) -> bool: def update_parts(self) -> bool:
if not self.rendered: if not self.rendered:
return False return False
self.debug_line.x2, self.debug_line.y2 = self.camera_rs.dx + ( self.debug_line.x2, self.debug_line.y2 = self.camera.dx + (
self.window_pointer.width / 2), self.camera_rs.dy + ( self.window_pointer.width / 2), self.camera.dy + (
self.window_pointer.height / 2) self.window_pointer.height / 2)
self.debug_d_pos_label.text = f'x: {self.camera_rs.dx} y: {self.camera_rs.dy}' self.debug_d_pos_label.text = f'x: {self.camera.dx} y: {self.camera.dy}'
self.debug_d_pos_label.position = self.camera_rs.dx + (self.window_pointer.width / 2), self.camera_rs.dy + ( self.debug_d_pos_label.position = self.camera.dx + (self.window_pointer.width / 2), self.camera.dy + (
self.window_pointer.height / 2) + 10, 0 self.window_pointer.height / 2) + 10, 0
self.need_update_parts = False self.need_update_parts = False
@ -289,7 +288,7 @@ class SR1ShipRender(BaseScreen):
self.update_parts() self.update_parts()
self.need_update_parts = False self.need_update_parts = False
with self.camera_rs: with self.camera:
self.part_box_batch.draw() self.part_box_batch.draw()
self.part_batch.draw() self.part_batch.draw()
self.part_box_batch.draw() self.part_box_batch.draw()
@ -323,29 +322,29 @@ class SR1ShipRender(BaseScreen):
mouse_dx = x - (window.width / 2) mouse_dx = x - (window.width / 2)
mouse_dy = y - (window.height / 2) mouse_dy = y - (window.height / 2)
# 鼠标缩放位置相对于屏幕中心的位置 # 鼠标缩放位置相对于屏幕中心的位置
mouse_dx_d = mouse_dx - self.camera_rs.dx mouse_dx_d = mouse_dx - self.camera.dx
mouse_dy_d = mouse_dy - self.camera_rs.dy mouse_dy_d = mouse_dy - self.camera.dy
# 鼠标相对偏移量的偏移量 # 鼠标相对偏移量的偏移量
if scroll_y == 0: if scroll_y == 0:
zoom_d = 1 zoom_d = 1
else: else:
zoom_d = ((2 ** scroll_y) - 1) * 0.5 + 1 zoom_d = ((2 ** scroll_y) - 1) * 0.5 + 1
# 缩放的变换量 # 缩放的变换量
if not (self.camera_rs.zoom == 10 and scroll_y > 0): if not (self.camera.zoom == 10 and scroll_y > 0):
if self.camera_rs.zoom * zoom_d >= 10: if self.camera.zoom * zoom_d >= 10:
zoom_d = 10 / self.camera_rs.zoom zoom_d = 10 / self.camera.zoom
self.camera_rs.zoom = 10 self.camera.zoom = 10
else: else:
self.camera_rs.zoom *= zoom_d self.camera.zoom *= zoom_d
mouse_dx_d *= (1 - zoom_d) mouse_dx_d *= (1 - zoom_d)
mouse_dy_d *= (1 - zoom_d) mouse_dy_d *= (1 - zoom_d)
self.camera_rs.dx += mouse_dx_d self.camera.dx += mouse_dx_d
self.camera_rs.dy += mouse_dy_d self.camera.dy += mouse_dy_d
self.debug_mouse_line.x2, self.debug_mouse_line.y2 = x, y self.debug_mouse_line.x2, self.debug_mouse_line.y2 = x, y
self.debug_mouse_delta_line.x2 = (mouse_dx - self.camera_rs.dx) * (1 - (0.5 ** scroll_y)) + ( self.debug_mouse_delta_line.x2 = (mouse_dx - self.camera.dx) * (1 - (0.5 ** scroll_y)) + (
window.width / 2) window.width / 2)
self.debug_mouse_delta_line.y2 = (mouse_dy - self.camera_rs.dy) * (1 - (0.5 ** scroll_y)) + ( self.debug_mouse_delta_line.y2 = (mouse_dy - self.camera.dy) * (1 - (0.5 ** scroll_y)) + (
window.height / 2) window.height / 2)
self.debug_mouse_label.text = f'x: {mouse_dx} y: {mouse_dy}' self.debug_mouse_label.text = f'x: {mouse_dx} y: {mouse_dy}'
self.debug_mouse_label.position = x, y + 10, 0 self.debug_mouse_label.position = x, y + 10, 0
@ -355,9 +354,9 @@ class SR1ShipRender(BaseScreen):
def on_command(self, command: CommandText, window: "ClientWindow"): def on_command(self, command: CommandText, window: "ClientWindow"):
if command.find('render'): if command.find('render'):
if command.find('reset'): if command.find('reset'):
self.camera_rs.zoom = 1 self.camera.zoom = 1
self.camera_rs.dx = 0 self.camera.dx = 0
self.camera_rs.dy = 0 self.camera.dy = 0
self.window_pointer.view = Vec4() self.window_pointer.view = Vec4()
else: else:
self.need_draw = True self.need_draw = True
@ -448,8 +447,8 @@ class SR1ShipRender(BaseScreen):
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.focus: if not self.focus:
return return
self.camera_rs.dx += dx self.camera.dx += dx
self.camera_rs.dy += dy self.camera.dy += dy
self.need_update_parts = True self.need_update_parts = True
# self.update_parts() # self.update_parts()