mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-19 08:41:35 +00:00
Merge branch 'master' of gitlab.com:openmw/openmw into lua_class_data
This commit is contained in:
commit
10030a55e0
165 changed files with 1373 additions and 921 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,6 +28,7 @@ Doxygen
|
||||||
.idea
|
.idea
|
||||||
cmake-build-*
|
cmake-build-*
|
||||||
files/windows/*.aps
|
files/windows/*.aps
|
||||||
|
.cache/clangd
|
||||||
## qt-creator
|
## qt-creator
|
||||||
CMakeLists.txt.user*
|
CMakeLists.txt.user*
|
||||||
.vs
|
.vs
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
Bug #4754: Stack of ammunition cannot be equipped partially
|
Bug #4754: Stack of ammunition cannot be equipped partially
|
||||||
Bug #4816: GetWeaponDrawn returns 1 before weapon is attached
|
Bug #4816: GetWeaponDrawn returns 1 before weapon is attached
|
||||||
Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses
|
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 #5129: Stuttering animation on Centurion Archer
|
Bug #5129: Stuttering animation on Centurion Archer
|
||||||
Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place
|
Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place
|
||||||
Bug #5371: Keyframe animation tracks are used for any file that begins with an X
|
Bug #5371: Keyframe animation tracks are used for any file that begins with an X
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load
|
Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load
|
||||||
Bug #6025: Subrecords cannot overlap records
|
Bug #6025: Subrecords cannot overlap records
|
||||||
Bug #6027: Collisionshape becomes spiderweb-like when the mesh is too complex
|
Bug #6027: Collisionshape becomes spiderweb-like when the mesh is too complex
|
||||||
|
Bug #6190: Unintuitive sun specularity time of day dependence
|
||||||
Bug #6222: global map cell size can crash openmw if set to too high a value
|
Bug #6222: global map cell size can crash openmw if set to too high a value
|
||||||
Bug #6313: Followers with high Fight can turn hostile
|
Bug #6313: Followers with high Fight can turn hostile
|
||||||
Bug #6427: Enemy health bar disappears before damaging effect ends
|
Bug #6427: Enemy health bar disappears before damaging effect ends
|
||||||
|
@ -68,6 +70,7 @@
|
||||||
Bug #7298: Water ripples from projectiles sometimes are not spawned
|
Bug #7298: Water ripples from projectiles sometimes are not spawned
|
||||||
Bug #7307: Alchemy "Magic Effect" search string does not match on tool tip for effects related to attributes
|
Bug #7307: Alchemy "Magic Effect" search string does not match on tool tip for effects related to attributes
|
||||||
Bug #7322: Shadows don't cover groundcover depending on the view angle and perspective with compute scene bounds = primitives
|
Bug #7322: Shadows don't cover groundcover depending on the view angle and perspective with compute scene bounds = primitives
|
||||||
|
Bug #7380: NiZBufferProperty issue
|
||||||
Bug #7413: Generated wilderness cells don't spawn fish
|
Bug #7413: Generated wilderness cells don't spawn fish
|
||||||
Bug #7415: Unbreakable lock discrepancies
|
Bug #7415: Unbreakable lock discrepancies
|
||||||
Bug #7428: AutoCalc flag is not used to calculate enchantment costs
|
Bug #7428: AutoCalc flag is not used to calculate enchantment costs
|
||||||
|
@ -90,11 +93,15 @@
|
||||||
Bug #7639: NPCs don't use hand-to-hand if their other melee skills were damaged during combat
|
Bug #7639: NPCs don't use hand-to-hand if their other melee skills were damaged during combat
|
||||||
Bug #7642: Items in repair and recharge menus aren't sorted alphabetically
|
Bug #7642: Items in repair and recharge menus aren't sorted alphabetically
|
||||||
Bug #7647: NPC walk cycle bugs after greeting player
|
Bug #7647: NPC walk cycle bugs after greeting player
|
||||||
|
Bug #7654: Tooltips for enchantments with invalid effects cause crashes
|
||||||
Bug #7660: Some inconsistencies regarding Invisibility breaking
|
Bug #7660: Some inconsistencies regarding Invisibility breaking
|
||||||
|
Bug #7675: Successful lock spell doesn't produce a sound
|
||||||
|
Bug #7679: Scene luminance value flashes when toggling shaders
|
||||||
Feature #3537: Shader-based water ripples
|
Feature #3537: Shader-based water ripples
|
||||||
Feature #5492: Let rain and snow collide with statics
|
Feature #5492: Let rain and snow collide with statics
|
||||||
Feature #6149: Dehardcode Lua API_REVISION
|
Feature #6149: Dehardcode Lua API_REVISION
|
||||||
Feature #6152: Playing music via lua scripts
|
Feature #6152: Playing music via lua scripts
|
||||||
|
Feature #6188: Specular lighting from point light sources
|
||||||
Feature #6447: Add LOD support to Object Paging
|
Feature #6447: Add LOD support to Object Paging
|
||||||
Feature #6491: Add support for Qt6
|
Feature #6491: Add support for Qt6
|
||||||
Feature #6556: Lua API for sounds
|
Feature #6556: Lua API for sounds
|
||||||
|
|
|
@ -86,7 +86,7 @@ declare -rA GROUPED_DEPS=(
|
||||||
libswresample3
|
libswresample3
|
||||||
libswscale5
|
libswscale5
|
||||||
libtinyxml2.6.2v5
|
libtinyxml2.6.2v5
|
||||||
libyaml-cpp0.7
|
libyaml-cpp0.8
|
||||||
python3-pip
|
python3-pip
|
||||||
xvfb
|
xvfb
|
||||||
"
|
"
|
||||||
|
@ -125,3 +125,4 @@ add-apt-repository -y ppa:openmw/openmw
|
||||||
add-apt-repository -y ppa:openmw/openmw-daily
|
add-apt-repository -y ppa:openmw/openmw-daily
|
||||||
add-apt-repository -y ppa:openmw/staging
|
add-apt-repository -y ppa:openmw/staging
|
||||||
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" >/dev/null
|
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" >/dev/null
|
||||||
|
apt list --installed
|
||||||
|
|
|
@ -1204,7 +1204,8 @@ namespace EsmTool
|
||||||
std::array<std::string_view, 10> weathers
|
std::array<std::string_view, 10> weathers
|
||||||
= { "Clear", "Cloudy", "Fog", "Overcast", "Rain", "Thunder", "Ash", "Blight", "Snow", "Blizzard" };
|
= { "Clear", "Cloudy", "Fog", "Overcast", "Rain", "Thunder", "Ash", "Blight", "Snow", "Blizzard" };
|
||||||
for (size_t i = 0; i < weathers.size(); ++i)
|
for (size_t i = 0; i < weathers.size(); ++i)
|
||||||
std::cout << " " << weathers[i] << ": " << mData.mData.mProbabilities[i] << std::endl;
|
std::cout << " " << weathers[i] << ": " << static_cast<unsigned>(mData.mData.mProbabilities[i])
|
||||||
|
<< std::endl;
|
||||||
std::cout << " Map Color: " << mData.mMapColor << std::endl;
|
std::cout << " Map Color: " << mData.mMapColor << std::endl;
|
||||||
if (!mData.mSleepList.empty())
|
if (!mData.mSleepList.empty())
|
||||||
std::cout << " Sleep List: " << mData.mSleepList << std::endl;
|
std::cout << " Sleep List: " << mData.mSleepList << std::endl;
|
||||||
|
|
|
@ -9,15 +9,14 @@ namespace ESSImport
|
||||||
|
|
||||||
void convertInventory(const Inventory& inventory, ESM::InventoryState& state)
|
void convertInventory(const Inventory& inventory, ESM::InventoryState& state)
|
||||||
{
|
{
|
||||||
int index = 0;
|
uint32_t index = 0;
|
||||||
for (const auto& item : inventory.mItems)
|
for (const auto& item : inventory.mItems)
|
||||||
{
|
{
|
||||||
ESM::ObjectState objstate;
|
ESM::ObjectState objstate;
|
||||||
objstate.blank();
|
objstate.blank();
|
||||||
objstate.mRef = item;
|
objstate.mRef = item;
|
||||||
objstate.mRef.mRefID = ESM::RefId::stringRefId(item.mId);
|
objstate.mRef.mRefID = ESM::RefId::stringRefId(item.mId);
|
||||||
objstate.mCount = std::abs(item.mCount); // restocking items have negative count in the savefile
|
objstate.mCount = item.mCount;
|
||||||
// openmw handles them differently, so no need to set any flags
|
|
||||||
state.mItems.push_back(objstate);
|
state.mItems.push_back(objstate);
|
||||||
if (item.mRelativeEquipmentSlot != -1)
|
if (item.mRelativeEquipmentSlot != -1)
|
||||||
// Note we should really write the absolute slot here, which we do not know about
|
// Note we should really write the absolute slot here, which we do not know about
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include <components/files/qtconversion.hpp>
|
#include <components/files/qtconversion.hpp>
|
||||||
#include <components/misc/strings/conversion.hpp>
|
#include <components/misc/strings/conversion.hpp>
|
||||||
#include <components/navmeshtool/protocol.hpp>
|
#include <components/navmeshtool/protocol.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/values.hpp>
|
||||||
#include <components/vfs/bsaarchive.hpp>
|
#include <components/vfs/bsaarchive.hpp>
|
||||||
|
|
||||||
#include "utils/profilescombobox.hpp"
|
#include "utils/profilescombobox.hpp"
|
||||||
|
@ -123,7 +123,7 @@ namespace Launcher
|
||||||
|
|
||||||
int getMaxNavMeshDbFileSizeMiB()
|
int getMaxNavMeshDbFileSizeMiB()
|
||||||
{
|
{
|
||||||
return Settings::Manager::getUInt64("max navmeshdb file size", "Navigator") / (1024 * 1024);
|
return Settings::navigator().mMaxNavmeshdbFileSize / (1024 * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<QString> findFirstPath(const QStringList& directories, const QString& fileName)
|
std::optional<QString> findFirstPath(const QStringList& directories, const QString& fileName)
|
||||||
|
@ -359,9 +359,8 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
|
||||||
|
|
||||||
void Launcher::DataFilesPage::saveSettings(const QString& profile)
|
void Launcher::DataFilesPage::saveSettings(const QString& profile)
|
||||||
{
|
{
|
||||||
if (const int value = ui.navMeshMaxSizeSpinBox->value(); value != getMaxNavMeshDbFileSizeMiB())
|
Settings::navigator().mMaxNavmeshdbFileSize.set(
|
||||||
Settings::Manager::setUInt64(
|
static_cast<std::uint64_t>(std::max(0, ui.navMeshMaxSizeSpinBox->value())) * 1024 * 1024);
|
||||||
"max navmeshdb file size", "Navigator", static_cast<std::uint64_t>(std::max(0, value)) * 1024 * 1024);
|
|
||||||
|
|
||||||
QString profileName = profile;
|
QString profileName = profile;
|
||||||
|
|
||||||
|
|
|
@ -158,32 +158,32 @@ bool Launcher::GraphicsPage::loadSettings()
|
||||||
lightingMethodComboBox->setCurrentIndex(lightingMethod);
|
lightingMethodComboBox->setCurrentIndex(lightingMethod);
|
||||||
|
|
||||||
// Shadows
|
// Shadows
|
||||||
if (Settings::Manager::getBool("actor shadows", "Shadows"))
|
if (Settings::shadows().mActorShadows)
|
||||||
actorShadowsCheckBox->setCheckState(Qt::Checked);
|
actorShadowsCheckBox->setCheckState(Qt::Checked);
|
||||||
if (Settings::Manager::getBool("player shadows", "Shadows"))
|
if (Settings::shadows().mPlayerShadows)
|
||||||
playerShadowsCheckBox->setCheckState(Qt::Checked);
|
playerShadowsCheckBox->setCheckState(Qt::Checked);
|
||||||
if (Settings::Manager::getBool("terrain shadows", "Shadows"))
|
if (Settings::shadows().mTerrainShadows)
|
||||||
terrainShadowsCheckBox->setCheckState(Qt::Checked);
|
terrainShadowsCheckBox->setCheckState(Qt::Checked);
|
||||||
if (Settings::Manager::getBool("object shadows", "Shadows"))
|
if (Settings::shadows().mObjectShadows)
|
||||||
objectShadowsCheckBox->setCheckState(Qt::Checked);
|
objectShadowsCheckBox->setCheckState(Qt::Checked);
|
||||||
if (Settings::Manager::getBool("enable indoor shadows", "Shadows"))
|
if (Settings::shadows().mEnableIndoorShadows)
|
||||||
indoorShadowsCheckBox->setCheckState(Qt::Checked);
|
indoorShadowsCheckBox->setCheckState(Qt::Checked);
|
||||||
|
|
||||||
shadowComputeSceneBoundsComboBox->setCurrentIndex(shadowComputeSceneBoundsComboBox->findText(
|
shadowComputeSceneBoundsComboBox->setCurrentIndex(
|
||||||
QString(tr(Settings::Manager::getString("compute scene bounds", "Shadows").c_str()))));
|
shadowComputeSceneBoundsComboBox->findText(QString(tr(Settings::shadows().mComputeSceneBounds.get().c_str()))));
|
||||||
|
|
||||||
int shadowDistLimit = Settings::Manager::getInt("maximum shadow map distance", "Shadows");
|
const int shadowDistLimit = Settings::shadows().mMaximumShadowMapDistance;
|
||||||
if (shadowDistLimit > 0)
|
if (shadowDistLimit > 0)
|
||||||
{
|
{
|
||||||
shadowDistanceCheckBox->setCheckState(Qt::Checked);
|
shadowDistanceCheckBox->setCheckState(Qt::Checked);
|
||||||
shadowDistanceSpinBox->setValue(shadowDistLimit);
|
shadowDistanceSpinBox->setValue(shadowDistLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
float shadowFadeStart = Settings::Manager::getFloat("shadow fade start", "Shadows");
|
const float shadowFadeStart = Settings::shadows().mShadowFadeStart;
|
||||||
if (shadowFadeStart != 0)
|
if (shadowFadeStart != 0)
|
||||||
fadeStartSpinBox->setValue(shadowFadeStart);
|
fadeStartSpinBox->setValue(shadowFadeStart);
|
||||||
|
|
||||||
int shadowRes = Settings::Manager::getInt("shadow map resolution", "Shadows");
|
const int shadowRes = Settings::shadows().mShadowMapResolution;
|
||||||
int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes));
|
int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes));
|
||||||
if (shadowResIndex != -1)
|
if (shadowResIndex != -1)
|
||||||
shadowResolutionComboBox->setCurrentIndex(shadowResIndex);
|
shadowResolutionComboBox->setCurrentIndex(shadowResIndex);
|
||||||
|
@ -240,55 +240,36 @@ void Launcher::GraphicsPage::saveSettings()
|
||||||
Settings::shaders().mLightingMethod.set(lightingMethodMap[lightingMethodComboBox->currentIndex()]);
|
Settings::shaders().mLightingMethod.set(lightingMethodMap[lightingMethodComboBox->currentIndex()]);
|
||||||
|
|
||||||
// Shadows
|
// Shadows
|
||||||
int cShadowDist = shadowDistanceCheckBox->checkState() != Qt::Unchecked ? shadowDistanceSpinBox->value() : 0;
|
const int cShadowDist = shadowDistanceCheckBox->checkState() != Qt::Unchecked ? shadowDistanceSpinBox->value() : 0;
|
||||||
if (Settings::Manager::getInt("maximum shadow map distance", "Shadows") != cShadowDist)
|
Settings::shadows().mMaximumShadowMapDistance.set(cShadowDist);
|
||||||
Settings::Manager::setInt("maximum shadow map distance", "Shadows", cShadowDist);
|
const float cFadeStart = fadeStartSpinBox->value();
|
||||||
float cFadeStart = fadeStartSpinBox->value();
|
if (cShadowDist > 0)
|
||||||
if (cShadowDist > 0 && Settings::Manager::getFloat("shadow fade start", "Shadows") != cFadeStart)
|
Settings::shadows().mShadowFadeStart.set(cFadeStart);
|
||||||
Settings::Manager::setFloat("shadow fade start", "Shadows", cFadeStart);
|
|
||||||
|
|
||||||
bool cActorShadows = actorShadowsCheckBox->checkState();
|
const bool cActorShadows = actorShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||||
bool cObjectShadows = objectShadowsCheckBox->checkState();
|
const bool cObjectShadows = objectShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||||
bool cTerrainShadows = terrainShadowsCheckBox->checkState();
|
const bool cTerrainShadows = terrainShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||||
bool cPlayerShadows = playerShadowsCheckBox->checkState();
|
const bool cPlayerShadows = playerShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||||
if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows)
|
if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows)
|
||||||
{
|
{
|
||||||
if (!Settings::Manager::getBool("enable shadows", "Shadows"))
|
Settings::shadows().mEnableShadows.set(true);
|
||||||
Settings::Manager::setBool("enable shadows", "Shadows", true);
|
Settings::shadows().mActorShadows.set(cActorShadows);
|
||||||
if (Settings::Manager::getBool("actor shadows", "Shadows") != cActorShadows)
|
Settings::shadows().mPlayerShadows.set(cPlayerShadows);
|
||||||
Settings::Manager::setBool("actor shadows", "Shadows", cActorShadows);
|
Settings::shadows().mObjectShadows.set(cObjectShadows);
|
||||||
if (Settings::Manager::getBool("player shadows", "Shadows") != cPlayerShadows)
|
Settings::shadows().mTerrainShadows.set(cTerrainShadows);
|
||||||
Settings::Manager::setBool("player shadows", "Shadows", cPlayerShadows);
|
|
||||||
if (Settings::Manager::getBool("object shadows", "Shadows") != cObjectShadows)
|
|
||||||
Settings::Manager::setBool("object shadows", "Shadows", cObjectShadows);
|
|
||||||
if (Settings::Manager::getBool("terrain shadows", "Shadows") != cTerrainShadows)
|
|
||||||
Settings::Manager::setBool("terrain shadows", "Shadows", cTerrainShadows);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Settings::Manager::getBool("enable shadows", "Shadows"))
|
Settings::shadows().mEnableShadows.set(false);
|
||||||
Settings::Manager::setBool("enable shadows", "Shadows", false);
|
Settings::shadows().mActorShadows.set(false);
|
||||||
if (Settings::Manager::getBool("actor shadows", "Shadows"))
|
Settings::shadows().mPlayerShadows.set(false);
|
||||||
Settings::Manager::setBool("actor shadows", "Shadows", false);
|
Settings::shadows().mObjectShadows.set(false);
|
||||||
if (Settings::Manager::getBool("player shadows", "Shadows"))
|
Settings::shadows().mTerrainShadows.set(false);
|
||||||
Settings::Manager::setBool("player shadows", "Shadows", false);
|
|
||||||
if (Settings::Manager::getBool("object shadows", "Shadows"))
|
|
||||||
Settings::Manager::setBool("object shadows", "Shadows", false);
|
|
||||||
if (Settings::Manager::getBool("terrain shadows", "Shadows"))
|
|
||||||
Settings::Manager::setBool("terrain shadows", "Shadows", false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIndoorShadows = indoorShadowsCheckBox->checkState();
|
Settings::shadows().mEnableIndoorShadows.set(indoorShadowsCheckBox->checkState() != Qt::Unchecked);
|
||||||
if (Settings::Manager::getBool("enable indoor shadows", "Shadows") != cIndoorShadows)
|
Settings::shadows().mShadowMapResolution.set(shadowResolutionComboBox->currentText().toInt());
|
||||||
Settings::Manager::setBool("enable indoor shadows", "Shadows", cIndoorShadows);
|
Settings::shadows().mComputeSceneBounds.set(shadowComputeSceneBoundsComboBox->currentText().toStdString());
|
||||||
|
|
||||||
int cShadowRes = shadowResolutionComboBox->currentText().toInt();
|
|
||||||
if (cShadowRes != Settings::Manager::getInt("shadow map resolution", "Shadows"))
|
|
||||||
Settings::Manager::setInt("shadow map resolution", "Shadows", cShadowRes);
|
|
||||||
|
|
||||||
auto cComputeSceneBounds = shadowComputeSceneBoundsComboBox->currentText().toStdString();
|
|
||||||
if (cComputeSceneBounds != Settings::Manager::getString("compute scene bounds", "Shadows"))
|
|
||||||
Settings::Manager::setString("compute scene bounds", "Shadows", cComputeSceneBounds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
|
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
|
||||||
|
|
|
@ -191,6 +191,7 @@ bool Launcher::SettingsPage::loadSettings()
|
||||||
}
|
}
|
||||||
loadSettingBool(Settings::game().mTurnToMovementDirection, *turnToMovementDirectionCheckBox);
|
loadSettingBool(Settings::game().mTurnToMovementDirection, *turnToMovementDirectionCheckBox);
|
||||||
loadSettingBool(Settings::game().mSmoothMovement, *smoothMovementCheckBox);
|
loadSettingBool(Settings::game().mSmoothMovement, *smoothMovementCheckBox);
|
||||||
|
loadSettingBool(Settings::game().mPlayerMovementIgnoresAnimation, *playerMovementIgnoresAnimationCheckBox);
|
||||||
|
|
||||||
distantLandCheckBox->setCheckState(
|
distantLandCheckBox->setCheckState(
|
||||||
Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging ? Qt::Checked : Qt::Unchecked);
|
Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging ? Qt::Checked : Qt::Unchecked);
|
||||||
|
@ -338,6 +339,7 @@ void Launcher::SettingsPage::saveSettings()
|
||||||
saveSettingBool(*shieldSheathingCheckBox, Settings::game().mShieldSheathing);
|
saveSettingBool(*shieldSheathingCheckBox, Settings::game().mShieldSheathing);
|
||||||
saveSettingBool(*turnToMovementDirectionCheckBox, Settings::game().mTurnToMovementDirection);
|
saveSettingBool(*turnToMovementDirectionCheckBox, Settings::game().mTurnToMovementDirection);
|
||||||
saveSettingBool(*smoothMovementCheckBox, Settings::game().mSmoothMovement);
|
saveSettingBool(*smoothMovementCheckBox, Settings::game().mSmoothMovement);
|
||||||
|
saveSettingBool(*playerMovementIgnoresAnimationCheckBox, Settings::game().mPlayerMovementIgnoresAnimation);
|
||||||
|
|
||||||
const bool wantDistantLand = distantLandCheckBox->checkState() == Qt::Checked;
|
const bool wantDistantLand = distantLandCheckBox->checkState() == Qt::Checked;
|
||||||
if (wantDistantLand != (Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging))
|
if (wantDistantLand != (Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging))
|
||||||
|
|
|
@ -318,9 +318,14 @@ bool OMW::Engine::frame(float frametime)
|
||||||
mViewer->eventTraversal();
|
mViewer->eventTraversal();
|
||||||
mViewer->updateTraversal();
|
mViewer->updateTraversal();
|
||||||
|
|
||||||
|
// update GUI by world data
|
||||||
{
|
{
|
||||||
ScopedProfile<UserStatsType::WindowManager> profile(frameStart, frameNumber, *timer, *stats);
|
ScopedProfile<UserStatsType::WindowManager> profile(frameStart, frameNumber, *timer, *stats);
|
||||||
mWorld->updateWindowManager();
|
|
||||||
|
if (mStateManager->getState() != MWBase::StateManager::State_NoGame)
|
||||||
|
{
|
||||||
|
mWorld->updateWindowManager();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mLuaWorker->allowUpdate(); // if there is a separate Lua thread, it starts the update now
|
mLuaWorker->allowUpdate(); // if there is a separate Lua thread, it starts the update now
|
||||||
|
|
|
@ -34,11 +34,26 @@ namespace MWClass
|
||||||
|
|
||||||
static const ESM4::Npc* chooseTemplate(const std::vector<const ESM4::Npc*>& recs, uint16_t flag)
|
static const ESM4::Npc* chooseTemplate(const std::vector<const ESM4::Npc*>& recs, uint16_t flag)
|
||||||
{
|
{
|
||||||
// In case of FO3 the function may return nullptr that will lead to "ESM4 NPC traits not found"
|
|
||||||
// exception and the NPC will not be added to the scene. But in any way it shouldn't cause a crash.
|
|
||||||
for (const auto* rec : recs)
|
for (const auto* rec : recs)
|
||||||
if (rec->mIsTES4 || rec->mIsFONV || !(rec->mBaseConfig.tes5.templateFlags & flag))
|
{
|
||||||
|
if (rec->mIsTES4)
|
||||||
return rec;
|
return rec;
|
||||||
|
else if (rec->mIsFONV)
|
||||||
|
{
|
||||||
|
// TODO: FO3 should use this branch as well. But it is not clear how to distinguish FO3 from
|
||||||
|
// TES5. Currently FO3 uses wrong template flags that can lead to "ESM4 NPC traits not found"
|
||||||
|
// exception the NPC will not be added to the scene. But in any way it shouldn't cause a crash.
|
||||||
|
if (!(rec->mBaseConfig.fo3.templateFlags & flag))
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
else if (rec->mIsFO4)
|
||||||
|
{
|
||||||
|
if (!(rec->mBaseConfig.fo4.templateFlags & flag))
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
else if (!(rec->mBaseConfig.tes5.templateFlags & flag))
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,8 +90,8 @@ namespace MWClass
|
||||||
const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore();
|
const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore();
|
||||||
auto npcRecs = withBaseTemplates<ESM4::LevelledNpc, ESM4::Npc>(ptr.get<ESM4::Npc>()->mBase);
|
auto npcRecs = withBaseTemplates<ESM4::LevelledNpc, ESM4::Npc>(ptr.get<ESM4::Npc>()->mBase);
|
||||||
|
|
||||||
data->mTraits = chooseTemplate(npcRecs, ESM4::Npc::TES5_UseTraits);
|
data->mTraits = chooseTemplate(npcRecs, ESM4::Npc::Template_UseTraits);
|
||||||
data->mBaseData = chooseTemplate(npcRecs, ESM4::Npc::TES5_UseBaseData);
|
data->mBaseData = chooseTemplate(npcRecs, ESM4::Npc::Template_UseBaseData);
|
||||||
|
|
||||||
if (!data->mTraits)
|
if (!data->mTraits)
|
||||||
throw std::runtime_error("ESM4 NPC traits not found");
|
throw std::runtime_error("ESM4 NPC traits not found");
|
||||||
|
@ -88,10 +103,13 @@ namespace MWClass
|
||||||
data->mIsFemale = data->mTraits->mBaseConfig.tes4.flags & ESM4::Npc::TES4_Female;
|
data->mIsFemale = data->mTraits->mBaseConfig.tes4.flags & ESM4::Npc::TES4_Female;
|
||||||
else if (data->mTraits->mIsFONV)
|
else if (data->mTraits->mIsFONV)
|
||||||
data->mIsFemale = data->mTraits->mBaseConfig.fo3.flags & ESM4::Npc::FO3_Female;
|
data->mIsFemale = data->mTraits->mBaseConfig.fo3.flags & ESM4::Npc::FO3_Female;
|
||||||
|
else if (data->mTraits->mIsFO4)
|
||||||
|
data->mIsFemale
|
||||||
|
= data->mTraits->mBaseConfig.fo4.flags & ESM4::Npc::TES5_Female; // FO4 flags are the same as TES5
|
||||||
else
|
else
|
||||||
data->mIsFemale = data->mTraits->mBaseConfig.tes5.flags & ESM4::Npc::TES5_Female;
|
data->mIsFemale = data->mTraits->mBaseConfig.tes5.flags & ESM4::Npc::TES5_Female;
|
||||||
|
|
||||||
if (auto inv = chooseTemplate(npcRecs, ESM4::Npc::TES5_UseInventory))
|
if (auto inv = chooseTemplate(npcRecs, ESM4::Npc::Template_UseInventory))
|
||||||
{
|
{
|
||||||
for (const ESM4::InventoryItem& item : inv->mInventory)
|
for (const ESM4::InventoryItem& item : inv->mInventory)
|
||||||
{
|
{
|
||||||
|
|
|
@ -556,6 +556,20 @@ namespace MWGui
|
||||||
std::unique_ptr<MWWorld::Action> action = ptr.getClass().use(ptr, force);
|
std::unique_ptr<MWWorld::Action> action = ptr.getClass().use(ptr, force);
|
||||||
action->execute(player);
|
action->execute(player);
|
||||||
|
|
||||||
|
// Handles partial equipping (final part)
|
||||||
|
if (mEquippedStackableCount.has_value())
|
||||||
|
{
|
||||||
|
// the count to unequip
|
||||||
|
int count = ptr.getRefData().getCount() - mDragAndDrop->mDraggedCount - mEquippedStackableCount.value();
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
|
||||||
|
invStore.unequipItemQuantity(ptr, count);
|
||||||
|
updateItemView();
|
||||||
|
}
|
||||||
|
mEquippedStackableCount.reset();
|
||||||
|
}
|
||||||
|
|
||||||
if (isVisible())
|
if (isVisible())
|
||||||
{
|
{
|
||||||
mItemView->update();
|
mItemView->update();
|
||||||
|
@ -581,27 +595,21 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handles partial equipping
|
// Handles partial equipping
|
||||||
const std::pair<std::vector<int>, bool> slots = ptr.getClass().getEquipmentSlots(ptr);
|
mEquippedStackableCount.reset();
|
||||||
|
const auto slots = ptr.getClass().getEquipmentSlots(ptr);
|
||||||
if (!slots.first.empty() && slots.second)
|
if (!slots.first.empty() && slots.second)
|
||||||
{
|
{
|
||||||
int equippedStackableCount = 0;
|
|
||||||
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
|
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
|
||||||
MWWorld::ConstContainerStoreIterator slotIt = invStore.getSlot(slots.first.front());
|
MWWorld::ConstContainerStoreIterator slotIt = invStore.getSlot(slots.first.front());
|
||||||
|
|
||||||
// Get the count before useItem()
|
// Save the currently equipped count before useItem()
|
||||||
if (slotIt != invStore.end() && slotIt->getCellRef().getRefId() == ptr.getCellRef().getRefId())
|
if (slotIt != invStore.end() && slotIt->getCellRef().getRefId() == ptr.getCellRef().getRefId())
|
||||||
equippedStackableCount = slotIt->getRefData().getCount();
|
mEquippedStackableCount = slotIt->getRefData().getCount();
|
||||||
|
else
|
||||||
useItem(ptr);
|
mEquippedStackableCount = 0;
|
||||||
int unequipCount = ptr.getRefData().getCount() - mDragAndDrop->mDraggedCount - equippedStackableCount;
|
|
||||||
if (unequipCount > 0)
|
|
||||||
{
|
|
||||||
invStore.unequipItemQuantity(ptr, unequipCount);
|
|
||||||
updateItemView();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
MWBase::Environment::get().getLuaManager()->useItem(ptr, MWMechanics::getPlayer(), false);
|
MWBase::Environment::get().getLuaManager()->useItem(ptr, MWMechanics::getPlayer(), false);
|
||||||
|
|
||||||
// If item is ingredient or potion don't stop drag and drop to simplify action of taking more than one 1
|
// If item is ingredient or potion don't stop drag and drop to simplify action of taking more than one 1
|
||||||
// item
|
// item
|
||||||
|
|
|
@ -74,6 +74,7 @@ namespace MWGui
|
||||||
DragAndDrop* mDragAndDrop;
|
DragAndDrop* mDragAndDrop;
|
||||||
|
|
||||||
int mSelectedItem;
|
int mSelectedItem;
|
||||||
|
std::optional<int> mEquippedStackableCount;
|
||||||
|
|
||||||
MWWorld::Ptr mPtr;
|
MWWorld::Ptr mPtr;
|
||||||
|
|
||||||
|
|
|
@ -183,6 +183,10 @@ namespace MWGui
|
||||||
return switchFocus(D_Down, false);
|
return switchFocus(D_Down, false);
|
||||||
case MyGUI::KeyCode::Tab:
|
case MyGUI::KeyCode::Tab:
|
||||||
return switchFocus(MyGUI::InputManager::getInstance().isShiftPressed() ? D_Prev : D_Next, true);
|
return switchFocus(MyGUI::InputManager::getInstance().isShiftPressed() ? D_Prev : D_Next, true);
|
||||||
|
case MyGUI::KeyCode::Period:
|
||||||
|
return switchFocus(D_Prev, true);
|
||||||
|
case MyGUI::KeyCode::Slash:
|
||||||
|
return switchFocus(D_Next, true);
|
||||||
case MyGUI::KeyCode::Return:
|
case MyGUI::KeyCode::Return:
|
||||||
case MyGUI::KeyCode::NumpadEnter:
|
case MyGUI::KeyCode::NumpadEnter:
|
||||||
case MyGUI::KeyCode::Space:
|
case MyGUI::KeyCode::Space:
|
||||||
|
|
|
@ -413,8 +413,8 @@ namespace MWGui
|
||||||
text << Misc::fileTimeToString(mCurrentSlot->mTimeStamp, "%Y.%m.%d %T") << "\n";
|
text << Misc::fileTimeToString(mCurrentSlot->mTimeStamp, "%Y.%m.%d %T") << "\n";
|
||||||
|
|
||||||
if (mCurrentSlot->mProfile.mMaximumHealth > 0)
|
if (mCurrentSlot->mProfile.mMaximumHealth > 0)
|
||||||
text << std::fixed << std::setprecision(0) << "#{sHealth} " << mCurrentSlot->mProfile.mCurrentHealth << "/"
|
text << "#{sHealth} " << static_cast<int>(mCurrentSlot->mProfile.mCurrentHealth) << "/"
|
||||||
<< mCurrentSlot->mProfile.mMaximumHealth << "\n";
|
<< static_cast<int>(mCurrentSlot->mProfile.mMaximumHealth) << "\n";
|
||||||
|
|
||||||
text << "#{sLevel} " << mCurrentSlot->mProfile.mPlayerLevel << "\n";
|
text << "#{sLevel} " << mCurrentSlot->mProfile.mPlayerLevel << "\n";
|
||||||
text << "#{sCell=" << mCurrentSlot->mProfile.mPlayerCellName << "}\n";
|
text << "#{sCell=" << mCurrentSlot->mProfile.mPlayerCellName << "}\n";
|
||||||
|
|
|
@ -355,12 +355,10 @@ namespace MWGui::Widgets
|
||||||
|
|
||||||
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
||||||
|
|
||||||
const ESM::MagicEffect* magicEffect = store.get<ESM::MagicEffect>().search(mEffectParams.mEffectID);
|
const ESM::MagicEffect* magicEffect = store.get<ESM::MagicEffect>().find(mEffectParams.mEffectID);
|
||||||
const ESM::Attribute* attribute = store.get<ESM::Attribute>().search(mEffectParams.mAttribute);
|
const ESM::Attribute* attribute = store.get<ESM::Attribute>().search(mEffectParams.mAttribute);
|
||||||
const ESM::Skill* skill = store.get<ESM::Skill>().search(mEffectParams.mSkill);
|
const ESM::Skill* skill = store.get<ESM::Skill>().search(mEffectParams.mSkill);
|
||||||
|
|
||||||
assert(magicEffect);
|
|
||||||
|
|
||||||
auto windowManager = MWBase::Environment::get().getWindowManager();
|
auto windowManager = MWBase::Environment::get().getWindowManager();
|
||||||
|
|
||||||
std::string_view pt = windowManager->getGameSettingString("spoint", {});
|
std::string_view pt = windowManager->getGameSettingString("spoint", {});
|
||||||
|
|
|
@ -1100,7 +1100,7 @@ namespace MWGui
|
||||||
std::string_view settingSection = tag.substr(0, comma_pos);
|
std::string_view settingSection = tag.substr(0, comma_pos);
|
||||||
std::string_view settingTag = tag.substr(comma_pos + 1, tag.length());
|
std::string_view settingTag = tag.substr(comma_pos + 1, tag.length());
|
||||||
|
|
||||||
_result = Settings::Manager::getString(settingTag, settingSection);
|
_result = Settings::get<MyGUI::Colour>(settingSection, settingTag).get().print();
|
||||||
}
|
}
|
||||||
else if (tag.starts_with(tokenToFind))
|
else if (tag.starts_with(tokenToFind))
|
||||||
{
|
{
|
||||||
|
@ -1115,7 +1115,7 @@ namespace MWGui
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::vector<std::string> split;
|
std::vector<std::string> split;
|
||||||
Misc::StringUtils::split(std::string{ tag }, split, ":");
|
Misc::StringUtils::split(tag, split, ":");
|
||||||
|
|
||||||
l10n::Manager& l10nManager = *MWBase::Environment::get().getL10nManager();
|
l10n::Manager& l10nManager = *MWBase::Environment::get().getL10nManager();
|
||||||
|
|
||||||
|
|
|
@ -344,6 +344,12 @@ namespace MWLua
|
||||||
playerScripts->uiModeChanged(argId, false);
|
playerScripts->uiModeChanged(argId, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LuaManager::useItem(const MWWorld::Ptr& object, const MWWorld::Ptr& actor, bool force)
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWorldModel()->registerPtr(object);
|
||||||
|
mEngineEvents.addToQueue(EngineEvents::OnUseItem{ getId(actor), getId(object), force });
|
||||||
|
}
|
||||||
|
|
||||||
void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)
|
void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
mObjectLists.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.
|
mObjectLists.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.
|
||||||
|
|
|
@ -77,10 +77,7 @@ namespace MWLua
|
||||||
{
|
{
|
||||||
mEngineEvents.addToQueue(EngineEvents::OnActivate{ getId(actor), getId(object) });
|
mEngineEvents.addToQueue(EngineEvents::OnActivate{ getId(actor), getId(object) });
|
||||||
}
|
}
|
||||||
void useItem(const MWWorld::Ptr& object, const MWWorld::Ptr& actor, bool force) override
|
void useItem(const MWWorld::Ptr& object, const MWWorld::Ptr& actor, bool force) override;
|
||||||
{
|
|
||||||
mEngineEvents.addToQueue(EngineEvents::OnUseItem{ getId(actor), getId(object), force });
|
|
||||||
}
|
|
||||||
void exteriorCreated(MWWorld::CellStore& cell) override
|
void exteriorCreated(MWWorld::CellStore& cell) override
|
||||||
{
|
{
|
||||||
mEngineEvents.addToQueue(EngineEvents::OnNewExterior{ cell });
|
mEngineEvents.addToQueue(EngineEvents::OnNewExterior{ cell });
|
||||||
|
|
|
@ -140,7 +140,7 @@ namespace MWLua
|
||||||
= sol::overload([](const GlobalStore& store, std::string_view globalId, float val) {
|
= sol::overload([](const GlobalStore& store, std::string_view globalId, float val) {
|
||||||
auto g = store.search(ESM::RefId::deserializeText(globalId));
|
auto g = store.search(ESM::RefId::deserializeText(globalId));
|
||||||
if (g == nullptr)
|
if (g == nullptr)
|
||||||
return;
|
throw std::runtime_error("No variable \"" + std::string(globalId) + "\" in GlobalStore");
|
||||||
char varType = MWBase::Environment::get().getWorld()->getGlobalVariableType(globalId);
|
char varType = MWBase::Environment::get().getWorld()->getGlobalVariableType(globalId);
|
||||||
if (varType == 's' || varType == 'l')
|
if (varType == 's' || varType == 'l')
|
||||||
{
|
{
|
||||||
|
|
|
@ -457,6 +457,9 @@ namespace MWLua
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return &*rec.mSchool;
|
return &*rec.mSchool;
|
||||||
});
|
});
|
||||||
|
skillT["attribute"] = sol::readonly_property([](const ESM::Skill& rec) -> std::string {
|
||||||
|
return ESM::Attribute::indexToRefId(rec.mData.mAttribute).serializeText();
|
||||||
|
});
|
||||||
|
|
||||||
auto schoolT = context.mLua->sol().new_usertype<ESM::MagicSchool>("MagicSchool");
|
auto schoolT = context.mLua->sol().new_usertype<ESM::MagicSchool>("MagicSchool");
|
||||||
schoolT[sol::meta_function::to_string]
|
schoolT[sol::meta_function::to_string]
|
||||||
|
|
|
@ -395,6 +395,11 @@ namespace MWLua
|
||||||
return dist <= actorsProcessingRange;
|
return dist <= actorsProcessingRange;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
actor["isDead"] = [](const Object& o) {
|
||||||
|
const auto& target = o.ptr();
|
||||||
|
return target.getClass().getCreatureStats(target).isDead();
|
||||||
|
};
|
||||||
|
|
||||||
actor["getEncumbrance"] = [](const Object& actor) -> float {
|
actor["getEncumbrance"] = [](const Object& actor) -> float {
|
||||||
const MWWorld::Ptr ptr = actor.ptr();
|
const MWWorld::Ptr ptr = actor.ptr();
|
||||||
return ptr.getClass().getEncumbrance(ptr);
|
return ptr.getClass().getEncumbrance(ptr);
|
||||||
|
|
|
@ -39,6 +39,6 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
const MagicEffects& magicEffects = actor.getClass().getCreatureStats(actor).getMagicEffects();
|
const MagicEffects& magicEffects = actor.getClass().getCreatureStats(actor).getMagicEffects();
|
||||||
return (magicEffects.getOrDefault(ESM::MagicEffect::Invisibility).getMagnitude() > 0)
|
return (magicEffects.getOrDefault(ESM::MagicEffect::Invisibility).getMagnitude() > 0)
|
||||||
|| (magicEffects.getOrDefault(ESM::MagicEffect::Chameleon).getMagnitude() > 75);
|
|| (magicEffects.getOrDefault(ESM::MagicEffect::Chameleon).getMagnitude() >= 75);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2386,49 +2386,55 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim && !isScriptedAnimPlaying() ? 0.f : duration);
|
osg::Vec3f movementFromAnimation
|
||||||
if (duration > 0.0f)
|
= mAnimation->runAnimation(mSkipAnim && !isScriptedAnimPlaying() ? 0.f : duration);
|
||||||
moved /= duration;
|
|
||||||
else
|
|
||||||
moved = osg::Vec3f(0.f, 0.f, 0.f);
|
|
||||||
|
|
||||||
moved.x() *= scale;
|
if (mPtr.getClass().isActor() && isMovementAnimationControlled() && !isScriptedAnimPlaying())
|
||||||
moved.y() *= scale;
|
|
||||||
|
|
||||||
// Ensure we're moving in generally the right direction...
|
|
||||||
if (speed > 0.f && moved != osg::Vec3f())
|
|
||||||
{
|
{
|
||||||
float l = moved.length();
|
if (duration > 0.0f)
|
||||||
if (std::abs(movement.x() - moved.x()) > std::abs(moved.x()) / 2
|
movementFromAnimation /= duration;
|
||||||
|| std::abs(movement.y() - moved.y()) > std::abs(moved.y()) / 2
|
else
|
||||||
|| std::abs(movement.z() - moved.z()) > std::abs(moved.z()) / 2)
|
movementFromAnimation = osg::Vec3f(0.f, 0.f, 0.f);
|
||||||
{
|
|
||||||
moved = movement;
|
|
||||||
// For some creatures getSpeed doesn't work, so we adjust speed to the animation.
|
|
||||||
// TODO: Fix Creature::getSpeed.
|
|
||||||
float newLength = moved.length();
|
|
||||||
if (newLength > 0 && !cls.isNpc())
|
|
||||||
moved *= (l / newLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mFloatToSurface && cls.isActor())
|
movementFromAnimation.x() *= scale;
|
||||||
{
|
movementFromAnimation.y() *= scale;
|
||||||
if (cls.getCreatureStats(mPtr).isDead()
|
|
||||||
|| (!godmode
|
|
||||||
&& cls.getCreatureStats(mPtr)
|
|
||||||
.getMagicEffects()
|
|
||||||
.getOrDefault(ESM::MagicEffect::Paralyze)
|
|
||||||
.getModifier()
|
|
||||||
> 0))
|
|
||||||
{
|
|
||||||
moved.z() = 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update movement
|
if (speed > 0.f && movementFromAnimation != osg::Vec3f())
|
||||||
if (isMovementAnimationControlled() && mPtr.getClass().isActor() && !isScriptedAnimPlaying())
|
{
|
||||||
world->queueMovement(mPtr, moved);
|
// Ensure we're moving in the right general direction. In vanilla, all horizontal movement is taken from
|
||||||
|
// animations, even when moving diagonally (which doesn't have a corresponding animation). So to acheive
|
||||||
|
// diagonal movement, we have to rotate the movement taken from the animation to the intended
|
||||||
|
// direction.
|
||||||
|
//
|
||||||
|
// Note that while a complete movement animation cycle will have a well defined direction, no individual
|
||||||
|
// frame will, and therefore we have to determine the direction based on the currently playing cycle
|
||||||
|
// instead.
|
||||||
|
float animMovementAngle = getAnimationMovementDirection();
|
||||||
|
float targetMovementAngle = std::atan2(-movement.x(), movement.y());
|
||||||
|
float diff = targetMovementAngle - animMovementAngle;
|
||||||
|
movementFromAnimation = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * movementFromAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(isPlayer && Settings::game().mPlayerMovementIgnoresAnimation))
|
||||||
|
movement = movementFromAnimation;
|
||||||
|
|
||||||
|
if (mFloatToSurface)
|
||||||
|
{
|
||||||
|
if (cls.getCreatureStats(mPtr).isDead()
|
||||||
|
|| (!godmode
|
||||||
|
&& cls.getCreatureStats(mPtr)
|
||||||
|
.getMagicEffects()
|
||||||
|
.getOrDefault(ESM::MagicEffect::Paralyze)
|
||||||
|
.getModifier()
|
||||||
|
> 0))
|
||||||
|
{
|
||||||
|
movement.z() = 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update movement
|
||||||
|
world->queueMovement(mPtr, movement);
|
||||||
|
}
|
||||||
|
|
||||||
mSkipAnim = false;
|
mSkipAnim = false;
|
||||||
|
|
||||||
|
@ -2909,6 +2915,39 @@ namespace MWMechanics
|
||||||
MWBase::Environment::get().getSoundManager()->playSound3D(mPtr, *soundId, volume, pitch);
|
MWBase::Environment::get().getSoundManager()->playSound3D(mPtr, *soundId, volume, pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float CharacterController::getAnimationMovementDirection() const
|
||||||
|
{
|
||||||
|
switch (mMovementState)
|
||||||
|
{
|
||||||
|
case CharState_RunLeft:
|
||||||
|
case CharState_SneakLeft:
|
||||||
|
case CharState_SwimWalkLeft:
|
||||||
|
case CharState_SwimRunLeft:
|
||||||
|
case CharState_WalkLeft:
|
||||||
|
return osg::PI_2f;
|
||||||
|
case CharState_RunRight:
|
||||||
|
case CharState_SneakRight:
|
||||||
|
case CharState_SwimWalkRight:
|
||||||
|
case CharState_SwimRunRight:
|
||||||
|
case CharState_WalkRight:
|
||||||
|
return -osg::PI_2f;
|
||||||
|
case CharState_RunForward:
|
||||||
|
case CharState_SneakForward:
|
||||||
|
case CharState_SwimRunForward:
|
||||||
|
case CharState_SwimWalkForward:
|
||||||
|
case CharState_WalkForward:
|
||||||
|
return mAnimation->getLegsYawRadians();
|
||||||
|
case CharState_RunBack:
|
||||||
|
case CharState_SneakBack:
|
||||||
|
case CharState_SwimWalkBack:
|
||||||
|
case CharState_SwimRunBack:
|
||||||
|
case CharState_WalkBack:
|
||||||
|
return mAnimation->getLegsYawRadians() - osg::PIf;
|
||||||
|
default:
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CharacterController::updateHeadTracking(float duration)
|
void CharacterController::updateHeadTracking(float duration)
|
||||||
{
|
{
|
||||||
const osg::Node* head = mAnimation->getNode("Bip01 Head");
|
const osg::Node* head = mAnimation->getNode("Bip01 Head");
|
||||||
|
|
|
@ -319,6 +319,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
void playSwishSound() const;
|
void playSwishSound() const;
|
||||||
|
|
||||||
|
float getAnimationMovementDirection() const;
|
||||||
|
|
||||||
MWWorld::MovementDirectionFlags getSupportedMovementDirections() const;
|
MWWorld::MovementDirectionFlags getSupportedMovementDirections() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -932,6 +932,9 @@ namespace MWMechanics
|
||||||
if (target.getCellRef().getLockLevel()
|
if (target.getCellRef().getLockLevel()
|
||||||
< magnitude) // If the door is not already locked to a higher value, lock it to spell magnitude
|
< magnitude) // If the door is not already locked to a higher value, lock it to spell magnitude
|
||||||
{
|
{
|
||||||
|
MWBase::Environment::get().getSoundManager()->playSound3D(
|
||||||
|
target, ESM::RefId::stringRefId("Open Lock"), 1.f, 1.f);
|
||||||
|
|
||||||
if (caster == getPlayer())
|
if (caster == getPlayer())
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}");
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}");
|
||||||
target.getCellRef().lock(magnitude);
|
target.getCellRef().lock(magnitude);
|
||||||
|
|
|
@ -1235,9 +1235,11 @@ namespace MWRender
|
||||||
mRootController->setEnabled(enable);
|
mRootController->setEnabled(enable);
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
mRootController->setRotate(osg::Quat(mLegsYawRadians, osg::Vec3f(0, 0, 1))
|
osg::Quat legYaw = osg::Quat(mLegsYawRadians, osg::Vec3f(0, 0, 1));
|
||||||
* osg::Quat(mBodyPitchRadians, osg::Vec3f(1, 0, 0)));
|
mRootController->setRotate(legYaw * osg::Quat(mBodyPitchRadians, osg::Vec3f(1, 0, 0)));
|
||||||
yawOffset = mLegsYawRadians;
|
yawOffset = mLegsYawRadians;
|
||||||
|
// When yawing the root, also update the accumulated movement.
|
||||||
|
movement = legYaw * movement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mSpineController)
|
if (mSpineController)
|
||||||
|
|
|
@ -247,7 +247,7 @@ namespace MWRender
|
||||||
defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
|
defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
|
||||||
stateset->setAttribute(defaultMat);
|
stateset->setAttribute(defaultMat);
|
||||||
|
|
||||||
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);
|
SceneUtil::ShadowManager::disableShadowsForStateSet(Settings::shadows(), *stateset);
|
||||||
|
|
||||||
// assign large value to effectively turn off fog
|
// assign large value to effectively turn off fog
|
||||||
// shaders don't respect glDisable(GL_FOG)
|
// shaders don't respect glDisable(GL_FOG)
|
||||||
|
|
|
@ -124,6 +124,8 @@ namespace MWRender
|
||||||
auto findArmorAddons = [&](const ESM4::Armor* armor) {
|
auto findArmorAddons = [&](const ESM4::Armor* armor) {
|
||||||
for (ESM::FormId armaId : armor->mAddOns)
|
for (ESM::FormId armaId : armor->mAddOns)
|
||||||
{
|
{
|
||||||
|
if (armaId.isZeroOrUnset())
|
||||||
|
continue;
|
||||||
const ESM4::ArmorAddon* arma = store->get<ESM4::ArmorAddon>().search(armaId);
|
const ESM4::ArmorAddon* arma = store->get<ESM4::ArmorAddon>().search(armaId);
|
||||||
if (!arma)
|
if (!arma)
|
||||||
{
|
{
|
||||||
|
|
|
@ -763,7 +763,7 @@ namespace MWRender
|
||||||
|
|
||||||
lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||||
|
|
||||||
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);
|
SceneUtil::ShadowManager::disableShadowsForStateSet(Settings::shadows(), *stateset);
|
||||||
|
|
||||||
// override sun for local map
|
// override sun for local map
|
||||||
SceneUtil::configureStateSetSunOverride(static_cast<SceneUtil::LightManager*>(mSceneRoot), light, stateset);
|
SceneUtil::configureStateSetSunOverride(static_cast<SceneUtil::LightManager*>(mSceneRoot), light, stateset);
|
||||||
|
|
|
@ -20,11 +20,6 @@ namespace MWRender
|
||||||
|
|
||||||
mResolveProgram = shaderManager.getProgram(vertex, std::move(resolveFragment));
|
mResolveProgram = shaderManager.getProgram(vertex, std::move(resolveFragment));
|
||||||
mLuminanceProgram = shaderManager.getProgram(vertex, std::move(luminanceFragment));
|
mLuminanceProgram = shaderManager.getProgram(vertex, std::move(luminanceFragment));
|
||||||
}
|
|
||||||
|
|
||||||
void LuminanceCalculator::compile()
|
|
||||||
{
|
|
||||||
int mipmapLevels = osg::Image::computeNumberOfMipmapLevels(mWidth, mHeight);
|
|
||||||
|
|
||||||
for (auto& buffer : mBuffers)
|
for (auto& buffer : mBuffers)
|
||||||
{
|
{
|
||||||
|
@ -38,7 +33,6 @@ namespace MWRender
|
||||||
osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR_MIPMAP_NEAREST);
|
osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR_MIPMAP_NEAREST);
|
||||||
buffer.mipmappedSceneLuminanceTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
|
buffer.mipmappedSceneLuminanceTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
|
||||||
buffer.mipmappedSceneLuminanceTex->setTextureSize(mWidth, mHeight);
|
buffer.mipmappedSceneLuminanceTex->setTextureSize(mWidth, mHeight);
|
||||||
buffer.mipmappedSceneLuminanceTex->setNumMipmapLevels(mipmapLevels);
|
|
||||||
|
|
||||||
buffer.luminanceTex = new osg::Texture2D;
|
buffer.luminanceTex = new osg::Texture2D;
|
||||||
buffer.luminanceTex->setInternalFormat(GL_R16F);
|
buffer.luminanceTex->setInternalFormat(GL_R16F);
|
||||||
|
@ -62,14 +56,6 @@ namespace MWRender
|
||||||
buffer.luminanceProxyFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
|
buffer.luminanceProxyFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
|
||||||
osg::FrameBufferAttachment(buffer.luminanceProxyTex));
|
osg::FrameBufferAttachment(buffer.luminanceProxyTex));
|
||||||
|
|
||||||
buffer.resolveSceneLumFbo = new osg::FrameBufferObject;
|
|
||||||
buffer.resolveSceneLumFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
|
|
||||||
osg::FrameBufferAttachment(buffer.mipmappedSceneLuminanceTex, mipmapLevels - 1));
|
|
||||||
|
|
||||||
buffer.sceneLumFbo = new osg::FrameBufferObject;
|
|
||||||
buffer.sceneLumFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
|
|
||||||
osg::FrameBufferAttachment(buffer.mipmappedSceneLuminanceTex));
|
|
||||||
|
|
||||||
buffer.sceneLumSS = new osg::StateSet;
|
buffer.sceneLumSS = new osg::StateSet;
|
||||||
buffer.sceneLumSS->setAttributeAndModes(mLuminanceProgram);
|
buffer.sceneLumSS->setAttributeAndModes(mLuminanceProgram);
|
||||||
buffer.sceneLumSS->addUniform(new osg::Uniform("sceneTex", 0));
|
buffer.sceneLumSS->addUniform(new osg::Uniform("sceneTex", 0));
|
||||||
|
@ -84,6 +70,26 @@ namespace MWRender
|
||||||
|
|
||||||
mBuffers[0].resolveSS->setTextureAttributeAndModes(1, mBuffers[1].luminanceTex);
|
mBuffers[0].resolveSS->setTextureAttributeAndModes(1, mBuffers[1].luminanceTex);
|
||||||
mBuffers[1].resolveSS->setTextureAttributeAndModes(1, mBuffers[0].luminanceTex);
|
mBuffers[1].resolveSS->setTextureAttributeAndModes(1, mBuffers[0].luminanceTex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuminanceCalculator::compile()
|
||||||
|
{
|
||||||
|
int mipmapLevels = osg::Image::computeNumberOfMipmapLevels(mWidth, mHeight);
|
||||||
|
|
||||||
|
for (auto& buffer : mBuffers)
|
||||||
|
{
|
||||||
|
buffer.mipmappedSceneLuminanceTex->setTextureSize(mWidth, mHeight);
|
||||||
|
buffer.mipmappedSceneLuminanceTex->setNumMipmapLevels(mipmapLevels);
|
||||||
|
buffer.mipmappedSceneLuminanceTex->dirtyTextureObject();
|
||||||
|
|
||||||
|
buffer.resolveSceneLumFbo = new osg::FrameBufferObject;
|
||||||
|
buffer.resolveSceneLumFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
|
||||||
|
osg::FrameBufferAttachment(buffer.mipmappedSceneLuminanceTex, mipmapLevels - 1));
|
||||||
|
|
||||||
|
buffer.sceneLumFbo = new osg::FrameBufferObject;
|
||||||
|
buffer.sceneLumFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
|
||||||
|
osg::FrameBufferAttachment(buffer.mipmappedSceneLuminanceTex));
|
||||||
|
}
|
||||||
|
|
||||||
mCompiled = true;
|
mCompiled = true;
|
||||||
}
|
}
|
||||||
|
@ -114,13 +120,14 @@ namespace MWRender
|
||||||
buffer.luminanceProxyFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
buffer.luminanceProxyFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
ext->glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
ext->glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
|
||||||
if (dirty)
|
if (mIsBlank)
|
||||||
{
|
{
|
||||||
// Use current frame data for previous frame to warm up calculations and prevent popin
|
// Use current frame data for previous frame to warm up calculations and prevent popin
|
||||||
mBuffers[(frameId + 1) % 2].resolveFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
mBuffers[(frameId + 1) % 2].resolveFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
ext->glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
ext->glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
|
||||||
buffer.luminanceProxyFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
buffer.luminanceProxyFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
mIsBlank = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.resolveFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
buffer.resolveFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
|
|
@ -58,6 +58,7 @@ namespace MWRender
|
||||||
|
|
||||||
bool mCompiled = false;
|
bool mCompiled = false;
|
||||||
bool mEnabled = false;
|
bool mEnabled = false;
|
||||||
|
bool mIsBlank = true;
|
||||||
|
|
||||||
int mWidth = 1;
|
int mWidth = 1;
|
||||||
int mHeight = 1;
|
int mHeight = 1;
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
PingPongCanvas::PingPongCanvas(Shader::ShaderManager& shaderManager)
|
PingPongCanvas::PingPongCanvas(
|
||||||
|
Shader::ShaderManager& shaderManager, const std::shared_ptr<LuminanceCalculator>& luminanceCalculator)
|
||||||
: mFallbackStateSet(new osg::StateSet)
|
: mFallbackStateSet(new osg::StateSet)
|
||||||
, mMultiviewResolveStateSet(new osg::StateSet)
|
, mMultiviewResolveStateSet(new osg::StateSet)
|
||||||
|
, mLuminanceCalculator(luminanceCalculator)
|
||||||
{
|
{
|
||||||
setUseDisplayList(false);
|
setUseDisplayList(false);
|
||||||
setUseVertexBufferObjects(true);
|
setUseVertexBufferObjects(true);
|
||||||
|
@ -26,8 +28,7 @@ namespace MWRender
|
||||||
|
|
||||||
addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, 3));
|
addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, 3));
|
||||||
|
|
||||||
mLuminanceCalculator = LuminanceCalculator(shaderManager);
|
mLuminanceCalculator->disable();
|
||||||
mLuminanceCalculator.disable();
|
|
||||||
|
|
||||||
Shader::ShaderManager::DefineMap defines;
|
Shader::ShaderManager::DefineMap defines;
|
||||||
Stereo::shaderStereoDefines(defines);
|
Stereo::shaderStereoDefines(defines);
|
||||||
|
@ -142,7 +143,7 @@ namespace MWRender
|
||||||
.getTexture());
|
.getTexture());
|
||||||
}
|
}
|
||||||
|
|
||||||
mLuminanceCalculator.dirty(mTextureScene->getTextureWidth(), mTextureScene->getTextureHeight());
|
mLuminanceCalculator->dirty(mTextureScene->getTextureWidth(), mTextureScene->getTextureHeight());
|
||||||
|
|
||||||
if (Stereo::getStereo())
|
if (Stereo::getStereo())
|
||||||
mRenderViewport
|
mRenderViewport
|
||||||
|
@ -158,11 +159,11 @@ namespace MWRender
|
||||||
{ GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT2_EXT },
|
{ GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT2_EXT },
|
||||||
{ GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT } } };
|
{ GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT } } };
|
||||||
|
|
||||||
(mAvgLum) ? mLuminanceCalculator.enable() : mLuminanceCalculator.disable();
|
(mAvgLum) ? mLuminanceCalculator->enable() : mLuminanceCalculator->disable();
|
||||||
|
|
||||||
// A histogram based approach is superior way to calculate scene luminance. Using mipmaps is more broadly
|
// A histogram based approach is superior way to calculate scene luminance. Using mipmaps is more broadly
|
||||||
// supported, so that's what we use for now.
|
// supported, so that's what we use for now.
|
||||||
mLuminanceCalculator.draw(*this, renderInfo, state, ext, frameId);
|
mLuminanceCalculator->draw(*this, renderInfo, state, ext, frameId);
|
||||||
|
|
||||||
auto buffer = buffers[0];
|
auto buffer = buffers[0];
|
||||||
|
|
||||||
|
@ -195,6 +196,39 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// When textures are created (or resized) we need to either dirty them and/or clear them.
|
||||||
|
// Otherwise, there will be undefined behavior when reading from a texture that has yet to be written to in a
|
||||||
|
// later pass.
|
||||||
|
for (const auto& attachment : mDirtyAttachments)
|
||||||
|
{
|
||||||
|
const auto [w, h]
|
||||||
|
= attachment.mSize.get(mTextureScene->getTextureWidth(), mTextureScene->getTextureHeight());
|
||||||
|
|
||||||
|
attachment.mTarget->setTextureSize(w, h);
|
||||||
|
if (attachment.mMipMap)
|
||||||
|
attachment.mTarget->setNumMipmapLevels(osg::Image::computeNumberOfMipmapLevels(w, h));
|
||||||
|
attachment.mTarget->dirtyTextureObject();
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::FrameBufferObject> fbo = new osg::FrameBufferObject;
|
||||||
|
|
||||||
|
fbo->setAttachment(
|
||||||
|
osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(attachment.mTarget));
|
||||||
|
fbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
|
||||||
|
glViewport(0, 0, attachment.mTarget->getTextureWidth(), attachment.mTarget->getTextureHeight());
|
||||||
|
state.haveAppliedAttribute(osg::StateAttribute::VIEWPORT);
|
||||||
|
glClearColor(attachment.mClearColor.r(), attachment.mClearColor.g(), attachment.mClearColor.b(),
|
||||||
|
attachment.mClearColor.a());
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
if (attachment.mTarget->getNumMipmapLevels() > 0)
|
||||||
|
{
|
||||||
|
state.setActiveTextureUnit(0);
|
||||||
|
state.applyTextureAttribute(0, attachment.mTarget);
|
||||||
|
ext->glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const size_t& index : filtered)
|
for (const size_t& index : filtered)
|
||||||
{
|
{
|
||||||
const auto& node = mPasses[index];
|
const auto& node = mPasses[index];
|
||||||
|
@ -202,8 +236,8 @@ namespace MWRender
|
||||||
node.mRootStateSet->setTextureAttribute(PostProcessor::Unit_Depth, mTextureDepth);
|
node.mRootStateSet->setTextureAttribute(PostProcessor::Unit_Depth, mTextureDepth);
|
||||||
|
|
||||||
if (mAvgLum)
|
if (mAvgLum)
|
||||||
node.mRootStateSet->setTextureAttribute(
|
node.mRootStateSet->setTextureAttribute(PostProcessor::TextureUnits::Unit_EyeAdaptation,
|
||||||
PostProcessor::TextureUnits::Unit_EyeAdaptation, mLuminanceCalculator.getLuminanceTexture(frameId));
|
mLuminanceCalculator->getLuminanceTexture(frameId));
|
||||||
|
|
||||||
if (mTextureNormals)
|
if (mTextureNormals)
|
||||||
node.mRootStateSet->setTextureAttribute(PostProcessor::TextureUnits::Unit_Normals, mTextureNormals);
|
node.mRootStateSet->setTextureAttribute(PostProcessor::TextureUnits::Unit_Normals, mTextureNormals);
|
||||||
|
@ -238,6 +272,23 @@ namespace MWRender
|
||||||
|
|
||||||
if (pass.mRenderTarget)
|
if (pass.mRenderTarget)
|
||||||
{
|
{
|
||||||
|
if (mDirtyAttachments.size() > 0)
|
||||||
|
{
|
||||||
|
const auto [w, h]
|
||||||
|
= pass.mSize.get(mTextureScene->getTextureWidth(), mTextureScene->getTextureHeight());
|
||||||
|
|
||||||
|
// Custom render targets must be shared between frame ids, so it's impossible to double buffer
|
||||||
|
// without expensive copies. That means the only thread-safe place to resize is in the draw
|
||||||
|
// thread.
|
||||||
|
osg::Texture2D* texture = const_cast<osg::Texture2D*>(dynamic_cast<const osg::Texture2D*>(
|
||||||
|
pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0)
|
||||||
|
.getTexture()));
|
||||||
|
|
||||||
|
texture->setTextureSize(w, h);
|
||||||
|
texture->setNumMipmapLevels(pass.mRenderTexture->getNumMipmapLevels());
|
||||||
|
texture->dirtyTextureObject();
|
||||||
|
}
|
||||||
|
|
||||||
pass.mRenderTarget->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
pass.mRenderTarget->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
|
|
||||||
if (pass.mRenderTexture->getNumMipmapLevels() > 0)
|
if (pass.mRenderTexture->getNumMipmapLevels() > 0)
|
||||||
|
@ -311,5 +362,7 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
bindDestinationFbo();
|
bindDestinationFbo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mDirtyAttachments.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,8 @@ namespace MWRender
|
||||||
class PingPongCanvas : public osg::Geometry
|
class PingPongCanvas : public osg::Geometry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PingPongCanvas(Shader::ShaderManager& shaderManager);
|
PingPongCanvas(
|
||||||
|
Shader::ShaderManager& shaderManager, const std::shared_ptr<LuminanceCalculator>& luminanceCalculator);
|
||||||
|
|
||||||
void drawGeometry(osg::RenderInfo& renderInfo) const;
|
void drawGeometry(osg::RenderInfo& renderInfo) const;
|
||||||
|
|
||||||
|
@ -30,6 +31,11 @@ namespace MWRender
|
||||||
|
|
||||||
void dirty() { mDirty = true; }
|
void dirty() { mDirty = true; }
|
||||||
|
|
||||||
|
void setDirtyAttachments(const std::vector<fx::Types::RenderTarget>& attachments)
|
||||||
|
{
|
||||||
|
mDirtyAttachments = attachments;
|
||||||
|
}
|
||||||
|
|
||||||
const fx::DispatchArray& getPasses() { return mPasses; }
|
const fx::DispatchArray& getPasses() { return mPasses; }
|
||||||
|
|
||||||
void setPasses(fx::DispatchArray&& passes);
|
void setPasses(fx::DispatchArray&& passes);
|
||||||
|
@ -65,11 +71,12 @@ namespace MWRender
|
||||||
osg::ref_ptr<osg::Texture> mTextureNormals;
|
osg::ref_ptr<osg::Texture> mTextureNormals;
|
||||||
|
|
||||||
mutable bool mDirty = false;
|
mutable bool mDirty = false;
|
||||||
|
mutable std::vector<fx::Types::RenderTarget> mDirtyAttachments;
|
||||||
mutable osg::ref_ptr<osg::Viewport> mRenderViewport;
|
mutable osg::ref_ptr<osg::Viewport> mRenderViewport;
|
||||||
mutable osg::ref_ptr<osg::FrameBufferObject> mMultiviewResolveFramebuffer;
|
mutable osg::ref_ptr<osg::FrameBufferObject> mMultiviewResolveFramebuffer;
|
||||||
mutable osg::ref_ptr<osg::FrameBufferObject> mDestinationFBO;
|
mutable osg::ref_ptr<osg::FrameBufferObject> mDestinationFBO;
|
||||||
mutable std::array<osg::ref_ptr<osg::FrameBufferObject>, 3> mFbos;
|
mutable std::array<osg::ref_ptr<osg::FrameBufferObject>, 3> mFbos;
|
||||||
mutable LuminanceCalculator mLuminanceCalculator;
|
mutable std::shared_ptr<LuminanceCalculator> mLuminanceCalculator;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,9 +118,14 @@ namespace MWRender
|
||||||
, mUsePostProcessing(Settings::postProcessing().mEnabled)
|
, mUsePostProcessing(Settings::postProcessing().mEnabled)
|
||||||
, mSamples(Settings::video().mAntialiasing)
|
, mSamples(Settings::video().mAntialiasing)
|
||||||
, mPingPongCull(new PingPongCull(this))
|
, mPingPongCull(new PingPongCull(this))
|
||||||
, mCanvases({ new PingPongCanvas(mRendering.getResourceSystem()->getSceneManager()->getShaderManager()),
|
|
||||||
new PingPongCanvas(mRendering.getResourceSystem()->getSceneManager()->getShaderManager()) })
|
|
||||||
{
|
{
|
||||||
|
auto& shaderManager = mRendering.getResourceSystem()->getSceneManager()->getShaderManager();
|
||||||
|
|
||||||
|
std::shared_ptr<LuminanceCalculator> luminanceCalculator = std::make_shared<LuminanceCalculator>(shaderManager);
|
||||||
|
|
||||||
|
for (auto& canvas : mCanvases)
|
||||||
|
canvas = new PingPongCanvas(shaderManager, luminanceCalculator);
|
||||||
|
|
||||||
mHUDCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
mHUDCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||||
mHUDCamera->setRenderOrder(osg::Camera::POST_RENDER);
|
mHUDCamera->setRenderOrder(osg::Camera::POST_RENDER);
|
||||||
mHUDCamera->setClearColor(osg::Vec4(0.45, 0.45, 0.14, 1.0));
|
mHUDCamera->setClearColor(osg::Vec4(0.45, 0.45, 0.14, 1.0));
|
||||||
|
@ -139,8 +144,7 @@ namespace MWRender
|
||||||
if (Settings::shaders().mSoftParticles || Settings::postProcessing().mTransparentPostpass)
|
if (Settings::shaders().mSoftParticles || Settings::postProcessing().mTransparentPostpass)
|
||||||
{
|
{
|
||||||
mTransparentDepthPostPass
|
mTransparentDepthPostPass
|
||||||
= new TransparentDepthBinCallback(mRendering.getResourceSystem()->getSceneManager()->getShaderManager(),
|
= new TransparentDepthBinCallback(shaderManager, Settings::postProcessing().mTransparentPostpass);
|
||||||
Settings::postProcessing().mTransparentPostpass);
|
|
||||||
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(mTransparentDepthPostPass);
|
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(mTransparentDepthPostPass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,25 +215,14 @@ namespace MWRender
|
||||||
if (Stereo::getStereo())
|
if (Stereo::getStereo())
|
||||||
Stereo::Manager::instance().screenResolutionChanged();
|
Stereo::Manager::instance().screenResolutionChanged();
|
||||||
|
|
||||||
auto width = renderWidth();
|
|
||||||
auto height = renderHeight();
|
|
||||||
for (auto& technique : mTechniques)
|
|
||||||
{
|
|
||||||
for (auto& [name, rt] : technique->getRenderTargetsMap())
|
|
||||||
{
|
|
||||||
const auto [w, h] = rt.mSize.get(width, height);
|
|
||||||
rt.mTarget->setTextureSize(w, h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t frameId = frame() % 2;
|
size_t frameId = frame() % 2;
|
||||||
|
|
||||||
createObjectsForFrame(frameId);
|
createObjectsForFrame(frameId);
|
||||||
|
|
||||||
mRendering.updateProjectionMatrix();
|
mRendering.updateProjectionMatrix();
|
||||||
mRendering.setScreenRes(width, height);
|
mRendering.setScreenRes(renderWidth(), renderHeight());
|
||||||
|
|
||||||
dirtyTechniques();
|
dirtyTechniques(true);
|
||||||
|
|
||||||
mDirty = true;
|
mDirty = true;
|
||||||
mDirtyFrameId = !frameId;
|
mDirtyFrameId = !frameId;
|
||||||
|
@ -534,7 +527,7 @@ namespace MWRender
|
||||||
mCanvases[frameId]->dirty();
|
mCanvases[frameId]->dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessor::dirtyTechniques()
|
void PostProcessor::dirtyTechniques(bool dirtyAttachments)
|
||||||
{
|
{
|
||||||
size_t frameId = frame() % 2;
|
size_t frameId = frame() % 2;
|
||||||
|
|
||||||
|
@ -548,6 +541,8 @@ namespace MWRender
|
||||||
mNormals = false;
|
mNormals = false;
|
||||||
mPassLights = false;
|
mPassLights = false;
|
||||||
|
|
||||||
|
std::vector<fx::Types::RenderTarget> attachmentsToDirty;
|
||||||
|
|
||||||
for (const auto& technique : mTechniques)
|
for (const auto& technique : mTechniques)
|
||||||
{
|
{
|
||||||
if (!technique->isValid())
|
if (!technique->isValid())
|
||||||
|
@ -613,8 +608,6 @@ namespace MWRender
|
||||||
uniform->mName.c_str(), *type, uniform->getNumElements()));
|
uniform->mName.c_str(), *type, uniform->getNumElements()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_map<osg::Texture2D*, osg::Texture2D*> renderTargetCache;
|
|
||||||
|
|
||||||
for (const auto& pass : technique->getPasses())
|
for (const auto& pass : technique->getPasses())
|
||||||
{
|
{
|
||||||
int subTexUnit = texUnit;
|
int subTexUnit = texUnit;
|
||||||
|
@ -626,32 +619,39 @@ namespace MWRender
|
||||||
|
|
||||||
if (!pass->getTarget().empty())
|
if (!pass->getTarget().empty())
|
||||||
{
|
{
|
||||||
const auto& rt = technique->getRenderTargetsMap()[pass->getTarget()];
|
auto& renderTarget = technique->getRenderTargetsMap()[pass->getTarget()];
|
||||||
|
subPass.mSize = renderTarget.mSize;
|
||||||
const auto [w, h] = rt.mSize.get(renderWidth(), renderHeight());
|
subPass.mRenderTexture = renderTarget.mTarget;
|
||||||
|
subPass.mMipMap = renderTarget.mMipMap;
|
||||||
subPass.mRenderTexture = new osg::Texture2D(*rt.mTarget);
|
|
||||||
renderTargetCache[rt.mTarget] = subPass.mRenderTexture;
|
|
||||||
subPass.mRenderTexture->setTextureSize(w, h);
|
|
||||||
subPass.mRenderTexture->setName(std::string(pass->getTarget()));
|
|
||||||
|
|
||||||
if (rt.mMipMap)
|
|
||||||
subPass.mRenderTexture->setNumMipmapLevels(osg::Image::computeNumberOfMipmapLevels(w, h));
|
|
||||||
|
|
||||||
subPass.mRenderTarget = new osg::FrameBufferObject;
|
subPass.mRenderTarget = new osg::FrameBufferObject;
|
||||||
subPass.mRenderTarget->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
|
subPass.mRenderTarget->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
|
||||||
osg::FrameBufferAttachment(subPass.mRenderTexture));
|
osg::FrameBufferAttachment(subPass.mRenderTexture));
|
||||||
|
|
||||||
|
const auto [w, h] = renderTarget.mSize.get(renderWidth(), renderHeight());
|
||||||
subPass.mStateSet->setAttributeAndModes(new osg::Viewport(0, 0, w, h));
|
subPass.mStateSet->setAttributeAndModes(new osg::Viewport(0, 0, w, h));
|
||||||
|
|
||||||
|
if (std::find_if(attachmentsToDirty.cbegin(), attachmentsToDirty.cend(),
|
||||||
|
[renderTarget](const auto& rt) { return renderTarget.mTarget == rt.mTarget; })
|
||||||
|
== attachmentsToDirty.cend())
|
||||||
|
{
|
||||||
|
attachmentsToDirty.push_back(fx::Types::RenderTarget(renderTarget));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& whitelist : pass->getRenderTargets())
|
for (const auto& name : pass->getRenderTargets())
|
||||||
{
|
{
|
||||||
auto it = technique->getRenderTargetsMap().find(whitelist);
|
auto& renderTarget = technique->getRenderTargetsMap()[name];
|
||||||
if (it != technique->getRenderTargetsMap().end() && renderTargetCache[it->second.mTarget])
|
subPass.mStateSet->setTextureAttribute(subTexUnit, renderTarget.mTarget);
|
||||||
|
subPass.mStateSet->addUniform(new osg::Uniform(name.c_str(), subTexUnit));
|
||||||
|
|
||||||
|
if (std::find_if(attachmentsToDirty.cbegin(), attachmentsToDirty.cend(),
|
||||||
|
[renderTarget](const auto& rt) { return renderTarget.mTarget == rt.mTarget; })
|
||||||
|
== attachmentsToDirty.cend())
|
||||||
{
|
{
|
||||||
subPass.mStateSet->setTextureAttribute(subTexUnit, renderTargetCache[it->second.mTarget]);
|
attachmentsToDirty.push_back(fx::Types::RenderTarget(renderTarget));
|
||||||
subPass.mStateSet->addUniform(new osg::Uniform(std::string(it->first).c_str(), subTexUnit++));
|
|
||||||
}
|
}
|
||||||
|
subTexUnit++;
|
||||||
}
|
}
|
||||||
|
|
||||||
node.mPasses.emplace_back(std::move(subPass));
|
node.mPasses.emplace_back(std::move(subPass));
|
||||||
|
@ -668,6 +668,9 @@ namespace MWRender
|
||||||
hud->updateTechniques();
|
hud->updateTechniques();
|
||||||
|
|
||||||
mRendering.getSkyManager()->setSunglare(sunglare);
|
mRendering.getSkyManager()->setSunglare(sunglare);
|
||||||
|
|
||||||
|
if (dirtyAttachments)
|
||||||
|
mCanvases[frameId]->setDirtyAttachments(attachmentsToDirty);
|
||||||
}
|
}
|
||||||
|
|
||||||
PostProcessor::Status PostProcessor::enableTechnique(
|
PostProcessor::Status PostProcessor::enableTechnique(
|
||||||
|
@ -681,7 +684,7 @@ namespace MWRender
|
||||||
int pos = std::min<int>(location.value_or(mTechniques.size()), mTechniques.size());
|
int pos = std::min<int>(location.value_or(mTechniques.size()), mTechniques.size());
|
||||||
|
|
||||||
mTechniques.insert(mTechniques.begin() + pos, technique);
|
mTechniques.insert(mTechniques.begin() + pos, technique);
|
||||||
dirtyTechniques();
|
dirtyTechniques(Settings::ShaderManager::get().getMode() == Settings::ShaderManager::Mode::Debug);
|
||||||
|
|
||||||
return Status_Toggled;
|
return Status_Toggled;
|
||||||
}
|
}
|
||||||
|
@ -774,7 +777,7 @@ namespace MWRender
|
||||||
for (auto& technique : mTemplates)
|
for (auto& technique : mTemplates)
|
||||||
technique->compile();
|
technique->compile();
|
||||||
|
|
||||||
dirtyTechniques();
|
dirtyTechniques(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessor::disableDynamicShaders()
|
void PostProcessor::disableDynamicShaders()
|
||||||
|
|
|
@ -204,7 +204,7 @@ namespace MWRender
|
||||||
|
|
||||||
void createObjectsForFrame(size_t frameId);
|
void createObjectsForFrame(size_t frameId);
|
||||||
|
|
||||||
void dirtyTechniques();
|
void dirtyTechniques(bool dirtyAttachments = false);
|
||||||
|
|
||||||
void update(size_t frameId);
|
void update(size_t frameId);
|
||||||
|
|
||||||
|
|
|
@ -331,8 +331,8 @@ namespace MWRender
|
||||||
// Shadows and radial fog have problems with fixed-function mode.
|
// Shadows and radial fog have problems with fixed-function mode.
|
||||||
bool forceShaders = Settings::fog().mRadialFog || Settings::fog().mExponentialFog
|
bool forceShaders = Settings::fog().mRadialFog || Settings::fog().mExponentialFog
|
||||||
|| Settings::shaders().mSoftParticles || Settings::shaders().mForceShaders
|
|| Settings::shaders().mSoftParticles || Settings::shaders().mForceShaders
|
||||||
|| Settings::Manager::getBool("enable shadows", "Shadows")
|
|| Settings::shadows().mEnableShadows || lightingMethod != SceneUtil::LightingMethod::FFP || reverseZ
|
||||||
|| lightingMethod != SceneUtil::LightingMethod::FFP || reverseZ || mSkyBlending || Stereo::getMultiview();
|
|| mSkyBlending || Stereo::getMultiview();
|
||||||
resourceSystem->getSceneManager()->setForceShaders(forceShaders);
|
resourceSystem->getSceneManager()->setForceShaders(forceShaders);
|
||||||
|
|
||||||
// FIXME: calling dummy method because terrain needs to know whether lighting is clamped
|
// FIXME: calling dummy method because terrain needs to know whether lighting is clamped
|
||||||
|
@ -367,22 +367,22 @@ namespace MWRender
|
||||||
sceneRoot->setName("Scene Root");
|
sceneRoot->setName("Scene Root");
|
||||||
|
|
||||||
int shadowCastingTraversalMask = Mask_Scene;
|
int shadowCastingTraversalMask = Mask_Scene;
|
||||||
if (Settings::Manager::getBool("actor shadows", "Shadows"))
|
if (Settings::shadows().mActorShadows)
|
||||||
shadowCastingTraversalMask |= Mask_Actor;
|
shadowCastingTraversalMask |= Mask_Actor;
|
||||||
if (Settings::Manager::getBool("player shadows", "Shadows"))
|
if (Settings::shadows().mPlayerShadows)
|
||||||
shadowCastingTraversalMask |= Mask_Player;
|
shadowCastingTraversalMask |= Mask_Player;
|
||||||
|
|
||||||
int indoorShadowCastingTraversalMask = shadowCastingTraversalMask;
|
int indoorShadowCastingTraversalMask = shadowCastingTraversalMask;
|
||||||
if (Settings::Manager::getBool("object shadows", "Shadows"))
|
if (Settings::shadows().mObjectShadows)
|
||||||
shadowCastingTraversalMask |= (Mask_Object | Mask_Static);
|
shadowCastingTraversalMask |= (Mask_Object | Mask_Static);
|
||||||
if (Settings::Manager::getBool("terrain shadows", "Shadows"))
|
if (Settings::shadows().mTerrainShadows)
|
||||||
shadowCastingTraversalMask |= Mask_Terrain;
|
shadowCastingTraversalMask |= Mask_Terrain;
|
||||||
|
|
||||||
mShadowManager = std::make_unique<SceneUtil::ShadowManager>(sceneRoot, mRootNode, shadowCastingTraversalMask,
|
mShadowManager = std::make_unique<SceneUtil::ShadowManager>(sceneRoot, mRootNode, shadowCastingTraversalMask,
|
||||||
indoorShadowCastingTraversalMask, Mask_Terrain | Mask_Object | Mask_Static,
|
indoorShadowCastingTraversalMask, Mask_Terrain | Mask_Object | Mask_Static, Settings::shadows(),
|
||||||
mResourceSystem->getSceneManager()->getShaderManager());
|
mResourceSystem->getSceneManager()->getShaderManager());
|
||||||
|
|
||||||
Shader::ShaderManager::DefineMap shadowDefines = mShadowManager->getShadowDefines();
|
Shader::ShaderManager::DefineMap shadowDefines = mShadowManager->getShadowDefines(Settings::shadows());
|
||||||
Shader::ShaderManager::DefineMap lightDefines = sceneRoot->getLightDefines();
|
Shader::ShaderManager::DefineMap lightDefines = sceneRoot->getLightDefines();
|
||||||
Shader::ShaderManager::DefineMap globalDefines
|
Shader::ShaderManager::DefineMap globalDefines
|
||||||
= mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
|
= mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
|
||||||
|
@ -702,7 +702,7 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
// need to wrap this in a StateUpdater?
|
// need to wrap this in a StateUpdater?
|
||||||
mSunLight->setDiffuse(diffuse);
|
mSunLight->setDiffuse(diffuse);
|
||||||
mSunLight->setSpecular(specular);
|
mSunLight->setSpecular(osg::Vec4f(specular.x(), specular.y(), specular.z(), specular.w() * sunVis));
|
||||||
|
|
||||||
mPostProcessor->getStateUpdater()->setSunColor(diffuse);
|
mPostProcessor->getStateUpdater()->setSunColor(diffuse);
|
||||||
mPostProcessor->getStateUpdater()->setSunVis(sunVis);
|
mPostProcessor->getStateUpdater()->setSunVis(sunVis);
|
||||||
|
@ -770,7 +770,7 @@ namespace MWRender
|
||||||
if (enabled)
|
if (enabled)
|
||||||
mShadowManager->enableOutdoorMode();
|
mShadowManager->enableOutdoorMode();
|
||||||
else
|
else
|
||||||
mShadowManager->enableIndoorMode();
|
mShadowManager->enableIndoorMode(Settings::shadows());
|
||||||
mPostProcessor->getStateUpdater()->setIsInterior(!enabled);
|
mPostProcessor->getStateUpdater()->setIsInterior(!enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1319,6 +1319,7 @@ namespace MWRender
|
||||||
const float lodFactor = Settings::terrain().mLodFactor;
|
const float lodFactor = Settings::terrain().mLodFactor;
|
||||||
const bool groundcover = Settings::groundcover().mEnabled;
|
const bool groundcover = Settings::groundcover().mEnabled;
|
||||||
const bool distantTerrain = Settings::terrain().mDistantTerrain;
|
const bool distantTerrain = Settings::terrain().mDistantTerrain;
|
||||||
|
const double expiryDelay = Settings::cells().mCacheExpiryDelay;
|
||||||
if (distantTerrain || groundcover)
|
if (distantTerrain || groundcover)
|
||||||
{
|
{
|
||||||
const int compMapResolution = Settings::terrain().mCompositeMapResolution;
|
const int compMapResolution = Settings::terrain().mCompositeMapResolution;
|
||||||
|
@ -1329,7 +1330,7 @@ namespace MWRender
|
||||||
const bool debugChunks = Settings::terrain().mDebugChunks;
|
const bool debugChunks = Settings::terrain().mDebugChunks;
|
||||||
auto quadTreeWorld = std::make_unique<Terrain::QuadTreeWorld>(mSceneRoot, mRootNode, mResourceSystem,
|
auto quadTreeWorld = std::make_unique<Terrain::QuadTreeWorld>(mSceneRoot, mRootNode, mResourceSystem,
|
||||||
mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug, compMapResolution, compMapLevel,
|
mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug, compMapResolution, compMapLevel,
|
||||||
lodFactor, vertexLodMod, maxCompGeometrySize, debugChunks, worldspace);
|
lodFactor, vertexLodMod, maxCompGeometrySize, debugChunks, worldspace, expiryDelay);
|
||||||
if (Settings::terrain().mObjectPaging)
|
if (Settings::terrain().mObjectPaging)
|
||||||
{
|
{
|
||||||
newChunkMgr.mObjectPaging
|
newChunkMgr.mObjectPaging
|
||||||
|
@ -1351,7 +1352,7 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
newChunkMgr.mTerrain = std::make_unique<Terrain::TerrainGrid>(mSceneRoot, mRootNode, mResourceSystem,
|
newChunkMgr.mTerrain = std::make_unique<Terrain::TerrainGrid>(mSceneRoot, mRootNode, mResourceSystem,
|
||||||
mTerrainStorage.get(), Mask_Terrain, worldspace, Mask_PreCompile, Mask_Debug);
|
mTerrainStorage.get(), Mask_Terrain, worldspace, expiryDelay, Mask_PreCompile, Mask_Debug);
|
||||||
|
|
||||||
newChunkMgr.mTerrain->setTargetFrameRate(Settings::cells().mTargetFramerate);
|
newChunkMgr.mTerrain->setTargetFrameRate(Settings::cells().mTargetFramerate);
|
||||||
float distanceMult = std::cos(osg::DegreesToRadians(std::min(mFieldOfView, 140.f)) / 2.f);
|
float distanceMult = std::cos(osg::DegreesToRadians(std::min(mFieldOfView, 140.f)) / 2.f);
|
||||||
|
|
|
@ -220,7 +220,7 @@ namespace
|
||||||
camera->setNodeMask(MWRender::Mask_RenderToTexture);
|
camera->setNodeMask(MWRender::Mask_RenderToTexture);
|
||||||
camera->setCullMask(MWRender::Mask_Sky);
|
camera->setCullMask(MWRender::Mask_Sky);
|
||||||
camera->addChild(mEarlyRenderBinRoot);
|
camera->addChild(mEarlyRenderBinRoot);
|
||||||
SceneUtil::ShadowManager::disableShadowsForStateSet(camera->getOrCreateStateSet());
|
SceneUtil::ShadowManager::disableShadowsForStateSet(Settings::shadows(), *camera->getOrCreateStateSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -271,7 +271,7 @@ namespace MWRender
|
||||||
if (!mSceneManager->getForceShaders())
|
if (!mSceneManager->getForceShaders())
|
||||||
skyroot->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(),
|
skyroot->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(),
|
||||||
osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
|
osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
|
||||||
SceneUtil::ShadowManager::disableShadowsForStateSet(skyroot->getOrCreateStateSet());
|
SceneUtil::ShadowManager::disableShadowsForStateSet(Settings::shadows(), *skyroot->getOrCreateStateSet());
|
||||||
parentNode->addChild(skyroot);
|
parentNode->addChild(skyroot);
|
||||||
|
|
||||||
mEarlyRenderBinRoot = new osg::Group;
|
mEarlyRenderBinRoot = new osg::Group;
|
||||||
|
|
|
@ -265,7 +265,8 @@ namespace MWRender
|
||||||
camera->setNodeMask(Mask_RenderToTexture);
|
camera->setNodeMask(Mask_RenderToTexture);
|
||||||
|
|
||||||
if (Settings::water().mRefractionScale != 1) // TODO: to be removed with issue #5709
|
if (Settings::water().mRefractionScale != 1) // TODO: to be removed with issue #5709
|
||||||
SceneUtil::ShadowManager::disableShadowsForStateSet(camera->getOrCreateStateSet());
|
SceneUtil::ShadowManager::disableShadowsForStateSet(
|
||||||
|
Settings::shadows(), *camera->getOrCreateStateSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply(osg::Camera* camera) override
|
void apply(osg::Camera* camera) override
|
||||||
|
@ -341,7 +342,7 @@ namespace MWRender
|
||||||
camera->addChild(mClipCullNode);
|
camera->addChild(mClipCullNode);
|
||||||
camera->setNodeMask(Mask_RenderToTexture);
|
camera->setNodeMask(Mask_RenderToTexture);
|
||||||
|
|
||||||
SceneUtil::ShadowManager::disableShadowsForStateSet(camera->getOrCreateStateSet());
|
SceneUtil::ShadowManager::disableShadowsForStateSet(Settings::shadows(), *camera->getOrCreateStateSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply(osg::Camera* camera) override
|
void apply(osg::Camera* camera) override
|
||||||
|
|
|
@ -111,12 +111,12 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState(
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::ContainerStore::storeEquipmentState(
|
void MWWorld::ContainerStore::storeEquipmentState(
|
||||||
const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const
|
const MWWorld::LiveCellRefBase& ref, size_t index, ESM::InventoryState& inventory) const
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::ContainerStore::readEquipmentState(
|
void MWWorld::ContainerStore::readEquipmentState(
|
||||||
const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory)
|
const MWWorld::ContainerStoreIterator& iter, size_t index, const ESM::InventoryState& inventory)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ void MWWorld::ContainerStore::storeState(const LiveCellRef<T>& ref, ESM::ObjectS
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void MWWorld::ContainerStore::storeStates(
|
void MWWorld::ContainerStore::storeStates(
|
||||||
const CellRefList<T>& collection, ESM::InventoryState& inventory, int& index, bool equipable) const
|
const CellRefList<T>& collection, ESM::InventoryState& inventory, size_t& index, bool equipable) const
|
||||||
{
|
{
|
||||||
for (const LiveCellRef<T>& liveCellRef : collection.mList)
|
for (const LiveCellRef<T>& liveCellRef : collection.mList)
|
||||||
{
|
{
|
||||||
|
@ -926,7 +926,7 @@ void MWWorld::ContainerStore::writeState(ESM::InventoryState& state) const
|
||||||
{
|
{
|
||||||
state.mItems.clear();
|
state.mItems.clear();
|
||||||
|
|
||||||
int index = 0;
|
size_t index = 0;
|
||||||
storeStates(potions, state, index);
|
storeStates(potions, state, index);
|
||||||
storeStates(appas, state, index);
|
storeStates(appas, state, index);
|
||||||
storeStates(armors, state, index, true);
|
storeStates(armors, state, index, true);
|
||||||
|
@ -947,12 +947,12 @@ void MWWorld::ContainerStore::readState(const ESM::InventoryState& inventory)
|
||||||
mModified = true;
|
mModified = true;
|
||||||
mResolved = true;
|
mResolved = true;
|
||||||
|
|
||||||
int index = 0;
|
size_t index = 0;
|
||||||
for (const ESM::ObjectState& state : inventory.mItems)
|
for (const ESM::ObjectState& state : inventory.mItems)
|
||||||
{
|
{
|
||||||
int type = MWBase::Environment::get().getESMStore()->find(state.mRef.mRefID);
|
int type = MWBase::Environment::get().getESMStore()->find(state.mRef.mRefID);
|
||||||
|
|
||||||
int thisIndex = index++;
|
size_t thisIndex = index++;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
|
|
@ -161,16 +161,16 @@ namespace MWWorld
|
||||||
void storeState(const LiveCellRef<T>& ref, ESM::ObjectState& state) const;
|
void storeState(const LiveCellRef<T>& ref, ESM::ObjectState& state) const;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void storeStates(
|
void storeStates(const CellRefList<T>& collection, ESM::InventoryState& inventory, size_t& index,
|
||||||
const CellRefList<T>& collection, ESM::InventoryState& inventory, int& index, bool equipable = false) const;
|
bool equipable = false) const;
|
||||||
|
|
||||||
void updateRechargingItems();
|
void updateRechargingItems();
|
||||||
|
|
||||||
virtual void storeEquipmentState(
|
virtual void storeEquipmentState(
|
||||||
const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const;
|
const MWWorld::LiveCellRefBase& ref, size_t index, ESM::InventoryState& inventory) const;
|
||||||
|
|
||||||
virtual void readEquipmentState(
|
virtual void readEquipmentState(
|
||||||
const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory);
|
const MWWorld::ContainerStoreIterator& iter, size_t index, const ESM::InventoryState& inventory);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ContainerStore();
|
ContainerStore();
|
||||||
|
|
|
@ -138,6 +138,59 @@ namespace
|
||||||
return npcsToReplace;
|
return npcsToReplace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class RecordType>
|
||||||
|
std::vector<RecordType> getSpellsToReplace(
|
||||||
|
const MWWorld::Store<RecordType>& spells, const MWWorld::Store<ESM::MagicEffect>& magicEffects)
|
||||||
|
{
|
||||||
|
std::vector<RecordType> spellsToReplace;
|
||||||
|
|
||||||
|
for (RecordType spell : spells)
|
||||||
|
{
|
||||||
|
if (spell.mEffects.mList.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
auto iter = spell.mEffects.mList.begin();
|
||||||
|
while (iter != spell.mEffects.mList.end())
|
||||||
|
{
|
||||||
|
const ESM::MagicEffect* mgef = magicEffects.search(iter->mEffectID);
|
||||||
|
if (!mgef)
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << RecordType::getRecordType() << " " << spell.mId
|
||||||
|
<< ": dropping invalid effect (index " << iter->mEffectID << ")";
|
||||||
|
iter = spell.mEffects.mList.erase(iter);
|
||||||
|
changed = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(mgef->mData.mFlags & ESM::MagicEffect::TargetAttribute) && iter->mAttribute != -1)
|
||||||
|
{
|
||||||
|
iter->mAttribute = -1;
|
||||||
|
Log(Debug::Verbose) << RecordType::getRecordType() << " " << spell.mId
|
||||||
|
<< ": dropping unexpected attribute argument of "
|
||||||
|
<< ESM::MagicEffect::indexToGmstString(iter->mEffectID) << " effect";
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(mgef->mData.mFlags & ESM::MagicEffect::TargetSkill) && iter->mSkill != -1)
|
||||||
|
{
|
||||||
|
iter->mSkill = -1;
|
||||||
|
Log(Debug::Verbose) << RecordType::getRecordType() << " " << spell.mId
|
||||||
|
<< ": dropping unexpected skill argument of "
|
||||||
|
<< ESM::MagicEffect::indexToGmstString(iter->mEffectID) << " effect";
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
spellsToReplace.emplace_back(spell);
|
||||||
|
}
|
||||||
|
|
||||||
|
return spellsToReplace;
|
||||||
|
}
|
||||||
|
|
||||||
// Custom enchanted items can reference scripts that no longer exist, this doesn't necessarily mean the base item no
|
// Custom enchanted items can reference scripts that no longer exist, this doesn't necessarily mean the base item no
|
||||||
// longer exists however. So instead of removing the item altogether, we're only removing the script.
|
// longer exists however. So instead of removing the item altogether, we're only removing the script.
|
||||||
template <class MapT>
|
template <class MapT>
|
||||||
|
@ -538,71 +591,24 @@ namespace MWWorld
|
||||||
|
|
||||||
removeMissingScripts(getWritable<ESM::Script>(), getWritable<ESM::Creature>().mStatic);
|
removeMissingScripts(getWritable<ESM::Script>(), getWritable<ESM::Creature>().mStatic);
|
||||||
|
|
||||||
// Validate spell effects for invalid arguments
|
// Validate spell effects and enchantments for invalid arguments
|
||||||
std::vector<ESM::Spell> spellsToReplace;
|
|
||||||
auto& spells = getWritable<ESM::Spell>();
|
auto& spells = getWritable<ESM::Spell>();
|
||||||
for (ESM::Spell spell : spells)
|
auto& enchantments = getWritable<ESM::Enchantment>();
|
||||||
{
|
auto& magicEffects = getWritable<ESM::MagicEffect>();
|
||||||
if (spell.mEffects.mList.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
auto iter = spell.mEffects.mList.begin();
|
|
||||||
while (iter != spell.mEffects.mList.end())
|
|
||||||
{
|
|
||||||
const ESM::MagicEffect* mgef = getWritable<ESM::MagicEffect>().search(iter->mEffectID);
|
|
||||||
if (!mgef)
|
|
||||||
{
|
|
||||||
Log(Debug::Verbose) << "Spell '" << spell.mId << "' has an invalid effect (index "
|
|
||||||
<< iter->mEffectID << ") present. Dropping the effect.";
|
|
||||||
iter = spell.mEffects.mList.erase(iter);
|
|
||||||
changed = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mgef->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
|
||||||
{
|
|
||||||
if (iter->mAttribute != -1)
|
|
||||||
{
|
|
||||||
iter->mAttribute = -1;
|
|
||||||
Log(Debug::Verbose)
|
|
||||||
<< ESM::MagicEffect::indexToGmstString(iter->mEffectID) << " effect of spell '" << spell.mId
|
|
||||||
<< "' has an attribute argument present. Dropping the argument.";
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (mgef->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
|
||||||
{
|
|
||||||
if (iter->mSkill != -1)
|
|
||||||
{
|
|
||||||
iter->mSkill = -1;
|
|
||||||
Log(Debug::Verbose)
|
|
||||||
<< ESM::MagicEffect::indexToGmstString(iter->mEffectID) << " effect of spell '" << spell.mId
|
|
||||||
<< "' has a skill argument present. Dropping the argument.";
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (iter->mSkill != -1 || iter->mAttribute != -1)
|
|
||||||
{
|
|
||||||
iter->mSkill = -1;
|
|
||||||
iter->mAttribute = -1;
|
|
||||||
Log(Debug::Verbose) << ESM::MagicEffect::indexToGmstString(iter->mEffectID) << " effect of spell '"
|
|
||||||
<< spell.mId << "' has argument(s) present. Dropping the argument(s).";
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed)
|
|
||||||
spellsToReplace.emplace_back(spell);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
std::vector<ESM::Spell> spellsToReplace = getSpellsToReplace(spells, magicEffects);
|
||||||
for (const ESM::Spell& spell : spellsToReplace)
|
for (const ESM::Spell& spell : spellsToReplace)
|
||||||
{
|
{
|
||||||
spells.eraseStatic(spell.mId);
|
spells.eraseStatic(spell.mId);
|
||||||
spells.insertStatic(spell);
|
spells.insertStatic(spell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<ESM::Enchantment> enchantmentsToReplace = getSpellsToReplace(enchantments, magicEffects);
|
||||||
|
for (const ESM::Enchantment& enchantment : enchantmentsToReplace)
|
||||||
|
{
|
||||||
|
enchantments.eraseStatic(enchantment.mId);
|
||||||
|
enchantments.insertStatic(enchantment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMStore::movePlayerRecord()
|
void ESMStore::movePlayerRecord()
|
||||||
|
|
|
@ -46,32 +46,32 @@ void MWWorld::InventoryStore::initSlots(TSlots& slots_)
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::storeEquipmentState(
|
void MWWorld::InventoryStore::storeEquipmentState(
|
||||||
const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const
|
const MWWorld::LiveCellRefBase& ref, size_t index, ESM::InventoryState& inventory) const
|
||||||
{
|
{
|
||||||
for (int i = 0; i < static_cast<int>(mSlots.size()); ++i)
|
for (int32_t i = 0; i < MWWorld::InventoryStore::Slots; ++i)
|
||||||
|
{
|
||||||
if (mSlots[i].getType() != -1 && mSlots[i]->getBase() == &ref)
|
if (mSlots[i].getType() != -1 && mSlots[i]->getBase() == &ref)
|
||||||
{
|
inventory.mEquipmentSlots[static_cast<uint32_t>(index)] = i;
|
||||||
inventory.mEquipmentSlots[index] = i;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (mSelectedEnchantItem.getType() != -1 && mSelectedEnchantItem->getBase() == &ref)
|
if (mSelectedEnchantItem.getType() != -1 && mSelectedEnchantItem->getBase() == &ref)
|
||||||
inventory.mSelectedEnchantItem = index;
|
inventory.mSelectedEnchantItem = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::readEquipmentState(
|
void MWWorld::InventoryStore::readEquipmentState(
|
||||||
const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory)
|
const MWWorld::ContainerStoreIterator& iter, size_t index, const ESM::InventoryState& inventory)
|
||||||
{
|
{
|
||||||
if (index == inventory.mSelectedEnchantItem)
|
if (index == inventory.mSelectedEnchantItem)
|
||||||
mSelectedEnchantItem = iter;
|
mSelectedEnchantItem = iter;
|
||||||
|
|
||||||
std::map<int, int>::const_iterator found = inventory.mEquipmentSlots.find(index);
|
auto found = inventory.mEquipmentSlots.find(index);
|
||||||
if (found != inventory.mEquipmentSlots.end())
|
if (found != inventory.mEquipmentSlots.end())
|
||||||
{
|
{
|
||||||
if (found->second < 0 || found->second >= MWWorld::InventoryStore::Slots)
|
if (found->second < 0 || found->second >= MWWorld::InventoryStore::Slots)
|
||||||
throw std::runtime_error("Invalid slot index in inventory state");
|
throw std::runtime_error("Invalid slot index in inventory state");
|
||||||
|
|
||||||
// make sure the item can actually be equipped in this slot
|
// make sure the item can actually be equipped in this slot
|
||||||
int slot = found->second;
|
int32_t slot = found->second;
|
||||||
std::pair<std::vector<int>, bool> allowedSlots = iter->getClass().getEquipmentSlots(*iter);
|
std::pair<std::vector<int>, bool> allowedSlots = iter->getClass().getEquipmentSlots(*iter);
|
||||||
if (!allowedSlots.first.size())
|
if (!allowedSlots.first.size())
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -81,9 +81,9 @@ namespace MWWorld
|
||||||
void fireEquipmentChangedEvent();
|
void fireEquipmentChangedEvent();
|
||||||
|
|
||||||
void storeEquipmentState(
|
void storeEquipmentState(
|
||||||
const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const override;
|
const MWWorld::LiveCellRefBase& ref, size_t index, ESM::InventoryState& inventory) const override;
|
||||||
void readEquipmentState(
|
void readEquipmentState(
|
||||||
const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory) override;
|
const MWWorld::ContainerStoreIterator& iter, size_t index, const ESM::InventoryState& inventory) override;
|
||||||
|
|
||||||
ContainerStoreIterator findSlot(int slot) const;
|
ContainerStoreIterator findSlot(int slot) const;
|
||||||
|
|
||||||
|
|
|
@ -789,8 +789,7 @@ namespace MWWorld
|
||||||
mRendering.configureFog(
|
mRendering.configureFog(
|
||||||
mResult.mFogDepth, underwaterFog, mResult.mDLFogFactor, mResult.mDLFogOffset / 100.0f, mResult.mFogColor);
|
mResult.mFogDepth, underwaterFog, mResult.mDLFogFactor, mResult.mDLFogOffset / 100.0f, mResult.mFogColor);
|
||||||
mRendering.setAmbientColour(mResult.mAmbientColor);
|
mRendering.setAmbientColour(mResult.mAmbientColor);
|
||||||
mRendering.setSunColour(
|
mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor, mResult.mGlareView * glareFade);
|
||||||
mResult.mSunColor, mResult.mSunColor * mResult.mGlareView * glareFade, mResult.mGlareView * glareFade);
|
|
||||||
|
|
||||||
mRendering.getSkyManager()->setWeather(mResult);
|
mRendering.getSkyManager()->setWeather(mResult);
|
||||||
|
|
||||||
|
|
|
@ -660,8 +660,8 @@ namespace MWWorld
|
||||||
|
|
||||||
std::string_view World::getCellName(const MWWorld::Cell& cell) const
|
std::string_view World::getCellName(const MWWorld::Cell& cell) const
|
||||||
{
|
{
|
||||||
if (!cell.isExterior() || !cell.getNameId().empty())
|
if (!cell.isExterior() || !cell.getDisplayName().empty())
|
||||||
return cell.getNameId();
|
return cell.getDisplayName();
|
||||||
|
|
||||||
return ESM::visit(ESM::VisitOverload{
|
return ESM::visit(ESM::VisitOverload{
|
||||||
[&](const ESM::Cell& cellIn) -> std::string_view { return getCellName(&cellIn); },
|
[&](const ESM::Cell& cellIn) -> std::string_view { return getCellName(&cellIn); },
|
||||||
|
|
|
@ -411,7 +411,7 @@ namespace
|
||||||
EXPECT_EQ(*result, expected);
|
EXPECT_EQ(*result, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestBulletNifLoader, for_root_bounding_box_should_return_shape_with_compound_shape_and_box_inside)
|
TEST_F(TestBulletNifLoader, for_root_bounding_box_should_return_shape_with_bounding_box_data)
|
||||||
{
|
{
|
||||||
mNode.mName = "Bounding Box";
|
mNode.mName = "Bounding Box";
|
||||||
mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
|
mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
|
||||||
|
@ -427,15 +427,11 @@ namespace
|
||||||
Resource::BulletShape expected;
|
Resource::BulletShape expected;
|
||||||
expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3);
|
expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3);
|
||||||
expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
|
expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
|
||||||
std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));
|
|
||||||
std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
|
|
||||||
shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());
|
|
||||||
expected.mCollisionShape.reset(shape.release());
|
|
||||||
|
|
||||||
EXPECT_EQ(*result, expected);
|
EXPECT_EQ(*result, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestBulletNifLoader, for_child_bounding_box_should_return_shape_with_compound_shape_with_box_inside)
|
TEST_F(TestBulletNifLoader, for_child_bounding_box_should_return_shape_with_bounding_box_data)
|
||||||
{
|
{
|
||||||
mNode.mName = "Bounding Box";
|
mNode.mName = "Bounding Box";
|
||||||
mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
|
mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
|
||||||
|
@ -453,15 +449,11 @@ namespace
|
||||||
Resource::BulletShape expected;
|
Resource::BulletShape expected;
|
||||||
expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3);
|
expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3);
|
||||||
expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
|
expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
|
||||||
std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));
|
|
||||||
std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
|
|
||||||
shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());
|
|
||||||
expected.mCollisionShape.reset(shape.release());
|
|
||||||
|
|
||||||
EXPECT_EQ(*result, expected);
|
EXPECT_EQ(*result, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestBulletNifLoader, for_root_with_bounds_and_child_bounding_box_but_should_use_bounding_box)
|
TEST_F(TestBulletNifLoader, for_root_with_bounds_and_child_bounding_box_should_use_bounding_box)
|
||||||
{
|
{
|
||||||
mNode.mName = "Bounding Box";
|
mNode.mName = "Bounding Box";
|
||||||
mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
|
mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
|
||||||
|
@ -483,10 +475,6 @@ namespace
|
||||||
Resource::BulletShape expected;
|
Resource::BulletShape expected;
|
||||||
expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3);
|
expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3);
|
||||||
expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
|
expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
|
||||||
std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));
|
|
||||||
std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
|
|
||||||
shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());
|
|
||||||
expected.mCollisionShape.reset(shape.release());
|
|
||||||
|
|
||||||
EXPECT_EQ(*result, expected);
|
EXPECT_EQ(*result, expected);
|
||||||
}
|
}
|
||||||
|
@ -519,10 +507,6 @@ namespace
|
||||||
Resource::BulletShape expected;
|
Resource::BulletShape expected;
|
||||||
expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3);
|
expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3);
|
||||||
expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
|
expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
|
||||||
std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));
|
|
||||||
std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
|
|
||||||
shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());
|
|
||||||
expected.mCollisionShape.reset(shape.release());
|
|
||||||
|
|
||||||
EXPECT_EQ(*result, expected);
|
EXPECT_EQ(*result, expected);
|
||||||
}
|
}
|
||||||
|
@ -555,10 +539,6 @@ namespace
|
||||||
Resource::BulletShape expected;
|
Resource::BulletShape expected;
|
||||||
expected.mCollisionBox.mExtents = osg::Vec3f(4, 5, 6);
|
expected.mCollisionBox.mExtents = osg::Vec3f(4, 5, 6);
|
||||||
expected.mCollisionBox.mCenter = osg::Vec3f(-4, -5, -6);
|
expected.mCollisionBox.mCenter = osg::Vec3f(-4, -5, -6);
|
||||||
std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(4, 5, 6)));
|
|
||||||
std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
|
|
||||||
shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-4, -5, -6)), box.release());
|
|
||||||
expected.mCollisionShape.reset(shape.release());
|
|
||||||
|
|
||||||
EXPECT_EQ(*result, expected);
|
EXPECT_EQ(*result, expected);
|
||||||
}
|
}
|
||||||
|
@ -977,12 +957,12 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestBulletNifLoader,
|
TEST_F(TestBulletNifLoader,
|
||||||
for_tri_shape_child_node_with_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision)
|
for_root_node_with_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision)
|
||||||
{
|
{
|
||||||
mNiStringExtraData.mData = "NCC__";
|
mNiStringExtraData.mData = "NCC__";
|
||||||
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
||||||
mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
|
|
||||||
mNiTriShape.mParents.push_back(&mNiNode);
|
mNiTriShape.mParents.push_back(&mNiNode);
|
||||||
|
mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||||
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
||||||
|
|
||||||
Nif::NIFFile file("test.nif");
|
Nif::NIFFile file("test.nif");
|
||||||
|
@ -1005,13 +985,13 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestBulletNifLoader,
|
TEST_F(TestBulletNifLoader,
|
||||||
for_tri_shape_child_node_with_not_first_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision)
|
for_root_node_with_not_first_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision)
|
||||||
{
|
{
|
||||||
mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2);
|
mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2);
|
||||||
mNiStringExtraData2.mData = "NCC__";
|
mNiStringExtraData2.mData = "NCC__";
|
||||||
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
|
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
|
||||||
mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
|
|
||||||
mNiTriShape.mParents.push_back(&mNiNode);
|
mNiTriShape.mParents.push_back(&mNiNode);
|
||||||
|
mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||||
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
||||||
|
|
||||||
Nif::NIFFile file("test.nif");
|
Nif::NIFFile file("test.nif");
|
||||||
|
@ -1032,8 +1012,62 @@ namespace
|
||||||
EXPECT_EQ(*result, expected);
|
EXPECT_EQ(*result, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(
|
||||||
|
TestBulletNifLoader, for_root_node_with_extra_data_string_starting_with_nc_should_return_shape_with_nocollision)
|
||||||
|
{
|
||||||
|
mNiStringExtraData.mData = "NC___";
|
||||||
|
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
||||||
|
mNiTriShape.mParents.push_back(&mNiNode);
|
||||||
|
mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||||
|
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
||||||
|
|
||||||
|
Nif::NIFFile file("test.nif");
|
||||||
|
file.mRoots.push_back(&mNiNode);
|
||||||
|
file.mHash = mHash;
|
||||||
|
|
||||||
|
const auto result = mLoader.load(file);
|
||||||
|
|
||||||
|
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
||||||
|
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
|
||||||
|
std::unique_ptr<btCompoundShape> compound(new btCompoundShape);
|
||||||
|
compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true));
|
||||||
|
|
||||||
|
Resource::BulletShape expected;
|
||||||
|
expected.mCollisionShape.reset(compound.release());
|
||||||
|
expected.mVisualCollisionType = Resource::VisualCollisionType::Default;
|
||||||
|
|
||||||
|
EXPECT_EQ(*result, expected);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TestBulletNifLoader,
|
TEST_F(TestBulletNifLoader,
|
||||||
for_tri_shape_child_node_with_extra_data_string_starting_with_nc_should_return_shape_with_nocollision)
|
for_root_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_nocollision)
|
||||||
|
{
|
||||||
|
mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2);
|
||||||
|
mNiStringExtraData2.mData = "NC___";
|
||||||
|
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
|
||||||
|
mNiTriShape.mParents.push_back(&mNiNode);
|
||||||
|
mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||||
|
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
||||||
|
|
||||||
|
Nif::NIFFile file("test.nif");
|
||||||
|
file.mRoots.push_back(&mNiNode);
|
||||||
|
file.mHash = mHash;
|
||||||
|
|
||||||
|
const auto result = mLoader.load(file);
|
||||||
|
|
||||||
|
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
||||||
|
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
|
||||||
|
std::unique_ptr<btCompoundShape> compound(new btCompoundShape);
|
||||||
|
compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true));
|
||||||
|
|
||||||
|
Resource::BulletShape expected;
|
||||||
|
expected.mCollisionShape.reset(compound.release());
|
||||||
|
expected.mVisualCollisionType = Resource::VisualCollisionType::Default;
|
||||||
|
|
||||||
|
EXPECT_EQ(*result, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_should_ignore_extra_data)
|
||||||
{
|
{
|
||||||
mNiStringExtraData.mData = "NC___";
|
mNiStringExtraData.mData = "NC___";
|
||||||
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
||||||
|
@ -1054,35 +1088,6 @@ namespace
|
||||||
|
|
||||||
Resource::BulletShape expected;
|
Resource::BulletShape expected;
|
||||||
expected.mCollisionShape.reset(compound.release());
|
expected.mCollisionShape.reset(compound.release());
|
||||||
expected.mVisualCollisionType = Resource::VisualCollisionType::Default;
|
|
||||||
|
|
||||||
EXPECT_EQ(*result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestBulletNifLoader,
|
|
||||||
for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_nocollision)
|
|
||||||
{
|
|
||||||
mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2);
|
|
||||||
mNiStringExtraData2.mData = "NC___";
|
|
||||||
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
|
|
||||||
mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
|
|
||||||
mNiTriShape.mParents.push_back(&mNiNode);
|
|
||||||
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
|
||||||
|
|
||||||
Nif::NIFFile file("test.nif");
|
|
||||||
file.mRoots.push_back(&mNiNode);
|
|
||||||
file.mHash = mHash;
|
|
||||||
|
|
||||||
const auto result = mLoader.load(file);
|
|
||||||
|
|
||||||
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
|
||||||
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
|
|
||||||
std::unique_ptr<btCompoundShape> compound(new btCompoundShape);
|
|
||||||
compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true));
|
|
||||||
|
|
||||||
Resource::BulletShape expected;
|
|
||||||
expected.mCollisionShape.reset(compound.release());
|
|
||||||
expected.mVisualCollisionType = Resource::VisualCollisionType::Default;
|
|
||||||
|
|
||||||
EXPECT_EQ(*result, expected);
|
EXPECT_EQ(*result, expected);
|
||||||
}
|
}
|
||||||
|
@ -1121,33 +1126,13 @@ namespace
|
||||||
EXPECT_EQ(*result, expected);
|
EXPECT_EQ(*result, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestBulletNifLoader,
|
|
||||||
for_tri_shape_child_node_with_extra_data_string_mrk_should_return_shape_with_null_collision_shape)
|
|
||||||
{
|
|
||||||
mNiStringExtraData.mData = "MRK";
|
|
||||||
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
|
||||||
mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
|
|
||||||
mNiTriShape.mParents.push_back(&mNiNode);
|
|
||||||
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
|
||||||
|
|
||||||
Nif::NIFFile file("test.nif");
|
|
||||||
file.mRoots.push_back(&mNiNode);
|
|
||||||
file.mHash = mHash;
|
|
||||||
|
|
||||||
const auto result = mLoader.load(file);
|
|
||||||
|
|
||||||
Resource::BulletShape expected;
|
|
||||||
|
|
||||||
EXPECT_EQ(*result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestBulletNifLoader, bsx_editor_marker_flag_disables_collision_for_markers)
|
TEST_F(TestBulletNifLoader, bsx_editor_marker_flag_disables_collision_for_markers)
|
||||||
{
|
{
|
||||||
mNiIntegerExtraData.mData = 34; // BSXFlags "has collision" | "editor marker"
|
|
||||||
mNiIntegerExtraData.recType = Nif::RC_BSXFlags;
|
|
||||||
mNiTriShape.mExtraList.push_back(Nif::ExtraPtr(&mNiIntegerExtraData));
|
|
||||||
mNiTriShape.mParents.push_back(&mNiNode);
|
mNiTriShape.mParents.push_back(&mNiNode);
|
||||||
mNiTriShape.mName = "EditorMarker";
|
mNiTriShape.mName = "EditorMarker";
|
||||||
|
mNiIntegerExtraData.mData = 34; // BSXFlags "has collision" | "editor marker"
|
||||||
|
mNiIntegerExtraData.recType = Nif::RC_BSXFlags;
|
||||||
|
mNiNode.mExtraList.push_back(Nif::ExtraPtr(&mNiIntegerExtraData));
|
||||||
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
||||||
|
|
||||||
Nif::NIFFile file("test.nif");
|
Nif::NIFFile file("test.nif");
|
||||||
|
@ -1162,18 +1147,14 @@ namespace
|
||||||
EXPECT_EQ(*result, expected);
|
EXPECT_EQ(*result, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestBulletNifLoader,
|
TEST_F(TestBulletNifLoader, mrk_editor_marker_flag_disables_collision_for_markers)
|
||||||
for_tri_shape_child_node_with_extra_data_string_mrk_and_other_collision_node_should_return_shape_with_triangle_mesh_shape_with_all_meshes)
|
|
||||||
{
|
{
|
||||||
|
mNiTriShape.mParents.push_back(&mNiNode);
|
||||||
|
mNiTriShape.mName = "Tri EditorMarker";
|
||||||
mNiStringExtraData.mData = "MRK";
|
mNiStringExtraData.mData = "MRK";
|
||||||
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
||||||
mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
|
mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||||
mNiTriShape.mParents.push_back(&mNiNode2);
|
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
||||||
mNiNode2.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
|
|
||||||
mNiNode2.recType = Nif::RC_RootCollisionNode;
|
|
||||||
mNiNode2.mParents.push_back(&mNiNode);
|
|
||||||
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiNode2) };
|
|
||||||
mNiNode.recType = Nif::RC_NiNode;
|
|
||||||
|
|
||||||
Nif::NIFFile file("test.nif");
|
Nif::NIFFile file("test.nif");
|
||||||
file.mRoots.push_back(&mNiNode);
|
file.mRoots.push_back(&mNiNode);
|
||||||
|
@ -1181,13 +1162,7 @@ namespace
|
||||||
|
|
||||||
const auto result = mLoader.load(file);
|
const auto result = mLoader.load(file);
|
||||||
|
|
||||||
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
|
||||||
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
|
|
||||||
std::unique_ptr<btCompoundShape> compound(new btCompoundShape);
|
|
||||||
compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true));
|
|
||||||
|
|
||||||
Resource::BulletShape expected;
|
Resource::BulletShape expected;
|
||||||
expected.mCollisionShape.reset(compound.release());
|
|
||||||
|
|
||||||
EXPECT_EQ(*result, expected);
|
EXPECT_EQ(*result, expected);
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ osg::Group {
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string formatOsgNodeForShaderProperty(std::string_view shaderPrefix)
|
std::string formatOsgNodeForBSShaderProperty(std::string_view shaderPrefix)
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << R"(
|
oss << R"(
|
||||||
|
@ -165,6 +165,72 @@ osg::Group {
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string formatOsgNodeForBSLightingShaderProperty(std::string_view shaderPrefix)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << R"(
|
||||||
|
osg::Group {
|
||||||
|
UniqueID 1
|
||||||
|
DataVariance STATIC
|
||||||
|
UserDataContainer TRUE {
|
||||||
|
osg::DefaultUserDataContainer {
|
||||||
|
UniqueID 2
|
||||||
|
UDC_UserObjects 1 {
|
||||||
|
osg::StringValueObject {
|
||||||
|
UniqueID 3
|
||||||
|
Name "fileHash"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Children 1 {
|
||||||
|
osg::Group {
|
||||||
|
UniqueID 4
|
||||||
|
DataVariance STATIC
|
||||||
|
UserDataContainer TRUE {
|
||||||
|
osg::DefaultUserDataContainer {
|
||||||
|
UniqueID 5
|
||||||
|
UDC_UserObjects 3 {
|
||||||
|
osg::UIntValueObject {
|
||||||
|
UniqueID 6
|
||||||
|
Name "recIndex"
|
||||||
|
Value 4294967295
|
||||||
|
}
|
||||||
|
osg::StringValueObject {
|
||||||
|
UniqueID 7
|
||||||
|
Name "shaderPrefix"
|
||||||
|
Value ")"
|
||||||
|
<< shaderPrefix << R"("
|
||||||
|
}
|
||||||
|
osg::BoolValueObject {
|
||||||
|
UniqueID 8
|
||||||
|
Name "shaderRequired"
|
||||||
|
Value TRUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StateSet TRUE {
|
||||||
|
osg::StateSet {
|
||||||
|
UniqueID 9
|
||||||
|
ModeList 1 {
|
||||||
|
GL_DEPTH_TEST ON
|
||||||
|
}
|
||||||
|
AttributeList 1 {
|
||||||
|
osg::Depth {
|
||||||
|
UniqueID 10
|
||||||
|
}
|
||||||
|
Value OFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
struct ShaderPrefixParams
|
struct ShaderPrefixParams
|
||||||
{
|
{
|
||||||
unsigned int mShaderType;
|
unsigned int mShaderType;
|
||||||
|
@ -194,7 +260,7 @@ osg::Group {
|
||||||
Nif::NIFFile file("test.nif");
|
Nif::NIFFile file("test.nif");
|
||||||
file.mRoots.push_back(&node);
|
file.mRoots.push_back(&node);
|
||||||
auto result = Loader::load(file, &mImageManager);
|
auto result = Loader::load(file, &mImageManager);
|
||||||
EXPECT_EQ(serialize(*result), formatOsgNodeForShaderProperty(GetParam().mExpectedShaderPrefix));
|
EXPECT_EQ(serialize(*result), formatOsgNodeForBSShaderProperty(GetParam().mExpectedShaderPrefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(Params, NifOsgLoaderBSShaderPrefixTest, ValuesIn(NifOsgLoaderBSShaderPrefixTest::sParams));
|
INSTANTIATE_TEST_SUITE_P(Params, NifOsgLoaderBSShaderPrefixTest, ValuesIn(NifOsgLoaderBSShaderPrefixTest::sParams));
|
||||||
|
@ -218,11 +284,13 @@ osg::Group {
|
||||||
property.mTextureSet = nullptr;
|
property.mTextureSet = nullptr;
|
||||||
property.mController = nullptr;
|
property.mController = nullptr;
|
||||||
property.mType = GetParam().mShaderType;
|
property.mType = GetParam().mShaderType;
|
||||||
|
property.mShaderFlags1 |= Nif::BSShaderFlags1::BSSFlag1_DepthTest;
|
||||||
|
property.mShaderFlags2 |= Nif::BSShaderFlags2::BSSFlag2_DepthWrite;
|
||||||
node.mProperties.push_back(Nif::RecordPtrT<Nif::NiProperty>(&property));
|
node.mProperties.push_back(Nif::RecordPtrT<Nif::NiProperty>(&property));
|
||||||
Nif::NIFFile file("test.nif");
|
Nif::NIFFile file("test.nif");
|
||||||
file.mRoots.push_back(&node);
|
file.mRoots.push_back(&node);
|
||||||
auto result = Loader::load(file, &mImageManager);
|
auto result = Loader::load(file, &mImageManager);
|
||||||
EXPECT_EQ(serialize(*result), formatOsgNodeForShaderProperty(GetParam().mExpectedShaderPrefix));
|
EXPECT_EQ(serialize(*result), formatOsgNodeForBSLightingShaderProperty(GetParam().mExpectedShaderPrefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
|
|
@ -16,10 +16,10 @@ namespace ESM
|
||||||
|
|
||||||
struct AIData
|
struct AIData
|
||||||
{
|
{
|
||||||
unsigned short mHello; // This is the base value for greeting distance [0, 65535]
|
uint16_t mHello; // This is the base value for greeting distance [0, 65535]
|
||||||
unsigned char mFight, mFlee, mAlarm; // These are probabilities [0, 100]
|
unsigned char mFight, mFlee, mAlarm; // These are probabilities [0, 100]
|
||||||
char mU1, mU2, mU3; // Unknown values
|
char mU1, mU2, mU3; // Unknown values
|
||||||
int mServices; // See the Services enum
|
int32_t mServices; // See the Services enum
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
@ -27,8 +27,8 @@ namespace ESM
|
||||||
|
|
||||||
struct AIWander
|
struct AIWander
|
||||||
{
|
{
|
||||||
short mDistance;
|
int16_t mDistance;
|
||||||
short mDuration;
|
int16_t mDuration;
|
||||||
unsigned char mTimeOfDay;
|
unsigned char mTimeOfDay;
|
||||||
unsigned char mIdle[8];
|
unsigned char mIdle[8];
|
||||||
unsigned char mShouldRepeat;
|
unsigned char mShouldRepeat;
|
||||||
|
@ -44,7 +44,7 @@ namespace ESM
|
||||||
struct AITarget
|
struct AITarget
|
||||||
{
|
{
|
||||||
float mX, mY, mZ;
|
float mX, mY, mZ;
|
||||||
short mDuration;
|
int16_t mDuration;
|
||||||
NAME32 mId;
|
NAME32 mId;
|
||||||
unsigned char mShouldRepeat;
|
unsigned char mShouldRepeat;
|
||||||
unsigned char mPadding;
|
unsigned char mPadding;
|
||||||
|
|
|
@ -196,7 +196,7 @@ namespace ESM
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while (esm.isNextSub("AIPK"))
|
while (esm.isNextSub("AIPK"))
|
||||||
{
|
{
|
||||||
int type;
|
int32_t type;
|
||||||
esm.getHT(type);
|
esm.getHT(type);
|
||||||
|
|
||||||
mPackages.emplace_back();
|
mPackages.emplace_back();
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace ESM
|
||||||
|
|
||||||
struct CreatureLevListState final : public ObjectState
|
struct CreatureLevListState final : public ObjectState
|
||||||
{
|
{
|
||||||
int mSpawnActorId;
|
int32_t mSpawnActorId;
|
||||||
bool mSpawn;
|
bool mSpawn;
|
||||||
|
|
||||||
void load(ESMReader& esm) override;
|
void load(ESMReader& esm) override;
|
||||||
|
|
|
@ -47,9 +47,8 @@ namespace ESM
|
||||||
std::vector<int> mSummonGraveyard;
|
std::vector<int> mSummonGraveyard;
|
||||||
|
|
||||||
TimeStamp mTradeTime;
|
TimeStamp mTradeTime;
|
||||||
int mGoldPool;
|
int32_t mGoldPool;
|
||||||
int mActorId;
|
int32_t mActorId;
|
||||||
// int mHitAttemptActorId;
|
|
||||||
|
|
||||||
enum Flags
|
enum Flags
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,14 +24,14 @@ namespace ESM
|
||||||
Flag_Global = 4 // make available from main menu (i.e. not location specific)
|
Flag_Global = 4 // make available from main menu (i.e. not location specific)
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
RefId mId;
|
RefId mId;
|
||||||
|
|
||||||
std::string mDescription;
|
std::string mDescription;
|
||||||
|
|
||||||
std::string mScriptText;
|
std::string mScriptText;
|
||||||
|
|
||||||
unsigned int mFlags;
|
uint32_t mFlags;
|
||||||
|
|
||||||
void load(ESMReader& esm, bool& isDeleted);
|
void load(ESMReader& esm, bool& isDeleted);
|
||||||
void save(ESMWriter& esm, bool isDeleted = false) const;
|
void save(ESMWriter& esm, bool isDeleted = false) const;
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace ESM
|
||||||
|
|
||||||
struct DoorState final : public ObjectState
|
struct DoorState final : public ObjectState
|
||||||
{
|
{
|
||||||
int mDoorState = 0;
|
int32_t mDoorState = 0;
|
||||||
|
|
||||||
void load(ESMReader& esm) override;
|
void load(ESMReader& esm) override;
|
||||||
void save(ESMWriter& esm, bool inInventory = false) const override;
|
void save(ESMWriter& esm, bool inInventory = false) const override;
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace ESM
|
||||||
|
|
||||||
static constexpr std::string_view getRecordType() { return "Filter"; }
|
static constexpr std::string_view getRecordType() { return "Filter"; }
|
||||||
|
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
RefId mId;
|
RefId mId;
|
||||||
|
|
||||||
std::string mDescription;
|
std::string mDescription;
|
||||||
|
|
|
@ -7,22 +7,25 @@
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr uint32_t sInvalidSlot = static_cast<uint32_t>(-1);
|
||||||
|
}
|
||||||
|
|
||||||
void InventoryState::load(ESMReader& esm)
|
void InventoryState::load(ESMReader& esm)
|
||||||
{
|
{
|
||||||
// obsolete
|
// obsolete
|
||||||
int index = 0;
|
uint32_t index = 0;
|
||||||
while (esm.isNextSub("IOBJ"))
|
while (esm.isNextSub("IOBJ"))
|
||||||
{
|
{
|
||||||
int unused; // no longer used
|
esm.skipHT<int32_t>();
|
||||||
esm.getHT(unused);
|
|
||||||
|
|
||||||
ObjectState state;
|
ObjectState state;
|
||||||
|
|
||||||
// obsolete
|
// obsolete
|
||||||
if (esm.isNextSub("SLOT"))
|
if (esm.isNextSub("SLOT"))
|
||||||
{
|
{
|
||||||
int slot;
|
int32_t slot;
|
||||||
esm.getHT(slot);
|
esm.getHT(slot);
|
||||||
mEquipmentSlots[index] = slot;
|
mEquipmentSlots[index] = slot;
|
||||||
}
|
}
|
||||||
|
@ -38,9 +41,9 @@ namespace ESM
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
|
|
||||||
int itemsCount = 0;
|
uint32_t itemsCount = 0;
|
||||||
esm.getHNOT(itemsCount, "ICNT");
|
esm.getHNOT(itemsCount, "ICNT");
|
||||||
for (int i = 0; i < itemsCount; i++)
|
for (; itemsCount > 0; --itemsCount)
|
||||||
{
|
{
|
||||||
ObjectState state;
|
ObjectState state;
|
||||||
|
|
||||||
|
@ -62,7 +65,7 @@ namespace ESM
|
||||||
{
|
{
|
||||||
// Get its name
|
// Get its name
|
||||||
ESM::RefId id = esm.getRefId();
|
ESM::RefId id = esm.getRefId();
|
||||||
int count;
|
int32_t count;
|
||||||
std::string parentGroup;
|
std::string parentGroup;
|
||||||
// Then get its count
|
// Then get its count
|
||||||
esm.getHNT(count, "COUN");
|
esm.getHNT(count, "COUN");
|
||||||
|
@ -91,9 +94,9 @@ namespace ESM
|
||||||
while (esm.isNextSub("EQUI"))
|
while (esm.isNextSub("EQUI"))
|
||||||
{
|
{
|
||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
int equipIndex;
|
int32_t equipIndex;
|
||||||
esm.getT(equipIndex);
|
esm.getT(equipIndex);
|
||||||
int slot;
|
int32_t slot;
|
||||||
esm.getT(slot);
|
esm.getT(slot);
|
||||||
mEquipmentSlots[equipIndex] = slot;
|
mEquipmentSlots[equipIndex] = slot;
|
||||||
}
|
}
|
||||||
|
@ -101,20 +104,24 @@ namespace ESM
|
||||||
if (esm.isNextSub("EQIP"))
|
if (esm.isNextSub("EQIP"))
|
||||||
{
|
{
|
||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
int slotsCount = 0;
|
uint32_t slotsCount = 0;
|
||||||
esm.getT(slotsCount);
|
esm.getT(slotsCount);
|
||||||
for (int i = 0; i < slotsCount; i++)
|
for (; slotsCount > 0; --slotsCount)
|
||||||
{
|
{
|
||||||
int equipIndex;
|
int32_t equipIndex;
|
||||||
esm.getT(equipIndex);
|
esm.getT(equipIndex);
|
||||||
int slot;
|
int32_t slot;
|
||||||
esm.getT(slot);
|
esm.getT(slot);
|
||||||
mEquipmentSlots[equipIndex] = slot;
|
mEquipmentSlots[equipIndex] = slot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mSelectedEnchantItem = -1;
|
uint32_t selectedEnchantItem = sInvalidSlot;
|
||||||
esm.getHNOT(mSelectedEnchantItem, "SELE");
|
esm.getHNOT(selectedEnchantItem, "SELE");
|
||||||
|
if (selectedEnchantItem == sInvalidSlot)
|
||||||
|
mSelectedEnchantItem.reset();
|
||||||
|
else
|
||||||
|
mSelectedEnchantItem = selectedEnchantItem;
|
||||||
|
|
||||||
// Old saves had restocking levelled items in a special map
|
// Old saves had restocking levelled items in a special map
|
||||||
// This turns items from that map into negative quantities
|
// This turns items from that map into negative quantities
|
||||||
|
@ -132,7 +139,7 @@ namespace ESM
|
||||||
|
|
||||||
void InventoryState::save(ESMWriter& esm) const
|
void InventoryState::save(ESMWriter& esm) const
|
||||||
{
|
{
|
||||||
int itemsCount = static_cast<int>(mItems.size());
|
uint32_t itemsCount = static_cast<uint32_t>(mItems.size());
|
||||||
if (itemsCount > 0)
|
if (itemsCount > 0)
|
||||||
{
|
{
|
||||||
esm.writeHNT("ICNT", itemsCount);
|
esm.writeHNT("ICNT", itemsCount);
|
||||||
|
@ -149,34 +156,32 @@ namespace ESM
|
||||||
esm.writeHNString("LGRP", it->first.second);
|
esm.writeHNString("LGRP", it->first.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TEffectMagnitudes::const_iterator it = mPermanentMagicEffectMagnitudes.begin();
|
for (const auto& [id, params] : mPermanentMagicEffectMagnitudes)
|
||||||
it != mPermanentMagicEffectMagnitudes.end(); ++it)
|
|
||||||
{
|
{
|
||||||
esm.writeHNRefId("MAGI", it->first);
|
esm.writeHNRefId("MAGI", id);
|
||||||
|
|
||||||
const std::vector<std::pair<float, float>>& params = it->second;
|
for (const auto& [rand, mult] : params)
|
||||||
for (std::vector<std::pair<float, float>>::const_iterator pIt = params.begin(); pIt != params.end(); ++pIt)
|
|
||||||
{
|
{
|
||||||
esm.writeHNT("RAND", pIt->first);
|
esm.writeHNT("RAND", rand);
|
||||||
esm.writeHNT("MULT", pIt->second);
|
esm.writeHNT("MULT", mult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int slotsCount = static_cast<int>(mEquipmentSlots.size());
|
uint32_t slotsCount = static_cast<uint32_t>(mEquipmentSlots.size());
|
||||||
if (slotsCount > 0)
|
if (slotsCount > 0)
|
||||||
{
|
{
|
||||||
esm.startSubRecord("EQIP");
|
esm.startSubRecord("EQIP");
|
||||||
esm.writeT(slotsCount);
|
esm.writeT(slotsCount);
|
||||||
for (std::map<int, int>::const_iterator it = mEquipmentSlots.begin(); it != mEquipmentSlots.end(); ++it)
|
for (const auto& [index, slot] : mEquipmentSlots)
|
||||||
{
|
{
|
||||||
esm.writeT(it->first);
|
esm.writeT(index);
|
||||||
esm.writeT(it->second);
|
esm.writeT(slot);
|
||||||
}
|
}
|
||||||
esm.endRecord("EQIP");
|
esm.endRecord("EQIP");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mSelectedEnchantItem != -1)
|
if (mSelectedEnchantItem)
|
||||||
esm.writeHNT("SELE", mSelectedEnchantItem);
|
esm.writeHNT("SELE", *mSelectedEnchantItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define OPENMW_ESM_INVENTORYSTATE_H
|
#define OPENMW_ESM_INVENTORYSTATE_H
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "objectstate.hpp"
|
#include "objectstate.hpp"
|
||||||
#include <components/esm/refid.hpp>
|
#include <components/esm/refid.hpp>
|
||||||
|
@ -19,20 +20,15 @@ namespace ESM
|
||||||
std::vector<ObjectState> mItems;
|
std::vector<ObjectState> mItems;
|
||||||
|
|
||||||
// <Index in mItems, equipment slot>
|
// <Index in mItems, equipment slot>
|
||||||
std::map<int, int> mEquipmentSlots;
|
std::map<uint32_t, int32_t> mEquipmentSlots;
|
||||||
|
|
||||||
std::map<std::pair<ESM::RefId, std::string>, int> mLevelledItemMap;
|
std::map<std::pair<ESM::RefId, std::string>, int32_t> mLevelledItemMap;
|
||||||
|
|
||||||
typedef std::map<ESM::RefId, std::vector<std::pair<float, float>>> TEffectMagnitudes;
|
std::map<ESM::RefId, std::vector<std::pair<float, float>>> mPermanentMagicEffectMagnitudes;
|
||||||
TEffectMagnitudes mPermanentMagicEffectMagnitudes;
|
|
||||||
|
|
||||||
int mSelectedEnchantItem; // For inventories only
|
std::optional<uint32_t> mSelectedEnchantItem; // For inventories only
|
||||||
|
|
||||||
InventoryState()
|
virtual ~InventoryState() = default;
|
||||||
: mSelectedEnchantItem(-1)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
virtual ~InventoryState() {}
|
|
||||||
|
|
||||||
virtual void load(ESMReader& esm);
|
virtual void load(ESMReader& esm);
|
||||||
virtual void save(ESMWriter& esm) const;
|
virtual void save(ESMWriter& esm) const;
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace ESM
|
||||||
///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum
|
///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum
|
||||||
void adjustRefNum(RefNum& refNum, const ESMReader& reader)
|
void adjustRefNum(RefNum& refNum, const ESMReader& reader)
|
||||||
{
|
{
|
||||||
unsigned int local = (refNum.mIndex & 0xff000000) >> 24;
|
uint32_t local = (refNum.mIndex & 0xff000000) >> 24;
|
||||||
|
|
||||||
// If we have an index value that does not make sense, assume that it was an addition
|
// If we have an index value that does not make sense, assume that it was an addition
|
||||||
// by the present plugin (but a faulty one)
|
// by the present plugin (but a faulty one)
|
||||||
|
@ -124,7 +124,7 @@ namespace ESM
|
||||||
switch (esm.retSubName().toInt())
|
switch (esm.retSubName().toInt())
|
||||||
{
|
{
|
||||||
case fourCC("INTV"):
|
case fourCC("INTV"):
|
||||||
int waterl;
|
int32_t waterl;
|
||||||
esm.getHT(waterl);
|
esm.getHT(waterl);
|
||||||
mWater = static_cast<float>(waterl);
|
mWater = static_cast<float>(waterl);
|
||||||
mWaterInt = true;
|
mWaterInt = true;
|
||||||
|
@ -192,7 +192,7 @@ namespace ESM
|
||||||
{
|
{
|
||||||
if (mWaterInt)
|
if (mWaterInt)
|
||||||
{
|
{
|
||||||
int water = (mWater >= 0) ? (int)(mWater + 0.5) : (int)(mWater - 0.5);
|
int32_t water = (mWater >= 0) ? static_cast<int32_t>(mWater + 0.5) : static_cast<int32_t>(mWater - 0.5);
|
||||||
esm.writeHNT("INTV", water);
|
esm.writeHNT("INTV", water);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -218,13 +218,13 @@ namespace ESM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::saveTempMarker(ESMWriter& esm, int tempCount) const
|
void Cell::saveTempMarker(ESMWriter& esm, int32_t tempCount) const
|
||||||
{
|
{
|
||||||
if (tempCount != 0)
|
if (tempCount != 0)
|
||||||
esm.writeHNT("NAM0", tempCount);
|
esm.writeHNT("NAM0", tempCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::restore(ESMReader& esm, int iCtx) const
|
void Cell::restore(ESMReader& esm, size_t iCtx) const
|
||||||
{
|
{
|
||||||
esm.restoreContext(mContextList.at(iCtx));
|
esm.restoreContext(mContextList.at(iCtx));
|
||||||
}
|
}
|
||||||
|
@ -321,7 +321,7 @@ namespace ESM
|
||||||
|
|
||||||
void Cell::blank()
|
void Cell::blank()
|
||||||
{
|
{
|
||||||
mName = "";
|
mName.clear();
|
||||||
mRegion = ESM::RefId();
|
mRegion = ESM::RefId();
|
||||||
mWater = 0;
|
mWater = 0;
|
||||||
mWaterInt = false;
|
mWaterInt = false;
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace ESM
|
||||||
RefNum mRefNum;
|
RefNum mRefNum;
|
||||||
|
|
||||||
// Coordinates of target exterior cell
|
// Coordinates of target exterior cell
|
||||||
int mTarget[2];
|
int32_t mTarget[2];
|
||||||
|
|
||||||
// The content file format does not support moving objects to an interior cell.
|
// The content file format does not support moving objects to an interior cell.
|
||||||
// The save game format does support moving to interior cells, but uses a different mechanism
|
// The save game format does support moving to interior cells, but uses a different mechanism
|
||||||
|
@ -153,13 +153,13 @@ namespace ESM
|
||||||
ESMReader& esm, bool saveContext = true); // Load everything, except NAME, DATAstruct and references
|
ESMReader& esm, bool saveContext = true); // Load everything, except NAME, DATAstruct and references
|
||||||
|
|
||||||
void save(ESMWriter& esm, bool isDeleted = false) const;
|
void save(ESMWriter& esm, bool isDeleted = false) const;
|
||||||
void saveTempMarker(ESMWriter& esm, int tempCount) const;
|
void saveTempMarker(ESMWriter& esm, int32_t tempCount) const;
|
||||||
|
|
||||||
bool isExterior() const { return !(mData.mFlags & Interior); }
|
bool isExterior() const { return !(mData.mFlags & Interior); }
|
||||||
|
|
||||||
int getGridX() const { return mData.mX; }
|
int32_t getGridX() const { return mData.mX; }
|
||||||
|
|
||||||
int getGridY() const { return mData.mY; }
|
int32_t getGridY() const { return mData.mY; }
|
||||||
|
|
||||||
bool hasWater() const { return ((mData.mFlags & HasWater) != 0) || isExterior(); }
|
bool hasWater() const { return ((mData.mFlags & HasWater) != 0) || isExterior(); }
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ namespace ESM
|
||||||
// somewhere other than the file system, you need to pre-open the
|
// somewhere other than the file system, you need to pre-open the
|
||||||
// ESMReader, and the filename must match the stored filename
|
// ESMReader, and the filename must match the stored filename
|
||||||
// exactly.
|
// exactly.
|
||||||
void restore(ESMReader& esm, int iCtx) const;
|
void restore(ESMReader& esm, size_t iCtx) const;
|
||||||
|
|
||||||
std::string getDescription() const;
|
std::string getDescription() const;
|
||||||
///< Return a short string describing the cell (mostly used for debugging/logging purpose)
|
///< Return a short string describing the cell (mostly used for debugging/logging purpose)
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace ESM
|
||||||
|
|
||||||
struct ContItem
|
struct ContItem
|
||||||
{
|
{
|
||||||
int mCount{ 0 };
|
int32_t mCount{ 0 };
|
||||||
ESM::RefId mItem;
|
ESM::RefId mItem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,12 +48,12 @@ namespace ESM
|
||||||
Unknown = 8
|
Unknown = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
RefId mId, mScript;
|
RefId mId, mScript;
|
||||||
std::string mName, mModel;
|
std::string mName, mModel;
|
||||||
|
|
||||||
float mWeight; // Not sure, might be max total weight allowed?
|
float mWeight; // Not sure, might be max total weight allowed?
|
||||||
int mFlags;
|
int32_t mFlags;
|
||||||
InventoryList mInventory;
|
InventoryList mInventory;
|
||||||
|
|
||||||
void load(ESMReader& esm, bool& isDeleted);
|
void load(ESMReader& esm, bool& isDeleted);
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace ESM
|
||||||
/// Return a string descriptor for this record type. Currently used for debugging / error logs only.
|
/// Return a string descriptor for this record type. Currently used for debugging / error logs only.
|
||||||
static std::string_view getRecordType() { return "Global"; }
|
static std::string_view getRecordType() { return "Global"; }
|
||||||
|
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
ESM::RefId mId;
|
ESM::RefId mId;
|
||||||
Variant mValue;
|
Variant mValue;
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace ESM
|
||||||
/// Return a string descriptor for this record type. Currently used for debugging / error logs only.
|
/// Return a string descriptor for this record type. Currently used for debugging / error logs only.
|
||||||
static std::string_view getRecordType() { return "GameSetting"; }
|
static std::string_view getRecordType() { return "GameSetting"; }
|
||||||
|
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
RefId mId;
|
RefId mId;
|
||||||
|
|
||||||
Variant mValue;
|
Variant mValue;
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace ESM
|
||||||
break;
|
break;
|
||||||
case fourCC("INDX"):
|
case fourCC("INDX"):
|
||||||
{
|
{
|
||||||
int length = 0;
|
uint32_t length = 0;
|
||||||
esm.getHT(length);
|
esm.getHT(length);
|
||||||
mList.resize(length);
|
mList.resize(length);
|
||||||
|
|
||||||
|
@ -87,12 +87,12 @@ namespace ESM
|
||||||
|
|
||||||
esm.writeHNT("DATA", mFlags);
|
esm.writeHNT("DATA", mFlags);
|
||||||
esm.writeHNT("NNAM", mChanceNone);
|
esm.writeHNT("NNAM", mChanceNone);
|
||||||
esm.writeHNT<int>("INDX", mList.size());
|
esm.writeHNT<uint32_t>("INDX", mList.size());
|
||||||
|
|
||||||
for (std::vector<LevelItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
|
for (const auto& item : mList)
|
||||||
{
|
{
|
||||||
esm.writeHNCRefId(recName, it->mId);
|
esm.writeHNCRefId(recName, item.mId);
|
||||||
esm.writeHNT("INTV", it->mLevel);
|
esm.writeHNT("INTV", item.mLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,15 +24,15 @@ namespace ESM
|
||||||
|
|
||||||
struct LevelledListBase
|
struct LevelledListBase
|
||||||
{
|
{
|
||||||
int mFlags;
|
int32_t mFlags;
|
||||||
unsigned char mChanceNone; // Chance that none are selected (0-100)
|
unsigned char mChanceNone; // Chance that none are selected (0-100)
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
RefId mId;
|
RefId mId;
|
||||||
|
|
||||||
struct LevelItem
|
struct LevelItem
|
||||||
{
|
{
|
||||||
RefId mId;
|
RefId mId;
|
||||||
short mLevel;
|
uint16_t mLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<LevelItem> mList;
|
std::vector<LevelItem> mList;
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace ESM
|
||||||
// mId is merely a user friendly name for the texture in the editor.
|
// mId is merely a user friendly name for the texture in the editor.
|
||||||
std::string mTexture;
|
std::string mTexture;
|
||||||
RefId mId;
|
RefId mId;
|
||||||
int mIndex;
|
int32_t mIndex;
|
||||||
|
|
||||||
void load(ESMReader& esm, bool& isDeleted);
|
void load(ESMReader& esm, bool& isDeleted);
|
||||||
void save(ESMWriter& esm, bool isDeleted = false) const;
|
void save(ESMWriter& esm, bool isDeleted = false) const;
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace ESM
|
||||||
|
|
||||||
esm.getSubNameIs("MEDT");
|
esm.getSubNameIs("MEDT");
|
||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
int school;
|
int32_t school;
|
||||||
esm.getT(school);
|
esm.getT(school);
|
||||||
mData.mSchool = MagicSchool::indexToSkillRefId(school);
|
mData.mSchool = MagicSchool::indexToSkillRefId(school);
|
||||||
esm.getT(mData.mBaseCost);
|
esm.getT(mData.mBaseCost);
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace ESM
|
||||||
/// Return a string descriptor for this record type. Currently used for debugging / error logs only.
|
/// Return a string descriptor for this record type. Currently used for debugging / error logs only.
|
||||||
static std::string_view getRecordType() { return "MagicEffect"; }
|
static std::string_view getRecordType() { return "MagicEffect"; }
|
||||||
|
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
RefId mId;
|
RefId mId;
|
||||||
|
|
||||||
enum Flags
|
enum Flags
|
||||||
|
@ -74,9 +74,9 @@ namespace ESM
|
||||||
{
|
{
|
||||||
RefId mSchool; // Skill id
|
RefId mSchool; // Skill id
|
||||||
float mBaseCost;
|
float mBaseCost;
|
||||||
int mFlags;
|
int32_t mFlags;
|
||||||
// Glow color for enchanted items with this effect
|
// Glow color for enchanted items with this effect
|
||||||
int mRed, mGreen, mBlue;
|
int32_t mRed, mGreen, mBlue;
|
||||||
|
|
||||||
float mUnknown1; // Called "Size X" in CS
|
float mUnknown1; // Called "Size X" in CS
|
||||||
float mSpeed; // Speed of fired projectile
|
float mSpeed; // Speed of fired projectile
|
||||||
|
@ -107,7 +107,7 @@ namespace ESM
|
||||||
// there. They can be redefined in mods by setting the name in GMST
|
// there. They can be redefined in mods by setting the name in GMST
|
||||||
// sEffectSummonCreature04/05 creature id in
|
// sEffectSummonCreature04/05 creature id in
|
||||||
// sMagicCreature04ID/05ID.
|
// sMagicCreature04ID/05ID.
|
||||||
int mIndex;
|
int32_t mIndex;
|
||||||
|
|
||||||
void load(ESMReader& esm, bool& isDeleted);
|
void load(ESMReader& esm, bool& isDeleted);
|
||||||
void save(ESMWriter& esm, bool isDeleted = false) const;
|
void save(ESMWriter& esm, bool isDeleted = false) const;
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace ESM
|
||||||
break;
|
break;
|
||||||
case fourCC("FLAG"):
|
case fourCC("FLAG"):
|
||||||
hasFlags = true;
|
hasFlags = true;
|
||||||
int flags;
|
int32_t flags;
|
||||||
esm.getHT(flags);
|
esm.getHT(flags);
|
||||||
mFlags = flags & 0xFF;
|
mFlags = flags & 0xFF;
|
||||||
mBloodType = ((flags >> 8) & 0xFF) >> 2;
|
mBloodType = ((flags >> 8) & 0xFF) >> 2;
|
||||||
|
|
|
@ -79,28 +79,28 @@ namespace ESM
|
||||||
|
|
||||||
struct NPDTstruct52
|
struct NPDTstruct52
|
||||||
{
|
{
|
||||||
short mLevel;
|
int16_t mLevel;
|
||||||
unsigned char mStrength, mIntelligence, mWillpower, mAgility, mSpeed, mEndurance, mPersonality, mLuck;
|
unsigned char mStrength, mIntelligence, mWillpower, mAgility, mSpeed, mEndurance, mPersonality, mLuck;
|
||||||
|
|
||||||
// mSkill can grow up to 200, it must be unsigned
|
// mSkill can grow up to 200, it must be unsigned
|
||||||
std::array<unsigned char, Skill::Length> mSkills;
|
std::array<unsigned char, Skill::Length> mSkills;
|
||||||
|
|
||||||
char mUnknown1;
|
char mUnknown1;
|
||||||
unsigned short mHealth, mMana, mFatigue;
|
uint16_t mHealth, mMana, mFatigue;
|
||||||
unsigned char mDisposition, mReputation, mRank;
|
unsigned char mDisposition, mReputation, mRank;
|
||||||
char mUnknown2;
|
char mUnknown2;
|
||||||
int mGold;
|
int32_t mGold;
|
||||||
}; // 52 bytes
|
}; // 52 bytes
|
||||||
|
|
||||||
// Structure for autocalculated characters.
|
// Structure for autocalculated characters.
|
||||||
// This is only used for load and save operations.
|
// This is only used for load and save operations.
|
||||||
struct NPDTstruct12
|
struct NPDTstruct12
|
||||||
{
|
{
|
||||||
short mLevel;
|
int16_t mLevel;
|
||||||
// see above
|
// see above
|
||||||
unsigned char mDisposition, mReputation, mRank;
|
unsigned char mDisposition, mReputation, mRank;
|
||||||
char mUnknown1, mUnknown2, mUnknown3;
|
char mUnknown1, mUnknown2, mUnknown3;
|
||||||
int mGold;
|
int32_t mGold;
|
||||||
}; // 12 bytes
|
}; // 12 bytes
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ namespace ESM
|
||||||
|
|
||||||
int getFactionRank() const; /// wrapper for mNpdt*, -1 = no rank
|
int getFactionRank() const; /// wrapper for mNpdt*, -1 = no rank
|
||||||
|
|
||||||
int mBloodType;
|
int32_t mBloodType;
|
||||||
unsigned char mFlags;
|
unsigned char mFlags;
|
||||||
|
|
||||||
InventoryList mInventory;
|
InventoryList mInventory;
|
||||||
|
@ -125,7 +125,7 @@ namespace ESM
|
||||||
|
|
||||||
AIPackageList mAiPackage;
|
AIPackageList mAiPackage;
|
||||||
|
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
RefId mId, mRace, mClass, mFaction, mScript;
|
RefId mId, mRace, mClass, mFaction, mScript;
|
||||||
std::string mModel, mName;
|
std::string mModel, mName;
|
||||||
|
|
||||||
|
|
|
@ -27,13 +27,13 @@ namespace ESM
|
||||||
|
|
||||||
struct SkillBonus
|
struct SkillBonus
|
||||||
{
|
{
|
||||||
int mSkill; // SkillEnum
|
int32_t mSkill; // SkillEnum
|
||||||
int mBonus;
|
int32_t mBonus;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MaleFemale
|
struct MaleFemale
|
||||||
{
|
{
|
||||||
int mMale, mFemale;
|
int32_t mMale, mFemale;
|
||||||
|
|
||||||
int getValue(bool male) const;
|
int getValue(bool male) const;
|
||||||
};
|
};
|
||||||
|
@ -63,13 +63,13 @@ namespace ESM
|
||||||
// as 'height' times 128. This has not been tested yet.
|
// as 'height' times 128. This has not been tested yet.
|
||||||
MaleFemaleF mHeight, mWeight;
|
MaleFemaleF mHeight, mWeight;
|
||||||
|
|
||||||
int mFlags; // 0x1 - playable, 0x2 - beast race
|
int32_t mFlags; // 0x1 - playable, 0x2 - beast race
|
||||||
|
|
||||||
}; // Size = 140 bytes
|
}; // Size = 140 bytes
|
||||||
|
|
||||||
RADTstruct mData;
|
RADTstruct mData;
|
||||||
|
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
std::string mName, mDescription;
|
std::string mName, mDescription;
|
||||||
RefId mId;
|
RefId mId;
|
||||||
SpellList mPowers;
|
SpellList mPowers;
|
||||||
|
|
|
@ -36,9 +36,9 @@ namespace ESM
|
||||||
};
|
};
|
||||||
|
|
||||||
// Type
|
// Type
|
||||||
int mType;
|
int32_t mType;
|
||||||
|
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
RefId mId, mCreature, mSound;
|
RefId mId, mCreature, mSound;
|
||||||
|
|
||||||
void load(ESMReader& esm, bool& isDeleted);
|
void load(ESMReader& esm, bool& isDeleted);
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace ESM
|
||||||
static std::string_view getRecordType() { return "StartScript"; }
|
static std::string_view getRecordType() { return "StartScript"; }
|
||||||
|
|
||||||
std::string mData;
|
std::string mData;
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
RefId mId;
|
RefId mId;
|
||||||
|
|
||||||
// Load a record and add it to the list
|
// Load a record and add it to the list
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace ESM
|
||||||
/// Return a string descriptor for this record type. Currently used for debugging / error logs only.
|
/// Return a string descriptor for this record type. Currently used for debugging / error logs only.
|
||||||
static std::string_view getRecordType() { return "Static"; }
|
static std::string_view getRecordType() { return "Static"; }
|
||||||
|
|
||||||
unsigned int mRecordFlags;
|
uint32_t mRecordFlags;
|
||||||
RefId mId;
|
RefId mId;
|
||||||
std::string mModel;
|
std::string mModel;
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,11 @@ namespace ESM
|
||||||
versions are 1.2 and 1.3. These correspond to:
|
versions are 1.2 and 1.3. These correspond to:
|
||||||
1.2 = 0x3f99999a and 1.3 = 0x3fa66666
|
1.2 = 0x3f99999a and 1.3 = 0x3fa66666
|
||||||
*/
|
*/
|
||||||
unsigned int version;
|
uint32_t version;
|
||||||
int type; // 0=esp, 1=esm, 32=ess (unused)
|
int32_t type; // 0=esp, 1=esm, 32=ess (unused)
|
||||||
std::string author; // Author's name
|
std::string author; // Author's name
|
||||||
std::string desc; // File description
|
std::string desc; // File description
|
||||||
int records; // Number of records
|
int32_t records; // Number of records
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GMDT
|
struct GMDT
|
||||||
|
|
|
@ -20,8 +20,8 @@ namespace ESM
|
||||||
{
|
{
|
||||||
while (esm.isNextSub("EFID"))
|
while (esm.isNextSub("EFID"))
|
||||||
{
|
{
|
||||||
int id;
|
int32_t id;
|
||||||
std::pair<int, float> params;
|
std::pair<int32_t, float> params;
|
||||||
esm.getHT(id);
|
esm.getHT(id);
|
||||||
esm.getHNT(params.first, "BASE");
|
esm.getHNT(params.first, "BASE");
|
||||||
if (esm.getFormatVersion() <= MaxClearModifiersFormatVersion)
|
if (esm.getFormatVersion() <= MaxClearModifiersFormatVersion)
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace ESM
|
||||||
struct MagicEffects
|
struct MagicEffects
|
||||||
{
|
{
|
||||||
// <Effect Id, Base value, Modifier>
|
// <Effect Id, Base value, Modifier>
|
||||||
std::map<int, std::pair<int, float>> mEffects;
|
std::map<int32_t, std::pair<int32_t, float>> mEffects;
|
||||||
|
|
||||||
void load(ESMReader& esm);
|
void load(ESMReader& esm);
|
||||||
void save(ESMWriter& esm) const;
|
void save(ESMWriter& esm) const;
|
||||||
|
@ -24,16 +24,16 @@ namespace ESM
|
||||||
|
|
||||||
struct SummonKey
|
struct SummonKey
|
||||||
{
|
{
|
||||||
SummonKey(int effectId, const ESM::RefId& sourceId, int index)
|
SummonKey(int32_t effectId, const ESM::RefId& sourceId, int32_t index)
|
||||||
: mEffectId(effectId)
|
: mEffectId(effectId)
|
||||||
, mSourceId(sourceId)
|
, mSourceId(sourceId)
|
||||||
, mEffectIndex(index)
|
, mEffectIndex(index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int mEffectId;
|
int32_t mEffectId;
|
||||||
ESM::RefId mSourceId;
|
ESM::RefId mSourceId;
|
||||||
int mEffectIndex;
|
int32_t mEffectIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline auto makeTupleRef(const SummonKey& value) noexcept
|
inline auto makeTupleRef(const SummonKey& value) noexcept
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace ESM
|
||||||
|
|
||||||
Faction faction;
|
Faction faction;
|
||||||
|
|
||||||
int expelled = 0;
|
int32_t expelled = 0;
|
||||||
esm.getHNOT(expelled, "FAEX");
|
esm.getHNOT(expelled, "FAEX");
|
||||||
|
|
||||||
if (expelled)
|
if (expelled)
|
||||||
|
@ -75,7 +75,7 @@ namespace ESM
|
||||||
esm.getHNOT(hasWerewolfAttributes, "HWAT");
|
esm.getHNOT(hasWerewolfAttributes, "HWAT");
|
||||||
if (hasWerewolfAttributes)
|
if (hasWerewolfAttributes)
|
||||||
{
|
{
|
||||||
StatState<int> dummy;
|
StatState<int32_t> dummy;
|
||||||
for (int i = 0; i < ESM::Attribute::Length; ++i)
|
for (int i = 0; i < ESM::Attribute::Length; ++i)
|
||||||
dummy.load(esm, intFallback);
|
dummy.load(esm, intFallback);
|
||||||
mWerewolfDeprecatedData = true;
|
mWerewolfDeprecatedData = true;
|
||||||
|
@ -130,21 +130,21 @@ namespace ESM
|
||||||
|
|
||||||
void NpcStats::save(ESMWriter& esm) const
|
void NpcStats::save(ESMWriter& esm) const
|
||||||
{
|
{
|
||||||
for (auto iter(mFactions.begin()); iter != mFactions.end(); ++iter)
|
for (const auto& [id, faction] : mFactions)
|
||||||
{
|
{
|
||||||
esm.writeHNRefId("FACT", iter->first);
|
esm.writeHNRefId("FACT", id);
|
||||||
|
|
||||||
if (iter->second.mExpelled)
|
if (faction.mExpelled)
|
||||||
{
|
{
|
||||||
int expelled = 1;
|
int32_t expelled = 1;
|
||||||
esm.writeHNT("FAEX", expelled);
|
esm.writeHNT("FAEX", expelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter->second.mRank >= 0)
|
if (faction.mRank >= 0)
|
||||||
esm.writeHNT("FARA", iter->second.mRank);
|
esm.writeHNT("FARA", faction.mRank);
|
||||||
|
|
||||||
if (iter->second.mReputation)
|
if (faction.mReputation)
|
||||||
esm.writeHNT("FARE", iter->second.mReputation);
|
esm.writeHNT("FARE", faction.mReputation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDisposition)
|
if (mDisposition)
|
||||||
|
@ -169,7 +169,7 @@ namespace ESM
|
||||||
esm.writeHNT("LPRO", mLevelProgress);
|
esm.writeHNT("LPRO", mLevelProgress);
|
||||||
|
|
||||||
bool saveSkillIncreases = false;
|
bool saveSkillIncreases = false;
|
||||||
for (int increase : mSkillIncrease)
|
for (int32_t increase : mSkillIncrease)
|
||||||
{
|
{
|
||||||
if (increase != 0)
|
if (increase != 0)
|
||||||
{
|
{
|
||||||
|
@ -183,8 +183,8 @@ namespace ESM
|
||||||
if (mSpecIncreases[0] != 0 || mSpecIncreases[1] != 0 || mSpecIncreases[2] != 0)
|
if (mSpecIncreases[0] != 0 || mSpecIncreases[1] != 0 || mSpecIncreases[2] != 0)
|
||||||
esm.writeHNT("SPEC", mSpecIncreases);
|
esm.writeHNT("SPEC", mSpecIncreases);
|
||||||
|
|
||||||
for (auto iter(mUsedIds.begin()); iter != mUsedIds.end(); ++iter)
|
for (const RefId& id : mUsedIds)
|
||||||
esm.writeHNRefId("USED", *iter);
|
esm.writeHNRefId("USED", id);
|
||||||
|
|
||||||
if (mTimeToStartDrowning)
|
if (mTimeToStartDrowning)
|
||||||
esm.writeHNT("DRTI", mTimeToStartDrowning);
|
esm.writeHNT("DRTI", mTimeToStartDrowning);
|
||||||
|
|
|
@ -23,8 +23,8 @@ namespace ESM
|
||||||
struct Faction
|
struct Faction
|
||||||
{
|
{
|
||||||
bool mExpelled;
|
bool mExpelled;
|
||||||
int mRank;
|
int32_t mRank;
|
||||||
int mReputation;
|
int32_t mReputation;
|
||||||
|
|
||||||
Faction();
|
Faction();
|
||||||
};
|
};
|
||||||
|
@ -33,18 +33,18 @@ namespace ESM
|
||||||
|
|
||||||
bool mWerewolfDeprecatedData;
|
bool mWerewolfDeprecatedData;
|
||||||
|
|
||||||
std::map<ESM::RefId, Faction> mFactions; // lower case IDs
|
std::map<ESM::RefId, Faction> mFactions;
|
||||||
int mDisposition;
|
int32_t mDisposition;
|
||||||
std::array<StatState<float>, ESM::Skill::Length> mSkills;
|
std::array<StatState<float>, ESM::Skill::Length> mSkills;
|
||||||
int mBounty;
|
int32_t mBounty;
|
||||||
int mReputation;
|
int32_t mReputation;
|
||||||
int mWerewolfKills;
|
int32_t mWerewolfKills;
|
||||||
int mLevelProgress;
|
int32_t mLevelProgress;
|
||||||
std::array<int, ESM::Attribute::Length> mSkillIncrease;
|
std::array<int32_t, ESM::Attribute::Length> mSkillIncrease;
|
||||||
std::array<int, 3> mSpecIncreases;
|
std::array<int32_t, 3> mSpecIncreases;
|
||||||
std::vector<ESM::RefId> mUsedIds; // lower case IDs
|
std::vector<ESM::RefId> mUsedIds;
|
||||||
float mTimeToStartDrowning;
|
float mTimeToStartDrowning;
|
||||||
int mCrimeId;
|
int32_t mCrimeId;
|
||||||
|
|
||||||
/// Initialize to default state
|
/// Initialize to default state
|
||||||
void blank();
|
void blank();
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace ESM
|
||||||
esm.getHNOT(mFlags, "FLAG");
|
esm.getHNOT(mFlags, "FLAG");
|
||||||
|
|
||||||
// obsolete
|
// obsolete
|
||||||
int unused;
|
int32_t unused;
|
||||||
esm.getHNOT(unused, "LTIM");
|
esm.getHNOT(unused, "LTIM");
|
||||||
|
|
||||||
mAnimationState.load(esm);
|
mAnimationState.load(esm);
|
||||||
|
@ -179,6 +179,6 @@ namespace ESM
|
||||||
throw std::logic_error(error.str());
|
throw std::logic_error(error.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectState::~ObjectState() {}
|
ObjectState::~ObjectState() = default;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,9 @@ namespace ESM
|
||||||
Locals mLocals;
|
Locals mLocals;
|
||||||
LuaScripts mLuaScripts;
|
LuaScripts mLuaScripts;
|
||||||
unsigned char mEnabled;
|
unsigned char mEnabled;
|
||||||
int mCount;
|
int32_t mCount;
|
||||||
Position mPosition;
|
Position mPosition;
|
||||||
unsigned int mFlags;
|
uint32_t mFlags;
|
||||||
|
|
||||||
// Is there any class-specific state following the ObjectState
|
// Is there any class-specific state following the ObjectState
|
||||||
bool mHasCustomState;
|
bool mHasCustomState;
|
||||||
|
|
|
@ -27,8 +27,8 @@ namespace ESM
|
||||||
RefId mMarkedCell;
|
RefId mMarkedCell;
|
||||||
ESM::RefId mBirthsign;
|
ESM::RefId mBirthsign;
|
||||||
|
|
||||||
int mCurrentCrimeId;
|
int32_t mCurrentCrimeId;
|
||||||
int mPaidCrimeId;
|
int32_t mPaidCrimeId;
|
||||||
|
|
||||||
float mSaveAttributes[Attribute::Length];
|
float mSaveAttributes[Attribute::Length];
|
||||||
float mSaveSkills[Skill::Length];
|
float mSaveSkills[Skill::Length];
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace ESM
|
||||||
Vector3 mPosition;
|
Vector3 mPosition;
|
||||||
Quaternion mOrientation;
|
Quaternion mOrientation;
|
||||||
|
|
||||||
int mActorId;
|
int32_t mActorId;
|
||||||
|
|
||||||
void load(ESMReader& esm);
|
void load(ESMReader& esm);
|
||||||
void save(ESMWriter& esm) const;
|
void save(ESMWriter& esm) const;
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace ESM
|
||||||
struct QuestState
|
struct QuestState
|
||||||
{
|
{
|
||||||
ESM::RefId mTopic; // lower case id
|
ESM::RefId mTopic; // lower case id
|
||||||
int mState;
|
int32_t mState;
|
||||||
unsigned char mFinished;
|
unsigned char mFinished;
|
||||||
|
|
||||||
void load(ESMReader& esm);
|
void load(ESMReader& esm);
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace ESM
|
||||||
|
|
||||||
std::vector<std::string> mContentFiles;
|
std::vector<std::string> mContentFiles;
|
||||||
std::string mPlayerName;
|
std::string mPlayerName;
|
||||||
int mPlayerLevel;
|
int32_t mPlayerLevel;
|
||||||
|
|
||||||
// ID of class
|
// ID of class
|
||||||
ESM::RefId mPlayerClassId;
|
ESM::RefId mPlayerClassId;
|
||||||
|
@ -34,7 +34,7 @@ namespace ESM
|
||||||
std::string mDescription;
|
std::string mDescription;
|
||||||
std::vector<char> mScreenshot; // raw jpg-encoded data
|
std::vector<char> mScreenshot; // raw jpg-encoded data
|
||||||
|
|
||||||
int mCurrentDay = 0;
|
int32_t mCurrentDay = 0;
|
||||||
float mCurrentHealth = 0;
|
float mCurrentHealth = 0;
|
||||||
float mMaximumHealth = 0;
|
float mMaximumHealth = 0;
|
||||||
|
|
||||||
|
|
|
@ -21,19 +21,19 @@ namespace ESM
|
||||||
// We changed stats values from integers to floats; ensure backwards compatibility
|
// We changed stats values from integers to floats; ensure backwards compatibility
|
||||||
if (intFallback)
|
if (intFallback)
|
||||||
{
|
{
|
||||||
int base = 0;
|
int32_t base = 0;
|
||||||
esm.getHNT(base, "STBA");
|
esm.getHNT(base, "STBA");
|
||||||
mBase = static_cast<T>(base);
|
mBase = static_cast<T>(base);
|
||||||
|
|
||||||
int mod = 0;
|
int32_t mod = 0;
|
||||||
esm.getHNOT(mod, "STMO");
|
esm.getHNOT(mod, "STMO");
|
||||||
mMod = static_cast<T>(mod);
|
mMod = static_cast<T>(mod);
|
||||||
|
|
||||||
int current = 0;
|
int32_t current = 0;
|
||||||
esm.getHNOT(current, "STCU");
|
esm.getHNOT(current, "STCU");
|
||||||
mCurrent = static_cast<T>(current);
|
mCurrent = static_cast<T>(current);
|
||||||
|
|
||||||
int oldDamage = 0;
|
int32_t oldDamage = 0;
|
||||||
esm.getHNOT(oldDamage, "STDA");
|
esm.getHNOT(oldDamage, "STDA");
|
||||||
mDamage = static_cast<float>(oldDamage);
|
mDamage = static_cast<float>(oldDamage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace ESM
|
||||||
template <typename T, bool orDefault = false>
|
template <typename T, bool orDefault = false>
|
||||||
struct GetValue
|
struct GetValue
|
||||||
{
|
{
|
||||||
constexpr T operator()(int value) const { return static_cast<T>(value); }
|
constexpr T operator()(int32_t value) const { return static_cast<T>(value); }
|
||||||
|
|
||||||
constexpr T operator()(float value) const { return static_cast<T>(value); }
|
constexpr T operator()(float value) const { return static_cast<T>(value); }
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ namespace ESM
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(int& value) const { value = static_cast<int>(mValue); }
|
void operator()(int32_t& value) const { value = static_cast<int32_t>(mValue); }
|
||||||
|
|
||||||
void operator()(float& value) const { value = static_cast<float>(mValue); }
|
void operator()(float& value) const { value = static_cast<float>(mValue); }
|
||||||
|
|
||||||
|
@ -58,9 +58,9 @@ namespace ESM
|
||||||
return std::get<std::string>(mData);
|
return std::get<std::string>(mData);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Variant::getInteger() const
|
int32_t Variant::getInteger() const
|
||||||
{
|
{
|
||||||
return std::visit(GetValue<int>{}, mData);
|
return std::visit(GetValue<int32_t>{}, mData);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Variant::getFloat() const
|
float Variant::getFloat() const
|
||||||
|
@ -194,17 +194,17 @@ namespace ESM
|
||||||
|
|
||||||
case VT_Short:
|
case VT_Short:
|
||||||
|
|
||||||
stream << "variant short: " << std::get<int>(mData);
|
stream << "variant short: " << std::get<int32_t>(mData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VT_Int:
|
case VT_Int:
|
||||||
|
|
||||||
stream << "variant int: " << std::get<int>(mData);
|
stream << "variant int: " << std::get<int32_t>(mData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VT_Long:
|
case VT_Long:
|
||||||
|
|
||||||
stream << "variant long: " << std::get<int>(mData);
|
stream << "variant long: " << std::get<int32_t>(mData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VT_Float:
|
case VT_Float:
|
||||||
|
@ -259,7 +259,7 @@ namespace ESM
|
||||||
std::get<std::string>(mData) = std::move(value);
|
std::get<std::string>(mData) = std::move(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Variant::setInteger(int value)
|
void Variant::setInteger(int32_t value)
|
||||||
{
|
{
|
||||||
std::visit(SetValue(value), mData);
|
std::visit(SetValue(value), mData);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace ESM
|
||||||
class Variant
|
class Variant
|
||||||
{
|
{
|
||||||
VarType mType;
|
VarType mType;
|
||||||
std::variant<std::monostate, int, float, std::string> mData;
|
std::variant<std::monostate, int32_t, float, std::string> mData;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Format
|
enum Format
|
||||||
|
@ -54,7 +54,7 @@ namespace ESM
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit Variant(int value)
|
explicit Variant(int32_t value)
|
||||||
: mType(VT_Long)
|
: mType(VT_Long)
|
||||||
, mData(value)
|
, mData(value)
|
||||||
{
|
{
|
||||||
|
@ -71,7 +71,7 @@ namespace ESM
|
||||||
const std::string& getString() const;
|
const std::string& getString() const;
|
||||||
///< Will throw an exception, if value can not be represented as a string.
|
///< Will throw an exception, if value can not be represented as a string.
|
||||||
|
|
||||||
int getInteger() const;
|
int32_t getInteger() const;
|
||||||
///< Will throw an exception, if value can not be represented as an integer (implicit
|
///< Will throw an exception, if value can not be represented as an integer (implicit
|
||||||
/// casting of float values is permitted).
|
/// casting of float values is permitted).
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ namespace ESM
|
||||||
void setString(std::string&& value);
|
void setString(std::string&& value);
|
||||||
///< Will throw an exception, if type is not compatible with string.
|
///< Will throw an exception, if type is not compatible with string.
|
||||||
|
|
||||||
void setInteger(int value);
|
void setInteger(int32_t value);
|
||||||
///< Will throw an exception, if type is not compatible with integer.
|
///< Will throw an exception, if type is not compatible with integer.
|
||||||
|
|
||||||
void setFloat(float value);
|
void setFloat(float value);
|
||||||
|
|
|
@ -46,7 +46,7 @@ namespace ESM
|
||||||
esm.writeHNString("STRV", in);
|
esm.writeHNString("STRV", in);
|
||||||
}
|
}
|
||||||
|
|
||||||
void readESMVariantValue(ESMReader& esm, Variant::Format format, VarType type, int& out)
|
void readESMVariantValue(ESMReader& esm, Variant::Format format, VarType type, int32_t& out)
|
||||||
{
|
{
|
||||||
if (type != VT_Short && type != VT_Long && type != VT_Int)
|
if (type != VT_Short && type != VT_Long && type != VT_Int)
|
||||||
throw std::logic_error("not an integer type");
|
throw std::logic_error("not an integer type");
|
||||||
|
@ -60,9 +60,9 @@ namespace ESM
|
||||||
if (std::isnan(value))
|
if (std::isnan(value))
|
||||||
out = 0;
|
out = 0;
|
||||||
else
|
else
|
||||||
out = static_cast<short>(value);
|
out = static_cast<int16_t>(value);
|
||||||
else if (type == VT_Long)
|
else if (type == VT_Long)
|
||||||
out = static_cast<int>(value);
|
out = static_cast<int32_t>(value);
|
||||||
else
|
else
|
||||||
esm.fail("unsupported global variable integer type");
|
esm.fail("unsupported global variable integer type");
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ namespace ESM
|
||||||
{
|
{
|
||||||
if (type == VT_Short)
|
if (type == VT_Short)
|
||||||
{
|
{
|
||||||
short value;
|
int16_t value;
|
||||||
esm.getHT(value);
|
esm.getHT(value);
|
||||||
out = value;
|
out = value;
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ namespace ESM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeESMVariantValue(ESMWriter& esm, Variant::Format format, VarType type, int in)
|
void writeESMVariantValue(ESMWriter& esm, Variant::Format format, VarType type, int32_t in)
|
||||||
{
|
{
|
||||||
if (type != VT_Short && type != VT_Long && type != VT_Int)
|
if (type != VT_Short && type != VT_Long && type != VT_Int)
|
||||||
throw std::logic_error("not an integer type");
|
throw std::logic_error("not an integer type");
|
||||||
|
@ -126,7 +126,7 @@ namespace ESM
|
||||||
else if (format == Variant::Format_Local)
|
else if (format == Variant::Format_Local)
|
||||||
{
|
{
|
||||||
if (type == VT_Short)
|
if (type == VT_Short)
|
||||||
esm.writeHNT("STTV", static_cast<short>(in));
|
esm.writeHNT("STTV", static_cast<int16_t>(in));
|
||||||
else if (type == VT_Int)
|
else if (type == VT_Int)
|
||||||
esm.writeHNT("INTV", in);
|
esm.writeHNT("INTV", in);
|
||||||
else
|
else
|
||||||
|
|
|
@ -12,13 +12,13 @@ namespace ESM
|
||||||
|
|
||||||
void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, float& value);
|
void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, float& value);
|
||||||
|
|
||||||
void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, int& value);
|
void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, int32_t& value);
|
||||||
|
|
||||||
void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, const std::string& value);
|
void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, const std::string& value);
|
||||||
|
|
||||||
void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, float value);
|
void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, float value);
|
||||||
|
|
||||||
void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, int value);
|
void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, int32_t value);
|
||||||
|
|
||||||
struct ReadESMVariantValue
|
struct ReadESMVariantValue
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,6 +76,9 @@ void ESM4::LandTexture::load(ESM4::Reader& reader)
|
||||||
case ESM4::SUB_MNAM:
|
case ESM4::SUB_MNAM:
|
||||||
reader.getFormId(mMaterial);
|
reader.getFormId(mMaterial);
|
||||||
break; // TES5, FO4
|
break; // TES5, FO4
|
||||||
|
case ESM4::SUB_INAM:
|
||||||
|
reader.get(mMaterialFlags);
|
||||||
|
break; // SSE
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("ESM4::LTEX::load - Unknown subrecord " + ESM::printName(subHdr.typeId));
|
throw std::runtime_error("ESM4::LTEX::load - Unknown subrecord " + ESM::printName(subHdr.typeId));
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,17 @@ namespace ESM4
|
||||||
|
|
||||||
// ----------------------
|
// ----------------------
|
||||||
|
|
||||||
|
// ------ SSE -----------
|
||||||
|
|
||||||
|
enum MaterialFlags
|
||||||
|
{
|
||||||
|
Flag_IsSnow = 0x1,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::uint32_t mMaterialFlags;
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
void load(ESM4::Reader& reader);
|
void load(ESM4::Reader& reader);
|
||||||
// void save(ESM4::Writer& writer) const;
|
// void save(ESM4::Writer& writer) const;
|
||||||
|
|
||||||
|
|
|
@ -116,9 +116,11 @@ void ESM4::Npc::load(ESM4::Reader& reader)
|
||||||
{
|
{
|
||||||
switch (subHdr.dataSize)
|
switch (subHdr.dataSize)
|
||||||
{
|
{
|
||||||
|
case 20: // FO4
|
||||||
|
mIsFO4 = true;
|
||||||
|
[[fallthrough]];
|
||||||
case 16: // TES4
|
case 16: // TES4
|
||||||
case 24: // FO3/FNV, TES5
|
case 24: // FO3/FNV, TES5
|
||||||
case 20: // FO4
|
|
||||||
reader.get(&mBaseConfig, subHdr.dataSize);
|
reader.get(&mBaseConfig, subHdr.dataSize);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -78,6 +78,7 @@ namespace ESM4
|
||||||
FO3_NoRotateHead = 0x40000000
|
FO3_NoRotateHead = 0x40000000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// In FO4 flags seem to be the same.
|
||||||
enum ACBS_TES5
|
enum ACBS_TES5
|
||||||
{
|
{
|
||||||
TES5_Female = 0x00000001,
|
TES5_Female = 0x00000001,
|
||||||
|
@ -101,27 +102,32 @@ namespace ESM4
|
||||||
TES5_Invulnerable = 0x80000000
|
TES5_Invulnerable = 0x80000000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// All FO3+ games.
|
||||||
enum Template_Flags
|
enum Template_Flags
|
||||||
{
|
{
|
||||||
TES5_UseTraits = 0x0001, // Destructible Object; Traits tab, including race, gender, height, weight,
|
Template_UseTraits = 0x0001, // Destructible Object; Traits tab, including race, gender, height, weight,
|
||||||
// voice type, death item; Sounds tab; Animation tab; Character Gen tabs
|
// voice type, death item; Sounds tab; Animation tab; Character Gen tabs
|
||||||
TES5_UseStats = 0x0002, // Stats tab, including level, autocalc, skills, health/magicka/stamina,
|
Template_UseStats = 0x0002, // Stats tab, including level, autocalc, skills, health/magicka/stamina,
|
||||||
// speed, bleedout, class
|
// speed, bleedout, class
|
||||||
TES5_UseFactions = 0x0004, // both factions and assigned crime faction
|
Template_UseFactions = 0x0004, // both factions and assigned crime faction
|
||||||
TES5_UseSpellList = 0x0008, // both spells and perks
|
Template_UseSpellList = 0x0008, // both spells and perks
|
||||||
TES5_UseAIData = 0x0010, // AI Data tab, including aggression/confidence/morality, combat style and
|
Template_UseAIData = 0x0010, // AI Data tab, including aggression/confidence/morality, combat style and
|
||||||
// gift filter
|
// gift filter
|
||||||
TES5_UseAIPackage = 0x0020, // only the basic Packages listed on the AI Packages tab;
|
Template_UseAIPackage = 0x0020, // only the basic Packages listed on the AI Packages tab;
|
||||||
// rest of tab controlled by Def Pack List
|
// rest of tab controlled by Def Pack List
|
||||||
TES5_UseBaseData = 0x0080, // including name and short name, and flags for Essential, Protected,
|
Template_UseModel = 0x0040, // FO3, FONV; probably not used in TES5+
|
||||||
// Respawn, Summonable, Simple Actor, and Doesn't affect stealth meter
|
Template_UseBaseData = 0x0080, // including name and short name, and flags for Essential, Protected,
|
||||||
TES5_UseInventory = 0x0100, // Inventory tab, including all outfits and geared-up item
|
// Respawn, Summonable, Simple Actor, and Doesn't affect stealth meter
|
||||||
// -- but not death item
|
Template_UseInventory = 0x0100, // Inventory tab, including all outfits and geared-up item,
|
||||||
TES5_UseScript = 0x0200,
|
// but not death item
|
||||||
TES5_UseDefined = 0x0400, // Def Pack List (the dropdown-selected package lists on the AI Packages tab)
|
Template_UseScript = 0x0200,
|
||||||
TES5_UseAtkData = 0x0800, // Attack Data tab, including override from behavior graph race,
|
|
||||||
// events, and data)
|
// The following flags were added in TES5+:
|
||||||
TES5_UseKeywords = 0x1000
|
|
||||||
|
Template_UseDefined = 0x0400, // Def Pack List (the dropdown-selected package lists on the AI Packages tab)
|
||||||
|
Template_UseAtkData = 0x0800, // Attack Data tab, including override from behavior graph race,
|
||||||
|
// events, and data)
|
||||||
|
Template_UseKeywords = 0x1000
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
@ -172,6 +178,7 @@ namespace ESM4
|
||||||
|
|
||||||
bool mIsTES4;
|
bool mIsTES4;
|
||||||
bool mIsFONV;
|
bool mIsFONV;
|
||||||
|
bool mIsFO4 = false;
|
||||||
|
|
||||||
std::string mEditorId;
|
std::string mEditorId;
|
||||||
std::string mFullName;
|
std::string mFullName;
|
||||||
|
|
|
@ -110,7 +110,7 @@ namespace ESM4
|
||||||
ESM::FormId mId; // from the header
|
ESM::FormId mId; // from the header
|
||||||
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
|
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
|
||||||
|
|
||||||
bool mIsTES5;
|
bool mIsTES5 = false;
|
||||||
|
|
||||||
std::string mEditorId;
|
std::string mEditorId;
|
||||||
std::string mFullName;
|
std::string mFullName;
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include <osg/StateSet>
|
#include <osg/StateSet>
|
||||||
|
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/sceneutil/clearcolor.hpp>
|
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
#include <components/stereo/multiview.hpp>
|
#include <components/stereo/multiview.hpp>
|
||||||
|
@ -190,6 +189,11 @@ mat4 omw_InvProjectionMatrix()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec3 omw_GetNormalsWorldSpace(vec2 uv)
|
||||||
|
{
|
||||||
|
return (vec4(omw_GetNormals(uv), 0.0) * omw.viewMatrix).rgb;
|
||||||
|
}
|
||||||
|
|
||||||
vec3 omw_GetWorldPosFromUV(vec2 uv)
|
vec3 omw_GetWorldPosFromUV(vec2 uv)
|
||||||
{
|
{
|
||||||
float depth = omw_GetDepth(uv);
|
float depth = omw_GetDepth(uv);
|
||||||
|
@ -321,9 +325,6 @@ float omw_EstimateFogCoverageFromUV(vec2 uv)
|
||||||
|
|
||||||
if (mBlendEq)
|
if (mBlendEq)
|
||||||
stateSet->setAttributeAndModes(new osg::BlendEquation(mBlendEq.value()));
|
stateSet->setAttributeAndModes(new osg::BlendEquation(mBlendEq.value()));
|
||||||
|
|
||||||
if (mClearColor)
|
|
||||||
stateSet->setAttributeAndModes(new SceneUtil::ClearColor(mClearColor.value(), GL_COLOR_BUFFER_BIT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pass::dirty()
|
void Pass::dirty()
|
||||||
|
@ -339,7 +340,7 @@ float omw_EstimateFogCoverageFromUV(vec2 uv)
|
||||||
if (mCompiled)
|
if (mCompiled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mLegacyGLSL = technique.getGLSLVersion() != 330;
|
mLegacyGLSL = technique.getGLSLVersion() < 330;
|
||||||
|
|
||||||
if (mType == Type::Pixel)
|
if (mType == Type::Pixel)
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,7 +72,6 @@ namespace fx
|
||||||
std::array<std::string, 3> mRenderTargets;
|
std::array<std::string, 3> mRenderTargets;
|
||||||
|
|
||||||
std::string mTarget;
|
std::string mTarget;
|
||||||
std::optional<osg::Vec4f> mClearColor;
|
|
||||||
|
|
||||||
std::optional<osg::BlendFunc::BlendFuncMode> mBlendSource;
|
std::optional<osg::BlendFunc::BlendFuncMode> mBlendSource;
|
||||||
std::optional<osg::BlendFunc::BlendFuncMode> mBlendDest;
|
std::optional<osg::BlendFunc::BlendFuncMode> mBlendDest;
|
||||||
|
|
|
@ -279,6 +279,7 @@ namespace fx
|
||||||
rt.mTarget->setSourceType(GL_UNSIGNED_BYTE);
|
rt.mTarget->setSourceType(GL_UNSIGNED_BYTE);
|
||||||
rt.mTarget->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
rt.mTarget->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||||
rt.mTarget->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
rt.mTarget->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
rt.mTarget->setName(std::string(mBlockName));
|
||||||
|
|
||||||
while (!isNext<Lexer::Close_bracket>() && !isNext<Lexer::Eof>())
|
while (!isNext<Lexer::Close_bracket>() && !isNext<Lexer::Eof>())
|
||||||
{
|
{
|
||||||
|
@ -312,6 +313,8 @@ namespace fx
|
||||||
rt.mTarget->setSourceFormat(parseSourceFormat());
|
rt.mTarget->setSourceFormat(parseSourceFormat());
|
||||||
else if (key == "mipmaps")
|
else if (key == "mipmaps")
|
||||||
rt.mMipMap = parseBool();
|
rt.mMipMap = parseBool();
|
||||||
|
else if (key == "clear_color")
|
||||||
|
rt.mClearColor = parseVec<osg::Vec4f, Lexer::Vec4>();
|
||||||
else
|
else
|
||||||
error(Misc::StringUtils::format("unexpected key '%s'", std::string(key)));
|
error(Misc::StringUtils::format("unexpected key '%s'", std::string(key)));
|
||||||
|
|
||||||
|
@ -797,9 +800,6 @@ namespace fx
|
||||||
if (!pass)
|
if (!pass)
|
||||||
pass = std::make_shared<fx::Pass>();
|
pass = std::make_shared<fx::Pass>();
|
||||||
|
|
||||||
bool clear = true;
|
|
||||||
osg::Vec4f clearColor = { 1, 1, 1, 1 };
|
|
||||||
|
|
||||||
while (!isNext<Lexer::Eof>())
|
while (!isNext<Lexer::Eof>())
|
||||||
{
|
{
|
||||||
expect<Lexer::Literal>("invalid key in block header");
|
expect<Lexer::Literal>("invalid key in block header");
|
||||||
|
@ -843,10 +843,6 @@ namespace fx
|
||||||
if (blendEq != osg::BlendEquation::FUNC_ADD)
|
if (blendEq != osg::BlendEquation::FUNC_ADD)
|
||||||
pass->mBlendEq = blendEq;
|
pass->mBlendEq = blendEq;
|
||||||
}
|
}
|
||||||
else if (key == "clear")
|
|
||||||
clear = parseBool();
|
|
||||||
else if (key == "clear_color")
|
|
||||||
clearColor = parseVec<osg::Vec4f, Lexer::Vec4>();
|
|
||||||
else
|
else
|
||||||
error(Misc::StringUtils::format("unrecognized key '%s' in block header", std::string(key)));
|
error(Misc::StringUtils::format("unrecognized key '%s' in block header", std::string(key)));
|
||||||
|
|
||||||
|
@ -864,9 +860,6 @@ namespace fx
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clear)
|
|
||||||
pass->mClearColor = clearColor;
|
|
||||||
|
|
||||||
error("malformed block header");
|
error("malformed block header");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,10 +54,14 @@ namespace fx
|
||||||
osg::ref_ptr<osg::FrameBufferObject> mRenderTarget;
|
osg::ref_ptr<osg::FrameBufferObject> mRenderTarget;
|
||||||
osg::ref_ptr<osg::Texture2D> mRenderTexture;
|
osg::ref_ptr<osg::Texture2D> mRenderTexture;
|
||||||
bool mResolve = false;
|
bool mResolve = false;
|
||||||
|
Types::SizeProxy mSize;
|
||||||
|
bool mMipMap;
|
||||||
|
|
||||||
SubPass(const SubPass& other, const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
|
SubPass(const SubPass& other, const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
|
||||||
: mStateSet(new osg::StateSet(*other.mStateSet, copyOp))
|
: mStateSet(new osg::StateSet(*other.mStateSet, copyOp))
|
||||||
, mResolve(other.mResolve)
|
, mResolve(other.mResolve)
|
||||||
|
, mSize(other.mSize)
|
||||||
|
, mMipMap(other.mMipMap)
|
||||||
{
|
{
|
||||||
if (other.mRenderTarget)
|
if (other.mRenderTarget)
|
||||||
mRenderTarget = new osg::FrameBufferObject(*other.mRenderTarget, copyOp);
|
mRenderTarget = new osg::FrameBufferObject(*other.mRenderTarget, copyOp);
|
||||||
|
|
|
@ -29,6 +29,16 @@ namespace fx
|
||||||
std::optional<int> mWidth;
|
std::optional<int> mWidth;
|
||||||
std::optional<int> mHeight;
|
std::optional<int> mHeight;
|
||||||
|
|
||||||
|
SizeProxy() = default;
|
||||||
|
|
||||||
|
SizeProxy(const SizeProxy& other)
|
||||||
|
: mWidthRatio(other.mWidthRatio)
|
||||||
|
, mHeightRatio(other.mHeightRatio)
|
||||||
|
, mWidth(other.mWidth)
|
||||||
|
, mHeight(other.mHeight)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
std::tuple<int, int> get(int width, int height) const
|
std::tuple<int, int> get(int width, int height) const
|
||||||
{
|
{
|
||||||
int scaledWidth = width;
|
int scaledWidth = width;
|
||||||
|
@ -53,6 +63,17 @@ namespace fx
|
||||||
osg::ref_ptr<osg::Texture2D> mTarget = new osg::Texture2D;
|
osg::ref_ptr<osg::Texture2D> mTarget = new osg::Texture2D;
|
||||||
SizeProxy mSize;
|
SizeProxy mSize;
|
||||||
bool mMipMap = false;
|
bool mMipMap = false;
|
||||||
|
osg::Vec4f mClearColor = osg::Vec4f(0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
RenderTarget() = default;
|
||||||
|
|
||||||
|
RenderTarget(const RenderTarget& other)
|
||||||
|
: mTarget(other.mTarget)
|
||||||
|
, mSize(other.mSize)
|
||||||
|
, mMipMap(other.mMipMap)
|
||||||
|
, mClearColor(other.mClearColor)
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
|
|
@ -8,9 +8,7 @@ namespace LuaUi
|
||||||
{
|
{
|
||||||
void LuaTileRect::_setAlign(const MyGUI::IntSize& _oldsize)
|
void LuaTileRect::_setAlign(const MyGUI::IntSize& _oldsize)
|
||||||
{
|
{
|
||||||
mCurrentCoord.set(0, 0, mCroppedParent->getWidth(), mCroppedParent->getHeight());
|
mCoord.set(0, 0, mCroppedParent->getWidth(), mCroppedParent->getHeight());
|
||||||
mAlign = MyGUI::Align::Stretch;
|
|
||||||
MyGUI::TileRect::_setAlign(_oldsize);
|
|
||||||
mTileSize = mSetTileSize;
|
mTileSize = mSetTileSize;
|
||||||
|
|
||||||
// zero tilesize stands for not tiling
|
// zero tilesize stands for not tiling
|
||||||
|
@ -25,6 +23,8 @@ namespace LuaUi
|
||||||
mTileSize.width = 1e7;
|
mTileSize.width = 1e7;
|
||||||
if (mTileSize.height <= 0)
|
if (mTileSize.height <= 0)
|
||||||
mTileSize.height = 1e7;
|
mTileSize.height = 1e7;
|
||||||
|
|
||||||
|
MyGUI::TileRect::_updateView();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaImage::initialize()
|
void LuaImage::initialize()
|
||||||
|
@ -55,13 +55,13 @@ namespace LuaUi
|
||||||
if (texture != nullptr)
|
if (texture != nullptr)
|
||||||
textureSize = MyGUI::IntSize(texture->getWidth(), texture->getHeight());
|
textureSize = MyGUI::IntSize(texture->getWidth(), texture->getHeight());
|
||||||
|
|
||||||
mTileRect->updateSize(MyGUI::IntSize(tileH ? textureSize.width : 0, tileV ? textureSize.height : 0));
|
|
||||||
setImageTile(textureSize);
|
|
||||||
|
|
||||||
if (atlasCoord.width == 0)
|
if (atlasCoord.width == 0)
|
||||||
atlasCoord.width = textureSize.width;
|
atlasCoord.width = textureSize.width;
|
||||||
if (atlasCoord.height == 0)
|
if (atlasCoord.height == 0)
|
||||||
atlasCoord.height = textureSize.height;
|
atlasCoord.height = textureSize.height;
|
||||||
|
|
||||||
|
mTileRect->updateSize(MyGUI::IntSize(tileH ? atlasCoord.width : 0, tileV ? atlasCoord.height : 0));
|
||||||
|
setImageTile(atlasCoord.size());
|
||||||
setImageCoord(atlasCoord);
|
setImageCoord(atlasCoord);
|
||||||
|
|
||||||
setColour(propertyValue("color", MyGUI::Colour(1, 1, 1, 1)));
|
setColour(propertyValue("color", MyGUI::Colour(1, 1, 1, 1)));
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace LuaUi
|
||||||
{
|
{
|
||||||
mEditBox = createWidget<MyGUI::EditBox>("LuaTextEdit", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default);
|
mEditBox = createWidget<MyGUI::EditBox>("LuaTextEdit", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default);
|
||||||
mEditBox->eventEditTextChange += MyGUI::newDelegate(this, &LuaTextEdit::textChange);
|
mEditBox->eventEditTextChange += MyGUI::newDelegate(this, &LuaTextEdit::textChange);
|
||||||
|
mEditBox->setMaxTextLength(std::numeric_limits<std::size_t>::max());
|
||||||
registerEvents(mEditBox);
|
registerEvents(mEditBox);
|
||||||
WidgetExtension::initialize();
|
WidgetExtension::initialize();
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue