Compare commits

...

6 Commits

Author SHA1 Message Date
364924a7b7
fixed #47 fixed #48 fixed #49
create #50 #51(
2023-07-24 10:07:17 +08:00
893872c012
fix #47 and fix #48 2023-07-24 09:56:52 +08:00
2ad60e398c
do stuf 2023-07-24 09:46:19 +08:00
24f17f6d4f
#47 fixed 2023-07-24 09:46:11 +08:00
e997633cc5
add raise for SR1Ship 2023-07-24 09:45:40 +08:00
7fed4032e0
adapt for rust render 2023-07-23 11:03:22 +08:00
11 changed files with 210 additions and 103 deletions

View File

@ -264,7 +264,7 @@ class ClientWindow(Window):
def start_game(self) -> None: def start_game(self) -> None:
self.set_icon(pyglet.image.load('assets/textures/icon.png')) self.set_icon(pyglet.image.load('assets/textures/icon.png'))
try: try:
pyglet.app.event_loop.run(1 / self.main_config['runtime']['fps']) pyglet.app.run(1 / self.main_config['runtime']['fps'])
except KeyboardInterrupt: except KeyboardInterrupt:
self.logger.warning("==========client stop. KeyboardInterrupt info==========") self.logger.warning("==========client stop. KeyboardInterrupt info==========")
traceback.print_exc() traceback.print_exc()

View File

@ -3,7 +3,43 @@
- 最新版本号 - 最新版本号
- DR game: 0.3.2.0 - DR game: 0.3.2.0
- DR rs: 0.2.19.0 - DR rs: 0.2.20.2
## 20230724 DR rs 0.2.20.2
### Fix
- [#49](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/49)
- missing field `touchingGround`
- SR1 says: `touchingGround` field is NOT Required
- make them happy
- SR1 说: `touchingGround` 字段也是可选的
- 让他们开心
- 我就看看我能发多少个 issue
## 20230724 DR rs 0.2.20.1
### Fix
- [#48](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/48)
- `missing field version`
- SR1 says: `version` field is NOT Required
- make them happy
- SR1 说: `version` 字段也是可选的
- 让他们开心
- 我谢谢您啊 Jundroo
- 我就看看我能发多少个 issue
## 20230724 DR rs 0.2.20.0
### Fix
- [#47](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/47)
- `editorAngle field is Option<i32>`
- SR1 says: `editorAngle` field is Optional
- make them happy
- SR1 说: `editorAngle` 字段是可选的
- 让他们开心
## 20230721 DR rs 0.2.19.0 ## 20230721 DR rs 0.2.19.0

View File

@ -144,7 +144,14 @@ if TYPE_CHECKING:
def __init__(self, def __init__(self,
file_path: Optional[str] = './assets/builtin/dock1.xml', file_path: Optional[str] = './assets/builtin/dock1.xml',
part_list: Optional[SR1PartList_rs] = None, part_list: Optional[SR1PartList_rs] = None,
ship_name: Optional[str] = 'NewShip'): ... ship_name: Optional[str] = 'NewShip'):
"""
读取 SR1Ship
:raise ValueError: 读取失败
:param file_path:
:param part_list:
:param ship_name:
"""
@property @property
def name(self) -> str: ... def name(self) -> str: ...

View File

@ -116,7 +116,7 @@ dependencies = [
[[package]] [[package]]
name = "difficult_rocket_rs" name = "difficult_rocket_rs"
version = "0.2.19" version = "0.2.20"
dependencies = [ dependencies = [
"pyo3", "pyo3",
"quick-xml", "quick-xml",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "difficult_rocket_rs" name = "difficult_rocket_rs"
version = "0.2.19" version = "0.2.20"
edition = "2021" edition = "2021"
license-file = '../../LICENSE' license-file = '../../LICENSE'
authors = [ authors = [

View File

@ -24,7 +24,7 @@ enum LoadState {
} }
#[pyfunction] #[pyfunction]
fn get_version_str() -> String { "0.2.19.0".to_string() } fn get_version_str() -> String { "0.2.20.2".to_string() }
#[pyfunction] #[pyfunction]
fn test_call(py_obj: &PyAny) -> PyResult<bool> { fn test_call(py_obj: &PyAny) -> PyResult<bool> {

View File

@ -9,6 +9,7 @@
pub mod data { pub mod data {
use std::collections::HashMap; use std::collections::HashMap;
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*; use pyo3::prelude::*;
use crate::sr1_data::part_list::RawPartList; use crate::sr1_data::part_list::RawPartList;
@ -170,14 +171,19 @@ pub mod data {
impl PySR1Ship { impl PySR1Ship {
#[new] #[new]
#[pyo3(text_signature = "(file_path = './assets/builtin/dock1.xml', part_list = None, ship_name = 'NewShip')")] #[pyo3(text_signature = "(file_path = './assets/builtin/dock1.xml', part_list = None, ship_name = 'NewShip')")]
fn new(file_path: String, part_list: Option<PySR1PartList>, ship_name: Option<String>) -> Self { fn new(file_path: String, part_list: Option<PySR1PartList>, ship_name: Option<String>) -> PyResult<Self> {
let mut ship = SR1Ship::from_file(file_path, Some(ship_name.unwrap_or("new ship".to_string()))).unwrap(); let ship = SR1Ship::from_file(file_path.clone(), Some(ship_name.unwrap_or("new ship".to_string())));
let part_list = match part_list { match ship {
Some(part_list) => part_list.data, Some(mut ship) => {
None => SR1PartList::from_file("./assets/builtin/PartList.xml".to_string()).unwrap(), let part_list = match part_list {
}; Some(part_list) => part_list.data,
ship.parse_part_list_to_part(&part_list); None => SR1PartList::from_file("./assets/builtin/PartList.xml".to_string()).unwrap(),
Self { ship, part_list } };
ship.parse_part_list_to_part(&part_list);
Ok(Self { ship, part_list })
}
None => Err(PyValueError::new_err(format!("Parse ship failed! {}", file_path))),
}
} }
#[getter] #[getter]

View File

@ -377,7 +377,7 @@ pub mod ship {
use super::part_list::SR1PartTypeEnum; use super::part_list::SR1PartTypeEnum;
use crate::types::sr1::{i8_to_bool, SR1PartDataTrait, SR1PartTypeAttr, SR1ShipTrait}; use crate::types::sr1::{i8_to_bool, option_i8_to_option_bool, SR1PartDataTrait, SR1PartTypeAttr, SR1ShipTrait};
use crate::types::sr1::{IdType, SR1PartData, SR1PartDataAttr, SR1Ship}; use crate::types::sr1::{IdType, SR1PartData, SR1PartDataAttr, SR1Ship};
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
@ -387,11 +387,15 @@ pub mod ship {
pub parts: Parts, pub parts: Parts,
#[serde(rename = "Connections")] #[serde(rename = "Connections")]
pub connects: Connections, pub connects: Connections,
pub version: i32, pub version: Option<i32>, // Option for https://github.com/shenjackyuanjie/Difficult-Rocket/issues/48
// SR1 says version is also optional, let them happy
// it's always 1
#[serde(rename = "liftedOff")] #[serde(rename = "liftedOff")]
pub lift_off: i8, pub lift_off: i8,
#[serde(rename = "touchingGround")] #[serde(rename = "touchingGround")]
pub touch_ground: i8, pub touch_ground: Option<i8>, // Option for https://github.com/shenjackyuanjie/Difficult-Rocket/issues/49
// SR1 says it's optional, let them happy
// NOT always 0
#[serde(rename = "DisconnectedParts")] #[serde(rename = "DisconnectedParts")]
pub disconnected: Option<DisconnectedParts>, pub disconnected: Option<DisconnectedParts>,
} }
@ -422,7 +426,8 @@ pub mod ship {
pub x: f64, pub x: f64,
pub y: f64, pub y: f64,
#[serde(rename = "editorAngle")] #[serde(rename = "editorAngle")]
pub editor_angle: i32, pub editor_angle: Option<i32>, // Option for https://github.com/shenjackyuanjie/Difficult-Rocket/issues/47
// SR1 says it's optional, let them happy
pub angle: f64, pub angle: f64,
#[serde(rename = "angleV")] #[serde(rename = "angleV")]
pub angle_v: f64, pub angle_v: f64,
@ -515,12 +520,6 @@ pub mod ship {
pub child_part: IdType, pub child_part: IdType,
} }
impl Part {
/// 根据 Part 的原始数据猜测 Part 的类型
/// jundroo 我日你先人
fn guess_part_type(&self) -> SR1PartTypeEnum { todo!() }
}
impl SR1PartDataTrait for Part { impl SR1PartDataTrait for Part {
fn to_sr_part_data(&self) -> SR1PartData { fn to_sr_part_data(&self) -> SR1PartData {
let attr = SR1PartDataAttr::from_raw(&self, None, true); let attr = SR1PartDataAttr::from_raw(&self, None, true);
@ -534,7 +533,7 @@ pub mod ship {
angle_v: self.angle_v.to_owned(), angle_v: self.angle_v.to_owned(),
flip_x: i8_to_bool(self.flip_x.unwrap_or(0_i8)), flip_x: i8_to_bool(self.flip_x.unwrap_or(0_i8)),
flip_y: i8_to_bool(self.flip_y.unwrap_or(0_i8)), flip_y: i8_to_bool(self.flip_y.unwrap_or(0_i8)),
editor_angle: self.editor_angle.to_owned(), editor_angle: self.editor_angle.unwrap_or(0_i32),
part_type, part_type,
part_type_id: self.part_type_id.clone(), part_type_id: self.part_type_id.clone(),
active: i8_to_bool(self.activated.unwrap_or(0_i8)), active: i8_to_bool(self.activated.unwrap_or(0_i8)),
@ -576,11 +575,11 @@ pub mod ship {
SR1Ship { SR1Ship {
name: name.unwrap_or("NewShip".to_string()), name: name.unwrap_or("NewShip".to_string()),
description: "".to_string(), description: "".to_string(),
version: self.version, version: self.version.unwrap_or(1_i32),
parts, parts,
connections, connections,
lift_off: i8_to_bool(self.lift_off.to_owned()), lift_off: i8_to_bool(self.lift_off.to_owned()),
touch_ground: i8_to_bool(self.touch_ground.to_owned()), touch_ground: option_i8_to_option_bool(self.touch_ground.to_owned()).unwrap_or(true),
disconnected, disconnected,
} }
} }

View File

@ -470,7 +470,7 @@ pub mod sr1 {
id: self.id, id: self.id,
x: self.x, x: self.x,
y: self.y, y: self.y,
editor_angle: self.editor_angle, editor_angle: Some(self.editor_angle),
angle: self.angle, angle: self.angle,
angle_v: self.angle_v, angle_v: self.angle_v,
flip_x: Some(bool_to_i8(self.flip_x)), flip_x: Some(bool_to_i8(self.flip_x)),
@ -708,8 +708,15 @@ pub mod sr1 {
return None; return None;
} }
// 解析为 RawShip // 解析为 RawShip
let ship: RawShip = RawShip::from_file(file_name).unwrap(); let ship: Option<RawShip> = RawShip::from_file(file_name);
Some(ship.to_sr_ship(ship_name)) match ship {
Some(ship) => {
// 解析为 SR1Ship
let sr_ship = ship.to_sr_ship(ship_name);
Some(sr_ship)
}
None => None,
}
} }
pub fn parse_part_list_to_part(&mut self, part_list: &SR1PartList) { pub fn parse_part_list_to_part(&mut self, part_list: &SR1PartList) {
@ -970,9 +977,9 @@ pub mod sr1 {
RawShip { RawShip {
parts: RawParts { parts }, parts: RawParts { parts },
connects: connections, connects: connections,
version: self.version, version: Some(self.version),
lift_off: bool_to_i8(self.lift_off), lift_off: bool_to_i8(self.lift_off),
touch_ground: bool_to_i8(self.touch_ground), touch_ground: Some(bool_to_i8(self.touch_ground)),
disconnected, disconnected,
} }
} }

View File

@ -16,7 +16,7 @@ from Difficult_Rocket.api.mod import ModInfo
from Difficult_Rocket.client import ClientWindow from Difficult_Rocket.client import ClientWindow
from Difficult_Rocket.api.types import Options, Version from Difficult_Rocket.api.types import Options, Version
DR_rust_version = Version("0.2.19.0") # DR_mod 的 Rust 编写部分的兼容版本 DR_rust_version = Version("0.2.20.2") # DR_mod 的 Rust 编写部分的兼容版本
logger = logging.getLogger('client.dr_game') logger = logging.getLogger('client.dr_game')

View File

@ -5,6 +5,7 @@
# ------------------------------- # -------------------------------
# import math # import math
import re
import time import time
import random import random
import logging import logging
@ -21,6 +22,7 @@ from pyglet.sprite import Sprite
# from pyglet.image import Texture # from pyglet.image import Texture
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
# pyglet OpenGL # pyglet OpenGL
from pyglet.gl import glViewport from pyglet.gl import glViewport
@ -50,12 +52,19 @@ logger.level = logging.DEBUG
sr_tr = Tr(lang_path=Path(__file__).parent / 'lang') sr_tr = Tr(lang_path=Path(__file__).parent / 'lang')
class SR1ShipRender_Option(Options): # NOQA class SR1ShipRenderStatus(Options): # NOQA
# debug option name = "SR1ShipRenderStatus"
debug_d_pos: bool = False # main status
debug_mouse_pos: bool = False draw_done: bool = False
debug_mouse_d_pos: bool = False draw_call: bool = False
draw_size: Tuple[int, int] = (100, 100) update_call: bool = False
focus: bool = True
moving: bool = False
# debug status
draw_d_pos: bool = False
draw_mouse_pos: bool = False
draw_mouse_d_pos: bool = False
class SR1ShipRender(BaseScreen): class SR1ShipRender(BaseScreen):
@ -67,16 +76,16 @@ class SR1ShipRender(BaseScreen):
self.logger = logger self.logger = logger
logger.info(sr_tr().mod.info.setup.start()) logger.info(sr_tr().mod.info.setup.start())
load_start_time = time.time_ns() load_start_time = time.time_ns()
self.rendered = False # status
self.focus = True self.status = SR1ShipRenderStatus()
self.need_draw = False
self.drawing = False
self.need_update_parts = False
self.render_option = SR1ShipRender_Option()
self.dx = 0 self.dx = 0
self.dy = 0 self.dy = 0
self.width = main_window.width self.width = main_window.width - 100
self.height = main_window.height self.height = main_window.height - 100
self.buffer = Framebuffer()
self.render_texture = Texture.create(self.width, self.height)
self.buffer.attach_texture(self.render_texture)
self.main_batch = Batch() self.main_batch = Batch()
self.part_group = Group(10, parent=main_window.main_group) self.part_group = Group(10, parent=main_window.main_group)
@ -88,10 +97,10 @@ class SR1ShipRender(BaseScreen):
batch=self.main_batch, group=Group(5, parent=self.part_group)) batch=self.main_batch, group=Group(5, parent=self.part_group))
self.render_d_line = Line(0, 0, 0, 0, width=5, color=(200, 200, 10, 255), self.render_d_line = Line(0, 0, 0, 0, width=5, color=(200, 200, 10, 255),
batch=self.main_batch, group=Group(5, parent=self.part_group)) batch=self.main_batch, group=Group(5, parent=self.part_group))
self.render_d_line.visible = self.render_option.debug_mouse_d_pos self.render_d_line.visible = self.status.draw_d_pos
self.render_d_label = Label('debug label NODATA', font_name=Fonts.微软等宽无线, self.render_d_label = Label('debug label NODATA', font_name=Fonts.微软等宽无线,
x=main_window.width / 2, y=main_window.height / 2) x=main_window.width / 2, y=main_window.height / 2)
self.render_d_label.visible = self.render_option.debug_d_pos self.render_d_label.visible = self.status.draw_d_pos
self.camera = CenterCamera(main_window, min_zoom=(1 / 2) ** 10, max_zoom=10) self.camera = CenterCamera(main_window, min_zoom=(1 / 2) ** 10, max_zoom=10)
# Optional data # Optional data
@ -115,6 +124,18 @@ 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((load_end_time - load_start_time) / 1000000000)) logger.info(sr_tr().mod.info.setup.use_time().format((load_end_time - load_start_time) / 1000000000))
@property
def size(self) -> Tuple[int, int]:
""" 渲染器的渲染大小 """
return self.width, self.height
@size.setter
def size(self, value: Tuple[int, int]):
if not self.width == value[0] or not self.height == value[1]:
self.width, self.height = value
self.render_texture = Texture.create(self.width, self.height)
self.buffer.attach_texture(self.render_texture)
def load_xml(self, file_path: str) -> bool: def load_xml(self, file_path: str) -> bool:
""" """
加载 xml 文件 加载 xml 文件
@ -131,8 +152,9 @@ class SR1ShipRender(BaseScreen):
logger.info(sr_tr().sr1.ship.xml.load_time().format( logger.info(sr_tr().sr1.ship.xml.load_time().format(
(time.time_ns() - start_time) / 1000000000)) (time.time_ns() - start_time) / 1000000000))
return True return True
except Exception as e: except Exception:
print(e) traceback.print_exc()
self.logger.error(traceback.format_exc())
return False return False
def gen_sprite(self, each_count: int = 100) -> Generator: def gen_sprite(self, each_count: int = 100) -> Generator:
@ -143,7 +165,7 @@ class SR1ShipRender(BaseScreen):
:return: 生成器 :return: 生成器
""" """
count = 0 count = 0
self.drawing = True self.status.draw_done = False
# rust 渲染 # rust 渲染
if DR_mod_runtime.use_DR_rust: if DR_mod_runtime.use_DR_rust:
cache = self.rust_ship.as_dict() cache = self.rust_ship.as_dict()
@ -226,72 +248,80 @@ class SR1ShipRender(BaseScreen):
# if count >= each_count: # if count >= each_count:
# count = 0 # count = 0
# yield count # yield count
self.drawing = False self.status.draw_done = True
raise GeneratorExit raise GeneratorExit
def render_ship(self): def render_ship(self):
""" """
渲染船 渲染船
""" """
self.status.draw_done = False
logger.info(sr_tr().sr1.ship.ship.load().format(self.ship_name)) logger.info(sr_tr().sr1.ship.ship.load().format(self.ship_name))
start_time = time.perf_counter_ns() start_time = time.perf_counter_ns()
self.parts_sprite: Dict[int, Sprite] = {} self.parts_sprite: Dict[int, Sprite] = {}
self.part_line_box = {} self.part_line_box = {}
self.part_line_list = [] self.part_line_list = []
# self.camera.zoom = 1.0 self.camera.zoom = 1.0
# self.camera.dx = 0 self.camera.dx = 0
# self.camera.dy = 0 self.camera.dy = 0
# 调用生成器 减少卡顿 # 调用生成器 减少卡顿
try: try:
self.gen_draw = self.gen_sprite() self.gen_draw = self.gen_sprite()
next(self.gen_draw) if not self.status.draw_done:
next(self.gen_draw)
except (GeneratorExit, StopIteration): except (GeneratorExit, StopIteration):
self.drawing = False self.status.draw_done = True
self.need_draw = False self.status.draw_call = False
full_mass = 0 full_mass = 0
if DR_mod_runtime.use_DR_rust: if DR_mod_runtime.use_DR_rust:
full_mass = self.rust_ship.mass full_mass = self.rust_ship.mass
logger.info(sr_tr().sr1.ship.ship.load_time().format( logger.info(sr_tr().sr1.ship.ship.load_time().format(
(time.perf_counter_ns() - start_time) / 1000000000)) (time.perf_counter_ns() - start_time) / 1000000000))
logger.info(sr_tr().sr1.ship.ship.info().format( logger.info(sr_tr().sr1.ship.ship.info().format(
len(self.rust_ship.as_list()), f'{full_mass}kg' if DR_mod_runtime.use_DR_rust else sr_tr().game.require_DR_rs())) len(self.rust_ship.as_list()),
self.rendered = True f'{full_mass}kg' if DR_mod_runtime.use_DR_rust else sr_tr().game.require_DR_rs()))
def draw_batch(self, window: "ClientWindow"): def draw_batch(self, window: "ClientWindow"):
if self.rendered: if self.status.draw_done:
self.render_d_label.text = f'x: {self.camera.dx} y: {self.camera.dy}' self.render_d_label.text = f'x: {self.camera.dx} y: {self.camera.dy}'
self.render_d_label.position = self.camera.dx + (self.window_pointer.width / 2), self.camera.dy + ( self.render_d_label.position = self.camera.dx + (self.window_pointer.width / 2), self.camera.dy + (
self.window_pointer.height / 2) + 10, 0 # 0 for z self.window_pointer.height / 2) + 10, 0 # 0 for z
self.render_d_line.x2 = self.camera.dx self.render_d_line.x2 = self.camera.dx
self.render_d_line.y2 = self.camera.dy self.render_d_line.y2 = self.camera.dy
self.buffer.bind()
window.clear()
with self.camera: with self.camera:
# glViewport(int(self.camera.dx), int(self.camera.dy), window.width // 2, window.height // 2) # 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) # glViewport(0, 0, window.width, window.height)
self.buffer.unbind()
self.render_texture.blit(x=0, y=0, z=0, width=self.width, height=self.height)
def on_draw(self, window: "ClientWindow"): def on_draw(self, window: "ClientWindow"):
if self.need_draw: if self.status.draw_call:
self.render_ship() self.render_ship()
if self.drawing: if not self.status.draw_done:
try: try:
next(self.gen_draw) next(self.gen_draw)
except (GeneratorExit, StopIteration): except (GeneratorExit, StopIteration):
self.drawing = False self.status.draw_done = True
self.logger.info(sr_tr().sr1.ship.ship.render.done()) self.logger.info(sr_tr().sr1.ship.ship.render.done())
except TypeError:
pass
self.debug_label.draw() self.debug_label.draw()
def on_resize(self, width: int, height: int, window: "ClientWindow"): def on_resize(self, width: int, height: int, window: "ClientWindow"):
self.debug_label.y = height - 100 self.debug_label.y = height - 100
if not self.rendered: if not self.status.draw_done:
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.render_option.draw_size = (width, height) 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.rendered: if not self.status.draw_done:
return return
mouse_dx = x - (window.width / 2) mouse_dx = x - (window.width / 2)
mouse_dy = y - (window.height / 2) mouse_dy = y - (window.height / 2)
@ -325,15 +355,16 @@ class SR1ShipRender(BaseScreen):
self.camera.dy = 0 self.camera.dy = 0
self.window_pointer.view = Vec4() self.window_pointer.view = Vec4()
else: else:
self.need_draw = True self.status.draw_call = True
print('应该渲染飞船的') print('应该渲染飞船的')
elif command.find('debug'): elif command.find('debug'):
if command.find('delta'): if command.find('delta'):
self.render_d_line.visible = not self.render_d_line.visible self.render_d_line.visible = not self.render_d_line.visible
self.render_option.debug_mouse_d_pos = self.render_d_line.visible self.status.draw_mouse_d_pos = self.render_d_line.visible
self.logger.info(f'sr1 mouse {self.render_option.debug_mouse_d_pos}') self.logger.info(f'sr1 mouse {self.status.draw_mouse_d_pos}')
elif command.find('ship'): elif command.find('ship'):
if self.rendered: if self.status.draw_done:
for index, sprite in self.parts_sprite.items(): for index, sprite in self.parts_sprite.items():
sprite.visible = not sprite.visible sprite.visible = not sprite.visible
@ -345,8 +376,7 @@ class SR1ShipRender(BaseScreen):
:param window: :param window:
:return: :return:
""" """
from pyglet.gl import GLubyte, GL_RGBA, GL_UNSIGNED_BYTE, \ from pyglet.gl import GLubyte, GL_RGBA, GL_UNSIGNED_BYTE, glReadPixels
glReadPixels
import pyglet import pyglet
format_str = "RGBA" format_str = "RGBA"
buf = (GLubyte * (len(format_str) * window.width * window.height))() buf = (GLubyte * (len(format_str) * window.width * window.height))()
@ -356,13 +386,13 @@ class SR1ShipRender(BaseScreen):
image_data = screenshot(self.window_pointer) image_data = screenshot(self.window_pointer)
image_data.save('test.png') image_data.save('test.png')
elif command.find('gen_img'): elif command.find('gen_img'):
if not self.rendered: if not self.status.draw_done:
return return
if not DR_mod_runtime.use_DR_rust: if not DR_mod_runtime.use_DR_rust:
# 这个功能依赖于 DR rs (简称,我懒得在Python端实现) # 这个功能依赖于 DR rs (简称,我懒得在Python端实现)
return return
img_box = self.rust_ship.img_pos img_box = self.rust_ship.img_pos
img_size = (img_box[2] - img_box[0] + 1000, img_box[3] - img_box[1] + 1000) img_size = (img_box[2] - img_box[0], img_box[3] - img_box[1])
# 中心点是左上角坐标 # 中心点是左上角坐标
img_center = (abs(img_box[0]), abs(img_box[3])) img_center = (abs(img_box[0]), abs(img_box[3]))
try: try:
@ -372,29 +402,37 @@ class SR1ShipRender(BaseScreen):
print('PIL not found') print('PIL not found')
return return
img = Image.new('RGBA', img_size) img = Image.new('RGBA', img_size)
for part, sprite in self.parts_sprite.items(): part_data = self.rust_ship.as_dict()
sprite_img = sprite.image for part, sprites in self.parts_sprite.items():
print(f"sprite_img: {sprite_img} {sprite_img.width} {sprite_img.height}") for index, sprite in enumerate(sprites):
img_data = sprite_img.get_image_data() sprite_img = sprite.image
fmt = img_data.format print(
if fmt != 'RGB': f"sprite_img: {sprite_img} {part_data[part][index][1].x * 60} {part_data[part][index][1].y * 60}")
fmt = 'RGBA' img_data = sprite_img.get_image_data()
pitch = -(img_data.width * len(fmt)) fmt = img_data.format
pil_image = Image.frombytes(fmt, (img_data.width, img_data.height), img_data.get_data(fmt, pitch)) if fmt != 'RGB':
pil_image = pil_image.rotate(SR1Rotation.get_rotation(self.part_data[part].angle), expand=True) fmt = 'RGBA'
if self.part_data[part].flip_y: pitch = -(img_data.width * len(fmt))
pil_image.transpose(Image.FLIP_TOP_BOTTOM) pil_image = Image.frombytes(fmt, (img_data.width, img_data.height), img_data.get_data(fmt, pitch))
if self.part_data[part].flip_x:
pil_image.transpose(Image.FLIP_LEFT_RIGHT) pil_image = pil_image.rotate(-SR1Rotation.get_rotation(part_data[part][index][1].angle),
img.paste(pil_image, ( expand=True)
int(self.part_data[part].x * 60 + img_center[0]),
int(-self.part_data[part].y * 60 + img_center[1])), if part_data[part][index][1].flip_y:
pil_image) pil_image.transpose(Image.FLIP_TOP_BOTTOM)
if part_data[part][index][1].flip_x:
pil_image.transpose(Image.FLIP_LEFT_RIGHT)
img.paste(pil_image, (
int(part_data[part][index][1].x * 60 + img_center[0]),
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'):
if command.find('save'): if command.find('save'):
if not self.rendered: if not self.status.draw_done:
return return
if not DR_mod_runtime.use_DR_rust: if not DR_mod_runtime.use_DR_rust:
return return
@ -404,21 +442,35 @@ class SR1ShipRender(BaseScreen):
glViewport(0, 0, 1000, 1000) 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.focus: if not self.status.focus:
return return
self.camera.dx += dx self.camera.dx += dx
self.camera.dy += dy self.camera.dy += dy
self.need_update_parts = True self.status.update_call = True
# self.update_parts()
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"):
for path in paths: if len(paths) > 1:
if self.load_xml(path): # 加载成功一个就停下 for path in paths:
break try:
self.render_ship() self.load_xml(path)
except Exception:
traceback.print_exc()
else:
if Path(paths[0]).is_dir():
for path in Path(paths[0]).glob('*.xml'):
try:
self.load_xml(str(path))
except ValueError:
traceback.print_exc()
if self.load_xml(paths[0]):
self.render_ship()
# for path in paths:
# if self.load_xml(path): # 加载成功一个就停下
# break
# self.render_ship()
if __name__ == '__main__': if __name__ == '__main__':
from objprint import op from objprint import op
op(SR1ShipRender_Option()) op(SR1ShipRenderStatus())