From 67a9dddb309033e9b51e04e9737aee647901a74d Mon Sep 17 00:00:00 2001 From: shenjack <3695888@qq.com> Date: Sun, 25 Jun 2023 15:00:37 +0800 Subject: [PATCH] add python camera --- Difficult_Rocket/utils/camera.py | 123 +++++++++++++++++++++++++++++++ mods/dr_game/sr1_ship.py | 57 +++++++------- 2 files changed, 151 insertions(+), 29 deletions(-) create mode 100644 Difficult_Rocket/utils/camera.py diff --git a/Difficult_Rocket/utils/camera.py b/Difficult_Rocket/utils/camera.py new file mode 100644 index 0000000..7a48b50 --- /dev/null +++ b/Difficult_Rocket/utils/camera.py @@ -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 + \ No newline at end of file diff --git a/mods/dr_game/sr1_ship.py b/mods/dr_game/sr1_ship.py index e92b810..cf4d87e 100644 --- a/mods/dr_game/sr1_ship.py +++ b/mods/dr_game/sr1_ship.py @@ -30,14 +30,14 @@ from Difficult_Rocket.utils.translate import Tr from Difficult_Rocket.api.types import Fonts, Options from Difficult_Rocket.command.line import CommandText from Difficult_Rocket.client.screen import BaseScreen +from Difficult_Rocket.utils.camera import CenterCamera from .types import SR1Textures, SR1PartTexture, SR1PartData, SR1Rotation, xml_bool if TYPE_CHECKING: from Difficult_Rocket.client import ClientWindow if DR_mod_runtime.use_DR_rust: - from .Difficult_Rocket_rs import (CenterCamera_rs, - SR1PartList_rs, + from .Difficult_Rocket_rs import (SR1PartList_rs, SR1Ship_rs, load_and_save_test, test_ship_read_and_write) @@ -138,9 +138,9 @@ class SR1ShipRender(BaseScreen): load_end_time = time.time_ns() logger.info(sr_tr().mod.info.setup.use_time().format( (load_end_time - load_start_time) / 1000000000)) + self.camera = CenterCamera(main_window, + min_zoom=(1 / 2) ** 10, max_zoom=10) if DR_mod_runtime.use_DR_rust: - self.camera_rs = CenterCamera_rs(main_window, - min_zoom=(1 / 2) ** 10, max_zoom=10) self.rust_parts = None 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.parts_sprite: Dict[int, Sprite] = {} self.part_line_box = {} - self.camera_rs.zoom = 1.0 - if DR_mod_runtime.use_DR_rust: - self.camera_rs.dx = 0 - self.camera_rs.dy = 0 + self.camera.zoom = 1.0 + self.camera.dx = 0 + self.camera.dy = 0 parts = self.xml_root.find('Parts') for part_xml in parts: if part_xml.tag != 'Part': @@ -266,11 +265,11 @@ class SR1ShipRender(BaseScreen): def update_parts(self) -> bool: if not self.rendered: return False - self.debug_line.x2, self.debug_line.y2 = self.camera_rs.dx + ( - self.window_pointer.width / 2), self.camera_rs.dy + ( + self.debug_line.x2, self.debug_line.y2 = self.camera.dx + ( + self.window_pointer.width / 2), self.camera.dy + ( 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.position = self.camera_rs.dx + (self.window_pointer.width / 2), 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.dx + (self.window_pointer.width / 2), self.camera.dy + ( self.window_pointer.height / 2) + 10, 0 self.need_update_parts = False @@ -289,7 +288,7 @@ class SR1ShipRender(BaseScreen): self.update_parts() self.need_update_parts = False - with self.camera_rs: + with self.camera: self.part_box_batch.draw() self.part_batch.draw() self.part_box_batch.draw() @@ -323,29 +322,29 @@ class SR1ShipRender(BaseScreen): mouse_dx = x - (window.width / 2) mouse_dy = y - (window.height / 2) # 鼠标缩放位置相对于屏幕中心的位置 - mouse_dx_d = mouse_dx - self.camera_rs.dx - mouse_dy_d = mouse_dy - self.camera_rs.dy + mouse_dx_d = mouse_dx - self.camera.dx + mouse_dy_d = mouse_dy - self.camera.dy # 鼠标相对偏移量的偏移量 if scroll_y == 0: zoom_d = 1 else: zoom_d = ((2 ** scroll_y) - 1) * 0.5 + 1 # 缩放的变换量 - if not (self.camera_rs.zoom == 10 and scroll_y > 0): - if self.camera_rs.zoom * zoom_d >= 10: - zoom_d = 10 / self.camera_rs.zoom - self.camera_rs.zoom = 10 + 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: - self.camera_rs.zoom *= zoom_d + self.camera.zoom *= zoom_d mouse_dx_d *= (1 - zoom_d) mouse_dy_d *= (1 - zoom_d) - self.camera_rs.dx += mouse_dx_d - self.camera_rs.dy += mouse_dy_d + self.camera.dx += mouse_dx_d + self.camera.dy += mouse_dy_d 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) - 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) self.debug_mouse_label.text = f'x: {mouse_dx} y: {mouse_dy}' self.debug_mouse_label.position = x, y + 10, 0 @@ -355,9 +354,9 @@ class SR1ShipRender(BaseScreen): def on_command(self, command: CommandText, window: "ClientWindow"): if command.find('render'): if command.find('reset'): - self.camera_rs.zoom = 1 - self.camera_rs.dx = 0 - self.camera_rs.dy = 0 + self.camera.zoom = 1 + self.camera.dx = 0 + self.camera.dy = 0 self.window_pointer.view = Vec4() else: 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"): if not self.focus: return - self.camera_rs.dx += dx - self.camera_rs.dy += dy + self.camera.dx += dx + self.camera.dy += dy self.need_update_parts = True # self.update_parts()