mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-06 00:19:41 +00:00
Merge remote-tracking branch 'remotes/origin/shared_shadow_maps' into multiview_test_branch
This commit is contained in:
commit
5beb0bc799
28 changed files with 894 additions and 293 deletions
|
@ -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,27 +46,29 @@ 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 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
|
||||
bool isCasting = MWBase::Environment::get().getMechanicsManager()->isCastingSpell(actor);
|
||||
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->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;
|
||||
|
||||
|
|
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.
|
||||
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::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, result.mHitObject,
|
||||
ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName);
|
||||
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
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;
|
||||
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);
|
||||
|
||||
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.
|
||||
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;
|
||||
}
|
||||
|
||||
++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)
|
||||
{
|
||||
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,65 +910,116 @@ 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)
|
||||
_viewDependentDataShareMap[config._id] = vdd;
|
||||
}
|
||||
|
||||
void SceneUtil::MWShadowTechnique::shareShadowMap(osgUtil::CullVisitor& cv, ViewDependentData* lhs, ViewDependentData* rhs)
|
||||
{
|
||||
// Prepare for rendering shadows using the shadow map owned by rhs.
|
||||
|
||||
// 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).
|
||||
|
||||
lhs->_viewDependentShadowMap = rhs->_viewDependentShadowMap;
|
||||
auto* stateset = lhs->getStateSet(cv.getTraversalNumber());
|
||||
stateset->clear();
|
||||
lhs->_lightDataList = rhs->_lightDataList;
|
||||
lhs->_numValidShadows = rhs->_numValidShadows;
|
||||
|
||||
ShadowDataList& sdl = lhs->getShadowDataList();
|
||||
ShadowDataList previous_sdl;
|
||||
previous_sdl.swap(sdl);
|
||||
for (auto rhs_sd : rhs->getShadowDataList())
|
||||
{
|
||||
if (mSetDummyStateWhenDisabled)
|
||||
osg::ref_ptr<ShadowData> lhs_sd;
|
||||
|
||||
if (previous_sdl.empty())
|
||||
{
|
||||
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);
|
||||
OSG_INFO << "Create new ShadowData" << std::endl;
|
||||
lhs_sd = new ShadowData(lhs);
|
||||
}
|
||||
|
||||
_shadowedScene->osg::Group::traverse(cv);
|
||||
|
||||
if (mSetDummyStateWhenDisabled)
|
||||
cv.popStateSet();
|
||||
|
||||
return;
|
||||
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)
|
||||
{
|
||||
addSharedVdd(*sharedConfig, vdd);
|
||||
if(sharedConfig->_projection)
|
||||
cv.pushProjectionMatrix(sharedConfig->_projection);
|
||||
if(sharedConfig->_modelView)
|
||||
cv.pushModelViewMatrix(sharedConfig->_modelView, sharedConfig->_referenceFrame);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* sharedVdd = getSharedVdd(*sharedConfig);
|
||||
if (sharedVdd)
|
||||
{
|
||||
OSG_INFO << "Using shared shadow map" << std::endl;
|
||||
shareShadowMap(cv, vdd, sharedVdd);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
OSG_WARN << "Warning, view configured to reuse shared shadow map but no shadow map has been shared. Shadows will be generated instead." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SceneUtil::MWShadowTechnique::endSharedShadowMap(osgUtil::CullVisitor& cv)
|
||||
{
|
||||
auto* sharedConfig = dynamic_cast<SharedShadowMapConfig*>(cv.getCurrentCamera()->getUserData());
|
||||
if (!sharedConfig)
|
||||
{
|
||||
OSG_INFO<<"Warning, now ViewDependentData created, unable to create shadows."<<std::endl;
|
||||
_shadowedScene->osg::Group::traverse(cv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sharedConfig->_master)
|
||||
{
|
||||
if (sharedConfig->_projection)
|
||||
cv.popProjectionMatrix();
|
||||
if (sharedConfig->_modelView)
|
||||
cv.popModelViewMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneUtil::MWShadowTechnique::castShadows(osgUtil::CullVisitor& cv, ViewDependentData* vdd)
|
||||
{
|
||||
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
|
||||
|
@ -976,64 +1027,17 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||
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;
|
||||
|
||||
if (cachedNearFarMode==osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
|
||||
{
|
||||
double left, right, top, bottom;
|
||||
if (orthographicViewFrustum)
|
||||
{
|
||||
viewProjectionMatrix.getOrtho(left, right, bottom, top, minZNear, maxZFar);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewProjectionMatrix.getFrustum(left, right, bottom, top, minZNear, maxZFar);
|
||||
}
|
||||
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)
|
||||
{
|
||||
cv.setComputeNearFarMode(settings->getComputeNearFarModeOverride());
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
// clamp the minZNear and maxZFar to those provided by ShadowSettings
|
||||
maxZFar = osg::minimum(settings->getMaximumShadowMapDistance(),maxZFar);
|
||||
if (minZNear>maxZFar) minZNear = maxZFar*settings->getMinimumShadowMapNearFarRatio();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (numValidShadows>0)
|
||||
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());
|
||||
}
|
||||
|
||||
// 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…
Reference in a new issue