From 7ec9b2932ed4c4affe4a28066690b87a82a67dc8 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 14 Apr 2020 16:49:54 +0100 Subject: [PATCH] Multiple improvements - version number update - pickup haptic level - default world quaking to 0 - earth quake now triggers haptics - Big changes to allow key mapping to work - X button will delete a mapping - Menu control uses only A/B (right-hand) X/Y (left-hand) due to mapping changes --- Projects/Android/AndroidManifest.xml | 4 +- Projects/Android/jni/QzDoom/VrInputDefault.c | 198 ++++++++---------- .../jni/gzdoom-g3.3mgw_mobile/src/doomstat.h | 2 +- .../src/g_statusbar/sbar_mugshot.cpp | 8 +- .../src/gl/renderer/gl_postprocess.cpp | 2 +- .../src/gl/stereo3d/gl_oculusquest.cpp | 8 +- .../src/gl/stereo3d/gl_stereo_cvars.cpp | 3 + .../gzdoom-g3.3mgw_mobile/src/menu/menu.cpp | 5 +- .../gzdoom-g3.3mgw_mobile/src/r_utility.cpp | 11 + .../jni/gzdoom-g3.3mgw_mobile/src/v_blend.cpp | 8 +- .../jni/gzdoom-g3.3mgw_mobile/src/version.h | 2 +- .../wadsrc/static/menudef.txt | 4 + assets/res/lzdoom.pk3 | Bin 1251547 -> 1251592 bytes 13 files changed, 134 insertions(+), 121 deletions(-) diff --git a/Projects/Android/AndroidManifest.xml b/Projects/Android/AndroidManifest.xml index 45bc8c0..d945b44 100644 --- a/Projects/Android/AndroidManifest.xml +++ b/Projects/Android/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="4" + android:versionName="0.3.0" android:installLocation="auto" > diff --git a/Projects/Android/jni/QzDoom/VrInputDefault.c b/Projects/Android/jni/QzDoom/VrInputDefault.c index 3e1d801..f2897b3 100644 --- a/Projects/Android/jni/QzDoom/VrInputDefault.c +++ b/Projects/Android/jni/QzDoom/VrInputDefault.c @@ -19,7 +19,7 @@ Authors : Simon Brown #include "doomkeys.h" int getGameState(); -int isMenuActive(); +int getMenuState(); void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base); void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, ovrInputStateTrackedRemote *pDominantTrackedRemoteOld, ovrTracking* pDominantTracking, @@ -30,7 +30,7 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, //Menu button - invoke menu handleTrackedControllerButton(&leftTrackedRemoteState_new, &leftTrackedRemoteState_old, ovrButton_Enter, KEY_ESCAPE); - if (getGameState() != 0 || isMenuActive()) //gamestate != GS_LEVEL + if (getGameState() != 0 || getMenuState() != 0) // If getMenuState returns 2, then we are waiting for a key mapping input, so send normal keymappings { Joy_GenerateButtonEvents((pOffTrackedRemoteOld->Joystick.x > 0.7f ? 1 : 0), (pOffTrackedRemoteNew->Joystick.x > 0.7f ? 1 : 0), 1, KEY_PAD_DPAD_RIGHT); Joy_GenerateButtonEvents((pDominantTrackedRemoteOld->Joystick.x > 0.7f ? 1 : 0), (pDominantTrackedRemoteNew->Joystick.x > 0.7f ? 1 : 0), 1, KEY_PAD_DPAD_RIGHT); @@ -43,53 +43,52 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, Joy_GenerateButtonEvents((pOffTrackedRemoteOld->Joystick.y > 0.7f ? 1 : 0), (pOffTrackedRemoteNew->Joystick.y > 0.7f ? 1 : 0), 1, KEY_PAD_DPAD_UP); Joy_GenerateButtonEvents((pDominantTrackedRemoteOld->Joystick.y > 0.7f ? 1 : 0), (pDominantTrackedRemoteNew->Joystick.y > 0.7f ? 1 : 0), 1, KEY_PAD_DPAD_UP); - - handleTrackedControllerButton(pDominantTrackedRemoteNew, pDominantTrackedRemoteOld, domButton1, KEY_PAD_A); - handleTrackedControllerButton(pDominantTrackedRemoteNew, pDominantTrackedRemoteOld, ovrButton_Trigger, KEY_PAD_A); - handleTrackedControllerButton(pDominantTrackedRemoteNew, pDominantTrackedRemoteOld, domButton2, KEY_PAD_B); - - handleTrackedControllerButton(pOffTrackedRemoteNew, pOffTrackedRemoteOld, domButton1, KEY_PAD_A); - handleTrackedControllerButton(pOffTrackedRemoteNew, pOffTrackedRemoteOld, ovrButton_Trigger, KEY_PAD_A); - handleTrackedControllerButton(pOffTrackedRemoteNew, pOffTrackedRemoteOld, domButton2, KEY_PAD_B); - - handleTrackedControllerButton(&leftTrackedRemoteState_new, &leftTrackedRemoteState_old, ovrButton_Enter, KEY_PAD_B); } - else + + //Dominant Grip works like a shift key + bool dominantGripPushedOld = + (pDominantTrackedRemoteOld->Buttons & ovrButton_GripTrigger) != 0; + bool dominantGripPushedNew = + (pDominantTrackedRemoteNew->Buttons & ovrButton_GripTrigger) != 0; + + + // Only do the following if we are definitely not in the menu + if (getMenuState() == 0) { - float distance = sqrtf(powf(pOffTracking->HeadPose.Pose.Position.x - pDominantTracking->HeadPose.Pose.Position.x, 2) + - powf(pOffTracking->HeadPose.Pose.Position.y - pDominantTracking->HeadPose.Pose.Position.y, 2) + - powf(pOffTracking->HeadPose.Pose.Position.z - pDominantTracking->HeadPose.Pose.Position.z, 2)); + float distance = sqrtf(powf(pOffTracking->HeadPose.Pose.Position.x - + pDominantTracking->HeadPose.Pose.Position.x, 2) + + powf(pOffTracking->HeadPose.Pose.Position.y - + pDominantTracking->HeadPose.Pose.Position.y, 2) + + powf(pOffTracking->HeadPose.Pose.Position.z - + pDominantTracking->HeadPose.Pose.Position.z, 2)); //Turn on weapon stabilisation? if ((pOffTrackedRemoteNew->Buttons & ovrButton_GripTrigger) != (pOffTrackedRemoteOld->Buttons & ovrButton_GripTrigger)) { - if (pOffTrackedRemoteNew->Buttons & ovrButton_GripTrigger) - { - if (distance < 0.50f) - { + if (pOffTrackedRemoteNew->Buttons & ovrButton_GripTrigger) { + if (distance < 0.50f) { weaponStabilised = true; } - } - else - { + } else { weaponStabilised = false; } } //dominant hand stuff first { - ///Weapon location relative to view + ///Weapon location relative to view weaponoffset[0] = pDominantTracking->HeadPose.Pose.Position.x - hmdPosition[0]; weaponoffset[1] = pDominantTracking->HeadPose.Pose.Position.y - hmdPosition[1]; weaponoffset[2] = pDominantTracking->HeadPose.Pose.Position.z - hmdPosition[2]; - { - vec2_t v; - rotateAboutOrigin(weaponoffset[0], weaponoffset[2], -(doomYaw - hmdorientation[YAW]), v); - weaponoffset[0] = v[1]; - weaponoffset[2] = v[0]; - } + { + vec2_t v; + rotateAboutOrigin(weaponoffset[0], weaponoffset[2], + -(doomYaw - hmdorientation[YAW]), v); + weaponoffset[0] = v[1]; + weaponoffset[2] = v[0]; + } //Set gun angles const ovrQuatf quatRemote = pDominantTracking->HeadPose.Pose.Orientation; @@ -99,15 +98,18 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, QuatToYawPitchRoll(quatRemote, rotation, weaponangles); - if (weaponStabilised) - { - float z = pOffTracking->HeadPose.Pose.Position.z - pDominantTracking->HeadPose.Pose.Position.z; - float x = pOffTracking->HeadPose.Pose.Position.x - pDominantTracking->HeadPose.Pose.Position.x; - float y = pOffTracking->HeadPose.Pose.Position.y - pDominantTracking->HeadPose.Pose.Position.y; + if (weaponStabilised) { + float z = pOffTracking->HeadPose.Pose.Position.z - + pDominantTracking->HeadPose.Pose.Position.z; + float x = pOffTracking->HeadPose.Pose.Position.x - + pDominantTracking->HeadPose.Pose.Position.x; + float y = pOffTracking->HeadPose.Pose.Position.y - + pDominantTracking->HeadPose.Pose.Position.y; float zxDist = length(x, z); if (zxDist != 0.0f && z != 0.0f) { - VectorSet(weaponangles, -degrees(atanf(y / zxDist)), -degrees(atan2f(x, -z)), weaponangles[ROLL]); + VectorSet(weaponangles, -degrees(atanf(y / zxDist)), -degrees(atan2f(x, -z)), + weaponangles[ROLL]); } } } @@ -120,33 +122,33 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, offhandoffset[1] = pOffTracking->HeadPose.Pose.Position.y - hmdPosition[1]; offhandoffset[2] = pOffTracking->HeadPose.Pose.Position.z - hmdPosition[2]; - vec2_t v; - rotateAboutOrigin(offhandoffset[0], offhandoffset[2], -(doomYaw - hmdorientation[YAW]), v); - offhandoffset[0] = v[1]; - offhandoffset[2] = v[0]; + vec2_t v; + rotateAboutOrigin(offhandoffset[0], offhandoffset[2], -(doomYaw - hmdorientation[YAW]), + v); + offhandoffset[0] = v[1]; + offhandoffset[2] = v[0]; vec3_t rotation = {0}; rotation[PITCH] = 20; QuatToYawPitchRoll(pOffTracking->HeadPose.Pose.Orientation, rotation, offhandangles); - if (vr_moveuseoffhand != 0) { - controllerYawHeading = offhandangles[YAW] - hmdorientation[YAW]; - } - else - { - controllerYawHeading = 0.0f; - } + if (vr_moveuseoffhand != 0) { + controllerYawHeading = offhandangles[YAW] - hmdorientation[YAW]; + } else { + controllerYawHeading = 0.0f; + } } //Positional movement { ALOGV(" Right-Controller-Position: %f, %f, %f", pDominantTracking->HeadPose.Pose.Position.x, - pDominantTracking->HeadPose.Pose.Position.y, - pDominantTracking->HeadPose.Pose.Position.z); + pDominantTracking->HeadPose.Pose.Position.y, + pDominantTracking->HeadPose.Pose.Position.z); vec2_t v; - rotateAboutOrigin(positionDeltaThisFrame[0], positionDeltaThisFrame[2], -(doomYaw - hmdorientation[YAW]), v); + rotateAboutOrigin(positionDeltaThisFrame[0], positionDeltaThisFrame[2], + -(doomYaw - hmdorientation[YAW]), v); positional_movementSideways = v[1]; positional_movementForward = v[0]; @@ -159,11 +161,11 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, { ALOGV(" Left-Controller-Position: %f, %f, %f", pOffTracking->HeadPose.Pose.Position.x, - pOffTracking->HeadPose.Pose.Position.y, - pOffTracking->HeadPose.Pose.Position.z); + pOffTracking->HeadPose.Pose.Position.y, + pOffTracking->HeadPose.Pose.Position.z); - //Teleport - only does anything if vr_teleport cvar is true - if (vr_use_teleport) { + //Teleport - only does anything if vr_teleport cvar is true + if (vr_use_teleport) { if (pOffTrackedRemoteOld->Joystick.y > 0.7f && !ready_teleport) { ready_teleport = true; } else if (pOffTrackedRemoteOld->Joystick.y < 0.7f & ready_teleport) { @@ -173,10 +175,10 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, } } - //Apply a filter and quadratic scaler so small movements are easier to make - //and we don't get movement jitter when the joystick doesn't quite center properly - float dist = length(pOffTrackedRemoteNew->Joystick.x, pOffTrackedRemoteNew->Joystick.y); - float nlf = nonLinearFilter(dist); + //Apply a filter and quadratic scaler so small movements are easier to make + //and we don't get movement jitter when the joystick doesn't quite center properly + float dist = length(pOffTrackedRemoteNew->Joystick.x, pOffTrackedRemoteNew->Joystick.y); + float nlf = nonLinearFilter(dist); float x = nlf * pOffTrackedRemoteNew->Joystick.x; float y = nlf * pOffTrackedRemoteNew->Joystick.y; @@ -185,7 +187,7 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, x = player_moving ? x : 0; y = player_moving ? y : 0; - //Adjust to be off-hand controller oriented + //Adjust to be off-hand controller oriented vec2_t v; rotateAboutOrigin(x, y, controllerYawHeading, v); @@ -197,86 +199,73 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, // Turning logic static int increaseSnap = true; - if (pDominantTrackedRemoteNew->Joystick.x > 0.6f) - { - if (increaseSnap) - { + if (pDominantTrackedRemoteNew->Joystick.x > 0.6f) { + if (increaseSnap) { resetDoomYaw = true; snapTurn -= vr_snapturn_angle; if (vr_snapturn_angle > 10.0f) { increaseSnap = false; } - if (snapTurn < -180.0f) - { + if (snapTurn < -180.0f) { snapTurn += 360.f; } } - } else if (pDominantTrackedRemoteNew->Joystick.x < 0.4f) { - increaseSnap = true; - } + } else if (pDominantTrackedRemoteNew->Joystick.x < 0.4f) { + increaseSnap = true; + } - static int decreaseSnap = true; - if (pDominantTrackedRemoteNew->Joystick.x < -0.6f) - { - if (decreaseSnap) - { + static int decreaseSnap = true; + if (pDominantTrackedRemoteNew->Joystick.x < -0.6f) { + if (decreaseSnap) { resetDoomYaw = true; - snapTurn += vr_snapturn_angle; + snapTurn += vr_snapturn_angle; - //If snap turn configured for less than 10 degrees - if (vr_snapturn_angle > 10.0f) { + //If snap turn configured for less than 10 degrees + if (vr_snapturn_angle > 10.0f) { decreaseSnap = false; } - if (snapTurn > 180.0f) - { + if (snapTurn > 180.0f) { snapTurn -= 360.f; } - } - } else if (pDominantTrackedRemoteNew->Joystick.x > -0.4f) - { - decreaseSnap = true; - } + } + } else if (pDominantTrackedRemoteNew->Joystick.x > -0.4f) { + decreaseSnap = true; + } } + } - //Now handle all the buttons + //Now handle all the buttons - irrespective of menu state - we might be trying to remap stuff + { { - //Dominant Grip works like a shift key - bool dominantGripPushedOld = (pDominantTrackedRemoteOld->Buttons & ovrButton_GripTrigger) != 0; - bool dominantGripPushedNew = (pDominantTrackedRemoteNew->Buttons & ovrButton_GripTrigger) != 0; - - //Weapon Chooser + //Weapon Chooser - This _could_ be remapped static int itemSwitched = 0; if (between(-0.2f, pDominantTrackedRemoteNew->Joystick.x, 0.2f) && (between(0.8f, pDominantTrackedRemoteNew->Joystick.y, 1.0f) || - between(-1.0f, pDominantTrackedRemoteNew->Joystick.y, -0.8f))) - { + between(-1.0f, pDominantTrackedRemoteNew->Joystick.y, -0.8f))) { if (itemSwitched == 0) { - if (between(0.8f, pDominantTrackedRemoteNew->Joystick.y, 1.0f)) - { + if (between(0.8f, pDominantTrackedRemoteNew->Joystick.y, 1.0f)) { Joy_GenerateButtonEvents(0, 1, 1, KEY_MWHEELDOWN); itemSwitched = 1; - } - else - { + } else { Joy_GenerateButtonEvents(0, 1, 1, KEY_MWHEELUP); itemSwitched = 2; } } } else { - if (itemSwitched == 1) - { + if (itemSwitched == 1) { Joy_GenerateButtonEvents(1, 0, 1, KEY_MWHEELDOWN); - } - else if (itemSwitched == 2) - { + } else if (itemSwitched == 2) { Joy_GenerateButtonEvents(1, 0, 1, KEY_MWHEELUP); } itemSwitched = 0; } - //Dominant Hand - Primary keys (no grip pushed) - All keys are remappable, default bindngs are shown below + } + + { + //Dominant Hand - Primary keys (no grip pushed) - All keys are re-mappable, default bindngs are shown below //Fire Joy_GenerateButtonEvents(((pDominantTrackedRemoteOld->Buttons & ovrButton_Trigger) != 0) && !dominantGripPushedOld ? 1 : 0, @@ -323,7 +312,6 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, - //Off Hand - Primary keys (no grip pushed) //No Default Binding @@ -350,10 +338,10 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, //Off Hand - Secondary keys (grip pushed) - //Toggle Run + //No Default Binding Joy_GenerateButtonEvents(((pOffTrackedRemoteOld->Buttons & ovrButton_Trigger) != 0) && dominantGripPushedOld ? 1 : 0, ((pOffTrackedRemoteNew->Buttons & ovrButton_Trigger) != 0) && dominantGripPushedNew ? 1 : 0, - 1, AKEYCODE_CAPS_LOCK); + 1, KEY_LALT); //Move Down Joy_GenerateButtonEvents(((pOffTrackedRemoteOld->Buttons & offButton1) != 0) && dominantGripPushedOld ? 1 : 0, diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/doomstat.h b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/doomstat.h index 2068659..cfb518e 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/doomstat.h +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/doomstat.h @@ -120,7 +120,7 @@ extern EMenuState menuactive; // Menu overlayed? extern int paused; // Game Pause? extern bool pauseext; -extern "C" int isMenuActive(); +extern "C" int getMenuState(); extern bool viewactive; diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/g_statusbar/sbar_mugshot.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/g_statusbar/sbar_mugshot.cpp index 2e17cf5..c9b3695 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/g_statusbar/sbar_mugshot.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/g_statusbar/sbar_mugshot.cpp @@ -46,6 +46,8 @@ #define ST_RAMPAGEDELAY (2*TICRATE) #define ST_MUCHPAIN 20 +EXTERN_CVAR(Float, vr_pickup_haptic_level) + TArray MugShotStates; @@ -351,8 +353,10 @@ int FMugShot::UpdateState(player_t *player, StateFlags stateflags) if (player->bonuscount) { //Short haptic blip on pickup - QzDoom_Vibrate(80, 0, 1.0); // left - QzDoom_Vibrate(80, 1, 1.0); // right + if (vr_pickup_haptic_level > 0.0) { + QzDoom_Vibrate(80, 0, vr_pickup_haptic_level); // left + QzDoom_Vibrate(80, 1, vr_pickup_haptic_level); // right + } SetState("grin", false); return 0; diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/renderer/gl_postprocess.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/renderer/gl_postprocess.cpp index f2d4c4d..d4012d0 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/renderer/gl_postprocess.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/renderer/gl_postprocess.cpp @@ -808,7 +808,7 @@ void FGLRenderer::Flush() glScissor(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); //Only adjust HUD if we are 3D and not showing menu (otherwise we are rendering to a cylinder compositor layer) - if (!is2D && !isMenuActive()) stereo3dMode.getEyePose(eye_ix)->AdjustHud(); + if (!is2D && !getMenuState()) stereo3dMode.getEyePose(eye_ix)->AdjustHud(); m2DDrawer->Draw(); FGLDebug::PopGroup(); diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/stereo3d/gl_oculusquest.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/stereo3d/gl_oculusquest.cpp index 256317f..68f7ce7 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/stereo3d/gl_oculusquest.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/stereo3d/gl_oculusquest.cpp @@ -405,7 +405,7 @@ namespace s3d movebob = 0; } - if (gamestate == GS_LEVEL && !isMenuActive()) { + if (gamestate == GS_LEVEL && !getMenuState()) { cachedScreenBlocks = screenblocks; screenblocks = 12; QzDoom_setUseScreenLayer(false); @@ -429,13 +429,13 @@ namespace s3d QzDoom_getTrackedRemotesOrientation(vr_control_scheme); //Some crazy stuff to ascertain the actual yaw that doom is using at the right times! - if (gamestate != GS_LEVEL || isMenuActive() || (gamestate == GS_LEVEL && resetDoomYaw)) + if (gamestate != GS_LEVEL || getMenuState() || (gamestate == GS_LEVEL && resetDoomYaw)) { doomYaw = (float)r_viewpoint.Angles.Yaw.Degrees; if (gamestate == GS_LEVEL && resetDoomYaw) { resetDoomYaw = false; } - if (gamestate != GS_LEVEL || isMenuActive()) + if (gamestate != GS_LEVEL || getMenuState()) { resetDoomYaw = true; } @@ -561,7 +561,7 @@ namespace s3d G_AddViewPitch(mAngleFromRadians(dPitch)); } - if (gamestate == GS_LEVEL && !isMenuActive()) + if (gamestate == GS_LEVEL && !getMenuState()) { doomYaw += hmdYawDeltaDegrees; } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/stereo3d/gl_stereo_cvars.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/stereo3d/gl_stereo_cvars.cpp index bce29b0..ff14886 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/stereo3d/gl_stereo_cvars.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/gl/stereo3d/gl_stereo_cvars.cpp @@ -67,6 +67,9 @@ CVAR(Float, vr_snapTurn, 45.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Int, vr_move_speed, 24, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Float, vr_run_multiplier, 1.6, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Float, vr_pickup_haptic_level, 0.2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Float, vr_quake_haptic_level, 0.8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + //HUD control CVAR(Float, vr_hud_scale, 0.3f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Float, vr_hud_stereo, 1.8f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/menu/menu.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/menu/menu.cpp index 7bf2169..11bd149 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/menu/menu.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/menu/menu.cpp @@ -370,12 +370,11 @@ void M_StartControlPanel (bool makeSound) BackbuttonAlpha = 0; } -int isMenuActive() +int getMenuState() { - return menuactive == MENU_On ? 1 : 0; + return (int)menuactive; } - //============================================================================= // // diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/r_utility.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/r_utility.cpp index b9348bc..fa00499 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/r_utility.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/r_utility.cpp @@ -75,6 +75,7 @@ extern bool DrawFSHUD; // [RH] Defined in d_main.cpp EXTERN_CVAR (Bool, cl_capfps) +EXTERN_CVAR (Float, vr_quake_haptic_level) // TYPES ------------------------------------------------------------------- @@ -749,6 +750,8 @@ static double QuakePower(double factor, double intensity, double offset) return factor * (offset + randumb); } +extern "C" void QzDoom_Vibrate(float duration, int channel, float intensity ); + //========================================================================== // // R_SetupFrame @@ -920,6 +923,14 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor { viewpoint.Pos.Z += QuakePower(quakefactor, jiggers.Intensity.Z, jiggers.Offset.Z); } + + //Haptic Quake + if (vr_quake_haptic_level > 0.0) { + double left = QuakePower(vr_quake_haptic_level, jiggers.Intensity.X, jiggers.Offset.X); + double right = QuakePower(vr_quake_haptic_level, jiggers.Intensity.Y, jiggers.Offset.Y); + QzDoom_Vibrate(10, 0, (float)left); // left + QzDoom_Vibrate(10, 1, (float)right); // right + } } } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/v_blend.cpp b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/v_blend.cpp index a12bd75..a55f67f 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/v_blend.cpp +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/v_blend.cpp @@ -57,6 +57,8 @@ CVAR( Float, blood_fade_scalar, 0.0f, CVAR_ARCHIVE ) // Default ro 0.0 for VR CVAR( Float, pickup_fade_scalar, 0.0f, CVAR_ARCHIVE ) // Default ro 0.0 for VR +EXTERN_CVAR(Float, vr_pickup_haptic_level) + // [RH] Amount of red flash for up to 114 damage points. Calculated by hand // using a logarithmic scale and my trusty HP48G. static uint8_t DamageToAlpha[114] = @@ -129,8 +131,10 @@ void V_AddPlayerBlend (player_t *CPlayer, float blend[4], float maxinvalpha, int cnt = CPlayer->bonuscount << 3; //Super short haptic blip on pickup - QzDoom_Vibrate(50, 0, 0.7); // left - QzDoom_Vibrate(50, 1, 0.7); // right + if (vr_pickup_haptic_level > 0.0) { + QzDoom_Vibrate(50, 0, vr_pickup_haptic_level); // left + QzDoom_Vibrate(50, 1, vr_pickup_haptic_level); // right + } // [SP] Allow player to tone down intensity of pickup flash. cnt = (int)( cnt * pickup_fade_scalar ); diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/version.h b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/version.h index 876df5f..c8a6274 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/version.h +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/src/version.h @@ -41,7 +41,7 @@ const char *GetVersionString(); /** Lots of different version numbers **/ -#define VERSIONSTR "QuestZDoom Beta-0.2 - LZDoom 3.83a" +#define VERSIONSTR "QuestZDoom Beta-0.3 - LZDoom 3.83a" // The version as seen in the Windows resource #define RC_FILEVERSION 3,83,1 diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/menudef.txt b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/menudef.txt index d7132fd..6495242 100644 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/menudef.txt +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile/wadsrc/static/menudef.txt @@ -2288,6 +2288,10 @@ OptionMenu VROptionsMenu protected Option "Use Teleport", "vr_teleport", "OnOff" Slider "Snap-turn Angle", "vr_snapTurn", 0.0, 90.0, 1.0, 2 + StaticText " " + Slider "Item Pickup Haptic Level", "vr_pickup_haptic_level", 0.0, 1.0, 0.1, 2 + Slider "Quake Haptic Level", "vr_quake_haptic_level", 0.0, 1.0, 0.1, 2 + StaticText " " Slider "Walking Speed", "vr_move_speed", 5, 50, 1, 2 Slider "Run Multiplier", "vr_run_multiplier", 0.0, 4.0, 0.1, 2 diff --git a/assets/res/lzdoom.pk3 b/assets/res/lzdoom.pk3 index 756f7982f060fe11cc01734440df3d3301725fb3..cf69d195c7330bf8f8dac69aff3d5f59e26440fd 100644 GIT binary patch delta 12321 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

grzp!{jE6AX*V=N-L;impM`w>kuhOLdafH}v0 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