mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 16:21:04 +02:00
1058 lines
26 KiB
C++
1058 lines
26 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
IceTech GPL Source Code
|
|
Copyright (C) 2026 Justin Marshall
|
|
|
|
This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
|
|
|
|
IceTech Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
IceTech Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with IceTech Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the IceTech 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 IceTech Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "DeviceContext.h"
|
|
|
|
idVec4 idDeviceContext::colorPurple;
|
|
idVec4 idDeviceContext::colorOrange;
|
|
idVec4 idDeviceContext::colorYellow;
|
|
idVec4 idDeviceContext::colorGreen;
|
|
idVec4 idDeviceContext::colorBlue;
|
|
idVec4 idDeviceContext::colorRed;
|
|
idVec4 idDeviceContext::colorBlack;
|
|
idVec4 idDeviceContext::colorWhite;
|
|
idVec4 idDeviceContext::colorNone;
|
|
|
|
|
|
idCVar gui_smallFontLimit( "gui_smallFontLimit", "0.30", CVAR_GUI | CVAR_ARCHIVE, "" );
|
|
idCVar gui_mediumFontLimit( "gui_mediumFontLimit", "0.60", CVAR_GUI | CVAR_ARCHIVE, "" );
|
|
|
|
static uint32_t DC_ReadUTF8Char( const char *text, int &index ) {
|
|
const unsigned char *s = (const unsigned char *)text + index;
|
|
uint32_t c = s[0];
|
|
|
|
if ( c < 0x80 || s[1] == '\0' ) {
|
|
index++;
|
|
return c;
|
|
}
|
|
if ( ( c & 0xE0 ) == 0xC0 ) {
|
|
index += 2;
|
|
return ( ( c & 0x1F ) << 6 ) | ( s[1] & 0x3F );
|
|
}
|
|
if ( ( c & 0xF0 ) == 0xE0 && s[2] != '\0' ) {
|
|
index += 3;
|
|
return ( ( c & 0x0F ) << 12 ) | ( ( s[1] & 0x3F ) << 6 ) | ( s[2] & 0x3F );
|
|
}
|
|
if ( ( c & 0xF8 ) == 0xF0 && s[2] != '\0' && s[3] != '\0' ) {
|
|
index += 4;
|
|
return ( ( c & 0x07 ) << 18 ) | ( ( s[1] & 0x3F ) << 12 ) | ( ( s[2] & 0x3F ) << 6 ) | ( s[3] & 0x3F );
|
|
}
|
|
|
|
index++;
|
|
return c;
|
|
}
|
|
|
|
|
|
idList<idFont *> idDeviceContext::fonts;
|
|
|
|
int idDeviceContext::FindFont( const char *name ) {
|
|
int c = fonts.Num();
|
|
for (int i = 0; i < c; i++) {
|
|
if (idStr::Icmp(name, fonts[i]->GetName()) == 0) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
idFont *font = renderSystem->RegisterFont( name );
|
|
if (font != NULL) {
|
|
for (int i = 0; i < fonts.Num(); i++) {
|
|
if (fonts[i] == font) {
|
|
return i;
|
|
}
|
|
}
|
|
return fonts.Append( font );
|
|
}
|
|
|
|
common->Printf( "Could not register font %s\n", name );
|
|
return -1;
|
|
}
|
|
|
|
void idDeviceContext::SetupFonts() {
|
|
fonts.SetGranularity( 1 );
|
|
|
|
fontLang = cvarSystem->GetCVarString( "sys_lang" );
|
|
|
|
// western european languages can use the english font
|
|
if ( fontLang == "french" || fontLang == "german" || fontLang == "spanish" || fontLang == "italian" ) {
|
|
fontLang = "english";
|
|
}
|
|
|
|
// Default font has to be added first
|
|
FindFont( "fonts" );
|
|
}
|
|
|
|
void idDeviceContext::SetFont( int num ) {
|
|
if ( num >= 0 && num < fonts.Num() ) {
|
|
activeFont = fonts[num];
|
|
} else {
|
|
activeFont = fonts[0];
|
|
}
|
|
}
|
|
|
|
|
|
void idDeviceContext::Init() {
|
|
xScale = 0.0;
|
|
SetSize(VIRTUAL_WIDTH, VIRTUAL_HEIGHT);
|
|
whiteImage = declManager->FindMaterial("guis/assets/white.tga");
|
|
whiteImage->SetSort( SS_GUI );
|
|
mbcs = false;
|
|
SetupFonts();
|
|
activeFont = fonts[0];
|
|
colorPurple = idVec4(1, 0, 1, 1);
|
|
colorOrange = idVec4(1, 1, 0, 1);
|
|
colorYellow = idVec4(0, 1, 1, 1);
|
|
colorGreen = idVec4(0, 1, 0, 1);
|
|
colorBlue = idVec4(0, 0, 1, 1);
|
|
colorRed = idVec4(1, 0, 0, 1);
|
|
colorWhite = idVec4(1, 1, 1, 1);
|
|
colorBlack = idVec4(0, 0, 0, 1);
|
|
colorNone = idVec4(0, 0, 0, 0);
|
|
cursorImages[CURSOR_ARROW] = declManager->FindMaterial("ui/assets/guicursor_arrow.tga");
|
|
cursorImages[CURSOR_HAND] = declManager->FindMaterial("ui/assets/guicursor_hand.tga");
|
|
scrollBarImages[SCROLLBAR_HBACK] = declManager->FindMaterial("ui/assets/scrollbarh.tga");
|
|
scrollBarImages[SCROLLBAR_VBACK] = declManager->FindMaterial("ui/assets/scrollbarv.tga");
|
|
scrollBarImages[SCROLLBAR_THUMB] = declManager->FindMaterial("ui/assets/scrollbar_thumb.tga");
|
|
scrollBarImages[SCROLLBAR_RIGHT] = declManager->FindMaterial("ui/assets/scrollbar_right.tga");
|
|
scrollBarImages[SCROLLBAR_LEFT] = declManager->FindMaterial("ui/assets/scrollbar_left.tga");
|
|
scrollBarImages[SCROLLBAR_UP] = declManager->FindMaterial("ui/assets/scrollbar_up.tga");
|
|
scrollBarImages[SCROLLBAR_DOWN] = declManager->FindMaterial("ui/assets/scrollbar_down.tga");
|
|
cursorImages[CURSOR_ARROW]->SetSort( SS_GUI );
|
|
cursorImages[CURSOR_HAND]->SetSort( SS_GUI );
|
|
scrollBarImages[SCROLLBAR_HBACK]->SetSort( SS_GUI );
|
|
scrollBarImages[SCROLLBAR_VBACK]->SetSort( SS_GUI );
|
|
scrollBarImages[SCROLLBAR_THUMB]->SetSort( SS_GUI );
|
|
scrollBarImages[SCROLLBAR_RIGHT]->SetSort( SS_GUI );
|
|
scrollBarImages[SCROLLBAR_LEFT]->SetSort( SS_GUI );
|
|
scrollBarImages[SCROLLBAR_UP]->SetSort( SS_GUI );
|
|
scrollBarImages[SCROLLBAR_DOWN]->SetSort( SS_GUI );
|
|
cursor = CURSOR_ARROW;
|
|
enableClipping = true;
|
|
overStrikeMode = true;
|
|
mat.Identity();
|
|
origin.Zero();
|
|
initialized = true;
|
|
}
|
|
|
|
void idDeviceContext::Shutdown() {
|
|
fontName.Clear();
|
|
clipRects.Clear();
|
|
fonts.Clear();
|
|
Clear();
|
|
}
|
|
|
|
void idDeviceContext::Clear() {
|
|
initialized = false;
|
|
activeFont = NULL;
|
|
mbcs = false;
|
|
}
|
|
|
|
idDeviceContext::idDeviceContext() {
|
|
Clear();
|
|
}
|
|
|
|
void idDeviceContext::SetTransformInfo(const idVec3 &org, const idMat3 &m) {
|
|
origin = org;
|
|
mat = m;
|
|
}
|
|
|
|
//
|
|
// added method
|
|
void idDeviceContext::GetTransformInfo(idVec3& org, idMat3& m )
|
|
{
|
|
m = mat;
|
|
org = origin;
|
|
}
|
|
//
|
|
|
|
void idDeviceContext::PopClipRect() {
|
|
if (clipRects.Num()) {
|
|
clipRects.RemoveIndex(clipRects.Num()-1);
|
|
}
|
|
}
|
|
|
|
void idDeviceContext::PushClipRect(idRectangle r) {
|
|
clipRects.Append(r);
|
|
}
|
|
|
|
void idDeviceContext::PushClipRect(float x, float y, float w, float h) {
|
|
clipRects.Append(idRectangle(x, y, w, h));
|
|
}
|
|
|
|
bool idDeviceContext::ClippedCoords(float *x, float *y, float *w, float *h, float *s1, float *t1, float *s2, float *t2) {
|
|
|
|
if ( enableClipping == false || clipRects.Num() == 0 ) {
|
|
return false;
|
|
}
|
|
|
|
int c = clipRects.Num();
|
|
while( --c > 0 ) {
|
|
idRectangle *clipRect = &clipRects[c];
|
|
|
|
float ox = *x;
|
|
float oy = *y;
|
|
float ow = *w;
|
|
float oh = *h;
|
|
|
|
if ( ow <= 0.0f || oh <= 0.0f ) {
|
|
break;
|
|
}
|
|
|
|
if (*x < clipRect->x) {
|
|
*w -= clipRect->x - *x;
|
|
*x = clipRect->x;
|
|
} else if (*x > clipRect->x + clipRect->w) {
|
|
*x = *w = *y = *h = 0;
|
|
}
|
|
if (*y < clipRect->y) {
|
|
*h -= clipRect->y - *y;
|
|
*y = clipRect->y;
|
|
} else if (*y > clipRect->y + clipRect->h) {
|
|
*x = *w = *y = *h = 0;
|
|
}
|
|
if (*w > clipRect->w) {
|
|
*w = clipRect->w - *x + clipRect->x;
|
|
} else if (*x + *w > clipRect->x + clipRect->w) {
|
|
*w = clipRect->Right() - *x;
|
|
}
|
|
if (*h > clipRect->h) {
|
|
*h = clipRect->h - *y + clipRect->y;
|
|
} else if (*y + *h > clipRect->y + clipRect->h) {
|
|
*h = clipRect->Bottom() - *y;
|
|
}
|
|
|
|
if ( s1 && s2 && t1 && t2 && ow > 0.0f ) {
|
|
float ns1, ns2, nt1, nt2;
|
|
// upper left
|
|
float u = ( *x - ox ) / ow;
|
|
ns1 = *s1 * ( 1.0f - u ) + *s2 * ( u );
|
|
|
|
// upper right
|
|
u = ( *x + *w - ox ) / ow;
|
|
ns2 = *s1 * ( 1.0f - u ) + *s2 * ( u );
|
|
|
|
// lower left
|
|
u = ( *y - oy ) / oh;
|
|
nt1 = *t1 * ( 1.0f - u ) + *t2 * ( u );
|
|
|
|
// lower right
|
|
u = ( *y + *h - oy ) / oh;
|
|
nt2 = *t1 * ( 1.0f - u ) + *t2 * ( u );
|
|
|
|
// set values
|
|
*s1 = ns1;
|
|
*s2 = ns2;
|
|
*t1 = nt1;
|
|
*t2 = nt2;
|
|
}
|
|
}
|
|
|
|
return (*w == 0 || *h == 0) ? true : false;
|
|
}
|
|
|
|
|
|
void idDeviceContext::AdjustCoords(float *x, float *y, float *w, float *h) {
|
|
if (x) {
|
|
*x *= xScale;
|
|
}
|
|
if (y) {
|
|
*y *= yScale;
|
|
}
|
|
if (w) {
|
|
*w *= xScale;
|
|
}
|
|
if (h) {
|
|
*h *= yScale;
|
|
}
|
|
}
|
|
|
|
void idDeviceContext::DrawStretchPic(float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial *shader) {
|
|
idDrawVert verts[4];
|
|
glIndex_t indexes[6];
|
|
indexes[0] = 3;
|
|
indexes[1] = 0;
|
|
indexes[2] = 2;
|
|
indexes[3] = 2;
|
|
indexes[4] = 0;
|
|
indexes[5] = 1;
|
|
verts[0].xyz[0] = x;
|
|
verts[0].xyz[1] = y;
|
|
verts[0].xyz[2] = 0;
|
|
verts[0].st[0] = s1;
|
|
verts[0].st[1] = t1;
|
|
verts[0].normal[0] = 0;
|
|
verts[0].normal[1] = 0;
|
|
verts[0].normal[2] = 1;
|
|
verts[0].tangents[0][0] = 1;
|
|
verts[0].tangents[0][1] = 0;
|
|
verts[0].tangents[0][2] = 0;
|
|
verts[0].tangents[1][0] = 0;
|
|
verts[0].tangents[1][1] = 1;
|
|
verts[0].tangents[1][2] = 0;
|
|
verts[1].xyz[0] = x + w;
|
|
verts[1].xyz[1] = y;
|
|
verts[1].xyz[2] = 0;
|
|
verts[1].st[0] = s2;
|
|
verts[1].st[1] = t1;
|
|
verts[1].normal[0] = 0;
|
|
verts[1].normal[1] = 0;
|
|
verts[1].normal[2] = 1;
|
|
verts[1].tangents[0][0] = 1;
|
|
verts[1].tangents[0][1] = 0;
|
|
verts[1].tangents[0][2] = 0;
|
|
verts[1].tangents[1][0] = 0;
|
|
verts[1].tangents[1][1] = 1;
|
|
verts[1].tangents[1][2] = 0;
|
|
verts[2].xyz[0] = x + w;
|
|
verts[2].xyz[1] = y + h;
|
|
verts[2].xyz[2] = 0;
|
|
verts[2].st[0] = s2;
|
|
verts[2].st[1] = t2;
|
|
verts[2].normal[0] = 0;
|
|
verts[2].normal[1] = 0;
|
|
verts[2].normal[2] = 1;
|
|
verts[2].tangents[0][0] = 1;
|
|
verts[2].tangents[0][1] = 0;
|
|
verts[2].tangents[0][2] = 0;
|
|
verts[2].tangents[1][0] = 0;
|
|
verts[2].tangents[1][1] = 1;
|
|
verts[2].tangents[1][2] = 0;
|
|
verts[3].xyz[0] = x;
|
|
verts[3].xyz[1] = y + h;
|
|
verts[3].xyz[2] = 0;
|
|
verts[3].st[0] = s1;
|
|
verts[3].st[1] = t2;
|
|
verts[3].normal[0] = 0;
|
|
verts[3].normal[1] = 0;
|
|
verts[3].normal[2] = 1;
|
|
verts[3].tangents[0][0] = 1;
|
|
verts[3].tangents[0][1] = 0;
|
|
verts[3].tangents[0][2] = 0;
|
|
verts[3].tangents[1][0] = 0;
|
|
verts[3].tangents[1][1] = 1;
|
|
verts[3].tangents[1][2] = 0;
|
|
|
|
bool ident = !mat.IsIdentity();
|
|
if ( ident ) {
|
|
verts[0].xyz -= origin;
|
|
verts[0].xyz *= mat;
|
|
verts[0].xyz += origin;
|
|
verts[1].xyz -= origin;
|
|
verts[1].xyz *= mat;
|
|
verts[1].xyz += origin;
|
|
verts[2].xyz -= origin;
|
|
verts[2].xyz *= mat;
|
|
verts[2].xyz += origin;
|
|
verts[3].xyz -= origin;
|
|
verts[3].xyz *= mat;
|
|
verts[3].xyz += origin;
|
|
}
|
|
|
|
renderSystem->DrawStretchPic( &verts[0], &indexes[0], 4, 6, shader, ident );
|
|
|
|
}
|
|
|
|
|
|
void idDeviceContext::DrawMaterial(float x, float y, float w, float h, const idMaterial *mat, const idVec4 &color, float scalex, float scaley) {
|
|
|
|
renderSystem->SetColor(color);
|
|
|
|
float s0, s1, t0, t1;
|
|
//
|
|
// handle negative scales as well
|
|
if ( scalex < 0 )
|
|
{
|
|
w *= -1;
|
|
scalex *= -1;
|
|
}
|
|
if ( scaley < 0 )
|
|
{
|
|
h *= -1;
|
|
scaley *= -1;
|
|
}
|
|
//
|
|
if( w < 0 ) { // flip about vertical
|
|
w = -w;
|
|
s0 = 1 * scalex;
|
|
s1 = 0;
|
|
}
|
|
else {
|
|
s0 = 0;
|
|
s1 = 1 * scalex;
|
|
}
|
|
|
|
if( h < 0 ) { // flip about horizontal
|
|
h = -h;
|
|
t0 = 1 * scaley;
|
|
t1 = 0;
|
|
}
|
|
else {
|
|
t0 = 0;
|
|
t1 = 1 * scaley;
|
|
}
|
|
|
|
if ( ClippedCoords( &x, &y, &w, &h, &s0, &t0, &s1, &t1 ) ) {
|
|
return;
|
|
}
|
|
|
|
AdjustCoords(&x, &y, &w, &h);
|
|
|
|
DrawStretchPic( x, y, w, h, s0, t0, s1, t1, mat);
|
|
}
|
|
|
|
void idDeviceContext::DrawMaterialRotated(float x, float y, float w, float h, const idMaterial *mat, const idVec4 &color, float scalex, float scaley, float angle) {
|
|
|
|
renderSystem->SetColor(color);
|
|
|
|
float s0, s1, t0, t1;
|
|
//
|
|
// handle negative scales as well
|
|
if ( scalex < 0 )
|
|
{
|
|
w *= -1;
|
|
scalex *= -1;
|
|
}
|
|
if ( scaley < 0 )
|
|
{
|
|
h *= -1;
|
|
scaley *= -1;
|
|
}
|
|
//
|
|
if( w < 0 ) { // flip about vertical
|
|
w = -w;
|
|
s0 = 1 * scalex;
|
|
s1 = 0;
|
|
}
|
|
else {
|
|
s0 = 0;
|
|
s1 = 1 * scalex;
|
|
}
|
|
|
|
if( h < 0 ) { // flip about horizontal
|
|
h = -h;
|
|
t0 = 1 * scaley;
|
|
t1 = 0;
|
|
}
|
|
else {
|
|
t0 = 0;
|
|
t1 = 1 * scaley;
|
|
}
|
|
|
|
if ( angle == 0.0f && ClippedCoords( &x, &y, &w, &h, &s0, &t0, &s1, &t1 ) ) {
|
|
return;
|
|
}
|
|
|
|
AdjustCoords(&x, &y, &w, &h);
|
|
|
|
DrawStretchPicRotated( x, y, w, h, s0, t0, s1, t1, mat, angle);
|
|
}
|
|
|
|
void idDeviceContext::DrawStretchPicRotated(float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial *shader, float angle) {
|
|
|
|
idDrawVert verts[4];
|
|
glIndex_t indexes[6];
|
|
indexes[0] = 3;
|
|
indexes[1] = 0;
|
|
indexes[2] = 2;
|
|
indexes[3] = 2;
|
|
indexes[4] = 0;
|
|
indexes[5] = 1;
|
|
verts[0].xyz[0] = x;
|
|
verts[0].xyz[1] = y;
|
|
verts[0].xyz[2] = 0;
|
|
verts[0].st[0] = s1;
|
|
verts[0].st[1] = t1;
|
|
verts[0].normal[0] = 0;
|
|
verts[0].normal[1] = 0;
|
|
verts[0].normal[2] = 1;
|
|
verts[0].tangents[0][0] = 1;
|
|
verts[0].tangents[0][1] = 0;
|
|
verts[0].tangents[0][2] = 0;
|
|
verts[0].tangents[1][0] = 0;
|
|
verts[0].tangents[1][1] = 1;
|
|
verts[0].tangents[1][2] = 0;
|
|
verts[1].xyz[0] = x + w;
|
|
verts[1].xyz[1] = y;
|
|
verts[1].xyz[2] = 0;
|
|
verts[1].st[0] = s2;
|
|
verts[1].st[1] = t1;
|
|
verts[1].normal[0] = 0;
|
|
verts[1].normal[1] = 0;
|
|
verts[1].normal[2] = 1;
|
|
verts[1].tangents[0][0] = 1;
|
|
verts[1].tangents[0][1] = 0;
|
|
verts[1].tangents[0][2] = 0;
|
|
verts[1].tangents[1][0] = 0;
|
|
verts[1].tangents[1][1] = 1;
|
|
verts[1].tangents[1][2] = 0;
|
|
verts[2].xyz[0] = x + w;
|
|
verts[2].xyz[1] = y + h;
|
|
verts[2].xyz[2] = 0;
|
|
verts[2].st[0] = s2;
|
|
verts[2].st[1] = t2;
|
|
verts[2].normal[0] = 0;
|
|
verts[2].normal[1] = 0;
|
|
verts[2].normal[2] = 1;
|
|
verts[2].tangents[0][0] = 1;
|
|
verts[2].tangents[0][1] = 0;
|
|
verts[2].tangents[0][2] = 0;
|
|
verts[2].tangents[1][0] = 0;
|
|
verts[2].tangents[1][1] = 1;
|
|
verts[2].tangents[1][2] = 0;
|
|
verts[3].xyz[0] = x;
|
|
verts[3].xyz[1] = y + h;
|
|
verts[3].xyz[2] = 0;
|
|
verts[3].st[0] = s1;
|
|
verts[3].st[1] = t2;
|
|
verts[3].normal[0] = 0;
|
|
verts[3].normal[1] = 0;
|
|
verts[3].normal[2] = 1;
|
|
verts[3].tangents[0][0] = 1;
|
|
verts[3].tangents[0][1] = 0;
|
|
verts[3].tangents[0][2] = 0;
|
|
verts[3].tangents[1][0] = 0;
|
|
verts[3].tangents[1][1] = 1;
|
|
verts[3].tangents[1][2] = 0;
|
|
|
|
bool ident = !mat.IsIdentity();
|
|
if ( ident ) {
|
|
verts[0].xyz -= origin;
|
|
verts[0].xyz *= mat;
|
|
verts[0].xyz += origin;
|
|
verts[1].xyz -= origin;
|
|
verts[1].xyz *= mat;
|
|
verts[1].xyz += origin;
|
|
verts[2].xyz -= origin;
|
|
verts[2].xyz *= mat;
|
|
verts[2].xyz += origin;
|
|
verts[3].xyz -= origin;
|
|
verts[3].xyz *= mat;
|
|
verts[3].xyz += origin;
|
|
}
|
|
|
|
//Generate a translation so we can translate to the center of the image rotate and draw
|
|
idVec3 origTrans;
|
|
origTrans.x = x+(w/2);
|
|
origTrans.y = y+(h/2);
|
|
origTrans.z = 0;
|
|
|
|
|
|
//Rotate the verts about the z axis before drawing them
|
|
idMat4 rotz;
|
|
rotz.Identity();
|
|
float sinAng = idMath::Sin(angle);
|
|
float cosAng = idMath::Cos(angle);
|
|
rotz[0][0] = cosAng;
|
|
rotz[0][1] = sinAng;
|
|
rotz[1][0] = -sinAng;
|
|
rotz[1][1] = cosAng;
|
|
for(int i = 0; i < 4; i++) {
|
|
//Translate to origin
|
|
verts[i].xyz -= origTrans;
|
|
|
|
//Rotate
|
|
verts[i].xyz = rotz * verts[i].xyz;
|
|
|
|
//Translate back
|
|
verts[i].xyz += origTrans;
|
|
}
|
|
|
|
|
|
renderSystem->DrawStretchPic( &verts[0], &indexes[0], 4, 6, shader, (angle == 0.0) ? false : true );
|
|
}
|
|
|
|
void idDeviceContext::DrawFilledRect( float x, float y, float w, float h, const idVec4 &color) {
|
|
|
|
if ( color.w == 0.0f ) {
|
|
return;
|
|
}
|
|
|
|
renderSystem->SetColor(color);
|
|
|
|
if (ClippedCoords(&x, &y, &w, &h, NULL, NULL, NULL, NULL)) {
|
|
return;
|
|
}
|
|
|
|
AdjustCoords(&x, &y, &w, &h);
|
|
DrawStretchPic( x, y, w, h, 0, 0, 0, 0, whiteImage);
|
|
}
|
|
|
|
|
|
void idDeviceContext::DrawRect( float x, float y, float w, float h, float size, const idVec4 &color) {
|
|
|
|
if ( color.w == 0.0f ) {
|
|
return;
|
|
}
|
|
|
|
renderSystem->SetColor(color);
|
|
|
|
if (ClippedCoords(&x, &y, &w, &h, NULL, NULL, NULL, NULL)) {
|
|
return;
|
|
}
|
|
|
|
AdjustCoords(&x, &y, &w, &h);
|
|
DrawStretchPic( x, y, size, h, 0, 0, 0, 0, whiteImage );
|
|
DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, whiteImage );
|
|
DrawStretchPic( x, y, w, size, 0, 0, 0, 0, whiteImage );
|
|
DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, whiteImage );
|
|
}
|
|
|
|
void idDeviceContext::DrawMaterialRect( float x, float y, float w, float h, float size, const idMaterial *mat, const idVec4 &color) {
|
|
|
|
if ( color.w == 0.0f ) {
|
|
return;
|
|
}
|
|
|
|
renderSystem->SetColor(color);
|
|
DrawMaterial( x, y, size, h, mat, color );
|
|
DrawMaterial( x + w - size, y, size, h, mat, color );
|
|
DrawMaterial( x, y, w, size, mat, color );
|
|
DrawMaterial( x, y + h - size, w, size, mat, color );
|
|
}
|
|
|
|
|
|
void idDeviceContext::SetCursor(int n) {
|
|
cursor = (n < CURSOR_ARROW || n >= CURSOR_COUNT) ? CURSOR_ARROW : n;
|
|
}
|
|
|
|
void idDeviceContext::DrawCursor(float *x, float *y, float size) {
|
|
if (*x < 0) {
|
|
*x = 0;
|
|
}
|
|
|
|
if (*x >= vidWidth) {
|
|
*x = vidWidth;
|
|
}
|
|
|
|
if (*y < 0) {
|
|
*y = 0;
|
|
}
|
|
|
|
if (*y >= vidHeight) {
|
|
*y = vidHeight;
|
|
}
|
|
|
|
renderSystem->SetColor(colorWhite);
|
|
AdjustCoords(x, y, &size, &size);
|
|
DrawStretchPic( *x, *y, size, size, 0, 0, 1, 1, cursorImages[cursor]);
|
|
}
|
|
/*
|
|
=======================================================================================================================
|
|
=======================================================================================================================
|
|
*/
|
|
|
|
void idDeviceContext::PaintChar(float x, float y, const scaledGlyphInfo_t &glyphInfo) {
|
|
y -= glyphInfo.top;
|
|
x += glyphInfo.left;
|
|
|
|
float w = glyphInfo.width;
|
|
float h = glyphInfo.height;
|
|
float s = glyphInfo.s1;
|
|
float t = glyphInfo.t1;
|
|
float s2 = glyphInfo.s2;
|
|
float t2 = glyphInfo.t2;
|
|
const idMaterial *hShader = glyphInfo.material;
|
|
|
|
if (ClippedCoords(&x, &y, &w, &h, &s, &t, &s2, &t2)) {
|
|
return;
|
|
}
|
|
|
|
DrawStretchPic(x, y, w, h, s, t, s2, t2, hShader);
|
|
}
|
|
|
|
int idDeviceContext::DrawText(float x, float y, float scale, idVec4 color, const char *text, float adjust, int limit, int style, int cursor) {
|
|
idVec4 newColor;
|
|
|
|
idStr drawText = text ? text : "";
|
|
int charIndex = 0;
|
|
int count = 0;
|
|
|
|
if ( text && color.w != 0.0f && activeFont != NULL ) {
|
|
renderSystem->SetColor(color);
|
|
memcpy(&newColor[0], &color[0], sizeof(idVec4));
|
|
int len = drawText.Length();
|
|
if (limit > 0 && len > limit) {
|
|
len = limit;
|
|
}
|
|
|
|
float prevGlyphSkip = 0.0f;
|
|
|
|
while ( charIndex < len ) {
|
|
if ( idStr::IsColor( drawText.c_str() + charIndex ) ) {
|
|
if ( drawText[ charIndex + 1 ] == C_COLOR_DEFAULT ) {
|
|
newColor = color;
|
|
} else {
|
|
newColor = idStr::ColorForIndex( drawText[ charIndex + 1 ] );
|
|
newColor[3] = color[3];
|
|
}
|
|
if (cursor == charIndex || cursor == charIndex + 1) {
|
|
float backup = 0.0f;
|
|
if ( prevGlyphSkip > 0.0f ) {
|
|
backup = ( prevGlyphSkip + adjust) / 5.0f;
|
|
}
|
|
if ( cursor == charIndex ) {
|
|
backup *= 2.0f;
|
|
} else {
|
|
renderSystem->SetColor(newColor);
|
|
}
|
|
DrawEditCursor(x - backup, y, scale);
|
|
}
|
|
renderSystem->SetColor(newColor);
|
|
charIndex += 2;
|
|
count += 2;
|
|
continue;
|
|
}
|
|
|
|
int glyphStart = charIndex;
|
|
uint32_t textChar = DC_ReadUTF8Char( drawText.c_str(), charIndex );
|
|
scaledGlyphInfo_t glyphInfo;
|
|
activeFont->GetScaledGlyph( scale, textChar, glyphInfo );
|
|
prevGlyphSkip = glyphInfo.xSkip;
|
|
|
|
PaintChar( x, y, glyphInfo );
|
|
|
|
if (cursor == glyphStart) {
|
|
DrawEditCursor(x, y, scale);
|
|
}
|
|
x += glyphInfo.xSkip + adjust;
|
|
count += charIndex - glyphStart;
|
|
}
|
|
if (cursor == len) {
|
|
DrawEditCursor(x, y, scale);
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void idDeviceContext::SetSize(float width, float height) {
|
|
vidWidth = VIRTUAL_WIDTH;
|
|
vidHeight = VIRTUAL_HEIGHT;
|
|
xScale = yScale = 0.0f;
|
|
if ( width != 0.0f && height != 0.0f ) {
|
|
xScale = vidWidth * ( 1.0f / width );
|
|
yScale = vidHeight * ( 1.0f / height );
|
|
}
|
|
}
|
|
|
|
int idDeviceContext::CharWidth( const char c, float scale ) {
|
|
return activeFont == NULL ? 0 : idMath::Ftoi( activeFont->GetGlyphWidth( scale, (const unsigned char)c ) );
|
|
}
|
|
|
|
int idDeviceContext::TextWidth( const char *text, float scale, int limit ) {
|
|
if ( text == NULL || activeFont == NULL ) {
|
|
return 0;
|
|
}
|
|
|
|
int i;
|
|
float width = 0.0f;
|
|
if ( limit > 0 ) {
|
|
for ( i = 0; text[i] != '\0' && i < limit; i++ ) {
|
|
if ( idStr::IsColor( text + i ) ) {
|
|
i++;
|
|
} else {
|
|
width += activeFont->GetGlyphWidth( scale, ((const unsigned char *)text)[i] );
|
|
}
|
|
}
|
|
} else {
|
|
for ( i = 0; text[i] != '\0'; i++ ) {
|
|
if ( idStr::IsColor( text + i ) ) {
|
|
i++;
|
|
} else {
|
|
width += activeFont->GetGlyphWidth( scale, ((const unsigned char *)text)[i] );
|
|
}
|
|
}
|
|
}
|
|
return idMath::Ftoi( width );
|
|
}
|
|
|
|
int idDeviceContext::TextHeight(const char *text, float scale, int limit) {
|
|
return activeFont == NULL ? 0 : idMath::Ftoi( activeFont->GetLineHeight( scale ) );
|
|
}
|
|
|
|
int idDeviceContext::MaxCharWidth(float scale) {
|
|
return activeFont == NULL ? 0 : idMath::Ftoi( activeFont->GetMaxCharWidth( scale ) );
|
|
}
|
|
|
|
int idDeviceContext::MaxCharHeight(float scale) {
|
|
return activeFont == NULL ? 0 : idMath::Ftoi( activeFont->GetLineHeight( scale ) );
|
|
}
|
|
|
|
const idMaterial *idDeviceContext::GetScrollBarImage(int index) {
|
|
if (index >= SCROLLBAR_HBACK && index < SCROLLBAR_COUNT) {
|
|
return scrollBarImages[index];
|
|
}
|
|
return scrollBarImages[SCROLLBAR_HBACK];
|
|
}
|
|
|
|
// this only supports left aligned text
|
|
idRegion *idDeviceContext::GetTextRegion(const char *text, float textScale, idRectangle rectDraw, float xStart, float yStart) {
|
|
#if 0
|
|
const char *p, *textPtr, *newLinePtr;
|
|
char buff[1024];
|
|
int len, textWidth, newLine, newLineWidth;
|
|
float y;
|
|
|
|
float charSkip = MaxCharWidth(textScale) + 1;
|
|
float lineSkip = MaxCharHeight(textScale);
|
|
|
|
textWidth = 0;
|
|
newLinePtr = NULL;
|
|
#endif
|
|
return NULL;
|
|
/*
|
|
if (text == NULL) {
|
|
return;
|
|
}
|
|
|
|
textPtr = text;
|
|
if (*textPtr == '\0') {
|
|
return;
|
|
}
|
|
|
|
y = lineSkip + rectDraw.y + yStart;
|
|
len = 0;
|
|
buff[0] = '\0';
|
|
newLine = 0;
|
|
newLineWidth = 0;
|
|
p = textPtr;
|
|
|
|
textWidth = 0;
|
|
while (p) {
|
|
if (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\0') {
|
|
newLine = len;
|
|
newLinePtr = p + 1;
|
|
newLineWidth = textWidth;
|
|
}
|
|
|
|
if ((newLine && textWidth > rectDraw.w) || *p == '\n' || *p == '\0') {
|
|
if (len) {
|
|
|
|
float x = rectDraw.x ;
|
|
|
|
buff[newLine] = '\0';
|
|
DrawText(x, y, textScale, color, buff, 0, 0, 0);
|
|
if (!wrap) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (*p == '\0') {
|
|
break;
|
|
}
|
|
|
|
y += lineSkip + 5;
|
|
p = newLinePtr;
|
|
len = 0;
|
|
newLine = 0;
|
|
newLineWidth = 0;
|
|
continue;
|
|
}
|
|
|
|
buff[len++] = *p++;
|
|
buff[len] = '\0';
|
|
textWidth = TextWidth( buff, textScale, -1 );
|
|
}
|
|
*/
|
|
}
|
|
|
|
void idDeviceContext::DrawEditCursor( float x, float y, float scale ) {
|
|
if ( (int)( com_ticNumber >> 4 ) & 1 ) {
|
|
return;
|
|
}
|
|
if ( activeFont == NULL ) {
|
|
return;
|
|
}
|
|
char cursorChar = (overStrikeMode) ? '_' : '|';
|
|
scaledGlyphInfo_t glyphInfo;
|
|
activeFont->GetScaledGlyph(scale, cursorChar, glyphInfo);
|
|
PaintChar(x, y, glyphInfo);
|
|
}
|
|
|
|
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 ) {
|
|
const char *p, *textPtr, *newLinePtr;
|
|
char buff[1024];
|
|
int len, newLine, newLineWidth, count;
|
|
float y;
|
|
float textWidth;
|
|
|
|
float charSkip = MaxCharWidth( textScale ) + 1;
|
|
float lineSkip = MaxCharHeight( textScale );
|
|
|
|
float cursorSkip = ( cursor >= 0 ? charSkip : 0 );
|
|
|
|
bool lineBreak, wordBreak;
|
|
|
|
textWidth = 0;
|
|
newLinePtr = NULL;
|
|
|
|
if (!calcOnly && !(text && *text)) {
|
|
if (cursor == 0) {
|
|
renderSystem->SetColor(color);
|
|
DrawEditCursor(rectDraw.x, lineSkip + rectDraw.y, textScale);
|
|
}
|
|
return idMath::FtoiFast( rectDraw.w / charSkip );
|
|
}
|
|
|
|
textPtr = text;
|
|
|
|
y = lineSkip + rectDraw.y;
|
|
len = 0;
|
|
buff[0] = '\0';
|
|
newLine = 0;
|
|
newLineWidth = 0;
|
|
p = textPtr;
|
|
|
|
if ( breaks ) {
|
|
breaks->Append(0);
|
|
}
|
|
count = 0;
|
|
textWidth = 0;
|
|
lineBreak = false;
|
|
wordBreak = false;
|
|
while (p) {
|
|
|
|
if ( *p == '\n' || *p == '\r' || *p == '\0' ) {
|
|
lineBreak = true;
|
|
if ((*p == '\n' && *(p + 1) == '\r') || (*p == '\r' && *(p + 1) == '\n')) {
|
|
p++;
|
|
}
|
|
}
|
|
|
|
int nextCharWidth = ( idStr::CharIsPrintable(*p) ? CharWidth( *p, textScale ) : cursorSkip );
|
|
// FIXME: this is a temp hack until the guis can be fixed not not overflow the bounding rectangles
|
|
// the side-effect is that list boxes and edit boxes will draw over their scroll bars
|
|
// The following line and the !linebreak in the if statement below should be removed
|
|
nextCharWidth = 0;
|
|
|
|
if ( !lineBreak && ( textWidth + nextCharWidth ) > rectDraw.w ) {
|
|
// The next character will cause us to overflow, if we haven't yet found a suitable
|
|
// break spot, set it to be this character
|
|
if ( len > 0 && newLine == 0 ) {
|
|
newLine = len;
|
|
newLinePtr = p;
|
|
newLineWidth = textWidth;
|
|
}
|
|
wordBreak = true;
|
|
} else if ( lineBreak || ( wrap && (*p == ' ' || *p == '\t') ) ) {
|
|
// The next character is in view, so if we are a break character, store our position
|
|
newLine = len;
|
|
newLinePtr = p + 1;
|
|
newLineWidth = textWidth;
|
|
}
|
|
|
|
if ( lineBreak || wordBreak ) {
|
|
float x = rectDraw.x;
|
|
|
|
if (textAlign == ALIGN_RIGHT) {
|
|
x = rectDraw.x + rectDraw.w - newLineWidth;
|
|
} else if (textAlign == ALIGN_CENTER) {
|
|
x = rectDraw.x + (rectDraw.w - newLineWidth) / 2;
|
|
}
|
|
|
|
if ( wrap || newLine > 0 ) {
|
|
buff[newLine] = '\0';
|
|
|
|
// This is a special case to handle breaking in the middle of a word.
|
|
// if we didn't do this, the cursor would appear on the end of this line
|
|
// and the beginning of the next.
|
|
if ( wordBreak && cursor >= newLine && newLine == len ) {
|
|
cursor++;
|
|
}
|
|
}
|
|
|
|
if (!calcOnly) {
|
|
count += DrawText(x, y, textScale, color, buff, 0, 0, 0, cursor);
|
|
}
|
|
|
|
if ( cursor < newLine ) {
|
|
cursor = -1;
|
|
} else if ( cursor >= 0 ) {
|
|
cursor -= ( newLine + 1 );
|
|
}
|
|
|
|
if ( !wrap ) {
|
|
return newLine;
|
|
}
|
|
|
|
if ( ( limit && count > limit ) || *p == '\0' ) {
|
|
break;
|
|
}
|
|
|
|
y += lineSkip + 5;
|
|
|
|
if ( !calcOnly && y > rectDraw.Bottom() ) {
|
|
break;
|
|
}
|
|
|
|
p = newLinePtr;
|
|
|
|
if (breaks) {
|
|
breaks->Append(p - text);
|
|
}
|
|
|
|
len = 0;
|
|
newLine = 0;
|
|
newLineWidth = 0;
|
|
textWidth = 0;
|
|
lineBreak = false;
|
|
wordBreak = false;
|
|
continue;
|
|
}
|
|
|
|
buff[len++] = *p++;
|
|
buff[len] = '\0';
|
|
// update the width
|
|
if ( *( buff + len - 1 ) != C_COLOR_ESCAPE && (len <= 1 || *( buff + len - 2 ) != C_COLOR_ESCAPE)) {
|
|
textWidth += activeFont != NULL ? activeFont->GetGlyphWidth( textScale, (const unsigned char)*( buff + len - 1 ) ) : 0.0f;
|
|
}
|
|
}
|
|
|
|
return idMath::FtoiFast( rectDraw.w / charSkip );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
idRectangle::String
|
|
=============
|
|
*/
|
|
char *idRectangle::String( void ) const {
|
|
static int index = 0;
|
|
static char str[ 8 ][ 48 ];
|
|
char *s;
|
|
|
|
// use an array so that multiple toString's won't collide
|
|
s = str[ index ];
|
|
index = (index + 1)&7;
|
|
|
|
sprintf( s, "%.2f %.2f %.2f %.2f", x, y, w, h );
|
|
|
|
return s;
|
|
}
|