Add smooth camera movement for non-discrete movement code path.

The camera key control routine now accumulates angular and positional velocity, and applies them per frame. There is also a friction component to reduce and then clear them over a few frames. This feels a lot closer to "noclip" movement, and allows mappers to navigate more precisely, which makes selecting brushes and faces in intricate areas much easier. YouTube preview of the movement here: https://www.youtube.com/watch?v=oiKwr3w0o5c
This commit is contained in:
Jay Dolan 2020-11-11 12:31:34 -05:00
parent 398e89f87f
commit 07a34e72a1
5 changed files with 147 additions and 43 deletions

View File

@ -45,7 +45,7 @@ CCamera *firstCam = NULL; // double linked list
CCamera *firstFreeCam = NULL; // single linked list
CCamera *currentCam = NULL; // single item
bool g_bEditOn = false;
int g_iEditMode = 0; // 0: editting points 1: adding points
int g_iEditMode = 0; // 0: editing points 1: adding points
int g_iActiveTarget = -1;
int g_iPreviewRunning = 0; // 0: no preview 1: start preview 2: preview in progress

View File

@ -54,6 +54,10 @@ typedef enum
#define MOVE_ROTLEFT 0x008
#define MOVE_STRAFERIGHT 0x010
#define MOVE_STRAFELEFT 0x020
#define MOVE_UP 0x040
#define MOVE_DOWN 0x080
#define MOVE_ROTUP 0x100
#define MOVE_ROTDOWN 0x200
typedef struct
{

View File

@ -430,31 +430,75 @@ void CamWnd::Cam_MouseControl( float dtime ){
}
void CamWnd::Cam_KeyControl( float dtime ) {
static vec3_t avelocity;
static vec3_t velocity;
// Update angles
if ( m_Camera.movementflags & MOVE_ROTLEFT ) {
m_Camera.angles[YAW] += 15 * dtime * g_PrefsDlg.m_nAngleSpeed;
avelocity[YAW] += dtime * g_PrefsDlg.m_nAngleSpeed;
}
if ( m_Camera.movementflags & MOVE_ROTRIGHT ) {
m_Camera.angles[YAW] -= 15 * dtime * g_PrefsDlg.m_nAngleSpeed;
avelocity[YAW] -= dtime * g_PrefsDlg.m_nAngleSpeed;
}
if ( m_Camera.movementflags & MOVE_ROTUP ) {
avelocity[PITCH] += dtime * g_PrefsDlg.m_nAngleSpeed;
}
if ( m_Camera.movementflags & MOVE_ROTDOWN ) {
avelocity[PITCH] -= dtime * g_PrefsDlg.m_nAngleSpeed;
}
// Update position
// Now rotate the angles by the scaled velocity
VectorMA( m_Camera.angles, dtime, avelocity, m_Camera.angles );
if ( m_Camera.angles[PITCH] > 90.f ) {
m_Camera.angles[PITCH] = 90.f;
} else if ( m_Camera.angles[PITCH] < -90.f) {
m_Camera.angles[PITCH] = -90.f;
}
// And then add some friction to slow us down
VectorScale( avelocity, 1.f - dtime, avelocity );
if ( VectorLength( avelocity ) < 1.f ) {
VectorClear( avelocity );
}
// Update velocity
if ( m_Camera.movementflags & MOVE_FORWARD ) {
VectorMA( m_Camera.origin, dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.forward, m_Camera.origin );
VectorMA( velocity, dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.forward, velocity );
}
if ( m_Camera.movementflags & MOVE_BACK ) {
VectorMA( m_Camera.origin, -dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.forward, m_Camera.origin );
VectorMA( velocity, -dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.forward, velocity );
}
if ( m_Camera.movementflags & MOVE_STRAFELEFT ) {
VectorMA( m_Camera.origin, -dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.right, m_Camera.origin );
VectorMA( velocity, -dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.right, velocity );
}
if ( m_Camera.movementflags & MOVE_STRAFERIGHT ) {
VectorMA( m_Camera.origin, dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.right, m_Camera.origin );
VectorMA( velocity, dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.right, velocity );
}
if ( m_Camera.movementflags & MOVE_UP ) {
VectorMA( velocity, dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.vup, velocity);
}
if ( m_Camera.movementflags & MOVE_DOWN ) {
VectorMA( velocity, -dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.vup, velocity);
}
// Now move the origin by the scaled velocity
VectorMA( m_Camera.origin, dtime, velocity, m_Camera.origin);
// And then add some friction to slow us down
VectorScale( velocity, 1.f - dtime, velocity );
if ( VectorLength( velocity ) < 1.f ) {
VectorClear( velocity );
}
// Save a screen update
if (!VectorCompare( vec3_origin, velocity ) || !VectorCompare( vec3_origin, avelocity ) ) {
// When m_bFreeMove is enabled, mousecontrol does the update
if ( m_bFreeMove ) {
return;
}
// Save a screen update (when m_bFreeMove is enabled, mousecontrol does the update)
if ( !m_bFreeMove && m_Camera.movementflags ) {
int nUpdate = ( g_PrefsDlg.m_bCamXYUpdate ) ? ( W_CAMERA | W_XY ) : ( W_CAMERA );
Sys_UpdateWindows( nUpdate );
g_pParentWnd->OnTimer();

View File

@ -378,6 +378,10 @@ void HandleKeyUp( GtkWidget *widget, gpointer data ){
case ID_CAMERA_RIGHT: g_pParentWnd->OnCameraRight( FALSE ); break;
case ID_CAMERA_STRAFELEFT: g_pParentWnd->OnCameraStrafeleft( FALSE ); break;
case ID_CAMERA_STRAFERIGHT: g_pParentWnd->OnCameraStraferight( FALSE ); break;
case ID_CAMERA_UP: g_pParentWnd->OnCameraUp( FALSE ); break;
case ID_CAMERA_DOWN: g_pParentWnd->OnCameraDown( FALSE ); break;
case ID_CAMERA_ANGLEUP: g_pParentWnd->OnCameraAngleup( FALSE ); break;
case ID_CAMERA_ANGLEDOWN: g_pParentWnd->OnCameraAngledown( FALSE ); break;
}
}
@ -695,10 +699,10 @@ gint HandleCommand( GtkWidget *widget, gpointer data ){
case ID_CAMERA_BACK: g_pParentWnd->OnCameraBack( TRUE ); break;
case ID_CAMERA_LEFT: g_pParentWnd->OnCameraLeft( TRUE ); break;
case ID_CAMERA_RIGHT: g_pParentWnd->OnCameraRight( TRUE ); break;
case ID_CAMERA_UP: g_pParentWnd->OnCameraUp(); break;
case ID_CAMERA_DOWN: g_pParentWnd->OnCameraDown(); break;
case ID_CAMERA_ANGLEUP: g_pParentWnd->OnCameraAngleup(); break;
case ID_CAMERA_ANGLEDOWN: g_pParentWnd->OnCameraAngledown(); break;
case ID_CAMERA_UP: g_pParentWnd->OnCameraUp( TRUE ); break;
case ID_CAMERA_DOWN: g_pParentWnd->OnCameraDown( TRUE ); break;
case ID_CAMERA_ANGLEUP: g_pParentWnd->OnCameraAngleup( TRUE ); break;
case ID_CAMERA_ANGLEDOWN: g_pParentWnd->OnCameraAngledown( TRUE ); break;
case ID_CAMERA_STRAFELEFT: g_pParentWnd->OnCameraStrafeleft( TRUE ); break;
case ID_CAMERA_STRAFERIGHT: g_pParentWnd->OnCameraStraferight( TRUE ); break;
case ID_GRID_TOGGLE: g_pParentWnd->OnGridToggle(); break;
@ -832,9 +836,9 @@ static gint mainframe_keypress( GtkWidget* widget, GdkEventKey* event, gpointer
static gint mainframe_keyrelease( GtkWidget* widget, GdkEventKey* event, gpointer data ){
unsigned int code = gdk_keyval_to_upper( event->keyval );
if ( gtk_accelerator_valid( event->keyval, (GdkModifierType)0 ) ) {
return TRUE;
}
// if ( gtk_accelerator_valid( event->keyval, (GdkModifierType)0 ) ) {
// return TRUE;
// }
for ( int i = 0; i < g_nCommandCount; i++ )
{
@ -849,6 +853,10 @@ static gint mainframe_keyrelease( GtkWidget* widget, GdkEventKey* event, gpointe
case ID_CAMERA_RIGHT:
case ID_CAMERA_STRAFELEFT:
case ID_CAMERA_STRAFERIGHT:
case ID_CAMERA_UP:
case ID_CAMERA_DOWN:
case ID_CAMERA_ANGLEUP:
case ID_CAMERA_ANGLEDOWN:
{
HandleKeyUp( NULL, GINT_TO_POINTER( g_Commands[i].m_nCommand ) );
g_signal_stop_emission_by_name( G_OBJECT( widget ), "key-release-event" );
@ -7253,33 +7261,81 @@ void MainFrame::OnCameraRight( bool keydown ){
}
}
void MainFrame::OnCameraUp(){
void MainFrame::OnCameraUp( bool keydown ){
if ( g_PrefsDlg.m_bCamDiscrete ) {
if ( keydown ) {
m_pCamWnd->Camera()->origin[2] += SPEED_MOVE;
int nUpdate = ( g_PrefsDlg.m_bCamXYUpdate ) ? ( W_CAMERA | W_XY | W_Z ) : ( W_CAMERA );
int nUpdate = ( g_PrefsDlg.m_bCamXYUpdate ) ? ( W_CAMERA | W_XY ) : ( W_CAMERA );
Sys_UpdateWindows( nUpdate );
}
}
else {
if ( keydown ) {
m_pCamWnd->Camera()->movementflags |= MOVE_UP;
}
else{
m_pCamWnd->Camera()->movementflags &= ~MOVE_UP;
}
}
}
void MainFrame::OnCameraDown(){
void MainFrame::OnCameraDown( bool keydown ){
if ( g_PrefsDlg.m_bCamDiscrete ) {
if ( keydown ) {
m_pCamWnd->Camera()->origin[2] -= SPEED_MOVE;
int nUpdate = ( g_PrefsDlg.m_bCamXYUpdate ) ? ( W_CAMERA | W_XY | W_Z ) : ( W_CAMERA );
int nUpdate = ( g_PrefsDlg.m_bCamXYUpdate ) ? ( W_CAMERA | W_XY ) : ( W_CAMERA );
Sys_UpdateWindows( nUpdate );
}
}
else {
if ( keydown ) {
m_pCamWnd->Camera()->movementflags |= MOVE_DOWN;
}
else{
m_pCamWnd->Camera()->movementflags &= ~MOVE_DOWN;
}
}
}
void MainFrame::OnCameraAngleup(){
m_pCamWnd->Camera()->angles[0] += SPEED_TURN;
if ( m_pCamWnd->Camera()->angles[0] > 85 ) {
m_pCamWnd->Camera()->angles[0] = 85;
void MainFrame::OnCameraAngleup( bool keydown ){
if ( g_PrefsDlg.m_bCamDiscrete ) {
if ( keydown ) {
m_pCamWnd->Camera()->angles[PITCH] += SPEED_TURN;
if ( m_pCamWnd->Camera()->angles[PITCH] > 85 ) {
m_pCamWnd->Camera()->angles[PITCH] = 85;
}
Sys_UpdateWindows( W_CAMERA | W_XY_OVERLAY );
}
}
else {
if ( keydown ) {
m_pCamWnd->Camera()->movementflags |= MOVE_ROTUP;
}
else{
m_pCamWnd->Camera()->movementflags &= ~MOVE_ROTUP;
}
}
}
void MainFrame::OnCameraAngledown(){
m_pCamWnd->Camera()->angles[0] -= SPEED_TURN;
if ( m_pCamWnd->Camera()->angles[0] < -85 ) {
m_pCamWnd->Camera()->angles[0] = -85;
void MainFrame::OnCameraAngledown( bool keydown ){
if ( g_PrefsDlg.m_bCamDiscrete ) {
if ( keydown ) {
m_pCamWnd->Camera()->angles[PITCH] -= SPEED_TURN;
if ( m_pCamWnd->Camera()->angles[PITCH] < -85 ) {
m_pCamWnd->Camera()->angles[PITCH] = -85;
}
Sys_UpdateWindows( W_CAMERA | W_XY_OVERLAY );
}
}
else {
if ( keydown ) {
m_pCamWnd->Camera()->movementflags |= MOVE_ROTDOWN;
}
else{
m_pCamWnd->Camera()->movementflags &= ~MOVE_ROTDOWN;
}
}
}
void MainFrame::OnCameraStrafeleft( bool keydown ){
// FIXME: as soon as gtk supports proper keyup/down support, remove this bit

View File

@ -703,16 +703,16 @@ void OnHelpLinks();
void OnHelpBugreport();
void OnViewClipper();
void OnToggleDetail();
void OnCameraAngledown();
void OnCameraAngleup();
void OnCameraAngledown( bool keydown );
void OnCameraAngleup( bool keydown );
void OnCameraBack( bool keydown );
void OnCameraDown();
void OnCameraDown( bool keydown );
void OnCameraForward( bool keydown );
void OnCameraLeft( bool keydown );
void OnCameraRight( bool keydown );
void OnCameraStrafeleft( bool keydown );
void OnCameraStraferight( bool keydown );
void OnCameraUp();
void OnCameraUp( bool keydown );
void OnGridToggle();
void OnPrefs();
void OnTogglecamera();