From 064c6646b3d1f270422dba2caf09b6bc75a1b27d Mon Sep 17 00:00:00 2001
From: Ben Millwood <thebenmachine@gmail.com>
Date: Wed, 14 Oct 2009 22:33:56 +0000
Subject: * New ownerdraw for speedometer   - Controlled by cg_drawSpeed, which
 is a bitfield     Bit 1 controls the text draw, bit 2 controls the graph, and
 bit 4     ignores vertical speed in the calculation.

---
 assets/ui/menudef.h              |   1 +
 assets/ui/tremulous_common_hud.h |  16 +++++
 src/cgame/cg_draw.c              | 136 +++++++++++++++++++++++++++++++++++++++
 src/cgame/cg_local.h             |   2 +
 src/cgame/cg_main.c              |   2 +
 src/cgame/cg_view.c              |   3 +
 6 files changed, 160 insertions(+)

diff --git a/assets/ui/menudef.h b/assets/ui/menudef.h
index a5f77468..3adc8a1a 100644
--- a/assets/ui/menudef.h
+++ b/assets/ui/menudef.h
@@ -182,6 +182,7 @@ enum
   CG_TIMER_SECS,
   CG_SNAPSHOT,
   CG_LAGOMETER,
+  CG_SPEEDOMETER,
   CG_PLAYER_CROSSHAIRNAMES,
   CG_STAGE_REPORT_TEXT,
   CG_ALIENS_SCORE_LABEL,
diff --git a/assets/ui/tremulous_common_hud.h b/assets/ui/tremulous_common_hud.h
index a5f53356..37610ead 100644
--- a/assets/ui/tremulous_common_hud.h
+++ b/assets/ui/tremulous_common_hud.h
@@ -90,6 +90,22 @@ itemDef
   ownerdraw CG_LAGOMETER
 }
 
+//SPEEDOMETER
+itemDef
+{
+  name "speedometer"
+  rect (320-(STAT_W/2)) 350 STAT_W STAT_H
+  aspectBias ALIGN_CENTER
+  type ITEM_TYPE_OWNERDRAW
+  ownerdraw CG_SPEEDOMETER
+  style WINDOW_STYLE_EMPTY
+  visible MENU_TRUE
+  decoration
+  backColor COMMON_HUD_R COMMON_HUD_G COMMON_HUD_B 0.2
+  foreColor COMMON_HUD_R COMMON_HUD_G COMMON_HUD_B 0.8
+  textscale 0.3
+}
+
 //CLOCK
 itemDef
 {
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c
index 2b74a3f9..111cb12f 100644
--- a/src/cgame/cg_draw.c
+++ b/src/cgame/cg_draw.c
@@ -2094,6 +2094,139 @@ static void CG_DrawLagometer( rectDef_t *rect, float text_x, float text_y,
   CG_DrawDisconnect( );
 }
 
+#define SPEEDOMETER_NUM_SAMPLES 160
+#define SPEEDOMETER_DRAW_TEXT   0x1
+#define SPEEDOMETER_DRAW_GRAPH  0x2
+#define SPEEDOMETER_IGNORE_Z    0x4
+float speedSamples[ SPEEDOMETER_NUM_SAMPLES ];
+// array indices
+int oldestSpeedSample = 0;
+int maxSpeedSample = 0;
+
+/*
+===================
+CG_AddSpeed
+
+append a speed to the sample history
+===================
+*/
+void CG_AddSpeed( void )
+{
+  float speed;
+  vec3_t vel;
+
+  VectorCopy( cg.snap->ps.velocity, vel );
+
+  if( cg_drawSpeed.integer & SPEEDOMETER_IGNORE_Z )
+    vel[ 2 ] = 0;
+
+  speed = VectorLength( vel );
+
+  if( speed > speedSamples[ maxSpeedSample ] )
+  {
+    maxSpeedSample = oldestSpeedSample;
+    speedSamples[ oldestSpeedSample++ ] = speed;
+    oldestSpeedSample %= SPEEDOMETER_NUM_SAMPLES;
+    return;
+  }
+
+  speedSamples[ oldestSpeedSample ] = speed;
+  if( maxSpeedSample == oldestSpeedSample++ )
+  {
+    // if old max was overwritten find a new one
+    int i;
+    for( maxSpeedSample = 0, i = 1; i < SPEEDOMETER_NUM_SAMPLES; i++ )
+    {
+      if( speedSamples[ i ] > speedSamples[ maxSpeedSample ] )
+        maxSpeedSample = i;
+    }
+  }
+
+  oldestSpeedSample %= SPEEDOMETER_NUM_SAMPLES;
+}
+
+/*
+===================
+CG_DrawSpeedText
+CG_DrawSpeedGraph
+CG_DrawSpeed
+===================
+*/
+#define SPEEDOMETER_MIN_RANGE 900
+#define SPEED_MED 1000.f
+#define SPEED_FAST 1600.f
+
+static void CG_DrawSpeedGraph( rectDef_t *rect, vec4_t foreColor,
+                               vec4_t backColor )
+{
+  int i;
+  float val, max, top;
+  // colour of graph is interpolated between these values
+  const vec3_t slow = { 0.0, 0.0, 1.0 };
+  const vec3_t medium = { 0.0, 1.0, 0.0 };
+  const vec3_t fast = { 1.0, 0.0, 0.0 };
+  vec4_t color;
+
+  max = speedSamples[ maxSpeedSample ];
+  if( max < SPEEDOMETER_MIN_RANGE )
+    max = SPEEDOMETER_MIN_RANGE;
+
+  trap_R_SetColor( backColor );
+  CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.whiteShader );
+
+  Vector4Copy( foreColor, color );
+
+  for( i = 1; i < SPEEDOMETER_NUM_SAMPLES; i++ )
+  {
+    val = speedSamples[ ( oldestSpeedSample + i ) % SPEEDOMETER_NUM_SAMPLES ];
+    if( val < SPEED_MED )
+      VectorLerp( val / SPEED_MED, slow, medium, color );
+    else if( val < SPEED_FAST )
+      VectorLerp( ( val - SPEED_MED ) / ( SPEED_FAST - SPEED_MED ),
+                  medium, fast, color );
+    else
+      VectorCopy( fast, color );
+    trap_R_SetColor( color );
+    top = rect->y + ( 1 - val / max ) * rect->h;
+    CG_DrawPic( rect->x + ( i / (float)SPEEDOMETER_NUM_SAMPLES ) * rect->w, top,
+                rect->w / (float)SPEEDOMETER_NUM_SAMPLES, val * rect->h / max,
+                cgs.media.whiteShader );
+  }
+  trap_R_SetColor( NULL );
+}
+
+static void CG_DrawSpeedText( rectDef_t *rect, float text_x, float text_y,
+                              float scale, vec4_t foreColor )
+{
+  char speedstr[ 16 ];
+  float val;
+  vec4_t color;
+
+  VectorCopy( foreColor, color );
+  color[ 3 ] = 1;
+
+  if( oldestSpeedSample == 0 )
+    val = speedSamples[ SPEEDOMETER_NUM_SAMPLES - 1 ];
+  else
+    val = speedSamples[ oldestSpeedSample - 1 ];
+
+  Com_sprintf( speedstr, sizeof( speedstr ), "%d", (int)val );
+
+  UI_Text_Paint(
+      rect->x + ( rect->w - UI_Text_Width( speedstr, scale, 0 ) ) / 2.0f,
+      rect->y + ( rect->h + UI_Text_Height( speedstr, scale, 0 ) ) / 2.0f,
+      scale, color, speedstr, 0, 0, ITEM_TEXTSTYLE_NORMAL );
+}
+
+static void CG_DrawSpeed( rectDef_t *rect, float text_x, float text_y, 
+                          float scale, vec4_t foreColor, vec4_t backColor )
+{
+  if( cg_drawSpeed.integer & SPEEDOMETER_DRAW_GRAPH )
+    CG_DrawSpeedGraph( rect, foreColor, backColor );
+  if( cg_drawSpeed.integer & SPEEDOMETER_DRAW_TEXT )
+    CG_DrawSpeedText( rect, text_x, text_y, scale, foreColor );
+}
+
 /*
 ===================
 CG_DrawConsole
@@ -2565,6 +2698,9 @@ void CG_OwnerDraw( float x, float y, float w, float h, float text_x,
     case CG_TEAMOVERLAY:
       CG_DrawTeamOverlay( &rect, scale, foreColor );
       break;
+    case CG_SPEEDOMETER:
+      CG_DrawSpeed( &rect, text_x, text_y, scale, foreColor, backColor );
+      break;
 
     case CG_DEMO_PLAYBACK:
       CG_DrawDemoPlayback( &rect, foreColor, shader );
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index 7302ac78..22d312d9 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -1474,6 +1474,7 @@ extern  vmCvar_t    cg_thirdPersonPitchFollow;
 extern  vmCvar_t    cg_thirdPersonRange;
 extern  vmCvar_t    cg_stereoSeparation;
 extern  vmCvar_t    cg_lagometer;
+extern  vmCvar_t    cg_drawSpeed;
 extern  vmCvar_t    cg_synchronousClients;
 extern  vmCvar_t    cg_stats;
 extern  vmCvar_t    cg_paused;
@@ -1615,6 +1616,7 @@ char        CG_GetColorCharForHealth( int clientnum );
 
 void        CG_AddLagometerFrameInfo( void );
 void        CG_AddLagometerSnapshotInfo( snapshot_t *snap );
+void        CG_AddSpeed( void );
 void        CG_CenterPrint( const char *str, int y, int charWidth );
 void        CG_DrawActive( stereoFrame_t stereoView );
 void        CG_OwnerDraw( float x, float y, float w, float h, float text_x,
diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c
index bd3acec5..562b8038 100644
--- a/src/cgame/cg_main.c
+++ b/src/cgame/cg_main.c
@@ -149,6 +149,7 @@ vmCvar_t  cg_thirdPersonPitchFollow;
 vmCvar_t  cg_thirdPersonRange;
 vmCvar_t  cg_stereoSeparation;
 vmCvar_t  cg_lagometer;
+vmCvar_t  cg_drawSpeed;
 vmCvar_t  cg_synchronousClients;
 vmCvar_t  cg_stats;
 vmCvar_t  cg_paused;
@@ -245,6 +246,7 @@ static cvarTable_t cvarTable[ ] =
   { &cg_crosshairSize, "cg_crosshairSize", "1", CVAR_ARCHIVE },
   { &cg_addMarks, "cg_marks", "1", CVAR_ARCHIVE },
   { &cg_lagometer, "cg_lagometer", "0", CVAR_ARCHIVE },
+  { &cg_drawSpeed, "cg_drawSpeed", "0", CVAR_ARCHIVE },
   { &cg_teslaTrailTime, "cg_teslaTrailTime", "250", CVAR_ARCHIVE  },
   { &cg_gun_x, "cg_gunX", "0", CVAR_CHEAT },
   { &cg_gun_y, "cg_gunY", "0", CVAR_CHEAT },
diff --git a/src/cgame/cg_view.c b/src/cgame/cg_view.c
index 85d5bea9..02284fca 100644
--- a/src/cgame/cg_view.c
+++ b/src/cgame/cg_view.c
@@ -1437,6 +1437,9 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
   cg.renderingThirdPerson = ( cg_thirdPerson.integer || ( cg.snap->ps.stats[ STAT_HEALTH ] <= 0 ) || 
                             ( cg.chaseFollow && cg.snap->ps.pm_flags & PMF_FOLLOW) );
 
+  // update speedometer
+  CG_AddSpeed( );
+
   // build cg.refdef
   inwater = CG_CalcViewValues( );
 
-- 
cgit