mirror of
https://github.com/OpenMW/openmw.git
synced 2025-04-01 09:06:40 +00:00
Merge remote-tracking branch 'upstream/master' into alpha-meddling
This commit is contained in:
commit
5e004356a2
50 changed files with 1105 additions and 398 deletions
|
@ -39,6 +39,7 @@
|
|||
Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work
|
||||
Bug #5416: Junk non-node records before the root node are not handled gracefully
|
||||
Bug #5422: The player loses all spells when resurrected
|
||||
Bug #5423: Guar follows actors too closely
|
||||
Bug #5424: Creatures do not headtrack player
|
||||
Bug #5425: Poison effect only appears for one frame
|
||||
Bug #5427: GetDistance unknown ID error is misleading
|
||||
|
@ -72,7 +73,9 @@
|
|||
Bug #5688: Water shader broken indoors with enable indoor shadows = false
|
||||
Bug #5695: ExplodeSpell for actors doesn't target the ground
|
||||
Bug #5703: OpenMW-CS menu system crashing on XFCE
|
||||
Bug #5706: AI sequences stop looping after the saved game is reloaded
|
||||
Bug #5731: Editor: skirts are invisible on characters
|
||||
Bug #5758: Paralyzed actors behavior is inconsistent with vanilla
|
||||
Feature #390: 3rd person look "over the shoulder"
|
||||
Feature #1536: Show more information about level on menu
|
||||
Feature #2386: Distant Statics in the form of Object Paging
|
||||
|
|
|
@ -585,6 +585,12 @@ if (WIN32)
|
|||
)
|
||||
endif()
|
||||
|
||||
if( "${MyGUI_VERSION}" VERSION_LESS_EQUAL "3.4.0" )
|
||||
set(WARNINGS_DISABLE ${WARNINGS_DISABLE}
|
||||
4866 # compiler may not enforce left-to-right evaluation order for call
|
||||
)
|
||||
endif()
|
||||
|
||||
foreach(d ${WARNINGS_DISABLE})
|
||||
set(WARNINGS "${WARNINGS} /wd${d}")
|
||||
endforeach(d)
|
||||
|
|
|
@ -199,6 +199,7 @@ namespace MWBase
|
|||
virtual std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor) = 0;
|
||||
virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) = 0;
|
||||
virtual std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor) = 0;
|
||||
virtual std::map<int, MWWorld::Ptr> getActorsFollowingByIndex(const MWWorld::Ptr& actor) = 0;
|
||||
|
||||
///Returns a list of actors who are fighting the given actor within the fAlarmDistance
|
||||
/** ie AiCombat is active and the target is the actor **/
|
||||
|
|
|
@ -286,6 +286,9 @@ namespace MWBase
|
|||
virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0;
|
||||
///< @return an updated Ptr
|
||||
|
||||
virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec) = 0;
|
||||
///< @return an updated Ptr
|
||||
|
||||
virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0;
|
||||
|
||||
virtual void rotateObject(const MWWorld::Ptr& ptr, float x, float y, float z,
|
||||
|
@ -516,7 +519,7 @@ namespace MWBase
|
|||
/// Returns true if levitation spell effect is allowed.
|
||||
virtual bool isLevitationEnabled() const = 0;
|
||||
|
||||
virtual bool getGodModeState() = 0;
|
||||
virtual bool getGodModeState() const = 0;
|
||||
|
||||
virtual bool toggleGodMode() = 0;
|
||||
|
||||
|
|
|
@ -451,6 +451,7 @@ namespace MWGui
|
|||
setTitle(mPtr.getClass().getName(mPtr));
|
||||
|
||||
updateTopics();
|
||||
updateTopicsPane(); // force update for new services
|
||||
|
||||
updateDisposition();
|
||||
restock();
|
||||
|
@ -487,12 +488,14 @@ namespace MWGui
|
|||
mHistoryContents.clear();
|
||||
}
|
||||
|
||||
void DialogueWindow::setKeywords(std::list<std::string> keyWords)
|
||||
bool DialogueWindow::setKeywords(std::list<std::string> keyWords)
|
||||
{
|
||||
if (mKeywords == keyWords && isCompanion() == mIsCompanion)
|
||||
return;
|
||||
return false;
|
||||
mIsCompanion = isCompanion();
|
||||
mKeywords = keyWords;
|
||||
updateTopicsPane();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DialogueWindow::updateTopicsPane()
|
||||
|
@ -556,6 +559,8 @@ namespace MWGui
|
|||
mTopicsList->adjustSize();
|
||||
|
||||
updateHistory();
|
||||
// The topics list has been regenerated so topic formatting needs to be updated
|
||||
updateTopicFormat();
|
||||
}
|
||||
|
||||
void DialogueWindow::updateHistory(bool scrollbar)
|
||||
|
@ -758,9 +763,9 @@ namespace MWGui
|
|||
|
||||
void DialogueWindow::updateTopics()
|
||||
{
|
||||
setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics());
|
||||
updateTopicsPane();
|
||||
updateTopicFormat();
|
||||
// Topic formatting needs to be updated regardless of whether the topic list has changed
|
||||
if (!setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics()))
|
||||
updateTopicFormat();
|
||||
}
|
||||
|
||||
bool DialogueWindow::isCompanion()
|
||||
|
|
|
@ -122,7 +122,8 @@ namespace MWGui
|
|||
|
||||
void setPtr(const MWWorld::Ptr& actor) override;
|
||||
|
||||
void setKeywords(std::list<std::string> keyWord);
|
||||
/// @return true if stale keywords were updated successfully
|
||||
bool setKeywords(std::list<std::string> keyWord);
|
||||
|
||||
void addResponse (const std::string& title, const std::string& text, bool needMargin = true);
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ void KeyboardNavigation::saveFocus(int mode)
|
|||
{
|
||||
mKeyFocus[mode] = focus;
|
||||
}
|
||||
else
|
||||
else if(shouldAcceptKeyFocus(mCurrentFocus))
|
||||
{
|
||||
mKeyFocus[mode] = mCurrentFocus;
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ void KeyboardNavigation::_unlinkWidget(MyGUI::Widget *widget)
|
|||
mCurrentFocus = nullptr;
|
||||
}
|
||||
|
||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||
void styleFocusedButton(MyGUI::Widget* w)
|
||||
{
|
||||
if (w)
|
||||
|
@ -103,6 +104,7 @@ void styleFocusedButton(MyGUI::Widget* w)
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool isRootParent(MyGUI::Widget* widget, MyGUI::Widget* root)
|
||||
{
|
||||
|
@ -126,7 +128,9 @@ void KeyboardNavigation::onFrame()
|
|||
|
||||
if (focus == mCurrentFocus)
|
||||
{
|
||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||
styleFocusedButton(mCurrentFocus);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -137,19 +141,21 @@ void KeyboardNavigation::onFrame()
|
|||
focus = mCurrentFocus;
|
||||
}
|
||||
|
||||
// style highlighted button (won't be needed for MyGUI 3.2.3)
|
||||
if (focus != mCurrentFocus)
|
||||
{
|
||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||
if (mCurrentFocus)
|
||||
{
|
||||
if (MyGUI::Button* b = mCurrentFocus->castType<MyGUI::Button>(false))
|
||||
b->_setWidgetState("normal");
|
||||
}
|
||||
|
||||
#endif
|
||||
mCurrentFocus = focus;
|
||||
}
|
||||
|
||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||
styleFocusedButton(mCurrentFocus);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KeyboardNavigation::setDefaultFocus(MyGUI::Widget *window, MyGUI::Widget *defaultFocus)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#include "loadingscreen.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Version>
|
||||
|
||||
#include <MyGUI_RenderManager.h>
|
||||
#include <MyGUI_ScrollBar.h>
|
||||
|
@ -43,6 +45,8 @@ namespace MWGui
|
|||
, mNestedLoadingCount(0)
|
||||
, mProgress(0)
|
||||
, mShowWallpaper(true)
|
||||
, mOldCallback(nullptr)
|
||||
, mHasCallback(false)
|
||||
{
|
||||
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
|
||||
|
||||
|
@ -136,18 +140,53 @@ namespace MWGui
|
|||
{
|
||||
public:
|
||||
CopyFramebufferToTextureCallback(osg::Texture2D* texture)
|
||||
: mTexture(texture)
|
||||
: mOneshot(true)
|
||||
, mTexture(texture)
|
||||
{
|
||||
}
|
||||
|
||||
void operator () (osg::RenderInfo& renderInfo) const override
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
mOneshot = false;
|
||||
}
|
||||
mSignal.notify_all();
|
||||
|
||||
int w = renderInfo.getCurrentCamera()->getViewport()->width();
|
||||
int h = renderInfo.getCurrentCamera()->getViewport()->height();
|
||||
mTexture->copyTexImage2D(*renderInfo.getState(), 0, 0, w, h);
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
mOneshot = false;
|
||||
}
|
||||
mSignal.notify_all();
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
while (mOneshot)
|
||||
mSignal.wait(lock);
|
||||
}
|
||||
|
||||
void waitUntilInvoked()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
while (mOneshot)
|
||||
mSignal.wait(lock);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
mOneshot = true;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable bool mOneshot;
|
||||
mutable std::mutex mMutex;
|
||||
mutable std::condition_variable mSignal;
|
||||
osg::ref_ptr<osg::Texture2D> mTexture;
|
||||
};
|
||||
|
||||
|
@ -322,7 +361,15 @@ namespace MWGui
|
|||
mCopyFramebufferToTextureCallback = new CopyFramebufferToTextureCallback(mTexture);
|
||||
}
|
||||
|
||||
#if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 10)
|
||||
mViewer->getCamera()->addInitialDrawCallback(mCopyFramebufferToTextureCallback);
|
||||
#else
|
||||
// TODO: Remove once we officially end support for OSG versions pre 3.5.10
|
||||
mOldCallback = mViewer->getCamera()->getInitialDrawCallback();
|
||||
mViewer->getCamera()->setInitialDrawCallback(mCopyFramebufferToTextureCallback);
|
||||
#endif
|
||||
mCopyFramebufferToTextureCallback->reset();
|
||||
mHasCallback = true;
|
||||
|
||||
mBackgroundImage->setBackgroundImage("");
|
||||
mBackgroundImage->setVisible(false);
|
||||
|
@ -365,10 +412,19 @@ namespace MWGui
|
|||
mViewer->renderingTraversals();
|
||||
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
|
||||
|
||||
if (mCopyFramebufferToTextureCallback)
|
||||
if (mHasCallback)
|
||||
{
|
||||
mCopyFramebufferToTextureCallback->waitUntilInvoked();
|
||||
|
||||
// Note that we are removing the callback before the draw thread has returned from it.
|
||||
// This is OK as we are retaining the ref_ptr.
|
||||
#if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 10)
|
||||
mViewer->getCamera()->removeInitialDrawCallback(mCopyFramebufferToTextureCallback);
|
||||
mCopyFramebufferToTextureCallback = nullptr;
|
||||
#else
|
||||
// TODO: Remove once we officially end support for OSG versions pre 3.5.10
|
||||
mViewer->getCamera()->setInitialDrawCallback(mOldCallback);
|
||||
#endif
|
||||
mHasCallback = false;
|
||||
}
|
||||
|
||||
mLastRenderTime = mTimer.time_m();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include <osg/Camera>
|
||||
#include <osg/Timer>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
|
@ -86,6 +87,8 @@ namespace MWGui
|
|||
|
||||
osg::ref_ptr<osg::Texture2D> mTexture;
|
||||
osg::ref_ptr<CopyFramebufferToTextureCallback> mCopyFramebufferToTextureCallback;
|
||||
osg::ref_ptr<osg::Camera::DrawCallback> mOldCallback;
|
||||
bool mHasCallback;
|
||||
std::unique_ptr<MyGUI::ITexture> mGuiTexture;
|
||||
|
||||
void changeWallpaper();
|
||||
|
|
|
@ -142,6 +142,29 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
|
|||
magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void forEachFollowingPackage(MWMechanics::Actors::PtrActorMap& actors, const MWWorld::Ptr& actor, const MWWorld::Ptr& player, T&& func)
|
||||
{
|
||||
for(auto& iter : actors)
|
||||
{
|
||||
const MWWorld::Ptr &iteratedActor = iter.first;
|
||||
if (iteratedActor == player || iteratedActor == actor)
|
||||
continue;
|
||||
|
||||
const MWMechanics::CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
|
||||
if (stats.isDead())
|
||||
continue;
|
||||
|
||||
// An actor counts as following if AiFollow is the current AiPackage,
|
||||
// or there are only Combat and Wander packages before the AiFollow package
|
||||
for (const auto& package : stats.getAiSequence())
|
||||
{
|
||||
if(!func(iter, package))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
|
@ -2512,26 +2535,14 @@ namespace MWMechanics
|
|||
std::list<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actor)
|
||||
{
|
||||
std::list<MWWorld::Ptr> list;
|
||||
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
|
||||
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)
|
||||
{
|
||||
const MWWorld::Ptr &iteratedActor = iter->first;
|
||||
if (iteratedActor == getPlayer() || iteratedActor == actor)
|
||||
continue;
|
||||
|
||||
const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
|
||||
if (stats.isDead())
|
||||
continue;
|
||||
|
||||
// An actor counts as following if AiFollow is the current AiPackage,
|
||||
// or there are only Combat and Wander packages before the AiFollow package
|
||||
for (const auto& package : stats.getAiSequence())
|
||||
{
|
||||
if (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||
list.push_back(iteratedActor);
|
||||
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||
list.push_back(iter.first);
|
||||
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -2575,32 +2586,38 @@ namespace MWMechanics
|
|||
std::list<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor)
|
||||
{
|
||||
std::list<int> list;
|
||||
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
|
||||
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)
|
||||
{
|
||||
const MWWorld::Ptr &iteratedActor = iter->first;
|
||||
if (iteratedActor == getPlayer() || iteratedActor == actor)
|
||||
continue;
|
||||
|
||||
const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
|
||||
if (stats.isDead())
|
||||
continue;
|
||||
|
||||
// An actor counts as following if AiFollow is the current AiPackage,
|
||||
// or there are only Combat and Wander packages before the AiFollow package
|
||||
for (const auto& package : stats.getAiSequence())
|
||||
if (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||
{
|
||||
if (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||
{
|
||||
list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());
|
||||
break;
|
||||
}
|
||||
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
|
||||
break;
|
||||
list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
std::map<int, MWWorld::Ptr> Actors::getActorsFollowingByIndex(const MWWorld::Ptr &actor)
|
||||
{
|
||||
std::map<int, MWWorld::Ptr> map;
|
||||
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)
|
||||
{
|
||||
if (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||
{
|
||||
int index = static_cast<const AiFollow*>(package.get())->getFollowIndex();
|
||||
map[index] = iter.first;
|
||||
return false;
|
||||
}
|
||||
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
std::list<MWWorld::Ptr> Actors::getActorsFighting(const MWWorld::Ptr& actor) {
|
||||
std::list<MWWorld::Ptr> list;
|
||||
std::vector<MWWorld::Ptr> neighbors;
|
||||
|
|
|
@ -181,6 +181,7 @@ namespace MWMechanics
|
|||
|
||||
/// Get the list of AiFollow::mFollowIndex for all actors following this target
|
||||
std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor);
|
||||
std::map<int, MWWorld::Ptr> getActorsFollowingByIndex(const MWWorld::Ptr& actor);
|
||||
|
||||
///Returns the list of actors which are fighting the given actor
|
||||
/**ie AiCombat is active and the target is the actor **/
|
||||
|
|
|
@ -113,24 +113,23 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
|||
if (!mActive)
|
||||
return false;
|
||||
|
||||
// The distances below are approximations based on observations of the original engine.
|
||||
// If only one actor is following the target, it uses 186.
|
||||
// If there are multiple actors following the same target, they form a group with each group member at 313 + (130 * i) distance to the target.
|
||||
|
||||
short followDistance = 186;
|
||||
std::list<int> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowingIndices(target);
|
||||
if (followers.size() >= 2)
|
||||
// In the original engine the first follower stays closer to the player than any subsequent followers.
|
||||
// Followers beyond the first usually attempt to stand inside each other.
|
||||
osg::Vec3f::value_type floatingDistance = 0;
|
||||
auto followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowingByIndex(target);
|
||||
if (followers.size() >= 2 && followers.cbegin()->first != mFollowIndex)
|
||||
{
|
||||
followDistance = 313;
|
||||
short i = 0;
|
||||
followers.sort();
|
||||
for (int followIndex : followers)
|
||||
osg::Vec3f::value_type maxSize = 0;
|
||||
for(auto& follower : followers)
|
||||
{
|
||||
if (followIndex == mFollowIndex)
|
||||
followDistance += 130 * i;
|
||||
++i;
|
||||
auto halfExtent = MWBase::Environment::get().getWorld()->getHalfExtents(follower.second).y();
|
||||
if(halfExtent > floatingDistance)
|
||||
floatingDistance = halfExtent;
|
||||
}
|
||||
}
|
||||
floatingDistance += MWBase::Environment::get().getWorld()->getHalfExtents(target).y();
|
||||
floatingDistance += MWBase::Environment::get().getWorld()->getHalfExtents(actor).y() * 2;
|
||||
short followDistance = static_cast<short>(floatingDistance);
|
||||
|
||||
if (!mAlwaysFollow) //Update if you only follow for a bit
|
||||
{
|
||||
|
|
|
@ -158,7 +158,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
|||
zTurn(actor, getZAngleToPoint(position, dest));
|
||||
smoothTurn(actor, getXAngleToPoint(position, dest), 0);
|
||||
world->removeActorPath(actor);
|
||||
return true;
|
||||
return isDestReached;
|
||||
}
|
||||
|
||||
world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);
|
||||
|
|
|
@ -403,7 +403,7 @@ const AiPackage& MWMechanics::AiSequence::getActivePackage()
|
|||
void AiSequence::fill(const ESM::AIPackageList &list)
|
||||
{
|
||||
// If there is more than one package in the list, enable repeating
|
||||
if (!list.mList.empty() && list.mList.begin() != (list.mList.end()-1))
|
||||
if (list.mList.size() >= 2)
|
||||
mRepeat = true;
|
||||
|
||||
for (const auto& esmPackage : list.mList)
|
||||
|
@ -459,8 +459,15 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
|||
int count = 0;
|
||||
for (auto& container : sequence.mPackages)
|
||||
{
|
||||
if (isActualAiPackage(static_cast<AiPackageTypeId>(container.mType)))
|
||||
count++;
|
||||
switch (container.mType)
|
||||
{
|
||||
case ESM::AiSequence::Ai_Wander:
|
||||
case ESM::AiSequence::Ai_Travel:
|
||||
case ESM::AiSequence::Ai_Escort:
|
||||
case ESM::AiSequence::Ai_Follow:
|
||||
case ESM::AiSequence::Ai_Activate:
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 1)
|
||||
|
|
|
@ -1965,7 +1965,7 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
movementSettings.mSpeedFactor *= 2.f;
|
||||
|
||||
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
|
||||
if (smoothMovement && !isFirstPersonPlayer)
|
||||
if (smoothMovement)
|
||||
{
|
||||
static const float playerTurningCoef = 1.0 / std::max(0.01f, Settings::Manager::getFloat("smooth movement player turning delay", "Game"));
|
||||
float angle = mPtr.getRefData().getPosition().rot[2];
|
||||
|
@ -1975,7 +1975,9 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
float deltaLen = delta.length();
|
||||
|
||||
float maxDelta;
|
||||
if (std::abs(speedDelta) < deltaLen / 2)
|
||||
if (isFirstPersonPlayer)
|
||||
maxDelta = 1;
|
||||
else if (std::abs(speedDelta) < deltaLen / 2)
|
||||
// Turning is smooth for player and less smooth for NPCs (otherwise NPC can miss a path point).
|
||||
maxDelta = duration * (isPlayer ? playerTurningCoef : 6.f);
|
||||
else if (isPlayer && speedDelta < -deltaLen / 2)
|
||||
|
@ -2013,7 +2015,10 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
bool canMove = cls.getMaxSpeed(mPtr) > 0;
|
||||
static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game");
|
||||
if (!turnToMovementDirection || isFirstPersonPlayer)
|
||||
{
|
||||
movementSettings.mIsStrafing = std::abs(vec.x()) > std::abs(vec.y()) * 2;
|
||||
stats.setSideMovementAngle(0);
|
||||
}
|
||||
else if (canMove)
|
||||
{
|
||||
float targetMovementAngle = vec.y() >= 0 ? std::atan2(-vec.x(), vec.y()) : std::atan2(vec.x(), -vec.y());
|
||||
|
@ -2265,18 +2270,19 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal);
|
||||
}
|
||||
|
||||
if (turnToMovementDirection)
|
||||
if (turnToMovementDirection && !isFirstPersonPlayer &&
|
||||
(movestate == CharState_SwimRunForward || movestate == CharState_SwimWalkForward ||
|
||||
movestate == CharState_SwimRunBack || movestate == CharState_SwimWalkBack))
|
||||
{
|
||||
float targetSwimmingPitch;
|
||||
if (inwater && vec.y() != 0 && !isFirstPersonPlayer && !movementSettings.mIsStrafing)
|
||||
targetSwimmingPitch = -mPtr.getRefData().getPosition().rot[0];
|
||||
else
|
||||
targetSwimmingPitch = 0;
|
||||
float maxSwimPitchDelta = 3.0f * duration;
|
||||
float swimmingPitch = mAnimation->getBodyPitchRadians();
|
||||
float targetSwimmingPitch = -mPtr.getRefData().getPosition().rot[0];
|
||||
float maxSwimPitchDelta = 3.0f * duration;
|
||||
swimmingPitch += osg::clampBetween(targetSwimmingPitch - swimmingPitch, -maxSwimPitchDelta, maxSwimPitchDelta);
|
||||
mAnimation->setBodyPitchRadians(swimmingPitch);
|
||||
}
|
||||
else
|
||||
mAnimation->setBodyPitchRadians(0);
|
||||
|
||||
static const bool swimUpwardCorrection = Settings::Manager::getBool("swim upward correction", "Game");
|
||||
if (inwater && isPlayer && !isFirstPersonPlayer && swimUpwardCorrection)
|
||||
{
|
||||
|
@ -2439,8 +2445,14 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
}
|
||||
}
|
||||
|
||||
if (mFloatToSurface && cls.isActor() && cls.getCreatureStats(mPtr).isDead() && cls.canSwim(mPtr))
|
||||
moved.z() = 1.0;
|
||||
if (mFloatToSurface && cls.isActor() && cls.canSwim(mPtr))
|
||||
{
|
||||
if (cls.getCreatureStats(mPtr).isDead()
|
||||
|| (!godmode && cls.getCreatureStats(mPtr).isParalyzed()))
|
||||
{
|
||||
moved.z() = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update movement
|
||||
if(!animationOnly && mMovementAnimationControlled && mPtr.getClass().isActor())
|
||||
|
|
|
@ -1654,6 +1654,11 @@ namespace MWMechanics
|
|||
return mActors.getActorsFollowingIndices(actor);
|
||||
}
|
||||
|
||||
std::map<int, MWWorld::Ptr> MechanicsManager::getActorsFollowingByIndex(const MWWorld::Ptr& actor)
|
||||
{
|
||||
return mActors.getActorsFollowingByIndex(actor);
|
||||
}
|
||||
|
||||
std::list<MWWorld::Ptr> MechanicsManager::getActorsFighting(const MWWorld::Ptr& actor) {
|
||||
return mActors.getActorsFighting(actor);
|
||||
}
|
||||
|
|
|
@ -150,6 +150,7 @@ namespace MWMechanics
|
|||
std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor) override;
|
||||
std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) override;
|
||||
std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor) override;
|
||||
std::map<int, MWWorld::Ptr> getActorsFollowingByIndex(const MWWorld::Ptr& actor) override;
|
||||
|
||||
std::list<MWWorld::Ptr> getActorsFighting(const MWWorld::Ptr& actor) override;
|
||||
std::list<MWWorld::Ptr> getEnemiesNearby(const MWWorld::Ptr& actor) override;
|
||||
|
|
|
@ -74,8 +74,9 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic
|
|||
|
||||
updateRotation();
|
||||
updateScale();
|
||||
resetPosition();
|
||||
updatePosition();
|
||||
addCollisionMask(getCollisionMask());
|
||||
updateCollisionObjectPosition();
|
||||
}
|
||||
|
||||
Actor::~Actor()
|
||||
|
@ -118,26 +119,34 @@ int Actor::getCollisionMask() const
|
|||
return collisionMask;
|
||||
}
|
||||
|
||||
void Actor::updatePositionUnsafe()
|
||||
{
|
||||
mWorldPosition = mPtr.getRefData().getPosition().asVec3();
|
||||
}
|
||||
|
||||
void Actor::updatePosition()
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
updatePositionUnsafe();
|
||||
updateWorldPosition();
|
||||
mPreviousPosition = mWorldPosition;
|
||||
mPosition = mWorldPosition;
|
||||
mSimulationPosition = mWorldPosition;
|
||||
mStandingOnPtr = nullptr;
|
||||
mSkipSimulation = true;
|
||||
}
|
||||
|
||||
void Actor::updateWorldPosition()
|
||||
{
|
||||
if (mWorldPosition != mPtr.getRefData().getPosition().asVec3())
|
||||
mWorldPositionChanged = true;
|
||||
mWorldPosition = mPtr.getRefData().getPosition().asVec3();
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::getWorldPosition() const
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
return mWorldPosition;
|
||||
}
|
||||
|
||||
void Actor::setSimulationPosition(const osg::Vec3f& position)
|
||||
{
|
||||
mSimulationPosition = position;
|
||||
if (!mSkipSimulation)
|
||||
mSimulationPosition = position;
|
||||
mSkipSimulation = false;
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::getSimulationPosition() const
|
||||
|
@ -145,20 +154,16 @@ osg::Vec3f Actor::getSimulationPosition() const
|
|||
return mSimulationPosition;
|
||||
}
|
||||
|
||||
void Actor::updateCollisionObjectPositionUnsafe()
|
||||
void Actor::updateCollisionObjectPosition()
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
mShape->setLocalScaling(Misc::Convert::toBullet(mScale));
|
||||
osg::Vec3f scaledTranslation = mRotation * osg::componentMultiply(mMeshTranslation, mScale);
|
||||
osg::Vec3f newPosition = scaledTranslation + mPosition;
|
||||
mLocalTransform.setOrigin(Misc::Convert::toBullet(newPosition));
|
||||
mLocalTransform.setRotation(Misc::Convert::toBullet(mRotation));
|
||||
mCollisionObject->setWorldTransform(mLocalTransform);
|
||||
}
|
||||
|
||||
void Actor::updateCollisionObjectPosition()
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
updateCollisionObjectPositionUnsafe();
|
||||
mWorldPositionChanged = false;
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::getCollisionObjectPosition() const
|
||||
|
@ -167,28 +172,20 @@ osg::Vec3f Actor::getCollisionObjectPosition() const
|
|||
return Misc::Convert::toOsg(mLocalTransform.getOrigin());
|
||||
}
|
||||
|
||||
void Actor::setPosition(const osg::Vec3f& position)
|
||||
bool Actor::setPosition(const osg::Vec3f& position)
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
mPreviousPosition = mPosition;
|
||||
mPosition = position;
|
||||
bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged;
|
||||
mPreviousPosition = mPosition + mPositionOffset;
|
||||
mPosition = position + mPositionOffset;
|
||||
mPositionOffset = osg::Vec3f();
|
||||
return hasChanged;
|
||||
}
|
||||
|
||||
void Actor::adjustPosition(const osg::Vec3f& offset)
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
mPosition += offset;
|
||||
mPreviousPosition += offset;
|
||||
}
|
||||
|
||||
void Actor::resetPosition()
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
updatePositionUnsafe();
|
||||
mPreviousPosition = mWorldPosition;
|
||||
mPosition = mWorldPosition;
|
||||
mSimulationPosition = mWorldPosition;
|
||||
updateCollisionObjectPositionUnsafe();
|
||||
mPositionOffset += offset;
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::getPosition() const
|
||||
|
@ -204,8 +201,6 @@ osg::Vec3f Actor::getPreviousPosition() const
|
|||
void Actor::updateRotation ()
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
if (mRotation == mPtr.getRefData().getBaseNode()->getAttitude())
|
||||
return;
|
||||
mRotation = mPtr.getRefData().getBaseNode()->getAttitude();
|
||||
}
|
||||
|
||||
|
@ -236,7 +231,6 @@ osg::Vec3f Actor::getHalfExtents() const
|
|||
|
||||
osg::Vec3f Actor::getOriginalHalfExtents() const
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
return mHalfExtents;
|
||||
}
|
||||
|
||||
|
@ -273,7 +267,6 @@ void Actor::setWalkingOnWater(bool walkingOnWater)
|
|||
|
||||
void Actor::setCanWaterWalk(bool waterWalk)
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
if (waterWalk != mCanWaterWalk)
|
||||
{
|
||||
mCanWaterWalk = waterWalk;
|
||||
|
|
|
@ -60,7 +60,7 @@ namespace MWPhysics
|
|||
* Set mWorldPosition to the position in the Ptr's RefData. This is used by the physics simulation to account for
|
||||
* when an object is "instantly" moved/teleported as opposed to being moved by the physics simulation.
|
||||
*/
|
||||
void updatePosition();
|
||||
void updateWorldPosition();
|
||||
osg::Vec3f getWorldPosition() const;
|
||||
|
||||
/**
|
||||
|
@ -90,9 +90,10 @@ namespace MWPhysics
|
|||
|
||||
/**
|
||||
* Store the current position into mPreviousPosition, then move to this position.
|
||||
* Returns true if the new position is different.
|
||||
*/
|
||||
void setPosition(const osg::Vec3f& position);
|
||||
void resetPosition();
|
||||
bool setPosition(const osg::Vec3f& position);
|
||||
void updatePosition();
|
||||
void adjustPosition(const osg::Vec3f& offset);
|
||||
|
||||
osg::Vec3f getPosition() const;
|
||||
|
@ -154,8 +155,6 @@ namespace MWPhysics
|
|||
void updateCollisionMask();
|
||||
void addCollisionMask(int collisionMask);
|
||||
int getCollisionMask() const;
|
||||
void updateCollisionObjectPositionUnsafe();
|
||||
void updatePositionUnsafe();
|
||||
|
||||
bool mCanWaterWalk;
|
||||
std::atomic<bool> mWalkingOnWater;
|
||||
|
@ -177,6 +176,9 @@ namespace MWPhysics
|
|||
osg::Vec3f mSimulationPosition;
|
||||
osg::Vec3f mPosition;
|
||||
osg::Vec3f mPreviousPosition;
|
||||
osg::Vec3f mPositionOffset;
|
||||
bool mWorldPositionChanged;
|
||||
bool mSkipSimulation;
|
||||
btTransform mLocalTransform;
|
||||
mutable std::mutex mPositionMutex;
|
||||
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
||||
|
||||
#include <components/misc/convert.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include "collisiontype.hpp"
|
||||
#include "projectile.hpp"
|
||||
|
||||
|
|
|
@ -41,15 +41,21 @@ namespace MWPhysics
|
|||
btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
||||
if (mProjectile)
|
||||
{
|
||||
auto* holder = static_cast<PtrHolder*>(rayResult.m_collisionObject->getUserPointer());
|
||||
if (auto* target = dynamic_cast<Actor*>(holder))
|
||||
switch (rayResult.m_collisionObject->getBroadphaseHandle()->m_collisionFilterGroup)
|
||||
{
|
||||
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);
|
||||
case CollisionType_Actor:
|
||||
{
|
||||
auto* target = static_cast<Actor*>(rayResult.m_collisionObject->getUserPointer());
|
||||
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||
break;
|
||||
}
|
||||
case CollisionType_Projectile:
|
||||
{
|
||||
auto* target = static_cast<Projectile*>(rayResult.m_collisionObject->getUserPointer());
|
||||
target->hit(mProjectile->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,12 +78,14 @@ namespace MWPhysics
|
|||
WorldFrameData& worldData)
|
||||
{
|
||||
auto* physicActor = actor.mActorRaw;
|
||||
auto ptr = actor.mPtr;
|
||||
const ESM::Position& refpos = actor.mRefpos;
|
||||
// Early-out for totally static creatures
|
||||
// (Not sure if gravity should still apply?)
|
||||
if (!ptr.getClass().isMobile(ptr))
|
||||
return;
|
||||
{
|
||||
const auto ptr = physicActor->getPtr();
|
||||
if (!ptr.getClass().isMobile(ptr))
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset per-frame data
|
||||
physicActor->setWalkingOnWater(false);
|
||||
|
@ -128,8 +130,9 @@ namespace MWPhysics
|
|||
velocity = velocity + inertia;
|
||||
}
|
||||
|
||||
// dead actors underwater will float to the surface, if the CharacterController tells us to do so
|
||||
if (actor.mMovement.z() > 0 && actor.mIsDead && actor.mPosition.z() < swimlevel)
|
||||
// Dead and paralyzed actors underwater will float to the surface,
|
||||
// if the CharacterController tells us to do so
|
||||
if (actor.mMovement.z() > 0 && actor.mFloatToSurface && actor.mPosition.z() < swimlevel)
|
||||
velocity = osg::Vec3f(0,0,1) * 25;
|
||||
|
||||
if (actor.mWantJump)
|
||||
|
@ -211,6 +214,7 @@ namespace MWPhysics
|
|||
if (result)
|
||||
{
|
||||
// don't let pure water creatures move out of water after stepMove
|
||||
const auto ptr = physicActor->getPtr();
|
||||
if (ptr.getClass().isPureWaterCreature(ptr) && newPosition.z() + halfExtents.z() > actor.mWaterlevel)
|
||||
newPosition = oldPosition;
|
||||
}
|
||||
|
|
|
@ -87,12 +87,13 @@ namespace
|
|||
|
||||
void updateMechanics(MWPhysics::ActorFrameData& actorData)
|
||||
{
|
||||
auto ptr = actorData.mActorRaw->getPtr();
|
||||
if (actorData.mDidJump)
|
||||
handleJump(actorData.mPtr);
|
||||
handleJump(ptr);
|
||||
|
||||
MWMechanics::CreatureStats& stats = actorData.mPtr.getClass().getCreatureStats(actorData.mPtr);
|
||||
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
||||
if (actorData.mNeedLand)
|
||||
stats.land(actorData.mPtr == MWMechanics::getPlayer() && (actorData.mFlying || actorData.mSwimming));
|
||||
stats.land(ptr == MWMechanics::getPlayer() && (actorData.mFlying || actorData.mSwimming));
|
||||
else if (actorData.mFallHeight < 0)
|
||||
stats.addToFallHeight(-actorData.mFallHeight);
|
||||
}
|
||||
|
@ -100,15 +101,6 @@ namespace
|
|||
osg::Vec3f interpolateMovements(MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt)
|
||||
{
|
||||
const float interpolationFactor = timeAccum / physicsDt;
|
||||
|
||||
// account for force change of actor's position in the main thread
|
||||
const auto correction = actorData.mActorRaw->getWorldPosition() - actorData.mOrigin;
|
||||
if (correction.length() != 0)
|
||||
{
|
||||
actorData.mActorRaw->adjustPosition(correction);
|
||||
actorData.mPosition = actorData.mActorRaw->getPosition();
|
||||
}
|
||||
|
||||
return actorData.mPosition * interpolationFactor + actorData.mActorRaw->getPreviousPosition() * (1.f - interpolationFactor);
|
||||
}
|
||||
|
||||
|
@ -213,45 +205,43 @@ namespace MWPhysics
|
|||
thread.join();
|
||||
}
|
||||
|
||||
const PtrPositionList& PhysicsTaskScheduler::moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||
const std::vector<MWWorld::Ptr>& PhysicsTaskScheduler::moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||
{
|
||||
// This function run in the main thread.
|
||||
// While the mSimulationMutex is held, background physics threads can't run.
|
||||
|
||||
std::unique_lock lock(mSimulationMutex);
|
||||
|
||||
for (auto& data : actorsData)
|
||||
data.updatePosition();
|
||||
mMovedActors.clear();
|
||||
|
||||
// start by finishing previous background computation
|
||||
if (mNumThreads != 0)
|
||||
{
|
||||
for (auto& data : mActorsFrameData)
|
||||
{
|
||||
// Ignore actors that were deleted while the background thread was running
|
||||
if (!data.mActor.lock())
|
||||
continue;
|
||||
const auto actorActive = [&data](const auto& newFrameData) -> bool
|
||||
{
|
||||
const auto actor = data.mActor.lock();
|
||||
return actor && actor->getPtr() == newFrameData.mActorRaw->getPtr();
|
||||
};
|
||||
// Only return actors that are still part of the scene
|
||||
if (std::any_of(actorsData.begin(), actorsData.end(), actorActive))
|
||||
{
|
||||
updateMechanics(data);
|
||||
|
||||
updateMechanics(data);
|
||||
if (mAdvanceSimulation)
|
||||
data.mActorRaw->setStandingOnPtr(data.mStandingOn);
|
||||
|
||||
if (mMovementResults.find(data.mPtr) != mMovementResults.end())
|
||||
data.mActorRaw->setSimulationPosition(mMovementResults[data.mPtr]);
|
||||
// these variables are accessed directly from the main thread, update them here to prevent accessing "too new" values
|
||||
if (mAdvanceSimulation)
|
||||
data.mActorRaw->setStandingOnPtr(data.mStandingOn);
|
||||
data.mActorRaw->setSimulationPosition(interpolateMovements(data, mTimeAccum, mPhysicsDt));
|
||||
mMovedActors.emplace_back(data.mActorRaw->getPtr());
|
||||
}
|
||||
}
|
||||
|
||||
if (mFrameNumber == frameNumber - 1)
|
||||
{
|
||||
stats.setAttribute(mFrameNumber, "physicsworker_time_begin", mTimer->delta_s(mFrameStart, mTimeBegin));
|
||||
stats.setAttribute(mFrameNumber, "physicsworker_time_taken", mTimer->delta_s(mTimeBegin, mTimeEnd));
|
||||
stats.setAttribute(mFrameNumber, "physicsworker_time_end", mTimer->delta_s(mFrameStart, mTimeEnd));
|
||||
}
|
||||
mFrameStart = frameStart;
|
||||
mTimeBegin = mTimer->tick();
|
||||
mFrameNumber = frameNumber;
|
||||
updateStats(frameStart, frameNumber, stats);
|
||||
}
|
||||
|
||||
// init
|
||||
for (auto& data : actorsData)
|
||||
data.updatePosition();
|
||||
mRemainingSteps = numSteps;
|
||||
mTimeAccum = timeAccum;
|
||||
mActorsFrameData = std::move(actorsData);
|
||||
|
@ -266,52 +256,28 @@ namespace MWPhysics
|
|||
|
||||
if (mNumThreads == 0)
|
||||
{
|
||||
mMovementResults.clear();
|
||||
syncComputation();
|
||||
|
||||
for (auto& data : mActorsFrameData)
|
||||
{
|
||||
if (mAdvanceSimulation)
|
||||
data.mActorRaw->setStandingOnPtr(data.mStandingOn);
|
||||
if (mMovementResults.find(data.mPtr) != mMovementResults.end())
|
||||
data.mActorRaw->setSimulationPosition(mMovementResults[data.mPtr]);
|
||||
}
|
||||
return mMovementResults;
|
||||
return mMovedActors;
|
||||
}
|
||||
|
||||
// Remove actors that were deleted while the background thread was running
|
||||
for (auto& data : mActorsFrameData)
|
||||
{
|
||||
if (!data.mActor.lock())
|
||||
mMovementResults.erase(data.mPtr);
|
||||
}
|
||||
std::swap(mMovementResults, mPreviousMovementResults);
|
||||
|
||||
// mMovementResults is shared between all workers instance
|
||||
// pre-allocate all nodes so that we don't need synchronization
|
||||
mMovementResults.clear();
|
||||
for (const auto& m : mActorsFrameData)
|
||||
mMovementResults[m.mPtr] = m.mPosition;
|
||||
|
||||
lock.unlock();
|
||||
mHasJob.notify_all();
|
||||
return mPreviousMovementResults;
|
||||
return mMovedActors;
|
||||
}
|
||||
|
||||
const PtrPositionList& PhysicsTaskScheduler::resetSimulation(const ActorMap& actors)
|
||||
const std::vector<MWWorld::Ptr>& PhysicsTaskScheduler::resetSimulation(const ActorMap& actors)
|
||||
{
|
||||
std::unique_lock lock(mSimulationMutex);
|
||||
mMovementResults.clear();
|
||||
mPreviousMovementResults.clear();
|
||||
mMovedActors.clear();
|
||||
mActorsFrameData.clear();
|
||||
|
||||
for (const auto& [_, actor] : actors)
|
||||
{
|
||||
actor->resetPosition();
|
||||
actor->setStandingOnPtr(nullptr);
|
||||
mMovementResults[actor->getPtr()] = actor->getWorldPosition();
|
||||
actor->updatePosition();
|
||||
actor->setSimulationPosition(actor->getWorldPosition()); // updatePosition skip next simulation, now we need to "consume" it
|
||||
actor->updateCollisionObjectPosition();
|
||||
mMovedActors.emplace_back(actor->getPtr());
|
||||
}
|
||||
return mMovementResults;
|
||||
return mMovedActors;
|
||||
}
|
||||
|
||||
void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const
|
||||
|
@ -379,18 +345,18 @@ namespace MWPhysics
|
|||
mCollisionWorld->removeCollisionObject(collisionObject);
|
||||
}
|
||||
|
||||
void PhysicsTaskScheduler::updateSingleAabb(std::weak_ptr<PtrHolder> ptr)
|
||||
void PhysicsTaskScheduler::updateSingleAabb(std::weak_ptr<PtrHolder> ptr, bool immediate)
|
||||
{
|
||||
if (mDeferAabbUpdate)
|
||||
{
|
||||
std::unique_lock lock(mUpdateAabbMutex);
|
||||
mUpdateAabb.insert(std::move(ptr));
|
||||
}
|
||||
else
|
||||
if (!mDeferAabbUpdate || immediate)
|
||||
{
|
||||
std::unique_lock lock(mCollisionWorldMutex);
|
||||
updatePtrAabb(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_lock lock(mUpdateAabbMutex);
|
||||
mUpdateAabb.insert(std::move(ptr));
|
||||
}
|
||||
}
|
||||
|
||||
bool PhysicsTaskScheduler::getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2)
|
||||
|
@ -493,7 +459,6 @@ namespace MWPhysics
|
|||
{
|
||||
auto& actorData = mActorsFrameData[job];
|
||||
handleFall(actorData, mAdvanceSimulation);
|
||||
mMovementResults[actorData.mPtr] = interpolateMovements(actorData, mTimeAccum, mPhysicsDt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,9 +476,7 @@ namespace MWPhysics
|
|||
{
|
||||
if(const auto actor = actorData.mActor.lock())
|
||||
{
|
||||
bool positionChanged = actorData.mPosition != actorData.mActorRaw->getPosition();
|
||||
actorData.mActorRaw->setPosition(actorData.mPosition);
|
||||
if (positionChanged)
|
||||
if (actor->setPosition(actorData.mPosition))
|
||||
{
|
||||
actor->updateCollisionObjectPosition();
|
||||
mCollisionWorld->updateSingleAabb(actor->getCollisionObject());
|
||||
|
@ -550,8 +513,24 @@ namespace MWPhysics
|
|||
for (auto& actorData : mActorsFrameData)
|
||||
{
|
||||
handleFall(actorData, mAdvanceSimulation);
|
||||
mMovementResults[actorData.mPtr] = interpolateMovements(actorData, mTimeAccum, mPhysicsDt);
|
||||
actorData.mActorRaw->setSimulationPosition(interpolateMovements(actorData, mTimeAccum, mPhysicsDt));
|
||||
updateMechanics(actorData);
|
||||
mMovedActors.emplace_back(actorData.mActorRaw->getPtr());
|
||||
if (mAdvanceSimulation)
|
||||
actorData.mActorRaw->setStandingOnPtr(actorData.mStandingOn);
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsTaskScheduler::updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||
{
|
||||
if (mFrameNumber == frameNumber - 1)
|
||||
{
|
||||
stats.setAttribute(mFrameNumber, "physicsworker_time_begin", mTimer->delta_s(mFrameStart, mTimeBegin));
|
||||
stats.setAttribute(mFrameNumber, "physicsworker_time_taken", mTimer->delta_s(mTimeBegin, mTimeEnd));
|
||||
stats.setAttribute(mFrameNumber, "physicsworker_time_end", mTimer->delta_s(mFrameStart, mTimeEnd));
|
||||
}
|
||||
mFrameStart = frameStart;
|
||||
mTimeBegin = mTimer->tick();
|
||||
mFrameNumber = frameNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,9 +32,9 @@ namespace MWPhysics
|
|||
/// @param timeAccum accumulated time from previous run to interpolate movements
|
||||
/// @param actorsData per actor data needed to compute new positions
|
||||
/// @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 std::vector<MWWorld::Ptr>& moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
||||
|
||||
const PtrPositionList& resetSimulation(const ActorMap& actors);
|
||||
const std::vector<MWWorld::Ptr>& resetSimulation(const ActorMap& actors);
|
||||
|
||||
// Thread safe wrappers
|
||||
void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const;
|
||||
|
@ -46,7 +46,7 @@ namespace MWPhysics
|
|||
void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask);
|
||||
void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask);
|
||||
void removeCollisionObject(btCollisionObject* collisionObject);
|
||||
void updateSingleAabb(std::weak_ptr<PtrHolder> ptr);
|
||||
void updateSingleAabb(std::weak_ptr<PtrHolder> ptr, bool immediate=false);
|
||||
bool getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2);
|
||||
|
||||
private:
|
||||
|
@ -57,11 +57,11 @@ namespace MWPhysics
|
|||
void refreshLOSCache();
|
||||
void updateAabbs();
|
||||
void updatePtrAabb(const std::weak_ptr<PtrHolder>& ptr);
|
||||
void updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
||||
|
||||
std::unique_ptr<WorldFrameData> mWorldFrameData;
|
||||
std::vector<ActorFrameData> mActorsFrameData;
|
||||
PtrPositionList mMovementResults;
|
||||
PtrPositionList mPreviousMovementResults;
|
||||
std::vector<MWWorld::Ptr> mMovedActors;
|
||||
const float mPhysicsDt;
|
||||
float mTimeAccum;
|
||||
std::shared_ptr<btCollisionWorld> mCollisionWorld;
|
||||
|
|
|
@ -437,7 +437,6 @@ namespace MWPhysics
|
|||
ActorMap::iterator found = mActors.find(ptr);
|
||||
if (found == mActors.end())
|
||||
return ptr.getRefData().getPosition().asVec3();
|
||||
found->second->resetPosition();
|
||||
return MovementSolver::traceDown(ptr, position, found->second.get(), mCollisionWorld.get(), maxHeight);
|
||||
}
|
||||
|
||||
|
@ -642,7 +641,7 @@ namespace MWPhysics
|
|||
if (foundActor != mActors.end())
|
||||
{
|
||||
foundActor->second->updatePosition();
|
||||
mTaskScheduler->updateSingleAabb(foundActor->second);
|
||||
mTaskScheduler->updateSingleAabb(foundActor->second, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -711,7 +710,7 @@ namespace MWPhysics
|
|||
mMovementQueue.clear();
|
||||
}
|
||||
|
||||
const PtrPositionList& PhysicsSystem::applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||
const std::vector<MWWorld::Ptr>& PhysicsSystem::applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||
{
|
||||
mTimeAccum += dt;
|
||||
|
||||
|
@ -770,7 +769,7 @@ namespace MWPhysics
|
|||
if (numSteps == 0)
|
||||
standingOn = physicActor->getStandingOnPtr();
|
||||
|
||||
actorsFrameData.emplace_back(std::move(physicActor), character, standingOn, moveToWaterSurface, movement, slowFall, waterlevel);
|
||||
actorsFrameData.emplace_back(std::move(physicActor), standingOn, moveToWaterSurface, movement, slowFall, waterlevel);
|
||||
}
|
||||
mMovementQueue.clear();
|
||||
return actorsFrameData;
|
||||
|
@ -912,25 +911,26 @@ namespace MWPhysics
|
|||
mDebugDrawer->addCollision(position, normal);
|
||||
}
|
||||
|
||||
ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr character, const MWWorld::Ptr standingOn,
|
||||
ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn,
|
||||
bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel)
|
||||
: mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn),
|
||||
mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface),
|
||||
mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos()
|
||||
{
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
mPtr = actor->getPtr();
|
||||
mFlying = world->isFlying(character);
|
||||
mSwimming = world->isSwimming(character);
|
||||
mWantJump = mPtr.getClass().getMovementSettings(mPtr).mPosition[2] != 0;
|
||||
mIsDead = mPtr.getClass().getCreatureStats(mPtr).isDead();
|
||||
const auto ptr = actor->getPtr();
|
||||
mFlying = world->isFlying(ptr);
|
||||
mSwimming = world->isSwimming(ptr);
|
||||
mWantJump = ptr.getClass().getMovementSettings(ptr).mPosition[2] != 0;
|
||||
auto& stats = ptr.getClass().getCreatureStats(ptr);
|
||||
const bool godmode = ptr == world->getPlayerConstPtr() && world->getGodModeState();
|
||||
mFloatToSurface = stats.isDead() || (!godmode && stats.isParalyzed());
|
||||
mWasOnGround = actor->getOnGround();
|
||||
}
|
||||
|
||||
void ActorFrameData::updatePosition()
|
||||
{
|
||||
mActorRaw->updatePosition();
|
||||
mOrigin = mActorRaw->getSimulationPosition();
|
||||
mActorRaw->updateWorldPosition();
|
||||
mPosition = mActorRaw->getPosition();
|
||||
if (mMoveToWaterSurface)
|
||||
{
|
||||
|
@ -938,7 +938,7 @@ namespace MWPhysics
|
|||
mActorRaw->setPosition(mPosition);
|
||||
}
|
||||
mOldHeight = mPosition.z();
|
||||
mRefpos = mPtr.getRefData().getPosition();
|
||||
mRefpos = mActorRaw->getPtr().getRefData().getPosition();
|
||||
}
|
||||
|
||||
WorldFrameData::WorldFrameData()
|
||||
|
|
|
@ -50,8 +50,6 @@ class btVector3;
|
|||
|
||||
namespace MWPhysics
|
||||
{
|
||||
using PtrPositionList = std::map<MWWorld::Ptr, osg::Vec3f>;
|
||||
|
||||
class HeightField;
|
||||
class Object;
|
||||
class Actor;
|
||||
|
@ -80,18 +78,17 @@ namespace MWPhysics
|
|||
|
||||
struct ActorFrameData
|
||||
{
|
||||
ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr character, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel);
|
||||
ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel);
|
||||
void updatePosition();
|
||||
std::weak_ptr<Actor> mActor;
|
||||
Actor* mActorRaw;
|
||||
MWWorld::Ptr mPtr;
|
||||
MWWorld::Ptr mStandingOn;
|
||||
bool mFlying;
|
||||
bool mSwimming;
|
||||
bool mWasOnGround;
|
||||
bool mWantJump;
|
||||
bool mDidJump;
|
||||
bool mIsDead;
|
||||
bool mFloatToSurface;
|
||||
bool mNeedLand;
|
||||
bool mMoveToWaterSurface;
|
||||
float mWaterlevel;
|
||||
|
@ -99,7 +96,6 @@ namespace MWPhysics
|
|||
float mOldHeight;
|
||||
float mFallHeight;
|
||||
osg::Vec3f mMovement;
|
||||
osg::Vec3f mOrigin;
|
||||
osg::Vec3f mPosition;
|
||||
ESM::Position mRefpos;
|
||||
};
|
||||
|
@ -210,7 +206,7 @@ namespace MWPhysics
|
|||
void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity);
|
||||
|
||||
/// Apply all queued movements, then clear the list.
|
||||
const PtrPositionList& applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
||||
const std::vector<MWWorld::Ptr>& applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
||||
|
||||
/// Clear the queued movements list without applying.
|
||||
void clearQueuedMovement();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef OPENMW_MWPHYSICS_PTRHOLDER_H
|
||||
#define OPENMW_MWPHYSICS_PTRHOLDER_H
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWPhysics
|
||||
|
@ -12,21 +14,27 @@ namespace MWPhysics
|
|||
|
||||
void updatePtr(const MWWorld::Ptr& updated)
|
||||
{
|
||||
std::scoped_lock lock(mMutex);
|
||||
mPtr = updated;
|
||||
}
|
||||
|
||||
MWWorld::Ptr getPtr()
|
||||
{
|
||||
std::scoped_lock lock(mMutex);
|
||||
return mPtr;
|
||||
}
|
||||
|
||||
MWWorld::ConstPtr getPtr() const
|
||||
{
|
||||
std::scoped_lock lock(mMutex);
|
||||
return mPtr;
|
||||
}
|
||||
|
||||
protected:
|
||||
MWWorld::Ptr mPtr;
|
||||
|
||||
private:
|
||||
mutable std::mutex mMutex;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
#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"
|
||||
|
|
|
@ -356,9 +356,6 @@ namespace MWRender
|
|||
else
|
||||
mViewModeToggleQueued = false;
|
||||
|
||||
if (mTrackingPtr.getClass().isActor())
|
||||
mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).setSideMovementAngle(0);
|
||||
|
||||
mFirstPersonView = !mFirstPersonView;
|
||||
updateStandingPreviewMode();
|
||||
instantTransition();
|
||||
|
|
|
@ -32,11 +32,7 @@ namespace MWScript
|
|||
std::vector<MWWorld::Ptr> actors;
|
||||
MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors);
|
||||
for (auto& actor : actors)
|
||||
{
|
||||
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||
actorPos += diff;
|
||||
MWBase::Environment::get().getWorld()->moveObject(actor, actorPos.x(), actorPos.y(), actorPos.z());
|
||||
}
|
||||
MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff);
|
||||
}
|
||||
|
||||
template<class R>
|
||||
|
@ -727,14 +723,12 @@ namespace MWScript
|
|||
return;
|
||||
|
||||
osg::Vec3f diff = ptr.getRefData().getBaseNode()->getAttitude() * posChange;
|
||||
osg::Vec3f worldPos(ptr.getRefData().getPosition().asVec3());
|
||||
worldPos += diff;
|
||||
|
||||
// We should move actors, standing on moving object, too.
|
||||
// This approach can be used to create elevators.
|
||||
moveStandingActors(ptr, diff);
|
||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x(), worldPos.y(), worldPos.z()));
|
||||
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -755,15 +749,14 @@ namespace MWScript
|
|||
Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());
|
||||
runtime.pop();
|
||||
|
||||
const float *objPos = ptr.getRefData().getPosition().pos;
|
||||
osg::Vec3f diff;
|
||||
|
||||
if (axis == "x")
|
||||
diff.x() += movement;
|
||||
diff.x() = movement;
|
||||
else if (axis == "y")
|
||||
diff.y() += movement;
|
||||
diff.y() = movement;
|
||||
else if (axis == "z")
|
||||
diff.z() += movement;
|
||||
diff.z() = movement;
|
||||
else
|
||||
return;
|
||||
|
||||
|
@ -771,7 +764,7 @@ namespace MWScript
|
|||
// This approach can be used to create elevators.
|
||||
moveStandingActors(ptr, diff);
|
||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z()));
|
||||
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1251,6 +1251,18 @@ namespace MWWorld
|
|||
return moveObjectImp(ptr, x, y, z, true, moveToActive);
|
||||
}
|
||||
|
||||
MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec)
|
||||
{
|
||||
auto* actor = mPhysics->getActor(ptr);
|
||||
if (actor)
|
||||
{
|
||||
actor->adjustPosition(vec);
|
||||
return ptr;
|
||||
}
|
||||
osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec;
|
||||
return moveObject(ptr, newpos.x(), newpos.y(), newpos.z());
|
||||
}
|
||||
|
||||
void World::scaleObject (const Ptr& ptr, float scale)
|
||||
{
|
||||
if (mPhysics->getActor(ptr))
|
||||
|
@ -1333,7 +1345,7 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
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
|
||||
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());
|
||||
|
@ -1344,12 +1356,6 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z());
|
||||
if (ptr.getClass().isActor())
|
||||
{
|
||||
MWPhysics::Actor* actor = mPhysics->getActor(ptr);
|
||||
if (actor)
|
||||
actor->resetPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void World::fixPosition()
|
||||
|
@ -1500,16 +1506,26 @@ namespace MWWorld
|
|||
mProjectileManager->processHits();
|
||||
mDiscardMovements = false;
|
||||
|
||||
for(const auto& [actor, position]: results)
|
||||
for(const auto& actor : results)
|
||||
{
|
||||
// Handle player last, in case a cell transition occurs
|
||||
if(actor != getPlayerPtr())
|
||||
{
|
||||
auto* physactor = mPhysics->getActor(actor);
|
||||
assert(physactor);
|
||||
const auto position = physactor->getSimulationPosition();
|
||||
moveObjectImp(actor, position.x(), position.y(), position.z(), false);
|
||||
}
|
||||
}
|
||||
|
||||
const auto player = results.find(getPlayerPtr());
|
||||
const auto player = std::find(results.begin(), results.end(), getPlayerPtr());
|
||||
if (player != results.end())
|
||||
moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false);
|
||||
{
|
||||
auto* physactor = mPhysics->getActor(*player);
|
||||
assert(physactor);
|
||||
const auto position = physactor->getSimulationPosition();
|
||||
moveObjectImp(*player, position.x(), position.y(), position.z(), false);
|
||||
}
|
||||
}
|
||||
|
||||
void World::updateNavigator()
|
||||
|
@ -2286,8 +2302,12 @@ namespace MWWorld
|
|||
if (stats.isDead())
|
||||
return false;
|
||||
|
||||
const bool isPlayer = ptr == getPlayerConstPtr();
|
||||
if (!(isPlayer && mGodMode) && stats.isParalyzed())
|
||||
return false;
|
||||
|
||||
if (ptr.getClass().canFly(ptr))
|
||||
return !stats.isParalyzed();
|
||||
return true;
|
||||
|
||||
if(stats.getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0
|
||||
&& isLevitationEnabled())
|
||||
|
@ -2897,7 +2917,7 @@ namespace MWWorld
|
|||
mRendering->rebuildPtr(getPlayerPtr());
|
||||
}
|
||||
|
||||
bool World::getGodModeState()
|
||||
bool World::getGodModeState() const
|
||||
{
|
||||
return mGodMode;
|
||||
}
|
||||
|
|
|
@ -380,6 +380,9 @@ namespace MWWorld
|
|||
MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override;
|
||||
///< @return an updated Ptr
|
||||
|
||||
MWWorld::Ptr moveObjectBy(const Ptr& ptr, osg::Vec3f vec) override;
|
||||
///< @return an updated Ptr
|
||||
|
||||
void scaleObject (const Ptr& ptr, float scale) override;
|
||||
|
||||
/// World rotates object, uses radians
|
||||
|
@ -616,7 +619,7 @@ namespace MWWorld
|
|||
/// Returns true if levitation spell effect is allowed.
|
||||
bool isLevitationEnabled() const override;
|
||||
|
||||
bool getGodModeState() override;
|
||||
bool getGodModeState() const override;
|
||||
|
||||
bool toggleGodMode() override;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
find_package(GTest REQUIRED)
|
||||
find_package(GMock REQUIRED)
|
||||
find_package(GTest 1.10 REQUIRED)
|
||||
find_package(GMock 1.10 REQUIRED)
|
||||
|
||||
if (GTEST_FOUND AND GMOCK_FOUND)
|
||||
include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})
|
||||
|
|
|
@ -299,22 +299,22 @@ namespace
|
|||
|
||||
struct NifFileMock : Nif::File
|
||||
{
|
||||
MOCK_CONST_METHOD1(getRecord, Nif::Record* (std::size_t));
|
||||
MOCK_CONST_METHOD0(numRecords, std::size_t ());
|
||||
MOCK_CONST_METHOD1(getRoot, Nif::Record* (std::size_t));
|
||||
MOCK_CONST_METHOD0(numRoots, std::size_t ());
|
||||
MOCK_CONST_METHOD1(getString, std::string (uint32_t));
|
||||
MOCK_METHOD1(setUseSkinning, void (bool));
|
||||
MOCK_CONST_METHOD0(getUseSkinning, bool ());
|
||||
MOCK_CONST_METHOD0(getFilename, std::string ());
|
||||
MOCK_CONST_METHOD0(getVersion, unsigned int ());
|
||||
MOCK_CONST_METHOD0(getUserVersion, unsigned int ());
|
||||
MOCK_CONST_METHOD0(getBethVersion, unsigned int ());
|
||||
MOCK_METHOD(Nif::Record*, getRecord, (std::size_t), (const, override));
|
||||
MOCK_METHOD(std::size_t, numRecords, (), (const, override));
|
||||
MOCK_METHOD(Nif::Record*, getRoot, (std::size_t), (const, override));
|
||||
MOCK_METHOD(std::size_t, numRoots, (), (const, override));
|
||||
MOCK_METHOD(std::string, getString, (uint32_t), (const, override));
|
||||
MOCK_METHOD(void, setUseSkinning, (bool), (override));
|
||||
MOCK_METHOD(bool, getUseSkinning, (), (const, override));
|
||||
MOCK_METHOD(std::string, getFilename, (), (const, override));
|
||||
MOCK_METHOD(unsigned int, getVersion, (), (const, override));
|
||||
MOCK_METHOD(unsigned int, getUserVersion, (), (const, override));
|
||||
MOCK_METHOD(unsigned int, getBethVersion, (), (const, override));
|
||||
};
|
||||
|
||||
struct RecordMock : Nif::Record
|
||||
{
|
||||
MOCK_METHOD1(read, void (Nif::NIFStream *nif));
|
||||
MOCK_METHOD(void, read, (Nif::NIFStream *nif), (override));
|
||||
};
|
||||
|
||||
struct TestBulletNifLoader : Test
|
||||
|
|
|
@ -151,7 +151,13 @@ add_component_dir (fallback
|
|||
fallback validate
|
||||
)
|
||||
|
||||
if(NOT WIN32 AND NOT ANDROID)
|
||||
if(WIN32)
|
||||
add_component_dir (crashcatcher
|
||||
windows_crashcatcher
|
||||
windows_crashmonitor
|
||||
windows_crashshm
|
||||
)
|
||||
elseif(NOT ANDROID)
|
||||
add_component_dir (crashcatcher
|
||||
crashcatcher
|
||||
)
|
||||
|
|
205
components/crashcatcher/windows_crashcatcher.cpp
Normal file
205
components/crashcatcher/windows_crashcatcher.cpp
Normal file
|
@ -0,0 +1,205 @@
|
|||
#include <cassert>
|
||||
#include <cwchar>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
#include "windows_crashcatcher.hpp"
|
||||
#include "windows_crashmonitor.hpp"
|
||||
#include "windows_crashshm.hpp"
|
||||
#include <SDL_messagebox.h>
|
||||
|
||||
namespace Crash
|
||||
{
|
||||
|
||||
HANDLE duplicateHandle(HANDLE handle)
|
||||
{
|
||||
HANDLE duplicate;
|
||||
if (!DuplicateHandle(GetCurrentProcess(), handle,
|
||||
GetCurrentProcess(), &duplicate,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS))
|
||||
{
|
||||
throw std::runtime_error("Crash monitor could not duplicate handle");
|
||||
}
|
||||
return duplicate;
|
||||
}
|
||||
|
||||
CrashCatcher* CrashCatcher::sInstance = nullptr;
|
||||
|
||||
CrashCatcher::CrashCatcher(int argc, char **argv, const std::string& crashLogPath)
|
||||
{
|
||||
assert(sInstance == nullptr); // don't allow two instances
|
||||
|
||||
sInstance = this;
|
||||
|
||||
HANDLE shmHandle = nullptr;
|
||||
for (int i=0; i<argc; ++i)
|
||||
{
|
||||
if (strcmp(argv[i], "--crash-monitor"))
|
||||
continue;
|
||||
|
||||
if (i >= argc - 1)
|
||||
throw std::runtime_error("Crash monitor is missing the SHM handle argument");
|
||||
|
||||
sscanf(argv[i + 1], "%p", &shmHandle);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!shmHandle)
|
||||
{
|
||||
setupIpc();
|
||||
startMonitorProcess(crashLogPath);
|
||||
installHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
CrashMonitor(shmHandle).run();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
CrashCatcher::~CrashCatcher()
|
||||
{
|
||||
sInstance = nullptr;
|
||||
|
||||
if (mShm && mSignalMonitorEvent)
|
||||
{
|
||||
shmLock();
|
||||
mShm->mEvent = CrashSHM::Event::Shutdown;
|
||||
shmUnlock();
|
||||
|
||||
SetEvent(mSignalMonitorEvent);
|
||||
}
|
||||
|
||||
if (mShmHandle)
|
||||
CloseHandle(mShmHandle);
|
||||
}
|
||||
|
||||
void CrashCatcher::setupIpc()
|
||||
{
|
||||
SECURITY_ATTRIBUTES attributes;
|
||||
ZeroMemory(&attributes, sizeof(attributes));
|
||||
attributes.bInheritHandle = TRUE;
|
||||
|
||||
mSignalAppEvent = CreateEventW(&attributes, FALSE, FALSE, NULL);
|
||||
mSignalMonitorEvent = CreateEventW(&attributes, FALSE, FALSE, NULL);
|
||||
|
||||
mShmHandle = CreateFileMappingW(INVALID_HANDLE_VALUE, &attributes, PAGE_READWRITE, HIWORD(sizeof(CrashSHM)), LOWORD(sizeof(CrashSHM)), NULL);
|
||||
if (mShmHandle == nullptr)
|
||||
throw std::runtime_error("Failed to allocate crash catcher shared memory");
|
||||
|
||||
mShm = reinterpret_cast<CrashSHM*>(MapViewOfFile(mShmHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashSHM)));
|
||||
if (mShm == nullptr)
|
||||
throw std::runtime_error("Failed to map crash catcher shared memory");
|
||||
|
||||
mShmMutex = CreateMutexW(&attributes, FALSE, NULL);
|
||||
if (mShmMutex == nullptr)
|
||||
throw std::runtime_error("Failed to create crash catcher shared memory mutex");
|
||||
}
|
||||
|
||||
void CrashCatcher::shmLock()
|
||||
{
|
||||
if (WaitForSingleObject(mShmMutex, CrashCatcherTimeout) != WAIT_OBJECT_0)
|
||||
throw std::runtime_error("SHM lock timed out");
|
||||
}
|
||||
|
||||
void CrashCatcher::shmUnlock()
|
||||
{
|
||||
ReleaseMutex(mShmMutex);
|
||||
}
|
||||
|
||||
void CrashCatcher::waitMonitor()
|
||||
{
|
||||
if (WaitForSingleObject(mSignalAppEvent, CrashCatcherTimeout) != WAIT_OBJECT_0)
|
||||
throw std::runtime_error("Waiting for monitor failed");
|
||||
}
|
||||
|
||||
void CrashCatcher::signalMonitor()
|
||||
{
|
||||
SetEvent(mSignalMonitorEvent);
|
||||
}
|
||||
|
||||
void CrashCatcher::installHandler()
|
||||
{
|
||||
SetUnhandledExceptionFilter(vectoredExceptionHandler);
|
||||
}
|
||||
|
||||
void CrashCatcher::startMonitorProcess(const std::string& crashLogPath)
|
||||
{
|
||||
std::wstring executablePath;
|
||||
DWORD copied = 0;
|
||||
do {
|
||||
executablePath.resize(executablePath.size() + MAX_PATH);
|
||||
copied = GetModuleFileNameW(nullptr, executablePath.data(), executablePath.size());
|
||||
} while (copied >= executablePath.size());
|
||||
executablePath.resize(copied);
|
||||
|
||||
memset(mShm->mStartup.mLogFilePath, 0, sizeof(mShm->mStartup.mLogFilePath));
|
||||
int length = crashLogPath.length();
|
||||
if (length > MAX_LONG_PATH) length = MAX_LONG_PATH;
|
||||
strncpy(mShm->mStartup.mLogFilePath, crashLogPath.c_str(), length);
|
||||
mShm->mStartup.mLogFilePath[length] = '\0';
|
||||
|
||||
// note that we don't need to lock the SHM here, the other process has not started yet
|
||||
mShm->mEvent = CrashSHM::Event::Startup;
|
||||
mShm->mStartup.mShmMutex = duplicateHandle(mShmMutex);
|
||||
mShm->mStartup.mAppProcessHandle = duplicateHandle(GetCurrentProcess());
|
||||
mShm->mStartup.mSignalApp = duplicateHandle(mSignalAppEvent);
|
||||
mShm->mStartup.mSignalMonitor = duplicateHandle(mSignalMonitorEvent);
|
||||
|
||||
std::wstringstream ss;
|
||||
ss << "--crash-monitor " << std::hex << duplicateHandle(mShmHandle);
|
||||
std::wstring arguments(ss.str());
|
||||
|
||||
STARTUPINFOW si;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
if (!CreateProcessW(executablePath.data(), arguments.data(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
|
||||
throw std::runtime_error("Could not start crash monitor process");
|
||||
|
||||
waitMonitor();
|
||||
}
|
||||
|
||||
LONG CrashCatcher::vectoredExceptionHandler(PEXCEPTION_POINTERS info)
|
||||
{
|
||||
switch (info->ExceptionRecord->ExceptionCode)
|
||||
{
|
||||
case EXCEPTION_SINGLE_STEP:
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
case DBG_PRINTEXCEPTION_C:
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
if (!sInstance)
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
|
||||
sInstance->handleVectoredException(info);
|
||||
|
||||
_Exit(1);
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
void CrashCatcher::handleVectoredException(PEXCEPTION_POINTERS info)
|
||||
{
|
||||
shmLock();
|
||||
|
||||
mShm->mEvent = CrashSHM::Event::Crashed;
|
||||
mShm->mCrashed.mThreadId = GetCurrentThreadId();
|
||||
mShm->mCrashed.mContext = *info->ContextRecord;
|
||||
mShm->mCrashed.mExceptionRecord = *info->ExceptionRecord;
|
||||
|
||||
shmUnlock();
|
||||
|
||||
signalMonitor();
|
||||
|
||||
// must remain until monitor has finished
|
||||
waitMonitor();
|
||||
|
||||
std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(mShm->mStartup.mLogFilePath) + "'.\n Please report this to https://gitlab.com/OpenMW/openmw/issues !";
|
||||
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr);
|
||||
}
|
||||
|
||||
} // namespace Crash
|
79
components/crashcatcher/windows_crashcatcher.hpp
Normal file
79
components/crashcatcher/windows_crashcatcher.hpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
#ifndef WINDOWS_CRASHCATCHER_HPP
|
||||
#define WINDOWS_CRASHCATCHER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#include <components/crashcatcher/crashcatcher.hpp>
|
||||
|
||||
namespace Crash
|
||||
{
|
||||
|
||||
// The implementation spawns the current executable as a monitor process which waits
|
||||
// for a global synchronization event which is sent when the parent process crashes.
|
||||
// The monitor process then extracts crash information from the parent process while
|
||||
// the parent process waits for the monitor process to finish. The crashed process
|
||||
// quits and the monitor writes the crash information to a file.
|
||||
//
|
||||
// To detect unexpected shutdowns of the application which are not handled by the
|
||||
// crash handler, the monitor periodically checks the exit code of the parent
|
||||
// process and exits if it does not return STILL_ACTIVE. You can test this by closing
|
||||
// the main openmw process in task manager.
|
||||
|
||||
static constexpr const int CrashCatcherTimeout = 2500;
|
||||
|
||||
struct CrashSHM;
|
||||
|
||||
class CrashCatcher final
|
||||
{
|
||||
public:
|
||||
|
||||
CrashCatcher(int argc, char **argv, const std::string& crashLogPath);
|
||||
~CrashCatcher();
|
||||
|
||||
private:
|
||||
|
||||
static CrashCatcher* sInstance;
|
||||
|
||||
// mapped SHM area
|
||||
CrashSHM* mShm = nullptr;
|
||||
// the handle is allocated by the catcher and passed to the monitor
|
||||
// process via the command line which maps the SHM and sends / receives
|
||||
// events through it
|
||||
HANDLE mShmHandle = nullptr;
|
||||
// mutex which guards SHM area
|
||||
HANDLE mShmMutex = nullptr;
|
||||
|
||||
// triggered when the monitor signals the application
|
||||
HANDLE mSignalAppEvent = INVALID_HANDLE_VALUE;
|
||||
|
||||
// triggered when the application wants to wake the monitor process
|
||||
HANDLE mSignalMonitorEvent = INVALID_HANDLE_VALUE;
|
||||
|
||||
void setupIpc();
|
||||
|
||||
void shmLock();
|
||||
|
||||
void shmUnlock();
|
||||
|
||||
void startMonitorProcess(const std::string& crashLogPath);
|
||||
|
||||
void waitMonitor();
|
||||
|
||||
void signalMonitor();
|
||||
|
||||
void installHandler();
|
||||
|
||||
void handleVectoredException(PEXCEPTION_POINTERS info);
|
||||
|
||||
public:
|
||||
|
||||
static LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS info);
|
||||
};
|
||||
|
||||
} // namespace Crash
|
||||
|
||||
#endif // WINDOWS_CRASHCATCHER_HPP
|
188
components/crashcatcher/windows_crashmonitor.cpp
Normal file
188
components/crashcatcher/windows_crashmonitor.cpp
Normal file
|
@ -0,0 +1,188 @@
|
|||
#undef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <Psapi.h>
|
||||
|
||||
#include <DbgHelp.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "windows_crashcatcher.hpp"
|
||||
#include "windows_crashmonitor.hpp"
|
||||
#include "windows_crashshm.hpp"
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
namespace Crash
|
||||
{
|
||||
|
||||
CrashMonitor::CrashMonitor(HANDLE shmHandle)
|
||||
: mShmHandle(shmHandle)
|
||||
{
|
||||
mShm = reinterpret_cast<CrashSHM*>(MapViewOfFile(mShmHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashSHM)));
|
||||
if (mShm == nullptr)
|
||||
throw std::runtime_error("Failed to map crash monitor shared memory");
|
||||
|
||||
// accessing SHM without lock is OK here, the parent waits for a signal before continuing
|
||||
|
||||
mShmMutex = mShm->mStartup.mShmMutex;
|
||||
mAppProcessHandle = mShm->mStartup.mAppProcessHandle;
|
||||
mSignalAppEvent = mShm->mStartup.mSignalApp;
|
||||
mSignalMonitorEvent = mShm->mStartup.mSignalMonitor;
|
||||
}
|
||||
|
||||
CrashMonitor::~CrashMonitor()
|
||||
{
|
||||
if (mShm)
|
||||
UnmapViewOfFile(mShm);
|
||||
|
||||
// the handles received from the app are duplicates, we must close them
|
||||
|
||||
if (mShmHandle)
|
||||
CloseHandle(mShmHandle);
|
||||
|
||||
if (mShmMutex)
|
||||
CloseHandle(mShmMutex);
|
||||
|
||||
if (mSignalAppEvent)
|
||||
CloseHandle(mSignalAppEvent);
|
||||
|
||||
if (mSignalMonitorEvent)
|
||||
CloseHandle(mSignalMonitorEvent);
|
||||
}
|
||||
|
||||
void CrashMonitor::shmLock()
|
||||
{
|
||||
if (WaitForSingleObject(mShmMutex, CrashCatcherTimeout) != WAIT_OBJECT_0)
|
||||
throw std::runtime_error("SHM monitor lock timed out");
|
||||
}
|
||||
|
||||
void CrashMonitor::shmUnlock()
|
||||
{
|
||||
ReleaseMutex(mShmMutex);
|
||||
}
|
||||
|
||||
void CrashMonitor::signalApp() const
|
||||
{
|
||||
SetEvent(mSignalAppEvent);
|
||||
}
|
||||
|
||||
bool CrashMonitor::waitApp() const
|
||||
{
|
||||
return WaitForSingleObject(mSignalMonitorEvent, CrashCatcherTimeout) == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
bool CrashMonitor::isAppAlive() const
|
||||
{
|
||||
DWORD code = 0;
|
||||
GetExitCodeProcess(mAppProcessHandle, &code);
|
||||
return code == STILL_ACTIVE;
|
||||
}
|
||||
|
||||
void CrashMonitor::run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// app waits for monitor start up, let it continue
|
||||
signalApp();
|
||||
|
||||
bool running = true;
|
||||
while (isAppAlive() && running)
|
||||
{
|
||||
if (waitApp())
|
||||
{
|
||||
shmLock();
|
||||
|
||||
switch (mShm->mEvent)
|
||||
{
|
||||
case CrashSHM::Event::None:
|
||||
break;
|
||||
case CrashSHM::Event::Crashed:
|
||||
handleCrash();
|
||||
running = false;
|
||||
break;
|
||||
case CrashSHM::Event::Shutdown:
|
||||
running = false;
|
||||
break;
|
||||
case CrashSHM::Event::Startup:
|
||||
break;
|
||||
}
|
||||
|
||||
shmUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Log(Debug::Error) << "Exception in crash monitor, exiting";
|
||||
}
|
||||
signalApp();
|
||||
}
|
||||
|
||||
std::wstring utf8ToUtf16(const std::string& utf8)
|
||||
{
|
||||
const int nLenWide = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size(), nullptr, 0);
|
||||
|
||||
std::wstring utf16;
|
||||
utf16.resize(nLenWide);
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size(), utf16.data(), nLenWide) != nLenWide)
|
||||
return {};
|
||||
|
||||
return utf16;
|
||||
}
|
||||
|
||||
void CrashMonitor::handleCrash()
|
||||
{
|
||||
DWORD processId = GetProcessId(mAppProcessHandle);
|
||||
|
||||
try
|
||||
{
|
||||
HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
|
||||
if (dbghelp == NULL)
|
||||
return;
|
||||
|
||||
using MiniDumpWirteDumpFn = BOOL (WINAPI*)(
|
||||
HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
||||
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
||||
);
|
||||
|
||||
MiniDumpWirteDumpFn miniDumpWriteDump = (MiniDumpWirteDumpFn)GetProcAddress(dbghelp, "MiniDumpWriteDump");
|
||||
if (miniDumpWriteDump == NULL)
|
||||
return;
|
||||
|
||||
std::wstring utf16Path = utf8ToUtf16(mShm->mStartup.mLogFilePath);
|
||||
if (utf16Path.empty())
|
||||
return;
|
||||
|
||||
if (utf16Path.length() > MAX_PATH)
|
||||
utf16Path = LR"(\\?\)" + utf16Path;
|
||||
|
||||
HANDLE hCrashLog = CreateFileW(utf16Path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hCrashLog == NULL || hCrashLog == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
if (auto err = GetLastError(); err != ERROR_ALREADY_EXISTS && err != 0)
|
||||
return;
|
||||
|
||||
EXCEPTION_POINTERS exp;
|
||||
exp.ContextRecord = &mShm->mCrashed.mContext;
|
||||
exp.ExceptionRecord = &mShm->mCrashed.mExceptionRecord;
|
||||
MINIDUMP_EXCEPTION_INFORMATION infos = {};
|
||||
infos.ThreadId = mShm->mCrashed.mThreadId;
|
||||
infos.ExceptionPointers = &exp;
|
||||
infos.ClientPointers = FALSE;
|
||||
MINIDUMP_TYPE type = (MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithHandleData);
|
||||
miniDumpWriteDump(mAppProcessHandle, processId, hCrashLog, type, &infos, 0, 0);
|
||||
}
|
||||
catch (const std::exception&e)
|
||||
{
|
||||
Log(Debug::Error) << "CrashMonitor: " << e.what();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Log(Debug::Error) << "CrashMonitor: unknown exception";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Crash
|
49
components/crashcatcher/windows_crashmonitor.hpp
Normal file
49
components/crashcatcher/windows_crashmonitor.hpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifndef WINDOWS_CRASHMONITOR_HPP
|
||||
#define WINDOWS_CRASHMONITOR_HPP
|
||||
|
||||
#include <windef.h>
|
||||
|
||||
namespace Crash
|
||||
{
|
||||
|
||||
struct CrashSHM;
|
||||
|
||||
class CrashMonitor final
|
||||
{
|
||||
public:
|
||||
|
||||
CrashMonitor(HANDLE shmHandle);
|
||||
|
||||
~CrashMonitor();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
|
||||
HANDLE mAppProcessHandle = nullptr;
|
||||
|
||||
// triggered when the monitor process wants to wake the parent process (received via SHM)
|
||||
HANDLE mSignalAppEvent = nullptr;
|
||||
// triggered when the application wants to wake the monitor process (received via SHM)
|
||||
HANDLE mSignalMonitorEvent = nullptr;
|
||||
|
||||
CrashSHM* mShm = nullptr;
|
||||
HANDLE mShmHandle = nullptr;
|
||||
HANDLE mShmMutex = nullptr;
|
||||
|
||||
void signalApp() const;
|
||||
|
||||
bool waitApp() const;
|
||||
|
||||
bool isAppAlive() const;
|
||||
|
||||
void shmLock();
|
||||
|
||||
void shmUnlock();
|
||||
|
||||
void handleCrash();
|
||||
};
|
||||
|
||||
} // namespace Crash
|
||||
|
||||
#endif // WINDOWS_CRASHMONITOR_HPP
|
45
components/crashcatcher/windows_crashshm.hpp
Normal file
45
components/crashcatcher/windows_crashshm.hpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifndef WINDOWS_CRASHSHM_HPP
|
||||
#define WINDOWS_CRASHSHM_HPP
|
||||
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
namespace Crash
|
||||
{
|
||||
|
||||
// Used to communicate between the app and the monitor, fields are is overwritten with each event.
|
||||
static constexpr const int MAX_LONG_PATH = 0x7fff;
|
||||
|
||||
struct CrashSHM
|
||||
{
|
||||
enum class Event
|
||||
{
|
||||
None,
|
||||
Startup,
|
||||
Crashed,
|
||||
Shutdown
|
||||
};
|
||||
|
||||
Event mEvent;
|
||||
|
||||
struct Startup
|
||||
{
|
||||
HANDLE mAppProcessHandle;
|
||||
HANDLE mSignalApp;
|
||||
HANDLE mSignalMonitor;
|
||||
HANDLE mShmMutex;
|
||||
char mLogFilePath[MAX_LONG_PATH];
|
||||
} mStartup;
|
||||
|
||||
struct Crashed
|
||||
{
|
||||
DWORD mThreadId;
|
||||
CONTEXT mContext;
|
||||
EXCEPTION_RECORD mExceptionRecord;
|
||||
} mCrashed;
|
||||
};
|
||||
|
||||
} // namespace Crash
|
||||
|
||||
#endif // WINDOWS_CRASHSHM_HPP
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#include <components/crashcatcher/crashcatcher.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <components/crashcatcher/windows_crashcatcher.hpp>
|
||||
# undef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
|
@ -163,7 +165,6 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c
|
|||
#endif
|
||||
|
||||
const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log";
|
||||
const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.log";
|
||||
boost::filesystem::ofstream logfile;
|
||||
|
||||
int ret = 0;
|
||||
|
@ -187,13 +188,18 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c
|
|||
std::cerr.rdbuf (&cerrsb);
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.dmp";
|
||||
Crash::CrashCatcher crashy(argc, argv, (cfgMgr.getLogPath() / crashLogName).make_preferred().string());
|
||||
#else
|
||||
const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.log";
|
||||
// install the crash handler as soon as possible. note that the log path
|
||||
// does not depend on config being read.
|
||||
crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / crashLogName).string());
|
||||
|
||||
#endif
|
||||
ret = innerApplication(argc, argv);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))
|
||||
if (!isatty(fileno(stdin)))
|
||||
|
|
|
@ -1245,9 +1245,12 @@ namespace NifOsg
|
|||
void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, int animflags)
|
||||
{
|
||||
assert(isTypeGeometry(nifNode->recType));
|
||||
osg::ref_ptr<osg::Drawable> drawable;
|
||||
osg::ref_ptr<osg::Geometry> geom (new osg::Geometry);
|
||||
handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags);
|
||||
// If the record had no valid geometry data in it, early-out
|
||||
if (geom->empty())
|
||||
return;
|
||||
osg::ref_ptr<osg::Drawable> drawable;
|
||||
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
|
||||
{
|
||||
if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))
|
||||
|
@ -1292,6 +1295,8 @@ namespace NifOsg
|
|||
assert(isTypeGeometry(nifNode->recType));
|
||||
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
|
||||
handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags);
|
||||
if (geometry->empty())
|
||||
return;
|
||||
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);
|
||||
rig->setSourceGeometry(geometry);
|
||||
rig->setName(nifNode->name);
|
||||
|
|
|
@ -26,7 +26,7 @@ Now Fonts folder should include ``openmw_font.xml`` file and three ``.ttf`` file
|
|||
|
||||
If desired, you can now delete the ``Data Files/Fonts`` directory.
|
||||
|
||||
It is also possible to adjust the font size and resolution::
|
||||
It is also possible to adjust the font size and resolution via ``settings.cfg`` file::
|
||||
|
||||
[GUI]
|
||||
font size = 16
|
||||
|
|
|
@ -23,6 +23,7 @@ set(SHADER_FILES
|
|||
shadows_fragment.glsl
|
||||
shadowcasting_vertex.glsl
|
||||
shadowcasting_fragment.glsl
|
||||
vertexcolors.glsl
|
||||
)
|
||||
|
||||
copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}")
|
||||
|
|
|
@ -1,85 +1,38 @@
|
|||
#define MAX_LIGHTS 8
|
||||
|
||||
uniform int colorMode;
|
||||
|
||||
const int ColorMode_None = 0;
|
||||
const int ColorMode_Emission = 1;
|
||||
const int ColorMode_AmbientAndDiffuse = 2;
|
||||
const int ColorMode_Ambient = 3;
|
||||
const int ColorMode_Diffuse = 4;
|
||||
const int ColorMode_Specular = 5;
|
||||
|
||||
void perLight(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal, vec4 diffuse, vec3 ambient)
|
||||
void perLight(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal)
|
||||
{
|
||||
vec3 lightDir;
|
||||
float lightDistance;
|
||||
|
||||
lightDir = gl_LightSource[lightIndex].position.xyz - (viewPos.xyz * gl_LightSource[lightIndex].position.w);
|
||||
lightDistance = length(lightDir);
|
||||
vec3 lightDir = gl_LightSource[lightIndex].position.xyz - viewPos * gl_LightSource[lightIndex].position.w;
|
||||
float lightDistance = length(lightDir);
|
||||
lightDir = normalize(lightDir);
|
||||
float illumination = clamp(1.0 / (gl_LightSource[lightIndex].constantAttenuation + gl_LightSource[lightIndex].linearAttenuation * lightDistance + gl_LightSource[lightIndex].quadraticAttenuation * lightDistance * lightDistance), 0.0, 1.0);
|
||||
|
||||
ambientOut = ambient * gl_LightSource[lightIndex].ambient.xyz * illumination;
|
||||
diffuseOut = diffuse.xyz * gl_LightSource[lightIndex].diffuse.xyz * max(dot(viewNormal.xyz, lightDir), 0.0) * illumination;
|
||||
ambientOut = gl_LightSource[lightIndex].ambient.xyz * illumination;
|
||||
diffuseOut = gl_LightSource[lightIndex].diffuse.xyz * max(dot(viewNormal, lightDir), 0.0) * illumination;
|
||||
}
|
||||
|
||||
#if PER_PIXEL_LIGHTING
|
||||
vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, float shadowing)
|
||||
void doLighting(vec3 viewPos, vec3 viewNormal, float shadowing, out vec3 diffuseLight, out vec3 ambientLight)
|
||||
#else
|
||||
vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, out vec3 shadowDiffuse)
|
||||
void doLighting(vec3 viewPos, vec3 viewNormal, out vec3 diffuseLight, out vec3 ambientLight, out vec3 shadowDiffuse)
|
||||
#endif
|
||||
{
|
||||
vec4 diffuse;
|
||||
vec3 ambient;
|
||||
if (colorMode == ColorMode_AmbientAndDiffuse)
|
||||
{
|
||||
diffuse = vertexColor;
|
||||
ambient = vertexColor.xyz;
|
||||
}
|
||||
else if (colorMode == ColorMode_Diffuse)
|
||||
{
|
||||
diffuse = vertexColor;
|
||||
ambient = gl_FrontMaterial.ambient.xyz;
|
||||
}
|
||||
else if (colorMode == ColorMode_Ambient)
|
||||
{
|
||||
diffuse = gl_FrontMaterial.diffuse;
|
||||
ambient = vertexColor.xyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
diffuse = gl_FrontMaterial.diffuse;
|
||||
ambient = gl_FrontMaterial.ambient.xyz;
|
||||
}
|
||||
vec4 lightResult = vec4(0.0, 0.0, 0.0, diffuse.a);
|
||||
|
||||
vec3 diffuseLight, ambientLight;
|
||||
perLight(ambientLight, diffuseLight, 0, viewPos, viewNormal, diffuse, ambient);
|
||||
vec3 ambientOut, diffuseOut;
|
||||
// This light gets added a second time in the loop to fix Mesa users' slowdown, so we need to negate its contribution here.
|
||||
perLight(ambientOut, diffuseOut, 0, viewPos, viewNormal);
|
||||
#if PER_PIXEL_LIGHTING
|
||||
lightResult.xyz += diffuseLight * shadowing - diffuseLight; // This light gets added a second time in the loop to fix Mesa users' slowdown, so we need to negate its contribution here.
|
||||
diffuseLight = diffuseOut * shadowing - diffuseOut;
|
||||
#else
|
||||
shadowDiffuse = diffuseLight;
|
||||
lightResult.xyz -= shadowDiffuse; // This light gets added a second time in the loop to fix Mesa users' slowdown, so we need to negate its contribution here.
|
||||
shadowDiffuse = diffuseOut;
|
||||
diffuseLight = -diffuseOut;
|
||||
#endif
|
||||
ambientLight = gl_LightModel.ambient.xyz;
|
||||
for (int i=0; i<MAX_LIGHTS; ++i)
|
||||
{
|
||||
perLight(ambientLight, diffuseLight, i, viewPos, viewNormal, diffuse, ambient);
|
||||
lightResult.xyz += ambientLight + diffuseLight;
|
||||
perLight(ambientOut, diffuseOut, i, viewPos, viewNormal);
|
||||
ambientLight += ambientOut;
|
||||
diffuseLight += diffuseOut;
|
||||
}
|
||||
|
||||
lightResult.xyz += gl_LightModel.ambient.xyz * ambient;
|
||||
|
||||
if (colorMode == ColorMode_Emission)
|
||||
lightResult.xyz += vertexColor.xyz;
|
||||
else
|
||||
lightResult.xyz += gl_FrontMaterial.emission.xyz;
|
||||
|
||||
#if @clamp
|
||||
lightResult = clamp(lightResult, vec4(0.0), vec4(1.0));
|
||||
#else
|
||||
lightResult = max(lightResult, 0.0);
|
||||
#endif
|
||||
return lightResult;
|
||||
}
|
||||
|
||||
|
||||
|
@ -88,7 +41,7 @@ vec3 getSpecular(vec3 viewNormal, vec3 viewDirection, float shininess, vec3 matS
|
|||
vec3 lightDir = normalize(gl_LightSource[0].position.xyz);
|
||||
float NdotL = dot(viewNormal, lightDir);
|
||||
if (NdotL <= 0.0)
|
||||
return vec3(0.,0.,0.);
|
||||
return vec3(0.0);
|
||||
vec3 halfVec = normalize(lightDir - viewDirection);
|
||||
float NdotH = dot(viewNormal, halfVec);
|
||||
return pow(max(NdotH, 0.0), max(1e-4, shininess)) * gl_LightSource[0].specular.xyz * matSpec;
|
||||
|
|
|
@ -57,13 +57,13 @@ varying float linearDepth;
|
|||
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
centroid varying vec4 lighting;
|
||||
centroid varying vec3 passLighting;
|
||||
centroid varying vec3 shadowDiffuseLighting;
|
||||
#endif
|
||||
centroid varying vec4 passColor;
|
||||
varying vec3 passViewPos;
|
||||
varying vec3 passNormal;
|
||||
|
||||
#include "vertexcolors.glsl"
|
||||
#include "shadows_fragment.glsl"
|
||||
#include "lighting.glsl"
|
||||
#include "parallax.glsl"
|
||||
|
@ -152,21 +152,27 @@ void main()
|
|||
|
||||
#endif
|
||||
|
||||
float shadowing = unshadowedLightRatio(linearDepth);
|
||||
vec4 diffuseColor = getDiffuseColor();
|
||||
gl_FragData[0].a *= diffuseColor.a;
|
||||
alphaTest();
|
||||
|
||||
float shadowing = unshadowedLightRatio(linearDepth);
|
||||
vec3 lighting;
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
lighting = passLighting + shadowDiffuseLighting * shadowing;
|
||||
#else
|
||||
vec3 diffuseLight, ambientLight;
|
||||
doLighting(passViewPos, normalize(viewNormal), shadowing, diffuseLight, ambientLight);
|
||||
lighting = diffuseColor.xyz * diffuseLight + getAmbientColor().xyz * ambientLight + getEmissionColor().xyz;
|
||||
#endif
|
||||
|
||||
#if @clamp
|
||||
gl_FragData[0] *= clamp(lighting + vec4(shadowDiffuseLighting * shadowing, 0), vec4(0.0), vec4(1.0));
|
||||
lighting = clamp(lighting, vec3(0.0), vec3(1.0));
|
||||
#else
|
||||
gl_FragData[0] *= lighting + vec4(shadowDiffuseLighting * shadowing, 0);
|
||||
lighting = max(lighting, 0.0);
|
||||
#endif
|
||||
|
||||
#else
|
||||
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor, shadowing);
|
||||
#endif
|
||||
|
||||
alphaTest();
|
||||
gl_FragData[0].xyz *= lighting;
|
||||
|
||||
#if @envMap && !@preLightEnv
|
||||
gl_FragData[0].xyz += texture2D(envMap, envTexCoordGen).xyz * envMapColor.xyz * envLuma;
|
||||
|
@ -182,11 +188,7 @@ void main()
|
|||
vec3 matSpec = specTex.xyz;
|
||||
#else
|
||||
float shininess = gl_FrontMaterial.shininess;
|
||||
vec3 matSpec;
|
||||
if (colorMode == ColorMode_Specular)
|
||||
matSpec = passColor.xyz;
|
||||
else
|
||||
matSpec = gl_FrontMaterial.specular.xyz;
|
||||
vec3 matSpec = getSpecularColor().xyz;
|
||||
#endif
|
||||
|
||||
if (matSpec != vec3(0.0))
|
||||
|
|
|
@ -43,13 +43,13 @@ varying float linearDepth;
|
|||
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
centroid varying vec4 lighting;
|
||||
centroid varying vec3 passLighting;
|
||||
centroid varying vec3 shadowDiffuseLighting;
|
||||
#endif
|
||||
centroid varying vec4 passColor;
|
||||
varying vec3 passViewPos;
|
||||
varying vec3 passNormal;
|
||||
|
||||
#include "vertexcolors.glsl"
|
||||
#include "shadows_vertex.glsl"
|
||||
|
||||
#include "lighting.glsl"
|
||||
|
@ -107,13 +107,17 @@ void main(void)
|
|||
specularMapUV = (gl_TextureMatrix[@specularMapUV] * gl_MultiTexCoord@specularMapUV).xy;
|
||||
#endif
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting);
|
||||
#endif
|
||||
passColor = gl_Color;
|
||||
passViewPos = viewPos.xyz;
|
||||
passNormal = gl_Normal.xyz;
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
vec3 diffuseLight, ambientLight;
|
||||
doLighting(viewPos.xyz, viewNormal, diffuseLight, ambientLight, shadowDiffuseLighting);
|
||||
passLighting = getDiffuseColor().xyz * diffuseLight + getAmbientColor().xyz * ambientLight + getEmissionColor().xyz;
|
||||
shadowDiffuseLighting *= getDiffuseColor().xyz;
|
||||
#endif
|
||||
|
||||
#if (@shadows_enabled)
|
||||
setupShadowCoords(viewPos, viewNormal);
|
||||
#endif
|
||||
|
|
|
@ -18,13 +18,13 @@ varying float linearDepth;
|
|||
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
centroid varying vec4 lighting;
|
||||
centroid varying vec3 passLighting;
|
||||
centroid varying vec3 shadowDiffuseLighting;
|
||||
#endif
|
||||
centroid varying vec4 passColor;
|
||||
varying vec3 passViewPos;
|
||||
varying vec3 passNormal;
|
||||
|
||||
#include "vertexcolors.glsl"
|
||||
#include "shadows_fragment.glsl"
|
||||
#include "lighting.glsl"
|
||||
#include "parallax.glsl"
|
||||
|
@ -68,30 +68,33 @@ void main()
|
|||
gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a;
|
||||
#endif
|
||||
|
||||
float shadowing = unshadowedLightRatio(linearDepth);
|
||||
vec4 diffuseColor = getDiffuseColor();
|
||||
gl_FragData[0].a *= diffuseColor.a;
|
||||
|
||||
float shadowing = unshadowedLightRatio(linearDepth);
|
||||
vec3 lighting;
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
lighting = passLighting + shadowDiffuseLighting * shadowing;
|
||||
#else
|
||||
vec3 diffuseLight, ambientLight;
|
||||
doLighting(passViewPos, normalize(viewNormal), shadowing, diffuseLight, ambientLight);
|
||||
lighting = diffuseColor.xyz * diffuseLight + getAmbientColor().xyz * ambientLight + getEmissionColor().xyz;
|
||||
#endif
|
||||
|
||||
#if @clamp
|
||||
gl_FragData[0] *= clamp(lighting + vec4(shadowDiffuseLighting * shadowing, 0), vec4(0.0), vec4(1.0));
|
||||
lighting = clamp(lighting, vec3(0.0), vec3(1.0));
|
||||
#else
|
||||
gl_FragData[0] *= lighting + vec4(shadowDiffuseLighting * shadowing, 0);
|
||||
lighting = max(lighting, 0.0);
|
||||
#endif
|
||||
|
||||
#else
|
||||
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor, shadowing);
|
||||
#endif
|
||||
gl_FragData[0].xyz *= lighting;
|
||||
|
||||
#if @specularMap
|
||||
float shininess = 128.0; // TODO: make configurable
|
||||
vec3 matSpec = vec3(diffuseTex.a);
|
||||
#else
|
||||
float shininess = gl_FrontMaterial.shininess;
|
||||
vec3 matSpec;
|
||||
if (colorMode == ColorMode_Specular)
|
||||
matSpec = passColor.xyz;
|
||||
else
|
||||
matSpec = gl_FrontMaterial.specular.xyz;
|
||||
vec3 matSpec = getSpecularColor().xyz;
|
||||
#endif
|
||||
|
||||
if (matSpec != vec3(0.0))
|
||||
|
|
|
@ -7,13 +7,13 @@ varying float linearDepth;
|
|||
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
centroid varying vec4 lighting;
|
||||
centroid varying vec3 passLighting;
|
||||
centroid varying vec3 shadowDiffuseLighting;
|
||||
#endif
|
||||
centroid varying vec4 passColor;
|
||||
varying vec3 passViewPos;
|
||||
varying vec3 passNormal;
|
||||
|
||||
#include "vertexcolors.glsl"
|
||||
#include "shadows_vertex.glsl"
|
||||
|
||||
#include "lighting.glsl"
|
||||
|
@ -31,13 +31,17 @@ void main(void)
|
|||
vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);
|
||||
#endif
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting);
|
||||
#endif
|
||||
passColor = gl_Color;
|
||||
passNormal = gl_Normal.xyz;
|
||||
passViewPos = viewPos.xyz;
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
vec3 diffuseLight, ambientLight;
|
||||
doLighting(viewPos.xyz, viewNormal, diffuseLight, ambientLight, shadowDiffuseLighting);
|
||||
passLighting = getDiffuseColor().xyz * diffuseLight + getAmbientColor().xyz * ambientLight + getEmissionColor().xyz;
|
||||
shadowDiffuseLighting *= getDiffuseColor().xyz;
|
||||
#endif
|
||||
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
|
||||
#if (@shadows_enabled)
|
||||
|
|
38
files/shaders/vertexcolors.glsl
Normal file
38
files/shaders/vertexcolors.glsl
Normal file
|
@ -0,0 +1,38 @@
|
|||
centroid varying vec4 passColor;
|
||||
|
||||
uniform int colorMode;
|
||||
|
||||
const int ColorMode_None = 0;
|
||||
const int ColorMode_Emission = 1;
|
||||
const int ColorMode_AmbientAndDiffuse = 2;
|
||||
const int ColorMode_Ambient = 3;
|
||||
const int ColorMode_Diffuse = 4;
|
||||
const int ColorMode_Specular = 5;
|
||||
|
||||
vec4 getEmissionColor()
|
||||
{
|
||||
if (colorMode == ColorMode_Emission)
|
||||
return passColor;
|
||||
return gl_FrontMaterial.emission;
|
||||
}
|
||||
|
||||
vec4 getAmbientColor()
|
||||
{
|
||||
if (colorMode == ColorMode_AmbientAndDiffuse || colorMode == ColorMode_Ambient)
|
||||
return passColor;
|
||||
return gl_FrontMaterial.ambient;
|
||||
}
|
||||
|
||||
vec4 getDiffuseColor()
|
||||
{
|
||||
if (colorMode == ColorMode_AmbientAndDiffuse || colorMode == ColorMode_Diffuse)
|
||||
return passColor;
|
||||
return gl_FrontMaterial.diffuse;
|
||||
}
|
||||
|
||||
vec4 getSpecularColor()
|
||||
{
|
||||
if (colorMode == ColorMode_Specular)
|
||||
return passColor;
|
||||
return gl_FrontMaterial.specular;
|
||||
}
|
Loading…
Reference in a new issue