Compare commits
38 commits
417a1714d1
...
f47d1c0331
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f47d1c0331 | ||
|
|
d3b482c103 | ||
|
|
87885fafdb | ||
|
|
b3938d3ecd | ||
|
|
03400d0a81 | ||
|
|
4c8899b373 | ||
|
|
424a50832d | ||
|
|
40dc3de468 | ||
|
|
d6e388b191 | ||
|
|
732c44c0d2 | ||
|
|
915f00b286 | ||
|
|
a59a4360cb | ||
|
|
7c28b24fb6 | ||
|
|
5d2b5552c5 | ||
|
|
856684a68c | ||
|
|
eaa676f9d6 | ||
|
|
426bec4395 | ||
|
|
d838d4a8ff | ||
|
|
137ffd14cf | ||
|
|
9f52b1e158 | ||
|
|
247c3cb323 | ||
|
|
7039fff6ab | ||
|
|
5f2e63e44c | ||
|
|
288867dda3 | ||
|
|
5cfd8d8823 | ||
|
|
24bb78e448 | ||
|
|
38ab10e2ff | ||
|
|
6a3a78a002 | ||
|
|
011147e39a | ||
|
|
bea6246ff7 | ||
|
|
74260033e0 | ||
|
|
aa585e00c7 | ||
|
|
b9eb26f55e | ||
|
|
7d3a00b0b5 | ||
|
|
00804d6d52 | ||
|
|
d5529f8458 | ||
|
|
b5e9e1e4da | ||
|
|
899dc271ca |
61 changed files with 3107 additions and 415 deletions
|
|
@ -4,6 +4,8 @@ BlanKart is a modification of SRB2Kart v2 Indev to make it closer to SRB2Kart ga
|
|||
|
||||
If you're interested in helping out, theres a matrix room located [here](https://matrix.to/#/#blankart:matrix.org)!
|
||||
|
||||
The VR work in this fork is inspired in part by [`kart-public-vr`](https://git.do.srb2.org/chreas/kart-public-vr), an earlier SRB2Kart OpenVR implementation.
|
||||
|
||||
# Notice
|
||||
|
||||
This is still in active development and things are going to change. If you find any bugs besure to report to the [issue tracker](https://codeberg.org/NepDisk/blankart/issues)!
|
||||
|
|
|
|||
|
|
@ -157,6 +157,9 @@ add_executable(BLANKART MACOSX_BUNDLE WIN32
|
|||
h_timers.cpp
|
||||
stun.c
|
||||
lonesha256.c
|
||||
vr/vr_main.c
|
||||
vr/vr_render.c
|
||||
vr/vr_math.c
|
||||
)
|
||||
|
||||
if(("${CMAKE_COMPILER_IS_GNUCC}" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
|
||||
|
|
@ -364,6 +367,13 @@ if(SRB2_CONFIG_ENABLE_DISCORDRPC)
|
|||
target_sources(BLANKART PRIVATE discord.c)
|
||||
endif()
|
||||
|
||||
# VR Support
|
||||
target_compile_definitions(BLANKART PRIVATE -DHAVE_VR)
|
||||
target_link_libraries(BLANKART PRIVATE openxr_loader)
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(BLANKART PRIVATE GL GLX X11)
|
||||
endif()
|
||||
|
||||
set(SRB2_HAVE_THREADS ON)
|
||||
target_compile_definitions(BLANKART PRIVATE -DHAVE_THREADS)
|
||||
|
||||
|
|
|
|||
|
|
@ -1455,7 +1455,7 @@ static UINT32 xxHashString32(const char *name)
|
|||
#define NAME cvar_map_t
|
||||
#define KEY_TY const char *
|
||||
#define VAL_TY consvar_t *
|
||||
#define HASH_FN xxHashString32
|
||||
#define HASH_FN FNV1a_HashLowercaseString
|
||||
#define CMPR_FN vt_cmpr_casestring
|
||||
#include "verstable.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -710,7 +710,7 @@ static char shiftxform[] =
|
|||
|
||||
INT32 CON_ShiftChar(INT32 ch)
|
||||
{
|
||||
if (I_UseNativeKeyboard() || ch >= sizeof(shiftxform))
|
||||
if (I_UseNativeKeyboard() || ch >= (INT32)sizeof(shiftxform))
|
||||
return ch;
|
||||
|
||||
// warning: shiftdown is NOT a boolean, it's 1 or 2 for lshift/rshift
|
||||
|
|
|
|||
|
|
@ -4449,8 +4449,8 @@ void CL_RemoveSplitscreenPlayer(UINT8 p)
|
|||
|
||||
static void GotOurIP(UINT32 address)
|
||||
{
|
||||
const unsigned char * p = (const unsigned char *)&address;
|
||||
#ifdef DEVELOP
|
||||
const unsigned char * p = (const unsigned char *)&address;
|
||||
CONS_Printf("Got IP of %u.%u.%u.%u\n", p[0], p[1], p[2], p[3]);
|
||||
#endif
|
||||
ourIP = address;
|
||||
|
|
@ -7215,6 +7215,7 @@ static void UpdatePingTable(void)
|
|||
}
|
||||
|
||||
// It's that time again! Send everyone a safe message to sign, so we can show off their signature and prove we're playing fair.
|
||||
#if 0
|
||||
static void SendChallenges(void)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -7375,6 +7376,7 @@ static void UpdateChallenges(void)
|
|||
HandleSigfail("Didn't receive client signatures.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void RenewHolePunch(void)
|
||||
{
|
||||
|
|
|
|||
327
src/d_main.cpp
327
src/d_main.cpp
|
|
@ -56,6 +56,7 @@
|
|||
#include "r_local.h"
|
||||
#include "r_voicepreference.hpp" // Preferences directory
|
||||
#include "s_sound.h"
|
||||
#include "screen.h"
|
||||
#include "st_stuff.h"
|
||||
#include "v_video.h"
|
||||
#include "w_wad.h"
|
||||
|
|
@ -102,9 +103,9 @@
|
|||
#define ASSET_HASH_GFX_KART 0xc91b0d43f5ba131f
|
||||
#define ASSET_HASH_TEXTURES_KART 0xb4211b2f32b6a291
|
||||
#define ASSET_HASH_CHARS_KART 0x1e68a3e01aa5c68b
|
||||
#define ASSET_HASH_MAPS_KART 0x2be29dfb3a146dfa
|
||||
#define ASSET_HASH_MAIN_PK3 0x4ea7e79e2d5d0d63
|
||||
#define ASSET_HASH_MAPPATCH_PK3 0x1745690024efbaf8
|
||||
#define ASSET_HASH_MAPS_KART 0x38558ed00da41ce9
|
||||
#define ASSET_HASH_MAIN_PK3 0xbeaab17108f0815a
|
||||
#define ASSET_HASH_MAPPATCH_PK3 0xdfb3c9da6c8c3adf
|
||||
#define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461
|
||||
#ifdef USE_PATCH_FILE
|
||||
#define ASSET_HASH_PATCH_PK3 0x0000000000000000
|
||||
|
|
@ -425,12 +426,80 @@ gamestate_t wipegamestate = GS_LEVEL;
|
|||
INT16 wipetypepre = -1;
|
||||
INT16 wipetypepost = -1;
|
||||
|
||||
#ifdef HAVE_VR
|
||||
#include "vr/vr_main.h"
|
||||
#include "vr/vr_render.h"
|
||||
static void D_Display_Internal(void);
|
||||
|
||||
static void D_Display(void)
|
||||
{
|
||||
static boolean vr_auto_started_menu = false;
|
||||
|
||||
if (vr_started && gamestate == GS_TITLESCREEN && !menustack[0] && !vr_auto_started_menu)
|
||||
{
|
||||
M_StartControlPanel();
|
||||
CONS_Printf("VR: Auto-opened title menu; menustack[0]=%d, gamestate=%d.\n",
|
||||
(INT32)menustack[0], (INT32)gamestate);
|
||||
vr_auto_started_menu = true;
|
||||
}
|
||||
else if (!vr_started || gamestate != GS_TITLESCREEN)
|
||||
vr_auto_started_menu = false;
|
||||
|
||||
const boolean vr_quad_ui = (cv_vruimode.value != 0);
|
||||
|
||||
if (vr_started && VR_BeginFrame())
|
||||
{
|
||||
if (VR_SetEye(0))
|
||||
{
|
||||
D_Display_Internal();
|
||||
VR_ReleaseEye(0);
|
||||
}
|
||||
|
||||
if (VR_SetEye(1))
|
||||
{
|
||||
D_Display_Internal();
|
||||
VR_ReleaseEye(1);
|
||||
}
|
||||
|
||||
if (vr_quad_ui && VR_BindUISwapchain())
|
||||
{
|
||||
D_Display_Internal();
|
||||
VR_BindDefaultFramebuffer();
|
||||
}
|
||||
|
||||
VR_EndFrame();
|
||||
}
|
||||
else
|
||||
{
|
||||
vr_render_pass = VR_PASS_NONE;
|
||||
D_Display_Internal();
|
||||
}
|
||||
}
|
||||
|
||||
static void D_Display_Internal(void)
|
||||
#else
|
||||
static void D_Display(void)
|
||||
#endif
|
||||
{
|
||||
boolean forcerefresh = false;
|
||||
static boolean wipe = false;
|
||||
INT32 wipedefindex = 0;
|
||||
UINT8 i;
|
||||
#ifdef HAVE_VR
|
||||
const boolean vr_quad_ui = (vr_started && cv_vruimode.value != 0);
|
||||
const boolean vr_in_eye_ui = (vr_started && !vr_quad_ui
|
||||
&& (vr_render_pass == VR_PASS_3D_LEFT || vr_render_pass == VR_PASS_3D_RIGHT));
|
||||
const boolean vr_draw_3d = (!vr_started || vr_render_pass == VR_PASS_NONE
|
||||
|| vr_render_pass == VR_PASS_3D_LEFT || vr_render_pass == VR_PASS_3D_RIGHT);
|
||||
const boolean vr_draw_ui = (!vr_started || vr_render_pass == VR_PASS_NONE || vr_render_pass == VR_PASS_UI
|
||||
|| (!vr_quad_ui && (vr_render_pass == VR_PASS_3D_LEFT || vr_render_pass == VR_PASS_3D_RIGHT)));
|
||||
const boolean vr_draw_title = (!vr_started || vr_render_pass != VR_PASS_UI);
|
||||
#else
|
||||
const boolean vr_in_eye_ui = false;
|
||||
const boolean vr_draw_3d = true;
|
||||
const boolean vr_draw_ui = true;
|
||||
const boolean vr_draw_title = true;
|
||||
#endif
|
||||
|
||||
ZoneScoped;
|
||||
|
||||
|
|
@ -539,79 +608,89 @@ static void D_Display(void)
|
|||
}
|
||||
|
||||
// do buffered drawing
|
||||
switch (gamestate)
|
||||
if (vr_draw_ui)
|
||||
{
|
||||
case GS_TITLESCREEN:
|
||||
if (!titlemapinaction || !curbghide) {
|
||||
F_TitleScreenDrawer();
|
||||
break;
|
||||
}
|
||||
/* FALLTHRU */
|
||||
case GS_LEVEL:
|
||||
if (!gametic)
|
||||
break;
|
||||
AM_Drawer();
|
||||
break;
|
||||
#ifdef HAVE_VR
|
||||
if (vr_in_eye_ui)
|
||||
VR_BeginInEyeUI();
|
||||
#endif
|
||||
|
||||
case GS_INTERMISSION:
|
||||
Y_IntermissionDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_VOTING:
|
||||
Y_VoteDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_TIMEATTACK:
|
||||
break;
|
||||
|
||||
case GS_INTRO:
|
||||
F_IntroDrawer();
|
||||
if (wipegamestate == (gamestate_t)-1)
|
||||
switch (gamestate)
|
||||
{
|
||||
wipe = true;
|
||||
wipedefindex = gamestate; // wipe_xxx_toblack
|
||||
}
|
||||
break;
|
||||
case GS_TITLESCREEN:
|
||||
if (!vr_draw_title)
|
||||
break;
|
||||
if (!titlemapinaction || !curbghide) {
|
||||
F_TitleScreenDrawer();
|
||||
break;
|
||||
}
|
||||
/* FALLTHRU */
|
||||
case GS_LEVEL:
|
||||
if (!gametic)
|
||||
break;
|
||||
AM_Drawer();
|
||||
break;
|
||||
|
||||
case GS_CUTSCENE:
|
||||
F_CutsceneDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_EVALUATION:
|
||||
F_GameEvaluationDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_CREDITS:
|
||||
F_CreditDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_BLANCREDITS:
|
||||
F_BlanCreditDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_SECRETCREDITS:
|
||||
F_SecretCreditsDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_WAITINGPLAYERS:
|
||||
// The clientconnect drawer is independent...
|
||||
if (netgame)
|
||||
{
|
||||
// I don't think HOM from nothing drawing is independent...
|
||||
F_WaitingPlayersDrawer();
|
||||
case GS_INTERMISSION:
|
||||
Y_IntermissionDrawer();
|
||||
HU_Drawer();
|
||||
}
|
||||
case GS_DEDICATEDSERVER:
|
||||
case GS_NULL:
|
||||
case FORCEWIPE:
|
||||
break;
|
||||
break;
|
||||
|
||||
case GS_VOTING:
|
||||
Y_VoteDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_TIMEATTACK:
|
||||
break;
|
||||
|
||||
case GS_INTRO:
|
||||
F_IntroDrawer();
|
||||
if (wipegamestate == (gamestate_t)-1)
|
||||
{
|
||||
wipe = true;
|
||||
wipedefindex = gamestate; // wipe_xxx_toblack
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_CUTSCENE:
|
||||
F_CutsceneDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_EVALUATION:
|
||||
F_GameEvaluationDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_CREDITS:
|
||||
F_CreditDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_BLANCREDITS:
|
||||
F_BlanCreditDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_SECRETCREDITS:
|
||||
F_SecretCreditsDrawer();
|
||||
HU_Drawer();
|
||||
break;
|
||||
|
||||
case GS_WAITINGPLAYERS:
|
||||
// The clientconnect drawer is independent...
|
||||
if (netgame)
|
||||
{
|
||||
// I don't think HOM from nothing drawing is independent...
|
||||
F_WaitingPlayersDrawer();
|
||||
HU_Drawer();
|
||||
}
|
||||
case GS_DEDICATEDSERVER:
|
||||
case GS_NULL:
|
||||
case FORCEWIPE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// STUPID race condition...
|
||||
|
|
@ -620,22 +699,31 @@ static void D_Display(void)
|
|||
|
||||
// clean up border stuff
|
||||
// see if the border needs to be initially drawn
|
||||
if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction && curbghide && (!hidetitlemap)))
|
||||
{
|
||||
|
||||
D_RenderLevel();
|
||||
|
||||
ps_uitime = I_GetPreciseTime();
|
||||
|
||||
if (gamestate == GS_LEVEL)
|
||||
if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction && curbghide && (!hidetitlemap)))
|
||||
{
|
||||
|
||||
if (vr_draw_3d)
|
||||
D_RenderLevel();
|
||||
|
||||
ps_uitime = I_GetPreciseTime();
|
||||
|
||||
#ifdef HAVE_VR
|
||||
if (vr_in_eye_ui)
|
||||
VR_BeginInEyeUI();
|
||||
#endif
|
||||
|
||||
if (vr_draw_ui && gamestate == GS_LEVEL)
|
||||
{
|
||||
ST_Drawer();
|
||||
F_TextPromptDrawer();
|
||||
HU_Drawer();
|
||||
}
|
||||
else if (vr_draw_ui)
|
||||
{
|
||||
if (vr_draw_title)
|
||||
F_TitleScreenDrawer();
|
||||
}
|
||||
}
|
||||
else
|
||||
F_TitleScreenDrawer();
|
||||
}
|
||||
else
|
||||
{
|
||||
ps_uitime = I_GetPreciseTime();
|
||||
|
|
@ -648,7 +736,7 @@ static void D_Display(void)
|
|||
V_SetPalette(0);
|
||||
|
||||
// draw pause pic
|
||||
if (paused && cv_showhud.value && !demo.playback)
|
||||
if (vr_draw_ui && paused && cv_showhud.value && !demo.playback)
|
||||
{
|
||||
INT32 py;
|
||||
patch_t *patch;
|
||||
|
|
@ -660,24 +748,45 @@ static void D_Display(void)
|
|||
V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - patch->width)/2, py, V_SNAPTOTOP, patch);
|
||||
}
|
||||
|
||||
if (demo.rewinding)
|
||||
if (vr_draw_ui && demo.rewinding)
|
||||
V_DrawFadeScreen(TC_RAINBOW, (leveltime & 0x20) ? SKINCOLOR_PASTEL : SKINCOLOR_MOONSLAM);
|
||||
|
||||
// vid size change is now finished if it was on...
|
||||
vid.recalc = false;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
I_lock_mutex(&m_menu_mutex);
|
||||
if (vr_draw_ui)
|
||||
{
|
||||
#ifdef HAVE_VR
|
||||
if (vr_in_eye_ui)
|
||||
VR_BeginInEyeUI();
|
||||
#endif
|
||||
M_Drawer(); // menu is drawn even on top of everything
|
||||
if (cv_songcredits.value && !( (G_GamestateUsesLevel() && hu_showscores) && (netgame || multiplayer) ))
|
||||
HU_DrawSongCredits(); // As are music credits.
|
||||
#ifdef HAVE_THREADS
|
||||
I_unlock_mutex(m_menu_mutex);
|
||||
#endif
|
||||
// focus lost moved to M_Drawer
|
||||
|
||||
CON_Drawer();
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started && vr_render_pass == VR_PASS_UI)
|
||||
{
|
||||
static boolean vr_logged_ui_pass = false;
|
||||
if (!vr_logged_ui_pass)
|
||||
{
|
||||
CONS_Printf("VR: UI pass active; menustack[0]=%d, gamestate=%d, vid=%dx%d dup=%d, ui=%ux%u.\n",
|
||||
(INT32)menustack[0], (INT32)gamestate, vid.width, vid.height, vid.dup, vr_ui_width, vr_ui_height);
|
||||
vr_logged_ui_pass = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
I_lock_mutex(&m_menu_mutex);
|
||||
#endif
|
||||
M_Drawer(); // menu is drawn even on top of everything
|
||||
if (cv_songcredits.value && !( (G_GamestateUsesLevel() && hu_showscores) && (netgame || multiplayer) ))
|
||||
HU_DrawSongCredits(); // As are music credits.
|
||||
#ifdef HAVE_THREADS
|
||||
I_unlock_mutex(m_menu_mutex);
|
||||
#endif
|
||||
// focus lost moved to M_Drawer
|
||||
|
||||
CON_Drawer();
|
||||
}
|
||||
|
||||
ps_uitime = I_GetPreciseTime() - ps_uitime;
|
||||
|
||||
|
|
@ -712,13 +821,16 @@ dedipostwipe:
|
|||
if (dedicated)
|
||||
return; // NOW we can bail
|
||||
|
||||
NetUpdate(); // send out any new accumulation
|
||||
|
||||
// It's safe to end the game now.
|
||||
if (G_GetExitGameFlag())
|
||||
if (vr_draw_ui)
|
||||
{
|
||||
Command_ExitGame_f();
|
||||
G_ClearExitGameFlag();
|
||||
NetUpdate(); // send out any new accumulation
|
||||
|
||||
// It's safe to end the game now.
|
||||
if (G_GetExitGameFlag())
|
||||
{
|
||||
Command_ExitGame_f();
|
||||
G_ClearExitGameFlag();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -726,10 +838,10 @@ dedipostwipe:
|
|||
//
|
||||
if (!wipe)
|
||||
{
|
||||
if (cv_shittyscreen.value)
|
||||
if (vr_draw_ui && cv_shittyscreen.value)
|
||||
V_DrawVhsEffect(cv_shittyscreen.value == 2);
|
||||
|
||||
if (cv_netstat.value)
|
||||
if (vr_draw_ui && cv_netstat.value)
|
||||
{
|
||||
char s[50];
|
||||
Net_GetNetStat();
|
||||
|
|
@ -746,7 +858,7 @@ dedipostwipe:
|
|||
V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-10, V_YELLOWMAP, s);
|
||||
}
|
||||
|
||||
if (cv_perfstats.value)
|
||||
if (vr_draw_ui && cv_perfstats.value)
|
||||
{
|
||||
M_DrawPerfStats();
|
||||
}
|
||||
|
|
@ -812,6 +924,15 @@ static double D_EndFrame(precise_t enterprecise, int *frameskip)
|
|||
double deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision();
|
||||
double deltatics = deltasecs * NEWTICRATE;
|
||||
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started)
|
||||
{
|
||||
if (frameskip)
|
||||
*frameskip = 0;
|
||||
return deltatics;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If time spent this game loop exceeds a single tic,
|
||||
// it's probably because of rendering.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1246,7 +1246,8 @@ boolean D_CheckNetGame(void)
|
|||
software_MAXPACKETLENGTH = hardware_MAXPACKETLENGTH;
|
||||
#else
|
||||
// MTU, IPV4, UDP header.
|
||||
software_MAXPACKETLENGTH = 1500 - 20 - 8;
|
||||
// 1500 - 20 - 8;
|
||||
software_MAXPACKETLENGTH = 1000;
|
||||
#endif
|
||||
if (M_CheckParm("-packetsize"))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ static void KartItemBreaker_OnChange(void);
|
|||
static void KartBumpSpark_OnChange(void);
|
||||
static void KartItemList_OnChange(void);
|
||||
static void KartTrailSlow_OnChange(void);
|
||||
static void PlayerBump_OnChange(void);
|
||||
|
||||
static void Schedule_OnChange(void);
|
||||
|
||||
|
|
@ -679,6 +680,11 @@ consvar_t cv_kartkeepstuff = CVAR_INIT ("kartkeepstuff", "No", CV_NETVAR|CV_CALL
|
|||
|
||||
consvar_t cv_trailslow = CVAR_INIT ("trailslow", "Yes", CV_NETVAR|CV_CALL|CV_NOINIT|CV_GUARD, CV_YesNo, KartTrailSlow_OnChange);
|
||||
|
||||
static CV_PossibleValue_t playerbump_cons_t[] = {{PBUMP_STANDARD, "Standard"},
|
||||
{PBUMP_GNDONLY, "Only on Ground"},
|
||||
{PBUMP_NONE, "None"},
|
||||
{0, NULL}};
|
||||
consvar_t cv_playerbump = CVAR_INIT ("playerbump", "Standard", CV_NETVAR|CV_CALL|CV_NOINIT|CV_GUARD, playerbump_cons_t, PlayerBump_OnChange);
|
||||
|
||||
#define ANTIBUMP_MAX (UINT32_MAX / TICRATE)
|
||||
static CV_PossibleValue_t antibump_cons_t[] = {{0, "MIN"}, {ANTIBUMP_MAX, "MAX"}, {0, NULL}};
|
||||
|
|
@ -728,7 +734,7 @@ consvar_t cv_kartbubble_boost_offroadignore = CVAR_INIT ("kartbubble_boost_offro
|
|||
|
||||
consvar_t cv_kartflame_fastfuel = CVAR_INIT ("kartflame_fastfuel", "On", CV_NETVAR, CV_OnOff, NULL);
|
||||
consvar_t cv_kartflame_offroadburn = CVAR_INIT ("kartflame_offroadburn", "On", CV_NETVAR, CV_OnOff, NULL);
|
||||
consvar_t cv_kartattraction_assistpower = CVAR_INIT ("kartattraction_assistpower", "0.5", CV_NETVAR|CV_FLOAT, CV_Unsigned, NULL);
|
||||
consvar_t cv_kartattraction_assistpower = CVAR_INIT ("kartattraction_assistpower", "0.2", CV_NETVAR|CV_FLOAT, CV_Unsigned, NULL);
|
||||
|
||||
// Attraction Shield damage toggles
|
||||
static CV_PossibleValue_t kartattractiondmg_cons_t[] = {{0, "None"}, {DMG_WIPEOUT, "Wipe-out"}, {DMG_FLIPOVER, "Flip-over"}, {DMG_EXPLODE, "Explode"}, {0, NULL}};
|
||||
|
|
@ -1506,6 +1512,19 @@ void D_RegisterClientCommands(void)
|
|||
CV_RegisterVar(&cv_scr_depth);
|
||||
CV_RegisterVar(&cv_scr_width);
|
||||
CV_RegisterVar(&cv_scr_height);
|
||||
CV_RegisterVar(&cv_vrviewmode);
|
||||
CV_RegisterVar(&cv_vrcomfortmode);
|
||||
CV_RegisterVar(&cv_vrenabled);
|
||||
CV_RegisterVar(&cv_vrresolution);
|
||||
CV_RegisterVar(&cv_vrscale);
|
||||
CV_RegisterVar(&cv_vruidistance);
|
||||
CV_RegisterVar(&cv_vruiscale);
|
||||
CV_RegisterVar(&cv_vruimode);
|
||||
CV_RegisterVar(&cv_vrposemode);
|
||||
CV_RegisterVar(&cv_vrplayerscale);
|
||||
CV_RegisterVar(&cv_vrspriterotate);
|
||||
CV_RegisterVar(&cv_vrdisableskystereo);
|
||||
CV_RegisterVar(&cv_vrtrackintro);
|
||||
CV_RegisterVar(&cv_parallelsoftware);
|
||||
|
||||
CV_RegisterVar(&cv_director);
|
||||
|
|
@ -2158,7 +2177,7 @@ static void SendNameAndColor(UINT8 n)
|
|||
// If you're not in a netgame, merely update the skin, color, and name.
|
||||
if (!netgame)
|
||||
{
|
||||
INT32 foundskin, voxid;
|
||||
INT32 foundskin;
|
||||
|
||||
CleanupPlayerName(playernum, cv_playername[n].zstring);
|
||||
strcpy(player_names[playernum], cv_playername[n].zstring);
|
||||
|
|
@ -9953,6 +9972,24 @@ static void KartTrailSlow_OnChange(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void PlayerBump_OnChange(void)
|
||||
{
|
||||
if (K_CanChangeRules(false) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (leveltime < starttime)
|
||||
{
|
||||
CONS_Printf(M_GetText("Player bump type has been changed to \"%s\".\n"), cv_playerbump.string);
|
||||
playerbumpactive = (playerbumptype_t)cv_playerbump.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Printf(M_GetText("Player bump type will be changed to \"%s\" next round.\n"), cv_playerbump.string);
|
||||
}
|
||||
}
|
||||
|
||||
static void Schedule_OnChange(void)
|
||||
{
|
||||
size_t i;
|
||||
|
|
|
|||
|
|
@ -264,6 +264,8 @@ extern consvar_t cv_kartantibump;
|
|||
|
||||
extern consvar_t cv_trailslow;
|
||||
|
||||
extern consvar_t cv_playerbump;
|
||||
|
||||
extern consvar_t cv_kartforcelegacyodds;
|
||||
|
||||
extern consvar_t cv_handleboostslip;
|
||||
|
|
|
|||
|
|
@ -101,34 +101,6 @@ INT32 flags; Bits = 3232 MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|M
|
|||
INT32 raisestate; Respawn frame = 32 S_NULL // raisestate
|
||||
}, */
|
||||
|
||||
#ifdef HWRENDER
|
||||
static INT32 searchvalue(const char *s)
|
||||
{
|
||||
while (s[0] != '=' && s[0])
|
||||
s++;
|
||||
if (s[0] == '=')
|
||||
return atoi(&s[1]);
|
||||
else
|
||||
{
|
||||
deh_warning("No value found");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static float searchfvalue(const char *s)
|
||||
{
|
||||
while (s[0] != '=' && s[0])
|
||||
s++;
|
||||
if (s[0] == '=')
|
||||
return (float)atof(&s[1]);
|
||||
else
|
||||
{
|
||||
deh_warning("No value found");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// These are for clearing all of various things
|
||||
void clear_emblems(void)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1919,5 +1919,10 @@ struct int_const_s const INT_CONST[] = {
|
|||
{"KEEPSTUFF_GROWSHRINK", KEEPSTUFF_GROWSHRINK},
|
||||
{"KEEPSTUFF_FLAME", KEEPSTUFF_FLAME},
|
||||
|
||||
// playerbumptype_t
|
||||
{"PBUMP_STANDARD", PBUMP_STANDARD},
|
||||
{"PBUMP_GNDONLY", PBUMP_GNDONLY},
|
||||
{"PBUMP_NONE", PBUMP_NONE},
|
||||
|
||||
{NULL,0}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ INT32 greasetics = 3*TICRATE;
|
|||
INT32 wipeoutslowtime = 20;
|
||||
INT32 wantedreduce = 5*TICRATE;
|
||||
INT32 wantedfrequency = 10*TICRATE;
|
||||
INT32 flametime = (((8*TICRATE)*3) + (4*TICRATE));
|
||||
INT32 flametime = 25*TICRATE + (2*TICRATE/10); // (((8*TICRATE)*3) + (4*TICRATE))
|
||||
|
||||
UINT8 use1upSound = 0;
|
||||
UINT8 maxXtraLife = 2; // Max extra lives from rings
|
||||
|
|
|
|||
10
src/g_game.h
10
src/g_game.h
|
|
@ -377,6 +377,16 @@ typedef struct
|
|||
} restatmessage_t;
|
||||
void G_HandleRestatMessage(restatmessage_t *rm);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PBUMP_STANDARD = 0,
|
||||
PBUMP_GNDONLY,
|
||||
PBUMP_NONE,
|
||||
NUMPLAYERBUMPTYPES
|
||||
} ATTRPACK playerbumptype_t;
|
||||
|
||||
extern playerbumptype_t playerbumpactive;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1298,6 +1298,8 @@ vector3_t G_GetCalibratedGyroOffset(INT32 p)
|
|||
|
||||
void G_UpdateGamepadAutoCalibration(INT32 p, vector3_t accel, vector3_t gyro, boolean allowautocalibration)
|
||||
{
|
||||
(void)gyro;
|
||||
|
||||
fixed_t trust = FV3_Distance(&localaccelcalibrationoffset[p], &accel);
|
||||
FV3_Load(
|
||||
&localaccelcalibrationoffset[p],
|
||||
|
|
@ -1569,4 +1571,4 @@ boolean G_GetGamepadCanUseTilt(INT32 p)
|
|||
|
||||
#undef GyroCalibrationTrust
|
||||
#undef GyroCalibrationStart
|
||||
#undef GyroCalibrationRollingAvgSamples
|
||||
#undef GyroCalibrationRollingAvgSamples
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2025 by haya3218.
|
||||
// Copyright (C) 2018-2024 by Kart Krew
|
||||
// Copyright (C) 2026 by Team Blankart.
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
|
|
@ -295,6 +296,12 @@ void K_DisplayItemTimers(void)
|
|||
{qche("K_TIRING")},
|
||||
1,
|
||||
},
|
||||
{ // bubbleshield
|
||||
"bubbleshield",
|
||||
stplyr->bubbleboost,
|
||||
{qche("K_TIBUBS")},
|
||||
1,
|
||||
},
|
||||
{ // flameshield
|
||||
"flameshield",
|
||||
stplyr->flametimer,
|
||||
|
|
|
|||
|
|
@ -78,6 +78,9 @@
|
|||
#include "../tables.h"
|
||||
#include "r_opengl/r_opengl.h"
|
||||
#include "../r_main.h" // for cv_fov
|
||||
#ifdef HAVE_VR
|
||||
#include "../vr/vr_main.h"
|
||||
#endif
|
||||
|
||||
typedef struct clipnode_s
|
||||
{
|
||||
|
|
@ -333,6 +336,11 @@ angle_t gld_FrustumAngle(angle_t tiltangle)
|
|||
|
||||
// ok, this is a gross hack that barely works...
|
||||
// but at least it doesn't overestimate too much...
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started)
|
||||
clipfov = 120.0;
|
||||
else
|
||||
#endif
|
||||
clipfov = atan(1 / (GLdouble)projMatrix[0]) * 360.0 / M_PI;
|
||||
floatangle = 2.0 + (45.0 + ((double)tilt / 1.9)) * clipfov / 90.0;
|
||||
if (floatangle >= 180.0)
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ void HWR_DrawAffinePatch(patch_t *gpatch, fixed_t x, fixed_t y, const affine_t *
|
|||
// i don't fucking know, i spent a day on this and got absolutely nowhere, but this guy knows:
|
||||
// https://nigeltao.github.io/blog/2021/inverting-3x2-affine-transformation-matrix.html
|
||||
float determinant = fa * fd - fb * fc;
|
||||
if (determinant == 0.0f)
|
||||
if (fpclassify(determinant) == FP_ZERO)
|
||||
return;
|
||||
float ba = fd / determinant;
|
||||
float bb = -fb / determinant;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@
|
|||
#include "../p_slopes.h"
|
||||
#include "hw_md2.h"
|
||||
|
||||
#ifdef HAVE_VR
|
||||
#include "../screen.h"
|
||||
#include "../vr/vr_main.h"
|
||||
#endif
|
||||
|
||||
// SRB2Kart
|
||||
#include "../k_kart.h"
|
||||
#include "../r_fps.h"
|
||||
|
|
@ -522,6 +527,18 @@ void HWR_RenderViewpoint(player_t *player, boolean drawSkyTexture, boolean is_sk
|
|||
// since our clipper uses this to determine our actual visible geometry
|
||||
GL_SetTransform(&atransform);
|
||||
HWR_ClearSprites();
|
||||
|
||||
#ifdef HAVE_VR
|
||||
// Let sprite billboards and clipping follow the user's HMD yaw in VR.
|
||||
if (vr_started && cv_vrspriterotate.value)
|
||||
{
|
||||
if (cv_kartencore.value)
|
||||
viewangle += ANGLE_MAX * atan2(vrHMDPoseMatrix[8], vrHMDPoseMatrix[0]) / M_PI * 0.5;
|
||||
else
|
||||
viewangle -= ANGLE_MAX * atan2(vrHMDPoseMatrix[8], vrHMDPoseMatrix[0]) / M_PI * 0.5;
|
||||
}
|
||||
#endif
|
||||
|
||||
HWR_ResetClipper();
|
||||
|
||||
if (rootportal)
|
||||
|
|
@ -560,7 +577,11 @@ void HWR_RenderViewpoint(player_t *player, boolean drawSkyTexture, boolean is_sk
|
|||
// HWR_DrawSkyBackground is not able to set the texture without
|
||||
// pausing batching first
|
||||
HWR_PauseBatching();
|
||||
if (skyboxmo[0] && cv_skybox.value && !is_skybox && !rootportal && !gl_debugportal)
|
||||
if (skyboxmo[0] && cv_skybox.value && !is_skybox && !rootportal && !gl_debugportal
|
||||
#ifdef HAVE_VR
|
||||
&& (!vr_started || !cv_vrcomfortmode.value)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
//if (gl_printportals)
|
||||
// CONS_Printf("drawing a skybox\n");
|
||||
|
|
@ -701,11 +722,33 @@ void HWR_RollTransform(FTransform *tr, angle_t roll)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_VR
|
||||
static inline boolean HWR_VRRenderingEye(void)
|
||||
{
|
||||
return (vr_started && !vr_drawing_ui &&
|
||||
(vr_render_pass == VR_PASS_3D_LEFT || vr_render_pass == VR_PASS_3D_RIGHT));
|
||||
}
|
||||
#endif
|
||||
|
||||
// -----------------+
|
||||
// HWR_ClearView : clear the viewwindow, with maximum z value. also clears stencil buffer.
|
||||
// -----------------+
|
||||
static inline void HWR_ClearView(void)
|
||||
{
|
||||
#ifdef HAVE_VR
|
||||
if (HWR_VRRenderingEye())
|
||||
{
|
||||
GL_GClipRect(0,
|
||||
0,
|
||||
(INT32)vr_render_width,
|
||||
(INT32)vr_render_height,
|
||||
ZCLIP_PLANE);
|
||||
|
||||
GL_ClearBuffer(false, true, true, NULL);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
GL_GClipRect(viewwindowx,
|
||||
viewwindowy,
|
||||
(viewwindowx + viewwidth),
|
||||
|
|
@ -719,7 +762,11 @@ void HWR_RenderPlayerView(void)
|
|||
{
|
||||
player_t * player = &players[displayplayers[viewssnum]];
|
||||
|
||||
const boolean skybox = (skyboxmo[0] && cv_skybox.value); // True if there's a skybox object and skyboxes are on
|
||||
const boolean skybox = (skyboxmo[0] && cv_skybox.value
|
||||
#ifdef HAVE_VR
|
||||
&& (!vr_started || !cv_vrcomfortmode.value)
|
||||
#endif
|
||||
); // True if there's a skybox object and skyboxes are on
|
||||
|
||||
FRGBAFloat ClearColor;
|
||||
|
||||
|
|
@ -744,6 +791,14 @@ void HWR_RenderPlayerView(void)
|
|||
if (viewssnum == 0) // Only do it if it's the first screen being rendered
|
||||
GL_ClearBuffer(true, true, true, &ClearColor); // Clear the Color Buffer, stops HOMs. Also seems to fix the skybox issue on Intel GPUs.
|
||||
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started)
|
||||
{
|
||||
const float player_scale = (cv_vrplayerscale.value && player->mo) ? FIXED_TO_FLOAT(player->mo->scale) : 1.0f;
|
||||
VR_ScaleViewMatrices(player_scale, cv_vrdisableskystereo.value ? 0 : 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
ps_hw_skyboxtime = I_GetPreciseTime();
|
||||
if (skybox) // If there's a skybox and we should be drawing the sky, draw the skybox
|
||||
{
|
||||
|
|
@ -798,6 +853,11 @@ void HWR_RenderPlayerView(void)
|
|||
|
||||
// added by Hurdler for correct splitscreen
|
||||
// moved here by hurdler so it works with the new near clipping plane
|
||||
#ifdef HAVE_VR
|
||||
if (HWR_VRRenderingEye())
|
||||
GL_GClipRect(0, 0, (INT32)vr_render_width, (INT32)vr_render_height, NZCLIP_PLANE);
|
||||
else
|
||||
#endif
|
||||
GL_GClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@
|
|||
#include "hw_gpu.h"
|
||||
#include "hw_shaders.h"
|
||||
#include "../z_zone.h"
|
||||
#ifdef HAVE_VR
|
||||
#include "../vr/vr_main.h"
|
||||
#endif
|
||||
|
||||
// ================
|
||||
// Shader sources
|
||||
|
|
@ -361,7 +364,17 @@ static void HWR_CompileShader(int index)
|
|||
|
||||
if (vertex_source)
|
||||
{
|
||||
char *preprocessed = HWR_PreprocessShader(vertex_source);
|
||||
char *source_to_preprocess = vertex_source;
|
||||
#ifdef HAVE_VR
|
||||
const int shader_target = index % NUMSHADERTARGETS;
|
||||
if (vr_started && shader_target >= SHADER_FLOOR && shader_target <= SHADER_SKY)
|
||||
source_to_preprocess = Z_StrDup(GLSL_VR_VERTEX_SHADER);
|
||||
#endif
|
||||
char *preprocessed = HWR_PreprocessShader(source_to_preprocess);
|
||||
#ifdef HAVE_VR
|
||||
if (source_to_preprocess != vertex_source)
|
||||
Z_Free(source_to_preprocess);
|
||||
#endif
|
||||
if (!preprocessed) return;
|
||||
GL_LoadShader(index, preprocessed, HWD_SHADERSTAGE_VERTEX);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,35 @@
|
|||
//
|
||||
|
||||
#define GLSL_FALLBACK_VERTEX_SHADER \
|
||||
"uniform mat4 vrEyeMatrix;\n" \
|
||||
"uniform mat4 vrEyeProjection;\n" \
|
||||
"uniform mat4 vrHeadPoseMatrix;\n" \
|
||||
"void main()\n" \
|
||||
"{\n" \
|
||||
"gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
|
||||
"gl_FrontColor = gl_Color;\n" \
|
||||
"gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \
|
||||
"gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \
|
||||
"\tif (vrEyeProjection[0][0] != 0.0)\n" \
|
||||
"\t{\n" \
|
||||
"\t\tgl_Position = vrEyeProjection * vrEyeMatrix * vrHeadPoseMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
|
||||
"\t\tgl_ClipVertex = vrEyeMatrix * vrHeadPoseMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
|
||||
"\t}\n" \
|
||||
"\telse\n" \
|
||||
"\t{\n" \
|
||||
"\t\tgl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
|
||||
"\t\tgl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \
|
||||
"\t}\n" \
|
||||
"\tgl_FrontColor = gl_Color;\n" \
|
||||
"\tgl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \
|
||||
"}\0"
|
||||
|
||||
#define GLSL_VR_VERTEX_SHADER \
|
||||
"uniform mat4 vrEyeMatrix;\n" \
|
||||
"uniform mat4 vrEyeProjection;\n" \
|
||||
"uniform mat4 vrHeadPoseMatrix;\n" \
|
||||
"void main()\n" \
|
||||
"{\n" \
|
||||
"\tgl_Position = vrEyeProjection * vrEyeMatrix * vrHeadPoseMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
|
||||
"\tgl_ClipVertex = vrEyeMatrix * vrHeadPoseMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
|
||||
"\tgl_FrontColor = gl_Color;\n" \
|
||||
"\tgl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \
|
||||
"}\0"
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
#include "../g_game.h"
|
||||
#include "../r_fps.h"
|
||||
|
||||
#ifdef HAVE_VR
|
||||
#include "../screen.h"
|
||||
#include "../vr/vr_main.h"
|
||||
#endif
|
||||
|
||||
|
||||
// ==========================================================================
|
||||
// Sky dome rendering, ported from PrBoom+
|
||||
|
|
@ -163,6 +168,11 @@ void HWR_BuildSkyDome(void)
|
|||
|
||||
void HWR_DrawSkyBackground(player_t *player)
|
||||
{
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started && cv_vrcomfortmode.value)
|
||||
return;
|
||||
#endif
|
||||
|
||||
GL_SetBlend(PF_Translucent|PF_NoDepthTest|PF_Modulated);
|
||||
|
||||
if (cv_glskydome.value)
|
||||
|
|
|
|||
|
|
@ -1781,7 +1781,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
|
|||
float base_topoffs = FIXED_TO_FLOAT(spr_topoffset);
|
||||
|
||||
#ifdef ROTSPRITE
|
||||
spriterotangle = R_SpriteRotationAngle(thing, NULL, &interp, false);
|
||||
spriterotangle = R_SpriteRotationAngle(thing, NULL, &interp, (affinesprite && vflip));
|
||||
|
||||
if (spriterotangle != 0
|
||||
&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE))
|
||||
|
|
|
|||
|
|
@ -28,6 +28,12 @@
|
|||
#include "../hw_shaders.h"
|
||||
#include "../hw_gpu.h"
|
||||
|
||||
#ifdef HAVE_VR
|
||||
#include "../../vr/vr_main.h"
|
||||
#include "../../screen.h"
|
||||
extern boolean gl_rendering_skybox;
|
||||
#endif
|
||||
|
||||
#if defined (HWRENDER) && !defined (NOROPENGL)
|
||||
|
||||
// requires GL 4.3
|
||||
|
|
@ -603,6 +609,7 @@ typedef void (APIENTRY *PFNglUniform4f) (GLint, GLfloat, GLfloat, GLfloat, GL
|
|||
typedef void (APIENTRY *PFNglUniform1fv) (GLint, GLsizei, const GLfloat*);
|
||||
typedef void (APIENTRY *PFNglUniform2fv) (GLint, GLsizei, const GLfloat*);
|
||||
typedef void (APIENTRY *PFNglUniform3fv) (GLint, GLsizei, const GLfloat*);
|
||||
typedef void (APIENTRY *PFNglUniformMatrix4fv) (GLint, GLsizei, GLboolean, const GLfloat*);
|
||||
typedef GLint (APIENTRY *PFNglGetUniformLocation) (GLuint, const GLchar*);
|
||||
|
||||
static PFNglCreateShader pglCreateShader;
|
||||
|
|
@ -625,6 +632,7 @@ static PFNglUniform4f pglUniform4f;
|
|||
static PFNglUniform1fv pglUniform1fv;
|
||||
static PFNglUniform2fv pglUniform2fv;
|
||||
static PFNglUniform3fv pglUniform3fv;
|
||||
static PFNglUniformMatrix4fv pglUniformMatrix4fv;
|
||||
static PFNglGetUniformLocation pglGetUniformLocation;
|
||||
|
||||
// 13062019
|
||||
|
|
@ -659,6 +667,12 @@ typedef enum
|
|||
|
||||
gluniform_scr_resolution,
|
||||
|
||||
#ifdef HAVE_VR
|
||||
gluniform_evm, // Eye View Matrix
|
||||
gluniform_epm, // Eye Projection Matrix
|
||||
gluniform_hpm, // Head Pose Matrix
|
||||
#endif
|
||||
|
||||
gluniform_max,
|
||||
} gluniform_t;
|
||||
|
||||
|
|
@ -740,6 +754,7 @@ void SetupGLFunc4(void)
|
|||
*(void**)&pglUniform1fv = GetGLFunc("glUniform1fv");
|
||||
*(void**)&pglUniform2fv = GetGLFunc("glUniform2fv");
|
||||
*(void**)&pglUniform3fv = GetGLFunc("glUniform3fv");
|
||||
*(void**)&pglUniformMatrix4fv = GetGLFunc("glUniformMatrix4fv");
|
||||
*(void**)&pglGetUniformLocation = GetGLFunc("glGetUniformLocation");
|
||||
#endif
|
||||
|
||||
|
|
@ -1389,7 +1404,10 @@ void GL_ClearBuffer(FBOOLEAN ColorMask,
|
|||
if (StencilMask)
|
||||
ClearMask |= GL_STENCIL_BUFFER_BIT;
|
||||
|
||||
GL_SetBlend(DepthMask ? PF_Occlude | CurrentPolyFlags : CurrentPolyFlags&~PF_Occlude);
|
||||
// A depth clear begins a fresh 3D pass. Do not preserve HUD/UI state such
|
||||
// as PF_NoDepthTest or translucent blending here, or later transparent
|
||||
// materials can draw through opaque world geometry in VR multi-pass frames.
|
||||
GL_SetBlend(DepthMask ? PF_Occlude : CurrentPolyFlags&~PF_Occlude);
|
||||
|
||||
pglClear(ClearMask);
|
||||
pglEnableClientState(GL_VERTEX_ARRAY); // We always use this one
|
||||
|
|
@ -1944,6 +1962,50 @@ static void Shader_SetUniforms(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAF
|
|||
|
||||
UNIFORM_1(shader->uniforms[gluniform_leveltime], shader_leveltime, pglUniform1f);
|
||||
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started)
|
||||
{
|
||||
static const GLfloat identityMatrix[16] = {
|
||||
1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
};
|
||||
static const GLfloat zeroMatrix[16] = {
|
||||
0.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 0.0f
|
||||
};
|
||||
const GLfloat *eyeviewmatrix = identityMatrix;
|
||||
const GLfloat *headposematrix = identityMatrix;
|
||||
const GLfloat *projectionmatrix = zeroMatrix;
|
||||
|
||||
if (!vr_drawing_ui && (vr_render_pass == VR_PASS_3D_LEFT || vr_render_pass == VR_PASS_3D_RIGHT))
|
||||
{
|
||||
if (gl_rendering_skybox)
|
||||
{
|
||||
eyeviewmatrix = vrEyeSkyboxViewMatrix[vr_current_eye];
|
||||
headposematrix = (cv_vrposemode.value == 1 || cv_vrposemode.value == 3) ? vrHMDPoseSkyboxMatrix : identityMatrix;
|
||||
}
|
||||
else
|
||||
{
|
||||
eyeviewmatrix = vrScaledEyeViewMatrix[vr_current_eye];
|
||||
headposematrix = (cv_vrposemode.value == 1 || cv_vrposemode.value == 3) ? vrHMDScaledPoseMatrix : identityMatrix;
|
||||
}
|
||||
|
||||
projectionmatrix = vrEyeProjMatrix[vr_current_eye];
|
||||
}
|
||||
|
||||
if (shader->uniforms[gluniform_evm] != -1)
|
||||
pglUniformMatrix4fv(shader->uniforms[gluniform_evm], 1, GL_FALSE, eyeviewmatrix);
|
||||
if (shader->uniforms[gluniform_epm] != -1)
|
||||
pglUniformMatrix4fv(shader->uniforms[gluniform_epm], 1, GL_FALSE, projectionmatrix);
|
||||
if (shader->uniforms[gluniform_hpm] != -1)
|
||||
pglUniformMatrix4fv(shader->uniforms[gluniform_hpm], 1, GL_FALSE, headposematrix);
|
||||
}
|
||||
#endif
|
||||
|
||||
UNIFORM_2(shader->uniforms[gluniform_scr_resolution], (GLfloat)vid.width, (GLfloat)vid.height, pglUniform2f);
|
||||
|
||||
#undef UNIFORM_1
|
||||
|
|
@ -2082,6 +2144,12 @@ static boolean Shader_CompileProgram(gl_shader_t *shader, GLint i)
|
|||
// misc. (custom shaders)
|
||||
shader->uniforms[gluniform_leveltime] = GETUNI("leveltime");
|
||||
|
||||
#ifdef HAVE_VR
|
||||
shader->uniforms[gluniform_evm] = GETUNI("vrEyeMatrix");
|
||||
shader->uniforms[gluniform_epm] = GETUNI("vrEyeProjection");
|
||||
shader->uniforms[gluniform_hpm] = GETUNI("vrHeadPoseMatrix");
|
||||
#endif
|
||||
|
||||
#undef GETUNI
|
||||
|
||||
// set permanent uniform values
|
||||
|
|
|
|||
40
src/i_time.c
40
src/i_time.c
|
|
@ -118,43 +118,3 @@ void I_UpdateTime(fixed_t timescale)
|
|||
g_time.timefrac = FLOAT_TO_FIXED(fractional);
|
||||
}
|
||||
}
|
||||
|
||||
void I_SleepDuration(precise_t duration)
|
||||
{
|
||||
UINT64 precision = I_GetPrecisePrecision();
|
||||
INT32 sleepvalue = cv_sleep.value;
|
||||
UINT64 delaygranularity;
|
||||
precise_t cur;
|
||||
precise_t dest;
|
||||
|
||||
{
|
||||
double gran = round(((double)(precision / 1000) * sleepvalue * MIN_SLEEP_DURATION_MS));
|
||||
delaygranularity = (UINT64)gran;
|
||||
}
|
||||
|
||||
cur = I_GetPreciseTime();
|
||||
dest = cur + duration;
|
||||
|
||||
// the reason this is not dest > cur is because the precise counter may wrap
|
||||
// two's complement arithmetic is our friend here, though!
|
||||
// e.g. cur 0xFFFFFFFFFFFFFFFE = -2, dest 0x0000000000000001 = 1
|
||||
// 0x0000000000000001 - 0xFFFFFFFFFFFFFFFE = 3
|
||||
while ((INT64)(dest - cur) > 0)
|
||||
{
|
||||
// If our cv_sleep value exceeds the remaining sleep duration, use the
|
||||
// hard sleep function.
|
||||
if (sleepvalue > 0 && (dest - cur) > delaygranularity)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
DWORD sleepDuration = (DWORD)min((INT64)(dest - cur), sleepvalue);
|
||||
SleepEx(sleepDuration, TRUE);
|
||||
#else
|
||||
I_Sleep(sleepvalue);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Otherwise, this is a spinloop.
|
||||
|
||||
cur = I_GetPreciseTime();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -817,7 +817,7 @@ static void K_BubbleShieldCollideDrain(player_t *player, mobj_t *bubble, INT16 d
|
|||
player->bubbleblowup /= 2;
|
||||
}
|
||||
|
||||
boolean K_BubbleShieldReflectSpecialCase(mobj_t *t)
|
||||
boolean K_BubbleShieldReflectSpecialCase(const mobj_t *t)
|
||||
{
|
||||
if ((!t) || (P_MobjWasRemoved(t)))
|
||||
return false; // Object doesn't even exist
|
||||
|
|
@ -1108,13 +1108,11 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
|
|||
{
|
||||
if (t1Condition == true && t2Condition == false)
|
||||
{
|
||||
P_DamageMobj(t2, t1, t1, 1, invindamage);
|
||||
return true;
|
||||
return P_DamageMobj(t2, t1, t1, 1, invindamage);
|
||||
}
|
||||
else if (t1Condition == false && t2Condition == true)
|
||||
{
|
||||
P_DamageMobj(t1, t2, t2, 1, invindamage);
|
||||
return true;
|
||||
return P_DamageMobj(t1, t2, t2, 1, invindamage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1168,19 +1166,17 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
|
|||
}
|
||||
if (P_IsObjectOnGround(t1) && P_IsObjectOnGround(t2))
|
||||
{
|
||||
P_DamageMobj(t2, t1, t1, 1, DMG_SQUISH);
|
||||
return P_DamageMobj(t2, t1, t1, 1, DMG_SQUISH);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (cv_kartairsquish.value)
|
||||
{
|
||||
case 1:
|
||||
P_DamageMobj(t2, t1, t1, 1, DMG_SQUISH);
|
||||
return true;
|
||||
return P_DamageMobj(t2, t1, t1, 1, DMG_SQUISH);
|
||||
break;
|
||||
case 2:
|
||||
P_DamageMobj(t2, t1, t1, 1, DMG_FLIPOVER);
|
||||
return true;
|
||||
return P_DamageMobj(t2, t1, t1, 1, DMG_FLIPOVER);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
|
|
@ -1213,27 +1209,24 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
|
|||
}
|
||||
else if (P_IsObjectOnGround(t1) && P_IsObjectOnGround(t2))
|
||||
{
|
||||
P_DamageMobj(t1, t2, t2, 1, DMG_SQUISH);
|
||||
return P_DamageMobj(t1, t2, t2, 1, DMG_SQUISH);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (cv_kartairsquish.value)
|
||||
{
|
||||
case 1:
|
||||
P_DamageMobj(t1, t2, t2, 1, DMG_SQUISH);
|
||||
return true;
|
||||
return P_DamageMobj(t1, t2, t2, 1, DMG_SQUISH);
|
||||
break;
|
||||
case 2:
|
||||
P_DamageMobj(t1, t2, t2, 1, DMG_FLIPOVER);
|
||||
return true;
|
||||
return P_DamageMobj(t1, t2, t2, 1, DMG_FLIPOVER);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Flame Shield dash damage
|
||||
|
|
@ -1242,13 +1235,11 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
|
|||
|
||||
if (t1Condition == true && t2Condition == false)
|
||||
{
|
||||
P_DamageMobj(t2, t1, t1, 1, DMG_FLIPOVER);
|
||||
return true;
|
||||
return P_DamageMobj(t2, t1, t1, 1, DMG_FLIPOVER);
|
||||
}
|
||||
else if (t1Condition == false && t2Condition == true)
|
||||
{
|
||||
P_DamageMobj(t1, t2, t2, 1, DMG_FLIPOVER);
|
||||
return true;
|
||||
return P_DamageMobj(t1, t2, t2, 1, DMG_FLIPOVER);
|
||||
}
|
||||
|
||||
// Attraction Shield tackle damage
|
||||
|
|
@ -1257,13 +1248,21 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
|
|||
t2Condition = (t2->player->attractionattack && t2->player->attractionattack_hipower && (K_GetShieldFromPlayer(t1->player) != KSHIELD_BUBBLE));
|
||||
if (t1Condition == true && t2Condition == false)
|
||||
{
|
||||
P_DamageMobj(t2, t1, t1, 1, DMG_FLIPOVER);
|
||||
return true;
|
||||
UINT8 damage = DMG_FLIPOVER;
|
||||
|
||||
if (gametypes[gametype]->rules & GTR_BUMPERS)
|
||||
damage |= DMG_STEAL;
|
||||
|
||||
return P_DamageMobj(t2, t1, t1, 1, damage);
|
||||
}
|
||||
else if (t1Condition == false && t2Condition == true)
|
||||
{
|
||||
P_DamageMobj(t1, t2, t2, 1, DMG_FLIPOVER);
|
||||
return true;
|
||||
UINT8 damage = DMG_FLIPOVER;
|
||||
|
||||
if (gametypes[gametype]->rules & GTR_BUMPERS)
|
||||
damage |= DMG_STEAL;
|
||||
|
||||
return P_DamageMobj(t1, t2, t2, 1, damage);
|
||||
}
|
||||
|
||||
// Battle Mode Sneaker and Bubble damage
|
||||
|
|
@ -1281,13 +1280,11 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
|
|||
|
||||
if (t1Condition == true && t2Condition == false)
|
||||
{
|
||||
P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT|DMG_STEAL);
|
||||
return true;
|
||||
return P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT|DMG_STEAL);
|
||||
}
|
||||
else if (t1Condition == false && t2Condition == true)
|
||||
{
|
||||
P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT|DMG_STEAL);
|
||||
return true;
|
||||
return P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT|DMG_STEAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ void K_AttractionShieldAttack(mobj_t *actor, fixed_t size, boolean hipower);
|
|||
|
||||
boolean K_BubbleShieldReflect(mobj_t *t1, mobj_t *t2);
|
||||
boolean K_BubbleShieldCanReflect(mobj_t *t);
|
||||
boolean K_BubbleShieldReflectSpecialCase(mobj_t *t);
|
||||
boolean K_BubbleShieldReflectSpecialCase(const mobj_t *t);
|
||||
|
||||
boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2);
|
||||
boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2);
|
||||
|
|
|
|||
351
src/k_hud.c
351
src/k_hud.c
|
|
@ -88,6 +88,7 @@ static CV_PossibleValue_t speedo_cons_t[]= {
|
|||
{0, "Default"},
|
||||
{1, "Small"},
|
||||
{2, "P-Meter"},
|
||||
{3, "Dial"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
|
|
@ -115,6 +116,9 @@ consvar_t cv_draftindicator = CVAR_INIT ("draftindicator", "On", CV_SAVE, CV_OnO
|
|||
|
||||
consvar_t cv_showstats = CVAR_INIT ("showstats", "On", CV_SAVE, CV_OnOff, NULL);
|
||||
|
||||
// make char potraits use their high-res version instead
|
||||
consvar_t cv_highresportrait = CVAR_INIT ("highresportrait", "Off", CV_SAVE, CV_OnOff, NULL);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NT_OFF = 0,
|
||||
|
|
@ -239,6 +243,13 @@ patch_t *kp_itemtarget_far[2][2];
|
|||
patch_t *kp_itemtarget_far_text[2];
|
||||
patch_t *kp_itemtarget_near[2][8];
|
||||
|
||||
// dial speedometer
|
||||
static patch_t *kp_dial = NULL;
|
||||
static patch_t *kp_dialfinish = NULL;
|
||||
static patch_t *kp_dialbase[4] = {NULL};
|
||||
static patch_t *kp_speedpatchesdial[8] = {NULL};
|
||||
static patch_t *kp_dialnum[20] = {NULL};
|
||||
|
||||
void K_RegisterKartHUDStuff(void)
|
||||
{
|
||||
K_ReloadHUDColorCvar();
|
||||
|
|
@ -292,6 +303,7 @@ void K_RegisterKartHUDStuff(void)
|
|||
CV_RegisterVar(&cv_newtabranking);
|
||||
CV_RegisterVar(&cv_draftindicator);
|
||||
CV_RegisterVar(&cv_showstats);
|
||||
CV_RegisterVar(&cv_highresportrait);
|
||||
|
||||
// k_items.c
|
||||
CV_RegisterVar(&cv_fancyroulette);
|
||||
|
|
@ -432,6 +444,41 @@ void K_LoadKartHUDGraphics(void)
|
|||
// Speedometer Sticker Color
|
||||
HU_UpdatePatch(&kp_speedometersticker[1], "SC_SMSTC");
|
||||
|
||||
// Dial speedometer
|
||||
HU_UpdatePatch(&kp_dial, "K_DSDIAL");
|
||||
|
||||
kp_dial->pivot.x = kp_dial->width / 2;
|
||||
kp_dial->pivot.y = kp_dial->height / 2;
|
||||
kp_dial->alignflags |= PATCHALIGN_USEPIVOTS;
|
||||
|
||||
HU_UpdatePatch(&kp_dialfinish, "K_DILFIN");
|
||||
HU_UpdatePatch(&kp_dialbase[0], "K_DSPBS1");
|
||||
HU_UpdatePatch(&kp_dialbase[1], "K_DSPBS2");
|
||||
HU_UpdatePatch(&kp_dialbase[2], "K_DSPBC1");
|
||||
HU_UpdatePatch(&kp_dialbase[3], "K_DSPBC2");
|
||||
HU_UpdatePatch(&kp_speedpatchesdial[0], "SP_DKMH");
|
||||
HU_UpdatePatch(&kp_speedpatchesdial[1], "SP_DMPH");
|
||||
HU_UpdatePatch(&kp_speedpatchesdial[2], "SP_DFRAC");
|
||||
HU_UpdatePatch(&kp_speedpatchesdial[3], "SP_DPERC");
|
||||
HU_UpdatePatch(&kp_speedpatchesdial[4], "SC_DKMH");
|
||||
HU_UpdatePatch(&kp_speedpatchesdial[5], "SC_DMPH");
|
||||
HU_UpdatePatch(&kp_speedpatchesdial[6], "SC_DFRAC");
|
||||
HU_UpdatePatch(&kp_speedpatchesdial[7], "SC_DPERC");
|
||||
|
||||
sprintf(buffer, "K_DSPNMx");
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
buffer[7] = '0'+(i%10);
|
||||
HU_UpdatePatch(&kp_dialnum[i], "%s", buffer);
|
||||
}
|
||||
|
||||
sprintf(buffer, "K_DSPNCx");
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
buffer[7] = '0'+(i%10);
|
||||
HU_UpdatePatch(&kp_dialnum[i+10], "%s", buffer);
|
||||
}
|
||||
|
||||
// Driftgauge Stickers
|
||||
HU_UpdatePatch(&kp_driftgauge[0], "K_DGAU0"); // Spee
|
||||
HU_UpdatePatch(&kp_driftgauge[1], "K_DGAU1"); // Achii
|
||||
|
|
@ -628,7 +675,7 @@ void K_LoadKartHUDGraphics(void)
|
|||
HU_UpdatePatch(&joyshadow, "JOYSHD");
|
||||
|
||||
// Input UI Wheel
|
||||
HU_UpdatePatch(&kp_inputwheel, "K_WHEEL0");
|
||||
HU_UpdatePatch(&kp_inputwheel, "B_WHEEL0");
|
||||
kp_inputwheel->pivot.x =
|
||||
(kp_inputwheel->width / 2) + kp_inputwheel->leftoffset;
|
||||
kp_inputwheel->pivot.y =
|
||||
|
|
@ -636,7 +683,7 @@ void K_LoadKartHUDGraphics(void)
|
|||
kp_inputwheel->alignflags |=
|
||||
(PATCHALIGN_AUTOCENTER | PATCHALIGN_USEPIVOTS);
|
||||
|
||||
HU_UpdatePatch(&kp_inputwheel_shadow, "K_WHEEL1");
|
||||
HU_UpdatePatch(&kp_inputwheel_shadow, "B_WHEEL1");
|
||||
kp_inputwheel_shadow->pivot.x =
|
||||
(kp_inputwheel_shadow->width / 2) + kp_inputwheel_shadow->leftoffset;
|
||||
kp_inputwheel_shadow->pivot.y =
|
||||
|
|
@ -818,6 +865,16 @@ skincolornum_t K_GetHudColor(void)
|
|||
return ((stplyr && gamestate == GS_LEVEL) ? stplyr->skincolor : cv_playercolor[0].value);
|
||||
}
|
||||
|
||||
static boolean K_IsHighResolution(void)
|
||||
{
|
||||
return (vid.width >= 640 && vid.height >= 400);
|
||||
}
|
||||
|
||||
boolean K_UseHighResPortraits(void)
|
||||
{
|
||||
return (cv_highresportrait.value && K_IsHighResolution());
|
||||
}
|
||||
|
||||
static boolean K_BigLapSticker(void)
|
||||
{
|
||||
return ((cv_numlaps.value > 9) && (!stplyr->exiting));
|
||||
|
|
@ -1308,11 +1365,13 @@ void K_getRingsDrawinfo(drawinfo_t *out)
|
|||
{
|
||||
INT32 fx, fy, splitflags = 0;
|
||||
|
||||
boolean dialspeedometer = (cv_newspeedometer.value == 3 && r_splitscreen < 1);
|
||||
|
||||
// pain and suffering defined below
|
||||
if (r_splitscreen < 2) // don't change shit for THIS splitscreen.
|
||||
{
|
||||
fx = RING_X;
|
||||
fy = RING_Y;
|
||||
fx = RING_X + ((dialspeedometer) ? 31 : 0);
|
||||
fy = RING_Y + ((dialspeedometer) ? 14 : 0);
|
||||
splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_HUDTRANS|V_SPLITSCREEN;
|
||||
}
|
||||
else
|
||||
|
|
@ -2295,7 +2354,9 @@ static boolean K_drawKartPositionFaces(void)
|
|||
else
|
||||
colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE);
|
||||
|
||||
patch_t *facerank = faceprefix[players[rankplayer[i]].skin][FACE_RANK];
|
||||
boolean hires = K_UseHighResPortraits();
|
||||
|
||||
patch_t *facerank = faceprefix[players[rankplayer[i]].skin][hires ? FACE_WANTED : FACE_RANK];
|
||||
|
||||
offsets.x = facerank->leftoffset;
|
||||
offsets.y = facerank->topoffset;
|
||||
|
|
@ -2314,14 +2375,14 @@ static boolean K_drawKartPositionFaces(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
V_DrawMappedPatch(FACE_X + offsets.x, Y + offsets.y, V_HUDTRANS|V_SNAPTOLEFT, facerank, colormap);
|
||||
V_DrawFixedPatch((FACE_X + offsets.x)<<FRACBITS, (Y + offsets.y)<<FRACBITS, hires ? FRACUNIT / 2 : FRACUNIT, V_HUDTRANS|V_SNAPTOLEFT, facerank, colormap);
|
||||
|
||||
if (players[rankplayer[i]].smonitortimer)
|
||||
{
|
||||
colormap = R_GetTranslationColormap(TC_BLINK, K_SMonitorColor(leveltime / 2), GTC_CACHE);
|
||||
smntrhudtrans = K_SMonitorHUDVisibility(players[rankplayer[i]].smonitortimer);
|
||||
|
||||
V_DrawMappedPatch(FACE_X + offsets.x, Y + offsets.y, smntrhudtrans|V_SNAPTOLEFT|V_ADD, facerank, colormap);
|
||||
V_DrawFixedPatch((FACE_X + offsets.x)<<FRACBITS, (Y + offsets.y)<<FRACBITS, hires ? FRACUNIT / 2 : FRACUNIT, smntrhudtrans|V_SNAPTOLEFT|V_ADD, facerank, colormap);
|
||||
}
|
||||
|
||||
if (LUA_HudEnabled(hud_battlebumpers))
|
||||
|
|
@ -2683,7 +2744,7 @@ void K_SetScoreboardModStatus(const char *name, SINT8 active)
|
|||
CONS_Alert(CONS_WARNING, "Server mod '%s' does not exist so status cannot be changed.\n", name);
|
||||
}
|
||||
|
||||
#define BASEMODS 21
|
||||
#define BASEMODS 23
|
||||
static void K_DrawServerMods(INT32 x, INT32 y)
|
||||
{
|
||||
UINT8 i, j;
|
||||
|
|
@ -2713,6 +2774,8 @@ static void K_DrawServerMods(INT32 x, INT32 y)
|
|||
{"Bump Spring", 0, &cv_kartbumpspring, -1, true},
|
||||
{"Keep Stuff", 0, NULL, K_KeepStuffActive() > 0, true},
|
||||
{"No BananaDrag", 0, NULL, K_TrailSlowActive() < 1, true},
|
||||
{"No Air Bumps", 0, NULL, K_GetPlayerBump() == PBUMP_GNDONLY, true},
|
||||
{"No PvP Bumps", 0, NULL, K_GetPlayerBump() == PBUMP_NONE, true},
|
||||
//TODO: separate drawer that enumerates item changes?
|
||||
};
|
||||
|
||||
|
|
@ -2850,9 +2913,12 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN
|
|||
else
|
||||
V_DrawString(x + 20, y2, ((tab[i].num == whiteplayer) ? hightlightcolor : 0)|V_ALLOWLOWERCASE, playername);
|
||||
|
||||
patch_t *facerank = faceprefix[players[tab[i].num].skin][FACE_RANK];
|
||||
boolean hires = K_UseHighResPortraits();
|
||||
patch_t *facerank = faceprefix[players[tab[i].num].skin][hires ? FACE_WANTED : FACE_RANK];
|
||||
INT16 leftoffset = facerank->leftoffset;
|
||||
INT16 topoffset = facerank->topoffset;
|
||||
|
||||
V_DrawMappedPatch(x+facerank->leftoffset, y-4+facerank->topoffset, 0, facerank, colormap);
|
||||
V_DrawFixedPatch((x+leftoffset)<<FRACBITS, (y-4+topoffset)<<FRACBITS, hires ? FRACUNIT/2 : FRACUNIT, 0, facerank, colormap);
|
||||
|
||||
/*if ((gametypes[gametype]->rules & GTR_BUMPERS) && players[tab[i].num].bumper > 0) -- not enough space for this
|
||||
{
|
||||
|
|
@ -3026,7 +3092,7 @@ static void K_drawKartStatsnLives(void)
|
|||
|
||||
if ((cv_lives_xoffset.value == 0 && cv_lives_yoffset.value == 0) || split)
|
||||
{
|
||||
if ((cv_newspeedometer.value == 0 || cv_newspeedometer.value == 2) && !K_RingsActive() && !split)
|
||||
if ((cv_newspeedometer.value == 0 || cv_newspeedometer.value == 2 || cv_newspeedometer.value == 3) && !K_RingsActive() && !split)
|
||||
{
|
||||
offsetx = 25;
|
||||
offsety = 15;
|
||||
|
|
@ -3039,8 +3105,11 @@ static void K_drawKartStatsnLives(void)
|
|||
|
||||
// We specify stplyr->skincolor since we want it to match the player and not the hud color.
|
||||
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE);
|
||||
patch_t *facerank = faceprefix[stplyr->skin][FACE_RANK];
|
||||
V_DrawMappedPatch(fx+59+offsetx+facerank->leftoffset, fy-16+offsety+facerank->topoffset, V_HUDTRANS|splitflags, facerank, colormap);
|
||||
boolean hires = K_UseHighResPortraits();
|
||||
patch_t *facerank = faceprefix[stplyr->skin][hires ? FACE_WANTED : FACE_RANK];
|
||||
INT16 topoffset = hires ? facerank->topoffset / 2 : facerank->topoffset;
|
||||
INT16 leftoffset = hires ? facerank->leftoffset / 2 : facerank->leftoffset;
|
||||
V_DrawFixedPatch((fx+59+offsetx+leftoffset)<<FRACBITS, (fy-16+offsety+topoffset)<<FRACBITS, hires ? FRACUNIT/2 : FRACUNIT, V_HUDTRANS|splitflags, facerank, colormap);
|
||||
if (stplyr->lives >= 0 && uselives)
|
||||
V_DrawScaledPatch(fx+77+offsetx, fy-11+offsety, V_HUDTRANS|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow OR underflow
|
||||
|
||||
|
|
@ -3057,8 +3126,8 @@ static void K_drawKartStatsnLives(void)
|
|||
colormapstat2 = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MUSTARD, GTC_CACHE);
|
||||
}
|
||||
|
||||
V_DrawFixedPatch((fx+56+offsetx+facerank->leftoffset)<< FRACBITS, (fy-19+offsety+facerank->topoffset)<< FRACBITS, FRACUNIT, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartspeed % 10)], colormapstat);
|
||||
V_DrawFixedPatch((fx+69+offsetx+facerank->leftoffset)<< FRACBITS, (fy-4+offsety+facerank->topoffset)<< FRACBITS, FRACUNIT, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartweight % 10)], colormapstat2);
|
||||
V_DrawFixedPatch((fx+56+offsetx+leftoffset)<< FRACBITS, (fy-19+offsety+topoffset)<< FRACBITS, FRACUNIT, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartspeed % 10)], colormapstat);
|
||||
V_DrawFixedPatch((fx+69+offsetx+leftoffset)<< FRACBITS, (fy-4+offsety+topoffset)<< FRACBITS, FRACUNIT, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartweight % 10)], colormapstat2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3137,33 +3206,201 @@ static void K_drawKartAccessibilityIcons(INT32 fx)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef ROTSPRITE
|
||||
|
||||
#define DIALSPDDIV 97090 // 1.48148; converts 200 to 135
|
||||
#define MPHDIV 68283 // 1.04192; converts 141 to 135
|
||||
|
||||
static void K_DrawDialNum(INT32 x, INT32 y, boolean colorized, INT32 flags, INT32 num, INT32 digits, const UINT8 *colormap)
|
||||
{
|
||||
INT32 w = 0;
|
||||
w = kp_dialnum[0]->width;
|
||||
|
||||
if (flags & V_NOSCALESTART)
|
||||
w *= vid.dup;
|
||||
|
||||
if (num < 0)
|
||||
num = -num;
|
||||
|
||||
// draw the number
|
||||
do
|
||||
{
|
||||
x -= (w);
|
||||
|
||||
if (colorized)
|
||||
V_DrawFixedPatch(x << FRACBITS, y << FRACBITS, FRACUNIT, flags, kp_dialnum[(num % 10) + 10], colormap);
|
||||
else
|
||||
V_DrawFixedPatch(x << FRACBITS, y << FRACBITS, FRACUNIT, flags, kp_dialnum[num % 10], colormap);
|
||||
|
||||
num /= 10;
|
||||
} while (--digits);
|
||||
}
|
||||
|
||||
static void K_DrawDialLaps(INT32 x, INT32 y, INT32 num, INT32 total, INT32 flags)
|
||||
{
|
||||
INT32 fx;
|
||||
fx = x + ((num < 10) ? 0 : 6);
|
||||
V_DrawRankNum(fx, y, flags, num, (num < 10) ? 1 : 2, NULL);
|
||||
V_DrawScaledPatch(fx + 2, y, flags, frameslash);
|
||||
V_DrawRankNum(fx + 13 + ((total < 10) ? 0 : 6), y, flags, total, (total < 10) ? 1 : 2, NULL);
|
||||
}
|
||||
|
||||
static void K_DrawDialSpeedometer(fixed_t speed,
|
||||
fixed_t divisor,
|
||||
UINT16 labeln,
|
||||
INT32 splitflags,
|
||||
boolean battlemode,
|
||||
boolean infoactive,
|
||||
boolean colorized)
|
||||
{
|
||||
const UINT8* colormap = R_GetTranslationColormap(TC_DEFAULT, K_GetHudColor(), GTC_CACHE);
|
||||
|
||||
INT32 ringoffset = 0;
|
||||
const UINT8 infoidx = (infoactive) ? 1 : 0;
|
||||
|
||||
const fixed_t spd = FixedDiv(speed, divisor);
|
||||
const angle_t speedangle =
|
||||
FixedAngle(((min(135 * FRACUNIT, spd) - (45 * FRACUNIT))));
|
||||
|
||||
if (K_RingsActive() == true)
|
||||
{
|
||||
ringoffset = -16;
|
||||
}
|
||||
|
||||
if (colorized) // Colourized hud
|
||||
{
|
||||
V_DrawMappedPatch(
|
||||
SPDM_X, SPDM_Y - 25, (V_HUDTRANS | splitflags), kp_dialbase[infoidx + 2], colormap);
|
||||
V_DrawMappedPatch(SPDM_X,
|
||||
SPDM_Y + 14,
|
||||
V_HUDTRANS | splitflags,
|
||||
kp_speedpatchesdial[labeln + 4],
|
||||
colormap);
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawScaledPatch(SPDM_X, SPDM_Y - 25, (V_HUDTRANS | splitflags), kp_dialbase[infoidx]);
|
||||
V_DrawScaledPatch(SPDM_X,
|
||||
SPDM_Y + 14,
|
||||
V_HUDTRANS | splitflags,
|
||||
kp_speedpatchesdial[labeln]);
|
||||
}
|
||||
|
||||
K_DrawDialNum(SPDM_X + 10,
|
||||
SPDM_Y + 9,
|
||||
colorized,
|
||||
V_HUDTRANS | splitflags,
|
||||
speed / FRACUNIT,
|
||||
3,
|
||||
colormap);
|
||||
|
||||
// gotta center the dial manually
|
||||
V_DrawRotatedPatch((SPDM_X - 19) << FRACBITS,
|
||||
(SPDM_Y - 1) << FRACBITS,
|
||||
speedangle,
|
||||
FRACUNIT,
|
||||
FRACUNIT,
|
||||
(V_HUDTRANS | splitflags),
|
||||
kp_dial,
|
||||
colormap);
|
||||
|
||||
if (!infoactive)
|
||||
{
|
||||
// no need to draw info if we're not supposed to
|
||||
return;
|
||||
}
|
||||
|
||||
// draw the info
|
||||
if (battlemode)
|
||||
{
|
||||
if (itembreaker)
|
||||
{
|
||||
V_DrawMappedPatch(SPDM_X + 28, SPDM_Y + 12, V_HUDTRANS | splitflags, kp_itemboxminimap, NULL);
|
||||
K_DrawDialLaps(SPDM_X + 46,
|
||||
SPDM_Y + 13,
|
||||
numtargets,
|
||||
nummapboxes,
|
||||
V_HUDTRANS | splitflags);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stplyr->bumper <= 0 && (gametypes[gametype]->rules & GTR_KARMA) && comeback)
|
||||
{
|
||||
V_DrawMappedPatch(
|
||||
SPDM_X + 28, SPDM_Y + 12, V_HUDTRANS | splitflags, kp_splitkarmabomb, colormap);
|
||||
K_DrawDialLaps(SPDM_X + 46,
|
||||
SPDM_Y + 13,
|
||||
stplyr->karmapoints,
|
||||
2,
|
||||
V_HUDTRANS | splitflags);
|
||||
}
|
||||
else // the above doesn't need to account for weird stuff since the max amount of karma
|
||||
// necessary is always 2 ^^^^
|
||||
{
|
||||
INT32 maxbumper = K_StartingBumperCount();
|
||||
V_DrawMappedPatch(
|
||||
SPDM_X + 28, SPDM_Y + 12, V_HUDTRANS | splitflags, kp_rankbumper, colormap);
|
||||
K_DrawDialLaps(SPDM_X + 46,
|
||||
SPDM_Y + 13,
|
||||
stplyr->bumper,
|
||||
maxbumper,
|
||||
V_HUDTRANS | splitflags);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawScaledPatch(SPDM_X + 28, SPDM_Y + 12, V_HUDTRANS | splitflags, kp_splitlapflag);
|
||||
|
||||
if (stplyr->exiting)
|
||||
V_DrawScaledPatch(SPDM_X + 39, SPDM_Y + 13, V_HUDTRANS | splitflags, kp_dialfinish);
|
||||
else
|
||||
K_DrawDialLaps(SPDM_X + 46,
|
||||
SPDM_Y + 13,
|
||||
stplyr->laps,
|
||||
numlaps,
|
||||
V_HUDTRANS | splitflags);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void K_drawKartSpeedometer(void)
|
||||
{
|
||||
static fixed_t convSpeed;
|
||||
static fixed_t convSpeed[2] = {0};
|
||||
UINT8 labeln = 0;
|
||||
UINT8 numbers[3];
|
||||
INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
|
||||
INT32 battleoffset = 0;
|
||||
INT32 ringoffset = 0;
|
||||
INT32 oldringoffset = 0;
|
||||
#ifdef ROTSPRITE
|
||||
fixed_t dial_divisor = DIALSPDDIV;
|
||||
#endif
|
||||
|
||||
switch (cv_kartspeedometer.value)
|
||||
{
|
||||
case 1: // Kilometers
|
||||
convSpeed = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale) / FRACUNIT; // 2.172409058
|
||||
convSpeed[0] = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale); // 2.172409058
|
||||
convSpeed[1] = convSpeed[0] / FRACUNIT;
|
||||
labeln = 0;
|
||||
break;
|
||||
case 2: // Miles
|
||||
convSpeed = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale) / FRACUNIT; // 1.349868774
|
||||
convSpeed[0] = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale); // 1.349868774
|
||||
convSpeed[1] = convSpeed[0] / FRACUNIT;
|
||||
labeln = 1;
|
||||
break;
|
||||
case 3: // Fracunits
|
||||
convSpeed = FixedDiv(stplyr->speed, mapobjectscale) / FRACUNIT; // 1.0. duh.
|
||||
convSpeed[0] = FixedDiv(stplyr->speed, mapobjectscale); // 1.0. duh.
|
||||
convSpeed[1] = convSpeed[0] / FRACUNIT;
|
||||
labeln = 2;
|
||||
break;
|
||||
case 4: // Sonic Drift 2 style percentage
|
||||
if (stplyr->mo)
|
||||
convSpeed = (FixedDiv(stplyr->speed, FixedMul(K_GetKartSpeed(stplyr, false, false), K_PlayerBaseFriction(stplyr, ORIG_FRICTION)))*100)>>FRACBITS;
|
||||
{
|
||||
convSpeed[0] = (FixedDiv(stplyr->speed, FixedMul(K_GetKartSpeed(stplyr, false, false), K_PlayerBaseFriction(stplyr, ORIG_FRICTION)))*100);
|
||||
convSpeed[1] = convSpeed[0] >> FRACBITS;
|
||||
}
|
||||
labeln = 3;
|
||||
break;
|
||||
default:
|
||||
|
|
@ -3172,8 +3409,11 @@ static void K_drawKartSpeedometer(void)
|
|||
|
||||
// Don't overflow
|
||||
// (negative speed IS really high speed :V)
|
||||
if (convSpeed > 999 || convSpeed < 0)
|
||||
convSpeed = 999;
|
||||
if (convSpeed[1] > 999 || convSpeed[1] < 0)
|
||||
{
|
||||
convSpeed[1] = 999;
|
||||
convSpeed[0] = convSpeed[1] * FRACUNIT;
|
||||
}
|
||||
|
||||
if (cv_speed_xoffset.value == 0 && cv_speed_yoffset.value == 0)
|
||||
{
|
||||
|
|
@ -3191,16 +3431,16 @@ static void K_drawKartSpeedometer(void)
|
|||
{
|
||||
switch (cv_kartspeedometer.value) {
|
||||
case 1:
|
||||
V_DrawKartString(SPDM_X + oldringoffset, SPDM_Y-18 + battleoffset + ringoffset, V_HUDTRANS|splitflags, va("%3d km/h", convSpeed));
|
||||
V_DrawKartString(SPDM_X + oldringoffset, SPDM_Y-18 + battleoffset + ringoffset, V_HUDTRANS|splitflags, va("%3d km/h", convSpeed[1]));
|
||||
break;
|
||||
case 2:
|
||||
V_DrawKartString(SPDM_X + oldringoffset, SPDM_Y-18 + battleoffset + ringoffset, V_HUDTRANS|splitflags, va("%3d mph", convSpeed));
|
||||
V_DrawKartString(SPDM_X + oldringoffset, SPDM_Y-18 + battleoffset + ringoffset, V_HUDTRANS|splitflags, va("%3d mph", convSpeed[1]));
|
||||
break;
|
||||
case 3:
|
||||
V_DrawKartString(SPDM_X + oldringoffset, SPDM_Y-18 + battleoffset + ringoffset, V_HUDTRANS|splitflags, va("%3d fu/t", convSpeed));
|
||||
V_DrawKartString(SPDM_X + oldringoffset, SPDM_Y-18 + battleoffset + ringoffset, V_HUDTRANS|splitflags, va("%3d fu/t", convSpeed[1]));
|
||||
break;
|
||||
case 4:
|
||||
V_DrawKartString(SPDM_X + oldringoffset, SPDM_Y-18 + battleoffset + ringoffset, V_HUDTRANS|splitflags, va("%4d %%", convSpeed));
|
||||
V_DrawKartString(SPDM_X + oldringoffset, SPDM_Y-18 + battleoffset + ringoffset, V_HUDTRANS|splitflags, va("%4d %%", convSpeed[1]));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -3208,9 +3448,9 @@ static void K_drawKartSpeedometer(void)
|
|||
}
|
||||
else if (cv_newspeedometer.value == 1)
|
||||
{
|
||||
numbers[0] = ((convSpeed / 100) % 10);
|
||||
numbers[1] = ((convSpeed / 10) % 10);
|
||||
numbers[2] = (convSpeed % 10);
|
||||
numbers[0] = ((convSpeed[1] / 100) % 10);
|
||||
numbers[1] = ((convSpeed[1] / 10) % 10);
|
||||
numbers[2] = (convSpeed[1] % 10);
|
||||
|
||||
if (!K_UseColorHud())
|
||||
{
|
||||
|
|
@ -3254,10 +3494,27 @@ static void K_drawKartSpeedometer(void)
|
|||
|
||||
V_DrawScaledPatch(SPDM_X, SPDM_Y-yoffset + battleoffset + ringoffset, V_HUDTRANS|splitflags, (!K_RingsActive() ? kp_kartzspeedo : kp_kartzspeedo_smol)[spdpatch]);
|
||||
}
|
||||
#ifdef ROTSPRITE
|
||||
else if (cv_newspeedometer.value == 3)
|
||||
{
|
||||
K_DrawDialSpeedometer(convSpeed[0],
|
||||
dial_divisor,
|
||||
labeln,
|
||||
splitflags,
|
||||
(boolean)((gametypes[gametype]->rules & GTR_BUMPERS) == GTR_BUMPERS),
|
||||
(LUA_HudEnabled(hud_gametypeinfo)),
|
||||
(K_UseColorHud()));
|
||||
}
|
||||
#endif
|
||||
|
||||
K_drawKartAccessibilityIcons((cv_newspeedometer.value == 0 || cv_newspeedometer.value == 2) ? 50 : 56);
|
||||
}
|
||||
|
||||
#ifdef ROTSPRITE
|
||||
#undef DIALSPDDIV
|
||||
#undef MPHDIV
|
||||
#endif
|
||||
|
||||
static void K_drawRingMeter(void)
|
||||
{
|
||||
UINT8 rn[2];
|
||||
|
|
@ -5463,8 +5720,7 @@ static void K_drawKartFirstPerson(void)
|
|||
// doesn't need to ever support 4p
|
||||
static void K_drawInput(void)
|
||||
{
|
||||
static INT32 pn = 0;
|
||||
INT32 target = 0, splitflags = (V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SLIDEIN);
|
||||
INT32 splitflags = (V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SLIDEIN);
|
||||
INT32 x = (BASEVIDWIDTH - 32)*FRACUNIT, y = (BASEVIDHEIGHT - 24)*FRACUNIT;
|
||||
UINT8 *shadowcolormap = NULL;
|
||||
INT32 offs, col;
|
||||
|
|
@ -6241,10 +6497,33 @@ static void K_SlipstreamIndicator(boolean tiny)
|
|||
V_DrawThinString(fx - V_StringWidth(str, flags)/2, fy, flags, str);
|
||||
}
|
||||
|
||||
// determines if gametype info (laps/bumpers) should be hidden
|
||||
static boolean K_DisableGametypeInfo(void)
|
||||
{
|
||||
if (!LUA_HudEnabled(hud_gametypeinfo))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ROTSPRITE
|
||||
if (r_splitscreen || (!cv_kartspeedometer.value))
|
||||
{
|
||||
// don't need to run checks if we're in splitscreen, or not using the speedometer
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cv_newspeedometer.value == 3)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void K_drawKartHUD(void)
|
||||
{
|
||||
boolean islonesome = false;
|
||||
boolean battlefullscreen = false;
|
||||
boolean gameinfovisible = false;
|
||||
UINT8 viewnum = R_GetViewNumber();
|
||||
boolean freecam = camera[viewnum].freecam; //disable some hud elements w/ freecam
|
||||
UINT8 i;
|
||||
|
|
@ -6352,6 +6631,9 @@ void K_drawKartHUD(void)
|
|||
|
||||
if (!stplyr->spectator && !freecam) // Bottom of the screen elements, don't need in spectate mode
|
||||
{
|
||||
// get gametype info visibility ahead of time
|
||||
gameinfovisible = (!K_DisableGametypeInfo());
|
||||
|
||||
if (demo.title) // Draw title logo instead in demo.titles
|
||||
{
|
||||
INT32 x = (BASEVIDWIDTH - 32), y = 128, snapflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT;
|
||||
|
|
@ -6388,12 +6670,15 @@ void K_drawKartHUD(void)
|
|||
{
|
||||
if (gametypes[gametype]->rules & GTR_CIRCUIT)
|
||||
{
|
||||
K_drawKartLaps();
|
||||
if (gameinfovisible)
|
||||
K_drawKartLaps();
|
||||
|
||||
K_drawKartStatsnLives();
|
||||
}
|
||||
else if (gametypes[gametype]->rules & GTR_BUMPERS)
|
||||
{
|
||||
K_drawKartBumpersOrKarma();
|
||||
if (gameinfovisible)
|
||||
K_drawKartBumpersOrKarma();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,8 @@ extern consvar_t cv_newtabranking;
|
|||
|
||||
extern consvar_t cv_kartdebughudtracker;
|
||||
|
||||
extern consvar_t cv_highresportrait;
|
||||
|
||||
extern patch_t *kp_facenum[MAXPLAYERS+1];
|
||||
extern patch_t *kp_itemboxminimap;
|
||||
extern patch_t *kp_minimapdot;
|
||||
|
|
@ -189,6 +191,8 @@ extern patch_t *kp_itemtarget_far[2][2];
|
|||
extern patch_t *kp_itemtarget_far_text[2];
|
||||
extern patch_t *kp_itemtarget_near[2][8];
|
||||
|
||||
boolean K_UseHighResPortraits(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2160,7 +2160,6 @@ void K_PlayerItemThink(player_t *player, boolean onground)
|
|||
{
|
||||
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
|
||||
{
|
||||
angle_t newangle;
|
||||
INT32 moloop;
|
||||
mobj_t *mo = NULL;
|
||||
mobj_t *prev = player->mo;
|
||||
|
|
@ -2233,7 +2232,6 @@ void K_PlayerItemThink(player_t *player, boolean onground)
|
|||
case KITEM_ORBINAUT:
|
||||
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
|
||||
{
|
||||
angle_t newangle;
|
||||
INT32 moloop;
|
||||
mobj_t *mo = NULL;
|
||||
mobj_t *prev = player->mo;
|
||||
|
|
@ -2267,7 +2265,6 @@ void K_PlayerItemThink(player_t *player, boolean onground)
|
|||
case KITEM_JAWZ:
|
||||
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
|
||||
{
|
||||
angle_t newangle;
|
||||
INT32 moloop;
|
||||
mobj_t *mo = NULL;
|
||||
mobj_t *prev = player->mo;
|
||||
|
|
@ -2404,7 +2401,7 @@ void K_PlayerItemThink(player_t *player, boolean onground)
|
|||
{
|
||||
if (K_GetShieldFromPlayer(player) != KSHIELD_ATTRACTION)
|
||||
{
|
||||
mobj_t *shield = K_SpawnEquippedItem(player, KITEMEQUIP_SHIELD, MT_ATTRACTIONSHIELD, 0, NULL);
|
||||
/*mobj_t *shield =*/ K_SpawnEquippedItem(player, KITEMEQUIP_SHIELD, MT_ATTRACTIONSHIELD, 0, NULL);
|
||||
S_StartSound(player->mo, sfx_attrsg);
|
||||
}
|
||||
|
||||
|
|
@ -2530,7 +2527,7 @@ void K_PlayerItemThink(player_t *player, boolean onground)
|
|||
{
|
||||
if (K_GetShieldFromPlayer(player) != KSHIELD_THUNDER)
|
||||
{
|
||||
mobj_t *shield = K_SpawnEquippedItem(player, KITEMEQUIP_SHIELD, MT_THUNDERSHIELD, 0, NULL);
|
||||
/*mobj_t *shield =*/K_SpawnEquippedItem(player, KITEMEQUIP_SHIELD, MT_THUNDERSHIELD, 0, NULL);
|
||||
S_StartSound(player->mo, sfx_s3k41);
|
||||
}
|
||||
|
||||
|
|
@ -2554,7 +2551,7 @@ void K_PlayerItemThink(player_t *player, boolean onground)
|
|||
case KITEM_BUBBLESHIELD:
|
||||
if (K_GetShieldFromPlayer(player) != KSHIELD_BUBBLE)
|
||||
{
|
||||
mobj_t *shield = K_SpawnEquippedItem(player, KITEMEQUIP_SHIELD, MT_BUBBLESHIELD, 0, NULL);
|
||||
/*mobj_t *shield =*/ K_SpawnEquippedItem(player, KITEMEQUIP_SHIELD, MT_BUBBLESHIELD, 0, NULL);
|
||||
S_StartSound(player->mo, sfx_s3k3f);
|
||||
if (player->bubblehealth <= 0 || player->bubblehealth > MAXBUBBLEHEALTH)
|
||||
player->bubblehealth = MAXBUBBLEHEALTH;
|
||||
|
|
@ -2640,7 +2637,7 @@ void K_PlayerItemThink(player_t *player, boolean onground)
|
|||
player->itemamount--;
|
||||
player->flametimer = flametime;
|
||||
player->flamedash = 0;
|
||||
mobj_t *shield = K_SpawnEquippedItem(player, KITEMEQUIP_SHIELD, MT_FLAMESHIELD, 0, NULL);
|
||||
/*mobj_t *shield =*/ K_SpawnEquippedItem(player, KITEMEQUIP_SHIELD, MT_FLAMESHIELD, 0, NULL);
|
||||
S_StartSound(player->mo, sfx_s3k3e);
|
||||
}
|
||||
break;
|
||||
|
|
@ -2980,7 +2977,6 @@ mobj_t *K_SpawnEquippedItem(player_t *player, kartitemequip_e equipstyle, mobjty
|
|||
switch (equipstyle)
|
||||
{
|
||||
case KITEMEQUIP_ORBIT: // Kart orbit items
|
||||
K_MatchGenericExtraFlags(mo, player->mo);
|
||||
newangle = (player->mo->angle + ANGLE_157h) + FixedAngle(((360 / player->itemamount) * moloop) << FRACBITS) + ANGLE_90;
|
||||
mo->angle = newangle;
|
||||
mo->movecount = player->itemamount;
|
||||
|
|
@ -2997,6 +2993,7 @@ mobj_t *K_SpawnEquippedItem(player_t *player, kartitemequip_e equipstyle, mobjty
|
|||
mo->movedir = mo->lastlook = moloop+1;
|
||||
break;
|
||||
case KITEMEQUIP_SHIELD: // Force Field Shields
|
||||
K_FlipFromObject(mo, player->mo);
|
||||
P_SetScale(mo, (mo->destscale = (5*mo->destscale)>>2));
|
||||
P_SetTarget(&player->shieldtracer, mo);
|
||||
break;
|
||||
|
|
|
|||
26
src/k_kart.c
26
src/k_kart.c
|
|
@ -491,6 +491,7 @@ void K_RegisterKartStuff(void)
|
|||
|
||||
CV_RegisterVar(&cv_kartkeepstuff);
|
||||
CV_RegisterVar(&cv_trailslow);
|
||||
CV_RegisterVar(&cv_playerbump);
|
||||
}
|
||||
|
||||
//}
|
||||
|
|
@ -7242,6 +7243,12 @@ INT32 K_GetBumpSpark(void)
|
|||
return max(min(BUMPSPARK_ALL, (bumpsparktype_t)bumpsparkactive), BUMPSPARK_NONE);
|
||||
}
|
||||
|
||||
// Returns the playerbump value as an enum.
|
||||
playerbumptype_t K_GetPlayerBump(void)
|
||||
{
|
||||
return max(min(PBUMP_NONE, (playerbumptype_t)playerbumpactive), PBUMP_STANDARD);
|
||||
}
|
||||
|
||||
/** \brief Decreases various kart timers and powers per frame. Called in P_PlayerThink in p_user.c
|
||||
|
||||
\param player player object passed from P_PlayerThink
|
||||
|
|
@ -7978,7 +7985,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
fixed_t vrate = (-FixedMul(rate * 2 - FRACUNIT, rate * 2 - FRACUNIT)) + FRACUNIT;
|
||||
FV2_Rotate(&tyreoffs, 45*FRACUNIT);
|
||||
player->mo->spritexoffset += Easing_Linear(rate, 0, (5*tyredistance/2));
|
||||
player->mo->spriteyoffset += Easing_Linear(vrate, 0, abs(tyreoffs.y/2) * P_MobjFlip(player->mo));
|
||||
player->mo->spriteyoffset += Easing_Linear(vrate, 0, abs(tyreoffs.y/2));
|
||||
|
||||
// CONS_Debug(DBG_PLAYER, "tyre dist : %4.3f\n", FIXED_TO_FLOAT(tyredistance));
|
||||
// CONS_Debug(DBG_PLAYER, "kart tilt : %4.3f\n", FIXED_TO_FLOAT(player->karttilt));
|
||||
|
|
@ -10058,7 +10065,7 @@ static boolean K_PlayerCanRecoverySpin(player_t *player)
|
|||
|
||||
static void K_RecoveryDash(player_t *player)
|
||||
{
|
||||
if (K_PlayerWantsRecoverySpin(player) && K_IsPlayerDamaged(player) && player->wipeoutslow == 0)
|
||||
if (K_RecoveryDashActive() && K_PlayerWantsRecoverySpin(player) && K_IsPlayerDamaged(player) && player->wipeoutslow == 0)
|
||||
{
|
||||
player->wipeoutslow = max(wipeoutslowtime + 1, player->spinouttimer + 4);
|
||||
}
|
||||
|
|
@ -10148,6 +10155,10 @@ static void K_RecoveryDash(player_t *player)
|
|||
travelangle = player->mo->angle;
|
||||
for (INT32 i = 0; i < 2; i++)
|
||||
{
|
||||
if (player->karttilt && ((player->karttilt < 0 && !(i & 1)) || (player->karttilt > 0 && (i & 1))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
newx = P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(28*FRACUNIT, player->mo->scale));
|
||||
newy = P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(28*FRACUNIT, player->mo->scale));
|
||||
skid = P_SpawnMobjFromMobj(player->mo, newx, newy, (10*player->mo->scale), MT_OVERLAY);
|
||||
|
|
@ -10189,6 +10200,14 @@ static void K_RecoveryDash(player_t *player)
|
|||
sparkangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
|
||||
for (INT32 i = 0; i < 2; i++)
|
||||
{
|
||||
if (P_IsObjectOnGround(player->mo) && player->karttilt &&
|
||||
((player->karttilt < 0 && !(i & 1)) || (player->karttilt > 0 && (i & 1)))
|
||||
)
|
||||
{
|
||||
// when the kart is tilting don't spawn the sparks for the tyre that is lifted off the ground
|
||||
// player has to be grounded in general for this to make sense, so never skip spawning sparks if player is airborne
|
||||
continue;
|
||||
}
|
||||
newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale));
|
||||
newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale));
|
||||
spark = P_SpawnMobj(newx, newy, player->mo->z, MT_DRIFTSPARK);
|
||||
|
|
@ -12143,6 +12162,7 @@ void K_KartAttractHomingAttack(player_t *player)
|
|||
{
|
||||
mobj_t *mo;
|
||||
angle_t effectangle = player->mo->angle;
|
||||
boolean raceFirst = ((gametypes[gametype]->rules & GTR_CIRCUIT) && player->position <= 1);
|
||||
if (player->speed > 0)
|
||||
{
|
||||
effectangle = R_PointToAngle2(0,0, player->mo->momx, player->mo->momy);
|
||||
|
|
@ -12156,7 +12176,7 @@ void K_KartAttractHomingAttack(player_t *player)
|
|||
|
||||
P_SpawnGhostMobj(player->mo);
|
||||
|
||||
if (player->attractionattack_hipower && player->position > 1 && lastTarg >= 0)
|
||||
if (player->attractionattack_hipower && lastTarg >= 0 && (!raceFirst))
|
||||
{
|
||||
player->attractionboost = Easing_InCubic(influence, ATTRACTIONSPEEDHIMIN, ATTRACTIONSPEEDHIMAX);
|
||||
|
||||
|
|
|
|||
|
|
@ -391,6 +391,7 @@ boolean K_ItemListActive(void);
|
|||
boolean K_ItemPushingActive(void);
|
||||
boolean K_TrailSlowActive(void);
|
||||
INT32 K_GetBumpSpark(void);
|
||||
playerbumptype_t K_GetPlayerBump(void);
|
||||
boolean K_BoostChain(player_t *player, INT32 timer, boolean chainsound);
|
||||
INT32 K_ChainOrDeincrementTime(player_t *player, INT32 timer, INT32 deincrement, boolean chainsound);
|
||||
boolean K_UsingLegacyCheckpoints(void);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "console.h" // cons_menuhighlight
|
||||
#include "k_itemlist.hpp"
|
||||
#include "k_vote.h"
|
||||
#include "k_hud.h"
|
||||
|
||||
static INT32 votetimer;
|
||||
|
||||
|
|
@ -601,9 +602,11 @@ void Y_VoteDrawer(void)
|
|||
if (players[i].skincolor)
|
||||
{
|
||||
UINT8 *colormap = R_GetTranslationColormap(players[i].skin, players[i].skincolor, GTC_CACHE);
|
||||
patch_t *facerank = faceprefix[players[i].skin][FACE_RANK];
|
||||
boolean hires = K_UseHighResPortraits();
|
||||
patch_t *facerank = faceprefix[players[i].skin][hires ? FACE_WANTED : FACE_RANK];
|
||||
fixed_t hiresscale = hires ? FRACUNIT/2 : FRACUNIT;
|
||||
|
||||
V_DrawMappedPatch(x+24+facerank->leftoffset, y+(highplayers ? smallfaceheight : bigfaceheight)+facerank->topoffset, V_SNAPTOLEFT, facerank, colormap);
|
||||
V_DrawFixedPatch((x+24+facerank->leftoffset)<<FRACBITS, (y+(highplayers ? smallfaceheight : bigfaceheight)+facerank->topoffset)<<FRACBITS, hiresscale, V_SNAPTOLEFT, facerank, colormap);
|
||||
}
|
||||
|
||||
if (!splitscreen && i == consoleplayer)
|
||||
|
|
|
|||
|
|
@ -4602,6 +4602,13 @@ static int lib_kGetBumpSpark(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
// Gets the currently active playerbump type.
|
||||
static int lib_kGetPlayerBump(lua_State *L)
|
||||
{
|
||||
lua_pushinteger(L, K_GetPlayerBump());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Checks if current map is using legacy boss3 bassed checkpoints. Useful for map compat.
|
||||
static int lib_kUsingLegacyCheckpoints(lua_State *L)
|
||||
{
|
||||
|
|
@ -5961,6 +5968,7 @@ static luaL_Reg lib[] = {
|
|||
{"K_ItemListActive", lib_kItemListActive},
|
||||
{"K_TrailSlowActive", lib_kTrailSlowActive},
|
||||
{"K_GetBumpSpark",lib_kGetBumpSpark},
|
||||
{"K_GetPlayerBump",lib_kGetPlayerBump},
|
||||
{"K_UsingLegacyCheckpoints",lib_kUsingLegacyCheckpoints},
|
||||
{"K_DoBoost",lib_kDoBoost},
|
||||
{"K_ClearBoost",lib_kClearBoost},
|
||||
|
|
|
|||
|
|
@ -388,28 +388,28 @@ int LUA_PushGlobals(lua_State *L, const char *word)
|
|||
lua_pushinteger(L, numgotboxes);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"itembreaker")) {
|
||||
lua_pushinteger(L, itembreaker);
|
||||
lua_pushboolean(L, itembreaker);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"battleprisons")) {
|
||||
lua_pushinteger(L, itembreaker);
|
||||
lua_pushboolean(L, itembreaker);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"ringsactive")) {
|
||||
lua_pushinteger(L, ringsactive);
|
||||
lua_pushboolean(L, ringsactive);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"stackingactive")) {
|
||||
lua_pushinteger(L, stackingactive);
|
||||
lua_pushboolean(L, stackingactive);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"chainingactive")) {
|
||||
lua_pushinteger(L, chainingactive);
|
||||
lua_pushboolean(L, chainingactive);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"slipdashactive")) {
|
||||
lua_pushinteger(L, slipdashactive);
|
||||
lua_pushboolean(L, slipdashactive);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"slopeboostactive")) {
|
||||
lua_pushinteger(L, slopeboostactive);
|
||||
lua_pushboolean(L, slopeboostactive);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"draftingactive")) {
|
||||
lua_pushinteger(L, draftingactive);
|
||||
lua_pushboolean(L, draftingactive);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"airdropactive")) {
|
||||
lua_pushinteger(L, airdropactive);
|
||||
|
|
@ -418,22 +418,25 @@ int LUA_PushGlobals(lua_State *L, const char *word)
|
|||
lua_pushinteger(L, bumpsparkactive);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"purpledriftactive")) {
|
||||
lua_pushinteger(L, purpledriftactive);
|
||||
lua_pushboolean(L, purpledriftactive);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"recoverydashactive")) {
|
||||
lua_pushinteger(L, recoverydashactive);
|
||||
lua_pushboolean(L, recoverydashactive);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"keepstuffactive")) {
|
||||
lua_pushinteger(L, keepstuffactive);
|
||||
lua_pushboolean(L, keepstuffactive);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"itemlittering")) {
|
||||
lua_pushinteger(L, itemlittering);
|
||||
lua_pushboolean(L, itemlittering);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"itempushing")) {
|
||||
lua_pushboolean(L, itempushing); // hmm... i think this should be a boolean
|
||||
return 1;
|
||||
} else if (fastcmp(word,"trailslow_active") || fastcmp(word,"trailslowactive")) {
|
||||
lua_pushinteger(L, trailslow_active);
|
||||
lua_pushboolean(L, trailslow_active); // I agree!
|
||||
return 1;
|
||||
} else if (fastcmp(word,"playerbumpactive")) {
|
||||
lua_pushinteger(L, playerbumpactive);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"hyubgone")) {
|
||||
lua_pushinteger(L, K_GetKartResult("hyudoro")->cooldown);
|
||||
|
|
@ -588,39 +591,41 @@ int LUA_WriteGlobals(lua_State *L, const char *word)
|
|||
else if (fastcmp(word,"introtime"))
|
||||
introtime = (tic_t)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word,"itembreaker"))
|
||||
itembreaker = (boolean)luaL_checkinteger(L, 2);
|
||||
itembreaker = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"battleprisons"))
|
||||
itembreaker = (boolean)luaL_checkinteger(L, 2);
|
||||
itembreaker = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"ringsactive"))
|
||||
ringsactive = (boolean)luaL_checkinteger(L, 2);
|
||||
ringsactive = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"stackingactive"))
|
||||
stackingactive = (boolean)luaL_checkinteger(L, 2);
|
||||
stackingactive = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"chainingactive"))
|
||||
chainingactive = (boolean)luaL_checkinteger(L, 2);
|
||||
chainingactive = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"slipdashactive"))
|
||||
slipdashactive = (boolean)luaL_checkinteger(L, 2);
|
||||
slipdashactive = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"slopeboostactive"))
|
||||
slopeboostactive = (boolean)luaL_checkinteger(L, 2);
|
||||
slopeboostactive = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"draftingactive"))
|
||||
draftingactive = (boolean)luaL_checkinteger(L, 2);
|
||||
draftingactive = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"airdropactive"))
|
||||
airdropactive = luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word,"bumpsparkactive"))
|
||||
bumpsparkactive = luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word,"purpledriftactive"))
|
||||
purpledriftactive = (boolean)luaL_checkinteger(L, 2);
|
||||
purpledriftactive = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"recoverydashactive"))
|
||||
recoverydashactive = (boolean)luaL_checkinteger(L, 2);
|
||||
recoverydashactive = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"itemlittering"))
|
||||
itemlittering = (boolean)luaL_checkinteger(L, 2);
|
||||
itemlittering = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"keepstuffactive"))
|
||||
keepstuffactive = (boolean)luaL_checkinteger(L, 2);
|
||||
keepstuffactive = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"itempushing"))
|
||||
itempushing = (boolean)luaL_checkinteger(L, 2);
|
||||
itempushing = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"trailslowactive"))
|
||||
trailslow_active = (boolean)luaL_checkinteger(L, 2);
|
||||
trailslow_active = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"trailslow_active"))
|
||||
trailslow_active = (boolean)luaL_checkinteger(L, 2);
|
||||
trailslow_active = luaL_checkboolean(L, 2);
|
||||
else if (fastcmp(word,"playerbumpactive"))
|
||||
playerbumpactive = (playerbumptype_t)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word,"gamespeed"))
|
||||
gamespeed = (UINT8)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word,"nummapboxes"))
|
||||
|
|
|
|||
93
src/m_menu.c
93
src/m_menu.c
|
|
@ -75,6 +75,7 @@
|
|||
#include "k_color.h"
|
||||
#include "k_grandprix.h"
|
||||
#include "k_follower.h"
|
||||
#include "r_voicepreference.hpp"
|
||||
#include "r_fps.h"
|
||||
#include "m_easing.h"
|
||||
|
||||
|
|
@ -811,6 +812,7 @@ void Moviemode_option_Onchange(void)
|
|||
#define SKINGRIDWIDTH 9
|
||||
#define SKINGRIDHEIGHT 6
|
||||
|
||||
static boolean gridcss_active;
|
||||
static INT32 gridcss_skinydrag;
|
||||
static INT32 gridcss_skinmemory;
|
||||
static INT32 gridcss_row;
|
||||
|
|
@ -1327,14 +1329,15 @@ static INT32 RemapGamepadButton(event_t *ev)
|
|||
static INT32 joyremap[][2] = {
|
||||
{ gc_accelerate, KEY_ENTER }, // these two first
|
||||
{ gc_brake, KEY_ESCAPE }, // in case of conflicts
|
||||
{ gc_fire, KEY_BACKSPACE },
|
||||
{ gc_drift, KEY_SPACE },
|
||||
{ gc_systemmenu, KEY_ESCAPE },
|
||||
{ gc_aimforward, KEY_UPARROW },
|
||||
{ gc_aimbackward, KEY_DOWNARROW },
|
||||
{ gc_turnleft, KEY_LEFTARROW },
|
||||
{ gc_turnright, KEY_RIGHTARROW },
|
||||
{ gc_lookback, KEY_TAB },
|
||||
{ gc_fire, KEY_BACKSPACE },
|
||||
{ gc_horncode, KEY_BACKSPACE },
|
||||
{ gc_drift, KEY_SPACE },
|
||||
{ gc_lookback, KEY_TAB },
|
||||
};
|
||||
|
||||
for (size_t r = 0; r < sizeof(joyremap)/sizeof(*joyremap); r++)
|
||||
|
|
@ -4862,8 +4865,8 @@ static void DrawReplayHutReplayInfo(void)
|
|||
V_DrawThinString(x, y+9, V_SNAPTOTOP|V_ALLOWLOWERCASE, va("(%d laps)", demolist[dir_on[menudepthleft]].numlaps));
|
||||
|
||||
const char *gametypename = gametypes[demolist[dir_on[menudepthleft]].gametype] ? gametypes[demolist[dir_on[menudepthleft]].gametype]->name : "Unknown";
|
||||
const char *gamespeed = kartspeed_cons_t[(demolist[dir_on[menudepthleft]].kartspeed & ~DF_ENCORE) + 1].strvalue;
|
||||
const char *gamemodestring = va("%s (%s speed)", gametypename, gamespeed);
|
||||
const char *displaygamespeed = kartspeed_cons_t[(demolist[dir_on[menudepthleft]].kartspeed & ~DF_ENCORE) + 1].strvalue;
|
||||
const char *gamemodestring = va("%s (%s speed)", gametypename, displaygamespeed);
|
||||
|
||||
V_DrawString(x, y+20, V_SNAPTOTOP|V_ALLOWLOWERCASE, gamemodestring);
|
||||
|
||||
|
|
@ -8009,7 +8012,7 @@ void MD_DrawGridCssSelector(void)
|
|||
}
|
||||
|
||||
// draw wanted portrait and cursor
|
||||
if (M_IsItemOn(MN_MP_PLAYERSETUP, "SKIN"))
|
||||
if (M_IsItemOn(MN_MP_PLAYERSETUP, "SKIN") && gridcss_active)
|
||||
{
|
||||
face = faceprefix[skintodisplay][FACE_WANTED];
|
||||
colmap = R_GetTranslationColormap(skintodisplay, cv_dummycolor.value, GTC_MENUCACHE);
|
||||
|
|
@ -8046,10 +8049,39 @@ static inline INT32 MapGridSelectToSkin(INT32 row, INT32 column)
|
|||
INT32 MR_HandleSetupMultiPlayerMenu(INT32 choice)
|
||||
{
|
||||
INT32 sortedIndex = 0;
|
||||
// don't consume input if we're not interacting with the grid CSS
|
||||
if (!(M_IsItemOn(MN_MP_PLAYERSETUP, "SKIN") && cv_skinselectstyle.value))
|
||||
|
||||
if (M_IsItemOn(MN_MP_PLAYERSETUP, "FOLLOWER") && (choice == KEY_BACKSPACE || choice == KEY_ENTER))
|
||||
{
|
||||
return false;
|
||||
if (cv_dummyfollower.value > -1)
|
||||
{
|
||||
follower_t fl = followers[cv_dummyfollower.value];
|
||||
S_StopSounds();
|
||||
S_StartSound(NULL, fl.hornsound);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// don't consume input if we're not interacting with the grid CSS
|
||||
if (cv_skinselectstyle.value != 1) return false;
|
||||
if (!M_IsItemOn(MN_MP_PLAYERSETUP, "SKIN")) return false;
|
||||
|
||||
if (!gridcss_active)
|
||||
{
|
||||
if (choice == KEY_ENTER)
|
||||
{
|
||||
gridcss_active = true;
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
return true;
|
||||
}
|
||||
if (choice == KEY_LEFTARROW || choice == KEY_RIGHTARROW)
|
||||
{
|
||||
gridcss_active = true;
|
||||
// fall-thru into navigation
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (choice)
|
||||
|
|
@ -8062,6 +8094,8 @@ INT32 MR_HandleSetupMultiPlayerMenu(INT32 choice)
|
|||
sortedIndex = FindSortedSkinIndex(cv_chooseskin.value);
|
||||
gridcss_row = sortedIndex % SKINGRIDWIDTH;
|
||||
gridcss_column = sortedIndex / SKINGRIDWIDTH;
|
||||
|
||||
gridcss_active = false;
|
||||
|
||||
gridcss_skinydrag = CLAMP(gridcss_column - SKINGRIDHEIGHT + 1, 0, ((numskins - 1) / SKINGRIDWIDTH) - SKINGRIDHEIGHT + 1);
|
||||
}
|
||||
|
|
@ -8088,6 +8122,8 @@ INT32 MR_HandleSetupMultiPlayerMenu(INT32 choice)
|
|||
sortedIndex = FindSortedSkinIndex(cv_chooseskin.value);
|
||||
gridcss_row = sortedIndex % SKINGRIDWIDTH;
|
||||
gridcss_column = sortedIndex / SKINGRIDWIDTH;
|
||||
|
||||
gridcss_active = false;
|
||||
|
||||
gridcss_skinydrag = CLAMP(gridcss_column - SKINGRIDHEIGHT + 1, 0, ((numskins - 1) / SKINGRIDWIDTH) - SKINGRIDHEIGHT + 1);
|
||||
}
|
||||
|
|
@ -8164,24 +8200,42 @@ INT32 MR_HandleSetupMultiPlayerMenu(INT32 choice)
|
|||
S_StartSound(NULL, sfx_menu1);
|
||||
}
|
||||
break;
|
||||
case KEY_TAB:
|
||||
M_SetItemOn(MN_MP_PLAYERSETUP, "FOLLOWER");
|
||||
case KEY_ENTER:
|
||||
if (cv_chooseskin.value < numskins)
|
||||
{
|
||||
skin_t *skin = &skins[cv_chooseskin.value];
|
||||
INT32 voxid = R_GetLocalPreferredVoiceForSkin(setupplayer, skin->name);
|
||||
kartvoice_t *voice = NULL;
|
||||
gridcss_skinmemory = cv_chooseskin.value;
|
||||
gridcss_active = false;
|
||||
|
||||
if (voxid == MAXSKINVOICES)
|
||||
{
|
||||
// Couldn't find a voice.
|
||||
// try current setting
|
||||
voxid = R_FindIDForVoice(skin, localvoicedata[setupplayer].name);
|
||||
// sorry nothing
|
||||
if (voxid == MAXSKINVOICES) voxid = 0;
|
||||
}
|
||||
voice = &skin->voices[voxid];
|
||||
S_StopSounds();
|
||||
S_StartSound(NULL, sfx_s221);
|
||||
if (voice != NULL)
|
||||
S_StartSound(NULL, R_GetLegacySkinSound(voice, sfx_kslow));
|
||||
}
|
||||
break;
|
||||
case KEY_ESCAPE:
|
||||
CV_SetValue(&cv_chooseskin, gridcss_skinmemory);
|
||||
sortedIndex = FindSortedSkinIndex(cv_chooseskin.value);
|
||||
gridcss_row = sortedIndex % SKINGRIDWIDTH;
|
||||
gridcss_column = sortedIndex / SKINGRIDWIDTH;
|
||||
|
||||
gridcss_skinydrag = CLAMP(gridcss_column - SKINGRIDHEIGHT + 1, 0, ((numskins - 1) / SKINGRIDWIDTH) - SKINGRIDHEIGHT - 1);
|
||||
gridcss_skinydrag = CLAMP(gridcss_column - SKINGRIDHEIGHT + 1, 0, ((numskins - 1) / SKINGRIDWIDTH) - SKINGRIDHEIGHT + 1);
|
||||
|
||||
gridcss_active = false;
|
||||
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
break;
|
||||
case KEY_ENTER:
|
||||
if (cv_chooseskin.value < numskins)
|
||||
{
|
||||
gridcss_skinmemory = cv_chooseskin.value;
|
||||
S_StartSound(NULL, sfx_s221);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
@ -8229,6 +8283,7 @@ INT32 MR_SetupMultiPlayer(INT32 arg)
|
|||
CV_SetValue(&cv_dummycolor, cv_playercolor[arg].value);
|
||||
G_SetPlayerControllerIndicatorColor(arg, cv_playercolor[arg].value);
|
||||
dummycolorplayer = arg;
|
||||
gridcss_active = false;
|
||||
|
||||
Skinsort_option_Onchange();
|
||||
|
||||
|
|
|
|||
|
|
@ -2557,6 +2557,20 @@ UINT32 FNV1a_QuickCaseHash(const char *message, size_t size)
|
|||
return hash;
|
||||
}
|
||||
|
||||
UINT32 FNV1a_HashLowercaseString(const char *message)
|
||||
{
|
||||
UINT32 hash = FNV1A_OFFSET_BASIS;
|
||||
|
||||
while (*message)
|
||||
{
|
||||
hash ^= tolower(*message);
|
||||
hash *= FNV1A_PRIME;
|
||||
message++;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
// Returns true if the string is empty.
|
||||
boolean M_IsStringEmpty(const char *s)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size);
|
|||
// Hashes some message using FNV-1a
|
||||
UINT32 FNV1a_QuickCaseHash(const char *message, size_t size);
|
||||
|
||||
UINT32 FNV1a_HashLowercaseString(const char *message);
|
||||
|
||||
boolean M_IsStringEmpty(const char *s);
|
||||
|
||||
int M_RoundUp(double number);
|
||||
|
|
|
|||
|
|
@ -1948,7 +1948,8 @@ void EV_DoMoveFloorByHeight(mtag_t tag, fixed_t height, fixed_t speed, mtag_t ch
|
|||
|
||||
// chained linedef executing ability
|
||||
// Only set it on one of the moving sectors (the smallest numbered)
|
||||
if (chain)
|
||||
boolean kartcheck = (mapnamespace == MNS_SRB2KART) ? firstone : true;
|
||||
if (kartcheck && chain)
|
||||
dofloor->tag = firstone ? (INT16)chain : -1;
|
||||
|
||||
// flat changing ability
|
||||
|
|
|
|||
26
src/p_map.c
26
src/p_map.c
|
|
@ -1356,6 +1356,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
|
||||
// Damage other players when possible.
|
||||
// Handle before bumpcode to prevent lower weights from getting affected.
|
||||
boolean pvp_hit = false;
|
||||
if (g_tm.thing->player && thing->player
|
||||
// Make sure they aren't able to damage you ANYWHERE along the Z axis, you have to be TOUCHING the person.
|
||||
&& !(thing->z + thing->height < g_tm.thing->z || thing->z > g_tm.thing->z + g_tm.thing->height))
|
||||
|
|
@ -1363,7 +1364,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
mobj_t *mo1 = g_tm.thing;
|
||||
mobj_t *mo2 = thing;
|
||||
|
||||
K_PvPTouchDamage(mo1, mo2);
|
||||
pvp_hit = K_PvPTouchDamage(mo1, mo2);
|
||||
}
|
||||
|
||||
if (thing->player)
|
||||
|
|
@ -1402,6 +1403,21 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
}
|
||||
else if (thing->player) // bounce when players collide
|
||||
{
|
||||
boolean cannotbump = false;
|
||||
|
||||
switch(playerbumpactive)
|
||||
{
|
||||
case PBUMP_GNDONLY:
|
||||
cannotbump = ((!P_IsObjectOnGround(thing) || !P_IsObjectOnGround(g_tm.thing)) && !pvp_hit);
|
||||
break;
|
||||
case PBUMP_NONE:
|
||||
cannotbump = !pvp_hit;
|
||||
break;
|
||||
case PBUMP_STANDARD:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// see if it went over / under
|
||||
if (g_tm.thing->z > thing->z + thing->height)
|
||||
return BMIT_CONTINUE; // overhead
|
||||
|
|
@ -1436,6 +1452,14 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
||||
if (cannotbump) // Unable to actually bump; don't process things further
|
||||
{
|
||||
// Just in case...
|
||||
g_tm.thing->player->justbumped = min(24, g_tm.thing->player->justbumped + 6);
|
||||
thing->player->justbumped = min(24, thing->player->justbumped + 6);
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
||||
if (thing->player->squishedtimer || thing->player->hyudorotimer
|
||||
|| thing->player->justbumped || thing->scale > g_tm.thing->scale + (mapobjectscale/8)
|
||||
|| g_tm.thing->player->squishedtimer || g_tm.thing->player->hyudorotimer
|
||||
|
|
|
|||
|
|
@ -4352,6 +4352,7 @@ static boolean P_NetSyncMisc(savebuffer_t *save, boolean resending)
|
|||
SYNC(airdropactive);
|
||||
SYNC(bumpsparkactive);
|
||||
SYNC(antibumptime);
|
||||
SYNC(playerbumpactive);
|
||||
|
||||
for (i = 0; i < sizeof(g_voteLevels)/sizeof(*g_voteLevels); i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ boolean itempushing;
|
|||
UINT8 bumpsparkactive;
|
||||
boolean itemlistactive;
|
||||
boolean trailslow_active = false;
|
||||
playerbumptype_t playerbumpactive = PBUMP_STANDARD;
|
||||
UINT16 bossdisabled;
|
||||
boolean stoppedclock;
|
||||
boolean levelloading;
|
||||
|
|
@ -8255,6 +8256,7 @@ static void P_InitLevelSettings(boolean reloadinggamestate)
|
|||
trailslow_active = false;
|
||||
bumpsparkactive = 0;
|
||||
antibumptime = 0;
|
||||
playerbumpactive = PBUMP_STANDARD;
|
||||
|
||||
if (cv_kartrings.value)
|
||||
ringsactive = true;
|
||||
|
|
@ -8304,6 +8306,7 @@ static void P_InitLevelSettings(boolean reloadinggamestate)
|
|||
trailslow_active = true;
|
||||
|
||||
bumpsparkactive = (UINT8)cv_kartbumpspark.value;
|
||||
playerbumpactive = (playerbumptype_t)cv_playerbump.value;
|
||||
|
||||
antibumptime = (tic_t)cv_kartantibump.value * TICRATE;
|
||||
|
||||
|
|
|
|||
|
|
@ -2637,15 +2637,6 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
|
|||
return false;
|
||||
}
|
||||
|
||||
if (mapnamespace == MNS_SRB2KART)
|
||||
{
|
||||
if (line->args[2] != TMP_CEILING)
|
||||
EV_DoFloor(line->args[1], line, moveFloorByFrontSector);
|
||||
if (line->args[2] != TMP_FLOOR)
|
||||
EV_DoCeiling(line->args[1], line, moveCeilingByFrontSector);
|
||||
break;
|
||||
}
|
||||
|
||||
copySector = line->frontsector;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
27
src/p_user.c
27
src/p_user.c
|
|
@ -50,6 +50,11 @@
|
|||
// Thok camera snap (ctrl-f "chalupa")
|
||||
#include "g_input.h"
|
||||
#include "m_menu.h" // menustack
|
||||
#include "screen.h"
|
||||
|
||||
#ifdef HAVE_VR
|
||||
#include "vr/vr_main.h"
|
||||
#endif
|
||||
|
||||
// SRB2kart
|
||||
#include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems
|
||||
|
|
@ -192,11 +197,20 @@ fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move)
|
|||
//
|
||||
boolean P_AutoPause(void)
|
||||
{
|
||||
boolean focuspause;
|
||||
|
||||
// Don't pause even on menu-up or focus-lost in netgames or record attack
|
||||
if (netgame || modeattacking || gamestate == GS_TITLESCREEN)
|
||||
return false;
|
||||
|
||||
return ((menustack[0] && !demo.playback) || ( window_notinfocus && cv_pauseifunfocused.value ));
|
||||
focuspause = (window_notinfocus && cv_pauseifunfocused.value);
|
||||
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started)
|
||||
focuspause = false;
|
||||
#endif
|
||||
|
||||
return ((menustack[0] && !demo.playback) || focuspause);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -3187,10 +3201,17 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
|
|||
const INT32 timeovercam = max(0, min(180, (player->karthud[khud_timeovercam] - 2*TICRATE)*15));
|
||||
camrotate += timeovercam;
|
||||
}
|
||||
else if (leveltime < introtime && !(modeattacking && !demo.playback)) // Whoooshy camera! (don't do this in RA when we PLAY, still do it in replays however~)
|
||||
else if (leveltime < introtime && !(modeattacking && !demo.playback)
|
||||
#ifdef HAVE_VR
|
||||
&& (!vr_started || cv_vrtrackintro.value)
|
||||
#endif
|
||||
) // Whoooshy camera! (don't do this in RA when we PLAY, still do it in replays however~)
|
||||
{
|
||||
const INT32 introcam = (introtime - leveltime);
|
||||
camrotate += introcam*5;
|
||||
#ifdef HAVE_VR
|
||||
if (!vr_started || cv_vrtrackintro.value > 1)
|
||||
#endif
|
||||
camrotate += introcam*5;
|
||||
camdist += (introcam * mapobjectscale)*3;
|
||||
camheight += (introcam * mapobjectscale)*2;
|
||||
}
|
||||
|
|
|
|||
12
src/r_fps.c
12
src/r_fps.c
|
|
@ -28,6 +28,9 @@
|
|||
#ifdef HWRENDER
|
||||
#include "hardware/hw_main.h"
|
||||
#endif
|
||||
#ifdef HAVE_VR
|
||||
#include "vr/vr_main.h"
|
||||
#endif
|
||||
|
||||
// The fraction of a tic being drawn (for interpolation between two tics)
|
||||
static fixed_t rendertimefrac;
|
||||
|
|
@ -55,6 +58,15 @@ UINT32 R_GetFramerateCap(void)
|
|||
return TICRATE;
|
||||
}
|
||||
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started)
|
||||
{
|
||||
// In VR, OpenXR xrWaitFrame is the display clock. Returning zero keeps
|
||||
// the legacy desktop cap from reducing rendering to TICRATE or half-rate.
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cv_fpscap.value == 0)
|
||||
{
|
||||
// 0: Match refresh rate
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ extern "C"
|
|||
return false;
|
||||
}
|
||||
|
||||
fprintf(f, va("// BlanKart preferences file for local player %d.\n", snum + 1));
|
||||
fprintf(f, "%s", va("// BlanKart preferences file for local player %d.\n", snum + 1));
|
||||
fprintf(
|
||||
f,
|
||||
"// Due to the nature of the unordered map system, data here may be shuffled!\n");
|
||||
|
|
@ -398,7 +398,7 @@ extern "C"
|
|||
file_output.copy(voicepref_buffer, VOICEPREFBUFSIZE);
|
||||
voicepref_buffer[VOICEPREFBUFSIZE - 1] = 0;
|
||||
|
||||
fprintf(f, voicepref_buffer);
|
||||
fprintf(f, "%s",voicepref_buffer);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
|
@ -432,7 +432,7 @@ extern "C"
|
|||
INT32 strikes = 0;
|
||||
|
||||
std::vector<std::string> data_to_load[2];
|
||||
char name_buffer[2][VOICENAMESIZE] = {0};
|
||||
char name_buffer[2][VOICENAMESIZE] = {{0}};
|
||||
|
||||
for (snum = 0; snum < MAXSPLITSCREENPLAYERS; snum++)
|
||||
{
|
||||
|
|
|
|||
78
src/screen.c
78
src/screen.c
|
|
@ -36,6 +36,10 @@
|
|||
#include "p_local.h" // P_AutoPause()
|
||||
#include "m_menu.h"
|
||||
|
||||
#ifdef HAVE_VR
|
||||
#include "vr/vr_main.h"
|
||||
#endif
|
||||
|
||||
#ifdef HWRENDER
|
||||
#include "hardware/hw_main.h"
|
||||
#include "hardware/hw_glob.h"
|
||||
|
|
@ -72,6 +76,46 @@ static void Highreshudscale_OnChange(void);
|
|||
static CV_PossibleValue_t highreshudscale_cons_t[] = {{4*FRACUNIT/5, "MIN"}, {6*FRACUNIT/5, "MAX"}, {0, NULL}};
|
||||
consvar_t cv_highreshudscale = CVAR_INIT ("highreshudscale", "1", CV_SAVE|CV_FLOAT|CV_CALL|CV_NOINIT|CV_NOSHOWHELP, highreshudscale_cons_t, Highreshudscale_OnChange);
|
||||
|
||||
static void SCR_ChangeVR(void);
|
||||
static void SCR_ChangeScaleVR(void);
|
||||
static void SCR_ChangeResolutionVR(void);
|
||||
|
||||
static CV_PossibleValue_t vrviewmode_cons_t[] = {{-1, "Hide"}, {0, "Left Eye"}, {1, "Right Eye"}, {0, NULL}};
|
||||
consvar_t cv_vrviewmode = CVAR_INIT ("vrviewmode", "0", CV_SAVE, vrviewmode_cons_t, NULL);
|
||||
|
||||
static CV_PossibleValue_t vrtrackintro_cons_t[] = {{0, "Off"}, {1, "Gentle"}, {2, "Standard"}, {0, NULL}};
|
||||
consvar_t cv_vrtrackintro = CVAR_INIT ("vrtrackintro", "1", CV_SAVE, vrtrackintro_cons_t, NULL);
|
||||
|
||||
consvar_t cv_vrcomfortmode = CVAR_INIT ("vrcomfortmode", "No", CV_SAVE, CV_YesNo, NULL);
|
||||
consvar_t cv_vrdisableskystereo = CVAR_INIT ("vrdisableskystereo", "No", CV_SAVE, CV_YesNo, NULL);
|
||||
consvar_t cv_vrplayerscale = CVAR_INIT ("vrplayerscale", "Yes", CV_SAVE, CV_YesNo, NULL);
|
||||
consvar_t cv_vrspriterotate = CVAR_INIT ("vrspriterotate", "Yes", CV_SAVE, CV_YesNo, NULL);
|
||||
consvar_t cv_vruidistance = CVAR_INIT ("vruidistance", "105", CV_SAVE, CV_Unsigned, NULL);
|
||||
consvar_t cv_vruiscale = CVAR_INIT ("vruiscale", "50", CV_SAVE, CV_Unsigned, NULL);
|
||||
|
||||
static CV_PossibleValue_t vruimode_cons_t[] = {{0, "InEye"}, {1, "Quad"}, {0, NULL}};
|
||||
consvar_t cv_vruimode = CVAR_INIT ("vruimode", "Quad", CV_SAVE, vruimode_cons_t, NULL);
|
||||
|
||||
static CV_PossibleValue_t vrposemode_cons_t[] = {
|
||||
{0, "OpenXRView"},
|
||||
{1, "OpenVRSplit"},
|
||||
{2, "OpenXRDebugPose"},
|
||||
{3, "OpenVRDebugPose"},
|
||||
{0, NULL}
|
||||
};
|
||||
consvar_t cv_vrposemode = CVAR_INIT ("vrposemode", "OpenXRView", CV_SAVE, vrposemode_cons_t, NULL);
|
||||
|
||||
static CV_PossibleValue_t vrresolution_cons_t[] = {
|
||||
{0, "50%"}, {1, "75%"}, {2, "100%"}, {3, "125%"},
|
||||
{4, "150%"}, {5, "175%"}, {6, "200%"}, {0, NULL}
|
||||
};
|
||||
consvar_t cv_vrresolution = CVAR_INIT ("vrresolution", "2", CV_SAVE|CV_NOINIT|CV_CALL, vrresolution_cons_t, SCR_ChangeResolutionVR);
|
||||
|
||||
static CV_PossibleValue_t vrscale_cons_t[] = {{0, "Small"}, {1, "Standard"}, {2, "Large"}, {0, NULL}};
|
||||
consvar_t cv_vrscale = CVAR_INIT ("vrscale", "1", CV_SAVE|CV_NOINIT|CV_CALL, vrscale_cons_t, SCR_ChangeScaleVR);
|
||||
|
||||
consvar_t cv_vrenabled = CVAR_INIT ("vrenabled", "Off", CV_SAVE|CV_CALL, CV_OnOff, SCR_ChangeVR);
|
||||
|
||||
static void Highreshudscale_OnChange(void)
|
||||
{
|
||||
SCR_Recalc();
|
||||
|
|
@ -514,6 +558,40 @@ void SCR_ChangeFullscreen(void)
|
|||
return;
|
||||
}
|
||||
|
||||
static void SCR_ChangeVR(void)
|
||||
{
|
||||
#ifdef HAVE_VR
|
||||
vr_enabled = (cv_vrenabled.value != 0);
|
||||
|
||||
if (!graphics_started)
|
||||
return;
|
||||
|
||||
if (cv_vrenabled.value)
|
||||
VR_Init();
|
||||
else
|
||||
VR_Shutdown();
|
||||
#else
|
||||
if (cv_vrenabled.value)
|
||||
CONS_Alert(CONS_ERROR, "This build does not include OpenXR support.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SCR_ChangeScaleVR(void)
|
||||
{
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started)
|
||||
CONS_Printf("VR: world scale changed; new tracking scale applies next frame.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SCR_ChangeResolutionVR(void)
|
||||
{
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started)
|
||||
CONS_Printf("VR: vrresolution changes require restarting VR so OpenXR swapchains can be recreated.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void SCR_ChangeRenderer(void)
|
||||
{
|
||||
if (chosenrendermode != render_none
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ extern CV_PossibleValue_t cv_renderer_t[];
|
|||
extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_renderhitbox, cv_fullscreen;
|
||||
extern consvar_t cv_highreshudscale;
|
||||
extern consvar_t cv_vhseffect, cv_shittyscreen, cv_votebgscaling;
|
||||
extern consvar_t cv_vrviewmode, cv_vrcomfortmode, cv_vrenabled, cv_vrresolution, cv_vrscale;
|
||||
extern consvar_t cv_vruidistance, cv_vruiscale, cv_vruimode, cv_vrposemode, cv_vrplayerscale, cv_vrspriterotate;
|
||||
extern consvar_t cv_vrdisableskystereo, cv_vrtrackintro;
|
||||
extern consvar_t cv_parallelsoftware;
|
||||
extern consvar_t cv_accuratefps;
|
||||
|
||||
|
|
|
|||
|
|
@ -1281,6 +1281,69 @@ void I_Sleep(UINT32 ms)
|
|||
SDL_Delay(ms);
|
||||
}
|
||||
|
||||
void I_SleepDuration(precise_t duration)
|
||||
{
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__OpenBSD__)
|
||||
UINT64 precision = I_GetPrecisePrecision();
|
||||
precise_t dest = I_GetPreciseTime() + duration;
|
||||
#ifdef __OpenBSD__
|
||||
precise_t slack = (precision / 50); // 20 ms slack
|
||||
#else
|
||||
precise_t slack = (precision / 5000); // 0.2 ms slack
|
||||
#endif
|
||||
if (duration > slack)
|
||||
{
|
||||
duration -= slack;
|
||||
struct timespec ts = {
|
||||
.tv_sec = static_cast<__time_t>(duration / precision),
|
||||
.tv_nsec = static_cast<__syscall_slong_t>(duration * 1000000000 / precision % 1000000000),
|
||||
};
|
||||
int status;
|
||||
#ifdef __OpenBSD__
|
||||
do status = nanosleep(&ts, &ts);
|
||||
#else
|
||||
do status = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts);
|
||||
#endif
|
||||
while (status == EINTR);
|
||||
}
|
||||
|
||||
// busy-wait the rest
|
||||
while (((INT64)dest - (INT64)I_GetPreciseTime()) > 0);
|
||||
#elif defined (MIN_SLEEP_DURATION_MS)
|
||||
UINT64 precision = I_GetPrecisePrecision();
|
||||
INT32 sleepvalue = cv_sleep.value;
|
||||
UINT64 delaygranularity;
|
||||
precise_t cur;
|
||||
precise_t dest;
|
||||
|
||||
{
|
||||
double gran = round(((double)(precision / 1000) * sleepvalue * MIN_SLEEP_DURATION_MS));
|
||||
delaygranularity = (UINT64)gran;
|
||||
}
|
||||
|
||||
cur = I_GetPreciseTime();
|
||||
dest = cur + duration;
|
||||
|
||||
// the reason this is not dest > cur is because the precise counter may wrap
|
||||
// two's complement arithmetic is our friend here, though!
|
||||
// e.g. cur 0xFFFFFFFFFFFFFFFE = -2, dest 0x0000000000000001 = 1
|
||||
// 0x0000000000000001 - 0xFFFFFFFFFFFFFFFE = 3
|
||||
while ((INT64)(dest - cur) > 0)
|
||||
{
|
||||
// If our cv_sleep value exceeds the remaining sleep duration, use the
|
||||
// hard sleep function.
|
||||
if (sleepvalue > 0 && (dest - cur) > delaygranularity)
|
||||
{
|
||||
I_Sleep(sleepvalue);
|
||||
}
|
||||
|
||||
// Otherwise, this is a spinloop.
|
||||
|
||||
cur = I_GetPreciseTime();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NEWSIGNALHANDLER
|
||||
static void newsignalhandler_Warn(const char *pr)
|
||||
{
|
||||
|
|
@ -1321,6 +1384,18 @@ static void I_Fork(void)
|
|||
I_RegisterChildSignals();
|
||||
break;
|
||||
default:
|
||||
// ignore those, those are handled by child process
|
||||
// otherwise parent might exit before it
|
||||
// and the below stuff wont run and your terminal will be left in an awkward state
|
||||
#ifdef SIGINT
|
||||
signal(SIGINT, SIG_IGN);
|
||||
#endif
|
||||
#ifdef SIGBREAK
|
||||
signal(SIGBREAK, SIG_IGN);
|
||||
#endif
|
||||
#ifdef SIGTERM
|
||||
signal(SIGTERM, SIG_IGN);
|
||||
#endif
|
||||
if (logstream)
|
||||
fclose(logstream);/* the child has this */
|
||||
|
||||
|
|
@ -1386,6 +1461,7 @@ INT32 I_StartupSystem(void)
|
|||
#if SDL_VERSION_ATLEAST(2,0,22)
|
||||
SDL_SetHint(SDL_HINT_APP_NAME, "BlanKart");
|
||||
#endif
|
||||
SDL_SetHint("SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS", "1");
|
||||
if (!SDL_Init(0))
|
||||
I_Error("SRB2Kart: SDL System Error: %s", SDL_GetError()); //Alam: Oh no....
|
||||
#ifndef NOMUMBLE
|
||||
|
|
@ -1444,7 +1520,6 @@ void I_Quit(void)
|
|||
if (myargmalloc)
|
||||
free(myargv); // Deallocate allocated memory
|
||||
|
||||
W_Shutdown();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
|
@ -1522,7 +1597,6 @@ FUNCIERROR void ATTRNORETURN I_Error(const char *error, ...)
|
|||
"SRB2Kart V2 " VERSIONSTRING " Recursive Error",
|
||||
buffer, NULL);
|
||||
|
||||
W_Shutdown();
|
||||
exit(-1); // recursive errors detected
|
||||
}
|
||||
}
|
||||
|
|
@ -1570,8 +1644,6 @@ FUNCIERROR void ATTRNORETURN I_Error(const char *error, ...)
|
|||
I_ShutdownSystem();
|
||||
SDL_Quit();
|
||||
|
||||
W_Shutdown();
|
||||
|
||||
#if defined (PARANOIA) || defined (DEVELOP)
|
||||
*(volatile INT32 *)0 = 4; //Alam: Debug!
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -54,6 +54,11 @@
|
|||
#include "../d_main.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../i_sound.h" // midi pause/unpause
|
||||
|
||||
#ifdef HAVE_VR
|
||||
#include "../vr/vr_main.h"
|
||||
#endif
|
||||
|
||||
#include "../i_gamepad.h"
|
||||
#include "../st_stuff.h"
|
||||
#include "../hu_stuff.h"
|
||||
|
|
@ -545,6 +550,11 @@ static void VID_Command_Mode_f (void)
|
|||
|
||||
static void Impl_SetFocused(boolean focused)
|
||||
{
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started)
|
||||
focused = true;
|
||||
#endif
|
||||
|
||||
window_notinfocus = !focused;
|
||||
|
||||
if (window_notinfocus)
|
||||
|
|
@ -1316,51 +1326,60 @@ void I_FinishUpdate(void)
|
|||
if (rendermode == render_none)
|
||||
return; //Alam: No software or OpenGl surface
|
||||
|
||||
SCR_CalculateFPS();
|
||||
#ifdef HAVE_VR
|
||||
const boolean vr_draw_ui = (!vr_started || vr_render_pass == VR_PASS_NONE || vr_render_pass == VR_PASS_UI);
|
||||
#else
|
||||
const boolean vr_draw_ui = true;
|
||||
#endif
|
||||
|
||||
if (st_overlay)
|
||||
if (vr_draw_ui)
|
||||
{
|
||||
if (cv_ticrate.value)
|
||||
SCR_DisplayTicRate();
|
||||
SCR_CalculateFPS();
|
||||
|
||||
if (cv_showping.value && netgame &&
|
||||
( consoleplayer != serverplayer || (server_lagless == 0 || server_lagless == -1) ))
|
||||
if (st_overlay)
|
||||
{
|
||||
if (server_lagless == 1)
|
||||
if (cv_ticrate.value)
|
||||
SCR_DisplayTicRate();
|
||||
|
||||
if (cv_showping.value && netgame &&
|
||||
( consoleplayer != serverplayer || (server_lagless == 0 || server_lagless == -1) ))
|
||||
{
|
||||
if (consoleplayer != serverplayer)
|
||||
SCR_DisplayLocalPing();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (
|
||||
player = 1;
|
||||
player < MAXPLAYERS;
|
||||
player++
|
||||
){
|
||||
if (D_IsPlayerHumanAndGaming(player))
|
||||
{
|
||||
if (server_lagless == 1)
|
||||
{
|
||||
if (consoleplayer != serverplayer)
|
||||
SCR_DisplayLocalPing();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (
|
||||
player = 1;
|
||||
player < MAXPLAYERS;
|
||||
player++
|
||||
){
|
||||
if (D_IsPlayerHumanAndGaming(player))
|
||||
{
|
||||
SCR_DisplayLocalPing();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cv_mindelay.value && consoleplayer == serverplayer && Playing())
|
||||
SCR_DisplayLocalPing();
|
||||
}
|
||||
if (cv_mindelay.value && consoleplayer == serverplayer && Playing())
|
||||
SCR_DisplayLocalPing();
|
||||
}
|
||||
|
||||
if (marathonmode)
|
||||
SCR_DisplayMarathonInfo();
|
||||
if (marathonmode)
|
||||
SCR_DisplayMarathonInfo();
|
||||
|
||||
// draw captions if enabled
|
||||
if (cv_closedcaptioning.value)
|
||||
SCR_ClosedCaptions();
|
||||
// draw captions if enabled
|
||||
if (cv_closedcaptioning.value)
|
||||
SCR_ClosedCaptions();
|
||||
|
||||
#ifdef HAVE_DISCORDRPC
|
||||
if (discordRequestList != NULL)
|
||||
ST_AskToJoinEnvelope();
|
||||
if (discordRequestList != NULL)
|
||||
ST_AskToJoinEnvelope();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (rendermode == render_soft && vid.screens[0])
|
||||
{
|
||||
|
|
@ -1389,7 +1408,12 @@ void I_FinishUpdate(void)
|
|||
else if (rendermode == render_opengl)
|
||||
{
|
||||
// Final postprocess step of palette rendering, after everything else has been drawn.
|
||||
if (HWR_ShouldUsePaletteRendering())
|
||||
#ifdef HAVE_VR
|
||||
const boolean vr_ui_pass = (vr_started && vr_render_pass == VR_PASS_UI);
|
||||
#else
|
||||
const boolean vr_ui_pass = false;
|
||||
#endif
|
||||
if (!vr_ui_pass && HWR_ShouldUsePaletteRendering())
|
||||
{
|
||||
GL_MakeScreenTexture(HWD_SCREENTEXTURE_GENERIC2);
|
||||
GL_SetShader(HWR_GetShaderFromTarget(SHADER_PALETTE_POSTPROCESS));
|
||||
|
|
@ -1736,7 +1760,12 @@ void I_StartupGraphics(void)
|
|||
return;
|
||||
|
||||
disable_mouse = static_cast<bool>(M_CheckParm("-nomouse"));
|
||||
disable_fullscreen = M_CheckParm("-win") ? true : false;
|
||||
#ifdef HAVE_VR
|
||||
const bool wantVR = (M_CheckParm("-openvr") || M_CheckParm("-vr") || cv_vrenabled.value);
|
||||
#else
|
||||
const bool wantVR = false;
|
||||
#endif
|
||||
disable_fullscreen = (M_CheckParm("-win") || wantVR) ? true : false;
|
||||
|
||||
keyboard_started = true;
|
||||
|
||||
|
|
@ -1786,6 +1815,8 @@ void I_StartupGraphics(void)
|
|||
// Choose OpenGL renderer
|
||||
else if (M_CheckParm("-opengl"))
|
||||
chosenrendermode = render_opengl;
|
||||
else if (wantVR)
|
||||
chosenrendermode = render_opengl;
|
||||
|
||||
// Don't startup OpenGL
|
||||
if (M_CheckParm("-nogl"))
|
||||
|
|
@ -1823,6 +1854,10 @@ void I_StartupGraphics(void)
|
|||
|
||||
VID_SetMode(VID_GetModeForSize(vid.width, vid.height));
|
||||
|
||||
#ifdef HAVE_VR
|
||||
VR_Init();
|
||||
#endif
|
||||
|
||||
SDLdoUngrabMouse();
|
||||
|
||||
SDL_RaiseWindow(window);
|
||||
|
|
@ -1902,5 +1937,3 @@ void I_SetBorderlessWindow(void)
|
|||
SDL_SetWindowBordered(window, bordered);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "../doomdef.h"
|
||||
#include "../d_main.h"
|
||||
#include "../screen.h"
|
||||
|
||||
#ifdef HWRENDER
|
||||
#include "../hardware/r_opengl/r_opengl.h"
|
||||
|
|
@ -171,34 +172,80 @@ boolean OglSdlSurface(INT32 w, INT32 h)
|
|||
|
||||
\return void
|
||||
*/
|
||||
#ifdef HAVE_VR
|
||||
#include "../vr/vr_main.h"
|
||||
#include "../vr/vr_render.h"
|
||||
#endif
|
||||
|
||||
void OglSdlFinishUpdate(boolean waitvbl)
|
||||
{
|
||||
static boolean oldwaitvbl = false;
|
||||
static int oldwaitvbl = -1;
|
||||
int sdlw, sdlh;
|
||||
if (oldwaitvbl != waitvbl)
|
||||
#ifdef HAVE_VR
|
||||
const boolean effective_waitvbl = (vr_started ? false : waitvbl);
|
||||
#else
|
||||
const boolean effective_waitvbl = waitvbl;
|
||||
#endif
|
||||
if (oldwaitvbl != (int)effective_waitvbl)
|
||||
{
|
||||
SDL_GL_SetSwapInterval(waitvbl);
|
||||
SDL_GL_SetSwapInterval(effective_waitvbl);
|
||||
}
|
||||
|
||||
oldwaitvbl = waitvbl;
|
||||
oldwaitvbl = (int)effective_waitvbl;
|
||||
|
||||
SDL_GetWindowSizeInPixels(window, &sdlw, &sdlh);
|
||||
|
||||
HWR_MakeScreenFinalTexture();
|
||||
if (gl_shadersavailable)
|
||||
GL_SetShader(HWR_GetShaderFromTarget(SHADER_FINAL_POST_PROCESS));
|
||||
HWR_DrawScreenFinalTexture(sdlw, sdlh);
|
||||
if (gl_shadersavailable)
|
||||
GL_UnSetShader();
|
||||
SDL_GL_SwapWindow(window);
|
||||
#ifdef HAVE_VR
|
||||
if (vr_started && vr_render_pass != VR_PASS_NONE)
|
||||
{
|
||||
if (vr_render_pass == VR_PASS_UI)
|
||||
return;
|
||||
|
||||
GL_GClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
|
||||
if (cv_vruimode.value == 0)
|
||||
{
|
||||
if (vr_current_eye == cv_vrviewmode.value)
|
||||
{
|
||||
if (VR_MirrorEyeToDefaultFramebuffer(sdlw, sdlh))
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
|
||||
// Sryder: We need to draw the final screen texture again into the other buffer in the original position so that
|
||||
// effects that want to take the old screen can do so after this
|
||||
// Generic2 has the screen image without palette rendering brightness adjustments.
|
||||
// Using that here will prevent brightness adjustments being applied twice.
|
||||
GL_DrawScreenTexture(HWD_SCREENTEXTURE_GENERIC2, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Optional desktop mirror of one eye. Use an FBO blit so the mirror
|
||||
// does not copy a 4096x4096 screen texture or write back into the
|
||||
// headset swapchain.
|
||||
if (vr_current_eye == cv_vrviewmode.value)
|
||||
{
|
||||
if (VR_MirrorEyeToDefaultFramebuffer(sdlw, sdlh))
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
HWR_MakeScreenFinalTexture();
|
||||
if (gl_shadersavailable)
|
||||
GL_SetShader(HWR_GetShaderFromTarget(SHADER_FINAL_POST_PROCESS));
|
||||
HWR_DrawScreenFinalTexture(sdlw, sdlh);
|
||||
if (gl_shadersavailable)
|
||||
GL_UnSetShader();
|
||||
SDL_GL_SwapWindow(window);
|
||||
|
||||
GL_GClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
|
||||
|
||||
// Sryder: We need to draw the final screen texture again into the other buffer in the original position so that
|
||||
// effects that want to take the old screen can do so after this
|
||||
// Generic2 has the screen image without palette rendering brightness adjustments.
|
||||
// Using that here will prevent brightness adjustments being applied twice.
|
||||
if (gl_shadersavailable)
|
||||
GL_SetShader(HWR_GetShaderFromTarget(SHADER_FINAL_POST_PROCESS));
|
||||
HWR_DrawScreenFinalTexture(sdlw, sdlh);
|
||||
if (gl_shadersavailable)
|
||||
GL_UnSetShader();
|
||||
}
|
||||
}
|
||||
|
||||
#endif //HWRENDER
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ void ST_LoadGraphics(void)
|
|||
|
||||
static boolean ST_IconCanSpinout(UINT8 icon)
|
||||
{
|
||||
return ((icon == FACE_MINIMAP)||(icon == FACE_RANK));
|
||||
return ((icon == FACE_MINIMAP)||(icon == FACE_RANK)||(icon == FACE_WANTED));
|
||||
}
|
||||
|
||||
// made separate so that skins code can reload custom face graphics
|
||||
|
|
@ -215,7 +215,7 @@ void ST_LoadFaceGraphics(INT32 skinnum)
|
|||
|
||||
if ((ST_IconCanSpinout(i)) && (!alreadycentered))
|
||||
{
|
||||
// Auto-center the pivots of all minimap and rank patches with
|
||||
// Auto-center the pivots of all minimap ,rank and wanted patches with
|
||||
// the ability to spinout.
|
||||
// This is a shitty, hacky solution... but it's less work for spinout
|
||||
// rotations!
|
||||
|
|
@ -982,6 +982,8 @@ void ST_AskToJoinEnvelope(void)
|
|||
|
||||
static fixed_t ST_CalculateFadeIn(player_t *player)
|
||||
{
|
||||
(void)player;
|
||||
|
||||
const tic_t length = TICRATE/4;
|
||||
|
||||
tic_t timer = lt_exitticker;
|
||||
|
|
|
|||
|
|
@ -920,11 +920,41 @@ affine_bounding_t* V_GetAffineBounds(const affine_t* transform,
|
|||
return out;
|
||||
}
|
||||
|
||||
static UINT32 V_GetAlphaLevel(INT32 scrn)
|
||||
{
|
||||
switch (scrn & V_ALPHAMASK)
|
||||
{
|
||||
case V_HUDTRANSHALF:
|
||||
return hudminusalpha[V_GetHUDTranslucency(scrn)];
|
||||
|
||||
case V_HUDTRANS:
|
||||
return 10 - V_GetHUDTranslucency(scrn);
|
||||
|
||||
case V_HUDTRANSDOUBLE:
|
||||
return hudplusalpha[V_GetHUDTranslucency(scrn)];
|
||||
|
||||
default:
|
||||
return (scrn & V_ALPHAMASK) >> V_ALPHASHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
void V_DrawAffinePatch(fixed_t x, fixed_t y, const affine_t *transform, INT32 scrn, patch_t *patch, const UINT8 *colormap)
|
||||
{
|
||||
UINT32 alphalevel = 0, blendmode = 0;
|
||||
UINT8 patchdrawtype = STANDARDDRAW;
|
||||
|
||||
if (rendermode == render_none)
|
||||
return;
|
||||
|
||||
if ((blendmode = ((scrn & V_BLENDMASK) >> V_BLENDSHIFT)))
|
||||
blendmode++; // realign to constants
|
||||
|
||||
if ((alphalevel = V_GetAlphaLevel(scrn)) >= 10)
|
||||
return;
|
||||
|
||||
if ((v_translevel = R_GetBlendTable(blendmode, alphalevel)))
|
||||
patchdrawtype = TRANSLUCENTDRAW;
|
||||
|
||||
INT32 dup;
|
||||
switch (scrn & V_SCALEPATCHMASK)
|
||||
{
|
||||
|
|
@ -1031,6 +1061,7 @@ void V_DrawAffinePatch(fixed_t x, fixed_t y, const affine_t *transform, INT32 sc
|
|||
|
||||
UINT8 * const destbase = (UINT8 * const)(dest_y + xx);
|
||||
INT32 dx = 0, dy = 0;
|
||||
UINT8 mypixel = TRANSPARENTPIXEL;
|
||||
for (dy = 0; dy < ymax; dy++)
|
||||
{
|
||||
// yoinked from NovaSquirrel's mode 7 preview
|
||||
|
|
@ -1056,9 +1087,20 @@ void V_DrawAffinePatch(fixed_t x, fixed_t y, const affine_t *transform, INT32 sc
|
|||
continue;
|
||||
|
||||
if (colormap != NULL)
|
||||
*dest = colormap[pixel & 0xff];
|
||||
mypixel = colormap[pixel & 0xff];
|
||||
else
|
||||
*dest = (UINT8)(pixel & 0xff);
|
||||
mypixel = (UINT8)(pixel & 0xff);
|
||||
|
||||
if (patchdrawtype == TRANSLUCENTDRAW)
|
||||
{
|
||||
// Translucent
|
||||
*dest = *(v_translevel + (((mypixel)<<8)&0xff00) + (*dest&0xff));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Opaque
|
||||
*dest = mypixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1118,24 +1160,6 @@ UINT32 V_GetHUDTranslucency(INT32 scrn)
|
|||
return st_translucency;
|
||||
}
|
||||
|
||||
static UINT32 V_GetAlphaLevel(INT32 scrn)
|
||||
{
|
||||
switch (scrn & V_ALPHAMASK)
|
||||
{
|
||||
case V_HUDTRANSHALF:
|
||||
return hudminusalpha[V_GetHUDTranslucency(scrn)];
|
||||
|
||||
case V_HUDTRANS:
|
||||
return 10 - V_GetHUDTranslucency(scrn);
|
||||
|
||||
case V_HUDTRANSDOUBLE:
|
||||
return hudplusalpha[V_GetHUDTranslucency(scrn)];
|
||||
|
||||
default:
|
||||
return (scrn & V_ALPHAMASK) >> V_ALPHASHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
// Draws a patch scaled to arbitrary size.
|
||||
void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap)
|
||||
{
|
||||
|
|
|
|||
601
src/vr/vr_main.c
Normal file
601
src/vr/vr_main.c
Normal file
|
|
@ -0,0 +1,601 @@
|
|||
#include "vr_main.h"
|
||||
|
||||
#ifdef HAVE_VR
|
||||
|
||||
#include "vr_render.h"
|
||||
#include "vr_math.h"
|
||||
#include "../m_argv.h"
|
||||
#include "../console.h"
|
||||
#include "../screen.h"
|
||||
#include "../i_video.h"
|
||||
#include "../r_fps.h"
|
||||
#include "../v_video.h"
|
||||
#include <stdbool.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include "../sdl/sdlmain.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef __linux__
|
||||
#include <GL/glx.h>
|
||||
#endif
|
||||
|
||||
int vr_current_eye = 0;
|
||||
boolean vr_started = false;
|
||||
boolean vr_enabled = false;
|
||||
boolean vr_drawing_ui = false;
|
||||
vr_render_pass_t vr_render_pass = VR_PASS_NONE;
|
||||
|
||||
XrInstance vr_instance = XR_NULL_HANDLE;
|
||||
XrSession vr_session = XR_NULL_HANDLE;
|
||||
XrSystemId vr_system_id = XR_NULL_SYSTEM_ID;
|
||||
XrSpace vr_local_space = XR_NULL_HANDLE;
|
||||
XrSpace vr_view_space = XR_NULL_HANDLE;
|
||||
|
||||
float vrHMDPoseMatrix[16];
|
||||
float vrHMDScaledPoseMatrix[16];
|
||||
float vrHMDPoseSkyboxMatrix[16];
|
||||
float vrEyeViewMatrix[2][16];
|
||||
float vrScaledEyeViewMatrix[2][16];
|
||||
float vrEyeSkyboxViewMatrix[2][16];
|
||||
float vrEyeProjMatrix[2][16];
|
||||
|
||||
int vrWorldScale[3] = {400, 250, 100};
|
||||
float vrPlayerScale = 1.0f;
|
||||
|
||||
uint32_t vr_render_width = 0;
|
||||
uint32_t vr_render_height = 0;
|
||||
|
||||
float* vrVisibleAreaVertices[2] = {NULL, NULL};
|
||||
float* vrVisibleAreaUVs[2] = {NULL, NULL};
|
||||
uint32_t vrVisibleAreaVertexCount[2] = {0, 0};
|
||||
|
||||
static PFN_xrGetOpenGLGraphicsRequirementsKHR pfnGetOpenGLGraphicsRequirementsKHR = NULL;
|
||||
static PFN_xrGetVisibilityMaskKHR pfnGetVisibilityMaskKHR = NULL;
|
||||
static PFN_xrEnumerateDisplayRefreshRatesFB pfnEnumerateDisplayRefreshRatesFB = NULL;
|
||||
static PFN_xrGetDisplayRefreshRateFB pfnGetDisplayRefreshRateFB = NULL;
|
||||
static PFN_xrRequestDisplayRefreshRateFB pfnRequestDisplayRefreshRateFB = NULL;
|
||||
static boolean displayRefreshRateExtensionEnabled = false;
|
||||
|
||||
static float VR_RenderScaleForMode(int mode)
|
||||
{
|
||||
static const float multipliers[] = {0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f};
|
||||
|
||||
if (mode < 0)
|
||||
mode = 0;
|
||||
else if (mode > 6)
|
||||
mode = 6;
|
||||
|
||||
return multipliers[mode];
|
||||
}
|
||||
|
||||
static int VR_StartupResolutionMode(void)
|
||||
{
|
||||
int mode = cv_vrresolution.string ? cv_vrresolution.value : 2;
|
||||
|
||||
if (M_CheckParm("+vrresolution") && M_IsNextParm()) {
|
||||
const char* value = M_GetNextParm();
|
||||
|
||||
if (!strcasecmp(value, "50%"))
|
||||
mode = 0;
|
||||
else if (!strcasecmp(value, "75%"))
|
||||
mode = 1;
|
||||
else if (!strcasecmp(value, "100%"))
|
||||
mode = 2;
|
||||
else if (!strcasecmp(value, "125%"))
|
||||
mode = 3;
|
||||
else if (!strcasecmp(value, "150%"))
|
||||
mode = 4;
|
||||
else if (!strcasecmp(value, "175%"))
|
||||
mode = 5;
|
||||
else if (!strcasecmp(value, "200%"))
|
||||
mode = 6;
|
||||
else
|
||||
mode = atoi(value);
|
||||
}
|
||||
|
||||
if (mode < 0)
|
||||
mode = 0;
|
||||
else if (mode > 6)
|
||||
mode = 6;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static boolean VR_RuntimeExtensionSupported(const char* extensionName)
|
||||
{
|
||||
uint32_t extensionCount = 0;
|
||||
XrResult res = xrEnumerateInstanceExtensionProperties(NULL, 0, &extensionCount, NULL);
|
||||
if (XR_FAILED(res) || extensionCount == 0)
|
||||
return false;
|
||||
|
||||
XrExtensionProperties* extensions =
|
||||
(XrExtensionProperties*)malloc(extensionCount * sizeof(XrExtensionProperties));
|
||||
if (!extensions)
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < extensionCount; i++) {
|
||||
extensions[i].type = XR_TYPE_EXTENSION_PROPERTIES;
|
||||
extensions[i].next = NULL;
|
||||
}
|
||||
|
||||
res = xrEnumerateInstanceExtensionProperties(NULL, extensionCount, &extensionCount, extensions);
|
||||
if (XR_FAILED(res)) {
|
||||
free(extensions);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < extensionCount; i++) {
|
||||
if (strcmp(extensions[i].extensionName, extensionName) == 0) {
|
||||
free(extensions);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
free(extensions);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void VR_ConfigureDisplayRefreshRate(void)
|
||||
{
|
||||
if (!displayRefreshRateExtensionEnabled || !pfnGetDisplayRefreshRateFB)
|
||||
return;
|
||||
|
||||
float currentRefreshRate = 0.0f;
|
||||
if (XR_SUCCEEDED(pfnGetDisplayRefreshRateFB(vr_session, ¤tRefreshRate)) &&
|
||||
currentRefreshRate > 0.0f) {
|
||||
CONS_Printf("VR: Runtime display refresh rate is %.2f Hz.\n", currentRefreshRate);
|
||||
|
||||
if (pfnRequestDisplayRefreshRateFB) {
|
||||
XrResult res = pfnRequestDisplayRefreshRateFB(vr_session, currentRefreshRate);
|
||||
if (XR_SUCCEEDED(res))
|
||||
CONS_Printf("VR: Requested runtime refresh pacing at %.2f Hz.\n", currentRefreshRate);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pfnEnumerateDisplayRefreshRatesFB)
|
||||
return;
|
||||
|
||||
uint32_t refreshRateCount = 0;
|
||||
if (XR_FAILED(pfnEnumerateDisplayRefreshRatesFB(vr_session, 0, &refreshRateCount, NULL)) ||
|
||||
refreshRateCount == 0)
|
||||
return;
|
||||
|
||||
float* refreshRates = (float*)malloc(refreshRateCount * sizeof(float));
|
||||
if (!refreshRates)
|
||||
return;
|
||||
|
||||
if (XR_SUCCEEDED(pfnEnumerateDisplayRefreshRatesFB(vr_session, refreshRateCount, &refreshRateCount, refreshRates))) {
|
||||
char buffer[256];
|
||||
size_t used = 0;
|
||||
|
||||
buffer[0] = '\0';
|
||||
for (uint32_t i = 0; i < refreshRateCount && used < sizeof(buffer); i++) {
|
||||
int written = snprintf(buffer + used, sizeof(buffer) - used,
|
||||
"%s%.2f", i ? ", " : "", refreshRates[i]);
|
||||
if (written < 0)
|
||||
break;
|
||||
used += (size_t)written;
|
||||
}
|
||||
CONS_Printf("VR: Runtime supported refresh rates: %s Hz.\n", buffer);
|
||||
}
|
||||
|
||||
free(refreshRates);
|
||||
}
|
||||
|
||||
static void openxr_process_visibility_mesh(int eye, uint32_t width, uint32_t height)
|
||||
{
|
||||
if (!pfnGetVisibilityMaskKHR) return;
|
||||
|
||||
XrVisibilityMaskKHR mask = {XR_TYPE_VISIBILITY_MASK_KHR};
|
||||
XrResult res = pfnGetVisibilityMaskKHR(vr_session, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, eye, XR_VISIBILITY_MASK_TYPE_HIDDEN_TRIANGLE_MESH_KHR, &mask);
|
||||
if (XR_SUCCEEDED(res) && mask.vertexCapacityInput == 0) {
|
||||
mask.vertexCapacityInput = mask.vertexCountOutput;
|
||||
mask.indexCapacityInput = mask.indexCountOutput;
|
||||
mask.vertices = (XrVector2f*)malloc(mask.vertexCapacityInput * sizeof(XrVector2f));
|
||||
mask.indices = (uint32_t*)malloc(mask.indexCapacityInput * sizeof(uint32_t));
|
||||
|
||||
res = pfnGetVisibilityMaskKHR(vr_session, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, eye, XR_VISIBILITY_MASK_TYPE_HIDDEN_TRIANGLE_MESH_KHR, &mask);
|
||||
if (XR_SUCCEEDED(res) && mask.indexCountOutput > 0) {
|
||||
vrVisibleAreaVertexCount[eye] = mask.indexCountOutput;
|
||||
float* vertex = vrVisibleAreaVertices[eye] = (float*)malloc(mask.indexCountOutput * 3 * sizeof(float));
|
||||
float* uv = vrVisibleAreaUVs[eye] = (float*)malloc(mask.indexCountOutput * 2 * sizeof(float));
|
||||
|
||||
uint32_t texsize = 512;
|
||||
while (texsize < width || texsize < height) texsize <<= 1;
|
||||
|
||||
float xfix = 1.0f / ((float)texsize / (float)width);
|
||||
float yfix = 1.0f / ((float)texsize / (float)height);
|
||||
|
||||
for (uint32_t i = 0; i < mask.indexCountOutput; i++) {
|
||||
uint32_t idx = mask.indices[i];
|
||||
vertex[i*3] = (mask.vertices[idx].x - 0.5f) * 2.0f;
|
||||
vertex[i*3+1] = (mask.vertices[idx].y - 0.5f) * 2.0f;
|
||||
vertex[i*3+2] = 1.0f;
|
||||
|
||||
uv[i*2] = mask.vertices[idx].x * xfix;
|
||||
uv[i*2+1] = mask.vertices[idx].y * yfix;
|
||||
}
|
||||
}
|
||||
free(mask.vertices);
|
||||
free(mask.indices);
|
||||
}
|
||||
}
|
||||
|
||||
boolean VR_Init(void)
|
||||
{
|
||||
if (vr_started) return true;
|
||||
|
||||
// Check for explicit VR command-line flags
|
||||
boolean wantVR = (M_CheckParm("-openvr") || M_CheckParm("-vr") || vr_enabled);
|
||||
|
||||
if (!wantVR)
|
||||
{
|
||||
CONS_Printf("VR: No -vr flag detected, skipping VR initialization.\n");
|
||||
CONS_Printf("VR: Use '-vr' command-line flag to enable OpenXR.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
CONS_Printf("VR: Initializing OpenXR...\n");
|
||||
|
||||
XrResult res;
|
||||
|
||||
const boolean visibilityMaskSupported =
|
||||
VR_RuntimeExtensionSupported(XR_KHR_VISIBILITY_MASK_EXTENSION_NAME);
|
||||
const boolean displayRefreshRateSupported =
|
||||
VR_RuntimeExtensionSupported(XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME);
|
||||
const char* enabledExtensions[3];
|
||||
uint32_t enabledExtensionCount = 0;
|
||||
|
||||
enabledExtensions[enabledExtensionCount++] = XR_KHR_OPENGL_ENABLE_EXTENSION_NAME;
|
||||
if (visibilityMaskSupported)
|
||||
enabledExtensions[enabledExtensionCount++] = XR_KHR_VISIBILITY_MASK_EXTENSION_NAME;
|
||||
if (displayRefreshRateSupported)
|
||||
enabledExtensions[enabledExtensionCount++] = XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME;
|
||||
|
||||
XrInstanceCreateInfo createInfo = {XR_TYPE_INSTANCE_CREATE_INFO};
|
||||
createInfo.next = NULL;
|
||||
strncpy(createInfo.applicationInfo.applicationName, "SRB2Kart Blankart", XR_MAX_APPLICATION_NAME_SIZE);
|
||||
createInfo.applicationInfo.applicationVersion = 1;
|
||||
strncpy(createInfo.applicationInfo.engineName, "SRB2Kart", XR_MAX_ENGINE_NAME_SIZE);
|
||||
createInfo.applicationInfo.engineVersion = 1;
|
||||
createInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
|
||||
createInfo.enabledExtensionCount = enabledExtensionCount;
|
||||
createInfo.enabledExtensionNames = enabledExtensions;
|
||||
createInfo.createFlags = 0;
|
||||
createInfo.enabledApiLayerCount = 0;
|
||||
createInfo.enabledApiLayerNames = NULL;
|
||||
|
||||
res = xrCreateInstance(&createInfo, &vr_instance);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: xrCreateInstance with optional extensions failed (XrResult=%d), retrying with OpenGL only...\n", (int)res);
|
||||
createInfo.enabledExtensionCount = 1;
|
||||
createInfo.enabledExtensionNames = enabledExtensions;
|
||||
displayRefreshRateExtensionEnabled = false;
|
||||
res = xrCreateInstance(&createInfo, &vr_instance);
|
||||
} else {
|
||||
displayRefreshRateExtensionEnabled = displayRefreshRateSupported;
|
||||
}
|
||||
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: Failed to create OpenXR instance (XrResult=%d).\n", (int)res);
|
||||
CONS_Printf("VR: Is an OpenXR runtime (Monado, SteamVR, etc.) installed and active?\n");
|
||||
CONS_Printf("VR: Check that XR_RUNTIME_JSON is set or /etc/xdg/openxr/1/active_runtime.json exists.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
CONS_Printf("VR: OpenXR instance created successfully.\n");
|
||||
|
||||
// Print runtime info
|
||||
XrInstanceProperties instanceProps = {XR_TYPE_INSTANCE_PROPERTIES};
|
||||
instanceProps.next = NULL;
|
||||
if (XR_SUCCEEDED(xrGetInstanceProperties(vr_instance, &instanceProps))) {
|
||||
CONS_Printf("VR: Runtime: %s (version %u.%u.%u)\n",
|
||||
instanceProps.runtimeName,
|
||||
XR_VERSION_MAJOR(instanceProps.runtimeVersion),
|
||||
XR_VERSION_MINOR(instanceProps.runtimeVersion),
|
||||
XR_VERSION_PATCH(instanceProps.runtimeVersion));
|
||||
}
|
||||
|
||||
xrGetInstanceProcAddr(vr_instance, "xrGetOpenGLGraphicsRequirementsKHR", (PFN_xrVoidFunction*)&pfnGetOpenGLGraphicsRequirementsKHR);
|
||||
xrGetInstanceProcAddr(vr_instance, "xrGetVisibilityMaskKHR", (PFN_xrVoidFunction*)&pfnGetVisibilityMaskKHR);
|
||||
if (displayRefreshRateExtensionEnabled) {
|
||||
xrGetInstanceProcAddr(vr_instance, "xrEnumerateDisplayRefreshRatesFB", (PFN_xrVoidFunction*)&pfnEnumerateDisplayRefreshRatesFB);
|
||||
xrGetInstanceProcAddr(vr_instance, "xrGetDisplayRefreshRateFB", (PFN_xrVoidFunction*)&pfnGetDisplayRefreshRateFB);
|
||||
xrGetInstanceProcAddr(vr_instance, "xrRequestDisplayRefreshRateFB", (PFN_xrVoidFunction*)&pfnRequestDisplayRefreshRateFB);
|
||||
}
|
||||
|
||||
XrSystemGetInfo systemInfo = {XR_TYPE_SYSTEM_GET_INFO};
|
||||
systemInfo.next = NULL;
|
||||
systemInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
|
||||
res = xrGetSystem(vr_instance, &systemInfo, &vr_system_id);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: Failed to get OpenXR system (XrResult=%d). Is a headset connected?\n", (int)res);
|
||||
xrDestroyInstance(vr_instance);
|
||||
vr_instance = XR_NULL_HANDLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
CONS_Printf("VR: OpenXR system acquired (systemId=%llu).\n", (unsigned long long)vr_system_id);
|
||||
|
||||
if (pfnGetOpenGLGraphicsRequirementsKHR) {
|
||||
XrGraphicsRequirementsOpenGLKHR graphicsRequirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR};
|
||||
graphicsRequirements.next = NULL;
|
||||
res = pfnGetOpenGLGraphicsRequirementsKHR(vr_instance, vr_system_id, &graphicsRequirements);
|
||||
if (XR_SUCCEEDED(res)) {
|
||||
CONS_Printf("VR: OpenGL requirements: min=%u.%u max=%u.%u\n",
|
||||
XR_VERSION_MAJOR(graphicsRequirements.minApiVersionSupported),
|
||||
XR_VERSION_MINOR(graphicsRequirements.minApiVersionSupported),
|
||||
XR_VERSION_MAJOR(graphicsRequirements.maxApiVersionSupported),
|
||||
XR_VERSION_MINOR(graphicsRequirements.maxApiVersionSupported));
|
||||
}
|
||||
}
|
||||
|
||||
// Query the runtime's recommended per-eye resolution before creating the
|
||||
// OpenXR session, then resize Blankart's render surface to match it.
|
||||
uint32_t viewCount = 0;
|
||||
xrEnumerateViewConfigurationViews(vr_instance, vr_system_id, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &viewCount, NULL);
|
||||
|
||||
if (viewCount < 2) {
|
||||
CONS_Printf("VR: ERROR - Expected 2 views for stereo, got %u.\n", viewCount);
|
||||
VR_Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
XrViewConfigurationView* configViews = (XrViewConfigurationView*)malloc(viewCount * sizeof(XrViewConfigurationView));
|
||||
for (uint32_t i = 0; i < viewCount; i++) {
|
||||
configViews[i].type = XR_TYPE_VIEW_CONFIGURATION_VIEW;
|
||||
configViews[i].next = NULL;
|
||||
}
|
||||
xrEnumerateViewConfigurationViews(vr_instance, vr_system_id, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, viewCount, &viewCount, configViews);
|
||||
|
||||
const uint32_t recommendedWidth = configViews[0].recommendedImageRectWidth;
|
||||
const uint32_t recommendedHeight = configViews[0].recommendedImageRectHeight;
|
||||
const uint32_t maxWidth = configViews[0].maxImageRectWidth;
|
||||
const uint32_t maxHeight = configViews[0].maxImageRectHeight;
|
||||
const int resolutionMode = VR_StartupResolutionMode();
|
||||
const float renderScale = VR_RenderScaleForMode(resolutionMode);
|
||||
|
||||
vr_render_width = (uint32_t)((float)recommendedWidth * renderScale + 0.5f);
|
||||
vr_render_height = (uint32_t)((float)recommendedHeight * renderScale + 0.5f);
|
||||
|
||||
if (maxWidth > 0 && vr_render_width > maxWidth)
|
||||
vr_render_width = maxWidth;
|
||||
if (maxHeight > 0 && vr_render_height > maxHeight)
|
||||
vr_render_height = maxHeight;
|
||||
if (vr_render_width < 1)
|
||||
vr_render_width = 1;
|
||||
if (vr_render_height < 1)
|
||||
vr_render_height = 1;
|
||||
|
||||
CONS_Printf("VR: Recommended render size: %ux%u (max: %ux%u); using %ux%u (%d%%).\n",
|
||||
recommendedWidth, recommendedHeight, maxWidth, maxHeight,
|
||||
vr_render_width, vr_render_height, (int)(renderScale * 100.0f + 0.5f));
|
||||
free(configViews);
|
||||
|
||||
if ((uint32_t)vid.width != vr_render_width || (uint32_t)vid.height != vr_render_height) {
|
||||
CONS_Printf("VR: Setting game render size to OpenXR eye size %ux%u.\n", vr_render_width, vr_render_height);
|
||||
VID_SetMode(VID_GetModeForSize((INT32)vr_render_width, (INT32)vr_render_height));
|
||||
}
|
||||
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(window);
|
||||
if (!props) {
|
||||
CONS_Printf("VR: Failed to get SDL window properties!\n");
|
||||
xrDestroyInstance(vr_instance);
|
||||
vr_instance = XR_NULL_HANDLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
XrGraphicsBindingOpenGLWin32KHR graphicsBinding = {XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR};
|
||||
graphicsBinding.next = NULL;
|
||||
graphicsBinding.hDC = (HDC)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HDC_POINTER, NULL);
|
||||
graphicsBinding.hGLRC = (HGLRC)SDL_GL_GetCurrentContext();
|
||||
CONS_Printf("VR: Win32 graphics binding: hDC=%p, hGLRC=%p\n", (void*)graphicsBinding.hDC, (void*)graphicsBinding.hGLRC);
|
||||
#elif defined(__linux__)
|
||||
XrGraphicsBindingOpenGLXlibKHR graphicsBinding = {XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR};
|
||||
graphicsBinding.next = NULL;
|
||||
graphicsBinding.xDisplay = (Display*)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL);
|
||||
|
||||
// Get the GLX context and drawable from the current context
|
||||
graphicsBinding.glxContext = glXGetCurrentContext();
|
||||
graphicsBinding.glxDrawable = glXGetCurrentDrawable();
|
||||
|
||||
// Get the visual ID and FB config from the current context
|
||||
if (graphicsBinding.xDisplay && graphicsBinding.glxContext) {
|
||||
// Query the FB config from the current context
|
||||
int fbConfigId = 0;
|
||||
glXQueryContext(graphicsBinding.xDisplay, graphicsBinding.glxContext, GLX_FBCONFIG_ID, &fbConfigId);
|
||||
|
||||
// Find the matching FB config
|
||||
int numConfigs = 0;
|
||||
int attribs[] = { GLX_FBCONFIG_ID, fbConfigId, None };
|
||||
GLXFBConfig* configs = glXChooseFBConfig(graphicsBinding.xDisplay,
|
||||
DefaultScreen(graphicsBinding.xDisplay), attribs, &numConfigs);
|
||||
if (configs && numConfigs > 0) {
|
||||
graphicsBinding.glxFBConfig = configs[0];
|
||||
|
||||
// Get visual ID from the FB config
|
||||
XVisualInfo* vi = glXGetVisualFromFBConfig(graphicsBinding.xDisplay, configs[0]);
|
||||
if (vi) {
|
||||
graphicsBinding.visualid = vi->visualid;
|
||||
XFree(vi);
|
||||
}
|
||||
XFree(configs);
|
||||
} else {
|
||||
CONS_Printf("VR: WARNING - Could not find matching GLX FB config.\n");
|
||||
graphicsBinding.glxFBConfig = NULL;
|
||||
graphicsBinding.visualid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
CONS_Printf("VR: Xlib graphics binding: display=%p, context=%p, drawable=%lu, visualid=%lu\n",
|
||||
(void*)graphicsBinding.xDisplay,
|
||||
(void*)graphicsBinding.glxContext,
|
||||
(unsigned long)graphicsBinding.glxDrawable,
|
||||
(unsigned long)graphicsBinding.visualid);
|
||||
|
||||
if (!graphicsBinding.xDisplay || !graphicsBinding.glxContext) {
|
||||
CONS_Printf("VR: ERROR - X11 display or GLX context is NULL! Is the game running under X11?\n");
|
||||
CONS_Printf("VR: Wayland-native is not yet supported; try running with SDL_VIDEODRIVER=x11\n");
|
||||
xrDestroyInstance(vr_instance);
|
||||
vr_instance = XR_NULL_HANDLE;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
XrSessionCreateInfo sessionCreateInfo = {XR_TYPE_SESSION_CREATE_INFO};
|
||||
sessionCreateInfo.next = &graphicsBinding;
|
||||
sessionCreateInfo.systemId = vr_system_id;
|
||||
sessionCreateInfo.createFlags = 0;
|
||||
|
||||
res = xrCreateSession(vr_instance, &sessionCreateInfo, &vr_session);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: Failed to create OpenXR session (XrResult=%d).\n", (int)res);
|
||||
xrDestroyInstance(vr_instance);
|
||||
vr_instance = XR_NULL_HANDLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
CONS_Printf("VR: OpenXR session created successfully.\n");
|
||||
|
||||
// Begin the session
|
||||
XrSessionBeginInfo beginInfo = {XR_TYPE_SESSION_BEGIN_INFO};
|
||||
beginInfo.next = NULL;
|
||||
beginInfo.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
||||
res = xrBeginSession(vr_session, &beginInfo);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: Failed to begin OpenXR session (XrResult=%d).\n", (int)res);
|
||||
xrDestroySession(vr_session);
|
||||
vr_session = XR_NULL_HANDLE;
|
||||
xrDestroyInstance(vr_instance);
|
||||
vr_instance = XR_NULL_HANDLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
CONS_Printf("VR: OpenXR session begun.\n");
|
||||
VR_ConfigureDisplayRefreshRate();
|
||||
|
||||
XrReferenceSpaceCreateInfo spaceCreateInfo = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO};
|
||||
spaceCreateInfo.next = NULL;
|
||||
spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
|
||||
spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f;
|
||||
spaceCreateInfo.poseInReferenceSpace.orientation.x = 0.0f;
|
||||
spaceCreateInfo.poseInReferenceSpace.orientation.y = 0.0f;
|
||||
spaceCreateInfo.poseInReferenceSpace.orientation.z = 0.0f;
|
||||
spaceCreateInfo.poseInReferenceSpace.position.x = 0.0f;
|
||||
spaceCreateInfo.poseInReferenceSpace.position.y = 0.0f;
|
||||
spaceCreateInfo.poseInReferenceSpace.position.z = 0.0f;
|
||||
res = xrCreateReferenceSpace(vr_session, &spaceCreateInfo, &vr_local_space);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: Failed to create local reference space.\n");
|
||||
VR_Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
|
||||
res = xrCreateReferenceSpace(vr_session, &spaceCreateInfo, &vr_view_space);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: Failed to create view reference space.\n");
|
||||
VR_Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!VR_InitSwapchains()) {
|
||||
CONS_Printf("VR: Failed to initialize swapchains!\n");
|
||||
VR_Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
CONS_Printf("VR: Swapchains initialized.\n");
|
||||
|
||||
if (pfnGetVisibilityMaskKHR) {
|
||||
openxr_process_visibility_mesh(0, vr_render_width, vr_render_height);
|
||||
openxr_process_visibility_mesh(1, vr_render_width, vr_render_height);
|
||||
CONS_Printf("VR: Visibility masks processed.\n");
|
||||
} else {
|
||||
CONS_Printf("VR: Visibility mask extension not available (cosmetic only).\n");
|
||||
}
|
||||
|
||||
vr_started = true;
|
||||
vr_enabled = true;
|
||||
CV_StealthSet(&cv_vidwait, "Off");
|
||||
CV_StealthSetValue(&cv_fpscap, -1);
|
||||
CV_StealthSet(&cv_ticrate, "Compact");
|
||||
CONS_Printf("VR: Desktop vsync disabled and FPS cap set to Unlimited; OpenXR xrWaitFrame will pace rendering.\n");
|
||||
CONS_Printf("VR: OpenXR initialization complete! VR is now active.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void VR_Shutdown(void)
|
||||
{
|
||||
if (!vr_started && vr_instance == XR_NULL_HANDLE) return;
|
||||
|
||||
if (vrVisibleAreaVertices[0]) { free(vrVisibleAreaVertices[0]); vrVisibleAreaVertices[0] = NULL; }
|
||||
if (vrVisibleAreaVertices[1]) { free(vrVisibleAreaVertices[1]); vrVisibleAreaVertices[1] = NULL; }
|
||||
if (vrVisibleAreaUVs[0]) { free(vrVisibleAreaUVs[0]); vrVisibleAreaUVs[0] = NULL; }
|
||||
if (vrVisibleAreaUVs[1]) { free(vrVisibleAreaUVs[1]); vrVisibleAreaUVs[1] = NULL; }
|
||||
|
||||
VR_DestroySwapchains();
|
||||
|
||||
if (vr_local_space != XR_NULL_HANDLE) xrDestroySpace(vr_local_space);
|
||||
if (vr_view_space != XR_NULL_HANDLE) xrDestroySpace(vr_view_space);
|
||||
if (vr_session != XR_NULL_HANDLE) xrDestroySession(vr_session);
|
||||
if (vr_instance != XR_NULL_HANDLE) xrDestroyInstance(vr_instance);
|
||||
|
||||
vr_local_space = XR_NULL_HANDLE;
|
||||
vr_view_space = XR_NULL_HANDLE;
|
||||
vr_session = XR_NULL_HANDLE;
|
||||
vr_instance = XR_NULL_HANDLE;
|
||||
vr_started = false;
|
||||
vr_enabled = false;
|
||||
vr_drawing_ui = false;
|
||||
displayRefreshRateExtensionEnabled = false;
|
||||
pfnEnumerateDisplayRefreshRatesFB = NULL;
|
||||
pfnGetDisplayRefreshRateFB = NULL;
|
||||
pfnRequestDisplayRefreshRateFB = NULL;
|
||||
}
|
||||
|
||||
void VR_ScaleViewMatrices(float player_scale, int skybox_scale)
|
||||
{
|
||||
memcpy(vrHMDScaledPoseMatrix, vrHMDPoseMatrix, sizeof(float) * 16);
|
||||
vrHMDScaledPoseMatrix[12] *= player_scale;
|
||||
vrHMDScaledPoseMatrix[13] *= player_scale;
|
||||
vrHMDScaledPoseMatrix[14] *= player_scale;
|
||||
|
||||
memcpy(vrScaledEyeViewMatrix[0], vrEyeViewMatrix[0], sizeof(float) * 16);
|
||||
memcpy(vrScaledEyeViewMatrix[1], vrEyeViewMatrix[1], sizeof(float) * 16);
|
||||
|
||||
vrScaledEyeViewMatrix[0][12] *= player_scale;
|
||||
vrScaledEyeViewMatrix[0][13] *= player_scale;
|
||||
vrScaledEyeViewMatrix[0][14] *= player_scale;
|
||||
vrScaledEyeViewMatrix[1][12] *= player_scale;
|
||||
vrScaledEyeViewMatrix[1][13] *= player_scale;
|
||||
vrScaledEyeViewMatrix[1][14] *= player_scale;
|
||||
|
||||
vrPlayerScale = player_scale;
|
||||
|
||||
memcpy(vrEyeSkyboxViewMatrix[0], vrScaledEyeViewMatrix[0], sizeof(float) * 16);
|
||||
memcpy(vrEyeSkyboxViewMatrix[1], vrScaledEyeViewMatrix[1], sizeof(float) * 16);
|
||||
|
||||
if(skybox_scale > 0)
|
||||
{
|
||||
vrEyeSkyboxViewMatrix[0][12] /= skybox_scale;
|
||||
vrEyeSkyboxViewMatrix[0][13] /= skybox_scale;
|
||||
vrEyeSkyboxViewMatrix[0][14] /= skybox_scale;
|
||||
vrEyeSkyboxViewMatrix[1][12] /= skybox_scale;
|
||||
vrEyeSkyboxViewMatrix[1][13] /= skybox_scale;
|
||||
vrEyeSkyboxViewMatrix[1][14] /= skybox_scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
vrEyeSkyboxViewMatrix[0][12] =
|
||||
vrEyeSkyboxViewMatrix[0][13] =
|
||||
vrEyeSkyboxViewMatrix[0][14] =
|
||||
vrEyeSkyboxViewMatrix[1][12] =
|
||||
vrEyeSkyboxViewMatrix[1][13] =
|
||||
vrEyeSkyboxViewMatrix[1][14] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_VR
|
||||
76
src/vr/vr_main.h
Normal file
76
src/vr/vr_main.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#ifndef __VR_MAIN_H__
|
||||
#define __VR_MAIN_H__
|
||||
|
||||
#ifdef HAVE_VR
|
||||
#include "../doomdef.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define XR_USE_PLATFORM_WIN32
|
||||
#define XR_USE_GRAPHICS_API_OPENGL
|
||||
#include <windows.h>
|
||||
#elif defined(__linux__)
|
||||
#define XR_USE_PLATFORM_XLIB
|
||||
#define XR_USE_GRAPHICS_API_OPENGL
|
||||
#include <X11/Xlib.h>
|
||||
#include <GL/glx.h>
|
||||
#endif
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
#include <openxr/openxr_platform.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Global state
|
||||
extern int vr_current_eye;
|
||||
extern boolean vr_started;
|
||||
extern boolean vr_enabled;
|
||||
extern boolean vr_drawing_ui;
|
||||
|
||||
typedef enum {
|
||||
VR_PASS_NONE,
|
||||
VR_PASS_3D_LEFT,
|
||||
VR_PASS_3D_RIGHT,
|
||||
VR_PASS_UI
|
||||
} vr_render_pass_t;
|
||||
|
||||
extern vr_render_pass_t vr_render_pass;
|
||||
|
||||
extern XrInstance vr_instance;
|
||||
extern XrSession vr_session;
|
||||
extern XrSystemId vr_system_id;
|
||||
extern XrSpace vr_local_space;
|
||||
extern XrSpace vr_view_space;
|
||||
|
||||
// Global matrices (size 16)
|
||||
extern float vrHMDPoseMatrix[16];
|
||||
extern float vrHMDScaledPoseMatrix[16];
|
||||
extern float vrHMDPoseSkyboxMatrix[16];
|
||||
extern float vrEyeViewMatrix[2][16];
|
||||
extern float vrScaledEyeViewMatrix[2][16];
|
||||
extern float vrEyeSkyboxViewMatrix[2][16];
|
||||
extern float vrEyeProjMatrix[2][16];
|
||||
|
||||
extern int vrWorldScale[3];
|
||||
extern float vrPlayerScale;
|
||||
|
||||
extern uint32_t vr_render_width;
|
||||
extern uint32_t vr_render_height;
|
||||
|
||||
// Visibility mask
|
||||
extern float* vrVisibleAreaVertices[2];
|
||||
extern float* vrVisibleAreaUVs[2];
|
||||
extern uint32_t vrVisibleAreaVertexCount[2];
|
||||
|
||||
// Lifecycle
|
||||
boolean VR_Init(void);
|
||||
void VR_Shutdown(void);
|
||||
void VR_ScaleViewMatrices(float player_scale, int skybox_scale);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // HAVE_VR
|
||||
#endif // __VR_MAIN_H__
|
||||
126
src/vr/vr_math.c
Normal file
126
src/vr/vr_math.c
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
#include "vr_math.h"
|
||||
|
||||
#ifdef HAVE_VR
|
||||
|
||||
#include <math.h>
|
||||
|
||||
void VR_MatrixInv(float* a, const float* b)
|
||||
{
|
||||
float x, y, z;
|
||||
// transpose of rotation matrix
|
||||
a[ 0] = b[ 0];
|
||||
a[ 5] = b[ 5];
|
||||
a[10] = b[10];
|
||||
x=b[1]; a[1]=b[4]; a[4]=x;
|
||||
x=b[2]; a[2]=b[8]; a[8]=x;
|
||||
x=b[6]; a[6]=b[9]; a[9]=x;
|
||||
|
||||
// copy projection part
|
||||
a[ 3] = b[ 3];
|
||||
a[ 7] = b[ 7];
|
||||
a[11] = b[11];
|
||||
a[15] = b[15];
|
||||
|
||||
// convert origin: new_pos = - new_rotation_matrix * old_pos
|
||||
x = (a[ 0]*b[12]) + (a[ 4]*b[13]) + (a[ 8]*b[14]);
|
||||
y = (a[ 1]*b[12]) + (a[ 5]*b[13]) + (a[ 9]*b[14]);
|
||||
z = (a[ 2]*b[12]) + (a[ 6]*b[13]) + (a[10]*b[14]);
|
||||
a[12] = -x;
|
||||
a[13] = -y;
|
||||
a[14] = -z;
|
||||
}
|
||||
|
||||
void VR_MatrixMultiply(float* out, const float* a, const float* b)
|
||||
{
|
||||
float r[16];
|
||||
|
||||
for (int col = 0; col < 4; col++) {
|
||||
for (int row = 0; row < 4; row++) {
|
||||
r[row + col * 4] =
|
||||
a[row + 0 * 4] * b[0 + col * 4] +
|
||||
a[row + 1 * 4] * b[1 + col * 4] +
|
||||
a[row + 2 * 4] * b[2 + col * 4] +
|
||||
a[row + 3 * 4] * b[3 + col * 4];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
out[i] = r[i];
|
||||
}
|
||||
|
||||
void VR_RotateVecByMat(const float* m, float* v)
|
||||
{
|
||||
float x = (m[0]*v[0]) + (m[4]*v[1]) + (m[ 8]*v[2]); // Note: corrected from original openvr_common.c
|
||||
float y = (m[1]*v[0]) + (m[5]*v[1]) + (m[ 9]*v[2]); // which used v[0] in all y places etc.
|
||||
float z = (m[2]*v[0]) + (m[6]*v[1]) + (m[10]*v[2]); // Wait, I should check how it was exactly.
|
||||
v[0] = x;
|
||||
v[1] = y;
|
||||
v[2] = z;
|
||||
}
|
||||
|
||||
void VR_PoseToMatrix(const XrPosef* pose, float* glMatrix)
|
||||
{
|
||||
// Convert quaternion to 3x3 rotation matrix
|
||||
float xx = pose->orientation.x * pose->orientation.x;
|
||||
float yy = pose->orientation.y * pose->orientation.y;
|
||||
float zz = pose->orientation.z * pose->orientation.z;
|
||||
float xy = pose->orientation.x * pose->orientation.y;
|
||||
float xz = pose->orientation.x * pose->orientation.z;
|
||||
float yz = pose->orientation.y * pose->orientation.z;
|
||||
float wx = pose->orientation.w * pose->orientation.x;
|
||||
float wy = pose->orientation.w * pose->orientation.y;
|
||||
float wz = pose->orientation.w * pose->orientation.z;
|
||||
|
||||
glMatrix[ 0] = 1.0f - 2.0f * (yy + zz);
|
||||
glMatrix[ 1] = 2.0f * (xy + wz);
|
||||
glMatrix[ 2] = 2.0f * (xz - wy);
|
||||
glMatrix[ 3] = 0.0f;
|
||||
|
||||
glMatrix[ 4] = 2.0f * (xy - wz);
|
||||
glMatrix[ 5] = 1.0f - 2.0f * (xx + zz);
|
||||
glMatrix[ 6] = 2.0f * (yz + wx);
|
||||
glMatrix[ 7] = 0.0f;
|
||||
|
||||
glMatrix[ 8] = 2.0f * (xz + wy);
|
||||
glMatrix[ 9] = 2.0f * (yz - wx);
|
||||
glMatrix[10] = 1.0f - 2.0f * (xx + yy);
|
||||
glMatrix[11] = 0.0f;
|
||||
|
||||
glMatrix[12] = pose->position.x;
|
||||
glMatrix[13] = pose->position.y;
|
||||
glMatrix[14] = pose->position.z;
|
||||
glMatrix[15] = 1.0f;
|
||||
}
|
||||
|
||||
void VR_FovToProjection(const XrFovf* fov, float nearZ, float farZ, float* glMatrix)
|
||||
{
|
||||
float tanLeft = tanf(fov->angleLeft);
|
||||
float tanRight = tanf(fov->angleRight);
|
||||
float tanDown = tanf(fov->angleDown);
|
||||
float tanUp = tanf(fov->angleUp);
|
||||
|
||||
float tanWidth = tanRight - tanLeft;
|
||||
float tanHeight = tanUp - tanDown;
|
||||
|
||||
glMatrix[ 0] = 2.0f / tanWidth;
|
||||
glMatrix[ 1] = 0.0f;
|
||||
glMatrix[ 2] = 0.0f;
|
||||
glMatrix[ 3] = 0.0f;
|
||||
|
||||
glMatrix[ 4] = 0.0f;
|
||||
glMatrix[ 5] = 2.0f / tanHeight;
|
||||
glMatrix[ 6] = 0.0f;
|
||||
glMatrix[ 7] = 0.0f;
|
||||
|
||||
glMatrix[ 8] = (tanRight + tanLeft) / tanWidth;
|
||||
glMatrix[ 9] = (tanUp + tanDown) / tanHeight;
|
||||
glMatrix[10] = -(farZ + nearZ) / (farZ - nearZ);
|
||||
glMatrix[11] = -1.0f;
|
||||
|
||||
glMatrix[12] = 0.0f;
|
||||
glMatrix[13] = 0.0f;
|
||||
glMatrix[14] = -(2.0f * farZ * nearZ) / (farZ - nearZ);
|
||||
glMatrix[15] = 0.0f;
|
||||
}
|
||||
|
||||
#endif // HAVE_VR
|
||||
33
src/vr/vr_math.h
Normal file
33
src/vr/vr_math.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef __VR_MATH_H__
|
||||
#define __VR_MATH_H__
|
||||
|
||||
#ifdef HAVE_VR
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Invert a 4x4 matrix (assumes it's a rigid body transform: rotation + translation)
|
||||
// safe to use same pointer for a and b (a = inv(b))
|
||||
void VR_MatrixInv(float* a, const float* b);
|
||||
|
||||
// Multiply two OpenGL 4x4 matrices (column-major): out = a * b
|
||||
void VR_MatrixMultiply(float* out, const float* a, const float* b);
|
||||
|
||||
// Rotate a 3D vector by a 4x4 matrix
|
||||
void VR_RotateVecByMat(const float* m, float* v);
|
||||
|
||||
// Convert an OpenXR XrPosef to an OpenGL 4x4 matrix (column-major)
|
||||
void VR_PoseToMatrix(const XrPosef* pose, float* glMatrix);
|
||||
|
||||
// Create an asymmetric OpenGL projection matrix from OpenXR FOV tangents
|
||||
void VR_FovToProjection(const XrFovf* fov, float nearZ, float farZ, float* glMatrix);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // HAVE_VR
|
||||
#endif // __VR_MATH_H__
|
||||
795
src/vr/vr_render.c
Normal file
795
src/vr/vr_render.c
Normal file
|
|
@ -0,0 +1,795 @@
|
|||
#include "vr_render.h"
|
||||
|
||||
#ifdef HAVE_VR
|
||||
#include "vr_math.h"
|
||||
#include "../console.h"
|
||||
#include "../screen.h"
|
||||
#include "../hardware/r_opengl/r_opengl.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#elif defined(__linux__)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#endif
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef GL_FRAMEBUFFER
|
||||
#define GL_FRAMEBUFFER 0x8D40
|
||||
#define GL_COLOR_ATTACHMENT0 0x8CE0
|
||||
#define GL_DEPTH_ATTACHMENT 0x8D00
|
||||
#define GL_DEPTH_COMPONENT24 0x81A6
|
||||
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
|
||||
#endif
|
||||
|
||||
#ifndef GL_RENDERBUFFER
|
||||
#define GL_RENDERBUFFER 0x8D41
|
||||
#endif
|
||||
#ifndef GL_READ_FRAMEBUFFER
|
||||
#define GL_READ_FRAMEBUFFER 0x8CA8
|
||||
#endif
|
||||
#ifndef GL_DRAW_FRAMEBUFFER
|
||||
#define GL_DRAW_FRAMEBUFFER 0x8CA9
|
||||
#endif
|
||||
|
||||
typedef void (APIENTRY * PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers);
|
||||
typedef void (APIENTRY * PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer);
|
||||
typedef void (APIENTRY * PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
|
||||
typedef void (APIENTRY * PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers);
|
||||
typedef void (APIENTRY * PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer);
|
||||
typedef void (APIENTRY * PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
typedef void (APIENTRY * PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
|
||||
typedef void (APIENTRY * PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers);
|
||||
typedef void (APIENTRY * PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers);
|
||||
typedef GLenum (APIENTRY * PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target);
|
||||
typedef void (APIENTRY * PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
|
||||
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
|
||||
static PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = NULL;
|
||||
static PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = NULL;
|
||||
static PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = NULL;
|
||||
static PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = NULL;
|
||||
static PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = NULL;
|
||||
static PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = NULL;
|
||||
static PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer = NULL;
|
||||
static PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = NULL;
|
||||
static PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = NULL;
|
||||
static PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = NULL;
|
||||
static PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = NULL;
|
||||
|
||||
XrSwapchain vr_swapchains[2] = {XR_NULL_HANDLE, XR_NULL_HANDLE};
|
||||
uint32_t vr_swapchain_lengths[2] = {0, 0};
|
||||
XrSwapchainImageOpenGLKHR* vr_swapchain_images[2] = {NULL, NULL};
|
||||
uint32_t* vr_framebuffers[2] = {NULL, NULL};
|
||||
uint32_t* vr_depthbuffers[2] = {NULL, NULL};
|
||||
|
||||
XrSwapchain vr_ui_swapchain = XR_NULL_HANDLE;
|
||||
uint32_t vr_ui_swapchain_length = 0;
|
||||
XrSwapchainImageOpenGLKHR* vr_ui_swapchain_images = NULL;
|
||||
uint32_t* vr_ui_framebuffers = NULL;
|
||||
|
||||
uint32_t vr_ui_width = 0;
|
||||
uint32_t vr_ui_height = 0;
|
||||
|
||||
enum {
|
||||
VR_DEFAULT_UI_SWAPCHAIN_WIDTH = 1920,
|
||||
VR_DEFAULT_UI_SWAPCHAIN_HEIGHT = 1080
|
||||
};
|
||||
|
||||
static void LoadFBOFunctions(void) {
|
||||
if (glGenFramebuffers) return;
|
||||
glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)SDL_GL_GetProcAddress("glGenFramebuffers");
|
||||
glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)SDL_GL_GetProcAddress("glBindFramebuffer");
|
||||
glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)SDL_GL_GetProcAddress("glFramebufferTexture2D");
|
||||
glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)SDL_GL_GetProcAddress("glGenRenderbuffers");
|
||||
glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)SDL_GL_GetProcAddress("glBindRenderbuffer");
|
||||
glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)SDL_GL_GetProcAddress("glRenderbufferStorage");
|
||||
glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)SDL_GL_GetProcAddress("glFramebufferRenderbuffer");
|
||||
glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)SDL_GL_GetProcAddress("glDeleteFramebuffers");
|
||||
glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)SDL_GL_GetProcAddress("glDeleteRenderbuffers");
|
||||
glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)SDL_GL_GetProcAddress("glCheckFramebufferStatus");
|
||||
glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)SDL_GL_GetProcAddress("glBlitFramebuffer");
|
||||
}
|
||||
|
||||
boolean VR_InitSwapchains(void)
|
||||
{
|
||||
LoadFBOFunctions();
|
||||
if (!glGenFramebuffers) {
|
||||
CONS_Printf("VR_InitSwapchains: FBO extensions not found!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int eye = 0; eye < 2; eye++) {
|
||||
XrSwapchainCreateInfo swapchainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO};
|
||||
swapchainCreateInfo.next = NULL;
|
||||
swapchainCreateInfo.createFlags = 0;
|
||||
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
swapchainCreateInfo.format = GL_SRGB8_ALPHA8;
|
||||
swapchainCreateInfo.sampleCount = 1;
|
||||
swapchainCreateInfo.width = vr_render_width;
|
||||
swapchainCreateInfo.height = vr_render_height;
|
||||
swapchainCreateInfo.faceCount = 1;
|
||||
swapchainCreateInfo.arraySize = 1;
|
||||
swapchainCreateInfo.mipCount = 1;
|
||||
|
||||
XrResult res = xrCreateSwapchain(vr_session, &swapchainCreateInfo, &vr_swapchains[eye]);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: Failed to create swapchain %d (XrResult=%d).\n", eye, (int)res);
|
||||
return false;
|
||||
}
|
||||
|
||||
xrEnumerateSwapchainImages(vr_swapchains[eye], 0, &vr_swapchain_lengths[eye], NULL);
|
||||
CONS_Printf("VR: Swapchain %d has %u images.\n", eye, vr_swapchain_lengths[eye]);
|
||||
|
||||
vr_swapchain_images[eye] = (XrSwapchainImageOpenGLKHR*)malloc(vr_swapchain_lengths[eye] * sizeof(XrSwapchainImageOpenGLKHR));
|
||||
for (uint32_t i = 0; i < vr_swapchain_lengths[eye]; i++) {
|
||||
vr_swapchain_images[eye][i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
|
||||
vr_swapchain_images[eye][i].next = NULL;
|
||||
}
|
||||
xrEnumerateSwapchainImages(vr_swapchains[eye], vr_swapchain_lengths[eye], &vr_swapchain_lengths[eye], (XrSwapchainImageBaseHeader*)vr_swapchain_images[eye]);
|
||||
|
||||
vr_framebuffers[eye] = (uint32_t*)malloc(vr_swapchain_lengths[eye] * sizeof(uint32_t));
|
||||
vr_depthbuffers[eye] = (uint32_t*)malloc(vr_swapchain_lengths[eye] * sizeof(uint32_t));
|
||||
|
||||
for (uint32_t i = 0; i < vr_swapchain_lengths[eye]; i++) {
|
||||
glGenFramebuffers(1, &vr_framebuffers[eye][i]);
|
||||
glGenRenderbuffers(1, &vr_depthbuffers[eye][i]);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, vr_framebuffers[eye][i]);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, vr_swapchain_images[eye][i].image, 0);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, vr_depthbuffers[eye][i]);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, vr_render_width, vr_render_height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, vr_depthbuffers[eye][i]);
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
CONS_Printf("VR: WARNING - FBO %u for eye %d is incomplete (status=0x%x)!\n", i, eye, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
// Keep UI at a stable 16:9 panel size. Eye targets can be square on canted HMDs.
|
||||
vr_ui_width = VR_DEFAULT_UI_SWAPCHAIN_WIDTH;
|
||||
vr_ui_height = VR_DEFAULT_UI_SWAPCHAIN_HEIGHT;
|
||||
|
||||
XrSwapchainCreateInfo uiSwapchainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO};
|
||||
uiSwapchainCreateInfo.arraySize = 1;
|
||||
uiSwapchainCreateInfo.format = GL_SRGB8_ALPHA8;
|
||||
uiSwapchainCreateInfo.width = vr_ui_width;
|
||||
uiSwapchainCreateInfo.height = vr_ui_height;
|
||||
uiSwapchainCreateInfo.mipCount = 1;
|
||||
uiSwapchainCreateInfo.faceCount = 1;
|
||||
uiSwapchainCreateInfo.sampleCount = 1;
|
||||
uiSwapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_SAMPLED_BIT;
|
||||
|
||||
XrResult res = xrCreateSwapchain(vr_session, &uiSwapchainCreateInfo, &vr_ui_swapchain);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: Failed to create UI swapchain.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
xrEnumerateSwapchainImages(vr_ui_swapchain, 0, &vr_ui_swapchain_length, NULL);
|
||||
vr_ui_swapchain_images = (XrSwapchainImageOpenGLKHR*)malloc(vr_ui_swapchain_length * sizeof(XrSwapchainImageOpenGLKHR));
|
||||
for (uint32_t i = 0; i < vr_ui_swapchain_length; i++) {
|
||||
vr_ui_swapchain_images[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
|
||||
vr_ui_swapchain_images[i].next = NULL;
|
||||
}
|
||||
xrEnumerateSwapchainImages(vr_ui_swapchain, vr_ui_swapchain_length, &vr_ui_swapchain_length, (XrSwapchainImageBaseHeader*)vr_ui_swapchain_images);
|
||||
|
||||
vr_ui_framebuffers = (uint32_t*)malloc(vr_ui_swapchain_length * sizeof(uint32_t));
|
||||
|
||||
for (uint32_t i = 0; i < vr_ui_swapchain_length; i++) {
|
||||
glGenFramebuffers(1, &vr_ui_framebuffers[i]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, vr_ui_framebuffers[i]);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, vr_ui_swapchain_images[i].image, 0);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
CONS_Printf("VR: Failed to create UI framebuffer %d\n", i);
|
||||
return false;
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
CONS_Printf("VR: UI swapchain created (%ux%u, %u images).\n",
|
||||
vr_ui_width, vr_ui_height, vr_ui_swapchain_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static XrPosef cachedUIPose;
|
||||
static boolean cachedUIPoseValid = false;
|
||||
|
||||
void VR_DestroySwapchains(void)
|
||||
{
|
||||
for (int eye = 0; eye < 2; eye++) {
|
||||
if (vr_framebuffers[eye] && glDeleteFramebuffers) {
|
||||
glDeleteFramebuffers(vr_swapchain_lengths[eye], vr_framebuffers[eye]);
|
||||
free(vr_framebuffers[eye]);
|
||||
vr_framebuffers[eye] = NULL;
|
||||
}
|
||||
if (vr_depthbuffers[eye] && glDeleteRenderbuffers) {
|
||||
glDeleteRenderbuffers(vr_swapchain_lengths[eye], vr_depthbuffers[eye]);
|
||||
free(vr_depthbuffers[eye]);
|
||||
vr_depthbuffers[eye] = NULL;
|
||||
}
|
||||
if (vr_swapchain_images[eye]) {
|
||||
free(vr_swapchain_images[eye]);
|
||||
vr_swapchain_images[eye] = NULL;
|
||||
}
|
||||
if (vr_swapchains[eye] != XR_NULL_HANDLE) {
|
||||
xrDestroySwapchain(vr_swapchains[eye]);
|
||||
vr_swapchains[eye] = XR_NULL_HANDLE;
|
||||
}
|
||||
vr_swapchain_lengths[eye] = 0;
|
||||
}
|
||||
|
||||
if (vr_ui_framebuffers && glDeleteFramebuffers) {
|
||||
glDeleteFramebuffers(vr_ui_swapchain_length, vr_ui_framebuffers);
|
||||
free(vr_ui_framebuffers);
|
||||
vr_ui_framebuffers = NULL;
|
||||
}
|
||||
if (vr_ui_swapchain_images) {
|
||||
free(vr_ui_swapchain_images);
|
||||
vr_ui_swapchain_images = NULL;
|
||||
}
|
||||
if (vr_ui_swapchain != XR_NULL_HANDLE) {
|
||||
xrDestroySwapchain(vr_ui_swapchain);
|
||||
vr_ui_swapchain = XR_NULL_HANDLE;
|
||||
}
|
||||
vr_ui_swapchain_length = 0;
|
||||
vr_ui_width = 0;
|
||||
vr_ui_height = 0;
|
||||
cachedUIPoseValid = false;
|
||||
}
|
||||
|
||||
// Cached per-frame state
|
||||
static XrFrameState frameState = {XR_TYPE_FRAME_STATE};
|
||||
static uint32_t swapchainImageIndex[2];
|
||||
static uint32_t uiSwapchainImageIndex;
|
||||
static boolean vr_frame_begun = false;
|
||||
static boolean eyeSwapchainAcquired[2] = {false, false};
|
||||
static boolean eyeSwapchainRendered[2] = {false, false};
|
||||
static boolean uiSwapchainAcquired = false;
|
||||
static XrView cachedViews[2]; // Cache the located views for EndFrame
|
||||
static boolean cachedViewsValid = false;
|
||||
static XrPosef cachedHeadPose;
|
||||
static boolean cachedHeadPoseValid = false;
|
||||
static XrSessionState vr_session_state = XR_SESSION_STATE_UNKNOWN;
|
||||
static boolean vr_session_running = false;
|
||||
|
||||
// Poll and handle OpenXR events (session state machine)
|
||||
static void VR_PollEvents(void)
|
||||
{
|
||||
if (!vr_started) return;
|
||||
|
||||
XrEventDataBuffer eventData = {XR_TYPE_EVENT_DATA_BUFFER};
|
||||
eventData.next = NULL;
|
||||
|
||||
while (xrPollEvent(vr_instance, &eventData) == XR_SUCCESS) {
|
||||
switch (eventData.type) {
|
||||
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: {
|
||||
XrEventDataSessionStateChanged* stateChanged = (XrEventDataSessionStateChanged*)&eventData;
|
||||
vr_session_state = stateChanged->state;
|
||||
CONS_Printf("VR: Session state changed to %d\n", (int)vr_session_state);
|
||||
|
||||
switch (vr_session_state) {
|
||||
case XR_SESSION_STATE_READY:
|
||||
// Session is already begun in VR_Init, but handle if needed
|
||||
vr_session_running = true;
|
||||
break;
|
||||
case XR_SESSION_STATE_SYNCHRONIZED:
|
||||
case XR_SESSION_STATE_VISIBLE:
|
||||
case XR_SESSION_STATE_FOCUSED:
|
||||
vr_session_running = true;
|
||||
break;
|
||||
case XR_SESSION_STATE_STOPPING:
|
||||
xrEndSession(vr_session);
|
||||
vr_session_running = false;
|
||||
break;
|
||||
case XR_SESSION_STATE_LOSS_PENDING:
|
||||
case XR_SESSION_STATE_EXITING:
|
||||
vr_session_running = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset for next event
|
||||
eventData.type = XR_TYPE_EVENT_DATA_BUFFER;
|
||||
eventData.next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void VR_SetIdentityMatrix(float* m)
|
||||
{
|
||||
memset(m, 0, sizeof(float) * 16);
|
||||
m[0] = 1.0f;
|
||||
m[5] = 1.0f;
|
||||
m[10] = 1.0f;
|
||||
m[15] = 1.0f;
|
||||
}
|
||||
|
||||
static void VR_ScaleMatrixTranslation(float* m, float scale)
|
||||
{
|
||||
m[12] *= scale;
|
||||
m[13] *= scale;
|
||||
m[14] *= scale;
|
||||
}
|
||||
|
||||
static float VR_YawFromQuaternion(const XrQuaternionf* q)
|
||||
{
|
||||
return atan2f(2.0f * (q->w * q->y + q->x * q->z),
|
||||
1.0f - 2.0f * (q->y * q->y + q->x * q->x));
|
||||
}
|
||||
|
||||
static float VR_RollFromQuaternion(const XrQuaternionf* q)
|
||||
{
|
||||
return atan2f(2.0f * (q->w * q->z + q->x * q->y),
|
||||
1.0f - 2.0f * (q->z * q->z + q->y * q->y));
|
||||
}
|
||||
|
||||
static XrQuaternionf VR_QuaternionMultiply(const XrQuaternionf* a, const XrQuaternionf* b)
|
||||
{
|
||||
XrQuaternionf q;
|
||||
|
||||
q.x = a->w * b->x + a->x * b->w + a->y * b->z - a->z * b->y;
|
||||
q.y = a->w * b->y - a->x * b->z + a->y * b->w + a->z * b->x;
|
||||
q.z = a->w * b->z + a->x * b->y - a->y * b->x + a->z * b->w;
|
||||
q.w = a->w * b->w - a->x * b->x - a->y * b->y - a->z * b->z;
|
||||
return q;
|
||||
}
|
||||
|
||||
static XrQuaternionf VR_QuaternionFromYaw(float yaw)
|
||||
{
|
||||
XrQuaternionf q;
|
||||
const float halfYaw = yaw * 0.5f;
|
||||
|
||||
q.x = 0.0f;
|
||||
q.y = sinf(halfYaw);
|
||||
q.z = 0.0f;
|
||||
q.w = cosf(halfYaw);
|
||||
return q;
|
||||
}
|
||||
|
||||
static XrQuaternionf VR_QuaternionFromRoll(float roll)
|
||||
{
|
||||
XrQuaternionf q;
|
||||
const float halfRoll = roll * 0.5f;
|
||||
|
||||
q.x = 0.0f;
|
||||
q.y = 0.0f;
|
||||
q.z = sinf(halfRoll);
|
||||
q.w = cosf(halfRoll);
|
||||
return q;
|
||||
}
|
||||
|
||||
static XrQuaternionf VR_QuaternionFromYawRoll(float yaw, float roll)
|
||||
{
|
||||
XrQuaternionf yawQ = VR_QuaternionFromYaw(yaw);
|
||||
XrQuaternionf rollQ = VR_QuaternionFromRoll(roll);
|
||||
return VR_QuaternionMultiply(&yawQ, &rollQ);
|
||||
}
|
||||
|
||||
static XrPosef VR_MakeUIPose(float distance)
|
||||
{
|
||||
XrPosef pose;
|
||||
|
||||
memset(&pose, 0, sizeof(pose));
|
||||
pose.orientation.w = 1.0f;
|
||||
pose.position.z = -distance;
|
||||
|
||||
if (!cachedHeadPoseValid)
|
||||
return pose;
|
||||
|
||||
const float yaw = VR_YawFromQuaternion(&cachedHeadPose.orientation);
|
||||
const float roll = VR_RollFromQuaternion(&cachedHeadPose.orientation);
|
||||
pose.orientation = VR_QuaternionFromYawRoll(yaw, roll);
|
||||
pose.position = cachedHeadPose.position;
|
||||
pose.position.x += -sinf(yaw) * distance;
|
||||
pose.position.z += -cosf(yaw) * distance;
|
||||
|
||||
return pose;
|
||||
}
|
||||
|
||||
static int VR_WorldScale(void)
|
||||
{
|
||||
int mode = cv_vrscale.string ? cv_vrscale.value : 1;
|
||||
|
||||
if (mode < 0)
|
||||
mode = 0;
|
||||
else if (mode > 2)
|
||||
mode = 2;
|
||||
|
||||
return vrWorldScale[mode];
|
||||
}
|
||||
|
||||
boolean VR_BeginFrame(void)
|
||||
{
|
||||
if (!vr_started) return false;
|
||||
|
||||
// Poll events first — this drives the session state machine
|
||||
VR_PollEvents();
|
||||
|
||||
if (!vr_session_running) return false;
|
||||
|
||||
cachedViewsValid = false;
|
||||
cachedHeadPoseValid = false;
|
||||
vr_frame_begun = false;
|
||||
eyeSwapchainAcquired[0] = false;
|
||||
eyeSwapchainAcquired[1] = false;
|
||||
eyeSwapchainRendered[0] = false;
|
||||
eyeSwapchainRendered[1] = false;
|
||||
uiSwapchainAcquired = false;
|
||||
|
||||
XrFrameWaitInfo frameWaitInfo = {XR_TYPE_FRAME_WAIT_INFO};
|
||||
frameWaitInfo.next = NULL;
|
||||
frameState.type = XR_TYPE_FRAME_STATE;
|
||||
frameState.next = NULL;
|
||||
XrResult res = xrWaitFrame(vr_session, &frameWaitInfo, &frameState);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: xrWaitFrame failed (XrResult=%d)\n", (int)res);
|
||||
return false;
|
||||
}
|
||||
|
||||
XrFrameBeginInfo frameBeginInfo = {XR_TYPE_FRAME_BEGIN_INFO};
|
||||
frameBeginInfo.next = NULL;
|
||||
res = xrBeginFrame(vr_session, &frameBeginInfo);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: xrBeginFrame failed (XrResult=%d)\n", (int)res);
|
||||
return false;
|
||||
}
|
||||
vr_frame_begun = true;
|
||||
|
||||
// Locate views to get per-eye poses and FOVs
|
||||
XrViewLocateInfo viewLocateInfo = {XR_TYPE_VIEW_LOCATE_INFO};
|
||||
viewLocateInfo.next = NULL;
|
||||
viewLocateInfo.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
||||
viewLocateInfo.displayTime = frameState.predictedDisplayTime;
|
||||
viewLocateInfo.space = vr_local_space;
|
||||
|
||||
XrViewState viewState = {XR_TYPE_VIEW_STATE};
|
||||
viewState.next = NULL;
|
||||
uint32_t viewCountOutput = 0;
|
||||
cachedViews[0].type = XR_TYPE_VIEW; cachedViews[0].next = NULL;
|
||||
cachedViews[1].type = XR_TYPE_VIEW; cachedViews[1].next = NULL;
|
||||
|
||||
res = xrLocateViews(vr_session, &viewLocateInfo, &viewState, 2, &viewCountOutput, cachedViews);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: xrLocateViews failed (XrResult=%d)\n", (int)res);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (viewCountOutput == 2 &&
|
||||
(viewState.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT) &&
|
||||
(viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT)) {
|
||||
cachedViewsValid = true;
|
||||
|
||||
XrSpaceLocation headLocation = {XR_TYPE_SPACE_LOCATION};
|
||||
res = xrLocateSpace(vr_view_space, vr_local_space, frameState.predictedDisplayTime, &headLocation);
|
||||
if (XR_SUCCEEDED(res) &&
|
||||
(headLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) &&
|
||||
(headLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)) {
|
||||
cachedHeadPose = headLocation.pose;
|
||||
cachedHeadPoseValid = true;
|
||||
} else {
|
||||
cachedHeadPose = cachedViews[0].pose;
|
||||
cachedHeadPose.position.x = (cachedViews[0].pose.position.x + cachedViews[1].pose.position.x) * 0.5f;
|
||||
cachedHeadPose.position.y = (cachedViews[0].pose.position.y + cachedViews[1].pose.position.y) * 0.5f;
|
||||
cachedHeadPose.position.z = (cachedViews[0].pose.position.z + cachedViews[1].pose.position.z) * 0.5f;
|
||||
cachedHeadPoseValid = true;
|
||||
}
|
||||
|
||||
float headPoseMatrix[16];
|
||||
float headInverseMeters[16];
|
||||
VR_PoseToMatrix(&cachedHeadPose, headPoseMatrix);
|
||||
VR_MatrixInv(headInverseMeters, headPoseMatrix);
|
||||
|
||||
const float worldScale = VR_WorldScale();
|
||||
const int poseMode = cv_vrposemode.value;
|
||||
|
||||
if (poseMode == 3)
|
||||
memcpy(vrHMDPoseMatrix, headPoseMatrix, sizeof(float) * 16);
|
||||
else
|
||||
memcpy(vrHMDPoseMatrix, headInverseMeters, sizeof(float) * 16);
|
||||
VR_ScaleMatrixTranslation(vrHMDPoseMatrix, worldScale);
|
||||
|
||||
memcpy(vrHMDScaledPoseMatrix, vrHMDPoseMatrix, sizeof(float) * 16);
|
||||
VR_ScaleMatrixTranslation(vrHMDScaledPoseMatrix, vrPlayerScale);
|
||||
|
||||
memcpy(vrHMDPoseSkyboxMatrix, vrHMDPoseMatrix, sizeof(float) * 16);
|
||||
vrHMDPoseSkyboxMatrix[12] = 0.0f;
|
||||
vrHMDPoseSkyboxMatrix[13] = 0.0f;
|
||||
vrHMDPoseSkyboxMatrix[14] = 0.0f;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
VR_FovToProjection(&cachedViews[i].fov, 0.9f, 32768.0f, vrEyeProjMatrix[i]);
|
||||
|
||||
float eyePoseMatrix[16];
|
||||
VR_PoseToMatrix(&cachedViews[i].pose, eyePoseMatrix);
|
||||
|
||||
if (poseMode == 1 || poseMode == 3) {
|
||||
float eyeToHeadMatrix[16];
|
||||
|
||||
// Match the old OpenVR path: keep the HMD pose and per-eye
|
||||
// head-to-eye transform separate, then multiply them in shader.
|
||||
VR_MatrixMultiply(eyeToHeadMatrix, headInverseMeters, eyePoseMatrix);
|
||||
VR_MatrixInv(vrEyeViewMatrix[i], eyeToHeadMatrix);
|
||||
} else if (poseMode == 2) {
|
||||
memcpy(vrEyeViewMatrix[i], eyePoseMatrix, sizeof(float) * 16);
|
||||
} else {
|
||||
// SDK-style path: render each eye from the full located view.
|
||||
VR_MatrixInv(vrEyeViewMatrix[i], eyePoseMatrix);
|
||||
}
|
||||
VR_ScaleMatrixTranslation(vrEyeViewMatrix[i], worldScale);
|
||||
}
|
||||
|
||||
VR_ScaleViewMatrices(vrPlayerScale, cv_vrdisableskystereo.value ? 0 : 1);
|
||||
} else {
|
||||
VR_SetIdentityMatrix(vrHMDPoseMatrix);
|
||||
VR_SetIdentityMatrix(vrHMDScaledPoseMatrix);
|
||||
VR_SetIdentityMatrix(vrHMDPoseSkyboxMatrix);
|
||||
VR_SetIdentityMatrix(vrEyeViewMatrix[0]);
|
||||
VR_SetIdentityMatrix(vrEyeViewMatrix[1]);
|
||||
VR_SetIdentityMatrix(vrScaledEyeViewMatrix[0]);
|
||||
VR_SetIdentityMatrix(vrScaledEyeViewMatrix[1]);
|
||||
VR_SetIdentityMatrix(vrEyeSkyboxViewMatrix[0]);
|
||||
VR_SetIdentityMatrix(vrEyeSkyboxViewMatrix[1]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean VR_SetEye(int eye)
|
||||
{
|
||||
if (!vr_started || !vr_session_running || !vr_frame_begun) return false;
|
||||
if (eye < 0 || eye > 1) return false;
|
||||
|
||||
vr_current_eye = eye;
|
||||
vr_drawing_ui = false;
|
||||
vr_render_pass = (eye == 0) ? VR_PASS_3D_LEFT : VR_PASS_3D_RIGHT;
|
||||
|
||||
XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO};
|
||||
acquireInfo.next = NULL;
|
||||
XrResult res = xrAcquireSwapchainImage(vr_swapchains[eye], &acquireInfo, &swapchainImageIndex[eye]);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: xrAcquireSwapchainImage failed for eye %d (XrResult=%d)\n", eye, (int)res);
|
||||
return false;
|
||||
}
|
||||
eyeSwapchainAcquired[eye] = true;
|
||||
|
||||
XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO};
|
||||
waitInfo.next = NULL;
|
||||
waitInfo.timeout = XR_INFINITE_DURATION;
|
||||
res = xrWaitSwapchainImage(vr_swapchains[eye], &waitInfo);
|
||||
if (XR_FAILED(res)) {
|
||||
CONS_Printf("VR: xrWaitSwapchainImage failed for eye %d (XrResult=%d)\n", eye, (int)res);
|
||||
XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO};
|
||||
releaseInfo.next = NULL;
|
||||
xrReleaseSwapchainImage(vr_swapchains[eye], &releaseInfo);
|
||||
eyeSwapchainAcquired[eye] = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, vr_framebuffers[eye][swapchainImageIndex[eye]]);
|
||||
glViewport(0, 0, vr_render_width, vr_render_height);
|
||||
SetModelView((GLint)vr_render_width, (GLint)vr_render_height);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VR_ReleaseEye(int eye)
|
||||
{
|
||||
if (!vr_started || !vr_session_running || eye < 0 || eye > 1 || !eyeSwapchainAcquired[eye]) return;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO};
|
||||
releaseInfo.next = NULL;
|
||||
xrReleaseSwapchainImage(vr_swapchains[eye], &releaseInfo);
|
||||
eyeSwapchainAcquired[eye] = false;
|
||||
eyeSwapchainRendered[eye] = true;
|
||||
}
|
||||
|
||||
void VR_BindDefaultFramebuffer(void)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
boolean VR_MirrorEyeToDefaultFramebuffer(int width, int height)
|
||||
{
|
||||
if (!vr_started || !vr_session_running || vr_current_eye < 0 || vr_current_eye > 1)
|
||||
return false;
|
||||
if (!eyeSwapchainAcquired[vr_current_eye] || !glBlitFramebuffer)
|
||||
return false;
|
||||
if (width <= 0 || height <= 0)
|
||||
return false;
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, vr_framebuffers[vr_current_eye][swapchainImageIndex[vr_current_eye]]);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glViewport(0, 0, width, height);
|
||||
glBlitFramebuffer(0, 0, (GLint)vr_render_width, (GLint)vr_render_height,
|
||||
0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VR_BeginInEyeUI(void)
|
||||
{
|
||||
if (!vr_started || !vr_session_running || !vr_frame_begun) return;
|
||||
if (vr_current_eye < 0 || vr_current_eye > 1 || !eyeSwapchainAcquired[vr_current_eye]) return;
|
||||
|
||||
vr_drawing_ui = true;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, vr_framebuffers[vr_current_eye][swapchainImageIndex[vr_current_eye]]);
|
||||
glViewport(0, 0, vr_render_width, vr_render_height);
|
||||
GL_UnSetShader();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
}
|
||||
|
||||
boolean VR_BindUISwapchain(void)
|
||||
{
|
||||
if (!vr_started || !vr_session_running || !vr_frame_begun || vr_ui_swapchain == XR_NULL_HANDLE) return false;
|
||||
|
||||
if (!uiSwapchainAcquired) {
|
||||
XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO};
|
||||
acquireInfo.next = NULL;
|
||||
XrResult res = xrAcquireSwapchainImage(vr_ui_swapchain, &acquireInfo, &uiSwapchainImageIndex);
|
||||
if (XR_FAILED(res)) return false;
|
||||
|
||||
XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO};
|
||||
waitInfo.next = NULL;
|
||||
waitInfo.timeout = XR_INFINITE_DURATION;
|
||||
res = xrWaitSwapchainImage(vr_ui_swapchain, &waitInfo);
|
||||
if (XR_FAILED(res)) {
|
||||
XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO};
|
||||
releaseInfo.next = NULL;
|
||||
xrReleaseSwapchainImage(vr_ui_swapchain, &releaseInfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
uiSwapchainAcquired = true;
|
||||
}
|
||||
|
||||
vr_render_pass = VR_PASS_UI;
|
||||
vr_drawing_ui = true;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, vr_ui_framebuffers[uiSwapchainImageIndex]);
|
||||
// Keep the hardware 2D renderer's logical screen size unchanged. Its UI
|
||||
// patch and screen-texture paths key off screen_width/screen_height, while
|
||||
// the OpenXR swapchain can still use its full physical viewport.
|
||||
SetModelView((GLint)vid.width, (GLint)vid.height);
|
||||
glViewport(0, 0, vr_ui_width, vr_ui_height);
|
||||
GL_UnSetShader();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VR_EndFrame(void)
|
||||
{
|
||||
if (!vr_started || !vr_session_running || !vr_frame_begun) return;
|
||||
|
||||
XrCompositionLayerProjectionView projectionViews[2];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
projectionViews[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
||||
projectionViews[i].next = NULL;
|
||||
|
||||
// Use the ACTUAL tracked view poses, not hardcoded placeholders
|
||||
if (cachedViewsValid) {
|
||||
projectionViews[i].pose = cachedViews[i].pose;
|
||||
projectionViews[i].fov = cachedViews[i].fov;
|
||||
} else {
|
||||
// Fallback identity pose (should rarely happen)
|
||||
projectionViews[i].pose.orientation.w = 1.0f;
|
||||
projectionViews[i].pose.orientation.x = 0.0f;
|
||||
projectionViews[i].pose.orientation.y = 0.0f;
|
||||
projectionViews[i].pose.orientation.z = 0.0f;
|
||||
projectionViews[i].pose.position.x = 0.0f;
|
||||
projectionViews[i].pose.position.y = 0.0f;
|
||||
projectionViews[i].pose.position.z = 0.0f;
|
||||
projectionViews[i].fov.angleUp = 0.9f;
|
||||
projectionViews[i].fov.angleDown = -0.9f;
|
||||
projectionViews[i].fov.angleLeft = -0.9f;
|
||||
projectionViews[i].fov.angleRight = 0.9f;
|
||||
}
|
||||
|
||||
projectionViews[i].subImage.swapchain = vr_swapchains[i];
|
||||
projectionViews[i].subImage.imageRect.offset.x = 0;
|
||||
projectionViews[i].subImage.imageRect.offset.y = 0;
|
||||
projectionViews[i].subImage.imageRect.extent.width = vr_render_width;
|
||||
projectionViews[i].subImage.imageRect.extent.height = vr_render_height;
|
||||
projectionViews[i].subImage.imageArrayIndex = 0;
|
||||
}
|
||||
|
||||
const XrCompositionLayerBaseHeader* layers[2];
|
||||
uint32_t layerCount = 0;
|
||||
|
||||
XrCompositionLayerProjection projectionLayer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION};
|
||||
if (cachedViewsValid && eyeSwapchainRendered[0] && eyeSwapchainRendered[1]) {
|
||||
projectionLayer.layerFlags = 0;
|
||||
projectionLayer.space = vr_local_space;
|
||||
projectionLayer.viewCount = 2;
|
||||
projectionLayer.views = projectionViews;
|
||||
layers[layerCount++] = (const XrCompositionLayerBaseHeader*)&projectionLayer;
|
||||
}
|
||||
|
||||
XrCompositionLayerQuad uiLayer = {XR_TYPE_COMPOSITION_LAYER_QUAD};
|
||||
if (uiSwapchainAcquired) {
|
||||
XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO};
|
||||
releaseInfo.next = NULL;
|
||||
xrReleaseSwapchainImage(vr_ui_swapchain, &releaseInfo);
|
||||
|
||||
uiLayer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT |
|
||||
XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT;
|
||||
uiLayer.space = cachedHeadPoseValid ? vr_local_space : vr_view_space;
|
||||
uiLayer.eyeVisibility = XR_EYE_VISIBILITY_BOTH;
|
||||
uiLayer.subImage.swapchain = vr_ui_swapchain;
|
||||
uiLayer.subImage.imageRect.offset.x = 0;
|
||||
uiLayer.subImage.imageRect.offset.y = 0;
|
||||
uiLayer.subImage.imageRect.extent.width = vr_ui_width;
|
||||
uiLayer.subImage.imageRect.extent.height = vr_ui_height;
|
||||
uiLayer.subImage.imageArrayIndex = 0;
|
||||
|
||||
const float uiDistance = cv_vruidistance.value > 0 ? (float)cv_vruidistance.value / 70.0f : 1.5f;
|
||||
const float uiWidth = cv_vruiscale.value > 0 ? (float)cv_vruiscale.value * 0.040f : 1.0f;
|
||||
|
||||
cachedUIPose = VR_MakeUIPose(uiDistance);
|
||||
cachedUIPoseValid = true;
|
||||
uiLayer.pose = cachedUIPose;
|
||||
|
||||
uiLayer.size.width = uiWidth;
|
||||
uiLayer.size.height = uiWidth * ((float)vr_ui_height / (float)vr_ui_width);
|
||||
|
||||
layers[layerCount++] = (const XrCompositionLayerBaseHeader*)&uiLayer;
|
||||
|
||||
uiSwapchainAcquired = false;
|
||||
}
|
||||
|
||||
XrFrameEndInfo frameEndInfo = {XR_TYPE_FRAME_END_INFO};
|
||||
frameEndInfo.next = NULL;
|
||||
frameEndInfo.displayTime = frameState.predictedDisplayTime;
|
||||
frameEndInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
|
||||
frameEndInfo.layerCount = layerCount;
|
||||
frameEndInfo.layers = layerCount > 0 ? layers : NULL;
|
||||
|
||||
XrResult res = xrEndFrame(vr_session, &frameEndInfo);
|
||||
if (XR_FAILED(res)) {
|
||||
static int endFrameErrorCount = 0;
|
||||
if (endFrameErrorCount < 5) {
|
||||
CONS_Printf("VR: xrEndFrame failed (XrResult=%d)\n", (int)res);
|
||||
endFrameErrorCount++;
|
||||
}
|
||||
}
|
||||
vr_frame_begun = false;
|
||||
vr_render_pass = VR_PASS_NONE;
|
||||
vr_drawing_ui = false;
|
||||
}
|
||||
|
||||
#endif // HAVE_VR
|
||||
42
src/vr/vr_render.h
Normal file
42
src/vr/vr_render.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef __VR_RENDER_H__
|
||||
#define __VR_RENDER_H__
|
||||
|
||||
#ifdef HAVE_VR
|
||||
#include "vr_main.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern XrSwapchain vr_swapchains[2];
|
||||
extern uint32_t vr_swapchain_lengths[2];
|
||||
extern XrSwapchainImageOpenGLKHR* vr_swapchain_images[2];
|
||||
extern uint32_t* vr_framebuffers[2];
|
||||
extern uint32_t* vr_depthbuffers[2];
|
||||
|
||||
extern XrSwapchain vr_ui_swapchain;
|
||||
extern uint32_t vr_ui_swapchain_length;
|
||||
extern XrSwapchainImageOpenGLKHR* vr_ui_swapchain_images;
|
||||
extern uint32_t* vr_ui_framebuffers;
|
||||
extern uint32_t vr_ui_width;
|
||||
extern uint32_t vr_ui_height;
|
||||
|
||||
boolean VR_InitSwapchains(void);
|
||||
void VR_DestroySwapchains(void);
|
||||
|
||||
// Frame Loop
|
||||
boolean VR_BeginFrame(void);
|
||||
boolean VR_SetEye(int eye);
|
||||
void VR_ReleaseEye(int eye);
|
||||
void VR_BindDefaultFramebuffer(void);
|
||||
void VR_BeginInEyeUI(void);
|
||||
boolean VR_MirrorEyeToDefaultFramebuffer(int width, int height);
|
||||
boolean VR_BindUISwapchain(void);
|
||||
void VR_EndFrame(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // HAVE_VR
|
||||
#endif // __VR_RENDER_H__
|
||||
|
|
@ -125,7 +125,7 @@ wadfile_t *wadfiles[MAX_WADFILES]; // 0 to numwadfiles-1 are valid
|
|||
// If not done on a Mac then open wad files
|
||||
// can prevent removable media they are on from
|
||||
// being ejected
|
||||
void W_Shutdown(void)
|
||||
DESTRUCTOR static void W_Shutdown(void)
|
||||
{
|
||||
lumpnum_map_cleanup(&lumpnumcache);
|
||||
|
||||
|
|
|
|||
|
|
@ -152,8 +152,6 @@ extern wadfile_t *wadfiles[MAX_WADFILES];
|
|||
|
||||
// =========================================================================
|
||||
|
||||
void W_Shutdown(void);
|
||||
|
||||
// Opens a WAD file. Returns the FILE * handle for the file, or NULL if not found or could not be opened
|
||||
FILE *W_OpenWadFile(const char **filename, boolean useerrors);
|
||||
// Load and add a wadfile to the active wad files, returns numbers of lumps, INT16_MAX on error
|
||||
|
|
|
|||
|
|
@ -547,9 +547,12 @@ void Y_IntermissionDrawer(void)
|
|||
UINT8 *colormap = R_GetTranslationColormap(*data.character[i], *data.color[i], GTC_CACHE);
|
||||
patch_t *facerank;
|
||||
|
||||
facerank = faceprefix[*data.character[i]][FACE_RANK];
|
||||
boolean hires = K_UseHighResPortraits() && !manyplayers16 && !displayitemrolls;
|
||||
facerank = faceprefix[*data.character[i]][hires ? FACE_WANTED : FACE_RANK];
|
||||
SINT8 hiresoffsetx = hires ? 8: 0;
|
||||
SINT8 hiresoffsety = hires ? 2: 0;
|
||||
|
||||
fixed_t scale = FRACUNIT;
|
||||
fixed_t scale = hires ? FRACUNIT / 2 : FRACUNIT;
|
||||
|
||||
if (manyplayers16)
|
||||
{
|
||||
|
|
@ -560,8 +563,10 @@ void Y_IntermissionDrawer(void)
|
|||
else
|
||||
{
|
||||
INT32 xoffs, yoffs;
|
||||
INT16 leftoffset = hires ? facerank->leftoffset / 2 : facerank->leftoffset;
|
||||
INT16 topoffset = hires ? facerank->topoffset / 2 :facerank->topoffset;
|
||||
|
||||
scale = (displayitemrolls) ? FRACUNIT/2 : FRACUNIT;
|
||||
scale = (hires || displayitemrolls) ? FRACUNIT/2 : FRACUNIT;
|
||||
|
||||
xoffs = FixedMul(16, scale);
|
||||
yoffs = FixedMul(4, scale);
|
||||
|
|
@ -569,8 +574,8 @@ void Y_IntermissionDrawer(void)
|
|||
if (displayitemrolls)
|
||||
{
|
||||
V_DrawFixedPatch(
|
||||
((x+11-xscroll_px)*FRACUNIT) + ((facerank->leftoffset) * scale),
|
||||
((y+1)*FRACUNIT) + ((facerank->topoffset) * scale),
|
||||
((x+11-xscroll_px)*FRACUNIT) + ((leftoffset) * scale),
|
||||
((y+1)*FRACUNIT) + ((topoffset) * scale),
|
||||
scale,
|
||||
0,
|
||||
facerank,
|
||||
|
|
@ -587,7 +592,7 @@ void Y_IntermissionDrawer(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
V_DrawFixedPatch((x+xoffs)<<FRACBITS, ((y-yoffs)<<FRACBITS), scale, 0, facerank, colormap);
|
||||
V_DrawFixedPatch((x+xoffs+hiresoffsetx)<<FRACBITS, ((y-yoffs- +hiresoffsety)<<FRACBITS), scale, 0, facerank, colormap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue