diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ec85d64d6..70a007ad5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -39,6 +39,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" +#include "../mwrender/npcanimation.hpp" #include "aicombataction.hpp" #include "movement.hpp" @@ -2377,6 +2378,18 @@ void CharacterController::update(float duration, bool animationOnly) mSkipAnim = false; mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead()); + +#ifdef USE_OPENXR + if (isPlayer) + { + auto disabled = MWBase::Environment::get().getWorld()->getPlayer().isDisabled(); + auto animation = static_cast(mAnimation); + if (disabled) + animation->setViewMode(MWRender::NpcAnimation::VM_VRNormal); + else + animation->setViewMode(MWRender::NpcAnimation::VM_VRFirstPerson); + } +#endif } void CharacterController::persistAnimationState() diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 736474003..341dca3e1 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -286,6 +286,7 @@ namespace MWPhysics return position; const bool isPlayer = (ptr == MWMechanics::getPlayer()); + auto* world = MWBase::Environment::get().getWorld(); // In VR, player should move according to current direction of // a selected limb, rather than current orientation of camera. @@ -320,7 +321,7 @@ namespace MWPhysics // While this is strictly speaking wrong, it's needed for MW compatibility. position.z() += halfExtents.z(); - static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get() + static const float fSwimHeightScale = world->getStore().get() .find("fSwimHeightScale")->mValue.getFloat(); float swimlevel = waterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale); @@ -355,13 +356,13 @@ namespace MWPhysics if (isPlayer) { ptr.getClass().skillUsageSucceeded(ptr, ESM::Skill::Acrobatics, 0); - MWBase::Environment::get().getWorld()->getPlayer().setJumping(true); + world->getPlayer().setJumping(true); } // Decrease fatigue - if (!isPlayer || !MWBase::Environment::get().getWorld()->getGodModeState()) + if (!isPlayer || !world->getGodModeState()) { - const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + const MWWorld::Store &gmst = world->getStore().get(); const float fFatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat(); const float fFatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat(); const float normalizedEncumbrance = std::min(1.f, ptr.getClass().getNormalizedEncumbrance(ptr)); @@ -374,11 +375,11 @@ namespace MWPhysics } // Now that we have the effective movement vector, apply wind forces to it - if (MWBase::Environment::get().getWorld()->isInStorm()) + if (world->isInStorm()) { - osg::Vec3f stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); + osg::Vec3f stormDirection = world->getStormDirection(); float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity / (stormDirection.length() * velocity.length()))); - static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get() + static const float fStromWalkMult = world->getStore().get() .find("fStromWalkMult")->mValue.getFloat(); velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f)); } @@ -388,8 +389,9 @@ namespace MWPhysics osg::Vec3f newPosition = position; #ifdef USE_OPENXR - if (isPlayer) + if (isPlayer && !world->getPlayer().isDisabled()) { + auto* inputManager = MWVR::Environment::get().getInputManager(); osg::Vec3 trackingOffset = inputManager->mHeadOffset; diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index b78403771..d3faa3bfa 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -417,11 +417,8 @@ namespace MWRender void Camera::processViewChange() { #ifdef USE_OPENXR - mAnimation->setViewMode(NpcAnimation::VM_VRHeadless); + //mAnimation->setViewMode(NpcAnimation::VM_VRFirstPerson); - // For comfort, in VR mode the camera should only track nodes that don't animate. - // As-is, only the first person mode adds any unanimated nodes, but we want the third-person mode body. - // So we look up the root node of the player to track that. SceneUtil::FindByNameVisitor findRootVisitor("Player Root", osg::NodeVisitor::TRAVERSE_PARENTS); mAnimation->getObjectRoot()->accept(findRootVisitor); mTrackingNode = findRootVisitor.mFoundNode; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 9a86234d8..40a75d902 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -949,7 +949,7 @@ void NpcAnimation::addControllers() mActiveControllers.emplace(node, mFirstPersonNeckController); } } - else if (mViewMode == VM_Normal || mViewMode == VM_VRHeadless) + else if (mViewMode != VM_HeadOnly) { WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get()); } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index c86110377..939b5b531 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -36,7 +36,8 @@ public: VM_Normal, VM_FirstPerson, VM_HeadOnly, - VM_VRHeadless, + VM_VRNormal, + VM_VRFirstPerson, }; protected: diff --git a/apps/openmw/mwvr/openxrinputmanager.cpp b/apps/openmw/mwvr/openxrinputmanager.cpp index a5c108c29..ad68950cb 100644 --- a/apps/openmw/mwvr/openxrinputmanager.cpp +++ b/apps/openmw/mwvr/openxrinputmanager.cpp @@ -943,7 +943,7 @@ private: auto player = mPlayer->getPlayer(); if (!mRealisticCombat || mRealisticCombat->ptr != player) mRealisticCombat.reset(new RealisticCombat::StateMachine(player)); - bool enabled = !guiMode && mPlayer->getDrawState() == MWMechanics::DrawState_Weapon; + bool enabled = !guiMode && mPlayer->getDrawState() == MWMechanics::DrawState_Weapon && !mPlayer->isDisabled(); mRealisticCombat->update(dt, enabled); } @@ -1126,13 +1126,6 @@ private: osg::Vec3 vrMovement = currentHeadPose.position - mPreviousHeadPose.position; mPreviousHeadPose = currentHeadPose; - float yaw = 0.f; - float pitch = 0.f; - float roll = 0.f; - getEulerAngles(currentHeadPose.orientation, yaw, pitch, roll); - - yaw += mYaw; - if (mRecenter) { mHeadOffset = osg::Vec3(0, 0, 0); @@ -1145,10 +1138,25 @@ private: osg::Quat gameworldYaw = osg::Quat(mYaw, osg::Vec3(0, 0, -1)); mHeadOffset += gameworldYaw * vrMovement; + float yaw = 0.f; + float pitch = 0.f; + float roll = 0.f; + getEulerAngles(currentHeadPose.orientation, yaw, pitch, roll); + + yaw += mYaw; + mVrAngles[0] = pitch; mVrAngles[1] = roll; mVrAngles[2] = yaw; - world->rotateObject(player, mVrAngles[0], mVrAngles[1], mVrAngles[2], MWBase::RotationFlag_none); + + if (!mPlayer->isDisabled()) + { + world->rotateObject(player, mVrAngles[0], mVrAngles[1], mVrAngles[2], MWBase::RotationFlag_none); + } + else { + // Update the camera directly to avoid rotating the disabled player + world->getRenderingManager().getCamera()->rotateCamera(-pitch, -roll, -yaw, false); + } } } diff --git a/apps/openmw/mwvr/vranimation.cpp b/apps/openmw/mwvr/vranimation.cpp index 340be4c1c..f7c965493 100644 --- a/apps/openmw/mwvr/vranimation.cpp +++ b/apps/openmw/mwvr/vranimation.cpp @@ -179,8 +179,6 @@ void ForearmController::operator()(osg::Node* node, osg::NodeVisitor* nv) // Finally, set transform transform->setMatrix(worldReference * osg::Matrix::inverse(worldToLimb) * transform->getMatrix()); - Log(Debug::Verbose) << "Updating hand: " << node->getName(); - // Omit nested callbacks to override animations of this node osg::ref_ptr ncb = getNestedCallback(); setNestedCallback(nullptr); @@ -431,7 +429,7 @@ VRAnimation::VRAnimation( const MWWorld::Ptr& ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem, bool disableSounds, std::shared_ptr xrSession) // Note that i let it construct as 3rd person and then later update it to VM_VRHeadless - // when OpenMW sets the view mode of the camera object. + // when the character controller updates : MWRender::NpcAnimation(ptr, parentNode, resourceSystem, disableSounds, VM_Normal, 55.f) , mSession(xrSession) , mIndexFingerControllers{nullptr, nullptr} @@ -474,9 +472,12 @@ VRAnimation::~VRAnimation() {}; void VRAnimation::setViewMode(NpcAnimation::ViewMode viewMode) { - if (viewMode != VM_VRHeadless) - Log(Debug::Warning) << "View mode of VRAnimation may only be VM_VRHeadless"; - NpcAnimation::setViewMode(VM_VRHeadless); + if (viewMode != VM_VRFirstPerson && viewMode != VM_VRNormal) + { + Log(Debug::Warning) << "Attempted to set view mode of VRAnimation to non-vr mode. Defaulted to VM_VRFirstPerson."; + viewMode = VM_VRFirstPerson; + } + NpcAnimation::setViewMode(viewMode); return; } @@ -484,25 +485,36 @@ void VRAnimation::updateParts() { NpcAnimation::updateParts(); - // Hide head and hair to avoid getting them in the player's face - // TODO: Hair might be acceptable ? - removeIndividualPart(ESM::PartReferenceType::PRT_Hair); - removeIndividualPart(ESM::PartReferenceType::PRT_Head); - removeIndividualPart(ESM::PartReferenceType::PRT_LForearm); - removeIndividualPart(ESM::PartReferenceType::PRT_LUpperarm); - removeIndividualPart(ESM::PartReferenceType::PRT_LWrist); - removeIndividualPart(ESM::PartReferenceType::PRT_RForearm); - removeIndividualPart(ESM::PartReferenceType::PRT_RUpperarm); - removeIndividualPart(ESM::PartReferenceType::PRT_RWrist); - removeIndividualPart(ESM::PartReferenceType::PRT_Cuirass); - removeIndividualPart(ESM::PartReferenceType::PRT_Groin); - removeIndividualPart(ESM::PartReferenceType::PRT_Neck); - removeIndividualPart(ESM::PartReferenceType::PRT_Skirt); - removeIndividualPart(ESM::PartReferenceType::PRT_Tail); - removeIndividualPart(ESM::PartReferenceType::PRT_LLeg); - removeIndividualPart(ESM::PartReferenceType::PRT_RLeg); - removeIndividualPart(ESM::PartReferenceType::PRT_LAnkle); - removeIndividualPart(ESM::PartReferenceType::PRT_RAnkle); + if (mViewMode == VM_VRFirstPerson) + { + // Hide everything other than the hands and feet. + removeIndividualPart(ESM::PartReferenceType::PRT_Hair); + removeIndividualPart(ESM::PartReferenceType::PRT_Head); + removeIndividualPart(ESM::PartReferenceType::PRT_LForearm); + removeIndividualPart(ESM::PartReferenceType::PRT_LUpperarm); + removeIndividualPart(ESM::PartReferenceType::PRT_LWrist); + removeIndividualPart(ESM::PartReferenceType::PRT_RForearm); + removeIndividualPart(ESM::PartReferenceType::PRT_RUpperarm); + removeIndividualPart(ESM::PartReferenceType::PRT_RWrist); + removeIndividualPart(ESM::PartReferenceType::PRT_Cuirass); + removeIndividualPart(ESM::PartReferenceType::PRT_Groin); + removeIndividualPart(ESM::PartReferenceType::PRT_Neck); + removeIndividualPart(ESM::PartReferenceType::PRT_Skirt); + removeIndividualPart(ESM::PartReferenceType::PRT_Tail); + removeIndividualPart(ESM::PartReferenceType::PRT_LLeg); + removeIndividualPart(ESM::PartReferenceType::PRT_RLeg); + removeIndividualPart(ESM::PartReferenceType::PRT_LAnkle); + removeIndividualPart(ESM::PartReferenceType::PRT_RAnkle); + removeIndividualPart(ESM::PartReferenceType::PRT_LKnee); + removeIndividualPart(ESM::PartReferenceType::PRT_RKnee); + } + else + { + removeIndividualPart(ESM::PartReferenceType::PRT_LForearm); + removeIndividualPart(ESM::PartReferenceType::PRT_LWrist); + removeIndividualPart(ESM::PartReferenceType::PRT_RForearm); + removeIndividualPart(ESM::PartReferenceType::PRT_RWrist); + } } void VRAnimation::setPointForward(bool enabled) diff --git a/apps/openmw/mwvr/vranimation.hpp b/apps/openmw/mwvr/vranimation.hpp index 5c353dda1..f4d6dcabf 100644 --- a/apps/openmw/mwvr/vranimation.hpp +++ b/apps/openmw/mwvr/vranimation.hpp @@ -46,14 +46,13 @@ public: /// to indicate the facing orientation of the character. virtual void setPitchFactor(float factor) { mPitchFactor = factor; } - /// Overriden to always be VM_VRHeadless + /// Overriden to always be a variant of VM_VR* virtual void setViewMode(ViewMode viewMode); /// 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); bool canPlaceObject(); diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 394e169d7..6e110ca2e 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -293,6 +293,18 @@ namespace MWWorld return MWBase::Environment::get().getMechanicsManager()->getActorsFighting(getPlayer()).size() != 0; } + bool Player::isDisabled() + { + bool disabled = false; + auto ptr = getPlayer(); + const MWWorld::Class& cls = ptr.getClass(); + auto& stats = cls.getCreatureStats(ptr); + disabled |= stats.getKnockedDown(); + disabled |= stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0.f; + disabled |= stats.isDead(); + return disabled; + } + bool Player::enemiesNearby() { return MWBase::Environment::get().getMechanicsManager()->getEnemiesNearby(getPlayer()).size() != 0; diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 2335c4e26..44852a355 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -127,6 +127,9 @@ namespace MWWorld ///Checks all nearby actors to see if anyone has an aipackage against you bool isInCombat(); + ///Checks if the player is currently in a state where he cannot act + bool isDisabled(); + bool enemiesNearby(); void clear();