mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-20 06:53:52 +00:00
Realistic combat, first draft
This commit is contained in:
parent
31f5b76394
commit
893b75d767
34 changed files with 645 additions and 1375 deletions
|
@ -113,11 +113,7 @@ endif ()
|
|||
|
||||
if(BUILD_VR_OPENXR)
|
||||
set(OPENMW_VR_FILES
|
||||
engine_vr.cpp
|
||||
mwvr/openxranimation.hpp
|
||||
mwvr/openxranimation.cpp
|
||||
mwvr/openxrenvironment.hpp
|
||||
mwvr/openxrenvironment.cpp
|
||||
vrengine.cpp
|
||||
mwvr/openxrinputmanager.hpp
|
||||
mwvr/openxrinputmanager.cpp
|
||||
mwvr/openxrlayer.hpp
|
||||
|
@ -140,6 +136,12 @@ if(BUILD_VR_OPENXR)
|
|||
mwvr/openxrviewer.cpp
|
||||
mwvr/openxrworldview.hpp
|
||||
mwvr/openxrworldview.cpp
|
||||
mwvr/realisticcombat.hpp
|
||||
mwvr/realisticcombat.cpp
|
||||
mwvr/vranimation.hpp
|
||||
mwvr/vranimation.cpp
|
||||
mwvr/vrenvironment.hpp
|
||||
mwvr/vrenvironment.cpp
|
||||
)
|
||||
|
||||
openmw_add_executable(openmw_vr
|
||||
|
|
|
@ -737,7 +737,7 @@ void OMW::Engine::go()
|
|||
|
||||
#ifdef USE_OPENXR
|
||||
auto* root = mViewer->getSceneData();
|
||||
auto* xrViewer = MWVR::OpenXREnvironment::get().getViewer();
|
||||
auto* xrViewer = MWVR::Environment::get().getViewer();
|
||||
xrViewer->addChild(root);
|
||||
mViewer->setSceneData(xrViewer);
|
||||
mXrEnvironment.setMenuManager(new MWVR::OpenXRMenuManager(mViewer));
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "mwworld/ptr.hpp"
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
#include "mwvr/openxrenvironment.hpp"
|
||||
#include "mwvr/vrenvironment.hpp"
|
||||
#endif
|
||||
|
||||
namespace Resource
|
||||
|
@ -212,7 +212,7 @@ namespace OMW
|
|||
Files::ConfigurationManager& mCfgMgr;
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
MWVR::OpenXREnvironment mXrEnvironment;
|
||||
MWVR::Environment mXrEnvironment;
|
||||
|
||||
void initVr();
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace osg
|
|||
class Quat;
|
||||
class Image;
|
||||
class Node;
|
||||
class Transform;
|
||||
}
|
||||
|
||||
namespace Loading
|
||||
|
@ -79,6 +80,11 @@ namespace MWWorld
|
|||
typedef std::vector<std::pair<MWWorld::Ptr,MWMechanics::Movement> > PtrMovementList;
|
||||
}
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
class PhysicsSystem;
|
||||
}
|
||||
|
||||
namespace MWBase
|
||||
{
|
||||
/// \brief Interface for the World (implemented in MWWorld)
|
||||
|
@ -391,6 +397,12 @@ namespace MWBase
|
|||
/// @param cursor Y (relative 0-1)
|
||||
/// @param number of objects to place
|
||||
|
||||
virtual MWWorld::Ptr placeObject (const MWWorld::ConstPtr& object, const MWRender::RayResult& ray, int amount) = 0;
|
||||
///< copy and place an object into the gameworld based on the given intersection
|
||||
/// @param object
|
||||
/// @param world position to place object
|
||||
/// @param number of objects to place
|
||||
|
||||
virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object, int amount) = 0;
|
||||
///< copy and place an object into the gameworld at the given actor's position
|
||||
/// @param actor giving the dropped object position
|
||||
|
@ -630,26 +642,12 @@ namespace MWBase
|
|||
|
||||
virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0;
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
using IntersectedObject = std::tuple<
|
||||
MWWorld::Ptr,
|
||||
osg::Node*,
|
||||
osg::Vec3f
|
||||
>;
|
||||
/// @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 getDistanceToPointedAtObject() = 0;
|
||||
///< Return the distance to the object and/or node the player is currently pointing at
|
||||
virtual void getPointedAtObject(MWRender::RayResult& result) = 0;
|
||||
///< Return pointer to the object and/or node the player is currently pointing at
|
||||
|
||||
virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& object, int amount) = 0;
|
||||
///< copy and place an object into the gameworld where the player is currently pointing
|
||||
/// @param object
|
||||
/// @param number of objects to place
|
||||
|
||||
virtual bool canPlaceObject() = 0;
|
||||
///< @return true if it is possible to place on object where the player is currently pointing
|
||||
#endif
|
||||
virtual MWPhysics::PhysicsSystem* getPhysicsSystem(void) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -227,7 +227,7 @@ namespace MWClass
|
|||
}
|
||||
|
||||
|
||||
void Creature::hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const
|
||||
bool Creature::hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Creature> *ref =
|
||||
ptr.get<ESM::Creature>();
|
||||
|
@ -235,7 +235,7 @@ namespace MWClass
|
|||
MWMechanics::CreatureStats &stats = getCreatureStats(ptr);
|
||||
|
||||
if (stats.getDrawState() != MWMechanics::DrawState_Weapon)
|
||||
return;
|
||||
return false;
|
||||
|
||||
// Get the weapon used (if hand-to-hand, weapon = inv.end())
|
||||
MWWorld::Ptr weapon;
|
||||
|
@ -259,12 +259,12 @@ namespace MWClass
|
|||
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors);
|
||||
if (result.first.isEmpty())
|
||||
return; // Didn't hit anything
|
||||
return false; // Didn't hit anything
|
||||
|
||||
MWWorld::Ptr victim = result.first;
|
||||
|
||||
if (!victim.getClass().isActor())
|
||||
return; // Can't hit non-actors
|
||||
return false; // Can't hit non-actors
|
||||
|
||||
osg::Vec3f hitPosition (result.second);
|
||||
|
||||
|
@ -274,7 +274,7 @@ namespace MWClass
|
|||
{
|
||||
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, osg::Vec3f(), false);
|
||||
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
int min,max;
|
||||
|
@ -330,6 +330,7 @@ namespace MWClass
|
|||
MWMechanics::diseaseContact(victim, ptr);
|
||||
|
||||
victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace MWClass
|
|||
virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const;
|
||||
///< Return creature stats
|
||||
|
||||
virtual void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const;
|
||||
virtual bool hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool simulated) const;
|
||||
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const;
|
||||
|
||||
|
|
|
@ -551,7 +551,7 @@ namespace MWClass
|
|||
}
|
||||
|
||||
|
||||
void Npc::hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const
|
||||
bool Npc::hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool simulated) const
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
|
||||
|
@ -564,7 +564,11 @@ namespace MWClass
|
|||
if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name())
|
||||
weapon = MWWorld::Ptr();
|
||||
|
||||
MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength);
|
||||
if (getCreatureStats(ptr).getDrawState() != MWMechanics::DrawState_Weapon)
|
||||
return false;
|
||||
|
||||
if (!simulated)
|
||||
MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength);
|
||||
|
||||
const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat();
|
||||
float dist = fCombatDistance * (!weapon.isEmpty() ?
|
||||
|
@ -581,14 +585,17 @@ namespace MWClass
|
|||
MWWorld::Ptr victim = result.first;
|
||||
osg::Vec3f hitPosition (result.second);
|
||||
if(victim.isEmpty()) // Didn't hit anything
|
||||
return;
|
||||
return false;
|
||||
|
||||
const MWWorld::Class &othercls = victim.getClass();
|
||||
if(!othercls.isActor()) // Can't hit non-actors
|
||||
return;
|
||||
return false;
|
||||
MWMechanics::CreatureStats &otherstats = othercls.getCreatureStats(victim);
|
||||
if(otherstats.isDead()) // Can't hit dead actors
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (simulated)
|
||||
return true;
|
||||
|
||||
if(ptr == MWMechanics::getPlayer())
|
||||
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
|
||||
|
@ -603,7 +610,7 @@ namespace MWClass
|
|||
{
|
||||
othercls.onHit(victim, 0.0f, false, weapon, ptr, osg::Vec3f(), false);
|
||||
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool healthdmg;
|
||||
|
@ -664,6 +671,7 @@ namespace MWClass
|
|||
MWMechanics::diseaseContact(victim, ptr);
|
||||
|
||||
othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace MWClass
|
|||
|
||||
virtual bool hasInventoryStore(const MWWorld::Ptr &ptr) const { return true; }
|
||||
|
||||
virtual void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const;
|
||||
virtual bool hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool simulated) const;
|
||||
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const;
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@
|
|||
#include "resourceskin.hpp"
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
#include "../mwvr/openxrenvironment.hpp"
|
||||
#include "../mwvr/vrenvironment.hpp"
|
||||
#include "../mwvr/openxrmenu.hpp"
|
||||
#endif
|
||||
|
||||
|
@ -1890,7 +1890,7 @@ namespace MWGui
|
|||
#ifdef USE_OPENXR
|
||||
// Temporary hack to force update of menu placement
|
||||
// (Menu gets recreated next tick)
|
||||
auto* xrMenuManager = MWVR::OpenXREnvironment::get().getMenuManager();
|
||||
auto* xrMenuManager = MWVR::Environment::get().getMenuManager();
|
||||
#endif
|
||||
|
||||
mVideoWidget->eventKeyButtonPressed.clear();
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
#include "spellcasting.hpp"
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
#include "../mwvr/openxranimation.hpp"
|
||||
#include "../mwvr/vranimation.hpp"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
|
@ -1029,7 +1029,7 @@ void CharacterController::handleTextKey(const std::string &groupname, const std:
|
|||
else if (groupname == "attack3" || groupname == "swimattack3")
|
||||
mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust);
|
||||
else
|
||||
mPtr.getClass().hit(mPtr, mAttackStrength);
|
||||
mPtr.getClass().hit(mPtr, mAttackStrength, -1);
|
||||
}
|
||||
else if (!groupname.empty()
|
||||
&& (groupname.compare(0, groupname.size()-1, "attack") == 0 || groupname.compare(0, groupname.size()-1, "swimattack") == 0)
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
#ifdef USE_OPENXR
|
||||
#include "../mwvr/openxrsession.hpp"
|
||||
#include "../mwvr/openxrinputmanager.hpp"
|
||||
#include "../mwvr/openxrenvironment.hpp"
|
||||
#include "../mwvr/vrenvironment.hpp"
|
||||
#endif
|
||||
|
||||
#include "collisiontype.hpp"
|
||||
|
@ -277,7 +277,7 @@ namespace MWPhysics
|
|||
|
||||
static osg::Vec3f move(osg::Vec3f position, const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time,
|
||||
bool isFlying, float waterlevel, float slowFall, const btCollisionWorld* collisionWorld,
|
||||
std::map<MWWorld::Ptr, MWWorld::Ptr>& standingCollisionTracker, bool isPlayer)
|
||||
std::map<MWWorld::Ptr, MWWorld::Ptr>& standingCollisionTracker)
|
||||
{
|
||||
ESM::Position refpos = ptr.getRefData().getPosition();
|
||||
// Early-out for totally static creatures
|
||||
|
@ -285,12 +285,14 @@ namespace MWPhysics
|
|||
if (!ptr.getClass().isMobile(ptr))
|
||||
return position;
|
||||
|
||||
const bool isPlayer = (ptr == MWMechanics::getPlayer());
|
||||
|
||||
// In VR, player should move according to current direction of
|
||||
// a selected limb, rather than current orientation of camera.
|
||||
#ifdef USE_OPENXR
|
||||
if (isPlayer)
|
||||
{
|
||||
auto* session = MWVR::OpenXREnvironment::get().getSession();
|
||||
auto* session = MWVR::Environment::get().getSession();
|
||||
if (session)
|
||||
{
|
||||
float yaw = session->movementYaw();
|
||||
|
@ -349,7 +351,6 @@ namespace MWPhysics
|
|||
|
||||
if (ptr.getClass().getMovementSettings(ptr).mPosition[2])
|
||||
{
|
||||
const bool isPlayer = (ptr == MWMechanics::getPlayer());
|
||||
// Advance acrobatics and set flag for GetPCJumping
|
||||
if (isPlayer)
|
||||
{
|
||||
|
@ -389,7 +390,7 @@ namespace MWPhysics
|
|||
#ifdef USE_OPENXR
|
||||
if (isPlayer)
|
||||
{
|
||||
auto* inputManager = MWVR::OpenXREnvironment::get().getInputManager();
|
||||
auto* inputManager = MWVR::Environment::get().getInputManager();
|
||||
|
||||
osg::Vec3 trackingOffset = inputManager->mHeadOffset;
|
||||
// Player's tracking height should not affect character position
|
||||
|
@ -449,7 +450,7 @@ namespace MWPhysics
|
|||
}
|
||||
}
|
||||
|
||||
// Best effort attempt at not losing any tracking
|
||||
// Try not to lose any tracking
|
||||
osg::Vec3 moved = newPosition - position;
|
||||
inputManager->mHeadOffset.x() -= moved.x();
|
||||
inputManager->mHeadOffset.y() -= moved.y();
|
||||
|
@ -1368,7 +1369,6 @@ namespace MWPhysics
|
|||
PtrVelocityList::iterator iter = mMovementQueue.begin();
|
||||
for(;iter != mMovementQueue.end();++iter)
|
||||
{
|
||||
bool isPlayer = iter->first == MWMechanics::getPlayer();
|
||||
ActorMap::iterator foundActor = mActors.find(iter->first);
|
||||
if (foundActor == mActors.end()) // actor was already removed from the scene
|
||||
continue;
|
||||
|
@ -1408,7 +1408,7 @@ namespace MWPhysics
|
|||
for (int i=0; i<numSteps; ++i)
|
||||
{
|
||||
position = MovementSolver::move(position, physicActor->getPtr(), physicActor, iter->second, mPhysicsDt,
|
||||
flying, waterlevel, slowFall, mCollisionWorld, mStandingCollisions, isPlayer);
|
||||
flying, waterlevel, slowFall, mCollisionWorld, mStandingCollisions);
|
||||
if (position != physicActor->getPosition())
|
||||
positionChanged = true;
|
||||
physicActor->setPosition(position); // always set even if unchanged to make sure interpolation is correct
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
#ifdef USE_OPENXR
|
||||
#include "../mwvr/openxrinputmanager.hpp"
|
||||
#include "../mwvr/openxrenvironment.hpp"
|
||||
#include "../mwvr/vrenvironment.hpp"
|
||||
#endif
|
||||
|
||||
#include "npcanimation.hpp"
|
||||
|
@ -127,7 +127,7 @@ namespace MWRender
|
|||
osg::Vec3d position = getFocalPoint();
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
auto* inputManager = MWVR::OpenXREnvironment::get().getInputManager();
|
||||
auto* inputManager = MWVR::Environment::get().getInputManager();
|
||||
if (inputManager)
|
||||
{
|
||||
position += inputManager->mHeadOffset;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include <components/sceneutil/util.hpp>
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/sceneutil/statesetupdater.hpp>
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
|
@ -68,8 +69,8 @@
|
|||
#include "actorspaths.hpp"
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
#include "../mwvr/openxranimation.hpp"
|
||||
#include "../mwvr/openxrenvironment.hpp"
|
||||
#include "../mwvr/vranimation.hpp"
|
||||
#include "../mwvr/vrenvironment.hpp"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
|
@ -1085,6 +1086,24 @@ namespace MWRender
|
|||
return getIntersectionResult(intersector);
|
||||
}
|
||||
|
||||
RayResult RenderingManager::castRay(const osg::Transform* source, float maxDistance, bool ignorePlayer, bool ignoreActors)
|
||||
{
|
||||
|
||||
if (source)
|
||||
{
|
||||
osg::Matrix worldMatrix = osg::computeLocalToWorld(source->getParentalNodePaths()[0]);
|
||||
|
||||
osg::Vec3f direction = worldMatrix.getRotate() * osg::Vec3f(1, 0, 0);
|
||||
direction.normalize();
|
||||
|
||||
osg::Vec3f raySource = worldMatrix.getTrans();
|
||||
osg::Vec3f rayTarget = worldMatrix.getTrans() + direction * maxDistance;
|
||||
|
||||
return castRay(raySource, rayTarget, ignorePlayer, ignoreActors);
|
||||
}
|
||||
return RayResult();
|
||||
}
|
||||
|
||||
RayResult RenderingManager::castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors)
|
||||
{
|
||||
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::PROJECTION,
|
||||
|
@ -1165,8 +1184,8 @@ namespace MWRender
|
|||
void RenderingManager::renderPlayer(const MWWorld::Ptr &player)
|
||||
{
|
||||
#ifdef USE_OPENXR
|
||||
MWVR::OpenXREnvironment::get().setPlayerAnimation(new MWVR::OpenXRAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, false, nullptr));
|
||||
mPlayerAnimation = MWVR::OpenXREnvironment::get().getPlayerAnimation();
|
||||
MWVR::Environment::get().setPlayerAnimation(new MWVR::VRAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, false, nullptr));
|
||||
mPlayerAnimation = MWVR::Environment::get().getPlayerAnimation();
|
||||
#else
|
||||
mPlayerAnimation = new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0, NpcAnimation::VM_Normal,
|
||||
mFirstPersonFieldOfView);
|
||||
|
|
|
@ -159,7 +159,11 @@ namespace MWRender
|
|||
void screenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd());
|
||||
bool screenshot360(osg::Image* image, std::string settingStr);
|
||||
|
||||
/// Cast a ray between two points
|
||||
RayResult castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors=false);
|
||||
|
||||
/// Cast a ray from a node in the scene graph
|
||||
RayResult castRay(const osg::Transform* source, float maxDistance, bool ignorePlayer, bool ignoreActors=false);
|
||||
|
||||
/// Return the object under the mouse cursor / crosshair position, given by nX and nY normalized screen coordinates,
|
||||
/// where (0,0) is the top left corner.
|
||||
|
|
|
@ -1,854 +0,0 @@
|
|||
#include "openxrengine.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <osgViewer/ViewerEventHandlers>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osgDB/WriteFile>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
|
||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||
#include <components/sdlutil/imagetosurface.hpp>
|
||||
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/resource/stats.hpp>
|
||||
|
||||
#include <components/compiler/extensions0.hpp>
|
||||
|
||||
#include <components/sceneutil/workqueue.hpp>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include <components/version/version.hpp>
|
||||
|
||||
#include <components/detournavigator/navigator.hpp>
|
||||
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
|
||||
#include "../mwgui/windowmanagerimp.hpp"
|
||||
|
||||
#include "../mwscript/scriptmanagerimp.hpp"
|
||||
#include "../mwscript/interpretercontext.hpp"
|
||||
|
||||
#include "../mwsound/soundmanagerimp.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/worldimp.hpp"
|
||||
|
||||
#include "../mwrender/vismask.hpp"
|
||||
|
||||
#include "../mwclass/classes.hpp"
|
||||
|
||||
#include "../mwdialogue/dialoguemanagerimp.hpp"
|
||||
#include "../mwdialogue/journalimp.hpp"
|
||||
#include "../mwdialogue/scripttest.hpp"
|
||||
|
||||
#include "../mwmechanics/mechanicsmanagerimp.hpp"
|
||||
|
||||
#include "../mwstate/statemanagerimp.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
void checkSDLError(int ret)
|
||||
{
|
||||
if (ret != 0)
|
||||
Log(Debug::Error) << "SDL error: " << SDL_GetError();
|
||||
}
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::executeLocalScripts()
|
||||
{
|
||||
MWWorld::LocalScripts& localScripts = mEnvironment.getWorld()->getLocalScripts();
|
||||
|
||||
localScripts.startIteration();
|
||||
std::pair<std::string, MWWorld::Ptr> script;
|
||||
while (localScripts.getNext(script))
|
||||
{
|
||||
MWScript::InterpreterContext interpreterContext (
|
||||
&script.second.getRefData().getLocals(), script.second);
|
||||
mEnvironment.getScriptManager()->run (script.first, interpreterContext);
|
||||
}
|
||||
}
|
||||
|
||||
bool MWVR::OpenXREngine::frame(float frametime)
|
||||
{
|
||||
try
|
||||
{
|
||||
mStartTick = mViewer->getStartTick();
|
||||
|
||||
mEnvironment.setFrameDuration(frametime);
|
||||
|
||||
// update input
|
||||
mEnvironment.getInputManager()->update(frametime, false);
|
||||
|
||||
// When the window is minimized, pause the game. Currently this *has* to be here to work around a MyGUI bug.
|
||||
// If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures (fixed in MyGUI 3.3.2),
|
||||
// and destroyed widgets will not be deleted (not fixed yet, https://github.com/MyGUI/mygui/issues/21)
|
||||
if (!mEnvironment.getInputManager()->isWindowVisible())
|
||||
return false;
|
||||
|
||||
// sound
|
||||
if (mUseSound)
|
||||
mEnvironment.getSoundManager()->update(frametime);
|
||||
|
||||
// Main menu opened? Then scripts are also paused.
|
||||
bool paused = mEnvironment.getWindowManager()->containsMode(MWGui::GM_MainMenu);
|
||||
|
||||
// update game state
|
||||
mEnvironment.getStateManager()->update (frametime);
|
||||
|
||||
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
||||
|
||||
osg::Timer_t beforeScriptTick = osg::Timer::instance()->tick();
|
||||
if (mEnvironment.getStateManager()->getState()!=
|
||||
MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
if (!paused)
|
||||
{
|
||||
if (mEnvironment.getWorld()->getScriptsEnabled())
|
||||
{
|
||||
// local scripts
|
||||
executeLocalScripts();
|
||||
|
||||
// global scripts
|
||||
mEnvironment.getScriptManager()->getGlobalScripts().run();
|
||||
}
|
||||
|
||||
mEnvironment.getWorld()->markCellAsUnchanged();
|
||||
}
|
||||
|
||||
if (!guiActive)
|
||||
{
|
||||
double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;
|
||||
mEnvironment.getWorld()->advanceTime(hours, true);
|
||||
mEnvironment.getWorld()->rechargeItems(frametime, true);
|
||||
}
|
||||
}
|
||||
osg::Timer_t afterScriptTick = osg::Timer::instance()->tick();
|
||||
|
||||
// update actors
|
||||
osg::Timer_t beforeMechanicsTick = osg::Timer::instance()->tick();
|
||||
if (mEnvironment.getStateManager()->getState()!=
|
||||
MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
mEnvironment.getMechanicsManager()->update(frametime,
|
||||
guiActive);
|
||||
}
|
||||
osg::Timer_t afterMechanicsTick = osg::Timer::instance()->tick();
|
||||
|
||||
if (mEnvironment.getStateManager()->getState()==
|
||||
MWBase::StateManager::State_Running)
|
||||
{
|
||||
MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
|
||||
if(!guiActive && player.getClass().getCreatureStats(player).isDead())
|
||||
mEnvironment.getStateManager()->endGame();
|
||||
}
|
||||
|
||||
// update physics
|
||||
osg::Timer_t beforePhysicsTick = osg::Timer::instance()->tick();
|
||||
if (mEnvironment.getStateManager()->getState()!=
|
||||
MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
mEnvironment.getWorld()->updatePhysics(frametime, guiActive);
|
||||
}
|
||||
osg::Timer_t afterPhysicsTick = osg::Timer::instance()->tick();
|
||||
|
||||
// update world
|
||||
osg::Timer_t beforeWorldTick = osg::Timer::instance()->tick();
|
||||
if (mEnvironment.getStateManager()->getState()!=
|
||||
MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
mEnvironment.getWorld()->update(frametime, guiActive);
|
||||
}
|
||||
osg::Timer_t afterWorldTick = osg::Timer::instance()->tick();
|
||||
|
||||
// update GUI
|
||||
mEnvironment.getWindowManager()->onFrame(frametime);
|
||||
|
||||
unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
|
||||
osg::Stats* stats = mViewer->getViewerStats();
|
||||
stats->setAttribute(frameNumber, "script_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeScriptTick));
|
||||
stats->setAttribute(frameNumber, "script_time_taken", osg::Timer::instance()->delta_s(beforeScriptTick, afterScriptTick));
|
||||
stats->setAttribute(frameNumber, "script_time_end", osg::Timer::instance()->delta_s(mStartTick, afterScriptTick));
|
||||
|
||||
stats->setAttribute(frameNumber, "mechanics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeMechanicsTick));
|
||||
stats->setAttribute(frameNumber, "mechanics_time_taken", osg::Timer::instance()->delta_s(beforeMechanicsTick, afterMechanicsTick));
|
||||
stats->setAttribute(frameNumber, "mechanics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterMechanicsTick));
|
||||
|
||||
stats->setAttribute(frameNumber, "physics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforePhysicsTick));
|
||||
stats->setAttribute(frameNumber, "physics_time_taken", osg::Timer::instance()->delta_s(beforePhysicsTick, afterPhysicsTick));
|
||||
stats->setAttribute(frameNumber, "physics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterPhysicsTick));
|
||||
|
||||
stats->setAttribute(frameNumber, "world_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeWorldTick));
|
||||
stats->setAttribute(frameNumber, "world_time_taken", osg::Timer::instance()->delta_s(beforeWorldTick, afterWorldTick));
|
||||
stats->setAttribute(frameNumber, "world_time_end", osg::Timer::instance()->delta_s(mStartTick, afterWorldTick));
|
||||
|
||||
if (stats->collectStats("resource"))
|
||||
{
|
||||
mResourceSystem->reportStats(frameNumber, stats);
|
||||
|
||||
stats->setAttribute(frameNumber, "WorkQueue", mWorkQueue->getNumItems());
|
||||
stats->setAttribute(frameNumber, "WorkThread", mWorkQueue->getNumActiveThreads());
|
||||
|
||||
mEnvironment.getWorld()->getNavigator()->reportStats(frameNumber, *stats);
|
||||
}
|
||||
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
Log(Debug::Error) << "Error in frame: " << e.what();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MWVR::OpenXREngine::OpenXREngine(Files::ConfigurationManager& configurationManager)
|
||||
: mWindow(nullptr)
|
||||
, mEncoding(ToUTF8::WINDOWS_1252)
|
||||
, mEncoder(nullptr)
|
||||
, mScreenCaptureOperation(nullptr)
|
||||
, mSkipMenu (false)
|
||||
, mUseSound (true)
|
||||
, mCompileAll (false)
|
||||
, mCompileAllDialogue (false)
|
||||
, mWarningsMode (1)
|
||||
, mScriptConsoleMode (false)
|
||||
, mActivationDistanceOverride(-1)
|
||||
, mGrab(true)
|
||||
, mExportFonts(false)
|
||||
, mRandomSeed(0)
|
||||
, mScriptContext (0)
|
||||
, mFSStrict (false)
|
||||
, mScriptBlacklistUse (true)
|
||||
, mNewGame (false)
|
||||
, mCfgMgr(configurationManager)
|
||||
{
|
||||
MWClass::registerClasses();
|
||||
|
||||
Uint32 flags = SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE|SDL_INIT_GAMECONTROLLER|SDL_INIT_JOYSTICK;
|
||||
if(SDL_WasInit(flags) == 0)
|
||||
{
|
||||
SDL_SetMainReady();
|
||||
if(SDL_Init(flags) != 0)
|
||||
{
|
||||
throw std::runtime_error("Could not initialize SDL! " + std::string(SDL_GetError()));
|
||||
}
|
||||
}
|
||||
|
||||
mStartTick = osg::Timer::instance()->tick();
|
||||
}
|
||||
|
||||
MWVR::OpenXREngine::~OpenXREngine()
|
||||
{
|
||||
mEnvironment.cleanup();
|
||||
|
||||
delete mScriptContext;
|
||||
mScriptContext = nullptr;
|
||||
|
||||
mWorkQueue = nullptr;
|
||||
|
||||
mViewer = nullptr;
|
||||
|
||||
mResourceSystem.reset();
|
||||
|
||||
delete mEncoder;
|
||||
mEncoder = nullptr;
|
||||
|
||||
mOpenXRManager = nullptr;
|
||||
|
||||
if (mWindow)
|
||||
{
|
||||
SDL_DestroyWindow(mWindow);
|
||||
mWindow = nullptr;
|
||||
}
|
||||
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::enableFSStrict(bool fsStrict)
|
||||
{
|
||||
mFSStrict = fsStrict;
|
||||
}
|
||||
|
||||
// Set data dir
|
||||
|
||||
void MWVR::OpenXREngine::setDataDirs (const Files::PathContainer& dataDirs)
|
||||
{
|
||||
mDataDirs = dataDirs;
|
||||
mDataDirs.insert(mDataDirs.begin(), (mResDir / "vfs"));
|
||||
mFileCollections = Files::Collections (mDataDirs, !mFSStrict);
|
||||
}
|
||||
|
||||
// Add BSA archive
|
||||
void MWVR::OpenXREngine::addArchive (const std::string& archive) {
|
||||
mArchives.push_back(archive);
|
||||
}
|
||||
|
||||
// Set resource dir
|
||||
void MWVR::OpenXREngine::setResourceDir (const boost::filesystem::path& parResDir)
|
||||
{
|
||||
mResDir = parResDir;
|
||||
}
|
||||
|
||||
// Set start cell name
|
||||
void MWVR::OpenXREngine::setCell (const std::string& cellName)
|
||||
{
|
||||
mCellName = cellName;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::addContentFile(const std::string& file)
|
||||
{
|
||||
mContentFiles.push_back(file);
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setSkipMenu (bool skipMenu, bool newGame)
|
||||
{
|
||||
mSkipMenu = skipMenu;
|
||||
mNewGame = newGame;
|
||||
}
|
||||
|
||||
std::string MWVR::OpenXREngine::loadSettings (Settings::Manager & settings)
|
||||
{
|
||||
// Create the settings manager and load default settings file
|
||||
const std::string localdefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string();
|
||||
const std::string globaldefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string();
|
||||
|
||||
// prefer local
|
||||
if (boost::filesystem::exists(localdefault))
|
||||
settings.loadDefault(localdefault);
|
||||
else if (boost::filesystem::exists(globaldefault))
|
||||
settings.loadDefault(globaldefault);
|
||||
else
|
||||
throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed.");
|
||||
|
||||
// load user settings if they exist
|
||||
const std::string settingspath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string();
|
||||
if (boost::filesystem::exists(settingspath))
|
||||
settings.loadUser(settingspath);
|
||||
|
||||
return settingspath;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::createWindow(Settings::Manager& settings)
|
||||
{
|
||||
int screen = settings.getInt("screen", "Video");
|
||||
int width = settings.getInt("resolution x", "Video");
|
||||
int height = settings.getInt("resolution y", "Video");
|
||||
bool fullscreen = settings.getBool("fullscreen", "Video");
|
||||
bool windowBorder = settings.getBool("window border", "Video");
|
||||
bool vsync = settings.getBool("vsync", "Video");
|
||||
int antialiasing = settings.getInt("antialiasing", "Video");
|
||||
|
||||
int pos_x = SDL_WINDOWPOS_CENTERED_DISPLAY(screen),
|
||||
pos_y = SDL_WINDOWPOS_CENTERED_DISPLAY(screen);
|
||||
|
||||
if(fullscreen)
|
||||
{
|
||||
pos_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(screen);
|
||||
pos_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(screen);
|
||||
}
|
||||
|
||||
Uint32 flags = SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE;
|
||||
if(fullscreen)
|
||||
flags |= SDL_WINDOW_FULLSCREEN;
|
||||
|
||||
if (!windowBorder)
|
||||
flags |= SDL_WINDOW_BORDERLESS;
|
||||
|
||||
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS,
|
||||
settings.getBool("minimize on focus loss", "Video") ? "1" : "0");
|
||||
|
||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8));
|
||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8));
|
||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8));
|
||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0));
|
||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24));
|
||||
|
||||
if (antialiasing > 0)
|
||||
{
|
||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1));
|
||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
|
||||
}
|
||||
|
||||
while (!mWindow)
|
||||
{
|
||||
mWindow = SDL_CreateWindow("OpenMW", pos_x, pos_y, width, height, flags);
|
||||
if (!mWindow)
|
||||
{
|
||||
// Try with a lower AA
|
||||
if (antialiasing > 0)
|
||||
{
|
||||
Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2;
|
||||
antialiasing /= 2;
|
||||
Settings::Manager::setInt("antialiasing", "Video", antialiasing);
|
||||
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "Failed to create SDL window: " << SDL_GetError();
|
||||
throw std::runtime_error(error.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setWindowIcon();
|
||||
|
||||
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
|
||||
SDL_GetWindowPosition(mWindow, &traits->x, &traits->y);
|
||||
SDL_GetWindowSize(mWindow, &traits->width, &traits->height);
|
||||
traits->windowName = SDL_GetWindowTitle(mWindow);
|
||||
traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS);
|
||||
traits->screenNum = SDL_GetWindowDisplayIndex(mWindow);
|
||||
// We tried to get rid of the hardcoding but failed: https://github.com/OpenMW/openmw/pull/1771
|
||||
// Here goes kcat's quote:
|
||||
// It's ultimately a chicken and egg problem, and the reason why the code is like it was in the first place.
|
||||
// It needs a context to get the current attributes, but it needs the attributes to set up the context.
|
||||
// So it just specifies the same values that were given to SDL in the hopes that it's good enough to what the window eventually gets.
|
||||
traits->red = 8;
|
||||
traits->green = 8;
|
||||
traits->blue = 8;
|
||||
traits->alpha = 0; // set to 0 to stop ScreenCaptureHandler reading the alpha channel
|
||||
traits->depth = 24;
|
||||
traits->stencil = 8;
|
||||
traits->vsync = vsync;
|
||||
traits->doubleBuffer = true;
|
||||
traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow);
|
||||
|
||||
osg::ref_ptr<SDLUtil::GraphicsWindowSDL2> graphicsWindow = new SDLUtil::GraphicsWindowSDL2(traits);
|
||||
if(!graphicsWindow->valid()) throw std::runtime_error("Failed to create GraphicsContext");
|
||||
|
||||
mOpenXRManager = std::make_unique<OpenXRManager>(graphicsWindow);
|
||||
|
||||
osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();
|
||||
camera->setGraphicsContext(graphicsWindow);
|
||||
camera->setViewport(0, 0, traits->width, traits->height);
|
||||
|
||||
mViewer->realize();
|
||||
|
||||
mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, traits->width, traits->height);
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setWindowIcon()
|
||||
{
|
||||
boost::filesystem::ifstream windowIconStream;
|
||||
std::string windowIcon = (mResDir / "mygui" / "openmw.png").string();
|
||||
windowIconStream.open(windowIcon, std::ios_base::in | std::ios_base::binary);
|
||||
if (windowIconStream.fail())
|
||||
Log(Debug::Error) << "Error: Failed to open " << windowIcon;
|
||||
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("png");
|
||||
if (!reader)
|
||||
{
|
||||
Log(Debug::Error) << "Error: Failed to read window icon, no png readerwriter found";
|
||||
return;
|
||||
}
|
||||
osgDB::ReaderWriter::ReadResult result = reader->readImage(windowIconStream);
|
||||
if (!result.success())
|
||||
Log(Debug::Error) << "Error: Failed to read " << windowIcon << ": " << result.message() << " code " << result.status();
|
||||
else
|
||||
{
|
||||
osg::ref_ptr<osg::Image> image = result.getImage();
|
||||
auto surface = SDLUtil::imageToSurface(image, true);
|
||||
SDL_SetWindowIcon(mWindow, surface.get());
|
||||
}
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::prepareEngine (Settings::Manager & settings)
|
||||
{
|
||||
mEnvironment.setStateManager (
|
||||
new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0)));
|
||||
|
||||
createWindow(settings);
|
||||
|
||||
osg::ref_ptr<osg::Group> rootNode (new osg::Group);
|
||||
mViewer->setSceneData(rootNode);
|
||||
|
||||
mVFS.reset(new VFS::Manager(mFSStrict));
|
||||
|
||||
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
||||
|
||||
mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get()));
|
||||
mResourceSystem->getSceneManager()->setUnRefImageDataAfterApply(false); // keep to Off for now to allow better state sharing
|
||||
mResourceSystem->getSceneManager()->setFilterSettings(
|
||||
Settings::Manager::getString("texture mag filter", "General"),
|
||||
Settings::Manager::getString("texture min filter", "General"),
|
||||
Settings::Manager::getString("texture mipmap", "General"),
|
||||
Settings::Manager::getInt("anisotropy", "General")
|
||||
);
|
||||
|
||||
int numThreads = Settings::Manager::getInt("preload num threads", "Cells");
|
||||
if (numThreads <= 0)
|
||||
throw std::runtime_error("Invalid setting: 'preload num threads' must be >0");
|
||||
mWorkQueue = new SceneUtil::WorkQueue(numThreads);
|
||||
|
||||
// Create input and UI first to set up a bootstrapping environment for
|
||||
// showing a loading screen and keeping the window responsive while doing so
|
||||
|
||||
std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input_v3.xml").string();
|
||||
bool keybinderUserExists = boost::filesystem::exists(keybinderUser);
|
||||
if(!keybinderUserExists)
|
||||
{
|
||||
std::string input2 = (mCfgMgr.getUserConfigPath() / "input_v2.xml").string();
|
||||
if(boost::filesystem::exists(input2)) {
|
||||
boost::filesystem::copy_file(input2, keybinderUser);
|
||||
keybinderUserExists = boost::filesystem::exists(keybinderUser);
|
||||
Log(Debug::Info) << "Loading keybindings file: " << keybinderUser;
|
||||
}
|
||||
}
|
||||
else
|
||||
Log(Debug::Info) << "Loading keybindings file: " << keybinderUser;
|
||||
|
||||
// find correct path to the game controller bindings
|
||||
// File format for controller mappings is different for SDL <= 2.0.4, 2.0.5, and >= 2.0.6
|
||||
SDL_version linkedSdlVersion;
|
||||
SDL_GetVersion(&linkedSdlVersion);
|
||||
std::string controllerFileName;
|
||||
if (linkedSdlVersion.major == 2 && linkedSdlVersion.minor == 0 && linkedSdlVersion.patch <= 4) {
|
||||
controllerFileName = "gamecontrollerdb_204.txt";
|
||||
} else if (linkedSdlVersion.major == 2 && linkedSdlVersion.minor == 0 && linkedSdlVersion.patch == 5) {
|
||||
controllerFileName = "gamecontrollerdb_205.txt";
|
||||
} else {
|
||||
controllerFileName = "gamecontrollerdb.txt";
|
||||
}
|
||||
|
||||
const std::string userdefault = mCfgMgr.getUserConfigPath().string() + "/" + controllerFileName;
|
||||
const std::string localdefault = mCfgMgr.getLocalPath().string() + "/" + controllerFileName;
|
||||
const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/" + controllerFileName;
|
||||
|
||||
std::string userGameControllerdb;
|
||||
if (boost::filesystem::exists(userdefault)){
|
||||
userGameControllerdb = userdefault;
|
||||
}
|
||||
else
|
||||
userGameControllerdb = "";
|
||||
|
||||
std::string gameControllerdb;
|
||||
if (boost::filesystem::exists(localdefault))
|
||||
gameControllerdb = localdefault;
|
||||
else if (boost::filesystem::exists(globaldefault))
|
||||
gameControllerdb = globaldefault;
|
||||
else
|
||||
gameControllerdb = ""; //if it doesn't exist, pass in an empty string
|
||||
|
||||
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
|
||||
mEnvironment.setInputManager (input);
|
||||
|
||||
std::string myguiResources = (mResDir / "mygui").string();
|
||||
osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
|
||||
guiRoot->setName("GUI Root");
|
||||
guiRoot->setNodeMask(MWRender::Mask_GUI);
|
||||
rootNode->addChild(guiRoot);
|
||||
MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
|
||||
mCfgMgr.getLogPath().string() + std::string("/"), myguiResources,
|
||||
mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts,
|
||||
Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
|
||||
mEnvironment.setWindowManager (window);
|
||||
|
||||
// Create sound system
|
||||
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
|
||||
|
||||
if (!mSkipMenu)
|
||||
{
|
||||
const std::string& logo = Fallback::Map::getString("Movies_Company_Logo");
|
||||
if (!logo.empty())
|
||||
window->playVideo(logo, true);
|
||||
}
|
||||
|
||||
// Create the world
|
||||
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
|
||||
mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName,
|
||||
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
|
||||
mEnvironment.getWorld()->setupPlayer();
|
||||
input->setPlayer(&mEnvironment.getWorld()->getPlayer());
|
||||
|
||||
window->setStore(mEnvironment.getWorld()->getStore());
|
||||
window->initUI();
|
||||
|
||||
//Load translation data
|
||||
mTranslationDataStorage.setEncoder(mEncoder);
|
||||
for (size_t i = 0; i < mContentFiles.size(); i++)
|
||||
mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]);
|
||||
|
||||
Compiler::registerExtensions (mExtensions);
|
||||
|
||||
// Create script system
|
||||
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full);
|
||||
mScriptContext->setExtensions (&mExtensions);
|
||||
|
||||
mEnvironment.setScriptManager (new MWScript::ScriptManager (mEnvironment.getWorld()->getStore(), *mScriptContext, mWarningsMode,
|
||||
mScriptBlacklistUse ? mScriptBlacklist : std::vector<std::string>()));
|
||||
|
||||
// Create game mechanics system
|
||||
MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager;
|
||||
mEnvironment.setMechanicsManager (mechanics);
|
||||
|
||||
// Create dialog system
|
||||
mEnvironment.setJournal (new MWDialogue::Journal);
|
||||
mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mTranslationDataStorage));
|
||||
|
||||
// scripts
|
||||
if (mCompileAll)
|
||||
{
|
||||
std::pair<int, int> result = mEnvironment.getScriptManager()->compileAll();
|
||||
if (result.first)
|
||||
Log(Debug::Info)
|
||||
<< "compiled " << result.second << " of " << result.first << " scripts ("
|
||||
<< 100*static_cast<double> (result.second)/result.first
|
||||
<< "%)";
|
||||
}
|
||||
if (mCompileAllDialogue)
|
||||
{
|
||||
std::pair<int, int> result = MWDialogue::ScriptTest::compileAll(&mExtensions, mWarningsMode);
|
||||
if (result.first)
|
||||
Log(Debug::Info)
|
||||
<< "compiled " << result.second << " of " << result.first << " dialogue script/actor combinations a("
|
||||
<< 100*static_cast<double> (result.second)/result.first
|
||||
<< "%)";
|
||||
}
|
||||
}
|
||||
|
||||
class WriteScreenshotToFileOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation
|
||||
{
|
||||
public:
|
||||
WriteScreenshotToFileOperation(const std::string& screenshotPath, const std::string& screenshotFormat)
|
||||
: mScreenshotPath(screenshotPath)
|
||||
, mScreenshotFormat(screenshotFormat)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator()(const osg::Image& image, const unsigned int context_id)
|
||||
{
|
||||
// Count screenshots.
|
||||
int shotCount = 0;
|
||||
|
||||
// Find the first unused filename with a do-while
|
||||
std::ostringstream stream;
|
||||
do
|
||||
{
|
||||
// Reset the stream
|
||||
stream.str("");
|
||||
stream.clear();
|
||||
|
||||
stream << mScreenshotPath << "/screenshot" << std::setw(3) << std::setfill('0') << shotCount++ << "." << mScreenshotFormat;
|
||||
|
||||
} while (boost::filesystem::exists(stream.str()));
|
||||
|
||||
boost::filesystem::ofstream outStream;
|
||||
outStream.open(boost::filesystem::path(stream.str()), std::ios::binary);
|
||||
|
||||
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(mScreenshotFormat);
|
||||
if (!readerwriter)
|
||||
{
|
||||
Log(Debug::Error) << "Error: Can't write screenshot, no '" << mScreenshotFormat << "' readerwriter found";
|
||||
return;
|
||||
}
|
||||
|
||||
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream);
|
||||
if (!result.success())
|
||||
{
|
||||
Log(Debug::Error) << "Error: Can't write screenshot: " << result.message() << " code " << result.status();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string mScreenshotPath;
|
||||
std::string mScreenshotFormat;
|
||||
};
|
||||
|
||||
// Initialise and enter main loop.
|
||||
|
||||
void MWVR::OpenXREngine::go()
|
||||
{
|
||||
assert (!mContentFiles.empty());
|
||||
|
||||
Log(Debug::Info) << "OSG version: " << osgGetVersion();
|
||||
SDL_version sdlVersion;
|
||||
SDL_GetVersion(&sdlVersion);
|
||||
Log(Debug::Info) << "SDL version: " << (int)sdlVersion.major << "." << (int)sdlVersion.minor << "." << (int)sdlVersion.patch;
|
||||
|
||||
Misc::Rng::init(mRandomSeed);
|
||||
|
||||
// Load settings
|
||||
Settings::Manager settings;
|
||||
std::string settingspath;
|
||||
settingspath = loadSettings (settings);
|
||||
|
||||
// Create encoder
|
||||
mEncoder = new ToUTF8::Utf8Encoder(mEncoding);
|
||||
|
||||
// Setup viewer
|
||||
mViewer = new osgViewer::Viewer;
|
||||
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
||||
|
||||
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,5)
|
||||
// Do not try to outsmart the OS thread scheduler (see bug #4785).
|
||||
mViewer->setUseConfigureAffinity(false);
|
||||
#endif
|
||||
|
||||
mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(),
|
||||
Settings::Manager::getString("screenshot format", "General"));
|
||||
|
||||
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation);
|
||||
|
||||
mViewer->addEventHandler(mScreenCaptureHandler);
|
||||
|
||||
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));
|
||||
|
||||
prepareEngine (settings);
|
||||
|
||||
// Setup profiler
|
||||
osg::ref_ptr<Resource::Profiler> statshandler = new Resource::Profiler;
|
||||
|
||||
statshandler->addUserStatsLine("Script", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||
"script_time_taken", 1000.0, true, false, "script_time_begin", "script_time_end", 10000);
|
||||
statshandler->addUserStatsLine("Mech", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||
"mechanics_time_taken", 1000.0, true, false, "mechanics_time_begin", "mechanics_time_end", 10000);
|
||||
statshandler->addUserStatsLine("Phys", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||
"physics_time_taken", 1000.0, true, false, "physics_time_begin", "physics_time_end", 10000);
|
||||
statshandler->addUserStatsLine("World", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||
"world_time_taken", 1000.0, true, false, "world_time_begin", "world_time_end", 10000);
|
||||
|
||||
mViewer->addEventHandler(statshandler);
|
||||
|
||||
osg::ref_ptr<Resource::StatsHandler> resourceshandler = new Resource::StatsHandler;
|
||||
mViewer->addEventHandler(resourceshandler);
|
||||
|
||||
// Start the game
|
||||
if (!mSaveGameFile.empty())
|
||||
{
|
||||
mEnvironment.getStateManager()->loadGame(mSaveGameFile);
|
||||
}
|
||||
else if (!mSkipMenu)
|
||||
{
|
||||
// start in main menu
|
||||
mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
||||
mEnvironment.getSoundManager()->playTitleMusic();
|
||||
const std::string& logo = Fallback::Map::getString("Movies_Morrowind_Logo");
|
||||
if (!logo.empty())
|
||||
mEnvironment.getWindowManager()->playVideo(logo, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
mEnvironment.getStateManager()->newGame (!mNewGame);
|
||||
}
|
||||
|
||||
if (!mStartupScript.empty() && mEnvironment.getStateManager()->getState() == MWState::StateManager::State_Running)
|
||||
{
|
||||
mEnvironment.getWindowManager()->executeInConsole(mStartupScript);
|
||||
}
|
||||
|
||||
// Start the main rendering loop
|
||||
osg::Timer frameTimer;
|
||||
double simulationTime = 0.0;
|
||||
while (!mViewer->done() && !mEnvironment.getStateManager()->hasQuitRequest())
|
||||
{
|
||||
double dt = frameTimer.time_s();
|
||||
frameTimer.setStartTick();
|
||||
dt = std::min(dt, 0.2);
|
||||
|
||||
mViewer->advance(simulationTime);
|
||||
|
||||
if (!frame(dt))
|
||||
{
|
||||
OpenThreads::Thread::microSleep(5000);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
mViewer->eventTraversal();
|
||||
mViewer->updateTraversal();
|
||||
|
||||
mEnvironment.getWorld()->updateWindowManager();
|
||||
|
||||
mViewer->renderingTraversals();
|
||||
|
||||
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
||||
if (!guiActive)
|
||||
simulationTime += dt;
|
||||
}
|
||||
|
||||
mEnvironment.limitFrameRate(frameTimer.time_s());
|
||||
}
|
||||
|
||||
// Save user settings
|
||||
settings.saveUser(settingspath);
|
||||
|
||||
Log(Debug::Info) << "Quitting peacefully.";
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setCompileAll (bool all)
|
||||
{
|
||||
mCompileAll = all;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setCompileAllDialogue (bool all)
|
||||
{
|
||||
mCompileAllDialogue = all;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setSoundUsage(bool soundUsage)
|
||||
{
|
||||
mUseSound = soundUsage;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setEncoding(const ToUTF8::FromType& encoding)
|
||||
{
|
||||
mEncoding = encoding;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setScriptConsoleMode (bool enabled)
|
||||
{
|
||||
mScriptConsoleMode = enabled;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setStartupScript (const std::string& path)
|
||||
{
|
||||
mStartupScript = path;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setActivationDistanceOverride (int distance)
|
||||
{
|
||||
mActivationDistanceOverride = distance;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setWarningsMode (int mode)
|
||||
{
|
||||
mWarningsMode = mode;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setScriptBlacklist (const std::vector<std::string>& list)
|
||||
{
|
||||
mScriptBlacklist = list;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setScriptBlacklistUse (bool use)
|
||||
{
|
||||
mScriptBlacklistUse = use;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::enableFontExport(bool exportFonts)
|
||||
{
|
||||
mExportFonts = exportFonts;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setSaveGameFile(const std::string &savegame)
|
||||
{
|
||||
mSaveGameFile = savegame;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREngine::setRandomSeed(unsigned int seed)
|
||||
{
|
||||
mRandomSeed = seed;
|
||||
}
|
|
@ -1,217 +0,0 @@
|
|||
#ifndef ENGINE_H
|
||||
#define ENGINE_H
|
||||
|
||||
#include <components/compiler/extensions.hpp>
|
||||
#include <components/files/collections.hpp>
|
||||
#include <components/translation/translation.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
#include <osgViewer/ViewerEventHandlers>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include "openxrmanager.hpp"
|
||||
|
||||
namespace Resource
|
||||
{
|
||||
class ResourceSystem;
|
||||
}
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
class WorkQueue;
|
||||
}
|
||||
|
||||
namespace VFS
|
||||
{
|
||||
class Manager;
|
||||
}
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
class ScriptManager;
|
||||
}
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class SoundManager;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class World;
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class WindowManager;
|
||||
}
|
||||
|
||||
namespace Files
|
||||
{
|
||||
struct ConfigurationManager;
|
||||
}
|
||||
|
||||
namespace osgViewer
|
||||
{
|
||||
class ScreenCaptureHandler;
|
||||
}
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
/// \brief Main VR engine class, that brings together all the components of OpenMW
|
||||
class OpenXREngine
|
||||
{
|
||||
private:
|
||||
SDL_Window* mWindow;
|
||||
std::unique_ptr<OpenXRManager> mOpenXRManager;
|
||||
std::unique_ptr<VFS::Manager> mVFS;
|
||||
std::unique_ptr<Resource::ResourceSystem> mResourceSystem;
|
||||
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
|
||||
MWBase::Environment mEnvironment;
|
||||
ToUTF8::FromType mEncoding;
|
||||
ToUTF8::Utf8Encoder* mEncoder;
|
||||
Files::PathContainer mDataDirs;
|
||||
std::vector<std::string> mArchives;
|
||||
boost::filesystem::path mResDir;
|
||||
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
||||
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
|
||||
osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;
|
||||
std::string mCellName;
|
||||
std::vector<std::string> mContentFiles;
|
||||
bool mSkipMenu;
|
||||
bool mUseSound;
|
||||
bool mCompileAll;
|
||||
bool mCompileAllDialogue;
|
||||
int mWarningsMode;
|
||||
std::string mFocusName;
|
||||
bool mScriptConsoleMode;
|
||||
std::string mStartupScript;
|
||||
int mActivationDistanceOverride;
|
||||
std::string mSaveGameFile;
|
||||
// Grab mouse?
|
||||
bool mGrab;
|
||||
|
||||
bool mExportFonts;
|
||||
unsigned int mRandomSeed;
|
||||
|
||||
Compiler::Extensions mExtensions;
|
||||
Compiler::Context *mScriptContext;
|
||||
|
||||
Files::Collections mFileCollections;
|
||||
bool mFSStrict;
|
||||
Translation::Storage mTranslationDataStorage;
|
||||
std::vector<std::string> mScriptBlacklist;
|
||||
bool mScriptBlacklistUse;
|
||||
bool mNewGame;
|
||||
|
||||
osg::Timer_t mStartTick;
|
||||
|
||||
private:
|
||||
|
||||
// not implemented
|
||||
OpenXREngine(const OpenXREngine&) = delete;
|
||||
OpenXREngine& operator= (const OpenXREngine&) = delete;
|
||||
|
||||
void executeLocalScripts();
|
||||
|
||||
bool frame (float dt);
|
||||
|
||||
/// Load settings from various files, returns the path to the user settings file
|
||||
std::string loadSettings (Settings::Manager & settings);
|
||||
|
||||
/// Prepare engine for game play
|
||||
void prepareEngine (Settings::Manager & settings);
|
||||
|
||||
void createWindow(Settings::Manager& settings);
|
||||
void setWindowIcon();
|
||||
|
||||
public:
|
||||
OpenXREngine(Files::ConfigurationManager& configurationManager);
|
||||
virtual ~OpenXREngine();
|
||||
|
||||
/// Enable strict filesystem mode (do not fold case)
|
||||
///
|
||||
/// \attention The strict mode must be specified before any path-related settings
|
||||
/// are given to the engine.
|
||||
void enableFSStrict(bool fsStrict);
|
||||
|
||||
/// Set data dirs
|
||||
void setDataDirs(const Files::PathContainer& dataDirs);
|
||||
|
||||
/// Add BSA archive
|
||||
void addArchive(const std::string& archive);
|
||||
|
||||
/// Set resource dir
|
||||
void setResourceDir(const boost::filesystem::path& parResDir);
|
||||
|
||||
/// Set start cell name
|
||||
void setCell(const std::string& cellName);
|
||||
|
||||
/**
|
||||
* @brief addContentFile - Adds content file (ie. esm/esp, or omwgame/omwaddon) to the content files container.
|
||||
* @param file - filename (extension is required)
|
||||
*/
|
||||
void addContentFile(const std::string& file);
|
||||
|
||||
/// Disable or enable all sounds
|
||||
void setSoundUsage(bool soundUsage);
|
||||
|
||||
/// Skip main menu and go directly into the game
|
||||
///
|
||||
/// \param newGame Start a new game instead off dumping the player into the game
|
||||
/// (ignored if !skipMenu).
|
||||
void setSkipMenu (bool skipMenu, bool newGame);
|
||||
|
||||
void setGrabMouse(bool grab) { mGrab = grab; }
|
||||
|
||||
/// Initialise and enter main loop.
|
||||
void go();
|
||||
|
||||
/// Compile all scripts (excludign dialogue scripts) at startup?
|
||||
void setCompileAll (bool all);
|
||||
|
||||
/// Compile all dialogue scripts at startup?
|
||||
void setCompileAllDialogue (bool all);
|
||||
|
||||
/// Font encoding
|
||||
void setEncoding(const ToUTF8::FromType& encoding);
|
||||
|
||||
/// Enable console-only script functionality
|
||||
void setScriptConsoleMode (bool enabled);
|
||||
|
||||
/// Set path for a script that is run on startup in the console.
|
||||
void setStartupScript (const std::string& path);
|
||||
|
||||
/// Override the game setting specified activation distance.
|
||||
void setActivationDistanceOverride (int distance);
|
||||
|
||||
void setWarningsMode (int mode);
|
||||
|
||||
void setScriptBlacklist (const std::vector<std::string>& list);
|
||||
|
||||
void setScriptBlacklistUse (bool use);
|
||||
|
||||
void enableFontExport(bool exportFonts);
|
||||
|
||||
/// Set the save game file to load after initialising the engine.
|
||||
void setSaveGameFile(const std::string& savegame);
|
||||
|
||||
void setRandomSeed(unsigned int seed);
|
||||
|
||||
private:
|
||||
Files::ConfigurationManager& mCfgMgr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* ENGINE_H */
|
|
@ -1,6 +1,6 @@
|
|||
#include "openxranimation.hpp"
|
||||
#include "vranimation.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
#include "openxrenvironment.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
|
||||
|
@ -265,7 +265,7 @@ struct OpenXRInput
|
|||
XrActionSet
|
||||
OpenXRInput::createActionSet()
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
XrActionSet actionSet = XR_NULL_HANDLE;
|
||||
XrActionSetCreateInfo createInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
|
||||
strcpy_s(createInfo.actionSetName, "gameplay");
|
||||
|
@ -308,7 +308,7 @@ OpenXRAction::~OpenXRAction() {
|
|||
|
||||
bool OpenXRAction::getFloat(XrPath subactionPath, float& value)
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = mAction;
|
||||
getInfo.subactionPath = subactionPath;
|
||||
|
@ -323,7 +323,7 @@ bool OpenXRAction::getFloat(XrPath subactionPath, float& value)
|
|||
|
||||
bool OpenXRAction::getBool(XrPath subactionPath, bool& value)
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = mAction;
|
||||
getInfo.subactionPath = subactionPath;
|
||||
|
@ -339,7 +339,7 @@ bool OpenXRAction::getBool(XrPath subactionPath, bool& value)
|
|||
// Pose action only checks if the pose is active or not
|
||||
bool OpenXRAction::getPose(XrPath subactionPath)
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
getInfo.action = mAction;
|
||||
getInfo.subactionPath = subactionPath;
|
||||
|
@ -352,7 +352,7 @@ bool OpenXRAction::getPose(XrPath subactionPath)
|
|||
|
||||
bool OpenXRAction::applyHaptics(XrPath subactionPath, float amplitude)
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
|
||||
vibration.amplitude = amplitude;
|
||||
vibration.duration = XR_MIN_HAPTIC_DURATION;
|
||||
|
@ -414,7 +414,7 @@ OpenXRInput::ControllerActionPaths
|
|||
OpenXRInput::generateControllerActionPaths(
|
||||
const std::string& controllerAction)
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
ControllerActionPaths actionPaths;
|
||||
|
||||
std::string left = std::string("/user/hand/left") + controllerAction;
|
||||
|
@ -473,7 +473,7 @@ OpenXRInput::OpenXRInput()
|
|||
, mHandPoseAction(std::move(createAction(XR_ACTION_TYPE_POSE_INPUT, "hand_pose", "Hand Pose", { LEFT_HAND, RIGHT_HAND })))
|
||||
, mHapticsAction(std::move(createAction(XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibrate_hand", "Vibrate Hand", { LEFT_HAND, RIGHT_HAND })))
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
{ // Set up default bindings for the oculus
|
||||
XrPath oculusTouchInteractionProfilePath;
|
||||
CHECK_XRCMD(
|
||||
|
@ -585,7 +585,7 @@ OpenXRInput::subactionPath(
|
|||
void
|
||||
OpenXRInput::updateControls()
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
if (!xr->impl().mSessionRunning)
|
||||
return;
|
||||
|
||||
|
@ -696,7 +696,7 @@ OpenXRInput::updateControls()
|
|||
|
||||
XrPath OpenXRInput::generateXrPath(const std::string& path)
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
XrPath xrpath = 0;
|
||||
CHECK_XRCMD(xrStringToPath(xr->impl().mInstance, path.c_str(), &xrpath));
|
||||
return xrpath;
|
||||
|
@ -720,7 +720,7 @@ OpenXRInput::getHandPoses(
|
|||
int64_t time,
|
||||
TrackedSpace space)
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
PoseSet handPoses{};
|
||||
XrSpace referenceSpace = XR_NULL_HANDLE;
|
||||
if (space == TrackedSpace::STAGE)
|
||||
|
@ -762,7 +762,7 @@ void OpenXRInputManager::updateActivationIndication(void)
|
|||
if (mPlayer)
|
||||
mPlayer->setPointing(show);
|
||||
|
||||
auto* playerAnimation = OpenXREnvironment::get().getPlayerAnimation();
|
||||
auto* playerAnimation = Environment::get().getPlayerAnimation();
|
||||
if (playerAnimation)
|
||||
playerAnimation->setPointForward(show);
|
||||
}
|
||||
|
@ -779,10 +779,11 @@ public:
|
|||
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();
|
||||
|
||||
MWWorld::Ptr dropped;
|
||||
if (world->canPlaceObject())
|
||||
dropped = world->placeObject(item.mBase, count);
|
||||
if (anim->canPlaceObject())
|
||||
dropped = world->placeObject(item.mBase, anim->getPointerTarget(), count);
|
||||
else
|
||||
dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count);
|
||||
dropped.getCellRef().setOwner("");
|
||||
|
@ -804,12 +805,11 @@ private:
|
|||
void OpenXRInputManager::pointActivation(bool onPress)
|
||||
{
|
||||
auto* world = MWBase::Environment::get().getWorld();
|
||||
if (world)
|
||||
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
||||
if (world && anim && anim->mPointerTarget.mHit)
|
||||
{
|
||||
MWRender::RayResult pointedAt;
|
||||
world->getPointedAtObject(pointedAt);
|
||||
auto* node = pointedAt.mHitNode;
|
||||
MWWorld::Ptr ptr = pointedAt.mHitObject;
|
||||
auto* node = anim->mPointerTarget.mHitNode;
|
||||
MWWorld::Ptr ptr = anim->mPointerTarget.mHitObject;
|
||||
auto& dnd = MWBase::Environment::get().getWindowManager()->getDragAndDrop();
|
||||
|
||||
if (node && node->getName() == "XR Menu Geometry")
|
||||
|
@ -894,17 +894,16 @@ private:
|
|||
mXRInput->updateControls();
|
||||
|
||||
auto* world = MWBase::Environment::get().getWorld();
|
||||
if (world)
|
||||
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
||||
if (world && anim && anim->mPointerTarget.mHit)
|
||||
{
|
||||
MWRender::RayResult pointedAt;
|
||||
world->getPointedAtObject(pointedAt);
|
||||
auto* node = pointedAt.mHitNode;
|
||||
auto* node = anim->mPointerTarget.mHitNode;
|
||||
if (node)
|
||||
{
|
||||
int w, h;
|
||||
SDL_GetWindowSize(mWindow, &w, &h);
|
||||
|
||||
osg::Vec3 local = pointedAt.mHitPointLocal;
|
||||
osg::Vec3 local = anim->mPointerTarget.mHitPointLocal;
|
||||
local.x() = (local.x() + 1.f) / 2.f;
|
||||
local.y() = 1.f - (local.y() + 1.f) / 2.f;
|
||||
|
||||
|
@ -915,6 +914,8 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
OpenXRActionEvent event{};
|
||||
while (mXRInput->nextActionEvent(event))
|
||||
{
|
||||
|
@ -926,17 +927,25 @@ private:
|
|||
MWInput::InputManager::update(dt, disableControls, disableEvents);
|
||||
|
||||
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
||||
auto* xrMenuManager = OpenXREnvironment::get().getMenuManager();
|
||||
auto* xrMenuManager = Environment::get().getMenuManager();
|
||||
if(xrMenuManager)
|
||||
xrMenuManager->showMenus(guiMode);
|
||||
|
||||
setPlayerControlsEnabled(!guiMode);
|
||||
|
||||
if (mPlayer)
|
||||
{
|
||||
auto player = mPlayer->getPlayer();
|
||||
if (!mRealisticCombat || mRealisticCombat->ptr != player)
|
||||
mRealisticCombat.reset(new RealisticCombat::StateMachine(player));
|
||||
mRealisticCombat->update(dt, !guiMode);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenXRInputManager::processEvent(const OpenXRActionEvent& event)
|
||||
{
|
||||
//auto* session = OpenXREnvironment::get().getSession();
|
||||
auto* xrMenuManager = OpenXREnvironment::get().getMenuManager();
|
||||
//auto* session = Environment::get().getSession();
|
||||
auto* xrMenuManager = Environment::get().getMenuManager();
|
||||
switch (event.action)
|
||||
{
|
||||
case A_GameMenu:
|
||||
|
@ -1101,11 +1110,11 @@ private:
|
|||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
auto player = mPlayer->getPlayer();
|
||||
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* session = OpenXREnvironment::get().getSession();
|
||||
auto* xr = Environment::get().getManager();
|
||||
auto* session = Environment::get().getSession();
|
||||
auto currentHeadPose = session->predictedPoses().head[(int)TrackedSpace::STAGE];
|
||||
xr->playerScale(currentHeadPose);
|
||||
currentHeadPose.position *= OpenXREnvironment::get().unitsPerMeter();
|
||||
currentHeadPose.position *= Environment::get().unitsPerMeter();
|
||||
osg::Vec3 vrMovement = currentHeadPose.position - mPreviousHeadPose.position;
|
||||
mPreviousHeadPose = currentHeadPose;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define OPENXR_INPUT_MANAGER_HPP
|
||||
|
||||
#include "openxrviewer.hpp"
|
||||
#include "realisticcombat.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
@ -47,6 +48,7 @@ namespace MWVR
|
|||
void pointActivation(bool onPress);
|
||||
|
||||
std::unique_ptr<OpenXRInput> mXRInput;
|
||||
std::unique_ptr<RealisticCombat::StateMachine> mRealisticCombat;
|
||||
Pose mPreviousHeadPose{};
|
||||
osg::Vec3 mHeadOffset{ 0,0,0 };
|
||||
bool mRecenter{ true };
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "openxrenvironment.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
|
@ -199,14 +199,14 @@ namespace MWVR
|
|||
OpenXRManager::RealizeOperation::operator()(
|
||||
osg::GraphicsContext* gc)
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
xr->realize(gc);
|
||||
}
|
||||
|
||||
bool
|
||||
OpenXRManager::RealizeOperation::realized()
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
return xr->realized();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "openxrmenu.hpp"
|
||||
#include "openxrenvironment.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
#include "openxrsession.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "openxranimation.hpp"
|
||||
#include "vranimation.hpp"
|
||||
#include <openxr/openxr.h>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/ClipNode>
|
||||
|
@ -122,7 +122,7 @@ private:
|
|||
osg::ref_ptr<osg::Vec3Array> normals{ new osg::Vec3Array(1) };
|
||||
|
||||
// Units are divided by 2 because geometry has an extent of 2 (-1 to 1)
|
||||
auto extent_units = extent_meters * OpenXREnvironment::get().unitsPerMeter() / 2.f;
|
||||
auto extent_units = extent_meters * Environment::get().unitsPerMeter() / 2.f;
|
||||
|
||||
// Define the menu quad
|
||||
osg::Vec3 top_left (-1, 1, 1);
|
||||
|
@ -164,7 +164,7 @@ private:
|
|||
mTransform->setAttitude(pose.orientation);
|
||||
mTransform->setPosition(pose.position);
|
||||
mTransform->addChild(mGeometry);
|
||||
//mTransform->addChild(OpenXRAnimation::createPointerGeometry());
|
||||
//mTransform->addChild(VRAnimation::createPointerGeometry());
|
||||
|
||||
// Add to scene graph
|
||||
mGeometryRoot->addChild(mTransform);
|
||||
|
@ -295,7 +295,7 @@ private:
|
|||
// Position the menu about two thirds of a meter in front of the player
|
||||
osg::Vec3 dir = center - eye;
|
||||
dir.normalize();
|
||||
mPose.position = eye + dir * OpenXREnvironment::get().unitsPerMeter() * 2.f / 3.f;
|
||||
mPose.position = eye + dir * Environment::get().unitsPerMeter() * 2.f / 3.f;
|
||||
|
||||
|
||||
mPose.orientation = camera->getViewMatrix().getRotate().inverse();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "openxrenvironment.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
|
@ -46,7 +46,7 @@ namespace MWVR
|
|||
{
|
||||
Timer timer("OpenXRSession::SwapBuffers");
|
||||
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
|
||||
if (!xr->sessionRunning())
|
||||
return;
|
||||
|
@ -64,7 +64,7 @@ namespace MWVR
|
|||
|
||||
void OpenXRSession::waitFrame()
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
xr->handleEvents();
|
||||
if (!xr->sessionRunning())
|
||||
return;
|
||||
|
@ -128,8 +128,8 @@ namespace MWVR
|
|||
|
||||
void OpenXRSession::predictNext(int extraPeriods)
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* input = OpenXREnvironment::get().getInputManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
auto* input = Environment::get().getInputManager();
|
||||
auto mPredictedDisplayTime = xr->impl().frameState().predictedDisplayTime;
|
||||
|
||||
auto previousHeadPose = mPredictedPoses.head[(int)TrackedSpace::STAGE];
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "openxrsession.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
#include "openxrenvironment.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
#include "Windows.h"
|
||||
#include "../mwrender/vismask.hpp"
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
|
@ -64,7 +64,7 @@ namespace MWVR
|
|||
|
||||
void OpenXRViewer::traversals()
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
xr->handleEvents();
|
||||
mViewer->updateTraversal();
|
||||
mViewer->renderingTraversals();
|
||||
|
@ -96,11 +96,11 @@ namespace MWVR
|
|||
|
||||
osg::Vec4 clearColor = mainCamera->getClearColor();
|
||||
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* xr = Environment::get().getManager();
|
||||
if (!xr->realized())
|
||||
xr->realize(context);
|
||||
|
||||
auto* session = OpenXREnvironment::get().getSession();
|
||||
auto* session = Environment::get().getSession();
|
||||
|
||||
OpenXRSwapchain::Config leftConfig;
|
||||
leftConfig.width = xr->impl().mConfigViews[(int)Side::LEFT_HAND].recommendedImageRectWidth;
|
||||
|
@ -186,7 +186,7 @@ namespace MWVR
|
|||
|
||||
if (includeMenu)
|
||||
{
|
||||
auto menuManager = OpenXREnvironment::get().getMenuManager();
|
||||
auto menuManager = Environment::get().getMenuManager();
|
||||
if (menuManager)
|
||||
{
|
||||
auto menu = menuManager->getMenu();
|
||||
|
@ -234,7 +234,7 @@ namespace MWVR
|
|||
OpenXRViewer::SwapBuffersCallback::swapBuffersImplementation(
|
||||
osg::GraphicsContext* gc)
|
||||
{
|
||||
auto* session = OpenXREnvironment::get().getSession();
|
||||
auto* session = Environment::get().getSession();
|
||||
session->swapBuffers(gc);
|
||||
}
|
||||
|
||||
|
@ -244,8 +244,8 @@ namespace MWVR
|
|||
if (!mConfigured)
|
||||
return;
|
||||
|
||||
auto* session = OpenXREnvironment::get().getSession();
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* session = Environment::get().getSession();
|
||||
auto* xr = Environment::get().getManager();
|
||||
|
||||
Timer timer("OpenXRViewer::SwapBuffers");
|
||||
|
||||
|
@ -288,13 +288,13 @@ namespace MWVR
|
|||
{
|
||||
OpenXRManager::RealizeOperation::operator()(gc);
|
||||
|
||||
OpenXREnvironment::get().getViewer()->realize(gc);
|
||||
Environment::get().getViewer()->realize(gc);
|
||||
}
|
||||
|
||||
bool
|
||||
OpenXRViewer::RealizeOperation::realized()
|
||||
{
|
||||
return OpenXREnvironment::get().getViewer()->realized();
|
||||
return Environment::get().getViewer()->realized();
|
||||
}
|
||||
|
||||
void OpenXRViewer::preDrawCallback(osg::RenderInfo& info)
|
||||
|
@ -305,8 +305,7 @@ namespace MWVR
|
|||
|
||||
if (name == "LeftEye")
|
||||
{
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* session = OpenXREnvironment::get().getSession();
|
||||
auto* xr = Environment::get().getManager();
|
||||
if (xr->sessionRunning())
|
||||
{
|
||||
xr->beginFrame();
|
||||
|
@ -341,8 +340,8 @@ namespace MWVR
|
|||
return;
|
||||
}
|
||||
|
||||
auto* xr = OpenXREnvironment::get().getManager();
|
||||
auto* session = OpenXREnvironment::get().getSession();
|
||||
auto* xr = Environment::get().getManager();
|
||||
auto* session = Environment::get().getSession();
|
||||
auto& poses = session->predictedPoses();
|
||||
auto handPosesStage = poses.hands[(int)TrackedSpace::STAGE];
|
||||
int side = (int)Side::LEFT_HAND;
|
||||
|
@ -357,14 +356,14 @@ namespace MWVR
|
|||
xr->playerScale(headStage);
|
||||
auto orientation = handStage.orientation;
|
||||
auto position = handStage.position - headStage.position;
|
||||
position = position * OpenXREnvironment::get().unitsPerMeter();
|
||||
position = position * Environment::get().unitsPerMeter();
|
||||
|
||||
auto camera = mViewer->getCamera();
|
||||
auto viewMatrix = camera->getViewMatrix();
|
||||
|
||||
|
||||
// Align orientation with the game world
|
||||
auto* inputManager = OpenXREnvironment::get().getInputManager();
|
||||
auto* inputManager = Environment::get().getInputManager();
|
||||
if (inputManager)
|
||||
{
|
||||
auto playerYaw = osg::Quat(-inputManager->mYaw, osg::Vec3d(0, 0, 1));
|
||||
|
@ -399,7 +398,7 @@ namespace MWVR
|
|||
if (hand_transform->getName() == "tracker r hand")
|
||||
offcenter.z() *= -1.;
|
||||
osg::Vec3 recenter = orientation * offcenter;
|
||||
position = position + recenter * OpenXREnvironment::get().unitsPerMeter();
|
||||
position = position + recenter * Environment::get().unitsPerMeter();
|
||||
|
||||
hand_transform->setAttitude(orientation);
|
||||
hand_transform->setPosition(position);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "openxrworldview.hpp"
|
||||
#include "openxrenvironment.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrmanagerimpl.hpp"
|
||||
#include "../mwinput/inputmanagerimp.hpp"
|
||||
|
@ -100,7 +100,7 @@ namespace MWVR
|
|||
{
|
||||
MWVR::Pose pose = predictedPose();
|
||||
mXR->playerScale(pose);
|
||||
osg::Vec3 position = pose.position * OpenXREnvironment::get().unitsPerMeter();
|
||||
osg::Vec3 position = pose.position * Environment::get().unitsPerMeter();
|
||||
osg::Quat orientation = pose.orientation;
|
||||
|
||||
float y = position.y();
|
||||
|
@ -166,7 +166,7 @@ namespace MWVR
|
|||
// Updating the head pose needs to happen after waitFrame(),
|
||||
// But i can't call waitFrame from the input manager since it might
|
||||
// not always be active.
|
||||
auto* inputManager = OpenXREnvironment::get().getInputManager();
|
||||
auto* inputManager = Environment::get().getInputManager();
|
||||
if (inputManager)
|
||||
inputManager->updateHead();
|
||||
|
||||
|
|
197
apps/openmw/mwvr/realisticcombat.cpp
Normal file
197
apps/openmw/mwvr/realisticcombat.cpp
Normal file
|
@ -0,0 +1,197 @@
|
|||
#include "realisticcombat.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
namespace MWVR { namespace RealisticCombat {
|
||||
|
||||
StateMachine::StateMachine(MWWorld::Ptr ptr) : ptr(ptr) {}
|
||||
|
||||
// Actions common to all transitions
|
||||
void StateMachine::transition(
|
||||
SwingState newState)
|
||||
{
|
||||
if (newState == state)
|
||||
throw std::logic_error("Cannot transition to current state");
|
||||
|
||||
maxSwingVelocity = 0.f;
|
||||
timeSinceEnteredState = 0.f;
|
||||
movementSinceEnteredState = 0.f;
|
||||
state = newState;
|
||||
}
|
||||
|
||||
void StateMachine::reset()
|
||||
{
|
||||
maxSwingVelocity = 0.f;
|
||||
timeSinceEnteredState = 0.f;
|
||||
velocity = 0.f;
|
||||
previousPosition = osg::Vec3(0.f, 0.f, 0.f);
|
||||
state = SwingState_Ready;
|
||||
}
|
||||
|
||||
void StateMachine::update(float dt, bool enabled)
|
||||
{
|
||||
auto* session = Environment::get().getSession();
|
||||
auto& handPose = session->predictedPoses().hands[(int)MWVR::TrackedSpace::STAGE][(int)MWVR::Side::RIGHT_HAND];
|
||||
auto& headPose = session->predictedPoses().head[(int)MWVR::TrackedSpace::STAGE];
|
||||
|
||||
if (mEnabled != enabled)
|
||||
{
|
||||
reset();
|
||||
mEnabled = enabled;
|
||||
}
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
timeSinceEnteredState += dt;
|
||||
|
||||
|
||||
// First determine direction of different swing types
|
||||
|
||||
// Thrust is radially out from the head. Which is the same as the position of the hand relative to the head, ignoring height component
|
||||
osg::Vec3 thrustDirection = handPose.position - headPose.position;
|
||||
thrustDirection.z() = 0;
|
||||
thrustDirection.normalize();
|
||||
|
||||
// Chop is straight down
|
||||
osg::Vec3 chopDirection = osg::Vec3(0.f, 0.f, -1.f);
|
||||
|
||||
// Swing is normal to the plane created by Chop x Thrust
|
||||
osg::Vec3 slashDirection = chopDirection ^ thrustDirection;
|
||||
slashDirection.normalize();
|
||||
|
||||
|
||||
// Next determine current hand movement
|
||||
|
||||
// If tracking is lost, openxr will return a position of 0
|
||||
// Reset position when tracking is re-acquired
|
||||
// Theoretically, the player's hand really could be at 0,0,0
|
||||
// but that's a super rare case so whatever.
|
||||
if (previousPosition == osg::Vec3(0.f, 0.f, 0.f))
|
||||
previousPosition = handPose.position;
|
||||
|
||||
osg::Vec3 movement = handPose.position - previousPosition;
|
||||
movementSinceEnteredState += movement.length();
|
||||
previousPosition = handPose.position;
|
||||
osg::Vec3 swingDirection = movement / dt;
|
||||
|
||||
// Compute swing velocity
|
||||
// Unidirectional
|
||||
thrustVelocity = swingDirection * thrustDirection;
|
||||
// Bidirectional
|
||||
slashVelocity = std::abs(swingDirection * slashDirection);
|
||||
chopVelocity = std::abs(swingDirection * chopDirection);
|
||||
|
||||
// Pick swing type based on greatest current velocity
|
||||
// Note i use abs() of thrust velocity to prevent accidentally triggering
|
||||
// chop or slash when player is withdrawing his limb.
|
||||
if (std::abs(thrustVelocity) > slashVelocity && std::abs(thrustVelocity) > chopVelocity)
|
||||
{
|
||||
velocity = thrustVelocity;
|
||||
swingType = ESM::Weapon::AT_Thrust;
|
||||
}
|
||||
else if (slashVelocity > chopVelocity)
|
||||
{
|
||||
velocity = slashVelocity;
|
||||
swingType = ESM::Weapon::AT_Slash;
|
||||
}
|
||||
else
|
||||
{
|
||||
velocity = chopVelocity;
|
||||
swingType = ESM::Weapon::AT_Chop;
|
||||
}
|
||||
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case SwingState_Ready:
|
||||
return update_readyState();
|
||||
case SwingState_Swinging:
|
||||
return update_swingingState();
|
||||
case SwingState_Impact:
|
||||
return update_impactState();
|
||||
}
|
||||
}
|
||||
|
||||
void StateMachine::update_readyState()
|
||||
{
|
||||
if (velocity >= minVelocity)
|
||||
if(timeSinceEnteredState >= minimumPeriod)
|
||||
return transition_readyToSwinging();
|
||||
}
|
||||
|
||||
void StateMachine::transition_readyToSwinging()
|
||||
{
|
||||
shouldSwish = true;
|
||||
transition(SwingState_Swinging);
|
||||
|
||||
// As an exception, update the new state immediately to allow
|
||||
// same-frame impacts.
|
||||
update_swingingState();
|
||||
}
|
||||
|
||||
void StateMachine::playSwish()
|
||||
{
|
||||
if (shouldSwish)
|
||||
{
|
||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
|
||||
std::string sound = "Weapon Swish";
|
||||
if (strength < 0.5f)
|
||||
sndMgr->playSound3D(ptr, sound, 1.0f, 0.8f); //Weak attack
|
||||
if (strength < 1.0f)
|
||||
sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f); //Medium attack
|
||||
else
|
||||
sndMgr->playSound3D(ptr, sound, 1.0f, 1.2f); //Strong attack
|
||||
shouldSwish = false;
|
||||
}
|
||||
}
|
||||
|
||||
void StateMachine::update_swingingState()
|
||||
{
|
||||
maxSwingVelocity = std::max(velocity, maxSwingVelocity);
|
||||
strength = std::min(1.f, (maxSwingVelocity - minVelocity) / maxVelocity);
|
||||
|
||||
if (velocity < minVelocity)
|
||||
return transition_swingingToReady();
|
||||
|
||||
// Require a minimum period of swinging before a hit can be made
|
||||
// This is to prevent annoying little microswings
|
||||
if (movementSinceEnteredState > minimumPeriod)
|
||||
{
|
||||
playSwish();
|
||||
|
||||
// Note: calling hit with simulated=true to avoid side effects
|
||||
if (ptr.getClass().hit(ptr, strength, swingType, true))
|
||||
return transition_swingingToImpact();
|
||||
}
|
||||
}
|
||||
|
||||
void StateMachine::transition_swingingToReady()
|
||||
{
|
||||
if (movementSinceEnteredState > minimumPeriod)
|
||||
{
|
||||
playSwish();
|
||||
ptr.getClass().hit(ptr, strength, swingType, false);
|
||||
}
|
||||
transition(SwingState_Ready);
|
||||
}
|
||||
|
||||
void StateMachine::transition_swingingToImpact()
|
||||
{
|
||||
playSwish();
|
||||
ptr.getClass().hit(ptr, strength, swingType, false);
|
||||
transition(SwingState_Impact);
|
||||
}
|
||||
|
||||
void StateMachine::update_impactState()
|
||||
{
|
||||
if (velocity < minVelocity)
|
||||
return transition_impactToReady();
|
||||
}
|
||||
|
||||
void StateMachine::transition_impactToReady()
|
||||
{
|
||||
transition(SwingState_Ready);
|
||||
}
|
||||
|
||||
}}
|
70
apps/openmw/mwvr/realisticcombat.hpp
Normal file
70
apps/openmw/mwvr/realisticcombat.hpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
#ifndef MWVR_REALISTICCOMBAT_H
|
||||
#define MWVR_REALISTICCOMBAT_H
|
||||
|
||||
#include <components/esm/loadweap.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "vrenvironment.hpp"
|
||||
#include "openxrsession.hpp"
|
||||
|
||||
namespace MWVR { namespace RealisticCombat {
|
||||
enum SwingState
|
||||
{
|
||||
SwingState_Ready = 0,
|
||||
SwingState_Swinging = 1,
|
||||
SwingState_Impact = 2
|
||||
};
|
||||
|
||||
struct StateMachine
|
||||
{
|
||||
// TODO: These should be configurable
|
||||
const float minVelocity = 1.f;
|
||||
const float maxVelocity = 4.f;
|
||||
|
||||
float velocity = 0.f;
|
||||
float maxSwingVelocity = 0.f;
|
||||
|
||||
SwingState state = SwingState_Ready;
|
||||
MWWorld::Ptr ptr = MWWorld::Ptr();
|
||||
int swingType = -1;
|
||||
float strength = 0.f;
|
||||
|
||||
float thrustVelocity{ 0.f };
|
||||
float slashVelocity{ 0.f };
|
||||
float chopVelocity{ 0.f };
|
||||
|
||||
float minimumPeriod{ .5f };
|
||||
float timeSinceEnteredState = { 0.f };
|
||||
float movementSinceEnteredState = { 0.f };
|
||||
|
||||
bool shouldSwish = false;
|
||||
bool mEnabled = false;
|
||||
|
||||
osg::Vec3 previousPosition{ 0.f,0.f,0.f };
|
||||
|
||||
StateMachine(MWWorld::Ptr ptr);
|
||||
|
||||
void playSwish();
|
||||
void reset();
|
||||
|
||||
void transition(SwingState newState);
|
||||
|
||||
void update(float dt, bool enabled);
|
||||
|
||||
void update_readyState();
|
||||
void transition_readyToSwinging();
|
||||
|
||||
void update_swingingState();
|
||||
void transition_swingingToReady();
|
||||
void transition_swingingToImpact();
|
||||
|
||||
void update_impactState();
|
||||
void transition_impactToReady();
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
#include "openxranimation.hpp"
|
||||
#include "vranimation.hpp"
|
||||
|
||||
#include <osg/UserDataContainer>
|
||||
#include <osg/MatrixTransform>
|
||||
|
@ -47,6 +47,7 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwrender/camera.hpp"
|
||||
#include "../mwrender/rotatecontroller.hpp"
|
||||
|
@ -58,7 +59,7 @@
|
|||
#include "../mwphysics/collisiontype.hpp"
|
||||
#include "../mwphysics/physicssystem.hpp"
|
||||
|
||||
#include "openxrenvironment.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
#include "openxrviewer.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
|
||||
|
@ -177,30 +178,13 @@ void FingerController::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||
// So that we can display a beam to visualize where the player is pointing.
|
||||
|
||||
// Dig up the pointer transform
|
||||
SceneUtil::FindByNameVisitor findPointerVisitor("Pointer Transform", osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
|
||||
tip->accept(findPointerVisitor);
|
||||
auto pointerTransform = findPointerVisitor.mFoundNode;
|
||||
if (pointerTransform)
|
||||
{
|
||||
// Get distance to pointer intersection
|
||||
auto pat = pointerTransform->asTransform()->asPositionAttitudeTransform();
|
||||
auto* world = MWBase::Environment::get().getWorld();
|
||||
if (world)
|
||||
{
|
||||
// TODO: Using the cached value from the input manager makes this off by one frame
|
||||
// So do one otherwise redundant intersection here.
|
||||
MWRender::RayResult result;
|
||||
world->getPointedAtObject(result);
|
||||
float intersected_distance = world->getDistanceToPointedAtObject();
|
||||
|
||||
// Stretch beam to point of intersection.
|
||||
pat->setScale(osg::Vec3(.25f, .25f, intersected_distance));
|
||||
}
|
||||
}
|
||||
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
||||
if (anim)
|
||||
anim->updatePointerTarget();
|
||||
}
|
||||
|
||||
|
||||
OpenXRAnimation::OpenXRAnimation(
|
||||
VRAnimation::VRAnimation(
|
||||
const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
|
||||
bool disableSounds, std::shared_ptr<OpenXRSession> xrSession)
|
||||
// Note that i let it construct as 3rd person and then later update it to VM_VRHeadless
|
||||
|
@ -218,17 +202,17 @@ OpenXRAnimation::OpenXRAnimation(
|
|||
createPointer();
|
||||
}
|
||||
|
||||
OpenXRAnimation::~OpenXRAnimation() {};
|
||||
VRAnimation::~VRAnimation() {};
|
||||
|
||||
void OpenXRAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
|
||||
void VRAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
|
||||
{
|
||||
if (viewMode != VM_VRHeadless)
|
||||
Log(Debug::Warning) << "View mode of OpenXRAnimation may only be VM_VRHeadless";
|
||||
Log(Debug::Warning) << "View mode of VRAnimation may only be VM_VRHeadless";
|
||||
NpcAnimation::setViewMode(VM_VRHeadless);
|
||||
return;
|
||||
}
|
||||
|
||||
void OpenXRAnimation::updateParts()
|
||||
void VRAnimation::updateParts()
|
||||
{
|
||||
NpcAnimation::updateParts();
|
||||
|
||||
|
@ -252,9 +236,10 @@ void OpenXRAnimation::updateParts()
|
|||
removeIndividualPart(ESM::PartReferenceType::PRT_LAnkle);
|
||||
removeIndividualPart(ESM::PartReferenceType::PRT_RAnkle);
|
||||
}
|
||||
void OpenXRAnimation::setPointForward(bool enabled)
|
||||
void VRAnimation::setPointForward(bool enabled)
|
||||
{
|
||||
auto found00 = mNodeMap.find("bip01 r finger1");
|
||||
//auto weapon = mNodeMap.find("weapon");
|
||||
if (found00 != mNodeMap.end())
|
||||
{
|
||||
auto base_joint = found00->second;
|
||||
|
@ -262,27 +247,50 @@ void OpenXRAnimation::setPointForward(bool enabled)
|
|||
assert(second_joint);
|
||||
|
||||
second_joint->removeChild(mPointerTransform);
|
||||
//weapon->second->removeChild(mWeaponDirectionTransform);
|
||||
mWeaponDirectionTransform->removeChild(mPointerGeometry);
|
||||
base_joint->removeUpdateCallback(mIndexFingerControllers[0]);
|
||||
if (enabled)
|
||||
{
|
||||
second_joint->addChild(mPointerTransform);
|
||||
//weapon->second->addChild(mWeaponDirectionTransform);
|
||||
mWeaponDirectionTransform->addChild(mPointerGeometry);
|
||||
base_joint->addUpdateCallback(mIndexFingerControllers[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenXRAnimation::createPointer(void)
|
||||
void VRAnimation::createPointer(void)
|
||||
{
|
||||
mPointerGeometry = createPointerGeometry();
|
||||
mPointerTransform = new osg::PositionAttitudeTransform();
|
||||
mPointerTransform = new osg::MatrixTransform();
|
||||
mPointerTransform->addChild(mPointerGeometry);
|
||||
mPointerTransform->asPositionAttitudeTransform()->setAttitude(osg::Quat(osg::DegreesToRadians(90.f), osg::Vec3(0.f, 1.f, 0.f)));
|
||||
mPointerTransform->asPositionAttitudeTransform()->setPosition(osg::Vec3(0.f, 0.f, 0.f));
|
||||
mPointerTransform->asPositionAttitudeTransform()->setScale(osg::Vec3(1.f, 1.f, 1.f));
|
||||
mPointerTransform->setName("Pointer Transform");
|
||||
|
||||
mWeaponPointerTransform = new osg::MatrixTransform();
|
||||
mWeaponPointerTransform->addChild(mPointerGeometry);
|
||||
mWeaponPointerTransform->setMatrix(
|
||||
osg::Matrix::scale(64.f, 1.f, 1.f)
|
||||
);
|
||||
mWeaponPointerTransform->setName("Weapon Pointer");
|
||||
|
||||
mWeaponDirectionTransform = new osg::MatrixTransform();
|
||||
mWeaponDirectionTransform->addChild(mWeaponPointerTransform);
|
||||
mWeaponDirectionTransform->setMatrix(
|
||||
osg::Matrix::rotate(osg::DegreesToRadians(-90.f), osg::Y_AXIS)
|
||||
);
|
||||
mWeaponDirectionTransform->setName("Weapon Direction");
|
||||
|
||||
mWeaponAdjustment = new osg::MatrixTransform();
|
||||
//mWeaponAdjustment->addChild(mWeaponPointerTransform);
|
||||
mWeaponAdjustment->setMatrix(
|
||||
osg::Matrix::rotate(osg::DegreesToRadians(90.f), osg::Y_AXIS)
|
||||
);
|
||||
mWeaponAdjustment->setName("Weapon Adjustment");
|
||||
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Geometry> OpenXRAnimation::createPointerGeometry(void)
|
||||
osg::ref_ptr<osg::Geometry> VRAnimation::createPointerGeometry(void)
|
||||
{
|
||||
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
|
||||
|
||||
|
@ -291,8 +299,8 @@ osg::ref_ptr<osg::Geometry> OpenXRAnimation::createPointerGeometry(void)
|
|||
|
||||
osg::Vec3 vertices[]{
|
||||
{0, 0, 0}, // origin
|
||||
{-1, 1, 1}, // top_left
|
||||
{-1, -1, 1}, // bottom_left
|
||||
{1, 1, -1}, // top_left
|
||||
{1, -1, -1}, // bottom_left
|
||||
{1, -1, 1}, // bottom_right
|
||||
{1, 1, 1}, // top_right
|
||||
};
|
||||
|
@ -350,12 +358,12 @@ osg::ref_ptr<osg::Geometry> OpenXRAnimation::createPointerGeometry(void)
|
|||
return geometry;
|
||||
}
|
||||
|
||||
osg::Vec3f OpenXRAnimation::runAnimation(float timepassed)
|
||||
osg::Vec3f VRAnimation::runAnimation(float timepassed)
|
||||
{
|
||||
return NpcAnimation::runAnimation(timepassed);
|
||||
}
|
||||
|
||||
void OpenXRAnimation::addControllers()
|
||||
void VRAnimation::addControllers()
|
||||
{
|
||||
NpcAnimation::addControllers();
|
||||
|
||||
|
@ -402,14 +410,58 @@ void OpenXRAnimation::addControllers()
|
|||
group->removeChildren(0, parent->getNumChildren());
|
||||
group->addChild(mModelOffset);
|
||||
mModelOffset->addChild(mObjectRoot);
|
||||
|
||||
auto weapon = mNodeMap.find("weapon");
|
||||
weapon->second->addChild(mWeaponDirectionTransform);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
void OpenXRAnimation::enableHeadAnimation(bool)
|
||||
void VRAnimation::enableHeadAnimation(bool)
|
||||
{
|
||||
NpcAnimation::enableHeadAnimation(false);
|
||||
}
|
||||
void OpenXRAnimation::setAccurateAiming(bool)
|
||||
void VRAnimation::setAccurateAiming(bool)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void VRAnimation::updatePointerTarget()
|
||||
{
|
||||
auto* world = MWBase::Environment::get().getWorld();
|
||||
if (world)
|
||||
{
|
||||
mPointerTransform->setMatrix(osg::Matrix::scale(1, 1, 1));
|
||||
mDistanceToPointerTarget = world->getTargetObject(mPointerTarget, mPointerTransform);
|
||||
|
||||
if(mDistanceToPointerTarget >= 0)
|
||||
mPointerTransform->setMatrix(osg::Matrix::scale(mDistanceToPointerTarget, 0.25f, 0.25f));
|
||||
else
|
||||
mPointerTransform->setMatrix(osg::Matrix::scale(10000.f, 0.25f, 0.25f));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
#ifndef MWVR_OPENRXANIMATION_H
|
||||
#define MWVR_OPENRXANIMATION_H
|
||||
#ifndef MWVR_VRANIMATION_H
|
||||
#define MWVR_VRANIMATION_H
|
||||
|
||||
#include "../mwrender/npcanimation.hpp"
|
||||
#include "../mwrender/renderingmanager.hpp"
|
||||
#include "openxrmanager.hpp"
|
||||
#include "openxrsession.hpp"
|
||||
|
||||
|
@ -13,7 +14,7 @@ class FingerController;
|
|||
class ForearmController;
|
||||
|
||||
/// Subclassing NpcAnimation to override behaviours not compatible with VR
|
||||
class OpenXRAnimation : public MWRender::NpcAnimation
|
||||
class VRAnimation : public MWRender::NpcAnimation
|
||||
{
|
||||
protected:
|
||||
virtual void addControllers();
|
||||
|
@ -28,9 +29,9 @@ public:
|
|||
* @param disableSounds Same as \a disableListener but for playing items sounds
|
||||
* @param xrSession The XR session that shall be used to track limbs
|
||||
*/
|
||||
OpenXRAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
|
||||
VRAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
|
||||
bool disableSounds, std::shared_ptr<OpenXRSession> xrSession );
|
||||
virtual ~OpenXRAnimation();
|
||||
virtual ~VRAnimation();
|
||||
|
||||
/// Overridden to always be false
|
||||
virtual void enableHeadAnimation(bool enable);
|
||||
|
@ -55,19 +56,31 @@ public:
|
|||
/// (Used to visualize direction of activation action)
|
||||
void setPointForward(bool enabled);
|
||||
|
||||
bool canPlaceObject();
|
||||
///< @return true if it is possible to place on object where the player is currently pointing
|
||||
|
||||
const MWRender::RayResult& getPointerTarget() const;
|
||||
///< @return pointer to the object the player's melee weapon is currently intersecting.
|
||||
|
||||
void updatePointerTarget();
|
||||
|
||||
public:
|
||||
void createPointer(void);
|
||||
static osg::ref_ptr<osg::Geometry> createPointerGeometry(void);
|
||||
|
||||
private:
|
||||
public:
|
||||
std::shared_ptr<OpenXRSession> mSession;
|
||||
ForearmController* mForearmControllers[2]{};
|
||||
HandController* mHandControllers[2]{};
|
||||
osg::ref_ptr<FingerController> mIndexFingerControllers[2];
|
||||
osg::ref_ptr<osg::MatrixTransform> mModelOffset;
|
||||
osg::ref_ptr<osg::Geometry> mPointerGeometry{ nullptr };
|
||||
osg::ref_ptr<osg::Transform> mPointerTransform{ nullptr };
|
||||
|
||||
osg::ref_ptr<osg::MatrixTransform> mPointerTransform{ nullptr };
|
||||
osg::ref_ptr<osg::MatrixTransform> mWeaponAdjustment{ nullptr };
|
||||
osg::ref_ptr<osg::MatrixTransform> mWeaponDirectionTransform{ nullptr };
|
||||
osg::ref_ptr<osg::MatrixTransform> mWeaponPointerTransform{ nullptr };
|
||||
MWRender::RayResult mPointerTarget{};
|
||||
float mDistanceToPointerTarget{ -1.f };
|
||||
};
|
||||
|
||||
}
|
|
@ -1,30 +1,30 @@
|
|||
#include "openxrenvironment.hpp"
|
||||
#include "vrenvironment.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "openxranimation.hpp"
|
||||
#include "vranimation.hpp"
|
||||
#include "openxrinputmanager.hpp"
|
||||
#include "openxrsession.hpp"
|
||||
#include "openxrmenu.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
MWVR::OpenXREnvironment *MWVR::OpenXREnvironment::sThis = 0;
|
||||
MWVR::Environment *MWVR::Environment::sThis = 0;
|
||||
|
||||
MWVR::OpenXREnvironment::OpenXREnvironment()
|
||||
MWVR::Environment::Environment()
|
||||
: mSession(nullptr)
|
||||
{
|
||||
assert (!sThis);
|
||||
sThis = this;
|
||||
}
|
||||
|
||||
MWVR::OpenXREnvironment::~OpenXREnvironment()
|
||||
MWVR::Environment::~Environment()
|
||||
{
|
||||
cleanup();
|
||||
sThis = 0;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREnvironment::cleanup()
|
||||
void MWVR::Environment::cleanup()
|
||||
{
|
||||
if (mSession)
|
||||
delete mSession;
|
||||
|
@ -43,13 +43,13 @@ void MWVR::OpenXREnvironment::cleanup()
|
|||
mOpenXRManager = nullptr;
|
||||
}
|
||||
|
||||
MWVR::OpenXREnvironment& MWVR::OpenXREnvironment::get()
|
||||
MWVR::Environment& MWVR::Environment::get()
|
||||
{
|
||||
assert (sThis);
|
||||
return *sThis;
|
||||
}
|
||||
|
||||
MWVR::OpenXRInputManager* MWVR::OpenXREnvironment::getInputManager() const
|
||||
MWVR::OpenXRInputManager* MWVR::Environment::getInputManager() const
|
||||
{
|
||||
auto* inputManager = MWBase::Environment::get().getInputManager();
|
||||
assert(inputManager);
|
||||
|
@ -58,63 +58,63 @@ MWVR::OpenXRInputManager* MWVR::OpenXREnvironment::getInputManager() const
|
|||
return xrInputManager;
|
||||
}
|
||||
|
||||
MWVR::OpenXRSession* MWVR::OpenXREnvironment::getSession() const
|
||||
MWVR::OpenXRSession* MWVR::Environment::getSession() const
|
||||
{
|
||||
return mSession;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREnvironment::setSession(MWVR::OpenXRSession* xrSession)
|
||||
void MWVR::Environment::setSession(MWVR::OpenXRSession* xrSession)
|
||||
{
|
||||
mSession = xrSession;
|
||||
}
|
||||
|
||||
MWVR::OpenXRMenuManager* MWVR::OpenXREnvironment::getMenuManager() const
|
||||
MWVR::OpenXRMenuManager* MWVR::Environment::getMenuManager() const
|
||||
{
|
||||
return mMenuManager;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREnvironment::setMenuManager(MWVR::OpenXRMenuManager* menuManager)
|
||||
void MWVR::Environment::setMenuManager(MWVR::OpenXRMenuManager* menuManager)
|
||||
{
|
||||
mMenuManager = menuManager;
|
||||
}
|
||||
|
||||
MWVR::OpenXRAnimation* MWVR::OpenXREnvironment::getPlayerAnimation() const
|
||||
MWVR::VRAnimation* MWVR::Environment::getPlayerAnimation() const
|
||||
{
|
||||
return mPlayerAnimation;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREnvironment::setPlayerAnimation(MWVR::OpenXRAnimation* xrAnimation)
|
||||
void MWVR::Environment::setPlayerAnimation(MWVR::VRAnimation* xrAnimation)
|
||||
{
|
||||
mPlayerAnimation = xrAnimation;
|
||||
}
|
||||
|
||||
|
||||
MWVR::OpenXRViewer* MWVR::OpenXREnvironment::getViewer() const
|
||||
MWVR::OpenXRViewer* MWVR::Environment::getViewer() const
|
||||
{
|
||||
return mViewer;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREnvironment::setViewer(MWVR::OpenXRViewer* xrViewer)
|
||||
void MWVR::Environment::setViewer(MWVR::OpenXRViewer* xrViewer)
|
||||
{
|
||||
mViewer = xrViewer;
|
||||
}
|
||||
|
||||
MWVR::OpenXRManager* MWVR::OpenXREnvironment::getManager() const
|
||||
MWVR::OpenXRManager* MWVR::Environment::getManager() const
|
||||
{
|
||||
return mOpenXRManager;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREnvironment::setManager(MWVR::OpenXRManager* xrManager)
|
||||
void MWVR::Environment::setManager(MWVR::OpenXRManager* xrManager)
|
||||
{
|
||||
mOpenXRManager = xrManager;
|
||||
}
|
||||
|
||||
float MWVR::OpenXREnvironment::unitsPerMeter() const
|
||||
float MWVR::Environment::unitsPerMeter() const
|
||||
{
|
||||
return mUnitsPerMeter;
|
||||
}
|
||||
|
||||
void MWVR::OpenXREnvironment::setUnitsPerMeter(float unitsPerMeter)
|
||||
void MWVR::Environment::setUnitsPerMeter(float unitsPerMeter)
|
||||
{
|
||||
mUnitsPerMeter = unitsPerMeter;
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
#ifndef OPENXR_ENVIRONMENT_H
|
||||
#define OPENXR_ENVIRONMENT_H
|
||||
#ifndef MWVR_ENVIRONMENT_H
|
||||
#define MWVR_ENVIRONMENT_H
|
||||
|
||||
namespace MWVR
|
||||
{
|
||||
class OpenXRAnimation;
|
||||
class VRAnimation;
|
||||
class OpenXRInputManager;
|
||||
class OpenXRSession;
|
||||
class OpenXRMenuManager;
|
||||
|
@ -14,29 +14,29 @@ namespace MWVR
|
|||
///
|
||||
/// This class allows each mw openxr subsystem to access any others subsystem's top-level manager class.
|
||||
///
|
||||
/// \attention OpenXREnvironment takes ownership of the manager class instances it is handed over in
|
||||
/// \attention Environment takes ownership of the manager class instances it is handed over in
|
||||
/// the set* functions.
|
||||
class OpenXREnvironment
|
||||
class Environment
|
||||
{
|
||||
|
||||
static OpenXREnvironment*sThis;
|
||||
static Environment* sThis;
|
||||
|
||||
OpenXREnvironment(const OpenXREnvironment&) = delete;
|
||||
Environment(const Environment&) = delete;
|
||||
///< not implemented
|
||||
|
||||
OpenXREnvironment& operator= (const OpenXREnvironment&) = delete;
|
||||
Environment& operator= (const Environment&) = delete;
|
||||
///< not implemented
|
||||
|
||||
public:
|
||||
|
||||
OpenXREnvironment();
|
||||
Environment();
|
||||
|
||||
~OpenXREnvironment();
|
||||
~Environment();
|
||||
|
||||
void cleanup();
|
||||
///< Delete all mwvr-subsystems.
|
||||
|
||||
static OpenXREnvironment& get();
|
||||
static Environment& get();
|
||||
///< Return instance of this class.
|
||||
|
||||
MWVR::OpenXRInputManager* getInputManager() const;
|
||||
|
@ -48,8 +48,8 @@ namespace MWVR
|
|||
MWVR::OpenXRMenuManager* getMenuManager() const;
|
||||
void setMenuManager(MWVR::OpenXRMenuManager* xrMenuManager);
|
||||
|
||||
MWVR::OpenXRAnimation* getPlayerAnimation() const;
|
||||
void setPlayerAnimation(MWVR::OpenXRAnimation* xrAnimation);
|
||||
MWVR::VRAnimation* getPlayerAnimation() const;
|
||||
void setPlayerAnimation(MWVR::VRAnimation* xrAnimation);
|
||||
|
||||
MWVR::OpenXRSession* getSession() const;
|
||||
void setSession(MWVR::OpenXRSession* xrSession);
|
||||
|
@ -66,7 +66,7 @@ namespace MWVR
|
|||
private:
|
||||
MWVR::OpenXRSession* mSession{ nullptr };
|
||||
MWVR::OpenXRMenuManager* mMenuManager{ nullptr };
|
||||
MWVR::OpenXRAnimation* mPlayerAnimation{ nullptr };
|
||||
MWVR::VRAnimation* mPlayerAnimation{ nullptr };
|
||||
MWVR::OpenXRViewer* mViewer{ nullptr };
|
||||
MWVR::OpenXRManager* mOpenXRManager{ nullptr };
|
||||
float mUnitsPerMeter{ 1.f };
|
|
@ -99,7 +99,7 @@ namespace MWWorld
|
|||
throw std::runtime_error ("class does not have item health");
|
||||
}
|
||||
|
||||
void Class::hit(const Ptr& ptr, float attackStrength, int type) const
|
||||
bool Class::hit(const Ptr& ptr, float attackStrength, int type, bool simulated) const
|
||||
{
|
||||
throw std::runtime_error("class cannot hit");
|
||||
}
|
||||
|
|
|
@ -121,12 +121,14 @@ namespace MWWorld
|
|||
///< Return item max health or throw an exception, if class does not have item health
|
||||
/// (default implementation: throw an exception)
|
||||
|
||||
virtual void hit(const Ptr& ptr, float attackStrength, int type=-1) const;
|
||||
virtual bool hit(const Ptr& ptr, float attackStrength, int type, bool simulated = false ) const;
|
||||
///< Execute a melee hit, using the current weapon. This will check the relevant skills
|
||||
/// of the given attacker, and whoever is hit.
|
||||
/// \param attackStrength how long the attack was charged for, a value in 0-1 range.
|
||||
/// \param type - type of attack, one of the MWMechanics::CreatureStats::AttackType
|
||||
/// enums. ignored for creature attacks.
|
||||
/// \param simulated - If true, this function will only check if a hit would be made, and have no side effects. This parameter has no effect for Creature classes.
|
||||
/// @return True if the attack had a victim, regardless if hit was successful or not.
|
||||
/// (default implementation: throw an exception)
|
||||
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const;
|
||||
|
|
|
@ -70,6 +70,11 @@
|
|||
#include "contentloader.hpp"
|
||||
#include "esmloader.hpp"
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
#include "../mwvr/vrenvironment.hpp"
|
||||
#include "../mwvr/vranimation.hpp"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
@ -1146,15 +1151,29 @@ namespace MWWorld
|
|||
// the origin of hitbox is an actor's front, not center
|
||||
distance += halfExtents.y();
|
||||
|
||||
// special cased for better aiming with the camera
|
||||
// if we do not hit anything, will use the default approach as fallback
|
||||
if (ptr == getPlayerPtr())
|
||||
{
|
||||
#ifdef USE_OPENXR
|
||||
// TODO: Configurable realistic fighting.
|
||||
|
||||
// Use current aim of weapon to impact
|
||||
// TODO: Use bounding box of weapon instead ?
|
||||
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
||||
osg::Matrix worldMatrix = osg::computeLocalToWorld(anim->mWeaponDirectionTransform->getParentalNodePaths()[0]);
|
||||
|
||||
auto result = mPhysics->getHitContact(ptr, worldMatrix.getTrans(), worldMatrix.getRotate(), distance, targets);
|
||||
if (!result.first.isEmpty())
|
||||
Log(Debug::Verbose) << "Hit: " << result.first.getTypeName();
|
||||
return result;
|
||||
#else
|
||||
// special cased for better aiming with the camera
|
||||
// if we do not hit anything, will use the default approach as fallback
|
||||
osg::Vec3f pos = getActorHeadTransform(ptr).getTrans();
|
||||
|
||||
std::pair<MWWorld::Ptr,osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance, targets);
|
||||
if(!result.first.isEmpty())
|
||||
return std::make_pair(result.first, result.second);
|
||||
#endif
|
||||
}
|
||||
|
||||
osg::Vec3f pos = ptr.getRefData().getPosition().asVec3();
|
||||
|
@ -3916,121 +3935,69 @@ namespace MWWorld
|
|||
return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal);
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
void World::getPointedAtObject(MWRender::RayResult& result)
|
||||
float World::getTargetObject(MWRender::RayResult& result, osg::Transform* pointer)
|
||||
{
|
||||
result = {};
|
||||
result.mHit = false;
|
||||
|
||||
if (MWBase::Environment::get().getWindowManager()->isGuiMode() &&
|
||||
MWBase::Environment::get().getWindowManager()->isConsoleMode())
|
||||
auto* windowManager = MWBase::Environment::get().getWindowManager();
|
||||
|
||||
if (windowManager->isGuiMode() && windowManager->isConsoleMode())
|
||||
{
|
||||
return getPointedAtObject(result, getMaxActivationDistance() * 50, false);
|
||||
return getTargetObject(result, pointer, getMaxActivationDistance() * 50, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWRender::RayResult pointedAtObject;
|
||||
getPointedAtObject(pointedAtObject, getActivationDistancePlusTelekinesis(), true);
|
||||
auto ptr = pointedAtObject.mHitObject;
|
||||
float maxDistance = getActivationDistancePlusTelekinesis();
|
||||
MWRender::RayResult rayToObject{};
|
||||
float distance = getTargetObject(rayToObject, pointer, maxDistance, true);
|
||||
auto ptr = rayToObject.mHitObject;
|
||||
if (!ptr.isEmpty() && !ptr.getClass().allowTelekinesis(ptr)
|
||||
&& mDistanceToFacedObject > getMaxActivationDistance() && !MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
return;
|
||||
result = pointedAtObject;
|
||||
}
|
||||
}
|
||||
|
||||
float World::getDistanceToPointedAtObject()
|
||||
{
|
||||
return mDistanceToPointedAtObject;
|
||||
}
|
||||
|
||||
void World::getPointedAtObject(MWRender::RayResult& result, float maxDistance, bool ignorePlayer)
|
||||
{
|
||||
auto sceneRoot = mRendering->getLightRoot();
|
||||
result = {};
|
||||
result.mHit = false;
|
||||
|
||||
// Find the transform giving the finger's pointing direction.
|
||||
SceneUtil::FindByNameVisitor findPointerVisitor("Pointer Transform", osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
|
||||
sceneRoot->accept(findPointerVisitor);
|
||||
auto pointerTransform = findPointerVisitor.mFoundNode;
|
||||
if (pointerTransform)
|
||||
{
|
||||
auto pat = pointerTransform->asTransform()->asPositionAttitudeTransform();
|
||||
|
||||
// Discover the world position of the finger
|
||||
// (This is actually the base of the last joint bone but so long as it is pointing forward it serves the same purpose).
|
||||
osg::Matrix worldMatrix = osg::computeLocalToWorld(pointerTransform->getParentalNodePaths()[0]);
|
||||
pat->computeLocalToWorldMatrix(worldMatrix, nullptr);
|
||||
osg::Vec3 translate;
|
||||
osg::Quat rotation;
|
||||
osg::Vec3 scale;
|
||||
osg::Quat scaleRotation;
|
||||
worldMatrix.decompose(translate, rotation, scale, scaleRotation);
|
||||
osg::Vec3f direction = rotation * osg::Vec3f(-1, 0, 0);
|
||||
direction.normalize();
|
||||
|
||||
osg::Vec3f raySource = translate;
|
||||
osg::Vec3f rayTarget = translate + direction * maxDistance;
|
||||
|
||||
auto rayToObject = mRendering->castRay(raySource, rayTarget, ignorePlayer, false);
|
||||
if (rayToObject.mHit)
|
||||
mDistanceToPointedAtObject = (rayToObject.mHitPointWorld - raySource).length();
|
||||
else
|
||||
// Leave a very large but not too large number to permit visualizing a beam going into "infinity"
|
||||
mDistanceToPointedAtObject = 10000.f;
|
||||
&& distance > getMaxActivationDistance() && !MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
return -1.f;
|
||||
result = rayToObject;
|
||||
return distance;
|
||||
}
|
||||
return -1.f;
|
||||
}
|
||||
|
||||
MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& object, int amount)
|
||||
float World::getTargetObject(MWRender::RayResult& result, osg::Transform* pointer, float maxDistance, bool ignorePlayer)
|
||||
{
|
||||
result = mRendering->castRay(pointer, maxDistance, ignorePlayer, false);
|
||||
if(result.mHit)
|
||||
return result.mRatio * maxDistance;
|
||||
return -1.f;
|
||||
}
|
||||
|
||||
MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& object, const MWRender::RayResult& ray, int amount)
|
||||
{
|
||||
const float maxDist = 200.f;
|
||||
|
||||
MWRender::RayResult pointedAt;
|
||||
getPointedAtObject(pointedAt);
|
||||
|
||||
CellStore* cell = getPlayerPtr().getCell();
|
||||
|
||||
ESM::Position pos = getPlayerPtr().getRefData().getPosition();
|
||||
|
||||
if (pointedAt.mHit)
|
||||
if (ray.mHit && !ray.mHitObject.isEmpty())
|
||||
{
|
||||
pos.pos[0] = pointedAt.mHitPointWorld.x();
|
||||
pos.pos[1] = pointedAt.mHitPointWorld.y();
|
||||
pos.pos[2] = pointedAt.mHitPointWorld.z();
|
||||
pos.pos[0] = ray.mHitPointWorld.x();
|
||||
pos.pos[1] = ray.mHitPointWorld.y();
|
||||
pos.pos[2] = ray.mHitPointWorld.z();
|
||||
}
|
||||
// We want only the Z part of the player's rotation
|
||||
// TODO: Use the hand's orientation?
|
||||
// TODO: Use the hand to orient in VR?
|
||||
pos.rot[0] = 0;
|
||||
pos.rot[1] = 0;
|
||||
|
||||
// copy the object and set its count
|
||||
Ptr dropped = copyObjectToCell(object, cell, pos, amount, true);
|
||||
MWWorld::Ptr dropped = copyObjectToCell(object, cell, pos, amount, true);
|
||||
|
||||
// only the player place items in the world, so no need to check actor
|
||||
PCDropped(dropped);
|
||||
|
||||
return dropped;
|
||||
}
|
||||
|
||||
bool World::canPlaceObject()
|
||||
MWPhysics::PhysicsSystem* World::getPhysicsSystem(void)
|
||||
{
|
||||
const float maxDist = 200.f;
|
||||
MWRender::RayResult pointedAt;
|
||||
getPointedAtObject(pointedAt);
|
||||
|
||||
if (pointedAt.mHit)
|
||||
{
|
||||
// check if the wanted position is on a flat surface, and not e.g. against a vertical wall
|
||||
if (std::acos((pointedAt.mHitNormalWorld / pointedAt.mHitNormalWorld.length()) * osg::Vec3f(0, 0, 1)) >= osg::DegreesToRadians(30.f))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return mPhysics.get();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwrender/renderingmanager.hpp"
|
||||
|
||||
#include "ptr.hpp"
|
||||
#include "scene.hpp"
|
||||
#include "esmstore.hpp"
|
||||
|
@ -729,24 +731,20 @@ namespace MWWorld
|
|||
|
||||
bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override;
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
private:
|
||||
float mDistanceToPointedAtObject;
|
||||
void getPointedAtObject(MWRender::RayResult& result, float maxDistance, bool ignorePlayer = true);
|
||||
public:
|
||||
void getPointedAtObject(MWRender::RayResult& result) override;
|
||||
///< Return pointer to the object and/or node the player is currently pointing at
|
||||
float getDistanceToPointedAtObject() override;
|
||||
///< Return the distance to the object and/or node the player is currently pointing at
|
||||
/// @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
|
||||
float getTargetObject(MWRender::RayResult& result, osg::Transform* pointer) override;
|
||||
float getTargetObject(MWRender::RayResult& result, osg::Transform* pointer, float maxDistance, bool ignorePlayer) override;
|
||||
|
||||
MWWorld::Ptr placeObject(const MWWorld::ConstPtr& object, int amount) override;
|
||||
///< copy and place an object into the gameworld where the player is currently pointing
|
||||
|
||||
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
|
||||
/// @param object
|
||||
/// @param world position to place object
|
||||
/// @param number of objects to place
|
||||
|
||||
bool canPlaceObject() override;
|
||||
///< @return true if it is possible to place on object where the player is currently pointing
|
||||
#endif
|
||||
|
||||
MWPhysics::PhysicsSystem* getPhysicsSystem(void) override;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue