1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-27 03:35:27 +00:00

Merge branch 'master' of gitlab.com:openmw/openmw into lua_class_data

This commit is contained in:
Zackhasacat 2023-10-31 22:23:45 -05:00
commit 371eeddf85
89 changed files with 899 additions and 493 deletions

View File

@ -140,6 +140,7 @@ Programmers
Lordrea
Łukasz Gołębiewski (lukago)
Lukasz Gromanowski (lgro)
Mads Sandvei (Foal)
Marc Bouvier (CramitDeFrog)
Marcin Hulist (Gohan)
Mark Siewert (mark76)

View File

@ -8,7 +8,9 @@
Bug #4204: Dead slaughterfish doesn't float to water surface after loading saved game
Bug #4207: RestoreHealth/Fatigue spells have a huge priority even if a success chance is near 0
Bug #4382: Sound output device does not change when it should
Bug #4508: Can't stack enchantment buffs from different instances of the same self-cast generic magic apparel
Bug #4610: Casting a Bound Weapon spell cancels the casting animation by equipping the weapon prematurely
Bug #4742: Actors with wander never stop walking after Loopgroup Walkforward
Bug #4754: Stack of ammunition cannot be equipped partially
Bug #4816: GetWeaponDrawn returns 1 before weapon is attached
Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses
@ -33,6 +35,7 @@
Bug #6807: Ultimate Galleon is not working properly
Bug #6893: Lua: Inconsistent behavior with actors affected by Disable and SetDelete commands
Bug #6894: Added item combines with equipped stack instead of creating a new unequipped stack
Bug #6932: Creatures flee from my followers and we have to chase after them
Bug #6939: OpenMW-CS: ID columns are too short
Bug #6949: Sun Damage effect doesn't work in quasi exteriors
Bug #6964: Nerasa Dralor Won't Follow
@ -58,6 +61,7 @@
Bug #7134: Saves with an invalid last generated RefNum can be loaded
Bug #7163: Myar Aranath: Wheat breaks the GUI
Bug #7172: Current music playlist continues playing indefinitely if next playlist is empty
Bug #7204: Missing actor scripts freeze the game
Bug #7229: Error marker loading failure is not handled
Bug #7243: Supporting loading external files from VFS from esm files
Bug #7284: "Your weapon has no effect." message doesn't always show when the player character attempts to attack
@ -70,6 +74,7 @@
Bug #7450: Evading obstacles does not work for actors missing certain animations
Bug #7459: Icons get stacked on the cursor when picking up multiple items simultaneously
Bug #7472: Crash when enchanting last projectiles
Bug #7502: Data directories dialog (0.48.0) forces adding subdirectory instead of intended directory
Bug #7505: Distant terrain does not support sample size greater than cell size
Bug #7553: Faction reaction loading is incorrect
Bug #7557: Terrain::ChunkManager::createChunk is called twice for the same position, lod on initial loading
@ -79,6 +84,12 @@
Bug #7609: ForceGreeting should not open dialogue for werewolves
Bug #7611: Beast races' idle animations slide after turning or jumping in place
Bug #7630: Charm can be cast on creatures
Bug #7631: Cannot trade with/talk to Creeper or Mudcrab Merchant when they're fleeing
Bug #7636: Animations bug out when switching between 1st and 3rd person, while playing a scripted animation
Bug #7637: Actors can sometimes move while playing scripted animations
Bug #7639: NPCs don't use hand-to-hand if their other melee skills were damaged during combat
Bug #7642: Items in repair and recharge menus aren't sorted alphabetically
Bug #7647: NPC walk cycle bugs after greeting player
Feature #3537: Shader-based water ripples
Feature #5492: Let rain and snow collide with statics
Feature #6149: Dehardcode Lua API_REVISION
@ -109,7 +120,9 @@
Feature #7546: Start the game on Fredas
Feature #7568: Uninterruptable scripted music
Feature #7618: Show the player character's health in the save details
Feature #7625: Add some missing console error outputs
Feature #7634: Support NiParticleBomb
Feature #7652: Sort inactive post processing shaders list properly
Task #5896: Do not use deprecated MyGUI properties
Task #7113: Move from std::atoi to std::from_char
Task #7117: Replace boost::scoped_array with std::vector

View File

@ -40,8 +40,35 @@ namespace
{
void contentSubdirs(const QString& path, QStringList& dirs)
{
QStringList fileFilter{ "*.esm", "*.esp", "*.omwaddon", "*.bsa", "*.ba2", "*.omwscripts" };
QStringList dirFilter{ "bookart", "icons", "meshes", "music", "sound", "textures" };
static const QStringList fileFilter{
"*.esm",
"*.esp",
"*.bsa",
"*.ba2",
"*.omwgame",
"*.omwaddon",
"*.omwscripts",
};
static const QStringList dirFilter{
"animations",
"bookart",
"fonts",
"icons",
"interface",
"l10n",
"meshes",
"music",
"mygui",
"scripts",
"shaders",
"sound",
"splash",
"strings",
"textures",
"trees",
"video",
};
QDir currentDir(path);
if (!currentDir.entryInfoList(fileFilter, QDir::Files).empty()
@ -587,18 +614,6 @@ void Launcher::DataFilesPage::updateCloneProfileOkButton(const QString& text)
mCloneProfileDialog->setOkButtonEnabled(!text.isEmpty() && ui.profilesComboBox->findText(text) == -1);
}
QString Launcher::DataFilesPage::selectDirectory()
{
QFileDialog fileDialog(this);
fileDialog.setFileMode(QFileDialog::Directory);
fileDialog.setOptions(QFileDialog::Option::ShowDirsOnly | QFileDialog::Option::ReadOnly);
if (fileDialog.exec() == QDialog::Rejected)
return {};
return QDir(fileDialog.selectedFiles()[0]).canonicalPath();
}
void Launcher::DataFilesPage::addSubdirectories(bool append)
{
int selectedRow = append ? ui.directoryListWidget->count() : ui.directoryListWidget->currentRow();
@ -606,22 +621,30 @@ void Launcher::DataFilesPage::addSubdirectories(bool append)
if (selectedRow == -1)
return;
const auto rootDir = selectDirectory();
if (rootDir.isEmpty())
QString rootPath = QFileDialog::getExistingDirectory(
this, tr("Select Directory"), QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::Option::ReadOnly);
if (rootPath.isEmpty())
return;
QStringList subdirs;
contentSubdirs(rootDir, subdirs);
const QDir rootDir(rootPath);
rootPath = rootDir.canonicalPath();
if (subdirs.empty())
QStringList subdirs;
contentSubdirs(rootPath, subdirs);
// Always offer to append the root directory just in case
if (subdirs.isEmpty() || subdirs[0] != rootPath)
subdirs.prepend(rootPath);
else if (subdirs.size() == 1)
{
// we didn't find anything that looks like a content directory, add directory selected by user
if (ui.directoryListWidget->findItems(rootDir, Qt::MatchFixedString).isEmpty())
{
ui.directoryListWidget->addItem(rootDir);
mNewDataDirs.push_back(rootDir);
refreshDataFilesView();
}
// We didn't find anything else that looks like a content directory
// Automatically add the directory selected by user
if (!ui.directoryListWidget->findItems(rootPath, Qt::MatchFixedString).isEmpty())
return;
ui.directoryListWidget->addItem(rootPath);
mNewDataDirs.push_back(rootPath);
refreshDataFilesView();
return;
}

View File

@ -131,7 +131,6 @@ namespace Launcher
void reloadCells(QStringList selectedFiles);
void refreshDataFilesView();
void updateNavMeshProgress(int minDataSize);
QString selectDirectory();
/**
* Returns the file paths of all selected content files

View File

@ -96,32 +96,29 @@ bool Launcher::GraphicsPage::loadSettings()
// Visuals
int vsync = Settings::Manager::getInt("vsync mode", "Video");
if (vsync < 0 || vsync > 2)
vsync = 0;
const int vsync = Settings::video().mVsyncMode;
vSyncComboBox->setCurrentIndex(vsync);
size_t windowMode = static_cast<size_t>(Settings::Manager::getInt("window mode", "Video"));
if (windowMode > static_cast<size_t>(Settings::WindowMode::Windowed))
windowMode = 0;
windowModeComboBox->setCurrentIndex(windowMode);
slotFullScreenChanged(windowMode);
const Settings::WindowMode windowMode = Settings::video().mWindowMode;
if (Settings::Manager::getBool("window border", "Video"))
windowModeComboBox->setCurrentIndex(static_cast<int>(windowMode));
handleWindowModeChange(windowMode);
if (Settings::video().mWindowBorder)
windowBorderCheckBox->setCheckState(Qt::Checked);
// aaValue is the actual value (0, 1, 2, 4, 8, 16)
int aaValue = Settings::Manager::getInt("antialiasing", "Video");
const int aaValue = Settings::video().mAntialiasing;
// aaIndex is the index into the allowed values in the pull down.
int aaIndex = antiAliasingComboBox->findText(QString::number(aaValue));
const int aaIndex = antiAliasingComboBox->findText(QString::number(aaValue));
if (aaIndex != -1)
antiAliasingComboBox->setCurrentIndex(aaIndex);
int width = Settings::Manager::getInt("resolution x", "Video");
int height = Settings::Manager::getInt("resolution y", "Video");
const int width = Settings::video().mResolutionX;
const int height = Settings::video().mResolutionY;
QString resolution = QString::number(width) + QString(" x ") + QString::number(height);
screenComboBox->setCurrentIndex(Settings::Manager::getInt("screen", "Video"));
screenComboBox->setCurrentIndex(Settings::video().mScreen);
int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
@ -137,7 +134,7 @@ bool Launcher::GraphicsPage::loadSettings()
customHeightSpinBox->setValue(height);
}
float fpsLimit = Settings::Manager::getFloat("framerate limit", "Video");
const float fpsLimit = Settings::video().mFramerateLimit;
if (fpsLimit != 0)
{
framerateLimitCheckBox->setCheckState(Qt::Checked);
@ -198,23 +195,10 @@ void Launcher::GraphicsPage::saveSettings()
{
// Visuals
// Ensure we only set the new settings if they changed. This is to avoid cluttering the
// user settings file (which by definition should only contain settings the user has touched)
int cVSync = vSyncComboBox->currentIndex();
if (cVSync != Settings::Manager::getInt("vsync mode", "Video"))
Settings::Manager::setInt("vsync mode", "Video", cVSync);
int cWindowMode = windowModeComboBox->currentIndex();
if (cWindowMode != Settings::Manager::getInt("window mode", "Video"))
Settings::Manager::setInt("window mode", "Video", cWindowMode);
bool cWindowBorder = windowBorderCheckBox->checkState();
if (cWindowBorder != Settings::Manager::getBool("window border", "Video"))
Settings::Manager::setBool("window border", "Video", cWindowBorder);
int cAAValue = antiAliasingComboBox->currentText().toInt();
if (cAAValue != Settings::Manager::getInt("antialiasing", "Video"))
Settings::Manager::setInt("antialiasing", "Video", cAAValue);
Settings::video().mVsyncMode.set(static_cast<SDLUtil::VSyncMode>(vSyncComboBox->currentIndex()));
Settings::video().mWindowMode.set(static_cast<Settings::WindowMode>(windowModeComboBox->currentIndex()));
Settings::video().mWindowBorder.set(windowBorderCheckBox->checkState() == Qt::Checked);
Settings::video().mAntialiasing.set(antiAliasingComboBox->currentText().toInt());
int cWidth = 0;
int cHeight = 0;
@ -234,25 +218,17 @@ void Launcher::GraphicsPage::saveSettings()
cHeight = customHeightSpinBox->value();
}
if (cWidth != Settings::Manager::getInt("resolution x", "Video"))
Settings::Manager::setInt("resolution x", "Video", cWidth);
if (cHeight != Settings::Manager::getInt("resolution y", "Video"))
Settings::Manager::setInt("resolution y", "Video", cHeight);
int cScreen = screenComboBox->currentIndex();
if (cScreen != Settings::Manager::getInt("screen", "Video"))
Settings::Manager::setInt("screen", "Video", cScreen);
Settings::video().mResolutionX.set(cWidth);
Settings::video().mResolutionY.set(cHeight);
Settings::video().mScreen.set(screenComboBox->currentIndex());
if (framerateLimitCheckBox->checkState() != Qt::Unchecked)
{
float cFpsLimit = framerateLimitSpinBox->value();
if (cFpsLimit != Settings::Manager::getFloat("framerate limit", "Video"))
Settings::Manager::setFloat("framerate limit", "Video", cFpsLimit);
Settings::video().mFramerateLimit.set(framerateLimitSpinBox->value());
}
else if (Settings::Manager::getFloat("framerate limit", "Video") != 0)
else if (Settings::video().mFramerateLimit != 0)
{
Settings::Manager::setFloat("framerate limit", "Video", 0);
Settings::video().mFramerateLimit.set(0);
}
// Lighting
@ -392,8 +368,12 @@ void Launcher::GraphicsPage::screenChanged(int screen)
void Launcher::GraphicsPage::slotFullScreenChanged(int mode)
{
if (mode == static_cast<int>(Settings::WindowMode::Fullscreen)
|| mode == static_cast<int>(Settings::WindowMode::WindowedFullscreen))
handleWindowModeChange(static_cast<Settings::WindowMode>(mode));
}
void Launcher::GraphicsPage::handleWindowModeChange(Settings::WindowMode mode)
{
if (mode == Settings::WindowMode::Fullscreen || mode == Settings::WindowMode::WindowedFullscreen)
{
standardRadioButton->toggle();
customRadioButton->setEnabled(false);

View File

@ -3,7 +3,7 @@
#include "ui_graphicspage.h"
#include <components/settings/settings.hpp>
#include <components/settings/windowmode.hpp>
namespace Files
{
@ -40,6 +40,7 @@ namespace Launcher
static QRect getMaximumResolution();
bool setupSDL();
void handleWindowModeChange(Settings::WindowMode state);
};
}
#endif

View File

@ -1,13 +1,5 @@
#include "maindialog.hpp"
#include <components/debug/debuglog.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/files/conversion.hpp>
#include <components/files/qtconversion.hpp>
#include <components/misc/helpviewer.hpp>
#include <components/misc/utf8qtextstream.hpp>
#include <components/version/version.hpp>
#include <QCloseEvent>
#include <QDir>
#include <QMessageBox>
@ -15,10 +7,15 @@
#include <QTime>
#include <components/debug/debugging.hpp>
#include <components/debug/debuglog.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/files/conversion.hpp>
#include <components/files/qtconfigpath.hpp>
#include <components/files/qtconversion.hpp>
#include <components/misc/helpviewer.hpp>
#include <components/misc/utf8qtextstream.hpp>
#include <components/settings/settings.hpp>
#include <components/version/version.hpp>
#include "datafilespage.hpp"
#include "graphicspage.hpp"

View File

@ -313,6 +313,8 @@ bool OMW::Engine::frame(float frametime)
mLuaManager->reportStats(frameNumber, *stats);
}
mStereoManager->updateSettings(Settings::camera().mNearClip, Settings::camera().mViewingDistance);
mViewer->eventTraversal();
mViewer->updateTraversal();
@ -452,14 +454,13 @@ void OMW::Engine::setSkipMenu(bool skipMenu, bool newGame)
void OMW::Engine::createWindow()
{
int screen = Settings::Manager::getInt("screen", "Video");
int width = Settings::Manager::getInt("resolution x", "Video");
int height = Settings::Manager::getInt("resolution y", "Video");
Settings::WindowMode windowMode
= static_cast<Settings::WindowMode>(Settings::Manager::getInt("window mode", "Video"));
bool windowBorder = Settings::Manager::getBool("window border", "Video");
int vsync = Settings::Manager::getInt("vsync mode", "Video");
unsigned int antialiasing = std::max(0, Settings::Manager::getInt("antialiasing", "Video"));
const int screen = Settings::video().mScreen;
const int width = Settings::video().mResolutionX;
const int height = Settings::video().mResolutionY;
const Settings::WindowMode windowMode = Settings::video().mWindowMode;
const bool windowBorder = Settings::video().mWindowBorder;
const SDLUtil::VSyncMode vsync = Settings::video().mVsyncMode;
unsigned antialiasing = static_cast<unsigned>(Settings::video().mAntialiasing);
int pos_x = SDL_WINDOWPOS_CENTERED_DISPLAY(screen), pos_y = SDL_WINDOWPOS_CENTERED_DISPLAY(screen);
@ -482,8 +483,7 @@ void OMW::Engine::createWindow()
if (!windowBorder)
flags |= SDL_WINDOW_BORDERLESS;
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS,
Settings::Manager::getBool("minimize on focus loss", "Video") ? "1" : "0");
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, Settings::video().mMinimizeOnFocusLoss ? "1" : "0");
checkSDLError(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8));
checkSDLError(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8));
@ -513,7 +513,7 @@ void OMW::Engine::createWindow()
Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying "
<< antialiasing / 2;
antialiasing /= 2;
Settings::Manager::setInt("antialiasing", "Video", antialiasing);
Settings::video().mAntialiasing.set(antialiasing);
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
continue;
}
@ -560,7 +560,7 @@ void OMW::Engine::createWindow()
SDL_DestroyWindow(mWindow);
mWindow = nullptr;
antialiasing /= 2;
Settings::Manager::setInt("antialiasing", "Video", antialiasing);
Settings::video().mAntialiasing.set(antialiasing);
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
continue;
}
@ -593,7 +593,63 @@ void OMW::Engine::createWindow()
realizeOperations->add(mSelectColorFormatOperation);
if (Stereo::getStereo())
realizeOperations->add(new Stereo::InitializeStereoOperation());
{
Stereo::Settings settings;
settings.mMultiview = Settings::stereo().mMultiview;
settings.mAllowDisplayListsForMultiview = Settings::stereo().mAllowDisplayListsForMultiview;
settings.mSharedShadowMaps = Settings::stereo().mSharedShadowMaps;
if (Settings::stereo().mUseCustomView)
{
const osg::Vec3 leftEyeOffset(Settings::stereoView().mLeftEyeOffsetX,
Settings::stereoView().mLeftEyeOffsetY, Settings::stereoView().mLeftEyeOffsetZ);
const osg::Quat leftEyeOrientation(Settings::stereoView().mLeftEyeOrientationX,
Settings::stereoView().mLeftEyeOrientationY, Settings::stereoView().mLeftEyeOrientationZ,
Settings::stereoView().mLeftEyeOrientationW);
const osg::Vec3 rightEyeOffset(Settings::stereoView().mRightEyeOffsetX,
Settings::stereoView().mRightEyeOffsetY, Settings::stereoView().mRightEyeOffsetZ);
const osg::Quat rightEyeOrientation(Settings::stereoView().mRightEyeOrientationX,
Settings::stereoView().mRightEyeOrientationY, Settings::stereoView().mRightEyeOrientationZ,
Settings::stereoView().mRightEyeOrientationW);
settings.mCustomView = Stereo::CustomView{
.mLeft = Stereo::View{
.pose = Stereo::Pose{
.position = leftEyeOffset,
.orientation = leftEyeOrientation,
},
.fov = Stereo::FieldOfView{
.angleLeft = Settings::stereoView().mLeftEyeFovLeft,
.angleRight = Settings::stereoView().mLeftEyeFovRight,
.angleUp = Settings::stereoView().mLeftEyeFovUp,
.angleDown = Settings::stereoView().mLeftEyeFovDown,
},
},
.mRight = Stereo::View{
.pose = Stereo::Pose{
.position = rightEyeOffset,
.orientation = rightEyeOrientation,
},
.fov = Stereo::FieldOfView{
.angleLeft = Settings::stereoView().mRightEyeFovLeft,
.angleRight = Settings::stereoView().mRightEyeFovRight,
.angleUp = Settings::stereoView().mRightEyeFovUp,
.angleDown = Settings::stereoView().mRightEyeFovDown,
},
},
};
}
if (Settings::stereo().mUseCustomEyeResolution)
settings.mEyeResolution
= osg::Vec2i(Settings::stereoView().mEyeResolutionX, Settings::stereoView().mEyeResolutionY);
realizeOperations->add(new Stereo::InitializeStereoOperation(settings));
}
mViewer->realize();
mGlMaxTextureImageUnits = identifyOp->getMaxTextureImageUnits();
@ -632,9 +688,9 @@ void OMW::Engine::prepareEngine()
mStateManager = std::make_unique<MWState::StateManager>(mCfgMgr.getUserDataPath() / "saves", mContentFiles);
mEnvironment.setStateManager(*mStateManager);
bool stereoEnabled
= Settings::Manager::getBool("stereo enabled", "Stereo") || osg::DisplaySettings::instance().get()->getStereo();
mStereoManager = std::make_unique<Stereo::Manager>(mViewer, stereoEnabled);
const bool stereoEnabled = Settings::stereo().mStereoEnabled || osg::DisplaySettings::instance().get()->getStereo();
mStereoManager = std::make_unique<Stereo::Manager>(
mViewer, stereoEnabled, Settings::camera().mNearClip, Settings::camera().mViewingDistance);
osg::ref_ptr<osg::Group> rootNode(new osg::Group);
mViewer->setSceneData(rootNode);
@ -866,7 +922,7 @@ void OMW::Engine::go()
// Do not try to outsmart the OS thread scheduler (see bug #4785).
mViewer->setUseConfigureAffinity(false);
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));
mEnvironment.setFrameRateLimit(Settings::video().mFramerateLimit);
prepareEngine();

View File

@ -14,6 +14,7 @@
#include <components/esm4/loadingr.hpp>
#include <components/esm4/loadligh.hpp>
#include <components/esm4/loadmisc.hpp>
#include <components/esm4/loadmstt.hpp>
#include <components/esm4/loadnpc.hpp>
#include <components/esm4/loadstat.hpp>
#include <components/esm4/loadterm.hpp>
@ -85,6 +86,7 @@ namespace MWClass
ESM4Named<ESM4::Ingredient>::registerSelf();
ESM4Light::registerSelf();
ESM4Named<ESM4::MiscItem>::registerSelf();
ESM4Named<ESM4::MovableStatic>::registerSelf();
ESM4Npc::registerSelf();
ESM4Named<ESM4::Potion>::registerSelf();
ESM4Static::registerSelf();

View File

@ -464,18 +464,20 @@ namespace MWClass
}
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const MWMechanics::AiSequence& aiSequence = stats.getAiSequence();
const bool isInCombat = aiSequence.isInCombat();
if (stats.isDead())
{
// by default user can loot friendly actors during death animation
if (Settings::game().mCanLootDuringDeathAnimation && !stats.getAiSequence().isInCombat())
if (Settings::game().mCanLootDuringDeathAnimation && !isInCombat)
return std::make_unique<MWWorld::ActionOpen>(ptr);
// otherwise wait until death animation
if (stats.isDeathAnimationFinished())
return std::make_unique<MWWorld::ActionOpen>(ptr);
}
else if (!stats.getAiSequence().isInCombat() && !stats.getKnockedDown())
else if ((!isInCombat || aiSequence.isFleeing()) && !stats.getKnockedDown())
return std::make_unique<MWWorld::ActionTalk>(ptr);
// Tribunal and some mod companions oddly enough must use open action as fallback
@ -570,7 +572,8 @@ namespace MWClass
if (customData.mCreatureStats.isDead() && customData.mCreatureStats.isDeathAnimationFinished())
return true;
return !customData.mCreatureStats.getAiSequence().isInCombat();
const MWMechanics::AiSequence& aiSeq = customData.mCreatureStats.getAiSequence();
return !aiSeq.isInCombat() || aiSeq.isFleeing();
}
MWGui::ToolTipInfo Creature::getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const

View File

@ -138,17 +138,17 @@ namespace MWClass
{
}
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override { return true; }
MWGui::ToolTipInfo getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const override
{
return ESM4Impl::getToolTipInfo(ptr.get<Record>()->mBase->mFullName, count);
}
std::string_view getName(const MWWorld::ConstPtr& ptr) const override
{
return ptr.get<Record>()->mBase->mFullName;
}
MWGui::ToolTipInfo getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const override
{
return ESM4Impl::getToolTipInfo(getName(ptr), count);
}
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override { return !getName(ptr).empty(); }
};
}

View File

@ -128,6 +128,11 @@ namespace MWGui
mLines.swap(lines);
std::stable_sort(mLines.begin(), mLines.end(),
[](const MWGui::ItemChargeView::Line& a, const MWGui::ItemChargeView::Line& b) {
return Misc::StringUtils::ciLess(a.mText->getCaption().asUTF8(), b.mText->getCaption().asUTF8());
});
layoutWidgets();
}

View File

@ -89,7 +89,7 @@ namespace
{
if (!Settings::map().mAllowZooming)
return Constants::CellGridRadius;
if (!Settings::Manager::getBool("distant terrain", "Terrain"))
if (!Settings::terrain().mDistantTerrain)
return Constants::CellGridRadius;
const int viewingDistanceInCells = Settings::camera().mViewingDistance / Constants::CellSizeInUnits;
return std::clamp(

View File

@ -47,6 +47,8 @@ namespace MWGui
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor;
std::vector<std::tuple<std::string, int, MWWorld::Ptr>> items;
for (MWWorld::ContainerStoreIterator iter(store.begin(categories)); iter != store.end(); ++iter)
{
if (iter->getClass().hasItemHealth(*iter))
@ -76,22 +78,31 @@ namespace MWGui
name += " - " + MyGUI::utility::toString(price)
+ MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>().find("sgp")->mValue.getString();
MyGUI::Button* button = mList->createWidget<MyGUI::Button>(price <= playerGold
? "SandTextButton"
: "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip
0, currentY, 0, lineHeight, MyGUI::Align::Default);
currentY += lineHeight;
button->setUserString("Price", MyGUI::utility::toString(price));
button->setUserData(MWWorld::Ptr(*iter));
button->setCaptionWithReplacing(name);
button->setSize(mList->getWidth(), lineHeight);
button->eventMouseWheel += MyGUI::newDelegate(this, &MerchantRepair::onMouseWheel);
button->setUserString("ToolTipType", "ItemPtr");
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick);
items.emplace_back(name, price, *iter);
}
}
std::stable_sort(items.begin(), items.end(),
[](const auto& a, const auto& b) { return Misc::StringUtils::ciLess(std::get<0>(a), std::get<0>(b)); });
for (const auto& [name, price, ptr] : items)
{
MyGUI::Button* button = mList->createWidget<MyGUI::Button>(price <= playerGold
? "SandTextButton"
: "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip
0, currentY, 0, lineHeight, MyGUI::Align::Default);
currentY += lineHeight;
button->setUserString("Price", MyGUI::utility::toString(price));
button->setUserData(MWWorld::Ptr(ptr));
button->setCaptionWithReplacing(name);
button->setSize(mList->getWidth(), lineHeight);
button->eventMouseWheel += MyGUI::newDelegate(this, &MerchantRepair::onMouseWheel);
button->setUserString("ToolTipType", "ItemPtr");
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick);
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the
// scrollbar is hidden
mList->setVisibleVScroll(false);

View File

@ -20,6 +20,7 @@
#include <components/fx/technique.hpp>
#include <components/fx/widgets.hpp>
#include <components/misc/strings/algorithm.hpp>
#include <components/misc/utf8stream.hpp>
#include <components/widgets/box.hpp>
@ -421,7 +422,12 @@ namespace MWGui
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
std::vector<std::string> techniques;
for (const auto& [name, _] : processor->getTechniqueMap())
techniques.push_back(name);
std::sort(techniques.begin(), techniques.end(), Misc::StringUtils::ciLess);
for (const std::string& name : techniques)
{
auto technique = processor->loadTechnique(name);

View File

@ -49,7 +49,7 @@ namespace MWGui
= new SortFilterItemModel(std::make_unique<InventoryItemModel>(MWMechanics::getPlayer()));
model->setFilter(SortFilterItemModel::Filter_OnlyRepairable);
mRepairBox->setModel(model);
mRepairBox->update();
// Reset scrollbars
mRepairBox->resetScrollbars();
}

View File

@ -258,7 +258,7 @@ namespace MWGui
, mKeyboardMode(true)
, mCurrentPage(-1)
{
bool terrain = Settings::Manager::getBool("distant terrain", "Terrain");
const bool terrain = Settings::terrain().mDistantTerrain;
const std::string_view widgetName = terrain ? "RenderingDistanceSlider" : "LargeRenderingDistanceSlider";
MyGUI::Widget* unusedSlider;
getWidget(unusedSlider, widgetName);
@ -354,7 +354,7 @@ namespace MWGui
+= MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings);
// fill resolution list
int screen = Settings::Manager::getInt("screen", "Video");
const int screen = Settings::video().mScreen;
int numDisplayModes = SDL_GetNumDisplayModes(screen);
std::vector<std::pair<int, int>> resolutions;
for (int i = 0; i < numDisplayModes; i++)
@ -396,8 +396,7 @@ namespace MWGui
updateMaxLightsComboBox(mMaxLights);
Settings::WindowMode windowMode
= static_cast<Settings::WindowMode>(Settings::Manager::getInt("window mode", "Video"));
const Settings::WindowMode windowMode = Settings::video().mWindowMode;
mWindowBorderButton->setEnabled(
windowMode != Settings::WindowMode::Fullscreen && windowMode != Settings::WindowMode::WindowedFullscreen);
@ -491,8 +490,8 @@ namespace MWGui
int resX, resY;
parseResolution(resX, resY, resStr);
Settings::Manager::setInt("resolution x", "Video", resX);
Settings::Manager::setInt("resolution y", "Video", resY);
Settings::video().mResolutionX.set(resX);
Settings::video().mResolutionY.set(resY);
apply();
}
@ -506,8 +505,8 @@ namespace MWGui
{
mResolutionList->setIndexSelected(MyGUI::ITEM_NONE);
int currentX = Settings::Manager::getInt("resolution x", "Video");
int currentY = Settings::Manager::getInt("resolution y", "Video");
const int currentX = Settings::video().mResolutionX;
const int currentY = Settings::video().mResolutionY;
for (size_t i = 0; i < mResolutionList->getItemCount(); ++i)
{
@ -591,23 +590,22 @@ namespace MWGui
"#{OMWEngine:ChangeRequiresRestart}", { "#{Interface:OK}" }, true);
}
void SettingsWindow::onVSyncModeChanged(MyGUI::ComboBox* _sender, size_t pos)
void SettingsWindow::onVSyncModeChanged(MyGUI::ComboBox* sender, size_t pos)
{
if (pos == MyGUI::ITEM_NONE)
return;
int index = static_cast<int>(_sender->getIndexSelected());
Settings::Manager::setInt("vsync mode", "Video", index);
Settings::video().mVsyncMode.set(static_cast<SDLUtil::VSyncMode>(sender->getIndexSelected()));
apply();
}
void SettingsWindow::onWindowModeChanged(MyGUI::ComboBox* _sender, size_t pos)
void SettingsWindow::onWindowModeChanged(MyGUI::ComboBox* sender, size_t pos)
{
if (pos == MyGUI::ITEM_NONE)
return;
int index = static_cast<int>(_sender->getIndexSelected());
if (index == static_cast<size_t>(Settings::WindowMode::WindowedFullscreen))
const Settings::WindowMode windowMode = static_cast<Settings::WindowMode>(sender->getIndexSelected());
if (windowMode == Settings::WindowMode::WindowedFullscreen)
{
mResolutionList->setEnabled(false);
mWindowModeHint->setVisible(true);
@ -618,12 +616,12 @@ namespace MWGui
mWindowModeHint->setVisible(false);
}
if (index == static_cast<size_t>(Settings::WindowMode::Windowed))
if (windowMode == Settings::WindowMode::Windowed)
mWindowBorderButton->setEnabled(true);
else
mWindowBorderButton->setEnabled(false);
Settings::Manager::setInt("window mode", "Video", index);
Settings::video().mWindowMode.set(windowMode);
apply();
}
@ -849,14 +847,12 @@ namespace MWGui
void SettingsWindow::updateWindowModeSettings()
{
size_t index = static_cast<size_t>(Settings::Manager::getInt("window mode", "Video"));
const Settings::WindowMode windowMode = Settings::video().mWindowMode;
const std::size_t windowModeIndex = static_cast<std::size_t>(windowMode);
if (index > static_cast<size_t>(Settings::WindowMode::Windowed))
index = MyGUI::ITEM_NONE;
mWindowModeList->setIndexSelected(windowModeIndex);
mWindowModeList->setIndexSelected(index);
if (index != static_cast<size_t>(Settings::WindowMode::Windowed) && index != MyGUI::ITEM_NONE)
if (windowMode != Settings::WindowMode::Windowed && windowModeIndex != MyGUI::ITEM_NONE)
{
// check if this resolution is supported in fullscreen
if (mResolutionList->getIndexSelected() != MyGUI::ITEM_NONE)
@ -864,8 +860,8 @@ namespace MWGui
const std::string& resStr = mResolutionList->getItemNameAt(mResolutionList->getIndexSelected());
int resX, resY;
parseResolution(resX, resY, resStr);
Settings::Manager::setInt("resolution x", "Video", resX);
Settings::Manager::setInt("resolution y", "Video", resY);
Settings::video().mResolutionX.set(resX);
Settings::video().mResolutionY.set(resY);
}
bool supported = false;
@ -882,8 +878,7 @@ namespace MWGui
fallbackY = resY;
}
if (resX == Settings::Manager::getInt("resolution x", "Video")
&& resY == Settings::Manager::getInt("resolution y", "Video"))
if (resX == Settings::video().mResolutionX && resY == Settings::video().mResolutionY)
supported = true;
}
@ -891,26 +886,21 @@ namespace MWGui
{
if (fallbackX != 0 && fallbackY != 0)
{
Settings::Manager::setInt("resolution x", "Video", fallbackX);
Settings::Manager::setInt("resolution y", "Video", fallbackY);
Settings::video().mResolutionX.set(fallbackX);
Settings::video().mResolutionY.set(fallbackY);
}
}
mWindowBorderButton->setEnabled(false);
}
if (index == static_cast<size_t>(Settings::WindowMode::WindowedFullscreen))
if (windowMode == Settings::WindowMode::WindowedFullscreen)
mResolutionList->setEnabled(false);
}
void SettingsWindow::updateVSyncModeSettings()
{
int index = static_cast<size_t>(Settings::Manager::getInt("vsync mode", "Video"));
if (index < 0 || index > 2)
index = 0;
mVSyncModeList->setIndexSelected(index);
mVSyncModeList->setIndexSelected(static_cast<size_t>(Settings::video().mVsyncMode));
}
void SettingsWindow::layoutControlsBox()

View File

@ -294,8 +294,7 @@ namespace MWGui
+= MyGUI::newDelegate(this, &WindowManager::onClipboardRequested);
mVideoWrapper = std::make_unique<SDLUtil::VideoWrapper>(window, viewer);
mVideoWrapper->setGammaContrast(
Settings::Manager::getFloat("gamma", "Video"), Settings::Manager::getFloat("contrast", "Video"));
mVideoWrapper->setGammaContrast(Settings::video().mGamma, Settings::video().mContrast);
if (useShaders)
mGuiPlatform->getRenderManagerPtr()->enableShaders(mResourceSystem->getSceneManager()->getShaderManager());
@ -1157,25 +1156,22 @@ namespace MWGui
changeRes = true;
else if (setting.first == "Video" && setting.second == "vsync mode")
mVideoWrapper->setSyncToVBlank(Settings::Manager::getInt("vsync mode", "Video"));
mVideoWrapper->setSyncToVBlank(Settings::video().mVsyncMode);
else if (setting.first == "Video" && (setting.second == "gamma" || setting.second == "contrast"))
mVideoWrapper->setGammaContrast(
Settings::Manager::getFloat("gamma", "Video"), Settings::Manager::getFloat("contrast", "Video"));
mVideoWrapper->setGammaContrast(Settings::video().mGamma, Settings::video().mContrast);
}
if (changeRes)
{
mVideoWrapper->setVideoMode(Settings::Manager::getInt("resolution x", "Video"),
Settings::Manager::getInt("resolution y", "Video"),
static_cast<Settings::WindowMode>(Settings::Manager::getInt("window mode", "Video")),
Settings::Manager::getBool("window border", "Video"));
mVideoWrapper->setVideoMode(Settings::video().mResolutionX, Settings::video().mResolutionY,
Settings::video().mWindowMode, Settings::video().mWindowBorder);
}
}
void WindowManager::windowResized(int x, int y)
{
Settings::Manager::setInt("resolution x", "Video", x);
Settings::Manager::setInt("resolution y", "Video", y);
Settings::video().mResolutionX.set(x);
Settings::video().mResolutionY.set(y);
// We only want to process changes to window-size related settings.
Settings::CategorySettingVector filter = { { "Video", "resolution x" }, { "Video", "resolution y" } };

View File

@ -4,7 +4,7 @@
#include <SDL_keyboard.h>
#include <components/settings/settings.hpp>
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
@ -170,10 +170,9 @@ namespace MWInput
void ActionManager::screenshot()
{
const std::string& settingStr = Settings::Manager::getString("screenshot type", "Video");
bool regularScreenshot = settingStr.empty() || settingStr == "regular";
const Settings::ScreenshotSettings& settings = Settings::video().mScreenshotType;
if (regularScreenshot)
if (settings.mType == Settings::ScreenshotType::Regular)
{
mScreenCaptureHandler->setFramesToCapture(1);
mScreenCaptureHandler->captureNextFrame(*mViewer);

View File

@ -42,8 +42,7 @@ namespace MWInput
float angle = 0;
SDL_DisplayOrientation currentOrientation
= SDL_GetDisplayOrientation(Settings::Manager::getInt("screen", "Video"));
SDL_DisplayOrientation currentOrientation = SDL_GetDisplayOrientation(Settings::video().mScreen);
switch (currentOrientation)
{
case SDL_ORIENTATION_UNKNOWN:

View File

@ -94,8 +94,8 @@ namespace MWLua
api["getViewTransform"] = [camera]() { return LuaUtil::TransformM{ camera->getViewMatrix() }; };
api["viewportToWorldVector"] = [camera, renderingManager](osg::Vec2f pos) -> osg::Vec3f {
double width = Settings::Manager::getInt("resolution x", "Video");
double height = Settings::Manager::getInt("resolution y", "Video");
const double width = Settings::video().mResolutionX;
const double height = Settings::video().mResolutionY;
double aspect = (height == 0.0) ? 1.0 : width / height;
double fovTan = std::tan(osg::DegreesToRadians(renderingManager->getFieldOfView()) / 2);
osg::Matrixf invertedViewMatrix;
@ -106,8 +106,8 @@ namespace MWLua
};
api["worldToViewportVector"] = [camera](osg::Vec3f pos) {
double width = Settings::Manager::getInt("resolution x", "Video");
double height = Settings::Manager::getInt("resolution y", "Video");
const double width = Settings::video().mResolutionX;
const double height = Settings::video().mResolutionY;
osg::Matrix windowMatrix
= osg::Matrix::translate(1.0, 1.0, 1.0) * osg::Matrix::scale(0.5 * width, 0.5 * height, 0.5);

View File

@ -36,6 +36,7 @@
#include <components/esm4/loadingr.hpp>
#include <components/esm4/loadligh.hpp>
#include <components/esm4/loadmisc.hpp>
#include <components/esm4/loadmstt.hpp>
#include <components/esm4/loadrefr.hpp>
#include <components/esm4/loadstat.hpp>
#include <components/esm4/loadtree.hpp>
@ -240,6 +241,9 @@ namespace MWLua
case ESM::REC_MISC4:
cell.mStore->template forEachType<ESM4::MiscItem>(visitor);
break;
case ESM::REC_MSTT4:
cell.mStore->template forEachType<ESM4::MovableStatic>(visitor);
break;
case ESM::REC_ALCH4:
cell.mStore->template forEachType<ESM4::Potion>(visitor);
break;

View File

@ -246,6 +246,13 @@ namespace MWLua
reloadAllScriptsImpl();
mReloadAllScriptsRequested = false;
}
if (mDelayedUiModeChangedArg)
{
if (playerScripts)
playerScripts->uiModeChanged(*mDelayedUiModeChangedArg, true);
mDelayedUiModeChangedArg = std::nullopt;
}
}
void LuaManager::applyDelayedActions()
@ -275,6 +282,7 @@ namespace MWLua
mGlobalScripts.removeAllScripts();
mGlobalScriptsStarted = false;
mNewGameStarted = false;
mDelayedUiModeChangedArg = std::nullopt;
if (!mPlayer.isEmpty())
{
mPlayer.getCellRef().unsetRefNum();
@ -325,9 +333,15 @@ namespace MWLua
{
if (mPlayer.isEmpty())
return;
ObjectId argId = arg.isEmpty() ? ObjectId() : getId(arg);
if (mApplyingDelayedActions)
{
mDelayedUiModeChangedArg = argId;
return;
}
PlayerScripts* playerScripts = dynamic_cast<PlayerScripts*>(mPlayer.getRefData().getLuaScripts());
if (playerScripts)
playerScripts->uiModeChanged(arg, mApplyingDelayedActions);
playerScripts->uiModeChanged(argId, false);
}
void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)

View File

@ -204,6 +204,7 @@ namespace MWLua
std::optional<DelayedAction> mTeleportPlayerAction;
std::vector<std::string> mUIMessages;
std::vector<std::pair<std::string, Misc::Color>> mInGameConsoleMessages;
std::optional<ObjectId> mDelayedUiModeChangedArg;
LuaUtil::LuaStorage mGlobalStorage{ mLua.sol() };
LuaUtil::LuaStorage mPlayerStorage{ mLua.sol() };

View File

@ -66,9 +66,9 @@ namespace MWLua
}
// `arg` is either forwarded from MWGui::pushGuiMode or empty
void uiModeChanged(const MWWorld::Ptr& arg, bool byLuaAction)
void uiModeChanged(ObjectId arg, bool byLuaAction)
{
if (arg.isEmpty())
if (arg.isZeroOrUnset())
callEngineHandlers(mUiModeChanged, byLuaAction);
else
callEngineHandlers(mUiModeChanged, byLuaAction, LObject(arg));

View File

@ -138,6 +138,18 @@ namespace MWLua
throw std::runtime_error("The argument must be a player.");
return input->getControlSwitch(key);
};
player["isTeleportingEnabled"] = [](const Object& player) -> bool {
if (player.ptr() != MWBase::Environment::get().getWorld()->getPlayerPtr())
throw std::runtime_error("The argument must be a player.");
return MWBase::Environment::get().getWorld()->isTeleportingEnabled();
};
player["setTeleportingEnabled"] = [](const Object& player, bool state) {
if (player.ptr() != MWBase::Environment::get().getWorld()->getPlayerPtr())
throw std::runtime_error("The argument must be a player.");
if (dynamic_cast<const LObject*>(&player) && !dynamic_cast<const SelfObject*>(&player))
throw std::runtime_error("Only player and global scripts can toggle teleportation.");
MWBase::Environment::get().getWorld()->enableTeleporting(state);
};
player["setControlSwitch"] = [input](const Object& player, std::string_view key, bool v) {
if (player.ptr() != MWBase::Environment::get().getWorld()->getPlayerPtr())
throw std::runtime_error("The argument must be a player.");

View File

@ -47,6 +47,7 @@ namespace MWLua
constexpr std::string_view ESM4Ingredient = "ESM4Ingredient";
constexpr std::string_view ESM4Light = "ESM4Light";
constexpr std::string_view ESM4MiscItem = "ESM4Miscellaneous";
constexpr std::string_view ESM4MovableStatic = "ESM4MovableStatic";
constexpr std::string_view ESM4Potion = "ESM4Potion";
constexpr std::string_view ESM4Static = "ESM4Static";
constexpr std::string_view ESM4Terminal = "ESM4Terminal";
@ -91,6 +92,7 @@ namespace MWLua
{ ESM::REC_INGR4, ObjectTypeName::ESM4Ingredient },
{ ESM::REC_LIGH4, ObjectTypeName::ESM4Light },
{ ESM::REC_MISC4, ObjectTypeName::ESM4MiscItem },
{ ESM::REC_MSTT4, ObjectTypeName::ESM4MovableStatic },
{ ESM::REC_ALCH4, ObjectTypeName::ESM4Potion },
{ ESM::REC_STAT4, ObjectTypeName::ESM4Static },
{ ESM::REC_TERM4, ObjectTypeName::ESM4Terminal },
@ -230,6 +232,7 @@ namespace MWLua
addType(ObjectTypeName::ESM4Ingredient, { ESM::REC_INGR4 });
addType(ObjectTypeName::ESM4Light, { ESM::REC_LIGH4 });
addType(ObjectTypeName::ESM4MiscItem, { ESM::REC_MISC4 });
addType(ObjectTypeName::ESM4MovableStatic, { ESM::REC_MSTT4 });
addType(ObjectTypeName::ESM4Potion, { ESM::REC_ALCH4 });
addType(ObjectTypeName::ESM4Static, { ESM::REC_STAT4 });
addESM4TerminalBindings(addType(ObjectTypeName::ESM4Terminal, { ESM::REC_TERM4 }), context);

View File

@ -239,10 +239,7 @@ namespace MWLua
return luaManager->uiResourceManager()->registerTexture(data);
};
api["screenSize"] = []() {
return osg::Vec2f(
Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video"));
};
api["screenSize"] = []() { return osg::Vec2f(Settings::video().mResolutionX, Settings::video().mResolutionY); };
api["_getAllUiModes"] = [](sol::this_state lua) {
sol::table res(lua, sol::create);

View File

@ -1998,12 +1998,12 @@ namespace MWMechanics
}
bool Actors::playAnimationGroup(
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool persist) const
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted) const
{
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
{
return iter->second->getCharacterController().playGroup(groupName, mode, number, persist);
return iter->second->getCharacterController().playGroup(groupName, mode, number, scripted);
}
else
{

View File

@ -113,7 +113,7 @@ namespace MWMechanics
void forceStateUpdate(const MWWorld::Ptr& ptr) const;
bool playAnimationGroup(
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool persist = false) const;
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted = false) const;
void skipAnimation(const MWWorld::Ptr& ptr) const;
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) const;
void persistAnimationStates() const;

View File

@ -708,7 +708,7 @@ namespace MWMechanics
mFleeDest = ESM::Pathgrid::Point(0, 0, 0);
}
bool AiCombatStorage::isFleeing()
bool AiCombatStorage::isFleeing() const
{
return mFleeState != FleeState_None;
}

View File

@ -70,7 +70,7 @@ namespace MWMechanics
void startFleeing();
void stopFleeing();
bool isFleeing();
bool isFleeing() const;
};
/// \brief Causes the actor to fight another actor

View File

@ -135,6 +135,15 @@ namespace MWMechanics
return mNumPursuitPackages > 0;
}
bool AiSequence::isFleeing() const
{
if (!isInCombat())
return false;
const AiCombatStorage* storage = mAiState.getPtr<AiCombatStorage>();
return storage && storage->isFleeing();
}
bool AiSequence::isEngagedWithActor() const
{
if (!isInCombat())
@ -272,7 +281,9 @@ namespace MWMechanics
}
else
{
float rating = MWMechanics::getBestActionRating(actor, target);
float rating = 0.f;
if (MWMechanics::canFight(actor, target))
rating = MWMechanics::getBestActionRating(actor, target);
const ESM::Position& targetPos = target.getRefData().getPosition();

View File

@ -117,6 +117,9 @@ namespace MWMechanics
/// Is there any pursuit package.
bool isInPursuit() const;
/// Is the actor fleeing?
bool isFleeing() const;
/// Removes all packages using the specified id.
void removePackagesById(AiPackageTypeId id);

View File

@ -38,6 +38,13 @@ namespace MWMechanics
return *result;
}
/// \brief returns pointer to stored object in the desired type
template <class Derived>
Derived* getPtr() const
{
return dynamic_cast<Derived*>(mStorage.get());
}
template <class Derived>
void copy(DerivedClassStorage& destination) const
{

View File

@ -229,7 +229,7 @@ namespace MWMechanics
}
if (mPathFinder.isPathConstructed())
storage.setState(AiWanderStorage::Wander_Walking);
storage.setState(AiWanderStorage::Wander_Walking, !mUsePathgrid);
}
if (!cStats.getMovementFlag(CreatureStats::Flag_ForceJump)
@ -499,7 +499,7 @@ namespace MWMechanics
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None))
{
if (mPathFinder.isPathConstructed())
storage.setState(AiWanderStorage::Wander_Walking);
storage.setState(AiWanderStorage::Wander_Walking, !mUsePathgrid);
else
storage.setState(AiWanderStorage::Wander_ChooseAction);
}

View File

@ -538,7 +538,7 @@ namespace MWMechanics
if (mAnimation->isPlaying("containerclose"))
return false;
mAnimation->play("containeropen", Priority_Persistent, MWRender::Animation::BlendMask_All, false, 1.0f,
mAnimation->play("containeropen", Priority_Scripted, MWRender::Animation::BlendMask_All, false, 1.0f,
"start", "stop", 0.f, 0);
if (mAnimation->isPlaying("containeropen"))
return false;
@ -559,7 +559,7 @@ namespace MWMechanics
if (animPlaying)
startPoint = 1.f - complete;
mAnimation->play("containerclose", Priority_Persistent, MWRender::Animation::BlendMask_All, false, 1.0f,
mAnimation->play("containerclose", Priority_Scripted, MWRender::Animation::BlendMask_All, false, 1.0f,
"start", "stop", startPoint, 0);
}
}
@ -827,8 +827,8 @@ namespace MWMechanics
void CharacterController::refreshCurrentAnims(
CharacterState idle, CharacterState movement, JumpingState jump, bool force)
{
// If the current animation is persistent, do not touch it
if (isPersistentAnimPlaying())
// If the current animation is scripted, do not touch it
if (isScriptedAnimPlaying())
return;
refreshHitRecoilAnims();
@ -882,7 +882,7 @@ namespace MWMechanics
mDeathState = chooseRandomDeathState();
// Do not interrupt scripted animation by death
if (isPersistentAnimPlaying())
if (isScriptedAnimPlaying())
return;
playDeath(startpoint, mDeathState);
@ -1481,8 +1481,8 @@ namespace MWMechanics
sndMgr->stopSound3D(mPtr, wolfRun);
}
// Combat for actors with persistent animations obviously will be buggy
if (isPersistentAnimPlaying())
// Combat for actors with scripted animations obviously will be buggy
if (isScriptedAnimPlaying())
return forcestateupdate;
float complete = 0.f;
@ -1852,17 +1852,32 @@ namespace MWMechanics
void CharacterController::updateAnimQueue()
{
if (mAnimQueue.size() > 1)
if (mAnimQueue.empty())
return;
if (!mAnimation->isPlaying(mAnimQueue.front().mGroup))
{
if (mAnimation->isPlaying(mAnimQueue.front().mGroup) == false)
// Remove the finished animation, unless it's a scripted animation that was interrupted by e.g. a rebuild of
// the animation object.
if (mAnimQueue.size() > 1 || !mAnimQueue.front().mScripted || mAnimQueue.front().mLoopCount == 0)
{
mAnimation->disable(mAnimQueue.front().mGroup);
mAnimQueue.pop_front();
bool loopfallback = mAnimQueue.front().mGroup.starts_with("idle");
mAnimation->play(mAnimQueue.front().mGroup, Priority_Default, MWRender::Animation::BlendMask_All, false,
1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount, loopfallback);
}
if (!mAnimQueue.empty())
{
// Move on to the remaining items of the queue
bool loopfallback = mAnimQueue.front().mGroup.starts_with("idle");
mAnimation->play(mAnimQueue.front().mGroup,
mAnimQueue.front().mScripted ? Priority_Scripted : Priority_Default,
MWRender::Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f,
mAnimQueue.front().mLoopCount, loopfallback);
}
}
else
{
mAnimQueue.front().mLoopCount = mAnimation->getCurrentLoopCount(mAnimQueue.front().mGroup);
}
if (!mAnimQueue.empty())
@ -2344,7 +2359,7 @@ namespace MWMechanics
}
}
if (!isMovementAnimationControlled())
if (!isMovementAnimationControlled() && !isScriptedAnimPlaying())
world->queueMovement(mPtr, vec);
}
@ -2371,8 +2386,7 @@ namespace MWMechanics
}
}
bool isPersist = isPersistentAnimPlaying();
osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim && !isPersist ? 0.f : duration);
osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim && !isScriptedAnimPlaying() ? 0.f : duration);
if (duration > 0.0f)
moved /= duration;
else
@ -2413,7 +2427,7 @@ namespace MWMechanics
}
// Update movement
if (isMovementAnimationControlled() && mPtr.getClass().isActor())
if (isMovementAnimationControlled() && mPtr.getClass().isActor() && !isScriptedAnimPlaying())
world->queueMovement(mPtr, moved);
mSkipAnim = false;
@ -2428,7 +2442,7 @@ namespace MWMechanics
state.mScriptedAnims.clear();
for (AnimationQueue::const_iterator iter = mAnimQueue.begin(); iter != mAnimQueue.end(); ++iter)
{
if (!iter->mPersist)
if (!iter->mScripted)
continue;
ESM::AnimationState::ScriptedAnimation anim;
@ -2464,7 +2478,7 @@ namespace MWMechanics
AnimationQueueEntry entry;
entry.mGroup = iter->mGroup;
entry.mLoopCount = iter->mLoopCount;
entry.mPersist = true;
entry.mScripted = true;
mAnimQueue.push_back(entry);
}
@ -2483,18 +2497,18 @@ namespace MWMechanics
mIdleState = CharState_SpecialIdle;
bool loopfallback = mAnimQueue.front().mGroup.starts_with("idle");
mAnimation->play(anim.mGroup, Priority_Persistent, MWRender::Animation::BlendMask_All, false, 1.0f, "start",
mAnimation->play(anim.mGroup, Priority_Scripted, MWRender::Animation::BlendMask_All, false, 1.0f, "start",
"stop", complete, anim.mLoopCount, loopfallback);
}
}
bool CharacterController::playGroup(std::string_view groupname, int mode, int count, bool persist)
bool CharacterController::playGroup(std::string_view groupname, int mode, int count, bool scripted)
{
if (!mAnimation || !mAnimation->hasAnimation(groupname))
return false;
// We should not interrupt persistent animations by non-persistent ones
if (isPersistentAnimPlaying() && !persist)
// We should not interrupt scripted animations with non-scripted ones
if (isScriptedAnimPlaying() && !scripted)
return true;
// If this animation is a looped animation (has a "loop start" key) that is already playing
@ -2523,17 +2537,17 @@ namespace MWMechanics
AnimationQueueEntry entry;
entry.mGroup = groupname;
entry.mLoopCount = count - 1;
entry.mPersist = persist;
entry.mScripted = scripted;
if (mode != 0 || mAnimQueue.empty() || !isAnimPlaying(mAnimQueue.front().mGroup))
{
clearAnimQueue(persist);
clearAnimQueue(scripted);
clearStateAnimation(mCurrentIdle);
mIdleState = CharState_SpecialIdle;
bool loopfallback = entry.mGroup.starts_with("idle");
mAnimation->play(groupname, persist && groupname != "idle" ? Priority_Persistent : Priority_Default,
mAnimation->play(groupname, scripted && groupname != "idle" ? Priority_Scripted : Priority_Default,
MWRender::Animation::BlendMask_All, false, 1.0f, ((mode == 2) ? "loop start" : "start"), "stop", 0.0f,
count - 1, loopfallback);
}
@ -2544,7 +2558,7 @@ namespace MWMechanics
// "PlayGroup idle" is a special case, used to remove to stop scripted animations playing
if (groupname == "idle")
entry.mPersist = false;
entry.mScripted = false;
mAnimQueue.push_back(entry);
@ -2556,12 +2570,12 @@ namespace MWMechanics
mSkipAnim = true;
}
bool CharacterController::isPersistentAnimPlaying() const
bool CharacterController::isScriptedAnimPlaying() const
{
if (!mAnimQueue.empty())
{
const AnimationQueueEntry& first = mAnimQueue.front();
return first.mPersist && isAnimPlaying(first.mGroup);
return first.mScripted && isAnimPlaying(first.mGroup);
}
return false;
@ -2584,13 +2598,13 @@ namespace MWMechanics
return movementAnimationControlled;
}
void CharacterController::clearAnimQueue(bool clearPersistAnims)
void CharacterController::clearAnimQueue(bool clearScriptedAnims)
{
// Do not interrupt scripted animations, if we want to keep them
if ((!isPersistentAnimPlaying() || clearPersistAnims) && !mAnimQueue.empty())
if ((!isScriptedAnimPlaying() || clearScriptedAnims) && !mAnimQueue.empty())
mAnimation->disable(mAnimQueue.front().mGroup);
if (clearPersistAnims)
if (clearScriptedAnims)
{
mAnimQueue.clear();
return;
@ -2598,7 +2612,7 @@ namespace MWMechanics
for (AnimationQueue::iterator it = mAnimQueue.begin(); it != mAnimQueue.end();)
{
if (!it->mPersist)
if (!it->mScripted)
it = mAnimQueue.erase(it);
else
++it;

View File

@ -40,7 +40,7 @@ namespace MWMechanics
Priority_Torch,
Priority_Storm,
Priority_Death,
Priority_Persistent,
Priority_Scripted,
Num_Priorities
};
@ -135,7 +135,7 @@ namespace MWMechanics
{
std::string mGroup;
size_t mLoopCount;
bool mPersist;
bool mScripted;
};
typedef std::deque<AnimationQueueEntry> AnimationQueue;
AnimationQueue mAnimQueue;
@ -207,7 +207,7 @@ namespace MWMechanics
void refreshMovementAnims(CharacterState movement, bool force = false);
void refreshIdleAnims(CharacterState idle, bool force = false);
void clearAnimQueue(bool clearPersistAnims = false);
void clearAnimQueue(bool clearScriptedAnims = false);
bool updateWeaponState();
void updateIdleStormState(bool inwater) const;
@ -215,7 +215,7 @@ namespace MWMechanics
std::string chooseRandomAttackAnimation() const;
static bool isRandomAttackAnimation(std::string_view group);
bool isPersistentAnimPlaying() const;
bool isScriptedAnimPlaying() const;
bool isMovementAnimationControlled() const;
void updateAnimQueue();
@ -270,7 +270,7 @@ namespace MWMechanics
void persistAnimationState() const;
void unpersistAnimationState();
bool playGroup(std::string_view groupname, int mode, int count, bool persist = false);
bool playGroup(std::string_view groupname, int mode, int count, bool scripted = false);
void skipAnim();
bool isAnimPlaying(std::string_view groupName) const;

View File

@ -760,12 +760,12 @@ namespace MWMechanics
}
bool MechanicsManager::playAnimationGroup(
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool persist)
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted)
{
if (ptr.getClass().isActor())
return mActors.playAnimationGroup(ptr, groupName, mode, number, persist);
return mActors.playAnimationGroup(ptr, groupName, mode, number, scripted);
else
return mObjects.playAnimationGroup(ptr, groupName, mode, number, persist);
return mObjects.playAnimationGroup(ptr, groupName, mode, number, scripted);
}
void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr)
{

View File

@ -142,7 +142,7 @@ namespace MWMechanics
/// Attempt to play an animation group
/// @return Success or error
bool playAnimationGroup(
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool persist = false) override;
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted = false) override;
void skipAnimation(const MWWorld::Ptr& ptr) override;
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) override;
void persistAnimationStates() override;

View File

@ -99,12 +99,12 @@ namespace MWMechanics
}
bool Objects::playAnimationGroup(
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool persist)
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted)
{
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
{
return iter->second->playGroup(groupName, mode, number, persist);
return iter->second->playGroup(groupName, mode, number, scripted);
}
else
{

View File

@ -46,7 +46,7 @@ namespace MWMechanics
void onClose(const MWWorld::Ptr& ptr);
bool playAnimationGroup(
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool persist = false);
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted = false);
void skipAnimation(const MWWorld::Ptr& ptr);
void persistAnimationStates();

View File

@ -118,13 +118,17 @@ namespace MWMechanics
}
int value = 50.f;
if (actor.getClass().isNpc())
{
ESM::RefId skill = item.getClass().getEquipmentSkill(item);
if (!skill.empty())
value = actor.getClass().getSkill(actor, skill);
}
else
ESM::RefId skill = item.getClass().getEquipmentSkill(item);
if (!skill.empty())
value = actor.getClass().getSkill(actor, skill);
// Prefer hand-to-hand if our skill is 0 (presumably due to magic)
if (value <= 0.f)
return 0.f;
// Note that a creature with a dagger and 0 Stealth will forgo the weapon despite using Combat for hit chance.
// The same creature will use a sword provided its Combat stat isn't 0. We're using the "skill" value here to
// decide whether to use the weapon at all, but adjusting the final rating based on actual hit chance - i.e. the
// Combat stat.
if (!actor.getClass().isNpc())
{
MWWorld::LiveCellRef<ESM::Creature>* ref = actor.get<ESM::Creature>();
value = ref->mBase->mData.mCombat;

View File

@ -1145,8 +1145,7 @@ namespace MWRender
bool hasScriptedAnims = false;
for (AnimStateMap::iterator stateiter = mStates.begin(); stateiter != mStates.end(); stateiter++)
{
if (stateiter->second.mPriority.contains(int(MWMechanics::Priority_Persistent))
&& stateiter->second.mPlaying)
if (stateiter->second.mPriority.contains(int(MWMechanics::Priority_Scripted)) && stateiter->second.mPlaying)
{
hasScriptedAnims = true;
break;
@ -1158,7 +1157,7 @@ namespace MWRender
while (stateiter != mStates.end())
{
AnimState& state = stateiter->second;
if (hasScriptedAnims && !state.mPriority.contains(int(MWMechanics::Priority_Persistent)))
if (hasScriptedAnims && !state.mPriority.contains(int(MWMechanics::Priority_Scripted)))
{
++stateiter;
continue;

View File

@ -154,7 +154,7 @@ namespace MWRender
public:
CharacterPreviewRTTNode(uint32_t sizeX, uint32_t sizeY)
: RTTNode(sizeX, sizeY, Settings::Manager::getInt("antialiasing", "Video"), false, 0,
: RTTNode(sizeX, sizeY, Settings::video().mAntialiasing, false, 0,
StereoAwareness::Unaware_MultiViewShaders, shouldAddMSAAIntermediateTarget())
, mAspectRatio(static_cast<float>(sizeX) / static_cast<float>(sizeY))
{

View File

@ -457,14 +457,14 @@ namespace MWRender
: GenericResourceManager<ChunkId>(nullptr, Settings::cells().mCacheExpiryDelay)
, Terrain::QuadTreeWorld::ChunkManager(worldspace)
, mSceneManager(sceneManager)
, mActiveGrid(Settings::terrain().mObjectPagingActiveGrid)
, mDebugBatches(Settings::terrain().mDebugChunks)
, mMergeFactor(Settings::terrain().mObjectPagingMergeFactor)
, mMinSize(Settings::terrain().mObjectPagingMinSize)
, mMinSizeMergeFactor(Settings::terrain().mObjectPagingMinSizeMergeFactor)
, mMinSizeCostMultiplier(Settings::terrain().mObjectPagingMinSizeCostMultiplier)
, mRefTrackerLocked(false)
{
mActiveGrid = Settings::Manager::getBool("object paging active grid", "Terrain");
mDebugBatches = Settings::Manager::getBool("debug chunks", "Terrain");
mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain");
mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain");
mMinSizeMergeFactor = Settings::Manager::getFloat("object paging min size merge factor", "Terrain");
mMinSizeCostMultiplier = Settings::Manager::getFloat("object paging min size cost multiplier", "Terrain");
}
std::map<ESM::RefNum, ESM::CellRef> ObjectPaging::collectESM3References(

View File

@ -112,7 +112,7 @@ namespace MWRender
: osg::Group()
, mEnableLiveReload(false)
, mRootNode(rootNode)
, mSamples(Settings::Manager::getInt("antialiasing", "Video"))
, mSamples(Settings::video().mAntialiasing)
, mDirty(false)
, mDirtyFrameId(0)
, mRendering(rendering)

View File

@ -1230,8 +1230,8 @@ namespace MWRender
if (mViewDistance < mNearClip)
throw std::runtime_error("Viewing distance is less than near clip");
double width = Settings::Manager::getInt("resolution x", "Video");
double height = Settings::Manager::getInt("resolution y", "Video");
const double width = Settings::video().mResolutionX;
const double height = Settings::video().mResolutionY;
double aspect = (height == 0.0) ? 1.0 : width / height;
float fov = mFieldOfView;
@ -1316,23 +1316,21 @@ namespace MWRender
return existingChunkMgr->second;
RenderingManager::WorldspaceChunkMgr newChunkMgr;
const float lodFactor = Settings::Manager::getFloat("lod factor", "Terrain");
const float lodFactor = Settings::terrain().mLodFactor;
const bool groundcover = Settings::groundcover().mEnabled;
bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
const bool distantTerrain = Settings::terrain().mDistantTerrain;
if (distantTerrain || groundcover)
{
const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain");
int compMapPower = Settings::Manager::getInt("composite map level", "Terrain");
compMapPower = std::max(-3, compMapPower);
float compMapLevel = pow(2, compMapPower);
const int vertexLodMod = Settings::Manager::getInt("vertex lod mod", "Terrain");
float maxCompGeometrySize = Settings::Manager::getFloat("max composite geometry size", "Terrain");
maxCompGeometrySize = std::max(maxCompGeometrySize, 1.f);
bool debugChunks = Settings::Manager::getBool("debug chunks", "Terrain");
const int compMapResolution = Settings::terrain().mCompositeMapResolution;
const int compMapPower = Settings::terrain().mCompositeMapLevel;
const float compMapLevel = std::pow(2, compMapPower);
const int vertexLodMod = Settings::terrain().mVertexLodMod;
const float maxCompGeometrySize = Settings::terrain().mMaxCompositeGeometrySize;
const bool debugChunks = Settings::terrain().mDebugChunks;
auto quadTreeWorld = std::make_unique<Terrain::QuadTreeWorld>(mSceneRoot, mRootNode, mResourceSystem,
mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug, compMapResolution, compMapLevel,
lodFactor, vertexLodMod, maxCompGeometrySize, debugChunks, worldspace);
if (Settings::Manager::getBool("object paging", "Terrain"))
if (Settings::terrain().mObjectPaging)
{
newChunkMgr.mObjectPaging
= std::make_unique<ObjectPaging>(mResourceSystem->getSceneManager(), worldspace);

View File

@ -8,6 +8,7 @@
#include <osg/Texture2D>
#include <osg/TextureCubeMap>
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/conversion.hpp>
#include <components/resource/resourcesystem.hpp>
@ -20,7 +21,6 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwgui/loadingscreen.hpp"
#include "postprocessor.hpp"
#include "util.hpp"
@ -29,7 +29,7 @@
namespace MWRender
{
enum Screenshot360Type
enum class Screenshot360Type
{
Spherical,
Cylindrical,
@ -161,59 +161,46 @@ namespace MWRender
bool ScreenshotManager::screenshot360(osg::Image* image)
{
int screenshotW = mViewer->getCamera()->getViewport()->width();
int screenshotH = mViewer->getCamera()->getViewport()->height();
Screenshot360Type screenshotMapping = Spherical;
const Settings::ScreenshotSettings& settings = Settings::video().mScreenshotType;
const std::string& settingStr = Settings::Manager::getString("screenshot type", "Video");
std::vector<std::string_view> settingArgs;
Misc::StringUtils::split(settingStr, settingArgs);
Screenshot360Type screenshotMapping = Screenshot360Type::Spherical;
if (settingArgs.size() > 0)
switch (settings.mType)
{
std::string_view typeStrings[4] = { "spherical", "cylindrical", "planet", "cubemap" };
bool found = false;
for (int i = 0; i < 4; ++i)
{
if (settingArgs[0] == typeStrings[i])
{
screenshotMapping = static_cast<Screenshot360Type>(i);
found = true;
break;
}
}
if (!found)
{
Log(Debug::Warning) << "Wrong screenshot type: " << settingArgs[0] << ".";
case Settings::ScreenshotType::Regular:
Log(Debug::Warning) << "Wrong screenshot 360 type: regular.";
return false;
}
case Settings::ScreenshotType::Cylindrical:
screenshotMapping = Screenshot360Type::Cylindrical;
break;
case Settings::ScreenshotType::Spherical:
screenshotMapping = Screenshot360Type::Spherical;
break;
case Settings::ScreenshotType::Planet:
screenshotMapping = Screenshot360Type::Planet;
break;
case Settings::ScreenshotType::Cubemap:
screenshotMapping = Screenshot360Type::RawCubemap;
break;
}
int screenshotW = mViewer->getCamera()->getViewport()->width();
if (settings.mWidth.has_value())
screenshotW = *settings.mWidth;
int screenshotH = mViewer->getCamera()->getViewport()->height();
if (settings.mHeight.has_value())
screenshotH = *settings.mHeight;
// planet mapping needs higher resolution
int cubeSize = screenshotMapping == Planet ? screenshotW : screenshotW / 2;
if (settingArgs.size() > 1)
{
screenshotW = std::min(10000, Misc::StringUtils::toNumeric<int>(settingArgs[1], 0));
}
if (settingArgs.size() > 2)
{
screenshotH = std::min(10000, Misc::StringUtils::toNumeric<int>(settingArgs[2], 0));
}
if (settingArgs.size() > 3)
{
cubeSize = std::min(5000, Misc::StringUtils::toNumeric<int>(settingArgs[3], 0));
}
bool rawCubemap = screenshotMapping == RawCubemap;
const int cubeSize = screenshotMapping == Screenshot360Type::Planet ? screenshotW : screenshotW / 2;
const bool rawCubemap = screenshotMapping == Screenshot360Type::RawCubemap;
if (rawCubemap)
screenshotW = cubeSize * 6; // the image will consist of 6 cube sides in a row
else if (screenshotMapping == Planet)
else if (screenshotMapping == Screenshot360Type::Planet)
screenshotH = screenshotW; // use square resolution for planet mapping
std::vector<osg::ref_ptr<osg::Image>> images;
@ -276,7 +263,7 @@ namespace MWRender
stateset->setAttributeAndModes(shaderMgr.getProgram("360"), osg::StateAttribute::ON);
stateset->addUniform(new osg::Uniform("cubeMap", 0));
stateset->addUniform(new osg::Uniform("mapping", screenshotMapping));
stateset->addUniform(new osg::Uniform("mapping", static_cast<int>(screenshotMapping)));
stateset->setTextureAttributeAndModes(0, cubeTexture, osg::StateAttribute::ON);
screenshotCamera->addChild(quad);

View File

@ -70,7 +70,7 @@ namespace MWRender
bool shouldAddMSAAIntermediateTarget()
{
return Settings::shaders().mAntialiasAlphaTest && Settings::Manager::getInt("antialiasing", "Video") > 1;
return Settings::shaders().mAntialiasAlphaTest && Settings::video().mAntialiasing > 1;
}
}

View File

@ -31,6 +31,7 @@
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/levelledlist.hpp"
#include "interpretercontext.hpp"
#include "ref.hpp"
namespace
@ -94,6 +95,12 @@ namespace MWScript
Interpreter::Type_Integer count = runtime[0].mInteger;
runtime.pop();
if (!MWBase::Environment::get().getESMStore()->find(item))
{
runtime.getContext().report("Failed to add item '" + item.getRefIdString() + "': unknown ID");
return;
}
if (count < 0)
count = static_cast<uint16_t>(count);
@ -210,6 +217,12 @@ namespace MWScript
Interpreter::Type_Integer count = runtime[0].mInteger;
runtime.pop();
if (!MWBase::Environment::get().getESMStore()->find(item))
{
runtime.getContext().report("Failed to remove item '" + item.getRefIdString() + "': unknown ID");
return;
}
if (count < 0)
count = static_cast<uint16_t>(count);

View File

@ -16,6 +16,7 @@
#include "../mwmechanics/npcstats.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "ref.hpp"
@ -89,6 +90,13 @@ namespace MWScript
ESM::RefId topic = ESM::RefId::stringRefId(runtime.getStringLiteral(runtime[0].mInteger));
runtime.pop();
if (!MWBase::Environment::get().getESMStore()->get<ESM::Dialogue>().search(topic))
{
runtime.getContext().report(
"Failed to add topic '" + topic.getRefIdString() + "': topic record not found");
return;
}
MWBase::Environment::get().getDialogueManager()->addTopic(topic);
}
};

View File

@ -41,6 +41,7 @@
#include <components/esm3/loadmisc.hpp>
#include <components/esm3/loadprob.hpp>
#include <components/esm3/loadrepa.hpp>
#include <components/esm3/loadscpt.hpp>
#include <components/esm3/loadstat.hpp>
#include <components/esm3/loadweap.hpp>
@ -184,6 +185,14 @@ namespace MWScript
MWWorld::Ptr target = R()(runtime, false);
ESM::RefId name = ESM::RefId::stringRefId(runtime.getStringLiteral(runtime[0].mInteger));
runtime.pop();
if (!MWBase::Environment::get().getESMStore()->get<ESM::Script>().search(name))
{
runtime.getContext().report(
"Failed to start global script '" + name.getRefIdString() + "': script record not found");
return;
}
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript(name, target);
}
};
@ -206,6 +215,14 @@ namespace MWScript
{
const ESM::RefId& name = ESM::RefId::stringRefId(runtime.getStringLiteral(runtime[0].mInteger));
runtime.pop();
if (!MWBase::Environment::get().getESMStore()->get<ESM::Script>().search(name))
{
runtime.getContext().report(
"Failed to stop global script '" + name.getRefIdString() + "': script record not found");
return;
}
MWBase::Environment::get().getScriptManager()->getGlobalScripts().removeScript(name);
}
};

View File

@ -57,6 +57,7 @@
#include <components/esm4/loadingr.hpp>
#include <components/esm4/loadligh.hpp>
#include <components/esm4/loadmisc.hpp>
#include <components/esm4/loadmstt.hpp>
#include <components/esm4/loadnpc.hpp>
#include <components/esm4/loadrefr.hpp>
#include <components/esm4/loadstat.hpp>
@ -160,31 +161,32 @@ namespace
template <typename T>
void writeReferenceCollection(ESM::ESMWriter& writer, const MWWorld::CellRefList<T>& collection)
{
if (!collection.mList.empty())
// references
for (const MWWorld::LiveCellRef<T>& liveCellRef : collection.mList)
{
// references
for (typename MWWorld::CellRefList<T>::List::const_iterator iter(collection.mList.begin());
iter != collection.mList.end(); ++iter)
if (ESM::isESM4Rec(T::sRecordId))
{
if (!iter->mData.hasChanged() && !iter->mRef.hasChanged() && iter->mRef.hasContentFile())
{
// Reference that came from a content file and has not been changed -> ignore
continue;
}
if (iter->mData.getCount() == 0 && !iter->mRef.hasContentFile())
{
// Deleted reference that did not come from a content file -> ignore
continue;
}
using StateType = typename RecordToState<T>::StateType;
StateType state;
iter->save(state);
// recordId currently unused
writer.writeHNT("OBJE", collection.mList.front().mBase->sRecordId);
state.save(writer);
// TODO: Implement loading/saving of REFR4 and ACHR4 with ESM3 reader/writer.
continue;
}
if (!liveCellRef.mData.hasChanged() && !liveCellRef.mRef.hasChanged() && liveCellRef.mRef.hasContentFile())
{
// Reference that came from a content file and has not been changed -> ignore
continue;
}
if (liveCellRef.mData.getCount() == 0 && !liveCellRef.mRef.hasContentFile())
{
// Deleted reference that did not come from a content file -> ignore
continue;
}
using StateType = typename RecordToState<T>::StateType;
StateType state;
liveCellRef.save(state);
// recordId currently unused
writer.writeHNT("OBJE", collection.mList.front().mBase->sRecordId);
state.save(writer);
}
}

View File

@ -73,6 +73,7 @@ namespace ESM4
struct Flora;
struct Ingredient;
struct MiscItem;
struct MovableStatic;
struct Terminal;
struct Tree;
struct Weapon;
@ -95,8 +96,9 @@ namespace MWWorld
CellRefList<ESM4::Static>, CellRefList<ESM4::Light>, CellRefList<ESM4::Activator>, CellRefList<ESM4::Potion>,
CellRefList<ESM4::Ammunition>, CellRefList<ESM4::Armor>, CellRefList<ESM4::Book>, CellRefList<ESM4::Clothing>,
CellRefList<ESM4::Container>, CellRefList<ESM4::Door>, CellRefList<ESM4::Flora>, CellRefList<ESM4::Ingredient>,
CellRefList<ESM4::Terminal>, CellRefList<ESM4::Tree>, CellRefList<ESM4::MiscItem>, CellRefList<ESM4::Weapon>,
CellRefList<ESM4::Furniture>, CellRefList<ESM4::Creature>, CellRefList<ESM4::Npc>>;
CellRefList<ESM4::Terminal>, CellRefList<ESM4::Tree>, CellRefList<ESM4::MiscItem>,
CellRefList<ESM4::MovableStatic>, CellRefList<ESM4::Weapon>, CellRefList<ESM4::Furniture>,
CellRefList<ESM4::Creature>, CellRefList<ESM4::Npc>>;
/// \brief Mutable state of a cell
class CellStore

View File

@ -84,7 +84,8 @@ namespace
}
std::vector<ESM::NPC> getNPCsToReplace(const MWWorld::Store<ESM::Faction>& factions,
const MWWorld::Store<ESM::Class>& classes, const std::unordered_map<ESM::RefId, ESM::NPC>& npcs)
const MWWorld::Store<ESM::Class>& classes, const MWWorld::Store<ESM::Script>& scripts,
const std::unordered_map<ESM::RefId, ESM::NPC>& npcs)
{
// Cache first class from store - we will use it if current class is not found
const ESM::RefId& defaultCls = getDefaultClass(classes);
@ -122,6 +123,14 @@ namespace
changed = true;
}
if (!npc.mScript.empty() && !scripts.search(npc.mScript))
{
Log(Debug::Verbose) << "NPC " << npc.mId << " (" << npc.mName << ") has nonexistent script "
<< npc.mScript << ", ignoring it.";
npc.mScript = ESM::RefId();
changed = true;
}
if (changed)
npcsToReplace.push_back(npc);
}
@ -138,9 +147,9 @@ namespace
{
if (!item.mScript.empty() && !scripts.search(item.mScript))
{
Log(Debug::Verbose) << MapT::mapped_type::getRecordType() << ' ' << id << " (" << item.mName
<< ") has nonexistent script " << item.mScript << ", ignoring it.";
item.mScript = ESM::RefId();
Log(Debug::Verbose) << "Item " << id << " (" << item.mName << ") has nonexistent script "
<< item.mScript << ", ignoring it.";
}
}
}
@ -292,6 +301,7 @@ namespace MWWorld
case ESM::REC_LVLC4:
case ESM::REC_LVLN4:
case ESM::REC_MISC4:
case ESM::REC_MSTT4:
case ESM::REC_NPC_4:
case ESM::REC_STAT4:
case ESM::REC_TERM4:
@ -517,8 +527,8 @@ namespace MWWorld
void ESMStore::validate()
{
auto& npcs = getWritable<ESM::NPC>();
std::vector<ESM::NPC> npcsToReplace
= getNPCsToReplace(getWritable<ESM::Faction>(), getWritable<ESM::Class>(), npcs.mStatic);
std::vector<ESM::NPC> npcsToReplace = getNPCsToReplace(
getWritable<ESM::Faction>(), getWritable<ESM::Class>(), getWritable<ESM::Script>(), npcs.mStatic);
for (const ESM::NPC& npc : npcsToReplace)
{
@ -526,6 +536,8 @@ namespace MWWorld
npcs.insertStatic(npc);
}
removeMissingScripts(getWritable<ESM::Script>(), getWritable<ESM::Creature>().mStatic);
// Validate spell effects for invalid arguments
std::vector<ESM::Spell> spellsToReplace;
auto& spells = getWritable<ESM::Spell>();
@ -605,8 +617,8 @@ namespace MWWorld
auto& npcs = getWritable<ESM::NPC>();
auto& scripts = getWritable<ESM::Script>();
std::vector<ESM::NPC> npcsToReplace
= getNPCsToReplace(getWritable<ESM::Faction>(), getWritable<ESM::Class>(), npcs.mDynamic);
std::vector<ESM::NPC> npcsToReplace = getNPCsToReplace(
getWritable<ESM::Faction>(), getWritable<ESM::Class>(), getWritable<ESM::Script>(), npcs.mDynamic);
for (const ESM::NPC& npc : npcsToReplace)
npcs.insert(npc);
@ -614,6 +626,7 @@ namespace MWWorld
removeMissingScripts(scripts, getWritable<ESM::Armor>().mDynamic);
removeMissingScripts(scripts, getWritable<ESM::Book>().mDynamic);
removeMissingScripts(scripts, getWritable<ESM::Clothing>().mDynamic);
removeMissingScripts(scripts, getWritable<ESM::Creature>().mDynamic);
removeMissingScripts(scripts, getWritable<ESM::Weapon>().mDynamic);
removeMissingObjects(getWritable<ESM::CreatureLevList>());

View File

@ -92,11 +92,13 @@ namespace ESM4
struct HeadPart;
struct Ingredient;
struct Land;
struct LandTexture;
struct LevelledCreature;
struct LevelledItem;
struct LevelledNpc;
struct Light;
struct MiscItem;
struct MovableStatic;
struct Npc;
struct Outfit;
struct Potion;
@ -139,10 +141,10 @@ namespace MWWorld
Store<ESM4::Armor>, Store<ESM4::ArmorAddon>, Store<ESM4::Book>, Store<ESM4::Cell>, Store<ESM4::Clothing>,
Store<ESM4::Container>, Store<ESM4::Creature>, Store<ESM4::Door>, Store<ESM4::Furniture>,
Store<ESM4::Flora>, Store<ESM4::Hair>, Store<ESM4::HeadPart>, Store<ESM4::Ingredient>, Store<ESM4::Land>,
Store<ESM4::LevelledCreature>, Store<ESM4::LevelledItem>, Store<ESM4::LevelledNpc>, Store<ESM4::Light>,
Store<ESM4::MiscItem>, Store<ESM4::Npc>, Store<ESM4::Outfit>, Store<ESM4::Potion>, Store<ESM4::Race>,
Store<ESM4::Reference>, Store<ESM4::Static>, Store<ESM4::Terminal>, Store<ESM4::Tree>, Store<ESM4::Weapon>,
Store<ESM4::World>>;
Store<ESM4::LandTexture>, Store<ESM4::LevelledCreature>, Store<ESM4::LevelledItem>,
Store<ESM4::LevelledNpc>, Store<ESM4::Light>, Store<ESM4::MiscItem>, Store<ESM4::MovableStatic>,
Store<ESM4::Npc>, Store<ESM4::Outfit>, Store<ESM4::Potion>, Store<ESM4::Race>, Store<ESM4::Reference>,
Store<ESM4::Static>, Store<ESM4::Terminal>, Store<ESM4::Tree>, Store<ESM4::Weapon>, Store<ESM4::World>>;
private:
template <typename T>

View File

@ -1363,11 +1363,13 @@ template class MWWorld::TypedDynamicStore<ESM4::Hair>;
template class MWWorld::TypedDynamicStore<ESM4::HeadPart>;
template class MWWorld::TypedDynamicStore<ESM4::Ingredient>;
template class MWWorld::TypedDynamicStore<ESM4::Land>;
template class MWWorld::TypedDynamicStore<ESM4::LandTexture>;
template class MWWorld::TypedDynamicStore<ESM4::LevelledCreature>;
template class MWWorld::TypedDynamicStore<ESM4::LevelledItem>;
template class MWWorld::TypedDynamicStore<ESM4::LevelledNpc>;
template class MWWorld::TypedDynamicStore<ESM4::Light>;
template class MWWorld::TypedDynamicStore<ESM4::MiscItem>;
template class MWWorld::TypedDynamicStore<ESM4::MovableStatic>;
template class MWWorld::TypedDynamicStore<ESM4::Npc>;
template class MWWorld::TypedDynamicStore<ESM4::Outfit>;
template class MWWorld::TypedDynamicStore<ESM4::Potion>;

View File

@ -87,6 +87,8 @@ add_component_dir (settings
settingvalue
shadermanager
values
screenshotsettings
windowmode
)
add_component_dir (bsa
@ -336,7 +338,15 @@ add_component_dir (fontloader
)
add_component_dir (sdlutil
gl4es_init sdlgraphicswindow imagetosurface sdlinputwrapper sdlvideowrapper events sdlcursormanager sdlmappings
events
gl4es_init
imagetosurface
sdlcursormanager
sdlgraphicswindow
sdlinputwrapper
sdlmappings
sdlvideowrapper
vsyncmode
)
add_component_dir (version

View File

@ -62,10 +62,12 @@
#include <components/esm4/loadingr.hpp>
#include <components/esm4/loadland.hpp>
#include <components/esm4/loadligh.hpp>
#include <components/esm4/loadltex.hpp>
#include <components/esm4/loadlvlc.hpp>
#include <components/esm4/loadlvli.hpp>
#include <components/esm4/loadlvln.hpp>
#include <components/esm4/loadmisc.hpp>
#include <components/esm4/loadmstt.hpp>
#include <components/esm4/loadnpc.hpp>
#include <components/esm4/loadotft.hpp>
#include <components/esm4/loadrace.hpp>

View File

@ -2708,7 +2708,8 @@ namespace NifOsg
stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON);
}
if (shaderprop->softEffect())
SceneUtil::setupSoftEffect(*node, shaderprop->mFalloffDepth, true);
SceneUtil::setupSoftEffect(
*node, shaderprop->mFalloffDepth, true, shaderprop->mFalloffDepth);
break;
}
default:

View File

@ -15,7 +15,7 @@
namespace SceneUtil
{
void setupSoftEffect(osg::Node& node, float size, bool falloff)
void setupSoftEffect(osg::Node& node, float size, bool falloff, float falloffDepth)
{
static const osg::ref_ptr<SceneUtil::AutoDepth> depth = new SceneUtil::AutoDepth(osg::Depth::LESS, 0, 1, false);
@ -23,6 +23,7 @@ namespace SceneUtil
stateset->addUniform(new osg::Uniform("particleSize", size));
stateset->addUniform(new osg::Uniform("particleFade", falloff));
stateset->addUniform(new osg::Uniform("softFalloffDepth", falloffDepth));
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
node.setUserValue(Misc::OsgUserValues::sXSoftEffect, true);
@ -35,6 +36,8 @@ namespace SceneUtil
std::string source;
constexpr float defaultFalloffDepth = 300.f; // arbitrary value that simply looks good with common cases
if (node.getUserValue(Misc::OsgUserValues::sExtraData, source) && !source.empty())
{
YAML::Node root = YAML::Load(source);
@ -47,8 +50,9 @@ namespace SceneUtil
{
auto size = it.second["size"].as<float>(45.f);
auto falloff = it.second["falloff"].as<bool>(false);
auto falloffDepth = it.second["falloffDepth"].as<float>(defaultFalloffDepth);
setupSoftEffect(node, size, falloff);
setupSoftEffect(node, size, falloff, falloffDepth);
}
}
@ -56,7 +60,8 @@ namespace SceneUtil
}
else if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(&node))
{
setupSoftEffect(node, partsys->getDefaultParticleTemplate().getSizeRange().maximum, false);
setupSoftEffect(
node, partsys->getDefaultParticleTemplate().getSizeRange().maximum, false, defaultFalloffDepth);
}
traverse(node);

View File

@ -15,7 +15,7 @@ namespace osg
namespace SceneUtil
{
void setupSoftEffect(osg::Node& node, float size, bool falloff);
void setupSoftEffect(osg::Node& node, float size, bool falloff, float falloffDepth);
class ProcessExtraDataVisitor : public osg::NodeVisitor
{

View File

@ -14,22 +14,16 @@ namespace SDLUtil
close(true);
}
GraphicsWindowSDL2::GraphicsWindowSDL2(osg::GraphicsContext::Traits* traits, int vsync)
GraphicsWindowSDL2::GraphicsWindowSDL2(osg::GraphicsContext::Traits* traits, VSyncMode vsyncMode)
: mWindow(nullptr)
, mContext(nullptr)
, mValid(false)
, mRealized(false)
, mOwnsWindow(false)
, mVSyncMode(vsyncMode)
{
_traits = traits;
if (vsync == 2)
mVSyncMode = VSyncMode::Adaptive;
else if (vsync == 1)
mVSyncMode = VSyncMode::Enabled;
else
mVSyncMode = VSyncMode::Disabled;
init();
if (GraphicsWindowSDL2::valid())
{

View File

@ -5,14 +5,10 @@
#include <osgViewer/GraphicsWindow>
#include "vsyncmode.hpp"
namespace SDLUtil
{
enum VSyncMode
{
Disabled = 0,
Enabled = 1,
Adaptive = 2
};
class GraphicsWindowSDL2 : public osgViewer::GraphicsWindow
{
@ -29,7 +25,7 @@ namespace SDLUtil
virtual ~GraphicsWindowSDL2();
public:
GraphicsWindowSDL2(osg::GraphicsContext::Traits* traits, int vsync);
GraphicsWindowSDL2(osg::GraphicsContext::Traits* traits, VSyncMode vsyncMode);
bool isSameKindAs(const Object* object) const override
{

View File

@ -1,7 +1,7 @@
#include "sdlinputwrapper.hpp"
#include <components/debug/debuglog.hpp>
#include <components/settings/settings.hpp>
#include <components/settings/values.hpp>
#include <osgViewer/Viewer>
@ -187,8 +187,10 @@ namespace SDLUtil
{
case SDL_DISPLAYEVENT_ORIENTATION:
if (mSensorListener
&& evt.display.display == (unsigned int)Settings::Manager::getInt("screen", "Video"))
&& evt.display.display == static_cast<Uint32>(Settings::video().mScreen))
{
mSensorListener->displayOrientationChanged();
}
break;
default:
break;

View File

@ -30,14 +30,8 @@ namespace SDLUtil
SDL_SetWindowGammaRamp(mWindow, mOldSystemGammaRamp, &mOldSystemGammaRamp[256], &mOldSystemGammaRamp[512]);
}
void VideoWrapper::setSyncToVBlank(int mode)
void VideoWrapper::setSyncToVBlank(VSyncMode vsyncMode)
{
VSyncMode vsyncMode = VSyncMode::Disabled;
if (mode == 1)
vsyncMode = VSyncMode::Enabled;
else if (mode == 2)
vsyncMode = VSyncMode::Adaptive;
osgViewer::Viewer::Windows windows;
mViewer->getWindows(windows);
mViewer->stopThreading();
@ -47,7 +41,7 @@ namespace SDLUtil
if (GraphicsWindowSDL2* sdl2win = dynamic_cast<GraphicsWindowSDL2*>(win))
sdl2win->setSyncToVBlank(vsyncMode);
else
win->setSyncToVBlank(static_cast<bool>(mode));
win->setSyncToVBlank(vsyncMode != VSyncMode::Disabled);
}
mViewer->startThreading();
}

View File

@ -5,6 +5,8 @@
#include <SDL_types.h>
#include "vsyncmode.hpp"
struct SDL_Window;
namespace osgViewer
@ -26,7 +28,7 @@ namespace SDLUtil
VideoWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> viewer);
~VideoWrapper();
void setSyncToVBlank(int mode);
void setSyncToVBlank(VSyncMode vsyncMode);
void setGammaContrast(float gamma, float contrast);

View File

@ -0,0 +1,14 @@
#ifndef OPENMW_COMPONENTS_SDLUTIL_VSYNCMODE_H
#define OPENMW_COMPONENTS_SDLUTIL_VSYNCMODE_H
namespace SDLUtil
{
enum VSyncMode
{
Disabled = 0,
Enabled = 1,
Adaptive = 2
};
}
#endif

View File

@ -31,14 +31,14 @@ namespace Settings
makeClampSanitizerDouble(-1, 1) };
SettingValue<double> mLeftEyeOrientationW{ mIndex, "Stereo View", "left eye orientation w",
makeClampSanitizerDouble(-1, 1) };
SettingValue<double> mLeftEyeFovLeft{ mIndex, "Stereo View", "left eye fov left",
makeClampSanitizerDouble(-osg::PI, osg::PI) };
SettingValue<double> mLeftEyeFovRight{ mIndex, "Stereo View", "left eye fov right",
makeClampSanitizerDouble(-osg::PI, osg::PI) };
SettingValue<double> mLeftEyeFovUp{ mIndex, "Stereo View", "left eye fov up",
makeClampSanitizerDouble(-osg::PI, osg::PI) };
SettingValue<double> mLeftEyeFovDown{ mIndex, "Stereo View", "left eye fov down",
makeClampSanitizerDouble(-osg::PI, osg::PI) };
SettingValue<float> mLeftEyeFovLeft{ mIndex, "Stereo View", "left eye fov left",
makeClampSanitizerFloat(-osg::PIf, osg::PIf) };
SettingValue<float> mLeftEyeFovRight{ mIndex, "Stereo View", "left eye fov right",
makeClampSanitizerFloat(-osg::PIf, osg::PIf) };
SettingValue<float> mLeftEyeFovUp{ mIndex, "Stereo View", "left eye fov up",
makeClampSanitizerFloat(-osg::PIf, osg::PIf) };
SettingValue<float> mLeftEyeFovDown{ mIndex, "Stereo View", "left eye fov down",
makeClampSanitizerFloat(-osg::PIf, osg::PIf) };
SettingValue<double> mRightEyeOffsetX{ mIndex, "Stereo View", "right eye offset x" };
SettingValue<double> mRightEyeOffsetY{ mIndex, "Stereo View", "right eye offset y" };
SettingValue<double> mRightEyeOffsetZ{ mIndex, "Stereo View", "right eye offset z" };
@ -50,14 +50,14 @@ namespace Settings
makeClampSanitizerDouble(-1, 1) };
SettingValue<double> mRightEyeOrientationW{ mIndex, "Stereo View", "right eye orientation w",
makeClampSanitizerDouble(-1, 1) };
SettingValue<double> mRightEyeFovLeft{ mIndex, "Stereo View", "right eye fov left",
makeClampSanitizerDouble(-osg::PI, osg::PI) };
SettingValue<double> mRightEyeFovRight{ mIndex, "Stereo View", "right eye fov right",
makeClampSanitizerDouble(-osg::PI, osg::PI) };
SettingValue<double> mRightEyeFovUp{ mIndex, "Stereo View", "right eye fov up",
makeClampSanitizerDouble(-osg::PI, osg::PI) };
SettingValue<double> mRightEyeFovDown{ mIndex, "Stereo View", "right eye fov down",
makeClampSanitizerDouble(-osg::PI, osg::PI) };
SettingValue<float> mRightEyeFovLeft{ mIndex, "Stereo View", "right eye fov left",
makeClampSanitizerFloat(-osg::PIf, osg::PIf) };
SettingValue<float> mRightEyeFovRight{ mIndex, "Stereo View", "right eye fov right",
makeClampSanitizerFloat(-osg::PIf, osg::PIf) };
SettingValue<float> mRightEyeFovUp{ mIndex, "Stereo View", "right eye fov up",
makeClampSanitizerFloat(-osg::PIf, osg::PIf) };
SettingValue<float> mRightEyeFovDown{ mIndex, "Stereo View", "right eye fov down",
makeClampSanitizerFloat(-osg::PIf, osg::PIf) };
};
}

View File

@ -1,8 +1,12 @@
#ifndef OPENMW_COMPONENTS_SETTINGS_CATEGORIES_VIDEO_H
#define OPENMW_COMPONENTS_SETTINGS_CATEGORIES_VIDEO_H
#include "components/settings/sanitizerimpl.hpp"
#include "components/settings/settingvalue.hpp"
#include <components/settings/sanitizerimpl.hpp>
#include <components/settings/screenshotsettings.hpp>
#include <components/settings/settingvalue.hpp>
#include <components/settings/windowmode.hpp>
#include <components/sdlutil/vsyncmode.hpp>
#include <osg/Math>
#include <osg/Vec2f>
@ -20,16 +24,16 @@ namespace Settings
SettingValue<int> mResolutionX{ mIndex, "Video", "resolution x", makeMaxSanitizerInt(1) };
SettingValue<int> mResolutionY{ mIndex, "Video", "resolution y", makeMaxSanitizerInt(1) };
SettingValue<int> mWindowMode{ mIndex, "Video", "window mode", makeEnumSanitizerInt({ 0, 1, 2 }) };
SettingValue<WindowMode> mWindowMode{ mIndex, "Video", "window mode" };
SettingValue<int> mScreen{ mIndex, "Video", "screen", makeMaxSanitizerInt(0) };
SettingValue<bool> mMinimizeOnFocusLoss{ mIndex, "Video", "minimize on focus loss" };
SettingValue<bool> mWindowBorder{ mIndex, "Video", "window border" };
SettingValue<int> mAntialiasing{ mIndex, "Video", "antialiasing", makeMaxSanitizerInt(0) };
SettingValue<int> mVsyncMode{ mIndex, "Video", "vsync mode", makeEnumSanitizerInt({ 0, 1, 2 }) };
SettingValue<SDLUtil::VSyncMode> mVsyncMode{ mIndex, "Video", "vsync mode" };
SettingValue<float> mFramerateLimit{ mIndex, "Video", "framerate limit", makeMaxSanitizerFloat(0) };
SettingValue<float> mContrast{ mIndex, "Video", "contrast", makeMaxStrictSanitizerFloat(0) };
SettingValue<float> mGamma{ mIndex, "Video", "gamma", makeMaxStrictSanitizerFloat(0) };
SettingValue<std::string> mScreenshotType{ mIndex, "Video", "screenshot type" };
SettingValue<ScreenshotSettings> mScreenshotType{ mIndex, "Video", "screenshot type" };
};
}

View File

@ -0,0 +1,29 @@
#ifndef OPENMW_COMPONENTS_SETTINGS_SCREENSHOTSETTINGS_H
#define OPENMW_COMPONENTS_SETTINGS_SCREENSHOTSETTINGS_H
#include <optional>
#include <ostream>
namespace Settings
{
enum class ScreenshotType
{
Regular,
Cylindrical,
Spherical,
Planet,
Cubemap,
};
struct ScreenshotSettings
{
ScreenshotType mType;
std::optional<int> mWidth;
std::optional<int> mHeight;
std::optional<int> mCubeSize;
auto operator<=>(const ScreenshotSettings& value) const = default;
};
}
#endif

View File

@ -119,6 +119,23 @@ namespace Settings
Log(Debug::Warning) << "Invalid HRTF mode value: " << static_cast<int>(value) << ", fallback to auto (-1)";
return -1;
}
ScreenshotType parseScreenshotType(std::string_view value)
{
if (value == "regular")
return ScreenshotType::Regular;
if (value == "spherical")
return ScreenshotType::Spherical;
if (value == "cylindrical")
return ScreenshotType::Cylindrical;
if (value == "planet")
return ScreenshotType::Planet;
if (value == "cubemap")
return ScreenshotType::Cubemap;
Log(Debug::Warning) << "Invalid screenshot type: " << value << ", fallback to regular";
return ScreenshotType::Regular;
}
}
CategorySettingValueMap Manager::mDefaultSettings = CategorySettingValueMap();
@ -501,6 +518,16 @@ namespace Settings
setInt(setting, category, toInt(value));
}
void Manager::set(std::string_view setting, std::string_view category, WindowMode value)
{
setInt(setting, category, static_cast<int>(value));
}
void Manager::set(std::string_view setting, std::string_view category, SDLUtil::VSyncMode value)
{
setInt(setting, category, static_cast<int>(value));
}
void Manager::recordInit(std::string_view setting, std::string_view category)
{
sInitialized.emplace(category, setting);
@ -547,4 +574,28 @@ namespace Settings
Log(Debug::Warning) << "Unknown lighting method '" << value << "', returning fallback '" << fallback << "'";
return SceneUtil::LightingMethod::PerObjectUniform;
}
ScreenshotSettings parseScreenshotSettings(std::string_view value)
{
std::vector<std::string_view> settingArgs;
Misc::StringUtils::split(value, settingArgs);
ScreenshotSettings result;
if (settingArgs.size() > 0)
result.mType = parseScreenshotType(settingArgs[0]);
else
result.mType = ScreenshotType::Regular;
if (settingArgs.size() > 1)
result.mWidth = std::min(10000, Misc::StringUtils::toNumeric<int>(settingArgs[1], 0));
if (settingArgs.size() > 2)
result.mHeight = std::min(10000, Misc::StringUtils::toNumeric<int>(settingArgs[2], 0));
if (settingArgs.size() > 3)
result.mCubeSize = std::min(5000, Misc::StringUtils::toNumeric<int>(settingArgs[3], 0));
return result;
}
}

View File

@ -5,9 +5,12 @@
#include "gyroscopeaxis.hpp"
#include "hrtfmode.hpp"
#include "navmeshrendermode.hpp"
#include "screenshotsettings.hpp"
#include "windowmode.hpp"
#include <components/detournavigator/collisionshapetype.hpp>
#include <components/sceneutil/lightingmethod.hpp>
#include <components/sdlutil/vsyncmode.hpp>
#include <filesystem>
#include <set>
@ -27,13 +30,6 @@ namespace Files
namespace Settings
{
enum class WindowMode
{
Fullscreen = 0,
WindowedFullscreen,
Windowed
};
///
/// \brief Settings management (can change during runtime)
///
@ -114,6 +110,8 @@ namespace Settings
static void set(std::string_view setting, std::string_view category, const MyGUI::Colour& value);
static void set(std::string_view setting, std::string_view category, SceneUtil::LightingMethod value);
static void set(std::string_view setting, std::string_view category, HrtfMode value);
static void set(std::string_view setting, std::string_view category, WindowMode value);
static void set(std::string_view setting, std::string_view category, SDLUtil::VSyncMode value);
private:
static std::set<std::pair<std::string_view, std::string_view>> sInitialized;
@ -239,6 +237,32 @@ namespace Settings
return HrtfMode::Enable;
return HrtfMode::Disable;
}
template <>
inline WindowMode Manager::getImpl<WindowMode>(std::string_view setting, std::string_view category)
{
const int value = getInt(setting, category);
if (value < 0 || 2 < value)
return WindowMode::Fullscreen;
return static_cast<WindowMode>(value);
}
template <>
inline SDLUtil::VSyncMode Manager::getImpl<SDLUtil::VSyncMode>(std::string_view setting, std::string_view category)
{
const int value = getInt(setting, category);
if (value < 0 || 2 < value)
return SDLUtil::VSyncMode::Disabled;
return static_cast<SDLUtil::VSyncMode>(value);
}
ScreenshotSettings parseScreenshotSettings(std::string_view value);
template <>
inline ScreenshotSettings Manager::getImpl<ScreenshotSettings>(std::string_view setting, std::string_view category)
{
return parseScreenshotSettings(getString(setting, category));
}
}
#endif // COMPONENTS_SETTINGS_H

View File

@ -42,6 +42,9 @@ namespace Settings
NavMeshRenderMode,
LightingMethod,
HrtfMode,
WindowMode,
VSyncMode,
ScreenshotSettings,
};
template <class T>
@ -161,6 +164,24 @@ namespace Settings
return SettingValueType::HrtfMode;
}
template <>
inline constexpr SettingValueType getSettingValueType<WindowMode>()
{
return SettingValueType::WindowMode;
}
template <>
inline constexpr SettingValueType getSettingValueType<SDLUtil::VSyncMode>()
{
return SettingValueType::VSyncMode;
}
template <>
inline constexpr SettingValueType getSettingValueType<ScreenshotSettings>()
{
return SettingValueType::ScreenshotSettings;
}
inline constexpr std::string_view getSettingValueTypeName(SettingValueType type)
{
switch (type)
@ -203,6 +224,12 @@ namespace Settings
return "lighting method";
case SettingValueType::HrtfMode:
return "hrtf mode";
case SettingValueType::WindowMode:
return "window mode";
case SettingValueType::VSyncMode:
return "vsync mode";
case SettingValueType::ScreenshotSettings:
return "screenshot settings";
}
return "unsupported";
}
@ -361,6 +388,17 @@ namespace Settings
}
return stream;
}
else if constexpr (std::is_same_v<T, ScreenshotSettings>)
{
stream << "ScreenshotSettings{ .mType = " << static_cast<int>(value.mValue.mType);
if (value.mValue.mWidth.has_value())
stream << ", .mWidth = " << *value.mValue.mWidth;
if (value.mValue.mHeight.has_value())
stream << ", .mHeight = " << *value.mValue.mHeight;
if (value.mValue.mCubeSize.has_value())
stream << ", .mCubeSize = " << *value.mValue.mCubeSize;
return stream << " }";
}
else
return stream << value.mValue;
}

View File

@ -0,0 +1,14 @@
#ifndef OPENMW_COMPONENTS_SETTINGS_WINDOWMODE_H
#define OPENMW_COMPONENTS_SETTINGS_WINDOWMODE_H
namespace Settings
{
enum class WindowMode
{
Fullscreen = 0,
WindowedFullscreen = 1,
Windowed = 2,
};
}
#endif

View File

@ -85,7 +85,7 @@ namespace Stereo
}
}
StereoFrustumManager::StereoFrustumManager(osg::Camera* camera)
StereoFrustumManager::StereoFrustumManager(bool sharedShadowMaps, osg::Camera* camera)
: mCamera(camera)
, mShadowTechnique(nullptr)
, mShadowFrustumCallback(nullptr)
@ -95,7 +95,7 @@ namespace Stereo
mMultiviewFrustumCallback = std::make_unique<MultiviewFrustumCallback>(this, camera);
}
if (Settings::Manager::getBool("shared shadow maps", "Stereo"))
if (sharedShadowMaps)
{
mShadowFrustumCallback = new ShadowFrustumCallback(this);
auto* renderer = static_cast<osgViewer::Renderer*>(mCamera->getRenderer());

View File

@ -45,7 +45,7 @@ namespace Stereo
class StereoFrustumManager
{
public:
StereoFrustumManager(osg::Camera* camera);
StereoFrustumManager(bool sharedShadowMaps, osg::Camera* camera);
~StereoFrustumManager();
void update(std::array<osg::Matrix, 2> projections);

View File

@ -124,12 +124,12 @@ namespace Stereo
}
}
void setVertexBufferHint(bool enableMultiview)
void setVertexBufferHint(bool enableMultiview, bool allowDisplayListsForMultiview)
{
if (getStereo() && enableMultiview)
{
auto* ds = osg::DisplaySettings::instance().get();
if (!Settings::Manager::getBool("allow display lists for multiview", "Stereo")
if (!allowDisplayListsForMultiview
&& ds->getVertexBufferHint() == osg::DisplaySettings::VertexBufferHint::NO_PREFERENCE)
{
// Note that this only works if this code is executed before realize() is called on the viewer.

View File

@ -37,7 +37,7 @@ namespace Stereo
void configureExtensions(unsigned int contextID, bool enableMultiview);
//! Sets the appropriate vertex buffer hint on OSG's display settings if needed
void setVertexBufferHint(bool enableMultiview);
void setVertexBufferHint(bool enableMultiview, bool allowDisplayListsForMultiview);
//! Creates a Texture2D as a texture view into a Texture2DArray
osg::ref_ptr<osg::Texture2D> createTextureView_Texture2DFromTexture2DArray(

View File

@ -114,13 +114,15 @@ namespace Stereo
return *sInstance;
}
Manager::Manager(osgViewer::Viewer* viewer, bool enableStereo)
Manager::Manager(osgViewer::Viewer* viewer, bool enableStereo, double near, double far)
: mViewer(viewer)
, mMainCamera(mViewer->getCamera())
, mUpdateCallback(new StereoUpdateCallback(this))
, mMasterProjectionMatrix(osg::Matrixd::identity())
, mEyeResolutionOverriden(false)
, mEyeResolutionOverride(0, 0)
, mNear(near)
, mFar(far)
, mFrustumManager(nullptr)
, mUpdateViewCallback(nullptr)
{
@ -132,13 +134,13 @@ namespace Stereo
Manager::~Manager() {}
void Manager::initializeStereo(osg::GraphicsContext* gc, bool enableMultiview)
void Manager::initializeStereo(osg::GraphicsContext* gc, bool enableMultiview, bool sharedShadowMaps)
{
auto ci = gc->getState()->getContextID();
configureExtensions(ci, enableMultiview);
mMainCamera->addUpdateCallback(mUpdateCallback);
mFrustumManager = std::make_unique<StereoFrustumManager>(mViewer->getCamera());
mFrustumManager = std::make_unique<StereoFrustumManager>(sharedShadowMaps, mViewer->getCamera());
if (getMultiview())
setupOVRMultiView2Technique();
@ -273,7 +275,7 @@ namespace Stereo
void Manager::updateStereoFramebuffer()
{
// VR-TODO: in VR, still need to have this framebuffer attached before the postprocessor is created
// auto samples = Settings::Manager::getInt("antialiasing", "Video");
// auto samples = /*do not use Settings here*/;
// auto eyeRes = eyeResolution();
// if (mMultiviewFramebuffer)
@ -289,20 +291,17 @@ namespace Stereo
void Manager::update()
{
const double near_ = Settings::camera().mNearClip;
const double far_ = Settings::camera().mViewingDistance;
if (mUpdateViewCallback)
{
mUpdateViewCallback->updateView(mView[0], mView[1]);
mViewOffsetMatrix[0] = mView[0].viewMatrix(true);
mViewOffsetMatrix[1] = mView[1].viewMatrix(true);
mProjectionMatrix[0] = mView[0].perspectiveMatrix(near_, far_, false);
mProjectionMatrix[1] = mView[1].perspectiveMatrix(near_, far_, false);
mProjectionMatrix[0] = mView[0].perspectiveMatrix(mNear, mFar, false);
mProjectionMatrix[1] = mView[1].perspectiveMatrix(mNear, mFar, false);
if (SceneUtil::AutoDepth::isReversed())
{
mProjectionMatrixReverseZ[0] = mView[0].perspectiveMatrix(near_, far_, true);
mProjectionMatrixReverseZ[1] = mView[1].perspectiveMatrix(near_, far_, true);
mProjectionMatrixReverseZ[0] = mView[0].perspectiveMatrix(mNear, mFar, true);
mProjectionMatrixReverseZ[1] = mView[1].perspectiveMatrix(mNear, mFar, true);
}
View masterView;
@ -310,7 +309,7 @@ namespace Stereo
masterView.fov.angleUp = std::max(mView[0].fov.angleUp, mView[1].fov.angleUp);
masterView.fov.angleLeft = std::min(mView[0].fov.angleLeft, mView[1].fov.angleLeft);
masterView.fov.angleRight = std::max(mView[0].fov.angleRight, mView[1].fov.angleRight);
auto projectionMatrix = masterView.perspectiveMatrix(near_, far_, false);
auto projectionMatrix = masterView.perspectiveMatrix(mNear, mFar, false);
mMainCamera->setProjectionMatrix(projectionMatrix);
}
else
@ -394,60 +393,30 @@ namespace Stereo
right = mRight;
}
InitializeStereoOperation::InitializeStereoOperation()
InitializeStereoOperation::InitializeStereoOperation(const Settings& settings)
: GraphicsOperation("InitializeStereoOperation", false)
, mMultiview(settings.mMultiview)
, mSharedShadowMaps(settings.mSharedShadowMaps)
, mCustomView(settings.mCustomView)
, mEyeResolution(settings.mEyeResolution)
{
// Ideally, this would have belonged to the operator(). But the vertex buffer
// hint has to be set before realize is called on the osg viewer, and so has to
// be done here instead.
Stereo::setVertexBufferHint(Settings::Manager::getBool("multiview", "Stereo"));
Stereo::setVertexBufferHint(settings.mMultiview, settings.mAllowDisplayListsForMultiview);
}
void InitializeStereoOperation::operator()(osg::GraphicsContext* graphicsContext)
{
auto& sm = Stereo::Manager::instance();
if (Settings::Manager::getBool("use custom view", "Stereo"))
{
Stereo::View left;
Stereo::View right;
if (mCustomView.has_value())
sm.setUpdateViewCallback(
std::make_shared<Stereo::Manager::CustomViewCallback>(mCustomView->mLeft, mCustomView->mRight));
left.pose.position.x() = Settings::Manager::getDouble("left eye offset x", "Stereo View");
left.pose.position.y() = Settings::Manager::getDouble("left eye offset y", "Stereo View");
left.pose.position.z() = Settings::Manager::getDouble("left eye offset z", "Stereo View");
left.pose.orientation.x() = Settings::Manager::getDouble("left eye orientation x", "Stereo View");
left.pose.orientation.y() = Settings::Manager::getDouble("left eye orientation y", "Stereo View");
left.pose.orientation.z() = Settings::Manager::getDouble("left eye orientation z", "Stereo View");
left.pose.orientation.w() = Settings::Manager::getDouble("left eye orientation w", "Stereo View");
left.fov.angleLeft = Settings::Manager::getDouble("left eye fov left", "Stereo View");
left.fov.angleRight = Settings::Manager::getDouble("left eye fov right", "Stereo View");
left.fov.angleUp = Settings::Manager::getDouble("left eye fov up", "Stereo View");
left.fov.angleDown = Settings::Manager::getDouble("left eye fov down", "Stereo View");
if (mEyeResolution.has_value())
sm.overrideEyeResolution(*mEyeResolution);
right.pose.position.x() = Settings::Manager::getDouble("right eye offset x", "Stereo View");
right.pose.position.y() = Settings::Manager::getDouble("right eye offset y", "Stereo View");
right.pose.position.z() = Settings::Manager::getDouble("right eye offset z", "Stereo View");
right.pose.orientation.x() = Settings::Manager::getDouble("right eye orientation x", "Stereo View");
right.pose.orientation.y() = Settings::Manager::getDouble("right eye orientation y", "Stereo View");
right.pose.orientation.z() = Settings::Manager::getDouble("right eye orientation z", "Stereo View");
right.pose.orientation.w() = Settings::Manager::getDouble("right eye orientation w", "Stereo View");
right.fov.angleLeft = Settings::Manager::getDouble("right eye fov left", "Stereo View");
right.fov.angleRight = Settings::Manager::getDouble("right eye fov right", "Stereo View");
right.fov.angleUp = Settings::Manager::getDouble("right eye fov up", "Stereo View");
right.fov.angleDown = Settings::Manager::getDouble("right eye fov down", "Stereo View");
auto customViewCallback = std::make_shared<Stereo::Manager::CustomViewCallback>(left, right);
sm.setUpdateViewCallback(customViewCallback);
}
if (Settings::Manager::getBool("use custom eye resolution", "Stereo"))
{
osg::Vec2i eyeResolution = osg::Vec2i();
eyeResolution.x() = Settings::Manager::getInt("eye resolution x", "Stereo View");
eyeResolution.y() = Settings::Manager::getInt("eye resolution y", "Stereo View");
sm.overrideEyeResolution(eyeResolution);
}
sm.initializeStereo(graphicsContext, Settings::Manager::getBool("multiview", "Stereo"));
sm.initializeStereo(graphicsContext, mMultiview, mSharedShadowMaps);
}
}

View File

@ -76,15 +76,23 @@ namespace Stereo
//! @Param viewer the osg viewer whose stereo should be managed.
//! @Param enableStereo whether or not stereo should be enabled.
//! @Param enableMultiview whether or not to make use of the GL_OVR_Multiview extension, if supported.
Manager(osgViewer::Viewer* viewer, bool enableStereo);
//! @Param near defines distance to near camera clipping plane from view point.
//! @Param far defines distance to far camera clipping plane from view point.
explicit Manager(osgViewer::Viewer* viewer, bool enableStereo, double near, double far);
~Manager();
//! Called during update traversal
void update();
void updateSettings(double near, double far)
{
mNear = near;
mFar = far;
}
//! Initializes all details of stereo if applicable. If the constructor was called with enableMultiview=true,
//! and the GL_OVR_Multiview extension is supported, Stereo::getMultiview() will return true after this call.
void initializeStereo(osg::GraphicsContext* gc, bool enableMultiview);
void initializeStereo(osg::GraphicsContext* gc, bool enableMultiview, bool sharedShadowMaps);
//! Callback that updates stereo configuration during the update pass
void setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb);
@ -138,6 +146,8 @@ namespace Stereo
std::shared_ptr<MultiviewFramebuffer> mMultiviewFramebuffer;
bool mEyeResolutionOverriden;
osg::Vec2i mEyeResolutionOverride;
double mNear;
double mFar;
std::array<View, 2> mView;
std::array<osg::Matrixd, 2> mViewOffsetMatrix;
@ -153,13 +163,34 @@ namespace Stereo
osg::ref_ptr<Identifier> mIdentifierRight = new Identifier();
};
struct CustomView
{
Stereo::View mLeft;
Stereo::View mRight;
};
struct Settings
{
bool mMultiview;
bool mAllowDisplayListsForMultiview;
bool mSharedShadowMaps;
std::optional<CustomView> mCustomView;
std::optional<osg::Vec2i> mEyeResolution;
};
//! Performs stereo-specific initialization operations.
class InitializeStereoOperation final : public osg::GraphicsOperation
{
public:
InitializeStereoOperation();
explicit InitializeStereoOperation(const Settings& settings);
void operator()(osg::GraphicsContext* graphicsContext) override;
private:
bool mMultiview;
bool mSharedShadowMaps;
std::optional<CustomView> mCustomView;
std::optional<osg::Vec2i> mEyeResolution;
};
}

View File

@ -31,13 +31,15 @@ This setting can either be activated in the OpenMW launcher or changed in `setti
Variables.
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
| Name | Description | Type | Default |
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
| size | Scaling ratio. Larger values will make a softer fade effect. Larger geometry requires higher values. | integer | 45 |
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
| falloff | Fades away geometry as camera gets closer. Geometry full fades when parallel to camera. | boolean | false |
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
+--------------+--------------------------------------------------------------------------------------------------------+---------+---------+
| Name | Description | Type | Default |
+--------------+--------------------------------------------------------------------------------------------------------+---------+---------+
| size | Scaling ratio. Larger values will make a softer fade effect. Larger geometry requires higher values. | integer | 45 |
+--------------+--------------------------------------------------------------------------------------------------------+---------+---------+
| falloff | Fades away geometry as camera gets closer. Geometry full fades when parallel to camera. | boolean | false |
+--------------+--------------------------------------------------------------------------------------------------------+---------+---------+
| falloffDepth | The units at which geometry starts to fade. | float | 300 |
+--------------+--------------------------------------------------------------------------------------------------------+---------+---------+
Example usage.
@ -48,6 +50,7 @@ Example usage.
"soft_effect" : {
"size": 250,
"falloff" : false,
"falloffDepth": 5,
}
}
}

View File

@ -194,3 +194,12 @@ Gamma is an exponent that makes colors brighter if greater than 1.0 and darker i
This setting can be changed in the Detail tab of the Video panel of the Options menu.
It has been reported to not work on some Linux systems,
and therefore the in-game setting in the Options menu has been disabled on Linux systems.
screenshot type
---------------
:Type: screenshot settings
:Default: regular
Type of screenshot to take (regular, cylindrical, spherical, planet or cubemap), optionally followed by
screenshot width, height and cubemap resolution in pixels (e.g. spherical 1600 1000 1200).

View File

@ -919,13 +919,26 @@
-- @function [parent=#Player] getCrimeLevel
-- @param openmw.core#GameObject player
-- @return #number
---
-- Whether the character generation for this player is finished.
-- @function [parent=#Player] isCharGenFinished
-- @param openmw.core#GameObject player
-- @return #boolean
---
-- Whether teleportation for this player is enabled.
-- @function [parent=#Player] isTeleportingEnabled
-- @param openmw.core#GameObject player
-- @param #boolean player
-- @return #boolean
---
-- Enables or disables teleportation for this player.
-- @function [parent=#Player] setTeleportingEnabled
-- @param openmw.core#GameObject player
-- @param #boolean state True to enable teleporting, false to disable.
---
-- Returns a list containing quests @{#PlayerQuest} for the specified player, indexed by quest ID.
-- @function [parent=#Player] quests
@ -1868,6 +1881,9 @@
--- Functions for @{#ESM4Miscellaneous} objects
-- @field [parent=#types] #ESM4Miscellaneous ESM4Miscellaneous
--- Functions for @{#ESM4MovableStatic} objects
-- @field [parent=#types] #ESM4MovableStatic ESM4MovableStatic
--- Functions for @{#ESM4Potion} objects
-- @field [parent=#types] #ESM4Potion ESM4Potion

View File

@ -640,7 +640,7 @@ contrast = 1.0
# Video gamma setting. (>0.0). No effect in Linux.
gamma = 1.0
# Type of screenshot to take (regular, cylindrical, spherical or planet), optionally followed by
# Type of screenshot to take (regular, cylindrical, spherical, planet or cubemap), optionally followed by
# screenshot width, height and cubemap resolution in pixels. (e.g. spherical 1600 1000 1200)
screenshot type = regular

View File

@ -38,6 +38,7 @@ uniform float alphaRef;
uniform sampler2D opaqueDepthTex;
uniform float particleSize;
uniform bool particleFade;
uniform float softFalloffDepth;
#endif
void main()
@ -71,7 +72,8 @@ void main()
far,
texture2D(opaqueDepthTex, screenCoords).x,
particleSize,
particleFade
particleFade,
softFalloffDepth
);
#endif

View File

@ -98,6 +98,7 @@ varying vec3 passNormal;
uniform sampler2D opaqueDepthTex;
uniform float particleSize;
uniform bool particleFade;
uniform float softFalloffDepth;
#endif
#if @particleOcclusion
@ -256,7 +257,8 @@ vec3 viewNormal = normalize(gl_NormalMatrix * normal);
far,
texture2D(opaqueDepthTex, screenCoords).x,
particleSize,
particleFade
particleFade,
softFalloffDepth
);
#endif

View File

@ -19,7 +19,8 @@ float calcSoftParticleFade(
float far,
float depth,
float size,
bool fade
bool fade,
float softFalloffDepth
)
{
float euclidianDepth = length(viewPos);
@ -32,13 +33,12 @@ float calcSoftParticleFade(
float falloff = size * falloffMultiplier;
float delta = particleDepth - sceneDepth;
const float nearMult = 300.0;
float viewBias = 1.0;
if (fade)
{
float VdotN = dot(viewDir, viewNormal);
viewBias = abs(VdotN) * quickstep(euclidianDepth / nearMult) * (1.0 - pow(1.0 + VdotN, 1.3));
viewBias = abs(VdotN) * quickstep(euclidianDepth / softFalloffDepth) * (1.0 - pow(1.0 - abs(VdotN), 1.3));
}
const float shift = 0.845;