Skip to content

Commit

Permalink
add bxt_tas_studio_replay_views
Browse files Browse the repository at this point in the history
  • Loading branch information
khanghugo committed Mar 30, 2024
1 parent 349d7ca commit 7214f85
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 2 deletions.
3 changes: 3 additions & 0 deletions bxt-strafe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ pub struct State {
// Number of frames for [`StrafeDir::LeftRight`] or [`StrafeDir::RightLeft`] which goes from
// `0` to `count - 1`.
pub strafe_cycle_frame_count: u32,
// In case of yaw and pitch override, this might be useful.
pub rendered_viewangles: Vec3,
}

impl State {
Expand All @@ -145,6 +147,7 @@ impl State {
jumped: false,
move_traces: ArrayVec::new(),
strafe_cycle_frame_count: 0,
rendered_viewangles: Vec3::ZERO,
};

rv.update_place(tracer);
Expand Down
1 change: 1 addition & 0 deletions src/hooks/bxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ pub struct OnTasPlaybackFrameData {
pub strafe_cycle_frame_count: u32,
pub prev_predicted_trace_fractions: [f32; 4],
pub prev_predicted_trace_normal_zs: [f32; 4],
pub rendered_viewangles: [f32; 3],
}

unsafe extern "C" fn on_tas_playback_frame(data: OnTasPlaybackFrameData) -> c_int {
Expand Down
23 changes: 23 additions & 0 deletions src/hooks/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,14 @@ pub static SCR_DrawLoading: Pointer<unsafe extern "C" fn()> = Pointer::empty_pat
]),
my_SCR_DrawLoading as _,
);
pub static SCR_DrawPause: Pointer<unsafe extern "C" fn()> = Pointer::empty_patterns(
b"SCR_DrawPause\0",
// To find, search for "cz_worldmap". You are in SCR_DrawPause().
Patterns(&[
pattern!(D9 05 ?? ?? ?? ?? D8 1D ?? ?? ?? ?? DF E0 F6 C4 44 7B ?? A1 ?? ?? ?? ?? 85 C0 74 ?? E8 ?? ?? ?? ?? 85),
]),
my_SCR_DrawPause as _,
);
pub static scr_fov_value: Pointer<*mut c_float> = Pointer::empty(b"scr_fov_value\0");
pub static shm: Pointer<*mut *mut dma_t> = Pointer::empty(b"shm\0");
pub static sv: Pointer<*mut c_void> = Pointer::empty(b"sv\0");
Expand Down Expand Up @@ -987,6 +995,7 @@ static POINTERS: &[&dyn PointerTrait] = &[
&S_PaintChannels,
&S_TransferStereo16,
&SCR_DrawLoading,
&SCR_DrawPause,
&scr_fov_value,
&shm,
&sv,
Expand Down Expand Up @@ -2191,6 +2200,19 @@ pub mod exported {
})
}

#[export_name = "SCR_DrawPause"]
pub unsafe extern "C" fn my_SCR_DrawPause() {
abort_on_panic(move || {
let marker = MainThreadMarker::new();

if !tas_studio::should_draw_pause(marker) {
return;
}

SCR_DrawPause.get(marker)();
})
}

#[export_name = "SV_Frame"]
pub unsafe extern "C" fn my_SV_Frame() {
abort_on_panic(move || {
Expand Down Expand Up @@ -2376,6 +2398,7 @@ pub mod exported {
let marker = MainThreadMarker::new();

campath::override_view(marker);
tas_studio::tas_playback_rendered_views(marker);

R_RenderView.get(marker)();
})
Expand Down
163 changes: 161 additions & 2 deletions src/modules/tas_studio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ impl Module for TasStudio {
&BXT_TAS_STUDIO_NEW,
&BXT_TAS_STUDIO_LOAD,
&BXT_TAS_STUDIO_CONVERT_HLTAS,
&BXT_TAS_STUDIO_REPLAY_VIEWS,
&BXT_TAS_STUDIO_REPLAY,
&BXT_TAS_STUDIO_SET_STOP_FRAME,
&BXT_TAS_STUDIO_SET_YAWSPEED,
Expand Down Expand Up @@ -391,11 +392,13 @@ fn norefresh_until_stop_frame_frame_idx(marker: MainThreadMarker, editor: &Edito
let norefresh_last_frames =
unsafe { bxt::BXT_TAS_NOREFRESH_UNTIL_LAST_FRAMES.get(marker)() } as usize;

if editor.stop_frame() == 0 {
let first_frame_idx = if editor.stop_frame() == 0 {
(editor.branch().frames.len() - 1).saturating_sub(norefresh_last_frames)
} else {
(editor.stop_frame() as usize).saturating_sub(norefresh_last_frames)
}
};

first_frame_idx.clamp(0, editor.branch().frames.len() - 1)
}

fn set_effective_norefresh_until_stop_frame(marker: MainThreadMarker, editor: &Editor) {
Expand Down Expand Up @@ -433,6 +436,71 @@ fn replay(marker: MainThreadMarker) {
};
}

static BXT_TAS_STUDIO_REPLAY_VIEWS: Command = Command::new(
b"bxt_tas_studio_replay_views\0",
handler!(
"bxt_tas_studio_replay_views
Replays the currently loaded camera views of TAS up to the stop frame.",
replay_views as fn(_)
),
);

fn replay_views(marker: MainThreadMarker) {
let mut state = STATE.borrow_mut(marker);
*state = match mem::take(&mut *state) {
State::Editing {
mut editor,
last_generation,
last_branch_idx,
simulate_at,
bridge,
} => {
editor.cancel_ongoing_adjustments();

sdl::set_relative_mouse_mode(marker, true);
client::activate_mouse(marker, true);

// bxt_tas_norefresh_until_last_frames = 0 means 1 frame being played. Heh.
let start_frame = norefresh_until_stop_frame_frame_idx(marker, &editor);

engine::prepend_command(marker, "bxt_hud 0; hud_draw 0\n");

State::PlayingViews {
editor,
last_generation,
last_branch_idx,
simulate_at,
bridge,
current_frame: start_frame,
}
}
// Press play view again to stop.
State::PlayingViews {
editor,
last_generation,
last_branch_idx,
simulate_at,
bridge,
..
} => {
sdl::set_relative_mouse_mode(marker, false);
client::activate_mouse(marker, false);
engine::prepend_command(marker, "bxt_hud 1; hud_draw 1\n");
ENABLE_FREECAM_ON_CALCREFDEF.set(marker, true);

State::Editing {
editor,
last_generation,
last_branch_idx,
simulate_at,
bridge,
}
}
other => other,
};
}

static BXT_TAS_STUDIO_SET_STOP_FRAME: Command = Command::new(
b"bxt_tas_studio_set_stop_frame\0",
handler!(
Expand Down Expand Up @@ -1451,6 +1519,16 @@ enum State {
simulate_at: Option<Instant>,
bridge: Bridge,
},
/// Playing camera views, will open the editor afterwards.
/// Playback is inside Editing mode so original data will be restored.
PlayingViews {
editor: Editor,
last_generation: u16,
last_branch_idx: usize,
simulate_at: Option<Instant>,
bridge: Bridge,
current_frame: usize,
},
}

impl Default for State {
Expand Down Expand Up @@ -1560,7 +1638,82 @@ pub unsafe fn maybe_receive_messages_from_remote_server(marker: MainThreadMarker
editor.recompute_extra_camera_frame_data_if_needed();
}
State::PreparingToPlayToEditor(_, _, _) => unreachable!(),
_ => (),
}
}

pub fn should_draw_pause(marker: MainThreadMarker) -> bool {
!matches!(*STATE.borrow_mut(marker), State::PlayingViews { .. })
}

pub fn tas_playback_rendered_views(marker: MainThreadMarker) {
if !TasStudio.is_enabled(marker) {
return;
}

let mut state = STATE.borrow_mut(marker);
*state = match mem::take(&mut *state) {
State::PlayingViews {
editor,
last_generation,
last_branch_idx,
simulate_at,
bridge,
current_frame,
} => {
if current_frame == editor.stop_frame() as usize
|| current_frame >= editor.branch().frames.len()
{
sdl::set_relative_mouse_mode(marker, false);
client::activate_mouse(marker, false);
engine::prepend_command(marker, "bxt_hud 1; hud_draw 1\n");
ENABLE_FREECAM_ON_CALCREFDEF.set(marker, true);

State::Editing {
editor,
last_generation,
last_branch_idx,
simulate_at,
bridge,
}
} else {
let r_refdef_vieworg = unsafe { &mut *engine::r_refdef_vieworg.get(marker) };
let r_refdef_viewangles = unsafe { &mut *engine::r_refdef_viewangles.get(marker) };

r_refdef_vieworg[0] = editor.branch().frames[current_frame].state.player.pos[0];
r_refdef_vieworg[1] = editor.branch().frames[current_frame].state.player.pos[1];
r_refdef_vieworg[2] = editor.branch().frames[current_frame].state.player.pos[2];

// We don't keep track of vieworg so just deduce it from here instead.
// It is not easy to keep track of vieworg because it isn't there to track.
// vieworg is calculated.
// It is also funny how vieworg is calculated here,
// but viewangles is passed from BXT.
r_refdef_vieworg[2] += 28.;
if editor.branch().frames[current_frame].state.player.ducking {
r_refdef_vieworg[2] -= 16.;
}

r_refdef_viewangles[0] = editor.branch().frames[current_frame]
.state
.rendered_viewangles[0];

r_refdef_viewangles[1] = editor.branch().frames[current_frame]
.state
.rendered_viewangles[1];

State::PlayingViews {
editor,
last_generation,
last_branch_idx,
simulate_at,
bridge,
current_frame: current_frame + 1,
}
}
}
other => other,
};
}

pub unsafe fn on_tas_playback_frame(
Expand Down Expand Up @@ -1602,6 +1755,12 @@ pub unsafe fn on_tas_playback_frame(
strafe_state.prev_frame_input.yaw = view_angles[1].to_radians();
}

// Store the rendered viewangles
// Viewangles is always available. Same as origin.
// Vieworigin is something that is not available per player. It is calculated upon
// rendering.
strafe_state.rendered_viewangles = data.rendered_viewangles.into();

// We don't have a good way to extract real trace results from the movement code, so let's
// make up trace results based on previous frame's predicted fractions and normal Zs from
// BXT.
Expand Down

0 comments on commit 7214f85

Please sign in to comment.