1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-15 22:19:54 +00:00

Touch input of activation now makes character point forward.

This commit is contained in:
Mads Buvik Sandvei 2020-02-16 14:53:35 +01:00
parent 0840d2dd92
commit 287886d545
8 changed files with 198 additions and 78 deletions

View file

@ -48,6 +48,10 @@
#include "actorutil.hpp"
#include "spellcasting.hpp"
#ifdef USE_OPENXR
#include "../mwvr/openxranimation.hpp"
#endif
namespace
{
@ -1666,6 +1670,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())
mAttackStrength = complete;
}
else
{
@ -1876,6 +1881,18 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
mAnimation->setAccurateAiming(mUpperBodyState > UpperCharState_WeapEquiped);
#ifdef USE_OPENXR
if (mPtr == getPlayer())
{
auto xrAnimation = dynamic_cast<MWVR::OpenXRAnimation*>(mAnimation);
if (xrAnimation)
{
bool pointing = MWBase::Environment::get().getWorld()->getPlayer().getPointing();
xrAnimation->setPointForward(pointing);
}
}
#endif
return forcestateupdate;
}

View file

@ -55,37 +55,36 @@
namespace MWVR
{
// This will work for a prototype. But finger/arm control might be better implemented using the
// existing animation system, implementing this as an animation source.
// But I'm not sure it would be since these are not classical animations.
// It would make it easier to control priority, and later allow for users to add their own stuff to animations based on VR/touch input.
// But openmw doesn't really have any concepts for user animation overrides.
/// Implements dummy control of the forearm, to control mesh/bone deformation of the hand.
class ForearmController : public osg::NodeCallback
{
public:
ForearmController(osg::Node* relativeTo, SceneUtil::PositionAttitudeTransform* tracker);
void setEnabled(bool enabled);
void setEnabled(bool enabled) { mEnabled = enabled; };
void operator()(osg::Node* node, osg::NodeVisitor* nv);
private:
bool mEnabled;
osg::Quat mRotate;
bool mEnabled = true;
osg::Quat mRotate{};
osg::Node* mRelativeTo;
osg::Matrix mOffset;
bool mOffsetInitialized;
osg::Matrix mOffset{ osg::Matrix::identity() };
bool mOffsetInitialized = false;
SceneUtil::PositionAttitudeTransform* mTracker;
};
ForearmController::ForearmController(osg::Node* relativeTo, SceneUtil::PositionAttitudeTransform* tracker)
: mEnabled(true)
, mRelativeTo(relativeTo)
, mOffset(osg::Matrix::identity())
, mOffsetInitialized(false)
: mRelativeTo(relativeTo)
, mTracker(tracker)
{
}
void ForearmController::setEnabled(bool enabled)
{
mEnabled = enabled;
}
void ForearmController::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (!mEnabled)
@ -117,11 +116,47 @@ void ForearmController::operator()(osg::Node* node, osg::NodeVisitor* nv)
transform->setMatrix(mOffset * worldReference * osg::Matrix::inverse(worldToLimb) * transform->getMatrix());
// TODO: Continued traversal is necessary to allow update of new hand poses such as gribbing a weapon.
// TODO: Continued traversal is necessary to allow update of new hand poses such as gripping a weapon.
// But I want to disable idle animations.
traverse(node, nv);
}
/// Implements control of a finger by overriding rotation
class FingerController : public osg::NodeCallback
{
public:
FingerController(osg::Quat rotate) : mRotate(rotate) {};
void setEnabled(bool enabled) { mEnabled = enabled; };
void operator()(osg::Node* node, osg::NodeVisitor* nv);
private:
bool mEnabled = true;
osg::Quat mRotate{};
};
void FingerController::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (!mEnabled)
{
traverse(node, nv);
return;
}
auto matrixTransform = node->asTransform()->asMatrixTransform();
auto matrix = matrixTransform->getMatrix();
matrix.setRotate(mRotate);
matrixTransform->setMatrix(matrix);
auto tip = matrixTransform->getChild(0);
auto tipMatrixTransform = tip->asTransform()->asMatrixTransform();
matrix = tipMatrixTransform->getMatrix();
matrix.setRotate(mRotate);
tipMatrixTransform->setMatrix(matrix);
//traverse(node, nv);
}
OpenXRAnimation::OpenXRAnimation(
const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
bool disableSounds, std::shared_ptr<OpenXRSession> xrSession)
@ -129,8 +164,14 @@ OpenXRAnimation::OpenXRAnimation(
// when OpenMW sets the view mode of the camera object.
: MWRender::NpcAnimation(ptr, parentNode, resourceSystem, disableSounds, VM_Normal, 55.f)
, mSession(xrSession)
, mIndexFingerControllers{nullptr, nullptr}
{
mIndexFingerControllers[0] = osg::ref_ptr<FingerController> (new FingerController(osg::Quat(0, 0, 0, 1)));
mIndexFingerControllers[1] = osg::ref_ptr<FingerController> (new FingerController(osg::Quat(0, 0, 0, 1)));
}
OpenXRAnimation::~OpenXRAnimation() {};
void OpenXRAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
{
if (viewMode != VM_VRHeadless)
@ -154,6 +195,16 @@ void OpenXRAnimation::updateParts()
removeIndividualPart(ESM::PartReferenceType::PRT_RUpperarm);
removeIndividualPart(ESM::PartReferenceType::PRT_RWrist);
}
void OpenXRAnimation::setPointForward(bool enabled)
{
auto found00 = mNodeMap.find("bip01 r finger1");
if (found00 != mNodeMap.end())
{
found00->second->removeUpdateCallback(mIndexFingerControllers[0]);
if (enabled)
found00->second->addUpdateCallback(mIndexFingerControllers[0]);
}
}
osg::Vec3f OpenXRAnimation::runAnimation(float timepassed)
{
return NpcAnimation::runAnimation(timepassed);
@ -187,7 +238,7 @@ void OpenXRAnimation::addControllers()
SceneUtil::PositionAttitudeTransform* tracker = dynamic_cast<SceneUtil::PositionAttitudeTransform*>(findTrackerVisitor.mFoundNode);
std::map<std::string, osg::ref_ptr<osg::MatrixTransform> >::const_iterator found = mNodeMap.find(i == 0 ? "bip01 l forearm" : "bip01 r forearm");
auto found = mNodeMap.find(i == 0 ? "bip01 l forearm" : "bip01 r forearm");
if (found != mNodeMap.end())
{
osg::Node* node = found->second;

View file

@ -9,6 +9,7 @@ namespace MWVR
{
class HandController;
class FingerController;
class ForearmController;
/// Subclassing NpcAnimation to override behaviours not compatible with VR
@ -29,7 +30,7 @@ public:
*/
OpenXRAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
bool disableSounds, std::shared_ptr<OpenXRSession> xrSession );
virtual ~OpenXRAnimation() {};
virtual ~OpenXRAnimation();
/// Overridden to always be false
virtual void enableHeadAnimation(bool enable);
@ -50,11 +51,15 @@ public:
/// Overriden to include VR modifications
virtual void updateParts();
/// Overrides finger animations to point forward
/// (Used to visualize direction of activation action)
void setPointForward(bool enabled);
private:
std::shared_ptr<OpenXRSession> mSession;
ForearmController* mForearmControllers[2]{};
HandController* mHandControllers[2]{};
osg::ref_ptr<FingerController> mIndexFingerControllers[2];
};
}

View file

@ -28,6 +28,7 @@
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/actorutil.hpp"
#include <openxr/openxr.h>
@ -42,7 +43,7 @@ namespace MWVR
{
struct OpenXRActionEvent
{
MWInput::InputManager::Actions action;
int action;
bool onPress;
float value = 0.f;
};
@ -116,10 +117,46 @@ namespace MWVR
float mValue = 0.f;
};
//! Wrapper around float type input actions, converting them to bools above a specified value
struct OpenXRAction_FloatToBool
{
OpenXRAction_FloatToBool(OpenXRAction action, float threshold);
operator XrAction() { return mAction; }
void update();
//! True if action changed to being pressed in the last update
bool actionOnPress() { return actionIsPressed() && actionChanged(); }
//! True if action changed to being released in the last update
bool actionOnRelease() { return actionIsReleased() && actionChanged(); };
//! True if the action is currently being pressed
bool actionIsPressed() { return mPressed; }
//! True if the action is currently not being pressed
bool actionIsReleased() { return !mPressed; }
//! True if the action changed state in the last update
bool actionChanged() { return mChanged; }
OpenXRAction_Float mAction;
float mThreshold;
bool mPressed = false;
bool mChanged = false;
};
struct OpenXRInput
{
using Actions = MWInput::InputManager::Actions;
// The OpenMW input manager iterates from 0 to A_Last in its actions enum.
// I don't know that it would cause any ill effects, but i nonetheless do not
// want to contaminate the input manager with my OpenXR specific actions.
// Therefore i add them here to a separate enum whose values start past A_Last.
enum XrActions
{
A_XrFirst = MWInput::InputManager::A_Last,
A_ActivateTouch,
A_XrLast
};
enum SubAction : signed
{
NONE = -1, //!< Used to ignore subaction or when action has no subaction. Not a valid input to createAction().
@ -160,7 +197,6 @@ namespace MWVR
ControllerActionPaths mSelectPath;
ControllerActionPaths mSqueezeValuePath;
ControllerActionPaths mSqueezeClickPath;
ControllerActionPaths mPosePath;
ControllerActionPaths mHapticPath;
ControllerActionPaths mMenuClickPath;
@ -172,7 +208,6 @@ namespace MWVR
ControllerActionPaths mYPath;
ControllerActionPaths mAPath;
ControllerActionPaths mBPath;
ControllerActionPaths mTriggerClickPath;
ControllerActionPaths mTriggerValuePath;
OpenXRAction_Bool mGameMenu;
@ -191,46 +226,14 @@ namespace MWVR
OpenXRAction_Float mLookLeftRight;
OpenXRAction_Float mMoveForwardBackward;
OpenXRAction_Float mMoveLeftRight;
//OpenXRAction mUnused;
//OpenXRAction mScreenshot;
//OpenXRAction mConsole;
//OpenXRAction mMoveLeft;
//OpenXRAction mMoveRight;
//OpenXRAction mMoveForward;
//OpenXRAction mMoveBackward;
//OpenXRAction mAutoMove;
//OpenXRAction mRest;
//OpenXRAction mJournal;
//OpenXRAction mRun;
//OpenXRAction mToggleSneak;
//OpenXRAction mAlwaysRun;
//OpenXRAction mQuickSave;
//OpenXRAction mQuickLoad;
//OpenXRAction mToggleWeapon;
//OpenXRAction mToggleSpell;
//OpenXRAction mTogglePOV;
//OpenXRAction mQuickKey1;
//OpenXRAction mQuickKey2;
//OpenXRAction mQuickKey3;
//OpenXRAction mQuickKey4;
//OpenXRAction mQuickKey5;
//OpenXRAction mQuickKey6;
//OpenXRAction mQuickKey7;
//OpenXRAction mQuickKey8;
//OpenXRAction mQuickKey9;
//OpenXRAction mQuickKey10;
//OpenXRAction mQuickKeysMenu;
//OpenXRAction mToggleHUD;
//OpenXRAction mToggleDebug;
//OpenXRAction mLookUpDown;
//OpenXRAction mZoomIn;
//OpenXRAction mZoomOut;
//! Needed to access all the actions that don't fit on the controllers
// Needed to access all the actions that don't fit on the controllers
OpenXRAction_Bool mActionsMenu;
//! Economize buttons by accessing spell actions and weapon actions on the same keys, but with/without this modifier
// Economize buttons by accessing spell actions and weapon actions on the same keys, but with/without this modifier
OpenXRAction_Bool mSpellModifier;
OpenXRAction_FloatToBool mActivateTouch;
// Hand tracking
OpenXRAction mHandPoseAction;
OpenXRAction mHapticsAction;
@ -353,6 +356,23 @@ namespace MWVR
return true;
}
OpenXRAction_FloatToBool::OpenXRAction_FloatToBool(
OpenXRAction action, float threshold)
: mAction(std::move(action))
, mThreshold(threshold)
{
}
void OpenXRAction_FloatToBool::update()
{
mAction.update();
bool old = mPressed;
float value = mAction.value();
mPressed = value > mThreshold;
mChanged = mPressed != old;
}
OpenXRAction_Bool::OpenXRAction_Bool(
OpenXRAction action)
: mAction(std::move(action))
@ -411,7 +431,6 @@ namespace MWVR
, mActionSet(createActionSet())
, mSelectPath(generateControllerActionPaths("/input/select/click"))
, mSqueezeValuePath(generateControllerActionPaths("/input/squeeze/value"))
, mSqueezeClickPath(generateControllerActionPaths("/input/squeeze/click"))
, mPosePath(generateControllerActionPaths("/input/aim/pose"))
, mHapticPath(generateControllerActionPaths("/output/haptic"))
, mMenuClickPath(generateControllerActionPaths("/input/menu/click"))
@ -423,7 +442,6 @@ namespace MWVR
, mYPath(generateControllerActionPaths("/input/y/click"))
, mAPath(generateControllerActionPaths("/input/a/click"))
, mBPath(generateControllerActionPaths("/input/b/click"))
, mTriggerClickPath(generateControllerActionPaths("/input/trigger/click"))
, mTriggerValuePath(generateControllerActionPaths("/input/trigger/value"))
, mGameMenu(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "game_menu", "GameMenu", { })))
, mInventory(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "inventory", "Inventory", { })))
@ -443,6 +461,7 @@ namespace MWVR
, mMoveLeftRight(std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "move_left_right", "MoveLeftRight", { })))
, mActionsMenu(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "actions_menu", "Actions Menu", { })))
, mSpellModifier(std::move(createAction(XR_ACTION_TYPE_BOOLEAN_INPUT, "spell_modifier", "Spell Modifier", { })))
, mActivateTouch(std::move(createAction(XR_ACTION_TYPE_FLOAT_INPUT, "activate_touched", "Activate Touch", { RIGHT_HAND })), 0.01f)
, mHandPoseAction(std::move(createAction(XR_ACTION_TYPE_POSE_INPUT, "hand_pose", "Hand Pose", { LEFT_HAND, RIGHT_HAND })))
, mHapticsAction(std::move(createAction(XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibrate_hand", "Vibrate Hand", { LEFT_HAND, RIGHT_HAND })))
{
@ -472,6 +491,7 @@ namespace MWVR
{mQuickMenu, mYPath[LEFT_HAND]},
{mSpellModifier, mSqueezeValuePath[LEFT_HAND]},
{mGameMenu, mMenuClickPath[LEFT_HAND]},
{mActivateTouch, mSqueezeValuePath[RIGHT_HAND]},
} };
XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
suggestedBindings.interactionProfile = oculusTouchInteractionProfilePath;
@ -483,10 +503,10 @@ namespace MWVR
mSpellModifier; // L-Squeeze
mActivate; // R-Squeeze
mUse; // R-Trigger
mJump; // L-Trigger. L-trigger has value, can be used to make measured jumps
mWeapon; // A
mSpell; // A + SpellModifier
mRun; // Based on movement thumbstick value: broken line ( 0 (stand), 0.5 (walk), 1.0 (run) ). Remember to scale fatigue use.
mJump; // L-Trigger. L-trigger has value, could use this to make measured jumps ?
mToggleWeapon; // A
mToggleSpell; // A + SpellModifier
mRun; // Based on movement thumbstick value ?
mCycleSpellLeft; // L-ThumbstickClick + SpellModifier
mCycleSpellRight; // R-ThumbstickClick + SpellModifier
mCycleWeaponLeft; // L-ThumbstickClick
@ -583,8 +603,9 @@ namespace MWVR
mMoveLeftRight.update();
mActionsMenu.update();
mSpellModifier.update();
mActivateTouch.update();
// This set of actions only care about on-press
// Simple fire-and-forget on-press actions
if (mActionsMenu.actionOnPress())
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_QuickKeysMenu, true });
@ -601,27 +622,29 @@ namespace MWVR
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Activate, true });
}
if (mUse.actionChanged())
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Use, mUse.actionOnPress() });
}
if (mJump.actionOnPress())
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Jump, true });
}
if (mSneak.actionOnPress())
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Sneak, true });
}
if (mSneak.actionOnRelease())
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Sneak, false });
}
if (mQuickMenu.actionOnPress())
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_QuickMenu, true });
}
// Actions that hold with the button
if (mUse.actionChanged())
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Use, mUse.actionOnPress() });
}
if (mSneak.actionChanged())
{
mActionEvents.emplace_back(OpenXRActionEvent{ MWInput::InputManager::A_Sneak, mSneak.actionOnPress() });
}
if (mActivateTouch.actionChanged())
{
mActionEvents.emplace_back(OpenXRActionEvent{ A_ActivateTouch, mActivateTouch.actionOnPress() });
}
// Weapon/Spell actions
if (mWeapon.actionOnPress() && !mSpellModifier.actionIsPressed())
{
@ -717,6 +740,11 @@ namespace MWVR
return mXRInput->getHandPoses(time, space);
}
void OpenXRInputManager::showActivationIndication(bool show)
{
mPlayer->setPointing(show);
}
OpenXRInputManager::OpenXRInputManager(
SDL_Window* window,
osg::ref_ptr<OpenXRViewer> viewer,
@ -922,6 +950,9 @@ namespace MWVR
case A_Use:
mInputBinder->getChannel(A_Use)->setValue(event.onPress);
break;
case OpenXRInput::A_ActivateTouch:
showActivationIndication(event.onPress);
break;
default:
Log(Debug::Warning) << "Unhandled XR action " << event.action;
}

View file

@ -39,6 +39,8 @@ namespace MWVR
PoseSet getHandPoses(int64_t time, TrackedSpace space);
void showActivationIndication(bool show);
osg::ref_ptr<OpenXRViewer> mXRViewer;
std::unique_ptr<OpenXRInput> mXRInput;
};

View file

@ -185,8 +185,8 @@ namespace MWVR
mirror_width = mMirrorTextureSwapchain->width() / 2;
mViews["LeftEye"]->swapchain().renderBuffer()->blit(gc, 0, 0, mirror_width, mMirrorTextureSwapchain->height());
mViews["RightEye"]->swapchain().renderBuffer()->blit(gc, mirror_width, 0, 2 * mirror_width, mMirrorTextureSwapchain->height());
mViews["RightEye"]->swapchain().renderBuffer()->blit(gc, 0, 0, mirror_width, mMirrorTextureSwapchain->height());
mViews["LeftEye"]->swapchain().renderBuffer()->blit(gc, mirror_width, 0, 2 * mirror_width, mMirrorTextureSwapchain->height());
if(includeMenu)
mViews["MenuView"]->swapchain().renderBuffer()->blit(gc, 2 * mirror_width, 0, 3 * mirror_width, mMirrorTextureSwapchain->height());

View file

@ -274,6 +274,16 @@ namespace MWWorld
return mJumping;
}
void Player::setPointing(bool pointing)
{
mPointing = pointing;
}
bool Player::getPointing() const
{
return mPointing;
}
bool Player::isInCombat() {
return MWBase::Environment::get().getMechanicsManager()->getActorsFighting(getPlayer()).size() != 0;
}

View file

@ -58,6 +58,7 @@ namespace MWWorld
bool mAttackingOrSpell;
bool mJumping;
bool mPointing;
public:
@ -117,6 +118,9 @@ namespace MWWorld
void setJumping(bool jumping);
bool getJumping() const;
void setPointing(bool pointing);
bool getPointing(void) const;
///Checks all nearby actors to see if anyone has an aipackage against you
bool isInCombat();