From e729bfc0424ad3cff172b9bf9f07c377616100a7 Mon Sep 17 00:00:00 2001 From: PieKing1215 Date: Mon, 16 Sep 2024 01:39:58 -0400 Subject: [PATCH] Add toggle for placement transform editing & clean up UI --- src/lib.rs | 27 ++++-- src/tweaks/editor_camera_speed.rs | 11 +-- src/tweaks/mod.rs | 22 +++-- src/tweaks/settings/toggle.rs | 15 ++++ src/tweaks/transform_edit.rs | 143 ++++++++++++++++++------------ 5 files changed, 144 insertions(+), 74 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d8464fa..9e9e31c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ use windows::Win32::Foundation::HINSTANCE; use windows::Win32::System::SystemServices::DLL_PROCESS_ATTACH; #[allow(clippy::missing_safety_doc)] +#[allow(non_snake_case)] #[no_mangle] pub unsafe extern "stdcall" fn DllMain(hmodule: HINSTANCE, reason: u32, _: *mut std::ffi::c_void) { if reason == DLL_PROCESS_ATTACH { @@ -215,7 +216,11 @@ impl ImguiRenderLoop for MainHud { style_padding.end(); for (tw, _) in &mut self.tweaks { - tw.constant_render(ui); + + if let Err(e) = tw.constant_render(ui) { + self.errors.push(e); + self.show = true; + } } if !self.show { @@ -296,26 +301,34 @@ impl ImguiRenderLoop for MainHud { let categories = self .tweaks .iter_mut() - .map(|(tw, _)| (tw.category().clone(), tw)) + .enumerate() + .map(|(i, (tw, _))| (tw.category().clone(), i)) .into_group_map(); - for (category, tweaks) in categories.into_iter().sorted_by(|a, b| { + for (category, mut tweak_indices) in categories.into_iter().sorted_by(|a, b| { if a.0.is_none() || b.0.is_none() { b.0.cmp(&a.0) } else { a.0.cmp(&b.0) } }) { - let render = || { - for tw in tweaks { - if let Err(e) = tw.render(ui) { + let mut render = || { + for i in &mut tweak_indices { + if let Err(e) = self.tweaks[*i].0.render(ui) { + self.errors.push(e); + self.show = true; + } + } + + for (tw, _) in &mut self.tweaks { + if let Err(e) = tw.render_category(ui, category.as_deref()) { self.errors.push(e); self.show = true; } } }; - if let Some(category) = category { + if let Some(category) = &category { if ui.collapsing_header(category, TreeNodeFlags::empty()) { ui.indent_by(8.0); render(); diff --git a/src/tweaks/editor_camera_speed.rs b/src/tweaks/editor_camera_speed.rs index 91cd7b4..c765e27 100644 --- a/src/tweaks/editor_camera_speed.rs +++ b/src/tweaks/editor_camera_speed.rs @@ -23,9 +23,6 @@ const DEFAULT_WHEEL_MULTIPLIER: f32 = 1.1; static SPEED: AtomicF32 = AtomicF32::new(1.0); -#[no_mangle] -static mut jmp_back_addr: usize = 0x0; - pub struct EditorCameraSpeedTweak { base_speed: f32, shift_multiplier: f32, @@ -87,7 +84,7 @@ impl Tweak for EditorCameraSpeedTweak { }) } - fn render(&mut self, ui: &hudhook::imgui::Ui) { + fn render(&mut self, ui: &hudhook::imgui::Ui) -> anyhow::Result<()> { ui.set_next_item_width(100.0); ui.slider("Camera Base Speed", 0.1, 4.0, &mut self.base_speed); if ui.is_item_hovered() { @@ -130,9 +127,11 @@ impl Tweak for EditorCameraSpeedTweak { // ui.text(format!("{}", self.current_speed)); // ui.text(format!("{}", self.current_wheel_multiplier)); + + Ok(()) } - fn constant_render(&mut self, ui: &hudhook::imgui::Ui) { + fn constant_render(&mut self, ui: &hudhook::imgui::Ui) -> anyhow::Result<()> { // if ui.io().mouse_wheel != 0.0 { // self.current_wheel_multiplier *= self.wheel_multiplier.powf(ui.io().mouse_wheel); // } @@ -152,6 +151,8 @@ impl Tweak for EditorCameraSpeedTweak { } SPEED.store(self.current_speed, Ordering::Release); + + Ok(()) } fn reset_to_default(&mut self) { diff --git a/src/tweaks/mod.rs b/src/tweaks/mod.rs index e6aaa50..c449fd5 100644 --- a/src/tweaks/mod.rs +++ b/src/tweaks/mod.rs @@ -35,9 +35,17 @@ pub trait Tweak { Ok(()) } - fn render(&mut self, _ui: &hudhook::imgui::Ui) {} + fn render(&mut self, _ui: &hudhook::imgui::Ui) -> anyhow::Result<()> { + Ok(()) + } - fn constant_render(&mut self, _ui: &hudhook::imgui::Ui) {} + fn render_category(&mut self, _ui: &hudhook::imgui::Ui, _category: Option<&str>) -> anyhow::Result<()> { + Ok(()) + } + + fn constant_render(&mut self, _ui: &hudhook::imgui::Ui) -> anyhow::Result<()> { + Ok(()) + } fn reset_to_default(&mut self) {} fn reset_to_vanilla(&mut self) {} @@ -112,13 +120,17 @@ impl TweakWrapper { for t in &mut self.settings { t.render(ui)?; } - self.inner.render(ui); + self.inner.render(ui)?; Ok(()) } - pub fn constant_render(&mut self, ui: &hudhook::imgui::Ui) { - self.inner.constant_render(ui); + pub fn render_category(&mut self, ui: &hudhook::imgui::Ui, category: Option<&str>) -> anyhow::Result<()> { + self.inner.render_category(ui, category) + } + + pub fn constant_render(&mut self, ui: &hudhook::imgui::Ui) -> anyhow::Result<()> { + self.inner.constant_render(ui) } pub fn reset_to_default(&mut self) -> anyhow::Result<()> { diff --git a/src/tweaks/settings/toggle.rs b/src/tweaks/settings/toggle.rs index 4368a51..c8cb328 100644 --- a/src/tweaks/settings/toggle.rs +++ b/src/tweaks/settings/toggle.rs @@ -28,6 +28,7 @@ impl<'b, 'r> ToggleBuilder<'b, 'r> { label: label.into(), injections: vec![], detours: vec![], + value_changed_listeners: vec![], }, } } @@ -65,6 +66,15 @@ impl<'b, 'r> ToggleBuilder<'b, 'r> { self } + #[must_use] + pub fn on_value_changed( + mut self, + callback: impl FnMut(bool) + Send + Sync + 'static, + ) -> Self { + self.toggle.value_changed_listeners.push(Box::new(callback)); + self + } + pub fn build(self) -> anyhow::Result<()> { self.tweak_builder .add_setting(Setting::new(self.toggle, self.defaults, self.config_key)) @@ -76,6 +86,7 @@ pub struct Toggle { tooltip: String, injections: Vec<(Injection, bool)>, detours: Vec<(Box, bool)>, + value_changed_listeners: Vec>, } impl SettingImpl for Toggle { @@ -114,6 +125,10 @@ impl SettingImpl for Toggle { } } + for listener in &mut self.value_changed_listeners { + listener(value); + } + Ok(()) } diff --git a/src/tweaks/transform_edit.rs b/src/tweaks/transform_edit.rs index 0712696..be59ce4 100644 --- a/src/tweaks/transform_edit.rs +++ b/src/tweaks/transform_edit.rs @@ -15,6 +15,7 @@ use crate::types::{ComponentBase, FlipParent, GameStateEditor, Transform}; use super::{Defaults, InjectAt, MemoryRegionExt, Tweak, TweakConfig}; +const EDIT_TRANSFORM_DEFAULTS: Defaults = Defaults::new(true, false); const SHIFT_COPY_TRANSFORM_DEFAULTS: Defaults = Defaults::new(true, false); type UpdateQuaternionFn = extern "fastcall" fn(*mut Transform); @@ -30,13 +31,12 @@ static mut SET_FLIP_FN: Option = None; static mut TRANSFORM: Option<*mut Transform> = None; static SHIFT_HELD: AtomicBool = AtomicBool::new(false); static FORCE_UPDATE_NEXT_TICK: AtomicBool = AtomicBool::new(false); +static DISABLE_NEXT_TICK: AtomicBool = AtomicBool::new(false); pub struct TransformEditTweak { - _update_quaternion_detour: GenericDetour, - _editor_destructor_detour: GenericDetour, disable_quaternion_slerp: bool, disable_quaternion_slerp_injection: Injection, - _safety_check_inject: Injection, + safety_check_inject: Injection, } impl TransformEditTweak { @@ -46,8 +46,10 @@ impl TransformEditTweak { self.disable_quaternion_slerp = !self.disable_quaternion_slerp; if self.disable_quaternion_slerp { self.disable_quaternion_slerp_injection.inject(); + self.safety_check_inject.inject(); } else { self.disable_quaternion_slerp_injection.remove_injection(); + self.safety_check_inject.remove_injection(); Self::force_update_quaternion(); } } @@ -85,6 +87,8 @@ impl Tweak for TransformEditTweak { where Self: Sized, { + builder.set_category(Some("Editor")); + let update_quaternion_detour = unsafe { #[no_mangle] extern "fastcall" fn update_quaternion_hook(tr: *mut Transform) { @@ -259,7 +263,7 @@ impl Tweak for TransformEditTweak { builder .toggle("Shift Copies Transform", SHIFT_COPY_TRANSFORM_DEFAULTS) .tooltip("If enabled, holding Shift while using Ctrl+Click to eyedrop component will copy the component's transform.") - // .config_key("shift_copies_transform") + .config_key("shift_copies_transform") .injection(hook_ctrl_click_injection, false) .build()?; @@ -284,17 +288,24 @@ impl Tweak for TransformEditTweak { SET_FLIP_FN = Some(std::mem::transmute::(set_flip_fn_addr)); } - unsafe { - update_quaternion_detour.enable()?; - editor_destructor_detour.enable()?; - } + builder + .toggle("Placement Transform Editing", EDIT_TRANSFORM_DEFAULTS) + .tooltip("If enabled, allows editing the placement transform.\nA grid of numbers allow you to edit the 3x3 rotation matrix of the component being placed (same as in xml).\nYou can also increment (or hold Alt to decrement) using Numpad 1-9 (make sure NumLock is on).\nNumpad 0 resets the matrix.\n(You may have to rotate a component in-editor once before it works)") + .config_key("placement_transform_editing") + .detour(update_quaternion_detour, false) + .detour(editor_destructor_detour, false) + .on_value_changed(|enabled| { + if !enabled { + FORCE_UPDATE_NEXT_TICK.store(true, Ordering::Release); + DISABLE_NEXT_TICK.store(true, Ordering::Release); + } + }) + .build()?; Ok(Self { - _update_quaternion_detour: update_quaternion_detour, - _editor_destructor_detour: editor_destructor_detour, disable_quaternion_slerp: false, disable_quaternion_slerp_injection, - _safety_check_inject: safety_check_inject, + safety_check_inject, }) } @@ -304,55 +315,60 @@ impl Tweak for TransformEditTweak { Ok(()) } - fn render(&mut self, ui: &hudhook::imgui::Ui) { - if let Some(tr) = unsafe { TRANSFORM } { - ui.text("Editor Placement Transform"); - if ui.is_item_hovered() { - ui.tooltip_text("These numbers represent the rotation matrix of the component being placed (same as in XML).\nYou can also increment (or hold Alt to decrement) using the Numpad (make sure NumLock is on).\nNumpad 0 resets the matrix."); - } - - let mut next = unsafe { (*tr).rotation_mat3i_cur }; - let mut changed = false; - #[allow(clippy::identity_op)] - for r in 0..3 { - ui.set_next_item_width(80.0); - if ui - .input_int(format!("{}", r * 3 + 1), &mut next[r * 3 + 0]) - .build() - { - changed = true; - } - ui.same_line(); - ui.set_next_item_width(80.0); - if ui - .input_int(format!("{}", r * 3 + 2), &mut next[r * 3 + 1]) - .build() - { - changed = true; + fn render_category(&mut self, ui: &hudhook::imgui::Ui, category: Option<&str>) -> anyhow::Result<()> { + if category.is_none() { + if let Some(tr) = unsafe { TRANSFORM } { + ui.separator(); + ui.text("Editor Placement Transform"); + if ui.is_item_hovered() { + ui.tooltip_text("These numbers represent the rotation matrix of the component being placed (same as in XML).\nYou can also increment (or hold Alt to decrement) using the Numpad (make sure NumLock is on).\nNumpad 0 resets the matrix."); } - ui.same_line(); - ui.set_next_item_width(80.0); - if ui - .input_int(format!("{}", r * 3 + 3), &mut next[r * 3 + 2]) - .build() - { - changed = true; + + let mut next = unsafe { (*tr).rotation_mat3i_cur }; + let mut changed = false; + #[allow(clippy::identity_op)] + for r in 0..3 { + ui.set_next_item_width(80.0); + if ui + .input_int(format!("{}", r * 3 + 1), &mut next[r * 3 + 0]) + .build() + { + changed = true; + } + ui.same_line(); + ui.set_next_item_width(80.0); + if ui + .input_int(format!("{}", r * 3 + 2), &mut next[r * 3 + 1]) + .build() + { + changed = true; + } + ui.same_line(); + ui.set_next_item_width(80.0); + if ui + .input_int(format!("{}", r * 3 + 3), &mut next[r * 3 + 2]) + .build() + { + changed = true; + } } - } - if changed { - #[allow(clippy::cast_precision_loss)] - unsafe { - (*tr).rotation_mat3i_cur = next; - (*tr).rotation_mat3i_prev = (*tr).rotation_mat3i_cur; - (*tr).rotation_mat3f_cur = next.map(|i| i as _); + if changed { + #[allow(clippy::cast_precision_loss)] + unsafe { + (*tr).rotation_mat3i_cur = next; + (*tr).rotation_mat3i_prev = (*tr).rotation_mat3i_cur; + (*tr).rotation_mat3f_cur = next.map(|i| i as _); + } } + + self.check_orthonormal(&next); } - - self.check_orthonormal(&next); } + + Ok(()) } - fn constant_render(&mut self, ui: &hudhook::imgui::Ui) { + fn constant_render(&mut self, ui: &hudhook::imgui::Ui) -> anyhow::Result<()> { let mut update = |idx: u8, add: i32| { if let Some(tr) = unsafe { TRANSFORM } { #[allow(clippy::cast_precision_loss)] @@ -410,7 +426,17 @@ impl Tweak for TransformEditTweak { Self::force_update_quaternion(); } + if DISABLE_NEXT_TICK.load(Ordering::Acquire) { + DISABLE_NEXT_TICK.store(false, Ordering::Release); + self.reset_transform(); + unsafe { + TRANSFORM = None; + } + } + SHIFT_HELD.store(ui.is_key_down(Key::LeftShift), Ordering::Release); + + Ok(()) } } @@ -507,10 +533,13 @@ extern "stdcall" fn hook_ctrl_click() { if SHIFT_HELD.load(Ordering::Acquire) { let flip = ((*component).flip_type as usize + 0x40) as *mut u8; let matrix = &mut (*component).matrix; - tr.rotation_mat3i_cur = *matrix; - // (*tr).rotation_mat3i_prev = (*tr).rotation_mat3i_cur; - // (*tr).rotation_mat3f_cur = (*tr).rotation_mat3i_cur.map(|i| i as _); - // (*tr).rotation_mat3i_cur = [1, 0, 0, 0, 1, 0, 0, 0, 1]; + let ortho = is_orthonormal(&*matrix); + if ortho || TRANSFORM.is_some() { + tr.rotation_mat3i_cur = *matrix; + // (*tr).rotation_mat3i_prev = (*tr).rotation_mat3i_cur; + // (*tr).rotation_mat3f_cur = (*tr).rotation_mat3i_cur.map(|i| i as _); + // (*tr).rotation_mat3i_cur = [1, 0, 0, 0, 1, 0, 0, 0, 1]; + } if let Some(update_quaternion) = UPDATE_QUATERNION_FN { update_quaternion(tr);