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:
commit
371eeddf85
@ -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)
|
||||
|
13
CHANGELOG.md
13
CHANGELOG.md
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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(); }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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" } };
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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() };
|
||||
|
@ -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));
|
||||
|
@ -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.");
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -708,7 +708,7 @@ namespace MWMechanics
|
||||
mFleeDest = ESM::Pathgrid::Point(0, 0, 0);
|
||||
}
|
||||
|
||||
bool AiCombatStorage::isFleeing()
|
||||
bool AiCombatStorage::isFleeing() const
|
||||
{
|
||||
return mFleeState != FleeState_None;
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ namespace MWMechanics
|
||||
|
||||
void startFleeing();
|
||||
void stopFleeing();
|
||||
bool isFleeing();
|
||||
bool isFleeing() const;
|
||||
};
|
||||
|
||||
/// \brief Causes the actor to fight another actor
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>());
|
||||
|
@ -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>
|
||||
|
@ -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>;
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
14
components/sdlutil/vsyncmode.hpp
Normal file
14
components/sdlutil/vsyncmode.hpp
Normal 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
|
@ -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) };
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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" };
|
||||
};
|
||||
}
|
||||
|
||||
|
29
components/settings/screenshotsettings.hpp
Normal file
29
components/settings/screenshotsettings.hpp
Normal 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
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
14
components/settings/windowmode.hpp
Normal file
14
components/settings/windowmode.hpp
Normal 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
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user