DeviceContext.cpp 29 KB

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