DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

DeviceContext.cpp (29938B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../idlib/precompiled.h"
     31 
     32 #include "DeviceContext.h"
     33 #include "../renderer/GuiModel.h"
     34 
     35 extern idCVar in_useJoystick;
     36 
     37 // bypass rendersystem to directly work on guiModel
     38 extern idGuiModel * tr_guiModel;
     39 
     40 idVec4 idDeviceContext::colorPurple;
     41 idVec4 idDeviceContext::colorOrange;
     42 idVec4 idDeviceContext::colorYellow;
     43 idVec4 idDeviceContext::colorGreen;
     44 idVec4 idDeviceContext::colorBlue;
     45 idVec4 idDeviceContext::colorRed;
     46 idVec4 idDeviceContext::colorBlack;
     47 idVec4 idDeviceContext::colorWhite;
     48 idVec4 idDeviceContext::colorNone;
     49 
     50 void idDeviceContext::Init() {
     51 	xScale = 1.0f;
     52 	yScale = 1.0f;
     53 	xOffset = 0.0f;
     54 	yOffset = 0.0f;
     55 	whiteImage = declManager->FindMaterial("guis/assets/white.tga");
     56 	whiteImage->SetSort( SS_GUI );
     57 	activeFont = renderSystem->RegisterFont( "" );
     58 	colorPurple = idVec4(1, 0, 1, 1);
     59 	colorOrange = idVec4(1, 1, 0, 1);
     60 	colorYellow = idVec4(0, 1, 1, 1);
     61 	colorGreen = idVec4(0, 1, 0, 1);
     62 	colorBlue = idVec4(0, 0, 1, 1);
     63 	colorRed = idVec4(1, 0, 0, 1);
     64 	colorWhite = idVec4(1, 1, 1, 1);
     65 	colorBlack = idVec4(0, 0, 0, 1);
     66 	colorNone = idVec4(0, 0, 0, 0);
     67 	cursorImages[CURSOR_ARROW] = declManager->FindMaterial("ui/assets/guicursor_arrow.tga");	
     68 	cursorImages[CURSOR_HAND] = declManager->FindMaterial("ui/assets/guicursor_hand.tga");
     69 	cursorImages[CURSOR_HAND_JOY1] = declManager->FindMaterial("ui/assets/guicursor_hand_cross.tga");
     70 	cursorImages[CURSOR_HAND_JOY2] = declManager->FindMaterial("ui/assets/guicursor_hand_circle.tga");
     71 	cursorImages[CURSOR_HAND_JOY3] = declManager->FindMaterial("ui/assets/guicursor_hand_square.tga");
     72 	cursorImages[CURSOR_HAND_JOY4] = declManager->FindMaterial("ui/assets/guicursor_hand_triangle.tga");
     73 	cursorImages[CURSOR_HAND_JOY1] = declManager->FindMaterial("ui/assets/guicursor_hand_a.tga");
     74 	cursorImages[CURSOR_HAND_JOY2] = declManager->FindMaterial("ui/assets/guicursor_hand_b.tga");
     75 	cursorImages[CURSOR_HAND_JOY3] = declManager->FindMaterial("ui/assets/guicursor_hand_x.tga");
     76 	cursorImages[CURSOR_HAND_JOY4] = declManager->FindMaterial("ui/assets/guicursor_hand_y.tga");
     77 	scrollBarImages[SCROLLBAR_HBACK] = declManager->FindMaterial("ui/assets/scrollbarh.tga");
     78 	scrollBarImages[SCROLLBAR_VBACK] = declManager->FindMaterial("ui/assets/scrollbarv.tga");
     79 	scrollBarImages[SCROLLBAR_THUMB] = declManager->FindMaterial("ui/assets/scrollbar_thumb.tga");
     80 	scrollBarImages[SCROLLBAR_RIGHT] = declManager->FindMaterial("ui/assets/scrollbar_right.tga");
     81 	scrollBarImages[SCROLLBAR_LEFT] = declManager->FindMaterial("ui/assets/scrollbar_left.tga");
     82 	scrollBarImages[SCROLLBAR_UP] = declManager->FindMaterial("ui/assets/scrollbar_up.tga");
     83 	scrollBarImages[SCROLLBAR_DOWN] = declManager->FindMaterial("ui/assets/scrollbar_down.tga");
     84 	cursorImages[CURSOR_ARROW]->SetSort( SS_GUI );
     85 	cursorImages[CURSOR_HAND]->SetSort( SS_GUI );
     86 	scrollBarImages[SCROLLBAR_HBACK]->SetSort( SS_GUI );
     87 	scrollBarImages[SCROLLBAR_VBACK]->SetSort( SS_GUI );
     88 	scrollBarImages[SCROLLBAR_THUMB]->SetSort( SS_GUI );
     89 	scrollBarImages[SCROLLBAR_RIGHT]->SetSort( SS_GUI );
     90 	scrollBarImages[SCROLLBAR_LEFT]->SetSort( SS_GUI );
     91 	scrollBarImages[SCROLLBAR_UP]->SetSort( SS_GUI );
     92 	scrollBarImages[SCROLLBAR_DOWN]->SetSort( SS_GUI );
     93 	cursor = CURSOR_ARROW;
     94 	enableClipping = true;
     95 	overStrikeMode = true;
     96 	mat.Identity();
     97 	matIsIdentity = true;
     98 	origin.Zero();
     99 	initialized = true;
    100 }
    101 
    102 void idDeviceContext::Shutdown() {
    103 	clipRects.Clear();
    104 	Clear();
    105 }
    106 
    107 void idDeviceContext::Clear() {
    108 	initialized = false;
    109 }
    110 
    111 idDeviceContext::idDeviceContext() {
    112 	Clear();
    113 }
    114 
    115 void idDeviceContext::SetTransformInfo(const idVec3 &org, const idMat3 &m) {
    116 	origin = org;
    117 	mat = m;
    118 	matIsIdentity = mat.IsIdentity();
    119 }
    120 
    121 // 
    122 //  added method
    123 void idDeviceContext::GetTransformInfo(idVec3& org, idMat3& m )
    124 {
    125 	m = mat;
    126 	org = origin;
    127 }
    128 // 
    129 
    130 void idDeviceContext::EnableClipping(bool b) { 
    131 	enableClipping = b; 
    132 };
    133 
    134 void idDeviceContext::PopClipRect() {
    135 	if (clipRects.Num()) {
    136 		clipRects.RemoveIndex(clipRects.Num()-1);
    137 	}
    138 }
    139 
    140 void idDeviceContext::PushClipRect(idRectangle r) {
    141 	clipRects.Append(r);
    142 }
    143 
    144 bool idDeviceContext::ClippedCoords(float *x, float *y, float *w, float *h, float *s1, float *t1, float *s2, float *t2) {
    145 
    146 	if ( enableClipping == false || clipRects.Num() == 0 ) {
    147 		return false;
    148 	}
    149 
    150 	int c = clipRects.Num();
    151 	while( --c > 0 ) {
    152 		idRectangle *clipRect = &clipRects[c];
    153  
    154 		float ox = *x;
    155 		float oy = *y;
    156 		float ow = *w;
    157 		float oh = *h;
    158 
    159 		if ( ow <= 0.0f || oh <= 0.0f ) {
    160 			break;
    161 		}
    162 
    163 		if (*x < clipRect->x) {
    164 			*w -= clipRect->x - *x;
    165 			*x = clipRect->x;
    166 		} else if (*x > clipRect->x + clipRect->w) {
    167 			*x = *w = *y = *h = 0;
    168 		}
    169 		if (*y < clipRect->y) {
    170 			*h -= clipRect->y - *y;
    171 			*y = clipRect->y;
    172 		} else if (*y > clipRect->y + clipRect->h) {
    173 			*x = *w = *y = *h = 0;
    174 		}
    175 		if (*w > clipRect->w) {
    176 			*w = clipRect->w - *x + clipRect->x;
    177 		} else if (*x + *w > clipRect->x + clipRect->w) {
    178 			*w = clipRect->Right() - *x;
    179 		}
    180 		if (*h > clipRect->h) {
    181 			*h = clipRect->h - *y + clipRect->y;
    182 		} else if (*y + *h > clipRect->y + clipRect->h) {
    183 			*h = clipRect->Bottom() - *y;
    184 		}
    185 
    186 		if ( s1 && s2 && t1 && t2 && ow > 0.0f ) {
    187 			float ns1, ns2, nt1, nt2;
    188 			// upper left
    189 			float u = ( *x - ox ) / ow;
    190 			ns1 = *s1 * ( 1.0f - u ) + *s2 * ( u );
    191 
    192 			// upper right
    193 			u = ( *x + *w - ox ) / ow;
    194 			ns2 = *s1 * ( 1.0f - u ) + *s2 * ( u );
    195 
    196 			// lower left
    197 			u = ( *y - oy ) / oh;
    198 			nt1 = *t1 * ( 1.0f - u ) + *t2 * ( u );
    199 
    200 			// lower right
    201 			u = ( *y + *h - oy ) / oh;
    202 			nt2 = *t1 * ( 1.0f - u ) + *t2 * ( u );
    203 
    204 			// set values
    205 			*s1 = ns1;
    206 			*s2 = ns2;
    207 			*t1 = nt1;
    208 			*t2 = nt2;
    209 		}
    210 	}
    211 
    212 	return (*w == 0 || *h == 0) ? true : false;
    213 }
    214 
    215 /*
    216 =============
    217 DrawStretchPic
    218 =============
    219 */
    220 void idDeviceContext::DrawWinding( idWinding & w, const idMaterial * mat ) {
    221 
    222 	idPlane p;
    223 
    224 	p.Normal().Set( 1.0f, 0.0f, 0.0f );
    225 	p.SetDist( 0.0f );
    226 	w.ClipInPlace( p );
    227 
    228 	p.Normal().Set( -1.0f, 0.0f, 0.0f );
    229 	p.SetDist( -SCREEN_WIDTH );
    230 	w.ClipInPlace( p );
    231 
    232 	p.Normal().Set( 0.0f, 1.0f, 0.0f );
    233 	p.SetDist( 0.0f );
    234 	w.ClipInPlace( p );
    235 
    236 	p.Normal().Set( 0.0f, -1.0f, 0.0f );
    237 	p.SetDist( -SCREEN_HEIGHT );
    238 	w.ClipInPlace( p );
    239 
    240 	if ( w.GetNumPoints() < 3 ) {
    241 		return;
    242 	}
    243 
    244 	int numIndexes = 0;
    245 	triIndex_t tempIndexes[(MAX_POINTS_ON_WINDING-2)*3];
    246 	for ( int j = 2; j < w.GetNumPoints(); j++ ) {
    247 		tempIndexes[numIndexes++] = 0;
    248 		tempIndexes[numIndexes++] = j - 1;
    249 		tempIndexes[numIndexes++] = j;
    250 	}
    251 	assert( numIndexes == ( w.GetNumPoints() - 2 ) * 3 );
    252 
    253 	idDrawVert * verts = renderSystem->AllocTris( w.GetNumPoints(), tempIndexes, numIndexes, mat );
    254 	if ( verts == NULL ) {
    255 		return;
    256 	}
    257 	uint32 currentColor = renderSystem->GetColor();
    258 
    259 	for ( int j = 0 ; j < w.GetNumPoints() ; j++ ) {
    260 		verts[j].xyz.x = xOffset + w[j].x * xScale;
    261 		verts[j].xyz.y = yOffset + w[j].y * yScale;
    262 		verts[j].xyz.z = w[j].z;
    263 		verts[j].SetTexCoord( w[j].s, w[j].t );
    264 		verts[j].SetColor( currentColor );
    265 		verts[j].ClearColor2();
    266 		verts[j].SetNormal( 0.0f, 0.0f, 1.0f );
    267 		verts[j].SetTangent( 1.0f, 0.0f, 0.0f );
    268 		verts[j].SetBiTangent( 0.0f, 1.0f, 0.0f );
    269 	}
    270 }
    271 
    272 void idDeviceContext::DrawStretchPic(float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial *shader) {
    273 	if ( matIsIdentity ) {
    274 		renderSystem->DrawStretchPic( xOffset + x * xScale, yOffset + y * yScale, w * xScale, h * yScale, s1, t1, s2, t2, shader );
    275 		return;
    276 	}
    277 
    278 	idFixedWinding winding;
    279 	winding.AddPoint( idVec5( x, y, 0.0f, s1, t1 ) );
    280 	winding.AddPoint( idVec5( x+w, y, 0.0f, s2, t1 ) );
    281 	winding.AddPoint( idVec5( x+w, y+h, 0.0f, s2, t2 ) );
    282 	winding.AddPoint( idVec5( x, y+h, 0.0f, s1, t2 ) );
    283 
    284 	for ( int i = 0; i < winding.GetNumPoints(); i++ ) {
    285 		winding[i].ToVec3() -= origin;
    286 		winding[i].ToVec3() *= mat;
    287 		winding[i].ToVec3() += origin;
    288 	}
    289 
    290 	DrawWinding( winding, shader );
    291 }
    292 
    293 
    294 void idDeviceContext::DrawMaterial(float x, float y, float w, float h, const idMaterial *mat, const idVec4 &color, float scalex, float scaley) {
    295 
    296 	renderSystem->SetColor(color);
    297 
    298 	float	s0, s1, t0, t1;
    299 // 
    300 //  handle negative scales as well	
    301 	if ( scalex < 0 )
    302 	{
    303 		w *= -1;
    304 		scalex *= -1;
    305 	}
    306 	if ( scaley < 0 )
    307 	{
    308 		h *= -1;
    309 		scaley *= -1;
    310 	}
    311 // 
    312 	if( w < 0 ) {	// flip about vertical
    313 		w  = -w;
    314 		s0 = 1 * scalex;
    315 		s1 = 0;
    316 	}
    317 	else {
    318 		s0 = 0;
    319 		s1 = 1 * scalex;
    320 	}
    321 
    322 	if( h < 0 ) {	// flip about horizontal
    323 		h  = -h;
    324 		t0 = 1 * scaley;
    325 		t1 = 0;
    326 	}
    327 	else {
    328 		t0 = 0;
    329 		t1 = 1 * scaley;
    330 	}
    331 
    332 	if ( ClippedCoords( &x, &y, &w, &h, &s0, &t0, &s1, &t1 ) ) {
    333 		return;
    334 	}
    335 
    336 	DrawStretchPic( x, y, w, h, s0, t0, s1, t1, mat);
    337 }
    338 
    339 void idDeviceContext::DrawMaterialRotated(float x, float y, float w, float h, const idMaterial *mat, const idVec4 &color, float scalex, float scaley, float angle) {
    340 	
    341 	renderSystem->SetColor(color);
    342 
    343 	float	s0, s1, t0, t1;
    344 	// 
    345 	//  handle negative scales as well	
    346 	if ( scalex < 0 )
    347 	{
    348 		w *= -1;
    349 		scalex *= -1;
    350 	}
    351 	if ( scaley < 0 )
    352 	{
    353 		h *= -1;
    354 		scaley *= -1;
    355 	}
    356 	// 
    357 	if( w < 0 ) {	// flip about vertical
    358 		w  = -w;
    359 		s0 = 1 * scalex;
    360 		s1 = 0;
    361 	}
    362 	else {
    363 		s0 = 0;
    364 		s1 = 1 * scalex;
    365 	}
    366 
    367 	if( h < 0 ) {	// flip about horizontal
    368 		h  = -h;
    369 		t0 = 1 * scaley;
    370 		t1 = 0;
    371 	}
    372 	else {
    373 		t0 = 0;
    374 		t1 = 1 * scaley;
    375 	}
    376 
    377 	if ( angle == 0.0f && ClippedCoords( &x, &y, &w, &h, &s0, &t0, &s1, &t1 ) ) {
    378 		return;
    379 	}
    380 
    381 	DrawStretchPicRotated( x, y, w, h, s0, t0, s1, t1, mat, angle);
    382 }
    383 
    384 void idDeviceContext::DrawStretchPicRotated(float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial *shader, float angle) {
    385 	
    386 	idFixedWinding winding;
    387 	winding.AddPoint( idVec5( x, y, 0.0f, s1, t1 ) );
    388 	winding.AddPoint( idVec5( x+w, y, 0.0f, s2, t1 ) );
    389 	winding.AddPoint( idVec5( x+w, y+h, 0.0f, s2, t2 ) );
    390 	winding.AddPoint( idVec5( x, y+h, 0.0f, s1, t2 ) );
    391 
    392 	for ( int i = 0; i < winding.GetNumPoints(); i++ ) {
    393 		winding[i].ToVec3() -= origin;
    394 		winding[i].ToVec3() *= mat;
    395 		winding[i].ToVec3() += origin;
    396 	}
    397 
    398 	//Generate a translation so we can translate to the center of the image rotate and draw
    399 	idVec3 origTrans;
    400 	origTrans.x = x+(w/2);
    401 	origTrans.y = y+(h/2);
    402 	origTrans.z = 0;
    403 
    404 
    405 	//Rotate the verts about the z axis before drawing them
    406 	idMat3 rotz;
    407 	rotz.Identity();
    408 	float sinAng, cosAng;
    409 	idMath::SinCos( angle, sinAng, cosAng );
    410 	rotz[0][0] = cosAng;
    411 	rotz[0][1] = sinAng;
    412 	rotz[1][0] = -sinAng;
    413 	rotz[1][1] = cosAng;
    414 	for (int i = 0; i < winding.GetNumPoints(); i++) {
    415 		winding[i].ToVec3() -= origTrans;
    416 		winding[i].ToVec3() *= rotz;
    417 		winding[i].ToVec3() += origTrans;
    418 	}
    419 	
    420 	DrawWinding( winding, shader );
    421 }
    422 
    423 void idDeviceContext::DrawFilledRect( float x, float y, float w, float h, const idVec4 &color) {
    424 
    425 	if ( color.w == 0.0f ) {
    426 		return;
    427 	}
    428 
    429 	renderSystem->SetColor(color);
    430 	
    431 	if (ClippedCoords(&x, &y, &w, &h, NULL, NULL, NULL, NULL)) {
    432 		return;
    433 	}
    434 
    435 	DrawStretchPic( x, y, w, h, 0, 0, 0, 0, whiteImage);
    436 }
    437 
    438 
    439 void idDeviceContext::DrawRect( float x, float y, float w, float h, float size, const idVec4 &color) {
    440 
    441 	if ( color.w == 0.0f ) {
    442 		return;
    443 	}
    444 
    445 	renderSystem->SetColor(color);
    446 	
    447 	if (ClippedCoords(&x, &y, &w, &h, NULL, NULL, NULL, NULL)) {
    448 		return;
    449 	}
    450 
    451 	DrawStretchPic( x, y, size, h, 0, 0, 0, 0, whiteImage );
    452 	DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, whiteImage );
    453 	DrawStretchPic( x, y, w, size, 0, 0, 0, 0, whiteImage );
    454 	DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, whiteImage );
    455 }
    456 
    457 void idDeviceContext::DrawMaterialRect( float x, float y, float w, float h, float size, const idMaterial *mat, const idVec4 &color) {
    458 
    459 	if ( color.w == 0.0f ) {
    460 		return;
    461 	}
    462 
    463 	renderSystem->SetColor(color);
    464 	DrawMaterial( x, y, size, h, mat, color );
    465 	DrawMaterial( x + w - size, y, size, h, mat, color );
    466 	DrawMaterial( x, y, w, size, mat, color );
    467 	DrawMaterial( x, y + h - size, w, size, mat, color );
    468 }
    469 
    470 
    471 void idDeviceContext::SetCursor(int n) {
    472 	
    473 	if ( n > CURSOR_ARROW && n < CURSOR_COUNT ) {
    474 
    475 		keyBindings_t binds = idKeyInput::KeyBindingsFromBinding( "_use", true );
    476 
    477 		keyNum_t keyNum = K_NONE;
    478 		if ( in_useJoystick.GetBool() ) {
    479 			 keyNum = idKeyInput::StringToKeyNum( binds.gamepad.c_str() );
    480 		} 
    481 
    482 		if ( keyNum != K_NONE ) {
    483 			
    484 			if ( keyNum == K_JOY1 ) {
    485 				cursor = CURSOR_HAND_JOY1;
    486 			} else if ( keyNum == K_JOY2 ) {
    487 				cursor = CURSOR_HAND_JOY2;
    488 			} else if ( keyNum == K_JOY3 ) {
    489 				cursor = CURSOR_HAND_JOY3;
    490 			} else if ( keyNum == K_JOY4 ) {
    491 				cursor = CURSOR_HAND_JOY4;
    492 			}
    493 
    494 		} else {
    495 			cursor = CURSOR_HAND;
    496 		}
    497 
    498 	} else {
    499 		cursor = CURSOR_ARROW;
    500 	}
    501 }
    502 
    503 void idDeviceContext::DrawCursor(float *x, float *y, float size) {
    504 	if (*x < 0) {
    505 		*x = 0;
    506 	}
    507 
    508 	if (*x >= VIRTUAL_WIDTH) {
    509 		*x = VIRTUAL_WIDTH;
    510 	}
    511 
    512 	if (*y < 0) {
    513 		*y = 0;
    514 	}
    515 
    516 	if (*y >= VIRTUAL_HEIGHT) {
    517 		*y = VIRTUAL_HEIGHT;
    518 	}
    519 
    520 	renderSystem->SetColor(colorWhite);
    521 	DrawStretchPic( *x, *y, size, size, 0, 0, 1, 1, cursorImages[cursor]);
    522 }
    523 /*
    524  =======================================================================================================================
    525  =======================================================================================================================
    526  */
    527 
    528 void idDeviceContext::PaintChar( float x, float y, const scaledGlyphInfo_t & glyphInfo ) {
    529 	y -= glyphInfo.top;
    530 	x += glyphInfo.left;
    531 
    532 	float w = glyphInfo.width;
    533 	float h = glyphInfo.height;
    534 	float s = glyphInfo.s1;
    535 	float t = glyphInfo.t1;
    536 	float s2 = glyphInfo.s2;
    537 	float t2 = glyphInfo.t2;
    538 	const idMaterial * hShader = glyphInfo.material;
    539 
    540 	if ( ClippedCoords( &x, &y, &w, &h, &s, &t, &s2, &t2) ) {
    541 		return;
    542 	}
    543 
    544 	DrawStretchPic(x, y, w, h, s, t, s2, t2, hShader);
    545 }
    546 
    547 int idDeviceContext::DrawText(float x, float y, float scale, idVec4 color, const char *text, float adjust, int limit, int style, int cursor) {
    548 	int			len;
    549 	idVec4		newColor;
    550 
    551 	idStr drawText = text;
    552 	int charIndex = 0;
    553 
    554 	if ( text && color.w != 0.0f ) {
    555 		renderSystem->SetColor(color);
    556 		memcpy(&newColor[0], &color[0], sizeof(idVec4));
    557 		len = drawText.Length();
    558 		if (limit > 0 && len > limit) {
    559 			len = limit;
    560 		}
    561 
    562 		float prevGlyphSkip = 0.0f;
    563 
    564 		while ( charIndex < len ) {
    565 			uint32 textChar = drawText.UTF8Char( charIndex );
    566 
    567 			if ( idStr::IsColor( drawText.c_str() + charIndex ) ) {
    568 				if ( drawText[ charIndex++ ] == C_COLOR_DEFAULT ) {
    569 					newColor = color;
    570 				} else {
    571 					newColor = idStr::ColorForIndex( charIndex );
    572 					newColor[3] = color[3];
    573 				}
    574 				if (cursor == charIndex-1 || cursor == charIndex) {
    575 					float backup = 0.0f;
    576 					if ( prevGlyphSkip > 0.0f ) {
    577 						backup = ( prevGlyphSkip + adjust) / 5.0f;
    578 					}
    579 					if ( cursor == charIndex-1 ) {
    580 						backup *= 2.0f;
    581 					} else {
    582 						renderSystem->SetColor(newColor);
    583 					}
    584 					DrawEditCursor(x - backup, y, scale);
    585 				}
    586 				renderSystem->SetColor(newColor);
    587 				continue;
    588 			} else {
    589 				scaledGlyphInfo_t glyphInfo;
    590 				activeFont->GetScaledGlyph( scale, textChar, glyphInfo );
    591 				prevGlyphSkip = glyphInfo.xSkip;
    592 
    593 				PaintChar( x, y, glyphInfo );
    594 
    595 				if (cursor == charIndex-1) {
    596 					DrawEditCursor(x, y, scale);
    597 				}
    598 				x += glyphInfo.xSkip + adjust;
    599 			}
    600 		}
    601 		if (cursor == len) {
    602 			DrawEditCursor(x, y, scale);
    603 		}
    604 	}
    605 	return drawText.Length();
    606 }
    607 
    608 void idDeviceContext::SetSize( float width, float height ) {
    609 	xScale = VIRTUAL_WIDTH / width;
    610 	yScale = VIRTUAL_HEIGHT / height;
    611 }
    612 
    613 void idDeviceContext::SetOffset( float x, float y ) {
    614 	xOffset = x;
    615 	yOffset = y;
    616 }
    617 
    618 int idDeviceContext::CharWidth( const char c, float scale ) {
    619 	return idMath::Ftoi( activeFont->GetGlyphWidth( scale, c ) );
    620 }
    621 
    622 int idDeviceContext::TextWidth( const char *text, float scale, int limit ) {
    623 	if ( text == NULL ) {
    624 		return 0;
    625 	}
    626 
    627 	int i;
    628 	float width = 0;
    629 	if ( limit > 0 ) {
    630 		for ( i = 0; text[i] != '\0' && i < limit; i++ ) {
    631 			if ( idStr::IsColor( text + i ) ) {
    632 				i++;
    633 			} else {
    634 				width += activeFont->GetGlyphWidth( scale, ((const unsigned char *)text)[i] );
    635 			}
    636 		}
    637 	} else {
    638 		for ( i = 0; text[i] != '\0'; i++ ) {
    639 			if ( idStr::IsColor( text + i ) ) {
    640 				i++;
    641 			} else {
    642 				width += activeFont->GetGlyphWidth( scale, ((const unsigned char *)text)[i] );
    643 			}
    644 		}
    645 	}
    646 	return idMath::Ftoi( width );
    647 }
    648 
    649 int idDeviceContext::TextHeight(const char *text, float scale, int limit) {
    650 	return idMath::Ftoi( activeFont->GetLineHeight( scale ) );
    651 }
    652 
    653 int idDeviceContext::MaxCharWidth(float scale) {
    654 	return idMath::Ftoi( activeFont->GetMaxCharWidth( scale ) );
    655 }
    656 
    657 int idDeviceContext::MaxCharHeight(float scale) {
    658 	return idMath::Ftoi( activeFont->GetLineHeight( scale ) );
    659 }
    660 
    661 const idMaterial *idDeviceContext::GetScrollBarImage(int index) {
    662 	if (index >= SCROLLBAR_HBACK && index < SCROLLBAR_COUNT) {
    663 		return scrollBarImages[index];
    664 	}
    665 	return scrollBarImages[SCROLLBAR_HBACK];
    666 }
    667 
    668 // this only supports left aligned text
    669 idRegion *idDeviceContext::GetTextRegion(const char *text, float textScale, idRectangle rectDraw, float xStart, float yStart) {
    670 	return NULL;
    671 }
    672 
    673 void idDeviceContext::DrawEditCursor( float x, float y, float scale ) {
    674 	if ( (int)( idLib::frameNumber >> 4 ) & 1 ) {
    675 		return;
    676 	}
    677 	char cursorChar = (overStrikeMode) ? '_' : '|';
    678 	scaledGlyphInfo_t glyphInfo;
    679 	activeFont->GetScaledGlyph( scale, cursorChar, glyphInfo );
    680  	PaintChar( x, y, glyphInfo );
    681 }
    682 
    683 int idDeviceContext::DrawText( const char *text, float textScale, int textAlign, idVec4 color, idRectangle rectDraw, bool wrap, int cursor, bool calcOnly, idList<int> *breaks, int limit ) {
    684 	int			count = 0;
    685 	int			charIndex = 0;
    686 	int			lastBreak = 0;
    687 	float		y = 0.0f;
    688 	float		textWidth = 0.0f;
    689 	float		textWidthAtLastBreak = 0.0f;
    690 
    691 	float		charSkip = MaxCharWidth( textScale ) + 1;
    692 	float		lineSkip = MaxCharHeight( textScale );
    693 
    694 	bool		lineBreak = false;
    695 	bool		wordBreak = false;
    696 
    697 	idStr drawText = text;
    698 	idStr textBuffer;
    699 
    700 	if (!calcOnly && !(text && *text)) {
    701 		if (cursor == 0) {
    702 			renderSystem->SetColor(color);
    703 			DrawEditCursor(rectDraw.x, lineSkip + rectDraw.y, textScale);
    704 		}
    705 		return idMath::Ftoi( rectDraw.w / charSkip );
    706 	}
    707 
    708 	y = lineSkip + rectDraw.y;
    709 
    710 	if ( breaks ) {
    711 		breaks->Append(0);
    712 	}
    713 
    714 	while ( charIndex < drawText.Length() ) {
    715 		uint32 textChar = drawText.UTF8Char( charIndex );
    716 
    717 		// See if we need to start a new line.
    718 		if ( textChar == '\n' || textChar == '\r' || charIndex == drawText.Length() ) {
    719 			lineBreak = true;
    720 			if ( charIndex < drawText.Length() ) {
    721 				// New line character and we still have more text to read.
    722 				char nextChar = drawText[ charIndex + 1 ];
    723 				if ( ( textChar == '\n' && nextChar == '\r' ) || ( textChar == '\r' && nextChar == '\n' ) ) {
    724 					// Just absorb extra newlines.
    725 					textChar = drawText.UTF8Char( charIndex );
    726 				}
    727 			}
    728 		}
    729 
    730 		// Check for escape colors if not then simply get the glyph width.
    731 		if ( textChar == C_COLOR_ESCAPE && charIndex < drawText.Length() ) {
    732 			textBuffer.AppendUTF8Char( textChar );
    733 			textChar = drawText.UTF8Char( charIndex );
    734 		}
    735 
    736 		// If the character isn't a new line then add it to the text buffer.
    737 		if ( textChar != '\n' && textChar != '\r' ) {
    738 			textWidth += activeFont->GetGlyphWidth( textScale, textChar );
    739 			textBuffer.AppendUTF8Char( textChar );
    740 		}
    741 
    742 		if ( !lineBreak && ( textWidth > rectDraw.w ) ) {
    743 			// The next character will cause us to overflow, if we haven't yet found a suitable
    744 			// break spot, set it to be this character
    745 			if ( textBuffer.Length() > 0 && lastBreak == 0 ) {
    746 				lastBreak = textBuffer.Length();
    747 				textWidthAtLastBreak = textWidth;
    748 			}
    749 			wordBreak = true;
    750 		} else if ( lineBreak || ( wrap && ( textChar == ' ' || textChar == '\t' ) ) ) {
    751 			// The next character is in view, so if we are a break character, store our position
    752 			lastBreak = textBuffer.Length();
    753 			textWidthAtLastBreak = textWidth;
    754 		}
    755 
    756 		// We need to go to a new line
    757 		if ( lineBreak || wordBreak ) {
    758 			float x = rectDraw.x;
    759 
    760 			if ( textWidthAtLastBreak > 0 ) {
    761 				textWidth = textWidthAtLastBreak;
    762 			}
    763 
    764 			// Align text if needed
    765 			if (textAlign == ALIGN_RIGHT) {
    766 				x = rectDraw.x + rectDraw.w - textWidth;
    767 			} else if (textAlign == ALIGN_CENTER) {
    768 				x = rectDraw.x + (rectDraw.w - textWidth) / 2;
    769 			}
    770 
    771 			if ( wrap || lastBreak > 0 ) {
    772 				// This is a special case to handle breaking in the middle of a word.
    773 				// if we didn't do this, the cursor would appear on the end of this line
    774 				// and the beginning of the next.
    775 				if ( wordBreak && cursor >= lastBreak && lastBreak == textBuffer.Length() ) {
    776 					cursor++;
    777 				}
    778 			}
    779 
    780 			// Draw what's in the current text buffer.
    781 			if (!calcOnly) {
    782 				if ( lastBreak > 0 ) {
    783 					count += DrawText(x, y, textScale, color, textBuffer.Left( lastBreak ).c_str(), 0, 0, 0, cursor);
    784 					textBuffer = textBuffer.Right( textBuffer.Length() - lastBreak );
    785 				} else {
    786 					count += DrawText(x, y, textScale, color, textBuffer.c_str(), 0, 0, 0, cursor);
    787 					textBuffer.Clear();
    788 				}
    789 			}
    790 
    791 			if ( cursor < lastBreak ) {
    792 				cursor = -1;
    793 			} else if ( cursor >= 0 ) {
    794 				cursor -= ( lastBreak + 1 );
    795 			}
    796 
    797 			// If wrap is disabled return at this point.
    798 			if ( !wrap ) {
    799 				return lastBreak;
    800 			}
    801 
    802 			// If we've hit the allowed character limit then break.
    803 			if ( limit && count > limit ) {
    804 				break;
    805 			}
    806 
    807 			y += lineSkip + 5;
    808 
    809 			if ( !calcOnly && y > rectDraw.Bottom() ) {
    810 				break;
    811 			}
    812 
    813 			// If breaks were requested then make a note of this one.
    814 			if (breaks) {
    815 				breaks->Append( drawText.Length() - charIndex );
    816 			}
    817 
    818 			// Reset necessary parms for next line.
    819 			lastBreak = 0;
    820 			textWidth = 0;
    821 			textWidthAtLastBreak = 0;
    822 			lineBreak = false;
    823 			wordBreak = false;
    824 
    825 			// Reassess the remaining width
    826 			for ( int i = 0; i < textBuffer.Length(); ) {
    827 				if ( textChar != C_COLOR_ESCAPE ) {
    828 					textWidth += activeFont->GetGlyphWidth( textScale, textBuffer.UTF8Char( i ) );
    829 				}
    830 			}
    831 
    832 			continue;
    833 		}
    834 	}
    835 
    836 	return idMath::Ftoi( rectDraw.w / charSkip );
    837 }
    838 
    839 /*
    840 =============
    841 idRectangle::String
    842 =============
    843 */
    844 char *idRectangle::String() const {
    845 	static	int		index = 0;
    846 	static	char	str[ 8 ][ 48 ];
    847 	char	*s;
    848 
    849 	// use an array so that multiple toString's won't collide
    850 	s = str[ index ];
    851 	index = (index + 1)&7;
    852 
    853 	sprintf( s, "%.2f %.2f %.2f %.2f", x, y, w, h );
    854 
    855 	return s;
    856 }
    857 
    858 
    859 /*
    860 ================================================================================================
    861 
    862 OPTIMIZED VERSIONS
    863 
    864 ================================================================================================
    865 */
    866 
    867 // this is only called for the cursor and debug strings, and it should
    868 // scope properly with push/pop clipRect
    869 void idDeviceContextOptimized::EnableClipping(bool b) { 
    870 	if ( b == enableClipping ) {
    871 		return;
    872 	}
    873 	enableClipping = b; 
    874 	if ( !enableClipping ) {
    875 		PopClipRect();
    876 	} else {
    877 		// the actual value of the rect is irrelvent
    878 		PushClipRect( idRectangle( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ) );
    879 
    880 		// allow drawing beyond the normal bounds for debug text
    881 		// this also allows the cursor to draw outside, so we might want
    882 		// to make this exactly the screen bounds, since we aren't likely
    883 		// to ever turn on the gui debug text again...
    884 		clipX1 = -SCREEN_WIDTH;
    885 		clipX2 = SCREEN_WIDTH * 2;
    886 		clipY1 = -SCREEN_HEIGHT;
    887 		clipY2 = SCREEN_HEIGHT * 2;
    888 	}
    889 };
    890 
    891 
    892 void idDeviceContextOptimized::PopClipRect() {
    893 	if (clipRects.Num()) {
    894 		clipRects.SetNum( clipRects.Num()-1 );	// don't resize the list, just change num
    895 	}
    896 	if ( clipRects.Num() > 0 ) {
    897 		const idRectangle & clipRect = clipRects[ clipRects.Num() - 1 ];
    898 		clipX1 = clipRect.x;
    899 		clipY1 = clipRect.y;
    900 		clipX2 = clipRect.x + clipRect.w;
    901 		clipY2 = clipRect.y + clipRect.h;
    902 	} else {
    903 		clipX1 = 0;
    904 		clipY1 = 0;
    905 		clipX2 = SCREEN_WIDTH;
    906 		clipY2 = SCREEN_HEIGHT;
    907 	}
    908 }
    909 
    910 static const idRectangle baseScreenRect( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
    911 
    912 void idDeviceContextOptimized::PushClipRect(idRectangle r) {
    913 	const idRectangle & prev = ( clipRects.Num() == 0 ) ? baseScreenRect : clipRects[clipRects.Num()-1];
    914 
    915 	// instead of storing the rect, store the intersection of the rect
    916 	// with the previous rect, so ClippedCoords() only has to test against one rect
    917 	idRectangle intersection = prev;
    918 	intersection.ClipAgainst( r, false );
    919 	clipRects.Append( intersection );
    920 
    921 	const idRectangle & clipRect = clipRects[ clipRects.Num() - 1 ];
    922 	clipX1 = clipRect.x;
    923 	clipY1 = clipRect.y;
    924 	clipX2 = clipRect.x + clipRect.w;
    925 	clipY2 = clipRect.y + clipRect.h;
    926 }
    927 
    928 bool idDeviceContextOptimized::ClippedCoords(float *x, float *y, float *w, float *h, float *s1, float *t1, float *s2, float *t2) {
    929 	const float ox = *x;
    930 	const float oy = *y;
    931 	const float ow = *w;
    932 	const float oh = *h;
    933 
    934 	// presume visible first
    935 	if ( ox >= clipX1 && oy >= clipY1 && ox + ow <= clipX2 && oy + oh <= clipY2 ) {
    936 		return false;
    937 	}
    938 
    939 	// do clipping
    940 	if ( ox < clipX1 ) {
    941 		*w -= clipX1 - ox;
    942 		*x = clipX1;
    943 	} else if ( ox > clipX2 ) {
    944 		return true;
    945 	}
    946 	if ( oy < clipY1) {
    947 		*h -= clipY1 - oy;
    948 		*y = clipY1;
    949 	} else if ( oy > clipY2) {
    950 		return true;
    951 	}
    952 	if ( *x + *w > clipX2 ) {
    953 		*w = clipX2 - *x;
    954 	}
    955 	if ( *y + *h > clipY2 ) {
    956 		*h = clipY2 - *y;
    957 	}
    958 
    959 	if ( *w <= 0 || *h <= 0 ) {
    960 		return true;
    961 	}
    962 
    963 	// filled rects won't pass in texcoords
    964 	if ( s1 ) {
    965 		float ns1, ns2, nt1, nt2;
    966 		// upper left
    967 		float u = ( *x - ox ) / ow;
    968 		ns1 = *s1 * ( 1.0f - u ) + *s2 * ( u );
    969 
    970 		// upper right
    971 		u = ( *x + *w - ox ) / ow;
    972 		ns2 = *s1 * ( 1.0f - u ) + *s2 * ( u );
    973 
    974 		// lower left
    975 		u = ( *y - oy ) / oh;
    976 		nt1 = *t1 * ( 1.0f - u ) + *t2 * ( u );
    977 
    978 		// lower right
    979 		u = ( *y + *h - oy ) / oh;
    980 		nt2 = *t1 * ( 1.0f - u ) + *t2 * ( u );
    981 
    982 		// set values
    983 		*s1 = ns1;
    984 		*s2 = ns2;
    985 		*t1 = nt1;
    986 		*t2 = nt2;
    987 	}
    988 
    989 	// still needs to be drawn
    990 	return false;
    991 }
    992 
    993 /*
    994 =============
    995 idDeviceContextOptimized::DrawText
    996 =============
    997 */
    998 static triIndex_t quadPicIndexes[6] = { 3, 0, 2, 2, 0, 1 };
    999 int idDeviceContextOptimized::DrawText(float x, float y, float scale, idVec4 color, const char *text, float adjust, int limit, int style, int cursor) {
   1000 	if ( !matIsIdentity || cursor != -1 ) {
   1001 		// fallback to old code
   1002 		return idDeviceContext::DrawText( x, y, scale, color, text, adjust, limit, style, cursor );
   1003 	}
   1004 
   1005 	idStr drawText = text;
   1006 
   1007 	if ( drawText.Length() == 0 ) {
   1008 		return 0;
   1009 	}
   1010 	if ( color.w == 0.0f ) {
   1011 		return 0;
   1012 	}
   1013 
   1014 	const uint32 currentColor = PackColor( color );
   1015 	uint32 currentColorNativeByteOrder = LittleLong( currentColor );
   1016 
   1017 	int len = drawText.Length();
   1018 	if (limit > 0 && len > limit) {
   1019 		len = limit;
   1020 	}
   1021 	
   1022 	int charIndex = 0;
   1023 	while ( charIndex < drawText.Length() ) {
   1024 		uint32 textChar = drawText.UTF8Char( charIndex );
   1025 		if ( textChar == C_COLOR_ESCAPE ) {
   1026 			// I'm not sure if inline text color codes are used anywhere in the game,
   1027 			// they may only be needed for multi-color user names
   1028 			idVec4		newColor;
   1029 			uint32 colorIndex = drawText.UTF8Char( charIndex );
   1030 			if ( colorIndex == C_COLOR_DEFAULT ) {
   1031 				newColor = color;
   1032 			} else {
   1033 				newColor = idStr::ColorForIndex( colorIndex );
   1034 				newColor[3] = color[3];
   1035 			}
   1036 			renderSystem->SetColor(newColor);
   1037 			currentColorNativeByteOrder = LittleLong( PackColor( newColor ) );
   1038 			continue;
   1039 		}
   1040 
   1041 		scaledGlyphInfo_t glyphInfo;
   1042 		activeFont->GetScaledGlyph( scale, textChar, glyphInfo );
   1043 
   1044 		// PaintChar( x, y, glyphInfo );
   1045 		float drawY = y - glyphInfo.top;
   1046 		float drawX = x + glyphInfo.left;
   1047 		float w = glyphInfo.width;
   1048 		float h = glyphInfo.height;
   1049 		float s = glyphInfo.s1;
   1050 		float t = glyphInfo.t1;
   1051 		float s2 = glyphInfo.s2;
   1052 		float t2 = glyphInfo.t2;
   1053 
   1054 		if ( !ClippedCoords( &drawX, &drawY, &w, &h, &s, &t, &s2, &t2) ) {
   1055 			float x1 = xOffset + drawX * xScale;
   1056 			float x2 = xOffset + ( drawX + w ) * xScale;
   1057 			float y1 = yOffset + drawY * yScale;
   1058 			float y2 = yOffset + ( drawY + h ) * yScale;
   1059 			idDrawVert * verts = tr_guiModel->AllocTris( 4, quadPicIndexes, 6, glyphInfo.material, 0, STEREO_DEPTH_TYPE_NONE );
   1060 			if ( verts != NULL ) {
   1061 				verts[0].xyz[0] = x1;
   1062 				verts[0].xyz[1] = y1;
   1063 				verts[0].xyz[2] = 0.0f;
   1064 				verts[0].SetTexCoord( s, t );
   1065 				verts[0].SetNativeOrderColor( currentColorNativeByteOrder );
   1066 				verts[0].ClearColor2();
   1067 
   1068 				verts[1].xyz[0] = x2;
   1069 				verts[1].xyz[1] = y1;
   1070 				verts[1].xyz[2] = 0.0f;
   1071 				verts[1].SetTexCoord( s2, t );
   1072 				verts[1].SetNativeOrderColor( currentColorNativeByteOrder );
   1073 				verts[1].ClearColor2();
   1074 
   1075 				verts[2].xyz[0] = x2;
   1076 				verts[2].xyz[1] = y2;
   1077 				verts[2].xyz[2] = 0.0f;
   1078 				verts[2].SetTexCoord( s2, t2 );
   1079 				verts[2].SetNativeOrderColor( currentColorNativeByteOrder );
   1080 				verts[2].ClearColor2();
   1081 
   1082 				verts[3].xyz[0] = x1;
   1083 				verts[3].xyz[1] = y2;
   1084 				verts[3].xyz[2] = 0.0f;
   1085 				verts[3].SetTexCoord( s, t2 );
   1086 				verts[3].SetNativeOrderColor( currentColorNativeByteOrder );
   1087 				verts[3].ClearColor2();
   1088 			}
   1089 		}
   1090 
   1091 		x += glyphInfo.xSkip + adjust;
   1092 	}
   1093 	return drawText.Length();
   1094 }