From abee6ca8417122db681ea864bbdec992402621a6 Mon Sep 17 00:00:00 2001 From: madsbuvi Date: Wed, 26 May 2021 21:40:18 +0200 Subject: [PATCH] Fix #109 also fixes lack of motion controller interaction during main menu and load game. --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/world.hpp | 13 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 15 +- apps/openmw/mwrender/renderingmanager.hpp | 8 + apps/openmw/mwrender/vismask.hpp | 1 + apps/openmw/mwvr/openxrtracker.cpp | 2 +- apps/openmw/mwvr/openxrtracker.hpp | 5 +- apps/openmw/mwvr/vranimation.cpp | 159 +---------------- apps/openmw/mwvr/vranimation.hpp | 23 +-- apps/openmw/mwvr/vrgui.cpp | 46 +++-- apps/openmw/mwvr/vrgui.hpp | 4 +- apps/openmw/mwvr/vrinputmanager.cpp | 21 +-- apps/openmw/mwvr/vrpointer.cpp | 203 ++++++++++++++++++++++ apps/openmw/mwvr/vrpointer.hpp | 45 +++++ apps/openmw/mwvr/vrtracking.cpp | 29 +++- apps/openmw/mwvr/vrtracking.hpp | 13 +- apps/openmw/mwvr/vrutil.cpp | 80 +++++++++ apps/openmw/mwvr/vrutil.hpp | 24 +++ apps/openmw/mwworld/worldimp.cpp | 68 +++----- apps/openmw/mwworld/worldimp.hpp | 10 +- 23 files changed, 518 insertions(+), 261 deletions(-) create mode 100644 apps/openmw/mwvr/vrpointer.cpp create mode 100644 apps/openmw/mwvr/vrpointer.hpp create mode 100644 apps/openmw/mwvr/vrutil.cpp create mode 100644 apps/openmw/mwvr/vrutil.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 6f5b4fa1b..e0d2ea998 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -262,7 +262,7 @@ if(BUILD_OPENMW_VR) add_openmw_dir (mwvr openxraction openxractionset openxrdebug openxrinput openxrmanager openxrmanagerimpl openxrplatform openxrswapchain openxrswapchainimage openxrswapchainimpl openxrtracker openxrtypeconversions realisticcombat - vranimation vrcamera vrenvironment vrframebuffer vrgui vrinputmanager vrinput vrlistbox vrmetamenu vrsession vrtracking vrtypes vrviewer vrvirtualkeyboard + vranimation vrcamera vrenvironment vrframebuffer vrgui vrinputmanager vrinput vrlistbox vrmetamenu vrpointer vrsession vrtracking vrtypes vrutil vrviewer vrvirtualkeyboard ) openmw_add_executable(openmw_vr diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5cd64faba..677174877 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -698,6 +698,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) Settings::Manager::getString("texture mipmap", "General"), Settings::Manager::getInt("anisotropy", "General") ); + mEnvironment.setResourceSystem(mResourceSystem.get()); int numThreads = Settings::Manager::getInt("preload num threads", "Cells"); if (numThreads <= 0) @@ -858,7 +859,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // Create dialog system mEnvironment.setJournal (new MWDialogue::Journal); mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mTranslationDataStorage)); - mEnvironment.setResourceSystem(mResourceSystem.get()); // scripts if (mCompileAll) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 16efa7def..044d31233 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -91,6 +91,11 @@ namespace MWWorld typedef std::vector > PtrMovementList; } +namespace MWVR +{ + class UserPointer; +} + namespace MWBase { /// \brief Interface for the World (implemented in MWWorld) @@ -272,6 +277,8 @@ namespace MWBase virtual float getMaxActivationDistance() = 0; + virtual float getActivationDistancePlusTelekinesis() = 0; + /// Returns a pointer to the object the provided object would hit (if within the /// specified distance), and the point where the hit occurs. This will attempt to /// use the "Head" node, or alternatively the "Bip01 Head" node as a basis. @@ -667,8 +674,10 @@ namespace MWBase /// @result pointer to the object and/or node the given node is currently pointing at /// @Return distance to the target object, or -1 if no object was targeted / in range - virtual float getTargetObject(MWRender::RayResult& result, osg::Transform* pointer) = 0; - virtual float getTargetObject(MWRender::RayResult& result, osg::Transform* pointer, float maxDistance, bool ignorePlayer) = 0; + virtual float getTargetObject(MWRender::RayResult& result, const osg::Vec3f& origin, const osg::Quat& orientation, float maxDistance, bool ignorePlayer) = 0; + + virtual MWVR::UserPointer& getUserPointer() = 0; + virtual MWWorld::Ptr getPointerTarget() = 0; /// @Return ESM::Weapon::Type enum describing the type of weapon currently drawn by the player. virtual int getActiveWeaponType(void) = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 75e77d28e..21bdb261e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -604,7 +604,7 @@ namespace MWGui // VR mode needs to render the 3D gui if (MWBase::Environment::get().getVrMode()) - disablemask = MWRender::Mask_3DGUI | MWRender::Mask_PreCompile | MWRender::Mask_RenderToTexture; + disablemask = MWRender::Mask_Pointer | MWRender::Mask_3DGUI | MWRender::Mask_PreCompile | MWRender::Mask_RenderToTexture; if (!enable && mViewer->getCamera()->getCullMask() != disablemask) { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 526eade6c..342b4ca39 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -53,6 +53,7 @@ #ifdef USE_OPENXR #include "../mwvr/vrenvironment.hpp" #include "../mwvr/vranimation.hpp" +#include "../mwvr/vrutil.hpp" #endif namespace @@ -1601,8 +1602,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle) 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"); + MWWorld::Ptr target = MWVR::Util::getWeaponTarget().first; #else MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject(); #endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 483431823..9c50e5837 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -76,6 +76,7 @@ #ifdef USE_OPENXR #include "../mwvr/vranimation.hpp" +#include "../mwvr/vrpointer.hpp" #include "../mwvr/vrviewer.hpp" #include "../mwvr/vrenvironment.hpp" #include "../mwvr/vrcamera.hpp" @@ -206,6 +207,7 @@ namespace MWRender , mWorkQueue(workQueue) , mUnrefQueue(new SceneUtil::UnrefQueue) , mNavigator(navigator) + , mUserPointer(new MWVR::UserPointer(rootNode)) , mMinimumAmbientLuminance(0.f) , mNightEyeFactor(0.f) , mFieldOfViewOverridden(false) @@ -942,9 +944,9 @@ namespace MWRender unsigned int mask = ~0u; mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water|Mask_SimpleWater|Mask_Groundcover); if (ignorePlayer) - mask &= ~(Mask_Player); + mask &= ~(Mask_Player|Mask_Pointer); if (ignoreActors) - mask &= ~(Mask_Actor|Mask_Player); + mask &= ~(Mask_Actor|Mask_Player|Mask_Pointer); mIntersectionVisitor->setTraversalMask(mask); return mIntersectionVisitor; @@ -1022,6 +1024,8 @@ namespace MWRender notifyWorldSpaceChanged(); if (mObjectPaging) mObjectPaging->clear(); + + mUserPointer->setParent(nullptr); } MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) @@ -1062,7 +1066,7 @@ namespace MWRender void RenderingManager::renderPlayer(const MWWorld::Ptr &player) { #ifdef USE_OPENXR - MWVR::Environment::get().setPlayerAnimation(new MWVR::VRAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, false, nullptr)); + MWVR::Environment::get().setPlayerAnimation(new MWVR::VRAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, false, mUserPointer)); mPlayerAnimation = MWVR::Environment::get().getPlayerAnimation(); #else mPlayerAnimation = new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0, NpcAnimation::VM_Normal, @@ -1403,4 +1407,9 @@ namespace MWRender if (mObjectPaging) mObjectPaging->getPagedRefnums(activeGrid, out); } + + MWVR::UserPointer& RenderingManager::userPointer() + { + return *mUserPointer; + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 7002f0175..91836b8d7 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -68,6 +68,11 @@ namespace DetourNavigator struct Settings; } +namespace MWVR +{ + class UserPointer; +} + namespace MWRender { class GroundcoverUpdater; @@ -250,6 +255,8 @@ namespace MWRender bool pagingUnlockCache(); void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); + MWVR::UserPointer& userPointer(); + private: void updateProjectionMatrix(); void updateTextureFiltering(); @@ -302,6 +309,7 @@ namespace MWRender std::unique_ptr mCamera; std::unique_ptr mViewOverShoulderController; osg::Vec3f mCurrentCameraPos; + std::shared_ptr mUserPointer; osg::ref_ptr mStateUpdater; diff --git a/apps/openmw/mwrender/vismask.hpp b/apps/openmw/mwrender/vismask.hpp index 1e0091057..80bf6d6cf 100644 --- a/apps/openmw/mwrender/vismask.hpp +++ b/apps/openmw/mwrender/vismask.hpp @@ -59,6 +59,7 @@ namespace MWRender // Vr masks Mask_3DGUI = (1 << 21), + Mask_Pointer = (1 << 22) }; } diff --git a/apps/openmw/mwvr/openxrtracker.cpp b/apps/openmw/mwvr/openxrtracker.cpp index 913fbebc0..3741072dc 100644 --- a/apps/openmw/mwvr/openxrtracker.cpp +++ b/apps/openmw/mwvr/openxrtracker.cpp @@ -67,7 +67,7 @@ namespace MWVR return 0; } - VRTrackingPose OpenXRTracker::getTrackingPose(DisplayTime predictedDisplayTime, VRPath path, VRPath reference) + VRTrackingPose OpenXRTracker::getTrackingPoseImpl(DisplayTime predictedDisplayTime, VRPath path, VRPath reference) { VRTrackingPose pose; pose.status = TrackingStatus::Good; diff --git a/apps/openmw/mwvr/openxrtracker.hpp b/apps/openmw/mwvr/openxrtracker.hpp index f99930e95..c3cee6f98 100644 --- a/apps/openmw/mwvr/openxrtracker.hpp +++ b/apps/openmw/mwvr/openxrtracker.hpp @@ -22,11 +22,12 @@ namespace MWVR //! The base space used to reference everything else. void setReferenceSpace(XrSpace referenceSpace); - VRTrackingPose getTrackingPose(DisplayTime predictedDisplayTime, VRPath path, VRPath reference = 0) override; - std::vector listSupportedTrackingPosePaths() const override; void updateTracking(DisplayTime predictedDisplayTime) override; + protected: + VRTrackingPose getTrackingPoseImpl(DisplayTime predictedDisplayTime, VRPath path, VRPath reference = 0) override; + private: std::array locateViews(DisplayTime predictedDisplayTime, XrSpace reference); void locate(VRTrackingPose& pose, XrSpace space, XrSpace reference, DisplayTime predictedDisplayTime); diff --git a/apps/openmw/mwvr/vranimation.cpp b/apps/openmw/mwvr/vranimation.cpp index e07169676..01b58c12d 100644 --- a/apps/openmw/mwvr/vranimation.cpp +++ b/apps/openmw/mwvr/vranimation.cpp @@ -3,6 +3,8 @@ #include "vrviewer.hpp" #include "vrinputmanager.hpp" #include "vrcamera.hpp" +#include "vrutil.hpp" +#include "vrpointer.hpp" #include #include @@ -38,6 +40,7 @@ #include "../mwrender/camera.hpp" #include "../mwrender/renderingmanager.hpp" +#include "../mwrender/vismask.hpp" namespace MWVR { @@ -323,15 +326,15 @@ namespace MWVR VRAnimation::VRAnimation( const MWWorld::Ptr& ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem, - bool disableSounds, std::shared_ptr xrSession) + bool disableSounds, std::shared_ptr userPointer) // Note that i let it construct as 3rd person and then later update it to VM_VRFirstPerson // when the character controller updates : MWRender::NpcAnimation(ptr, parentNode, resourceSystem, disableSounds, VM_Normal, 55.f) - , mSession(xrSession) , mIndexFingerControllers{ nullptr, nullptr } // The player model needs to be pushed back a little to make sure the player's view point is naturally protruding // Pushing the camera forward instead would produce an unnatural extra movement when rotating the player model. , mModelOffset(new osg::MatrixTransform(osg::Matrix::translate(osg::Vec3(0, -15, 0)))) + , mUserPointer(userPointer) { for (int i = 0; i < 2; i++) { @@ -344,17 +347,8 @@ namespace MWVR mWeaponDirectionTransform->setUpdateCallback(new WeaponDirectionController); mModelOffset->setName("ModelOffset"); - mPointerGeometry = createPointerGeometry(); - mPointerRescale = new osg::MatrixTransform(); - mPointerRescale->addChild(mPointerGeometry); - mPointerTransform = new osg::MatrixTransform(); - mPointerTransform->addChild(mPointerRescale); - mPointerTransform->setName("Pointer Transform"); - // Morrowind's hands don't actually point forward, so we have to reorient the pointer. - mPointerTransform->setMatrix(osg::Matrix::rotate(osg::Quat(-osg::PI_2, osg::Vec3f(0, 0, 1)))); mWeaponPointerTransform = new osg::MatrixTransform(); - mWeaponPointerTransform->addChild(mPointerGeometry); mWeaponPointerTransform->setMatrix( osg::Matrix::scale(0.f, 0.f, 0.f) ); @@ -408,7 +402,7 @@ namespace MWVR if (mViewMode == VM_VRFirstPerson) { - // Hide everything other than the hands and feet. + // Hide everything other than hands removeIndividualPart(ESM::PartReferenceType::PRT_Hair); removeIndividualPart(ESM::PartReferenceType::PRT_Head); removeIndividualPart(ESM::PartReferenceType::PRT_LForearm); @@ -478,97 +472,11 @@ namespace MWVR } } - mPointerTransform->removeChild(mPointerRescale); - if (enabled) - { - mPointerTransform->addChild(mPointerRescale); - } - else - { - mPointerTarget = MWRender::RayResult{}; - } + mUserPointer->setEnabled(enabled); mFingerPointingMode = enabled; } - osg::ref_ptr VRAnimation::createPointerGeometry(void) - { - osg::ref_ptr geometry = new osg::Geometry(); - - // Create pointer geometry, which will point from the tip of the player's finger. - // The geometry will be a Four sided pyramid, with the top at the player's fingers - - osg::Vec3 vertices[]{ - {0, 0, 0}, // origin - {-1, 1, -1}, // A - {-1, 1, 1}, // B - {1, 1, 1}, // C - {1, 1, -1}, // D - }; - - osg::Vec4 colors[]{ - osg::Vec4(1.0f, 0.0f, 0.0f, 0.0f), - osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f), - osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f), - osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f), - osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f), - }; - - const int O = 0; - const int A = 1; - const int B = 2; - const int C = 3; - const int D = 4; - - const int triangles[] = - { - A,D,B, - B,D,C, - O,D,A, - O,C,D, - O,B,C, - O,A,B, - }; - int numVertices = sizeof(triangles) / sizeof(*triangles); - osg::ref_ptr vertexArray = new osg::Vec3Array(numVertices); - osg::ref_ptr colorArray = new osg::Vec4Array(numVertices); - for (int i = 0; i < numVertices; i++) - { - (*vertexArray)[i] = vertices[triangles[i]]; - (*colorArray)[i] = colors[triangles[i]]; - } - - geometry->setVertexArray(vertexArray); - geometry->setColorArray(colorArray, osg::Array::BIND_PER_VERTEX); - geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, numVertices)); - geometry->setSupportsDisplayList(false); - geometry->setDataVariance(osg::Object::STATIC); - - auto stateset = geometry->getOrCreateStateSet(); - stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - stateset->setMode(GL_BLEND, osg::StateAttribute::ON); - stateset->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - osg::ref_ptr fog(new osg::Fog); - fog->setStart(10000000); - fog->setEnd(10000000); - stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); - - osg::ref_ptr lightmodel = new osg::LightModel; - lightmodel->setAmbientIntensity(osg::Vec4(1.0, 1.0, 1.0, 1.0)); - stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON); - SceneUtil::ShadowManager::disableShadowsForStateSet(stateset); - - osg::ref_ptr material = new osg::Material; - material->setColorMode(osg::Material::ColorMode::AMBIENT_AND_DIFFUSE); - stateset->setAttributeAndModes(material, osg::StateAttribute::ON); - - mResourceSystem->getSceneManager()->recreateShaders(geometry); - mSkeleton->setIsTracked(true); - - return geometry; - } - float VRAnimation::getVelocity(const std::string& groupname) const { return 0.0f; @@ -582,7 +490,7 @@ namespace MWVR if (mSkeleton) mSkeleton->markBoneMatriceDirty(); - updatePointerTarget(); + mUserPointer->updatePointerTarget(); } osg::Vec3f VRAnimation::runAnimation(float timepassed) @@ -624,8 +532,7 @@ namespace MWVR auto finger = mNodeMap.find("bip01 r finger11"); if (finger != mNodeMap.end()) { - finger->second->removeChild(mPointerTransform); - finger->second->addChild(mPointerTransform); + mUserPointer->setParent(finger->second); } mSkeleton->setIsTracked(true); } @@ -638,56 +545,8 @@ namespace MWVR NpcAnimation::setAccurateAiming(false); } - bool VRAnimation::canPlaceObject() - { - const float maxDist = 200.f; - if (mPointerTarget.mHit) - { - // check if the wanted position is on a flat surface, and not e.g. against a vertical wall - if (std::acos((mPointerTarget.mHitNormalWorld / mPointerTarget.mHitNormalWorld.length()) * osg::Vec3f(0, 0, 1)) >= osg::DegreesToRadians(30.f)) - return false; - - return true; - } - - return false; - } - - 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(); - if (world) - { - mPointerRescale->setMatrix(osg::Matrix::scale(1, 1, 1)); - mDistanceToPointerTarget = world->getTargetObject(mPointerTarget, mPointerTransform); - - if (mDistanceToPointerTarget >= 0) - mPointerRescale->setMatrix(osg::Matrix::scale(0.25f, mDistanceToPointerTarget, 0.25f)); - else - mPointerRescale->setMatrix(osg::Matrix::scale(0.25f, 10000.f, 0.25f)); - } - } - } diff --git a/apps/openmw/mwvr/vranimation.hpp b/apps/openmw/mwvr/vranimation.hpp index 84fec3bbc..641ba2cfe 100644 --- a/apps/openmw/mwvr/vranimation.hpp +++ b/apps/openmw/mwvr/vranimation.hpp @@ -9,10 +9,10 @@ namespace MWVR { - class HandController; class FingerController; class TrackingController; + class UserPointer; /// Subclassing NpcAnimation to implement VR related behaviour class VRAnimation : public MWRender::NpcAnimation, public VRTrackingListener @@ -31,7 +31,7 @@ namespace MWVR * @param xrSession The XR session that shall be used to track limbs */ VRAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem, - bool disableSounds, std::shared_ptr xrSession); + bool disableSounds, std::shared_ptr userPointer); virtual ~VRAnimation(); /// Overridden to always be false @@ -55,23 +55,10 @@ namespace MWVR /// @return Whether animation is currently in finger pointing mode bool fingerPointingMode() const { return mFingerPointingMode; } - /// @return true if it is possible to place on object where the player is currently pointing - bool canPlaceObject(); - - /// @return pointer to the object the player's melee weapon is currently intersecting. - const MWRender::RayResult& getPointerTarget() const; - - /// Update what object this vr animation is currently pointing at. - void updatePointerTarget(); - - /// @return whatever ref is the current pointer target, if any - MWWorld::Ptr getTarget(const std::string& directorNode); - /// @return world transform that yields the position and orientation of the current weapon osg::Matrix getWeaponTransformMatrix() const; protected: - osg::ref_ptr createPointerGeometry(void); float getVelocity(const std::string& groupname) const override; @@ -85,13 +72,9 @@ namespace MWVR osg::ref_ptr mModelOffset; bool mFingerPointingMode{ false }; - osg::ref_ptr mPointerGeometry{ nullptr }; - osg::ref_ptr mPointerRescale{ nullptr }; - osg::ref_ptr mPointerTransform{ nullptr }; + std::shared_ptr mUserPointer; osg::ref_ptr mWeaponDirectionTransform{ nullptr }; osg::ref_ptr mWeaponPointerTransform{ nullptr }; - MWRender::RayResult mPointerTarget{}; - float mDistanceToPointerTarget{ -1.f }; }; } diff --git a/apps/openmw/mwvr/vrgui.cpp b/apps/openmw/mwvr/vrgui.cpp index 1ad15f989..5299ed3f7 100644 --- a/apps/openmw/mwvr/vrgui.cpp +++ b/apps/openmw/mwvr/vrgui.cpp @@ -2,11 +2,12 @@ #include +#include "vranimation.hpp" #include "vrenvironment.hpp" +#include "vrpointer.hpp" #include "vrsession.hpp" -#include "openxrmanagerimpl.hpp" #include "openxrinput.hpp" -#include "vranimation.hpp" +#include "openxrmanagerimpl.hpp" #include #include @@ -760,22 +761,26 @@ namespace MWVR bool VRGUIManager::updateFocus() { - auto* anim = MWVR::Environment::get().getPlayerAnimation(); - if (anim && anim->getPointerTarget().mHit) + auto* world = MWBase::Environment::get().getWorld(); + if (world) { - std::shared_ptr newFocusLayer = nullptr; - auto* node = anim->getPointerTarget().mHitNode; - if (node->getName() == "VRGUILayer") - { - VRGUILayerUserData* userData = static_cast(node->getUserData()); - newFocusLayer = userData->mLayer.lock(); - } - - if (newFocusLayer && newFocusLayer->mLayerName != "Notification") + auto& pointer = world->getUserPointer(); + if (pointer.getPointerTarget().mHit) { - setFocusLayer(newFocusLayer.get()); - computeGuiCursor(anim->getPointerTarget().mHitPointLocal); - return true; + std::shared_ptr newFocusLayer = nullptr; + auto* node = pointer.getPointerTarget().mHitNode; + if (node->getName() == "VRGUILayer") + { + VRGUILayerUserData* userData = static_cast(node->getUserData()); + newFocusLayer = userData->mLayer.lock(); + } + + if (newFocusLayer && newFocusLayer->mLayerName != "Notification") + { + setFocusLayer(newFocusLayer.get()); + computeGuiCursor(pointer.getPointerTarget().mHitPointLocal); + return true; + } } } return false; @@ -802,8 +807,11 @@ namespace MWVR mFocusLayer = layer; if (mFocusLayer) { - Log(Debug::Verbose) << "Set focus layer to " << mFocusLayer->mWidgets.front()->mMainWidget->getLayer()->getName(); - setPick(mFocusLayer->mWidgets.front(), true); + if (!mFocusLayer->mWidgets.empty()) + { + Log(Debug::Verbose) << "Set focus layer to " << mFocusLayer->mWidgets.front()->mMainWidget->getLayer()->getName(); + setPick(mFocusLayer->mWidgets.front(), true); + } } else { @@ -977,7 +985,7 @@ namespace MWVR mStationaryPath = tm->stringToVRPath("/ui/input/stationary/pose"); } - VRTrackingPose VRGUITracking::getTrackingPose(DisplayTime predictedDisplayTime, VRPath path, VRPath reference) + VRTrackingPose VRGUITracking::getTrackingPoseImpl(DisplayTime predictedDisplayTime, VRPath path, VRPath reference) { if (path == mStationaryPath) return mStationaryPose; diff --git a/apps/openmw/mwvr/vrgui.hpp b/apps/openmw/mwvr/vrgui.hpp index de8fdcce7..bee9834a6 100644 --- a/apps/openmw/mwvr/vrgui.hpp +++ b/apps/openmw/mwvr/vrgui.hpp @@ -72,11 +72,13 @@ namespace MWVR public: VRGUITracking(const std::string& source); - virtual VRTrackingPose getTrackingPose(DisplayTime predictedDisplayTime, VRPath path, VRPath reference = 0) override; virtual std::vector listSupportedTrackingPosePaths() const override; virtual void updateTracking(DisplayTime predictedDisplayTime) override; void resetStationaryPose(); + protected: + virtual VRTrackingPose getTrackingPoseImpl(DisplayTime predictedDisplayTime, VRPath path, VRPath reference = 0) override; + private: VRPath mStationaryPath = 0; VRPath mHeadPath = 0; diff --git a/apps/openmw/mwvr/vrinputmanager.cpp b/apps/openmw/mwvr/vrinputmanager.cpp index 0f21b6344..dd38c39ca 100644 --- a/apps/openmw/mwvr/vrinputmanager.cpp +++ b/apps/openmw/mwvr/vrinputmanager.cpp @@ -1,11 +1,12 @@ #include "vrinputmanager.hpp" -#include "vrviewer.hpp" +#include "vranimation.hpp" #include "vrcamera.hpp" +#include "vrenvironment.hpp" #include "vrgui.hpp" -#include "vranimation.hpp" +#include "vrpointer.hpp" +#include "vrviewer.hpp" #include "openxrinput.hpp" -#include "vrenvironment.hpp" #include "openxrmanager.hpp" #include "openxrmanagerimpl.hpp" #include "openxraction.hpp" @@ -86,11 +87,11 @@ namespace MWVR virtual MWWorld::Ptr copyItem(const MWGui::ItemStack& item, size_t count, bool /*allowAutoEquip*/) { MWBase::World* world = MWBase::Environment::get().getWorld(); - MWVR::VRAnimation* anim = MWVR::Environment::get().getPlayerAnimation(); + auto& pointer = world->getUserPointer(); MWWorld::Ptr dropped; - if (anim->canPlaceObject()) - dropped = world->placeObject(item.mBase, anim->getPointerTarget(), count); + if (pointer.canPlaceObject()) + dropped = world->placeObject(item.mBase, pointer.getPointerTarget(), count); else dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count); dropped.getCellRef().setOwner(""); @@ -112,11 +113,11 @@ namespace MWVR void VRInputManager::pointActivation(bool onPress) { auto* world = MWBase::Environment::get().getWorld(); - auto* anim = MWVR::Environment::get().getPlayerAnimation(); - if (world && anim && anim->getPointerTarget().mHit) + auto& pointer = world->getUserPointer(); + if (world && pointer.getPointerTarget().mHit) { - auto* node = anim->getPointerTarget().mHitNode; - MWWorld::Ptr ptr = anim->getPointerTarget().mHitObject; + auto* node = pointer.getPointerTarget().mHitNode; + MWWorld::Ptr ptr = pointer.getPointerTarget().mHitObject; auto wm = MWBase::Environment::get().getWindowManager(); auto& dnd = wm->getDragAndDrop(); diff --git a/apps/openmw/mwvr/vrpointer.cpp b/apps/openmw/mwvr/vrpointer.cpp new file mode 100644 index 000000000..7d5df7b70 --- /dev/null +++ b/apps/openmw/mwvr/vrpointer.cpp @@ -0,0 +1,203 @@ +#include "vrpointer.hpp" +#include "vrutil.hpp" +#include "vrenvironment.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwrender/renderingmanager.hpp" +#include "../mwrender/vismask.hpp" + +namespace MWVR +{ + UserPointer::UserPointer(osg::Group* root) + : mRoot(root) + { + mPointerGeometry = createPointerGeometry(); + mPointerRescale = new osg::MatrixTransform(); + mPointerRescale->addChild(mPointerGeometry); + mPointerTransform = new osg::MatrixTransform(); + mPointerTransform->addChild(mPointerRescale); + mPointerTransform->setName("Pointer Transform"); + mPointerTransform->setNodeMask(MWRender::VisMask::Mask_Pointer); + + auto tm = MWVR::Environment::get().getTrackingManager(); + tm->bind(this, "pcworld"); + mHandPath = tm->stringToVRPath("/user/hand/right/input/aim/pose"); + + setEnabled(true); + } + + UserPointer::~UserPointer() + { + } + + void UserPointer::setParent(osg::Group* group) + { + bool enabled = mEnabled; + setEnabled(false); + mParent = group; + setEnabled(enabled); + } + + void UserPointer::setEnabled(bool enabled) + { + mRoot->removeChild(mPointerTransform); + if (mParent) + { + mParent->removeChild(mPointerTransform); + if (enabled) + { + mParent->addChild(mPointerTransform); + // Morrowind's hands don't actually point forward, so we have to reorient the pointer. + mPointerTransform->setMatrix(osg::Matrix::rotate(osg::Quat(-osg::PI_2, osg::Vec3f(0, 0, 1)))); + } + } + else if(enabled) + { + mRoot->addChild(mPointerTransform); + } + mEnabled = enabled; + } + + void UserPointer::onTrackingUpdated(VRTrackingSource& source, DisplayTime predictedDisplayTime) + { + // If no parent is set, then the actor is currently unloaded + // And we need to point directly from tracking data and the root + if (!mParent) + { + auto tp = source.getTrackingPose(predictedDisplayTime, mHandPath, 0); + osg::Matrix worldReference = osg::Matrix::identity(); + worldReference.preMultTranslate(tp.pose.position); + worldReference.preMultRotate(tp.pose.orientation); + mPointerTransform->setMatrix(worldReference); + updatePointerTarget(); + + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(mPointerGeometry); + } + } + + const MWRender::RayResult& UserPointer::getPointerTarget() const + { + return mPointerTarget; + } + + bool UserPointer::canPlaceObject() const + { + return mCanPlaceObject; + } + + void UserPointer::updatePointerTarget() + { + auto* world = MWBase::Environment::get().getWorld(); + if (world) + { + mPointerRescale->setMatrix(osg::Matrix::scale(1, 1, 1)); + + //mDistanceToPointerTarget = world->getTargetObject(mPointerTarget, mPointerTransform); + //osg::computeLocalToWorld(mPointerTransform->getParentalNodePaths()[0]); + mDistanceToPointerTarget = Util::getPoseTarget(mPointerTarget, Util::getNodePose(mPointerTransform), true); + + mCanPlaceObject = false; + if (mPointerTarget.mHit) + { + // check if the wanted position is on a flat surface, and not e.g. against a vertical wall + mCanPlaceObject = !(std::acos((mPointerTarget.mHitNormalWorld / mPointerTarget.mHitNormalWorld.length()) * osg::Vec3f(0, 0, 1)) >= osg::DegreesToRadians(30.f)); + } + + if (mDistanceToPointerTarget > 0.f) + mPointerRescale->setMatrix(osg::Matrix::scale(0.25f, mDistanceToPointerTarget, 0.25f)); + else + mPointerRescale->setMatrix(osg::Matrix::scale(0.25f, 10000.f, 0.25f)); + } + } + + osg::ref_ptr UserPointer::createPointerGeometry() + { + osg::ref_ptr geometry = new osg::Geometry(); + + // Create pointer geometry, which will point from the tip of the player's finger. + // The geometry will be a Four sided pyramid, with the top at the player's fingers + + osg::Vec3 vertices[]{ + {0, 0, 0}, // origin + {-1, 1, -1}, // A + {-1, 1, 1}, // B + {1, 1, 1}, // C + {1, 1, -1}, // D + }; + + osg::Vec4 colors[]{ + osg::Vec4(1.0f, 0.0f, 0.0f, 0.0f), + osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f), + osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f), + osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f), + osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f), + }; + + const int O = 0; + const int A = 1; + const int B = 2; + const int C = 3; + const int D = 4; + + const int triangles[] = + { + A,D,B, + B,D,C, + O,D,A, + O,C,D, + O,B,C, + O,A,B, + }; + int numVertices = sizeof(triangles) / sizeof(*triangles); + osg::ref_ptr vertexArray = new osg::Vec3Array(numVertices); + osg::ref_ptr colorArray = new osg::Vec4Array(numVertices); + for (int i = 0; i < numVertices; i++) + { + (*vertexArray)[i] = vertices[triangles[i]]; + (*colorArray)[i] = colors[triangles[i]]; + } + + geometry->setVertexArray(vertexArray); + geometry->setColorArray(colorArray, osg::Array::BIND_PER_VERTEX); + geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, numVertices)); + geometry->setSupportsDisplayList(false); + geometry->setDataVariance(osg::Object::STATIC); + + auto stateset = geometry->getOrCreateStateSet(); + stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); + stateset->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + osg::ref_ptr fog(new osg::Fog); + fog->setStart(10000000); + fog->setEnd(10000000); + stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); + + osg::ref_ptr lightmodel = new osg::LightModel; + lightmodel->setAmbientIntensity(osg::Vec4(1.0, 1.0, 1.0, 1.0)); + stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON); + SceneUtil::ShadowManager::disableShadowsForStateSet(stateset); + + osg::ref_ptr material = new osg::Material; + material->setColorMode(osg::Material::ColorMode::AMBIENT_AND_DIFFUSE); + stateset->setAttributeAndModes(material, osg::StateAttribute::ON); + + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(geometry); + + return geometry; + } + +} diff --git a/apps/openmw/mwvr/vrpointer.hpp b/apps/openmw/mwvr/vrpointer.hpp new file mode 100644 index 000000000..bf285730d --- /dev/null +++ b/apps/openmw/mwvr/vrpointer.hpp @@ -0,0 +1,45 @@ +#ifndef MWVR_POINTER_H +#define MWVR_POINTER_H + +#include "../mwrender/npcanimation.hpp" +#include "../mwrender/renderingmanager.hpp" +#include "openxrmanager.hpp" +#include "vrsession.hpp" +#include "vrtracking.hpp" + +namespace MWVR +{ + //! Controls the beam used to target/select objects. + class UserPointer : public VRTrackingListener + { + public: + UserPointer(osg::Group* root); + ~UserPointer(); + + void updatePointerTarget(); + const MWRender::RayResult& getPointerTarget() const; + bool canPlaceObject() const; + void setParent(osg::Group* group); + void setEnabled(bool enabled); + protected: + void onTrackingUpdated(VRTrackingSource& source, DisplayTime predictedDisplayTime) override; + + private: + osg::ref_ptr createPointerGeometry(); + + osg::ref_ptr mPointerGeometry{ nullptr }; + osg::ref_ptr mPointerRescale{ nullptr }; + osg::ref_ptr mPointerTransform{ nullptr }; + + osg::ref_ptr mParent{ nullptr }; + osg::ref_ptr mRoot{ nullptr }; + VRPath mHandPath; + + bool mEnabled; + MWRender::RayResult mPointerTarget{}; + float mDistanceToPointerTarget{ -1.f }; + bool mCanPlaceObject{ false }; + }; +} + +#endif diff --git a/apps/openmw/mwvr/vrtracking.cpp b/apps/openmw/mwvr/vrtracking.cpp index b5db6c2b8..59f22ff9c 100644 --- a/apps/openmw/mwvr/vrtracking.cpp +++ b/apps/openmw/mwvr/vrtracking.cpp @@ -201,6 +201,28 @@ namespace MWVR Environment::get().getTrackingManager()->unregisterTrackingSource(this); } + VRTrackingPose VRTrackingSource::getTrackingPose(DisplayTime predictedDisplayTime, VRPath path, VRPath reference) + { + auto it = mCache.find(std::pair(path, reference)); + + if (it == mCache.end()) + { + mCache[std::pair(path, reference)] = getTrackingPoseImpl(predictedDisplayTime, path, reference); + mCache[std::pair(path, reference)].time = predictedDisplayTime; + } + + if (predictedDisplayTime <= it->second.time) + return it->second; + + auto tp = getTrackingPoseImpl(predictedDisplayTime, path, reference); + tp.time = predictedDisplayTime; + if (!tp.status) + tp.pose = it->second.pose; + it->second = tp; + + return tp; + } + bool VRTrackingSource::availablePosesChanged() const { return mAvailablePosesChanged; @@ -211,6 +233,11 @@ namespace MWVR mAvailablePosesChanged = false; } + void VRTrackingSource::clearCache() + { + mCache.clear(); + } + void VRTrackingSource::notifyAvailablePosesChanged() { mAvailablePosesChanged = true; @@ -262,7 +289,7 @@ namespace MWVR } } - VRTrackingPose VRTrackingToWorldBinding::getTrackingPose(DisplayTime predictedDisplayTime, VRPath path, VRPath reference) + VRTrackingPose VRTrackingToWorldBinding::getTrackingPoseImpl(DisplayTime predictedDisplayTime, VRPath path, VRPath reference) { auto tp = mSource->getTrackingPose(predictedDisplayTime, path, reference); tp.pose.position *= Constants::UnitsPerMeter; diff --git a/apps/openmw/mwvr/vrtracking.hpp b/apps/openmw/mwvr/vrtracking.hpp index c4d6a7d22..31b804b38 100644 --- a/apps/openmw/mwvr/vrtracking.hpp +++ b/apps/openmw/mwvr/vrtracking.hpp @@ -39,6 +39,7 @@ namespace MWVR { TrackingStatus status = TrackingStatus::Unknown; //!< State of the prediction. Pose pose = {}; //!< The predicted pose. + DisplayTime time; //!< The time for which the pose was predicted. }; //! Source for tracking data. Converts paths to poses at predicted times. @@ -59,12 +60,12 @@ namespace MWVR //! @brief Predicted pose of the given path at the predicted time //! - //! \arg predictedDisplayTime[in] Time to predict. This is normally the predicted display time. + //! \arg predictedDisplayTime[in] Time to predict. This is normally the predicted display time. If time is 0, the last pose that was predicted is returned. //! \arg path[in] path of the pose requested. Should match an available pose path. //! \arg reference[in] path of the pose to use as reference. If 0, pose is referenced to the VR stage. //! //! \return A structure describing a pose and the tracking status. - virtual VRTrackingPose getTrackingPose(DisplayTime predictedDisplayTime, VRPath path, VRPath reference = 0) = 0; + VRTrackingPose getTrackingPose(DisplayTime predictedDisplayTime, VRPath path, VRPath reference = 0); //! List currently supported tracking paths. virtual std::vector listSupportedTrackingPosePaths() const = 0; @@ -79,7 +80,13 @@ namespace MWVR //! \arg predictedDisplayTime [in] the predicted display time. The pose shall be predicted for this time based on current tracking data. virtual void updateTracking(DisplayTime predictedDisplayTime) = 0; + void clearCache(); + protected: + virtual VRTrackingPose getTrackingPoseImpl(DisplayTime predictedDisplayTime, VRPath path, VRPath reference = 0) = 0; + + std::map, VRTrackingPose> mCache; + void notifyAvailablePosesChanged(); bool mAvailablePosesChanged = true; @@ -119,7 +126,7 @@ namespace MWVR protected: //! Fetches a pose from the source, and then aligns it with the game world if the reference is 0 (stage). - VRTrackingPose getTrackingPose(DisplayTime predictedDisplayTime, VRPath path, VRPath movementReference = 0) override; + VRTrackingPose getTrackingPoseImpl(DisplayTime predictedDisplayTime, VRPath path, VRPath movementReference = 0) override; //! List currently supported tracking paths. std::vector listSupportedTrackingPosePaths() const override; diff --git a/apps/openmw/mwvr/vrutil.cpp b/apps/openmw/mwvr/vrutil.cpp new file mode 100644 index 000000000..311c27fd6 --- /dev/null +++ b/apps/openmw/mwvr/vrutil.cpp @@ -0,0 +1,80 @@ +#include "vrutil.hpp" +#include "vrenvironment.hpp" +#include "vrtracking.hpp" +#include "vranimation.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/class.hpp" +#include "../mwrender/renderingmanager.hpp" + +#include "osg/Transform" + +namespace MWVR +{ + namespace Util + { + std::pair getHitContact(float distance, std::vector& targets) + { + return std::pair(); + } + + std::pair getTouchTarget() + { + MWRender::RayResult result; + auto* tm = Environment::get().getTrackingManager(); + VRPath rightHandPath = tm->stringToVRPath("/user/hand/right/input/aim/pose"); + auto* source = tm->getSource("pcworld"); + auto distance = getPoseTarget(result, source->getTrackingPose(0, rightHandPath).pose, true); + return std::pair(result.mHitObject, distance); + } + + std::pair getWeaponTarget() + { + auto* anim = MWVR::Environment::get().getPlayerAnimation(); + + MWRender::RayResult result; + auto distance = getPoseTarget(result, getNodePose(anim->getNode("weapon bone")), false); + return std::pair(result.mHitObject, distance); + } + + float getPoseTarget(MWRender::RayResult& result, const Pose& pose, bool allowTelekinesis) + { + auto* wm = MWBase::Environment::get().getWindowManager(); + auto* world = MWBase::Environment::get().getWorld(); + + if (wm->isGuiMode() && wm->isConsoleMode()) + return world->getTargetObject(result, pose.position, pose.orientation, world->getMaxActivationDistance() * 50, true); + else + { + float activationDistance = 0.f; + if (allowTelekinesis) + activationDistance = world->getActivationDistancePlusTelekinesis(); + else + activationDistance = world->getMaxActivationDistance(); + + auto distance = world->getTargetObject(result, pose.position, pose.orientation, world->getMaxActivationDistance(), true); + + if (!result.mHitObject.isEmpty() && !result.mHitObject.getClass().allowTelekinesis(result.mHitObject) + && distance > world->getMaxActivationDistance() && !MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + result.mHit = false; + result.mHitObject = nullptr; + distance = 0.f; + }; + return distance; + } + } + + Pose getNodePose(const osg::Node* node) + { + osg::Matrix worldMatrix = osg::computeLocalToWorld(node->getParentalNodePaths()[0]); + Pose pose; + pose.position = worldMatrix.getTrans(); + pose.orientation = worldMatrix.getRotate(); + return pose; + } + } +} diff --git a/apps/openmw/mwvr/vrutil.hpp b/apps/openmw/mwvr/vrutil.hpp new file mode 100644 index 000000000..ed4a12cd0 --- /dev/null +++ b/apps/openmw/mwvr/vrutil.hpp @@ -0,0 +1,24 @@ +#ifndef VR_UTIL_HPP +#define VR_UTIL_HPP + +#include "../mwworld/ptr.hpp" + +#include "vrtypes.hpp" + +namespace MWRender +{ + struct RayResult; +} + +namespace MWVR +{ + namespace Util { + std::pair getTouchTarget(); + std::pair getWeaponTarget(); + float getPoseTarget(MWRender::RayResult& result, const Pose& pose, bool allowTelekinesis); + Pose getNodePose(const osg::Node* node); + } + +} + +#endif diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f22a5af7d..9fc326cf3 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -76,9 +76,11 @@ #include "esmloader.hpp" #ifdef USE_OPENXR -#include "../mwvr/vrenvironment.hpp" #include "../mwvr/vranimation.hpp" +#include "../mwvr/vrenvironment.hpp" #include "../mwvr/vrinputmanager.hpp" +#include "../mwvr/vrpointer.hpp" +#include "../mwvr/vrutil.hpp" #endif namespace @@ -1027,13 +1029,7 @@ 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->getPointerTarget().mHit) - return anim->getPointerTarget().mHitObject; - else - return MWWorld::Ptr(); + return getPointerTarget(); #endif MWWorld::Ptr facedObject; @@ -3105,7 +3101,10 @@ namespace MWWorld // for player we can take faced object first MWWorld::Ptr target; -#ifndef USE_OPENXR +#ifdef USE_OPENXR + if (actor == MWMechanics::getPlayer()) + target = MWVR::Util::getTouchTarget().first; +#else // Does not apply to VR if (actor == MWMechanics::getPlayer()) target = getFacedObject(); @@ -3154,6 +3153,8 @@ namespace MWWorld orient = worldMatrix.getRotate(); } #endif + Log(Debug::Verbose) << "Origin: " << origin; + Log(Debug::Verbose) << "Orient: " << orient; osg::Vec3f direction = orient * osg::Vec3f(0,1,0); float distance = getMaxActivationDistance(); @@ -4030,35 +4031,14 @@ namespace MWWorld return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal); } - float World::getTargetObject(MWRender::RayResult& result, osg::Transform* pointer) - { - result = {}; - result.mHit = false; - - auto* windowManager = MWBase::Environment::get().getWindowManager(); - - if (windowManager->isGuiMode() && windowManager->isConsoleMode()) - { - return getTargetObject(result, pointer, getMaxActivationDistance() * 50, true); - } - else - { - float maxDistance = getActivationDistancePlusTelekinesis(); - MWRender::RayResult rayToObject{}; - float distance = getTargetObject(rayToObject, pointer, maxDistance, true); - auto ptr = rayToObject.mHitObject; - if (!ptr.isEmpty() && !ptr.getClass().allowTelekinesis(ptr) - && distance > getMaxActivationDistance() && !MWBase::Environment::get().getWindowManager()->isGuiMode()) - return -1.f; - result = rayToObject; - return distance; - } - return -1.f; - } - - float World::getTargetObject(MWRender::RayResult& result, osg::Transform* pointer, float maxDistance, bool ignorePlayer) + float World::getTargetObject(MWRender::RayResult& result, const osg::Vec3f& origin, const osg::Quat& orientation, float maxDistance, bool ignorePlayer) { - result = mRendering->castRay(pointer, maxDistance, ignorePlayer, false); + osg::Vec3f direction = orientation * osg::Vec3f(0, 1, 0); + direction.normalize(); + osg::Vec3f end = origin + direction * maxDistance; + result = mRendering->castRay(origin, end, ignorePlayer); + if(!result.mHit) + return 0.f; MWWorld::Ptr facedObject = result.mHitObject; if (facedObject.isEmpty() && result.mHitRefnum.hasContentFile()) @@ -4071,9 +4051,17 @@ namespace MWWorld } result.mHitObject = facedObject; - if(result.mHit) - return result.mRatio * maxDistance; - return -1.f; + return result.mRatio * maxDistance; + } + + MWVR::UserPointer& World::getUserPointer() + { + return mRendering->userPointer(); + } + + MWWorld::Ptr World::getPointerTarget() + { + return getUserPointer().getPointerTarget().mHitObject; } MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& object, const MWRender::RayResult& ray, int amount) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 37be8174e..3ab25687a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -177,7 +177,7 @@ namespace MWWorld const std::vector& content, const std::vector& groundcover, ContentLoader& contentLoader); float feetToGameUnits(float feet); - float getActivationDistancePlusTelekinesis(); + float getActivationDistancePlusTelekinesis() override; MWWorld::ConstPtr getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id ); MWWorld::ConstPtr getClosestMarkerFromExteriorPosition( const osg::Vec3f& worldPos, const std::string &id ); @@ -742,10 +742,12 @@ namespace MWWorld bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override; - /// @result pointer to the object and/or node the given node is currently pointing at + /// Intersects the scene from the origin, in the specified orientation and distance, storing the %result in the result structure. /// @Return distance to the target object, or -1 if no object was targeted / in range - float getTargetObject(MWRender::RayResult& result, osg::Transform* pointer) override; - float getTargetObject(MWRender::RayResult& result, osg::Transform* pointer, float maxDistance, bool ignorePlayer) override; + float getTargetObject(MWRender::RayResult& result, const osg::Vec3f& origin, const osg::Quat& orientation, float maxDistance, bool ignorePlayer) override; + + MWVR::UserPointer& getUserPointer() override; + MWWorld::Ptr getPointerTarget() override; MWWorld::Ptr placeObject(const MWWorld::ConstPtr& object, const MWRender::RayResult& ray, int amount) override; ///< copy and place an object into the gameworld based on the given intersection