Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add camera view editing mode #100

Merged
merged 3 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/hooks/bxt.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Bunnymod XT.

use std::ffi::CString;
use std::os::raw::{c_char, c_int};
use std::os::raw::{c_char, c_float, c_int};
use std::ptr::NonNull;

use hltas::HLTAS;
Expand All @@ -27,6 +27,8 @@ pub static BXT_TAS_NOREFRESH_UNTIL_LAST_FRAMES: Pointer<unsafe extern "C" fn() -
Pointer::empty(b"bxt_tas_norefresh_until_last_frames\0");
pub static BXT_TAS_STUDIO_NOREFRESH_OVERRIDE: Pointer<unsafe extern "C" fn(c_int)> =
Pointer::empty(b"bxt_tas_studio_norefresh_override\0");
pub static BXT_TAS_STUDIO_FREECAM_SET_ORIGIN: Pointer<unsafe extern "C" fn([c_float; 3])> =
Pointer::empty(b"bxt_tas_studio_freecam_set_origin\0");

static POINTERS: &[&dyn PointerTrait] = &[
&BXT_ON_TAS_PLAYBACK_FRAME,
Expand All @@ -37,6 +39,7 @@ static POINTERS: &[&dyn PointerTrait] = &[
&BXT_TAS_NEW,
&BXT_TAS_NOREFRESH_UNTIL_LAST_FRAMES,
&BXT_TAS_STUDIO_NOREFRESH_OVERRIDE,
&BXT_TAS_STUDIO_FREECAM_SET_ORIGIN,
];

#[cfg(unix)]
Expand Down
248 changes: 245 additions & 3 deletions src/modules/tas_studio/editor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ pub struct Editor {
/// Adjustment to insert a camera line.
insert_camera_line_adjustment: Option<InsertCameraLineAdjustment>,

/// Adjustment to a camera line depending on the context.
///
/// Adjusts the yaw of `target_yaw x` line.
///
/// Adjusts the change mode and final value of `change x to x over x` line.
camera_view_adjustment: Option<CameraViewAdjustment>,

/// Smoothing window size in seconds.
smooth_window_s: f32,
/// Smoothing small window size in seconds.
Expand Down Expand Up @@ -193,14 +200,20 @@ struct ExtraCameraEditorFrameData {
smoothing_input_region: Option<(usize, usize)>,
/// Index into `script.lines` of a change frame that ends on this frame.
change_line_that_ends_here: Vec<usize>,
/// Index of a frame where the change line which starts on this frame ends.
change_ends_at: Vec<usize>,
/// Index of a change line and a frame where the frame is end frame of change line.
change_ends_at: Vec<LineChangeEndsAt>,
/// Index into `script.lines` of a camera frame that starts or ends on this frame.
camera_line_that_starts_or_ends_here: Vec<usize>,
/// Index into `script.lines` of a camera frame that starts on this frame.
camera_line_that_starts_here: Vec<usize>,
}

#[derive(Debug, Default, Clone)]
struct LineChangeEndsAt {
line_idx: usize,
end_frame_idx: usize,
}

/// Data for handling adjustment done by pressing and dragging the mouse.
#[derive(Debug, Clone, Copy)]
struct MouseAdjustment<T> {
Expand Down Expand Up @@ -296,6 +309,22 @@ struct InsertCameraLineAdjustment {
did_split: bool,
}

/// `CameraViewAdjustmentMode::Alt` adjusts different things depending on the line type. For
/// target_yaw lines, it adjusts the velocity_lock angle.
#[derive(Debug, Clone, Copy)]
pub enum CameraViewAdjustmentMode {
Yaw,
Pitch,
Alt,
}

/// Data for handling camera view adjustment.
#[derive(Debug, Clone, Copy)]
pub struct CameraViewAdjustment {
pub mode: CameraViewAdjustmentMode,
camera_line_idx: usize,
}

#[derive(Debug, Default, Clone, Copy)]
pub struct KeyboardState {
/// Whether the "faster" key is pressed.
Expand Down Expand Up @@ -374,6 +403,14 @@ impl ManualOpError {
}
}

#[derive(Clone, Copy)]
pub struct Callbacks<'a> {
pub enable_mouse_look: &'a dyn Fn(),
pub disable_mouse_look: &'a dyn Fn(),
pub get_viewangles: &'a dyn Fn() -> [f32; 3],
pub change_view_origin: &'a dyn Fn(Vec3),
}

impl Editor {
pub fn open_db(mut db: Db) -> eyre::Result<Self> {
let branches = db.branches()?;
Expand Down Expand Up @@ -415,6 +452,7 @@ impl Editor {
first_shown_frame_idx: 0,
hovered_line_idx: None,
insert_camera_line_adjustment: None,
camera_view_adjustment: None,
smooth_window_s: 0.15,
smooth_small_window_s: 0.03,
smooth_small_window_multiplier: 3.,
Expand Down Expand Up @@ -482,6 +520,10 @@ impl Editor {
self.undo_log.len()
}

pub fn camera_view_adjustment(&self) -> Option<CameraViewAdjustment> {
self.camera_view_adjustment
}

pub fn set_in_camera_editor(&mut self, value: bool) {
if self.in_camera_editor == value {
return;
Expand Down Expand Up @@ -687,7 +729,10 @@ impl Editor {
.push(line_idx);
branch.extra_cam[frame_idx]
.change_ends_at
.push(end_frame_idx);
.push(LineChangeEndsAt {
line_idx,
end_frame_idx,
});
branch.extra_cam[end_frame_idx]
.camera_line_that_starts_or_ends_here
.push(line_idx);
Expand Down Expand Up @@ -726,6 +771,7 @@ impl Editor {
|| self.side_strafe_yawspeed_adjustment.is_some()
|| self.adjacent_side_strafe_yawspeed_adjustment.is_some()
|| self.insert_camera_line_adjustment.is_some()
|| self.camera_view_adjustment.is_some()
}

/// Updates the editor state.
Expand All @@ -736,6 +782,7 @@ impl Editor {
mouse: MouseState,
keyboard: KeyboardState,
deadline: Instant,
callbacks: Callbacks,
) -> eyre::Result<()> {
let _span = info_span!("Editor::tick").entered();

Expand All @@ -750,6 +797,7 @@ impl Editor {
self.tick_adjacent_side_strafe_yawspeed_adjustment(mouse, keyboard)?;

self.tick_insert_camera_line_adjustment(mouse, keyboard)?;
self.tick_camera_view_adjustment(mouse, self.prev_mouse_state, callbacks)?;

// Predict any frames that need prediction.
//
Expand Down Expand Up @@ -1229,6 +1277,71 @@ impl Editor {
});
}
}

if let Some(hovered_line_idx) = self.hovered_line_idx {
// For camera editor camera line editing.
if mouse.buttons.is_right_down() && !self.prev_mouse_state.buttons.is_right_down() {
assert!(self.camera_view_adjustment.is_none());
assert!(!self.is_any_adjustment_active());

let line = &self.branch().branch.script.lines[hovered_line_idx];

// Find the end frame of change line or camera line so we can change origin.
// In case of change line we need to know the end frame.
// Camera line we can just use the `line_first_frame_idx`.
let line_first_frame_idx = line_first_frame_idx(self.script())
.nth(hovered_line_idx)
.expect("invalid line index");

let change_line_end_frame_index = &self.branch().extra_cam
[line_first_frame_idx]
.change_ends_at
.iter()
.find(|LineChangeEndsAt { line_idx, .. }| *line_idx == hovered_line_idx)
.map(|LineChangeEndsAt { end_frame_idx, .. }| *end_frame_idx);

let end_frame_index =
if let Some(change_line_end_frame_index) = change_line_end_frame_index {
*change_line_end_frame_index
} else {
// So, for camera line, it is 1 off but not for the change line.
// If anything crashes, it is this one.
line_first_frame_idx - 1
};

// Enable mouse look so we can look around to have a good reference.
(callbacks.enable_mouse_look)();

// Change origin to the position of the end of that line.
let mut vieworg = self.branch().frames[end_frame_index].state.player.pos;

vieworg[2] += 28.;
if self.branch().frames[end_frame_index].state.player.ducking {
vieworg[2] -= 16.;
}

(callbacks.change_view_origin)(vieworg);

// Figuring whether the line is generally changing pitch or yaw.
let mode = match line {
Line::VectorialStrafingConstraints(_) => CameraViewAdjustmentMode::Yaw,
Line::Change(Change { target, .. }) => match target {
ChangeTarget::Yaw
| ChangeTarget::VectorialStrafingYaw
| ChangeTarget::VectorialStrafingYawOffset => {
CameraViewAdjustmentMode::Yaw
}
ChangeTarget::Pitch => CameraViewAdjustmentMode::Pitch,
},
_ => CameraViewAdjustmentMode::Alt,
};

self.camera_view_adjustment = Some(CameraViewAdjustment {
mode,
camera_line_idx: hovered_line_idx,
});
}
}
}

// Finally, update the previous mouse and keyboard state.
Expand Down Expand Up @@ -1832,6 +1945,130 @@ impl Editor {
Ok(())
}

pub fn tick_camera_view_adjustment(
&mut self,
mouse: MouseState,
prev_mouse: MouseState,
callbacks: Callbacks,
) -> eyre::Result<()> {
let Some(CameraViewAdjustment {
ref mut mode,
camera_line_idx,
}) = &mut self.camera_view_adjustment
else {
return Ok(());
};

// Switching line mode.
// Should be a click.
if mouse.buttons.is_left_down() && !prev_mouse.buttons.is_left_down() {
// Cycling
// Currently there is no `pitch` line...
*mode = match *mode {
CameraViewAdjustmentMode::Yaw => CameraViewAdjustmentMode::Pitch,
CameraViewAdjustmentMode::Pitch => CameraViewAdjustmentMode::Alt,
CameraViewAdjustmentMode::Alt => CameraViewAdjustmentMode::Yaw,
}
}

// Right mouse button hold means adjusting.
if mouse.buttons.is_right_down() {
// Keep looking around
return Ok(());
}

let branch = &mut self.branches[self.branch_idx];

let viewangles = (callbacks.get_viewangles)();
let line = &mut branch.branch.script.lines[*camera_line_idx];

// Disable as soon as possible in case of failure or lock.
(callbacks.disable_mouse_look)();

let mut buffer = Vec::new();
hltas::write::gen_line(&mut buffer, line)?;
let from = String::from_utf8(buffer)
.expect("Line serialization should never produce invalid UTF-8");

match line {
Line::VectorialStrafingConstraints(constraints) => match constraints {
// Since there is no `pitch` line,
// there won't be any attempts to change `target_yaw` into `pitch`.
// `target_yaw velocity_lock` will be changed to `target_yaw x`
VectorialStrafingConstraints::VelocityYawLocking { .. } => match mode {
CameraViewAdjustmentMode::Yaw => {
*constraints = VectorialStrafingConstraints::Yaw {
yaw: viewangles[1],
tolerance: 0.,
};
}
CameraViewAdjustmentMode::Pitch => (),
CameraViewAdjustmentMode::Alt => (),
},
VectorialStrafingConstraints::Yaw { yaw, .. } => {
match mode {
CameraViewAdjustmentMode::Yaw => *yaw = viewangles[1],
CameraViewAdjustmentMode::Pitch => (),
// Alt mode will change this to `target_yaw velocity_lock`.
CameraViewAdjustmentMode::Alt => {
*constraints =
VectorialStrafingConstraints::VelocityYawLocking { tolerance: 0. };
}
}
}
_ => (),
},
Line::Change(Change {
target,
final_value,
..
}) => {
match target {
ChangeTarget::Yaw | ChangeTarget::VectorialStrafingYaw => match mode {
CameraViewAdjustmentMode::Yaw => *final_value = viewangles[1],
CameraViewAdjustmentMode::Pitch => {
*target = ChangeTarget::Pitch;
*final_value = viewangles[0];
}
CameraViewAdjustmentMode::Alt => (),
},
ChangeTarget::Pitch => match mode {
CameraViewAdjustmentMode::Yaw => {
// Pitch will always do `target_yaw` change.
*target = ChangeTarget::VectorialStrafingYaw;
*final_value = viewangles[1];
}
CameraViewAdjustmentMode::Pitch => *final_value = viewangles[0],
CameraViewAdjustmentMode::Alt => (),
},
ChangeTarget::VectorialStrafingYawOffset => {
if matches!(mode, CameraViewAdjustmentMode::Yaw) {
*final_value = viewangles[1];
}
}
}
}
_ => (),
};

let mut buffer = Vec::new();
hltas::write::gen_line(&mut buffer, line)?;
let to = String::from_utf8(buffer)
.expect("Line serialization should never produce invalid UTF-8");

let op = Operation::Replace {
line_idx: *camera_line_idx,
from,
to,
};
self.apply_operation(op)?;

// Setting back to `None` to indicate finished operation.
self.camera_view_adjustment = None;

Ok(())
}

pub fn cancel_ongoing_adjustments(&mut self) {
if let Some(adjustment) = self.frame_count_adjustment.take() {
let original_value = adjustment.original_value;
Expand Down Expand Up @@ -2027,6 +2264,11 @@ impl Editor {

self.invalidate(curr_frame_idx);
}

// Camera adjustment only takes effects after the key is released.
// So there is no need to add this.
// if let Some(CameraViewAdjustment { mode, starting_frame_idx, camera_line_idx }) =
// self.camera_view_adjustment.take() {}
}

/// Stores already-applied operation.
Expand Down
Loading
Loading