mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-17 10:21:11 +00:00
Merge branch 'master' of gitlab.com:openmw/openmw into lua_controller_cursor
This commit is contained in:
commit
3ef2f71062
@ -16,8 +16,10 @@
|
||||
Bug #4754: Stack of ammunition cannot be equipped partially
|
||||
Bug #4816: GetWeaponDrawn returns 1 before weapon is attached
|
||||
Bug #4822: Non-weapon equipment and body parts can't inherit time from parent animation
|
||||
Bug #4898: Odd/Incorrect lighting on meshes
|
||||
Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses
|
||||
Bug #5062: Root bone rotations for NPC animation don't work the same as for creature animation
|
||||
Bug #5065: Actors with scripted animation still try to wander and turn around without moving
|
||||
Bug #5066: Quirks with starting and stopping scripted animations
|
||||
Bug #5129: Stuttering animation on Centurion Archer
|
||||
Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place
|
||||
@ -128,7 +130,9 @@
|
||||
Bug #7742: Governing attribute training limit should use the modified attribute
|
||||
Bug #7758: Water walking is not taken into account to compute path cost on the water
|
||||
Bug #7761: Rain and ambient loop sounds are mutually exclusive
|
||||
Bug #7765: OpenMW-CS: Touch Record option is broken
|
||||
Bug #7770: Sword of the Perithia: Script execution failure
|
||||
Bug #7780: Non-ASCII texture paths in NIF files don't work
|
||||
Feature #2566: Handle NAM9 records for manual cell references
|
||||
Feature #3537: Shader-based water ripples
|
||||
Feature #5173: Support for NiFogProperty
|
||||
|
@ -174,7 +174,7 @@ namespace
|
||||
|
||||
constexpr double expiryDelay = 0;
|
||||
Resource::ImageManager imageManager(&vfs, expiryDelay);
|
||||
Resource::NifFileManager nifFileManager(&vfs);
|
||||
Resource::NifFileManager nifFileManager(&vfs, &encoder.getStatelessEncoder());
|
||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay);
|
||||
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
|
||||
|
||||
|
@ -85,7 +85,7 @@ namespace ESSImport
|
||||
Misc::StringUtils::lowerCaseInPlace(group);
|
||||
|
||||
ESM::AnimationState::ScriptedAnimation scriptedAnim;
|
||||
scriptedAnim.mGroup = group;
|
||||
scriptedAnim.mGroup = std::move(group);
|
||||
scriptedAnim.mTime = anis.mTime;
|
||||
scriptedAnim.mAbsolute = true;
|
||||
// Neither loop count nor queueing seems to be supported by the ess format.
|
||||
|
@ -306,12 +306,12 @@ namespace ESSImport
|
||||
mMarkers.push_back(marker);
|
||||
}
|
||||
|
||||
newcell.mRefs = cellrefs;
|
||||
newcell.mRefs = std::move(cellrefs);
|
||||
|
||||
if (cell.isExterior())
|
||||
mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell;
|
||||
mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = std::move(newcell);
|
||||
else
|
||||
mIntCells[cell.mName] = newcell;
|
||||
mIntCells[cell.mName] = std::move(newcell);
|
||||
}
|
||||
|
||||
void ConvertCell::writeCell(const Cell& cell, ESM::ESMWriter& esm)
|
||||
|
@ -34,7 +34,6 @@ Launcher::GraphicsPage::GraphicsPage(QWidget* parent)
|
||||
connect(standardRadioButton, &QRadioButton::toggled, this, &GraphicsPage::slotStandardToggled);
|
||||
connect(screenComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &GraphicsPage::screenChanged);
|
||||
connect(framerateLimitCheckBox, &QCheckBox::toggled, this, &GraphicsPage::slotFramerateLimitToggled);
|
||||
connect(shadowDistanceCheckBox, &QCheckBox::toggled, this, &GraphicsPage::slotShadowDistLimitToggled);
|
||||
}
|
||||
|
||||
bool Launcher::GraphicsPage::setupSDL()
|
||||
@ -126,58 +125,6 @@ bool Launcher::GraphicsPage::loadSettings()
|
||||
framerateLimitSpinBox->setValue(fpsLimit);
|
||||
}
|
||||
|
||||
// Lighting
|
||||
int lightingMethod = 1;
|
||||
switch (Settings::shaders().mLightingMethod)
|
||||
{
|
||||
case SceneUtil::LightingMethod::FFP:
|
||||
lightingMethod = 0;
|
||||
break;
|
||||
case SceneUtil::LightingMethod::PerObjectUniform:
|
||||
lightingMethod = 1;
|
||||
break;
|
||||
case SceneUtil::LightingMethod::SingleUBO:
|
||||
lightingMethod = 2;
|
||||
break;
|
||||
}
|
||||
lightingMethodComboBox->setCurrentIndex(lightingMethod);
|
||||
|
||||
// Shadows
|
||||
if (Settings::shadows().mActorShadows)
|
||||
actorShadowsCheckBox->setCheckState(Qt::Checked);
|
||||
if (Settings::shadows().mPlayerShadows)
|
||||
playerShadowsCheckBox->setCheckState(Qt::Checked);
|
||||
if (Settings::shadows().mTerrainShadows)
|
||||
terrainShadowsCheckBox->setCheckState(Qt::Checked);
|
||||
if (Settings::shadows().mObjectShadows)
|
||||
objectShadowsCheckBox->setCheckState(Qt::Checked);
|
||||
if (Settings::shadows().mEnableIndoorShadows)
|
||||
indoorShadowsCheckBox->setCheckState(Qt::Checked);
|
||||
|
||||
const auto& boundMethod = Settings::shadows().mComputeSceneBounds.get();
|
||||
if (boundMethod == "bounds")
|
||||
shadowComputeSceneBoundsComboBox->setCurrentIndex(0);
|
||||
else if (boundMethod == "primitives")
|
||||
shadowComputeSceneBoundsComboBox->setCurrentIndex(1);
|
||||
else
|
||||
shadowComputeSceneBoundsComboBox->setCurrentIndex(2);
|
||||
|
||||
const int shadowDistLimit = Settings::shadows().mMaximumShadowMapDistance;
|
||||
if (shadowDistLimit > 0)
|
||||
{
|
||||
shadowDistanceCheckBox->setCheckState(Qt::Checked);
|
||||
shadowDistanceSpinBox->setValue(shadowDistLimit);
|
||||
}
|
||||
|
||||
const float shadowFadeStart = Settings::shadows().mShadowFadeStart;
|
||||
if (shadowFadeStart != 0)
|
||||
fadeStartSpinBox->setValue(shadowFadeStart);
|
||||
|
||||
const int shadowRes = Settings::shadows().mShadowMapResolution;
|
||||
int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes));
|
||||
if (shadowResIndex != -1)
|
||||
shadowResolutionComboBox->setCurrentIndex(shadowResIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -220,53 +167,6 @@ void Launcher::GraphicsPage::saveSettings()
|
||||
{
|
||||
Settings::video().mFramerateLimit.set(0);
|
||||
}
|
||||
|
||||
// Lighting
|
||||
static constexpr std::array<SceneUtil::LightingMethod, 3> lightingMethodMap = {
|
||||
SceneUtil::LightingMethod::FFP,
|
||||
SceneUtil::LightingMethod::PerObjectUniform,
|
||||
SceneUtil::LightingMethod::SingleUBO,
|
||||
};
|
||||
Settings::shaders().mLightingMethod.set(lightingMethodMap[lightingMethodComboBox->currentIndex()]);
|
||||
|
||||
// Shadows
|
||||
const int cShadowDist = shadowDistanceCheckBox->checkState() != Qt::Unchecked ? shadowDistanceSpinBox->value() : 0;
|
||||
Settings::shadows().mMaximumShadowMapDistance.set(cShadowDist);
|
||||
const float cFadeStart = fadeStartSpinBox->value();
|
||||
if (cShadowDist > 0)
|
||||
Settings::shadows().mShadowFadeStart.set(cFadeStart);
|
||||
|
||||
const bool cActorShadows = actorShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||
const bool cObjectShadows = objectShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||
const bool cTerrainShadows = terrainShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||
const bool cPlayerShadows = playerShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||
if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows)
|
||||
{
|
||||
Settings::shadows().mEnableShadows.set(true);
|
||||
Settings::shadows().mActorShadows.set(cActorShadows);
|
||||
Settings::shadows().mPlayerShadows.set(cPlayerShadows);
|
||||
Settings::shadows().mObjectShadows.set(cObjectShadows);
|
||||
Settings::shadows().mTerrainShadows.set(cTerrainShadows);
|
||||
}
|
||||
else
|
||||
{
|
||||
Settings::shadows().mEnableShadows.set(false);
|
||||
Settings::shadows().mActorShadows.set(false);
|
||||
Settings::shadows().mPlayerShadows.set(false);
|
||||
Settings::shadows().mObjectShadows.set(false);
|
||||
Settings::shadows().mTerrainShadows.set(false);
|
||||
}
|
||||
|
||||
Settings::shadows().mEnableIndoorShadows.set(indoorShadowsCheckBox->checkState() != Qt::Unchecked);
|
||||
Settings::shadows().mShadowMapResolution.set(shadowResolutionComboBox->currentText().toInt());
|
||||
|
||||
auto index = shadowComputeSceneBoundsComboBox->currentIndex();
|
||||
if (index == 0)
|
||||
Settings::shadows().mComputeSceneBounds.set("bounds");
|
||||
else if (index == 1)
|
||||
Settings::shadows().mComputeSceneBounds.set("primitives");
|
||||
else
|
||||
Settings::shadows().mComputeSceneBounds.set("none");
|
||||
}
|
||||
|
||||
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
|
||||
@ -377,9 +277,3 @@ void Launcher::GraphicsPage::slotFramerateLimitToggled(bool checked)
|
||||
{
|
||||
framerateLimitSpinBox->setEnabled(checked);
|
||||
}
|
||||
|
||||
void Launcher::GraphicsPage::slotShadowDistLimitToggled(bool checked)
|
||||
{
|
||||
shadowDistanceSpinBox->setEnabled(checked);
|
||||
fadeStartSpinBox->setEnabled(checked);
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ namespace Launcher
|
||||
void slotFullScreenChanged(int state);
|
||||
void slotStandardToggled(bool checked);
|
||||
void slotFramerateLimitToggled(bool checked);
|
||||
void slotShadowDistLimitToggled(bool checked);
|
||||
|
||||
private:
|
||||
QVector<QStringList> mResolutionsPerScreen;
|
||||
|
@ -212,6 +212,65 @@ bool Launcher::SettingsPage::loadSettings()
|
||||
loadSettingBool(Settings::fog().mExponentialFog, *exponentialFogCheckBox);
|
||||
loadSettingBool(Settings::fog().mSkyBlending, *skyBlendingCheckBox);
|
||||
skyBlendingStartComboBox->setValue(Settings::fog().mSkyBlendingStart);
|
||||
|
||||
loadSettingBool(Settings::shadows().mActorShadows, *actorShadowsCheckBox);
|
||||
loadSettingBool(Settings::shadows().mPlayerShadows, *playerShadowsCheckBox);
|
||||
loadSettingBool(Settings::shadows().mTerrainShadows, *terrainShadowsCheckBox);
|
||||
loadSettingBool(Settings::shadows().mObjectShadows, *objectShadowsCheckBox);
|
||||
loadSettingBool(Settings::shadows().mEnableIndoorShadows, *indoorShadowsCheckBox);
|
||||
|
||||
const auto& boundMethod = Settings::shadows().mComputeSceneBounds.get();
|
||||
if (boundMethod == "bounds")
|
||||
shadowComputeSceneBoundsComboBox->setCurrentIndex(0);
|
||||
else if (boundMethod == "primitives")
|
||||
shadowComputeSceneBoundsComboBox->setCurrentIndex(1);
|
||||
else
|
||||
shadowComputeSceneBoundsComboBox->setCurrentIndex(2);
|
||||
|
||||
const int shadowDistLimit = Settings::shadows().mMaximumShadowMapDistance;
|
||||
if (shadowDistLimit > 0)
|
||||
{
|
||||
shadowDistanceCheckBox->setCheckState(Qt::Checked);
|
||||
shadowDistanceSpinBox->setValue(shadowDistLimit);
|
||||
shadowDistanceSpinBox->setEnabled(true);
|
||||
fadeStartSpinBox->setEnabled(true);
|
||||
}
|
||||
|
||||
const float shadowFadeStart = Settings::shadows().mShadowFadeStart;
|
||||
if (shadowFadeStart != 0)
|
||||
fadeStartSpinBox->setValue(shadowFadeStart);
|
||||
|
||||
const int shadowRes = Settings::shadows().mShadowMapResolution;
|
||||
int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes));
|
||||
if (shadowResIndex != -1)
|
||||
shadowResolutionComboBox->setCurrentIndex(shadowResIndex);
|
||||
|
||||
connect(shadowDistanceCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotShadowDistLimitToggled);
|
||||
|
||||
lightsMaxLightsSpinBox->setValue(Settings::shaders().mMaxLights);
|
||||
lightsMaximumDistanceSpinBox->setValue(Settings::shaders().mMaximumLightDistance);
|
||||
lightFadeMultiplierSpinBox->setValue(Settings::shaders().mLightFadeStart);
|
||||
lightsBoundingSphereMultiplierSpinBox->setValue(Settings::shaders().mLightBoundsMultiplier);
|
||||
lightsMinimumInteriorBrightnessSpinBox->setValue(Settings::shaders().mMinimumInteriorBrightness);
|
||||
|
||||
connect(lightingMethodComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&SettingsPage::slotLightTypeCurrentIndexChanged);
|
||||
|
||||
int lightingMethod = 1;
|
||||
switch (Settings::shaders().mLightingMethod)
|
||||
{
|
||||
case SceneUtil::LightingMethod::FFP:
|
||||
lightingMethod = 0;
|
||||
break;
|
||||
case SceneUtil::LightingMethod::PerObjectUniform:
|
||||
lightingMethod = 1;
|
||||
break;
|
||||
case SceneUtil::LightingMethod::SingleUBO:
|
||||
lightingMethod = 2;
|
||||
break;
|
||||
}
|
||||
lightingMethodComboBox->setCurrentIndex(lightingMethod);
|
||||
slotLightTypeCurrentIndexChanged(lightingMethod);
|
||||
}
|
||||
|
||||
// Audio
|
||||
@ -359,6 +418,58 @@ void Launcher::SettingsPage::saveSettings()
|
||||
saveSettingBool(*exponentialFogCheckBox, Settings::fog().mExponentialFog);
|
||||
saveSettingBool(*skyBlendingCheckBox, Settings::fog().mSkyBlending);
|
||||
Settings::fog().mSkyBlendingStart.set(skyBlendingStartComboBox->value());
|
||||
|
||||
static constexpr std::array<SceneUtil::LightingMethod, 3> lightingMethodMap = {
|
||||
SceneUtil::LightingMethod::FFP,
|
||||
SceneUtil::LightingMethod::PerObjectUniform,
|
||||
SceneUtil::LightingMethod::SingleUBO,
|
||||
};
|
||||
Settings::shaders().mLightingMethod.set(lightingMethodMap[lightingMethodComboBox->currentIndex()]);
|
||||
|
||||
const int cShadowDist
|
||||
= shadowDistanceCheckBox->checkState() != Qt::Unchecked ? shadowDistanceSpinBox->value() : 0;
|
||||
Settings::shadows().mMaximumShadowMapDistance.set(cShadowDist);
|
||||
const float cFadeStart = fadeStartSpinBox->value();
|
||||
if (cShadowDist > 0)
|
||||
Settings::shadows().mShadowFadeStart.set(cFadeStart);
|
||||
|
||||
const bool cActorShadows = actorShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||
const bool cObjectShadows = objectShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||
const bool cTerrainShadows = terrainShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||
const bool cPlayerShadows = playerShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||
if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows)
|
||||
{
|
||||
Settings::shadows().mEnableShadows.set(true);
|
||||
Settings::shadows().mActorShadows.set(cActorShadows);
|
||||
Settings::shadows().mPlayerShadows.set(cPlayerShadows);
|
||||
Settings::shadows().mObjectShadows.set(cObjectShadows);
|
||||
Settings::shadows().mTerrainShadows.set(cTerrainShadows);
|
||||
}
|
||||
else
|
||||
{
|
||||
Settings::shadows().mEnableShadows.set(false);
|
||||
Settings::shadows().mActorShadows.set(false);
|
||||
Settings::shadows().mPlayerShadows.set(false);
|
||||
Settings::shadows().mObjectShadows.set(false);
|
||||
Settings::shadows().mTerrainShadows.set(false);
|
||||
}
|
||||
|
||||
Settings::shadows().mEnableIndoorShadows.set(indoorShadowsCheckBox->checkState() != Qt::Unchecked);
|
||||
Settings::shadows().mShadowMapResolution.set(shadowResolutionComboBox->currentText().toInt());
|
||||
|
||||
auto index = shadowComputeSceneBoundsComboBox->currentIndex();
|
||||
if (index == 0)
|
||||
Settings::shadows().mComputeSceneBounds.set("bounds");
|
||||
else if (index == 1)
|
||||
Settings::shadows().mComputeSceneBounds.set("primitives");
|
||||
else
|
||||
Settings::shadows().mComputeSceneBounds.set("none");
|
||||
|
||||
Settings::shaders().mMaxLights.set(lightsMaxLightsSpinBox->value());
|
||||
Settings::shaders().mMaximumLightDistance.set(lightsMaximumDistanceSpinBox->value());
|
||||
Settings::shaders().mLightFadeStart.set(lightFadeMultiplierSpinBox->value());
|
||||
Settings::shaders().mLightBoundsMultiplier.set(lightsBoundingSphereMultiplierSpinBox->value());
|
||||
Settings::shaders().mMinimumInteriorBrightness.set(lightsMinimumInteriorBrightnessSpinBox->value());
|
||||
}
|
||||
|
||||
// Audio
|
||||
@ -461,3 +572,17 @@ void Launcher::SettingsPage::slotSkyBlendingToggled(bool checked)
|
||||
skyBlendingStartComboBox->setEnabled(checked);
|
||||
skyBlendingStartLabel->setEnabled(checked);
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::slotShadowDistLimitToggled(bool checked)
|
||||
{
|
||||
shadowDistanceSpinBox->setEnabled(checked);
|
||||
fadeStartSpinBox->setEnabled(checked);
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::slotLightTypeCurrentIndexChanged(int index)
|
||||
{
|
||||
lightsMaximumDistanceSpinBox->setEnabled(index != 0);
|
||||
lightsMaxLightsSpinBox->setEnabled(index != 0);
|
||||
lightsBoundingSphereMultiplierSpinBox->setEnabled(index != 0);
|
||||
lightsMinimumInteriorBrightnessSpinBox->setEnabled(index != 0);
|
||||
}
|
||||
|
@ -32,6 +32,8 @@ namespace Launcher
|
||||
void slotAnimSourcesToggled(bool checked);
|
||||
void slotPostProcessToggled(bool checked);
|
||||
void slotSkyBlendingToggled(bool checked);
|
||||
void slotShadowDistLimitToggled(bool checked);
|
||||
void slotLightTypeCurrentIndexChanged(int index);
|
||||
|
||||
private:
|
||||
Config::GameSettings& mGameSettings;
|
||||
|
@ -11,459 +11,229 @@
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QTabWidget" name="DisplayTabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="DisplayWrapper">
|
||||
<attribute name="title">
|
||||
<string>Display</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="screenLabel">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="screenLabel">
|
||||
<property name="text">
|
||||
<string>Screen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="windowModeLabel">
|
||||
<property name="text">
|
||||
<string>Window mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" 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> × </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="4" column="1">
|
||||
<widget class="QComboBox" name="antiAliasingComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Screen:</string>
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" 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="5" column="1">
|
||||
<widget class="QComboBox" name="screenComboBox"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="windowModeLabel">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Window Mode:</string>
|
||||
<string>2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="resolutionLabel">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Resolution:</string>
|
||||
<string>4</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="vSyncLabel">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Vertical Sync:</string>
|
||||
<string>8</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="antiAliasingLabel">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Anti-aliasing:</string>
|
||||
<string>16</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" 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> × </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="3" column="1">
|
||||
<widget class="QComboBox" name="windowModeComboBox">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Fullscreen</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Windowed Fullscreen</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Windowed</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="windowBorderCheckBox">
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="framerateLimitCheckBox">
|
||||
<property name="text">
|
||||
<string>Framerate limit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="windowBorderCheckBox">
|
||||
<property name="text">
|
||||
<string>Window border</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="screenComboBox"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="vSyncComboBox">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Window Border</string>
|
||||
<string>Disabled</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="vSyncComboBox">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disabled</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Enabled</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Adaptive</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="framerateLimitCheckBox">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Framerate Limit:</string>
|
||||
<string>Enabled</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" 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.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>15.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>300.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="LightingWrapper">
|
||||
<attribute name="title">
|
||||
<string>Lighting</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="lightingLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="lightingMethodLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lightingMethodLabel">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Lighting Method:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="lightingMethodComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>legacy</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>shaders compatibility</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>shaders</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="ShadowWrapper">
|
||||
<attribute name="title">
|
||||
<string>Shadows</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="shadowsLayout" columnstretch="0,0">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="playerShadowsCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable shadows exclusively for the player character. May have a very minor performance impact.</p></body></html></string>
|
||||
<string>Adaptive</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="windowModeComboBox">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Enable Player Shadows</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="actorShadowsCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable shadows for NPCs and creatures besides the player character. May have a minor performance impact.</p></body></html></string>
|
||||
<string>Fullscreen</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Enable Actor Shadows</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="objectShadowsCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable shadows for primarily inanimate objects. May have a significant performance impact.</p></body></html></string>
|
||||
<string>Windowed Fullscreen</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Enable Object Shadows</string>
|
||||
<string>Windowed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="terrainShadowsCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable shadows for the terrain including distant terrain. May have a significant performance and shadow quality impact.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Terrain Shadows</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="indoorShadowsCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Due to limitations with Morrowind's data, only actors can cast shadows indoors, which some might feel is distracting.</p><p>Has no effect if actor/player shadows are not enabled.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Indoor Shadows</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="shadowComputeSceneBoundsLabel">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Type of "compute scene bounds" computation method to be used. Bounds (default) for good balance between performance and shadow quality, primitives for better looking shadows or none for no computation.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Shadow Near Far Computation Method:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="shadowComputeSceneBoundsComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>bounds</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>primitives</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>none</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="shadowResolutionLabel">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The resolution of each individual shadow map. Increasing it significantly improves shadow quality but may have a minor performance impact.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Shadow Map Resolution:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" 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="7" column="0">
|
||||
<widget class="QCheckBox" name="shadowDistanceCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The distance from the camera at which shadows completely disappear.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Shadow Distance Limit:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QSpinBox" name="shadowDistanceSpinBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>64 game units is 1 real life yard or about 0.9 m</p></body></html></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="8" column="0">
|
||||
<widget class="QLabel" name="fadeStartLabel">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The fraction of the limit above at which shadows begin to gradually fade away.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Fade Start Multiplier:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" 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.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.900000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" 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="1" 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.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>15.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>300.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="antiAliasingLabel">
|
||||
<property name="text">
|
||||
<string>Anti-aliasing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="vSyncLabel">
|
||||
<property name="text">
|
||||
<string>Vertical synchronization</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>514</width>
|
||||
<width>515</width>
|
||||
<height>397</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -129,16 +129,22 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
@ -7,20 +7,20 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>720</width>
|
||||
<height>565</height>
|
||||
<height>635</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>720</width>
|
||||
<height>565</height>
|
||||
<height>635</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>OpenMW Launcher</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../launcher/launcher.qrc">
|
||||
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||
<normaloff>:/images/openmw.png</normaloff>:/images/openmw.png</iconset>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
@ -120,7 +120,7 @@ QToolButton {
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../launcher/launcher.qrc">
|
||||
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||
<normaloff>:/images/openmw-plugin.png</normaloff>:/images/openmw-plugin.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
@ -141,11 +141,11 @@ QToolButton {
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../launcher/launcher.qrc">
|
||||
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||
<normaloff>:/images/preferences-video.png</normaloff>:/images/preferences-video.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Graphics</string>
|
||||
<string>Display</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Allows to change graphics settings</string>
|
||||
@ -156,7 +156,7 @@ QToolButton {
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../launcher/launcher.qrc">
|
||||
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||
<normaloff>:/images/preferences.png</normaloff>:/images/preferences.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
@ -171,7 +171,7 @@ QToolButton {
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../launcher/launcher.qrc">
|
||||
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||
<normaloff>:/images/preferences-advanced.png</normaloff>:/images/preferences-advanced.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
@ -183,7 +183,7 @@ QToolButton {
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../launcher/launcher.qrc"/>
|
||||
<include location="../../../files/launcher/launcher.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -221,7 +221,7 @@ namespace NavMeshTool
|
||||
constexpr double expiryDelay = 0;
|
||||
|
||||
Resource::ImageManager imageManager(&vfs, expiryDelay);
|
||||
Resource::NifFileManager nifFileManager(&vfs);
|
||||
Resource::NifFileManager nifFileManager(&vfs, &encoder.getStatelessEncoder());
|
||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay);
|
||||
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
|
||||
DetourNavigator::RecastGlobalAllocator::init();
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <components/vfs/bsaarchive.hpp>
|
||||
#include <components/vfs/filesystemarchive.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
@ -84,7 +85,7 @@ void readNIF(
|
||||
try
|
||||
{
|
||||
Nif::NIFFile file(fullPath);
|
||||
Nif::Reader reader(file);
|
||||
Nif::Reader reader(file, nullptr);
|
||||
if (vfs != nullptr)
|
||||
reader.parse(vfs->get(pathStr));
|
||||
else
|
||||
|
@ -624,7 +624,7 @@ bool CSMFilter::Parser::parse(const std::string& filter, bool allowPredefined)
|
||||
}
|
||||
|
||||
if (node)
|
||||
mFilter = node;
|
||||
mFilter = std::move(node);
|
||||
else
|
||||
{
|
||||
// Empty filter string equals to filter "true".
|
||||
|
@ -171,7 +171,7 @@ void CSMTools::ReportModel::flagAsReplaced(int index)
|
||||
|
||||
hint[0] = 'r';
|
||||
|
||||
line.mHint = hint;
|
||||
line.mHint = std::move(hint);
|
||||
|
||||
emit dataChanged(this->index(index, 0), this->index(index, columnCount()));
|
||||
}
|
||||
|
@ -468,13 +468,13 @@ namespace CSMWorld
|
||||
if (type == UniversalId::Type_Creature)
|
||||
{
|
||||
// Valid creature record
|
||||
setupCreature(id, data);
|
||||
setupCreature(id, std::move(data));
|
||||
emit actorChanged(id);
|
||||
}
|
||||
else if (type == UniversalId::Type_Npc)
|
||||
{
|
||||
// Valid npc record
|
||||
setupNpc(id, data);
|
||||
setupNpc(id, std::move(data));
|
||||
emit actorChanged(id);
|
||||
}
|
||||
else
|
||||
@ -665,7 +665,7 @@ namespace CSMWorld
|
||||
RaceDataPtr data = mCachedRaces.get(race);
|
||||
if (data)
|
||||
{
|
||||
setupRace(race, data);
|
||||
setupRace(race, std::move(data));
|
||||
// Race was changed. Need to mark actor dependencies as dirty.
|
||||
// Cannot use markDirtyDependency because that would invalidate
|
||||
// the current iterator.
|
||||
@ -683,7 +683,7 @@ namespace CSMWorld
|
||||
ActorDataPtr data = mCachedActors.get(actor);
|
||||
if (data)
|
||||
{
|
||||
setupActor(actor, data);
|
||||
setupActor(actor, std::move(data));
|
||||
}
|
||||
}
|
||||
mDirtyActors.clear();
|
||||
|
@ -504,7 +504,7 @@ namespace CSMWorld
|
||||
|
||||
auto record2 = std::make_unique<Record<ESXRecordT>>();
|
||||
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
|
||||
record2->mModified = record;
|
||||
record2->mModified = std::move(record);
|
||||
|
||||
insertRecord(std::move(record2), getAppendIndex(id, type), type);
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ CSMWorld::TouchCommand::TouchCommand(IdTable& table, const std::string& id, QUnd
|
||||
|
||||
void CSMWorld::TouchCommand::redo()
|
||||
{
|
||||
mOld.reset(mTable.getRecord(mId).clone().get());
|
||||
mOld = mTable.getRecord(mId).clone();
|
||||
mChanged = mTable.touchRecord(mId);
|
||||
}
|
||||
|
||||
@ -181,9 +181,8 @@ const std::string& CSMWorld::TouchLandCommand::getDestinationId() const
|
||||
|
||||
void CSMWorld::TouchLandCommand::onRedo()
|
||||
{
|
||||
mOld = mLands.getRecord(mId).clone();
|
||||
mChanged = mLands.touchRecord(mId);
|
||||
if (mChanged)
|
||||
mOld.reset(mLands.getRecord(mId).clone().get());
|
||||
}
|
||||
|
||||
void CSMWorld::TouchLandCommand::onUndo()
|
||||
|
@ -149,7 +149,8 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
|
||||
mResourcesManager.setVFS(mVFS.get());
|
||||
|
||||
constexpr double expiryDelay = 0;
|
||||
mResourceSystem = std::make_unique<Resource::ResourceSystem>(mVFS.get(), expiryDelay);
|
||||
mResourceSystem
|
||||
= std::make_unique<Resource::ResourceSystem>(mVFS.get(), expiryDelay, &mEncoder.getStatelessEncoder());
|
||||
|
||||
Shader::ShaderManager::DefineMap defines
|
||||
= mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
|
||||
@ -1107,7 +1108,7 @@ void CSMWorld::Data::loadFallbackEntries()
|
||||
newMarker.mModel = model;
|
||||
newMarker.mRecordFlags = 0;
|
||||
auto record = std::make_unique<CSMWorld::Record<ESM::Static>>();
|
||||
record->mBase = newMarker;
|
||||
record->mBase = std::move(newMarker);
|
||||
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||
mReferenceables.appendRecord(std::move(record), CSMWorld::UniversalId::Type_Static);
|
||||
}
|
||||
@ -1123,7 +1124,7 @@ void CSMWorld::Data::loadFallbackEntries()
|
||||
newMarker.mModel = model;
|
||||
newMarker.mRecordFlags = 0;
|
||||
auto record = std::make_unique<CSMWorld::Record<ESM::Door>>();
|
||||
record->mBase = newMarker;
|
||||
record->mBase = std::move(newMarker);
|
||||
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||
mReferenceables.appendRecord(std::move(record), CSMWorld::UniversalId::Type_Door);
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data& data)
|
||||
completer->setPopup(popup); // The completer takes ownership of the popup
|
||||
completer->setMaxVisibleItems(10);
|
||||
|
||||
mCompleters[current->first] = completer;
|
||||
mCompleters[current->first] = std::move(completer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <components/misc/strings/lower.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
CSMWorld::Resources::Resources(
|
||||
const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type, const char* const* extensions)
|
||||
|
@ -91,7 +91,7 @@ void CSVDoc::AdjusterWidget::setName(const QString& name, bool addon)
|
||||
{
|
||||
// path already points to the local data directory
|
||||
message = "Will be saved as: " + Files::pathToQString(path);
|
||||
mResultPath = path;
|
||||
mResultPath = std::move(path);
|
||||
}
|
||||
// in all other cases, ensure the path points to data-local and do an existing file check
|
||||
else
|
||||
|
@ -410,7 +410,7 @@ bool CSVDoc::ViewManager::removeDocument(CSVDoc::View* view)
|
||||
remainingViews.push_back(*iter);
|
||||
}
|
||||
mDocumentManager.removeDocument(document);
|
||||
mViews = remainingViews;
|
||||
mViews = std::move(remainingViews);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -514,7 +514,7 @@ void CSVRender::PagedWorldspaceWidget::moveCellSelection(int x, int y)
|
||||
addCellToScene(*iter);
|
||||
}
|
||||
|
||||
mSelection = newSelection;
|
||||
mSelection = std::move(newSelection);
|
||||
}
|
||||
|
||||
void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera(int offsetX, int offsetY)
|
||||
|
@ -541,7 +541,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
|
||||
= landTable.data(landTable.getModelIndex(cellId, textureColumn))
|
||||
.value<CSMWorld::LandTexturesColumn::DataType>();
|
||||
newTerrainOtherCell[yInOtherCell * landTextureSize + xInOtherCell] = brushInt;
|
||||
pushEditToCommand(newTerrainOtherCell, document, landTable, cellId);
|
||||
pushEditToCommand(newTerrainOtherCell, document, landTable, std::move(cellId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -702,7 +702,7 @@ void CSVRender::TerrainTextureMode::createTexture(const std::string& textureFile
|
||||
QModelIndex index(ltexTable.getModelIndex(newId, ltexTable.findColumnIndex(CSMWorld::Columns::ColumnId_Texture)));
|
||||
undoStack.push(new CSMWorld::ModifyCommand(ltexTable, index, textureFileNameVariant));
|
||||
undoStack.endMacro();
|
||||
mBrushTexture = newId;
|
||||
mBrushTexture = std::move(newId);
|
||||
}
|
||||
|
||||
bool CSVRender::TerrainTextureMode::allowLandTextureEditing(const std::string& cellId)
|
||||
|
@ -184,11 +184,11 @@ void CSVRender::WorldspaceWidget::selectDefaultNavigationMode()
|
||||
|
||||
void CSVRender::WorldspaceWidget::centerOrbitCameraOnSelection()
|
||||
{
|
||||
std::vector<osg::ref_ptr<TagBase>> selection = getSelection(~0u);
|
||||
std::vector<osg::ref_ptr<TagBase>> selection = getSelection(Mask_Reference);
|
||||
|
||||
for (std::vector<osg::ref_ptr<TagBase>>::iterator it = selection.begin(); it != selection.end(); ++it)
|
||||
{
|
||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(it->get()))
|
||||
if (CSVRender::ObjectTag* objectTag = static_cast<CSVRender::ObjectTag*>(it->get()))
|
||||
{
|
||||
mOrbitCamControl->setCenter(objectTag->mObject->getPosition().asVec3());
|
||||
}
|
||||
@ -440,7 +440,7 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||
osg::Node* node = *nodeIter;
|
||||
if (osg::ref_ptr<CSVRender::TagBase> tag = dynamic_cast<CSVRender::TagBase*>(node->getUserData()))
|
||||
{
|
||||
WorldspaceHitResult hit = { true, tag, 0, 0, 0, intersection.getWorldIntersectPoint() };
|
||||
WorldspaceHitResult hit = { true, std::move(tag), 0, 0, 0, intersection.getWorldIntersectPoint() };
|
||||
if (intersection.indexList.size() >= 3)
|
||||
{
|
||||
hit.index0 = intersection.indexList[0];
|
||||
@ -757,13 +757,14 @@ void CSVRender::WorldspaceWidget::toggleHiddenInstances()
|
||||
if (selection.empty())
|
||||
return;
|
||||
|
||||
const CSVRender::ObjectTag* firstSelection = dynamic_cast<CSVRender::ObjectTag*>(selection.begin()->get());
|
||||
const CSVRender::ObjectTag* firstSelection = static_cast<CSVRender::ObjectTag*>(selection.begin()->get());
|
||||
assert(firstSelection != nullptr);
|
||||
|
||||
const CSVRender::Mask firstMask
|
||||
= firstSelection->mObject->getRootNode()->getNodeMask() == Mask_Hidden ? Mask_Reference : Mask_Hidden;
|
||||
|
||||
for (const auto& object : selection)
|
||||
if (const auto objectTag = dynamic_cast<CSVRender::ObjectTag*>(object.get()))
|
||||
if (const auto objectTag = static_cast<CSVRender::ObjectTag*>(object.get()))
|
||||
objectTag->mObject->getRootNode()->setNodeMask(firstMask);
|
||||
}
|
||||
|
||||
|
@ -213,7 +213,7 @@ void CSVWidget::TextureBrushWindow::setBrushTexture(std::string brushTexture)
|
||||
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel));
|
||||
}
|
||||
|
||||
mBrushTexture = newBrushTextureId;
|
||||
mBrushTexture = std::move(newBrushTextureId);
|
||||
|
||||
emit passTextureId(mBrushTexture);
|
||||
emit passBrushShape(mBrushShape); // updates the icon tooltip
|
||||
|
@ -146,7 +146,7 @@ void CSVWorld::ExtendedCommandConfigurator::setupCheckBoxes(const std::vector<CS
|
||||
CSMWorld::UniversalId type = types[counter];
|
||||
current->first->setText(QString::fromUtf8(type.getTypeName().c_str()));
|
||||
current->first->setChecked(true);
|
||||
current->second = type;
|
||||
current->second = std::move(type);
|
||||
++counter;
|
||||
}
|
||||
else
|
||||
|
@ -169,7 +169,7 @@ void CSVWorld::TableSubView::createFilterRequest(std::vector<CSMWorld::Universal
|
||||
{
|
||||
CSVFilter::FilterData filterData;
|
||||
filterData.searchData = it->getId();
|
||||
filterData.columns = col;
|
||||
filterData.columns = std::move(col);
|
||||
sourceFilter.emplace_back(filterData);
|
||||
}
|
||||
|
||||
@ -195,7 +195,7 @@ void CSVWorld::TableSubView::createFilterRequest(std::vector<CSMWorld::Universal
|
||||
|
||||
CSVFilter::FilterData filterData;
|
||||
filterData.searchData = qData;
|
||||
filterData.columns = searchColumns;
|
||||
filterData.columns = std::move(searchColumns);
|
||||
|
||||
sourceFilterByValue.emplace_back(filterData);
|
||||
|
||||
|
@ -321,11 +321,7 @@ bool OMW::Engine::frame(float frametime)
|
||||
// update GUI by world data
|
||||
{
|
||||
ScopedProfile<UserStatsType::WindowManager> profile(frameStart, frameNumber, *timer, *stats);
|
||||
|
||||
if (mStateManager->getState() != MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
mWorld->updateWindowManager();
|
||||
}
|
||||
mWorld->updateWindowManager();
|
||||
}
|
||||
|
||||
mLuaWorker->allowUpdate(); // if there is a separate Lua thread, it starts the update now
|
||||
@ -706,7 +702,8 @@ void OMW::Engine::prepareEngine()
|
||||
|
||||
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
||||
|
||||
mResourceSystem = std::make_unique<Resource::ResourceSystem>(mVFS.get(), Settings::cells().mCacheExpiryDelay);
|
||||
mResourceSystem = std::make_unique<Resource::ResourceSystem>(
|
||||
mVFS.get(), Settings::cells().mCacheExpiryDelay, &mEncoder.get()->getStatelessEncoder());
|
||||
mResourceSystem->getSceneManager()->getShaderManager().setMaxTextureUnits(mGlMaxTextureImageUnits);
|
||||
mResourceSystem->getSceneManager()->setUnRefImageDataAfterApply(
|
||||
false); // keep to Off for now to allow better state sharing
|
||||
@ -826,7 +823,7 @@ void OMW::Engine::prepareEngine()
|
||||
}
|
||||
listener->loadingOff();
|
||||
|
||||
mWorld->init(mViewer, rootNode, mWorkQueue.get(), *mUnrefQueue);
|
||||
mWorld->init(mViewer, std::move(rootNode), mWorkQueue.get(), *mUnrefQueue);
|
||||
mEnvironment.setWorldScene(mWorld->getWorldScene());
|
||||
mWorld->setupPlayer();
|
||||
mWorld->setRandomSeed(mRandomSeed);
|
||||
|
@ -187,6 +187,8 @@ namespace MWBase
|
||||
|
||||
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
|
||||
|
||||
virtual bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const = 0;
|
||||
|
||||
/// Save the current animation state of managed references to their RefData.
|
||||
virtual void persistAnimationStates() = 0;
|
||||
|
||||
|
@ -37,23 +37,6 @@ namespace MWClass
|
||||
return true;
|
||||
}
|
||||
|
||||
void Actor::block(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
const MWWorld::InventoryStore& inv = getInventoryStore(ptr);
|
||||
MWWorld::ConstContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
||||
if (shield == inv.end())
|
||||
return;
|
||||
|
||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
const ESM::RefId skill = shield->getClass().getEquipmentSkill(*shield);
|
||||
if (skill == ESM::Skill::LightArmor)
|
||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f);
|
||||
else if (skill == ESM::Skill::MediumArmor)
|
||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Medium Armor Hit"), 1.0f, 1.0f);
|
||||
else if (skill == ESM::Skill::HeavyArmor)
|
||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::getRotationVector(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWMechanics::Movement& movement = getMovementSettings(ptr);
|
||||
|
@ -45,8 +45,6 @@ namespace MWClass
|
||||
|
||||
bool useAnim() const override;
|
||||
|
||||
void block(const MWWorld::Ptr& ptr) const override;
|
||||
|
||||
osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const override;
|
||||
///< Return desired rotations, as euler angles. Sets getMovementSettings(ptr).mRotation to zero.
|
||||
|
||||
|
@ -183,16 +183,17 @@ namespace MWClass
|
||||
return getClassModel<ESM::Creature>(ptr);
|
||||
}
|
||||
|
||||
void Creature::getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const
|
||||
void Creature::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const
|
||||
{
|
||||
std::string model = getModel(ptr);
|
||||
if (!model.empty())
|
||||
models.push_back(model);
|
||||
|
||||
// FIXME: use const version of InventoryStore functions once they are available
|
||||
if (hasInventoryStore(ptr))
|
||||
const MWWorld::CustomData* customData = ptr.getRefData().getCustomData();
|
||||
if (customData && hasInventoryStore(ptr))
|
||||
{
|
||||
const MWWorld::InventoryStore& invStore = getInventoryStore(ptr);
|
||||
const auto& invStore
|
||||
= static_cast<const MWWorld::InventoryStore&>(*customData->asCreatureCustomData().mContainerStore);
|
||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||
{
|
||||
MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);
|
||||
@ -339,10 +340,7 @@ namespace MWClass
|
||||
MWMechanics::applyElementalShields(ptr, victim);
|
||||
|
||||
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))
|
||||
{
|
||||
damage = 0;
|
||||
victim.getClass().block(victim);
|
||||
}
|
||||
|
||||
MWMechanics::diseaseContact(victim, ptr);
|
||||
|
||||
@ -513,7 +511,7 @@ namespace MWClass
|
||||
throw std::runtime_error("this creature has no inventory store");
|
||||
}
|
||||
|
||||
bool Creature::hasInventoryStore(const MWWorld::Ptr& ptr) const
|
||||
bool Creature::hasInventoryStore(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return isFlagBitSet(ptr, ESM::Creature::Weapon);
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ namespace MWClass
|
||||
MWWorld::InventoryStore& getInventoryStore(const MWWorld::Ptr& ptr) const override;
|
||||
///< Return inventory store
|
||||
|
||||
bool hasInventoryStore(const MWWorld::Ptr& ptr) const override;
|
||||
bool hasInventoryStore(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
ESM::RefId getScript(const MWWorld::ConstPtr& ptr) const override;
|
||||
///< Return name of the script attached to ptr
|
||||
@ -107,7 +107,7 @@ namespace MWClass
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;
|
||||
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const override;
|
||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||
///< list getModel().
|
||||
|
||||
|
@ -99,25 +99,6 @@ namespace MWClass
|
||||
customData.mSpawn = true;
|
||||
}
|
||||
|
||||
void CreatureLevList::getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const
|
||||
{
|
||||
// disable for now, too many false positives
|
||||
/*
|
||||
const MWWorld::LiveCellRef<ESM::CreatureLevList> *ref = ptr.get<ESM::CreatureLevList>();
|
||||
for (std::vector<ESM::LevelledListBase::LevelItem>::const_iterator it = ref->mBase->mList.begin(); it !=
|
||||
ref->mBase->mList.end(); ++it)
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if (it->mLevel > player.getClass().getCreatureStats(player).getLevel())
|
||||
continue;
|
||||
|
||||
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
||||
MWWorld::ManualRef ref(store, it->mId);
|
||||
ref.getPtr().getClass().getModelsToPreload(ref.getPtr(), models);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void CreatureLevList::insertObjectRendering(
|
||||
const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
|
||||
{
|
||||
|
@ -20,10 +20,6 @@ namespace MWClass
|
||||
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;
|
||||
///< @return true if this object has a tooltip when focused (default implementation: true)
|
||||
|
||||
void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;
|
||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||
///< list getModel().
|
||||
|
||||
void insertObjectRendering(const MWWorld::Ptr& ptr, const std::string& model,
|
||||
MWRender::RenderingInterface& renderingInterface) const override;
|
||||
///< Add reference into a cell for rendering
|
||||
|
@ -436,10 +436,11 @@ namespace MWClass
|
||||
return model;
|
||||
}
|
||||
|
||||
void Npc::getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const
|
||||
void Npc::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::NPC>* npc = ptr.get<ESM::NPC>();
|
||||
const ESM::Race* race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().search(npc->mBase->mRace);
|
||||
const auto& esmStore = MWBase::Environment::get().getESMStore();
|
||||
const ESM::Race* race = esmStore->get<ESM::Race>().search(npc->mBase->mRace);
|
||||
if (race && race->mData.mFlags & ESM::Race::Beast)
|
||||
models.push_back(Settings::models().mBaseanimkna);
|
||||
|
||||
@ -453,56 +454,57 @@ namespace MWClass
|
||||
|
||||
if (!npc->mBase->mHead.empty())
|
||||
{
|
||||
const ESM::BodyPart* head
|
||||
= MWBase::Environment::get().getESMStore()->get<ESM::BodyPart>().search(npc->mBase->mHead);
|
||||
const ESM::BodyPart* head = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHead);
|
||||
if (head)
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(head->mModel));
|
||||
}
|
||||
if (!npc->mBase->mHair.empty())
|
||||
{
|
||||
const ESM::BodyPart* hair
|
||||
= MWBase::Environment::get().getESMStore()->get<ESM::BodyPart>().search(npc->mBase->mHair);
|
||||
const ESM::BodyPart* hair = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHair);
|
||||
if (hair)
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(hair->mModel));
|
||||
}
|
||||
|
||||
bool female = (npc->mBase->mFlags & ESM::NPC::Female);
|
||||
|
||||
// FIXME: use const version of InventoryStore functions once they are available
|
||||
// preload equipped items
|
||||
const MWWorld::InventoryStore& invStore = getInventoryStore(ptr);
|
||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||
const MWWorld::CustomData* customData = ptr.getRefData().getCustomData();
|
||||
if (customData)
|
||||
{
|
||||
MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);
|
||||
if (equipped != invStore.end())
|
||||
const MWWorld::InventoryStore& invStore = customData->asNpcCustomData().mInventoryStore;
|
||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||
{
|
||||
std::vector<ESM::PartReference> parts;
|
||||
if (equipped->getType() == ESM::Clothing::sRecordId)
|
||||
MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);
|
||||
if (equipped != invStore.end())
|
||||
{
|
||||
const ESM::Clothing* clothes = equipped->get<ESM::Clothing>()->mBase;
|
||||
parts = clothes->mParts.mParts;
|
||||
}
|
||||
else if (equipped->getType() == ESM::Armor::sRecordId)
|
||||
{
|
||||
const ESM::Armor* armor = equipped->get<ESM::Armor>()->mBase;
|
||||
parts = armor->mParts.mParts;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string model = equipped->getClass().getModel(*equipped);
|
||||
if (!model.empty())
|
||||
models.push_back(model);
|
||||
}
|
||||
const auto addParts = [&](const std::vector<ESM::PartReference>& parts) {
|
||||
for (const ESM::PartReference& partRef : parts)
|
||||
{
|
||||
const ESM::RefId& partname
|
||||
= (female && !partRef.mFemale.empty()) || (!female && partRef.mMale.empty())
|
||||
? partRef.mFemale
|
||||
: partRef.mMale;
|
||||
|
||||
for (std::vector<ESM::PartReference>::const_iterator it = parts.begin(); it != parts.end(); ++it)
|
||||
{
|
||||
const ESM::RefId& partname
|
||||
= (female && !it->mFemale.empty()) || (!female && it->mMale.empty()) ? it->mFemale : it->mMale;
|
||||
|
||||
const ESM::BodyPart* part
|
||||
= MWBase::Environment::get().getESMStore()->get<ESM::BodyPart>().search(partname);
|
||||
if (part && !part->mModel.empty())
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
||||
const ESM::BodyPart* part = esmStore->get<ESM::BodyPart>().search(partname);
|
||||
if (part && !part->mModel.empty())
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
||||
}
|
||||
};
|
||||
if (equipped->getType() == ESM::Clothing::sRecordId)
|
||||
{
|
||||
const ESM::Clothing* clothes = equipped->get<ESM::Clothing>()->mBase;
|
||||
addParts(clothes->mParts.mParts);
|
||||
}
|
||||
else if (equipped->getType() == ESM::Armor::sRecordId)
|
||||
{
|
||||
const ESM::Armor* armor = equipped->get<ESM::Armor>()->mBase;
|
||||
addParts(armor->mParts.mParts);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string model = equipped->getClass().getModel(*equipped);
|
||||
if (!model.empty())
|
||||
models.push_back(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -512,9 +514,8 @@ namespace MWClass
|
||||
{
|
||||
const std::vector<const ESM::BodyPart*>& parts
|
||||
= MWRender::NpcAnimation::getBodyParts(race->mId, female, false, false);
|
||||
for (std::vector<const ESM::BodyPart*>::const_iterator it = parts.begin(); it != parts.end(); ++it)
|
||||
for (const ESM::BodyPart* part : parts)
|
||||
{
|
||||
const ESM::BodyPart* part = *it;
|
||||
if (part && !part->mModel.empty())
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
||||
}
|
||||
@ -678,10 +679,7 @@ namespace MWClass
|
||||
MWMechanics::applyElementalShields(ptr, victim);
|
||||
|
||||
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))
|
||||
{
|
||||
damage = 0;
|
||||
victim.getClass().block(victim);
|
||||
}
|
||||
|
||||
if (victim == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState())
|
||||
damage = 0;
|
||||
|
@ -74,7 +74,7 @@ namespace MWClass
|
||||
MWWorld::InventoryStore& getInventoryStore(const MWWorld::Ptr& ptr) const override;
|
||||
///< Return inventory store
|
||||
|
||||
bool hasInventoryStore(const MWWorld::Ptr& ptr) const override { return true; }
|
||||
bool hasInventoryStore(const MWWorld::ConstPtr& ptr) const override { return true; }
|
||||
|
||||
bool evaluateHit(const MWWorld::Ptr& ptr, MWWorld::Ptr& victim, osg::Vec3f& hitPosition) const override;
|
||||
|
||||
@ -85,7 +85,7 @@ namespace MWClass
|
||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
||||
const MWMechanics::DamageSourceType sourceType) const override;
|
||||
|
||||
void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;
|
||||
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const override;
|
||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||
///< list getModel().
|
||||
|
||||
|
@ -364,9 +364,8 @@ namespace MWGui
|
||||
if (mCurrentWindowSize == _sender->getSize())
|
||||
return;
|
||||
|
||||
mTopicsList->adjustSize();
|
||||
redrawTopicsList();
|
||||
updateHistory();
|
||||
updateTopicFormat();
|
||||
mCurrentWindowSize = _sender->getSize();
|
||||
}
|
||||
|
||||
@ -534,6 +533,14 @@ namespace MWGui
|
||||
return true;
|
||||
}
|
||||
|
||||
void DialogueWindow::redrawTopicsList()
|
||||
{
|
||||
mTopicsList->adjustSize();
|
||||
|
||||
// The topics list has been regenerated so topic formatting needs to be updated
|
||||
updateTopicFormat();
|
||||
}
|
||||
|
||||
void DialogueWindow::updateTopicsPane()
|
||||
{
|
||||
mTopicsList->clear();
|
||||
@ -591,11 +598,9 @@ namespace MWGui
|
||||
t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated);
|
||||
mTopicLinks[topicId] = std::move(t);
|
||||
}
|
||||
mTopicsList->adjustSize();
|
||||
|
||||
redrawTopicsList();
|
||||
updateHistory();
|
||||
// The topics list has been regenerated so topic formatting needs to be updated
|
||||
updateTopicFormat();
|
||||
}
|
||||
|
||||
void DialogueWindow::updateHistory(bool scrollbar)
|
||||
@ -756,21 +761,12 @@ namespace MWGui
|
||||
+ std::string("/100"));
|
||||
}
|
||||
|
||||
bool dispositionWasVisible = mDispositionBar->getVisible();
|
||||
|
||||
if (dispositionVisible && !dispositionWasVisible)
|
||||
if (mDispositionBar->getVisible() != dispositionVisible)
|
||||
{
|
||||
mDispositionBar->setVisible(true);
|
||||
int offset = mDispositionBar->getHeight() + 5;
|
||||
mDispositionBar->setVisible(dispositionVisible);
|
||||
const int offset = (mDispositionBar->getHeight() + 5) * (dispositionVisible ? 1 : -1);
|
||||
mTopicsList->setCoord(mTopicsList->getCoord() + MyGUI::IntCoord(0, offset, 0, -offset));
|
||||
mTopicsList->adjustSize();
|
||||
}
|
||||
else if (!dispositionVisible && dispositionWasVisible)
|
||||
{
|
||||
mDispositionBar->setVisible(false);
|
||||
int offset = mDispositionBar->getHeight() + 5;
|
||||
mTopicsList->setCoord(mTopicsList->getCoord() - MyGUI::IntCoord(0, offset, 0, -offset));
|
||||
mTopicsList->adjustSize();
|
||||
redrawTopicsList();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,6 +190,7 @@ namespace MWGui
|
||||
void updateDisposition();
|
||||
void restock();
|
||||
void deleteLater();
|
||||
void redrawTopicsList();
|
||||
|
||||
bool mIsCompanion;
|
||||
std::list<std::string> mKeywords;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/settings/values.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/settings/values.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
#include <components/widgets/sharedstatebutton.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
@ -131,7 +132,7 @@ namespace
|
||||
void updateMaxLightsComboBox(MyGUI::ComboBox* box)
|
||||
{
|
||||
constexpr int min = 8;
|
||||
constexpr int max = 32;
|
||||
constexpr int max = 64;
|
||||
constexpr int increment = 8;
|
||||
const int maxLights = Settings::shaders().mMaxLights;
|
||||
// show increments of 8 in dropdown
|
||||
|
@ -23,6 +23,11 @@ namespace
|
||||
float mTimeOffset = 0.f;
|
||||
};
|
||||
|
||||
struct StreamMusicArgs
|
||||
{
|
||||
float mFade = 1.f;
|
||||
};
|
||||
|
||||
PlaySoundArgs getPlaySoundArgs(const sol::optional<sol::table>& options)
|
||||
{
|
||||
PlaySoundArgs args;
|
||||
@ -55,6 +60,17 @@ namespace
|
||||
return MWSound::PlayMode::NoEnvNoScaling;
|
||||
return MWSound::PlayMode::NoEnv;
|
||||
}
|
||||
|
||||
StreamMusicArgs getStreamMusicArgs(const sol::optional<sol::table>& options)
|
||||
{
|
||||
StreamMusicArgs args;
|
||||
|
||||
if (options.has_value())
|
||||
{
|
||||
args.mFade = options->get_or("fadeOut", 1.f);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
@ -95,9 +111,10 @@ namespace MWLua
|
||||
return MWBase::Environment::get().getSoundManager()->getSoundPlaying(MWWorld::Ptr(), fileName);
|
||||
};
|
||||
|
||||
api["streamMusic"] = [](std::string_view fileName) {
|
||||
api["streamMusic"] = [](std::string_view fileName, const sol::optional<sol::table>& options) {
|
||||
auto args = getStreamMusicArgs(options);
|
||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
sndMgr->streamMusic(std::string(fileName), MWSound::MusicType::Scripted);
|
||||
sndMgr->streamMusic(std::string(fileName), MWSound::MusicType::Scripted, args.mFade);
|
||||
};
|
||||
|
||||
api["isMusicPlaying"] = []() { return MWBase::Environment::get().getSoundManager()->isMusicPlaying(); };
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "../stats.hpp"
|
||||
#include "actor.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
@ -42,6 +43,20 @@ namespace MWLua
|
||||
record["soulValue"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mSoul; });
|
||||
record["type"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mType; });
|
||||
record["baseGold"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mGold; });
|
||||
record["combatSkill"]
|
||||
= sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mCombat; });
|
||||
record["magicSkill"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mMagic; });
|
||||
record["stealthSkill"]
|
||||
= sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mStealth; });
|
||||
record["attack"] = sol::readonly_property([context](const ESM::Creature& rec) -> sol::table {
|
||||
sol::state_view& lua = context.mLua->sol();
|
||||
sol::table res(lua, sol::create);
|
||||
int index = 1;
|
||||
for (auto attack : rec.mData.mAttack)
|
||||
res[index++] = attack;
|
||||
return LuaUtil::makeReadOnly(res);
|
||||
});
|
||||
|
||||
addActorServicesBindings<ESM::Creature>(record, context);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <components/settings/values.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/pathutil.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
|
@ -2034,6 +2034,14 @@ namespace MWMechanics
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Actors::checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
const auto iter = mIndex.find(ptr.mRef);
|
||||
if (iter != mIndex.end())
|
||||
return iter->second->getCharacterController().isScriptedAnimPlaying();
|
||||
return false;
|
||||
}
|
||||
|
||||
void Actors::persistAnimationStates() const
|
||||
{
|
||||
for (const Actor& actor : mActors)
|
||||
|
@ -116,6 +116,7 @@ namespace MWMechanics
|
||||
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;
|
||||
bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const;
|
||||
void persistAnimationStates() const;
|
||||
|
||||
void getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out) const;
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
@ -120,12 +121,12 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
const DetourNavigator::AgentBounds agentBounds = world->getPathfindingAgentBounds(actor);
|
||||
|
||||
/// Stops the actor when it gets too close to a unloaded cell
|
||||
//... At current time, this test is unnecessary. AI shuts down when actor is more than "actors processing range"
|
||||
// setting value
|
||||
//... units from player, and exterior cells are 8192 units long and wide.
|
||||
/// Stops the actor when it gets too close to a unloaded cell or when the actor is playing a scripted animation
|
||||
//... At current time, the first test is unnecessary. AI shuts down when actor is more than
|
||||
//... "actors processing range" setting value units from player, and exterior cells are 8192 units long and wide.
|
||||
//... But AI processing distance may increase in the future.
|
||||
if (isNearInactiveCell(position))
|
||||
if (isNearInactiveCell(position)
|
||||
|| MWBase::Environment::get().getMechanicsManager()->checkScriptedAnimationPlaying(actor))
|
||||
{
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
|
@ -217,7 +217,6 @@ namespace MWMechanics
|
||||
std::string chooseRandomAttackAnimation() const;
|
||||
static bool isRandomAttackAnimation(std::string_view group);
|
||||
|
||||
bool isScriptedAnimPlaying() const;
|
||||
bool isMovementAnimationControlled() const;
|
||||
|
||||
void updateAnimQueue();
|
||||
@ -278,6 +277,7 @@ namespace MWMechanics
|
||||
bool playGroup(std::string_view groupname, int mode, int count, bool scripted = false);
|
||||
void skipAnim();
|
||||
bool isAnimPlaying(std::string_view groupName) const;
|
||||
bool isScriptedAnimPlaying() const;
|
||||
|
||||
enum KillResult
|
||||
{
|
||||
|
@ -135,6 +135,15 @@ namespace MWMechanics
|
||||
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||
if (Misc::Rng::roll0to99(prng) < x)
|
||||
{
|
||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
const ESM::RefId skill = shield->getClass().getEquipmentSkill(*shield);
|
||||
if (skill == ESM::Skill::LightArmor)
|
||||
sndMgr->playSound3D(blocker, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f);
|
||||
else if (skill == ESM::Skill::MediumArmor)
|
||||
sndMgr->playSound3D(blocker, ESM::RefId::stringRefId("Medium Armor Hit"), 1.0f, 1.0f);
|
||||
else if (skill == ESM::Skill::HeavyArmor)
|
||||
sndMgr->playSound3D(blocker, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f);
|
||||
|
||||
// Reduce shield durability by incoming damage
|
||||
int shieldhealth = shield->getClass().getItemHealth(*shield);
|
||||
|
||||
|
@ -771,6 +771,14 @@ namespace MWMechanics
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MechanicsManager::checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
if (ptr.getClass().isActor())
|
||||
return mActors.checkScriptedAnimationPlaying(ptr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MechanicsManager::onOpen(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (ptr.getClass().isActor())
|
||||
|
@ -145,6 +145,7 @@ namespace MWMechanics
|
||||
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;
|
||||
bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const override;
|
||||
void persistAnimationStates() override;
|
||||
|
||||
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
|
||||
|
@ -101,7 +101,7 @@ namespace MWRender
|
||||
templateNode, mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager(), &rotation);
|
||||
}
|
||||
return SceneUtil::attach(
|
||||
templateNode, mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager());
|
||||
std::move(templateNode), mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager());
|
||||
}
|
||||
|
||||
std::string ActorAnimation::getShieldMesh(const MWWorld::ConstPtr& shield, bool female) const
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <components/sceneutil/keyframe.hpp>
|
||||
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/sceneutil/lightutil.hpp>
|
||||
|
@ -422,7 +422,8 @@ namespace MWRender
|
||||
if (cellX > mMaxX || cellX < mMinX || cellY > mMaxY || cellY < mMinY)
|
||||
return;
|
||||
|
||||
requestOverlayTextureUpdate(originX, mHeight - originY, cellSize, cellSize, localMapTexture, false, true);
|
||||
requestOverlayTextureUpdate(
|
||||
originX, mHeight - originY, cellSize, cellSize, std::move(localMapTexture), false, true);
|
||||
}
|
||||
|
||||
void GlobalMap::clear()
|
||||
@ -554,7 +555,7 @@ namespace MWRender
|
||||
{
|
||||
mOverlayImage = image;
|
||||
|
||||
requestOverlayTextureUpdate(0, 0, mWidth, mHeight, texture, true, false);
|
||||
requestOverlayTextureUpdate(0, 0, mWidth, mHeight, std::move(texture), true, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -562,7 +563,7 @@ namespace MWRender
|
||||
// In the latter case, we'll want filtering.
|
||||
// Create a RTT Camera and draw the image onto mOverlayImage in the next frame.
|
||||
requestOverlayTextureUpdate(destBox.mLeft, destBox.mTop, destBox.mRight - destBox.mLeft,
|
||||
destBox.mBottom - destBox.mTop, texture, true, true, srcBox.mLeft / float(imageWidth),
|
||||
destBox.mBottom - destBox.mTop, std::move(texture), true, true, srcBox.mLeft / float(imageWidth),
|
||||
srcBox.mTop / float(imageHeight), srcBox.mRight / float(imageWidth),
|
||||
srcBox.mBottom / float(imageHeight));
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ namespace MWRender
|
||||
auto resolveFragment = shaderManager.getShader("luminance/resolve.frag", defines);
|
||||
|
||||
mResolveProgram = shaderManager.getProgram(vertex, std::move(resolveFragment));
|
||||
mLuminanceProgram = shaderManager.getProgram(vertex, std::move(luminanceFragment));
|
||||
mLuminanceProgram = shaderManager.getProgram(std::move(vertex), std::move(luminanceFragment));
|
||||
|
||||
for (auto& buffer : mBuffers)
|
||||
{
|
||||
|
@ -68,7 +68,7 @@ namespace MWRender
|
||||
ptr.getClass().adjustScale(ptr, scaleVec, true);
|
||||
insert->setScale(scaleVec);
|
||||
|
||||
ptr.getRefData().setBaseNode(insert);
|
||||
ptr.getRefData().setBaseNode(std::move(insert));
|
||||
}
|
||||
|
||||
void Objects::insertModel(const MWWorld::Ptr& ptr, const std::string& mesh, bool allowLight)
|
||||
|
@ -288,6 +288,8 @@ namespace MWRender
|
||||
pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0)
|
||||
.getTexture()));
|
||||
|
||||
assert(texture != nullptr);
|
||||
|
||||
texture->setTextureSize(w, h);
|
||||
texture->setNumMipmapLevels(pass.mRenderTexture->getNumMipmapLevels());
|
||||
texture->dirtyTextureObject();
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <components/stereo/multiview.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
@ -662,6 +663,11 @@ namespace MWRender
|
||||
|
||||
for (const auto& name : pass->getRenderTargets())
|
||||
{
|
||||
if (name.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& renderTarget = technique->getRenderTargetsMap()[name];
|
||||
subPass.mStateSet->setTextureAttribute(subTexUnit, renderTarget.mTarget);
|
||||
subPass.mStateSet->addUniform(new osg::Uniform(name.c_str(), subTexUnit));
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "precipitationocclusion.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <osgUtil/CullVisitor>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
@ -120,16 +122,19 @@ namespace MWRender
|
||||
|
||||
void PrecipitationOccluder::update()
|
||||
{
|
||||
if (!mRange.has_value())
|
||||
return;
|
||||
|
||||
const osg::Vec3 pos = mSceneCamera->getInverseViewMatrix().getTrans();
|
||||
|
||||
const float zmin = pos.z() - mRange.z() - Constants::CellSizeInUnits;
|
||||
const float zmax = pos.z() + mRange.z() + Constants::CellSizeInUnits;
|
||||
const float zmin = pos.z() - mRange->z() - Constants::CellSizeInUnits;
|
||||
const float zmax = pos.z() + mRange->z() + Constants::CellSizeInUnits;
|
||||
const float near = 0;
|
||||
const float far = zmax - zmin;
|
||||
|
||||
const float left = -mRange.x() / 2;
|
||||
const float left = -mRange->x() / 2;
|
||||
const float right = -left;
|
||||
const float top = mRange.y() / 2;
|
||||
const float top = mRange->y() / 2;
|
||||
const float bottom = -top;
|
||||
|
||||
if (SceneUtil::AutoDepth::isReversed())
|
||||
@ -163,10 +168,14 @@ namespace MWRender
|
||||
mSkyCullCallback = nullptr;
|
||||
|
||||
mRootNode->removeChild(mCamera);
|
||||
mRange = std::nullopt;
|
||||
}
|
||||
|
||||
void PrecipitationOccluder::updateRange(const osg::Vec3f range)
|
||||
{
|
||||
assert(range.x() != 0);
|
||||
assert(range.y() != 0);
|
||||
assert(range.z() != 0);
|
||||
const osg::Vec3f margin = { -50, -50, 0 };
|
||||
mRange = range - margin;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <osg/Camera>
|
||||
#include <osg/Texture2D>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class PrecipitationOccluder
|
||||
@ -27,7 +29,7 @@ namespace MWRender
|
||||
osg::ref_ptr<osg::Camera> mCamera;
|
||||
osg::ref_ptr<osg::Camera> mSceneCamera;
|
||||
osg::ref_ptr<osg::Texture2D> mDepthTexture;
|
||||
osg::Vec3f mRange;
|
||||
std::optional<osg::Vec3f> mRange;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -716,9 +716,12 @@ namespace MWRender
|
||||
// need to wrap this in a StateUpdater?
|
||||
mSunLight->setPosition(osg::Vec4(position.x(), position.y(), position.z(), 0));
|
||||
|
||||
// The sun is not synchronized with the sunlight because sunlight origin can't reach the horizon
|
||||
// This is based on exterior sun orbit and won't make sense for interiors, see WeatherManager::update
|
||||
position.z() = 400.f - std::abs(position.x());
|
||||
mSky->setSunDirection(position);
|
||||
|
||||
mPostProcessor->getStateUpdater()->setSunPos(mSunLight->getPosition(), mNight);
|
||||
mPostProcessor->getStateUpdater()->setSunPos(osg::Vec4f(position, 0.f), mNight);
|
||||
}
|
||||
|
||||
void RenderingManager::addCell(const MWWorld::CellStore* store)
|
||||
|
@ -106,7 +106,7 @@ namespace MWRender
|
||||
mProgramBlobber = shaderManager.getProgram(
|
||||
vertex, shaderManager.getShader("ripples_blobber.frag", defineMap, osg::Shader::FRAGMENT));
|
||||
mProgramSimulation = shaderManager.getProgram(
|
||||
vertex, shaderManager.getShader("ripples_simulate.frag", defineMap, osg::Shader::FRAGMENT));
|
||||
std::move(vertex), shaderManager.getShader("ripples_simulate.frag", defineMap, osg::Shader::FRAGMENT));
|
||||
}
|
||||
|
||||
void RipplesSurface::setupComputePipeline()
|
||||
|
@ -764,7 +764,7 @@ namespace MWRender
|
||||
cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||
cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||
|
||||
mCloudUpdater->setTexture(cloudTex);
|
||||
mCloudUpdater->setTexture(std::move(cloudTex));
|
||||
}
|
||||
|
||||
if (mStormDirection != weather.mStormDirection)
|
||||
@ -786,7 +786,7 @@ namespace MWRender
|
||||
cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||
cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||
|
||||
mNextCloudUpdater->setTexture(cloudTex);
|
||||
mNextCloudUpdater->setTexture(std::move(cloudTex));
|
||||
mNextStormDirection = weather.mStormDirection;
|
||||
}
|
||||
}
|
||||
|
@ -722,8 +722,8 @@ namespace MWRender
|
||||
mRainSettingsUpdater = new RainSettingsUpdater();
|
||||
node->setUpdateCallback(mRainSettingsUpdater);
|
||||
|
||||
mShaderWaterStateSetUpdater
|
||||
= new ShaderWaterStateSetUpdater(this, mReflection, mRefraction, mRipples, std::move(program), normalMap);
|
||||
mShaderWaterStateSetUpdater = new ShaderWaterStateSetUpdater(
|
||||
this, mReflection, mRefraction, mRipples, std::move(program), std::move(normalMap));
|
||||
node->addCullCallback(mShaderWaterStateSetUpdater);
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <components/settings/values.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/pathutil.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <components/loadinglistener/loadinglistener.hpp>
|
||||
|
||||
#include <components/files/conversion.hpp>
|
||||
#include <components/misc/algorithm.hpp>
|
||||
#include <components/settings/values.hpp>
|
||||
|
||||
#include <osg/Image>
|
||||
@ -81,10 +82,8 @@ std::map<int, int> MWState::StateManager::buildContentFileIndexMap(const ESM::ES
|
||||
|
||||
for (int iPrev = 0; iPrev < static_cast<int>(prev.size()); ++iPrev)
|
||||
{
|
||||
std::string id = Misc::StringUtils::lowerCase(prev[iPrev].name);
|
||||
|
||||
for (int iCurrent = 0; iCurrent < static_cast<int>(current.size()); ++iCurrent)
|
||||
if (id == Misc::StringUtils::lowerCase(current[iCurrent]))
|
||||
if (Misc::StringUtils::ciEqual(prev[iPrev].name, current[iCurrent]))
|
||||
{
|
||||
map.insert(std::make_pair(iPrev, iCurrent));
|
||||
break;
|
||||
@ -410,9 +409,38 @@ void MWState::StateManager::loadGame(const std::filesystem::path& filepath)
|
||||
loadGame(character, filepath);
|
||||
}
|
||||
|
||||
struct VersionMismatchError : public std::runtime_error
|
||||
struct SaveFormatVersionError : public std::exception
|
||||
{
|
||||
using std::runtime_error::runtime_error;
|
||||
using std::exception::exception;
|
||||
|
||||
SaveFormatVersionError(ESM::FormatVersion savegameFormat, const std::string& message)
|
||||
: mSavegameFormat(savegameFormat)
|
||||
, mErrorMessage(message)
|
||||
{
|
||||
}
|
||||
|
||||
const char* what() const noexcept override { return mErrorMessage.c_str(); }
|
||||
ESM::FormatVersion getFormatVersion() const { return mSavegameFormat; }
|
||||
|
||||
protected:
|
||||
ESM::FormatVersion mSavegameFormat = ESM::DefaultFormatVersion;
|
||||
std::string mErrorMessage;
|
||||
};
|
||||
|
||||
struct SaveVersionTooOldError : SaveFormatVersionError
|
||||
{
|
||||
SaveVersionTooOldError(ESM::FormatVersion savegameFormat)
|
||||
: SaveFormatVersionError(savegameFormat, "format version " + std::to_string(savegameFormat) + " is too old")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct SaveVersionTooNewError : SaveFormatVersionError
|
||||
{
|
||||
SaveVersionTooNewError(ESM::FormatVersion savegameFormat)
|
||||
: SaveFormatVersionError(savegameFormat, "format version " + std::to_string(savegameFormat) + " is too new")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void MWState::StateManager::loadGame(const Character* character, const std::filesystem::path& filepath)
|
||||
@ -428,23 +456,9 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
||||
|
||||
ESM::FormatVersion version = reader.getFormatVersion();
|
||||
if (version > ESM::CurrentSaveGameFormatVersion)
|
||||
throw VersionMismatchError("#{OMWEngine:LoadingRequiresNewVersionError}");
|
||||
throw SaveVersionTooNewError(version);
|
||||
else if (version < ESM::MinSupportedSaveGameFormatVersion)
|
||||
{
|
||||
const char* release;
|
||||
// Report the last version still capable of reading this save
|
||||
if (version <= ESM::OpenMW0_48SaveGameFormatVersion)
|
||||
release = "OpenMW 0.48.0";
|
||||
else
|
||||
{
|
||||
// Insert additional else if statements above to cover future releases
|
||||
static_assert(ESM::MinSupportedSaveGameFormatVersion <= ESM::OpenMW0_49SaveGameFormatVersion);
|
||||
release = "OpenMW 0.49.0";
|
||||
}
|
||||
auto l10n = MWBase::Environment::get().getL10nManager()->getContext("OMWEngine");
|
||||
std::string message = l10n->formatMessage("LoadingRequiresOldVersionError", { "version" }, { release });
|
||||
throw VersionMismatchError(message);
|
||||
}
|
||||
throw SaveVersionTooOldError(version);
|
||||
|
||||
std::map<int, int> contentFileMap = buildContentFileIndexMap(reader);
|
||||
reader.setContentFileMapping(&contentFileMap);
|
||||
@ -626,23 +640,49 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
||||
|
||||
MWBase::Environment::get().getLuaManager()->gameLoaded();
|
||||
}
|
||||
catch (const SaveVersionTooNewError& e)
|
||||
{
|
||||
std::string error = "#{OMWEngine:LoadingRequiresNewVersionError}";
|
||||
printSavegameFormatError(e.what(), error);
|
||||
}
|
||||
catch (const SaveVersionTooOldError& e)
|
||||
{
|
||||
const char* release;
|
||||
// Report the last version still capable of reading this save
|
||||
if (e.getFormatVersion() <= ESM::OpenMW0_48SaveGameFormatVersion)
|
||||
release = "OpenMW 0.48.0";
|
||||
else
|
||||
{
|
||||
// Insert additional else if statements above to cover future releases
|
||||
static_assert(ESM::MinSupportedSaveGameFormatVersion <= ESM::OpenMW0_49SaveGameFormatVersion);
|
||||
release = "OpenMW 0.49.0";
|
||||
}
|
||||
auto l10n = MWBase::Environment::get().getL10nManager()->getContext("OMWEngine");
|
||||
std::string error = l10n->formatMessage("LoadingRequiresOldVersionError", { "version" }, { release });
|
||||
printSavegameFormatError(e.what(), error);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
Log(Debug::Error) << "Failed to load saved game: " << e.what();
|
||||
|
||||
cleanup(true);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_MainMenu);
|
||||
|
||||
std::vector<std::string> buttons;
|
||||
buttons.emplace_back("#{Interface:OK}");
|
||||
|
||||
std::string error = "#{OMWEngine:LoadingFailed}: " + std::string(e.what());
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error, buttons);
|
||||
printSavegameFormatError(e.what(), error);
|
||||
}
|
||||
}
|
||||
|
||||
void MWState::StateManager::printSavegameFormatError(
|
||||
const std::string& exceptionText, const std::string& messageBoxText)
|
||||
{
|
||||
Log(Debug::Error) << "Failed to load saved game: " << exceptionText;
|
||||
|
||||
cleanup(true);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_MainMenu);
|
||||
|
||||
std::vector<std::string> buttons;
|
||||
buttons.emplace_back("#{Interface:OK}");
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(messageBoxText, buttons);
|
||||
}
|
||||
|
||||
void MWState::StateManager::quickLoad()
|
||||
{
|
||||
if (Character* currentCharacter = getCurrentCharacter())
|
||||
|
@ -22,6 +22,8 @@ namespace MWState
|
||||
private:
|
||||
void cleanup(bool force = false);
|
||||
|
||||
void printSavegameFormatError(const std::string& exceptionText, const std::string& messageBoxText);
|
||||
|
||||
bool confirmLoading(const std::vector<std::string_view>& missingFiles) const;
|
||||
|
||||
void writeScreenshot(std::vector<char>& imageData) const;
|
||||
|
@ -52,20 +52,13 @@ namespace MWWorld
|
||||
|
||||
struct ListModelsVisitor
|
||||
{
|
||||
ListModelsVisitor(std::vector<std::string>& out)
|
||||
: mOut(out)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool operator()(const MWWorld::Ptr& ptr)
|
||||
bool operator()(const MWWorld::ConstPtr& ptr)
|
||||
{
|
||||
ptr.getClass().getModelsToPreload(ptr, mOut);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual ~ListModelsVisitor() = default;
|
||||
|
||||
std::vector<std::string>& mOut;
|
||||
};
|
||||
|
||||
@ -90,8 +83,8 @@ namespace MWWorld
|
||||
{
|
||||
mTerrainView = mTerrain->createView();
|
||||
|
||||
ListModelsVisitor visitor(mMeshes);
|
||||
cell->forEach(visitor);
|
||||
ListModelsVisitor visitor{ mMeshes };
|
||||
cell->forEachConst(visitor);
|
||||
}
|
||||
|
||||
void abort() override { mAbort = true; }
|
||||
|
@ -209,8 +209,8 @@ namespace MWWorld
|
||||
/// false will abort the iteration.
|
||||
/// \note Prefer using forEachConst when possible.
|
||||
/// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in
|
||||
/// unintended behaviour. \attention This function also lists deleted (count 0) objects! \return Iteration
|
||||
/// completed?
|
||||
/// unintended behaviour. \attention This function also lists deleted (count 0) objects!
|
||||
/// \return Iteration completed?
|
||||
template <class Visitor>
|
||||
bool forEach(Visitor&& visitor)
|
||||
{
|
||||
@ -224,12 +224,12 @@ namespace MWWorld
|
||||
|
||||
mHasState = true;
|
||||
|
||||
for (unsigned int i = 0; i < mMergedRefs.size(); ++i)
|
||||
for (LiveCellRefBase* mergedRef : mMergedRefs)
|
||||
{
|
||||
if (!isAccessible(mMergedRefs[i]->mData, mMergedRefs[i]->mRef))
|
||||
if (!isAccessible(mergedRef->mData, mergedRef->mRef))
|
||||
continue;
|
||||
|
||||
if (!visitor(MWWorld::Ptr(mMergedRefs[i], this)))
|
||||
if (!visitor(MWWorld::Ptr(mergedRef, this)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -238,8 +238,8 @@ namespace MWWorld
|
||||
/// Call visitor (MWWorld::ConstPtr) for each reference. visitor must return a bool. Returning
|
||||
/// false will abort the iteration.
|
||||
/// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in
|
||||
/// unintended behaviour. \attention This function also lists deleted (count 0) objects! \return Iteration
|
||||
/// completed?
|
||||
/// unintended behaviour. \attention This function also lists deleted (count 0) objects!
|
||||
/// \return Iteration completed?
|
||||
template <class Visitor>
|
||||
bool forEachConst(Visitor&& visitor) const
|
||||
{
|
||||
@ -249,12 +249,12 @@ namespace MWWorld
|
||||
if (mMergedRefsNeedsUpdate)
|
||||
updateMergedRefs();
|
||||
|
||||
for (unsigned int i = 0; i < mMergedRefs.size(); ++i)
|
||||
for (const LiveCellRefBase* mergedRef : mMergedRefs)
|
||||
{
|
||||
if (!isAccessible(mMergedRefs[i]->mData, mMergedRefs[i]->mRef))
|
||||
if (!isAccessible(mergedRef->mData, mergedRef->mRef))
|
||||
continue;
|
||||
|
||||
if (!visitor(MWWorld::ConstPtr(mMergedRefs[i], this)))
|
||||
if (!visitor(MWWorld::ConstPtr(mergedRef, this)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -263,8 +263,8 @@ namespace MWWorld
|
||||
/// Call visitor (ref) for each reference of given type. visitor must return a bool. Returning
|
||||
/// false will abort the iteration.
|
||||
/// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in
|
||||
/// unintended behaviour. \attention This function also lists deleted (count 0) objects! \return Iteration
|
||||
/// completed?
|
||||
/// unintended behaviour. \attention This function also lists deleted (count 0) objects!
|
||||
/// \return Iteration completed?
|
||||
template <class T, class Visitor>
|
||||
bool forEachType(Visitor&& visitor)
|
||||
{
|
||||
|
@ -118,11 +118,6 @@ namespace MWWorld
|
||||
throw std::runtime_error("class cannot hit");
|
||||
}
|
||||
|
||||
void Class::block(const Ptr& ptr) const
|
||||
{
|
||||
throw std::runtime_error("class cannot block");
|
||||
}
|
||||
|
||||
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker,
|
||||
const osg::Vec3f& hitPosition, bool successful, const MWMechanics::DamageSourceType sourceType) const
|
||||
{
|
||||
@ -149,7 +144,7 @@ namespace MWWorld
|
||||
throw std::runtime_error("class does not have an inventory store");
|
||||
}
|
||||
|
||||
bool Class::hasInventoryStore(const Ptr& ptr) const
|
||||
bool Class::hasInventoryStore(const ConstPtr& ptr) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -320,7 +315,7 @@ namespace MWWorld
|
||||
return false;
|
||||
}
|
||||
|
||||
void Class::getModelsToPreload(const Ptr& ptr, std::vector<std::string>& models) const
|
||||
void Class::getModelsToPreload(const ConstPtr& ptr, std::vector<std::string>& models) const
|
||||
{
|
||||
std::string model = getModel(ptr);
|
||||
if (!model.empty())
|
||||
|
@ -151,10 +151,6 @@ namespace MWWorld
|
||||
/// actor responsible for the attack. \a successful specifies if the hit is
|
||||
/// successful or not. \a sourceType classifies the damage source.
|
||||
|
||||
virtual void block(const Ptr& ptr) const;
|
||||
///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield
|
||||
/// (default implementation: throw an exception)
|
||||
|
||||
virtual std::unique_ptr<Action> activate(const Ptr& ptr, const Ptr& actor) const;
|
||||
///< Generate action for activation (default implementation: return a null action).
|
||||
|
||||
@ -170,7 +166,7 @@ namespace MWWorld
|
||||
///< Return inventory store or throw an exception, if class does not have a
|
||||
/// inventory store (default implementation: throw an exception)
|
||||
|
||||
virtual bool hasInventoryStore(const Ptr& ptr) const;
|
||||
virtual bool hasInventoryStore(const ConstPtr& ptr) const;
|
||||
///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false)
|
||||
|
||||
virtual bool canLock(const ConstPtr& ptr) const;
|
||||
@ -284,7 +280,7 @@ namespace MWWorld
|
||||
virtual bool useAnim() const;
|
||||
///< Whether or not to use animated variant of model (default false)
|
||||
|
||||
virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const;
|
||||
virtual void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const;
|
||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||
///< list getModel().
|
||||
|
||||
|
@ -747,21 +747,21 @@ namespace MWWorld
|
||||
const float dayDuration = adjustedNightStart - mSunriseTime;
|
||||
const float nightDuration = 24.f - dayDuration;
|
||||
|
||||
double theta;
|
||||
float orbit;
|
||||
if (!is_night)
|
||||
{
|
||||
theta = static_cast<float>(osg::PI) * (adjustedHour - mSunriseTime) / dayDuration;
|
||||
float t = (adjustedHour - mSunriseTime) / dayDuration;
|
||||
orbit = 1.f - 2.f * t;
|
||||
}
|
||||
else
|
||||
{
|
||||
theta = static_cast<float>(osg::PI)
|
||||
- static_cast<float>(osg::PI) * (adjustedHour - adjustedNightStart) / nightDuration;
|
||||
float t = (adjustedHour - adjustedNightStart) / nightDuration;
|
||||
orbit = 2.f * t - 1.f;
|
||||
}
|
||||
|
||||
osg::Vec3f final(static_cast<float>(cos(theta)),
|
||||
-0.268f, // approx tan( -15 degrees )
|
||||
static_cast<float>(sin(theta)));
|
||||
mRendering.setSunDirection(final * -1);
|
||||
// Hardcoded constant from Morrowind
|
||||
const osg::Vec3f sunDir(-400.f * orbit, 75.f, -100.f);
|
||||
mRendering.setSunDirection(sunDir);
|
||||
mRendering.setNight(is_night);
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,7 @@
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/scriptmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
@ -997,6 +998,9 @@ namespace MWWorld
|
||||
{
|
||||
MWWorld::Ptr facedObject;
|
||||
|
||||
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
|
||||
return facedObject;
|
||||
|
||||
if (MWBase::Environment::get().getWindowManager()->isGuiMode()
|
||||
&& MWBase::Environment::get().getWindowManager()->isConsoleMode())
|
||||
facedObject = getFacedObject(getMaxActivationDistance() * 50, false);
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <components/detournavigator/debug.hpp>
|
||||
#include <components/detournavigator/gettilespositions.hpp>
|
||||
#include <components/detournavigator/settings.hpp>
|
||||
|
||||
@ -86,4 +87,79 @@ namespace
|
||||
|
||||
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));
|
||||
}
|
||||
|
||||
struct TilesPositionsRangeParams
|
||||
{
|
||||
TilesPositionsRange mA;
|
||||
TilesPositionsRange mB;
|
||||
TilesPositionsRange mResult;
|
||||
};
|
||||
|
||||
struct DetourNavigatorGetIntersectionTest : TestWithParam<TilesPositionsRangeParams>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(DetourNavigatorGetIntersectionTest, should_return_expected_result)
|
||||
{
|
||||
EXPECT_EQ(getIntersection(GetParam().mA, GetParam().mB), GetParam().mResult);
|
||||
EXPECT_EQ(getIntersection(GetParam().mB, GetParam().mA), GetParam().mResult);
|
||||
}
|
||||
|
||||
const TilesPositionsRangeParams getIntersectionParams[] = {
|
||||
{ .mA = TilesPositionsRange{}, .mB = TilesPositionsRange{}, .mResult = TilesPositionsRange{} },
|
||||
{
|
||||
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } },
|
||||
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 3, 3 } },
|
||||
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } },
|
||||
},
|
||||
{
|
||||
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 2, 2 }, .mEnd = TilePosition{ 3, 3 } },
|
||||
.mResult = TilesPositionsRange{},
|
||||
},
|
||||
{
|
||||
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } },
|
||||
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 1, 1 } },
|
||||
},
|
||||
{
|
||||
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 0, 2 }, .mEnd = TilePosition{ 3, 3 } },
|
||||
.mResult = TilesPositionsRange{},
|
||||
},
|
||||
{
|
||||
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 2, 0 }, .mEnd = TilePosition{ 3, 3 } },
|
||||
.mResult = TilesPositionsRange{},
|
||||
},
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
GetIntersectionParams, DetourNavigatorGetIntersectionTest, ValuesIn(getIntersectionParams));
|
||||
|
||||
struct DetourNavigatorGetUnionTest : TestWithParam<TilesPositionsRangeParams>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(DetourNavigatorGetUnionTest, should_return_expected_result)
|
||||
{
|
||||
EXPECT_EQ(getUnion(GetParam().mA, GetParam().mB), GetParam().mResult);
|
||||
EXPECT_EQ(getUnion(GetParam().mB, GetParam().mA), GetParam().mResult);
|
||||
}
|
||||
|
||||
const TilesPositionsRangeParams getUnionParams[] = {
|
||||
{ .mA = TilesPositionsRange{}, .mB = TilesPositionsRange{}, .mResult = TilesPositionsRange{} },
|
||||
{
|
||||
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } },
|
||||
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 3, 3 } },
|
||||
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 3, 3 } },
|
||||
},
|
||||
{
|
||||
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } },
|
||||
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } },
|
||||
},
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(GetUnionParams, DetourNavigatorGetUnionTest, ValuesIn(getUnionParams));
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ namespace
|
||||
using namespace DetourNavigator;
|
||||
using namespace DetourNavigator::Tests;
|
||||
|
||||
constexpr int heightfieldTileSize = ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1);
|
||||
|
||||
struct DetourNavigatorNavigatorTest : Test
|
||||
{
|
||||
Settings mSettings = makeSettings();
|
||||
@ -53,7 +55,6 @@ namespace
|
||||
AreaCosts mAreaCosts;
|
||||
Loading::Listener mListener;
|
||||
const osg::Vec2i mCellPosition{ 0, 0 };
|
||||
const int mHeightfieldTileSize = ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1);
|
||||
const float mEndTolerance = 0;
|
||||
const btTransform mTransform{ btMatrix3x3::getIdentity(), btVector3(256, 256, 0) };
|
||||
const ObjectTransform mObjectTransform{ ESM::Position{ { 256, 256, 0 }, { 0, 0, 0 } }, 0.0f };
|
||||
@ -129,7 +130,7 @@ namespace
|
||||
{
|
||||
}
|
||||
|
||||
T& shape() { return static_cast<T&>(*mInstance->mCollisionShape); }
|
||||
T& shape() const { return static_cast<T&>(*mInstance->mCollisionShape); }
|
||||
const osg::ref_ptr<const Resource::BulletShapeInstance>& instance() const { return mInstance; }
|
||||
|
||||
private:
|
||||
@ -167,7 +168,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, update_then_find_path_should_return_path)
|
||||
{
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||
@ -189,7 +190,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, find_path_to_the_start_position_should_contain_single_point)
|
||||
{
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||
@ -211,7 +212,7 @@ namespace
|
||||
mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())));
|
||||
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||
compound.shape().addChildShape(
|
||||
@ -256,7 +257,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, update_changed_object_should_change_navmesh)
|
||||
{
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||
compound.shape().addChildShape(
|
||||
@ -335,7 +336,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, only_one_heightfield_per_cell_is_allowed)
|
||||
{
|
||||
const HeightfieldSurface surface1 = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize1 = mHeightfieldTileSize * (surface1.mSize - 1);
|
||||
const int cellSize1 = heightfieldTileSize * (surface1.mSize - 1);
|
||||
|
||||
const std::array<float, 5 * 5> heightfieldData2{ {
|
||||
-25, -25, -25, -25, -25, // row 0
|
||||
@ -345,7 +346,7 @@ namespace
|
||||
-25, -25, -25, -25, -25, // row 4
|
||||
} };
|
||||
const HeightfieldSurface surface2 = makeSquareHeightfieldSurface(heightfieldData2);
|
||||
const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1);
|
||||
const int cellSize2 = heightfieldTileSize * (surface2.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
mNavigator->addHeightfield(mCellPosition, cellSize1, surface1, nullptr);
|
||||
@ -412,7 +413,7 @@ namespace
|
||||
0, -50, -100, -100, -100, // row 4
|
||||
} };
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
mNavigator->addWater(mCellPosition, cellSize, 300, nullptr);
|
||||
@ -446,7 +447,7 @@ namespace
|
||||
0, 0, 0, 0, 0, 0, 0, // row 6
|
||||
} };
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
|
||||
@ -480,7 +481,7 @@ namespace
|
||||
0, 0, 0, 0, 0, 0, 0, // row 6
|
||||
} };
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||
@ -513,7 +514,7 @@ namespace
|
||||
0, 0, 0, 0, 0, 0, 0, // row 6
|
||||
} };
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
|
||||
@ -566,7 +567,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, update_heightfield_remove_and_update_then_find_path_should_return_path)
|
||||
{
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||
@ -602,7 +603,7 @@ namespace
|
||||
0, -25, -100, -100, -100, -100, // row 5
|
||||
} };
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||
@ -629,7 +630,7 @@ namespace
|
||||
mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())));
|
||||
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
const btVector3 shift = getHeightfieldShift(mCellPosition, cellSize, surface.mMinHeight, surface.mMaxHeight);
|
||||
|
||||
std::vector<CollisionShapeInstance<btBoxShape>> boxes;
|
||||
@ -718,7 +719,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, update_then_raycast_should_return_position)
|
||||
{
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||
@ -737,7 +738,7 @@ namespace
|
||||
update_for_oscillating_object_that_does_not_change_navmesh_should_not_trigger_navmesh_update)
|
||||
{
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
CollisionShapeInstance oscillatingBox(std::make_unique<btBoxShape>(btVector3(20, 20, 20)));
|
||||
const btVector3 oscillatingBoxShapePosition(288, 288, 400);
|
||||
@ -777,7 +778,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, should_provide_path_over_flat_heightfield)
|
||||
{
|
||||
const HeightfieldPlane plane{ 100 };
|
||||
const int cellSize = mHeightfieldTileSize * 4;
|
||||
const int cellSize = heightfieldTileSize * 4;
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
mNavigator->addHeightfield(mCellPosition, cellSize, plane, nullptr);
|
||||
@ -796,7 +797,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, for_not_reachable_destination_find_path_should_provide_partial_path)
|
||||
{
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
|
||||
@ -822,7 +823,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, end_tolerance_should_extent_available_destinations)
|
||||
{
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
|
||||
@ -948,7 +949,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nav_mesh_position)
|
||||
{
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||
@ -966,7 +967,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_too_far)
|
||||
{
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||
@ -984,7 +985,7 @@ namespace
|
||||
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_flags_do_not_match)
|
||||
{
|
||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
|
||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||
@ -998,4 +999,142 @@ namespace
|
||||
EXPECT_EQ(findNearestNavMeshPosition(*mNavigator, mAgentBounds, position, searchAreaHalfExtents, Flag_swim),
|
||||
std::nullopt);
|
||||
}
|
||||
|
||||
struct DetourNavigatorUpdateTest : TestWithParam<std::function<void(Navigator&)>>
|
||||
{
|
||||
};
|
||||
|
||||
std::vector<TilePosition> getUsedTiles(const NavMeshCacheItem& navMesh)
|
||||
{
|
||||
std::vector<TilePosition> result;
|
||||
navMesh.forEachUsedTile([&](const TilePosition& position, const auto&...) { result.push_back(position); });
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_P(DetourNavigatorUpdateTest, update_should_change_covered_area_when_player_moves)
|
||||
{
|
||||
Loading::Listener listener;
|
||||
Settings settings = makeSettings();
|
||||
settings.mMaxTilesNumber = 5;
|
||||
NavigatorImpl navigator(settings, nullptr);
|
||||
const AgentBounds agentBounds{ CollisionShapeType::Aabb, { 29, 29, 66 } };
|
||||
ASSERT_TRUE(navigator.addAgent(agentBounds));
|
||||
|
||||
GetParam()(navigator);
|
||||
|
||||
{
|
||||
auto updateGuard = navigator.makeUpdateGuard();
|
||||
navigator.update(osg::Vec3f(3000, 3000, 0), updateGuard.get());
|
||||
}
|
||||
|
||||
navigator.wait(WaitConditionType::allJobsDone, &listener);
|
||||
|
||||
{
|
||||
const auto navMesh = navigator.getNavMesh(agentBounds);
|
||||
ASSERT_NE(navMesh, nullptr);
|
||||
|
||||
const TilePosition expectedTiles[] = { { 3, 4 }, { 4, 3 }, { 4, 4 }, { 4, 5 }, { 5, 4 } };
|
||||
const auto usedTiles = getUsedTiles(*navMesh->lockConst());
|
||||
EXPECT_THAT(usedTiles, UnorderedElementsAreArray(expectedTiles)) << usedTiles;
|
||||
}
|
||||
|
||||
{
|
||||
auto updateGuard = navigator.makeUpdateGuard();
|
||||
navigator.update(osg::Vec3f(4000, 3000, 0), updateGuard.get());
|
||||
}
|
||||
|
||||
navigator.wait(WaitConditionType::allJobsDone, &listener);
|
||||
|
||||
{
|
||||
const auto navMesh = navigator.getNavMesh(agentBounds);
|
||||
ASSERT_NE(navMesh, nullptr);
|
||||
|
||||
const TilePosition expectedTiles[] = { { 4, 4 }, { 5, 3 }, { 5, 4 }, { 5, 5 }, { 6, 4 } };
|
||||
const auto usedTiles = getUsedTiles(*navMesh->lockConst());
|
||||
EXPECT_THAT(usedTiles, UnorderedElementsAreArray(expectedTiles)) << usedTiles;
|
||||
}
|
||||
}
|
||||
|
||||
struct AddHeightfieldSurface
|
||||
{
|
||||
static constexpr std::size_t sSize = 65;
|
||||
static constexpr float sHeights[sSize * sSize]{};
|
||||
|
||||
void operator()(Navigator& navigator) const
|
||||
{
|
||||
const osg::Vec2i cellPosition(0, 0);
|
||||
const HeightfieldSurface surface{
|
||||
.mHeights = sHeights,
|
||||
.mSize = sSize,
|
||||
.mMinHeight = -1,
|
||||
.mMaxHeight = 1,
|
||||
};
|
||||
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||
navigator.addHeightfield(cellPosition, cellSize, surface, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
struct AddHeightfieldPlane
|
||||
{
|
||||
void operator()(Navigator& navigator) const
|
||||
{
|
||||
const osg::Vec2i cellPosition(0, 0);
|
||||
const HeightfieldPlane plane{ .mHeight = 0 };
|
||||
const int cellSize = 8192;
|
||||
navigator.addHeightfield(cellPosition, cellSize, plane, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
struct AddWater
|
||||
{
|
||||
void operator()(Navigator& navigator) const
|
||||
{
|
||||
const osg::Vec2i cellPosition(0, 0);
|
||||
const float level = 0;
|
||||
const int cellSize = 8192;
|
||||
navigator.addWater(cellPosition, cellSize, level, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
struct AddObject
|
||||
{
|
||||
const float mSize = 8192;
|
||||
CollisionShapeInstance<btBoxShape> mBox{ std::make_unique<btBoxShape>(btVector3(mSize, mSize, 1)) };
|
||||
const ObjectTransform mTransform{
|
||||
.mPosition = ESM::Position{ .pos = { 0, 0, 0 }, .rot{ 0, 0, 0 } },
|
||||
.mScale = 1.0f,
|
||||
};
|
||||
|
||||
void operator()(Navigator& navigator) const
|
||||
{
|
||||
navigator.addObject(ObjectId(&mBox.shape()), ObjectShapes(mBox.instance(), mTransform),
|
||||
btTransform::getIdentity(), nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
struct AddAll
|
||||
{
|
||||
AddHeightfieldSurface mAddHeightfieldSurface;
|
||||
AddHeightfieldPlane mAddHeightfieldPlane;
|
||||
AddWater mAddWater;
|
||||
AddObject mAddObject;
|
||||
|
||||
void operator()(Navigator& navigator) const
|
||||
{
|
||||
mAddHeightfieldSurface(navigator);
|
||||
mAddHeightfieldPlane(navigator);
|
||||
mAddWater(navigator);
|
||||
mAddObject(navigator);
|
||||
}
|
||||
};
|
||||
|
||||
const std::function<void(Navigator&)> addNavMeshData[] = {
|
||||
AddHeightfieldSurface{},
|
||||
AddHeightfieldPlane{},
|
||||
AddWater{},
|
||||
AddObject{},
|
||||
AddAll{},
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(DifferentNavMeshData, DetourNavigatorUpdateTest, ValuesIn(addNavMeshData));
|
||||
}
|
||||
|
@ -42,12 +42,24 @@ namespace testing
|
||||
<< ", " << value.y() << ", " << value.z() << ')';
|
||||
}
|
||||
|
||||
template <>
|
||||
inline testing::Message& Message::operator<<(const osg::Vec2i& value)
|
||||
{
|
||||
return (*this) << "{" << value.x() << ", " << value.y() << '}';
|
||||
}
|
||||
|
||||
template <>
|
||||
inline testing::Message& Message::operator<<(const Wrapper<osg::Vec3f>& value)
|
||||
{
|
||||
return (*this) << value.mValue;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline testing::Message& Message::operator<<(const Wrapper<osg::Vec2i>& value)
|
||||
{
|
||||
return (*this) << value.mValue;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline testing::Message& Message::operator<<(const Wrapper<float>& value)
|
||||
{
|
||||
@ -72,6 +84,12 @@ namespace testing
|
||||
return writeRange(*this, value, 1);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline testing::Message& Message::operator<<(const std::vector<osg::Vec2i>& value)
|
||||
{
|
||||
return writeRange(*this, value, 1);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline testing::Message& Message::operator<<(const std::vector<float>& value)
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <components/esm/fourcc.hpp>
|
||||
#include <components/esm3/aisequence.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
#include <components/esm3/esmwriter.hpp>
|
||||
#include <components/esm3/loadcont.hpp>
|
||||
@ -410,6 +411,85 @@ namespace ESM
|
||||
EXPECT_EQ(result.mStringId, record.mStringId);
|
||||
}
|
||||
|
||||
TEST_P(Esm3SaveLoadRecordTest, aiSequenceAiWanderShouldNotChange)
|
||||
{
|
||||
AiSequence::AiWander record;
|
||||
record.mData.mDistance = 1;
|
||||
record.mData.mDuration = 2;
|
||||
record.mData.mTimeOfDay = 3;
|
||||
constexpr std::uint8_t idle[8] = { 4, 5, 6, 7, 8, 9, 10, 11 };
|
||||
static_assert(std::size(idle) == std::size(record.mData.mIdle));
|
||||
std::copy(std::begin(idle), std::end(idle), record.mData.mIdle);
|
||||
record.mData.mShouldRepeat = 12;
|
||||
record.mDurationData.mRemainingDuration = 13;
|
||||
record.mStoredInitialActorPosition = true;
|
||||
constexpr float initialActorPosition[3] = { 15, 16, 17 };
|
||||
static_assert(std::size(initialActorPosition) == std::size(record.mInitialActorPosition.mValues));
|
||||
std::copy(
|
||||
std::begin(initialActorPosition), std::end(initialActorPosition), record.mInitialActorPosition.mValues);
|
||||
|
||||
AiSequence::AiWander result;
|
||||
saveAndLoadRecord(record, GetParam(), result);
|
||||
|
||||
EXPECT_EQ(result.mData.mDistance, record.mData.mDistance);
|
||||
EXPECT_EQ(result.mData.mDuration, record.mData.mDuration);
|
||||
EXPECT_EQ(result.mData.mTimeOfDay, record.mData.mTimeOfDay);
|
||||
EXPECT_THAT(result.mData.mIdle, ElementsAreArray(record.mData.mIdle));
|
||||
EXPECT_EQ(result.mData.mShouldRepeat, record.mData.mShouldRepeat);
|
||||
EXPECT_EQ(result.mDurationData.mRemainingDuration, record.mDurationData.mRemainingDuration);
|
||||
EXPECT_EQ(result.mStoredInitialActorPosition, record.mStoredInitialActorPosition);
|
||||
EXPECT_THAT(result.mInitialActorPosition.mValues, ElementsAreArray(record.mInitialActorPosition.mValues));
|
||||
}
|
||||
|
||||
TEST_P(Esm3SaveLoadRecordTest, aiSequenceAiTravelShouldNotChange)
|
||||
{
|
||||
AiSequence::AiTravel record;
|
||||
record.mData.mX = 1;
|
||||
record.mData.mY = 2;
|
||||
record.mData.mZ = 3;
|
||||
record.mHidden = true;
|
||||
record.mRepeat = true;
|
||||
|
||||
AiSequence::AiTravel result;
|
||||
saveAndLoadRecord(record, GetParam(), result);
|
||||
|
||||
EXPECT_EQ(result.mData.mX, record.mData.mX);
|
||||
EXPECT_EQ(result.mData.mY, record.mData.mY);
|
||||
EXPECT_EQ(result.mData.mZ, record.mData.mZ);
|
||||
EXPECT_EQ(result.mHidden, record.mHidden);
|
||||
EXPECT_EQ(result.mRepeat, record.mRepeat);
|
||||
}
|
||||
|
||||
TEST_P(Esm3SaveLoadRecordTest, aiSequenceAiEscortShouldNotChange)
|
||||
{
|
||||
AiSequence::AiEscort record;
|
||||
record.mData.mX = 1;
|
||||
record.mData.mY = 2;
|
||||
record.mData.mZ = 3;
|
||||
record.mData.mDuration = 4;
|
||||
record.mTargetActorId = 5;
|
||||
record.mTargetId = generateRandomRefId(32);
|
||||
record.mCellId = generateRandomString(257);
|
||||
record.mRemainingDuration = 6;
|
||||
record.mRepeat = true;
|
||||
|
||||
AiSequence::AiEscort result;
|
||||
saveAndLoadRecord(record, GetParam(), result);
|
||||
|
||||
EXPECT_EQ(result.mData.mX, record.mData.mX);
|
||||
EXPECT_EQ(result.mData.mY, record.mData.mY);
|
||||
EXPECT_EQ(result.mData.mZ, record.mData.mZ);
|
||||
if (GetParam() <= MaxOldAiPackageFormatVersion)
|
||||
EXPECT_EQ(result.mData.mDuration, record.mRemainingDuration);
|
||||
else
|
||||
EXPECT_EQ(result.mData.mDuration, record.mData.mDuration);
|
||||
EXPECT_EQ(result.mTargetActorId, record.mTargetActorId);
|
||||
EXPECT_EQ(result.mTargetId, record.mTargetId);
|
||||
EXPECT_EQ(result.mCellId, record.mCellId);
|
||||
EXPECT_EQ(result.mRemainingDuration, record.mRemainingDuration);
|
||||
EXPECT_EQ(result.mRepeat, record.mRepeat);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(FormatVersions, Esm3SaveLoadRecordTest, ValuesIn(getFormats()));
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <components/misc/strings/conversion.hpp>
|
||||
#include <components/vfs/archive.hpp>
|
||||
#include <components/vfs/file.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/pathutil.hpp>
|
||||
|
||||
@ -60,7 +61,7 @@ namespace TestingOpenMW
|
||||
void listResources(VFS::FileMap& out) override
|
||||
{
|
||||
for (const auto& [key, value] : mFiles)
|
||||
out.emplace(VFS::Path::normalizeFilename(key), value);
|
||||
out.emplace(key, value);
|
||||
}
|
||||
|
||||
bool contains(std::string_view file) const override { return mFiles.contains(file); }
|
||||
|
@ -810,8 +810,7 @@ bool Wizard::UnshieldWorker::extractFile(
|
||||
if (!dir.mkpath(path))
|
||||
return false;
|
||||
|
||||
QString fileName(path);
|
||||
fileName.append(QString::fromUtf8(unshield_file_name(unshield, index)));
|
||||
path.append(QString::fromUtf8(unshield_file_name(unshield, index)));
|
||||
|
||||
// Calculate the percentage done
|
||||
int progress = (((float)counter / (float)unshield_file_count(unshield)) * 100);
|
||||
@ -825,13 +824,13 @@ bool Wizard::UnshieldWorker::extractFile(
|
||||
emit textChanged(tr("Extracting: %1").arg(QString::fromUtf8(unshield_file_name(unshield, index))));
|
||||
emit progressChanged(progress);
|
||||
|
||||
QByteArray array(fileName.toUtf8());
|
||||
QByteArray array(path.toUtf8());
|
||||
success = unshield_file_save(unshield, index, array.constData());
|
||||
|
||||
if (!success)
|
||||
{
|
||||
qDebug() << "error";
|
||||
dir.remove(fileName);
|
||||
dir.remove(path);
|
||||
}
|
||||
|
||||
return success;
|
||||
|
@ -76,7 +76,7 @@ namespace Bsa
|
||||
fail("Corrupted BSA");
|
||||
}
|
||||
|
||||
mFolders[dirHash][{ nameHash, extHash }] = file;
|
||||
mFolders[dirHash][{ nameHash, extHash }] = std::move(file);
|
||||
|
||||
FileStruct fileStruct{};
|
||||
mFiles.push_back(fileStruct);
|
||||
@ -177,7 +177,7 @@ namespace Bsa
|
||||
return std::nullopt; // folder not found
|
||||
|
||||
uint32_t fileHash = generateHash(fileName);
|
||||
uint32_t extHash = *reinterpret_cast<const uint32_t*>(ext.data() + 1);
|
||||
uint32_t extHash = generateExtensionHash(ext);
|
||||
auto iter = it->second.find({ fileHash, extHash });
|
||||
if (iter == it->second.end())
|
||||
return std::nullopt; // file not found
|
||||
|
@ -46,4 +46,12 @@ namespace Bsa
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t generateExtensionHash(std::string_view extension)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
for (size_t i = 0; i < 4 && i < extension.size() - 1; i++)
|
||||
result |= static_cast<uint8_t>(extension[i + 1]) << (8 * i);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Bsa
|
||||
|
@ -7,6 +7,7 @@
|
||||
namespace Bsa
|
||||
{
|
||||
uint32_t generateHash(const std::string& name);
|
||||
uint32_t generateExtensionHash(std::string_view extension);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -172,7 +172,7 @@ namespace Bsa
|
||||
return FileRecord(); // folder not found, return default which has offset of sInvalidOffset
|
||||
|
||||
uint32_t fileHash = generateHash(fileName);
|
||||
uint32_t extHash = *reinterpret_cast<const uint32_t*>(ext.data() + 1);
|
||||
uint32_t extHash = generateExtensionHash(ext);
|
||||
auto iter = it->second.find({ fileHash, extHash });
|
||||
if (iter == it->second.end())
|
||||
return FileRecord(); // file not found, return default which has offset of sInvalidOffset
|
||||
|
@ -371,7 +371,7 @@ bool Config::GameSettings::writeFileWithComments(QFile& file)
|
||||
{
|
||||
if ((keyMatch.captured(1) + "=" + keyMatch.captured(2)) == keyVal)
|
||||
{
|
||||
*iter = settingLine;
|
||||
*iter = std::move(settingLine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -601,7 +601,7 @@ namespace DetourNavigator
|
||||
if (mSettings.get().mEnableRecastMeshFileNameRevision)
|
||||
recastMeshRevision = revision;
|
||||
if (mSettings.get().mEnableNavMeshFileNameRevision)
|
||||
navMeshRevision = revision;
|
||||
navMeshRevision = std::move(revision);
|
||||
}
|
||||
if (recastMesh && mSettings.get().mEnableWriteRecastMeshToFile)
|
||||
writeToFile(*recastMesh,
|
||||
|
@ -76,4 +76,13 @@ namespace DetourNavigator
|
||||
return {};
|
||||
return TilesPositionsRange{ TilePosition(beginX, beginY), TilePosition(endX, endY) };
|
||||
}
|
||||
|
||||
TilesPositionsRange getUnion(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept
|
||||
{
|
||||
const int beginX = std::min(a.mBegin.x(), b.mBegin.x());
|
||||
const int endX = std::max(a.mEnd.x(), b.mEnd.x());
|
||||
const int beginY = std::min(a.mBegin.y(), b.mBegin.y());
|
||||
const int endY = std::max(a.mEnd.y(), b.mEnd.y());
|
||||
return TilesPositionsRange{ .mBegin = TilePosition(beginX, beginY), .mEnd = TilePosition(endX, endY) };
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,8 @@ namespace DetourNavigator
|
||||
}
|
||||
|
||||
TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept;
|
||||
|
||||
TilesPositionsRange getUnion(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -166,6 +166,7 @@ namespace DetourNavigator
|
||||
return;
|
||||
mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision();
|
||||
mPlayerTile = playerTile;
|
||||
mRecastMeshManager.setRange(makeRange(playerTile, mSettings.mMaxTilesNumber), guard);
|
||||
const auto changedTiles = mRecastMeshManager.takeChangedTiles(guard);
|
||||
const TilesPositionsRange range = mRecastMeshManager.getLimitedObjectsRange();
|
||||
for (const auto& [agentBounds, cached] : mCache)
|
||||
|
@ -54,6 +54,15 @@ namespace DetourNavigator
|
||||
private:
|
||||
const std::optional<std::unique_lock<Mutex>> mImpl;
|
||||
};
|
||||
|
||||
TilesPositionsRange getIndexRange(const auto& index)
|
||||
{
|
||||
const auto bounds = index.bounds();
|
||||
return TilesPositionsRange{
|
||||
.mBegin = makeTilePosition(bounds.min_corner()),
|
||||
.mEnd = makeTilePosition(bounds.max_corner()) + TilePosition(1, 1),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings)
|
||||
@ -104,14 +113,28 @@ namespace DetourNavigator
|
||||
|
||||
TilesPositionsRange TileCachedRecastMeshManager::getLimitedObjectsRange() const
|
||||
{
|
||||
if (mObjects.empty())
|
||||
return {};
|
||||
const auto bounds = mObjectIndex.bounds();
|
||||
const TilesPositionsRange objectsRange{
|
||||
.mBegin = makeTilePosition(bounds.min_corner()),
|
||||
.mEnd = makeTilePosition(bounds.max_corner()) + TilePosition(1, 1),
|
||||
};
|
||||
return getIntersection(mRange, objectsRange);
|
||||
std::optional<TilesPositionsRange> result;
|
||||
if (!mWater.empty())
|
||||
result = getIndexRange(mWaterIndex);
|
||||
if (!mHeightfields.empty())
|
||||
{
|
||||
const TilesPositionsRange range = getIndexRange(mHeightfieldIndex);
|
||||
if (result.has_value())
|
||||
result = getUnion(*result, range);
|
||||
else
|
||||
result = range;
|
||||
}
|
||||
if (!mObjects.empty())
|
||||
{
|
||||
const TilesPositionsRange range = getIndexRange(mObjectIndex);
|
||||
if (result.has_value())
|
||||
result = getUnion(*result, range);
|
||||
else
|
||||
result = range;
|
||||
}
|
||||
if (result.has_value())
|
||||
return getIntersection(mRange, *result);
|
||||
return {};
|
||||
}
|
||||
|
||||
void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace, const UpdateGuard* guard)
|
||||
|
10
components/esm/decompose.hpp
Normal file
10
components/esm/decompose.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef OPENMW_COMPONENTS_ESM_DECOMPOSE_H
|
||||
#define OPENMW_COMPONENTS_ESM_DECOMPOSE_H
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
template <class T>
|
||||
void decompose(T&& value, const auto& apply) = delete;
|
||||
}
|
||||
|
||||
#endif
|
@ -3,32 +3,58 @@
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
#include <components/misc/concepts.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
template <Misc::SameAsWithoutCvref<AiSequence::AiWanderData> T>
|
||||
void decompose(T&& v, const auto& f)
|
||||
{
|
||||
f(v.mDistance, v.mDuration, v.mTimeOfDay, v.mIdle, v.mShouldRepeat);
|
||||
}
|
||||
|
||||
template <Misc::SameAsWithoutCvref<AiSequence::AiWanderDuration> T>
|
||||
void decompose(T&& v, const auto& f)
|
||||
{
|
||||
std::uint32_t unused = 0;
|
||||
f(v.mRemainingDuration, unused);
|
||||
}
|
||||
|
||||
template <Misc::SameAsWithoutCvref<AiSequence::AiTravelData> T>
|
||||
void decompose(T&& v, const auto& f)
|
||||
{
|
||||
f(v.mX, v.mY, v.mZ);
|
||||
}
|
||||
|
||||
template <Misc::SameAsWithoutCvref<AiSequence::AiEscortData> T>
|
||||
void decompose(T&& v, const auto& f)
|
||||
{
|
||||
f(v.mX, v.mY, v.mZ, v.mDuration);
|
||||
}
|
||||
|
||||
namespace AiSequence
|
||||
{
|
||||
|
||||
void AiWander::load(ESMReader& esm)
|
||||
{
|
||||
esm.getHNT("DATA", mData.mDistance, mData.mDuration, mData.mTimeOfDay, mData.mIdle, mData.mShouldRepeat);
|
||||
esm.getHNT("STAR", mDurationData.mRemainingDuration, mDurationData.unused); // was mStartTime
|
||||
esm.getNamedComposite("DATA", mData);
|
||||
esm.getNamedComposite("STAR", mDurationData); // was mStartTime
|
||||
mStoredInitialActorPosition = esm.getHNOT("POS_", mInitialActorPosition.mValues);
|
||||
}
|
||||
|
||||
void AiWander::save(ESMWriter& esm) const
|
||||
{
|
||||
esm.writeHNT("DATA", mData);
|
||||
esm.writeHNT("STAR", mDurationData);
|
||||
esm.writeNamedComposite("DATA", mData);
|
||||
esm.writeNamedComposite("STAR", mDurationData); // was mStartTime
|
||||
if (mStoredInitialActorPosition)
|
||||
esm.writeHNT("POS_", mInitialActorPosition);
|
||||
esm.writeHNT("POS_", mInitialActorPosition.mValues);
|
||||
}
|
||||
|
||||
void AiTravel::load(ESMReader& esm)
|
||||
{
|
||||
esm.getHNT("DATA", mData.mX, mData.mY, mData.mZ);
|
||||
esm.getNamedComposite("DATA", mData);
|
||||
esm.getHNT(mHidden, "HIDD");
|
||||
mRepeat = false;
|
||||
esm.getHNOT(mRepeat, "REPT");
|
||||
@ -36,7 +62,7 @@ namespace ESM
|
||||
|
||||
void AiTravel::save(ESMWriter& esm) const
|
||||
{
|
||||
esm.writeHNT("DATA", mData);
|
||||
esm.writeNamedComposite("DATA", mData);
|
||||
esm.writeHNT("HIDD", mHidden);
|
||||
if (mRepeat)
|
||||
esm.writeHNT("REPT", mRepeat);
|
||||
@ -44,7 +70,7 @@ namespace ESM
|
||||
|
||||
void AiEscort::load(ESMReader& esm)
|
||||
{
|
||||
esm.getHNT("DATA", mData.mX, mData.mY, mData.mZ, mData.mDuration);
|
||||
esm.getNamedComposite("DATA", mData);
|
||||
mTargetId = esm.getHNRefId("TARG");
|
||||
mTargetActorId = -1;
|
||||
esm.getHNOT(mTargetActorId, "TAID");
|
||||
@ -64,7 +90,7 @@ namespace ESM
|
||||
|
||||
void AiEscort::save(ESMWriter& esm) const
|
||||
{
|
||||
esm.writeHNT("DATA", mData);
|
||||
esm.writeNamedComposite("DATA", mData);
|
||||
esm.writeHNRefId("TARG", mTargetId);
|
||||
esm.writeHNT("TAID", mTargetActorId);
|
||||
esm.writeHNT("DURA", mRemainingDuration);
|
||||
@ -76,7 +102,7 @@ namespace ESM
|
||||
|
||||
void AiFollow::load(ESMReader& esm)
|
||||
{
|
||||
esm.getHNT("DATA", mData.mX, mData.mY, mData.mZ, mData.mDuration);
|
||||
esm.getNamedComposite("DATA", mData);
|
||||
mTargetId = esm.getHNRefId("TARG");
|
||||
mTargetActorId = -1;
|
||||
esm.getHNOT(mTargetActorId, "TAID");
|
||||
@ -101,7 +127,7 @@ namespace ESM
|
||||
|
||||
void AiFollow::save(ESMWriter& esm) const
|
||||
{
|
||||
esm.writeHNT("DATA", mData);
|
||||
esm.writeNamedComposite("DATA", mData);
|
||||
esm.writeHNRefId("TARG", mTargetId);
|
||||
esm.writeHNT("TAID", mTargetActorId);
|
||||
esm.writeHNT("DURA", mRemainingDuration);
|
||||
|
@ -36,32 +36,31 @@ namespace ESM
|
||||
virtual ~AiPackage() {}
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct AiWanderData
|
||||
{
|
||||
int16_t mDistance;
|
||||
int16_t mDuration;
|
||||
unsigned char mTimeOfDay;
|
||||
unsigned char mIdle[8];
|
||||
unsigned char mShouldRepeat;
|
||||
std::uint8_t mTimeOfDay;
|
||||
std::uint8_t mIdle[8];
|
||||
std::uint8_t mShouldRepeat;
|
||||
};
|
||||
|
||||
struct AiWanderDuration
|
||||
{
|
||||
float mRemainingDuration;
|
||||
int32_t unused;
|
||||
};
|
||||
|
||||
struct AiTravelData
|
||||
{
|
||||
float mX, mY, mZ;
|
||||
};
|
||||
|
||||
struct AiEscortData
|
||||
{
|
||||
float mX, mY, mZ;
|
||||
int16_t mDuration;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
struct AiWander : AiPackage
|
||||
{
|
||||
AiWanderData mData;
|
||||
|
@ -12,8 +12,10 @@
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
#include "components/esm/decompose.hpp"
|
||||
#include "components/esm/esmcommon.hpp"
|
||||
#include "components/esm/refid.hpp"
|
||||
|
||||
#include "loadtes3.hpp"
|
||||
|
||||
namespace ESM
|
||||
@ -177,6 +179,16 @@ namespace ESM
|
||||
(getT(args), ...);
|
||||
}
|
||||
|
||||
void getNamedComposite(NAME name, auto& value)
|
||||
{
|
||||
decompose(value, [&](auto&... args) { getHNT(name, args...); });
|
||||
}
|
||||
|
||||
void getComposite(auto& value)
|
||||
{
|
||||
decompose(value, [&](auto&... args) { (getT(args), ...); });
|
||||
}
|
||||
|
||||
template <typename T, typename = std::enable_if_t<IsReadable<T>>>
|
||||
void skipHT()
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <list>
|
||||
#include <type_traits>
|
||||
|
||||
#include "components/esm/decompose.hpp"
|
||||
#include "components/esm/esmcommon.hpp"
|
||||
#include "components/esm/refid.hpp"
|
||||
|
||||
@ -121,6 +122,20 @@ namespace ESM
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
void writeNamedComposite(NAME name, const auto& value)
|
||||
{
|
||||
decompose(value, [&](const auto&... args) {
|
||||
startSubRecord(name);
|
||||
(writeT(args), ...);
|
||||
endRecord(name);
|
||||
});
|
||||
}
|
||||
|
||||
void writeComposite(const auto& value)
|
||||
{
|
||||
decompose(value, [&](const auto&... args) { (writeT(args), ...); });
|
||||
}
|
||||
|
||||
// Prevent using writeHNT with strings. This already happened by accident and results in
|
||||
// state being discarded without any error on writing or reading it. :(
|
||||
// writeHNString and friends must be used instead.
|
||||
@ -132,7 +147,7 @@ namespace ESM
|
||||
void writeHNT(NAME name, const T (&data)[size], int) = delete;
|
||||
|
||||
template <typename T>
|
||||
void writeHNT(NAME name, const T& data, int size)
|
||||
void writeHNT(NAME name, const T& data, std::size_t size)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeT(data, size);
|
||||
|
@ -74,7 +74,7 @@ namespace ESM
|
||||
esm.getHNT(multiplier, "MULT");
|
||||
params.emplace_back(rand, multiplier);
|
||||
}
|
||||
mPermanentMagicEffectMagnitudes[id] = params;
|
||||
mPermanentMagicEffectMagnitudes[id] = std::move(params);
|
||||
}
|
||||
|
||||
while (esm.isNextSub("EQUI"))
|
||||
|
@ -4,12 +4,19 @@
|
||||
#include <sstream>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/concepts.hpp>
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
template <Misc::SameAsWithoutCvref<Script::SCHDstruct> T>
|
||||
void decompose(T&& v, const auto& f)
|
||||
{
|
||||
f(v.mNumShorts, v.mNumLongs, v.mNumFloats, v.mScriptDataSize, v.mStringTableSize);
|
||||
}
|
||||
|
||||
void Script::loadSCVR(ESMReader& esm)
|
||||
{
|
||||
uint32_t s = mData.mStringTableSize;
|
||||
@ -99,11 +106,7 @@ namespace ESM
|
||||
{
|
||||
esm.getSubHeader();
|
||||
mId = esm.getMaybeFixedRefIdSize(32);
|
||||
esm.getT(mData.mNumShorts);
|
||||
esm.getT(mData.mNumLongs);
|
||||
esm.getT(mData.mNumFloats);
|
||||
esm.getT(mData.mScriptDataSize);
|
||||
esm.getT(mData.mStringTableSize);
|
||||
esm.getComposite(mData);
|
||||
|
||||
hasHeader = true;
|
||||
break;
|
||||
@ -157,7 +160,7 @@ namespace ESM
|
||||
|
||||
esm.startSubRecord("SCHD");
|
||||
esm.writeMaybeFixedSizeRefId(mId, 32);
|
||||
esm.writeT(mData, 20);
|
||||
esm.writeComposite(mData);
|
||||
esm.endRecord("SCHD");
|
||||
|
||||
if (isDeleted)
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
#include "../misc/algorithm.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
void SavedGame::load(ESMReader& esm)
|
||||
@ -67,7 +69,9 @@ namespace ESM
|
||||
std::vector<std::string_view> missingFiles;
|
||||
for (const std::string& contentFile : mContentFiles)
|
||||
{
|
||||
if (std::find(allContentFiles.begin(), allContentFiles.end(), contentFile) == allContentFiles.end())
|
||||
auto it = std::find_if(allContentFiles.begin(), allContentFiles.end(),
|
||||
[&](const std::string& file) { return Misc::StringUtils::ciEqual(file, contentFile); });
|
||||
if (it == allContentFiles.end())
|
||||
{
|
||||
missingFiles.emplace_back(contentFile);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user