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 bxt_tas_studio_replay_views #99

Merged
merged 4 commits into from
Jul 27, 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
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],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need a _v2 function name and a change on bxt side?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea we need to change the on_tas_playback_frame() function to match. #101 also makes changes to the playback frame data so this PR (a lot shorter) needs merging first to avoid weird conflicts.

}

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);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be before or after campath?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot think of how this would matter. The only time both of these are called is when someone loads a campath and then play a campath while they also are playing back TAS views. Talking about stretch.


R_RenderView.get(marker)();
})
Expand Down
22 changes: 13 additions & 9 deletions src/modules/tas_studio/editor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,18 @@ impl Editor {
self.hovered_frame_idx.map(|idx| &self.branch().frames[idx])
}

pub fn has_all_accurate_frames(&self) -> bool {
let frame_count = self
.branch()
.branch
.script
.frame_bulks()
.map(|bulk| bulk.frame_count.get() as usize)
.sum::<usize>();

self.branch().first_predicted_frame == frame_count + 1
}

pub fn undo_log_len(&self) -> usize {
self.undo_log.len()
}
Expand Down Expand Up @@ -2814,16 +2826,8 @@ impl Editor {
return Err(ManualOpError::CannotDoDuringAdjustment);
}

let frame_count = self
.branch()
.branch
.script
.frame_bulks()
.map(|bulk| bulk.frame_count.get() as usize)
.sum::<usize>();

// Only smooth when we have all accurate frames.
if self.branch().first_predicted_frame != frame_count + 1 {
if !self.has_all_accurate_frames() {
return Err(ManualOpError::UserError(
"all frames must be accurate (simulated by the \
second game) to apply global smoothing"
Expand Down
181 changes: 179 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,89 @@ 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,
} => {
if !editor.has_all_accurate_frames() {
con_print(
marker,
"You need to have second game done simulating to playback views.\n",
);

State::Editing {
editor,
last_generation,
last_branch_idx,
simulate_at,
bridge,
}
} else {
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 => {
con_print(marker, "You need to be in the editor to playback views.\n");
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 +1537,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,9 +1656,87 @@ pub unsafe fn maybe_receive_messages_from_remote_server(marker: MainThreadMarker
editor.recompute_extra_camera_frame_data_if_needed();
}
State::PreparingToPlayToEditor(_, _, _) => unreachable!(),
State::PlayingViews { .. } => {
// Do nothing
// While PlayingViews is happening, nothing else happens or will happen.
// Because PlayingViews only starts when all accurate frames are received.
// Meaning there is nothing second game could do anything to affect the state.
}
}
}

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) };

let state = &editor.branch().frames[current_frame].state;

r_refdef_vieworg[0] = state.player.pos[0];
r_refdef_vieworg[1] = state.player.pos[1];
r_refdef_vieworg[2] = 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.
Comment on lines +1715 to +1716
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was gonna ask why this is necessary but I guess bxt-strafe just doesn't handle angle overrides yet?

Ideally it would handle overrides (then they will also work without simulation), but I guess that's effort?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea I'd say bxt-strafe is not aware of the viewangles / rendered angle data so you might call this precursor to that. This is just half of the work if the simulation wants the overrides. However, this one is specifically just the rendered yaw, not the actual player yaw.

r_refdef_vieworg[2] += 28.;
if state.player.ducking {
r_refdef_vieworg[2] -= 16.;
}

r_refdef_viewangles[0] = state.rendered_viewangles[0];

r_refdef_viewangles[1] = 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(
marker: MainThreadMarker,
data: OnTasPlaybackFrameData,
Expand Down Expand Up @@ -1602,6 +1776,9 @@ pub unsafe fn on_tas_playback_frame(
strafe_state.prev_frame_input.yaw = view_angles[1].to_radians();
}

// Store rendered viewangles.
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
Loading