mirror of
https://github.com/DrBeef/ioq3quest.git
synced 2025-03-03 07:50:58 +00:00
Small spectator fixes
- Switch between Follow modes (1st and 3rd person) using the X button - Switch back to the "Spectate" mode using the A button when in follow mode - 1st Person follow mode is on a virtual screen (to avoid nausea) - Slider for haptic intensity: affects both controller and bhaptics level - Move 3rd person Spectator and Death Cam closer to player's viewpoint
This commit is contained in:
parent
9426f8605b
commit
e4d49b44e6
12 changed files with 133 additions and 39 deletions
|
@ -2,8 +2,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.drbeef.ioq3quest"
|
||||
android:installLocation="preferExternal"
|
||||
android:versionCode="25"
|
||||
android:versionName="0.18.0">
|
||||
android:versionCode="26"
|
||||
android:versionName="0.19.0">
|
||||
<uses-feature android:name="android.hardware.vr.headtracking" android:version="1" android:required="true" />
|
||||
<uses-feature android:glEsVersion="0x00030001" />
|
||||
<!-- <uses-feature android:name="oculus.software.overlay_keyboard" android:required="false"/>-->
|
||||
|
|
|
@ -2725,33 +2725,43 @@ void CG_DrawActive( stereoFrame_t stereoView ) {
|
|||
vec3_t baseOrg;
|
||||
VectorCopy( cg.refdef.vieworg, baseOrg );
|
||||
|
||||
float heightOffset = 0.0f;
|
||||
float worldscale = trap_Cvar_VariableValue("vr_worldscale");
|
||||
if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW))
|
||||
if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_THIRDPERSON))
|
||||
{
|
||||
worldscale *= SPECTATOR_WORLDSCALE_MULTIPLIER;
|
||||
//Just move camera down about 20cm
|
||||
heightOffset = -0.2f;
|
||||
}
|
||||
else if (( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) &&
|
||||
( cg.snap->ps.pm_type != PM_INTERMISSION ))
|
||||
{
|
||||
worldscale *= DEATH_WORLDSCALE_MULTIPLIER;
|
||||
//Just move camera down about 50cm
|
||||
heightOffset = -0.5f;
|
||||
}
|
||||
|
||||
|
||||
float ipd = trap_Cvar_VariableValue("r_stereoSeparation") / 1000.0f;
|
||||
float separation = worldscale * (ipd / 2) * (stereoView == STEREO_LEFT ? -1.0f : 1.0f);
|
||||
|
||||
if (cgs.localServer) {
|
||||
cg.refdef.vieworg[2] -= PLAYER_HEIGHT;
|
||||
cg.refdef.vieworg[2] += vr->hmdposition[1] * worldscale;
|
||||
}
|
||||
else
|
||||
if (cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_FIRSTPERSON)
|
||||
{
|
||||
//Do nothing to view height if we are following in first person
|
||||
}
|
||||
else
|
||||
{
|
||||
cg.refdef.vieworg[2] -= PLAYER_HEIGHT;
|
||||
cg.refdef.vieworg[2] += (vr->hmdposition[1] + heightOffset) * worldscale;
|
||||
}
|
||||
|
||||
if (!cgs.localServer)
|
||||
{
|
||||
//If connected to external server, allow some amount of faked positional tracking
|
||||
cg.refdef.vieworg[2] -= PLAYER_HEIGHT;
|
||||
cg.refdef.vieworg[2] += vr->hmdposition[1] * worldscale;
|
||||
//If connected to a remote server, allow some amount of faked positional tracking
|
||||
if (cg.snap->ps.stats[STAT_HEALTH] > 0 &&
|
||||
//Don't use fake positional if following another player - this is handled in the
|
||||
//VR third person code
|
||||
!( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW)))
|
||||
!( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_THIRDPERSON)))
|
||||
{
|
||||
vec3_t pos, hmdposition, vieworg;
|
||||
VectorClear(pos);
|
||||
|
|
|
@ -77,7 +77,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
#define NUM_CROSSHAIRS 10
|
||||
|
||||
//multiplying size you go to when dead looking down on the match
|
||||
#define DEATH_WORLDSCALE_MULTIPLIER 40
|
||||
#define DEATH_WORLDSCALE_MULTIPLIER 30
|
||||
#define SPECTATOR_WORLDSCALE_MULTIPLIER 10
|
||||
|
||||
#define PLAYER_HEIGHT 48
|
||||
|
|
|
@ -2030,7 +2030,7 @@ static void CG_PlayerSprites( centity_t *cent ) {
|
|||
//Put a sprite over the followed player's head
|
||||
if ( cent->currentState.number == cg.snap->ps.clientNum &&
|
||||
cg.renderingThirdPerson &&
|
||||
cg.snap->ps.pm_flags & PMF_FOLLOW) {
|
||||
(cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_THIRDPERSON)) {
|
||||
CG_PlayerFloatSprite( cent, cgs.media.friendShader );
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -227,16 +227,15 @@ CG_OffsetVRThirdPersonView
|
|||
static void CG_OffsetVRThirdPersonView( void ) {
|
||||
float scale = 1.0f;
|
||||
|
||||
if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW))
|
||||
if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_THIRDPERSON))
|
||||
{
|
||||
scale *= SPECTATOR_WORLDSCALE_MULTIPLIER;
|
||||
|
||||
//Check to see if the followed player has moved far enough away to mean we should update our location
|
||||
vec3_t currentOrigin;
|
||||
VectorCopy(cg.refdef.vieworg, currentOrigin);
|
||||
VectorSubtract(currentOrigin, cg.vr_vieworigin, currentOrigin);
|
||||
currentOrigin[2] = 0;
|
||||
if (VectorLength(currentOrigin) > 400)
|
||||
vec3_t current;
|
||||
VectorSubtract(cg.refdef.vieworg, cg.vr_vieworigin, current);
|
||||
current[2] = 0;
|
||||
if (VectorLength(current) > 400)
|
||||
{
|
||||
VectorCopy(cg.refdef.vieworg, cg.vr_vieworigin);
|
||||
|
||||
|
@ -665,7 +664,7 @@ static int CG_CalcViewValues( stereoFrame_t stereoView ) {
|
|||
trap_Cvar_Set( "vr_noSkybox", (((ps->stats[STAT_HEALTH] <= 0) &&
|
||||
( ps->pm_type != PM_INTERMISSION )) ||
|
||||
cg.demoPlayback ||
|
||||
(cg.snap->ps.pm_flags & PMF_FOLLOW) ? "1" : "0" ));
|
||||
(cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_THIRDPERSON) ? "1" : "0" ));
|
||||
|
||||
// intermission view
|
||||
static float hmdYaw = 0;
|
||||
|
@ -722,7 +721,7 @@ static int CG_CalcViewValues( stereoFrame_t stereoView ) {
|
|||
|
||||
if (( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) &&
|
||||
( cg.predictedPlayerState.pm_type != PM_INTERMISSION ) ||
|
||||
( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW)))
|
||||
( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_THIRDPERSON)))
|
||||
{
|
||||
//If dead, or spectating, view the map from above
|
||||
CG_OffsetVRThirdPersonView();
|
||||
|
@ -797,7 +796,7 @@ static int CG_CalcViewValues( stereoFrame_t stereoView ) {
|
|||
angles[ROLL] = vr->hmdorientation[ROLL];
|
||||
AnglesToAxis( angles, cg.refdef.viewaxis );
|
||||
}
|
||||
else if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW))
|
||||
else if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_THIRDPERSON))
|
||||
{
|
||||
//If we're following someone,
|
||||
vec3_t angles;
|
||||
|
@ -805,7 +804,7 @@ static int CG_CalcViewValues( stereoFrame_t stereoView ) {
|
|||
angles[YAW] = vr->clientviewangles[YAW];
|
||||
AnglesToAxis(angles, cg.refdef.viewaxis);
|
||||
}
|
||||
else
|
||||
else if (!(cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_FIRSTPERSON))
|
||||
{
|
||||
//We are connected to a multiplayer server, so make the appropriate adjustment to the view
|
||||
//angles as we send orientation to the server that includes the weapon angles
|
||||
|
@ -815,6 +814,10 @@ static int CG_CalcViewValues( stereoFrame_t stereoView ) {
|
|||
angles[YAW] = deltaYaw + vr->clientviewangles[YAW];
|
||||
AnglesToAxis(angles, cg.refdef.viewaxis);
|
||||
}
|
||||
else
|
||||
{
|
||||
AnglesToAxis(cg.refdefViewAngles, cg.refdef.viewaxis);
|
||||
}
|
||||
} else {
|
||||
if (vr->weapon_zoomed) {
|
||||
vec3_t angles;
|
||||
|
@ -823,7 +826,7 @@ static int CG_CalcViewValues( stereoFrame_t stereoView ) {
|
|||
angles[YAW] = (cg.refdefViewAngles[YAW] - vr->hmdorientation[YAW]) + vr->weaponangles[YAW];
|
||||
AnglesToAxis(angles, cg.refdef.viewaxis);
|
||||
}
|
||||
else if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW))
|
||||
else if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_THIRDPERSON))
|
||||
{
|
||||
//If we're following someone,
|
||||
vec3_t angles;
|
||||
|
@ -954,7 +957,7 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
|
|||
|
||||
// decide on third person view
|
||||
cg.renderingThirdPerson = cg.predictedPlayerState.pm_type == PM_SPECTATOR ||
|
||||
cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) ||
|
||||
cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_THIRDPERSON) ||
|
||||
cg_thirdPerson.integer;
|
||||
|
||||
// build cg.refdef
|
||||
|
|
|
@ -1714,6 +1714,7 @@ void CG_AddViewWeapon( playerState_t *ps ) {
|
|||
|
||||
//Scale / Move gun etc
|
||||
float scale = 1.0f;
|
||||
if (!(cg.snap->ps.pm_flags & PMF_FOLLOW && vr->follow_mode == VRFM_FIRSTPERSON))
|
||||
{
|
||||
char cvar_name[64];
|
||||
Com_sprintf(cvar_name, sizeof(cvar_name), "vr_weapon_adjustment_%i", ps->weapon);
|
||||
|
|
|
@ -55,8 +55,9 @@ VR OPTIONS MENU
|
|||
#define ID_SENDROLL 141
|
||||
#define ID_LASERSIGHT 142
|
||||
#define ID_GORE 143
|
||||
#define ID_HAPTICINTENSITY 144
|
||||
|
||||
#define ID_BACK 144
|
||||
#define ID_BACK 145
|
||||
|
||||
#define NUM_HUDDEPTH 6
|
||||
#define NUM_DIRECTIONMODE 2
|
||||
|
@ -86,6 +87,7 @@ typedef struct {
|
|||
menuslider_s hudyoffset;
|
||||
menuradiobutton_s sendroll;
|
||||
menuradiobutton_s lasersight;
|
||||
menuslider_s hapticintensity;
|
||||
menulist_s gore;
|
||||
|
||||
menubitmap_s back;
|
||||
|
@ -219,6 +221,10 @@ static void VR_Event( void* ptr, int notification ) {
|
|||
trap_Cvar_SetValue( "vr_lasersight", s_VR.lasersight.curvalue);
|
||||
break;
|
||||
|
||||
case ID_HAPTICINTENSITY:
|
||||
trap_Cvar_SetValue( "vr_hapticIntensity", s_VR.hapticintensity.curvalue);
|
||||
break;
|
||||
|
||||
case ID_GORE: {
|
||||
switch ((int)s_VR.gore.curvalue) {
|
||||
case 0:
|
||||
|
@ -478,6 +484,17 @@ static void VR_MenuInit( void ) {
|
|||
s_VR.lasersight.generic.x = VR_X_POS;
|
||||
s_VR.lasersight.generic.y = y;
|
||||
|
||||
y += BIGCHAR_HEIGHT;
|
||||
s_VR.hapticintensity.generic.type = MTYPE_SLIDER;
|
||||
s_VR.hapticintensity.generic.x = VR_X_POS;
|
||||
s_VR.hapticintensity.generic.y = y;
|
||||
s_VR.hapticintensity.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
|
||||
s_VR.hapticintensity.generic.name = "Haptic Intensity:";
|
||||
s_VR.hapticintensity.generic.id = ID_HAPTICINTENSITY;
|
||||
s_VR.hapticintensity.generic.callback = VR_Event;
|
||||
s_VR.hapticintensity.minvalue = 0;
|
||||
s_VR.hapticintensity.maxvalue = 1.0;
|
||||
|
||||
y += BIGCHAR_HEIGHT + 10;
|
||||
s_VR.gore.generic.type = MTYPE_SPINCONTROL;
|
||||
s_VR.gore.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
|
||||
|
@ -518,6 +535,7 @@ static void VR_MenuInit( void ) {
|
|||
Menu_AddItem( &s_VR.menu, &s_VR.hudyoffset );
|
||||
Menu_AddItem( &s_VR.menu, &s_VR.sendroll );
|
||||
Menu_AddItem( &s_VR.menu, &s_VR.lasersight );
|
||||
Menu_AddItem( &s_VR.menu, &s_VR.hapticintensity );
|
||||
Menu_AddItem( &s_VR.menu, &s_VR.gore );
|
||||
|
||||
Menu_AddItem( &s_VR.menu, &s_VR.back );
|
||||
|
|
|
@ -33,6 +33,7 @@ cvar_t *vr_rollWhenHit = NULL;
|
|||
cvar_t *vr_hudYOffset = NULL;
|
||||
cvar_t *vr_sendRollToServer = NULL;
|
||||
cvar_t *vr_lasersight = NULL;
|
||||
cvar_t *vr_hapticIntensity = NULL;
|
||||
|
||||
engine_t* VR_Init( ovrJava java )
|
||||
{
|
||||
|
@ -67,6 +68,7 @@ void VR_InitCvars( void )
|
|||
vr_hudYOffset = Cvar_Get ("vr_hudYOffset", "0", CVAR_ARCHIVE);
|
||||
vr_sendRollToServer = Cvar_Get ("vr_sendRollToServer", "1", CVAR_ARCHIVE);
|
||||
vr_lasersight = Cvar_Get ("vr_lasersight", "0", CVAR_ARCHIVE);
|
||||
vr_hapticIntensity = Cvar_Get ("vr_hapticIntensity", "1.0", CVAR_ARCHIVE);
|
||||
|
||||
// Values are: scale,right,up,forward,pitch,yaw,roll
|
||||
// VALUES PROVIDED BY SkillFur - Thank-you!
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
|
||||
#define NUM_WEAPON_SAMPLES 10
|
||||
|
||||
typedef enum {
|
||||
VRFM_THIRDPERSON,
|
||||
VRFM_FIRSTPERSON
|
||||
} followMode_t;
|
||||
|
||||
typedef struct {
|
||||
qboolean weapon_stabilised;
|
||||
qboolean weapon_zoomed;
|
||||
|
@ -13,6 +18,7 @@ typedef struct {
|
|||
qboolean right_handed;
|
||||
qboolean virtual_screen;
|
||||
qboolean local_server; // used in bg_pmove.c
|
||||
followMode_t follow_mode;
|
||||
|
||||
int realign; // used to realign the fake 6DoF playspace in a multiplayer game
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ extern cvar_t *vr_heightAdjust;
|
|||
extern cvar_t *vr_twoHandedWeapons;
|
||||
extern cvar_t *vr_refreshrate;
|
||||
extern cvar_t *vr_weaponScope;
|
||||
extern cvar_t *vr_hapticIntensity;
|
||||
|
||||
jclass callbackClass;
|
||||
jmethodID android_haptic_event;
|
||||
|
@ -206,7 +207,7 @@ void VR_Vibrate( int duration, int chan, float intensity )
|
|||
return;
|
||||
|
||||
vibration_channel_duration[channel-1] = duration;
|
||||
vibration_channel_intensity[channel-1] = intensity;
|
||||
vibration_channel_intensity[channel-1] = intensity * vr_hapticIntensity->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -268,9 +269,14 @@ static void IN_SendButtonAction(const char* action, qboolean pressed)
|
|||
|
||||
void VR_HapticEvent(const char* event, int position, int flags, int intensity, float angle, float yHeight )
|
||||
{
|
||||
if (vr_hapticIntensity->value == 0.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
engine_t* engine = VR_GetEngine();
|
||||
jstring StringArg1 = (*(engine->java.Env))->NewStringUTF(engine->java.Env, event);
|
||||
(*(engine->java.Env))->CallVoidMethod(engine->java.Env, engine->java.ActivityObject, android_haptic_event, StringArg1, position, flags, intensity, angle, yHeight);
|
||||
(*(engine->java.Env))->CallVoidMethod(engine->java.Env, engine->java.ActivityObject, android_haptic_event, StringArg1, position, flags, intensity * vr_hapticIntensity->value, angle, yHeight);
|
||||
|
||||
//Controller Haptic Support
|
||||
int weaponFireChannel = vr.weapon_stabilised ? 3 : (vr_righthanded->integer ? 2 : 1);
|
||||
|
@ -714,14 +720,21 @@ static void IN_VRButtonsChanged( qboolean isRightController, uint32_t buttons )
|
|||
//Jump
|
||||
if ((buttons & ovrButton_A) && !(controller->buttons & ovrButton_A))
|
||||
{
|
||||
if (IN_GetButtonAction("A", action))
|
||||
if (cl.snap.ps.pm_flags & PMF_FOLLOW)
|
||||
{
|
||||
IN_SendButtonAction(action, qtrue);
|
||||
Cbuf_AddText("cmd team spectator\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IN_GetButtonAction("A", action))
|
||||
{
|
||||
IN_SendButtonAction(action, qtrue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!(buttons & ovrButton_A) && (controller->buttons & ovrButton_A))
|
||||
{
|
||||
if (IN_GetButtonAction("A", action))
|
||||
if (IN_GetButtonAction("A", action) && !(cl.snap.ps.pm_flags & PMF_FOLLOW))
|
||||
{
|
||||
IN_SendButtonAction(action, qfalse);
|
||||
}
|
||||
|
@ -741,17 +754,28 @@ static void IN_VRButtonsChanged( qboolean isRightController, uint32_t buttons )
|
|||
}
|
||||
|
||||
//X default is "use item"
|
||||
if ((buttons & ovrButton_X) && !(controller->buttons & ovrButton_X)) {
|
||||
if (IN_GetButtonAction("X", action))
|
||||
if ((buttons & ovrButton_X) && !(controller->buttons & ovrButton_X))
|
||||
{
|
||||
if (cl.snap.ps.pm_flags & PMF_FOLLOW)
|
||||
{
|
||||
IN_SendButtonAction(action, qtrue);
|
||||
//Switch follow mode
|
||||
vr.follow_mode = 1 - vr.follow_mode;
|
||||
}
|
||||
} else if (!(buttons & ovrButton_X) && (controller->buttons & ovrButton_X)) {
|
||||
if (IN_GetButtonAction("X", action))
|
||||
else
|
||||
{
|
||||
if (IN_GetButtonAction("X", action))
|
||||
{
|
||||
IN_SendButtonAction(action, qtrue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!(buttons & ovrButton_X) && (controller->buttons & ovrButton_X))
|
||||
{
|
||||
if (IN_GetButtonAction("X", action) && !(cl.snap.ps.pm_flags & PMF_FOLLOW))
|
||||
{
|
||||
IN_SendButtonAction(action, qfalse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Y default is Gesture
|
||||
if ((buttons & ovrButton_Y) && !(controller->buttons & ovrButton_Y)) {
|
||||
|
|
|
@ -268,7 +268,8 @@ void VR_DrawFrame( engine_t* engine ) {
|
|||
int eyeW, eyeH;
|
||||
VR_GetResolution(engine, &eyeW, &eyeH);
|
||||
|
||||
if (VR_useScreenLayer())
|
||||
if (VR_useScreenLayer() ||
|
||||
(cl.snap.ps.pm_flags & PMF_FOLLOW && vr.follow_mode == VRFM_FIRSTPERSON))
|
||||
{
|
||||
static ovrLayer_Union2 cylinderLayer;
|
||||
memset( &cylinderLayer, 0, sizeof( ovrLayer_Union2 ) );
|
||||
|
|
29
android/deploy.bat
Normal file
29
android/deploy.bat
Normal file
|
@ -0,0 +1,29 @@
|
|||
@echo off
|
||||
|
||||
setlocal
|
||||
|
||||
set BUILD_TYPE=release
|
||||
set VERSION=0.19.0
|
||||
|
||||
set adb="%ANDROID_SDK_ROOT%\platform-tools\adb.exe"
|
||||
|
||||
pushd android
|
||||
|
||||
set PACKAGE_NAME=com.drbeef.ioq3quest
|
||||
set ANDROID_STORAGE_LOCATION=/sdcard/ioquake3quest/
|
||||
set APK_LOCATION=.\app\build\outputs\apk\%BUILD_TYPE%\ioq3quest-%BUILD_TYPE%-%VERSION%.apk
|
||||
|
||||
%adb% install -r %APK_LOCATION%
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
%adb% uninstall %PACKAGE_NAME%
|
||||
%adb% install %APK_LOCATION%
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
popd
|
||||
popd
|
||||
echo "Failed to install apk."
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
:END
|
||||
endlocal
|
Loading…
Reference in a new issue