Fix #109 also fixes lack of motion controller interaction during main menu and load game.

pull/615/head
madsbuvi 4 years ago
parent fb4a1ebd46
commit abee6ca841

@ -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

@ -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)

@ -91,6 +91,11 @@ namespace MWWorld
typedef std::vector<std::pair<MWWorld::Ptr,MWMechanics::Movement> > 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;

@ -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)
{

@ -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

@ -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;
}
}

@ -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<ESM::RefNum> &out);
MWVR::UserPointer& userPointer();
private:
void updateProjectionMatrix();
void updateTextureFiltering();
@ -302,6 +309,7 @@ namespace MWRender
std::unique_ptr<Camera> mCamera;
std::unique_ptr<ViewOverShoulderController> mViewOverShoulderController;
osg::Vec3f mCurrentCameraPos;
std::shared_ptr<MWVR::UserPointer> mUserPointer;
osg::ref_ptr<StateUpdater> mStateUpdater;

@ -59,6 +59,7 @@ namespace MWRender
// Vr masks
Mask_3DGUI = (1 << 21),
Mask_Pointer = (1 << 22)
};
}

@ -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;

@ -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<VRPath> listSupportedTrackingPosePaths() const override;
void updateTracking(DisplayTime predictedDisplayTime) override;
protected:
VRTrackingPose getTrackingPoseImpl(DisplayTime predictedDisplayTime, VRPath path, VRPath reference = 0) override;
private:
std::array<View, 2> locateViews(DisplayTime predictedDisplayTime, XrSpace reference);
void locate(VRTrackingPose& pose, XrSpace space, XrSpace reference, DisplayTime predictedDisplayTime);

@ -3,6 +3,8 @@
#include "vrviewer.hpp"
#include "vrinputmanager.hpp"
#include "vrcamera.hpp"
#include "vrutil.hpp"
#include "vrpointer.hpp"
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
@ -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<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
bool disableSounds, std::shared_ptr<VRSession> xrSession)
bool disableSounds, std::shared_ptr<UserPointer> 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<osg::Geometry> VRAnimation::createPointerGeometry(void)
{
osg::ref_ptr<osg::Geometry> 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<osg::Vec3Array> vertexArray = new osg::Vec3Array(numVertices);
osg::ref_ptr<osg::Vec4Array> 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<osg::Fog> fog(new osg::Fog);
fog->setStart(10000000);
fog->setEnd(10000000);
stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
osg::ref_ptr<osg::LightModel> 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<osg::Material> 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));
}
}
}

@ -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<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
bool disableSounds, std::shared_ptr<VRSession> xrSession);
bool disableSounds, std::shared_ptr<UserPointer> 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<osg::Geometry> createPointerGeometry(void);
float getVelocity(const std::string& groupname) const override;
@ -85,13 +72,9 @@ namespace MWVR
osg::ref_ptr<osg::MatrixTransform> mModelOffset;
bool mFingerPointingMode{ false };
osg::ref_ptr<osg::Geometry> mPointerGeometry{ nullptr };
osg::ref_ptr<osg::MatrixTransform> mPointerRescale{ nullptr };
osg::ref_ptr<osg::MatrixTransform> mPointerTransform{ nullptr };
std::shared_ptr<UserPointer> mUserPointer;
osg::ref_ptr<osg::MatrixTransform> mWeaponDirectionTransform{ nullptr };
osg::ref_ptr<osg::MatrixTransform> mWeaponPointerTransform{ nullptr };
MWRender::RayResult mPointerTarget{};
float mDistanceToPointerTarget{ -1.f };
};
}

@ -2,11 +2,12 @@
#include <cmath>
#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 <osg/Texture2D>
#include <osg/ClipNode>
@ -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<VRGUILayer> newFocusLayer = nullptr;
auto* node = anim->getPointerTarget().mHitNode;
if (node->getName() == "VRGUILayer")
{
VRGUILayerUserData* userData = static_cast<VRGUILayerUserData*>(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<VRGUILayer> newFocusLayer = nullptr;
auto* node = pointer.getPointerTarget().mHitNode;
if (node->getName() == "VRGUILayer")
{
VRGUILayerUserData* userData = static_cast<VRGUILayerUserData*>(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;

@ -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<VRPath> 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;

@ -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();

@ -0,0 +1,203 @@
#include "vrpointer.hpp"
#include "vrutil.hpp"
#include "vrenvironment.hpp"
#include <osg/MatrixTransform>
#include <osg/Drawable>
#include <osg/BlendFunc>
#include <osg/Fog>
#include <osg/LightModel>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/shadow.hpp>
#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<osg::Geometry> UserPointer::createPointerGeometry()
{
osg::ref_ptr<osg::Geometry> 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<osg::Vec3Array> vertexArray = new osg::Vec3Array(numVertices);
osg::ref_ptr<osg::Vec4Array> 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<osg::Fog> fog(new osg::Fog);
fog->setStart(10000000);
fog->setEnd(10000000);
stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
osg::ref_ptr<osg::LightModel> 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<osg::Material> 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;
}
}

@ -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<osg::Geometry> createPointerGeometry();
osg::ref_ptr<osg::Geometry> mPointerGeometry{ nullptr };
osg::ref_ptr<osg::MatrixTransform> mPointerRescale{ nullptr };
osg::ref_ptr<osg::MatrixTransform> mPointerTransform{ nullptr };
osg::ref_ptr<osg::Group> mParent{ nullptr };
osg::ref_ptr<osg::Group> mRoot{ nullptr };
VRPath mHandPath;
bool mEnabled;
MWRender::RayResult mPointerTarget{};
float mDistanceToPointerTarget{ -1.f };
bool mCanPlaceObject{ false };
};
}
#endif

@ -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;

@ -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<VRPath> 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<std::pair<VRPath, VRPath>, 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<VRPath> listSupportedTrackingPosePaths() const override;

@ -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<MWWorld::Ptr, osg::Vec3f> getHitContact(float distance, std::vector<MWWorld::Ptr>& targets)
{
return std::pair<MWWorld::Ptr, osg::Vec3f>();
}
std::pair<MWWorld::Ptr, float> 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<MWWorld::Ptr, float>(result.mHitObject, distance);
}
std::pair<MWWorld::Ptr, float> getWeaponTarget()
{
auto* anim = MWVR::Environment::get().getPlayerAnimation();
MWRender::RayResult result;
auto distance = getPoseTarget(result, getNodePose(anim->getNode("weapon bone")), false);
return std::pair<MWWorld::Ptr, float>(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;
}
}
}

@ -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<MWWorld::Ptr, float> getTouchTarget();
std::pair<MWWorld::Ptr, float> getWeaponTarget();
float getPoseTarget(MWRender::RayResult& result, const Pose& pose, bool allowTelekinesis);
Pose getNodePose(const osg::Node* node);
}
}
#endif

@ -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)

@ -177,7 +177,7 @@ namespace MWWorld
const std::vector<std::string>& content, const std::vector<std::string>& 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

Loading…
Cancel
Save