mirror of
https://github.com/bearstonem/openmohaa-vr.git
synced 2026-10-20 17:00:17 +02:00
Restore desktop HUD behavior and objective state, fix stereo text lifetimes, HUD placement and colors, sprite culling, and font shader passes. Handle swapchain timeouts and incomplete stereo frames safely, remove forced GPU waits, and correct rendering timing attribution. Track client-game source additions in CMake. Validated desktop and Android GL1/GL2 builds plus targeted regression and sanitizer checks. Headset appearance and performance remain unverified.
1079 lines
34 KiB
C++
1079 lines
34 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2024 the OpenMoHAA team
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
OpenMoHAA 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 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
OpenMoHAA 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 OpenMoHAA source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "cl_ui.h"
|
|
#ifdef USE_OPENXR
|
|
#include "../vr/vr_common.h"
|
|
#endif
|
|
#include "../qcommon/localization.h"
|
|
|
|
#include "../server/server.h"
|
|
|
|
CLASS_DECLARATION(UIWidget, View3D, NULL) {
|
|
{&W_Activated, &View3D::OnActivate },
|
|
{&W_Deactivated, &View3D::OnDeactivate},
|
|
{&W_LeftMouseDown, &View3D::Pressed },
|
|
{NULL, NULL }
|
|
};
|
|
|
|
cvar_t *subs[MAX_SUBTITLES];
|
|
cvar_t *teams[MAX_SUBTITLES];
|
|
float fadeTime[MAX_SUBTITLES];
|
|
float subLife[MAX_SUBTITLES];
|
|
float alpha[MAX_SUBTITLES];
|
|
char oldStrings[MAX_SUBTITLES][2048];
|
|
|
|
View3D::View3D()
|
|
{
|
|
// set as transparent
|
|
setBackgroundColor(UClear, true);
|
|
// no border
|
|
setBorderStyle(border_none);
|
|
AllowActivate(true);
|
|
|
|
m_printfadetime = 0.0;
|
|
m_printUpdateTime = cls.realtime;
|
|
m_subtitleUpdateTime = cls.realtime;
|
|
m_print_mat = NULL;
|
|
m_locationprint = qfalse;
|
|
}
|
|
|
|
void View3D::UpdateCenterPrint(const char *s, float alpha)
|
|
{
|
|
m_printstring = s;
|
|
|
|
if (s[0] == '@') {
|
|
m_print_mat = uWinMan.RegisterShader(s + 1);
|
|
} else {
|
|
m_print_mat = NULL;
|
|
}
|
|
|
|
m_printalpha = alpha;
|
|
m_printfadetime = 4000.0;
|
|
m_printUpdateTime = cls.realtime;
|
|
m_locationprint = qfalse;
|
|
}
|
|
|
|
void View3D::UpdateLocationPrint(int x, int y, const char *s, float alpha)
|
|
{
|
|
m_printstring = s;
|
|
m_printalpha = alpha;
|
|
m_printfadetime = 4000.0;
|
|
m_printUpdateTime = cls.realtime;
|
|
m_x_coord = x;
|
|
m_y_coord = y;
|
|
m_locationprint = qtrue;
|
|
}
|
|
|
|
void View3D::FrameInitialized(void)
|
|
{
|
|
Connect(this, W_Activated, W_Activated);
|
|
Connect(this, W_Deactivated, W_Deactivated);
|
|
}
|
|
|
|
void View3D::Pressed(Event *ev)
|
|
{
|
|
IN_MouseOff();
|
|
OnActivate(ev);
|
|
}
|
|
|
|
void View3D::OnActivate(Event *ev)
|
|
{
|
|
UIWidget *wid;
|
|
UList<UIWidget *> widgets;
|
|
|
|
UI_CloseInventory();
|
|
Key_SetCatcher(Key_GetCatcher() & ~KEYCATCH_UI);
|
|
|
|
for (wid = getParent()->getFirstChild(); wid; wid = getParent()->getNextChild(wid)) {
|
|
if (wid->getAlwaysOnBottom() && wid != this) {
|
|
widgets.AddTail(wid);
|
|
}
|
|
}
|
|
|
|
widgets.IterateFromHead();
|
|
while (widgets.IsCurrentValid()) {
|
|
widgets.getCurrent()->BringToFrontPropogated();
|
|
widgets.IterateNext();
|
|
}
|
|
}
|
|
|
|
void View3D::OnDeactivate(Event *ev)
|
|
{
|
|
Key_SetCatcher(Key_GetCatcher() | KEYCATCH_UI);
|
|
}
|
|
|
|
void View3D::DrawFPS(void)
|
|
{
|
|
char string[128];
|
|
|
|
setFont("verdana-14");
|
|
// Changed in OPM
|
|
// 1 just shows simple FPS
|
|
// 2 displays the number of tris
|
|
// 3 displays a black box at the bottom to correctly see the FPS counter
|
|
if (fps->integer == 3) {
|
|
re.SetColor(UBlack);
|
|
re.DrawBox(
|
|
0.0,
|
|
m_frame.pos.y + m_frame.size.height - m_font->getHeight() * 4.0,
|
|
m_frame.pos.x + m_frame.size.width,
|
|
m_font->getHeight() * 4.0
|
|
);
|
|
}
|
|
|
|
Com_sprintf(string, sizeof(string), "FPS %4.1f", currentfps);
|
|
if (currentfps > 23.94) {
|
|
if (cl_greenfps->integer) {
|
|
m_font->setColor(UGreen);
|
|
} else {
|
|
m_font->setColor(UWhite);
|
|
}
|
|
} else if (currentfps > 18.0) {
|
|
m_font->setColor(UYellow);
|
|
} else {
|
|
// low fps
|
|
m_font->setColor(URed);
|
|
}
|
|
|
|
// Added in OPM (fps_location)
|
|
// 0 = default (bottom left)
|
|
// 1 = bottom right
|
|
// 2 = top right under the time limit
|
|
switch(fps_location->integer) {
|
|
case 0:
|
|
default:
|
|
m_font->Print(
|
|
m_font->getHeight(getHighResScale()) * 10.0 / getHighResScale()[0],
|
|
(m_frame.pos.y + m_frame.size.height - m_font->getHeight(getHighResScale()) * 3.0) / getHighResScale()[1],
|
|
string,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
break;
|
|
case 1:
|
|
m_font->Print(
|
|
(m_frame.pos.x + m_frame.size.width - m_font->getWidth(string, -1) * getHighResScale()[0] - m_font->getHeight(getHighResScale())) / getHighResScale()[0],
|
|
(m_frame.pos.y + m_frame.size.height - m_font->getHeight(getHighResScale()) * 3.0) / getHighResScale()[1],
|
|
string,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
break;
|
|
case 2:
|
|
m_font->Print(
|
|
(m_frame.pos.x + m_frame.size.width - m_font->getWidth(string, -1) * getHighResScale()[0] - m_font->getHeight(getHighResScale())) / getHighResScale()[0],
|
|
(m_frame.pos.y + 40.0 * getHighResScale()[0]) / getHighResScale()[1],
|
|
string,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
break;
|
|
}
|
|
|
|
// Draw elements count
|
|
if (cl_greenfps->integer) {
|
|
m_font->setColor(UGreen);
|
|
} else {
|
|
m_font->setColor(UWhite);
|
|
}
|
|
|
|
if (fps->integer >= 2) {
|
|
Com_sprintf(string, sizeof(string), "wt%5d wv%5d cl%d", cls.world_tris, cls.world_verts, cls.character_lights);
|
|
|
|
// Added in OPM (fps_location)
|
|
switch(fps_location->integer) {
|
|
case 0:
|
|
default:
|
|
m_font->Print(
|
|
(m_font->getHeight(getHighResScale()) * 10.0) / getHighResScale()[0],
|
|
(m_frame.pos.y + m_frame.size.height - m_font->getHeight(getHighResScale()) * 2.0) / getHighResScale()[1],
|
|
string,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
break;
|
|
case 1:
|
|
m_font->Print(
|
|
(m_frame.pos.x + m_frame.size.width - m_font->getWidth(string, -1) * getHighResScale()[0] - m_font->getHeight(getHighResScale())) / getHighResScale()[0],
|
|
(m_frame.pos.y + m_frame.size.height - m_font->getHeight(getHighResScale()) * 2.0) / getHighResScale()[1],
|
|
string,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
break;
|
|
case 2:
|
|
m_font->Print(
|
|
(m_frame.pos.x + m_frame.size.width - m_font->getWidth(string, -1) * getHighResScale()[0] - m_font->getHeight(getHighResScale())) / getHighResScale()[0],
|
|
(m_frame.pos.y + 40 * getHighResScale()[0] + m_font->getHeight(getHighResScale())) / getHighResScale()[1],
|
|
string,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
break;
|
|
}
|
|
|
|
Com_sprintf(
|
|
string,
|
|
sizeof(string),
|
|
"t%5d v%5d Mtex%5.2f",
|
|
cls.total_tris,
|
|
cls.total_verts,
|
|
(float)cls.total_texels * 0.00000095367432
|
|
);
|
|
|
|
// Added in OPM (fps_location)
|
|
switch(fps_location->integer) {
|
|
case 0:
|
|
default:
|
|
m_font->Print(
|
|
(m_font->getHeight(getHighResScale()) * 10.0) / getHighResScale()[0],
|
|
(m_frame.pos.y + m_frame.size.height - m_font->getHeight(getHighResScale())) / getHighResScale()[1],
|
|
string,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
break;
|
|
case 1:
|
|
m_font->Print(
|
|
(m_frame.pos.x + m_frame.size.width - m_font->getWidth(string, -1) * getHighResScale()[0] - m_font->getHeight(getHighResScale())) / getHighResScale()[0],
|
|
(m_frame.pos.y + m_frame.size.height - m_font->getHeight(getHighResScale())) / getHighResScale()[1],
|
|
string,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
break;
|
|
case 2:
|
|
m_font->Print(
|
|
(m_frame.pos.x + m_frame.size.width - m_font->getWidth(string, -1) * getHighResScale()[0] - m_font->getHeight(getHighResScale())) / getHighResScale()[0],
|
|
(m_frame.pos.y + 40.0 * getHighResScale()[0] + m_font->getHeight(getHighResScale()) * 2.0) / getHighResScale()[1],
|
|
string,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_font->setColor(UBlack);
|
|
}
|
|
|
|
/*
|
|
void ProfPrint(UIFont* m_font, float minY, int line, char* label, prof_var_t* var, int level)
|
|
{
|
|
|
|
}
|
|
*/
|
|
|
|
void View3D::DrawProf(void)
|
|
{
|
|
// FIXME: unimplemented
|
|
}
|
|
|
|
void View3D::PrintSound(int channel, const char *name, float vol, int rvol, float pitch, float base, int& line)
|
|
{
|
|
char buf[255];
|
|
float x;
|
|
float xStep;
|
|
float height;
|
|
|
|
height = m_font->getHeight(getHighResScale());
|
|
xStep = height;
|
|
|
|
x = 0;
|
|
Com_sprintf(buf, sizeof(buf), "%d", channel);
|
|
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, getHighResScale());
|
|
|
|
x += xStep + xStep;
|
|
Com_sprintf(buf, sizeof(buf), "%s", name);
|
|
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, getHighResScale());
|
|
|
|
x += xStep * 30.0;
|
|
Com_sprintf(buf, sizeof(buf), "vol:%.2f", vol);
|
|
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, getHighResScale());
|
|
|
|
x += xStep * 8;
|
|
Com_sprintf(buf, sizeof(buf), "rvol:%.2f", (float)(rvol / 128.f));
|
|
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, getHighResScale());
|
|
|
|
x += xStep * 5;
|
|
Com_sprintf(buf, sizeof(buf), "pit:%.2f", pitch);
|
|
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, getHighResScale());
|
|
|
|
x += xStep * 5;
|
|
Com_sprintf(buf, sizeof(buf), "base:%d", (int)base);
|
|
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, getHighResScale());
|
|
|
|
line++;
|
|
}
|
|
|
|
void View3D::DrawSoundOverlay(void)
|
|
{
|
|
setFont("verdana-14");
|
|
m_font->setColor(UWhite);
|
|
|
|
// FIXME: Unimplemented
|
|
if (sound_overlay->integer) {
|
|
Com_Printf("sound_overlay isn't supported with OpenAL/SDL right now.\n");
|
|
Cvar_Set("sound_overlay", "0");
|
|
}
|
|
}
|
|
|
|
void DisplayServerNetProfileInfo(UIFont *font, float y, netprofclient_t *netprofile)
|
|
{
|
|
font->Print(104, y, va("%i", netprofile->upstream.packetsPerSec));
|
|
font->Print(144, y, va("%i", netprofile->downstream.packetsPerSec));
|
|
font->Print(184, y, va("%i", netprofile->upstream.packetsPerSec + netprofile->downstream.packetsPerSec));
|
|
font->Print(234, y, va("%i", netprofile->upstream.percentFragmented));
|
|
font->Print(264, y, va("%i", netprofile->downstream.percentFragmented));
|
|
font->Print(
|
|
294,
|
|
y,
|
|
va("%i",
|
|
(unsigned int)((float)(netprofile->downstream.numFragmented + netprofile->upstream.numFragmented)
|
|
/ (float)(netprofile->upstream.totalPackets + netprofile->downstream.totalPackets))),
|
|
-1
|
|
);
|
|
font->Print(334, y, va("%i", netprofile->upstream.percentDropped));
|
|
font->Print(364, y, va("%i", netprofile->downstream.percentDropped));
|
|
font->Print(
|
|
394,
|
|
y,
|
|
va("%i",
|
|
(unsigned int)((float)(netprofile->downstream.numDropped + netprofile->upstream.numDropped)
|
|
/ (float)(netprofile->upstream.totalPackets + netprofile->downstream.totalPackets))),
|
|
-1
|
|
);
|
|
font->Print(434, y, va("%i", netprofile->upstream.percentDropped));
|
|
font->Print(464, y, va("%i", netprofile->downstream.percentDropped));
|
|
font->Print(
|
|
494,
|
|
y,
|
|
va("%i",
|
|
(unsigned int)((float)(netprofile->downstream.totalBytesConnectionLess
|
|
+ netprofile->upstream.totalBytesConnectionLess)
|
|
/ (float)(netprofile->downstream.totalSize + netprofile->upstream.totalSize))),
|
|
-1
|
|
);
|
|
font->Print(534, y, va("%i", netprofile->upstream.bytesPerSec));
|
|
font->Print(594, y, va("%i", netprofile->downstream.bytesPerSec));
|
|
font->Print(654, y, va("%i", netprofile->downstream.bytesPerSec + netprofile->upstream.bytesPerSec));
|
|
font->Print(714, y, va("%i", netprofile->rate));
|
|
}
|
|
|
|
void DisplayClientNetProfile(UIFont *font, float x, float y, netprofclient_t *netprofile)
|
|
{
|
|
float columns[5];
|
|
float fontHeight;
|
|
float columnHeight;
|
|
|
|
fontHeight = font->getHeight();
|
|
columns[0] = x + 120;
|
|
columns[1] = x + 230;
|
|
columns[2] = x + 330;
|
|
columns[3] = x + 430;
|
|
columns[4] = x + 530;
|
|
columnHeight = y;
|
|
|
|
font->Print(x, y, va("Rate: %i", netprofile->rate));
|
|
|
|
columnHeight += fontHeight * 1.5;
|
|
font->Print(x, columnHeight, "Data Type");
|
|
font->Print(columns[0], columnHeight, "Packets per Sec");
|
|
font->Print(columns[1], columnHeight, "% Fragmented");
|
|
font->Print(columns[2], columnHeight, "% Dropped");
|
|
font->Print(columns[3], columnHeight, "% OOB data");
|
|
font->Print(columns[4], columnHeight, "Data per Sec");
|
|
|
|
columnHeight += fontHeight * 0.5;
|
|
font->Print(x, columnHeight, "----------");
|
|
font->Print(columns[0], columnHeight, "----------");
|
|
font->Print(columns[1], columnHeight, "----------");
|
|
font->Print(columns[2], columnHeight, "----------");
|
|
font->Print(columns[3], columnHeight, "----------");
|
|
font->Print(columns[4], columnHeight, "----------");
|
|
|
|
columnHeight += fontHeight;
|
|
font->Print(x, columnHeight, "Data In");
|
|
font->Print(columns[0], columnHeight, va("%i", netprofile->downstream.packetsPerSec));
|
|
font->Print(columns[1], columnHeight, va("%i%%", netprofile->downstream.percentFragmented));
|
|
font->Print(columns[2], columnHeight, va("%i%%", netprofile->downstream.percentDropped));
|
|
font->Print(columns[3], columnHeight, va("%i%%", netprofile->downstream.percentConnectionLess));
|
|
font->Print(columns[4], columnHeight, va("%i", netprofile->downstream.bytesPerSec));
|
|
|
|
columnHeight += fontHeight;
|
|
font->Print(x, columnHeight, "Data Out");
|
|
font->Print(columns[0], columnHeight, va("%i", netprofile->upstream.packetsPerSec));
|
|
font->Print(columns[1], columnHeight, va("%i%%", netprofile->upstream.percentFragmented));
|
|
font->Print(columns[2], columnHeight, va("%i%%", netprofile->upstream.percentDropped));
|
|
font->Print(columns[3], columnHeight, va("%i%%", netprofile->upstream.percentConnectionLess));
|
|
font->Print(columns[4], columnHeight, va("%i", netprofile->upstream.bytesPerSec));
|
|
|
|
columnHeight += fontHeight;
|
|
|
|
font->Print(x, columnHeight, "Total Data");
|
|
|
|
font->Print(
|
|
columns[0], columnHeight, va("%i", netprofile->upstream.packetsPerSec + netprofile->downstream.packetsPerSec)
|
|
);
|
|
font->Print(
|
|
columns[1],
|
|
columnHeight,
|
|
va("%i%%",
|
|
(unsigned int)((float)(netprofile->downstream.numFragmented + netprofile->upstream.numFragmented)
|
|
/ (float)(netprofile->upstream.totalPackets + netprofile->downstream.totalPackets)))
|
|
);
|
|
font->Print(
|
|
columns[2],
|
|
columnHeight,
|
|
va("%i%%",
|
|
(unsigned int)((float)(netprofile->downstream.numDropped + netprofile->upstream.numDropped)
|
|
/ (double)(netprofile->upstream.totalPackets + netprofile->downstream.totalPackets)))
|
|
);
|
|
font->Print(
|
|
columns[3],
|
|
columnHeight,
|
|
va("%i%%",
|
|
(unsigned int)((float)(netprofile->downstream.totalBytesConnectionLess
|
|
+ netprofile->upstream.totalBytesConnectionLess)
|
|
/ (float)(netprofile->downstream.totalSize + netprofile->upstream.totalSize)))
|
|
);
|
|
font->Print(
|
|
columns[4], columnHeight, va("%i", netprofile->upstream.bytesPerSec + netprofile->downstream.bytesPerSec)
|
|
);
|
|
}
|
|
|
|
void View3D::DrawNetProfile(void)
|
|
{
|
|
float fontHeight;
|
|
float yOffset;
|
|
int i;
|
|
|
|
if (sv_netprofileoverlay->integer && sv_netprofile->integer && com_sv_running->integer) {
|
|
float columnHeight;
|
|
float valueHeight;
|
|
float separatorHeight;
|
|
float categoryHeight;
|
|
float currentHeight;
|
|
netprofclient_t netproftotal;
|
|
|
|
setFont("verdana-14");
|
|
m_font->setColor(UWhite);
|
|
|
|
fontHeight = m_font->getHeight();
|
|
yOffset = sv_netprofileoverlay->integer + 8;
|
|
|
|
if (svs.netprofile.rate) {
|
|
m_font->Print(8, yOffset, va("Server Net Profile Max Rate: %i", svs.netprofile.rate), -1);
|
|
} else {
|
|
m_font->Print(8, yOffset, "Server Net Profile Max Rate: none", -1);
|
|
}
|
|
|
|
columnHeight = fontHeight + fontHeight + yOffset;
|
|
valueHeight = columnHeight + fontHeight;
|
|
separatorHeight = fontHeight * 1.5 + columnHeight;
|
|
categoryHeight = fontHeight * 0.5 + columnHeight;
|
|
|
|
m_font->Print(8, categoryHeight, "Data Source");
|
|
m_font->Print(8, separatorHeight, "---------------");
|
|
m_font->Print(104, columnHeight, "Packets per Sec");
|
|
m_font->Print(104, valueHeight, "In");
|
|
m_font->Print(144, valueHeight, "Out");
|
|
m_font->Print(184, valueHeight, "Total");
|
|
m_font->Print(104, separatorHeight, "---");
|
|
m_font->Print(144, separatorHeight, "-----");
|
|
m_font->Print(184, separatorHeight, "------");
|
|
m_font->Print(234, columnHeight, "% Fragmented");
|
|
m_font->Print(234, valueHeight, "In");
|
|
m_font->Print(264, valueHeight, "Out");
|
|
m_font->Print(294, valueHeight, "Total");
|
|
m_font->Print(234, separatorHeight, "---");
|
|
m_font->Print(264, separatorHeight, "-----");
|
|
m_font->Print(294, separatorHeight, "------");
|
|
m_font->Print(334, columnHeight, "% Dropped");
|
|
m_font->Print(334, valueHeight, "In");
|
|
m_font->Print(364, valueHeight, "Out");
|
|
m_font->Print(394, valueHeight, "Total");
|
|
m_font->Print(334, separatorHeight, "---");
|
|
m_font->Print(364, separatorHeight, "-----");
|
|
m_font->Print(394, separatorHeight, "------");
|
|
m_font->Print(434, columnHeight, "% OOB Data");
|
|
m_font->Print(434, valueHeight, "In");
|
|
m_font->Print(464, valueHeight, "Out");
|
|
m_font->Print(494, valueHeight, "Total");
|
|
m_font->Print(434, separatorHeight, "---");
|
|
m_font->Print(464, separatorHeight, "-----");
|
|
m_font->Print(494, separatorHeight, "------");
|
|
m_font->Print(534, columnHeight, "Data per Sec");
|
|
m_font->Print(534, valueHeight, "In");
|
|
m_font->Print(594, valueHeight, "Out");
|
|
m_font->Print(654, valueHeight, "Total");
|
|
m_font->Print(534, separatorHeight, "---");
|
|
m_font->Print(594, separatorHeight, "-----");
|
|
m_font->Print(654, separatorHeight, "------");
|
|
m_font->Print(714, categoryHeight, "Rate");
|
|
m_font->Print(714, separatorHeight, "------");
|
|
|
|
currentHeight = fontHeight * 2.5 + columnHeight;
|
|
SV_NET_CalcTotalNetProfile(&netproftotal, qfalse);
|
|
m_font->Print(8, columnHeight + fontHeight * 2.5, "Total");
|
|
DisplayServerNetProfileInfo(m_font, currentHeight, &netproftotal);
|
|
|
|
currentHeight += fontHeight * 1.5;
|
|
m_font->Print(8, currentHeight, "Clientless");
|
|
DisplayServerNetProfileInfo(m_font, currentHeight, &svs.netprofile);
|
|
|
|
currentHeight += fontHeight;
|
|
|
|
for (i = 0; i < svs.iNumClients; i++) {
|
|
client_t *client = &svs.clients[i];
|
|
if (client->state != CS_ACTIVE || !client->gentity) {
|
|
continue;
|
|
}
|
|
|
|
if (client->netchan.remoteAddress.type == NA_LOOPBACK) {
|
|
m_font->Print(8.0, currentHeight, va("#%i-Loopback", i), -1, 0);
|
|
|
|
} else {
|
|
m_font->Print(8.0, currentHeight, va("Client #%i", i), -1, 0);
|
|
}
|
|
|
|
DisplayServerNetProfileInfo(m_font, currentHeight, &client->netprofile);
|
|
currentHeight = currentHeight + fontHeight;
|
|
}
|
|
} else if (cl_netprofileoverlay->integer && cl_netprofile->integer && com_cl_running->integer) {
|
|
setFont("verdana-14");
|
|
m_font->setColor(UWhite);
|
|
|
|
fontHeight = m_font->getHeight();
|
|
yOffset = cl_netprofileoverlay->integer + 16;
|
|
|
|
m_font->Print(16, yOffset, "Client Net Profile", -1);
|
|
|
|
NetProfileCalcStats(&cls.netprofile.downstream, 500);
|
|
NetProfileCalcStats(&cls.netprofile.upstream, 500);
|
|
|
|
DisplayClientNetProfile(m_font, 16, yOffset + fontHeight * 2, &cls.netprofile);
|
|
}
|
|
}
|
|
|
|
void View3D::Draw2D(void)
|
|
{
|
|
#ifdef USE_OPENXR
|
|
VR_TraceEvent(VRTRACE_DRAW2D);
|
|
#endif
|
|
#ifdef USE_OPENXR
|
|
const int fadeStart = Sys_Milliseconds();
|
|
int printStart = fadeStart;
|
|
int overlayStart = fadeStart;
|
|
int fadeEnd;
|
|
int overlayEnd = fadeStart;
|
|
#endif
|
|
|
|
if (!cls.no_menus) {
|
|
DrawFades();
|
|
}
|
|
|
|
DrawLetterbox();
|
|
|
|
#ifdef USE_OPENXR
|
|
fadeEnd = Sys_Milliseconds();
|
|
#endif
|
|
|
|
if ((cl_debuggraph->integer || cl_timegraph->integer) && !cls.no_menus) {
|
|
SCR_DrawDebugGraph();
|
|
} else if (!cls.no_menus) {
|
|
if (cge) {
|
|
#ifdef USE_OPENXR
|
|
const int cgameStart = Sys_Milliseconds();
|
|
VR_TraceEvent(VRTRACE_CGAME_HUD);
|
|
#endif
|
|
cge->CG_Draw2D();
|
|
#ifdef USE_OPENXR
|
|
VR_TraceHudTimes(Sys_Milliseconds() - cgameStart);
|
|
#endif
|
|
}
|
|
|
|
#ifdef USE_OPENXR
|
|
printStart = Sys_Milliseconds();
|
|
#endif
|
|
|
|
if (m_locationprint) {
|
|
LocationPrint();
|
|
} else {
|
|
CenterPrint();
|
|
}
|
|
|
|
#ifdef USE_OPENXR
|
|
overlayStart = Sys_Milliseconds();
|
|
#endif
|
|
|
|
if (!cls.no_menus) {
|
|
DrawSoundOverlay();
|
|
DrawNetProfile();
|
|
DrawSubtitleOverlay();
|
|
}
|
|
|
|
#ifdef USE_OPENXR
|
|
overlayEnd = Sys_Milliseconds();
|
|
#endif
|
|
}
|
|
|
|
if (fps->integer && !cls.no_menus) {
|
|
DrawFPS();
|
|
DrawProf();
|
|
}
|
|
|
|
#ifdef USE_OPENXR
|
|
// Everything from here accounted for: whatever the parts do not add up to
|
|
// is not in this function at all.
|
|
VR_TraceHudParts(0, fadeEnd - fadeStart, overlayStart - printStart,
|
|
overlayEnd - overlayStart, Sys_Milliseconds() - overlayEnd);
|
|
#endif
|
|
}
|
|
|
|
// Fixed in OPM
|
|
// Advance by elapsed client time, not by the number of rendered eyes.
|
|
void View3D::UpdatePrintTime(void)
|
|
{
|
|
const int elapsed = cls.realtime - m_printUpdateTime;
|
|
m_printUpdateTime = cls.realtime;
|
|
if (elapsed > 0) {
|
|
m_printfadetime = Q_max(0.0f, m_printfadetime - elapsed);
|
|
}
|
|
}
|
|
|
|
void View3D::CenterPrint(void)
|
|
{
|
|
float alpha;
|
|
const char *p;
|
|
qhandle_t mat;
|
|
float x, y;
|
|
float w, h;
|
|
|
|
UpdatePrintTime();
|
|
if (!m_printfadetime) {
|
|
return;
|
|
}
|
|
|
|
p = Sys_LV_CL_ConvertString(m_printstring);
|
|
if (m_printfadetime > 3250) {
|
|
alpha = 1.f - (m_printfadetime - 3250.f) / 750.f * m_printalpha;
|
|
} else if (m_printfadetime >= 750) {
|
|
alpha = 1.f;
|
|
} else {
|
|
alpha = m_printfadetime / 750.f * m_printalpha;
|
|
}
|
|
|
|
alpha = Q_clamp_float(alpha, 0, 1);
|
|
|
|
if (!m_print_mat) {
|
|
UIRect2D frame;
|
|
m_font->setColor(UColor(0, 0, 0, alpha));
|
|
|
|
frame = getClientFrame();
|
|
|
|
m_font->PrintJustified(
|
|
UIRect2D(frame.pos.x + 1, frame.pos.y + 1, frame.size.width, frame.size.height),
|
|
m_iFontAlignmentHorizontal,
|
|
m_iFontAlignmentVertical,
|
|
p,
|
|
getVirtualScale()
|
|
);
|
|
|
|
m_font->setColor(UColor(1, 1, 1, alpha));
|
|
|
|
frame = getClientFrame();
|
|
|
|
m_font->PrintJustified(frame, m_iFontAlignmentHorizontal, m_iFontAlignmentVertical, p, getVirtualScale());
|
|
|
|
m_font->setColor(UBlack);
|
|
} else if ((mat = m_print_mat->GetMaterial())) {
|
|
vec4_t col {alpha, alpha, alpha, alpha};
|
|
|
|
re.SetColor(col);
|
|
|
|
w = re.GetShaderWidth(mat);
|
|
h = re.GetShaderHeight(mat);
|
|
x = (m_frame.pos.x + m_frame.size.width - w) * 0.5f;
|
|
y = (m_frame.pos.y + m_frame.size.height - h) * 0.5f;
|
|
|
|
re.DrawStretchPic(x, y, w, h, 0, 0, 1, 1, mat);
|
|
}
|
|
}
|
|
|
|
void View3D::LocationPrint(void)
|
|
{
|
|
fonthorzjustify_t horiz;
|
|
fontvertjustify_t vert;
|
|
int x, y;
|
|
const char *p;
|
|
float alpha;
|
|
UIRect2D frame;
|
|
|
|
UpdatePrintTime();
|
|
if (!m_printfadetime) {
|
|
m_locationprint = false;
|
|
return;
|
|
}
|
|
|
|
horiz = FONT_JUSTHORZ_LEFT;
|
|
vert = FONT_JUSTVERT_TOP;
|
|
|
|
p = Sys_LV_CL_ConvertString(m_printstring);
|
|
if (m_printfadetime > 3250) {
|
|
alpha = 1.f - (m_printfadetime - 3250.f) / 750.f * m_printalpha;
|
|
} else if (m_printfadetime >= 750) {
|
|
alpha = 1.f;
|
|
} else {
|
|
alpha = m_printfadetime / 750.f * m_printalpha;
|
|
}
|
|
|
|
alpha = Q_clamp_float(alpha, 0, 1);
|
|
|
|
x = m_x_coord / 640.f * m_screenframe.size.width;
|
|
y = (480 - m_font->getHeight(getHighResScale()) - m_y_coord) / 480.f * m_screenframe.size.height;
|
|
|
|
if (m_x_coord == -1) {
|
|
horiz = FONT_JUSTHORZ_CENTER;
|
|
x = 0;
|
|
}
|
|
if (m_y_coord == -1) {
|
|
vert = FONT_JUSTVERT_CENTER;
|
|
y = 0;
|
|
}
|
|
|
|
m_font->setColor(UColor(0, 0, 0, alpha));
|
|
frame = getClientFrame();
|
|
|
|
m_font->PrintJustified(
|
|
UIRect2D(frame.pos.x + x + 1, frame.pos.y + y + 1, frame.size.width, frame.size.height),
|
|
horiz,
|
|
vert,
|
|
p,
|
|
getVirtualScale()
|
|
);
|
|
|
|
m_font->setColor(UColor(1, 1, 1, alpha));
|
|
frame = getClientFrame();
|
|
|
|
m_font->PrintJustified(
|
|
UIRect2D(frame.pos.x + x, frame.pos.y + y, frame.size.width, frame.size.height),
|
|
horiz,
|
|
vert,
|
|
p,
|
|
getVirtualScale()
|
|
);
|
|
|
|
m_font->setColor(UBlack);
|
|
}
|
|
|
|
void View3D::DrawLetterbox(void)
|
|
{
|
|
float frac;
|
|
vec4_t col;
|
|
|
|
col[0] = col[1] = col[2] = 0;
|
|
col[3] = 1;
|
|
|
|
frac = (float)cl.snap.ps.stats[STAT_LETTERBOX] / MAX_LETTERBOX_SIZE;
|
|
if (frac <= 0) {
|
|
m_letterbox_active = false;
|
|
return;
|
|
}
|
|
|
|
m_letterbox_active = true;
|
|
re.SetColor(col);
|
|
|
|
re.DrawBox(0.0, 0.0, m_screenframe.size.width, m_screenframe.size.height * frac);
|
|
re.DrawBox(
|
|
0.0,
|
|
m_screenframe.size.height - m_screenframe.size.height * frac,
|
|
m_screenframe.size.width,
|
|
m_screenframe.size.height
|
|
);
|
|
}
|
|
|
|
void View3D::DrawFades(void)
|
|
{
|
|
if (cl.snap.ps.blend[3] > 0) {
|
|
re.SetColor(cl.snap.ps.blend);
|
|
if (cl.snap.ps.stats[STAT_ADDFADE]) {
|
|
re.AddBox(0.0, 0.0, m_screenframe.size.width, m_screenframe.size.height);
|
|
} else {
|
|
re.DrawBox(0.0, 0.0, m_screenframe.size.width, m_screenframe.size.height);
|
|
}
|
|
}
|
|
}
|
|
|
|
void View3D::Draw(void)
|
|
{
|
|
#ifdef USE_OPENXR
|
|
const int drawStart = Sys_Milliseconds();
|
|
VR_TraceEvent(VRTRACE_VIEW3D);
|
|
#endif
|
|
|
|
//
|
|
// Added in OPM
|
|
// The one place the 3D scene and the flat content over it meet, and so the
|
|
// one place a VR frame has to be able to take them apart - see
|
|
// vrDrawMode_t. The eye passes want the scene and nothing else; the wrist
|
|
// panel wants everything else and not the scene.
|
|
//
|
|
if (clc.state != CA_DISCONNECTED && cl_vrDrawMode != VRDRAW_PANEL) {
|
|
SCR_DrawScreenField();
|
|
}
|
|
|
|
if (cl_vrDrawMode == VRDRAW_WORLD) {
|
|
//
|
|
// Added in OPM
|
|
// Everything flat has gone to the wrist panel except the reticle and
|
|
// the scope surround, which have to stay with the world - see
|
|
// CG_Draw2D, which draws only those while vr_hudPass says so.
|
|
//
|
|
#ifdef USE_OPENXR
|
|
int hudStart = Sys_Milliseconds();
|
|
#endif
|
|
if (cge && !cls.no_menus) {
|
|
set2D();
|
|
// Set2DWindow flushes the queued world commands. Charge that
|
|
// work to the world before starting the HUD timer.
|
|
#ifdef USE_OPENXR
|
|
hudStart = Sys_Milliseconds();
|
|
#endif
|
|
cge->CG_Draw2D();
|
|
}
|
|
#ifdef USE_OPENXR
|
|
VR_TraceViewTimes(hudStart - drawStart, Sys_Milliseconds() - hudStart);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
set2D();
|
|
|
|
#ifdef USE_OPENXR
|
|
// The transition issues the pending world draw calls; starting the timer
|
|
// before it incorrectly reports those calls as HUD setup time.
|
|
const int hudStart = Sys_Milliseconds();
|
|
#endif
|
|
re.SavePerformanceCounters();
|
|
|
|
Draw2D();
|
|
|
|
#ifdef USE_OPENXR
|
|
VR_TraceViewTimes(hudStart - drawStart, Sys_Milliseconds() - hudStart);
|
|
#endif
|
|
}
|
|
|
|
float avWidth = 0.0;
|
|
|
|
void View3D::InitSubtitle(void)
|
|
{
|
|
float totalWidth;
|
|
|
|
m_subtitleUpdateTime = cls.realtime;
|
|
for (int i = 0; i < 4; i++) {
|
|
subs[i] = Cvar_Get(va("subtitle%d", i), "", 0);
|
|
teams[i] = Cvar_Get(va("subteam%d", i), "0", 0);
|
|
Q_strncpyz(oldStrings[i], subs[i]->string, sizeof(oldStrings[i]));
|
|
fadeTime[i] = 4000.0;
|
|
subLife[i] = 4000.0;
|
|
}
|
|
|
|
totalWidth = 0.0;
|
|
for (char j = 'A'; j <= 'Z'; j++) {
|
|
totalWidth += m_font->getCharWidth(j);
|
|
}
|
|
|
|
avWidth = totalWidth / 26.0;
|
|
}
|
|
|
|
void View3D::DrawSubtitleOverlay(void)
|
|
{
|
|
cvar_t *subAlpha;
|
|
int i;
|
|
float minX, maxX;
|
|
int line;
|
|
const int elapsed = Q_max(0, cls.realtime - m_subtitleUpdateTime);
|
|
|
|
m_subtitleUpdateTime = cls.realtime;
|
|
|
|
subAlpha = Cvar_Get("subAlpha", "0.5", 0);
|
|
|
|
setFont("facfont-20");
|
|
m_font->setColor(URed);
|
|
|
|
for (i = 0; i < MAX_SUBTITLES; i++) {
|
|
if (strcmp(oldStrings[i], subs[i]->string)) {
|
|
fadeTime[i] = 2500 * ((strlen(subs[i]->string) / 68) + 1.f) + 1500;
|
|
subLife[i] = fadeTime[i];
|
|
Q_strncpyz(oldStrings[i], subs[i]->string, sizeof(oldStrings[i]));
|
|
} else {
|
|
fadeTime[i] = Q_max(0.0f, fadeTime[i] - elapsed);
|
|
}
|
|
|
|
if (fadeTime[i] <= 0) {
|
|
oldStrings[i][0] = 0;
|
|
if (subs[i]->string && subs[i]->string[0]) {
|
|
Cvar_Set(va("subtitle%d", i), "");
|
|
}
|
|
}
|
|
|
|
if (fadeTime[i] > subLife[i] - 750.f) {
|
|
alpha[i] = 1.f - (fadeTime[i] - (subLife[i] - 750.f)) / 750.f;
|
|
} else if (fadeTime[i] < 750) {
|
|
alpha[i] = fadeTime[i] / 750.f;
|
|
} else {
|
|
alpha[i] = 1.f;
|
|
}
|
|
}
|
|
|
|
minX = m_screenframe.size.height - m_font->getHeight(getHighResScale()) * 10;
|
|
maxX = ((m_frame.pos.x + m_frame.size.width) - (m_frame.pos.x + m_frame.size.width) * 0.2f) / getHighResScale()[0];
|
|
line = 0;
|
|
|
|
for (i = 0; i < MAX_SUBTITLES; i++) {
|
|
if (fadeTime[i] <= 0) {
|
|
continue;
|
|
}
|
|
|
|
if (m_font->getWidth(subs[i]->string, sizeof(oldStrings[i])) > maxX) {
|
|
char buf[2048];
|
|
char *c;
|
|
char *end;
|
|
char *start;
|
|
float total;
|
|
float width;
|
|
int blockcount;
|
|
|
|
c = subs[i]->string;
|
|
|
|
total = 0;
|
|
end = NULL;
|
|
start = buf;
|
|
buf[0] = 0;
|
|
|
|
while (*c) {
|
|
blockcount = m_font->DBCSGetWordBlockCount(c, -1);
|
|
if (!blockcount) {
|
|
break;
|
|
}
|
|
|
|
width = m_font->getWidth(c, blockcount);
|
|
|
|
if (total + width > maxX) {
|
|
m_font->setColor(UColor(0, 0, 0, alpha[i] * subAlpha->value));
|
|
m_font->Print(
|
|
18,
|
|
(m_font->getHeight(getHighResScale()) * line + minX + 1.f) / getHighResScale()[1],
|
|
buf,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
|
|
m_font->setColor(UColor(1, 1, 1, alpha[i] * subAlpha->value));
|
|
m_font->Print(
|
|
20,
|
|
(m_font->getHeight(getHighResScale()) * line + minX) / getHighResScale()[1],
|
|
buf,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
|
|
line++;
|
|
|
|
total = 0;
|
|
start = buf;
|
|
}
|
|
|
|
end = start + blockcount + 1;
|
|
if (end > buf + MAX_STRING_CHARS) {
|
|
Com_DPrintf("ERROR - word longer than possible line\n");
|
|
break;
|
|
}
|
|
|
|
memcpy(start, c, blockcount);
|
|
start += blockcount;
|
|
total += width;
|
|
*start = 0;
|
|
|
|
c += blockcount;
|
|
}
|
|
|
|
m_font->setColor(UColor(0, 0, 0, alpha[i] * subAlpha->value));
|
|
m_font->Print(
|
|
18,
|
|
(m_font->getHeight(getHighResScale()) * line + minX + 1.f) / getHighResScale()[1],
|
|
buf,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
|
|
m_font->setColor(UColor(1, 1, 1, alpha[i] * subAlpha->value));
|
|
m_font->Print(
|
|
20,
|
|
(m_font->getHeight(getHighResScale()) * line + minX) / getHighResScale()[1],
|
|
buf,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
line++;
|
|
} else {
|
|
m_font->setColor(UColor(0, 0, 0, alpha[i] * subAlpha->value));
|
|
m_font->Print(
|
|
18,
|
|
(m_font->getHeight(getHighResScale()) * line + minX + 1.f) / getHighResScale()[1],
|
|
subs[i]->string,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
|
|
m_font->setColor(UColor(1, 1, 1, alpha[i] * subAlpha->value));
|
|
m_font->Print(
|
|
20,
|
|
(m_font->getHeight(getHighResScale()) * line + minX) / getHighResScale()[1],
|
|
subs[i]->string,
|
|
-1,
|
|
getHighResScale()
|
|
);
|
|
|
|
line++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void View3D::ClearCenterPrint(void)
|
|
{
|
|
m_printfadetime = 0.0;
|
|
}
|
|
|
|
qboolean View3D::LetterboxActive(void)
|
|
{
|
|
return m_letterbox_active;
|
|
}
|
|
|
|
CLASS_DECLARATION(UIWidget, ConsoleView, NULL) {
|
|
{NULL, NULL}
|
|
};
|
|
|
|
void ConsoleView::Draw(void) {}
|