1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-19 16:39:41 +00:00

Add OpenMW commits up to 7 Nov 2019

# Conflicts:
#	apps/openmw/mwmechanics/aifollow.hpp
This commit is contained in:
David Cernat 2019-11-08 06:20:41 +02:00
commit 5762a36fc2
56 changed files with 1333 additions and 376 deletions

View file

@ -13,8 +13,7 @@ Debian:
before_script:
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
- apt-get update -yq
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev
- apt-get install -y libmygui-dev libbullet-dev
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev
stage: build
script:
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi

View file

@ -4,6 +4,7 @@
Bug #1515: Opening console masks dialogue, inventory menu
Bug #1933: Actors can have few stocks of the same item
Bug #2395: Duplicated plugins in the launcher when multiple data directories provide the same plugin
Bug #2679: Unable to map mouse wheel under control settings
Bug #2969: Scripted items can stack
Bug #2976: Data lines in global openmw.cfg take priority over user openmw.cfg
Bug #2987: Editor: some chance and AI data fields can overflow
@ -35,6 +36,7 @@
Bug #4456: AiActivate should not be cancelled after target activation
Bug #4540: Rain delay when exiting water
Bug #4594: Actors without AI packages don't use Hello dialogue
Bug #4598: Script parser does not support non-ASCII characters
Bug #4600: Crash when no sound output is available or --no-sound is used.
Bug #4639: Black screen after completing first mages guild mission + training
Bug #4650: Focus is lost after pressing ESC in confirmation dialog inside savegame dialog
@ -132,6 +134,7 @@
Bug #5074: Paralyzed actors greet the player
Bug #5075: Enchanting cast style can be changed if there's no object
Bug #5078: DisablePlayerLooking is broken
Bug #5081: OpenMW-CS: Apparatus type "Alembic" is erroneously named "Albemic"
Bug #5082: Scrolling with controller in GUI mode is broken
Bug #5087: Some valid script names can't be used as string arguments
Bug #5089: Swimming/Underwater creatures only swim around ground level
@ -152,6 +155,7 @@
Bug #5134: Doors rotation by "Lock" console command is inconsistent
Bug #5136: LegionUniform script: can not access local variables
Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries
Bug #5138: Actors stuck in half closed door
Bug #5149: Failing lock pick attempts isn't always a crime
Bug #5155: Lock/unlock behavior differs from vanilla
Bug #5158: Objects without a name don't fallback to their ID
@ -182,6 +186,7 @@
Feature #4255: Handle broken RepairedOnMe script function
Feature #4316: Implement RaiseRank/LowerRank functions properly
Feature #4360: Improve default controller bindings
Feature #4544: Actors movement deceleration
Feature #4673: Weapon sheathing
Feature #4675: Support for NiRollController
Feature #4730: Native animated containers support

View file

@ -44,6 +44,7 @@ New Editor Features:
- Land heightmap/shape editing and vertex selection (#5170)
Bug Fixes:
- The Mouse Wheel can now be used for key bindings (#2679)
- Scripted Items cannot be stacked anymore to avoid multiple script execution (#2969)
- Stray text after an "else" statement is now ignored, like in the original engine, to handle mods which erroneously use "else if" statements (#3006)
- "SetPos" and "SetPosition" commands now more closely replicate the original engine's behaviour (#3109)

View file

@ -807,8 +807,10 @@ std::string magicEffectFlags(int flags)
if (flags & ESM::MagicEffect::CastSelf) properties += "CastSelf ";
if (flags & ESM::MagicEffect::CastTouch) properties += "CastTouch ";
if (flags & ESM::MagicEffect::CastTarget) properties += "CastTarget ";
if (flags & ESM::MagicEffect::UncappedDamage) properties += "UncappedDamage ";
if (flags & ESM::MagicEffect::AppliedOnce) properties += "AppliedOnce ";
if (flags & ESM::MagicEffect::Stealth) properties += "Stealth ";
if (flags & ESM::MagicEffect::NonRecastable) properties += "NonRecastable ";
if (flags & ESM::MagicEffect::IllegalDaedra) properties += "IllegalDaedra ";
if (flags & ESM::MagicEffect::Unreflectable) properties += "Unreflectable ";
if (flags & ESM::MagicEffect::CasterLinked) properties += "CasterLinked ";
if (flags & ESM::MagicEffect::AllowSpellmaking) properties += "AllowSpellmaking ";

View file

@ -82,9 +82,15 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game");
loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
connect(animSourcesCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotAnimSourcesToggled(bool)));
loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
if (animSourcesCheckBox->checkState())
{
loadSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
}
// Input Settings
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
loadSettingBool(grabCursorCheckBox, "grab cursor", "Input");
loadSettingBool(toggleSneakCheckBox, "toggle sneak", "Input");
@ -143,9 +149,11 @@ void Launcher::AdvancedPage::saveSettings()
saveSettingBool(requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game");
saveSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
// Input Settings
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
saveSettingBool(grabCursorCheckBox, "grab cursor", "Input");
saveSettingBool(toggleSneakCheckBox, "toggle sneak", "Input");
@ -186,3 +194,14 @@ void Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames)
{
loadCellsForAutocomplete(cellNames);
}
void Launcher::AdvancedPage::slotAnimSourcesToggled(bool checked)
{
weaponSheathingCheckBox->setEnabled(checked);
shieldSheathingCheckBox->setEnabled(checked);
if (!checked)
{
weaponSheathingCheckBox->setCheckState(Qt::Unchecked);
shieldSheathingCheckBox->setCheckState(Qt::Unchecked);
}
}

View file

@ -29,6 +29,7 @@ namespace Launcher
private slots:
void on_skipMenuCheckBox_stateChanged(int state);
void on_runScriptAfterStartupBrowseButton_clicked();
void slotAnimSourcesToggled(bool checked);
private:
Files::ConfigurationManager &mCfgMgr;

View file

@ -45,6 +45,7 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings:
connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool)));
connect(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int)));
connect(framerateLimitCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotFramerateLimitToggled(bool)));
connect(shadowDistanceCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotShadowDistLimitToggled(bool)));
}
@ -129,6 +130,33 @@ bool Launcher::GraphicsPage::loadSettings()
framerateLimitSpinBox->setValue(fpsLimit);
}
if (mEngineSettings.getBool("actor shadows", "Shadows"))
actorShadowsCheckBox->setCheckState(Qt::Checked);
if (mEngineSettings.getBool("player shadows", "Shadows"))
playerShadowsCheckBox->setCheckState(Qt::Checked);
if (mEngineSettings.getBool("terrain shadows", "Shadows"))
objectShadowsCheckBox->setCheckState(Qt::Checked);
if (mEngineSettings.getBool("object shadows", "Shadows"))
objectShadowsCheckBox->setCheckState(Qt::Checked);
if (mEngineSettings.getBool("enable indoor shadows", "Shadows"))
indoorShadowsCheckBox->setCheckState(Qt::Checked);
int shadowDistLimit = mEngineSettings.getInt("maximum shadow map distance", "Shadows");
if (shadowDistLimit > 0)
{
shadowDistanceCheckBox->setCheckState(Qt::Checked);
shadowDistanceSpinBox->setValue(shadowDistLimit);
}
float shadowFadeStart = mEngineSettings.getFloat("shadow fade start", "Shadows");
if (shadowFadeStart != 0)
fadeStartSpinBox->setValue(shadowFadeStart);
int shadowRes = mEngineSettings.getInt("shadow map resolution", "Shadows");
int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes));
if (shadowResIndex != -1)
shadowResolutionComboBox->setCurrentIndex(shadowResIndex);
return true;
}
@ -185,6 +213,52 @@ void Launcher::GraphicsPage::saveSettings()
{
mEngineSettings.setFloat("framerate limit", "Video", 0);
}
int cShadowDist = shadowDistanceCheckBox->checkState() ? shadowDistanceSpinBox->value() : 0;
if (mEngineSettings.getInt("maximum shadow map distance", "Shadows") != cShadowDist)
mEngineSettings.setInt("maximum shadow map distance", "Shadows", cShadowDist);
float cFadeStart = fadeStartSpinBox->value();
if (cShadowDist > 0 && mEngineSettings.getFloat("shadow fade start", "Shadows") != cFadeStart)
mEngineSettings.setFloat("shadow fade start", "Shadows", cFadeStart);
bool cActorShadows = actorShadowsCheckBox->checkState();
bool cObjectShadows = objectShadowsCheckBox->checkState();
bool cTerrainShadows = terrainShadowsCheckBox->checkState();
bool cPlayerShadows = playerShadowsCheckBox->checkState();
if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows)
{
if (mEngineSettings.getBool("enable shadows", "Shadows") != true)
mEngineSettings.setBool("enable shadows", "Shadows", true);
if (mEngineSettings.getBool("actor shadows", "Shadows") != cActorShadows)
mEngineSettings.setBool("actor shadows", "Shadows", cActorShadows);
if (mEngineSettings.getBool("player shadows", "Shadows") != cPlayerShadows)
mEngineSettings.setBool("player shadows", "Shadows", cPlayerShadows);
if (mEngineSettings.getBool("object shadows", "Shadows") != cObjectShadows)
mEngineSettings.setBool("object shadows", "Shadows", cObjectShadows);
if (mEngineSettings.getBool("terrain shadows", "Shadows") != cTerrainShadows)
mEngineSettings.setBool("terrain shadows", "Shadows", cTerrainShadows);
}
else
{
if (mEngineSettings.getBool("enable shadows", "Shadows"))
mEngineSettings.setBool("enable shadows", "Shadows", false);
if (mEngineSettings.getBool("actor shadows", "Shadows"))
mEngineSettings.setBool("actor shadows", "Shadows", false);
if (mEngineSettings.getBool("player shadows", "Shadows"))
mEngineSettings.setBool("player shadows", "Shadows", false);
if (mEngineSettings.getBool("object shadows", "Shadows"))
mEngineSettings.setBool("object shadows", "Shadows", false);
if (mEngineSettings.getBool("terrain shadows", "Shadows"))
mEngineSettings.setBool("terrain shadows", "Shadows", false);
}
bool cIndoorShadows = indoorShadowsCheckBox->checkState();
if (mEngineSettings.getBool("enable indoor shadows", "Shadows") != cIndoorShadows)
mEngineSettings.setBool("enable indoor shadows", "Shadows", cIndoorShadows);
int cShadowRes = shadowResolutionComboBox->currentText().toInt();
if (cShadowRes != mEngineSettings.getInt("shadow map resolution", "Shadows"))
mEngineSettings.setInt("shadow map resolution", "Shadows", cShadowRes);
}
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
@ -290,3 +364,9 @@ void Launcher::GraphicsPage::slotFramerateLimitToggled(bool checked)
{
framerateLimitSpinBox->setEnabled(checked);
}
void Launcher::GraphicsPage::slotShadowDistLimitToggled(bool checked)
{
shadowDistanceSpinBox->setEnabled(checked);
fadeStartSpinBox->setEnabled(checked);
}

View file

@ -32,6 +32,7 @@ namespace Launcher
void slotFullScreenChanged(int state);
void slotStandardToggled(bool checked);
void slotFramerateLimitToggled(bool checked);
void slotShadowDistLimitToggled(bool checked);
private:
Files::ConfigurationManager &mCfgMgr;

View file

@ -52,5 +52,5 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message
if ( bodyPart.mRace.empty() )
messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error);
else if ( mRaces.searchId( bodyPart.mRace ) == -1 )
messages.add(id, "Race '" + bodyPart.mRace + " does not exist", "", CSMDoc::Message::Severity_Error);
messages.add(id, "Race '" + bodyPart.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error);
}

View file

@ -242,10 +242,37 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::V
}
}
bool CSVRender::TerrainSelection::noCell(const std::string& cellId)
{
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
const CSMWorld::IdCollection<CSMWorld::Cell>& cellCollection = document.getData().getCells();
return cellCollection.searchId (cellId) == -1;
}
bool CSVRender::TerrainSelection::noLand(const std::string& cellId)
{
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
const CSMWorld::IdCollection<CSMWorld::Land>& landCollection = document.getData().getLand();
return landCollection.searchId (cellId) == -1;
}
bool CSVRender::TerrainSelection::noLandLoaded(const std::string& cellId)
{
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
const CSMWorld::IdCollection<CSMWorld::Land>& landCollection = document.getData().getLand();
return !landCollection.getRecord(cellId).get().isDataLoaded(ESM::Land::DATA_VNML);
}
bool CSVRender::TerrainSelection::isLandLoaded(const std::string& cellId)
{
if (!noCell(cellId) && !noLand(cellId) && !noLandLoaded(cellId)) return true;
return false;
}
int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global vertex coordinates
{
int cellX = std::floor((1.0f*x / (ESM::Land::LAND_SIZE - 1)));
int cellY = std::floor((1.0f*y / (ESM::Land::LAND_SIZE - 1)));
int cellX = std::floor(static_cast<float>(x) / (ESM::Land::LAND_SIZE - 1));
int cellY = std::floor(static_cast<float>(y) / (ESM::Land::LAND_SIZE - 1));
int localX = x - cellX * (ESM::Land::LAND_SIZE - 1);
int localY = y - cellY * (ESM::Land::LAND_SIZE - 1);
@ -253,7 +280,18 @@ int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global ver
float landHeight = 0.f;
if (CSVRender::Cell* cell = dynamic_cast<CSVRender::Cell*>(mWorldspaceWidget->getCell(coords)))
{
landHeight = cell->getSumOfAlteredAndTrueHeight(cellX, cellY, localX, localY);
}
else if (isLandLoaded(CSMWorld::CellCoordinates::generateId(cellX, cellY)))
{
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
std::string cellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);
const CSMWorld::LandHeightsColumn::DataType mPointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();
return mPointer[localY*ESM::Land::LAND_SIZE + localX];
}
return landHeight;
}

View file

@ -44,10 +44,10 @@ namespace CSVRender
std::vector<std::pair<int, int>> getTerrainSelection() const;
protected:
void update();
protected:
void drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices);
void drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices);
@ -55,6 +55,14 @@ namespace CSVRender
private:
bool noCell(const std::string& cellId);
bool noLand(const std::string& cellId);
bool noLandLoaded(const std::string& cellId);
bool isLandLoaded(const std::string& cellId);
osg::Group* mParentNode;
WorldspaceWidget *mWorldspaceWidget;
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;

View file

@ -112,12 +112,7 @@ void CSVRender::TerrainShapeMode::primaryEditPressed(const WorldspaceHitResult&
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, true);
}
}
if (CSVRender::PagedWorldspaceWidget *paged =
dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
{
paged->resetAllAlteredHeights();
mTotalDiffY = 0;
}
clearTransientEdits();
}
void CSVRender::TerrainShapeMode::primarySelectPressed(const WorldspaceHitResult& hit)
@ -214,28 +209,15 @@ void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos)
{
if (mDragMode == InteractionType_PrimaryEdit)
{
if (mIsEditing)
{
mTotalDiffY = 0;
mIsEditing = false;
}
applyTerrainEditChanges();
if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
paged->resetAllAlteredHeights();
clearTransientEdits();
}
}
void CSVRender::TerrainShapeMode::dragAborted()
{
if (CSVRender::PagedWorldspaceWidget *paged =
dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
{
paged->resetAllAlteredHeights();
mTotalDiffY = 0;
}
clearTransientEdits();
}
void CSVRender::TerrainShapeMode::dragWheel (int diff, double speedFactor)
@ -266,17 +248,22 @@ void CSVRender::TerrainShapeMode::sortAndLimitAlteredCells()
if (passes > 2)
{
Log(Debug::Warning) << "Warning: User edit exceeds accepted slope steepness. Automatic limiting has failed, edit has been discarded.";
if (CSVRender::PagedWorldspaceWidget *paged =
dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
{
paged->resetAllAlteredHeights();
mAlteredCells.clear();
return;
}
clearTransientEdits();
return;
}
}
}
void CSVRender::TerrainShapeMode::clearTransientEdits()
{
mTotalDiffY = 0;
mIsEditing = false;
mAlteredCells.clear();
if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
paged->resetAllAlteredHeights();
mTerrainShapeSelection->update();
}
void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
@ -385,7 +372,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId);
}
undoStack.endMacro();
mAlteredCells.clear();
clearTransientEdits();
}
float CSVRender::TerrainShapeMode::calculateBumpShape(float distance, int radius, float height)
@ -498,6 +485,7 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair<int, int>
}
}
}
mTerrainShapeSelection->update();
}
void CSVRender::TerrainShapeMode::setFlattenToolTargetHeight(const WorldspaceHitResult& hit)
@ -1039,6 +1027,17 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi
return steepnessIsWithinLimits;
}
bool CSVRender::TerrainShapeMode::isInCellSelection(int globalSelectionX, int globalSelectionY)
{
if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
{
std::pair<int, int> vertexCoords = std::make_pair(globalSelectionX, globalSelectionY);
std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords);
return paged->getCellSelection().has(CSMWorld::CellCoordinates::fromId(cellId).first) && isLandLoaded(cellId);
}
return false;
}
void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>& vertexCoords, unsigned char selectMode, bool dragOperation)
{
int r = mBrushSize / 2;
@ -1046,7 +1045,7 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>&
if (mBrushShape == CSVWidget::BrushShape_Point)
{
selections.emplace_back(vertexCoords);
if (isInCellSelection(vertexCoords.first, vertexCoords.second)) selections.emplace_back(vertexCoords.first, vertexCoords.second);
}
if (mBrushShape == CSVWidget::BrushShape_Square)
@ -1055,7 +1054,7 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>&
{
for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j)
{
selections.emplace_back(std::make_pair(i, j));
if (isInCellSelection(i, j)) selections.emplace_back(i, j);
}
}
}
@ -1069,7 +1068,7 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>&
int distanceX = abs(i - vertexCoords.first);
int distanceY = abs(j - vertexCoords.second);
int distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance <= r) selections.emplace_back(std::make_pair(i, j));
if (isInCellSelection(i, j) && distance <= r) selections.emplace_back(i, j);
}
}
}
@ -1080,7 +1079,9 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>&
{
for(auto const& value: mCustomBrushShape)
{
selections.emplace_back(std::make_pair(vertexCoords.first + value.first, vertexCoords.second + value.second));
std::pair<int, int> localVertexCoords (vertexCoords.first + value.first, vertexCoords.second + value.second);
std::string cellId (CSMWorld::CellCoordinates::vertexGlobalToCellId(localVertexCoords));
if (isInCellSelection(localVertexCoords.first, localVertexCoords.second)) selections.emplace_back(localVertexCoords);
}
}
}

View file

@ -95,6 +95,9 @@ namespace CSVRender
/// Remove duplicates and sort mAlteredCells, then limitAlteredHeights forward and reverse
void sortAndLimitAlteredCells();
/// Reset everything in the current edit
void clearTransientEdits();
/// Move pending alteredHeights changes to omwgame/omwaddon -data
void applyTerrainEditChanges();
@ -128,6 +131,9 @@ namespace CSVRender
/// Check that the edit doesn't break save format limits, fix if necessary, return true if slope steepness is within limits
bool limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode = false);
/// Check if global selection coordinate belongs to cell in view
bool isInCellSelection(int globalSelectionX, int globalSelectionY);
/// Handle brush mechanics for terrain shape selection
void selectTerrainShapes (const std::pair<int, int>& vertexCoords, unsigned char selectMode, bool dragOperation);

View file

@ -65,7 +65,10 @@ void CSVWorld::ScriptHighlighter::parseEOF (Compiler::Scanner& scanner)
void CSVWorld::ScriptHighlighter::highlight (const Compiler::TokenLoc& loc, Type type)
{
int length = static_cast<int> (loc.mLiteral.size());
// We should take in account multibyte characters
int length = 0;
const char* token = loc.mLiteral.c_str();
while (*token) length += (*token++ & 0xc0) != 0x80;
int index = loc.mColumn;

View file

@ -621,8 +621,11 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
if(boost::filesystem::exists(input2)) {
boost::filesystem::copy_file(input2, keybinderUser);
keybinderUserExists = boost::filesystem::exists(keybinderUser);
Log(Debug::Info) << "Loading keybindings file: " << keybinderUser;
}
}
else
Log(Debug::Info) << "Loading keybindings file: " << keybinderUser;
// find correct path to the game controller bindings
// File format for controller mappings is different for SDL <= 2.0.4, 2.0.5, and >= 2.0.6

View file

@ -706,6 +706,8 @@ namespace MWClass
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f;
moveSpeed *= ptr.getClass().getMovementSettings(ptr).mSpeedFactor;
return moveSpeed;
}

View file

@ -1175,6 +1175,8 @@ namespace MWClass
if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing)
moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat();
moveSpeed *= ptr.getClass().getMovementSettings(ptr).mSpeedFactor;
return moveSpeed;
}

View file

@ -477,6 +477,14 @@ namespace MWInput
case A_ToggleDebug:
MWBase::Environment::get().getWindowManager()->toggleDebugWindow();
break;
case A_ZoomIn:
if (mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"] && !MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->setCameraDistance(ZOOM_SCALE, true, true);
break;
case A_ZoomOut:
if (mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"] && !MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->setCameraDistance(-ZOOM_SCALE, true, true);
break;
case A_QuickSave:
quickSave();
break;
@ -763,7 +771,9 @@ namespace MWInput
actionIsActive(A_MoveRight) ||
actionIsActive(A_Jump) ||
actionIsActive(A_Sneak) ||
actionIsActive(A_TogglePOV))
actionIsActive(A_TogglePOV) ||
actionIsActive(A_ZoomIn) ||
actionIsActive(A_ZoomOut) )
{
resetIdleTime();
} else {
@ -961,6 +971,14 @@ namespace MWInput
}
}
void InputManager::mouseWheelMoved(const SDL_MouseWheelEvent &arg)
{
if (mInputBinder->detectingBindingState() || !mControlsDisabled)
mInputBinder->mouseWheelMoved(arg);
mJoystickLastUsed = false;
}
void InputManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg )
{
mInputBinder->mouseMoved (arg);
@ -1008,9 +1026,6 @@ namespace MWInput
if (arg.zrel && mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"]) //Check to make sure you are allowed to zoomout and there is a change
{
MWBase::Environment::get().getWorld()->changeVanityModeScale(static_cast<float>(arg.zrel));
if (Settings::Manager::getBool("allow third person zoom", "Input"))
MWBase::Environment::get().getWorld()->setCameraDistance(static_cast<float>(arg.zrel), true, true);
}
}
}
@ -1105,13 +1120,13 @@ namespace MWInput
{
if(arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
{
mGamepadZoom = static_cast<float>(arg.value / 10000 * 8.5f);
return; // Do not propogate event.
mGamepadZoom = arg.value * 0.85f / 1000.f;
return; // Do not propagate event.
}
else if(arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT)
{
mGamepadZoom = static_cast<float>(-(arg.value / 10000 * 8.5f));
return; // Do not propogate event.
mGamepadZoom = -arg.value * 0.85f / 1000.f;
return; // Do not propagate event.
}
}
}
@ -1527,6 +1542,10 @@ namespace MWInput
defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT;
defaultMouseButtonBindings[A_Use] = SDL_BUTTON_LEFT;
std::map<int, ICS::InputControlSystem::MouseWheelClick> defaultMouseWheelBindings;
defaultMouseWheelBindings[A_ZoomIn] = ICS::InputControlSystem::MouseWheelClick::UP;
defaultMouseWheelBindings[A_ZoomOut] = ICS::InputControlSystem::MouseWheelClick::DOWN;
for (int i = 0; i < A_Last; ++i)
{
ICS::Control* control;
@ -1545,6 +1564,7 @@ namespace MWInput
if (!controlExists || force ||
( mInputBinder->getKeyBinding (control, ICS::Control::INCREASE) == SDL_SCANCODE_UNKNOWN
&& mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS
&& mInputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE) == ICS::InputControlSystem::MouseWheelClick::UNASSIGNED
))
{
clearAllKeyBindings(control);
@ -1561,6 +1581,12 @@ namespace MWInput
control->setInitialValue(0.0f);
mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE);
}
else if (defaultMouseWheelBindings.find(i) != defaultMouseWheelBindings.end()
&& (force || !mInputBinder->isMouseWheelBound(defaultMouseWheelBindings[i])))
{
control->setInitialValue(0.f);
mInputBinder->addMouseWheelBinding(control, defaultMouseWheelBindings[i], ICS::Control::INCREASE);
}
if (i == A_LookLeftRight && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_4) && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_6))
{
@ -1651,6 +1677,10 @@ namespace MWInput
if (action == A_Screenshot)
return "Screenshot";
else if (action == A_ZoomIn)
return "Zoom In";
else if (action == A_ZoomOut)
return "Zoom Out";
descriptions[A_Use] = "sUse";
descriptions[A_Activate] = "sActivate";
@ -1703,10 +1733,25 @@ namespace MWInput
SDL_Scancode key = mInputBinder->getKeyBinding (c, ICS::Control::INCREASE);
unsigned int mouse = mInputBinder->getMouseButtonBinding (c, ICS::Control::INCREASE);
ICS::InputControlSystem::MouseWheelClick wheel = mInputBinder->getMouseWheelBinding(c, ICS::Control::INCREASE);
if (key != SDL_SCANCODE_UNKNOWN)
return MyGUI::TextIterator::toTagsString(mInputBinder->scancodeToString (key));
else if (mouse != ICS_MAX_DEVICE_BUTTONS)
return "#{sMouse} " + std::to_string(mouse);
else if (wheel != ICS::InputControlSystem::MouseWheelClick::UNASSIGNED)
switch (wheel)
{
case ICS::InputControlSystem::MouseWheelClick::UP:
return "Mouse Wheel Up";
case ICS::InputControlSystem::MouseWheelClick::DOWN:
return "Mouse Wheel Down";
case ICS::InputControlSystem::MouseWheelClick::RIGHT:
return "Mouse Wheel Right";
case ICS::InputControlSystem::MouseWheelClick::LEFT:
return "Mouse Wheel Left";
default:
return "#{sNone}";
}
else
return "#{sNone}";
}
@ -1793,6 +1838,8 @@ namespace MWInput
ret.push_back(A_MoveLeft);
ret.push_back(A_MoveRight);
ret.push_back(A_TogglePOV);
ret.push_back(A_ZoomIn);
ret.push_back(A_ZoomOut);
ret.push_back(A_Run);
ret.push_back(A_AlwaysRun);
ret.push_back(A_Sneak);
@ -1831,6 +1878,8 @@ namespace MWInput
{
std::vector<int> ret;
ret.push_back(A_TogglePOV);
ret.push_back(A_ZoomIn);
ret.push_back(A_ZoomOut);
ret.push_back(A_Sneak);
ret.push_back(A_Activate);
ret.push_back(A_Use);
@ -1870,13 +1919,6 @@ namespace MWInput
mInputBinder->enableDetectingBindingState (c, ICS::Control::INCREASE);
}
void InputManager::mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction)
{
// we don't want mouse movement bindings
return;
}
void InputManager::keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, SDL_Scancode key, ICS::Control::ControlChangingDirection direction)
{
@ -1908,6 +1950,13 @@ namespace MWInput
MWBase::Environment::get().getWindowManager ()->notifyInputActionBound ();
}
void InputManager::mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction)
{
// we don't want mouse movement bindings
return;
}
void InputManager::mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, unsigned int button, ICS::Control::ControlChangingDirection direction)
{
@ -1919,6 +1968,17 @@ namespace MWInput
MWBase::Environment::get().getWindowManager ()->notifyInputActionBound ();
}
void InputManager::mouseWheelBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, ICS::InputControlSystem::MouseWheelClick click, ICS::Control::ControlChangingDirection direction)
{
if(!mDetectingKeyboard)
return;
clearAllKeyBindings(control);
control->setInitialValue(0.0f);
ICS::DetectingBindingListener::mouseWheelBindingDetected(ICS, control, click, direction);
MWBase::Environment::get().getWindowManager()->notifyInputActionBound();
}
void InputManager::joystickAxisBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control
, int axis, ICS::Control::ControlChangingDirection direction)
{
@ -1953,6 +2013,8 @@ namespace MWInput
mInputBinder->removeKeyBinding (mInputBinder->getKeyBinding (control, ICS::Control::INCREASE));
if (mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS)
mInputBinder->removeMouseButtonBinding (mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE));
if (mInputBinder->getMouseWheelBinding (control, ICS::Control::INCREASE) != ICS::InputControlSystem::MouseWheelClick::UNASSIGNED)
mInputBinder->removeMouseWheelBinding (mInputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE));
}
void InputManager::clearAllControllerBindings (ICS::Control* control)

View file

@ -56,6 +56,7 @@ struct SDL_Window;
namespace MWInput
{
const float ZOOM_SCALE = 120.f; /// Used for scrolling camera in and out
/**
* @brief Class that handles all input and key bindings for OpenMW.
@ -120,6 +121,8 @@ namespace MWInput
virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id );
virtual void mouseMoved( const SDLUtil::MouseMotionEvent &arg );
virtual void mouseWheelMoved( const SDL_MouseWheelEvent &arg);
virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg);
virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg);
virtual void axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg);
@ -133,15 +136,18 @@ namespace MWInput
virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue);
virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction);
virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, SDL_Scancode key, ICS::Control::ControlChangingDirection direction);
virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction);
virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, unsigned int button, ICS::Control::ControlChangingDirection direction);
virtual void mouseWheelBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, ICS::InputControlSystem::MouseWheelClick click, ICS::Control::ControlChangingDirection direction);
virtual void joystickAxisBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control
, int axis, ICS::Control::ControlChangingDirection direction);
@ -268,33 +274,33 @@ namespace MWInput
A_Unused,
A_Screenshot, // Take a screenshot
A_Screenshot, // Take a screenshot
A_Inventory, // Toggle inventory screen
A_Inventory, // Toggle inventory screen
A_Console, // Toggle console screen
A_Console, // Toggle console screen
A_MoveLeft, // Move player left / right
A_MoveLeft, // Move player left / right
A_MoveRight,
A_MoveForward, // Forward / Backward
A_MoveForward, // Forward / Backward
A_MoveBackward,
A_Activate,
A_Use, //Use weapon, spell, etc.
A_Use, //Use weapon, spell, etc.
A_Jump,
A_AutoMove, //Toggle Auto-move forward
A_Rest, //Rest
A_Journal, //Journal
A_Weapon, //Draw/Sheath weapon
A_Spell, //Ready/Unready Casting
A_Run, //Run when held
A_CycleSpellLeft, //cycling through spells
A_AutoMove, //Toggle Auto-move forward
A_Rest, //Rest
A_Journal, //Journal
A_Weapon, //Draw/Sheath weapon
A_Spell, //Ready/Unready Casting
A_Run, //Run when held
A_CycleSpellLeft, //cycling through spells
A_CycleSpellRight,
A_CycleWeaponLeft,//Cycling through weapons
A_CycleWeaponLeft, //Cycling through weapons
A_CycleWeaponRight,
A_ToggleSneak, //Toggles Sneak
A_AlwaysRun, //Toggle Walking/Running
A_ToggleSneak, //Toggles Sneak
A_AlwaysRun, //Toggle Walking/Running
A_Sneak,
A_QuickSave,
@ -322,12 +328,15 @@ namespace MWInput
A_ToggleDebug,
A_LookUpDown, //Joystick look
A_LookUpDown, //Joystick look
A_LookLeftRight,
A_MoveForwardBackward,
A_MoveLeftRight,
A_Last // Marker for the last item
A_ZoomIn,
A_ZoomOut,
A_Last // Marker for the last item
};
};
}

View file

@ -161,6 +161,7 @@ namespace MWMechanics
{
static const int GREETING_SHOULD_START = 4; //how many updates should pass before NPC can greet player
static const int GREETING_SHOULD_END = 10;
static const float DECELERATE_DISTANCE = 512.f;
class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor
{
@ -453,6 +454,26 @@ namespace MWMechanics
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
}
void Actors::updateMovementSpeed(const MWWorld::Ptr& actor)
{
float previousSpeedFactor = actor.getClass().getMovementSettings(actor).mSpeedFactor;
float newSpeedFactor = 1.f;
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
MWMechanics::AiSequence& seq = stats.getAiSequence();
if (!seq.isEmpty() && seq.getActivePackage()->useVariableSpeed())
{
osg::Vec3f targetPos = seq.getActivePackage()->getDestination();
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
float distance = (targetPos - actorPos).length();
if (distance < DECELERATE_DISTANCE)
newSpeedFactor = std::max(0.7f, 0.1f * previousSpeedFactor * (distance/64.f + 2.f));
}
actor.getClass().getMovementSettings(actor).mSpeedFactor = newSpeedFactor;
}
void Actors::updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly)
{
if (!actor.getClass().isActor() || actor == getPlayer())
@ -1865,6 +1886,7 @@ namespace MWMechanics
stats.getAiSequence().execute(iter->first, *ctrl, duration);
updateGreetingState(iter->first, timerUpdateHello > 0);
playIdleDialogue(iter->first);
updateMovementSpeed(iter->first);
}
}
}

View file

@ -121,6 +121,7 @@ namespace MWMechanics
void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer);
void playIdleDialogue(const MWWorld::Ptr& actor);
void updateMovementSpeed(const MWWorld::Ptr& actor);
void updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly);
void turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir);

View file

@ -36,12 +36,16 @@ namespace MWMechanics
virtual int getTypeId() const;
virtual bool useVariableSpeed() const { return true;}
virtual bool sideWithTarget() const { return true; }
void writeState(ESM::AiSequence::AiSequence &sequence) const;
void fastForward(const MWWorld::Ptr& actor, AiState& state);
virtual osg::Vec3f getDestination() { return osg::Vec3f(mX, mY, mZ); }
private:
std::string mCellId;
float mX;

View file

@ -7,6 +7,8 @@
#include <components/esm/defs.hpp>
#include "../mwworld/ptr.hpp"
#include "pathfinding.hpp"
namespace ESM
@ -61,6 +63,8 @@ namespace MWMechanics
virtual int getTypeId() const;
virtual bool useVariableSpeed() const { return true;}
/// Returns the actor being followed
std::string getFollowedActor();
@ -72,6 +76,15 @@ namespace MWMechanics
void fastForward(const MWWorld::Ptr& actor, AiState& state);
virtual osg::Vec3f getDestination()
{
MWWorld::Ptr target = getTarget();
if (target.isEmpty())
return osg::Vec3f(0, 0, 0);
return target.getRefData().getPosition().asVec3();
}
/*
Start of tes3mp addition

View file

@ -73,6 +73,9 @@ namespace MWMechanics
/// Higher number is higher priority (0 being the lowest)
virtual unsigned int getPriority() const {return 0;}
/// Check if package use movement with variable speed
virtual bool useVariableSpeed() const { return false;}
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {}
/// Simulates the passing of time
@ -99,6 +102,8 @@ namespace MWMechanics
/// Return true if this package should repeat. Currently only used for Wander packages.
virtual bool getRepeat() const;
virtual osg::Vec3f getDestination() { return osg::Vec3f(0, 0, 0); }
/// Reset pathfinding state
void reset();

View file

@ -388,6 +388,11 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
}
}
bool MWMechanics::AiSequence::isEmpty() const
{
return mPackages.empty();
}
AiPackage* MWMechanics::AiSequence::getActivePackage()
{
if(mPackages.empty())

View file

@ -132,6 +132,8 @@ namespace MWMechanics
\see ESM::AIPackageList **/
void fill (const ESM::AIPackageList& list);
bool isEmpty() const;
void writeState (ESM::AiSequence::AiSequence& sequence) const;
void readState (const ESM::AiSequence::AiSequence& sequence);
};

View file

@ -32,6 +32,10 @@ namespace MWMechanics
virtual int getTypeId() const;
virtual bool useVariableSpeed() const { return true;}
virtual osg::Vec3f getDestination() { return osg::Vec3f(mX, mY, mZ); }
private:
float mX;
float mY;

View file

@ -10,11 +10,13 @@ namespace MWMechanics
{
float mPosition[3];
float mRotation[3];
float mSpeedFactor;
Movement()
{
mPosition[0] = mPosition[1] = mPosition[2] = 0.0f;
mRotation[0] = mRotation[1] = mRotation[2] = 0.0f;
mSpeedFactor = 1.f;
}
osg::Vec3f asVec3()

View file

@ -107,7 +107,7 @@ namespace MWMechanics
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(
it->mEffectID);
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce))
x = std::max(1.f, x);
x *= 0.1f * magicEffect->mData.mBaseCost;

View file

@ -331,7 +331,7 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt, ++i)
{
const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
if ((effectIt->mEffectID != ESM::MagicEffect::Corprus) && (magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) // APPLIED_ONCE
if ((effectIt->mEffectID != ESM::MagicEffect::Corprus) && (magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce))
{
float random = 1.f;
if (mSpells[spell].mEffectRands.find(i) != mSpells[spell].mEffectRands.end())

View file

@ -361,7 +361,7 @@ namespace Compiler
opcodePlayLoopSound3DExplicit);
extensions.registerInstruction ("playloopsound3dvp", "cff", opcodePlayLoopSound3DVP,
opcodePlayLoopSound3DVPExplicit);
extensions.registerInstruction ("stopsound", "c", opcodeStopSound,
extensions.registerInstruction ("stopsound", "cXX", opcodeStopSound,
opcodeStopSoundExplicit);
extensions.registerFunction ("getsoundplaying", 'l', "c", opcodeGetSoundPlaying,
opcodeGetSoundPlayingExplicit);
@ -537,7 +537,7 @@ namespace Compiler
extensions.registerInstruction("setpos","cf",opcodeSetPos,opcodeSetPosExplicit);
extensions.registerFunction("getpos",'f',"c",opcodeGetPos,opcodeGetPosExplicit);
extensions.registerFunction("getstartingpos",'f',"c",opcodeGetStartingPos,opcodeGetStartingPosExplicit);
extensions.registerInstruction("position","ffffX",opcodePosition,opcodePositionExplicit);
extensions.registerInstruction("position","ffffz",opcodePosition,opcodePositionExplicit);
extensions.registerInstruction("positioncell","ffffcX",opcodePositionCell,opcodePositionCellExplicit);
extensions.registerInstruction("placeitemcell","ccffffX",opcodePlaceItemCell);
extensions.registerInstruction("placeitem","cffffX",opcodePlaceItem);

View file

@ -1,8 +1,6 @@
#include "scanner.hpp"
#include <cassert>
#include <cctype>
#include <sstream>
#include <iterator>
#include "exception.hpp"
@ -14,14 +12,12 @@
namespace Compiler
{
bool Scanner::get (char& c)
bool Scanner::get (MultiChar& c)
{
mStream.get (c);
if (!mStream.good())
if (!c.getFrom(mStream))
return false;
mPrevLoc =mLoc;
mPrevLoc = mLoc;
if (c=='\n')
{
@ -34,15 +30,15 @@ namespace Compiler
else
{
++mLoc.mColumn;
mLoc.mLiteral += c;
c.appendTo(mLoc.mLiteral);
}
return true;
}
void Scanner::putback (char c)
void Scanner::putback (MultiChar& c)
{
mStream.putback (c);
c.putback(mStream);
mLoc = mPrevLoc;
}
@ -80,7 +76,7 @@ namespace Compiler
break;
}
char c;
MultiChar c;
if (!get (c))
{
@ -91,7 +87,7 @@ namespace Compiler
{
std::string comment;
comment += c;
c.appendTo(comment);
while (get (c))
{
@ -101,7 +97,7 @@ namespace Compiler
break;
}
else
comment += c;
c.appendTo(comment);
}
TokenLoc loc (mLoc);
@ -109,7 +105,7 @@ namespace Compiler
return parser.parseComment (comment, loc, *this);
}
else if (isWhitespace (c))
else if (c.isWhitespace())
{
mLoc.mLiteral.clear();
return true;
@ -120,7 +116,7 @@ namespace Compiler
mLoc.mLiteral.clear();
return true;
}
else if (std::isalpha (c) || c=='_' || c=='"')
else if (c.isAlpha() || c=='_' || c=='"')
{
bool cont = false;
@ -130,7 +126,7 @@ namespace Compiler
return cont;
}
}
else if (std::isdigit (c))
else if (c.isDigit())
{
bool cont = false;
@ -162,24 +158,24 @@ namespace Compiler
throw SourceException();
}
bool Scanner::scanInt (char c, Parser& parser, bool& cont)
bool Scanner::scanInt (MultiChar& c, Parser& parser, bool& cont)
{
assert(c != '\0');
std::string value;
value += c;
c.appendTo(value);
bool error = false;
while (get (c))
{
if (std::isdigit (c))
if (c.isDigit())
{
value += c;
c.appendTo(value);
}
else if (c!='-' && isStringCharacter (c))
else if (!c.isMinusSign() && isStringCharacter (c))
{
error = true;
value += c;
c.appendTo(value);
}
else if (c=='.')
{
@ -224,19 +220,19 @@ namespace Compiler
{
std::string value = intValue + ".";
char c;
MultiChar c;
bool empty = intValue.empty() || intValue=="-";
bool error = false;
while (get (c))
{
if (std::isdigit (c))
if (c.isDigit())
{
value += c;
c.appendTo(value);
empty = false;
}
else if (std::isalpha (c) || c=='_')
else if (c.isAlpha() || c=='_')
error = true;
else
{
@ -279,10 +275,10 @@ namespace Compiler
0
};
bool Scanner::scanName (char c, Parser& parser, bool& cont)
bool Scanner::scanName (MultiChar& c, Parser& parser, bool& cont)
{
std::string name;
name += c;
c.appendTo(name);
if (!scanName (name))
return false;
@ -315,8 +311,8 @@ namespace Compiler
// Russian localization and some mods use a quirk - add newline character directly
// to compiled bytecode via HEX-editor to implement multiline messageboxes.
// Of course, original editor will not compile such script.
// Allow messageboxes to bybass the "incomplete string or name" error.
// Of course, original editor can not compile such script.
// Allow messageboxes to bypass the "incomplete string or name" error.
if (lowerCase == "messagebox")
enableIgnoreNewlines();
else if (isKeyword)
@ -344,7 +340,7 @@ namespace Compiler
bool Scanner::scanName (std::string& name)
{
char c;
MultiChar c;
bool error = false;
while (get (c))
@ -353,7 +349,7 @@ namespace Compiler
{
if (c=='"')
{
name += c;
c.appendTo(name);
break;
}
// ignoring escape sequences for now, because they are messing up stupid Windows path names.
@ -380,20 +376,20 @@ namespace Compiler
}
else if (!(c=='"' && name.empty()))
{
if (!isStringCharacter (c) && !(mTolerantNames && (c=='.' || c=='-')))
if (!isStringCharacter (c) && !(mTolerantNames && (c=='.' || c == '-')))
{
putback (c);
break;
}
}
name += c;
c.appendTo(name);
}
return !error;
}
bool Scanner::scanSpecial (char c, Parser& parser, bool& cont)
bool Scanner::scanSpecial (MultiChar& c, Parser& parser, bool& cont)
{
int special = -1;
@ -410,7 +406,7 @@ namespace Compiler
{
putback (c);
if (std::isdigit (c))
if (c.isDigit())
return scanFloat ("", parser, cont);
}
@ -428,7 +424,7 @@ namespace Compiler
else if (c == '>' || c == '<') // Treat => and =< as ==
{
special = S_cmpEQ;
mErrorHandler.warning (std::string("invalid operator =") + c + ", treating it as ==", mLoc);
mErrorHandler.warning (std::string("invalid operator =") + c.data() + ", treating it as ==", mLoc);
}
else
{
@ -463,7 +459,7 @@ namespace Compiler
else
return false;
}
else if (c=='-')
else if (c.isMinusSign())
{
if (get (c))
{
@ -478,32 +474,6 @@ namespace Compiler
else
special = S_minus;
}
else if (static_cast<unsigned char> (c)==0xe2)
{
/// Workaround for some translator who apparently can't keep his minus in order
/// \todo disable for later script formats
if (get (c) && static_cast<unsigned char> (c)==0x80 &&
get (c) && static_cast<unsigned char> (c)==0x93)
{
if (get (c))
{
if (c=='>')
special = S_ref;
else
{
putback (c);
special = S_minus;
}
}
else
special = S_minus;
}
else
{
mErrorHandler.error ("Invalid character", mLoc);
return false;
}
}
else if (c=='<')
{
if (get (c))
@ -582,20 +552,21 @@ namespace Compiler
return true;
}
bool Scanner::isStringCharacter (char c, bool lookAhead)
bool Scanner::isStringCharacter (MultiChar& c, bool lookAhead)
{
return std::isalpha (c) || std::isdigit (c) || c=='_' ||
/// \todo disable this when doing more stricter compiling
c=='`' || c=='\'' ||
if (lookAhead && c.isMinusSign())
{
/// \todo disable this when doing more stricter compiling. Also, find out who is
/// responsible for allowing it in the first place and meet up with that person in
/// a dark alley.
(c=='-' && (!lookAhead || isStringCharacter (mStream.peek(), false)));
}
MultiChar next;
if (next.peek(mStream) && isStringCharacter (next, false))
return true;
}
bool Scanner::isWhitespace (char c)
{
return c==' ' || c=='\t';
return c.isAlpha() || c.isDigit() || c=='_' ||
/// \todo disable this when doing more stricter compiling
c=='`' || c=='\'';
}
// constructor

View file

@ -1,9 +1,11 @@
#ifndef COMPILER_SCANNER_H_INCLUDED
#define COMPILER_SCANNER_H_INCLUDED
#include <cctype>
#include <string>
#include <iosfwd>
#include <vector>
#include <sstream>
#include "tokenloc.hpp"
@ -18,6 +20,160 @@ namespace Compiler
/// This class translate a char-stream to a token stream (delivered via
/// parser-callbacks).
class MultiChar
{
public:
MultiChar()
{
blank();
}
MultiChar(const char ch)
{
blank();
mData[0] = ch;
mLength = getCharLength(ch);
}
int getCharLength(const char ch)
{
unsigned char c = ch;
if (c<=127) return 0;
else if ((c & 0xE0) == 0xC0) return 1;
else if ((c & 0xF0) == 0xE0) return 2;
else if ((c & 0xF8) == 0xF0) return 3;
else return -1;
}
bool operator== (const char ch)
{
return mData[0]==ch && mData[1]==0 && mData[2]==0 && mData[3]==0;
}
bool operator== (const MultiChar& ch)
{
return mData[0]==ch.mData[0] && mData[1]==ch.mData[1] && mData[2]==ch.mData[2] && mData[3]==ch.mData[3];
}
bool operator!= (const char ch)
{
return mData[0]!=ch || mData[1]!=0 || mData[2]!=0 || mData[3]!=0;
}
bool isWhitespace()
{
return (mData[0]==' ' || mData[0]=='\t') && mData[1]==0 && mData[2]==0 && mData[3]==0;
}
bool isDigit()
{
return std::isdigit(mData[0]) && mData[1]==0 && mData[2]==0 && mData[3]==0;
}
bool isMinusSign()
{
if (mData[0] == '-' && mData[1] == 0 && mData[2] == 0 && mData[3] == 0)
return true;
return mData[0] == '\xe2' && mData[1] == '\x80' && mData[2] == '\x93' && mData[3] == 0;
}
bool isAlpha()
{
if (isMinusSign())
return false;
return std::isalpha(mData[0]) || mData[1]!=0 || mData[2]!=0 || mData[3]!=0;
}
void appendTo(std::string& str)
{
for (int i = 0; i <= mLength; i++)
str += mData[i];
}
void putback (std::istream& in)
{
for (int i = mLength; i >= 0; i--)
in.putback (mData[i]);
}
bool getFrom(std::istream& in)
{
blank();
char ch = in.peek();
if (!in.good())
return false;
int length = getCharLength(ch);
if (length < 0) return false;
for (int i = 0; i <= length; i++)
{
in.get (ch);
if (!in.good())
return false;
mData[i] = ch;
}
mLength = length;
return true;
}
bool peek(std::istream& in)
{
std::streampos p_orig = in.tellg();
char ch = in.peek();
if (!in.good())
return false;
int length = getCharLength(ch);
if (length < 0) return false;
for (int i = 0; i <= length; i++)
{
if (length >= i)
{
in.get (ch);
if (!in.good())
return false;
mData[i] = ch;
}
}
mLength = length;
in.seekg(p_orig);
return true;
};
void blank()
{
std::fill(mData, mData + sizeof(mData), 0);
mLength = -1;
}
std::string data()
{
// NB: mLength is the number of the last element in the array
return std::string(mData, mLength + 1);
}
private:
char mData[4];
int mLength;
};
class Scanner
{
enum putback_type
@ -79,26 +235,24 @@ namespace Compiler
Scanner (const Scanner&);
Scanner& operator= (const Scanner&);
bool get (char& c);
bool get (MultiChar& c);
void putback (char c);
void putback (MultiChar& c);
bool scanToken (Parser& parser);
bool scanInt (char c, Parser& parser, bool& cont);
bool scanInt (MultiChar& c, Parser& parser, bool& cont);
bool scanFloat (const std::string& intValue, Parser& parser, bool& cont);
bool scanName (char c, Parser& parser, bool& cont);
bool scanName (MultiChar& c, Parser& parser, bool& cont);
/// \param name May contain the start of the name (one or more characters)
bool scanName (std::string& name);
bool scanSpecial (char c, Parser& parser, bool& cont);
bool scanSpecial (MultiChar& c, Parser& parser, bool& cont);
bool isStringCharacter (char c, bool lookAhead = true);
static bool isWhitespace (char c);
bool isStringCharacter (MultiChar& c, bool lookAhead = true);
public:

View file

@ -30,15 +30,17 @@ struct MagicEffect
CastSelf = 0x40, // Allows range - cast on self.
CastTouch = 0x80, // Allows range - cast on touch.
CastTarget = 0x100, // Allows range - cast on target.
UncappedDamage = 0x1000, // Negates multiple cap behaviours. Allows an effect to reduce an attribute below zero; removes the normal minimum effect duration of 1 second.
AppliedOnce = 0x1000, // An effect that is applied once it lands, instead of continuously. Allows an effect to reduce an attribute below zero; removes the normal minimum effect duration of 1 second.
Stealth = 0x2000, // Unused
NonRecastable = 0x4000, // Does not land if parent spell is already affecting target. Shows "you cannot re-cast" message for self target.
IllegalDaedra = 0x8000, // Unused
Unreflectable = 0x10000, // Cannot be reflected, the effect always lands normally.
CasterLinked = 0x20000, // Must quench if caster is dead, or not an NPC/creature. Not allowed in containter/door trap spells.
// Originally modifiable flags
AllowSpellmaking = 0x200, // Can be used for spellmaking
AllowEnchanting = 0x400, // Can be used for enchanting
NegativeLight = 0x800 // Negative light source
NegativeLight = 0x800 // Unused
};
enum MagnitudeDisplayType

View file

@ -843,6 +843,11 @@ void SceneUtil::MWShadowTechnique::setPolygonOffset(float factor, float units)
}
}
void SceneUtil::MWShadowTechnique::setShadowFadeStart(float shadowFadeStart)
{
_shadowFadeStart = shadowFadeStart;
}
void SceneUtil::MWShadowTechnique::enableFrontFaceCulling()
{
_useFrontFaceCulling = true;
@ -1493,6 +1498,9 @@ void MWShadowTechnique::createShaders()
osg::ref_ptr<osg::Uniform> baseTextureUnit = new osg::Uniform("baseTextureUnit",(int)_baseTextureUnit);
_uniforms.push_back(baseTextureUnit.get());
_uniforms.push_back(new osg::Uniform("maximumShadowMapDistance", (float)settings->getMaximumShadowMapDistance()));
_uniforms.push_back(new osg::Uniform("shadowFadeStart", (float)_shadowFadeStart));
for(unsigned int sm_i=0; sm_i<settings->getNumShadowMapsPerLight(); ++sm_i)
{
{

View file

@ -76,6 +76,8 @@ namespace SceneUtil {
virtual void setPolygonOffset(float factor, float units);
virtual void setShadowFadeStart(float shadowFadeStart);
virtual void enableFrontFaceCulling();
virtual void disableFrontFaceCulling();
@ -257,6 +259,8 @@ namespace SceneUtil {
bool _useFrontFaceCulling = true;
float _shadowFadeStart = 0.0;
class DebugHUD : public osg::Referenced
{
public:

View file

@ -27,6 +27,14 @@ namespace SceneUtil
mShadowSettings->setNumShadowMapsPerLight(numberOfShadowMapsPerLight);
mShadowSettings->setBaseShadowTextureUnit(8 - numberOfShadowMapsPerLight);
const float maximumShadowMapDistance = Settings::Manager::getFloat("maximum shadow map distance", "Shadows");
if (maximumShadowMapDistance > 0)
{
const float shadowFadeStart = std::min(std::max(0.f, Settings::Manager::getFloat("shadow fade start", "Shadows")), 1.f);
mShadowSettings->setMaximumShadowMapDistance(maximumShadowMapDistance);
mShadowTechnique->setShadowFadeStart(maximumShadowMapDistance * shadowFadeStart);
}
mShadowSettings->setMinimumShadowMapNearFarRatio(Settings::Manager::getFloat("minimum lispsm near far ratio", "Shadows"));
if (Settings::Manager::getBool("compute tight scene bounds", "Shadows"))
mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES);
@ -117,6 +125,8 @@ namespace SceneUtil
definesWithShadows["shadowNormalOffset"] = std::to_string(Settings::Manager::getFloat("normal offset distance", "Shadows"));
definesWithShadows["limitShadowMapDistance"] = Settings::Manager::getFloat("maximum shadow map distance", "Shadows") > 0 ? "1" : "0";
return definesWithShadows;
}
@ -138,6 +148,8 @@ namespace SceneUtil
definesWithoutShadows["shadowNormalOffset"] = "0.0";
definesWithoutShadows["limitShadowMapDistance"] = "0";
return definesWithoutShadows;
}

View file

@ -30,6 +30,7 @@ public:
virtual void mouseMoved( const MouseMotionEvent &arg ) = 0;
virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0;
virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0;
virtual void mouseWheelMoved( const SDL_MouseWheelEvent &arg) = 0;
};
class KeyListener

View file

@ -77,6 +77,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> v
break;
case SDL_MOUSEWHEEL:
mMouseListener->mouseMoved(_packageMouseMotion(evt));
mMouseListener->mouseWheelMoved(evt.wheel);
break;
case SDL_MOUSEBUTTONDOWN:
mMouseListener->mousePressed(evt.button, evt.button.button);

View file

@ -108,7 +108,7 @@ void QuadTreeNode::initNeighbours()
getChild(i)->initNeighbours();
}
void QuadTreeNode::traverse(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist)
void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist)
{
if (!hasValidBounds())
return;
@ -124,7 +124,7 @@ void QuadTreeNode::traverse(ViewData* vd, const osg::Vec3f& viewPoint, LodCallba
else
{
for (unsigned int i=0; i<getNumChildren(); ++i)
getChild(i)->traverse(vd, viewPoint, lodCallback, maxDist);
getChild(i)->traverseNodes(vd, viewPoint, lodCallback, maxDist);
}
}

View file

@ -92,7 +92,7 @@ namespace Terrain
const osg::Vec2f& getCenter() const;
/// Traverse nodes according to LOD selection.
void traverse(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist);
void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist);
/// Traverse to a specific node and add only that node.
void traverseTo(ViewData* vd, float size, const osg::Vec2f& center);

View file

@ -353,7 +353,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv)
mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5,y+0.5));
}
else
mRootNode->traverse(vd, cv->getViewPoint(), mLodCallback, mViewDistance);
mRootNode->traverseNodes(vd, cv->getViewPoint(), mLodCallback, mViewDistance);
}
else
{
@ -446,7 +446,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic
ViewData* vd = static_cast<ViewData*>(view);
vd->setViewPoint(viewPoint);
mRootNode->traverse(vd, viewPoint, mLodCallback, mViewDistance);
mRootNode->traverseNodes(vd, viewPoint, mLodCallback, mViewDistance);
for (unsigned int i=0; i<vd->getNumEntries() && !abort; ++i)
{

View file

@ -197,10 +197,6 @@ The appearance and count of shown ammunition depends on type and count of equipp
It is important to make sure the names of empty nodes start with ``"Bip01 "``, or the engine will optimize them out.
4. Shields
Shield holstering is not supported at the moment since it conflicts with any mods which use pseudo-shields as held items (such as Animated Morrowind and Hold It).
An example of a mod which uses this feature is `Weapon Sheathing`_.

View file

@ -54,18 +54,6 @@ based on whether the caps lock key was on or off at the time you exited.
This settings can be toggled in game by pressing the CapsLock key and exiting.
allow third person zoom
-----------------------
:Type: boolean
:Range: True/False
:Default: False
Allow zooming in and out using the middle mouse wheel in third person view.
This feature may not work correctly if the mouse wheel is bound to other actions,
and may be triggered accidentally in some cases, so is disabled by default.
This setting can only be configured by editing the settings configuration file.
camera sensitivity
------------------

View file

@ -16,7 +16,6 @@ Unlike in the original Morrowind engine, 'Shadow Mapping' is used, which can hav
Bear in mind that this will force OpenMW to use shaders as if :ref:`force shaders` was enabled.
A keen developer may be able to implement compatibility with fixed-function mode using the advice of `this post <https://github.com/OpenMW/openmw/pull/1547#issuecomment-369657381>`_, but it may be more difficult than it seems.
number of shadow maps
---------------------
@ -28,6 +27,28 @@ Control how many shadow maps to use - more of these means each shadow map texel
Using too many shadow maps will lead to them overriding texture slots used for other effects, producing unpleasant artefacts.
A value of three is recommended in most cases, but other values may produce better results or performance.
maximum shadow map distance
---------------------------
:Type: float
:Range: The whole range of 32-bit floating point
:Default: 8192
The maximum distance from the camera shadows cover, limiting their overall area coverage
and improving their quality and performance at the cost of removing shadows of distant objects or terrain.
Set this to a non-positive value to remove the limit.
shadow fade start
-------------------
:Type: float
:Range: 0.0-1.0
:Default: 0.9
The fraction of the maximum shadow map distance at which the shadows will begin to fade away.
Tweaking it will make the transition proportionally more or less smooth.
This setting has no effect if the maximum shadow map distance is non-positive (infinite).
allow shadow map overlap
------------------------
@ -213,4 +234,4 @@ minimum lispsm near far ratio
Controls the minimum near/far ratio for the Light Space Perspective Shadow Map transformation.
Helps prevent too much detail being brought towards the camera at the expense of detail further from the camera.
Increasing this pushes detail further away by moving the frustum apex further from the near plane.
Increasing this pushes detail further away by moving the frustum apex further from the near plane.

View file

@ -226,6 +226,15 @@ namespace ICS
loadJoystickButtonBinders(xmlControl);
/* ----------------------------------------------------------------------------------------
* OPENMW CODE STARTS HERE
* Mouse Wheel support added by Michael Stopa (Stomy) */
loadMouseWheelBinders(xmlControl);
/* OPENMW CODE ENDS HERE
* ------------------------------------------------------------------------------------- */
// Attach controls to channels
TiXmlElement* xmlChannel = xmlControl->FirstChildElement("Channel");
while(xmlChannel)
@ -307,6 +316,15 @@ namespace ICS
mControlsMouseButtonBinderMap.clear();
mControlsJoystickButtonBinderMap.clear();
/* ----------------------------------------------------------------------------------------
* OPENMW CODE STARTS HERE
* Mouse Wheel support added by Michael Stopa (Stomy) */
mControlsMouseWheelBinderMap.clear();
/* OPENMW CODE ENDS HERE
* ------------------------------------------------------------------------------------- */
ICS_LOG(" - InputControlSystem deleted - ");
}
@ -491,6 +509,67 @@ namespace ICS
control.InsertEndChild(binder);
}
/* ----------------------------------------------------------------------------------------
* OPENMW CODE STARTS HERE
* Mouse Wheel support added by Stomy */
if(getMouseWheelBinding(*o, Control::INCREASE) != MouseWheelClick::UNASSIGNED)
{
TiXmlElement binder( "MouseWheelBinder" );
MouseWheelClick click = getMouseWheelBinding(*o, Control::INCREASE);
bool skip = false;
switch (click) {
case MouseWheelClick::UP: {
binder.SetAttribute("button", "UP");
} break;
case MouseWheelClick::DOWN: {
binder.SetAttribute("button", "DOWN");
} break;
case MouseWheelClick::RIGHT: {
binder.SetAttribute("button", "RIGHT");
} break;
case MouseWheelClick::LEFT: {
binder.SetAttribute("button", "LEFT");
} break;
default: {
skip = true;
} break;
}
binder.SetAttribute( "direction", "INCREASE" );
if (!skip)
control.InsertEndChild(binder);
}
if(getMouseWheelBinding(*o, Control::DECREASE) != MouseWheelClick::UNASSIGNED)
{
TiXmlElement binder( "MouseWheelBinder" );
MouseWheelClick click = getMouseWheelBinding(*o, Control::INCREASE);
bool skip = false;
switch (click) {
case MouseWheelClick::UP: {
binder.SetAttribute("button", "UP");
} break;
case MouseWheelClick::DOWN: {
binder.SetAttribute("button", "DOWN");
} break;
case MouseWheelClick::RIGHT: {
binder.SetAttribute("button", "RIGHT");
} break;
case MouseWheelClick::LEFT: {
binder.SetAttribute("button", "LEFT");
} break;
default: {
skip = true;
} break;
}
binder.SetAttribute( "direction", "DECREASE" );
if (!skip)
control.InsertEndChild(binder);
}
/* OPENMW CODE ENDS HERE
* ------------------------------------------------------------------------------------- */
if(getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)
!= ICS_MAX_DEVICE_BUTTONS)
{

View file

@ -214,6 +214,26 @@ namespace ICS
Uint16 mClientWidth;
Uint16 mClientHeight;
/* ----------------------------------------------------------------------------------------
* OPENMW CODE STARTS HERE
* Mouse Wheel support added by Michael Stopa (Stomy) */
public:
enum class MouseWheelClick : int { UNASSIGNED = 0, UP = 1, DOWN = 2, LEFT = 3, RIGHT = 4};
void mouseWheelMoved(const SDL_MouseWheelEvent &evt);
void addMouseWheelBinding(Control* control, MouseWheelClick click, Control::ControlChangingDirection direction);
void removeMouseWheelBinding(MouseWheelClick click);
MouseWheelClick getMouseWheelBinding(Control* control, ICS::Control::ControlChangingDirection direction);
bool isMouseWheelBound(MouseWheelClick button) const;
protected:
void loadMouseWheelBinders(TiXmlElement* xmlControlNode);
ControlsButtonBinderMapType mControlsMouseWheelBinderMap;
/* OPENMW CODE ENDS HERE
* ------------------------------------------------------------------------------------- */
};
class DllExport DetectingBindingListener
@ -234,6 +254,16 @@ namespace ICS
virtual void joystickButtonBindingDetected(InputControlSystem* ICS, int deviceID, Control* control
, unsigned int button, Control::ControlChangingDirection direction);
/* ----------------------------------------------------------------------------------------
* OPENMW CODE STARTS HERE
* Mouse Wheel support added by Michael Stopa (Stomy) */
virtual void mouseWheelBindingDetected(InputControlSystem* ICS, Control* control,
InputControlSystem::MouseWheelClick click,
Control::ControlChangingDirection direction);
/* OPENMW CODE ENDS HERE
* ------------------------------------------------------------------------------------- */
};
extern const float ICS_MAX;

View file

@ -396,4 +396,171 @@ namespace ICS
ICS->cancelDetectingBindingState();
}
/* ----------------------------------------------------------------------------------------
* OPENMW CODE STARTS HERE
* Mouse Wheel support added by Michael Stopa (Stomy) */
void InputControlSystem::loadMouseWheelBinders(TiXmlElement* xmlControlNode)
{
TiXmlElement* xmlMouseWheelBinder = xmlControlNode->FirstChildElement("MouseWheelBinder");
while (xmlMouseWheelBinder)
{
Control::ControlChangingDirection dir = Control::STOP;
if (std::string(xmlMouseWheelBinder->Attribute("direction")) == "INCREASE")
{
dir = Control::INCREASE;
}
else if (std::string(xmlMouseWheelBinder->Attribute("direction")) == "DECREASE")
{
dir = Control::DECREASE;
}
MouseWheelClick click = MouseWheelClick::UNASSIGNED;
if (std::string(xmlMouseWheelBinder->Attribute("button")) == "UP")
{
click = MouseWheelClick::UP;
}
else if (std::string(xmlMouseWheelBinder->Attribute("button")) == "DOWN")
{
click = MouseWheelClick::DOWN;
}
else if (std::string(xmlMouseWheelBinder->Attribute("button")) == "DOWN")
{
click = MouseWheelClick::RIGHT;
}
else if (std::string(xmlMouseWheelBinder->Attribute("button")) == "DOWN")
{
click = MouseWheelClick::LEFT;
}
addMouseWheelBinding(mControls.back(), click, dir);
xmlMouseWheelBinder = xmlMouseWheelBinder->NextSiblingElement("MouseWheelBinder");
}
}
void InputControlSystem::addMouseWheelBinding(
Control* control,
MouseWheelClick click,
Control::ControlChangingDirection direction)
{
ICS_LOG("\tAdding MouseWheelBinder [button="
+ ToString<int>(static_cast<int>(click)) + ", direction="
+ ToString<int>(direction) + "]");
ControlButtonBinderItem controlButtonBinderItem;
controlButtonBinderItem.control = control;
controlButtonBinderItem.direction = direction;
mControlsMouseWheelBinderMap[static_cast<int>(click)] = controlButtonBinderItem;
}
void InputControlSystem::removeMouseWheelBinding(MouseWheelClick click)
{
ControlsAxisBinderMapType::iterator it = mControlsMouseWheelBinderMap.find(static_cast<int>(click));
if (it != mControlsMouseWheelBinderMap.end())
{
mControlsMouseWheelBinderMap.erase(it);
}
}
InputControlSystem::MouseWheelClick InputControlSystem::getMouseWheelBinding(
Control* control,
ICS::Control::ControlChangingDirection direction)
{
ControlsAxisBinderMapType::iterator it = mControlsMouseWheelBinderMap.begin();
while (it != mControlsMouseWheelBinderMap.end())
{
if (it->first > 0 && it->second.control == control && it->second.direction == direction)
{
return (MouseWheelClick)(it->first);
}
++it;
}
return MouseWheelClick::UNASSIGNED;
}
bool InputControlSystem::isMouseWheelBound(MouseWheelClick button) const
{
return mControlsMouseWheelBinderMap.find(static_cast<int>(button)) != mControlsMouseWheelBinderMap.end();
}
void InputControlSystem::mouseWheelMoved(const SDL_MouseWheelEvent &evt)
{
if (mActive)
{
MouseWheelClick click = MouseWheelClick::UNASSIGNED;
int value;
if (evt.y != 0)
{
value = evt.y;
if (evt.direction == SDL_MOUSEWHEEL_FLIPPED)
value *= -1;
if (value > 0)
click = MouseWheelClick::UP;
else
click = MouseWheelClick::DOWN;
}
else if (evt.x != 0)
{
value = evt.x;
if (evt.direction == SDL_MOUSEWHEEL_FLIPPED)
value *= -1;
if (value > 0)
click = MouseWheelClick::RIGHT;
else
click = MouseWheelClick::LEFT;
}
else
return;
if(!mDetectingBindingControl)
{
// This assumes a single event is sent for every single mouse wheel direction, if they are
// buffered up then all bindings will have to be resolved on every invocation of this function
ControlButtonBinderItem wheelBinderItem = mControlsMouseWheelBinderMap[static_cast<int>(click)];
Control* control = wheelBinderItem.control;
if (control)
{
control->setIgnoreAutoReverse(false);
if (wheelBinderItem.direction == Control::INCREASE)
{
control->setValue(static_cast<float>(std::abs(value)));
control->setChangingDirection(Control::STOP);
}
else if (wheelBinderItem.direction == Control::DECREASE)
{
control->setValue(static_cast<float>(std::abs(value)));
control->setChangingDirection(Control::STOP);
}
}
}
else if(mDetectingBindingListener)
{
mDetectingBindingListener->mouseWheelBindingDetected(this,
mDetectingBindingControl, click, mDetectingBindingDirection);
}
}
}
void DetectingBindingListener::mouseWheelBindingDetected(
InputControlSystem* ICS,
Control* control,
InputControlSystem::MouseWheelClick click,
Control::ControlChangingDirection direction)
{
ICS->removeMouseWheelBinding(click);
InputControlSystem::MouseWheelClick oldClick = ICS->getMouseWheelBinding(control, direction);
if (oldClick != InputControlSystem::MouseWheelClick::UNASSIGNED)
{
ICS->removeMouseWheelBinding(oldClick);
}
ICS->addMouseWheelBinding(control, click, direction);
ICS->cancelDetectingBindingState();
}
/* OPENMW CODE ENDS HERE
* ------------------------------------------------------------------------------------- */
}

View file

@ -48,4 +48,5 @@ Copyright 2018 Bret Curtis <psi29a@gmail.com>
<url type="homepage">https://openmw.org</url>
<url type="bugtracker">https://gitlab.com/OpenMW/openmw/issues</url>
<url type="faq">https://openmw.org/faq/</url>
<content_rating type="oars-1.1" />
</component>

View file

@ -348,9 +348,6 @@ toggle sneak = false
# Player is running by default.
always run = false
# Zoom in and out from player in third person view with mouse wheel.
allow third person zoom = false
# Camera sensitivity when not in GUI mode. (>0.0, e.g. 0.1 to 5.0).
camera sensitivity = 1.0
@ -749,6 +746,12 @@ enable shadows = false
# How many shadow maps to use - more of these means each shadow map texel covers less area, producing better looking shadows, but may decrease performance.
number of shadow maps = 3
# The distance from the camera at which shadows fade away completely. Set to 0 to make the distance infinite.
maximum shadow map distance = 8192
# Fraction of the maximum distance at which shadows begin to gradually fade away.
shadow fade start = 0.9
# If true, allow shadow maps to overlap. Counter-intuitively, will produce better results when the light is behind the camera. When enabled, OpenMW uses Cascaded Shadow Maps and when disabled, it uses Parallel Split Shadow Maps.
allow shadow map overlap = true

View file

@ -114,7 +114,7 @@ void main()
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, decalTex.xyz, decalTex.a);
#endif
float shadowing = unshadowedLightRatio();
float shadowing = unshadowedLightRatio(depth);
#if !PER_PIXEL_LIGHTING

View file

@ -1,6 +1,8 @@
#define SHADOWS @shadows_enabled
#if SHADOWS
uniform float maximumShadowMapDistance;
uniform float shadowFadeStart;
@foreach shadow_texture_unit_index @shadow_texture_unit_list
uniform sampler2DShadow shadowTexture@shadow_texture_unit_index;
varying vec4 shadowSpaceCoords@shadow_texture_unit_index;
@ -11,10 +13,15 @@
@endforeach
#endif // SHADOWS
float unshadowedLightRatio()
float unshadowedLightRatio(float distance)
{
float shadowing = 1.0;
#if SHADOWS
#if @limitShadowMapDistance
float fade = clamp((distance - shadowFadeStart) / (maximumShadowMapDistance - shadowFadeStart), 0.0, 1.0);
if (fade == 1.0)
return shadowing;
#endif
#if @shadowMapsOverlap
bool doneShadows = false;
@foreach shadow_texture_unit_index @shadow_texture_unit_list
@ -41,6 +48,9 @@ float unshadowedLightRatio()
shadowing = min(shadow2DProj(shadowTexture@shadow_texture_unit_index, shadowSpaceCoords@shadow_texture_unit_index).r, shadowing);
@endforeach
#endif
#if @limitShadowMapDistance
shadowing = mix(shadowing, 1.0, fade);
#endif
#endif // SHADOWS
return shadowing;
}

View file

@ -66,7 +66,7 @@ void main()
gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a;
#endif
float shadowing = unshadowedLightRatio();
float shadowing = unshadowedLightRatio(depth);
#if !PER_PIXEL_LIGHTING

View file

@ -160,7 +160,7 @@ void main(void)
vec2 UV = worldPos.xy / (8192.0*5.0) * 3.0;
UV.y *= -1.0;
float shadow = unshadowedLightRatio();
float shadow = unshadowedLightRatio(depthPassthrough);
vec2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z;
screenCoords.y = (1.0-screenCoords.y);

View file

@ -169,6 +169,63 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="animSourcesCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load per-group KF-files and skeleton files from Animations folder&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use additional animation sources</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="sheathingGroup" native="true">
<layout class="QVBoxLayout" name="sheathingLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>20</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="weaponSheathingCheckBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Render holstered weapons (with quivers and scabbards), requires modded assets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Weapon sheathing</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="shieldSheathingCheckBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Render holstered shield, requires modded assets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Shield sheathing</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
@ -178,16 +235,6 @@
<string>Input</string>
</property>
<layout class="QVBoxLayout" name="inputGroupVerticalLayout">
<item>
<widget class="QCheckBox" name="allowThirdPersonZoomCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allow zooming in and out using the middle mouse wheel in third person view. This feature may not work correctly if the mouse wheel is bound to other actions, and may be triggered accidentally in some cases, so is disabled by default.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Allow third person zoom</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="grabCursorCheckBox">
<property name="toolTip">

View file

@ -6,191 +6,353 @@
<rect>
<x>0</x>
<y>0</y>
<width>437</width>
<height>343</height>
<width>650</width>
<height>340</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="displayGroup">
<property name="title">
<string>Display</string>
<widget class="QTabWidget" name="DisplayTabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
<item row="0" column="0">
<widget class="QCheckBox" name="vSyncCheckBox">
<property name="text">
<string>Vertical Sync</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="fullScreenCheckBox">
<property name="text">
<string>Full Screen</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="windowBorderCheckBox">
<property name="text">
<string>Window Border</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="antiAliasingLabel">
<property name="text">
<string>Anti-aliasing:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="screenLabel">
<property name="text">
<string>Screen:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="resolutionLabel">
<property name="text">
<string>Resolution:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="framerateLimitCheckBox">
<property name="text">
<string>Framerate Limit:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="antiAliasingComboBox">
<item>
<property name="text">
<string>0</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>4</string>
</property>
</item>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="screenComboBox"/>
</item>
<item row="5" column="1">
<layout class="QGridLayout" name="resolutionLayout">
<item row="1" column="2">
<layout class="QHBoxLayout" name="customResolutionLayout" stretch="1,0,1">
<item>
<widget class="QSpinBox" name="customWidthSpinBox">
<property name="minimum">
<number>800</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="multiplyLabel">
<widget class="QWidget" name="DisplayWrapper">
<attribute name="title">
<string>Display</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QWidget" name="DisplayWidget">
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,0">
<item row="3" column="0">
<widget class="QLabel" name="antiAliasingLabel">
<property name="text">
<string> x </string>
<string>Anti-aliasing:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="customHeightSpinBox">
<item row="6" column="0">
<widget class="QCheckBox" name="framerateLimitCheckBox">
<property name="text">
<string>Framerate Limit:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<layout class="QGridLayout" name="resolutionLayout">
<item row="1" column="2">
<layout class="QHBoxLayout" name="customResolutionLayout" stretch="1,0,1">
<item>
<widget class="QSpinBox" name="customWidthSpinBox">
<property name="minimum">
<number>800</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="multiplyLabel">
<property name="text">
<string> x </string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="customHeightSpinBox">
<property name="minimum">
<number>600</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="customRadioButton">
<property name="text">
<string>Custom:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="standardRadioButton">
<property name="text">
<string>Standard:</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="resolutionComboBox"/>
</item>
</layout>
</item>
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="framerateLimitSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="suffix">
<string> FPS</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<number>600</number>
<double>1</double>
</property>
<property name="maximum">
<double>1000</double>
</property>
<property name="singleStep">
<double>15</double>
</property>
<property name="value">
<double>300</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="windowBorderCheckBox">
<property name="text">
<string>Window Border</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="fullScreenCheckBox">
<property name="text">
<string>Full Screen</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="resolutionLabel">
<property name="text">
<string>Resolution:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="screenComboBox"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="screenLabel">
<property name="text">
<string>Screen:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="antiAliasingComboBox">
<item>
<property name="text">
<string>0</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>4</string>
</property>
</item>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="vSyncCheckBox">
<property name="text">
<string>Vertical Sync</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="customRadioButton">
<property name="text">
<string>Custom:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="standardRadioButton">
<property name="text">
<string>Standard:</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="resolutionComboBox"/>
</item>
</layout>
</item>
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="framerateLimitSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="suffix">
<string> FPS</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>1</double>
</property>
<property name="maximum">
<double>1000</double>
</property>
<property name="singleStep">
<double>15</double>
</property>
<property name="value">
<double>300</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="ShadowWrapper">
<attribute name="title">
<string>Shadows</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QWidget" name="ShadowWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="shadowsLayout" columnstretch="0,0">
<item row="6" column="0">
<widget class="QCheckBox" name="shadowDistanceCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The distance from the camera at which shadows completely disappear.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Shadow Distance Limit:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="shadowDistanceSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;64 game units is 1 real life yard or about 0.9 m&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="suffix">
<string> unit(s)</string>
</property>
<property name="minimum">
<number>512</number>
</property>
<property name="maximum">
<number>81920</number>
</property>
<property name="value">
<number>8192</number>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="fadeStartLabel">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The fraction of the limit above at which shadows begin to gradually fade away.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Fade Start Multiplier:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="shadowResolutionComboBox">
<item>
<property name="text">
<string>512</string>
</property>
</item>
<item>
<property name="text">
<string>1024</string>
</property>
</item>
<item>
<property name="text">
<string>2048</string>
</property>
</item>
<item>
<property name="text">
<string>4096</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="actorShadowsCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable shadows for NPCs and creatures besides the player character. May have a minor performance impact.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Actor Shadows</string>
</property>
</widget>
</item>
<item row="3" column="0" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="terrainShadowsCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable shadows for the terrain including distant terrain. May have a significant performance and shadow quality impact.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Terrain Shadows</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QDoubleSpinBox" name="fadeStartSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="decimals">
<number>2</number>
</property>
<property name="minimum">
<double>0</double>
</property>
<property name="maximum">
<double>1</double>
</property>
<property name="value">
<double>0.90</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="playerShadowsCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable shadows exclusively for the player character. May have a very minor performance impact.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Player Shadows</string>
</property>
</widget>
</item>
<item row="5" column="0" alignment="Qt::AlignLeft">
<widget class="QLabel" name="shadowResolutionLabel">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The resolution of each individual shadow map. Increasing it significantly improves shadow quality but may have a minor performance impact.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Shadow Map Resolution:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="objectShadowsCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable shadows for primarily inanimate objects. May have a significant performance impact.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Object Shadows</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="indoorShadowsCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Due to limitations with Morrowind's data, only actors can cast shadows indoors, which some might feel is distracting.&lt;/p&gt;&lt;p&gt;Has no effect if actor/player shadows are not enabled.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Indoor Shadows</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>61</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>