mirror of
https://github.com/OpenMW/openmw.git
synced 2025-04-01 08:36: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 #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 #5416: Junk non-node records before the root node are not handled gracefully
|
||||||
Bug #5422: The player loses all spells when resurrected
|
Bug #5422: The player loses all spells when resurrected
|
||||||
|
Bug #5423: Guar follows actors too closely
|
||||||
Bug #5424: Creatures do not headtrack player
|
Bug #5424: Creatures do not headtrack player
|
||||||
Bug #5425: Poison effect only appears for one frame
|
Bug #5425: Poison effect only appears for one frame
|
||||||
Bug #5427: GetDistance unknown ID error is misleading
|
Bug #5427: GetDistance unknown ID error is misleading
|
||||||
|
@ -72,7 +73,9 @@
|
||||||
Bug #5688: Water shader broken indoors with enable indoor shadows = false
|
Bug #5688: Water shader broken indoors with enable indoor shadows = false
|
||||||
Bug #5695: ExplodeSpell for actors doesn't target the ground
|
Bug #5695: ExplodeSpell for actors doesn't target the ground
|
||||||
Bug #5703: OpenMW-CS menu system crashing on XFCE
|
Bug #5703: OpenMW-CS menu system crashing on XFCE
|
||||||
|
Bug #5706: AI sequences stop looping after the saved game is reloaded
|
||||||
Bug #5731: Editor: skirts are invisible on characters
|
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 #390: 3rd person look "over the shoulder"
|
||||||
Feature #1536: Show more information about level on menu
|
Feature #1536: Show more information about level on menu
|
||||||
Feature #2386: Distant Statics in the form of Object Paging
|
Feature #2386: Distant Statics in the form of Object Paging
|
||||||
|
|
|
@ -584,6 +584,12 @@ if (WIN32)
|
||||||
5031 # #pragma warning(pop): likely mismatch, popping warning state pushed in different file (config_begin.hpp, config_end.hpp)
|
5031 # #pragma warning(pop): likely mismatch, popping warning state pushed in different file (config_begin.hpp, config_end.hpp)
|
||||||
)
|
)
|
||||||
endif()
|
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})
|
foreach(d ${WARNINGS_DISABLE})
|
||||||
set(WARNINGS "${WARNINGS} /wd${d}")
|
set(WARNINGS "${WARNINGS} /wd${d}")
|
||||||
|
|
|
@ -199,6 +199,7 @@ namespace MWBase
|
||||||
virtual std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor) = 0;
|
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<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) = 0;
|
||||||
virtual std::list<int> getActorsFollowingIndices(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
|
///Returns a list of actors who are fighting the given actor within the fAlarmDistance
|
||||||
/** ie AiCombat is active and the target is the actor **/
|
/** 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;
|
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
|
///< @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 scaleObject (const MWWorld::Ptr& ptr, float scale) = 0;
|
||||||
|
|
||||||
virtual void rotateObject(const MWWorld::Ptr& ptr, float x, float y, float z,
|
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.
|
/// Returns true if levitation spell effect is allowed.
|
||||||
virtual bool isLevitationEnabled() const = 0;
|
virtual bool isLevitationEnabled() const = 0;
|
||||||
|
|
||||||
virtual bool getGodModeState() = 0;
|
virtual bool getGodModeState() const = 0;
|
||||||
|
|
||||||
virtual bool toggleGodMode() = 0;
|
virtual bool toggleGodMode() = 0;
|
||||||
|
|
||||||
|
|
|
@ -451,6 +451,7 @@ namespace MWGui
|
||||||
setTitle(mPtr.getClass().getName(mPtr));
|
setTitle(mPtr.getClass().getName(mPtr));
|
||||||
|
|
||||||
updateTopics();
|
updateTopics();
|
||||||
|
updateTopicsPane(); // force update for new services
|
||||||
|
|
||||||
updateDisposition();
|
updateDisposition();
|
||||||
restock();
|
restock();
|
||||||
|
@ -487,12 +488,14 @@ namespace MWGui
|
||||||
mHistoryContents.clear();
|
mHistoryContents.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::setKeywords(std::list<std::string> keyWords)
|
bool DialogueWindow::setKeywords(std::list<std::string> keyWords)
|
||||||
{
|
{
|
||||||
if (mKeywords == keyWords && isCompanion() == mIsCompanion)
|
if (mKeywords == keyWords && isCompanion() == mIsCompanion)
|
||||||
return;
|
return false;
|
||||||
mIsCompanion = isCompanion();
|
mIsCompanion = isCompanion();
|
||||||
mKeywords = keyWords;
|
mKeywords = keyWords;
|
||||||
|
updateTopicsPane();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::updateTopicsPane()
|
void DialogueWindow::updateTopicsPane()
|
||||||
|
@ -556,6 +559,8 @@ namespace MWGui
|
||||||
mTopicsList->adjustSize();
|
mTopicsList->adjustSize();
|
||||||
|
|
||||||
updateHistory();
|
updateHistory();
|
||||||
|
// The topics list has been regenerated so topic formatting needs to be updated
|
||||||
|
updateTopicFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::updateHistory(bool scrollbar)
|
void DialogueWindow::updateHistory(bool scrollbar)
|
||||||
|
@ -758,9 +763,9 @@ namespace MWGui
|
||||||
|
|
||||||
void DialogueWindow::updateTopics()
|
void DialogueWindow::updateTopics()
|
||||||
{
|
{
|
||||||
setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics());
|
// Topic formatting needs to be updated regardless of whether the topic list has changed
|
||||||
updateTopicsPane();
|
if (!setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics()))
|
||||||
updateTopicFormat();
|
updateTopicFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DialogueWindow::isCompanion()
|
bool DialogueWindow::isCompanion()
|
||||||
|
|
|
@ -122,7 +122,8 @@ namespace MWGui
|
||||||
|
|
||||||
void setPtr(const MWWorld::Ptr& actor) override;
|
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);
|
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;
|
mKeyFocus[mode] = focus;
|
||||||
}
|
}
|
||||||
else
|
else if(shouldAcceptKeyFocus(mCurrentFocus))
|
||||||
{
|
{
|
||||||
mKeyFocus[mode] = mCurrentFocus;
|
mKeyFocus[mode] = mCurrentFocus;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,7 @@ void KeyboardNavigation::_unlinkWidget(MyGUI::Widget *widget)
|
||||||
mCurrentFocus = nullptr;
|
mCurrentFocus = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||||
void styleFocusedButton(MyGUI::Widget* w)
|
void styleFocusedButton(MyGUI::Widget* w)
|
||||||
{
|
{
|
||||||
if (w)
|
if (w)
|
||||||
|
@ -103,6 +104,7 @@ void styleFocusedButton(MyGUI::Widget* w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool isRootParent(MyGUI::Widget* widget, MyGUI::Widget* root)
|
bool isRootParent(MyGUI::Widget* widget, MyGUI::Widget* root)
|
||||||
{
|
{
|
||||||
|
@ -126,7 +128,9 @@ void KeyboardNavigation::onFrame()
|
||||||
|
|
||||||
if (focus == mCurrentFocus)
|
if (focus == mCurrentFocus)
|
||||||
{
|
{
|
||||||
|
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||||
styleFocusedButton(mCurrentFocus);
|
styleFocusedButton(mCurrentFocus);
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,19 +141,21 @@ void KeyboardNavigation::onFrame()
|
||||||
focus = mCurrentFocus;
|
focus = mCurrentFocus;
|
||||||
}
|
}
|
||||||
|
|
||||||
// style highlighted button (won't be needed for MyGUI 3.2.3)
|
|
||||||
if (focus != mCurrentFocus)
|
if (focus != mCurrentFocus)
|
||||||
{
|
{
|
||||||
|
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||||
if (mCurrentFocus)
|
if (mCurrentFocus)
|
||||||
{
|
{
|
||||||
if (MyGUI::Button* b = mCurrentFocus->castType<MyGUI::Button>(false))
|
if (MyGUI::Button* b = mCurrentFocus->castType<MyGUI::Button>(false))
|
||||||
b->_setWidgetState("normal");
|
b->_setWidgetState("normal");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
mCurrentFocus = focus;
|
mCurrentFocus = focus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||||
styleFocusedButton(mCurrentFocus);
|
styleFocusedButton(mCurrentFocus);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardNavigation::setDefaultFocus(MyGUI::Widget *window, MyGUI::Widget *defaultFocus)
|
void KeyboardNavigation::setDefaultFocus(MyGUI::Widget *window, MyGUI::Widget *defaultFocus)
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
#include "loadingscreen.hpp"
|
#include "loadingscreen.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
#include <osgViewer/Viewer>
|
#include <osgViewer/Viewer>
|
||||||
|
|
||||||
#include <osg/Texture2D>
|
#include <osg/Texture2D>
|
||||||
|
#include <osg/Version>
|
||||||
|
|
||||||
#include <MyGUI_RenderManager.h>
|
#include <MyGUI_RenderManager.h>
|
||||||
#include <MyGUI_ScrollBar.h>
|
#include <MyGUI_ScrollBar.h>
|
||||||
|
@ -43,6 +45,8 @@ namespace MWGui
|
||||||
, mNestedLoadingCount(0)
|
, mNestedLoadingCount(0)
|
||||||
, mProgress(0)
|
, mProgress(0)
|
||||||
, mShowWallpaper(true)
|
, mShowWallpaper(true)
|
||||||
|
, mOldCallback(nullptr)
|
||||||
|
, mHasCallback(false)
|
||||||
{
|
{
|
||||||
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
|
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
|
||||||
|
|
||||||
|
@ -136,18 +140,53 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CopyFramebufferToTextureCallback(osg::Texture2D* texture)
|
CopyFramebufferToTextureCallback(osg::Texture2D* texture)
|
||||||
: mTexture(texture)
|
: mOneshot(true)
|
||||||
|
, mTexture(texture)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator () (osg::RenderInfo& renderInfo) const override
|
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 w = renderInfo.getCurrentCamera()->getViewport()->width();
|
||||||
int h = renderInfo.getCurrentCamera()->getViewport()->height();
|
int h = renderInfo.getCurrentCamera()->getViewport()->height();
|
||||||
mTexture->copyTexImage2D(*renderInfo.getState(), 0, 0, w, h);
|
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:
|
private:
|
||||||
|
mutable bool mOneshot;
|
||||||
|
mutable std::mutex mMutex;
|
||||||
|
mutable std::condition_variable mSignal;
|
||||||
osg::ref_ptr<osg::Texture2D> mTexture;
|
osg::ref_ptr<osg::Texture2D> mTexture;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -322,7 +361,15 @@ namespace MWGui
|
||||||
mCopyFramebufferToTextureCallback = new CopyFramebufferToTextureCallback(mTexture);
|
mCopyFramebufferToTextureCallback = new CopyFramebufferToTextureCallback(mTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 10)
|
||||||
mViewer->getCamera()->addInitialDrawCallback(mCopyFramebufferToTextureCallback);
|
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->setBackgroundImage("");
|
||||||
mBackgroundImage->setVisible(false);
|
mBackgroundImage->setVisible(false);
|
||||||
|
@ -365,10 +412,19 @@ namespace MWGui
|
||||||
mViewer->renderingTraversals();
|
mViewer->renderingTraversals();
|
||||||
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
|
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);
|
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();
|
mLastRenderTime = mTimer.time_m();
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <osg/Camera>
|
||||||
#include <osg/Timer>
|
#include <osg/Timer>
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
|
@ -86,6 +87,8 @@ namespace MWGui
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> mTexture;
|
osg::ref_ptr<osg::Texture2D> mTexture;
|
||||||
osg::ref_ptr<CopyFramebufferToTextureCallback> mCopyFramebufferToTextureCallback;
|
osg::ref_ptr<CopyFramebufferToTextureCallback> mCopyFramebufferToTextureCallback;
|
||||||
|
osg::ref_ptr<osg::Camera::DrawCallback> mOldCallback;
|
||||||
|
bool mHasCallback;
|
||||||
std::unique_ptr<MyGUI::ITexture> mGuiTexture;
|
std::unique_ptr<MyGUI::ITexture> mGuiTexture;
|
||||||
|
|
||||||
void changeWallpaper();
|
void changeWallpaper();
|
||||||
|
|
|
@ -142,6 +142,29 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
|
||||||
magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
|
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
|
namespace MWMechanics
|
||||||
|
@ -2512,26 +2535,14 @@ namespace MWMechanics
|
||||||
std::list<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actor)
|
std::list<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actor)
|
||||||
{
|
{
|
||||||
std::list<MWWorld::Ptr> list;
|
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 (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||||
if (iteratedActor == getPlayer() || iteratedActor == actor)
|
list.push_back(iter.first);
|
||||||
continue;
|
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
|
||||||
|
return false;
|
||||||
const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
|
return true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2575,32 +2586,38 @@ namespace MWMechanics
|
||||||
std::list<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor)
|
std::list<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor)
|
||||||
{
|
{
|
||||||
std::list<int> list;
|
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 (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||||
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(static_cast<const AiFollow*>(package.get())->getFollowIndex());
|
||||||
{
|
return false;
|
||||||
list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
return list;
|
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> Actors::getActorsFighting(const MWWorld::Ptr& actor) {
|
||||||
std::list<MWWorld::Ptr> list;
|
std::list<MWWorld::Ptr> list;
|
||||||
std::vector<MWWorld::Ptr> neighbors;
|
std::vector<MWWorld::Ptr> neighbors;
|
||||||
|
|
|
@ -181,6 +181,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
/// Get the list of AiFollow::mFollowIndex for all actors following this target
|
/// Get the list of AiFollow::mFollowIndex for all actors following this target
|
||||||
std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor);
|
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
|
///Returns the list of actors which are fighting the given actor
|
||||||
/**ie AiCombat is active and the target is the 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)
|
if (!mActive)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// The distances below are approximations based on observations of the original engine.
|
// In the original engine the first follower stays closer to the player than any subsequent followers.
|
||||||
// If only one actor is following the target, it uses 186.
|
// Followers beyond the first usually attempt to stand inside each other.
|
||||||
// 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.
|
osg::Vec3f::value_type floatingDistance = 0;
|
||||||
|
auto followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowingByIndex(target);
|
||||||
short followDistance = 186;
|
if (followers.size() >= 2 && followers.cbegin()->first != mFollowIndex)
|
||||||
std::list<int> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowingIndices(target);
|
|
||||||
if (followers.size() >= 2)
|
|
||||||
{
|
{
|
||||||
followDistance = 313;
|
osg::Vec3f::value_type maxSize = 0;
|
||||||
short i = 0;
|
for(auto& follower : followers)
|
||||||
followers.sort();
|
|
||||||
for (int followIndex : followers)
|
|
||||||
{
|
{
|
||||||
if (followIndex == mFollowIndex)
|
auto halfExtent = MWBase::Environment::get().getWorld()->getHalfExtents(follower.second).y();
|
||||||
followDistance += 130 * i;
|
if(halfExtent > floatingDistance)
|
||||||
++i;
|
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
|
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));
|
zTurn(actor, getZAngleToPoint(position, dest));
|
||||||
smoothTurn(actor, getXAngleToPoint(position, dest), 0);
|
smoothTurn(actor, getXAngleToPoint(position, dest), 0);
|
||||||
world->removeActorPath(actor);
|
world->removeActorPath(actor);
|
||||||
return true;
|
return isDestReached;
|
||||||
}
|
}
|
||||||
|
|
||||||
world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);
|
world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);
|
||||||
|
|
|
@ -403,7 +403,7 @@ const AiPackage& MWMechanics::AiSequence::getActivePackage()
|
||||||
void AiSequence::fill(const ESM::AIPackageList &list)
|
void AiSequence::fill(const ESM::AIPackageList &list)
|
||||||
{
|
{
|
||||||
// If there is more than one package in the list, enable repeating
|
// 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;
|
mRepeat = true;
|
||||||
|
|
||||||
for (const auto& esmPackage : list.mList)
|
for (const auto& esmPackage : list.mList)
|
||||||
|
@ -459,8 +459,15 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (auto& container : sequence.mPackages)
|
for (auto& container : sequence.mPackages)
|
||||||
{
|
{
|
||||||
if (isActualAiPackage(static_cast<AiPackageTypeId>(container.mType)))
|
switch (container.mType)
|
||||||
count++;
|
{
|
||||||
|
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)
|
if (count > 1)
|
||||||
|
|
|
@ -1965,7 +1965,7 @@ void CharacterController::update(float duration, bool animationOnly)
|
||||||
movementSettings.mSpeedFactor *= 2.f;
|
movementSettings.mSpeedFactor *= 2.f;
|
||||||
|
|
||||||
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
|
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"));
|
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];
|
float angle = mPtr.getRefData().getPosition().rot[2];
|
||||||
|
@ -1975,7 +1975,9 @@ void CharacterController::update(float duration, bool animationOnly)
|
||||||
float deltaLen = delta.length();
|
float deltaLen = delta.length();
|
||||||
|
|
||||||
float maxDelta;
|
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).
|
// Turning is smooth for player and less smooth for NPCs (otherwise NPC can miss a path point).
|
||||||
maxDelta = duration * (isPlayer ? playerTurningCoef : 6.f);
|
maxDelta = duration * (isPlayer ? playerTurningCoef : 6.f);
|
||||||
else if (isPlayer && speedDelta < -deltaLen / 2)
|
else if (isPlayer && speedDelta < -deltaLen / 2)
|
||||||
|
@ -2013,7 +2015,10 @@ void CharacterController::update(float duration, bool animationOnly)
|
||||||
bool canMove = cls.getMaxSpeed(mPtr) > 0;
|
bool canMove = cls.getMaxSpeed(mPtr) > 0;
|
||||||
static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game");
|
static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game");
|
||||||
if (!turnToMovementDirection || isFirstPersonPlayer)
|
if (!turnToMovementDirection || isFirstPersonPlayer)
|
||||||
|
{
|
||||||
movementSettings.mIsStrafing = std::abs(vec.x()) > std::abs(vec.y()) * 2;
|
movementSettings.mIsStrafing = std::abs(vec.x()) > std::abs(vec.y()) * 2;
|
||||||
|
stats.setSideMovementAngle(0);
|
||||||
|
}
|
||||||
else if (canMove)
|
else if (canMove)
|
||||||
{
|
{
|
||||||
float targetMovementAngle = vec.y() >= 0 ? std::atan2(-vec.x(), vec.y()) : std::atan2(vec.x(), -vec.y());
|
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);
|
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 swimmingPitch = mAnimation->getBodyPitchRadians();
|
||||||
|
float targetSwimmingPitch = -mPtr.getRefData().getPosition().rot[0];
|
||||||
|
float maxSwimPitchDelta = 3.0f * duration;
|
||||||
swimmingPitch += osg::clampBetween(targetSwimmingPitch - swimmingPitch, -maxSwimPitchDelta, maxSwimPitchDelta);
|
swimmingPitch += osg::clampBetween(targetSwimmingPitch - swimmingPitch, -maxSwimPitchDelta, maxSwimPitchDelta);
|
||||||
mAnimation->setBodyPitchRadians(swimmingPitch);
|
mAnimation->setBodyPitchRadians(swimmingPitch);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
mAnimation->setBodyPitchRadians(0);
|
||||||
|
|
||||||
static const bool swimUpwardCorrection = Settings::Manager::getBool("swim upward correction", "Game");
|
static const bool swimUpwardCorrection = Settings::Manager::getBool("swim upward correction", "Game");
|
||||||
if (inwater && isPlayer && !isFirstPersonPlayer && swimUpwardCorrection)
|
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))
|
if (mFloatToSurface && cls.isActor() && cls.canSwim(mPtr))
|
||||||
moved.z() = 1.0;
|
{
|
||||||
|
if (cls.getCreatureStats(mPtr).isDead()
|
||||||
|
|| (!godmode && cls.getCreatureStats(mPtr).isParalyzed()))
|
||||||
|
{
|
||||||
|
moved.z() = 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update movement
|
// Update movement
|
||||||
if(!animationOnly && mMovementAnimationControlled && mPtr.getClass().isActor())
|
if(!animationOnly && mMovementAnimationControlled && mPtr.getClass().isActor())
|
||||||
|
|
|
@ -1654,6 +1654,11 @@ namespace MWMechanics
|
||||||
return mActors.getActorsFollowingIndices(actor);
|
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) {
|
std::list<MWWorld::Ptr> MechanicsManager::getActorsFighting(const MWWorld::Ptr& actor) {
|
||||||
return mActors.getActorsFighting(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> getActorsSidingWith(const MWWorld::Ptr& actor) override;
|
||||||
std::list<MWWorld::Ptr> getActorsFollowing(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::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> getActorsFighting(const MWWorld::Ptr& actor) override;
|
||||||
std::list<MWWorld::Ptr> getEnemiesNearby(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();
|
updateRotation();
|
||||||
updateScale();
|
updateScale();
|
||||||
resetPosition();
|
updatePosition();
|
||||||
addCollisionMask(getCollisionMask());
|
addCollisionMask(getCollisionMask());
|
||||||
|
updateCollisionObjectPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
Actor::~Actor()
|
Actor::~Actor()
|
||||||
|
@ -118,26 +119,34 @@ int Actor::getCollisionMask() const
|
||||||
return collisionMask;
|
return collisionMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actor::updatePositionUnsafe()
|
|
||||||
{
|
|
||||||
mWorldPosition = mPtr.getRefData().getPosition().asVec3();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Actor::updatePosition()
|
void Actor::updatePosition()
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mPositionMutex);
|
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
|
osg::Vec3f Actor::getWorldPosition() const
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mPositionMutex);
|
|
||||||
return mWorldPosition;
|
return mWorldPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actor::setSimulationPosition(const osg::Vec3f& position)
|
void Actor::setSimulationPosition(const osg::Vec3f& position)
|
||||||
{
|
{
|
||||||
mSimulationPosition = position;
|
if (!mSkipSimulation)
|
||||||
|
mSimulationPosition = position;
|
||||||
|
mSkipSimulation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f Actor::getSimulationPosition() const
|
osg::Vec3f Actor::getSimulationPosition() const
|
||||||
|
@ -145,20 +154,16 @@ osg::Vec3f Actor::getSimulationPosition() const
|
||||||
return mSimulationPosition;
|
return mSimulationPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actor::updateCollisionObjectPositionUnsafe()
|
void Actor::updateCollisionObjectPosition()
|
||||||
{
|
{
|
||||||
|
std::scoped_lock lock(mPositionMutex);
|
||||||
mShape->setLocalScaling(Misc::Convert::toBullet(mScale));
|
mShape->setLocalScaling(Misc::Convert::toBullet(mScale));
|
||||||
osg::Vec3f scaledTranslation = mRotation * osg::componentMultiply(mMeshTranslation, mScale);
|
osg::Vec3f scaledTranslation = mRotation * osg::componentMultiply(mMeshTranslation, mScale);
|
||||||
osg::Vec3f newPosition = scaledTranslation + mPosition;
|
osg::Vec3f newPosition = scaledTranslation + mPosition;
|
||||||
mLocalTransform.setOrigin(Misc::Convert::toBullet(newPosition));
|
mLocalTransform.setOrigin(Misc::Convert::toBullet(newPosition));
|
||||||
mLocalTransform.setRotation(Misc::Convert::toBullet(mRotation));
|
mLocalTransform.setRotation(Misc::Convert::toBullet(mRotation));
|
||||||
mCollisionObject->setWorldTransform(mLocalTransform);
|
mCollisionObject->setWorldTransform(mLocalTransform);
|
||||||
}
|
mWorldPositionChanged = false;
|
||||||
|
|
||||||
void Actor::updateCollisionObjectPosition()
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(mPositionMutex);
|
|
||||||
updateCollisionObjectPositionUnsafe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f Actor::getCollisionObjectPosition() const
|
osg::Vec3f Actor::getCollisionObjectPosition() const
|
||||||
|
@ -167,28 +172,20 @@ osg::Vec3f Actor::getCollisionObjectPosition() const
|
||||||
return Misc::Convert::toOsg(mLocalTransform.getOrigin());
|
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);
|
std::scoped_lock lock(mPositionMutex);
|
||||||
mPreviousPosition = mPosition;
|
bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged;
|
||||||
mPosition = position;
|
mPreviousPosition = mPosition + mPositionOffset;
|
||||||
|
mPosition = position + mPositionOffset;
|
||||||
|
mPositionOffset = osg::Vec3f();
|
||||||
|
return hasChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actor::adjustPosition(const osg::Vec3f& offset)
|
void Actor::adjustPosition(const osg::Vec3f& offset)
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mPositionMutex);
|
std::scoped_lock lock(mPositionMutex);
|
||||||
mPosition += offset;
|
mPositionOffset += offset;
|
||||||
mPreviousPosition += offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Actor::resetPosition()
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(mPositionMutex);
|
|
||||||
updatePositionUnsafe();
|
|
||||||
mPreviousPosition = mWorldPosition;
|
|
||||||
mPosition = mWorldPosition;
|
|
||||||
mSimulationPosition = mWorldPosition;
|
|
||||||
updateCollisionObjectPositionUnsafe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f Actor::getPosition() const
|
osg::Vec3f Actor::getPosition() const
|
||||||
|
@ -204,8 +201,6 @@ osg::Vec3f Actor::getPreviousPosition() const
|
||||||
void Actor::updateRotation ()
|
void Actor::updateRotation ()
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mPositionMutex);
|
std::scoped_lock lock(mPositionMutex);
|
||||||
if (mRotation == mPtr.getRefData().getBaseNode()->getAttitude())
|
|
||||||
return;
|
|
||||||
mRotation = mPtr.getRefData().getBaseNode()->getAttitude();
|
mRotation = mPtr.getRefData().getBaseNode()->getAttitude();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +231,6 @@ osg::Vec3f Actor::getHalfExtents() const
|
||||||
|
|
||||||
osg::Vec3f Actor::getOriginalHalfExtents() const
|
osg::Vec3f Actor::getOriginalHalfExtents() const
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mPositionMutex);
|
|
||||||
return mHalfExtents;
|
return mHalfExtents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +267,6 @@ void Actor::setWalkingOnWater(bool walkingOnWater)
|
||||||
|
|
||||||
void Actor::setCanWaterWalk(bool waterWalk)
|
void Actor::setCanWaterWalk(bool waterWalk)
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mPositionMutex);
|
|
||||||
if (waterWalk != mCanWaterWalk)
|
if (waterWalk != mCanWaterWalk)
|
||||||
{
|
{
|
||||||
mCanWaterWalk = waterWalk;
|
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
|
* 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.
|
* when an object is "instantly" moved/teleported as opposed to being moved by the physics simulation.
|
||||||
*/
|
*/
|
||||||
void updatePosition();
|
void updateWorldPosition();
|
||||||
osg::Vec3f getWorldPosition() const;
|
osg::Vec3f getWorldPosition() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,9 +90,10 @@ namespace MWPhysics
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the current position into mPreviousPosition, then move to this position.
|
* 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);
|
bool setPosition(const osg::Vec3f& position);
|
||||||
void resetPosition();
|
void updatePosition();
|
||||||
void adjustPosition(const osg::Vec3f& offset);
|
void adjustPosition(const osg::Vec3f& offset);
|
||||||
|
|
||||||
osg::Vec3f getPosition() const;
|
osg::Vec3f getPosition() const;
|
||||||
|
@ -154,8 +155,6 @@ namespace MWPhysics
|
||||||
void updateCollisionMask();
|
void updateCollisionMask();
|
||||||
void addCollisionMask(int collisionMask);
|
void addCollisionMask(int collisionMask);
|
||||||
int getCollisionMask() const;
|
int getCollisionMask() const;
|
||||||
void updateCollisionObjectPositionUnsafe();
|
|
||||||
void updatePositionUnsafe();
|
|
||||||
|
|
||||||
bool mCanWaterWalk;
|
bool mCanWaterWalk;
|
||||||
std::atomic<bool> mWalkingOnWater;
|
std::atomic<bool> mWalkingOnWater;
|
||||||
|
@ -177,6 +176,9 @@ namespace MWPhysics
|
||||||
osg::Vec3f mSimulationPosition;
|
osg::Vec3f mSimulationPosition;
|
||||||
osg::Vec3f mPosition;
|
osg::Vec3f mPosition;
|
||||||
osg::Vec3f mPreviousPosition;
|
osg::Vec3f mPreviousPosition;
|
||||||
|
osg::Vec3f mPositionOffset;
|
||||||
|
bool mWorldPositionChanged;
|
||||||
|
bool mSkipSimulation;
|
||||||
btTransform mLocalTransform;
|
btTransform mLocalTransform;
|
||||||
mutable std::mutex mPositionMutex;
|
mutable std::mutex mPositionMutex;
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
|
|
||||||
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
||||||
|
|
||||||
#include <components/misc/convert.hpp>
|
|
||||||
|
|
||||||
#include "../mwbase/world.hpp"
|
|
||||||
#include "../mwbase/environment.hpp"
|
|
||||||
|
|
||||||
#include "collisiontype.hpp"
|
#include "collisiontype.hpp"
|
||||||
#include "projectile.hpp"
|
#include "projectile.hpp"
|
||||||
|
|
||||||
|
|
|
@ -41,15 +41,21 @@ namespace MWPhysics
|
||||||
btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
||||||
if (mProjectile)
|
if (mProjectile)
|
||||||
{
|
{
|
||||||
auto* holder = static_cast<PtrHolder*>(rayResult.m_collisionObject->getUserPointer());
|
switch (rayResult.m_collisionObject->getBroadphaseHandle()->m_collisionFilterGroup)
|
||||||
if (auto* target = dynamic_cast<Actor*>(holder))
|
|
||||||
{
|
{
|
||||||
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
case CollisionType_Actor:
|
||||||
}
|
{
|
||||||
else if (auto* target = dynamic_cast<Projectile*>(holder))
|
auto* target = static_cast<Actor*>(rayResult.m_collisionObject->getUserPointer());
|
||||||
{
|
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||||
target->hit(mProjectile->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
break;
|
||||||
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
}
|
||||||
|
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)
|
WorldFrameData& worldData)
|
||||||
{
|
{
|
||||||
auto* physicActor = actor.mActorRaw;
|
auto* physicActor = actor.mActorRaw;
|
||||||
auto ptr = actor.mPtr;
|
|
||||||
const ESM::Position& refpos = actor.mRefpos;
|
const ESM::Position& refpos = actor.mRefpos;
|
||||||
// Early-out for totally static creatures
|
// Early-out for totally static creatures
|
||||||
// (Not sure if gravity should still apply?)
|
// (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
|
// Reset per-frame data
|
||||||
physicActor->setWalkingOnWater(false);
|
physicActor->setWalkingOnWater(false);
|
||||||
|
@ -128,8 +130,9 @@ namespace MWPhysics
|
||||||
velocity = velocity + inertia;
|
velocity = velocity + inertia;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dead actors underwater will float to the surface, if the CharacterController tells us to do so
|
// Dead and paralyzed actors underwater will float to the surface,
|
||||||
if (actor.mMovement.z() > 0 && actor.mIsDead && actor.mPosition.z() < swimlevel)
|
// 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;
|
velocity = osg::Vec3f(0,0,1) * 25;
|
||||||
|
|
||||||
if (actor.mWantJump)
|
if (actor.mWantJump)
|
||||||
|
@ -211,6 +214,7 @@ namespace MWPhysics
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
// don't let pure water creatures move out of water after stepMove
|
// 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)
|
if (ptr.getClass().isPureWaterCreature(ptr) && newPosition.z() + halfExtents.z() > actor.mWaterlevel)
|
||||||
newPosition = oldPosition;
|
newPosition = oldPosition;
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,12 +87,13 @@ namespace
|
||||||
|
|
||||||
void updateMechanics(MWPhysics::ActorFrameData& actorData)
|
void updateMechanics(MWPhysics::ActorFrameData& actorData)
|
||||||
{
|
{
|
||||||
|
auto ptr = actorData.mActorRaw->getPtr();
|
||||||
if (actorData.mDidJump)
|
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)
|
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)
|
else if (actorData.mFallHeight < 0)
|
||||||
stats.addToFallHeight(-actorData.mFallHeight);
|
stats.addToFallHeight(-actorData.mFallHeight);
|
||||||
}
|
}
|
||||||
|
@ -100,15 +101,6 @@ namespace
|
||||||
osg::Vec3f interpolateMovements(MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt)
|
osg::Vec3f interpolateMovements(MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt)
|
||||||
{
|
{
|
||||||
const float interpolationFactor = timeAccum / 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);
|
return actorData.mPosition * interpolationFactor + actorData.mActorRaw->getPreviousPosition() * (1.f - interpolationFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,45 +205,43 @@ namespace MWPhysics
|
||||||
thread.join();
|
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.
|
// This function run in the main thread.
|
||||||
// While the mSimulationMutex is held, background physics threads can't run.
|
// While the mSimulationMutex is held, background physics threads can't run.
|
||||||
|
|
||||||
std::unique_lock lock(mSimulationMutex);
|
std::unique_lock lock(mSimulationMutex);
|
||||||
|
|
||||||
for (auto& data : actorsData)
|
mMovedActors.clear();
|
||||||
data.updatePosition();
|
|
||||||
|
|
||||||
// start by finishing previous background computation
|
// start by finishing previous background computation
|
||||||
if (mNumThreads != 0)
|
if (mNumThreads != 0)
|
||||||
{
|
{
|
||||||
for (auto& data : mActorsFrameData)
|
for (auto& data : mActorsFrameData)
|
||||||
{
|
{
|
||||||
// Ignore actors that were deleted while the background thread was running
|
const auto actorActive = [&data](const auto& newFrameData) -> bool
|
||||||
if (!data.mActor.lock())
|
{
|
||||||
continue;
|
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);
|
// these variables are accessed directly from the main thread, update them here to prevent accessing "too new" values
|
||||||
if (mAdvanceSimulation)
|
if (mAdvanceSimulation)
|
||||||
data.mActorRaw->setStandingOnPtr(data.mStandingOn);
|
data.mActorRaw->setStandingOnPtr(data.mStandingOn);
|
||||||
|
data.mActorRaw->setSimulationPosition(interpolateMovements(data, mTimeAccum, mPhysicsDt));
|
||||||
if (mMovementResults.find(data.mPtr) != mMovementResults.end())
|
mMovedActors.emplace_back(data.mActorRaw->getPtr());
|
||||||
data.mActorRaw->setSimulationPosition(mMovementResults[data.mPtr]);
|
}
|
||||||
}
|
}
|
||||||
|
updateStats(frameStart, frameNumber, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
|
for (auto& data : actorsData)
|
||||||
|
data.updatePosition();
|
||||||
mRemainingSteps = numSteps;
|
mRemainingSteps = numSteps;
|
||||||
mTimeAccum = timeAccum;
|
mTimeAccum = timeAccum;
|
||||||
mActorsFrameData = std::move(actorsData);
|
mActorsFrameData = std::move(actorsData);
|
||||||
|
@ -266,52 +256,28 @@ namespace MWPhysics
|
||||||
|
|
||||||
if (mNumThreads == 0)
|
if (mNumThreads == 0)
|
||||||
{
|
{
|
||||||
mMovementResults.clear();
|
|
||||||
syncComputation();
|
syncComputation();
|
||||||
|
return mMovedActors;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
lock.unlock();
|
||||||
mHasJob.notify_all();
|
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);
|
std::unique_lock lock(mSimulationMutex);
|
||||||
mMovementResults.clear();
|
mMovedActors.clear();
|
||||||
mPreviousMovementResults.clear();
|
|
||||||
mActorsFrameData.clear();
|
mActorsFrameData.clear();
|
||||||
|
|
||||||
for (const auto& [_, actor] : actors)
|
for (const auto& [_, actor] : actors)
|
||||||
{
|
{
|
||||||
actor->resetPosition();
|
actor->updatePosition();
|
||||||
actor->setStandingOnPtr(nullptr);
|
actor->setSimulationPosition(actor->getWorldPosition()); // updatePosition skip next simulation, now we need to "consume" it
|
||||||
mMovementResults[actor->getPtr()] = actor->getWorldPosition();
|
actor->updateCollisionObjectPosition();
|
||||||
|
mMovedActors.emplace_back(actor->getPtr());
|
||||||
}
|
}
|
||||||
return mMovementResults;
|
return mMovedActors;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const
|
void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const
|
||||||
|
@ -379,18 +345,18 @@ namespace MWPhysics
|
||||||
mCollisionWorld->removeCollisionObject(collisionObject);
|
mCollisionWorld->removeCollisionObject(collisionObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsTaskScheduler::updateSingleAabb(std::weak_ptr<PtrHolder> ptr)
|
void PhysicsTaskScheduler::updateSingleAabb(std::weak_ptr<PtrHolder> ptr, bool immediate)
|
||||||
{
|
{
|
||||||
if (mDeferAabbUpdate)
|
if (!mDeferAabbUpdate || immediate)
|
||||||
{
|
|
||||||
std::unique_lock lock(mUpdateAabbMutex);
|
|
||||||
mUpdateAabb.insert(std::move(ptr));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mCollisionWorldMutex);
|
std::unique_lock lock(mCollisionWorldMutex);
|
||||||
updatePtrAabb(ptr);
|
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)
|
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];
|
auto& actorData = mActorsFrameData[job];
|
||||||
handleFall(actorData, mAdvanceSimulation);
|
handleFall(actorData, mAdvanceSimulation);
|
||||||
mMovementResults[actorData.mPtr] = interpolateMovements(actorData, mTimeAccum, mPhysicsDt);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,9 +476,7 @@ namespace MWPhysics
|
||||||
{
|
{
|
||||||
if(const auto actor = actorData.mActor.lock())
|
if(const auto actor = actorData.mActor.lock())
|
||||||
{
|
{
|
||||||
bool positionChanged = actorData.mPosition != actorData.mActorRaw->getPosition();
|
if (actor->setPosition(actorData.mPosition))
|
||||||
actorData.mActorRaw->setPosition(actorData.mPosition);
|
|
||||||
if (positionChanged)
|
|
||||||
{
|
{
|
||||||
actor->updateCollisionObjectPosition();
|
actor->updateCollisionObjectPosition();
|
||||||
mCollisionWorld->updateSingleAabb(actor->getCollisionObject());
|
mCollisionWorld->updateSingleAabb(actor->getCollisionObject());
|
||||||
|
@ -550,8 +513,24 @@ namespace MWPhysics
|
||||||
for (auto& actorData : mActorsFrameData)
|
for (auto& actorData : mActorsFrameData)
|
||||||
{
|
{
|
||||||
handleFall(actorData, mAdvanceSimulation);
|
handleFall(actorData, mAdvanceSimulation);
|
||||||
mMovementResults[actorData.mPtr] = interpolateMovements(actorData, mTimeAccum, mPhysicsDt);
|
actorData.mActorRaw->setSimulationPosition(interpolateMovements(actorData, mTimeAccum, mPhysicsDt));
|
||||||
updateMechanics(actorData);
|
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 timeAccum accumulated time from previous run to interpolate movements
|
||||||
/// @param actorsData per actor data needed to compute new positions
|
/// @param actorsData per actor data needed to compute new positions
|
||||||
/// @return new position of each actor
|
/// @return new position of each actor
|
||||||
const PtrPositionList& moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
const 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
|
// Thread safe wrappers
|
||||||
void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const;
|
void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const;
|
||||||
|
@ -46,7 +46,7 @@ namespace MWPhysics
|
||||||
void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask);
|
void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask);
|
||||||
void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask);
|
void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask);
|
||||||
void removeCollisionObject(btCollisionObject* collisionObject);
|
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);
|
bool getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -57,11 +57,11 @@ namespace MWPhysics
|
||||||
void refreshLOSCache();
|
void refreshLOSCache();
|
||||||
void updateAabbs();
|
void updateAabbs();
|
||||||
void updatePtrAabb(const std::weak_ptr<PtrHolder>& ptr);
|
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::unique_ptr<WorldFrameData> mWorldFrameData;
|
||||||
std::vector<ActorFrameData> mActorsFrameData;
|
std::vector<ActorFrameData> mActorsFrameData;
|
||||||
PtrPositionList mMovementResults;
|
std::vector<MWWorld::Ptr> mMovedActors;
|
||||||
PtrPositionList mPreviousMovementResults;
|
|
||||||
const float mPhysicsDt;
|
const float mPhysicsDt;
|
||||||
float mTimeAccum;
|
float mTimeAccum;
|
||||||
std::shared_ptr<btCollisionWorld> mCollisionWorld;
|
std::shared_ptr<btCollisionWorld> mCollisionWorld;
|
||||||
|
|
|
@ -437,7 +437,6 @@ namespace MWPhysics
|
||||||
ActorMap::iterator found = mActors.find(ptr);
|
ActorMap::iterator found = mActors.find(ptr);
|
||||||
if (found == mActors.end())
|
if (found == mActors.end())
|
||||||
return ptr.getRefData().getPosition().asVec3();
|
return ptr.getRefData().getPosition().asVec3();
|
||||||
found->second->resetPosition();
|
|
||||||
return MovementSolver::traceDown(ptr, position, found->second.get(), mCollisionWorld.get(), maxHeight);
|
return MovementSolver::traceDown(ptr, position, found->second.get(), mCollisionWorld.get(), maxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,7 +641,7 @@ namespace MWPhysics
|
||||||
if (foundActor != mActors.end())
|
if (foundActor != mActors.end())
|
||||||
{
|
{
|
||||||
foundActor->second->updatePosition();
|
foundActor->second->updatePosition();
|
||||||
mTaskScheduler->updateSingleAabb(foundActor->second);
|
mTaskScheduler->updateSingleAabb(foundActor->second, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -711,7 +710,7 @@ namespace MWPhysics
|
||||||
mMovementQueue.clear();
|
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;
|
mTimeAccum += dt;
|
||||||
|
|
||||||
|
@ -770,7 +769,7 @@ namespace MWPhysics
|
||||||
if (numSteps == 0)
|
if (numSteps == 0)
|
||||||
standingOn = physicActor->getStandingOnPtr();
|
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();
|
mMovementQueue.clear();
|
||||||
return actorsFrameData;
|
return actorsFrameData;
|
||||||
|
@ -912,25 +911,26 @@ namespace MWPhysics
|
||||||
mDebugDrawer->addCollision(position, normal);
|
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)
|
bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel)
|
||||||
: mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn),
|
: mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn),
|
||||||
mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface),
|
mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface),
|
||||||
mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos()
|
mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos()
|
||||||
{
|
{
|
||||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
mPtr = actor->getPtr();
|
const auto ptr = actor->getPtr();
|
||||||
mFlying = world->isFlying(character);
|
mFlying = world->isFlying(ptr);
|
||||||
mSwimming = world->isSwimming(character);
|
mSwimming = world->isSwimming(ptr);
|
||||||
mWantJump = mPtr.getClass().getMovementSettings(mPtr).mPosition[2] != 0;
|
mWantJump = ptr.getClass().getMovementSettings(ptr).mPosition[2] != 0;
|
||||||
mIsDead = mPtr.getClass().getCreatureStats(mPtr).isDead();
|
auto& stats = ptr.getClass().getCreatureStats(ptr);
|
||||||
|
const bool godmode = ptr == world->getPlayerConstPtr() && world->getGodModeState();
|
||||||
|
mFloatToSurface = stats.isDead() || (!godmode && stats.isParalyzed());
|
||||||
mWasOnGround = actor->getOnGround();
|
mWasOnGround = actor->getOnGround();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActorFrameData::updatePosition()
|
void ActorFrameData::updatePosition()
|
||||||
{
|
{
|
||||||
mActorRaw->updatePosition();
|
mActorRaw->updateWorldPosition();
|
||||||
mOrigin = mActorRaw->getSimulationPosition();
|
|
||||||
mPosition = mActorRaw->getPosition();
|
mPosition = mActorRaw->getPosition();
|
||||||
if (mMoveToWaterSurface)
|
if (mMoveToWaterSurface)
|
||||||
{
|
{
|
||||||
|
@ -938,7 +938,7 @@ namespace MWPhysics
|
||||||
mActorRaw->setPosition(mPosition);
|
mActorRaw->setPosition(mPosition);
|
||||||
}
|
}
|
||||||
mOldHeight = mPosition.z();
|
mOldHeight = mPosition.z();
|
||||||
mRefpos = mPtr.getRefData().getPosition();
|
mRefpos = mActorRaw->getPtr().getRefData().getPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldFrameData::WorldFrameData()
|
WorldFrameData::WorldFrameData()
|
||||||
|
|
|
@ -50,8 +50,6 @@ class btVector3;
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
using PtrPositionList = std::map<MWWorld::Ptr, osg::Vec3f>;
|
|
||||||
|
|
||||||
class HeightField;
|
class HeightField;
|
||||||
class Object;
|
class Object;
|
||||||
class Actor;
|
class Actor;
|
||||||
|
@ -80,18 +78,17 @@ namespace MWPhysics
|
||||||
|
|
||||||
struct ActorFrameData
|
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();
|
void updatePosition();
|
||||||
std::weak_ptr<Actor> mActor;
|
std::weak_ptr<Actor> mActor;
|
||||||
Actor* mActorRaw;
|
Actor* mActorRaw;
|
||||||
MWWorld::Ptr mPtr;
|
|
||||||
MWWorld::Ptr mStandingOn;
|
MWWorld::Ptr mStandingOn;
|
||||||
bool mFlying;
|
bool mFlying;
|
||||||
bool mSwimming;
|
bool mSwimming;
|
||||||
bool mWasOnGround;
|
bool mWasOnGround;
|
||||||
bool mWantJump;
|
bool mWantJump;
|
||||||
bool mDidJump;
|
bool mDidJump;
|
||||||
bool mIsDead;
|
bool mFloatToSurface;
|
||||||
bool mNeedLand;
|
bool mNeedLand;
|
||||||
bool mMoveToWaterSurface;
|
bool mMoveToWaterSurface;
|
||||||
float mWaterlevel;
|
float mWaterlevel;
|
||||||
|
@ -99,7 +96,6 @@ namespace MWPhysics
|
||||||
float mOldHeight;
|
float mOldHeight;
|
||||||
float mFallHeight;
|
float mFallHeight;
|
||||||
osg::Vec3f mMovement;
|
osg::Vec3f mMovement;
|
||||||
osg::Vec3f mOrigin;
|
|
||||||
osg::Vec3f mPosition;
|
osg::Vec3f mPosition;
|
||||||
ESM::Position mRefpos;
|
ESM::Position mRefpos;
|
||||||
};
|
};
|
||||||
|
@ -210,7 +206,7 @@ namespace MWPhysics
|
||||||
void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity);
|
void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity);
|
||||||
|
|
||||||
/// Apply all queued movements, then clear the list.
|
/// 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.
|
/// Clear the queued movements list without applying.
|
||||||
void clearQueuedMovement();
|
void clearQueuedMovement();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef OPENMW_MWPHYSICS_PTRHOLDER_H
|
#ifndef OPENMW_MWPHYSICS_PTRHOLDER_H
|
||||||
#define OPENMW_MWPHYSICS_PTRHOLDER_H
|
#define OPENMW_MWPHYSICS_PTRHOLDER_H
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
|
@ -12,21 +14,27 @@ namespace MWPhysics
|
||||||
|
|
||||||
void updatePtr(const MWWorld::Ptr& updated)
|
void updatePtr(const MWWorld::Ptr& updated)
|
||||||
{
|
{
|
||||||
|
std::scoped_lock lock(mMutex);
|
||||||
mPtr = updated;
|
mPtr = updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr getPtr()
|
MWWorld::Ptr getPtr()
|
||||||
{
|
{
|
||||||
|
std::scoped_lock lock(mMutex);
|
||||||
return mPtr;
|
return mPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::ConstPtr getPtr() const
|
MWWorld::ConstPtr getPtr() const
|
||||||
{
|
{
|
||||||
|
std::scoped_lock lock(mMutex);
|
||||||
return mPtr;
|
return mPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MWWorld::Ptr mPtr;
|
MWWorld::Ptr mPtr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::mutex mMutex;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,6 @@
|
||||||
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
||||||
#include <BulletCollision/CollisionShapes/btConvexShape.h>
|
#include <BulletCollision/CollisionShapes/btConvexShape.h>
|
||||||
|
|
||||||
#include "../mwbase/world.hpp"
|
|
||||||
#include "../mwbase/environment.hpp"
|
|
||||||
|
|
||||||
#include "collisiontype.hpp"
|
#include "collisiontype.hpp"
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
#include "closestnotmeconvexresultcallback.hpp"
|
#include "closestnotmeconvexresultcallback.hpp"
|
||||||
|
|
|
@ -356,9 +356,6 @@ namespace MWRender
|
||||||
else
|
else
|
||||||
mViewModeToggleQueued = false;
|
mViewModeToggleQueued = false;
|
||||||
|
|
||||||
if (mTrackingPtr.getClass().isActor())
|
|
||||||
mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).setSideMovementAngle(0);
|
|
||||||
|
|
||||||
mFirstPersonView = !mFirstPersonView;
|
mFirstPersonView = !mFirstPersonView;
|
||||||
updateStandingPreviewMode();
|
updateStandingPreviewMode();
|
||||||
instantTransition();
|
instantTransition();
|
||||||
|
|
|
@ -32,11 +32,7 @@ namespace MWScript
|
||||||
std::vector<MWWorld::Ptr> actors;
|
std::vector<MWWorld::Ptr> actors;
|
||||||
MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors);
|
MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors);
|
||||||
for (auto& actor : actors)
|
for (auto& actor : actors)
|
||||||
{
|
MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff);
|
||||||
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
|
||||||
actorPos += diff;
|
|
||||||
MWBase::Environment::get().getWorld()->moveObject(actor, actorPos.x(), actorPos.y(), actorPos.z());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class R>
|
template<class R>
|
||||||
|
@ -727,14 +723,12 @@ namespace MWScript
|
||||||
return;
|
return;
|
||||||
|
|
||||||
osg::Vec3f diff = ptr.getRefData().getBaseNode()->getAttitude() * posChange;
|
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.
|
// We should move actors, standing on moving object, too.
|
||||||
// This approach can be used to create elevators.
|
// This approach can be used to create elevators.
|
||||||
moveStandingActors(ptr, diff);
|
moveStandingActors(ptr, diff);
|
||||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
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());
|
Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
const float *objPos = ptr.getRefData().getPosition().pos;
|
|
||||||
osg::Vec3f diff;
|
osg::Vec3f diff;
|
||||||
|
|
||||||
if (axis == "x")
|
if (axis == "x")
|
||||||
diff.x() += movement;
|
diff.x() = movement;
|
||||||
else if (axis == "y")
|
else if (axis == "y")
|
||||||
diff.y() += movement;
|
diff.y() = movement;
|
||||||
else if (axis == "z")
|
else if (axis == "z")
|
||||||
diff.z() += movement;
|
diff.z() = movement;
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -771,7 +764,7 @@ namespace MWScript
|
||||||
// This approach can be used to create elevators.
|
// This approach can be used to create elevators.
|
||||||
moveStandingActors(ptr, diff);
|
moveStandingActors(ptr, diff);
|
||||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
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);
|
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)
|
void World::scaleObject (const Ptr& ptr, float scale)
|
||||||
{
|
{
|
||||||
if (mPhysics->getActor(ptr))
|
if (mPhysics->getActor(ptr))
|
||||||
|
@ -1333,7 +1345,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
const float terrainHeight = ptr.getCell()->isExterior() ? getTerrainHeightAt(pos) : -std::numeric_limits<float>::max();
|
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.
|
// We still should trace down dead persistent actors - they do not use the "swimdeath" animation.
|
||||||
bool swims = ptr.getClass().isActor() && isSwimming(ptr) && !(ptr.getClass().isPersistent(ptr) && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished());
|
bool swims = ptr.getClass().isActor() && isSwimming(ptr) && !(ptr.getClass().isPersistent(ptr) && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished());
|
||||||
|
@ -1344,12 +1356,6 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z());
|
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()
|
void World::fixPosition()
|
||||||
|
@ -1500,16 +1506,26 @@ namespace MWWorld
|
||||||
mProjectileManager->processHits();
|
mProjectileManager->processHits();
|
||||||
mDiscardMovements = false;
|
mDiscardMovements = false;
|
||||||
|
|
||||||
for(const auto& [actor, position]: results)
|
for(const auto& actor : results)
|
||||||
{
|
{
|
||||||
// Handle player last, in case a cell transition occurs
|
// Handle player last, in case a cell transition occurs
|
||||||
if(actor != getPlayerPtr())
|
if(actor != getPlayerPtr())
|
||||||
|
{
|
||||||
|
auto* physactor = mPhysics->getActor(actor);
|
||||||
|
assert(physactor);
|
||||||
|
const auto position = physactor->getSimulationPosition();
|
||||||
moveObjectImp(actor, position.x(), position.y(), position.z(), false);
|
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())
|
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()
|
void World::updateNavigator()
|
||||||
|
@ -2286,8 +2302,12 @@ namespace MWWorld
|
||||||
if (stats.isDead())
|
if (stats.isDead())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
const bool isPlayer = ptr == getPlayerConstPtr();
|
||||||
|
if (!(isPlayer && mGodMode) && stats.isParalyzed())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (ptr.getClass().canFly(ptr))
|
if (ptr.getClass().canFly(ptr))
|
||||||
return !stats.isParalyzed();
|
return true;
|
||||||
|
|
||||||
if(stats.getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0
|
if(stats.getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0
|
||||||
&& isLevitationEnabled())
|
&& isLevitationEnabled())
|
||||||
|
@ -2897,7 +2917,7 @@ namespace MWWorld
|
||||||
mRendering->rebuildPtr(getPlayerPtr());
|
mRendering->rebuildPtr(getPlayerPtr());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool World::getGodModeState()
|
bool World::getGodModeState() const
|
||||||
{
|
{
|
||||||
return mGodMode;
|
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;
|
MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override;
|
||||||
///< @return an updated Ptr
|
///< @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;
|
void scaleObject (const Ptr& ptr, float scale) override;
|
||||||
|
|
||||||
/// World rotates object, uses radians
|
/// World rotates object, uses radians
|
||||||
|
@ -616,7 +619,7 @@ namespace MWWorld
|
||||||
/// Returns true if levitation spell effect is allowed.
|
/// Returns true if levitation spell effect is allowed.
|
||||||
bool isLevitationEnabled() const override;
|
bool isLevitationEnabled() const override;
|
||||||
|
|
||||||
bool getGodModeState() override;
|
bool getGodModeState() const override;
|
||||||
|
|
||||||
bool toggleGodMode() override;
|
bool toggleGodMode() override;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
find_package(GTest REQUIRED)
|
find_package(GTest 1.10 REQUIRED)
|
||||||
find_package(GMock REQUIRED)
|
find_package(GMock 1.10 REQUIRED)
|
||||||
|
|
||||||
if (GTEST_FOUND AND GMOCK_FOUND)
|
if (GTEST_FOUND AND GMOCK_FOUND)
|
||||||
include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})
|
include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})
|
||||||
|
|
|
@ -299,22 +299,22 @@ namespace
|
||||||
|
|
||||||
struct NifFileMock : Nif::File
|
struct NifFileMock : Nif::File
|
||||||
{
|
{
|
||||||
MOCK_CONST_METHOD1(getRecord, Nif::Record* (std::size_t));
|
MOCK_METHOD(Nif::Record*, getRecord, (std::size_t), (const, override));
|
||||||
MOCK_CONST_METHOD0(numRecords, std::size_t ());
|
MOCK_METHOD(std::size_t, numRecords, (), (const, override));
|
||||||
MOCK_CONST_METHOD1(getRoot, Nif::Record* (std::size_t));
|
MOCK_METHOD(Nif::Record*, getRoot, (std::size_t), (const, override));
|
||||||
MOCK_CONST_METHOD0(numRoots, std::size_t ());
|
MOCK_METHOD(std::size_t, numRoots, (), (const, override));
|
||||||
MOCK_CONST_METHOD1(getString, std::string (uint32_t));
|
MOCK_METHOD(std::string, getString, (uint32_t), (const, override));
|
||||||
MOCK_METHOD1(setUseSkinning, void (bool));
|
MOCK_METHOD(void, setUseSkinning, (bool), (override));
|
||||||
MOCK_CONST_METHOD0(getUseSkinning, bool ());
|
MOCK_METHOD(bool, getUseSkinning, (), (const, override));
|
||||||
MOCK_CONST_METHOD0(getFilename, std::string ());
|
MOCK_METHOD(std::string, getFilename, (), (const, override));
|
||||||
MOCK_CONST_METHOD0(getVersion, unsigned int ());
|
MOCK_METHOD(unsigned int, getVersion, (), (const, override));
|
||||||
MOCK_CONST_METHOD0(getUserVersion, unsigned int ());
|
MOCK_METHOD(unsigned int, getUserVersion, (), (const, override));
|
||||||
MOCK_CONST_METHOD0(getBethVersion, unsigned int ());
|
MOCK_METHOD(unsigned int, getBethVersion, (), (const, override));
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RecordMock : Nif::Record
|
struct RecordMock : Nif::Record
|
||||||
{
|
{
|
||||||
MOCK_METHOD1(read, void (Nif::NIFStream *nif));
|
MOCK_METHOD(void, read, (Nif::NIFStream *nif), (override));
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TestBulletNifLoader : Test
|
struct TestBulletNifLoader : Test
|
||||||
|
|
|
@ -151,7 +151,13 @@ add_component_dir (fallback
|
||||||
fallback validate
|
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
|
add_component_dir (crashcatcher
|
||||||
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 <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include <components/crashcatcher/crashcatcher.hpp>
|
#include <components/crashcatcher/crashcatcher.hpp>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
# include <components/crashcatcher/windows_crashcatcher.hpp>
|
||||||
# undef WIN32_LEAN_AND_MEAN
|
# undef WIN32_LEAN_AND_MEAN
|
||||||
# define WIN32_LEAN_AND_MEAN
|
# define WIN32_LEAN_AND_MEAN
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
|
@ -163,7 +165,6 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log";
|
const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log";
|
||||||
const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.log";
|
|
||||||
boost::filesystem::ofstream logfile;
|
boost::filesystem::ofstream logfile;
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -187,13 +188,18 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c
|
||||||
std::cerr.rdbuf (&cerrsb);
|
std::cerr.rdbuf (&cerrsb);
|
||||||
#endif
|
#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
|
// install the crash handler as soon as possible. note that the log path
|
||||||
// does not depend on config being read.
|
// does not depend on config being read.
|
||||||
crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / crashLogName).string());
|
crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / crashLogName).string());
|
||||||
|
#endif
|
||||||
ret = innerApplication(argc, argv);
|
ret = innerApplication(argc, argv);
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))
|
#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))
|
||||||
if (!isatty(fileno(stdin)))
|
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)
|
void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, int animflags)
|
||||||
{
|
{
|
||||||
assert(isTypeGeometry(nifNode->recType));
|
assert(isTypeGeometry(nifNode->recType));
|
||||||
osg::ref_ptr<osg::Drawable> drawable;
|
|
||||||
osg::ref_ptr<osg::Geometry> geom (new osg::Geometry);
|
osg::ref_ptr<osg::Geometry> geom (new osg::Geometry);
|
||||||
handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags);
|
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)
|
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
|
||||||
{
|
{
|
||||||
if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))
|
if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))
|
||||||
|
@ -1292,6 +1295,8 @@ namespace NifOsg
|
||||||
assert(isTypeGeometry(nifNode->recType));
|
assert(isTypeGeometry(nifNode->recType));
|
||||||
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
|
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
|
||||||
handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags);
|
handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags);
|
||||||
|
if (geometry->empty())
|
||||||
|
return;
|
||||||
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);
|
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);
|
||||||
rig->setSourceGeometry(geometry);
|
rig->setSourceGeometry(geometry);
|
||||||
rig->setName(nifNode->name);
|
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.
|
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]
|
[GUI]
|
||||||
font size = 16
|
font size = 16
|
||||||
|
|
|
@ -23,6 +23,7 @@ set(SHADER_FILES
|
||||||
shadows_fragment.glsl
|
shadows_fragment.glsl
|
||||||
shadowcasting_vertex.glsl
|
shadowcasting_vertex.glsl
|
||||||
shadowcasting_fragment.glsl
|
shadowcasting_fragment.glsl
|
||||||
|
vertexcolors.glsl
|
||||||
)
|
)
|
||||||
|
|
||||||
copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}")
|
copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}")
|
||||||
|
|
|
@ -1,85 +1,38 @@
|
||||||
#define MAX_LIGHTS 8
|
#define MAX_LIGHTS 8
|
||||||
|
|
||||||
uniform int colorMode;
|
void perLight(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal)
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
vec3 lightDir;
|
vec3 lightDir = gl_LightSource[lightIndex].position.xyz - viewPos * gl_LightSource[lightIndex].position.w;
|
||||||
float lightDistance;
|
float lightDistance = length(lightDir);
|
||||||
|
|
||||||
lightDir = gl_LightSource[lightIndex].position.xyz - (viewPos.xyz * gl_LightSource[lightIndex].position.w);
|
|
||||||
lightDistance = length(lightDir);
|
|
||||||
lightDir = normalize(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);
|
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;
|
ambientOut = gl_LightSource[lightIndex].ambient.xyz * illumination;
|
||||||
diffuseOut = diffuse.xyz * gl_LightSource[lightIndex].diffuse.xyz * max(dot(viewNormal.xyz, lightDir), 0.0) * illumination;
|
diffuseOut = gl_LightSource[lightIndex].diffuse.xyz * max(dot(viewNormal, lightDir), 0.0) * illumination;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PER_PIXEL_LIGHTING
|
#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
|
#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
|
#endif
|
||||||
{
|
{
|
||||||
vec4 diffuse;
|
vec3 ambientOut, diffuseOut;
|
||||||
vec3 ambient;
|
// This light gets added a second time in the loop to fix Mesa users' slowdown, so we need to negate its contribution here.
|
||||||
if (colorMode == ColorMode_AmbientAndDiffuse)
|
perLight(ambientOut, diffuseOut, 0, viewPos, viewNormal);
|
||||||
{
|
|
||||||
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);
|
|
||||||
#if PER_PIXEL_LIGHTING
|
#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
|
#else
|
||||||
shadowDiffuse = diffuseLight;
|
shadowDiffuse = diffuseOut;
|
||||||
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.
|
diffuseLight = -diffuseOut;
|
||||||
#endif
|
#endif
|
||||||
|
ambientLight = gl_LightModel.ambient.xyz;
|
||||||
for (int i=0; i<MAX_LIGHTS; ++i)
|
for (int i=0; i<MAX_LIGHTS; ++i)
|
||||||
{
|
{
|
||||||
perLight(ambientLight, diffuseLight, i, viewPos, viewNormal, diffuse, ambient);
|
perLight(ambientOut, diffuseOut, i, viewPos, viewNormal);
|
||||||
lightResult.xyz += ambientLight + diffuseLight;
|
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);
|
vec3 lightDir = normalize(gl_LightSource[0].position.xyz);
|
||||||
float NdotL = dot(viewNormal, lightDir);
|
float NdotL = dot(viewNormal, lightDir);
|
||||||
if (NdotL <= 0.0)
|
if (NdotL <= 0.0)
|
||||||
return vec3(0.,0.,0.);
|
return vec3(0.0);
|
||||||
vec3 halfVec = normalize(lightDir - viewDirection);
|
vec3 halfVec = normalize(lightDir - viewDirection);
|
||||||
float NdotH = dot(viewNormal, halfVec);
|
float NdotH = dot(viewNormal, halfVec);
|
||||||
return pow(max(NdotH, 0.0), max(1e-4, shininess)) * gl_LightSource[0].specular.xyz * matSpec;
|
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)
|
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
||||||
|
|
||||||
#if !PER_PIXEL_LIGHTING
|
#if !PER_PIXEL_LIGHTING
|
||||||
centroid varying vec4 lighting;
|
centroid varying vec3 passLighting;
|
||||||
centroid varying vec3 shadowDiffuseLighting;
|
centroid varying vec3 shadowDiffuseLighting;
|
||||||
#endif
|
#endif
|
||||||
centroid varying vec4 passColor;
|
|
||||||
varying vec3 passViewPos;
|
varying vec3 passViewPos;
|
||||||
varying vec3 passNormal;
|
varying vec3 passNormal;
|
||||||
|
|
||||||
|
#include "vertexcolors.glsl"
|
||||||
#include "shadows_fragment.glsl"
|
#include "shadows_fragment.glsl"
|
||||||
#include "lighting.glsl"
|
#include "lighting.glsl"
|
||||||
#include "parallax.glsl"
|
#include "parallax.glsl"
|
||||||
|
@ -152,21 +152,27 @@ void main()
|
||||||
|
|
||||||
#endif
|
#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
|
#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
|
#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
|
#else
|
||||||
gl_FragData[0] *= lighting + vec4(shadowDiffuseLighting * shadowing, 0);
|
lighting = max(lighting, 0.0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else
|
gl_FragData[0].xyz *= lighting;
|
||||||
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor, shadowing);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
alphaTest();
|
|
||||||
|
|
||||||
#if @envMap && !@preLightEnv
|
#if @envMap && !@preLightEnv
|
||||||
gl_FragData[0].xyz += texture2D(envMap, envTexCoordGen).xyz * envMapColor.xyz * envLuma;
|
gl_FragData[0].xyz += texture2D(envMap, envTexCoordGen).xyz * envMapColor.xyz * envLuma;
|
||||||
|
@ -182,11 +188,7 @@ void main()
|
||||||
vec3 matSpec = specTex.xyz;
|
vec3 matSpec = specTex.xyz;
|
||||||
#else
|
#else
|
||||||
float shininess = gl_FrontMaterial.shininess;
|
float shininess = gl_FrontMaterial.shininess;
|
||||||
vec3 matSpec;
|
vec3 matSpec = getSpecularColor().xyz;
|
||||||
if (colorMode == ColorMode_Specular)
|
|
||||||
matSpec = passColor.xyz;
|
|
||||||
else
|
|
||||||
matSpec = gl_FrontMaterial.specular.xyz;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (matSpec != vec3(0.0))
|
if (matSpec != vec3(0.0))
|
||||||
|
|
|
@ -43,13 +43,13 @@ varying float linearDepth;
|
||||||
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
||||||
|
|
||||||
#if !PER_PIXEL_LIGHTING
|
#if !PER_PIXEL_LIGHTING
|
||||||
centroid varying vec4 lighting;
|
centroid varying vec3 passLighting;
|
||||||
centroid varying vec3 shadowDiffuseLighting;
|
centroid varying vec3 shadowDiffuseLighting;
|
||||||
#endif
|
#endif
|
||||||
centroid varying vec4 passColor;
|
|
||||||
varying vec3 passViewPos;
|
varying vec3 passViewPos;
|
||||||
varying vec3 passNormal;
|
varying vec3 passNormal;
|
||||||
|
|
||||||
|
#include "vertexcolors.glsl"
|
||||||
#include "shadows_vertex.glsl"
|
#include "shadows_vertex.glsl"
|
||||||
|
|
||||||
#include "lighting.glsl"
|
#include "lighting.glsl"
|
||||||
|
@ -107,13 +107,17 @@ void main(void)
|
||||||
specularMapUV = (gl_TextureMatrix[@specularMapUV] * gl_MultiTexCoord@specularMapUV).xy;
|
specularMapUV = (gl_TextureMatrix[@specularMapUV] * gl_MultiTexCoord@specularMapUV).xy;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !PER_PIXEL_LIGHTING
|
|
||||||
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting);
|
|
||||||
#endif
|
|
||||||
passColor = gl_Color;
|
passColor = gl_Color;
|
||||||
passViewPos = viewPos.xyz;
|
passViewPos = viewPos.xyz;
|
||||||
passNormal = gl_Normal.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)
|
#if (@shadows_enabled)
|
||||||
setupShadowCoords(viewPos, viewNormal);
|
setupShadowCoords(viewPos, viewNormal);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,13 +18,13 @@ varying float linearDepth;
|
||||||
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
||||||
|
|
||||||
#if !PER_PIXEL_LIGHTING
|
#if !PER_PIXEL_LIGHTING
|
||||||
centroid varying vec4 lighting;
|
centroid varying vec3 passLighting;
|
||||||
centroid varying vec3 shadowDiffuseLighting;
|
centroid varying vec3 shadowDiffuseLighting;
|
||||||
#endif
|
#endif
|
||||||
centroid varying vec4 passColor;
|
|
||||||
varying vec3 passViewPos;
|
varying vec3 passViewPos;
|
||||||
varying vec3 passNormal;
|
varying vec3 passNormal;
|
||||||
|
|
||||||
|
#include "vertexcolors.glsl"
|
||||||
#include "shadows_fragment.glsl"
|
#include "shadows_fragment.glsl"
|
||||||
#include "lighting.glsl"
|
#include "lighting.glsl"
|
||||||
#include "parallax.glsl"
|
#include "parallax.glsl"
|
||||||
|
@ -68,30 +68,33 @@ void main()
|
||||||
gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a;
|
gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float shadowing = unshadowedLightRatio(linearDepth);
|
vec4 diffuseColor = getDiffuseColor();
|
||||||
|
gl_FragData[0].a *= diffuseColor.a;
|
||||||
|
|
||||||
|
float shadowing = unshadowedLightRatio(linearDepth);
|
||||||
|
vec3 lighting;
|
||||||
#if !PER_PIXEL_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
|
#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
|
#else
|
||||||
gl_FragData[0] *= lighting + vec4(shadowDiffuseLighting * shadowing, 0);
|
lighting = max(lighting, 0.0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else
|
gl_FragData[0].xyz *= lighting;
|
||||||
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor, shadowing);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if @specularMap
|
#if @specularMap
|
||||||
float shininess = 128.0; // TODO: make configurable
|
float shininess = 128.0; // TODO: make configurable
|
||||||
vec3 matSpec = vec3(diffuseTex.a);
|
vec3 matSpec = vec3(diffuseTex.a);
|
||||||
#else
|
#else
|
||||||
float shininess = gl_FrontMaterial.shininess;
|
float shininess = gl_FrontMaterial.shininess;
|
||||||
vec3 matSpec;
|
vec3 matSpec = getSpecularColor().xyz;
|
||||||
if (colorMode == ColorMode_Specular)
|
|
||||||
matSpec = passColor.xyz;
|
|
||||||
else
|
|
||||||
matSpec = gl_FrontMaterial.specular.xyz;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (matSpec != vec3(0.0))
|
if (matSpec != vec3(0.0))
|
||||||
|
|
|
@ -7,13 +7,13 @@ varying float linearDepth;
|
||||||
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
|
||||||
|
|
||||||
#if !PER_PIXEL_LIGHTING
|
#if !PER_PIXEL_LIGHTING
|
||||||
centroid varying vec4 lighting;
|
centroid varying vec3 passLighting;
|
||||||
centroid varying vec3 shadowDiffuseLighting;
|
centroid varying vec3 shadowDiffuseLighting;
|
||||||
#endif
|
#endif
|
||||||
centroid varying vec4 passColor;
|
|
||||||
varying vec3 passViewPos;
|
varying vec3 passViewPos;
|
||||||
varying vec3 passNormal;
|
varying vec3 passNormal;
|
||||||
|
|
||||||
|
#include "vertexcolors.glsl"
|
||||||
#include "shadows_vertex.glsl"
|
#include "shadows_vertex.glsl"
|
||||||
|
|
||||||
#include "lighting.glsl"
|
#include "lighting.glsl"
|
||||||
|
@ -31,13 +31,17 @@ void main(void)
|
||||||
vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);
|
vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !PER_PIXEL_LIGHTING
|
|
||||||
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting);
|
|
||||||
#endif
|
|
||||||
passColor = gl_Color;
|
passColor = gl_Color;
|
||||||
passNormal = gl_Normal.xyz;
|
passNormal = gl_Normal.xyz;
|
||||||
passViewPos = viewPos.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;
|
uv = gl_MultiTexCoord0.xy;
|
||||||
|
|
||||||
#if (@shadows_enabled)
|
#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