-
Notifications
You must be signed in to change notification settings - Fork 9
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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"); | ||
|
@@ -987,6 +995,7 @@ static POINTERS: &[&dyn PointerTrait] = &[ | |
&S_PaintChannels, | ||
&S_TransferStereo16, | ||
&SCR_DrawLoading, | ||
&SCR_DrawPause, | ||
&scr_fov_value, | ||
&shm, | ||
&sv, | ||
|
@@ -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 || { | ||
|
@@ -2376,6 +2398,7 @@ pub mod exported { | |
let marker = MainThreadMarker::new(); | ||
|
||
campath::override_view(marker); | ||
tas_studio::tas_playback_rendered_views(marker); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be before or after campath? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)(); | ||
}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, | ||
|
@@ -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) { | ||
|
@@ -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!( | ||
|
@@ -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 { | ||
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
|
@@ -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. | ||
|
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.