Merge remote-tracking branch 'remotes/origin/shared_shadow_maps' into multiview_test_branch

pull/615/head
Mads Buvik Sandvei 4 years ago
commit 5beb0bc799

@ -8,6 +8,7 @@
Bug #2473: Unable to overstock merchants
Bug #2798: Mutable ESM records
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 #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
Bug #3789: Crash in visitEffectSources while in battle
@ -67,6 +68,7 @@
Bug #5656: Sneaking characters block hits while standing
Bug #5661: Region sounds don't play at the right interval
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
Feature #390: 3rd person look "over the shoulder"
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
actionequip timestamp actionalchemy cellstore actionapply actioneat
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
)
add_openmw_dir (mwphysics
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile
closestnotmeconvexresultcallback raycasting mtphysics
)

@ -393,8 +393,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
, mNewGame (false)
, mCfgMgr(configurationManager)
{
MWClass::registerClasses();
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;
@ -867,6 +865,8 @@ void OMW::Engine::go()
std::string settingspath;
settingspath = loadSettings (settings);
MWClass::registerClasses();
// Create encoder
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);
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>());
// Font encoding settings
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));
// directory settings

@ -2,6 +2,7 @@
#include <components/esm/loadcont.hpp>
#include <components/esm/containerstate.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -59,6 +60,11 @@ namespace MWClass
return *this;
}
Container::Container()
{
mHarvestEnabled = Settings::Manager::getBool("graphic herbalism", "Game");
}
void Container::ensureCustomData (const MWWorld::Ptr& ptr) const
{
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);
if (animation == nullptr)
return false;

@ -30,11 +30,16 @@ namespace MWClass
class Container : public MWWorld::Class
{
bool mHarvestEnabled;
void ensureCustomData (const MWWorld::Ptr& ptr) const;
MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;
bool canBeHarvested(const MWWorld::ConstPtr& ptr) const;
public:
Container();
void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering

@ -48,9 +48,9 @@ namespace MWGui
const MWWorld::ESMStore &store =
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)
{
@ -59,14 +59,14 @@ namespace MWGui
std::string effectIDStr = ESM::MagicEffect::effectIdToString(effectId);
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);

@ -46,26 +46,28 @@ bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::Charac
{
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 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 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;
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));
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;
}
if (!turned)
return false;
// Check if the actor is already casting another spell
bool isCasting = MWBase::Environment::get().getMechanicsManager()->isCastingSpell(actor);

@ -70,7 +70,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic
mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
mCollisionObject->setActivationState(DISABLE_DEACTIVATION);
mCollisionObject->setCollisionShape(mShape.get());
mCollisionObject->setUserPointer(static_cast<PtrHolder*>(this));
mCollisionObject->setUserPointer(this);
updateRotation();
updateScale();

@ -2,6 +2,14 @@
#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
{
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)
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;
if (normalInWorldSpace)
hitNormalWorld = convexResult.m_hitNormalLocal;

@ -1,18 +1,22 @@
#include "closestnotmerayresultcallback.hpp"
#include <algorithm>
#include <utility>
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
#include "../mwworld/class.hpp"
#include "actor.hpp"
#include "collisiontype.hpp"
#include "projectile.hpp"
#include "ptrholder.hpp"
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)
, 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()))
{
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())
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
{
class Projectile;
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
{
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;
private:
const btCollisionObject* mMe;
const std::vector<const btCollisionObject*> mTargets;
Projectile* mProjectile;
};
}

@ -17,6 +17,7 @@
#include "mtphysics.hpp"
#include "object.hpp"
#include "physicssystem.hpp"
#include "projectile.hpp"
namespace
{
@ -297,12 +298,19 @@ namespace MWPhysics
return mPreviousMovementResults;
}
const PtrPositionList& PhysicsTaskScheduler::resetSimulation()
const PtrPositionList& PhysicsTaskScheduler::resetSimulation(const ActorMap& actors)
{
std::unique_lock lock(mSimulationMutex);
mMovementResults.clear();
mPreviousMovementResults.clear();
mActorsFrameData.clear();
for (const auto& [_, actor] : actors)
{
actor->resetPosition();
actor->setStandingOnPtr(nullptr);
mMovementResults[actor->getPtr()] = actor->getWorldPosition();
}
return mMovementResults;
}
@ -448,6 +456,11 @@ namespace MWPhysics
object->commitPositionChange();
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
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
void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const;

@ -24,7 +24,7 @@ namespace MWPhysics
mCollisionObject.reset(new btCollisionObject);
mCollisionObject->setCollisionShape(shapeInstance->getCollisionShape());
mCollisionObject->setUserPointer(static_cast<PtrHolder*>(this));
mCollisionObject->setUserPointer(this);
setScale(ptr.getCellRef().getScale());
setRotation(Misc::Convert::toBullet(ptr.getRefData().getBaseNode()->getAttitude()));

@ -46,6 +46,8 @@
#include "collisiontype.hpp"
#include "actor.hpp"
#include "projectile.hpp"
#include "trace.h"
#include "object.hpp"
#include "heightfield.hpp"
@ -64,6 +66,7 @@ namespace MWPhysics
, mResourceSystem(resourceSystem)
, mDebugDrawEnabled(false)
, mTimeAccum(0.0f)
, mProjectileId(0)
, mWaterHeight(0)
, mWaterEnabled(false)
, mParentNode(parentNode)
@ -112,7 +115,7 @@ namespace MWPhysics
mObjects.clear();
mActors.clear();
mProjectiles.clear();
}
void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue)
@ -248,7 +251,7 @@ namespace MWPhysics
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)
{
@ -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_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)
{
ObjectMap::iterator found = mObjects.find(old);
@ -553,6 +563,14 @@ namespace MWPhysics
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)
{
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)
{
ObjectMap::iterator found = mObjects.find(ptr);
@ -632,6 +661,15 @@ namespace MWPhysics
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()
{
ActorMap::iterator found = mActors.find(MWMechanics::getPlayer());
@ -677,14 +715,7 @@ namespace MWPhysics
mTimeAccum -= numSteps * mPhysicsDt;
if (skipSimulation)
{
for (auto& [_, actor] : mActors)
{
actor->resetPosition();
actor->setStandingOnPtr(nullptr);
}
return mTaskScheduler->resetSimulation();
}
return mTaskScheduler->resetSimulation(mActors);
return mTaskScheduler->moveActors(numSteps, mTimeAccum, prepareFrameData(numSteps), frameStart, frameNumber, stats);
}

@ -56,6 +56,9 @@ namespace MWPhysics
class Object;
class Actor;
class PhysicsTaskScheduler;
class Projectile;
using ActorMap = std::map<MWWorld::ConstPtr, std::shared_ptr<Actor>>;
struct ContactPoint
{
@ -125,6 +128,10 @@ namespace MWPhysics
void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World);
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);
Actor* getActor(const MWWorld::Ptr& ptr);
@ -132,6 +139,8 @@ namespace MWPhysics
const Object* getObject(const MWWorld::ConstPtr& ptr) const;
Projectile* getProjectile(int projectileId) const;
// Object or Actor
void remove (const MWWorld::Ptr& ptr);
@ -139,7 +148,6 @@ namespace MWPhysics
void updateRotation (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 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.
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>(),
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;
@ -265,9 +273,11 @@ namespace MWPhysics
std::set<Object*> mAnimatedObjects; // stores pointers to elements in mObjects
using ActorMap = std::map<MWWorld::ConstPtr, std::shared_ptr<Actor>>;
ActorMap mActors;
using ProjectileMap = std::map<int, std::shared_ptr<Projectile>>;
ProjectileMap mProjectiles;
using HeightFieldMap = std::map<std::pair<int, int>, HeightField *>;
HeightFieldMap mHeightFields;
@ -278,6 +288,8 @@ namespace MWPhysics
float mTimeAccum;
unsigned int mProjectileId;
float mWaterHeight;
bool mWaterEnabled;

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

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

@ -5,6 +5,9 @@
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
#include <BulletCollision/CollisionShapes/btConvexShape.h>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "collisiontype.hpp"
#include "actor.hpp"
#include "closestnotmeconvexresultcallback.hpp"

@ -26,7 +26,7 @@ namespace
void addScripts(MWWorld::ContainerStore& store, MWWorld::CellStore* cell)
{
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);
if(!script.empty())
@ -43,13 +43,10 @@ namespace
{
float sum = 0;
for (typename MWWorld::CellRefList<T>::List::const_iterator iter (
cellRefList.mList.begin());
iter!=cellRefList.mList.end();
++iter)
for (const auto& iter : cellRefList.mList)
{
if (iter->mData.getCount()>0)
sum += iter->mData.getCount()*iter->mBase->mData.mWeight;
if (iter.mData.getCount()>0)
sum += iter.mData.getCount()*iter.mBase->mData.mWeight;
}
return sum;
@ -62,12 +59,11 @@ namespace
store->resolve();
std::string id2 = Misc::StringUtils::lowerCase (id);
for (typename MWWorld::CellRefList<T>::List::iterator iter (list.mList.begin());
iter!=list.mList.end(); ++iter)
for (auto& iter : list.mList)
{
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);
return ptr;
}
@ -81,7 +77,7 @@ MWWorld::ResolutionListener::~ResolutionListener()
{
if(!mStore.mModified && mStore.mResolved && !mStore.mPtr.isEmpty())
{
for(const MWWorld::Ptr& ptr : mStore)
for(const auto&& ptr : mStore)
ptr.getRefData().setCount(0);
mStore.fillNonRandom(mStore.mPtr.get<ESM::Container>()->mBase->mInventory, "", mStore.mSeed);
addScripts(mStore, mStore.mPtr.mCell);
@ -127,15 +123,14 @@ template<typename T>
void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection,
ESM::InventoryState& inventory, int& index, bool equipable) const
{
for (typename CellRefList<T>::List::const_iterator iter (collection.mList.begin());
iter!=collection.mList.end(); ++iter)
for (const auto& iter : collection.mList)
{
if (iter->mData.getCount() == 0)
if (iter.mData.getCount() == 0)
continue;
ESM::ObjectState state;
storeState (*iter, state);
storeState (iter, state);
if (equipable)
storeEquipmentState(*iter, index, inventory);
storeEquipmentState(iter, index, inventory);
inventory.mItems.push_back (state);
++index;
}
@ -188,7 +183,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end()
int MWWorld::ContainerStore::count(const std::string &id) const
{
int total=0;
for (const auto& iter : *this)
for (const auto&& iter : *this)
if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))
total += iter.getRefData().getCount();
return total;
@ -430,14 +425,14 @@ void MWWorld::ContainerStore::rechargeItems(float duration)
updateRechargingItems();
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;
// attempt to restack when fully recharged
if (it->first->getCellRef().getEnchantmentCharge() == it->second)
it->first = restack(*it->first);
if (it.first->getCellRef().getEnchantmentCharge() == it.second)
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
{
for (auto iter(begin()); iter != end(); ++iter)
for (const auto&& iter : *this)
{
if (iter->getClass().showsInInventory(*iter))
if (iter.getClass().showsInInventory(iter))
return true;
}
@ -601,8 +596,8 @@ void MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const s
void MWWorld::ContainerStore::clear()
{
for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
iter->getRefData().setCount (0);
for (auto&& iter : *this)
iter.getRefData().setCount (0);
flagAsModified();
mModified = true;
@ -623,7 +618,7 @@ void MWWorld::ContainerStore::resolve()
{
if(!mResolved && !mPtr.isEmpty())
{
for(const MWWorld::Ptr& ptr : *this)
for(const auto&& ptr : *this)
ptr.getRefData().setCount(0);
Misc::Rng::Seed seed{mSeed};
fill(mPtr.get<ESM::Container>()->mBase->mInventory, "", seed);
@ -644,7 +639,7 @@ MWWorld::ResolutionHandle MWWorld::ContainerStore::resolveTemporarily()
}
if(!mResolved && !mPtr.isEmpty())
{
for(const MWWorld::Ptr& ptr : *this)
for(const auto&& ptr : *this)
ptr.getRefData().setCount(0);
Misc::Rng::Seed seed{mSeed};
fill(mPtr.get<ESM::Container>()->mBase->mInventory, "", seed);
@ -727,10 +722,10 @@ MWWorld::Ptr MWWorld::ContainerStore::findReplacement(const std::string& id)
{
MWWorld::Ptr item;
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;
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), id))
int iterHealth = iter.getClass().hasItemHealth(iter) ? iter.getClass().getItemHealth(iter) : 1;
if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))
{
// Prefer the stack with the lowest remaining uses
// 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) ||
(itemHealth <= 0 && iterHealth > 0))
{
item = *iter;
item = iter;
itemHealth = iterHealth;
}
}
@ -867,11 +862,8 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory)
mResolved = true;
int index = 0;
for (std::vector<ESM::ObjectState>::const_iterator
iter (inventory.mItems.begin()); iter!=inventory.mItems.end(); ++iter)
for (const ESM::ObjectState& state : inventory.mItems)
{
const ESM::ObjectState& state = *iter;
int type = MWBase::Environment::get().getWorld()->getStore().find(state.mRef.mRefID);
int thisIndex = index++;

@ -2,6 +2,7 @@
#include <iomanip>
#include <memory>
#include <osg/PositionAttitudeTransform>
#include <components/debug/debuglog.hpp>
@ -43,6 +44,7 @@
#include "../mwsound/sound.hpp"
#include "../mwphysics/physicssystem.hpp"
#include "../mwphysics/projectile.hpp"
namespace
{
@ -284,7 +286,7 @@ namespace MWWorld
else
state.mActorId = -1;
std::string texture = "";
std::string texture;
state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, state.mSourceName, state.mSpellId);
@ -302,6 +304,7 @@ namespace MWWorld
MWWorld::Ptr ptr = ref.getPtr();
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
@ -312,7 +315,9 @@ namespace MWWorld
if (sound)
state.mSounds.push_back(sound);
}
state.mProjectileId = mPhysics->addProjectile(caster, pos);
state.mToDelete = false;
mMagicBolts.push_back(state);
}
@ -325,7 +330,6 @@ namespace MWWorld
state.mIdArrow = projectile.getCellRef().getRefId();
state.mCasterHandle = actor;
state.mAttackStrength = attackStrength;
int type = projectile.get<ESM::Weapon>()->mBase->mData.mType;
state.mThrown = MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown;
@ -336,6 +340,8 @@ namespace MWWorld
if (!ptr.getClass().getEnchantment(ptr).empty())
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
state.mProjectileId = mPhysics->addProjectile(actor, pos);
state.mToDelete = false;
mProjectiles.push_back(state);
}
@ -360,63 +366,58 @@ namespace MWWorld
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))
{
cleanupProjectile(*it);
it = mProjectiles.erase(it);
}
else
++it;
if (isCleanable(projectileState))
cleanupProjectile(projectileState);
}
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();)
for (auto& magicBoltState : mMagicBolts)
{
if (isCleanable(*it))
{
cleanupMagicBolt(*it);
it = mMagicBolts.erase(it);
}
else
++it;
if (isCleanable(magicBoltState))
cleanupMagicBolt(magicBoltState);
}
}
}
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.
MWWorld::Ptr caster = it->getCaster();
MWWorld::Ptr caster = magicBoltState.getCaster();
if (!caster.isEmpty() && caster.getClass().isActor())
{
if (caster.getRefData().getCount() <= 0 || caster.getClass().getCreatureStats(caster).isDead())
{
cleanupMagicBolt(*it);
it = mMagicBolts.erase(it);
cleanupMagicBolt(magicBoltState);
continue;
}
}
osg::Quat orient = it->mNode->getAttitude();
osg::Quat orient = magicBoltState.mNode->getAttitude();
static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fTargetSpellMaxSpeed")->mValue.getFloat();
float speed = fTargetSpellMaxSpeed * it->mSpeed;
float speed = fTargetSpellMaxSpeed * magicBoltState.mSpeed;
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
direction.normalize();
osg::Vec3f pos(it->mNode->getPosition());
osg::Vec3f pos(magicBoltState.mNode->getPosition());
osg::Vec3f newPos = pos + direction * duration * speed;
for (size_t soundIter = 0; soundIter != it->mSounds.size(); soundIter++)
{
it->mSounds.at(soundIter)->setPosition(newPos);
}
for (const auto& sound : magicBoltState.mSounds)
sound->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.
std::vector<MWWorld::Ptr> targetActors;
@ -425,7 +426,7 @@ namespace MWWorld
// Check for impact
// 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;
if (result.mHit)
@ -433,16 +434,16 @@ namespace MWWorld
hit = true;
if (result.mHitObject.isEmpty())
{
// terrain
// terrain or projectile
}
else
{
MWMechanics::CastSpell cast(caster, result.mHitObject);
cast.mHitPosition = pos;
cast.mId = it->mSpellId;
cast.mSourceName = it->mSourceName;
cast.mId = magicBoltState.mSpellId;
cast.mSourceName = magicBoltState.mSourceName;
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));
}
}
@ -453,47 +454,46 @@ namespace MWWorld
if (hit)
{
MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, result.mHitObject,
ESM::RT_Target, it->mSpellId, it->mSourceName);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
for (size_t soundIter = 0; soundIter != it->mSounds.size(); soundIter++)
sndMgr->stopSound(it->mSounds.at(soundIter));
MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, result.mHitObject,
ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName);
mParent->removeChild(it->mNode);
it = mMagicBolts.erase(it);
continue;
cleanupMagicBolt(magicBoltState);
}
else
++it;
}
}
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
// 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 newPos = pos + it->mVelocity * duration;
osg::Vec3f pos(projectileState.mNode->getPosition());
osg::Vec3f newPos = pos + projectileState.mVelocity * duration;
// 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;
orient.makeRotate(osg::Vec3f(0,1,0), it->mVelocity);
it->mNode->setAttitude(orient);
orient.makeRotate(osg::Vec3f(0,1,0), projectileState.mVelocity);
projectileState.mNode->setAttitude(orient);
}
it->mNode->setPosition(newPos);
projectileState.mNode->setPosition(newPos);
mPhysics->updateProjectile(projectileState.mProjectileId, newPos);
update(*it, duration);
update(projectileState, duration);
MWWorld::Ptr caster = it->getCaster();
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.
std::vector<MWWorld::Ptr> targetActors;
@ -502,50 +502,149 @@ namespace MWWorld
// Check for impact
// 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);
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.
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), projectileState.mIdArrow);
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::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;
}
if (caster.isEmpty())
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));
if (underwater)
mRendering->emitWaterRipple(newPos);
mParent->removeChild(it->mNode);
it = mProjectiles.erase(it);
cleanupProjectile(projectileState);
}
}
}
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;
}
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;
}
++it;
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)
{
mParent->removeChild(state.mNode);
mPhysics->removeProjectile(state.mProjectileId);
state.mToDelete = true;
}
void ProjectileManager::cleanupMagicBolt(ProjectileManager::MagicBoltState& state)
{
mParent->removeChild(state.mNode);
mPhysics->removeProjectile(state.mProjectileId);
state.mToDelete = true;
for (size_t soundIter = 0; soundIter != state.mSounds.size(); soundIter++)
{
MWBase::Environment::get().getSoundManager()->stopSound(state.mSounds.at(soundIter));
@ -554,15 +653,12 @@ namespace MWWorld
void ProjectileManager::clear()
{
for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)
{
cleanupProjectile(*it);
}
for (auto& mProjectile : mProjectiles)
cleanupProjectile(mProjectile);
mProjectiles.clear();
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it)
{
cleanupMagicBolt(*it);
}
for (auto& mMagicBolt : mMagicBolts)
cleanupMagicBolt(mMagicBolt);
mMagicBolts.clear();
}
@ -619,6 +715,7 @@ namespace MWWorld
state.mVelocity = esm.mVelocity;
state.mIdArrow = esm.mId;
state.mAttackStrength = esm.mAttackStrength;
state.mToDelete = false;
std::string model;
try
@ -626,9 +723,10 @@ namespace MWWorld
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId);
MWWorld::Ptr ptr = ref.getPtr();
model = ptr.getClass().getModel(ptr);
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition));
}
catch(...)
{
@ -640,7 +738,7 @@ namespace MWWorld
mProjectiles.push_back(state);
return true;
}
else if (type == ESM::REC_MPRJ)
if (type == ESM::REC_MPRJ)
{
ESM::MagicBoltState esm;
esm.load(reader);
@ -649,7 +747,8 @@ namespace MWWorld
state.mIdMagic.push_back(esm.mId);
state.mSpellId = esm.mSpellId;
state.mActorId = esm.mActorId;
std::string texture = "";
state.mToDelete = false;
std::string texture;
try
{
@ -672,6 +771,7 @@ namespace MWWorld
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0));
MWWorld::Ptr ptr = ref.getPtr();
model = ptr.getClass().getModel(ptr);
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition));
}
catch(...)
{

@ -56,6 +56,8 @@ namespace MWWorld
void update(float dt);
void processHits();
/// Removes all current projectiles. Should be called when switching to a new worldspace.
void clear();
@ -76,6 +78,9 @@ namespace MWWorld
std::shared_ptr<MWRender::EffectAnimationTime> mEffectAnimationTime;
int mActorId;
int mProjectileId;
osg::Vec3f mHitPosition;
// 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.
@ -88,6 +93,8 @@ namespace MWWorld
// MW-id of an arrow projectile
std::string mIdArrow;
bool mToDelete;
};
struct MagicBoltState : public State
@ -125,6 +132,8 @@ namespace MWWorld
void moveProjectiles(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,
bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture = "");
void update (State& state, float duration);

@ -1332,25 +1332,24 @@ namespace MWWorld
return;
}
float terrainHeight = -std::numeric_limits<float>::max();
if (ptr.getCell()->isExterior())
terrainHeight = getTerrainHeightAt(pos);
if (pos.z() < terrainHeight)
pos.z() = terrainHeight;
pos.z() += 20; // place slightly above. will snap down to ground with code below
const float terrainHeight = ptr.getCell()->isExterior() ? getTerrainHeightAt(pos) : -std::numeric_limits<float>::max();
pos.z() = std::max(pos.z(), terrainHeight + 20); // place slightly above terrain. will snap down to ground with code below
// 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());
if (force || !ptr.getClass().isActor() || (!isFlying(ptr) && !swims && isActorCollisionEnabled(ptr)))
{
osg::Vec3f traced = mPhysics->traceDown(ptr, pos, Constants::CellSizeInUnits);
if (traced.z() < pos.z())
pos.z() = traced.z();
pos.z() = std::min(pos.z(), traced.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()
@ -1449,17 +1448,11 @@ namespace MWWorld
ipos.pos[1] = spawnPoint.y();
ipos.pos[2] = spawnPoint.z();
if (!referenceObject.getClass().isActor())
{
ipos.rot[0] = referenceObject.getRefData().getPosition().rot[0];
ipos.rot[1] = referenceObject.getRefData().getPosition().rot[1];
}
else
if (referenceObject.getClass().isActor())
{
ipos.rot[0] = 0;
ipos.rot[1] = 0;
}
ipos.rot[2] = referenceObject.getRefData().getPosition().rot[2];
MWWorld::Ptr placed = copyObjectToCell(ptr, referenceCell, ipos, ptr.getRefData().getCount(), false);
adjustPosition(placed, true); // snap to ground
@ -1504,6 +1497,7 @@ namespace MWWorld
mProjectileManager->update(duration);
const auto results = mPhysics->applyQueuedMovement(duration, mDiscardMovements, frameStart, frameNumber, stats);
mProjectileManager->processHits();
mDiscardMovements = false;
for(const auto& [actor, position]: results)

@ -910,130 +910,134 @@ MWShadowTechnique::ViewDependentData* MWShadowTechnique::getViewDependentData(os
return vdd.release();
}
void MWShadowTechnique::update(osg::NodeVisitor& nv)
MWShadowTechnique::ViewDependentData* MWShadowTechnique::getSharedVdd(const SharedShadowMapConfig& config)
{
OSG_INFO<<"MWShadowTechnique::update(osg::NodeVisitor& "<<&nv<<")"<<std::endl;
_shadowedScene->osg::Group::traverse(nv);
auto it = _viewDependentDataShareMap.find(config._id);
if (it != _viewDependentDataShareMap.end())
return it->second;
return nullptr;
}
void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
void MWShadowTechnique::addSharedVdd(const SharedShadowMapConfig& config, ViewDependentData* vdd)
{
if (!_enableShadows)
{
if (mSetDummyStateWhenDisabled)
{
osg::ref_ptr<osg::StateSet> dummyState = new osg::StateSet();
_viewDependentDataShareMap[config._id] = vdd;
}
ShadowSettings* settings = getShadowedScene()->getShadowSettings();
int baseUnit = settings->getBaseShadowTextureUnit();
int endUnit = baseUnit + settings->getNumShadowMapsPerLight();
for (int i = baseUnit; i < endUnit; ++i)
{
dummyState->setTextureAttributeAndModes(i, _fallbackShadowMapTexture, osg::StateAttribute::ON);
dummyState->addUniform(new osg::Uniform(("shadowTexture" + std::to_string(i - baseUnit)).c_str(), i));
dummyState->addUniform(new osg::Uniform(("shadowTextureUnit" + std::to_string(i - baseUnit)).c_str(), i));
}
void SceneUtil::MWShadowTechnique::shareShadowMap(osgUtil::CullVisitor& cv, ViewDependentData* lhs, ViewDependentData* rhs)
{
// Prepare for rendering shadows using the shadow map owned by rhs.
cv.pushStateSet(dummyState);
}
// To achieve this i first copy all data that is not specific to this cv's camera and thus read-only,
// trusting openmw and osg won't overwrite that data before this frame is done rendering.
// This works due to the double buffering of CullVisitors by osg, but also requires that cull passes are serialized (relative to one another).
// Then initialize new copies of the data that will be written with view-specific data
// (the stateset and the texgens).
_shadowedScene->osg::Group::traverse(cv);
lhs->_viewDependentShadowMap = rhs->_viewDependentShadowMap;
auto* stateset = lhs->getStateSet(cv.getTraversalNumber());
stateset->clear();
lhs->_lightDataList = rhs->_lightDataList;
lhs->_numValidShadows = rhs->_numValidShadows;
if (mSetDummyStateWhenDisabled)
cv.popStateSet();
ShadowDataList& sdl = lhs->getShadowDataList();
ShadowDataList previous_sdl;
previous_sdl.swap(sdl);
for (auto rhs_sd : rhs->getShadowDataList())
{
osg::ref_ptr<ShadowData> lhs_sd;
return;
if (previous_sdl.empty())
{
OSG_INFO << "Create new ShadowData" << std::endl;
lhs_sd = new ShadowData(lhs);
}
else
{
OSG_INFO << "Taking ShadowData from from of previous_sdl" << std::endl;
lhs_sd = previous_sdl.front();
previous_sdl.erase(previous_sdl.begin());
}
lhs_sd->_camera = rhs_sd->_camera;
lhs_sd->_textureUnit = rhs_sd->_textureUnit;
lhs_sd->_texture = rhs_sd->_texture;
sdl.push_back(lhs_sd);
}
}
OSG_INFO<<std::endl<<std::endl<<"MWShadowTechnique::cull(osg::CullVisitor&"<<&cv<<")"<<std::endl;
if (!_shadowCastingStateSet)
bool MWShadowTechnique::trySharedShadowMap(osgUtil::CullVisitor& cv, ViewDependentData* vdd)
{
auto* sharedConfig = dynamic_cast<SharedShadowMapConfig*>(cv.getCurrentCamera()->getUserData());
if (!sharedConfig)
{
OSG_INFO<<"Warning, init() has not yet been called so ShadowCastingStateSet has not been setup yet, unable to create shadows."<<std::endl;
_shadowedScene->osg::Group::traverse(cv);
return;
return false;
}
ViewDependentData* vdd = getViewDependentData(&cv);
if (!vdd)
if (sharedConfig->_master)
{
OSG_INFO<<"Warning, now ViewDependentData created, unable to create shadows."<<std::endl;
_shadowedScene->osg::Group::traverse(cv);
return;
addSharedVdd(*sharedConfig, vdd);
if(sharedConfig->_projection)
cv.pushProjectionMatrix(sharedConfig->_projection);
if(sharedConfig->_modelView)
cv.pushModelViewMatrix(sharedConfig->_modelView, sharedConfig->_referenceFrame);
return false;
}
ShadowSettings* settings = getShadowedScene()->getShadowSettings();
OSG_INFO<<"cv->getProjectionMatrix()="<<*cv.getProjectionMatrix()<<std::endl;
osg::CullSettings::ComputeNearFarMode cachedNearFarMode = cv.getComputeNearFarMode();
osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix();
// check whether this main views projection is perspective or orthographic
bool orthographicViewFrustum = viewProjectionMatrix(0,3)==0.0 &&
viewProjectionMatrix(1,3)==0.0 &&
viewProjectionMatrix(2,3)==0.0;
double minZNear = 0.0;
double maxZFar = dbl_max;
if (cachedNearFarMode==osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
else
{
double left, right, top, bottom;
if (orthographicViewFrustum)
auto* sharedVdd = getSharedVdd(*sharedConfig);
if (sharedVdd)
{
viewProjectionMatrix.getOrtho(left, right, bottom, top, minZNear, maxZFar);
OSG_INFO << "Using shared shadow map" << std::endl;
shareShadowMap(cv, vdd, sharedVdd);
return true;
}
else
{
viewProjectionMatrix.getFrustum(left, right, bottom, top, minZNear, maxZFar);
OSG_WARN << "Warning, view configured to reuse shared shadow map but no shadow map has been shared. Shadows will be generated instead." << std::endl;
}
OSG_INFO<<"minZNear="<<minZNear<<", maxZFar="<<maxZFar<<std::endl;
}
// set the compute near/far mode to the highest quality setting to ensure we push the near plan out as far as possible
if (settings->getComputeNearFarModeOverride()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
return false;
}
void SceneUtil::MWShadowTechnique::endSharedShadowMap(osgUtil::CullVisitor& cv)
{
auto* sharedConfig = dynamic_cast<SharedShadowMapConfig*>(cv.getCurrentCamera()->getUserData());
if (!sharedConfig)
{
cv.setComputeNearFarMode(settings->getComputeNearFarModeOverride());
return;
}
// 1. Traverse main scene graph
cv.pushStateSet( _shadowRecievingPlaceholderStateSet.get() );
osg::ref_ptr<osgUtil::StateGraph> decoratorStateGraph = cv.getCurrentStateGraph();
cullShadowReceivingScene(&cv);
cv.popStateSet();
if (cv.getComputeNearFarMode()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
if (sharedConfig->_master)
{
OSG_INFO<<"Just done main subgraph traversak"<<std::endl;
// make sure that the near plane is computed correctly so that any projection matrix computations
// are all done correctly.
cv.computeNearPlane();
if (sharedConfig->_projection)
cv.popProjectionMatrix();
if (sharedConfig->_modelView)
cv.popModelViewMatrix();
}
}
// clamp the minZNear and maxZFar to those provided by ShadowSettings
maxZFar = osg::minimum(settings->getMaximumShadowMapDistance(),maxZFar);
if (minZNear>maxZFar) minZNear = maxZFar*settings->getMinimumShadowMapNearFarRatio();
void SceneUtil::MWShadowTechnique::castShadows(osgUtil::CullVisitor& cv, ViewDependentData* vdd)
{
ShadowSettings* settings = getShadowedScene()->getShadowSettings();
osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix();
// check whether this main views projection is perspective or orthographic
bool orthographicViewFrustum = viewProjectionMatrix(0,3)==0.0 &&
viewProjectionMatrix(1,3)==0.0 &&
viewProjectionMatrix(2,3)==0.0;
// Compute near/far of the camera's projection matrix
double minZNear = 0.0;
double maxZFar = dbl_max;
computeProjectionNearFar(cv, orthographicViewFrustum, minZNear, maxZFar);
//OSG_NOTICE<<"maxZFar "<<maxZFar<<std::endl;
// Workaround for absurdly huge viewing distances where OSG would otherwise push the near plane out.
double cachedNearFarRatio = cv.getNearFarRatio();
cv.setNearFarRatio(minZNear / maxZFar);
Frustum frustum(&cv, minZNear, maxZFar);
if (_debugHud)
{
osg::ref_ptr<osg::Vec3Array> vertexArray = new osg::Vec3Array();
for (osg::Vec3d &vertex : frustum.corners)
vertexArray->push_back((osg::Vec3)vertex);
_debugHud->setFrustumVertices(vertexArray, cv.getTraversalNumber());
}
// Reduce near/far as much as possible
double reducedNear, reducedFar;
if (cv.getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
{
@ -1046,8 +1050,14 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
reducedFar = maxZFar;
}
// return compute near far mode back to it's original settings
cv.setComputeNearFarMode(cachedNearFarMode);
Frustum frustum(&cv, minZNear, maxZFar);
if (_debugHud)
{
osg::ref_ptr<osg::Vec3Array> vertexArray = new osg::Vec3Array();
for (osg::Vec3d& vertex : frustum.corners)
vertexArray->push_back((osg::Vec3)vertex);
_debugHud->setFrustumVertices(vertexArray, cv.getTraversalNumber());
}
OSG_INFO<<"frustum.eye="<<frustum.eye<<", frustum.centerNearPlane, "<<frustum.centerNearPlane<<" distance = "<<(frustum.eye-frustum.centerNearPlane).length()<<std::endl;
@ -1399,10 +1409,6 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
}
}
// 4.4 compute main scene graph TexGen + uniform settings + setup state
//
assignTexGenSettings(&cv, camera.get(), textureUnit, sd->_texgen.get());
// mark the light as one that has active shadows and requires shaders
pl.textureUnits.push_back(textureUnit);
@ -1426,8 +1432,139 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
_debugHud->draw(sd->_texture, sm_i, camera->getViewMatrix() * camera->getProjectionMatrix(), cv);
}
}
vdd->setNumValidShadows(numValidShadows);
cv.setNearFarRatio(cachedNearFarRatio);
}
void SceneUtil::MWShadowTechnique::assignTexGenSettings(osgUtil::CullVisitor& cv, ViewDependentData* vdd)
{
for (auto& sd : vdd->getShadowDataList())
{
assignTexGenSettings(&cv, sd->_camera, sd->_textureUnit, sd->_texgen);
}
}
void SceneUtil::MWShadowTechnique::computeProjectionNearFar(osgUtil::CullVisitor& cv, bool orthographicViewFrustum, double& znear, double& zfar)
{
ShadowSettings* settings = getShadowedScene()->getShadowSettings();
osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix();
znear = 0.0;
zfar = dbl_max;
if (cv.getComputeNearFarMode() == osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
{
double left, right, top, bottom;
if (orthographicViewFrustum)
{
viewProjectionMatrix.getOrtho(left, right, bottom, top, znear, zfar);
}
else
{
viewProjectionMatrix.getFrustum(left, right, bottom, top, znear, zfar);
}
OSG_INFO << "minZNear=" << znear << ", maxZFar=" << zfar << std::endl;
}
// clamp the minZNear and maxZFar to those provided by ShadowSettings
zfar = osg::minimum(settings->getMaximumShadowMapDistance(), zfar);
if (znear > zfar) znear = zfar * settings->getMinimumShadowMapNearFarRatio();
}
void MWShadowTechnique::update(osg::NodeVisitor& nv)
{
OSG_INFO<<"MWShadowTechnique::update(osg::NodeVisitor& "<<&nv<<")"<<std::endl;
_shadowedScene->osg::Group::traverse(nv);
}
void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
{
if (!_enableShadows)
{
if (mSetDummyStateWhenDisabled)
{
osg::ref_ptr<osg::StateSet> dummyState = new osg::StateSet();
ShadowSettings* settings = getShadowedScene()->getShadowSettings();
int baseUnit = settings->getBaseShadowTextureUnit();
int endUnit = baseUnit + settings->getNumShadowMapsPerLight();
for (int i = baseUnit; i < endUnit; ++i)
{
dummyState->setTextureAttributeAndModes(i, _fallbackShadowMapTexture, osg::StateAttribute::ON);
dummyState->addUniform(new osg::Uniform(("shadowTexture" + std::to_string(i - baseUnit)).c_str(), i));
dummyState->addUniform(new osg::Uniform(("shadowTextureUnit" + std::to_string(i - baseUnit)).c_str(), i));
}
cv.pushStateSet(dummyState);
}
_shadowedScene->osg::Group::traverse(cv);
if (mSetDummyStateWhenDisabled)
cv.popStateSet();
return;
}
OSG_INFO<<std::endl<<std::endl<<"MWShadowTechnique::cull(osg::CullVisitor&"<<&cv<<")"<<std::endl;
if (!_shadowCastingStateSet)
{
OSG_INFO<<"Warning, init() has not yet been called so ShadowCastingStateSet has not been setup yet, unable to create shadows."<<std::endl;
_shadowedScene->osg::Group::traverse(cv);
return;
}
ViewDependentData* vdd = getViewDependentData(&cv);
if (!vdd)
{
OSG_INFO<<"Warning, now ViewDependentData created, unable to create shadows."<<std::endl;
_shadowedScene->osg::Group::traverse(cv);
return;
}
ShadowSettings* settings = getShadowedScene()->getShadowSettings();
osg::CullSettings::ComputeNearFarMode cachedNearFarMode = cv.getComputeNearFarMode();
OSG_INFO<<"cv->getProjectionMatrix()="<<*cv.getProjectionMatrix()<<std::endl;
// set the compute near/far mode to the highest quality setting to ensure we push the near plan out as far as possible
if (settings->getComputeNearFarModeOverride()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
{
cv.setComputeNearFarMode(settings->getComputeNearFarModeOverride());
}
if (numValidShadows>0)
// 1. Traverse main scene graph
osg::ref_ptr<osgUtil::StateGraph> decoratorStateGraph = cullShadowReceivingScene(&cv);
if (cv.getComputeNearFarMode()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
{
OSG_INFO<<"Just done main subgraph traversak"<<std::endl;
// make sure that the near plane is computed correctly so that any projection matrix computations
// are all done correctly.
cv.computeNearPlane();
}
// return compute near far mode back to it's original settings
cv.setComputeNearFarMode(cachedNearFarMode);
bool doCastShadow = !trySharedShadowMap(cv, vdd);
if (doCastShadow)
{
castShadows(cv, vdd);
}
endSharedShadowMap(cv);
// 4.4 compute main scene graph TexGen + uniform settings + setup state
//
assignTexGenSettings(cv, vdd);
if (vdd->_numValidShadows>0)
{
decoratorStateGraph->setStateSet(selectStateSetForRenderingShadow(*vdd, cv.getTraversalNumber()));
}
@ -2968,27 +3105,31 @@ bool MWShadowTechnique::assignTexGenSettings(osgUtil::CullVisitor* cv, osg::Came
// Place texgen with modelview which removes big offsets (making it float friendly)
osg::ref_ptr<osg::RefMatrix> refMatrix =
new osg::RefMatrix( camera->getInverseViewMatrix() * (*(cv->getModelViewMatrix())) );
new osg::RefMatrix( camera->getInverseViewMatrix() * (*cv->getModelViewMatrix()) );
osgUtil::RenderStage* currentStage = cv->getCurrentRenderBin()->getStage();
currentStage->getPositionalStateContainer()->addPositionedTextureAttribute( textureUnit, refMatrix.get(), texgen );
return true;
}
void MWShadowTechnique::cullShadowReceivingScene(osgUtil::CullVisitor* cv) const
osg::ref_ptr<osgUtil::StateGraph> MWShadowTechnique::cullShadowReceivingScene(osgUtil::CullVisitor* cv) const
{
OSG_INFO<<"cullShadowReceivingScene()"<<std::endl;
// record the traversal mask on entry so we can reapply it later.
unsigned int traversalMask = cv->getTraversalMask();
cv->pushStateSet(_shadowRecievingPlaceholderStateSet.get());
osg::ref_ptr<osgUtil::StateGraph> decoratorStateGraph = cv->getCurrentStateGraph();
cv->setTraversalMask( traversalMask & _shadowedScene->getShadowSettings()->getReceivesShadowTraversalMask() );
_shadowedScene->osg::Group::traverse(*cv);
cv->setTraversalMask( traversalMask );
cv->popStateSet();
return;
return decoratorStateGraph;
}
void MWShadowTechnique::cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Camera* camera) const

@ -142,6 +142,28 @@ namespace SceneUtil {
// forward declare
class ViewDependentData;
/// Configuration of shadow maps shared by multiple views
struct SharedShadowMapConfig : public osg::Referenced
{
virtual ~SharedShadowMapConfig() {}
/// String identifier of the shared shadow map
std::string _id{ "" };
/// If true, this camera will generate the shadow map
bool _master{ false };
/// If set, will override projection matrix of camera when generating shadow map.
osg::ref_ptr<osg::RefMatrix> _projection{ nullptr };
/// If set, will override model view matrix of camera when generating shadow map.
osg::ref_ptr<osg::RefMatrix> _modelView{ nullptr };
/// Reference frame of the view matrix
osg::Transform::ReferenceFrame
_referenceFrame{ osg::Transform::ABSOLUTE_RF };
};
struct LightData : public osg::Referenced
{
LightData(ViewDependentData* vdd);
@ -196,7 +218,12 @@ namespace SceneUtil {
virtual void releaseGLObjects(osg::State* = 0) const;
unsigned int numValidShadows(void) const { return _numValidShadows; }
void setNumValidShadows(unsigned int numValidShadows) { _numValidShadows = numValidShadows; }
protected:
friend class MWShadowTechnique;
virtual ~ViewDependentData() {}
MWShadowTechnique* _viewDependentShadowMap;
@ -205,13 +232,29 @@ namespace SceneUtil {
LightDataList _lightDataList;
ShadowDataList _shadowDataList;
unsigned int _numValidShadows;
};
virtual ViewDependentData* createViewDependentData(osgUtil::CullVisitor* cv);
ViewDependentData* getViewDependentData(osgUtil::CullVisitor* cv);
ViewDependentData* getSharedVdd(const SharedShadowMapConfig& config);
void addSharedVdd(const SharedShadowMapConfig& config, ViewDependentData* vdd);
void shareShadowMap(osgUtil::CullVisitor& cv, ViewDependentData* lhs, ViewDependentData* rhs);
bool trySharedShadowMap(osgUtil::CullVisitor& cv, ViewDependentData* vdd);
void endSharedShadowMap(osgUtil::CullVisitor& cv);
void castShadows(osgUtil::CullVisitor& cv, ViewDependentData* vdd);
void assignTexGenSettings(osgUtil::CullVisitor& cv, ViewDependentData* vdd);
void computeProjectionNearFar(osgUtil::CullVisitor& cv, bool orthographicViewFrustum, double& znear, double& zfar);
virtual void createShaders();
@ -227,7 +270,7 @@ namespace SceneUtil {
virtual bool assignTexGenSettings(osgUtil::CullVisitor* cv, osg::Camera* camera, unsigned int textureUnit, osg::TexGen* texgen);
virtual void cullShadowReceivingScene(osgUtil::CullVisitor* cv) const;
virtual osg::ref_ptr<osgUtil::StateGraph> cullShadowReceivingScene(osgUtil::CullVisitor* cv) const;
virtual void cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Camera* camera) const;
@ -238,7 +281,9 @@ namespace SceneUtil {
typedef std::map< osgUtil::CullVisitor*, osg::ref_ptr<ViewDependentData> > ViewDependentDataMap;
mutable std::mutex _viewDependentDataMapMutex;
typedef std::map< std::string, osg::ref_ptr<ViewDependentData> > ViewDependentDataShareMap;
ViewDependentDataMap _viewDependentDataMap;
ViewDependentDataShareMap _viewDependentDataShareMap;
osg::ref_ptr<osg::StateSet> _shadowRecievingPlaceholderStateSet;

@ -361,6 +361,9 @@ trainers training skills based on base skill = false
# Make stealing items from NPCs that were knocked down possible during combat.
always allow stealing from knocked out actors = false
# Enables visually harvesting plants for models that support it.
graphic herbalism = true
[General]
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).

Loading…
Cancel
Save