GUI fixes. Animation fixes, i now understand movement accumulation better and was able to clean up some of the hacks with a better result. Lockpicks and probes now work. Haptics. Ready to be playtested.

pull/615/head
Mads Buvik Sandvei 5 years ago
parent cad6468518
commit 6474d703ae

@ -65,6 +65,8 @@ namespace MWBase
virtual void enableDetectingBindingMode (int action, bool keyboard) = 0;
virtual void resetToDefaultKeyBindings() = 0;
virtual void resetToDefaultControllerBindings() = 0;
virtual void applyHapticsLeftHand(float intensity) = 0;
virtual void applyHapticsRightHand(float intensity) = 0;
/// Returns if the last used input device was a joystick or a keyboard
/// @return true if joystick, false otherwise

@ -14,6 +14,7 @@
#include "../mwmechanics/difficultyscaling.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
@ -329,11 +330,11 @@ namespace MWClass
MWMechanics::diseaseContact(victim, ptr);
victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true, attackStrength);
return true;
}
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const
{
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
@ -348,6 +349,8 @@ namespace MWClass
if (isMobile(ptr) && !attacker.isEmpty())
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
bool attackerIsPlayer = attacker == MWMechanics::getPlayer();
// Attacker and target store each other as hitattemptactor if they have no one stored yet
if (!attacker.isEmpty() && attacker.getClass().isActor())
{
@ -355,20 +358,20 @@ namespace MWClass
// First handle the attacked actor
if ((stats.getHitAttemptActorId() == -1)
&& (statsAttacker.getAiSequence().isInCombat(ptr)
|| attacker == MWMechanics::getPlayer()))
|| attackerIsPlayer))
stats.setHitAttemptActorId(statsAttacker.getActorId());
// Next handle the attacking actor
if ((statsAttacker.getHitAttemptActorId() == -1)
&& (statsAttacker.getAiSequence().isInCombat(ptr)
|| attacker == MWMechanics::getPlayer()))
|| attackerIsPlayer))
statsAttacker.setHitAttemptActorId(stats.getActorId());
}
if (!object.isEmpty())
stats.setLastHitAttemptObject(object.getCellRef().getRefId());
if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer())
if (setOnPcHitMe && !attacker.isEmpty() && attackerIsPlayer)
{
const std::string &script = ptr.get<ESM::Creature>()->mBase->mScript;
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
@ -379,7 +382,7 @@ namespace MWClass
if (!successful)
{
// Missed
if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
if (!attacker.isEmpty() && attackerIsPlayer)
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f);
return;
}
@ -427,6 +430,16 @@ namespace MWClass
stats.setFatigue(fatigue);
}
}
if(successful)
{
auto* inputManager = MWBase::Environment::get().getInputManager();
if (attackerIsPlayer && hitStrength > 0.f)
{
float hapticIntensity = std::max(0.25f, std::min(1.f, hitStrength));
inputManager->applyHapticsRightHand(hapticIntensity);
}
}
}
std::shared_ptr<MWWorld::Action> Creature::activate (const MWWorld::Ptr& ptr,

@ -57,7 +57,7 @@ namespace MWClass
virtual bool hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool simulated) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const;
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;

@ -12,6 +12,7 @@
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
@ -670,15 +671,16 @@ namespace MWClass
MWMechanics::diseaseContact(victim, ptr);
othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true, attackStrength);
return true;
}
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
bool wasDead = stats.isDead();
float rawDamage = damage;
// Note OnPcHitMe is not set for friendly hits.
bool setOnPcHitMe = true;
@ -689,6 +691,8 @@ namespace MWClass
stats.setAttacked(true);
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
}
bool attackerIsPlayer = attacker == MWMechanics::getPlayer();
bool victimIsPlayer = ptr == MWMechanics::getPlayer();
// Attacker and target store each other as hitattemptactor if they have no one stored yet
if (!attacker.isEmpty() && attacker.getClass().isActor())
@ -697,20 +701,20 @@ namespace MWClass
// First handle the attacked actor
if ((stats.getHitAttemptActorId() == -1)
&& (statsAttacker.getAiSequence().isInCombat(ptr)
|| attacker == MWMechanics::getPlayer()))
|| attackerIsPlayer))
stats.setHitAttemptActorId(statsAttacker.getActorId());
// Next handle the attacking actor
if ((statsAttacker.getHitAttemptActorId() == -1)
&& (statsAttacker.getAiSequence().isInCombat(ptr)
|| attacker == MWMechanics::getPlayer()))
|| attackerIsPlayer))
statsAttacker.setHitAttemptActorId(stats.getActorId());
}
if (!object.isEmpty())
stats.setLastHitAttemptObject(object.getCellRef().getRefId());
if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer())
if (setOnPcHitMe && !attacker.isEmpty() && attackerIsPlayer)
{
const std::string &script = getScript(ptr);
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
@ -721,7 +725,7 @@ namespace MWClass
if (!successful)
{
// Missed
if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
if (!attacker.isEmpty() && attackerIsPlayer)
sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f);
return;
}
@ -736,7 +740,7 @@ namespace MWClass
if (damage < 0.001f)
damage = 0;
bool godmode = ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
bool godmode = victimIsPlayer && MWBase::Environment::get().getWorld()->getGodModeState();
if (godmode)
damage = 0;
@ -860,6 +864,23 @@ namespace MWClass
MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, attacker);
}
// Apply haptics
if (successful)
{
auto* inputManager = MWBase::Environment::get().getInputManager();
if (victimIsPlayer)
{
float maxHealth = getCreatureStats(ptr).getHealth().getModified();
float hapticIntensity = std::max(0.25f, std::min(1.f, rawDamage / ( maxHealth / 4.f)));
inputManager->applyHapticsLeftHand(hapticIntensity);
}
else if (attackerIsPlayer && hitStrength > 0.f)
{
float hapticIntensity = std::max(0.25f, std::min(1.f, hitStrength));
inputManager->applyHapticsRightHand(hapticIntensity);
}
}
}
std::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,

@ -72,7 +72,7 @@ namespace MWClass
virtual bool hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool simulated) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const;
virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const;
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().

@ -30,7 +30,11 @@ namespace MWGui
std::string ToolTips::sSchoolNames[] = {"#{sSchoolAlteration}", "#{sSchoolConjuration}", "#{sSchoolDestruction}", "#{sSchoolIllusion}", "#{sSchoolMysticism}", "#{sSchoolRestoration}"};
ToolTips::ToolTips() :
#ifdef USE_OPENXR
Layout("openmw_tooltips_vr.layout")
#else
Layout("openmw_tooltips.layout")
#endif
, mFocusToolTipX(0.0)
, mFocusToolTipY(0.0)
, mHorizontalScrollIndex(0)

@ -1945,7 +1945,7 @@ namespace MWGui
// (Menu gets recreated next tick)
if (xrMenuManager)
{
xrMenuManager->showGUIs(false);
xrMenuManager->updateTracking();
xrMenuManager = nullptr;
}
#endif

@ -239,6 +239,9 @@ namespace MWInput
bool checkAllowedToUseItems() const;
void applyHapticsLeftHand(float intensity) override {};
void applyHapticsRightHand(float intensity) override {};
protected:
void toggleMainMenu();
void toggleSpell();

@ -49,6 +49,11 @@
#include "actorutil.hpp"
#include "spellcasting.hpp"
#ifdef USE_OPENXR
#include "../mwvr/vrenvironment.hpp"
#include "../mwvr/vranimation.hpp"
#endif
namespace
{
@ -1582,9 +1587,14 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{
MWWorld::ContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
MWWorld::Ptr item = *weapon;
std::string resultMessage, resultSound;
// TODO: this will only work for the player, and needs to be fixed if NPCs should ever use lockpicks/probes.
#ifdef USE_OPENXR
auto* anim = MWVR::Environment::get().getPlayerAnimation();
auto target = anim->getTarget("weapon bone");
#else
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject();
std::string resultMessage, resultSound;
#endif
if(!target.isEmpty())
{

@ -211,7 +211,7 @@ namespace MWMechanics
if (Misc::Rng::roll0to99() >= getHitChance(attacker, victim, skillValue))
{
victim.getClass().onHit(victim, damage, false, projectile, attacker, osg::Vec3f(), false);
victim.getClass().onHit(victim, damage, false, projectile, attacker, osg::Vec3f(), false, attackStrength);
MWMechanics::reduceWeaponCondition(damage, false, weapon, attacker);
return;
}
@ -259,7 +259,7 @@ namespace MWMechanics
victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
}
victim.getClass().onHit(victim, damage, true, projectile, attacker, hitPosition, true);
victim.getClass().onHit(victim, damage, true, projectile, attacker, hitPosition, true, attackStrength);
}
}

@ -1062,29 +1062,18 @@ namespace MWRender
return mNodeMap;
}
// In VR, only jump, walk, and run groups should accumulate movement.
static bool vrAccum(const std::string& groupname)
{
#ifdef USE_OPENXR
if (groupname.compare(0, 4, "jump"))
if (groupname.compare(0, 4, "walk"))
if (groupname.compare(0, 3, "run"))
if (groupname.compare(0, 4, "swim"))
return false;
#else
(void)groupname;
#endif
return true;
}
static bool vrOverride(const std::string& groupname, const std::string& bone)
{
#ifdef USE_OPENXR
// TODO: It's difficult to design a good override system when
// I don't have a good understanding of the animation code. So for
// now i just block adding updates for nodes that should not be animated in VR.
// TODO: Some overrides cause NaN during cull.
// I believe this happens if an override causes a bone to never receive
// a valid matrix, but i'm not totally sure.
// Add any bone+animation pair that is messing with Vr comfort here.
// Add any bone+groupname pair that is messing with Vr comfort here.
using Overrides = std::set<std::string>;
using GroupOverrides = std::map<std::string, Overrides>;
static GroupOverrides sVrOverrides =
@ -1165,7 +1154,11 @@ namespace MWRender
mActiveControllers.insert(std::make_pair(node, it->second));
if (blendMask == 0 && node == mAccumRoot
&& (!isPlayer || vrAccum(active->first)))
#ifdef USE_OPENXR
// TODO: Little hack to keep certain animations from wobbling the camera in VR
&& (!isPlayer)
#endif
)
{
mAccumCtrl = it->second;

@ -442,7 +442,7 @@ public:
void disable(const std::string &groupname);
/** Retrieves the velocity (in units per second) that the animation will move. */
float getVelocity(const std::string &groupname) const;
virtual float getVelocity(const std::string &groupname) const;
virtual osg::Vec3f runAnimation(float duration);

@ -113,10 +113,8 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
return;
#ifdef USE_OPENXR
// In VR player rotation and weapon aim are unrelated.
auto* anim = MWVR::Environment::get().getPlayerAnimation();
osg::Matrix worldMatrix = osg::computeLocalToWorld(anim->mWeaponDirectionTransform->getParentalNodePaths()[0]);
osg::Quat orient = worldMatrix.getRotate();
// In VR weapon aim is taken from the real orientation of the weapon.
osg::Quat orient = MWVR::Environment::get().getPlayerAnimation()->getWeaponTransformMatrix().getRotate();
#else
// The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise.
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))

@ -127,8 +127,6 @@ public:
mOnActivate = changed && mActive;
mOnDeactivate = changed && !mActive;
//if(openMWActionCode() == MWInput::InputManager::A_Journal)
Log(Debug::Verbose) << "Action[" << mXRAction.mName << "]: old=" << old << " shouldQueue=" << shouldQueue() << ", active=" << mActive << ", value=" << mValue << " onActivate=" << mOnActivate << ", mOnDeactivate=" << mOnDeactivate;
if (shouldQueue())
{
queue.push_back(this);
@ -287,6 +285,7 @@ struct OpenXRInput
{
A_XrFirst = MWInput::InputManager::A_Last,
A_ActivateTouch,
A_RepositionMenu,
A_XrLast
};
@ -326,6 +325,11 @@ struct OpenXRInput
const Action* nextAction();
PoseSet getHandPoses(int64_t time, TrackedSpace space);
void applyHaptics(SubAction subAction, float intensity)
{
mHapticsAction.applyHaptics(subactionPath(subAction), intensity);
}
SubActionPaths mSubactionPath;
XrActionSet mActionSet = XR_NULL_HANDLE;
@ -345,6 +349,7 @@ struct OpenXRInput
ControllerActionPaths mTriggerValuePath;
ActionPtr mGameMenu;
ActionPtr mRepositionMenu;
ActionPtr mInventory;
ActionPtr mActivate;
ActionPtr mUse;
@ -355,18 +360,19 @@ struct OpenXRInput
ActionPtr mCycleSpellRight;
ActionPtr mCycleWeaponLeft;
ActionPtr mCycleWeaponRight;
ActionPtr mToggleSneak;
ActionPtr mSneak;
ActionPtr mQuickMenu;
ActionPtr mLookLeftRight;
ActionPtr mMoveForwardBackward;
ActionPtr mMoveLeftRight;
ActionPtr mJournal;
//OpenXRAction_Delayed mQuickSave;
// Needed to access all the actions that don't fit on the controllers
ActionPtr mQuickKeysMenu;
ActionPtr mQuickSave;
ActionPtr mRest;
ActionPtr mActivateTouch;
ActionPtr mAlwaysRun;
ActionPtr mAutoMove;
ActionPtr mToggleHUD;
ActionPtr mToggleDebug;
// Hand tracking
OpenXRAction mHandPoseAction;
@ -480,6 +486,8 @@ bool OpenXRAction::getPose(XrPath subactionPath)
bool OpenXRAction::applyHaptics(XrPath subactionPath, float amplitude)
{
amplitude = std::max(0.f, std::min(1.f, amplitude));
auto* xr = Environment::get().getManager();
XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
vibration.amplitude = amplitude;
@ -534,26 +542,31 @@ OpenXRInput::OpenXRInput()
, mAPath(generateControllerActionPaths("/input/a/click"))
, mBPath(generateControllerActionPaths("/input/b/click"))
, mTriggerValuePath(generateControllerActionPaths("/input/trigger/value"))
, mGameMenu(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_GameMenu, "game_menu", "GameMenu", { })))
, mGameMenu(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_GameMenu, "game_menu", "Game Menu", { })))
, mRepositionMenu(std::move(createMWAction<ButtonLongPressAction>(A_RepositionMenu, "reposition_menu", "Reposition Menu", { })))
, mInventory(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_Inventory, "inventory", "Inventory", { })))
, mActivate(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_Activate, "activate", "Activate", { })))
, mUse(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_Use, "use", "Use", { })))
, mUse(std::move(createMWAction<ButtonHoldAction>(MWInput::InputManager::A_Use, "use", "Use", { })))
, mJump(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_Jump, "jump", "Jump", { })))
, mToggleWeapon(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_ToggleWeapon, "weapon", "Weapon", { })))
, mToggleSpell(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_ToggleSpell, "spell", "Spell", { })))
, mCycleSpellLeft(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleSpellLeft, "cycle_spell_left", "CycleSpellLeft", { })))
, mCycleSpellRight(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleSpellRight, "cycle_spell_right", "CycleSpellRight", { })))
, mCycleWeaponLeft(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleWeaponLeft, "cycle_weapon_left", "CycleWeaponLeft", { })))
, mCycleWeaponRight(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleWeaponRight, "cycle_weapon_right", "CycleWeaponRight", { })))
, mToggleSneak(std::move(createMWAction<ButtonHoldAction>(MWInput::InputManager::A_ToggleSneak, "sneak", "Sneak", { })))
, mQuickMenu(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_QuickMenu, "quick_menu", "QuickMenu", { })))
, mLookLeftRight(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_LookLeftRight, "look_left_right", "LookLeftRight", { })))
, mMoveForwardBackward(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_MoveForwardBackward, "move_forward_backward", "MoveForwardBackward", { })))
, mMoveLeftRight(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_MoveLeftRight, "move_left_right", "MoveLeftRight", { })))
, mJournal(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_Journal, "journal_book", "Journal Book", { })))
//, mQuickSave(std::move(createMWAction<ButtonHoldAction>(MWInput::InputManager::A_QuickSave, "quick_save", "Quick Save", { })))
, mQuickKeysMenu(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_QuickKeysMenu, "quick_keys_menu", "Quick Keys Menu", { })))
, mCycleSpellLeft(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleSpellLeft, "cycle_spell_left", "Cycle Spell Left", { })))
, mCycleSpellRight(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleSpellRight, "cycle_spell_right", "Cycle Spell Right", { })))
, mCycleWeaponLeft(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleWeaponLeft, "cycle_weapon_left", "Cycle Weapon Left", { })))
, mCycleWeaponRight(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleWeaponRight, "cycle_weapon_right", "Cycle Weapon Right", { })))
, mSneak(std::move(createMWAction<ButtonHoldAction>(MWInput::InputManager::A_Sneak, "sneak", "Sneak", { })))
, mQuickMenu(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_QuickMenu, "quick_menu", "Quick Menu", { })))
, mLookLeftRight(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_LookLeftRight, "look_left_right", "Look Left Right", { })))
, mMoveForwardBackward(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_MoveForwardBackward, "move_forward_backward", "Move Forward Backward", { })))
, mMoveLeftRight(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_MoveLeftRight, "move_left_right", "Move Left Right", { })))
, mJournal(std::move(createMWAction<ButtonLongPressAction>(MWInput::InputManager::A_Journal, "journal_book", "Journal Book", { })))
, mQuickSave(std::move(createMWAction<ButtonLongPressAction>(MWInput::InputManager::A_QuickSave, "quick_save", "Quick Save", { })))
, mRest(std::move(createMWAction<ButtonLongPressAction>(MWInput::InputManager::A_Rest, "rest", "Rest", { })))
, mActivateTouch(std::move(createMWAction<AxisAction>(A_ActivateTouch, "activate_touched", "Activate Touch", { RIGHT_HAND })))
, mAlwaysRun(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_AlwaysRun, "always_run", "Always Run", { })))
, mAutoMove(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_AutoMove, "auto_move", "Auto Move", { })))
, mToggleHUD(std::move(createMWAction<ButtonLongPressAction>(MWInput::InputManager::A_ToggleHUD, "toggle_hud", "Toggle HUD", { })))
, mToggleDebug(std::move(createMWAction<ButtonLongPressAction>(MWInput::InputManager::A_ToggleDebug, "toggle_debug", "Toggle DEBUG", { })))
, mHandPoseAction(std::move(createXRAction(XR_ACTION_TYPE_POSE_INPUT, "hand_pose", "Hand Pose", { LEFT_HAND, RIGHT_HAND })))
, mHapticsAction(std::move(createXRAction(XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibrate_hand", "Vibrate Hand", { LEFT_HAND, RIGHT_HAND })))
{
@ -562,6 +575,81 @@ OpenXRInput::OpenXRInput()
XrPath oculusTouchInteractionProfilePath;
CHECK_XRCMD(
xrStringToPath(xr->impl().mInstance, "/interaction_profiles/oculus/touch_controller", &oculusTouchInteractionProfilePath));
/*
// Applicable actions not (yet) included
A_QuickKey1,
A_QuickKey2,
A_QuickKey3,
A_QuickKey4,
A_QuickKey5,
A_QuickKey6,
A_QuickKey7,
A_QuickKey8,
A_QuickKey9,
A_QuickKey10,
A_QuickKeysMenu,
A_QuickLoad,
A_CycleSpellLeft,
A_CycleSpellRight,
A_CycleWeaponLeft,
A_CycleWeaponRight,
A_Screenshot, // Generate a VR screenshot?
A_Console, // Currently awkward due to a lack of virtual keyboard, but should be included when that's in place
*/
/*
Oculus Bindings:
L-Squeeze:
Hold: Sneak
R-Squeeze:
Hold: Enable Pointer
L-Trigger:
Press: Jump
R-Trigger:
IF POINTER:
Activate
ELSE:
Use
L-Thumbstick:
X-Axis: MoveForwardBackward
Y-Axis: MoveLeftRight
Button:
Press: AlwaysRun
Long: ToggleHUD
Touch:
R-Thumbstick:
X-Axis: LookLeftRight
Y-Axis:
Button:
Press: AutoMove
Long: ToggleDebug
Touch:
X:
Press: Toggle Spell
Long:
Y:
Press: Rest
Long: Quick Save
A:
Press: Toggle Weapon
Long:
B:
Press: Inventory
Long: Journal
Menu:
Press: GameMenun
Long: Reposition GUI
*/
std::vector<XrActionSuggestedBinding> bindings{ {
{mHandPoseAction, mPosePath[LEFT_HAND]},
{mHandPoseAction, mPosePath[RIGHT_HAND]},
@ -574,16 +662,22 @@ OpenXRInput::OpenXRInput()
{*mUse, mTriggerValuePath[RIGHT_HAND]},
{*mJump, mTriggerValuePath[LEFT_HAND]},
{*mToggleWeapon, mAPath[RIGHT_HAND]},
{*mToggleSpell, mAPath[RIGHT_HAND]},
{*mCycleSpellLeft, mThumbstickClickPath[LEFT_HAND]},
{*mCycleSpellRight, mThumbstickClickPath[RIGHT_HAND]},
{*mCycleWeaponLeft, mThumbstickClickPath[LEFT_HAND]},
{*mCycleWeaponRight, mThumbstickClickPath[RIGHT_HAND]},
{*mToggleSneak, mXPath[LEFT_HAND]},
{*mToggleSpell, mXPath[LEFT_HAND]},
//{*mCycleSpellLeft, mThumbstickClickPath[LEFT_HAND]},
//{*mCycleSpellRight, mThumbstickClickPath[RIGHT_HAND]},
//{*mCycleWeaponLeft, mThumbstickClickPath[LEFT_HAND]},
//{*mCycleWeaponRight, mThumbstickClickPath[RIGHT_HAND]},
{*mAlwaysRun, mThumbstickClickPath[LEFT_HAND]},
{*mAutoMove, mThumbstickClickPath[RIGHT_HAND]},
{*mToggleHUD, mThumbstickClickPath[LEFT_HAND]},
{*mToggleDebug, mThumbstickClickPath[RIGHT_HAND]},
{*mSneak, mSqueezeValuePath[LEFT_HAND]},
{*mInventory, mBPath[RIGHT_HAND]},
{*mJournal, mYPath[LEFT_HAND]},
//{*mQuickSave, mYPath[LEFT_HAND]},
{*mRest, mYPath[LEFT_HAND]},
{*mJournal, mBPath[RIGHT_HAND]},
{*mQuickSave, mYPath[LEFT_HAND]},
{*mGameMenu, mMenuClickPath[LEFT_HAND]},
{*mRepositionMenu, mMenuClickPath[LEFT_HAND]},
{*mActivateTouch, mSqueezeValuePath[RIGHT_HAND]},
} };
XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
@ -591,24 +685,6 @@ OpenXRInput::OpenXRInput()
suggestedBindings.suggestedBindings = bindings.data();
suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
CHECK_XRCMD(xrSuggestInteractionProfileBindings(xr->impl().mInstance, &suggestedBindings));
/*
mActivate; // R-Squeeze
mUse; // R-Trigger
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
mCycleWeaponRight; // R-ThumbstickClick
mToggleSneak; // X
mInventory; // B
mQuickMenu; // Y
mGameMenu; // Menu
*/
}
{ // Set up action spaces
@ -691,6 +767,7 @@ OpenXRInput::updateControls()
CHECK_XRCMD(xrSyncActions(xr->impl().mSession, &syncInfo));
mGameMenu->updateAndQueue(mActionQueue);
mRepositionMenu->updateAndQueue(mActionQueue);
mInventory->updateAndQueue(mActionQueue);
mActivate->updateAndQueue(mActionQueue);
mUse->updateAndQueue(mActionQueue);
@ -701,15 +778,28 @@ OpenXRInput::updateControls()
mCycleSpellRight->updateAndQueue(mActionQueue);
mCycleWeaponLeft->updateAndQueue(mActionQueue);
mCycleWeaponRight->updateAndQueue(mActionQueue);
mToggleSneak->updateAndQueue(mActionQueue);
mSneak->updateAndQueue(mActionQueue);
mQuickMenu->updateAndQueue(mActionQueue);
mLookLeftRight->updateAndQueue(mActionQueue);
mMoveForwardBackward->updateAndQueue(mActionQueue);
mMoveLeftRight->updateAndQueue(mActionQueue);
mJournal->updateAndQueue(mActionQueue);
//mQuickSave->updateAndQueue(mActionQueue);
mQuickKeysMenu->updateAndQueue(mActionQueue);
mQuickSave->updateAndQueue(mActionQueue);
mRest->updateAndQueue(mActionQueue);
mActivateTouch->updateAndQueue(mActionQueue);
mAlwaysRun->updateAndQueue(mActionQueue);
mAutoMove->updateAndQueue(mActionQueue);
mToggleHUD->updateAndQueue(mActionQueue);
mToggleDebug->updateAndQueue(mActionQueue);
//if (mActivateTouch->isActive())
//{
// mHapticsAction.applyHaptics(mSubactionPath[RIGHT_HAND], 0.5);
//}
//if (mSneak->isActive())
//{
// mHapticsAction.applyHaptics(mSubactionPath[LEFT_HAND], 0.5);
//}
}
XrPath OpenXRInput::generateXrPath(const std::string& path)
@ -846,11 +936,11 @@ private:
{
injectMousePress(SDL_BUTTON_LEFT, onPress);
}
//else if (onPress)
//{
// // Other actions should only happen on release;
// return;
//}
else if (onPress)
{
// Other actions should only happen on release;
return;
}
else if (dnd.mIsOnDragAndDrop)
{
// Intersected with the world while drag and drop is active
@ -879,6 +969,17 @@ private:
mouseReleased(arg, sdlButton);
}
// TODO: Configurable haptics: on/off + max intensity
void OpenXRInputManager::applyHapticsLeftHand(float intensity)
{
mXRInput->applyHaptics(OpenXRInput::LEFT_HAND, intensity);
}
void OpenXRInputManager::applyHapticsRightHand(float intensity)
{
mXRInput->applyHaptics(OpenXRInput::RIGHT_HAND, intensity);
}
OpenXRInputManager::OpenXRInputManager(
SDL_Window* window,
osg::ref_ptr<osgViewer::Viewer> viewer,
@ -903,7 +1004,7 @@ private:
{
// VR mode has no concept of these
mControlSwitch["vanitymode"] = false;
mGuiCursorEnabled = false;
//mGuiCursorEnabled = false;
}
OpenXRInputManager::~OpenXRInputManager()
@ -913,7 +1014,7 @@ private:
void OpenXRInputManager::changeInputMode(bool mode)
{
// VR mode has no concept of these
mGuiCursorEnabled = false;
//mGuiCursorEnabled = false;
MWInput::InputManager::changeInputMode(mode);
MWBase::Environment::get().getWindowManager()->showCrosshair(false);
MWBase::Environment::get().getWindowManager()->setCursorVisible(false);
@ -927,10 +1028,13 @@ private:
mXRInput->updateControls();
auto* vrGuiManager = Environment::get().getGUIManager();
vrGuiManager->updateFocus();
bool vrHasFocus = vrGuiManager->updateFocus();
auto guiCursor = vrGuiManager->guiCursor();
mGuiCursorX = guiCursor.x();
mGuiCursorY = guiCursor.y();
if (vrHasFocus)
{
mGuiCursorX = guiCursor.x();
mGuiCursorY = guiCursor.y();
}
while (auto* action = mXRInput->nextAction())
{
@ -942,9 +1046,6 @@ private:
MWInput::InputManager::update(dt, disableControls, disableEvents);
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
auto* xrGUIManager = Environment::get().getGUIManager();
if(xrGUIManager)
xrGUIManager->showGUIs(guiMode);
setPlayerControlsEnabled(!guiMode);
@ -981,6 +1082,14 @@ private:
case A_MoveForwardBackward:
mInputBinder->getChannel(A_MoveForwardBackward)->setValue(-action->value() / 2.f + 0.5f);
break;
case A_Sneak:
if(!mSneakToggles)
mInputBinder->getChannel(A_Sneak)->setValue(action->isActive() ? 1.f : 0.f);
break;
case A_Use:
if (!(mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode()))
mInputBinder->getChannel(A_Use)->setValue(action->value());
break;
default:
break;
}
@ -992,10 +1101,6 @@ private:
{
case A_GameMenu:
toggleMainMenu();
// Explicitly request position update here so that the player can move the menu
// using the menu key when the menu can't be toggled.
// TODO: This should respond to a menu HODL instead
// xrGUIManager->updateTracking();
break;
case A_Screenshot:
screenshot();
@ -1058,9 +1163,11 @@ private:
showQuickKeysMenu();
break;
case A_ToggleHUD:
Log(Debug::Verbose) << "Toggle HUD";
MWBase::Environment::get().getWindowManager()->toggleHud();
break;
case A_ToggleDebug:
Log(Debug::Verbose) << "Toggle Debug";
MWBase::Environment::get().getWindowManager()->toggleDebugWindow();
break;
case A_QuickSave:
@ -1085,27 +1192,33 @@ private:
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))
MWBase::Environment::get().getWindowManager()->cycleWeapon(true);
break;
case A_ToggleSneak:
toggleSneaking();
break;
case A_Jump:
mAttemptJump = true;
break;
case OpenXRInput::A_RepositionMenu:
xrGUIManager->updateTracking();
break;
case A_Use:
if (mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode())
pointActivation(true);
default:
break;
}
}
// A few actions need both activate and deactivate
// A few actions need to fire on deactivation
if (action->onDeactivate())
{
switch (action->openMWActionCode())
{
case A_Use:
mInputBinder->getChannel(A_Use)->setValue(0.f);
if (mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode())
pointActivation(action->onActivate());
else
mInputBinder->getChannel(A_Use)->setValue(action->isActive());
pointActivation(false);
break;
case A_Sneak:
if (mSneakToggles)
toggleSneaking();
break;
default:
break;

@ -49,6 +49,9 @@ namespace MWVR
void injectMousePress(int sdlButton, bool onPress);
void applyHapticsLeftHand(float intensity) override;
void applyHapticsRightHand(float intensity) override;
std::unique_ptr<OpenXRInput> mXRInput;
std::unique_ptr<RealisticCombat::StateMachine> mRealisticCombat;
Pose mPreviousHeadPose{};

@ -507,6 +507,8 @@ void VRAnimation::updateParts()
removeIndividualPart(ESM::PartReferenceType::PRT_RAnkle);
removeIndividualPart(ESM::PartReferenceType::PRT_LKnee);
removeIndividualPart(ESM::PartReferenceType::PRT_RKnee);
removeIndividualPart(ESM::PartReferenceType::PRT_LFoot);
removeIndividualPart(ESM::PartReferenceType::PRT_RFoot);
}
else
{
@ -613,6 +615,11 @@ osg::ref_ptr<osg::Geometry> VRAnimation::createPointerGeometry(void)
return geometry;
}
float VRAnimation::getVelocity(const std::string& groupname) const
{
return 0.0f;
}
osg::Vec3f VRAnimation::runAnimation(float timepassed)
{
return NpcAnimation::runAnimation(timepassed);
@ -662,6 +669,13 @@ void VRAnimation::addControllers()
group->addChild(mModelOffset);
mModelOffset->addChild(mObjectRoot);
}
auto wb = mNodeMap.find("weapon bone");
if (wb != mNodeMap.end())
{
wb->second->removeChild(mWeaponPointerTransform);
wb->second->addChild(mWeaponPointerTransform);
}
}
void VRAnimation::enableHeadAnimation(bool)
{
@ -692,6 +706,23 @@ const MWRender::RayResult& VRAnimation::getPointerTarget() const
return mPointerTarget;
}
MWWorld::Ptr VRAnimation::getTarget(const std::string& directorNode)
{
auto node = mNodeMap.find(directorNode);
auto* world = MWBase::Environment::get().getWorld();
MWRender::RayResult result{};
if (node != mNodeMap.end())
if (world)
world->getTargetObject(result, node->second);
return result.mHitObject;
}
osg::Matrix VRAnimation::getWeaponTransformMatrix() const
{
return osg::computeLocalToWorld(mWeaponDirectionTransform->getParentalNodePaths()[0]);
}
void VRAnimation::updatePointerTarget()
{
auto* world = MWBase::Environment::get().getWorld();

@ -63,9 +63,15 @@ public:
void updatePointerTarget();
public:
MWWorld::Ptr getTarget(const std::string& directorNode);
osg::Matrix getWeaponTransformMatrix() const;
protected:
static osg::ref_ptr<osg::Geometry> createPointerGeometry(void);
float getVelocity(const std::string& groupname) const override;
public:
std::shared_ptr<OpenXRSession> mSession;
osg::ref_ptr<ForearmController> mForearmControllers[2];

@ -134,11 +134,11 @@ private:
VRGUILayer::VRGUILayer(
osg::ref_ptr<osg::Group> geometryRoot,
osg::ref_ptr<osg::Group> cameraRoot,
std::string filter,
std::string layerName,
LayerConfig config,
VRGUIManager* parent)
: mConfig(config)
, mFilter(filter)
, mLayerName(layerName)
, mGeometryRoot(geometryRoot)
, mCameraRoot(cameraRoot)
{
@ -176,6 +176,9 @@ VRGUILayer::VRGUILayer(
mGeometry->setName("VRGUILayer");
// Create the camera that will render the menu texture
std::string filter = mLayerName;
if (!mConfig.extraLayers.empty())
filter = filter + ";" + mConfig.extraLayers;
mGUICamera = new GUICamera(config.pixelResolution.x(), config.pixelResolution.y(), config.backgroundColor);
osgMyGUI::RenderManager& renderManager = static_cast<osgMyGUI::RenderManager&>(MyGUI::RenderManager::getInstance());
mMyGUICamera = renderManager.createGUICamera(osg::Camera::NESTED_RENDER, filter);
@ -201,8 +204,10 @@ VRGUILayer::VRGUILayer(
mCameraRoot->addChild(mGUICamera);
// Edit offset to account for priority
if(!mConfig.sideBySide)
if (!mConfig.sideBySide)
{
mConfig.offset.y() -= 0.001f * mConfig.priority;
}
mTransform->addUpdateCallback(new LayerUpdateCallback(this));
}
@ -302,7 +307,7 @@ void VRGUILayer::updateRect()
}
// Some widgets don't capture the full visual
if (mFilter == "JournalBooks" || mFilter == "MessageBox" )
if (mLayerName == "JournalBooks" )
{
mRealRect.left = 0.f;
mRealRect.top = 0.f;
@ -310,7 +315,7 @@ void VRGUILayer::updateRect()
mRealRect.bottom = 1.f;
}
if (mFilter == "Notification")
if (mLayerName == "Notification")
{
// The latest widget for notification is always the top one
// So we just have to stretch the rectangle to the bottom
@ -357,7 +362,7 @@ void VRGUILayer::update()
{
mTransform->setScale(osg::Vec3(w / res, 1.f, h / res));
}
if (mFilter == "Notification")
if (mLayerName == "Notification")
{
auto viewSize = MyGUI::RenderManager::getInstance().getViewSize();
h = (1.f - mRealRect.top) * viewSize.height;
@ -421,44 +426,28 @@ VRGUIManager::~VRGUIManager(void)
{
}
void VRGUIManager::showGUIs(bool show)
{
}
static const LayerConfig createDefaultConfig(int priority)
static const LayerConfig createDefaultConfig(int priority, bool background = true, SizingMode sizingMode = SizingMode::Auto)
{
return LayerConfig{
1,
priority,
false, // side-by-side
osg::Vec4{0.f,0.f,0.f,.75f}, // background
background ? osg::Vec4{0.f,0.f,0.f,.75f} : osg::Vec4{}, // background
osg::Vec3(0.f,0.66f,-.25f), // offset
osg::Vec2(0.f,0.f), // center (model space)
osg::Vec2(1.f, 1.f), // extent (meters)
1024, // Spatial resolution (pixels per meter)
osg::Vec2i(2048,2048), // Texture resolution
osg::Vec2(1,1),
SizingMode::Auto,
TrackingMode::Menu
sizingMode,
TrackingMode::Menu,
"Popup"
};
}
LayerConfig gDefaultConfig = createDefaultConfig(1);
LayerConfig gJournalBooksConfig = LayerConfig
{
2,
gDefaultConfig.sideBySide,
osg::Vec4{}, // background
gDefaultConfig.offset,
gDefaultConfig.center,
gDefaultConfig.extent,
gDefaultConfig.spatialResolution,
gDefaultConfig.pixelResolution,
gDefaultConfig.myGUIViewSize,
SizingMode::Fixed,
gDefaultConfig.trackingMode
};
LayerConfig gDefaultWindowsConfig = createDefaultConfig(3);
LayerConfig gMessageBoxConfig = gJournalBooksConfig;
LayerConfig gNotificationConfig = gJournalBooksConfig;
LayerConfig gJournalBooksConfig = createDefaultConfig(2, false, SizingMode::Fixed);
LayerConfig gDefaultWindowsConfig = createDefaultConfig(3, true);
LayerConfig gMessageBoxConfig = createDefaultConfig(6, false, SizingMode::Auto);;
LayerConfig gNotificationConfig = createDefaultConfig(7, false, SizingMode::Fixed);;
static const float sSideBySideRadius = 1.f;
static const float sSideBySideAzimuthInterval = -osg::PI_4;
@ -475,7 +464,8 @@ static const LayerConfig createSideBySideConfig(int priority)
gDefaultConfig.pixelResolution,
osg::Vec2(0.70f, 0.70f),
SizingMode::Fixed,
gDefaultConfig.trackingMode
gDefaultConfig.trackingMode,
""
};
};
@ -499,6 +489,7 @@ LayerConfig gStatusHUDConfig = LayerConfig
gDefaultConfig.myGUIViewSize,
SizingMode::Auto,
TrackingMode::HudLeftHand,
""
};
LayerConfig gPopupConfig = LayerConfig
@ -514,6 +505,7 @@ LayerConfig gPopupConfig = LayerConfig
gDefaultConfig.myGUIViewSize,
SizingMode::Auto,
TrackingMode::HudRightHand,
""
};
@ -521,8 +513,7 @@ LayerConfig gPopupConfig = LayerConfig
static std::map<std::string, LayerConfig&> gLayerConfigs =
{
{"StatusHUD", gStatusHUDConfig},
//{"MinimapHUD", gMinimapHUDConfig},
{"Popup", gPopupConfig},
{"Tooltip", gPopupConfig},
{"JournalBooks", gJournalBooksConfig},
{"InventoryCompanionWindow", gInventoryCompanionWindowConfig},
{"InventoryWindow", gInventoryWindowConfig},
@ -532,13 +523,13 @@ static std::map<std::string, LayerConfig&> gLayerConfigs =
{"DialogueWindow", gDialogueWindowConfig},
{"MessageBox", gMessageBoxConfig},
{"Windows", gDefaultWindowsConfig},
{"Notification", gNotificationConfig}
{"Notification", gNotificationConfig},
};
static std::set<std::string> layerBlacklist =
{
"Overlay",
"AdditiveOverlay"
"AdditiveOverlay",
};
void VRGUIManager::updateSideBySideLayers()
@ -582,9 +573,6 @@ void VRGUIManager::insertLayer(const std::string& name)
layer->mGeometry->setUserData(new VRGUILayerUserData(mLayers[name]));
// Default new layer's pick to false
// TODO: re-add widget->setLayerPick(false) somewhere;
if (config.sideBySide)
{
mSideBySideLayers.push_back(layer);
@ -715,7 +703,7 @@ void VRGUIManager::updateTracking(void)
layer.second->updateTracking(mHeadPose);
}
void VRGUIManager::updateFocus()
bool VRGUIManager::updateFocus()
{
auto* anim = MWVR::Environment::get().getPlayerAnimation();
if (anim && anim->mPointerTarget.mHit)
@ -728,12 +716,14 @@ void VRGUIManager::updateFocus()
newFocusLayer = userData->mLayer.lock();
}
if (newFocusLayer && newFocusLayer->mFilter != "Notification")
if (newFocusLayer && newFocusLayer->mLayerName != "Notification")
{
setFocusLayer(newFocusLayer.get());
computeGuiCursor(anim->mPointerTarget.mHitPointLocal);
return true;
}
}
return false;
}
void VRGUIManager::setFocusLayer(VRGUILayer* layer)

@ -61,6 +61,7 @@ namespace MWVR
osg::Vec2 myGUIViewSize; //!< Resizable elements are resized to this (fraction of full view)
SizingMode sizingMode; //!< How to size the layer
TrackingMode trackingMode; //!< Tracking mode
std::string extraLayers; //!< Additional layers to draw (list separated by any non-alphabetic)
bool operator<(const LayerConfig& rhs) const { return priority < rhs.priority; }
};
@ -71,7 +72,7 @@ namespace MWVR
VRGUILayer(
osg::ref_ptr<osg::Group> geometryRoot,
osg::ref_ptr<osg::Group> cameraRoot,
std::string filter,
std::string layerName,
LayerConfig config,
VRGUIManager* parent);
~VRGUILayer();
@ -95,7 +96,7 @@ namespace MWVR
public:
Pose mTrackedPose{};
LayerConfig mConfig;
std::string mFilter;
std::string mLayerName;
std::vector<MWGui::Layout*> mWidgets;
osg::ref_ptr<osg::Group> mGeometryRoot;
osg::ref_ptr<osg::Geometry> mGeometry{ new osg::Geometry };
@ -123,8 +124,6 @@ namespace MWVR
~VRGUIManager(void);
void showGUIs(bool show);
void setVisible(MWGui::Layout*, bool visible);
void updateSideBySideLayers();
@ -139,7 +138,7 @@ namespace MWVR
void updateTracking(void);
void updateFocus();
bool updateFocus();
void setFocusLayer(VRGUILayer* layer);

@ -109,7 +109,7 @@ namespace MWWorld
throw std::runtime_error("class cannot block");
}
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, const osg::Vec3f& hitPosition, bool successful, float hitStrength) const
{
throw std::runtime_error("class cannot be hit");
}

@ -131,11 +131,12 @@ namespace MWWorld
/// @return True if the attack had a victim, regardless if hit was successful or not.
/// (default implementation: throw an exception)
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength = 0.f) const;
///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is
/// true (else fatigue) by \a object (sword, arrow, etc). \a attacker specifies the
/// actor responsible for the attack, and \a successful specifies if the hit is
/// successful or not.
/// successful or not. \a hitStrength is the fraction of max attack strength applied, and is
/// used to determine haptic feedback intensity.
virtual void block (const Ptr& ptr) const;
///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield

@ -277,8 +277,7 @@ namespace MWWorld
#ifdef USE_OPENXR
if (caster == MWBase::Environment::get().getWorld()->getPlayerPtr())
{
auto* anim = MWVR::Environment::get().getPlayerAnimation();
osg::Matrix worldMatrix = osg::computeLocalToWorld(anim->mWeaponDirectionTransform->getParentalNodePaths()[0]);
osg::Matrix worldMatrix = MWVR::Environment::get().getPlayerAnimation()->getWeaponTransformMatrix();
orient = worldMatrix.getRotate();
pos = worldMatrix.getTrans();
}

@ -1101,7 +1101,8 @@ namespace MWWorld
MWWorld::Ptr World::getFacedObject()
{
#ifdef USE_OPENXR
// TODO: Rename this method to getTargetObject?
// "getFacedObject" doesn't make sense with finger pointing.
auto* anim = MWVR::Environment::get().getPlayerAnimation();
if (anim && anim->mPointerTarget.mHit)
return anim->mPointerTarget.mHitObject;
@ -1163,12 +1164,8 @@ namespace MWWorld
if (ptr == getPlayerPtr())
{
#ifdef USE_OPENXR
// TODO: Configurable realistic fighting ?
// Use current aim of weapon to impact
// TODO: Use bounding box of weapon instead ?
auto* anim = MWVR::Environment::get().getPlayerAnimation();
osg::Matrix worldMatrix = osg::computeLocalToWorld(anim->mWeaponDirectionTransform->getParentalNodePaths()[0]);
osg::Matrix worldMatrix = MWVR::Environment::get().getPlayerAnimation()->getWeaponTransformMatrix();
auto result = mPhysics->getHitContact(ptr, worldMatrix.getTrans(), worldMatrix.getRotate(), distance, targets);
if (!result.first.isEmpty())
@ -3110,8 +3107,7 @@ namespace MWWorld
#ifdef USE_OPENXR
if (actor == MWMechanics::getPlayer())
{
auto* anim = MWVR::Environment::get().getPlayerAnimation();
osg::Matrix worldMatrix = osg::computeLocalToWorld(anim->mWeaponDirectionTransform->getParentalNodePaths()[0]);
osg::Matrix worldMatrix = MWVR::Environment::get().getPlayerAnimation()->getWeaponTransformMatrix();
origin = worldMatrix.getTrans();
orient = worldMatrix.getRotate();
}

@ -563,8 +563,10 @@ void GUICamera::collectDrawCalls(std::string filter)
auto layer = myGUILayers->getLayer(i);
auto name = layer->getName();
if (name == filter)
if (filter.find(name) != std::string::npos)
{
layer->renderToTarget(this, mUpdate);
}
}
}
end();

@ -92,6 +92,7 @@ set(MYGUI_FILES
openmw_text.skin.xml
openmw_text_input.layout
openmw_tooltips.layout
openmw_tooltips_vr.layout
openmw_trade_window.layout
openmw_trade_window_vr.layout
openmw_trainingwindow.layout

@ -19,6 +19,7 @@
</Layer>
<Layer name="Debug" overlapped="true" pick="true"/>
<Layer name="Notification" overlapped="false" pick="false"/>
<Layer name="Tooltip" overlapped="true" pick="true"/>
<Layer name="Popup" overlapped="true" pick="true"/>
<Layer name="DragAndDrop" overlapped="false" pick="false"/>
<Layer name="LoadingScreen" overlapped="false" pick="true"/>

@ -0,0 +1,322 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout">
<Widget type="Widget" layer="Tooltip" position="0 0 300 300" name="_Main">
<!-- Dynamically constructed tooltip goes here -->
<Widget type="Widget" skin="HUD_Box_NoTransp" position="0 0 300 300" align="Stretch" name="DynamicToolTipBox">
</Widget>
<!-- Text tooltip, one line -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 300 300" align="Stretch" name="TextToolTipOneLine">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<Widget type="AutoSizedTextBox" skin="SandText" position="8 8 284 284" align="Left Top" name="TextOneLine">
<Property key="TextAlign" value="Left Top"/>
</Widget>
</Widget>
<!-- Text tooltip, multiline -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 0" align="Stretch" name="TextToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="6"/>
<Widget type="AutoSizedEditBox" skin="SandText" position="8 8 268 0" align="Left Top" name="Text">
<Property key="MultiLine" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="TextAlign" value="Left Top"/>
</Widget>
</Widget>
<!-- Faction tooltip -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 0" align="Stretch" name="FactionToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<Widget type="AutoSizedEditBox" skin="SandText" position="8 8 436 0" align="Left Top" name="FactionText">
<Property key="MultiLine" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="TextAlign" value="Left Top"/>
</Widget>
</Widget>
<!-- Race tooltip -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 56" align="Stretch" name="RaceToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<Property key="Spacing" value="8"/>
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 8 0 18" align="Left Top" name="CenteredCaption">
<Property key="TextAlign" value="Center"/>
<UserString key="HStretch" value="true"/>
</Widget>
<Widget type="AutoSizedEditBox" skin="SandText" position="8 30 430 18" align="Left Top" name="CenteredCaptionText">
<Property key="MultiLine" value="true"/>
<Property key="Shrink" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="TextAlign" value="Left Top"/>
</Widget>
</Widget>
<!-- Specialization tooltip -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 56" align="Stretch" name="SpecializationToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 8 140 18" align="Left Top" name="Caption">
<Property key="TextAlign" value="Center"/>
</Widget>
<Widget type="AutoSizedEditBox" skin="SandText" position="0 30 140 18" align="Left Top" name="ColumnText">
<Property key="MultiLine" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="TextAlign" value="Left Top"/>
</Widget>
</Widget>
<!-- Class tooltip -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 78" align="Stretch" name="ClassToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<Property key="Spacing" value="8"/>
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 8 0 18" align="Left Top HStretch" name="ClassName">
<Property key="TextAlign" value="Center"/>
<UserString key="HStretch" value="true"/>
</Widget>
<Widget type="AutoSizedEditBox" skin="SandText" position="8 30 430 18" align="Left Top" name="ClassDescription">
<Property key="MultiLine" value="true"/>
<Property key="Shrink" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="TextAlign" value="Left Top"/>
</Widget>
<Widget type="AutoSizedTextBox" skin="SandText" position="8 52 0 18" align="Left Bottom" name="ClassSpecialisation">
<Property key="TextAlign" value="Center"/>
<UserString key="HStretch" value="true"/>
</Widget>
</Widget>
<!-- Hand-to-hand tooltip -->
<Widget type="HBox" skin="HUD_Box_NoTransp" position="0 0 300 300" align="Stretch" name="HandToHandToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="6"/>
<Widget type="VBox">
<UserString key="VStretch" value="true"/>
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="HandToHandImage"/>
<Widget type="Spacer"/>
</Widget>
<Widget type="AutoSizedTextBox" skin="SandText" position="44 8 248 284" align="Left Top" name="HandToHandText">
<Property key="TextAlign" value="Center"/>
</Widget>
</Widget>
<!-- Health/Magicka/Fatigue tooltip -->
<Widget type="HBox" skin="HUD_Box_NoTransp" position="0 0 0 0" align="Stretch" name="HealthToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="14"/>
<Property key="Spacing" value="8"/>
<Widget type="VBox">
<UserString key="VStretch" value="true"/>
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="HealthImage"/>
<Widget type="Spacer"/>
</Widget>
<Widget type="AutoSizedEditBox" skin="SandText" position="44 8 392 0" align="Left Top" name="HealthDescription">
<Property key="MultiLine" value="true"/>
<Property key="Shrink" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="TextAlign" value="Left Top"/>
<UserString key="HStretch" value="true"/>
</Widget>
</Widget>
<!-- Attribute tooltip -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 0" align="Stretch" name="AttributeToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<Property key="Spacing" value="8"/>
<Widget type="HBox">
<UserString key="HStretch" value="true"/>
<Property key="Spacing" value="8"/>
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="AttributeImage"/>
<Widget type="AutoSizedTextBox" skin="NormalText" position="44 8 0 32" align="Left Top" name="AttributeName">
<Property key="TextAlign" value="Left VCenter"/>
<UserString key="HStretch" value="true"/>
</Widget>
</Widget>
<Widget type="AutoSizedEditBox" skin="SandText" position="8 44 436 248" align="Left Top" name="AttributeDescription">
<Property key="MultiLine" value="true"/>
<Property key="Shrink" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="TextAlign" value="Left Top"/>
<UserString key="HStretch" value="true"/>
</Widget>
</Widget>
<!-- Skill tooltip -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 98" align="Stretch" name="SkillToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<Widget type="HBox">
<UserString key="HStretch" value="true"/>
<Property key="Spacing" value="8"/>
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="SkillImage"/>
<Widget type="VBox">
<Widget type="AutoSizedTextBox" skin="NormalText" position="44 8 0 16" align="Left Top" name="SkillName">
<Property key="TextAlign" value="Left"/>
<UserString key="HStretch" value="true"/>
</Widget>
<Widget type="AutoSizedTextBox" skin="SandText" position="44 24 0 16" align="Left Top" name="SkillAttribute">
<Property key="TextAlign" value="Left"/>
<UserString key="HStretch" value="true"/>
</Widget>
</Widget>
</Widget>
<Widget type="Widget" skin="" position="0 0 0 2" align="Left Top" />
<Widget type="AutoSizedEditBox" skin="SandText" position="8 44 430 0" align="Left Top" name="SkillDescription">
<Property key="MultiLine" value="true"/>
<Property key="Shrink" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="TextAlign" value="Left Top"/>
<Property key="Spacing" value="28"/>
<UserString key="HStretch" value="true"/>
</Widget>
<Widget type="Widget" skin="" position="0 0 0 2" align="Left Top" />
<Widget type="AutoSizedTextBox" skin="SandText" position="8 48 200 18" align="Left Bottom" name="SkillMaxed">
<Property key="Caption" value="#{sSkillMaxReached}"/>
<Property key="TextAlign" value="Center"/>
<UserString key="HStretch" value="true"/>
</Widget>
<Widget type="VBox" name="SkillProgressVBox">
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 48 200 18" align="Left Bottom">
<Property key="Caption" value="#{sSkillProgress}"/>
<Property key="TextAlign" value="Center"/>
</Widget>
<Widget type="ProgressBar" skin="MW_Progress_Red" position="50 70 200 20" align="HCenter Bottom" name="SkillProgress">
<Widget type="TextBox" skin="ProgressText" position="0 0 200 16" align="Stretch" name="SkillProgressText">
<Property key="TextAlign" value="Center"/>
</Widget>
</Widget>
</Widget>
</Widget>
<!-- Skill tooltip (without progress bar) -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 52" align="Stretch" name="SkillNoProgressToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<Widget type="HBox">
<UserString key="HStretch" value="true"/>
<Property key="Spacing" value="8"/>
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="SkillNoProgressImage"/>
<Widget type="VBox">
<Widget type="AutoSizedEditBox" skin="NormalText" position="44 8 0 16" align="Left Top" name="SkillNoProgressName">
<Property key="TextAlign" value="Left"/>
<UserString key="HStretch" value="true"/>
</Widget>
<Widget type="AutoSizedTextBox" skin="SandText" position="44 24 0 16" align="Left Top" name="SkillNoProgressAttribute">
<Property key="TextAlign" value="Left"/>
<UserString key="HStretch" value="true"/>
</Widget>
</Widget>
</Widget>
<Widget type="Widget" skin="" position="0 0 0 2" align="Left Top" />
<Widget type="AutoSizedEditBox" skin="SandText" position="8 44 430 0" align="Left Top" name="SkillNoProgressDescription">
<Property key="MultiLine" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="TextAlign" value="Left Top"/>
<Property key="Spacing" value="28"/>
</Widget>
</Widget>
<!-- Level tooltip -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 300 58" align="HStretch" name="LevelToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 8 284 18" align="Left Top">
<Property key="Caption" value="#{sLevelProgress}"/>
<Property key="TextAlign" value="Center"/>
</Widget>
<Widget type="ProgressBar" skin="MW_Progress_Red" position="50 30 180 20" align="HCenter Bottom" name="LevelProgress">
<Widget type="TextBox" skin="ProgressText" position="0 0 180 20" align="HCenter" name="LevelProgressText">
<Property key="TextAlign" value="HCenter Top"/>
</Widget>
</Widget>
</Widget>
<!-- Birthsign tooltip -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 300 300" align="Stretch" name="BirthSignToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<!-- Birthsign image -->
<Widget type="Widget" skin="MW_Box" position="18 13 263 137" align="Top HCenter">
<Widget type="ImageBox" skin="ImageBox" position="2 2 259 133" name="BirthSignImage" align="Left Top"/>
</Widget>
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 154 284 138" align="Top" name="BirthSignText">
<Property key="TextAlign" value="Top HCenter"/>
</Widget>
</Widget>
<!-- Magic effect tooltip -->
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 0" align="Stretch" name="MagicEffectToolTip">
<Property key="AutoResize" value="true"/>
<Property key="Padding" value="8"/>
<Widget type="HBox">
<UserString key="HStretch" value="true"/>
<Property key="Spacing" value="8"/>
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="MagicEffectImage"/>
<Widget type="VBox">
<Widget type="AutoSizedTextBox" skin="NormalText" position="44 8 404 16" align="Left Top" name="MagicEffectName">
<Property key="TextAlign" value="Left"/>
<UserString key="HStretch" value="true"/>
</Widget>
<Widget type="AutoSizedTextBox" skin="SandText" position="44 24 404 16" align="Left Top" name="MagicEffectSchool">
<Property key="TextAlign" value="Left"/>
<UserString key="HStretch" value="true"/>
</Widget>
</Widget>
</Widget>
<Widget type="AutoSizedEditBox" skin="SandText" position="8 44 436 0" align="Left Top" name="MagicEffectDescription">
<Property key="MultiLine" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="TextAlign" value="Left Top"/>
</Widget>
</Widget>
</Widget>
</MyGUI>
Loading…
Cancel
Save