/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. Copyright (C) 2000-2013 Darklegion Development This file is part of Tremulous. Tremulous is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Tremulous is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Tremulous; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cg_drawtools.c -- helper functions called by cg_draw, cg_scoreboard, cg_info, etc #include "cg_local.h" /* =============== CG_DrawPlane Draw a quad in 3 space - basically CG_DrawPic in 3 space =============== */ void CG_DrawPlane( vec3_t origin, vec3_t down, vec3_t right, qhandle_t shader ) { polyVert_t verts[ 4 ]; vec3_t temp; VectorCopy( origin, verts[ 0 ].xyz ); verts[ 0 ].st[ 0 ] = 0; verts[ 0 ].st[ 1 ] = 0; verts[ 0 ].modulate[ 0 ] = 255; verts[ 0 ].modulate[ 1 ] = 255; verts[ 0 ].modulate[ 2 ] = 255; verts[ 0 ].modulate[ 3 ] = 255; VectorAdd( origin, right, temp ); VectorCopy( temp, verts[ 1 ].xyz ); verts[ 1 ].st[ 0 ] = 1; verts[ 1 ].st[ 1 ] = 0; verts[ 1 ].modulate[ 0 ] = 255; verts[ 1 ].modulate[ 1 ] = 255; verts[ 1 ].modulate[ 2 ] = 255; verts[ 1 ].modulate[ 3 ] = 255; VectorAdd( origin, right, temp ); VectorAdd( temp, down, temp ); VectorCopy( temp, verts[ 2 ].xyz ); verts[ 2 ].st[ 0 ] = 1; verts[ 2 ].st[ 1 ] = 1; verts[ 2 ].modulate[ 0 ] = 255; verts[ 2 ].modulate[ 1 ] = 255; verts[ 2 ].modulate[ 2 ] = 255; verts[ 2 ].modulate[ 3 ] = 255; VectorAdd( origin, down, temp ); VectorCopy( temp, verts[ 3 ].xyz ); verts[ 3 ].st[ 0 ] = 0; verts[ 3 ].st[ 1 ] = 1; verts[ 3 ].modulate[ 0 ] = 255; verts[ 3 ].modulate[ 1 ] = 255; verts[ 3 ].modulate[ 2 ] = 255; verts[ 3 ].modulate[ 3 ] = 255; trap_R_AddPolyToScene( shader, 4, verts ); } /* ================ CG_AdjustFrom640 Adjusted for resolution and screen aspect ratio ================ */ void CG_AdjustFrom640( float *x, float *y, float *w, float *h ) { #if 0 // adjust for wide screens if ( cgs.glconfig.vidWidth * 480 > cgs.glconfig.vidHeight * 640 ) { *x += 0.5 * ( cgs.glconfig.vidWidth - ( cgs.glconfig.vidHeight * 640 / 480 ) ); } #endif // scale for screen sizes *x *= cgs.screenXScale; *y *= cgs.screenYScale; *w *= cgs.screenXScale; *h *= cgs.screenYScale; } /* ================ CG_FillRect Coordinates are 640*480 virtual values ================= */ void CG_FillRect( float x, float y, float width, float height, const float *color ) { trap_R_SetColor( color ); CG_AdjustFrom640( &x, &y, &width, &height ); trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cgs.media.whiteShader ); trap_R_SetColor( NULL ); } /* ================ CG_DrawSides Coords are virtual 640x480 ================ */ void CG_DrawSides( float x, float y, float w, float h, float size ) { float sizeY; CG_AdjustFrom640( &x, &y, &w, &h ); sizeY = size * cgs.screenYScale; size *= cgs.screenXScale; trap_R_DrawStretchPic( x, y + sizeY, size, h - ( sizeY * 2.0f ), 0, 0, 0, 0, cgs.media.whiteShader ); trap_R_DrawStretchPic( x + w - size, y + sizeY, size, h - ( sizeY * 2.0f ), 0, 0, 0, 0, cgs.media.whiteShader ); } void CG_DrawTopBottom( float x, float y, float w, float h, float size ) { CG_AdjustFrom640( &x, &y, &w, &h ); size *= cgs.screenYScale; trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); } /* ================ CG_DrawRect Coordinates are 640*480 virtual values ================= */ void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ) { trap_R_SetColor( color ); CG_DrawTopBottom( x, y, width, height, size ); CG_DrawSides( x, y, width, height, size ); trap_R_SetColor( NULL ); } /* ================ CG_DrawPic Coordinates are 640*480 virtual values ================= */ void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) { CG_AdjustFrom640( &x, &y, &width, &height ); trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); } /* ================ CG_SetClipRegion ================= */ void CG_SetClipRegion( float x, float y, float w, float h ) { vec4_t clip; CG_AdjustFrom640( &x, &y, &w, &h ); clip[ 0 ] = x; clip[ 1 ] = y; clip[ 2 ] = x + w; clip[ 3 ] = y + h; trap_R_SetClipRegion( clip ); } /* ================ CG_ClearClipRegion ================= */ void CG_ClearClipRegion( void ) { trap_R_SetClipRegion( NULL ); } /* ================ CG_DrawFadePic Coordinates are 640*480 virtual values ================= */ void CG_DrawFadePic( float x, float y, float width, float height, vec4_t fcolor, vec4_t tcolor, float amount, qhandle_t hShader ) { vec4_t finalcolor; float inverse; inverse = 100 - amount; CG_AdjustFrom640( &x, &y, &width, &height ); finalcolor[ 0 ] = ( ( inverse * fcolor[ 0 ] ) + ( amount * tcolor[ 0 ] ) ) / 100; finalcolor[ 1 ] = ( ( inverse * fcolor[ 1 ] ) + ( amount * tcolor[ 1 ] ) ) / 100; finalcolor[ 2 ] = ( ( inverse * fcolor[ 2 ] ) + ( amount * tcolor[ 2 ] ) ) / 100; finalcolor[ 3 ] = ( ( inverse * fcolor[ 3 ] ) + ( amount * tcolor[ 3 ] ) ) / 100; trap_R_SetColor( finalcolor ); trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); trap_R_SetColor( NULL ); } /* ================= CG_DrawStrlen Returns character count, skiping color escape codes ================= */ int CG_DrawStrlen( const char *str ) { const char *s = str; int count = 0; while( *s ) { if( Q_IsColorString( s ) ) s += 2; else { count++; s++; } } return count; } /* ============= CG_TileClearBox This repeats a 64*64 tile graphic to fill the screen around a sized down refresh window. ============= */ static void CG_TileClearBox( int x, int y, int w, int h, qhandle_t hShader ) { float s1, t1, s2, t2; s1 = x / 64.0; t1 = y / 64.0; s2 = ( x + w ) / 64.0; t2 = ( y + h ) / 64.0; trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, hShader ); } /* ============== CG_TileClear Clear around a sized down screen ============== */ void CG_TileClear( void ) { int top, bottom, left, right; int w, h; w = cgs.glconfig.vidWidth; h = cgs.glconfig.vidHeight; if( cg.refdef.x == 0 && cg.refdef.y == 0 && cg.refdef.width == w && cg.refdef.height == h ) return; // full screen rendering top = cg.refdef.y; bottom = top + cg.refdef.height - 1; left = cg.refdef.x; right = left + cg.refdef.width - 1; // clear above view screen CG_TileClearBox( 0, 0, w, top, cgs.media.backTileShader ); // clear below view screen CG_TileClearBox( 0, bottom, w, h - bottom, cgs.media.backTileShader ); // clear left of view screen CG_TileClearBox( 0, top, left, bottom - top + 1, cgs.media.backTileShader ); // clear right of view screen CG_TileClearBox( right, top, w - right, bottom - top + 1, cgs.media.backTileShader ); } /* ================ CG_FadeColor ================ */ float *CG_FadeColor( int startMsec, int totalMsec ) { static vec4_t color; int t; if( startMsec == 0 ) return NULL; t = cg.time - startMsec; if( t >= totalMsec ) return NULL; // fade out if( totalMsec - t < FADE_TIME ) color[ 3 ] = ( totalMsec - t ) * 1.0 / FADE_TIME; else color[ 3 ] = 1.0; color[ 0 ] = color[ 1 ] = color[ 2 ] = 1; return color; } /* ================ CG_WorldToScreen ================ */ qboolean CG_WorldToScreen( vec3_t point, float *x, float *y ) { vec3_t trans; float xc, yc; float px, py; float z; px = tan( cg.refdef.fov_x * M_PI / 360.0f ); py = tan( cg.refdef.fov_y * M_PI / 360.0f ); VectorSubtract( point, cg.refdef.vieworg, trans ); xc = ( 640.0f * cg_viewsize.integer ) / 200.0f; yc = ( 480.0f * cg_viewsize.integer ) / 200.0f; z = DotProduct( trans, cg.refdef.viewaxis[ 0 ] ); if( z <= 0.001f ) return qfalse; if( x ) *x = 320.0f - DotProduct( trans, cg.refdef.viewaxis[ 1 ] ) * xc / ( z * px ); if( y ) *y = 240.0f - DotProduct( trans, cg.refdef.viewaxis[ 2 ] ) * yc / ( z * py ); return qtrue; } /* ================ CG_KeyBinding ================ */ char *CG_KeyBinding( const char *bind ) { static char key[ 32 ]; char bindbuff[ MAX_CVAR_VALUE_STRING ]; int i; key[ 0 ] = '\0'; // NOTE: change K_LAST_KEY to MAX_KEYS for full key support (eventually) for( i = 0; i < K_LAST_KEY; i++ ) { trap_Key_GetBindingBuf( i, bindbuff, sizeof( bindbuff ) ); if( !Q_stricmp( bindbuff, bind ) ) { trap_Key_KeynumToStringBuf( i, key, sizeof( key ) ); break; } } if( !key[ 0 ] ) { Q_strncpyz( key, "\\", sizeof( key ) ); Q_strcat( key, sizeof( key ), bind ); } return key; } /* ================= CG_GetColorCharForHealth ================= */ char CG_GetColorCharForHealth( int clientnum ) { char health_char = '2'; int healthPercent; int maxHealth; int curWeaponClass = cgs.clientinfo[ clientnum ].curWeaponClass; if( cgs.clientinfo[ clientnum ].team == TEAM_ALIENS ) maxHealth = BG_Class( curWeaponClass )->health; else maxHealth = BG_Class( PCL_HUMAN )->health; healthPercent = (int) ( 100.0f * (float) cgs.clientinfo[ clientnum ].health / (float) maxHealth ); if( healthPercent < 33 ) health_char = '1'; else if( healthPercent < 67 ) health_char = '3'; return health_char; } /* ================ CG_DrawSphere ================ */ void CG_DrawSphere( const vec3_t center, float radius, int customShader, const float *shaderRGBA ) { refEntity_t re; memset( &re, 0, sizeof( re ) ); re.reType = RT_MODEL; re.hModel = cgs.media.sphereModel; re.customShader = customShader; re.renderfx = RF_NOSHADOW; if( shaderRGBA != NULL ) { int i; for( i = 0; i < 4; ++i ) re.shaderRGBA[ i ] = 255 * shaderRGBA[ i ]; } VectorCopy( center, re.origin ); radius *= 0.01f; VectorSet( re.axis[ 0 ], radius, 0, 0 ); VectorSet( re.axis[ 1 ], 0, radius, 0 ); VectorSet( re.axis[ 2 ], 0, 0, radius ); re.nonNormalizedAxes = qtrue; trap_R_AddRefEntityToScene( &re ); } /* ================ CG_DrawSphericalCone ================ */ void CG_DrawSphericalCone( const vec3_t tip, const vec3_t rotation, float radius, qboolean a240, int customShader, const float *shaderRGBA ) { refEntity_t re; memset( &re, 0, sizeof( re ) ); re.reType = RT_MODEL; re.hModel = a240 ? cgs.media.sphericalCone240Model : cgs.media.sphericalCone64Model; re.customShader = customShader; re.renderfx = RF_NOSHADOW; if( shaderRGBA != NULL ) { int i; for( i = 0; i < 4; ++i ) re.shaderRGBA[ i ] = 255 * shaderRGBA[ i ]; } VectorCopy( tip, re.origin ); radius *= 0.01f; AnglesToAxis( rotation, re.axis ); VectorScale( re.axis[ 0 ], radius, re.axis[ 0 ] ); VectorScale( re.axis[ 1 ], radius, re.axis[ 1 ] ); VectorScale( re.axis[ 2 ], radius, re.axis[ 2 ] ); re.nonNormalizedAxes = qtrue; trap_R_AddRefEntityToScene( &re ); } /* ================ CG_DrawRangeMarker ================ */ void CG_DrawRangeMarker( rangeMarkerType_t rmType, const vec3_t origin, const float *angles, float range, qboolean drawSurface, qboolean drawIntersection, qboolean drawFrontline, const vec3_t rgb, float surfaceOpacity, float lineOpacity, float lineThickness ) { if( drawSurface ) { qhandle_t pcsh; vec4_t rgba; pcsh = cgs.media.plainColorShader; VectorCopy( rgb, rgba ); rgba[ 3 ] = surfaceOpacity; switch( rmType ) { case RMT_SPHERE: CG_DrawSphere( origin, range, pcsh, rgba ); break; case RMT_SPHERICAL_CONE_64: CG_DrawSphericalCone( origin, angles, range, qfalse, pcsh, rgba ); break; case RMT_SPHERICAL_CONE_240: CG_DrawSphericalCone( origin, angles, range, qtrue, pcsh, rgba ); break; } } if( drawIntersection || drawFrontline ) { const cgMediaBinaryShader_t *mbsh; cgBinaryShaderSetting_t *bshs; int i; if( cg.numBinaryShadersUsed >= NUM_BINARY_SHADERS ) return; mbsh = &cgs.media.binaryShaders[ cg.numBinaryShadersUsed ]; if( rmType == RMT_SPHERE ) { if( range > lineThickness / 2 ) { if( drawIntersection ) CG_DrawSphere( origin, range - lineThickness / 2, mbsh->b1, NULL ); CG_DrawSphere( origin, range - lineThickness / 2, mbsh->f2, NULL ); } if( drawIntersection ) CG_DrawSphere( origin, range + lineThickness / 2, mbsh->b2, NULL ); CG_DrawSphere( origin, range + lineThickness / 2, mbsh->f1, NULL ); } else if( rmType == RMT_SPHERICAL_CONE_64 || rmType == RMT_SPHERICAL_CONE_240 ) { qboolean a240; float f, r; vec3_t forward, tip; a240 = ( rmType == RMT_SPHERICAL_CONE_240 ); f = lineThickness * ( a240 ? 0.26f : 0.8f ); r = f + lineThickness * ( a240 ? 0.23f : 0.43f ); AngleVectors( angles, forward, NULL, NULL ); if( range > r ) { VectorMA( origin, f, forward, tip ); if( drawIntersection ) CG_DrawSphericalCone( tip, angles, range - r, a240, mbsh->b1, NULL ); CG_DrawSphericalCone( tip, angles, range - r, a240, mbsh->f2, NULL ); } VectorMA( origin, -f, forward, tip ); if( drawIntersection ) CG_DrawSphericalCone( tip, angles, range + r, a240, mbsh->b2, NULL ); CG_DrawSphericalCone( tip, angles, range + r, a240, mbsh->f1, NULL ); } bshs = &cg.binaryShaderSettings[ cg.numBinaryShadersUsed ]; for( i = 0; i < 3; ++i ) bshs->color[ i ] = 255 * lineOpacity * rgb[ i ]; bshs->drawIntersection = drawIntersection; bshs->drawFrontline = drawFrontline; ++cg.numBinaryShadersUsed; } }