mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-10-04 19:56:30 +00:00
Merge remote-tracking branch 'remotes/origin/multiview_test_branch' into openxr_vr_geometryshader_feature_branch
This commit is contained in:
commit
b98b4db4c1
39 changed files with 1065 additions and 577 deletions
|
@ -8,6 +8,7 @@
|
||||||
Bug #2473: Unable to overstock merchants
|
Bug #2473: Unable to overstock merchants
|
||||||
Bug #2798: Mutable ESM records
|
Bug #2798: Mutable ESM records
|
||||||
Bug #2976 [reopened]: Issues combining settings from the command line and both config files
|
Bug #2976 [reopened]: Issues combining settings from the command line and both config files
|
||||||
|
Bug #3372: Projectiles and magic bolts go through moving targets
|
||||||
Bug #3676: NiParticleColorModifier isn't applied properly
|
Bug #3676: NiParticleColorModifier isn't applied properly
|
||||||
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
||||||
Bug #3789: Crash in visitEffectSources while in battle
|
Bug #3789: Crash in visitEffectSources while in battle
|
||||||
|
@ -67,6 +68,7 @@
|
||||||
Bug #5656: Sneaking characters block hits while standing
|
Bug #5656: Sneaking characters block hits while standing
|
||||||
Bug #5661: Region sounds don't play at the right interval
|
Bug #5661: Region sounds don't play at the right interval
|
||||||
Bug #5688: Water shader broken indoors with enable indoor shadows = false
|
Bug #5688: Water shader broken indoors with enable indoor shadows = false
|
||||||
|
Bug #5695: ExplodeSpell for actors doesn't target the ground
|
||||||
Bug #5703: OpenMW-CS menu system crashing on XFCE
|
Bug #5703: OpenMW-CS menu system crashing on XFCE
|
||||||
Feature #390: 3rd person look "over the shoulder"
|
Feature #390: 3rd person look "over the shoulder"
|
||||||
Feature #2386: Distant Statics in the form of Object Paging
|
Feature #2386: Distant Statics in the form of Object Paging
|
||||||
|
|
|
@ -66,13 +66,13 @@ add_openmw_dir (mwworld
|
||||||
cells localscripts customdata inventorystore ptr actionopen actionread actionharvest
|
cells localscripts customdata inventorystore ptr actionopen actionread actionharvest
|
||||||
actionequip timestamp actionalchemy cellstore actionapply actioneat
|
actionequip timestamp actionalchemy cellstore actionapply actioneat
|
||||||
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
|
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
|
||||||
contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager
|
contentloader esmloader actiontrap cellreflist cellref weather projectilemanager
|
||||||
cellpreloader datetimemanager
|
cellpreloader datetimemanager
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwphysics
|
add_openmw_dir (mwphysics
|
||||||
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
|
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
|
||||||
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver
|
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile
|
||||||
closestnotmeconvexresultcallback raycasting mtphysics
|
closestnotmeconvexresultcallback raycasting mtphysics
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -399,8 +399,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
||||||
, mNewGame (false)
|
, mNewGame (false)
|
||||||
, mCfgMgr(configurationManager)
|
, mCfgMgr(configurationManager)
|
||||||
{
|
{
|
||||||
MWClass::registerClasses();
|
|
||||||
|
|
||||||
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); // We use only gamepads
|
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); // We use only gamepads
|
||||||
|
|
||||||
Uint32 flags = SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE|SDL_INIT_GAMECONTROLLER|SDL_INIT_JOYSTICK|SDL_INIT_SENSOR;
|
Uint32 flags = SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE|SDL_INIT_GAMECONTROLLER|SDL_INIT_JOYSTICK|SDL_INIT_SENSOR;
|
||||||
|
@ -771,7 +769,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VR mode will override this setting by setting mStereoOverride.
|
// VR mode will override this setting by setting mStereoOverride.
|
||||||
mStereoEnabled = mStereoOverride || Settings::Manager::getBool("stereo enabled", "Stereo");
|
mStereoEnabled = mEnvironment.getVrMode() || Settings::Manager::getBool("stereo enabled", "Stereo");
|
||||||
|
|
||||||
// geometry shader must be enabled before the RenderingManager sets up any shaders
|
// geometry shader must be enabled before the RenderingManager sets up any shaders
|
||||||
// therefore this part is separate from the rest of stereo setup.
|
// therefore this part is separate from the rest of stereo setup.
|
||||||
|
@ -907,6 +905,8 @@ void OMW::Engine::go()
|
||||||
std::string settingspath;
|
std::string settingspath;
|
||||||
settingspath = loadSettings (settings);
|
settingspath = loadSettings (settings);
|
||||||
|
|
||||||
|
MWClass::registerClasses();
|
||||||
|
|
||||||
// Create encoder
|
// Create encoder
|
||||||
mEncoder = new ToUTF8::Utf8Encoder(mEncoding);
|
mEncoder = new ToUTF8::Utf8Encoder(mEncoding);
|
||||||
|
|
||||||
|
|
|
@ -153,13 +153,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||||
cfgMgr.mergeComposingVariables(variables, composingVariables, desc);
|
cfgMgr.mergeComposingVariables(variables, composingVariables, desc);
|
||||||
|
|
||||||
Version::Version v = Version::getOpenmwVersion(variables["resources"].as<Files::EscapePath>().mPath.string());
|
Version::Version v = Version::getOpenmwVersion(variables["resources"].as<Files::EscapePath>().mPath.string());
|
||||||
std::cout << v.describe() << std::endl;
|
Log(Debug::Info) << v.describe();
|
||||||
|
|
||||||
engine.setGrabMouse(!variables["no-grab"].as<bool>());
|
engine.setGrabMouse(!variables["no-grab"].as<bool>());
|
||||||
|
|
||||||
// Font encoding settings
|
// Font encoding settings
|
||||||
std::string encoding(variables["encoding"].as<Files::EscapeHashString>().toStdString());
|
std::string encoding(variables["encoding"].as<Files::EscapeHashString>().toStdString());
|
||||||
std::cout << ToUTF8::encodingUsingMessage(encoding) << std::endl;
|
Log(Debug::Info) << ToUTF8::encodingUsingMessage(encoding);
|
||||||
engine.setEncoding(ToUTF8::calculateEncoding(encoding));
|
engine.setEncoding(ToUTF8::calculateEncoding(encoding));
|
||||||
|
|
||||||
// directory settings
|
// directory settings
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <components/esm/loadcont.hpp>
|
#include <components/esm/loadcont.hpp>
|
||||||
#include <components/esm/containerstate.hpp>
|
#include <components/esm/containerstate.hpp>
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
@ -59,6 +60,11 @@ namespace MWClass
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Container::Container()
|
||||||
|
{
|
||||||
|
mHarvestEnabled = Settings::Manager::getBool("graphic herbalism", "Game");
|
||||||
|
}
|
||||||
|
|
||||||
void Container::ensureCustomData (const MWWorld::Ptr& ptr) const
|
void Container::ensureCustomData (const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
if (!ptr.getRefData().getCustomData())
|
if (!ptr.getRefData().getCustomData())
|
||||||
|
@ -72,8 +78,10 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canBeHarvested(const MWWorld::ConstPtr& ptr)
|
bool Container::canBeHarvested(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
|
if (!mHarvestEnabled)
|
||||||
|
return false;
|
||||||
const MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr);
|
const MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr);
|
||||||
if (animation == nullptr)
|
if (animation == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -30,11 +30,16 @@ namespace MWClass
|
||||||
|
|
||||||
class Container : public MWWorld::Class
|
class Container : public MWWorld::Class
|
||||||
{
|
{
|
||||||
|
bool mHarvestEnabled;
|
||||||
|
|
||||||
void ensureCustomData (const MWWorld::Ptr& ptr) const;
|
void ensureCustomData (const MWWorld::Ptr& ptr) const;
|
||||||
|
|
||||||
MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;
|
MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;
|
||||||
|
|
||||||
|
bool canBeHarvested(const MWWorld::ConstPtr& ptr) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Container();
|
||||||
|
|
||||||
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
|
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
|
||||||
///< Add reference into a cell for rendering
|
///< Add reference into a cell for rendering
|
||||||
|
|
|
@ -48,9 +48,9 @@ namespace MWGui
|
||||||
const MWWorld::ESMStore &store =
|
const MWWorld::ESMStore &store =
|
||||||
MWBase::Environment::get().getWorld()->getStore();
|
MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < effects.mList.size(); ++i)
|
for (const auto& effect : effects.mList)
|
||||||
{
|
{
|
||||||
short effectId = effects.mList[i].mEffectID;
|
short effectId = effect.mEffectID;
|
||||||
|
|
||||||
if (effectId != -1)
|
if (effectId != -1)
|
||||||
{
|
{
|
||||||
|
@ -59,14 +59,14 @@ namespace MWGui
|
||||||
std::string effectIDStr = ESM::MagicEffect::effectIdToString(effectId);
|
std::string effectIDStr = ESM::MagicEffect::effectIdToString(effectId);
|
||||||
std::string fullEffectName = wm->getGameSettingString(effectIDStr, "");
|
std::string fullEffectName = wm->getGameSettingString(effectIDStr, "");
|
||||||
|
|
||||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill && effects.mList[i].mSkill != -1)
|
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill && effect.mSkill != -1)
|
||||||
{
|
{
|
||||||
fullEffectName += " " + wm->getGameSettingString(ESM::Skill::sSkillNameIds[effects.mList[i].mSkill], "");
|
fullEffectName += " " + wm->getGameSettingString(ESM::Skill::sSkillNameIds[effect.mSkill], "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute && effects.mList[i].mAttribute != -1)
|
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute && effect.mAttribute != -1)
|
||||||
{
|
{
|
||||||
fullEffectName += " " + wm->getGameSettingString(ESM::Attribute::sGmstAttributeIds[effects.mList[i].mAttribute], "");
|
fullEffectName += " " + wm->getGameSettingString(ESM::Attribute::sGmstAttributeIds[effect.mAttribute], "");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string convert = Misc::StringUtils::lowerCaseUtf8(fullEffectName);
|
std::string convert = Misc::StringUtils::lowerCaseUtf8(fullEffectName);
|
||||||
|
|
|
@ -46,27 +46,29 @@ bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::Charac
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f targetPos = target.getRefData().getPosition().asVec3();
|
|
||||||
if (target.getClass().isActor())
|
|
||||||
{
|
|
||||||
osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(target);
|
|
||||||
targetPos.z() += halfExtents.z() * 2 * 0.75f;
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
|
||||||
osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
|
|
||||||
actorPos.z() += halfExtents.z() * 2 * 0.75f;
|
|
||||||
|
|
||||||
osg::Vec3f dir = targetPos - actorPos;
|
|
||||||
|
|
||||||
bool turned = smoothTurn(actor, getZAngleToDir(dir), 2, osg::DegreesToRadians(3.f));
|
|
||||||
turned &= smoothTurn(actor, getXAngleToDir(dir), 0, osg::DegreesToRadians(3.f));
|
|
||||||
|
|
||||||
if (!turned)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::Vec3f targetPos = target.getRefData().getPosition().asVec3();
|
||||||
|
// If the target of an on-target spell is an actor that is not the caster
|
||||||
|
// the target position must be adjusted so that it's not casted at the actor's feet.
|
||||||
|
if (target != actor && target.getClass().isActor())
|
||||||
|
{
|
||||||
|
osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(target);
|
||||||
|
targetPos.z() += halfExtents.z() * 2 * 0.75f;
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
||||||
|
osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
|
||||||
|
actorPos.z() += halfExtents.z() * 2 * 0.75f;
|
||||||
|
|
||||||
|
osg::Vec3f dir = targetPos - actorPos;
|
||||||
|
|
||||||
|
bool turned = smoothTurn(actor, getZAngleToDir(dir), 2, osg::DegreesToRadians(3.f));
|
||||||
|
turned &= smoothTurn(actor, getXAngleToDir(dir), 0, osg::DegreesToRadians(3.f));
|
||||||
|
|
||||||
|
if (!turned)
|
||||||
|
return false;
|
||||||
|
|
||||||
// Check if the actor is already casting another spell
|
// Check if the actor is already casting another spell
|
||||||
bool isCasting = MWBase::Environment::get().getMechanicsManager()->isCastingSpell(actor);
|
bool isCasting = MWBase::Environment::get().getMechanicsManager()->isCastingSpell(actor);
|
||||||
if (isCasting && !mCasting)
|
if (isCasting && !mCasting)
|
||||||
|
|
|
@ -70,7 +70,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic
|
||||||
mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
|
mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
|
||||||
mCollisionObject->setActivationState(DISABLE_DEACTIVATION);
|
mCollisionObject->setActivationState(DISABLE_DEACTIVATION);
|
||||||
mCollisionObject->setCollisionShape(mShape.get());
|
mCollisionObject->setCollisionShape(mShape.get());
|
||||||
mCollisionObject->setUserPointer(static_cast<PtrHolder*>(this));
|
mCollisionObject->setUserPointer(this);
|
||||||
|
|
||||||
updateRotation();
|
updateRotation();
|
||||||
updateScale();
|
updateScale();
|
||||||
|
|
|
@ -2,6 +2,14 @@
|
||||||
|
|
||||||
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
||||||
|
|
||||||
|
#include <components/misc/convert.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
|
||||||
|
#include "collisiontype.hpp"
|
||||||
|
#include "projectile.hpp"
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
ClosestNotMeConvexResultCallback::ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot)
|
ClosestNotMeConvexResultCallback::ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot)
|
||||||
|
@ -10,11 +18,26 @@ namespace MWPhysics
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
btScalar ClosestNotMeConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
|
btScalar ClosestNotMeConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
|
||||||
{
|
{
|
||||||
if (convexResult.m_hitCollisionObject == mMe)
|
if (convexResult.m_hitCollisionObject == mMe)
|
||||||
return btScalar(1);
|
return btScalar(1);
|
||||||
|
|
||||||
|
if (convexResult.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Projectile)
|
||||||
|
{
|
||||||
|
auto* projectileHolder = static_cast<Projectile*>(convexResult.m_hitCollisionObject->getUserPointer());
|
||||||
|
if (!projectileHolder->isActive())
|
||||||
|
return btScalar(1);
|
||||||
|
auto* targetHolder = static_cast<PtrHolder*>(mMe->getUserPointer());
|
||||||
|
const MWWorld::Ptr target = targetHolder->getPtr();
|
||||||
|
// do nothing if we hit the caster. Sometimes the launching origin is inside of caster collision shape
|
||||||
|
if (projectileHolder->getCaster() != target)
|
||||||
|
{
|
||||||
|
projectileHolder->hit(target, convexResult.m_hitPointLocal, convexResult.m_hitNormalLocal);
|
||||||
|
return btScalar(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
btVector3 hitNormalWorld;
|
btVector3 hitNormalWorld;
|
||||||
if (normalInWorldSpace)
|
if (normalInWorldSpace)
|
||||||
hitNormalWorld = convexResult.m_hitNormalLocal;
|
hitNormalWorld = convexResult.m_hitNormalLocal;
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
#include "closestnotmerayresultcallback.hpp"
|
#include "closestnotmerayresultcallback.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
|
#include "actor.hpp"
|
||||||
|
#include "collisiontype.hpp"
|
||||||
|
#include "projectile.hpp"
|
||||||
#include "ptrholder.hpp"
|
#include "ptrholder.hpp"
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector<const btCollisionObject*>& targets, const btVector3& from, const btVector3& to)
|
ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to, Projectile* proj)
|
||||||
: btCollisionWorld::ClosestRayResultCallback(from, to)
|
: btCollisionWorld::ClosestRayResultCallback(from, to)
|
||||||
, mMe(me), mTargets(targets)
|
, mMe(me), mTargets(std::move(targets)), mProjectile(proj)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,11 +28,27 @@ namespace MWPhysics
|
||||||
{
|
{
|
||||||
if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end()))
|
if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end()))
|
||||||
{
|
{
|
||||||
PtrHolder* holder = static_cast<PtrHolder*>(rayResult.m_collisionObject->getUserPointer());
|
auto* holder = static_cast<PtrHolder*>(rayResult.m_collisionObject->getUserPointer());
|
||||||
if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor())
|
if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor())
|
||||||
return 1.f;
|
return 1.f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
|
||||||
|
btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
||||||
|
if (mProjectile)
|
||||||
|
{
|
||||||
|
auto* holder = static_cast<PtrHolder*>(rayResult.m_collisionObject->getUserPointer());
|
||||||
|
if (auto* target = dynamic_cast<Actor*>(holder))
|
||||||
|
{
|
||||||
|
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||||
|
}
|
||||||
|
else if (auto* target = dynamic_cast<Projectile*>(holder))
|
||||||
|
{
|
||||||
|
target->hit(mProjectile->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||||
|
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rayResult.m_hitFraction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,18 @@ class btCollisionObject;
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
|
class Projectile;
|
||||||
|
|
||||||
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
|
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector<const btCollisionObject*>& targets, const btVector3& from, const btVector3& to);
|
ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to, Projectile* proj=nullptr);
|
||||||
|
|
||||||
btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override;
|
btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override;
|
||||||
private:
|
private:
|
||||||
const btCollisionObject* mMe;
|
const btCollisionObject* mMe;
|
||||||
const std::vector<const btCollisionObject*> mTargets;
|
const std::vector<const btCollisionObject*> mTargets;
|
||||||
|
Projectile* mProjectile;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "mtphysics.hpp"
|
#include "mtphysics.hpp"
|
||||||
#include "object.hpp"
|
#include "object.hpp"
|
||||||
#include "physicssystem.hpp"
|
#include "physicssystem.hpp"
|
||||||
|
#include "projectile.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -297,12 +298,19 @@ namespace MWPhysics
|
||||||
return mPreviousMovementResults;
|
return mPreviousMovementResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PtrPositionList& PhysicsTaskScheduler::resetSimulation()
|
const PtrPositionList& PhysicsTaskScheduler::resetSimulation(const ActorMap& actors)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mSimulationMutex);
|
std::unique_lock lock(mSimulationMutex);
|
||||||
mMovementResults.clear();
|
mMovementResults.clear();
|
||||||
mPreviousMovementResults.clear();
|
mPreviousMovementResults.clear();
|
||||||
mActorsFrameData.clear();
|
mActorsFrameData.clear();
|
||||||
|
|
||||||
|
for (const auto& [_, actor] : actors)
|
||||||
|
{
|
||||||
|
actor->resetPosition();
|
||||||
|
actor->setStandingOnPtr(nullptr);
|
||||||
|
mMovementResults[actor->getPtr()] = actor->getWorldPosition();
|
||||||
|
}
|
||||||
return mMovementResults;
|
return mMovementResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,6 +456,11 @@ namespace MWPhysics
|
||||||
object->commitPositionChange();
|
object->commitPositionChange();
|
||||||
mCollisionWorld->updateSingleAabb(object->getCollisionObject());
|
mCollisionWorld->updateSingleAabb(object->getCollisionObject());
|
||||||
}
|
}
|
||||||
|
else if (const auto projectile = std::dynamic_pointer_cast<Projectile>(p))
|
||||||
|
{
|
||||||
|
projectile->commitPositionChange();
|
||||||
|
mCollisionWorld->updateSingleAabb(projectile->getCollisionObject());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace MWPhysics
|
||||||
/// @return new position of each actor
|
/// @return new position of each actor
|
||||||
const PtrPositionList& moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
const PtrPositionList& moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
||||||
|
|
||||||
const PtrPositionList& resetSimulation();
|
const PtrPositionList& resetSimulation(const ActorMap& actors);
|
||||||
|
|
||||||
// Thread safe wrappers
|
// Thread safe wrappers
|
||||||
void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const;
|
void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const;
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace MWPhysics
|
||||||
mCollisionObject.reset(new btCollisionObject);
|
mCollisionObject.reset(new btCollisionObject);
|
||||||
mCollisionObject->setCollisionShape(shapeInstance->getCollisionShape());
|
mCollisionObject->setCollisionShape(shapeInstance->getCollisionShape());
|
||||||
|
|
||||||
mCollisionObject->setUserPointer(static_cast<PtrHolder*>(this));
|
mCollisionObject->setUserPointer(this);
|
||||||
|
|
||||||
setScale(ptr.getCellRef().getScale());
|
setScale(ptr.getCellRef().getScale());
|
||||||
setRotation(Misc::Convert::toBullet(ptr.getRefData().getBaseNode()->getAttitude()));
|
setRotation(Misc::Convert::toBullet(ptr.getRefData().getBaseNode()->getAttitude()));
|
||||||
|
|
|
@ -46,6 +46,8 @@
|
||||||
|
|
||||||
#include "collisiontype.hpp"
|
#include "collisiontype.hpp"
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
|
|
||||||
|
#include "projectile.hpp"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "object.hpp"
|
#include "object.hpp"
|
||||||
#include "heightfield.hpp"
|
#include "heightfield.hpp"
|
||||||
|
@ -64,6 +66,7 @@ namespace MWPhysics
|
||||||
, mResourceSystem(resourceSystem)
|
, mResourceSystem(resourceSystem)
|
||||||
, mDebugDrawEnabled(false)
|
, mDebugDrawEnabled(false)
|
||||||
, mTimeAccum(0.0f)
|
, mTimeAccum(0.0f)
|
||||||
|
, mProjectileId(0)
|
||||||
, mWaterHeight(0)
|
, mWaterHeight(0)
|
||||||
, mWaterEnabled(false)
|
, mWaterEnabled(false)
|
||||||
, mParentNode(parentNode)
|
, mParentNode(parentNode)
|
||||||
|
@ -112,7 +115,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
mObjects.clear();
|
mObjects.clear();
|
||||||
mActors.clear();
|
mActors.clear();
|
||||||
|
mProjectiles.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue)
|
void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue)
|
||||||
|
@ -248,7 +251,7 @@ namespace MWPhysics
|
||||||
return 0.f;
|
return 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
RayCastingResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr> targets, int mask, int group) const
|
RayCastingResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr> targets, int mask, int group, int projId) const
|
||||||
{
|
{
|
||||||
if (from == to)
|
if (from == to)
|
||||||
{
|
{
|
||||||
|
@ -285,7 +288,7 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo);
|
ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo, getProjectile(projId));
|
||||||
resultCallback.m_collisionFilterGroup = group;
|
resultCallback.m_collisionFilterGroup = group;
|
||||||
resultCallback.m_collisionFilterMask = mask;
|
resultCallback.m_collisionFilterMask = mask;
|
||||||
|
|
||||||
|
@ -502,6 +505,13 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysicsSystem::removeProjectile(const int projectileId)
|
||||||
|
{
|
||||||
|
ProjectileMap::iterator foundProjectile = mProjectiles.find(projectileId);
|
||||||
|
if (foundProjectile != mProjectiles.end())
|
||||||
|
mProjectiles.erase(foundProjectile);
|
||||||
|
}
|
||||||
|
|
||||||
void PhysicsSystem::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated)
|
void PhysicsSystem::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated)
|
||||||
{
|
{
|
||||||
ObjectMap::iterator found = mObjects.find(old);
|
ObjectMap::iterator found = mObjects.find(old);
|
||||||
|
@ -553,6 +563,14 @@ namespace MWPhysics
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Projectile* PhysicsSystem::getProjectile(int projectileId) const
|
||||||
|
{
|
||||||
|
ProjectileMap::const_iterator found = mProjectiles.find(projectileId);
|
||||||
|
if (found != mProjectiles.end())
|
||||||
|
return found->second.get();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void PhysicsSystem::updateScale(const MWWorld::Ptr &ptr)
|
void PhysicsSystem::updateScale(const MWWorld::Ptr &ptr)
|
||||||
{
|
{
|
||||||
ObjectMap::iterator found = mObjects.find(ptr);
|
ObjectMap::iterator found = mObjects.find(ptr);
|
||||||
|
@ -572,6 +590,17 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysicsSystem::updateProjectile(const int projectileId, const osg::Vec3f &position)
|
||||||
|
{
|
||||||
|
ProjectileMap::iterator foundProjectile = mProjectiles.find(projectileId);
|
||||||
|
if (foundProjectile != mProjectiles.end())
|
||||||
|
{
|
||||||
|
foundProjectile->second->setPosition(position);
|
||||||
|
mTaskScheduler->updateSingleAabb(foundProjectile->second);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr)
|
void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr)
|
||||||
{
|
{
|
||||||
ObjectMap::iterator found = mObjects.find(ptr);
|
ObjectMap::iterator found = mObjects.find(ptr);
|
||||||
|
@ -632,6 +661,15 @@ namespace MWPhysics
|
||||||
mActors.emplace(ptr, std::move(actor));
|
mActors.emplace(ptr, std::move(actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position)
|
||||||
|
{
|
||||||
|
mProjectileId++;
|
||||||
|
auto projectile = std::make_shared<Projectile>(mProjectileId, caster, position, mTaskScheduler.get(), this);
|
||||||
|
mProjectiles.emplace(mProjectileId, std::move(projectile));
|
||||||
|
|
||||||
|
return mProjectileId;
|
||||||
|
}
|
||||||
|
|
||||||
bool PhysicsSystem::toggleCollisionMode()
|
bool PhysicsSystem::toggleCollisionMode()
|
||||||
{
|
{
|
||||||
ActorMap::iterator found = mActors.find(MWMechanics::getPlayer());
|
ActorMap::iterator found = mActors.find(MWMechanics::getPlayer());
|
||||||
|
@ -677,14 +715,7 @@ namespace MWPhysics
|
||||||
mTimeAccum -= numSteps * mPhysicsDt;
|
mTimeAccum -= numSteps * mPhysicsDt;
|
||||||
|
|
||||||
if (skipSimulation)
|
if (skipSimulation)
|
||||||
{
|
return mTaskScheduler->resetSimulation(mActors);
|
||||||
for (auto& [_, actor] : mActors)
|
|
||||||
{
|
|
||||||
actor->resetPosition();
|
|
||||||
actor->setStandingOnPtr(nullptr);
|
|
||||||
}
|
|
||||||
return mTaskScheduler->resetSimulation();
|
|
||||||
}
|
|
||||||
|
|
||||||
return mTaskScheduler->moveActors(numSteps, mTimeAccum, prepareFrameData(numSteps), frameStart, frameNumber, stats);
|
return mTaskScheduler->moveActors(numSteps, mTimeAccum, prepareFrameData(numSteps), frameStart, frameNumber, stats);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,9 @@ namespace MWPhysics
|
||||||
class Object;
|
class Object;
|
||||||
class Actor;
|
class Actor;
|
||||||
class PhysicsTaskScheduler;
|
class PhysicsTaskScheduler;
|
||||||
|
class Projectile;
|
||||||
|
|
||||||
|
using ActorMap = std::map<MWWorld::ConstPtr, std::shared_ptr<Actor>>;
|
||||||
|
|
||||||
struct ContactPoint
|
struct ContactPoint
|
||||||
{
|
{
|
||||||
|
@ -125,6 +128,10 @@ namespace MWPhysics
|
||||||
void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World);
|
void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World);
|
||||||
void addActor (const MWWorld::Ptr& ptr, const std::string& mesh);
|
void addActor (const MWWorld::Ptr& ptr, const std::string& mesh);
|
||||||
|
|
||||||
|
int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position);
|
||||||
|
void updateProjectile(const int projectileId, const osg::Vec3f &position);
|
||||||
|
void removeProjectile(const int projectileId);
|
||||||
|
|
||||||
void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated);
|
void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated);
|
||||||
|
|
||||||
Actor* getActor(const MWWorld::Ptr& ptr);
|
Actor* getActor(const MWWorld::Ptr& ptr);
|
||||||
|
@ -132,6 +139,8 @@ namespace MWPhysics
|
||||||
|
|
||||||
const Object* getObject(const MWWorld::ConstPtr& ptr) const;
|
const Object* getObject(const MWWorld::ConstPtr& ptr) const;
|
||||||
|
|
||||||
|
Projectile* getProjectile(int projectileId) const;
|
||||||
|
|
||||||
// Object or Actor
|
// Object or Actor
|
||||||
void remove (const MWWorld::Ptr& ptr);
|
void remove (const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
|
@ -139,7 +148,6 @@ namespace MWPhysics
|
||||||
void updateRotation (const MWWorld::Ptr& ptr);
|
void updateRotation (const MWWorld::Ptr& ptr);
|
||||||
void updatePosition (const MWWorld::Ptr& ptr);
|
void updatePosition (const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
|
|
||||||
void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject);
|
void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject);
|
||||||
|
|
||||||
void removeHeightField (int x, int y);
|
void removeHeightField (int x, int y);
|
||||||
|
@ -170,7 +178,7 @@ namespace MWPhysics
|
||||||
/// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors.
|
/// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors.
|
||||||
RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(),
|
RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(),
|
||||||
std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(),
|
std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(),
|
||||||
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const override;
|
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff, int projId=-1) const override;
|
||||||
|
|
||||||
RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const override;
|
RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const override;
|
||||||
|
|
||||||
|
@ -265,9 +273,11 @@ namespace MWPhysics
|
||||||
|
|
||||||
std::set<Object*> mAnimatedObjects; // stores pointers to elements in mObjects
|
std::set<Object*> mAnimatedObjects; // stores pointers to elements in mObjects
|
||||||
|
|
||||||
using ActorMap = std::map<MWWorld::ConstPtr, std::shared_ptr<Actor>>;
|
|
||||||
ActorMap mActors;
|
ActorMap mActors;
|
||||||
|
|
||||||
|
using ProjectileMap = std::map<int, std::shared_ptr<Projectile>>;
|
||||||
|
ProjectileMap mProjectiles;
|
||||||
|
|
||||||
using HeightFieldMap = std::map<std::pair<int, int>, HeightField *>;
|
using HeightFieldMap = std::map<std::pair<int, int>, HeightField *>;
|
||||||
HeightFieldMap mHeightFields;
|
HeightFieldMap mHeightFields;
|
||||||
|
|
||||||
|
@ -278,6 +288,8 @@ namespace MWPhysics
|
||||||
|
|
||||||
float mTimeAccum;
|
float mTimeAccum;
|
||||||
|
|
||||||
|
unsigned int mProjectileId;
|
||||||
|
|
||||||
float mWaterHeight;
|
float mWaterHeight;
|
||||||
bool mWaterEnabled;
|
bool mWaterEnabled;
|
||||||
|
|
||||||
|
|
89
apps/openmw/mwphysics/projectile.cpp
Normal file
89
apps/openmw/mwphysics/projectile.cpp
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <BulletCollision/CollisionShapes/btSphereShape.h>
|
||||||
|
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
||||||
|
|
||||||
|
#include <LinearMath/btVector3.h>
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
#include <components/misc/convert.hpp>
|
||||||
|
#include <components/resource/bulletshape.hpp>
|
||||||
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
|
#include "collisiontype.hpp"
|
||||||
|
#include "mtphysics.hpp"
|
||||||
|
#include "projectile.hpp"
|
||||||
|
|
||||||
|
namespace MWPhysics
|
||||||
|
{
|
||||||
|
Projectile::Projectile(int projectileId, const MWWorld::Ptr& caster, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
|
||||||
|
: mActive(true)
|
||||||
|
, mCaster(caster)
|
||||||
|
, mPhysics(physicssystem)
|
||||||
|
, mTaskScheduler(scheduler)
|
||||||
|
, mProjectileId(projectileId)
|
||||||
|
{
|
||||||
|
mShape.reset(new btSphereShape(1.f));
|
||||||
|
mConvexShape = static_cast<btConvexShape*>(mShape.get());
|
||||||
|
|
||||||
|
mCollisionObject.reset(new btCollisionObject);
|
||||||
|
mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
|
||||||
|
mCollisionObject->setActivationState(DISABLE_DEACTIVATION);
|
||||||
|
mCollisionObject->setCollisionShape(mShape.get());
|
||||||
|
mCollisionObject->setUserPointer(this);
|
||||||
|
|
||||||
|
setPosition(position);
|
||||||
|
|
||||||
|
const int collisionMask = CollisionType_World | CollisionType_HeightMap |
|
||||||
|
CollisionType_Actor | CollisionType_Door | CollisionType_Water | CollisionType_Projectile;
|
||||||
|
mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_Projectile, collisionMask);
|
||||||
|
|
||||||
|
commitPositionChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
Projectile::~Projectile()
|
||||||
|
{
|
||||||
|
if (mCollisionObject)
|
||||||
|
{
|
||||||
|
if (!mActive)
|
||||||
|
mPhysics->reportCollision(mHitPosition, mHitNormal);
|
||||||
|
mTaskScheduler->removeCollisionObject(mCollisionObject.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Projectile::commitPositionChange()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mPositionMutex);
|
||||||
|
if (mTransformUpdatePending)
|
||||||
|
{
|
||||||
|
mCollisionObject->setWorldTransform(mLocalTransform);
|
||||||
|
mTransformUpdatePending = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Projectile::setPosition(const osg::Vec3f &position)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mPositionMutex);
|
||||||
|
mLocalTransform.setOrigin(Misc::Convert::toBullet(position));
|
||||||
|
mTransformUpdatePending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Projectile::hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal)
|
||||||
|
{
|
||||||
|
if (!mActive.load(std::memory_order_acquire))
|
||||||
|
return;
|
||||||
|
std::unique_lock<std::mutex> lock(mPositionMutex);
|
||||||
|
mHitTarget = target;
|
||||||
|
mHitPosition = pos;
|
||||||
|
mHitNormal = normal;
|
||||||
|
mActive.store(false, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Projectile::activate()
|
||||||
|
{
|
||||||
|
assert(!mActive);
|
||||||
|
mActive.store(true, std::memory_order_release);
|
||||||
|
}
|
||||||
|
}
|
106
apps/openmw/mwphysics/projectile.hpp
Normal file
106
apps/openmw/mwphysics/projectile.hpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#ifndef OPENMW_MWPHYSICS_PROJECTILE_H
|
||||||
|
#define OPENMW_MWPHYSICS_PROJECTILE_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "components/misc/convert.hpp"
|
||||||
|
|
||||||
|
#include "ptrholder.hpp"
|
||||||
|
|
||||||
|
class btCollisionObject;
|
||||||
|
class btCollisionShape;
|
||||||
|
class btConvexShape;
|
||||||
|
class btVector3;
|
||||||
|
|
||||||
|
namespace osg
|
||||||
|
{
|
||||||
|
class Vec3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Resource
|
||||||
|
{
|
||||||
|
class BulletShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWPhysics
|
||||||
|
{
|
||||||
|
class PhysicsTaskScheduler;
|
||||||
|
class PhysicsSystem;
|
||||||
|
|
||||||
|
class Projectile final : public PtrHolder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Projectile(const int projectileId, const MWWorld::Ptr& caster, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem);
|
||||||
|
~Projectile() override;
|
||||||
|
|
||||||
|
btConvexShape* getConvexShape() const { return mConvexShape; }
|
||||||
|
|
||||||
|
void commitPositionChange();
|
||||||
|
|
||||||
|
void setPosition(const osg::Vec3f& position);
|
||||||
|
|
||||||
|
btCollisionObject* getCollisionObject() const
|
||||||
|
{
|
||||||
|
return mCollisionObject.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getProjectileId() const
|
||||||
|
{
|
||||||
|
return mProjectileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isActive() const
|
||||||
|
{
|
||||||
|
return mActive.load(std::memory_order_acquire);
|
||||||
|
}
|
||||||
|
|
||||||
|
MWWorld::Ptr getTarget() const
|
||||||
|
{
|
||||||
|
assert(!mActive);
|
||||||
|
return mHitTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWWorld::Ptr getCaster() const { return mCaster; }
|
||||||
|
|
||||||
|
osg::Vec3f getHitPos() const
|
||||||
|
{
|
||||||
|
assert(!mActive);
|
||||||
|
return Misc::Convert::toOsg(mHitPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal);
|
||||||
|
void activate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::unique_ptr<btCollisionShape> mShape;
|
||||||
|
btConvexShape* mConvexShape;
|
||||||
|
|
||||||
|
std::unique_ptr<btCollisionObject> mCollisionObject;
|
||||||
|
btTransform mLocalTransform;
|
||||||
|
bool mTransformUpdatePending;
|
||||||
|
std::atomic<bool> mActive;
|
||||||
|
MWWorld::Ptr mCaster;
|
||||||
|
MWWorld::Ptr mHitTarget;
|
||||||
|
btVector3 mHitPosition;
|
||||||
|
btVector3 mHitNormal;
|
||||||
|
|
||||||
|
mutable std::mutex mPositionMutex;
|
||||||
|
|
||||||
|
osg::Vec3f mPosition;
|
||||||
|
|
||||||
|
PhysicsSystem *mPhysics;
|
||||||
|
PhysicsTaskScheduler *mTaskScheduler;
|
||||||
|
|
||||||
|
Projectile(const Projectile&);
|
||||||
|
Projectile& operator=(const Projectile&);
|
||||||
|
|
||||||
|
int mProjectileId;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -29,7 +29,7 @@ namespace MWPhysics
|
||||||
/// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors.
|
/// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors.
|
||||||
virtual RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(),
|
virtual RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(),
|
||||||
std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(),
|
std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(),
|
||||||
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const = 0;
|
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff, int projId=-1) const = 0;
|
||||||
|
|
||||||
virtual RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const = 0;
|
virtual RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const = 0;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
||||||
#include <BulletCollision/CollisionShapes/btConvexShape.h>
|
#include <BulletCollision/CollisionShapes/btConvexShape.h>
|
||||||
|
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
|
||||||
#include "collisiontype.hpp"
|
#include "collisiontype.hpp"
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
#include "closestnotmeconvexresultcallback.hpp"
|
#include "closestnotmeconvexresultcallback.hpp"
|
||||||
|
|
|
@ -521,7 +521,7 @@ namespace MWVR
|
||||||
{
|
{
|
||||||
XrCompositionLayerProjectionView xrLayer;
|
XrCompositionLayerProjectionView xrLayer;
|
||||||
xrLayer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
xrLayer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
||||||
xrLayer.subImage = layer.swapchain->impl().xrSubImage();
|
xrLayer.subImage = toXR(layer.subImage, false);
|
||||||
xrLayer.pose = toXR(layer.pose);
|
xrLayer.pose = toXR(layer.pose);
|
||||||
xrLayer.fov = toXR(layer.fov);
|
xrLayer.fov = toXR(layer.fov);
|
||||||
xrLayer.next = nullptr;
|
xrLayer.next = nullptr;
|
||||||
|
@ -529,6 +529,21 @@ namespace MWVR
|
||||||
return xrLayer;
|
return xrLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XrSwapchainSubImage toXR(MWVR::SubImage subImage, bool depthImage)
|
||||||
|
{
|
||||||
|
XrSwapchainSubImage xrSubImage{};
|
||||||
|
if(depthImage)
|
||||||
|
xrSubImage.swapchain = subImage.swapchain->impl().xrSwapchainDepth();
|
||||||
|
else
|
||||||
|
xrSubImage.swapchain = subImage.swapchain->impl().xrSwapchain();
|
||||||
|
xrSubImage.imageRect.extent.width = subImage.width;
|
||||||
|
xrSubImage.imageRect.extent.height = subImage.height;
|
||||||
|
xrSubImage.imageRect.offset.x = subImage.x;
|
||||||
|
xrSubImage.imageRect.offset.y = subImage.y;
|
||||||
|
xrSubImage.imageArrayIndex = 0;
|
||||||
|
return xrSubImage;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
OpenXRManagerImpl::endFrame(FrameInfo frameInfo, const std::array<CompositionLayerProjectionView, 2>* layerStack)
|
OpenXRManagerImpl::endFrame(FrameInfo frameInfo, const std::array<CompositionLayerProjectionView, 2>* layerStack)
|
||||||
{
|
{
|
||||||
|
@ -555,8 +570,8 @@ namespace MWVR
|
||||||
compositionLayerDepth = mLayerDepth;
|
compositionLayerDepth = mLayerDepth;
|
||||||
compositionLayerDepth[(int)Side::LEFT_SIDE].farZ = farClip;
|
compositionLayerDepth[(int)Side::LEFT_SIDE].farZ = farClip;
|
||||||
compositionLayerDepth[(int)Side::RIGHT_SIDE].farZ = farClip;
|
compositionLayerDepth[(int)Side::RIGHT_SIDE].farZ = farClip;
|
||||||
compositionLayerDepth[(int)Side::LEFT_SIDE].subImage = (*layerStack)[(int)Side::LEFT_SIDE].swapchain->impl().xrSubImageDepth();
|
compositionLayerDepth[(int)Side::LEFT_SIDE].subImage = toXR((*layerStack)[(int)Side::LEFT_SIDE].subImage, true);
|
||||||
compositionLayerDepth[(int)Side::RIGHT_SIDE].subImage = (*layerStack)[(int)Side::RIGHT_SIDE].swapchain->impl().xrSubImageDepth();
|
compositionLayerDepth[(int)Side::RIGHT_SIDE].subImage = toXR((*layerStack)[(int)Side::RIGHT_SIDE].subImage, true);
|
||||||
if (compositionLayerDepth[(int)Side::LEFT_SIDE].subImage.swapchain != XR_NULL_HANDLE
|
if (compositionLayerDepth[(int)Side::LEFT_SIDE].subImage.swapchain != XR_NULL_HANDLE
|
||||||
&& compositionLayerDepth[(int)Side::RIGHT_SIDE].subImage.swapchain != XR_NULL_HANDLE)
|
&& compositionLayerDepth[(int)Side::RIGHT_SIDE].subImage.swapchain != XR_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace MWVR
|
||||||
XrQuaternionf toXR(osg::Quat quat);
|
XrQuaternionf toXR(osg::Quat quat);
|
||||||
|
|
||||||
XrCompositionLayerProjectionView toXR(MWVR::CompositionLayerProjectionView layer);
|
XrCompositionLayerProjectionView toXR(MWVR::CompositionLayerProjectionView layer);
|
||||||
|
XrSwapchainSubImage toXR(MWVR::SubImage, bool depthImage);
|
||||||
|
|
||||||
/// \brief Implementation of OpenXRManager
|
/// \brief Implementation of OpenXRManager
|
||||||
struct OpenXRManagerImpl
|
struct OpenXRManagerImpl
|
||||||
|
|
|
@ -156,8 +156,6 @@ namespace MWVR
|
||||||
beginPhase(FramePhase::Swap);
|
beginPhase(FramePhase::Swap);
|
||||||
|
|
||||||
auto* frameMeta = getFrame(FramePhase::Swap).get();
|
auto* frameMeta = getFrame(FramePhase::Swap).get();
|
||||||
auto leftView = viewer.getView("LeftEye");
|
|
||||||
auto rightView = viewer.getView("RightEye");
|
|
||||||
|
|
||||||
if (frameMeta->mShouldSyncFrameLoop)
|
if (frameMeta->mShouldSyncFrameLoop)
|
||||||
{
|
{
|
||||||
|
@ -165,11 +163,9 @@ namespace MWVR
|
||||||
{
|
{
|
||||||
viewer.blitEyesToMirrorTexture(gc);
|
viewer.blitEyesToMirrorTexture(gc);
|
||||||
gc->swapBuffersImplementation();
|
gc->swapBuffersImplementation();
|
||||||
leftView->swapBuffers(gc);
|
|
||||||
rightView->swapBuffers(gc);
|
|
||||||
std::array<CompositionLayerProjectionView, 2> layerStack{};
|
std::array<CompositionLayerProjectionView, 2> layerStack{};
|
||||||
layerStack[(int)Side::LEFT_SIDE].swapchain = &leftView->swapchain();
|
layerStack[(int)Side::LEFT_SIDE].subImage = viewer.subImage(Side::LEFT_SIDE);
|
||||||
layerStack[(int)Side::RIGHT_SIDE].swapchain = &rightView->swapchain();
|
layerStack[(int)Side::RIGHT_SIDE].subImage = viewer.subImage(Side::RIGHT_SIDE);
|
||||||
layerStack[(int)Side::LEFT_SIDE].pose = frameMeta->mPredictedPoses.eye[(int)Side::LEFT_SIDE] / mPlayerScale;
|
layerStack[(int)Side::LEFT_SIDE].pose = frameMeta->mPredictedPoses.eye[(int)Side::LEFT_SIDE] / mPlayerScale;
|
||||||
layerStack[(int)Side::RIGHT_SIDE].pose = frameMeta->mPredictedPoses.eye[(int)Side::RIGHT_SIDE] / mPlayerScale;
|
layerStack[(int)Side::RIGHT_SIDE].pose = frameMeta->mPredictedPoses.eye[(int)Side::RIGHT_SIDE] / mPlayerScale;
|
||||||
layerStack[(int)Side::LEFT_SIDE].fov = frameMeta->mPredictedPoses.view[(int)Side::LEFT_SIDE].fov;
|
layerStack[(int)Side::LEFT_SIDE].fov = frameMeta->mPredictedPoses.view[(int)Side::LEFT_SIDE].fov;
|
||||||
|
|
|
@ -90,9 +90,18 @@ namespace MWVR
|
||||||
bool operator==(const PoseSet& rhs) const;
|
bool operator==(const PoseSet& rhs) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CompositionLayerProjectionView
|
struct SubImage
|
||||||
{
|
{
|
||||||
class OpenXRSwapchain* swapchain;
|
class OpenXRSwapchain* swapchain;
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
int32_t width;
|
||||||
|
int32_t height;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CompositionLayerProjectionView
|
||||||
|
{
|
||||||
|
SubImage subImage;
|
||||||
Pose pose;
|
Pose pose;
|
||||||
FieldOfView fov;
|
FieldOfView fov;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,138 +1,138 @@
|
||||||
#include "vrview.hpp"
|
//#include "vrview.hpp"
|
||||||
|
//
|
||||||
#include "openxrmanager.hpp"
|
//#include "openxrmanager.hpp"
|
||||||
#include "openxrmanagerimpl.hpp"
|
//#include "openxrmanagerimpl.hpp"
|
||||||
#include "vrsession.hpp"
|
//#include "vrsession.hpp"
|
||||||
#include "vrenvironment.hpp"
|
//#include "vrenvironment.hpp"
|
||||||
|
//
|
||||||
#include <components/debug/debuglog.hpp>
|
//#include <components/debug/debuglog.hpp>
|
||||||
|
//
|
||||||
#include <osgViewer/Renderer>
|
//#include <osgViewer/Renderer>
|
||||||
|
//
|
||||||
namespace MWVR {
|
//namespace MWVR {
|
||||||
|
//
|
||||||
VRView::VRView(
|
// VRView::VRView(
|
||||||
std::string name,
|
// std::string name,
|
||||||
SwapchainConfig config,
|
// SwapchainConfig config,
|
||||||
osg::ref_ptr<osg::State> state)
|
// osg::ref_ptr<osg::State> state)
|
||||||
: mSwapchainConfig{ config }
|
// : mSwapchainConfig{ config }
|
||||||
, mSwapchain(new OpenXRSwapchain(state, mSwapchainConfig))
|
// , mSwapchain(new OpenXRSwapchain(state, mSwapchainConfig))
|
||||||
, mName(name)
|
// , mName(name)
|
||||||
{
|
// {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
VRView::~VRView()
|
// VRView::~VRView()
|
||||||
{
|
// {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
class CullCallback : public osg::NodeCallback
|
// class CullCallback : public osg::NodeCallback
|
||||||
{
|
// {
|
||||||
void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
// void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
{
|
// {
|
||||||
const auto& name = node->getName();
|
// const auto& name = node->getName();
|
||||||
if (name == "LeftEye")
|
// if (name == "LeftEye")
|
||||||
Environment::get().getSession()->beginPhase(VRSession::FramePhase::Cull);
|
// Environment::get().getSession()->beginPhase(VRSession::FramePhase::Cull);
|
||||||
traverse(node, nv);
|
// traverse(node, nv);
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
osg::Camera* VRView::createCamera(int order, const osg::Vec4& clearColor, osg::GraphicsContext* gc)
|
// osg::Camera* VRView::createCamera(int order, const osg::Vec4& clearColor, osg::GraphicsContext* gc)
|
||||||
{
|
// {
|
||||||
osg::ref_ptr<osg::Camera> camera = new osg::Camera();
|
// osg::ref_ptr<osg::Camera> camera = new osg::Camera();
|
||||||
camera->setClearColor(clearColor);
|
// camera->setClearColor(clearColor);
|
||||||
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
// camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
// camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
||||||
camera->setRenderOrder(osg::Camera::PRE_RENDER, order);
|
// camera->setRenderOrder(osg::Camera::PRE_RENDER, order);
|
||||||
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
|
// camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
|
||||||
camera->setAllowEventFocus(false);
|
// camera->setAllowEventFocus(false);
|
||||||
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
// camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
||||||
camera->setViewport(0, 0, mSwapchain->width(), mSwapchain->height());
|
// camera->setViewport(0, 0, mSwapchain->width(), mSwapchain->height());
|
||||||
camera->setGraphicsContext(gc);
|
// camera->setGraphicsContext(gc);
|
||||||
|
//
|
||||||
camera->setInitialDrawCallback(new VRView::InitialDrawCallback());
|
// camera->setInitialDrawCallback(new VRView::InitialDrawCallback());
|
||||||
camera->setCullCallback(new CullCallback);
|
// camera->setCullCallback(new CullCallback);
|
||||||
|
//
|
||||||
return camera.release();
|
// return camera.release();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
void VRView::prerenderCallback(osg::RenderInfo& renderInfo)
|
// void VRView::prerenderCallback(osg::RenderInfo& renderInfo)
|
||||||
{
|
// {
|
||||||
if(Environment::get().getSession()->getFrame(VRSession::FramePhase::Draw)->mShouldRender)
|
// if(Environment::get().getSession()->getFrame(VRSession::FramePhase::Draw)->mShouldRender)
|
||||||
mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext());
|
// mSwapchain->beginFrame(renderInfo.getState()->getGraphicsContext());
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
void VRView::InitialDrawCallback::operator()(osg::RenderInfo& renderInfo) const
|
// void VRView::InitialDrawCallback::operator()(osg::RenderInfo& renderInfo) const
|
||||||
{
|
// {
|
||||||
const auto& name = renderInfo.getCurrentCamera()->getName();
|
// const auto& name = renderInfo.getCurrentCamera()->getName();
|
||||||
if (name == "LeftEye")
|
// if (name == "LeftEye")
|
||||||
Environment::get().getSession()->beginPhase(VRSession::FramePhase::Draw);
|
// Environment::get().getSession()->beginPhase(VRSession::FramePhase::Draw);
|
||||||
|
//
|
||||||
osg::GraphicsOperation* graphicsOperation = renderInfo.getCurrentCamera()->getRenderer();
|
// osg::GraphicsOperation* graphicsOperation = renderInfo.getCurrentCamera()->getRenderer();
|
||||||
osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation);
|
// osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation);
|
||||||
if (renderer != nullptr)
|
// if (renderer != nullptr)
|
||||||
{
|
// {
|
||||||
// Disable normal OSG FBO camera setup
|
// // Disable normal OSG FBO camera setup
|
||||||
renderer->setCameraRequiresSetUp(false);
|
// renderer->setCameraRequiresSetUp(false);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
void VRView::UpdateSlaveCallback::updateSlave(
|
// void VRView::UpdateSlaveCallback::updateSlave(
|
||||||
osg::View& view,
|
// osg::View& view,
|
||||||
osg::View::Slave& slave)
|
// osg::View::Slave& slave)
|
||||||
{
|
// {
|
||||||
mView->updateSlave(view, slave);
|
// mView->updateSlave(view, slave);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
void VRView::postrenderCallback(osg::RenderInfo& renderInfo)
|
// void VRView::postrenderCallback(osg::RenderInfo& renderInfo)
|
||||||
{
|
// {
|
||||||
auto name = renderInfo.getCurrentCamera()->getName();
|
// auto name = renderInfo.getCurrentCamera()->getName();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
void VRView::swapBuffers(osg::GraphicsContext* gc)
|
// void VRView::swapBuffers(osg::GraphicsContext* gc)
|
||||||
{
|
// {
|
||||||
mSwapchain->endFrame(gc);
|
// mSwapchain->endFrame(gc);
|
||||||
}
|
// }
|
||||||
void VRView::updateSlave(osg::View& view, osg::View::Slave& slave)
|
// void VRView::updateSlave(osg::View& view, osg::View::Slave& slave)
|
||||||
{
|
// {
|
||||||
auto* camera = slave._camera.get();
|
// auto* camera = slave._camera.get();
|
||||||
|
//
|
||||||
// Update current cached cull mask of camera if it is active
|
// // Update current cached cull mask of camera if it is active
|
||||||
auto mask = camera->getCullMask();
|
// auto mask = camera->getCullMask();
|
||||||
if (mask == 0)
|
// if (mask == 0)
|
||||||
camera->setCullMask(mCullMask);
|
// camera->setCullMask(mCullMask);
|
||||||
else
|
// else
|
||||||
mCullMask = mask;
|
// mCullMask = mask;
|
||||||
|
//
|
||||||
// If the session is not active, we do not want to waste resources rendering frames.
|
// // If the session is not active, we do not want to waste resources rendering frames.
|
||||||
if (Environment::get().getSession()->getFrame(VRSession::FramePhase::Update)->mShouldRender)
|
// if (Environment::get().getSession()->getFrame(VRSession::FramePhase::Update)->mShouldRender)
|
||||||
{
|
// {
|
||||||
Side side = Side::RIGHT_SIDE;
|
// Side side = Side::RIGHT_SIDE;
|
||||||
if (mName == "LeftEye")
|
// if (mName == "LeftEye")
|
||||||
{
|
// {
|
||||||
|
//
|
||||||
Environment::get().getViewer()->vrShadow().updateShadowConfig(view);
|
// Environment::get().getViewer()->vrShadow().updateShadowConfig(view);
|
||||||
side = Side::LEFT_SIDE;
|
// side = Side::LEFT_SIDE;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
auto* session = Environment::get().getSession();
|
// auto* session = Environment::get().getSession();
|
||||||
auto viewMatrix = view.getCamera()->getViewMatrix();
|
// auto viewMatrix = view.getCamera()->getViewMatrix();
|
||||||
|
//
|
||||||
// If the camera does not have a view, use the VR stage directly
|
// // If the camera does not have a view, use the VR stage directly
|
||||||
bool useStage = !(viewMatrix.getTrans().length() > 0.01);
|
// bool useStage = !(viewMatrix.getTrans().length() > 0.01);
|
||||||
|
//
|
||||||
// If the view matrix is still the identity matrix, conventions have to be swapped around.
|
// // If the view matrix is still the identity matrix, conventions have to be swapped around.
|
||||||
bool swapConventions = viewMatrix.isIdentity();
|
// bool swapConventions = viewMatrix.isIdentity();
|
||||||
|
//
|
||||||
viewMatrix = viewMatrix * session->viewMatrix(VRSession::FramePhase::Update, side, !useStage, !swapConventions);
|
// viewMatrix = viewMatrix * session->viewMatrix(VRSession::FramePhase::Update, side, !useStage, !swapConventions);
|
||||||
|
//
|
||||||
camera->setViewMatrix(viewMatrix);
|
// camera->setViewMatrix(viewMatrix);
|
||||||
|
//
|
||||||
auto projectionMatrix = session->projectionMatrix(VRSession::FramePhase::Update, side);
|
// auto projectionMatrix = session->projectionMatrix(VRSession::FramePhase::Update, side);
|
||||||
camera->setProjectionMatrix(projectionMatrix);
|
// camera->setProjectionMatrix(projectionMatrix);
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
camera->setCullMask(0);
|
// camera->setCullMask(0);
|
||||||
}
|
// }
|
||||||
slave.updateSlaveImplementation(view);
|
// slave.updateSlaveImplementation(view);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
|
@ -1,65 +1,65 @@
|
||||||
#ifndef MWVR_VRVIEW_H
|
//#ifndef MWVR_VRVIEW_H
|
||||||
#define MWVR_VRVIEW_H
|
//#define MWVR_VRVIEW_H
|
||||||
|
//
|
||||||
#include <cassert>
|
//#include <cassert>
|
||||||
#include "openxrmanager.hpp"
|
//#include "openxrmanager.hpp"
|
||||||
#include "openxrswapchain.hpp"
|
//#include "openxrswapchain.hpp"
|
||||||
|
//
|
||||||
struct XrSwapchainSubImage;
|
//struct XrSwapchainSubImage;
|
||||||
|
//
|
||||||
namespace MWVR
|
//namespace MWVR
|
||||||
{
|
//{
|
||||||
class VRViewer;
|
// class VRViewer;
|
||||||
|
//
|
||||||
/// \brief Manipulates a slave camera by replacing its framebuffer with one destined for openxr
|
// /// \brief Manipulates a slave camera by replacing its framebuffer with one destined for openxr
|
||||||
class VRView : public osg::Referenced
|
// class VRView : public osg::Referenced
|
||||||
{
|
// {
|
||||||
public:
|
// public:
|
||||||
|
//
|
||||||
class InitialDrawCallback : public osg::Camera::DrawCallback
|
// class InitialDrawCallback : public osg::Camera::DrawCallback
|
||||||
{
|
// {
|
||||||
public:
|
// public:
|
||||||
virtual void operator()(osg::RenderInfo& renderInfo) const;
|
// virtual void operator()(osg::RenderInfo& renderInfo) const;
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
|
// class UpdateSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
|
||||||
{
|
// {
|
||||||
public:
|
// public:
|
||||||
UpdateSlaveCallback(osg::ref_ptr<VRView> view) : mView(view) {}
|
// UpdateSlaveCallback(osg::ref_ptr<VRView> view) : mView(view) {}
|
||||||
void updateSlave(osg::View& view, osg::View::Slave& slave) override;
|
// void updateSlave(osg::View& view, osg::View::Slave& slave) override;
|
||||||
|
//
|
||||||
private:
|
// private:
|
||||||
osg::ref_ptr<VRView> mView;
|
// osg::ref_ptr<VRView> mView;
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
public:
|
// public:
|
||||||
VRView(std::string name, SwapchainConfig config, osg::ref_ptr<osg::State> state);
|
// VRView(std::string name, SwapchainConfig config, osg::ref_ptr<osg::State> state);
|
||||||
virtual ~VRView();
|
// virtual ~VRView();
|
||||||
|
//
|
||||||
public:
|
// public:
|
||||||
//! Prepare for render (set FBO)
|
// //! Prepare for render (set FBO)
|
||||||
virtual void prerenderCallback(osg::RenderInfo& renderInfo);
|
// virtual void prerenderCallback(osg::RenderInfo& renderInfo);
|
||||||
|
//
|
||||||
//! Finalize render
|
// //! Finalize render
|
||||||
virtual void postrenderCallback(osg::RenderInfo& renderInfo);
|
// virtual void postrenderCallback(osg::RenderInfo& renderInfo);
|
||||||
|
//
|
||||||
//! Create camera for this view
|
// //! Create camera for this view
|
||||||
osg::Camera* createCamera(int order, const osg::Vec4& clearColor, osg::GraphicsContext* gc);
|
// osg::Camera* createCamera(int order, const osg::Vec4& clearColor, osg::GraphicsContext* gc);
|
||||||
|
//
|
||||||
//! Get the view surface
|
// //! Get the view surface
|
||||||
OpenXRSwapchain& swapchain(void) { return *mSwapchain; }
|
// OpenXRSwapchain& swapchain(void) { return *mSwapchain; }
|
||||||
|
//
|
||||||
//! Present to the openxr swapchain
|
// //! Present to the openxr swapchain
|
||||||
void swapBuffers(osg::GraphicsContext* gc);
|
// void swapBuffers(osg::GraphicsContext* gc);
|
||||||
|
//
|
||||||
void updateSlave(osg::View& view, osg::View::Slave& slave);
|
// void updateSlave(osg::View& view, osg::View::Slave& slave);
|
||||||
public:
|
// public:
|
||||||
SwapchainConfig mSwapchainConfig;
|
// SwapchainConfig mSwapchainConfig;
|
||||||
std::unique_ptr<OpenXRSwapchain> mSwapchain;
|
// std::unique_ptr<OpenXRSwapchain> mSwapchain;
|
||||||
std::string mName{};
|
// std::string mName{};
|
||||||
osg::Node::NodeMask mCullMask;
|
// osg::Node::NodeMask mCullMask;
|
||||||
bool mRendering{ false };
|
// bool mRendering{ false };
|
||||||
};
|
// };
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
#endif
|
//#endif
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "vrviewer.hpp"
|
#include "vrviewer.hpp"
|
||||||
|
|
||||||
#include "openxrmanagerimpl.hpp"
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
#include "openxrswapchain.hpp"
|
||||||
#include "vrenvironment.hpp"
|
#include "vrenvironment.hpp"
|
||||||
#include "vrsession.hpp"
|
#include "vrsession.hpp"
|
||||||
#include "vrframebuffer.hpp"
|
#include "vrframebuffer.hpp"
|
||||||
|
@ -36,10 +37,8 @@ namespace MWVR
|
||||||
VRViewer::VRViewer(
|
VRViewer::VRViewer(
|
||||||
osg::ref_ptr<osgViewer::Viewer> viewer)
|
osg::ref_ptr<osgViewer::Viewer> viewer)
|
||||||
: mViewer(viewer)
|
: mViewer(viewer)
|
||||||
, mViews()
|
|
||||||
, mPreDraw(new PredrawCallback(this))
|
, mPreDraw(new PredrawCallback(this))
|
||||||
, mPostDraw(new PostdrawCallback(this))
|
, mPostDraw(new PostdrawCallback(this))
|
||||||
, mVrShadow()
|
|
||||||
, mConfigured(false)
|
, mConfigured(false)
|
||||||
, mMsaaResolveMirrorTexture{}
|
, mMsaaResolveMirrorTexture{}
|
||||||
, mMirrorTexture{ nullptr }
|
, mMirrorTexture{ nullptr }
|
||||||
|
@ -91,6 +90,30 @@ namespace MWVR
|
||||||
return VRViewer::MirrorTextureEye::Both;
|
return VRViewer::MirrorTextureEye::Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VRViewer::InitialDrawCallback::operator()(osg::RenderInfo& renderInfo) const
|
||||||
|
{
|
||||||
|
const auto& name = renderInfo.getCurrentCamera()->getName();
|
||||||
|
if (name == "LeftEye")
|
||||||
|
Environment::get().getSession()->beginPhase(VRSession::FramePhase::Draw);
|
||||||
|
|
||||||
|
osg::GraphicsOperation* graphicsOperation = renderInfo.getCurrentCamera()->getRenderer();
|
||||||
|
osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation);
|
||||||
|
if (renderer != nullptr)
|
||||||
|
{
|
||||||
|
// Disable normal OSG FBO camera setup
|
||||||
|
renderer->setCameraRequiresSetUp(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CullCallback : public osg::NodeCallback
|
||||||
|
{
|
||||||
|
void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
Environment::get().getSession()->beginPhase(VRSession::FramePhase::Cull);
|
||||||
|
traverse(node, nv);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void VRViewer::realize(osg::GraphicsContext* context)
|
void VRViewer::realize(osg::GraphicsContext* context)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
@ -101,9 +124,9 @@ namespace MWVR
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give the main camera an initial draw callback that disables camera setup (we don't want it)
|
// Give the main camera an initial draw callback that disables camera setup (we don't want it)
|
||||||
auto mainCamera = mCameras["MainCamera"] = mViewer->getCamera();
|
auto mainCamera = mViewer->getCamera();
|
||||||
mainCamera->setName("Main");
|
mainCamera->setName("Main");
|
||||||
mainCamera->setInitialDrawCallback(new VRView::InitialDrawCallback());
|
mainCamera->setInitialDrawCallback(new InitialDrawCallback());
|
||||||
|
|
||||||
auto* xr = Environment::get().getManager();
|
auto* xr = Environment::get().getManager();
|
||||||
xr->realize(context);
|
xr->realize(context);
|
||||||
|
@ -112,17 +135,7 @@ namespace MWVR
|
||||||
// For the rest of runtime this is handled by vrsession
|
// For the rest of runtime this is handled by vrsession
|
||||||
xr->handleEvents();
|
xr->handleEvents();
|
||||||
|
|
||||||
// Small feature culling
|
// Set up swapchain config
|
||||||
bool smallFeatureCulling = Settings::Manager::getBool("small feature culling", "Camera");
|
|
||||||
auto smallFeatureCullingPixelSize = Settings::Manager::getFloat("small feature culling pixel size", "Camera");
|
|
||||||
osg::Camera::CullingMode cullingMode = osg::Camera::DEFAULT_CULLING | osg::Camera::FAR_PLANE_CULLING;
|
|
||||||
if (!smallFeatureCulling)
|
|
||||||
cullingMode &= ~osg::CullStack::SMALL_FEATURE_CULLING;
|
|
||||||
else
|
|
||||||
cullingMode |= osg::CullStack::SMALL_FEATURE_CULLING;
|
|
||||||
|
|
||||||
// Configure eyes, their cameras, and their enslavement.
|
|
||||||
osg::Vec4 clearColor = mainCamera->getClearColor();
|
|
||||||
auto config = xr->getRecommendedSwapchainConfig();
|
auto config = xr->getRecommendedSwapchainConfig();
|
||||||
|
|
||||||
std::array<std::string, 2> xConfString;
|
std::array<std::string, 2> xConfString;
|
||||||
|
@ -151,42 +164,41 @@ namespace MWVR
|
||||||
|
|
||||||
config[i].name = sViewNames[i];
|
config[i].name = sViewNames[i];
|
||||||
|
|
||||||
auto view = new VRView(name, config[i], context->getState());
|
mSubImages[i].width = config[i].selectedWidth;
|
||||||
mViews[name] = view;
|
mSubImages[i].height = config[i].selectedHeight;
|
||||||
auto camera = mCameras[name] = view->createCamera(i + 2, clearColor, context);
|
if (i > 0)
|
||||||
camera->setPreDrawCallback(mPreDraw);
|
{
|
||||||
camera->setFinalDrawCallback(mPostDraw);
|
mSubImages[i].x = mSubImages[i - 1].x + mSubImages[i - 1].width;
|
||||||
camera->setCullMask(~MWRender::Mask_GUI & ~MWRender::Mask_SimpleWater & ~MWRender::Mask_UpdateVisitor);
|
mSubImages[i].y = mSubImages[i - 1].y + mSubImages[i - 1].height;
|
||||||
camera->setName(name);
|
}
|
||||||
if (smallFeatureCulling)
|
else
|
||||||
camera->setSmallFeatureCullingPixelSize(smallFeatureCullingPixelSize);
|
{
|
||||||
camera->setCullingMode(cullingMode);
|
mSubImages[i].x = mSubImages[i].y = 0;
|
||||||
mViewer->addSlave(camera, true);
|
}
|
||||||
auto* slave = mViewer->findSlaveForCamera(camera);
|
|
||||||
slave->_updateSlaveCallback = new VRView::UpdateSlaveCallback(view);
|
|
||||||
|
|
||||||
mVrShadow.configureShadowsForCamera(camera, i == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mSwapchainConfig.name = "Main";
|
||||||
|
mSwapchainConfig.selectedWidth = config[0].selectedWidth + config[1].selectedWidth;
|
||||||
|
mSwapchainConfig.selectedHeight = std::max(config[0].selectedHeight, config[1].selectedHeight);
|
||||||
|
mSwapchainConfig.selectedSamples = std::max(config[0].selectedSamples, config[1].selectedSamples);
|
||||||
|
|
||||||
|
mSwapchain.reset(new OpenXRSwapchain(context->getState(), mSwapchainConfig));
|
||||||
|
|
||||||
|
mSubImages[0].swapchain = mSubImages[1].swapchain = mSwapchain.get();
|
||||||
|
|
||||||
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
||||||
|
|
||||||
setupMirrorTexture();
|
setupMirrorTexture();
|
||||||
|
|
||||||
mMainCameraGC = mainCamera->getGraphicsContext();
|
mainCamera->getGraphicsContext()->setSwapCallback(new VRViewer::SwapBuffersCallback(this));
|
||||||
mMainCameraGC->setSwapCallback(new VRViewer::SwapBuffersCallback(this));
|
mainCamera->setPreDrawCallback(mPreDraw);
|
||||||
mainCamera->setGraphicsContext(nullptr);
|
mainCamera->setPostDrawCallback(mPostDraw);
|
||||||
|
mainCamera->setCullCallback(new CullCallback);
|
||||||
mConfigured = true;
|
mConfigured = true;
|
||||||
|
|
||||||
Log(Debug::Verbose) << "Realized";
|
Log(Debug::Verbose) << "Realized";
|
||||||
}
|
}
|
||||||
|
|
||||||
VRView* VRViewer::getView(std::string name)
|
|
||||||
{
|
|
||||||
auto it = mViews.find(name);
|
|
||||||
if (it != mViews.end())
|
|
||||||
return it->second.get();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VRViewer::setupMirrorTexture()
|
void VRViewer::setupMirrorTexture()
|
||||||
{
|
{
|
||||||
mMirrorTextureEnabled = Settings::Manager::getBool("mirror texture", "VR");
|
mMirrorTextureEnabled = Settings::Manager::getBool("mirror texture", "VR");
|
||||||
|
@ -228,58 +240,55 @@ namespace MWVR
|
||||||
setupMirrorTexture();
|
setupMirrorTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VRViewer::enableMainCamera(void)
|
SubImage VRViewer::subImage(Side side)
|
||||||
{
|
{
|
||||||
mCameras["MainCamera"]->setGraphicsContext(mMainCameraGC);
|
return mSubImages[static_cast<int>(side)];
|
||||||
}
|
|
||||||
|
|
||||||
void VRViewer::disableMainCamera(void)
|
|
||||||
{
|
|
||||||
mCameras["MainCamera"]->setGraphicsContext(nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc)
|
void VRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc)
|
||||||
{
|
{
|
||||||
if (mMirrorTextureShouldBeCleanedUp)
|
//if (mMirrorTextureShouldBeCleanedUp)
|
||||||
{
|
//{
|
||||||
mMirrorTexture.reset(nullptr);
|
// mMirrorTexture.reset(nullptr);
|
||||||
mMsaaResolveMirrorTexture.clear();
|
// mMsaaResolveMirrorTexture.clear();
|
||||||
mMirrorTextureShouldBeCleanedUp = false;
|
// mMirrorTextureShouldBeCleanedUp = false;
|
||||||
}
|
//}
|
||||||
if (!mMirrorTextureEnabled)
|
//if (!mMirrorTextureEnabled)
|
||||||
return;
|
// return;
|
||||||
if (!mMirrorTexture)
|
//if (!mMirrorTexture)
|
||||||
{
|
//{
|
||||||
mMirrorTexture.reset(new VRFramebuffer(gc->getState(), mCameras["MainCamera"]->getViewport()->width(), mCameras["MainCamera"]->getViewport()->height(), 0));
|
// mMirrorTexture.reset(new VRFramebuffer(gc->getState(), mCameras["MainCamera"]->getViewport()->width(), mCameras["MainCamera"]->getViewport()->height(), 0));
|
||||||
}
|
//}
|
||||||
|
|
||||||
auto* state = gc->getState();
|
auto* state = gc->getState();
|
||||||
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
|
||||||
|
|
||||||
int screenWidth = mCameras["MainCamera"]->getViewport()->width();
|
//auto mainCamera = mViewer->getCamera();
|
||||||
int mirrorWidth = screenWidth / mMirrorTextureViews.size();
|
//int screenWidth = mainCamera->getViewport()->width();
|
||||||
int screenHeight = mCameras["MainCamera"]->getViewport()->height();
|
//int mirrorWidth = screenWidth / mMirrorTextureViews.size();
|
||||||
|
//int screenHeight = mainCamera->getViewport()->height();
|
||||||
|
|
||||||
// Since OpenXR does not include native support for mirror textures, we have to generate them ourselves
|
//// Since OpenXR does not include native support for mirror textures, we have to generate them ourselves
|
||||||
// which means resolving msaa twice.
|
//// which means resolving msaa twice.
|
||||||
for (unsigned i = 0; i < mMirrorTextureViews.size(); i++)
|
//for (unsigned i = 0; i < mMirrorTextureViews.size(); i++)
|
||||||
{
|
//{
|
||||||
auto& view = mViews[mMirrorTextureViews[i]];
|
// if(!mMsaaResolveMirrorTexture)
|
||||||
if(!mMsaaResolveMirrorTexture[mMirrorTextureViews[i]])
|
// mMsaaResolveMirrorTexture.reset(new VRFramebuffer(gc->getState(),
|
||||||
mMsaaResolveMirrorTexture[mMirrorTextureViews[i]].reset(new VRFramebuffer(gc->getState(),
|
// mSwapchain->width(),
|
||||||
view->swapchain().width(),
|
// mSwapchain->height(),
|
||||||
view->swapchain().height(),
|
// 0));
|
||||||
0));
|
|
||||||
|
|
||||||
auto& resolveTexture = *mMsaaResolveMirrorTexture[mMirrorTextureViews[i]];
|
// auto& resolveTexture = *mMsaaResolveMirrorTexture[mMirrorTextureViews[i]];
|
||||||
resolveTexture.bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
|
// resolveTexture.bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
|
||||||
view->swapchain().renderBuffer()->blit(gc, 0, 0, resolveTexture.width(), resolveTexture.height());
|
// view->swapchain().renderBuffer()->blit(gc, 0, 0, resolveTexture.width(), resolveTexture.height());
|
||||||
mMirrorTexture->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
|
// mMirrorTexture->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
|
||||||
resolveTexture.blit(gc, i * mirrorWidth, 0, (i + 1) * mirrorWidth, screenHeight);
|
// resolveTexture.blit(gc, i * mirrorWidth, 0, (i + 1) * mirrorWidth, screenHeight);
|
||||||
}
|
//}
|
||||||
|
|
||||||
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
||||||
mMirrorTexture->blit(gc, 0, 0, screenWidth, screenHeight);
|
//mMirrorTexture->blit(gc, 0, 0, screenWidth, screenHeight);
|
||||||
|
|
||||||
|
mSwapchain->endFrame(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -305,18 +314,14 @@ namespace MWVR
|
||||||
|
|
||||||
void VRViewer::preDrawCallback(osg::RenderInfo& info)
|
void VRViewer::preDrawCallback(osg::RenderInfo& info)
|
||||||
{
|
{
|
||||||
auto* camera = info.getCurrentCamera();
|
if(Environment::get().getSession()->getFrame(VRSession::FramePhase::Draw)->mShouldRender)
|
||||||
auto name = camera->getName();
|
mSwapchain->beginFrame(info.getState()->getGraphicsContext());
|
||||||
mViews[name]->prerenderCallback(info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VRViewer::postDrawCallback(osg::RenderInfo& info)
|
void VRViewer::postDrawCallback(osg::RenderInfo& info)
|
||||||
{
|
{
|
||||||
auto* camera = info.getCurrentCamera();
|
auto* camera = info.getCurrentCamera();
|
||||||
auto name = camera->getName();
|
auto name = camera->getName();
|
||||||
auto& view = mViews[name];
|
|
||||||
|
|
||||||
view->postrenderCallback(info);
|
|
||||||
|
|
||||||
// This happens sometimes, i've not been able to catch it when as happens
|
// This happens sometimes, i've not been able to catch it when as happens
|
||||||
// to see why and how i can stop it.
|
// to see why and how i can stop it.
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace MWVR
|
||||||
{
|
{
|
||||||
class VRFramebuffer;
|
class VRFramebuffer;
|
||||||
class VRView;
|
class VRView;
|
||||||
|
class OpenXRSwapchain;
|
||||||
|
|
||||||
/// \brief Manages stereo rendering and mirror texturing.
|
/// \brief Manages stereo rendering and mirror texturing.
|
||||||
///
|
///
|
||||||
|
@ -64,6 +65,12 @@ namespace MWVR
|
||||||
VRViewer* mViewer;
|
VRViewer* mViewer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class InitialDrawCallback : public osg::Camera::DrawCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void operator()(osg::RenderInfo& renderInfo) const;
|
||||||
|
};
|
||||||
|
|
||||||
static const std::array<const char*, 2> sViewNames;
|
static const std::array<const char*, 2> sViewNames;
|
||||||
enum class MirrorTextureEye
|
enum class MirrorTextureEye
|
||||||
{
|
{
|
||||||
|
@ -84,33 +91,30 @@ namespace MWVR
|
||||||
void blitEyesToMirrorTexture(osg::GraphicsContext* gc);
|
void blitEyesToMirrorTexture(osg::GraphicsContext* gc);
|
||||||
void realize(osg::GraphicsContext* gc);
|
void realize(osg::GraphicsContext* gc);
|
||||||
bool realized() { return mConfigured; }
|
bool realized() { return mConfigured; }
|
||||||
VRView* getView(std::string name);
|
|
||||||
VrShadow& vrShadow() { return mVrShadow; }
|
|
||||||
void setupMirrorTexture();
|
void setupMirrorTexture();
|
||||||
void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed);
|
void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed);
|
||||||
|
|
||||||
void enableMainCamera(void);
|
SubImage subImage(Side side);
|
||||||
void disableMainCamera(void);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::mutex mMutex{};
|
||||||
|
bool mConfigured{ false };
|
||||||
|
|
||||||
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
|
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
|
||||||
std::map<std::string, osg::ref_ptr<VRView> > mViews;
|
|
||||||
std::map<std::string, osg::ref_ptr<osg::Camera> > mCameras{};
|
|
||||||
osg::ref_ptr<PredrawCallback> mPreDraw{ nullptr };
|
osg::ref_ptr<PredrawCallback> mPreDraw{ nullptr };
|
||||||
osg::ref_ptr<PostdrawCallback> mPostDraw{ nullptr };
|
osg::ref_ptr<PostdrawCallback> mPostDraw{ nullptr };
|
||||||
osg::GraphicsContext* mMainCameraGC{ nullptr };
|
|
||||||
std::map< std::string, std::unique_ptr<VRFramebuffer> > mMsaaResolveMirrorTexture;
|
std::unique_ptr<VRFramebuffer> mMsaaResolveMirrorTexture;
|
||||||
std::unique_ptr<VRFramebuffer> mMirrorTexture;
|
std::unique_ptr<VRFramebuffer> mMirrorTexture;
|
||||||
VrShadow mVrShadow;
|
|
||||||
|
|
||||||
std::mutex mMutex{};
|
|
||||||
|
|
||||||
bool mConfigured{ false };
|
|
||||||
std::vector<std::string> mMirrorTextureViews;
|
std::vector<std::string> mMirrorTextureViews;
|
||||||
bool mMirrorTextureShouldBeCleanedUp{ false };
|
bool mMirrorTextureShouldBeCleanedUp{ false };
|
||||||
bool mMirrorTextureEnabled{ false };
|
bool mMirrorTextureEnabled{ false };
|
||||||
bool mFlipMirrorTextureOrder{ false };
|
bool mFlipMirrorTextureOrder{ false };
|
||||||
MirrorTextureEye mMirrorTextureEye{ MirrorTextureEye::Both };
|
MirrorTextureEye mMirrorTextureEye{ MirrorTextureEye::Both };
|
||||||
|
|
||||||
|
std::unique_ptr<OpenXRSwapchain> mSwapchain;
|
||||||
|
std::array<SubImage, 2> mSubImages;
|
||||||
|
SwapchainConfig mSwapchainConfig;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace
|
||||||
void addScripts(MWWorld::ContainerStore& store, MWWorld::CellStore* cell)
|
void addScripts(MWWorld::ContainerStore& store, MWWorld::CellStore* cell)
|
||||||
{
|
{
|
||||||
auto& scripts = MWBase::Environment::get().getWorld()->getLocalScripts();
|
auto& scripts = MWBase::Environment::get().getWorld()->getLocalScripts();
|
||||||
for(const MWWorld::Ptr& ptr : store)
|
for(const auto&& ptr : store)
|
||||||
{
|
{
|
||||||
const std::string& script = ptr.getClass().getScript(ptr);
|
const std::string& script = ptr.getClass().getScript(ptr);
|
||||||
if(!script.empty())
|
if(!script.empty())
|
||||||
|
@ -43,13 +43,10 @@ namespace
|
||||||
{
|
{
|
||||||
float sum = 0;
|
float sum = 0;
|
||||||
|
|
||||||
for (typename MWWorld::CellRefList<T>::List::const_iterator iter (
|
for (const auto& iter : cellRefList.mList)
|
||||||
cellRefList.mList.begin());
|
|
||||||
iter!=cellRefList.mList.end();
|
|
||||||
++iter)
|
|
||||||
{
|
{
|
||||||
if (iter->mData.getCount()>0)
|
if (iter.mData.getCount()>0)
|
||||||
sum += iter->mData.getCount()*iter->mBase->mData.mWeight;
|
sum += iter.mData.getCount()*iter.mBase->mData.mWeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
|
@ -62,12 +59,11 @@ namespace
|
||||||
store->resolve();
|
store->resolve();
|
||||||
std::string id2 = Misc::StringUtils::lowerCase (id);
|
std::string id2 = Misc::StringUtils::lowerCase (id);
|
||||||
|
|
||||||
for (typename MWWorld::CellRefList<T>::List::iterator iter (list.mList.begin());
|
for (auto& iter : list.mList)
|
||||||
iter!=list.mList.end(); ++iter)
|
|
||||||
{
|
{
|
||||||
if (Misc::StringUtils::ciEqual(iter->mBase->mId, id2) && iter->mData.getCount())
|
if (Misc::StringUtils::ciEqual(iter.mBase->mId, id2) && iter.mData.getCount())
|
||||||
{
|
{
|
||||||
MWWorld::Ptr ptr (&*iter, nullptr);
|
MWWorld::Ptr ptr (&iter, nullptr);
|
||||||
ptr.setContainerStore (store);
|
ptr.setContainerStore (store);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
@ -81,7 +77,7 @@ MWWorld::ResolutionListener::~ResolutionListener()
|
||||||
{
|
{
|
||||||
if(!mStore.mModified && mStore.mResolved && !mStore.mPtr.isEmpty())
|
if(!mStore.mModified && mStore.mResolved && !mStore.mPtr.isEmpty())
|
||||||
{
|
{
|
||||||
for(const MWWorld::Ptr& ptr : mStore)
|
for(const auto&& ptr : mStore)
|
||||||
ptr.getRefData().setCount(0);
|
ptr.getRefData().setCount(0);
|
||||||
mStore.fillNonRandom(mStore.mPtr.get<ESM::Container>()->mBase->mInventory, "", mStore.mSeed);
|
mStore.fillNonRandom(mStore.mPtr.get<ESM::Container>()->mBase->mInventory, "", mStore.mSeed);
|
||||||
addScripts(mStore, mStore.mPtr.mCell);
|
addScripts(mStore, mStore.mPtr.mCell);
|
||||||
|
@ -127,15 +123,14 @@ template<typename T>
|
||||||
void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection,
|
void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection,
|
||||||
ESM::InventoryState& inventory, int& index, bool equipable) const
|
ESM::InventoryState& inventory, int& index, bool equipable) const
|
||||||
{
|
{
|
||||||
for (typename CellRefList<T>::List::const_iterator iter (collection.mList.begin());
|
for (const auto& iter : collection.mList)
|
||||||
iter!=collection.mList.end(); ++iter)
|
|
||||||
{
|
{
|
||||||
if (iter->mData.getCount() == 0)
|
if (iter.mData.getCount() == 0)
|
||||||
continue;
|
continue;
|
||||||
ESM::ObjectState state;
|
ESM::ObjectState state;
|
||||||
storeState (*iter, state);
|
storeState (iter, state);
|
||||||
if (equipable)
|
if (equipable)
|
||||||
storeEquipmentState(*iter, index, inventory);
|
storeEquipmentState(iter, index, inventory);
|
||||||
inventory.mItems.push_back (state);
|
inventory.mItems.push_back (state);
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
|
@ -188,7 +183,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end()
|
||||||
int MWWorld::ContainerStore::count(const std::string &id) const
|
int MWWorld::ContainerStore::count(const std::string &id) const
|
||||||
{
|
{
|
||||||
int total=0;
|
int total=0;
|
||||||
for (const auto& iter : *this)
|
for (const auto&& iter : *this)
|
||||||
if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))
|
if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))
|
||||||
total += iter.getRefData().getCount();
|
total += iter.getRefData().getCount();
|
||||||
return total;
|
return total;
|
||||||
|
@ -430,14 +425,14 @@ void MWWorld::ContainerStore::rechargeItems(float duration)
|
||||||
updateRechargingItems();
|
updateRechargingItems();
|
||||||
mRechargingItemsUpToDate = true;
|
mRechargingItemsUpToDate = true;
|
||||||
}
|
}
|
||||||
for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it)
|
for (auto& it : mRechargingItems)
|
||||||
{
|
{
|
||||||
if (!MWMechanics::rechargeItem(*it->first, it->second, duration))
|
if (!MWMechanics::rechargeItem(*it.first, it.second, duration))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// attempt to restack when fully recharged
|
// attempt to restack when fully recharged
|
||||||
if (it->first->getCellRef().getEnchantmentCharge() == it->second)
|
if (it.first->getCellRef().getEnchantmentCharge() == it.second)
|
||||||
it->first = restack(*it->first);
|
it.first = restack(*it.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,9 +476,9 @@ int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const
|
||||||
|
|
||||||
bool MWWorld::ContainerStore::hasVisibleItems() const
|
bool MWWorld::ContainerStore::hasVisibleItems() const
|
||||||
{
|
{
|
||||||
for (auto iter(begin()); iter != end(); ++iter)
|
for (const auto&& iter : *this)
|
||||||
{
|
{
|
||||||
if (iter->getClass().showsInInventory(*iter))
|
if (iter.getClass().showsInInventory(iter))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,8 +596,8 @@ void MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const s
|
||||||
|
|
||||||
void MWWorld::ContainerStore::clear()
|
void MWWorld::ContainerStore::clear()
|
||||||
{
|
{
|
||||||
for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
|
for (auto&& iter : *this)
|
||||||
iter->getRefData().setCount (0);
|
iter.getRefData().setCount (0);
|
||||||
|
|
||||||
flagAsModified();
|
flagAsModified();
|
||||||
mModified = true;
|
mModified = true;
|
||||||
|
@ -623,7 +618,7 @@ void MWWorld::ContainerStore::resolve()
|
||||||
{
|
{
|
||||||
if(!mResolved && !mPtr.isEmpty())
|
if(!mResolved && !mPtr.isEmpty())
|
||||||
{
|
{
|
||||||
for(const MWWorld::Ptr& ptr : *this)
|
for(const auto&& ptr : *this)
|
||||||
ptr.getRefData().setCount(0);
|
ptr.getRefData().setCount(0);
|
||||||
Misc::Rng::Seed seed{mSeed};
|
Misc::Rng::Seed seed{mSeed};
|
||||||
fill(mPtr.get<ESM::Container>()->mBase->mInventory, "", seed);
|
fill(mPtr.get<ESM::Container>()->mBase->mInventory, "", seed);
|
||||||
|
@ -644,7 +639,7 @@ MWWorld::ResolutionHandle MWWorld::ContainerStore::resolveTemporarily()
|
||||||
}
|
}
|
||||||
if(!mResolved && !mPtr.isEmpty())
|
if(!mResolved && !mPtr.isEmpty())
|
||||||
{
|
{
|
||||||
for(const MWWorld::Ptr& ptr : *this)
|
for(const auto&& ptr : *this)
|
||||||
ptr.getRefData().setCount(0);
|
ptr.getRefData().setCount(0);
|
||||||
Misc::Rng::Seed seed{mSeed};
|
Misc::Rng::Seed seed{mSeed};
|
||||||
fill(mPtr.get<ESM::Container>()->mBase->mInventory, "", seed);
|
fill(mPtr.get<ESM::Container>()->mBase->mInventory, "", seed);
|
||||||
|
@ -727,10 +722,10 @@ MWWorld::Ptr MWWorld::ContainerStore::findReplacement(const std::string& id)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr item;
|
MWWorld::Ptr item;
|
||||||
int itemHealth = 1;
|
int itemHealth = 1;
|
||||||
for (MWWorld::ContainerStoreIterator iter = begin(); iter != end(); ++iter)
|
for (auto&& iter : *this)
|
||||||
{
|
{
|
||||||
int iterHealth = iter->getClass().hasItemHealth(*iter) ? iter->getClass().getItemHealth(*iter) : 1;
|
int iterHealth = iter.getClass().hasItemHealth(iter) ? iter.getClass().getItemHealth(iter) : 1;
|
||||||
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), id))
|
if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))
|
||||||
{
|
{
|
||||||
// Prefer the stack with the lowest remaining uses
|
// Prefer the stack with the lowest remaining uses
|
||||||
// Try to get item with zero durability only if there are no other items found
|
// Try to get item with zero durability only if there are no other items found
|
||||||
|
@ -738,7 +733,7 @@ MWWorld::Ptr MWWorld::ContainerStore::findReplacement(const std::string& id)
|
||||||
(iterHealth > 0 && iterHealth < itemHealth) ||
|
(iterHealth > 0 && iterHealth < itemHealth) ||
|
||||||
(itemHealth <= 0 && iterHealth > 0))
|
(itemHealth <= 0 && iterHealth > 0))
|
||||||
{
|
{
|
||||||
item = *iter;
|
item = iter;
|
||||||
itemHealth = iterHealth;
|
itemHealth = iterHealth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -867,11 +862,8 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory)
|
||||||
mResolved = true;
|
mResolved = true;
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (std::vector<ESM::ObjectState>::const_iterator
|
for (const ESM::ObjectState& state : inventory.mItems)
|
||||||
iter (inventory.mItems.begin()); iter!=inventory.mItems.end(); ++iter)
|
|
||||||
{
|
{
|
||||||
const ESM::ObjectState& state = *iter;
|
|
||||||
|
|
||||||
int type = MWBase::Environment::get().getWorld()->getStore().find(state.mRef.mRefID);
|
int type = MWBase::Environment::get().getWorld()->getStore().find(state.mRef.mRefID);
|
||||||
|
|
||||||
int thisIndex = index++;
|
int thisIndex = index++;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <osg/PositionAttitudeTransform>
|
#include <osg/PositionAttitudeTransform>
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
#include "../mwsound/sound.hpp"
|
#include "../mwsound/sound.hpp"
|
||||||
|
|
||||||
#include "../mwphysics/physicssystem.hpp"
|
#include "../mwphysics/physicssystem.hpp"
|
||||||
|
#include "../mwphysics/projectile.hpp"
|
||||||
|
|
||||||
#ifdef USE_OPENXR
|
#ifdef USE_OPENXR
|
||||||
#include "../mwvr/vrenvironment.hpp"
|
#include "../mwvr/vrenvironment.hpp"
|
||||||
|
@ -298,7 +300,7 @@ namespace MWWorld
|
||||||
else
|
else
|
||||||
state.mActorId = -1;
|
state.mActorId = -1;
|
||||||
|
|
||||||
std::string texture = "";
|
std::string texture;
|
||||||
|
|
||||||
state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, state.mSourceName, state.mSpellId);
|
state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, state.mSourceName, state.mSpellId);
|
||||||
|
|
||||||
|
@ -316,6 +318,7 @@ namespace MWWorld
|
||||||
MWWorld::Ptr ptr = ref.getPtr();
|
MWWorld::Ptr ptr = ref.getPtr();
|
||||||
|
|
||||||
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
||||||
|
|
||||||
createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture);
|
createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture);
|
||||||
|
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
|
@ -327,6 +330,8 @@ namespace MWWorld
|
||||||
state.mSounds.push_back(sound);
|
state.mSounds.push_back(sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.mProjectileId = mPhysics->addProjectile(caster, pos);
|
||||||
|
state.mToDelete = false;
|
||||||
mMagicBolts.push_back(state);
|
mMagicBolts.push_back(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,7 +344,6 @@ namespace MWWorld
|
||||||
state.mIdArrow = projectile.getCellRef().getRefId();
|
state.mIdArrow = projectile.getCellRef().getRefId();
|
||||||
state.mCasterHandle = actor;
|
state.mCasterHandle = actor;
|
||||||
state.mAttackStrength = attackStrength;
|
state.mAttackStrength = attackStrength;
|
||||||
|
|
||||||
int type = projectile.get<ESM::Weapon>()->mBase->mData.mType;
|
int type = projectile.get<ESM::Weapon>()->mBase->mData.mType;
|
||||||
state.mThrown = MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown;
|
state.mThrown = MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown;
|
||||||
|
|
||||||
|
@ -350,6 +354,8 @@ namespace MWWorld
|
||||||
if (!ptr.getClass().getEnchantment(ptr).empty())
|
if (!ptr.getClass().getEnchantment(ptr).empty())
|
||||||
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
|
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
|
||||||
|
|
||||||
|
state.mProjectileId = mPhysics->addProjectile(actor, pos);
|
||||||
|
state.mToDelete = false;
|
||||||
mProjectiles.push_back(state);
|
mProjectiles.push_back(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,63 +380,58 @@ namespace MWWorld
|
||||||
return (state.mNode->getPosition() - playerPos).length2() >= farawayThreshold*farawayThreshold;
|
return (state.mNode->getPosition() - playerPos).length2() >= farawayThreshold*farawayThreshold;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end();)
|
for (auto& projectileState : mProjectiles)
|
||||||
{
|
{
|
||||||
if (isCleanable(*it))
|
if (isCleanable(projectileState))
|
||||||
{
|
cleanupProjectile(projectileState);
|
||||||
cleanupProjectile(*it);
|
|
||||||
it = mProjectiles.erase(it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();)
|
for (auto& magicBoltState : mMagicBolts)
|
||||||
{
|
{
|
||||||
if (isCleanable(*it))
|
if (isCleanable(magicBoltState))
|
||||||
{
|
cleanupMagicBolt(magicBoltState);
|
||||||
cleanupMagicBolt(*it);
|
|
||||||
it = mMagicBolts.erase(it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectileManager::moveMagicBolts(float duration)
|
void ProjectileManager::moveMagicBolts(float duration)
|
||||||
{
|
{
|
||||||
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();)
|
for (auto& magicBoltState : mMagicBolts)
|
||||||
{
|
{
|
||||||
|
if (magicBoltState.mToDelete)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId);
|
||||||
|
if (!projectile->isActive())
|
||||||
|
continue;
|
||||||
// If the actor caster is gone, the magic bolt needs to be removed from the scene during the next frame.
|
// If the actor caster is gone, the magic bolt needs to be removed from the scene during the next frame.
|
||||||
MWWorld::Ptr caster = it->getCaster();
|
MWWorld::Ptr caster = magicBoltState.getCaster();
|
||||||
if (!caster.isEmpty() && caster.getClass().isActor())
|
if (!caster.isEmpty() && caster.getClass().isActor())
|
||||||
{
|
{
|
||||||
if (caster.getRefData().getCount() <= 0 || caster.getClass().getCreatureStats(caster).isDead())
|
if (caster.getRefData().getCount() <= 0 || caster.getClass().getCreatureStats(caster).isDead())
|
||||||
{
|
{
|
||||||
cleanupMagicBolt(*it);
|
cleanupMagicBolt(magicBoltState);
|
||||||
it = mMagicBolts.erase(it);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Quat orient = it->mNode->getAttitude();
|
osg::Quat orient = magicBoltState.mNode->getAttitude();
|
||||||
static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||||
.find("fTargetSpellMaxSpeed")->mValue.getFloat();
|
.find("fTargetSpellMaxSpeed")->mValue.getFloat();
|
||||||
float speed = fTargetSpellMaxSpeed * it->mSpeed;
|
float speed = fTargetSpellMaxSpeed * magicBoltState.mSpeed;
|
||||||
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
||||||
direction.normalize();
|
direction.normalize();
|
||||||
osg::Vec3f pos(it->mNode->getPosition());
|
osg::Vec3f pos(magicBoltState.mNode->getPosition());
|
||||||
osg::Vec3f newPos = pos + direction * duration * speed;
|
osg::Vec3f newPos = pos + direction * duration * speed;
|
||||||
|
|
||||||
for (size_t soundIter = 0; soundIter != it->mSounds.size(); soundIter++)
|
for (const auto& sound : magicBoltState.mSounds)
|
||||||
{
|
sound->setPosition(newPos);
|
||||||
it->mSounds.at(soundIter)->setPosition(newPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
it->mNode->setPosition(newPos);
|
magicBoltState.mNode->setPosition(newPos);
|
||||||
|
|
||||||
update(*it, duration);
|
mPhysics->updateProjectile(magicBoltState.mProjectileId, newPos);
|
||||||
|
|
||||||
|
update(magicBoltState, duration);
|
||||||
|
|
||||||
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
|
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
|
||||||
std::vector<MWWorld::Ptr> targetActors;
|
std::vector<MWWorld::Ptr> targetActors;
|
||||||
|
@ -439,7 +440,7 @@ namespace MWWorld
|
||||||
|
|
||||||
// Check for impact
|
// Check for impact
|
||||||
// TODO: use a proper btRigidBody / btGhostObject?
|
// TODO: use a proper btRigidBody / btGhostObject?
|
||||||
MWPhysics::RayCastingResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile);
|
const auto result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, magicBoltState.mProjectileId);
|
||||||
|
|
||||||
bool hit = false;
|
bool hit = false;
|
||||||
if (result.mHit)
|
if (result.mHit)
|
||||||
|
@ -447,16 +448,16 @@ namespace MWWorld
|
||||||
hit = true;
|
hit = true;
|
||||||
if (result.mHitObject.isEmpty())
|
if (result.mHitObject.isEmpty())
|
||||||
{
|
{
|
||||||
// terrain
|
// terrain or projectile
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MWMechanics::CastSpell cast(caster, result.mHitObject);
|
MWMechanics::CastSpell cast(caster, result.mHitObject);
|
||||||
cast.mHitPosition = pos;
|
cast.mHitPosition = pos;
|
||||||
cast.mId = it->mSpellId;
|
cast.mId = magicBoltState.mSpellId;
|
||||||
cast.mSourceName = it->mSourceName;
|
cast.mSourceName = magicBoltState.mSourceName;
|
||||||
cast.mStack = false;
|
cast.mStack = false;
|
||||||
cast.inflict(result.mHitObject, caster, it->mEffects, ESM::RT_Target, false, true);
|
cast.inflict(result.mHitObject, caster, magicBoltState.mEffects, ESM::RT_Target, false, true);
|
||||||
mPhysics->reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal));
|
mPhysics->reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -467,47 +468,46 @@ namespace MWWorld
|
||||||
|
|
||||||
if (hit)
|
if (hit)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, result.mHitObject,
|
MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, result.mHitObject,
|
||||||
ESM::RT_Target, it->mSpellId, it->mSourceName);
|
ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName);
|
||||||
|
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
cleanupMagicBolt(magicBoltState);
|
||||||
for (size_t soundIter = 0; soundIter != it->mSounds.size(); soundIter++)
|
|
||||||
sndMgr->stopSound(it->mSounds.at(soundIter));
|
|
||||||
|
|
||||||
mParent->removeChild(it->mNode);
|
|
||||||
|
|
||||||
it = mMagicBolts.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectileManager::moveProjectiles(float duration)
|
void ProjectileManager::moveProjectiles(float duration)
|
||||||
{
|
{
|
||||||
for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end();)
|
for (auto& projectileState : mProjectiles)
|
||||||
{
|
{
|
||||||
|
if (projectileState.mToDelete)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
||||||
|
if (!projectile->isActive())
|
||||||
|
continue;
|
||||||
// gravity constant - must be way lower than the gravity affecting actors, since we're not
|
// gravity constant - must be way lower than the gravity affecting actors, since we're not
|
||||||
// simulating aerodynamics at all
|
// simulating aerodynamics at all
|
||||||
it->mVelocity -= osg::Vec3f(0, 0, Constants::GravityConst * Constants::UnitsPerMeter * 0.1f) * duration;
|
projectileState.mVelocity -= osg::Vec3f(0, 0, Constants::GravityConst * Constants::UnitsPerMeter * 0.1f) * duration;
|
||||||
|
|
||||||
osg::Vec3f pos(it->mNode->getPosition());
|
osg::Vec3f pos(projectileState.mNode->getPosition());
|
||||||
osg::Vec3f newPos = pos + it->mVelocity * duration;
|
osg::Vec3f newPos = pos + projectileState.mVelocity * duration;
|
||||||
|
|
||||||
// rotation does not work well for throwing projectiles - their roll angle will depend on shooting direction.
|
// rotation does not work well for throwing projectiles - their roll angle will depend on shooting direction.
|
||||||
if (!it->mThrown)
|
if (!projectileState.mThrown)
|
||||||
{
|
{
|
||||||
osg::Quat orient;
|
osg::Quat orient;
|
||||||
orient.makeRotate(osg::Vec3f(0,1,0), it->mVelocity);
|
orient.makeRotate(osg::Vec3f(0,1,0), projectileState.mVelocity);
|
||||||
it->mNode->setAttitude(orient);
|
projectileState.mNode->setAttitude(orient);
|
||||||
}
|
}
|
||||||
|
|
||||||
it->mNode->setPosition(newPos);
|
projectileState.mNode->setPosition(newPos);
|
||||||
|
|
||||||
update(*it, duration);
|
mPhysics->updateProjectile(projectileState.mProjectileId, newPos);
|
||||||
|
|
||||||
MWWorld::Ptr caster = it->getCaster();
|
update(projectileState, duration);
|
||||||
|
|
||||||
|
MWWorld::Ptr caster = projectileState.getCaster();
|
||||||
|
|
||||||
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
|
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
|
||||||
std::vector<MWWorld::Ptr> targetActors;
|
std::vector<MWWorld::Ptr> targetActors;
|
||||||
|
@ -516,50 +516,149 @@ namespace MWWorld
|
||||||
|
|
||||||
// Check for impact
|
// Check for impact
|
||||||
// TODO: use a proper btRigidBody / btGhostObject?
|
// TODO: use a proper btRigidBody / btGhostObject?
|
||||||
MWPhysics::RayCastingResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile);
|
const auto result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, projectileState.mProjectileId);
|
||||||
|
|
||||||
bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos);
|
bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos);
|
||||||
|
|
||||||
if (result.mHit || underwater)
|
if (result.mHit || underwater)
|
||||||
{
|
{
|
||||||
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mIdArrow);
|
|
||||||
|
|
||||||
// Try to get a Ptr to the bow that was used. It might no longer exist.
|
// Try to get a Ptr to the bow that was used. It might no longer exist.
|
||||||
|
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), projectileState.mIdArrow);
|
||||||
MWWorld::Ptr bow = projectileRef.getPtr();
|
MWWorld::Ptr bow = projectileRef.getPtr();
|
||||||
if (!caster.isEmpty() && it->mIdArrow != it->mBowId)
|
if (!caster.isEmpty() && projectileState.mIdArrow != projectileState.mBowId)
|
||||||
{
|
{
|
||||||
MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster);
|
MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster);
|
||||||
MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||||
if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), it->mBowId))
|
if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId))
|
||||||
bow = *invIt;
|
bow = *invIt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (caster.isEmpty())
|
if (caster.isEmpty())
|
||||||
caster = result.mHitObject;
|
caster = result.mHitObject;
|
||||||
|
|
||||||
MWMechanics::projectileHit(caster, result.mHitObject, bow, projectileRef.getPtr(), result.mHit ? result.mHitPos : newPos, it->mAttackStrength);
|
MWMechanics::projectileHit(caster, result.mHitObject, bow, projectileRef.getPtr(), result.mHit ? result.mHitPos : newPos, projectileState.mAttackStrength);
|
||||||
mPhysics->reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal));
|
mPhysics->reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal));
|
||||||
|
|
||||||
if (underwater)
|
if (underwater)
|
||||||
mRendering->emitWaterRipple(newPos);
|
mRendering->emitWaterRipple(newPos);
|
||||||
|
|
||||||
mParent->removeChild(it->mNode);
|
cleanupProjectile(projectileState);
|
||||||
it = mProjectiles.erase(it);
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectileManager::processHits()
|
||||||
|
{
|
||||||
|
for (auto& projectileState : mProjectiles)
|
||||||
|
{
|
||||||
|
if (projectileState.mToDelete)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
||||||
|
if (projectile->isActive())
|
||||||
|
continue;
|
||||||
|
const auto target = projectile->getTarget();
|
||||||
|
const auto pos = projectile->getHitPos();
|
||||||
|
MWWorld::Ptr caster = projectileState.getCaster();
|
||||||
|
assert(target != caster);
|
||||||
|
if (!isValidTarget(caster, target))
|
||||||
|
{
|
||||||
|
projectile->activate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
++it;
|
if (caster.isEmpty())
|
||||||
|
caster = target;
|
||||||
|
|
||||||
|
// Try to get a Ptr to the bow that was used. It might no longer exist.
|
||||||
|
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), projectileState.mIdArrow);
|
||||||
|
MWWorld::Ptr bow = projectileRef.getPtr();
|
||||||
|
if (!caster.isEmpty() && projectileState.mIdArrow != projectileState.mBowId)
|
||||||
|
{
|
||||||
|
MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster);
|
||||||
|
MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||||
|
if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId))
|
||||||
|
bow = *invIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
projectileState.mHitPosition = pos;
|
||||||
|
cleanupProjectile(projectileState);
|
||||||
|
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);
|
||||||
}
|
}
|
||||||
|
for (auto& magicBoltState : mMagicBolts)
|
||||||
|
{
|
||||||
|
if (magicBoltState.mToDelete)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId);
|
||||||
|
if (projectile->isActive())
|
||||||
|
continue;
|
||||||
|
const auto target = projectile->getTarget();
|
||||||
|
const auto pos = projectile->getHitPos();
|
||||||
|
MWWorld::Ptr caster = magicBoltState.getCaster();
|
||||||
|
assert(target != caster);
|
||||||
|
if (!isValidTarget(caster, target))
|
||||||
|
{
|
||||||
|
projectile->activate();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
magicBoltState.mHitPosition = pos;
|
||||||
|
cleanupMagicBolt(magicBoltState);
|
||||||
|
|
||||||
|
MWMechanics::CastSpell cast(caster, target);
|
||||||
|
cast.mHitPosition = pos;
|
||||||
|
cast.mId = magicBoltState.mSpellId;
|
||||||
|
cast.mSourceName = magicBoltState.mSourceName;
|
||||||
|
cast.mStack = false;
|
||||||
|
cast.inflict(target, caster, magicBoltState.mEffects, ESM::RT_Target, false, true);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, target, ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName);
|
||||||
|
}
|
||||||
|
mProjectiles.erase(std::remove_if(mProjectiles.begin(), mProjectiles.end(), [](const State& state) { return state.mToDelete; }),
|
||||||
|
mProjectiles.end());
|
||||||
|
mMagicBolts.erase(std::remove_if(mMagicBolts.begin(), mMagicBolts.end(), [](const State& state) { return state.mToDelete; }),
|
||||||
|
mMagicBolts.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProjectileManager::isValidTarget(const MWWorld::Ptr& caster, const MWWorld::Ptr& target)
|
||||||
|
{
|
||||||
|
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
|
||||||
|
std::vector<MWWorld::Ptr> targetActors;
|
||||||
|
if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer())
|
||||||
|
{
|
||||||
|
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
|
||||||
|
if (!targetActors.empty())
|
||||||
|
{
|
||||||
|
bool validTarget = false;
|
||||||
|
for (MWWorld::Ptr& targetActor : targetActors)
|
||||||
|
{
|
||||||
|
if (targetActor == target)
|
||||||
|
{
|
||||||
|
validTarget = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return validTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectileManager::cleanupProjectile(ProjectileManager::ProjectileState& state)
|
void ProjectileManager::cleanupProjectile(ProjectileManager::ProjectileState& state)
|
||||||
{
|
{
|
||||||
mParent->removeChild(state.mNode);
|
mParent->removeChild(state.mNode);
|
||||||
|
mPhysics->removeProjectile(state.mProjectileId);
|
||||||
|
state.mToDelete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectileManager::cleanupMagicBolt(ProjectileManager::MagicBoltState& state)
|
void ProjectileManager::cleanupMagicBolt(ProjectileManager::MagicBoltState& state)
|
||||||
{
|
{
|
||||||
mParent->removeChild(state.mNode);
|
mParent->removeChild(state.mNode);
|
||||||
|
mPhysics->removeProjectile(state.mProjectileId);
|
||||||
|
state.mToDelete = true;
|
||||||
for (size_t soundIter = 0; soundIter != state.mSounds.size(); soundIter++)
|
for (size_t soundIter = 0; soundIter != state.mSounds.size(); soundIter++)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getSoundManager()->stopSound(state.mSounds.at(soundIter));
|
MWBase::Environment::get().getSoundManager()->stopSound(state.mSounds.at(soundIter));
|
||||||
|
@ -568,15 +667,12 @@ namespace MWWorld
|
||||||
|
|
||||||
void ProjectileManager::clear()
|
void ProjectileManager::clear()
|
||||||
{
|
{
|
||||||
for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)
|
for (auto& mProjectile : mProjectiles)
|
||||||
{
|
cleanupProjectile(mProjectile);
|
||||||
cleanupProjectile(*it);
|
|
||||||
}
|
|
||||||
mProjectiles.clear();
|
mProjectiles.clear();
|
||||||
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it)
|
|
||||||
{
|
for (auto& mMagicBolt : mMagicBolts)
|
||||||
cleanupMagicBolt(*it);
|
cleanupMagicBolt(mMagicBolt);
|
||||||
}
|
|
||||||
mMagicBolts.clear();
|
mMagicBolts.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,6 +729,7 @@ namespace MWWorld
|
||||||
state.mVelocity = esm.mVelocity;
|
state.mVelocity = esm.mVelocity;
|
||||||
state.mIdArrow = esm.mId;
|
state.mIdArrow = esm.mId;
|
||||||
state.mAttackStrength = esm.mAttackStrength;
|
state.mAttackStrength = esm.mAttackStrength;
|
||||||
|
state.mToDelete = false;
|
||||||
|
|
||||||
std::string model;
|
std::string model;
|
||||||
try
|
try
|
||||||
|
@ -640,9 +737,10 @@ namespace MWWorld
|
||||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId);
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId);
|
||||||
MWWorld::Ptr ptr = ref.getPtr();
|
MWWorld::Ptr ptr = ref.getPtr();
|
||||||
model = ptr.getClass().getModel(ptr);
|
model = ptr.getClass().getModel(ptr);
|
||||||
|
|
||||||
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
|
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
|
||||||
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
|
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
|
||||||
|
|
||||||
|
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition));
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
|
@ -654,7 +752,7 @@ namespace MWWorld
|
||||||
mProjectiles.push_back(state);
|
mProjectiles.push_back(state);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (type == ESM::REC_MPRJ)
|
if (type == ESM::REC_MPRJ)
|
||||||
{
|
{
|
||||||
ESM::MagicBoltState esm;
|
ESM::MagicBoltState esm;
|
||||||
esm.load(reader);
|
esm.load(reader);
|
||||||
|
@ -663,7 +761,8 @@ namespace MWWorld
|
||||||
state.mIdMagic.push_back(esm.mId);
|
state.mIdMagic.push_back(esm.mId);
|
||||||
state.mSpellId = esm.mSpellId;
|
state.mSpellId = esm.mSpellId;
|
||||||
state.mActorId = esm.mActorId;
|
state.mActorId = esm.mActorId;
|
||||||
std::string texture = "";
|
state.mToDelete = false;
|
||||||
|
std::string texture;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -686,6 +785,7 @@ namespace MWWorld
|
||||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0));
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0));
|
||||||
MWWorld::Ptr ptr = ref.getPtr();
|
MWWorld::Ptr ptr = ref.getPtr();
|
||||||
model = ptr.getClass().getModel(ptr);
|
model = ptr.getClass().getModel(ptr);
|
||||||
|
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition));
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,6 +56,8 @@ namespace MWWorld
|
||||||
|
|
||||||
void update(float dt);
|
void update(float dt);
|
||||||
|
|
||||||
|
void processHits();
|
||||||
|
|
||||||
/// Removes all current projectiles. Should be called when switching to a new worldspace.
|
/// Removes all current projectiles. Should be called when switching to a new worldspace.
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
@ -76,6 +78,9 @@ namespace MWWorld
|
||||||
std::shared_ptr<MWRender::EffectAnimationTime> mEffectAnimationTime;
|
std::shared_ptr<MWRender::EffectAnimationTime> mEffectAnimationTime;
|
||||||
|
|
||||||
int mActorId;
|
int mActorId;
|
||||||
|
int mProjectileId;
|
||||||
|
|
||||||
|
osg::Vec3f mHitPosition;
|
||||||
|
|
||||||
// TODO: this will break when the game is saved and reloaded, since there is currently
|
// TODO: this will break when the game is saved and reloaded, since there is currently
|
||||||
// no way to write identifiers for non-actors to a savegame.
|
// no way to write identifiers for non-actors to a savegame.
|
||||||
|
@ -88,6 +93,8 @@ namespace MWWorld
|
||||||
|
|
||||||
// MW-id of an arrow projectile
|
// MW-id of an arrow projectile
|
||||||
std::string mIdArrow;
|
std::string mIdArrow;
|
||||||
|
|
||||||
|
bool mToDelete;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MagicBoltState : public State
|
struct MagicBoltState : public State
|
||||||
|
@ -125,6 +132,8 @@ namespace MWWorld
|
||||||
void moveProjectiles(float dt);
|
void moveProjectiles(float dt);
|
||||||
void moveMagicBolts(float dt);
|
void moveMagicBolts(float dt);
|
||||||
|
|
||||||
|
bool isValidTarget(const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
|
||||||
|
|
||||||
void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient,
|
void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient,
|
||||||
bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture = "");
|
bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture = "");
|
||||||
void update (State& state, float duration);
|
void update (State& state, float duration);
|
||||||
|
|
|
@ -1373,25 +1373,24 @@ namespace MWWorld
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float terrainHeight = -std::numeric_limits<float>::max();
|
const float terrainHeight = ptr.getCell()->isExterior() ? getTerrainHeightAt(pos) : -std::numeric_limits<float>::max();
|
||||||
if (ptr.getCell()->isExterior())
|
pos.z() = std::max(pos.z(), terrainHeight + 20); // place slightly above terrain. will snap down to ground with code below
|
||||||
terrainHeight = getTerrainHeightAt(pos);
|
|
||||||
|
|
||||||
if (pos.z() < terrainHeight)
|
|
||||||
pos.z() = terrainHeight;
|
|
||||||
|
|
||||||
pos.z() += 20; // place slightly above. will snap down to ground with code below
|
|
||||||
|
|
||||||
// We still should trace down dead persistent actors - they do not use the "swimdeath" animation.
|
// We still should trace down dead persistent actors - they do not use the "swimdeath" animation.
|
||||||
bool swims = ptr.getClass().isActor() && isSwimming(ptr) && !(ptr.getClass().isPersistent(ptr) && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished());
|
bool swims = ptr.getClass().isActor() && isSwimming(ptr) && !(ptr.getClass().isPersistent(ptr) && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished());
|
||||||
if (force || !ptr.getClass().isActor() || (!isFlying(ptr) && !swims && isActorCollisionEnabled(ptr)))
|
if (force || !ptr.getClass().isActor() || (!isFlying(ptr) && !swims && isActorCollisionEnabled(ptr)))
|
||||||
{
|
{
|
||||||
osg::Vec3f traced = mPhysics->traceDown(ptr, pos, Constants::CellSizeInUnits);
|
osg::Vec3f traced = mPhysics->traceDown(ptr, pos, Constants::CellSizeInUnits);
|
||||||
if (traced.z() < pos.z())
|
pos.z() = std::min(pos.z(), traced.z());
|
||||||
pos.z() = traced.z();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z());
|
moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z());
|
||||||
|
if (force) // force physics to use the new position
|
||||||
|
{
|
||||||
|
auto actor = mPhysics->getActor(ptr);
|
||||||
|
if(actor)
|
||||||
|
actor->resetPosition();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::fixPosition()
|
void World::fixPosition()
|
||||||
|
@ -1490,17 +1489,11 @@ namespace MWWorld
|
||||||
ipos.pos[1] = spawnPoint.y();
|
ipos.pos[1] = spawnPoint.y();
|
||||||
ipos.pos[2] = spawnPoint.z();
|
ipos.pos[2] = spawnPoint.z();
|
||||||
|
|
||||||
if (!referenceObject.getClass().isActor())
|
if (referenceObject.getClass().isActor())
|
||||||
{
|
|
||||||
ipos.rot[0] = referenceObject.getRefData().getPosition().rot[0];
|
|
||||||
ipos.rot[1] = referenceObject.getRefData().getPosition().rot[1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
ipos.rot[0] = 0;
|
ipos.rot[0] = 0;
|
||||||
ipos.rot[1] = 0;
|
ipos.rot[1] = 0;
|
||||||
}
|
}
|
||||||
ipos.rot[2] = referenceObject.getRefData().getPosition().rot[2];
|
|
||||||
|
|
||||||
MWWorld::Ptr placed = copyObjectToCell(ptr, referenceCell, ipos, ptr.getRefData().getCount(), false);
|
MWWorld::Ptr placed = copyObjectToCell(ptr, referenceCell, ipos, ptr.getRefData().getCount(), false);
|
||||||
adjustPosition(placed, true); // snap to ground
|
adjustPosition(placed, true); // snap to ground
|
||||||
|
@ -1545,6 +1538,7 @@ namespace MWWorld
|
||||||
mProjectileManager->update(duration);
|
mProjectileManager->update(duration);
|
||||||
|
|
||||||
const auto results = mPhysics->applyQueuedMovement(duration, mDiscardMovements, frameStart, frameNumber, stats);
|
const auto results = mPhysics->applyQueuedMovement(duration, mDiscardMovements, frameStart, frameNumber, stats);
|
||||||
|
mProjectileManager->processHits();
|
||||||
mDiscardMovements = false;
|
mDiscardMovements = false;
|
||||||
|
|
||||||
for(const auto& [actor, position]: results)
|
for(const auto& [actor, position]: results)
|
||||||
|
|
|
@ -233,11 +233,19 @@ namespace Misc
|
||||||
, mTechnique(technique)
|
, mTechnique(technique)
|
||||||
, mGeometryShaderMask(geometryShaderMask)
|
, mGeometryShaderMask(geometryShaderMask)
|
||||||
, mNoShaderMask(noShaderMask)
|
, mNoShaderMask(noShaderMask)
|
||||||
|
, mMasterConfig(new SharedShadowMapConfig)
|
||||||
|
, mSlaveConfig(new SharedShadowMapConfig)
|
||||||
|
, mSharedShadowMaps(Settings::Manager::getBool("shared shadow maps", "Stereo"))
|
||||||
{
|
{
|
||||||
if (technique == Technique::None)
|
if (technique == Technique::None)
|
||||||
// Do nothing
|
// Do nothing
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
mMasterConfig->_id = "STEREO";
|
||||||
|
mMasterConfig->_master = true;
|
||||||
|
mSlaveConfig->_id = "STEREO";
|
||||||
|
mSlaveConfig->_master = false;
|
||||||
|
|
||||||
SceneUtil::FindByNameVisitor findScene("Scene Root");
|
SceneUtil::FindByNameVisitor findScene("Scene Root");
|
||||||
mRoot->accept(findScene);
|
mRoot->accept(findScene);
|
||||||
mScene = findScene.mFoundNode;
|
mScene = findScene.mFoundNode;
|
||||||
|
@ -292,6 +300,12 @@ namespace Misc
|
||||||
mRightCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
mRightCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
mRightCamera->setCullMask(mMainCamera->getCullMask());
|
mRightCamera->setCullMask(mMainCamera->getCullMask());
|
||||||
|
|
||||||
|
if (mSharedShadowMaps)
|
||||||
|
{
|
||||||
|
mLeftCamera->setUserData(mMasterConfig);
|
||||||
|
mRightCamera->setUserData(mSlaveConfig);
|
||||||
|
}
|
||||||
|
|
||||||
// Slave cameras must have their viewports defined immediately
|
// Slave cameras must have their viewports defined immediately
|
||||||
auto width = mMainCamera->getViewport()->width();
|
auto width = mMainCamera->getViewport()->width();
|
||||||
auto height = mMainCamera->getViewport()->height();
|
auto height = mMainCamera->getViewport()->height();
|
||||||
|
@ -366,65 +380,66 @@ namespace Misc
|
||||||
auto width = mMainCamera->getViewport()->width();
|
auto width = mMainCamera->getViewport()->width();
|
||||||
auto height = mMainCamera->getViewport()->height();
|
auto height = mMainCamera->getViewport()->height();
|
||||||
|
|
||||||
|
// To correctly cull when drawing stereo using the geometry shader, the main camera must
|
||||||
|
// draw a fake view+perspective that includes the full frustums of both the left and right eyes.
|
||||||
|
// This frustum will be computed as a perspective frustum from a position P slightly behind the eyes L and R
|
||||||
|
// where it creates the minimum frustum encompassing both eyes' frustums.
|
||||||
|
// NOTE: I make an assumption that the eyes lie in a horizontal plane relative to the base view,
|
||||||
|
// and lie mirrored around the Y axis (straight ahead).
|
||||||
|
// Re-think this if that turns out to be a bad assumption.
|
||||||
|
View frustumView;
|
||||||
|
|
||||||
|
// Compute Frustum angles. A simple min/max.
|
||||||
|
/* Example values for reference:
|
||||||
|
Left:
|
||||||
|
angleLeft -0.767549932 float
|
||||||
|
angleRight 0.620896876 float
|
||||||
|
angleDown -0.837898076 float
|
||||||
|
angleUp 0.726982594 float
|
||||||
|
|
||||||
|
Right:
|
||||||
|
angleLeft -0.620896876 float
|
||||||
|
angleRight 0.767549932 float
|
||||||
|
angleDown -0.837898076 float
|
||||||
|
angleUp 0.726982594 float
|
||||||
|
*/
|
||||||
|
frustumView.fov.angleLeft = std::min(left.fov.angleLeft, right.fov.angleLeft);
|
||||||
|
frustumView.fov.angleRight = std::max(left.fov.angleRight, right.fov.angleRight);
|
||||||
|
frustumView.fov.angleDown = std::min(left.fov.angleDown, right.fov.angleDown);
|
||||||
|
frustumView.fov.angleUp = std::max(left.fov.angleUp, right.fov.angleUp);
|
||||||
|
|
||||||
|
// Check that the case works for this approach
|
||||||
|
auto maxAngle = std::max(frustumView.fov.angleRight - frustumView.fov.angleLeft, frustumView.fov.angleUp - frustumView.fov.angleDown);
|
||||||
|
if (maxAngle > osg::PI)
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "Total FOV exceeds 180 degrees. Case cannot be culled in single-pass VR. Disabling culling to cope. Consider switching to dual-pass VR.";
|
||||||
|
mMainCamera->setCullingActive(false);
|
||||||
|
return;
|
||||||
|
// TODO: An explicit frustum projection could cope, so implement that later. Guarantee you there will be VR headsets with total horizontal fov > 180 in the future. Maybe already.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the law of sines on the triangle spanning PLR to determine P
|
||||||
|
double angleLeft = std::abs(frustumView.fov.angleLeft);
|
||||||
|
double angleRight = std::abs(frustumView.fov.angleRight);
|
||||||
|
double lengthRL = (rightEye - leftEye).length();
|
||||||
|
double ratioRL = lengthRL / std::sin(osg::PI - angleLeft - angleRight);
|
||||||
|
double lengthLP = ratioRL * std::sin(angleRight);
|
||||||
|
|
||||||
|
osg::Vec3d directionLP = osg::Vec3(std::cos(-angleLeft), std::sin(-angleLeft), 0);
|
||||||
|
osg::Vec3d LP = directionLP * lengthLP;
|
||||||
|
frustumView.pose.position = leftEye + LP;
|
||||||
|
//frustumView.pose.position.x() += 1000;
|
||||||
|
|
||||||
|
// Base view position is 0.0, by definition.
|
||||||
|
// The length of the vector P is therefore the required offset to near/far.
|
||||||
|
auto nearFarOffset = frustumView.pose.position.length();
|
||||||
|
|
||||||
|
// Generate the frustum matrices
|
||||||
|
auto frustumViewMatrix = viewMatrix * frustumView.pose.viewMatrix(true);
|
||||||
|
auto frustumProjectionMatrix = frustumView.fov.perspectiveMatrix(near + nearFarOffset, far + nearFarOffset);
|
||||||
|
|
||||||
if (mTechnique == Technique::GeometryShader_IndexedViewports)
|
if (mTechnique == Technique::GeometryShader_IndexedViewports)
|
||||||
{
|
{
|
||||||
// To correctly cull when drawing stereo using the geometry shader, the main camera must
|
|
||||||
// draw a fake view+perspective that includes the full frustums of both the left and right eyes.
|
|
||||||
// This frustum will be computed as a perspective frustum from a position P slightly behind the eyes L and R
|
|
||||||
// where it creates the minimum frustum encompassing both eyes' frustums.
|
|
||||||
// NOTE: I make an assumption that the eyes lie in a horizontal plane relative to the base view,
|
|
||||||
// and lie mirrored around the Y axis (straight ahead).
|
|
||||||
// Re-think this if that turns out to be a bad assumption.
|
|
||||||
View frustumView;
|
|
||||||
|
|
||||||
// Compute Frustum angles. A simple min/max.
|
|
||||||
/* Example values for reference:
|
|
||||||
Left:
|
|
||||||
angleLeft -0.767549932 float
|
|
||||||
angleRight 0.620896876 float
|
|
||||||
angleDown -0.837898076 float
|
|
||||||
angleUp 0.726982594 float
|
|
||||||
|
|
||||||
Right:
|
|
||||||
angleLeft -0.620896876 float
|
|
||||||
angleRight 0.767549932 float
|
|
||||||
angleDown -0.837898076 float
|
|
||||||
angleUp 0.726982594 float
|
|
||||||
*/
|
|
||||||
frustumView.fov.angleLeft = std::min(left.fov.angleLeft, right.fov.angleLeft);
|
|
||||||
frustumView.fov.angleRight = std::max(left.fov.angleRight, right.fov.angleRight);
|
|
||||||
frustumView.fov.angleDown = std::min(left.fov.angleDown, right.fov.angleDown);
|
|
||||||
frustumView.fov.angleUp = std::max(left.fov.angleUp, right.fov.angleUp);
|
|
||||||
|
|
||||||
// Check that the case works for this approach
|
|
||||||
auto maxAngle = std::max(frustumView.fov.angleRight - frustumView.fov.angleLeft, frustumView.fov.angleUp - frustumView.fov.angleDown);
|
|
||||||
if (maxAngle > osg::PI)
|
|
||||||
{
|
|
||||||
Log(Debug::Error) << "Total FOV exceeds 180 degrees. Case cannot be culled in single-pass VR. Disabling culling to cope. Consider switching to dual-pass VR.";
|
|
||||||
mMainCamera->setCullingActive(false);
|
|
||||||
return;
|
|
||||||
// TODO: An explicit frustum projection could cope, so implement that later. Guarantee you there will be VR headsets with total fov > 180 in the future. Maybe already.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the law of sines on the triangle spanning PLR to determine P
|
|
||||||
double angleLeft = std::abs(frustumView.fov.angleLeft);
|
|
||||||
double angleRight = std::abs(frustumView.fov.angleRight);
|
|
||||||
double lengthRL = (rightEye - leftEye).length();
|
|
||||||
double ratioRL = lengthRL / std::sin(osg::PI - angleLeft - angleRight);
|
|
||||||
double lengthLP = ratioRL * std::sin(angleRight);
|
|
||||||
|
|
||||||
osg::Vec3d directionLP = osg::Vec3(std::cos(-angleLeft), std::sin(-angleLeft), 0);
|
|
||||||
osg::Vec3d LP = directionLP * lengthLP;
|
|
||||||
frustumView.pose.position = leftEye + LP;
|
|
||||||
//frustumView.pose.position.x() += 1000;
|
|
||||||
|
|
||||||
// Base view position is 0.0, by definition.
|
|
||||||
// The length of the vector P is therefore the required offset to near/far.
|
|
||||||
auto nearFarOffset = frustumView.pose.position.length();
|
|
||||||
|
|
||||||
// Generate the frustum matrices
|
|
||||||
auto frustumViewMatrix = viewMatrix * frustumView.pose.viewMatrix(true);
|
|
||||||
auto frustumProjectionMatrix = frustumView.fov.perspectiveMatrix(near + nearFarOffset, far + nearFarOffset);
|
|
||||||
|
|
||||||
// Update camera with frustum matrices
|
// Update camera with frustum matrices
|
||||||
mMainCamera->setViewMatrix(frustumViewMatrix);
|
mMainCamera->setViewMatrix(frustumViewMatrix);
|
||||||
|
@ -439,6 +454,18 @@ namespace Misc
|
||||||
|
|
||||||
mLeftCamera->setViewport(0, 0, width / 2, height);
|
mLeftCamera->setViewport(0, 0, width / 2, height);
|
||||||
mRightCamera->setViewport(width / 2, 0, width / 2, height);
|
mRightCamera->setViewport(width / 2, 0, width / 2, height);
|
||||||
|
|
||||||
|
if (mMasterConfig->_projection == nullptr)
|
||||||
|
mMasterConfig->_projection = new osg::RefMatrix;
|
||||||
|
if (mMasterConfig->_modelView == nullptr)
|
||||||
|
mMasterConfig->_modelView = new osg::RefMatrix;
|
||||||
|
|
||||||
|
if (mSharedShadowMaps)
|
||||||
|
{
|
||||||
|
mMasterConfig->_referenceFrame = mMainCamera->getReferenceFrame();
|
||||||
|
mMasterConfig->_modelView->set(frustumViewMatrix);
|
||||||
|
mMasterConfig->_projection->set(projectionMatrix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,9 +489,9 @@ namespace Misc
|
||||||
stereoViewProjectionsUniform->setElement(1, frustumViewMatrixInverse * mRightCamera->getViewMatrix() * mRightCamera->getProjectionMatrix());
|
stereoViewProjectionsUniform->setElement(1, frustumViewMatrixInverse * mRightCamera->getViewMatrix() * mRightCamera->getProjectionMatrix());
|
||||||
}
|
}
|
||||||
|
|
||||||
void StereoView::setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb)
|
void StereoView::setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb_)
|
||||||
{
|
{
|
||||||
this->cb = cb;
|
cb = cb_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void disableStereoForCamera(osg::Camera* camera)
|
void disableStereoForCamera(osg::Camera* camera)
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <components/sceneutil/mwshadowtechnique.hpp>
|
||||||
|
|
||||||
// Some cursed headers like to define these
|
// Some cursed headers like to define these
|
||||||
#if defined(near) || defined(far)
|
#if defined(near) || defined(far)
|
||||||
#undef near
|
#undef near
|
||||||
|
@ -122,6 +124,11 @@ namespace Misc
|
||||||
osg::ref_ptr<osg::Camera> mLeftCamera{ new osg::Camera };
|
osg::ref_ptr<osg::Camera> mLeftCamera{ new osg::Camera };
|
||||||
osg::ref_ptr<osg::Camera> mRightCamera{ new osg::Camera };
|
osg::ref_ptr<osg::Camera> mRightCamera{ new osg::Camera };
|
||||||
|
|
||||||
|
using SharedShadowMapConfig = SceneUtil::MWShadowTechnique::SharedShadowMapConfig;
|
||||||
|
osg::ref_ptr<SharedShadowMapConfig> mMasterConfig;
|
||||||
|
osg::ref_ptr<SharedShadowMapConfig> mSlaveConfig;
|
||||||
|
bool mSharedShadowMaps;
|
||||||
|
|
||||||
// Camera viewports
|
// Camera viewports
|
||||||
bool flipViewOrder{ true };
|
bool flipViewOrder{ true };
|
||||||
|
|
||||||
|
|
|
@ -934,9 +934,9 @@ void SceneUtil::MWShadowTechnique::shareShadowMap(osgUtil::CullVisitor& cv, View
|
||||||
// Then initialize new copies of the data that will be written with view-specific data
|
// Then initialize new copies of the data that will be written with view-specific data
|
||||||
// (the stateset and the texgens).
|
// (the stateset and the texgens).
|
||||||
|
|
||||||
lhs->_traversalNumber = rhs->_traversalNumber;
|
|
||||||
lhs->_viewDependentShadowMap = rhs->_viewDependentShadowMap;
|
lhs->_viewDependentShadowMap = rhs->_viewDependentShadowMap;
|
||||||
lhs->getStateSet()->clear();
|
auto* stateset = lhs->getStateSet(cv.getTraversalNumber());
|
||||||
|
stateset->clear();
|
||||||
lhs->_lightDataList = rhs->_lightDataList;
|
lhs->_lightDataList = rhs->_lightDataList;
|
||||||
lhs->_numValidShadows = rhs->_numValidShadows;
|
lhs->_numValidShadows = rhs->_numValidShadows;
|
||||||
|
|
||||||
|
@ -1480,6 +1480,7 @@ void MWShadowTechnique::update(osg::NodeVisitor& nv)
|
||||||
|
|
||||||
void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!_enableShadows)
|
if (!_enableShadows)
|
||||||
{
|
{
|
||||||
if (mSetDummyStateWhenDisabled)
|
if (mSetDummyStateWhenDisabled)
|
||||||
|
@ -1554,7 +1555,6 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
||||||
|
|
||||||
if (doCastShadow)
|
if (doCastShadow)
|
||||||
{
|
{
|
||||||
vdd->setTraversalNumber(vdd->getTraversalNumber() + 1);
|
|
||||||
castShadows(cv, vdd);
|
castShadows(cv, vdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1566,7 +1566,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
||||||
|
|
||||||
if (vdd->_numValidShadows>0)
|
if (vdd->_numValidShadows>0)
|
||||||
{
|
{
|
||||||
decoratorStateGraph->setStateSet(selectStateSetForRenderingShadow(*vdd));
|
decoratorStateGraph->setStateSet(selectStateSetForRenderingShadow(*vdd, cv.getTraversalNumber()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// OSG_NOTICE<<"End of shadow setup Projection matrix "<<*cv.getProjectionMatrix()<<std::endl;
|
// OSG_NOTICE<<"End of shadow setup Projection matrix "<<*cv.getProjectionMatrix()<<std::endl;
|
||||||
|
@ -3148,17 +3148,17 @@ void MWShadowTechnique::cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Ca
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::StateSet* MWShadowTechnique::selectStateSetForRenderingShadow(ViewDependentData& vdd) const
|
osg::StateSet* MWShadowTechnique::selectStateSetForRenderingShadow(ViewDependentData& vdd, unsigned int traversalNumber) const
|
||||||
{
|
{
|
||||||
OSG_INFO<<" selectStateSetForRenderingShadow() "<<vdd.getStateSet()<<std::endl;
|
OSG_INFO<<" selectStateSetForRenderingShadow() "<<vdd.getStateSet(traversalNumber)<<std::endl;
|
||||||
|
|
||||||
osg::ref_ptr<osg::StateSet> stateset = vdd.getStateSet();
|
osg::ref_ptr<osg::StateSet> stateset = vdd.getStateSet(traversalNumber);
|
||||||
|
|
||||||
stateset->clear();
|
stateset->clear();
|
||||||
|
|
||||||
stateset->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON);
|
stateset->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON);
|
||||||
|
|
||||||
for(const auto& uniform : _uniforms[vdd.getTraversalNumber() % 2])
|
for(const auto& uniform : _uniforms[traversalNumber % 2])
|
||||||
{
|
{
|
||||||
OSG_INFO<<"addUniform("<<uniform->getName()<<")"<<std::endl;
|
OSG_INFO<<"addUniform("<<uniform->getName()<<")"<<std::endl;
|
||||||
stateset->addUniform(uniform);
|
stateset->addUniform(uniform);
|
||||||
|
|
|
@ -214,7 +214,7 @@ namespace SceneUtil {
|
||||||
|
|
||||||
ShadowDataList& getShadowDataList() { return _shadowDataList; }
|
ShadowDataList& getShadowDataList() { return _shadowDataList; }
|
||||||
|
|
||||||
osg::StateSet* getStateSet() { return _stateset[_traversalNumber % 2].get(); }
|
osg::StateSet* getStateSet(unsigned int traversalNumber) { return _stateset[traversalNumber % 2].get(); }
|
||||||
|
|
||||||
virtual void releaseGLObjects(osg::State* = 0) const;
|
virtual void releaseGLObjects(osg::State* = 0) const;
|
||||||
|
|
||||||
|
@ -222,10 +222,6 @@ namespace SceneUtil {
|
||||||
|
|
||||||
void setNumValidShadows(unsigned int numValidShadows) { _numValidShadows = numValidShadows; }
|
void setNumValidShadows(unsigned int numValidShadows) { _numValidShadows = numValidShadows; }
|
||||||
|
|
||||||
void setTraversalNumber(unsigned int traversalNumber) { _traversalNumber = traversalNumber; }
|
|
||||||
|
|
||||||
unsigned int getTraversalNumber() { return _traversalNumber; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class MWShadowTechnique;
|
friend class MWShadowTechnique;
|
||||||
virtual ~ViewDependentData() {}
|
virtual ~ViewDependentData() {}
|
||||||
|
@ -238,7 +234,6 @@ namespace SceneUtil {
|
||||||
ShadowDataList _shadowDataList;
|
ShadowDataList _shadowDataList;
|
||||||
|
|
||||||
unsigned int _numValidShadows;
|
unsigned int _numValidShadows;
|
||||||
unsigned int _traversalNumber;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ViewDependentData* createViewDependentData(osgUtil::CullVisitor* cv);
|
virtual ViewDependentData* createViewDependentData(osgUtil::CullVisitor* cv);
|
||||||
|
@ -279,7 +274,7 @@ namespace SceneUtil {
|
||||||
|
|
||||||
virtual void cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Camera* camera) const;
|
virtual void cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Camera* camera) const;
|
||||||
|
|
||||||
virtual osg::StateSet* selectStateSetForRenderingShadow(ViewDependentData& vdd) const;
|
virtual osg::StateSet* selectStateSetForRenderingShadow(ViewDependentData& vdd, unsigned int traversalNumber) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~MWShadowTechnique();
|
virtual ~MWShadowTechnique();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "sdlgraphicswindow.hpp"
|
#include "sdlgraphicswindow.hpp"
|
||||||
|
|
||||||
#include <osgViewer/viewer>
|
#include <osgViewer/Viewer>
|
||||||
|
|
||||||
#include <SDL_video.h>
|
#include <SDL_video.h>
|
||||||
|
|
||||||
|
|
|
@ -361,6 +361,9 @@ trainers training skills based on base skill = false
|
||||||
# Make stealing items from NPCs that were knocked down possible during combat.
|
# Make stealing items from NPCs that were knocked down possible during combat.
|
||||||
always allow stealing from knocked out actors = false
|
always allow stealing from knocked out actors = false
|
||||||
|
|
||||||
|
# Enables visually harvesting plants for models that support it.
|
||||||
|
graphic herbalism = true
|
||||||
|
|
||||||
[General]
|
[General]
|
||||||
|
|
||||||
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).
|
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).
|
||||||
|
@ -962,6 +965,20 @@ defer aabb update = true
|
||||||
# Loading arbitrary meshes is not advised and may cause instability.
|
# Loading arbitrary meshes is not advised and may cause instability.
|
||||||
load unsupported nif files = false
|
load unsupported nif files = false
|
||||||
|
|
||||||
|
[Stereo]
|
||||||
|
|
||||||
|
# Enable/disable stereo view. This setting is ignored in VR.
|
||||||
|
stereo enabled = false
|
||||||
|
|
||||||
|
# Method used to render stereo if enabled
|
||||||
|
# Must be one of the following: BruteForce, GeometryShader
|
||||||
|
# BruteForce: Generates stereo using two cameras and two cull/render passes. Choose this if your game is GPU-bound.
|
||||||
|
# GeometryShader: Generates stereo in a single pass using automatically generated geometry shaders. May break custom shaders. Choose this if your game is CPU-bound.
|
||||||
|
stereo method = GeometryShader
|
||||||
|
|
||||||
|
# May accelerate the BruteForce method when shadows are enabled
|
||||||
|
shared shadow maps = true
|
||||||
|
|
||||||
[VR]
|
[VR]
|
||||||
# Should match your real height in the format meters.centimeters. This is used to scale your position within the vr stage to better match your character.
|
# Should match your real height in the format meters.centimeters. This is used to scale your position within the vr stage to better match your character.
|
||||||
real height = 1.85
|
real height = 1.85
|
||||||
|
|
Loading…
Reference in a new issue