Feature/py camera #35
123
Difficult_Rocket/utils/camera.py
Normal file
123
Difficult_Rocket/utils/camera.py
Normal 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
|
||||||
|
|
@ -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))
|
||||||
|
self.camera = CenterCamera(main_window,
|
||||||
|
min_zoom=(1 / 2) ** 10, max_zoom=10)
|
||||||
if DR_mod_runtime.use_DR_rust:
|
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.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()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user