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 }