mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 03:15:32 +00:00
Add OpenMW commits up to 4 Jul 2020
# Conflicts: # .travis.yml # CI/before_script.linux.sh # apps/openmw/engine.cpp # apps/openmw/mwbase/windowmanager.hpp # apps/openmw/mwgui/charactercreation.cpp # apps/openmw/mwgui/windowmanagerimp.hpp # apps/openmw/mwmechanics/character.cpp
This commit is contained in:
commit
5eb7eb8d88
123 changed files with 1935 additions and 1159 deletions
|
@ -97,4 +97,4 @@ script:
|
|||
# - "chat.freenode.net#openmw"
|
||||
# on_success: change
|
||||
# on_failure: always
|
||||
# use_notice: true
|
||||
# use_notice: true
|
|
@ -155,6 +155,7 @@ Programmers
|
|||
Paul McElroy (Greendogo)
|
||||
pchan3
|
||||
Perry Hugh
|
||||
Petr Mikheev (ptmikheev)
|
||||
Phillip Andrews (PhillipAnd)
|
||||
Pi03k
|
||||
Pieter van der Kloet (pvdk)
|
||||
|
|
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -29,9 +29,20 @@
|
|||
Bug #5441: Enemies can't push a player character when in critical strike stance
|
||||
Bug #5451: Magic projectiles don't disappear with the caster
|
||||
Bug #5452: Autowalk is being included in savegames
|
||||
Bug #5472: Mistify mod causes CTD in 0.46 on Mac
|
||||
Bug #5479: NPCs who should be walking around town are standing around without walking
|
||||
Bug #5484: Zero value items shouldn't be able to be bought or sold for 1 gold
|
||||
Bug #5485: Intimidate doesn't increase disposition on marginal wins
|
||||
Bug #5490: Hits to carried left slot aren't redistributed if there's no shield equipped
|
||||
Bug #5499: Faction advance is available when requirements not met
|
||||
Bug #5502: Dead zone for analogue stick movement is too small
|
||||
Bug #5507: Sound volume is not clamped on ingame settings update
|
||||
Feature #390: 3rd person look "over the shoulder"
|
||||
Feature #2386: Distant Statics in the form of Object Paging
|
||||
Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher
|
||||
Feature #5362: Show the soul gems' trapped soul in count dialog
|
||||
Feature #5445: Handle NiLines
|
||||
Feature #5457: Realistic diagonal movement
|
||||
Task #5480: Drop Qt4 support
|
||||
|
||||
0.46.0
|
||||
|
|
19
CI/before_script.linux.sh
Executable file → Normal file
19
CI/before_script.linux.sh
Executable file → Normal file
|
@ -15,32 +15,21 @@ fi
|
|||
|
||||
export RAKNET_ROOT=~/CrabNet
|
||||
|
||||
|
||||
if [[ -z "${BUILD_OPENMW}" ]]; then export BUILD_OPENMW=ON; fi
|
||||
|
||||
${ANALYZE} cmake .. \
|
||||
-DCMAKE_C_COMPILER="${CC}" \
|
||||
-DCMAKE_CXX_COMPILER="${CXX}" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DBUILD_OPENMW=${BUILD_OPENMW} \
|
||||
-DBUILD_OPENCS=OFF \
|
||||
-DBUILD_LAUNCHER=${BUILD_OPENMW_CS} \
|
||||
-DBUILD_BSATOOL=${BUILD_OPENMW_CS} \
|
||||
-DBUILD_ESMTOOL=${BUILD_OPENMW_CS} \
|
||||
-DBUILD_MWINIIMPORTER=${BUILD_OPENMW_CS} \
|
||||
-DBUILD_ESSIMPORTER=${BUILD_OPENMW_CS} \
|
||||
-DBUILD_WIZARD=${BUILD_OPENMW_CS} \
|
||||
-DBUILD_NIFTEST=${BUILD_OPENMW_CS} \
|
||||
-DBUILD_OPENMW_MP=ON \
|
||||
-DBUILD_BROWSER=ON \
|
||||
-DBUILD_MASTER=ON \
|
||||
-DBUILD_UNITTESTS=1 \
|
||||
-DUSE_SYSTEM_TINYXML=1 \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DBINDIR=/usr/games \
|
||||
-DCMAKE_BUILD_TYPE="None" \
|
||||
-DBUILD_UNITTESTS=TRUE \
|
||||
-DUSE_SYSTEM_TINYXML=TRUE \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DBINDIR="/usr/games" \
|
||||
-DCMAKE_BUILD_TYPE="DEBUG" \
|
||||
-DGTEST_ROOT="${GOOGLETEST_DIR}" \
|
||||
-DGMOCK_ROOT="${GOOGLETEST_DIR}" \
|
||||
-DRakNet_LIBRARY_RELEASE=~/CrabNet/lib/libRakNetLibStatic.a \
|
||||
|
|
|
@ -497,13 +497,6 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
||||
fi
|
||||
|
||||
# Qt
|
||||
if [ -z $APPVEYOR ]; then
|
||||
download "AQt installer" \
|
||||
"https://files.pythonhosted.org/packages/f3/bb/aee972f08deecca31bfc46b5aedfad1ce6c7f3aaf1288d685e4a914b53ac/aqtinstall-0.8-py2.py3-none-any.whl" \
|
||||
"aqtinstall-0.8-py2.py3-none-any.whl"
|
||||
fi
|
||||
|
||||
# SDL2
|
||||
download "SDL 2.0.12" \
|
||||
"https://www.libsdl.org/release/SDL2-devel-2.0.12-VC.zip" \
|
||||
|
@ -511,11 +504,11 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
|
||||
# Google test and mock
|
||||
if [ ! -z $TEST_FRAMEWORK ]; then
|
||||
echo "Google test 1.8.1..."
|
||||
echo "Google test 1.10.0..."
|
||||
if [ -d googletest ]; then
|
||||
printf " Google test exists, skipping."
|
||||
else
|
||||
git clone -b release-1.8.1 https://github.com/google/googletest.git
|
||||
git clone -b release-1.10.0 https://github.com/google/googletest.git
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
@ -732,9 +725,16 @@ fi
|
|||
fi
|
||||
if [ -z $APPVEYOR ]; then
|
||||
cd $DEPS_INSTALL
|
||||
QT_SDK="$(real_pwd)/Qt/5.15.0/msvc${MSVC_REAL_YEAR}${SUFFIX}"
|
||||
|
||||
if [ -d 'Qt/5.15.0' ]; then
|
||||
qt_version="5.15.0"
|
||||
if [ "win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}" == "win64_msvc2017_64" ]; then
|
||||
echo "This combination of options is known not to work. Falling back to Qt 5.14.2."
|
||||
qt_version="5.14.2"
|
||||
fi
|
||||
|
||||
QT_SDK="$(real_pwd)/Qt/${qt_version}/msvc${MSVC_REAL_YEAR}${SUFFIX}"
|
||||
|
||||
if [ -d "Qt/${qt_version}" ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
if [ $MISSINGPYTHON -ne 0 ]; then
|
||||
|
@ -745,20 +745,20 @@ fi
|
|||
pushd "$DEPS" > /dev/null
|
||||
if ! [ -d 'aqt-venv' ]; then
|
||||
echo " Creating Virtualenv for aqt..."
|
||||
eval python -m venv aqt-venv $STRIP
|
||||
run_cmd python -m venv aqt-venv
|
||||
fi
|
||||
if [ -d 'aqt-venv/bin' ]; then
|
||||
VENV_BIN_DIR='bin'
|
||||
elif [ -d 'aqt-venv/Scripts' ]; then
|
||||
VENV_BIN_DIR='Scripts'
|
||||
else
|
||||
echo "Error: Failed to create virtualenv."
|
||||
exit 1
|
||||
echo "Error: Failed to create virtualenv in expected location."
|
||||
wrappedExit 1
|
||||
fi
|
||||
|
||||
if ! [ -e "aqt-venv/${VENV_BIN_DIR}/aqt" ]; then
|
||||
echo " Installing aqt wheel into virtualenv..."
|
||||
eval "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall-0.8-py2.py3-none-any.whl $STRIP
|
||||
run_cmd "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall==0.9.2
|
||||
fi
|
||||
popd > /dev/null
|
||||
|
||||
|
@ -767,7 +767,7 @@ fi
|
|||
mkdir Qt
|
||||
cd Qt
|
||||
|
||||
eval "${DEPS}/aqt-venv/${VENV_BIN_DIR}/aqt" install 5.15.0 windows desktop "win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}" $STRIP
|
||||
run_cmd "${DEPS}/aqt-venv/${VENV_BIN_DIR}/aqt" install $qt_version windows desktop "win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}"
|
||||
|
||||
printf " Cleaning up extraneous data... "
|
||||
rm -rf Qt/{aqtinstall.log,Tools}
|
||||
|
@ -820,7 +820,7 @@ cd $DEPS
|
|||
echo
|
||||
# Google Test and Google Mock
|
||||
if [ ! -z $TEST_FRAMEWORK ]; then
|
||||
printf "Google test 1.8.1 ..."
|
||||
printf "Google test 1.10.0 ..."
|
||||
|
||||
cd googletest
|
||||
if [ ! -d build ]; then
|
||||
|
|
|
@ -535,6 +535,9 @@ if(WIN32)
|
|||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug)
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
||||
|
||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug)
|
||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
||||
|
||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/resources" DESTINATION "." CONFIGURATIONS Debug)
|
||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/resources" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
|||
("plain,p", "Print contents of dialogs, books and scripts. "
|
||||
"(skipped by default)"
|
||||
"Only affects dump mode.")
|
||||
("quiet,q", "Supress all record information. Useful for speed tests.")
|
||||
("quiet,q", "Suppress all record information. Useful for speed tests.")
|
||||
("loadcells,C", "Browse through contents of all cells.")
|
||||
|
||||
( "encoding,e", bpo::value<std::string>(&(info.encoding))->
|
||||
|
|
|
@ -740,9 +740,9 @@ void Record<ESM::Faction>::print()
|
|||
std::cout << " Attribute2 Requirement: "
|
||||
<< mData.mData.mRankData[i].mAttribute2 << std::endl;
|
||||
std::cout << " One Skill at Level: "
|
||||
<< mData.mData.mRankData[i].mSkill1 << std::endl;
|
||||
<< mData.mData.mRankData[i].mPrimarySkill << std::endl;
|
||||
std::cout << " Two Skills at Level: "
|
||||
<< mData.mData.mRankData[i].mSkill2 << std::endl;
|
||||
<< mData.mData.mRankData[i].mFavouredSkill << std::endl;
|
||||
std::cout << " Faction Reaction: "
|
||||
<< mData.mData.mRankData[i].mFactReaction << std::endl;
|
||||
}
|
||||
|
|
|
@ -104,6 +104,7 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
|
||||
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
|
||||
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
|
||||
loadSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI");
|
||||
int showOwnedIndex = mEngineSettings.getInt("show owned", "Game");
|
||||
// Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid.
|
||||
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
|
||||
|
@ -171,6 +172,7 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
|
||||
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
|
||||
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
|
||||
saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI");
|
||||
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
|
||||
if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
|
||||
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
|
||||
|
|
|
@ -403,7 +403,7 @@ bool Launcher::MainDialog::setupGameData()
|
|||
QAbstractButton *skipButton =
|
||||
msgBox.addButton(tr("Skip"), QMessageBox::RejectRole);
|
||||
|
||||
Q_UNUSED(skipButton); // Supress compiler unused warning
|
||||
Q_UNUSED(skipButton); // Suppress compiler unused warning
|
||||
|
||||
msgBox.exec();
|
||||
|
||||
|
|
|
@ -216,7 +216,6 @@ endif(APPLE)
|
|||
|
||||
target_link_libraries(openmw-cs
|
||||
${OSG_LIBRARIES}
|
||||
${OPENTHREADS_LIBRARIES}
|
||||
${OSGTEXT_LIBRARIES}
|
||||
${OSGUTIL_LIBRARIES}
|
||||
${OSGVIEWER_LIBRARIES}
|
||||
|
|
|
@ -116,7 +116,7 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value
|
|||
Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return 0;
|
||||
return Qt::ItemFlags();
|
||||
|
||||
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ bool CSMWorld::IdTree::setData (const QModelIndex &index, const QVariant &value,
|
|||
Qt::ItemFlags CSMWorld::IdTree::flags (const QModelIndex & index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return 0;
|
||||
return Qt::ItemFlags();
|
||||
|
||||
if (index.internalId() != 0)
|
||||
{
|
||||
|
|
|
@ -1143,8 +1143,8 @@ namespace CSMWorld
|
|||
case 0: return QString(faction.mRanks[subRowIndex].c_str());
|
||||
case 1: return rankData.mAttribute1;
|
||||
case 2: return rankData.mAttribute2;
|
||||
case 3: return rankData.mSkill1;
|
||||
case 4: return rankData.mSkill2;
|
||||
case 3: return rankData.mPrimarySkill;
|
||||
case 4: return rankData.mFavouredSkill;
|
||||
case 5: return rankData.mFactReaction;
|
||||
default: throw std::runtime_error("Rank subcolumn index out of range");
|
||||
}
|
||||
|
@ -1165,8 +1165,8 @@ namespace CSMWorld
|
|||
case 0: faction.mRanks[subRowIndex] = value.toString().toUtf8().constData(); break;
|
||||
case 1: rankData.mAttribute1 = value.toInt(); break;
|
||||
case 2: rankData.mAttribute2 = value.toInt(); break;
|
||||
case 3: rankData.mSkill1 = value.toInt(); break;
|
||||
case 4: rankData.mSkill2 = value.toInt(); break;
|
||||
case 3: rankData.mPrimarySkill = value.toInt(); break;
|
||||
case 4: rankData.mFavouredSkill = value.toInt(); break;
|
||||
case 5: rankData.mFactReaction = value.toInt(); break;
|
||||
default: throw std::runtime_error("Rank index out of range");
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace CSVPrefs
|
|||
|
||||
public:
|
||||
|
||||
ContextMenuList(QWidget* parent = 0);
|
||||
ContextMenuList(QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace CSVRender
|
|||
public:
|
||||
|
||||
OrbitCameraMode(WorldspaceWidget* worldspaceWidget, const QIcon& icon, const QString& tooltip = "",
|
||||
QWidget* parent = 0);
|
||||
QWidget* parent = nullptr);
|
||||
~OrbitCameraMode();
|
||||
|
||||
virtual void activate(CSVWidget::SceneToolbar* toolbar);
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include "scenewidget.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <QEvent>
|
||||
#include <QResizeEvent>
|
||||
#include <QTimer>
|
||||
|
@ -184,7 +187,7 @@ void CompositeViewer::update()
|
|||
double minFrameTime = _runMaxFrameRate > 0.0 ? 1.0 / _runMaxFrameRate : 0.0;
|
||||
if (dt < minFrameTime)
|
||||
{
|
||||
OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-dt));
|
||||
std::this_thread::sleep_for(std::chrono::duration<double>(minFrameTime - dt));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,7 +335,7 @@ void SceneWidget::mouseMoveEvent (QMouseEvent *event)
|
|||
|
||||
void SceneWidget::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
mCurrentCamControl->handleMouseScrollEvent(event->delta());
|
||||
mCurrentCamControl->handleMouseScrollEvent(event->angleDelta().y());
|
||||
}
|
||||
|
||||
void SceneWidget::update(double dt)
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace CSVRender
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RenderWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);
|
||||
RenderWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
|
||||
virtual ~RenderWidget();
|
||||
|
||||
/// Initiates a request to redraw the view
|
||||
|
@ -78,8 +78,8 @@ namespace CSVRender
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSystem, QWidget* parent = 0,
|
||||
Qt::WindowFlags f = 0, bool retrieveInput = true);
|
||||
SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSystem, QWidget* parent = nullptr,
|
||||
Qt::WindowFlags f = Qt::WindowFlags(), bool retrieveInput = true);
|
||||
virtual ~SceneWidget();
|
||||
|
||||
CSVWidget::SceneToolMode *makeLightingSelector (CSVWidget::SceneToolbar *parent);
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
#include "cameracontroller.hpp"
|
||||
|
||||
CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent)
|
||||
: SceneWidget (document.getData().getResourceSystem(), parent, 0, false)
|
||||
: SceneWidget (document.getData().getResourceSystem(), parent, Qt::WindowFlags(), false)
|
||||
, mSceneElements(0)
|
||||
, mRun(0)
|
||||
, mDocument(document)
|
||||
|
@ -677,8 +677,7 @@ void CSVRender::WorldspaceWidget::wheelEvent (QWheelEvent *event)
|
|||
factor *= mDragShiftFactor;
|
||||
|
||||
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
|
||||
|
||||
editMode.dragWheel (event->delta(), factor);
|
||||
editMode.dragWheel (event->angleDelta().y(), factor);
|
||||
}
|
||||
else
|
||||
SceneWidget::wheelEvent(event);
|
||||
|
|
|
@ -273,7 +273,7 @@ void CSVTools::ReportTable::settingChanged (const CSMPrefs::Setting *setting)
|
|||
if (key.startsWith (base))
|
||||
{
|
||||
QString modifierString = key.mid (base.size());
|
||||
Qt::KeyboardModifiers modifiers = 0;
|
||||
Qt::KeyboardModifiers modifiers;
|
||||
|
||||
if (modifierString=="-s")
|
||||
modifiers = Qt::ShiftModifier;
|
||||
|
|
|
@ -671,7 +671,7 @@ void CSVWorld::Table::settingChanged (const CSMPrefs::Setting *setting)
|
|||
{
|
||||
std::string modifierString = setting->getKey().substr (6);
|
||||
|
||||
Qt::KeyboardModifiers modifiers = 0;
|
||||
Qt::KeyboardModifiers modifiers;
|
||||
|
||||
if (modifierString=="-s")
|
||||
modifiers = Qt::ShiftModifier;
|
||||
|
|
|
@ -41,7 +41,7 @@ add_openmw_dir (mwgui
|
|||
itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
|
||||
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
|
||||
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
|
||||
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours
|
||||
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours statswatcher
|
||||
)
|
||||
|
||||
add_openmw_dir (mwdialogue
|
||||
|
@ -57,7 +57,7 @@ add_openmw_dir (mwscript
|
|||
|
||||
add_openmw_dir (mwsound
|
||||
soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output
|
||||
loudness movieaudiofactory alext efx efx-presets
|
||||
loudness movieaudiofactory alext efx efx-presets regionsoundselector watersoundupdater volumesettings
|
||||
)
|
||||
|
||||
add_openmw_dir (mwworld
|
||||
|
@ -169,7 +169,6 @@ include_directories(
|
|||
|
||||
target_link_libraries(tes3mp
|
||||
${OSG_LIBRARIES}
|
||||
${OPENTHREADS_LIBRARIES}
|
||||
${OSGPARTICLE_LIBRARIES}
|
||||
${OSGUTIL_LIBRARIES}
|
||||
${OSGDB_LIBRARIES}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
|
@ -79,6 +81,140 @@ namespace
|
|||
if (ret != 0)
|
||||
Log(Debug::Error) << "SDL error: " << SDL_GetError();
|
||||
}
|
||||
|
||||
struct UserStats
|
||||
{
|
||||
const std::string mLabel;
|
||||
const std::string mBegin;
|
||||
const std::string mEnd;
|
||||
const std::string mTaken;
|
||||
|
||||
UserStats(const std::string& label, const std::string& prefix)
|
||||
: mLabel(label),
|
||||
mBegin(prefix + "_time_begin"),
|
||||
mEnd(prefix + "_time_end"),
|
||||
mTaken(prefix + "_time_taken")
|
||||
{}
|
||||
};
|
||||
|
||||
enum class UserStatsType : std::size_t
|
||||
{
|
||||
Input,
|
||||
Sound,
|
||||
State,
|
||||
Script,
|
||||
Mechanics,
|
||||
Physics,
|
||||
World,
|
||||
Gui,
|
||||
|
||||
Number,
|
||||
};
|
||||
|
||||
template <UserStatsType type>
|
||||
struct UserStatsValue
|
||||
{
|
||||
static const UserStats sValue;
|
||||
};
|
||||
|
||||
template <>
|
||||
const UserStats UserStatsValue<UserStatsType::Input>::sValue {"Input", "input"};
|
||||
|
||||
template <>
|
||||
const UserStats UserStatsValue<UserStatsType::Sound>::sValue {"Sound", "sound"};
|
||||
|
||||
template <>
|
||||
const UserStats UserStatsValue<UserStatsType::State>::sValue {"State", "state"};
|
||||
|
||||
template <>
|
||||
const UserStats UserStatsValue<UserStatsType::Script>::sValue {"Script", "script"};
|
||||
|
||||
template <>
|
||||
const UserStats UserStatsValue<UserStatsType::Mechanics>::sValue {"Mech", "mechanics"};
|
||||
|
||||
template <>
|
||||
const UserStats UserStatsValue<UserStatsType::Physics>::sValue {"Phys", "physics"};
|
||||
|
||||
template <>
|
||||
const UserStats UserStatsValue<UserStatsType::World>::sValue {"World", "world"};
|
||||
|
||||
template <>
|
||||
const UserStats UserStatsValue<UserStatsType::Gui>::sValue {"Gui", "gui"};
|
||||
|
||||
template <UserStatsType type>
|
||||
struct ForEachUserStatsValue
|
||||
{
|
||||
template <class F>
|
||||
static void apply(F&& f)
|
||||
{
|
||||
f(UserStatsValue<type>::sValue);
|
||||
using Next = ForEachUserStatsValue<static_cast<UserStatsType>(static_cast<std::size_t>(type) + 1)>;
|
||||
Next::apply(std::forward<F>(f));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ForEachUserStatsValue<UserStatsType::Number>
|
||||
{
|
||||
template <class F>
|
||||
static void apply(F&&) {}
|
||||
};
|
||||
|
||||
template <class F>
|
||||
void forEachUserStatsValue(F&& f)
|
||||
{
|
||||
ForEachUserStatsValue<static_cast<UserStatsType>(0)>::apply(std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <UserStatsType sType>
|
||||
class ScopedProfile
|
||||
{
|
||||
public:
|
||||
ScopedProfile(osg::Timer_t frameStart, unsigned int frameNumber, const osg::Timer& timer, osg::Stats& stats)
|
||||
: mScopeStart(timer.tick()),
|
||||
mFrameStart(frameStart),
|
||||
mFrameNumber(frameNumber),
|
||||
mTimer(timer),
|
||||
mStats(stats)
|
||||
{
|
||||
}
|
||||
|
||||
ScopedProfile(const ScopedProfile&) = delete;
|
||||
ScopedProfile& operator=(const ScopedProfile&) = delete;
|
||||
|
||||
~ScopedProfile()
|
||||
{
|
||||
const osg::Timer_t end = mTimer.tick();
|
||||
const UserStats& stats = UserStatsValue<sType>::sValue;
|
||||
|
||||
mStats.setAttribute(mFrameNumber, stats.mBegin, mTimer.delta_s(mFrameStart, mScopeStart));
|
||||
mStats.setAttribute(mFrameNumber, stats.mTaken, mTimer.delta_s(mScopeStart, end));
|
||||
mStats.setAttribute(mFrameNumber, stats.mEnd, mTimer.delta_s(mFrameStart, end));
|
||||
}
|
||||
|
||||
private:
|
||||
const osg::Timer_t mScopeStart;
|
||||
const osg::Timer_t mFrameStart;
|
||||
const unsigned int mFrameNumber;
|
||||
const osg::Timer& mTimer;
|
||||
osg::Stats& mStats;
|
||||
};
|
||||
|
||||
void initStatsHandler(Resource::Profiler& profiler)
|
||||
{
|
||||
const osg::Vec4f textColor(1.f, 1.f, 1.f, 1.f);
|
||||
const osg::Vec4f barColor(1.f, 1.f, 1.f, 1.f);
|
||||
const float multiplier = 1000;
|
||||
const bool average = true;
|
||||
const bool averageInInverseSpace = false;
|
||||
const float maxValue = 10000;
|
||||
|
||||
forEachUserStatsValue([&] (const UserStats& v)
|
||||
{
|
||||
profiler.addUserStatsLine(v.mLabel, textColor, barColor, v.mTaken, multiplier,
|
||||
average, averageInInverseSpace, v.mBegin, v.mEnd, maxValue);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void OMW::Engine::executeLocalScripts()
|
||||
|
@ -128,35 +264,45 @@ bool OMW::Engine::frame(float frametime)
|
|||
{
|
||||
try
|
||||
{
|
||||
mStartTick = mViewer->getStartTick();
|
||||
const osg::Timer_t frameStart = mViewer->getStartTick();
|
||||
const unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
|
||||
const osg::Timer* const timer = osg::Timer::instance();
|
||||
osg::Stats* const stats = mViewer->getViewerStats();
|
||||
|
||||
mEnvironment.setFrameDuration(frametime);
|
||||
|
||||
// update input
|
||||
mEnvironment.getInputManager()->update(frametime, false);
|
||||
{
|
||||
ScopedProfile<UserStatsType::Input> profile(frameStart, frameNumber, *timer, *stats);
|
||||
mEnvironment.getInputManager()->update(frametime, false);
|
||||
}
|
||||
|
||||
// When the window is minimized, pause the game. Currently this *has* to be here to work around a MyGUI bug.
|
||||
// If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures (fixed in MyGUI 3.3.2),
|
||||
// and destroyed widgets will not be deleted (not fixed yet, https://github.com/MyGUI/mygui/issues/21)
|
||||
if (!mEnvironment.getWindowManager()->isWindowVisible())
|
||||
{
|
||||
mEnvironment.getSoundManager()->pausePlayback();
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
ScopedProfile<UserStatsType::Sound> profile(frameStart, frameNumber, *timer, *stats);
|
||||
|
||||
The game cannot be paused in multiplayer, so prevent that from happening even here
|
||||
*/
|
||||
//return false;
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
if (!mEnvironment.getWindowManager()->isWindowVisible())
|
||||
{
|
||||
mEnvironment.getSoundManager()->pausePlayback();
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
The game cannot be paused in multiplayer, so prevent that from happening even here
|
||||
*/
|
||||
//return false;
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
}
|
||||
else
|
||||
mEnvironment.getSoundManager()->resumePlayback();
|
||||
|
||||
// sound
|
||||
if (mUseSound)
|
||||
mEnvironment.getSoundManager()->update(frametime);
|
||||
}
|
||||
else
|
||||
mEnvironment.getSoundManager()->resumePlayback();
|
||||
|
||||
// sound
|
||||
if (mUseSound)
|
||||
mEnvironment.getSoundManager()->update(frametime);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
@ -183,9 +329,10 @@ bool OMW::Engine::frame(float frametime)
|
|||
*/
|
||||
|
||||
// update game state
|
||||
mEnvironment.getStateManager()->update (frametime);
|
||||
|
||||
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
||||
{
|
||||
ScopedProfile<UserStatsType::State> profile(frameStart, frameNumber, *timer, *stats);
|
||||
mEnvironment.getStateManager()->update (frametime);
|
||||
}
|
||||
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
@ -193,103 +340,91 @@ bool OMW::Engine::frame(float frametime)
|
|||
Whether the GUI is active should have no relevance in multiplayer, so the guiActive
|
||||
boolean is always set to false instead
|
||||
*/
|
||||
guiActive = false;
|
||||
//bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
||||
bool guiActive = false;
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
|
||||
osg::Timer_t beforeScriptTick = osg::Timer::instance()->tick();
|
||||
if (mEnvironment.getStateManager()->getState()!=
|
||||
MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
if (!paused)
|
||||
{
|
||||
if (mEnvironment.getWorld()->getScriptsEnabled())
|
||||
{
|
||||
// local scripts
|
||||
executeLocalScripts();
|
||||
ScopedProfile<UserStatsType::Script> profile(frameStart, frameNumber, *timer, *stats);
|
||||
|
||||
// global scripts
|
||||
mEnvironment.getScriptManager()->getGlobalScripts().run();
|
||||
if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
if (!paused)
|
||||
{
|
||||
if (mEnvironment.getWorld()->getScriptsEnabled())
|
||||
{
|
||||
// local scripts
|
||||
executeLocalScripts();
|
||||
|
||||
// global scripts
|
||||
mEnvironment.getScriptManager()->getGlobalScripts().run();
|
||||
}
|
||||
|
||||
mEnvironment.getWorld()->markCellAsUnchanged();
|
||||
}
|
||||
|
||||
mEnvironment.getWorld()->markCellAsUnchanged();
|
||||
if (!guiActive)
|
||||
{
|
||||
double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;
|
||||
mEnvironment.getWorld()->advanceTime(hours, true);
|
||||
mEnvironment.getWorld()->rechargeItems(frametime, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!guiActive)
|
||||
// update mechanics
|
||||
{
|
||||
ScopedProfile<UserStatsType::Mechanics> profile(frameStart, frameNumber, *timer, *stats);
|
||||
|
||||
if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;
|
||||
mEnvironment.getWorld()->advanceTime(hours, true);
|
||||
mEnvironment.getWorld()->rechargeItems(frametime, true);
|
||||
mEnvironment.getMechanicsManager()->update(frametime, guiActive);
|
||||
}
|
||||
}
|
||||
osg::Timer_t afterScriptTick = osg::Timer::instance()->tick();
|
||||
|
||||
// update actors
|
||||
osg::Timer_t beforeMechanicsTick = osg::Timer::instance()->tick();
|
||||
if (mEnvironment.getStateManager()->getState()!=
|
||||
MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
mEnvironment.getMechanicsManager()->update(frametime,
|
||||
guiActive);
|
||||
}
|
||||
osg::Timer_t afterMechanicsTick = osg::Timer::instance()->tick();
|
||||
if (mEnvironment.getStateManager()->getState() == MWBase::StateManager::State_Running)
|
||||
{
|
||||
MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
if (mEnvironment.getStateManager()->getState()==
|
||||
MWBase::StateManager::State_Running)
|
||||
{
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
In multiplayer, the game should not end when the player dies,
|
||||
so the code here has been commented out
|
||||
*/
|
||||
//MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
|
||||
//if(!guiActive && player.getClass().getCreatureStats(player).isDead())
|
||||
// mEnvironment.getStateManager()->endGame();
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
In multiplayer, the game should not end when the player dies,
|
||||
so the code here has been commented out
|
||||
*/
|
||||
//if(!guiActive && player.getClass().getCreatureStats(player).isDead())
|
||||
// mEnvironment.getStateManager()->endGame();
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// update physics
|
||||
osg::Timer_t beforePhysicsTick = osg::Timer::instance()->tick();
|
||||
if (mEnvironment.getStateManager()->getState()!=
|
||||
MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
mEnvironment.getWorld()->updatePhysics(frametime, guiActive);
|
||||
ScopedProfile<UserStatsType::Physics> profile(frameStart, frameNumber, *timer, *stats);
|
||||
|
||||
if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
mEnvironment.getWorld()->updatePhysics(frametime, guiActive);
|
||||
}
|
||||
}
|
||||
osg::Timer_t afterPhysicsTick = osg::Timer::instance()->tick();
|
||||
|
||||
// update world
|
||||
osg::Timer_t beforeWorldTick = osg::Timer::instance()->tick();
|
||||
if (mEnvironment.getStateManager()->getState()!=
|
||||
MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
mEnvironment.getWorld()->update(frametime, guiActive);
|
||||
ScopedProfile<UserStatsType::World> profile(frameStart, frameNumber, *timer, *stats);
|
||||
|
||||
if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
mEnvironment.getWorld()->update(frametime, guiActive);
|
||||
}
|
||||
}
|
||||
osg::Timer_t afterWorldTick = osg::Timer::instance()->tick();
|
||||
|
||||
// update GUI
|
||||
mEnvironment.getWindowManager()->update(frametime);
|
||||
|
||||
unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
|
||||
osg::Stats* stats = mViewer->getViewerStats();
|
||||
stats->setAttribute(frameNumber, "script_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeScriptTick));
|
||||
stats->setAttribute(frameNumber, "script_time_taken", osg::Timer::instance()->delta_s(beforeScriptTick, afterScriptTick));
|
||||
stats->setAttribute(frameNumber, "script_time_end", osg::Timer::instance()->delta_s(mStartTick, afterScriptTick));
|
||||
|
||||
stats->setAttribute(frameNumber, "mechanics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeMechanicsTick));
|
||||
stats->setAttribute(frameNumber, "mechanics_time_taken", osg::Timer::instance()->delta_s(beforeMechanicsTick, afterMechanicsTick));
|
||||
stats->setAttribute(frameNumber, "mechanics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterMechanicsTick));
|
||||
|
||||
stats->setAttribute(frameNumber, "physics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforePhysicsTick));
|
||||
stats->setAttribute(frameNumber, "physics_time_taken", osg::Timer::instance()->delta_s(beforePhysicsTick, afterPhysicsTick));
|
||||
stats->setAttribute(frameNumber, "physics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterPhysicsTick));
|
||||
|
||||
stats->setAttribute(frameNumber, "world_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeWorldTick));
|
||||
stats->setAttribute(frameNumber, "world_time_taken", osg::Timer::instance()->delta_s(beforeWorldTick, afterWorldTick));
|
||||
stats->setAttribute(frameNumber, "world_time_end", osg::Timer::instance()->delta_s(mStartTick, afterWorldTick));
|
||||
{
|
||||
ScopedProfile<UserStatsType::Gui> profile(frameStart, frameNumber, *timer, *stats);
|
||||
mEnvironment.getWindowManager()->update(frametime);
|
||||
}
|
||||
|
||||
if (stats->collectStats("resource"))
|
||||
{
|
||||
|
@ -302,7 +437,6 @@ bool OMW::Engine::frame(float frametime)
|
|||
|
||||
mEnvironment.reportStats(frameNumber, *stats);
|
||||
}
|
||||
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
@ -345,8 +479,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
|||
throw std::runtime_error("Could not initialize SDL! " + std::string(SDL_GetError()));
|
||||
}
|
||||
}
|
||||
|
||||
mStartTick = osg::Timer::instance()->tick();
|
||||
}
|
||||
|
||||
OMW::Engine::~Engine()
|
||||
|
@ -856,14 +988,7 @@ void OMW::Engine::go()
|
|||
// Setup profiler
|
||||
osg::ref_ptr<Resource::Profiler> statshandler = new Resource::Profiler;
|
||||
|
||||
statshandler->addUserStatsLine("Script", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||
"script_time_taken", 1000.0, true, false, "script_time_begin", "script_time_end", 10000);
|
||||
statshandler->addUserStatsLine("Mech", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||
"mechanics_time_taken", 1000.0, true, false, "mechanics_time_begin", "mechanics_time_end", 10000);
|
||||
statshandler->addUserStatsLine("Phys", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||
"physics_time_taken", 1000.0, true, false, "physics_time_begin", "physics_time_end", 10000);
|
||||
statshandler->addUserStatsLine("World", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||
"world_time_taken", 1000.0, true, false, "world_time_begin", "world_time_end", 10000);
|
||||
initStatsHandler(*statshandler);
|
||||
|
||||
mViewer->addEventHandler(statshandler);
|
||||
|
||||
|
@ -915,7 +1040,7 @@ void OMW::Engine::go()
|
|||
|
||||
if (!frame(dt))
|
||||
{
|
||||
OpenThreads::Thread::microSleep(5000);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -111,8 +111,6 @@ namespace OMW
|
|||
bool mScriptBlacklistUse;
|
||||
bool mNewGame;
|
||||
|
||||
osg::Timer_t mStartTick;
|
||||
|
||||
// not implemented
|
||||
Engine (const Engine&);
|
||||
Engine& operator= (const Engine&);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "environment.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <OpenThreads/Thread>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "world.hpp"
|
||||
#include "scriptmanager.hpp"
|
||||
|
@ -99,7 +99,7 @@ void MWBase::Environment::limitFrameRate(double dt) const
|
|||
double minFrameTime = 1.0 / static_cast<double>(mFrameRateLimit);
|
||||
if (thisFrameTime < minFrameTime)
|
||||
{
|
||||
OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-thisFrameTime));
|
||||
std::this_thread::sleep_for(std::chrono::duration<double>(minFrameTime - thisFrameTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,10 +67,6 @@ namespace MWBase
|
|||
virtual void drop (const MWWorld::CellStore *cellStore) = 0;
|
||||
///< Deregister all objects in the given cell.
|
||||
|
||||
virtual void watchActor (const MWWorld::Ptr& ptr) = 0;
|
||||
///< On each update look for changes in a previously registered actor and update the
|
||||
/// GUI accordingly.
|
||||
|
||||
virtual void update (float duration, bool paused) = 0;
|
||||
///< Update objects
|
||||
///
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <set>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwsound/type.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
@ -44,14 +45,7 @@ namespace MWSound
|
|||
LoopNoEnv = Loop | NoEnv,
|
||||
LoopRemoveAtDistance = Loop | RemoveAtDistance
|
||||
};
|
||||
enum class Type {
|
||||
Sfx = 1<<4, /* Normal SFX sound */
|
||||
Voice = 1<<5, /* Voice sound */
|
||||
Foot = 1<<6, /* Footstep sound */
|
||||
Music = 1<<7, /* Music track */
|
||||
Movie = 1<<8, /* Movie audio track */
|
||||
Mask = Sfx | Voice | Foot | Music | Movie
|
||||
};
|
||||
|
||||
// Used for creating a type mask for SoundManager::pauseSounds and resumeSounds
|
||||
inline int operator~(Type a) { return ~static_cast<int>(a); }
|
||||
inline int operator&(Type a, Type b) { return static_cast<int>(a) & static_cast<int>(b); }
|
||||
|
@ -163,9 +157,6 @@ namespace MWBase
|
|||
virtual void stopSound(const MWWorld::CellStore *cell) = 0;
|
||||
///< Stop all sounds for the given cell.
|
||||
|
||||
virtual void stopSound(const std::string& soundId) = 0;
|
||||
///< Stop a non-3d looping sound
|
||||
|
||||
virtual void fadeOutSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float duration) = 0;
|
||||
///< Fade out given sound (that is already playing) of given object
|
||||
///< @param reference Reference to object, whose sound is faded out
|
||||
|
|
|
@ -188,27 +188,11 @@ namespace MWBase
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
/// Set value for the given ID.
|
||||
virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value) = 0;
|
||||
virtual void setValue (int parSkill, const MWMechanics::SkillValue& value) = 0;
|
||||
virtual void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value) = 0;
|
||||
virtual void setValue (const std::string& id, const std::string& value) = 0;
|
||||
virtual void setValue (const std::string& id, int value) = 0;
|
||||
|
||||
/// Set time left for the player to start drowning (update the drowning bar)
|
||||
/// @param time time left to start drowning
|
||||
/// @param maxTime how long we can be underwater (in total) until drowning starts
|
||||
virtual void setDrowningTimeLeft (float time, float maxTime) = 0;
|
||||
|
||||
virtual void setPlayerClass (const ESM::Class &class_) = 0;
|
||||
///< set current class of player
|
||||
|
||||
virtual void configureSkills (const SkillList& major, const SkillList& minor) = 0;
|
||||
///< configure skill groups, each set contains the skill ID for that group.
|
||||
|
||||
virtual void updateSkillArea() = 0;
|
||||
///< update display of skills, factions, birth sign, reputation and bounty
|
||||
|
||||
virtual void changeCell(const MWWorld::CellStore* cell) = 0;
|
||||
///< change the active cell
|
||||
|
||||
|
@ -445,6 +429,9 @@ namespace MWBase
|
|||
virtual void windowResized(int x, int y) = 0;
|
||||
virtual void windowClosed() = 0;
|
||||
virtual bool isWindowVisible() = 0;
|
||||
|
||||
virtual void watchActor(const MWWorld::Ptr& ptr) = 0;
|
||||
virtual MWWorld::Ptr getWatchedActor() const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -724,10 +724,11 @@ namespace MWClass
|
|||
moveSpeed = getSwimSpeed(ptr);
|
||||
else
|
||||
moveSpeed = getWalkSpeed(ptr);
|
||||
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
|
||||
moveSpeed *= 0.75f;
|
||||
|
||||
moveSpeed *= ptr.getClass().getMovementSettings(ptr).mSpeedFactor;
|
||||
const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr);
|
||||
if (movementSettings.mIsStrafing)
|
||||
moveSpeed *= 0.75f;
|
||||
moveSpeed *= movementSettings.mSpeedFactor;
|
||||
|
||||
return moveSpeed;
|
||||
}
|
||||
|
|
|
@ -929,7 +929,22 @@ namespace MWClass
|
|||
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
|
||||
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
|
||||
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
|
||||
if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name())
|
||||
bool hasArmor = !armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name();
|
||||
// If there's no item in the carried left slot or if it is not a shield redistribute the hit.
|
||||
if (!hasArmor && hitslot == MWWorld::InventoryStore::Slot_CarriedLeft)
|
||||
{
|
||||
if (Misc::Rng::rollDice(2) == 0)
|
||||
hitslot = MWWorld::InventoryStore::Slot_Cuirass;
|
||||
else
|
||||
hitslot = MWWorld::InventoryStore::Slot_LeftPauldron;
|
||||
armorslot = inv.getSlot(hitslot);
|
||||
if (armorslot != inv.end())
|
||||
{
|
||||
armor = *armorslot;
|
||||
hasArmor = !armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name();
|
||||
}
|
||||
}
|
||||
if (hasArmor)
|
||||
{
|
||||
if (!object.isEmpty() || attacker.isEmpty() || attacker.getClass().isNpc()) // Unarmed creature attacks don't affect armor condition
|
||||
{
|
||||
|
@ -1189,13 +1204,14 @@ namespace MWClass
|
|||
moveSpeed = getRunSpeed(ptr);
|
||||
else
|
||||
moveSpeed = getWalkSpeed(ptr);
|
||||
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
|
||||
moveSpeed *= 0.75f;
|
||||
|
||||
if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing)
|
||||
moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat();
|
||||
|
||||
moveSpeed *= ptr.getClass().getMovementSettings(ptr).mSpeedFactor;
|
||||
const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr);
|
||||
if (movementSettings.mIsStrafing)
|
||||
moveSpeed *= 0.75f;
|
||||
moveSpeed *= movementSettings.mSpeedFactor;
|
||||
|
||||
return moveSpeed;
|
||||
}
|
||||
|
|
|
@ -368,7 +368,6 @@ namespace MWGui
|
|||
if (klass)
|
||||
{
|
||||
mPlayerClass = *klass;
|
||||
MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass);
|
||||
}
|
||||
MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog);
|
||||
mPickClassDialog = 0;
|
||||
|
@ -434,7 +433,6 @@ namespace MWGui
|
|||
End of tes3mp change (major)
|
||||
*/
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->setValue("name", mPlayerName);
|
||||
MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName);
|
||||
MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog);
|
||||
mNameDialog = 0;
|
||||
|
@ -537,7 +535,6 @@ namespace MWGui
|
|||
|
||||
MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass);
|
||||
mPlayerClass = klass;
|
||||
MWBase::Environment::get().getWindowManager()->setPlayerClass(klass);
|
||||
|
||||
// Do not delete dialog, so that choices are remembered in case we want to go back and adjust them later
|
||||
mCreateClassDialog->setVisible(false);
|
||||
|
@ -734,7 +731,6 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(mGenerateClass);
|
||||
|
||||
mPlayerClass = *klass;
|
||||
MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass);
|
||||
|
||||
updatePlayerHealth();
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
#ifndef CHARACTER_CREATION_HPP
|
||||
#define CHARACTER_CREATION_HPP
|
||||
|
||||
#include <components/esm/loadskil.hpp>
|
||||
#include <components/esm/loadclas.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "../mwmechanics/stat.hpp"
|
||||
#include "statswatcher.hpp"
|
||||
|
||||
namespace osg
|
||||
{
|
||||
|
@ -35,21 +34,21 @@ namespace MWGui
|
|||
class ReviewDialog;
|
||||
class MessageBoxManager;
|
||||
|
||||
class CharacterCreation
|
||||
class CharacterCreation : public StatsListener
|
||||
{
|
||||
public:
|
||||
typedef std::vector<int> SkillList;
|
||||
|
||||
CharacterCreation(osg::Group* parent, Resource::ResourceSystem* resourceSystem);
|
||||
~CharacterCreation();
|
||||
virtual ~CharacterCreation();
|
||||
|
||||
//Show a dialog
|
||||
void spawnDialog(const char id);
|
||||
|
||||
void setValue (const std::string& id, const MWMechanics::AttributeValue& value);
|
||||
void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value);
|
||||
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value);
|
||||
void configureSkills (const SkillList& major, const SkillList& minor);
|
||||
void setValue (const std::string& id, const MWMechanics::AttributeValue& value) override;
|
||||
void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value) override;
|
||||
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) override;
|
||||
void configureSkills(const SkillList& major, const SkillList& minor) override;
|
||||
|
||||
void onFrame(float duration);
|
||||
|
||||
|
|
|
@ -765,6 +765,9 @@ namespace MWGui
|
|||
|
||||
void DialogueWindow::updateTopicFormat()
|
||||
{
|
||||
if (!Settings::Manager::getBool("color topic enable", "GUI"))
|
||||
return;
|
||||
|
||||
std::string specialColour = Settings::Manager::getString("color topic specific", "GUI");
|
||||
std::string oldColour = Settings::Manager::getString("color topic exhausted", "GUI");
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
#define OPENMW_GAME_MWGUI_HUD_H
|
||||
|
||||
#include "mapwindow.hpp"
|
||||
|
||||
#include "../mwmechanics/stat.hpp"
|
||||
#include "statswatcher.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
@ -17,12 +16,12 @@ namespace MWGui
|
|||
class ItemWidget;
|
||||
class SpellWidget;
|
||||
|
||||
class HUD : public WindowBase, public LocalMapBase
|
||||
class HUD : public WindowBase, public LocalMapBase, public StatsListener
|
||||
{
|
||||
public:
|
||||
HUD(CustomMarkerCollection& customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender);
|
||||
virtual ~HUD();
|
||||
void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value);
|
||||
void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value) override;
|
||||
|
||||
/// Set time left for the player to start drowning
|
||||
/// @param time time left to start drowning
|
||||
|
@ -48,7 +47,7 @@ namespace MWGui
|
|||
void setCrosshairVisible(bool visible);
|
||||
void setCrosshairOwned(bool owned);
|
||||
|
||||
void onFrame(float dt);
|
||||
void onFrame(float dt) override;
|
||||
|
||||
void setCellName(const std::string& cellName);
|
||||
|
||||
|
@ -59,7 +58,7 @@ namespace MWGui
|
|||
void setEnemy(const MWWorld::Ptr& enemy);
|
||||
void resetEnemy();
|
||||
|
||||
void clear();
|
||||
void clear() override;
|
||||
|
||||
private:
|
||||
MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning;
|
||||
|
@ -113,8 +112,8 @@ namespace MWGui
|
|||
void onMapClicked(MyGUI::Widget* _sender);
|
||||
|
||||
// LocalMapBase
|
||||
virtual void customMarkerCreated(MyGUI::Widget* marker);
|
||||
virtual void doorMarkerCreated(MyGUI::Widget* marker);
|
||||
virtual void customMarkerCreated(MyGUI::Widget* marker) override;
|
||||
virtual void doorMarkerCreated(MyGUI::Widget* marker) override;
|
||||
|
||||
void updateEnemyHealthBar();
|
||||
|
||||
|
|
209
apps/openmw/mwgui/statswatcher.cpp
Normal file
209
apps/openmw/mwgui/statswatcher.cpp
Normal file
|
@ -0,0 +1,209 @@
|
|||
#include "statswatcher.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/spellutil.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
// mWatchedTimeToStartDrowning = -1 for correct drowning state check,
|
||||
// if stats.getTimeToStartDrowning() == 0 already on game start
|
||||
StatsWatcher::StatsWatcher()
|
||||
: mWatchedLevel(-1), mWatchedTimeToStartDrowning(-1), mWatchedStatsEmpty(true)
|
||||
{
|
||||
}
|
||||
|
||||
void StatsWatcher::watchActor(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mWatched = ptr;
|
||||
}
|
||||
|
||||
void StatsWatcher::update()
|
||||
{
|
||||
if (mWatched.isEmpty())
|
||||
return;
|
||||
|
||||
MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
|
||||
const MWMechanics::NpcStats &stats = mWatched.getClass().getNpcStats(mWatched);
|
||||
for (int i = 0;i < ESM::Attribute::Length;++i)
|
||||
{
|
||||
if (stats.getAttribute(i) != mWatchedAttributes[i] || mWatchedStatsEmpty)
|
||||
{
|
||||
std::stringstream attrname;
|
||||
attrname << "AttribVal"<<(i+1);
|
||||
|
||||
mWatchedAttributes[i] = stats.getAttribute(i);
|
||||
setValue(attrname.str(), stats.getAttribute(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (stats.getHealth() != mWatchedHealth || mWatchedStatsEmpty)
|
||||
{
|
||||
static const std::string hbar("HBar");
|
||||
mWatchedHealth = stats.getHealth();
|
||||
setValue(hbar, stats.getHealth());
|
||||
}
|
||||
if (stats.getMagicka() != mWatchedMagicka || mWatchedStatsEmpty)
|
||||
{
|
||||
static const std::string mbar("MBar");
|
||||
mWatchedMagicka = stats.getMagicka();
|
||||
setValue(mbar, stats.getMagicka());
|
||||
}
|
||||
if (stats.getFatigue() != mWatchedFatigue || mWatchedStatsEmpty)
|
||||
{
|
||||
static const std::string fbar("FBar");
|
||||
mWatchedFatigue = stats.getFatigue();
|
||||
setValue(fbar, stats.getFatigue());
|
||||
}
|
||||
|
||||
float timeToDrown = stats.getTimeToStartDrowning();
|
||||
|
||||
if (timeToDrown != mWatchedTimeToStartDrowning)
|
||||
{
|
||||
static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||
.find("fHoldBreathTime")->mValue.getFloat();
|
||||
|
||||
mWatchedTimeToStartDrowning = timeToDrown;
|
||||
|
||||
if(timeToDrown >= fHoldBreathTime || timeToDrown == -1.0) // -1.0 is a special value during initialization
|
||||
winMgr->setDrowningBarVisibility(false);
|
||||
else
|
||||
{
|
||||
winMgr->setDrowningBarVisibility(true);
|
||||
winMgr->setDrowningTimeLeft(stats.getTimeToStartDrowning(), fHoldBreathTime);
|
||||
}
|
||||
}
|
||||
|
||||
//Loop over ESM::Skill::SkillEnum
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
{
|
||||
if(stats.getSkill(i) != mWatchedSkills[i] || mWatchedStatsEmpty)
|
||||
{
|
||||
mWatchedSkills[i] = stats.getSkill(i);
|
||||
setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (stats.getLevel() != mWatchedLevel || mWatchedStatsEmpty)
|
||||
{
|
||||
mWatchedLevel = stats.getLevel();
|
||||
setValue("level", mWatchedLevel);
|
||||
}
|
||||
|
||||
if (mWatched.getClass().isNpc())
|
||||
{
|
||||
const ESM::NPC *watchedRecord = mWatched.get<ESM::NPC>()->mBase;
|
||||
|
||||
if (watchedRecord->mName != mWatchedName || mWatchedStatsEmpty)
|
||||
{
|
||||
mWatchedName = watchedRecord->mName;
|
||||
setValue("name", watchedRecord->mName);
|
||||
}
|
||||
|
||||
if (watchedRecord->mRace != mWatchedRace || mWatchedStatsEmpty)
|
||||
{
|
||||
mWatchedRace = watchedRecord->mRace;
|
||||
const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore()
|
||||
.get<ESM::Race>().find(watchedRecord->mRace);
|
||||
setValue("race", race->mName);
|
||||
}
|
||||
|
||||
if (watchedRecord->mClass != mWatchedClass || mWatchedStatsEmpty)
|
||||
{
|
||||
mWatchedClass = watchedRecord->mClass;
|
||||
const ESM::Class *cls = MWBase::Environment::get().getWorld()->getStore()
|
||||
.get<ESM::Class>().find(watchedRecord->mClass);
|
||||
setValue("class", cls->mName);
|
||||
|
||||
MWBase::WindowManager::SkillList majorSkills (5);
|
||||
MWBase::WindowManager::SkillList minorSkills (5);
|
||||
|
||||
for (int i=0; i<5; ++i)
|
||||
{
|
||||
minorSkills[i] = cls->mData.mSkills[i][0];
|
||||
majorSkills[i] = cls->mData.mSkills[i][1];
|
||||
}
|
||||
|
||||
configureSkills(majorSkills, minorSkills);
|
||||
}
|
||||
}
|
||||
|
||||
mWatchedStatsEmpty = false;
|
||||
|
||||
// Update the equipped weapon icon
|
||||
MWWorld::InventoryStore& inv = mWatched.getClass().getInventoryStore(mWatched);
|
||||
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if (weapon == inv.end())
|
||||
winMgr->unsetSelectedWeapon();
|
||||
else
|
||||
winMgr->setSelectedWeapon(*weapon);
|
||||
|
||||
// Update the selected spell icon
|
||||
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
|
||||
if (enchantItem != inv.end())
|
||||
winMgr->setSelectedEnchantItem(*enchantItem);
|
||||
else
|
||||
{
|
||||
const std::string& spell = winMgr->getSelectedSpell();
|
||||
if (!spell.empty())
|
||||
winMgr->setSelectedSpell(spell, int(MWMechanics::getSpellSuccessChance(spell, mWatched)));
|
||||
else
|
||||
winMgr->unsetSelectedSpell();
|
||||
}
|
||||
}
|
||||
|
||||
void StatsWatcher::addListener(StatsListener* listener)
|
||||
{
|
||||
mListeners.insert(listener);
|
||||
}
|
||||
|
||||
void StatsWatcher::removeListener(StatsListener* listener)
|
||||
{
|
||||
mListeners.erase(listener);
|
||||
}
|
||||
|
||||
void StatsWatcher::setValue(const std::string& id, const MWMechanics::AttributeValue& value)
|
||||
{
|
||||
for (StatsListener* listener : mListeners)
|
||||
listener->setValue(id, value);
|
||||
}
|
||||
|
||||
void StatsWatcher::setValue(ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value)
|
||||
{
|
||||
/// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we
|
||||
/// allow custom skills.
|
||||
for (StatsListener* listener : mListeners)
|
||||
listener->setValue(parSkill, value);
|
||||
}
|
||||
|
||||
void StatsWatcher::setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value)
|
||||
{
|
||||
for (StatsListener* listener : mListeners)
|
||||
listener->setValue(id, value);
|
||||
}
|
||||
|
||||
void StatsWatcher::setValue(const std::string& id, const std::string& value)
|
||||
{
|
||||
for (StatsListener* listener : mListeners)
|
||||
listener->setValue(id, value);
|
||||
}
|
||||
|
||||
void StatsWatcher::setValue(const std::string& id, int value)
|
||||
{
|
||||
for (StatsListener* listener : mListeners)
|
||||
listener->setValue(id, value);
|
||||
}
|
||||
|
||||
void StatsWatcher::configureSkills(const std::vector<int>& major, const std::vector<int>& minor)
|
||||
{
|
||||
for (StatsListener* listener : mListeners)
|
||||
listener->configureSkills(major, minor);
|
||||
}
|
||||
}
|
69
apps/openmw/mwgui/statswatcher.hpp
Normal file
69
apps/openmw/mwgui/statswatcher.hpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#ifndef MWGUI_STATSWATCHER_H
|
||||
#define MWGUI_STATSWATCHER_H
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <components/esm/attr.hpp>
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../mwmechanics/stat.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class StatsListener
|
||||
{
|
||||
public:
|
||||
/// Set value for the given ID.
|
||||
virtual void setValue(const std::string& id, const MWMechanics::AttributeValue& value) {}
|
||||
virtual void setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value) {}
|
||||
virtual void setValue(const std::string& id, const std::string& value) {}
|
||||
virtual void setValue(const std::string& id, int value) {}
|
||||
virtual void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) {}
|
||||
virtual void configureSkills(const std::vector<int>& major, const std::vector<int>& minor) {}
|
||||
};
|
||||
|
||||
class StatsWatcher
|
||||
{
|
||||
MWWorld::Ptr mWatched;
|
||||
|
||||
MWMechanics::AttributeValue mWatchedAttributes[ESM::Attribute::Length];
|
||||
MWMechanics::SkillValue mWatchedSkills[ESM::Skill::Length];
|
||||
|
||||
MWMechanics::DynamicStat<float> mWatchedHealth;
|
||||
MWMechanics::DynamicStat<float> mWatchedMagicka;
|
||||
MWMechanics::DynamicStat<float> mWatchedFatigue;
|
||||
|
||||
std::string mWatchedName;
|
||||
std::string mWatchedRace;
|
||||
std::string mWatchedClass;
|
||||
|
||||
int mWatchedLevel;
|
||||
|
||||
float mWatchedTimeToStartDrowning;
|
||||
|
||||
bool mWatchedStatsEmpty;
|
||||
|
||||
std::set<StatsListener*> mListeners;
|
||||
|
||||
void setValue(const std::string& id, const MWMechanics::AttributeValue& value);
|
||||
void setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value);
|
||||
void setValue(const std::string& id, const std::string& value);
|
||||
void setValue(const std::string& id, int value);
|
||||
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value);
|
||||
void configureSkills(const std::vector<int>& major, const std::vector<int>& minor);
|
||||
|
||||
public:
|
||||
StatsWatcher();
|
||||
|
||||
void update();
|
||||
void addListener(StatsListener* listener);
|
||||
void removeListener(StatsListener* listener);
|
||||
|
||||
void watchActor(const MWWorld::Ptr& ptr);
|
||||
MWWorld::Ptr getWatchedActor() const { return mWatched; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -620,10 +620,10 @@ namespace MWGui
|
|||
|
||||
text += "\n";
|
||||
|
||||
if (rankData.mSkill1 > 0)
|
||||
text += "\n#{sNeedOneSkill} " + MyGUI::utility::toString(rankData.mSkill1);
|
||||
if (rankData.mSkill2 > 0)
|
||||
text += " #{sand} #{sNeedTwoSkills} " + MyGUI::utility::toString(rankData.mSkill2);
|
||||
if (rankData.mPrimarySkill > 0)
|
||||
text += "\n#{sNeedOneSkill} " + MyGUI::utility::toString(rankData.mPrimarySkill);
|
||||
if (rankData.mFavouredSkill > 0)
|
||||
text += " #{sand} #{sNeedTwoSkills} " + MyGUI::utility::toString(rankData.mFavouredSkill);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
#ifndef MWGUI_STATS_WINDOW_H
|
||||
#define MWGUI_STATS_WINDOW_H
|
||||
|
||||
#include "../mwmechanics/stat.hpp"
|
||||
#include "statswatcher.hpp"
|
||||
#include "windowpinnablebase.hpp"
|
||||
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class WindowManager;
|
||||
|
||||
class StatsWindow : public WindowPinnableBase, public NoDrop
|
||||
class StatsWindow : public WindowPinnableBase, public NoDrop, public StatsListener
|
||||
{
|
||||
public:
|
||||
typedef std::map<std::string, int> FactionList;
|
||||
|
@ -20,24 +18,24 @@ namespace MWGui
|
|||
StatsWindow(DragAndDrop* drag);
|
||||
|
||||
/// automatically updates all the data in the stats window, but only if it has changed.
|
||||
void onFrame(float dt);
|
||||
void onFrame(float dt) override;
|
||||
|
||||
void setBar(const std::string& name, const std::string& tname, int val, int max);
|
||||
void setPlayerName(const std::string& playerName);
|
||||
|
||||
/// Set value for the given ID.
|
||||
void setValue (const std::string& id, const MWMechanics::AttributeValue& value);
|
||||
void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value);
|
||||
void setValue (const std::string& id, const std::string& value);
|
||||
void setValue (const std::string& id, int value);
|
||||
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value);
|
||||
void setValue (const std::string& id, const MWMechanics::AttributeValue& value) override;
|
||||
void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value) override;
|
||||
void setValue (const std::string& id, const std::string& value) override;
|
||||
void setValue (const std::string& id, int value) override;
|
||||
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) override;
|
||||
void configureSkills(const SkillList& major, const SkillList& minor) override;
|
||||
|
||||
void configureSkills (const SkillList& major, const SkillList& minor);
|
||||
void setReputation (int reputation) { if (reputation != mReputation) mChanged = true; this->mReputation = reputation; }
|
||||
void setBounty (int bounty) { if (bounty != mBounty) mChanged = true; this->mBounty = bounty; }
|
||||
void updateSkillArea();
|
||||
|
||||
virtual void onOpen() { onWindowResize(mMainWidget->castType<MyGUI::Window>()); }
|
||||
virtual void onOpen() override { onWindowResize(mMainWidget->castType<MyGUI::Window>()); }
|
||||
|
||||
private:
|
||||
void addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
|
||||
|
@ -72,8 +70,8 @@ namespace MWGui
|
|||
const int mMinFullWidth;
|
||||
|
||||
protected:
|
||||
virtual void onPinToggled();
|
||||
virtual void onTitleDoubleClicked();
|
||||
virtual void onPinToggled() override;
|
||||
virtual void onTitleDoubleClicked() override;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "windowmanagerimp.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
|
@ -284,6 +286,8 @@ namespace MWGui
|
|||
mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer);
|
||||
mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"),
|
||||
Settings::Manager::getFloat("contrast", "Video"));
|
||||
|
||||
mStatsWatcher.reset(new StatsWatcher());
|
||||
}
|
||||
|
||||
void WindowManager::loadUserFonts()
|
||||
|
@ -487,6 +491,10 @@ namespace MWGui
|
|||
|
||||
// Set up visibility
|
||||
updateVisible();
|
||||
|
||||
mStatsWatcher->addListener(mHud);
|
||||
mStatsWatcher->addListener(mStatsWindow);
|
||||
mStatsWatcher->addListener(mCharGen);
|
||||
}
|
||||
|
||||
int WindowManager::getFontHeight() const
|
||||
|
@ -499,8 +507,11 @@ namespace MWGui
|
|||
if (newgame)
|
||||
{
|
||||
disallowAll();
|
||||
|
||||
mStatsWatcher->removeListener(mCharGen);
|
||||
delete mCharGen;
|
||||
mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem);
|
||||
mStatsWatcher->addListener(mCharGen);
|
||||
}
|
||||
else
|
||||
allow(GW_ALL);
|
||||
|
@ -510,6 +521,8 @@ namespace MWGui
|
|||
{
|
||||
try
|
||||
{
|
||||
mStatsWatcher.reset();
|
||||
|
||||
mKeyboardNavigation.reset();
|
||||
|
||||
MyGUI::LanguageManager::getInstance().eventRequestTag.clear();
|
||||
|
@ -679,58 +692,11 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
void WindowManager::setValue (const std::string& id, const MWMechanics::AttributeValue& value)
|
||||
{
|
||||
mStatsWindow->setValue (id, value);
|
||||
mCharGen->setValue(id, value);
|
||||
}
|
||||
|
||||
void WindowManager::setValue (int parSkill, const MWMechanics::SkillValue& value)
|
||||
{
|
||||
/// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we
|
||||
/// allow custom skills.
|
||||
mStatsWindow->setValue(static_cast<ESM::Skill::SkillEnum> (parSkill), value);
|
||||
mCharGen->setValue(static_cast<ESM::Skill::SkillEnum> (parSkill), value);
|
||||
}
|
||||
|
||||
void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value)
|
||||
{
|
||||
mStatsWindow->setValue (id, value);
|
||||
mHud->setValue (id, value);
|
||||
mCharGen->setValue(id, value);
|
||||
}
|
||||
|
||||
void WindowManager::setValue (const std::string& id, const std::string& value)
|
||||
{
|
||||
mStatsWindow->setValue (id, value);
|
||||
}
|
||||
|
||||
void WindowManager::setValue (const std::string& id, int value)
|
||||
{
|
||||
mStatsWindow->setValue (id, value);
|
||||
}
|
||||
|
||||
void WindowManager::setDrowningTimeLeft (float time, float maxTime)
|
||||
{
|
||||
mHud->setDrowningTimeLeft(time, maxTime);
|
||||
}
|
||||
|
||||
void WindowManager::setPlayerClass (const ESM::Class &class_)
|
||||
{
|
||||
mStatsWindow->setValue("class", class_.mName);
|
||||
}
|
||||
|
||||
void WindowManager::configureSkills (const SkillList& major, const SkillList& minor)
|
||||
{
|
||||
mStatsWindow->configureSkills (major, minor);
|
||||
mCharGen->configureSkills(major, minor);
|
||||
}
|
||||
|
||||
void WindowManager::updateSkillArea()
|
||||
{
|
||||
mStatsWindow->updateSkillArea();
|
||||
}
|
||||
|
||||
void WindowManager::removeDialog(Layout*dialog)
|
||||
{
|
||||
if (!dialog)
|
||||
|
@ -781,7 +747,7 @@ namespace MWGui
|
|||
MWBase::Environment::get().getInputManager()->update(dt, true, false);
|
||||
|
||||
if (!mWindowVisible)
|
||||
OpenThreads::Thread::microSleep(5000);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
else
|
||||
{
|
||||
mViewer->eventTraversal();
|
||||
|
@ -947,7 +913,9 @@ namespace MWGui
|
|||
if (mCharGen)
|
||||
mCharGen->onFrame(frameDuration);
|
||||
|
||||
updateActivatedQuickKey ();
|
||||
updateActivatedQuickKey();
|
||||
|
||||
mStatsWatcher->update();
|
||||
|
||||
cleanupGarbage();
|
||||
}
|
||||
|
@ -1917,7 +1885,7 @@ namespace MWGui
|
|||
if (!mWindowVisible)
|
||||
{
|
||||
mVideoWidget->pause();
|
||||
OpenThreads::Thread::microSleep(5000);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2368,4 +2336,14 @@ namespace MWGui
|
|||
for (unsigned int i=0; i<mWindows.size(); ++i)
|
||||
mWindows[i]->setVisible(visible);
|
||||
}
|
||||
|
||||
void WindowManager::watchActor(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mStatsWatcher->watchActor(ptr);
|
||||
}
|
||||
|
||||
MWWorld::Ptr WindowManager::getWatchedActor() const
|
||||
{
|
||||
return mStatsWatcher->getWatchedActor();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,13 +13,12 @@
|
|||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include <components/sdlutil/events.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
#include "mapwindow.hpp"
|
||||
#include "statswatcher.hpp"
|
||||
#include "textcolours.hpp"
|
||||
|
||||
#include <MyGUI_KeyCode.h>
|
||||
|
@ -229,22 +228,11 @@ namespace MWGui
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
///< Set value for the given ID.
|
||||
virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value);
|
||||
virtual void setValue (int parSkill, const MWMechanics::SkillValue& value);
|
||||
virtual void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value);
|
||||
virtual void setValue (const std::string& id, const std::string& value);
|
||||
virtual void setValue (const std::string& id, int value);
|
||||
|
||||
/// Set time left for the player to start drowning (update the drowning bar)
|
||||
/// @param time time left to start drowning
|
||||
/// @param maxTime how long we can be underwater (in total) until drowning starts
|
||||
virtual void setDrowningTimeLeft (float time, float maxTime);
|
||||
|
||||
virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player
|
||||
virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group.
|
||||
virtual void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty
|
||||
|
||||
virtual void changeCell(const MWWorld::CellStore* cell); ///< change the active cell
|
||||
|
||||
/*
|
||||
|
@ -374,6 +362,9 @@ namespace MWGui
|
|||
virtual void windowClosed();
|
||||
virtual bool isWindowVisible();
|
||||
|
||||
virtual void watchActor(const MWWorld::Ptr& ptr);
|
||||
virtual MWWorld::Ptr getWatchedActor() const;
|
||||
|
||||
virtual void executeInConsole (const std::string& path);
|
||||
|
||||
/*
|
||||
|
@ -486,6 +477,7 @@ namespace MWGui
|
|||
osgViewer::Viewer* mViewer;
|
||||
|
||||
std::unique_ptr<Gui::FontLoader> mFontLoader;
|
||||
std::unique_ptr<StatsWatcher> mStatsWatcher;
|
||||
|
||||
bool mConsoleOnlyScripts;
|
||||
|
||||
|
|
|
@ -237,6 +237,11 @@ namespace MWInput
|
|||
}
|
||||
}
|
||||
|
||||
void BindingsManager::setJoystickDeadZone(float deadZone)
|
||||
{
|
||||
mInputBinder->setJoystickDeadZone(deadZone);
|
||||
}
|
||||
|
||||
float BindingsManager::getActionValue (int id) const
|
||||
{
|
||||
return mInputBinder->getChannel(id)->getValue();
|
||||
|
|
|
@ -36,6 +36,8 @@ namespace MWInput
|
|||
|
||||
void setPlayerControlsEnabled(bool enabled);
|
||||
|
||||
void setJoystickDeadZone(float deadZone);
|
||||
|
||||
bool isLeftOrRightButton(int action, bool joystick) const;
|
||||
|
||||
bool actionIsActive(int id) const;
|
||||
|
|
|
@ -72,6 +72,10 @@ namespace MWInput
|
|||
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
||||
if (uiScale != 0.f)
|
||||
mInvUiScalingFactor = 1.f / uiScale;
|
||||
|
||||
float deadZoneRadius = Settings::Manager::getFloat("joystick dead zone", "Input");
|
||||
deadZoneRadius = std::min(std::max(deadZoneRadius, 0.0f), 0.5f);
|
||||
mBindingsManager->setJoystickDeadZone(deadZoneRadius);
|
||||
}
|
||||
|
||||
void ControllerManager::processChangedSettings(const Settings::CategorySettingVector& changed)
|
||||
|
@ -100,10 +104,9 @@ namespace MWInput
|
|||
// game mode does not move the position of the GUI cursor
|
||||
float xMove = xAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed;
|
||||
float yMove = yAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed;
|
||||
if (xMove != 0 || yMove != 0 || zAxis != 0)
|
||||
float mouseWheelMove = -zAxis * dt * 1500.0f;
|
||||
if (xMove != 0 || yMove != 0 || mouseWheelMove != 0)
|
||||
{
|
||||
int mouseWheelMove = static_cast<int>(-zAxis * dt * 1500.0f);
|
||||
|
||||
mMouseManager->injectMouseMove(xMove, yMove, mouseWheelMove);
|
||||
mMouseManager->warpMouse();
|
||||
MWBase::Environment::get().getWindowManager()->setCursorActive(true);
|
||||
|
|
|
@ -229,10 +229,10 @@ namespace MWInput
|
|||
|
||||
bool MouseManager::injectMouseButtonRelease(Uint8 button)
|
||||
{
|
||||
return MyGUI::InputManager::getInstance().injectMousePress(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(button));
|
||||
return MyGUI::InputManager::getInstance().injectMouseRelease(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(button));
|
||||
}
|
||||
|
||||
void MouseManager::injectMouseMove(int xMove, int yMove, int mouseWheelMove)
|
||||
void MouseManager::injectMouseMove(float xMove, float yMove, float mouseWheelMove)
|
||||
{
|
||||
mGuiCursorX += xMove;
|
||||
mGuiCursorY += yMove;
|
||||
|
@ -242,7 +242,7 @@ namespace MWInput
|
|||
mGuiCursorX = std::max(0.f, std::min(mGuiCursorX, float(viewSize.width - 1)));
|
||||
mGuiCursorY = std::max(0.f, std::min(mGuiCursorY, float(viewSize.height - 1)));
|
||||
|
||||
MyGUI::InputManager::getInstance().injectMouseMove(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), mMouseWheel);
|
||||
MyGUI::InputManager::getInstance().injectMouseMove(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), static_cast<int>(mMouseWheel));
|
||||
}
|
||||
|
||||
void MouseManager::warpMouse()
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace MWInput
|
|||
|
||||
bool injectMouseButtonPress(Uint8 button);
|
||||
bool injectMouseButtonRelease(Uint8 button);
|
||||
void injectMouseMove(int xMove, int yMove, int mouseWheelMove);
|
||||
void injectMouseMove(float xMove, float yMove, float mouseWheelMove);
|
||||
void warpMouse();
|
||||
|
||||
void setMouseLookEnabled(bool enabled) { mMouseLookEnabled = enabled; }
|
||||
|
|
|
@ -924,6 +924,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
|||
, mAttackingOrSpell(false)
|
||||
, mCastingManualSpell(false)
|
||||
, mTimeUntilWake(0.f)
|
||||
, mIsMovingBackward(false)
|
||||
{
|
||||
if(!mAnimation)
|
||||
return;
|
||||
|
@ -2097,41 +2098,25 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak) && !flying;
|
||||
bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !flying;
|
||||
CreatureStats &stats = cls.getCreatureStats(mPtr);
|
||||
Movement& movementSettings = cls.getMovementSettings(mPtr);
|
||||
|
||||
//Force Jump Logic
|
||||
|
||||
bool isMoving = (std::abs(cls.getMovementSettings(mPtr).mPosition[0]) > .5 || std::abs(cls.getMovementSettings(mPtr).mPosition[1]) > .5);
|
||||
bool isMoving = (std::abs(movementSettings.mPosition[0]) > .5 || std::abs(movementSettings.mPosition[1]) > .5);
|
||||
if(!inwater && !flying && solid)
|
||||
{
|
||||
//Force Jump
|
||||
if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceJump))
|
||||
{
|
||||
if(onground)
|
||||
{
|
||||
cls.getMovementSettings(mPtr).mPosition[2] = 1;
|
||||
}
|
||||
else
|
||||
cls.getMovementSettings(mPtr).mPosition[2] = 0;
|
||||
}
|
||||
movementSettings.mPosition[2] = onground ? 1 : 0;
|
||||
//Force Move Jump, only jump if they're otherwise moving
|
||||
if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceMoveJump) && isMoving)
|
||||
{
|
||||
|
||||
if(onground)
|
||||
{
|
||||
cls.getMovementSettings(mPtr).mPosition[2] = 1;
|
||||
}
|
||||
else
|
||||
cls.getMovementSettings(mPtr).mPosition[2] = 0;
|
||||
}
|
||||
movementSettings.mPosition[2] = onground ? 1 : 0;
|
||||
}
|
||||
|
||||
osg::Vec3f vec(cls.getMovementSettings(mPtr).asVec3());
|
||||
osg::Vec3f rot = cls.getRotationVector(mPtr);
|
||||
osg::Vec3f vec(movementSettings.asVec3());
|
||||
vec.normalize();
|
||||
|
||||
if(mHitState != CharState_None && mJumpState == JumpState_None)
|
||||
vec = osg::Vec3f(0.f, 0.f, 0.f);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
|
@ -2164,30 +2149,60 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
osg::Vec3f rot = cls.getRotationVector(mPtr);
|
||||
|
||||
speed = cls.getSpeed(mPtr);
|
||||
float analogueMult = 1.f;
|
||||
if(isPlayer)
|
||||
float analogueMult = 1.0f;
|
||||
if (isPlayer)
|
||||
{
|
||||
// TODO: Move this code to mwinput.
|
||||
// Joystick analogue movement.
|
||||
float xAxis = std::abs(cls.getMovementSettings(mPtr).mPosition[0]);
|
||||
float yAxis = std::abs(cls.getMovementSettings(mPtr).mPosition[1]);
|
||||
analogueMult = ((xAxis > yAxis) ? xAxis : yAxis);
|
||||
|
||||
// If Strafing, our max speed is slower so multiply by X axis instead.
|
||||
if(std::abs(vec.x()/2.0f) > std::abs(vec.y()))
|
||||
analogueMult = xAxis;
|
||||
float xAxis = std::abs(movementSettings.mPosition[0]);
|
||||
float yAxis = std::abs(movementSettings.mPosition[1]);
|
||||
analogueMult = std::max(xAxis, yAxis);
|
||||
|
||||
// Due to the half way split between walking/running, we multiply speed by 2 while walking, unless a keyboard was used.
|
||||
if(!isrunning && !sneak && !flying && analogueMult <= 0.5f)
|
||||
analogueMult *= 2.f;
|
||||
|
||||
movementSettings.mSpeedFactor = analogueMult;
|
||||
}
|
||||
|
||||
speed *= analogueMult;
|
||||
float effectiveRotation = rot.z();
|
||||
static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game");
|
||||
if (turnToMovementDirection && !(isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson()))
|
||||
{
|
||||
float targetMovementAngle = vec.y() >= 0 ? std::atan2(-vec.x(), vec.y()) : std::atan2(vec.x(), -vec.y());
|
||||
movementSettings.mIsStrafing = (stats.getDrawState() != MWMechanics::DrawState_Nothing || inwater)
|
||||
&& std::abs(targetMovementAngle) > osg::DegreesToRadians(60.0f);
|
||||
if (movementSettings.mIsStrafing)
|
||||
targetMovementAngle = 0;
|
||||
float delta = targetMovementAngle - stats.getSideMovementAngle();
|
||||
float cosDelta = cosf(delta);
|
||||
|
||||
if ((vec.y() < 0) == mIsMovingBackward)
|
||||
movementSettings.mSpeedFactor *= std::min(std::max(cosDelta, 0.f) + 0.3f, 1.f); // slow down when turn
|
||||
if (std::abs(delta) < osg::DegreesToRadians(20.0f))
|
||||
mIsMovingBackward = vec.y() < 0;
|
||||
|
||||
float maxDelta = osg::PI * duration * (2.5f - cosDelta);
|
||||
delta = osg::clampBetween(delta, -maxDelta, maxDelta);
|
||||
stats.setSideMovementAngle(stats.getSideMovementAngle() + delta);
|
||||
effectiveRotation += delta;
|
||||
}
|
||||
else
|
||||
movementSettings.mIsStrafing = std::abs(vec.x()) > std::abs(vec.y()) * 2;
|
||||
|
||||
mAnimation->setLegsYawRadians(stats.getSideMovementAngle());
|
||||
if (stats.getDrawState() == MWMechanics::DrawState_Nothing || inwater)
|
||||
mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 2);
|
||||
else
|
||||
mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 4);
|
||||
|
||||
speed = cls.getSpeed(mPtr);
|
||||
vec.x() *= speed;
|
||||
vec.y() *= speed;
|
||||
|
||||
if(mHitState != CharState_None && mJumpState == JumpState_None)
|
||||
vec = osg::Vec3f();
|
||||
|
||||
CharacterState movestate = CharState_None;
|
||||
CharacterState idlestate = CharState_SpecialIdle;
|
||||
JumpingState jumpstate = JumpState_None;
|
||||
|
@ -2349,7 +2364,7 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
|
||||
inJump = false;
|
||||
|
||||
if(std::abs(vec.x()/2.0f) > std::abs(vec.y()))
|
||||
if (movementSettings.mIsStrafing)
|
||||
{
|
||||
if(vec.x() > 0.0f)
|
||||
movestate = (inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight)
|
||||
|
@ -2360,18 +2375,18 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
: (sneak ? CharState_SneakLeft
|
||||
: (isrunning ? CharState_RunLeft : CharState_WalkLeft)));
|
||||
}
|
||||
else if(vec.y() != 0.0f)
|
||||
else if (vec.length2() > 0.0f)
|
||||
{
|
||||
if(vec.y() > 0.0f)
|
||||
if (vec.y() >= 0.0f)
|
||||
movestate = (inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward)
|
||||
: (sneak ? CharState_SneakForward
|
||||
: (isrunning ? CharState_RunForward : CharState_WalkForward)));
|
||||
else if(vec.y() < 0.0f)
|
||||
else
|
||||
movestate = (inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack)
|
||||
: (sneak ? CharState_SneakBack
|
||||
: (isrunning ? CharState_RunBack : CharState_WalkBack)));
|
||||
}
|
||||
else if(rot.z() != 0.0f)
|
||||
else if (effectiveRotation != 0.0f)
|
||||
{
|
||||
// Do not play turning animation for player if rotation speed is very slow.
|
||||
// Actual threshold should take framerate in account.
|
||||
|
@ -2384,9 +2399,9 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
bool isFirstPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson();
|
||||
if (!sneak && jumpstate == JumpState_None && !isFirstPlayer && mPtr.getClass().isBipedal(mPtr))
|
||||
{
|
||||
if(rot.z() > rotationThreshold)
|
||||
if(effectiveRotation > rotationThreshold)
|
||||
movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight;
|
||||
else if(rot.z() < -rotationThreshold)
|
||||
else if(effectiveRotation < -rotationThreshold)
|
||||
movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft;
|
||||
}
|
||||
}
|
||||
|
@ -2509,9 +2524,9 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f));
|
||||
|
||||
movement = vec;
|
||||
cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = 0;
|
||||
movementSettings.mPosition[0] = movementSettings.mPosition[1] = 0;
|
||||
if (movement.z() == 0.f)
|
||||
cls.getMovementSettings(mPtr).mPosition[2] = 0;
|
||||
movementSettings.mPosition[2] = 0;
|
||||
// Can't reset jump state (mPosition[2]) here in full; we don't know for sure whether the PhysicSystem will actually handle it in this frame
|
||||
// due to the fixed minimum timestep used for the physics update. It will be reset in PhysicSystem::move once the jump is handled.
|
||||
|
||||
|
@ -2547,20 +2562,17 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
if(speed > 0.f)
|
||||
{
|
||||
float l = moved.length();
|
||||
|
||||
if((movement.x() < 0.0f && movement.x() < moved.x()*2.0f) ||
|
||||
(movement.x() > 0.0f && movement.x() > moved.x()*2.0f))
|
||||
moved.x() = movement.x();
|
||||
if((movement.y() < 0.0f && movement.y() < moved.y()*2.0f) ||
|
||||
(movement.y() > 0.0f && movement.y() > moved.y()*2.0f))
|
||||
moved.y() = movement.y();
|
||||
if((movement.z() < 0.0f && movement.z() < moved.z()*2.0f) ||
|
||||
(movement.z() > 0.0f && movement.z() > moved.z()*2.0f))
|
||||
moved.z() = movement.z();
|
||||
// but keep the original speed
|
||||
float newLength = moved.length();
|
||||
if (newLength > 0)
|
||||
moved *= (l / newLength);
|
||||
if (std::abs(movement.x() - moved.x()) > std::abs(moved.x()) / 2 ||
|
||||
std::abs(movement.y() - moved.y()) > std::abs(moved.y()) / 2 ||
|
||||
std::abs(movement.z() - moved.z()) > std::abs(moved.z()) / 2)
|
||||
{
|
||||
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() && cls.getCreatureStats(mPtr).isDead() && cls.canSwim(mPtr))
|
||||
|
|
|
@ -195,6 +195,8 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
|||
|
||||
float mTimeUntilWake;
|
||||
|
||||
bool mIsMovingBackward;
|
||||
|
||||
void setAttackTypeBasedOnMovement();
|
||||
|
||||
void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false);
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace MWMechanics
|
|||
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
||||
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
||||
mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1),
|
||||
mDeathAnimation(-1), mTimeOfDeath(), mLevel (0)
|
||||
mDeathAnimation(-1), mTimeOfDeath(), mSideMovementAngle(0), mLevel (0)
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
mAiSettings[i] = 0;
|
||||
|
|
|
@ -80,6 +80,9 @@ namespace MWMechanics
|
|||
|
||||
MWWorld::TimeStamp mTimeOfDeath;
|
||||
|
||||
// The difference between view direction and lower body direction.
|
||||
float mSideMovementAngle;
|
||||
|
||||
public:
|
||||
typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
|
||||
private:
|
||||
|
@ -319,6 +322,9 @@ namespace MWMechanics
|
|||
void addCorprusSpell(const std::string& sourceId, CorprusStats& stats);
|
||||
|
||||
void removeCorprusSpell(const std::string& sourceId);
|
||||
|
||||
float getSideMovementAngle() const { return mSideMovementAngle; }
|
||||
void setSideMovementAngle(float angle) { mSideMovementAngle = angle; }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -250,10 +250,8 @@ namespace MWMechanics
|
|||
invStore.autoEquip(ptr);
|
||||
}
|
||||
|
||||
// mWatchedTimeToStartDrowning = -1 for correct drowning state check,
|
||||
// if stats.getTimeToStartDrowning() == 0 already on game start
|
||||
MechanicsManager::MechanicsManager()
|
||||
: mWatchedLevel(-1), mWatchedTimeToStartDrowning(-1), mWatchedStatsEmpty (true), mUpdatePlayer (true), mClassSelected (false),
|
||||
: mUpdatePlayer (true), mClassSelected (false),
|
||||
mRaceSelected (false), mAI(true)
|
||||
{
|
||||
//buildPlayer no longer here, needs to be done explicitly after all subsystems are up and running
|
||||
|
@ -275,16 +273,16 @@ namespace MWMechanics
|
|||
|
||||
void MechanicsManager::remove(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if(ptr == mWatched)
|
||||
mWatched = MWWorld::Ptr();
|
||||
if(ptr == MWBase::Environment::get().getWindowManager()->getWatchedActor())
|
||||
MWBase::Environment::get().getWindowManager()->watchActor(MWWorld::Ptr());
|
||||
mActors.removeActor(ptr);
|
||||
mObjects.removeObject(ptr);
|
||||
}
|
||||
|
||||
void MechanicsManager::updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)
|
||||
{
|
||||
if(old == mWatched)
|
||||
mWatched = ptr;
|
||||
if(old == MWBase::Environment::get().getWindowManager()->getWatchedActor())
|
||||
MWBase::Environment::get().getWindowManager()->watchActor(ptr);
|
||||
|
||||
if(ptr.getClass().isActor())
|
||||
mActors.updateActor(old, ptr);
|
||||
|
@ -292,19 +290,12 @@ namespace MWMechanics
|
|||
mObjects.updateObject(old, ptr);
|
||||
}
|
||||
|
||||
|
||||
void MechanicsManager::drop(const MWWorld::CellStore *cellStore)
|
||||
{
|
||||
mActors.dropActors(cellStore, mWatched);
|
||||
mActors.dropActors(cellStore, getPlayer());
|
||||
mObjects.dropObjects(cellStore);
|
||||
}
|
||||
|
||||
|
||||
void MechanicsManager::watchActor(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mWatched = ptr;
|
||||
}
|
||||
|
||||
void MechanicsManager::restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId)
|
||||
{
|
||||
auto& stats = actor.getClass().getCreatureStats (actor);
|
||||
|
@ -325,133 +316,10 @@ namespace MWMechanics
|
|||
|
||||
void MechanicsManager::update(float duration, bool paused)
|
||||
{
|
||||
if(!mWatched.isEmpty())
|
||||
{
|
||||
MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
|
||||
const MWMechanics::NpcStats &stats = mWatched.getClass().getNpcStats(mWatched);
|
||||
for(int i = 0;i < ESM::Attribute::Length;++i)
|
||||
{
|
||||
if(stats.getAttribute(i) != mWatchedAttributes[i] || mWatchedStatsEmpty)
|
||||
{
|
||||
std::stringstream attrname;
|
||||
attrname << "AttribVal"<<(i+1);
|
||||
|
||||
mWatchedAttributes[i] = stats.getAttribute(i);
|
||||
winMgr->setValue(attrname.str(), stats.getAttribute(i));
|
||||
}
|
||||
}
|
||||
|
||||
if(stats.getHealth() != mWatchedHealth || mWatchedStatsEmpty)
|
||||
{
|
||||
static const std::string hbar("HBar");
|
||||
mWatchedHealth = stats.getHealth();
|
||||
winMgr->setValue(hbar, stats.getHealth());
|
||||
}
|
||||
if(stats.getMagicka() != mWatchedMagicka || mWatchedStatsEmpty)
|
||||
{
|
||||
static const std::string mbar("MBar");
|
||||
mWatchedMagicka = stats.getMagicka();
|
||||
winMgr->setValue(mbar, stats.getMagicka());
|
||||
}
|
||||
if(stats.getFatigue() != mWatchedFatigue || mWatchedStatsEmpty)
|
||||
{
|
||||
static const std::string fbar("FBar");
|
||||
mWatchedFatigue = stats.getFatigue();
|
||||
winMgr->setValue(fbar, stats.getFatigue());
|
||||
}
|
||||
|
||||
float timeToDrown = stats.getTimeToStartDrowning();
|
||||
|
||||
if(timeToDrown != mWatchedTimeToStartDrowning)
|
||||
{
|
||||
static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||
.find("fHoldBreathTime")->mValue.getFloat();
|
||||
|
||||
mWatchedTimeToStartDrowning = timeToDrown;
|
||||
|
||||
if(timeToDrown >= fHoldBreathTime || timeToDrown == -1.0) // -1.0 is a special value during initialization
|
||||
winMgr->setDrowningBarVisibility(false);
|
||||
else
|
||||
{
|
||||
winMgr->setDrowningBarVisibility(true);
|
||||
winMgr->setDrowningTimeLeft(stats.getTimeToStartDrowning(), fHoldBreathTime);
|
||||
}
|
||||
}
|
||||
|
||||
//Loop over ESM::Skill::SkillEnum
|
||||
for(int i = 0; i < ESM::Skill::Length; ++i)
|
||||
{
|
||||
if(stats.getSkill(i) != mWatchedSkills[i] || mWatchedStatsEmpty)
|
||||
{
|
||||
mWatchedSkills[i] = stats.getSkill(i);
|
||||
winMgr->setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i));
|
||||
}
|
||||
}
|
||||
|
||||
if(stats.getLevel() != mWatchedLevel)
|
||||
{
|
||||
mWatchedLevel = stats.getLevel();
|
||||
winMgr->setValue("level", mWatchedLevel);
|
||||
}
|
||||
|
||||
mWatchedStatsEmpty = false;
|
||||
|
||||
// Update the equipped weapon icon
|
||||
MWWorld::InventoryStore& inv = mWatched.getClass().getInventoryStore(mWatched);
|
||||
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if (weapon == inv.end())
|
||||
winMgr->unsetSelectedWeapon();
|
||||
else
|
||||
winMgr->setSelectedWeapon(*weapon);
|
||||
|
||||
// Update the selected spell icon
|
||||
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
|
||||
if (enchantItem != inv.end())
|
||||
winMgr->setSelectedEnchantItem(*enchantItem);
|
||||
else
|
||||
{
|
||||
const std::string& spell = winMgr->getSelectedSpell();
|
||||
if (!spell.empty())
|
||||
winMgr->setSelectedSpell(spell, int(getSpellSuccessChance(spell, mWatched)));
|
||||
else
|
||||
winMgr->unsetSelectedSpell();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (mUpdatePlayer)
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
|
||||
// basic player profile; should not change anymore after the creation phase is finished.
|
||||
MWBase::WindowManager *winMgr =
|
||||
MWBase::Environment::get().getWindowManager();
|
||||
|
||||
const ESM::NPC *player =
|
||||
world->getPlayerPtr().get<ESM::NPC>()->mBase;
|
||||
|
||||
const ESM::Race *race =
|
||||
world->getStore().get<ESM::Race>().find(player->mRace);
|
||||
const ESM::Class *cls =
|
||||
world->getStore().get<ESM::Class>().find(player->mClass);
|
||||
|
||||
winMgr->setValue ("name", player->mName);
|
||||
winMgr->setValue ("race", race->mName);
|
||||
winMgr->setValue ("class", cls->mName);
|
||||
|
||||
mUpdatePlayer = false;
|
||||
|
||||
MWBase::WindowManager::SkillList majorSkills (5);
|
||||
MWBase::WindowManager::SkillList minorSkills (5);
|
||||
|
||||
for (int i=0; i<5; ++i)
|
||||
{
|
||||
minorSkills[i] = cls->mData.mSkills[i][0];
|
||||
majorSkills[i] = cls->mData.mSkills[i][1];
|
||||
}
|
||||
|
||||
winMgr->configureSkills (majorSkills, minorSkills);
|
||||
|
||||
// HACK? The player has been changed, so a new Animation object may
|
||||
// have been made for them. Make sure they're properly updated.
|
||||
MWWorld::Ptr ptr = getPlayer();
|
||||
|
@ -531,7 +399,7 @@ namespace MWMechanics
|
|||
|
||||
int MechanicsManager::getHoursToRest() const
|
||||
{
|
||||
return mActors.getHoursToRest(mWatched);
|
||||
return mActors.getHoursToRest(getPlayer());
|
||||
}
|
||||
|
||||
void MechanicsManager::setPlayerName (const std::string& name)
|
||||
|
@ -693,7 +561,9 @@ namespace MWMechanics
|
|||
|
||||
int MechanicsManager::getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying)
|
||||
{
|
||||
if (ptr.getTypeName() == typeid(ESM::Creature).name())
|
||||
// Make sure zero base price items/services can't be bought/sold for 1 gold
|
||||
// and return the intended base price for creature merchants
|
||||
if (basePrice == 0 || ptr.getTypeName() == typeid(ESM::Creature).name())
|
||||
return basePrice;
|
||||
|
||||
const MWMechanics::NpcStats &sellerStats = ptr.getClass().getNpcStats(ptr);
|
||||
|
@ -811,8 +681,11 @@ namespace MWMechanics
|
|||
{
|
||||
if (std::abs(c) < iPerMinChange)
|
||||
{
|
||||
x = 0;
|
||||
y = -iPerMinChange;
|
||||
// Deviating from Morrowind here: it doesn't increase disposition on marginal wins,
|
||||
// which seems to be a bug (MCP fixes it too).
|
||||
// Original logic: x = 0, y = -iPerMinChange
|
||||
x = -iPerMinChange;
|
||||
y = x; // This goes unused.
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -21,20 +21,6 @@ namespace MWMechanics
|
|||
{
|
||||
class MechanicsManager : public MWBase::MechanicsManager
|
||||
{
|
||||
MWWorld::Ptr mWatched;
|
||||
|
||||
AttributeValue mWatchedAttributes[8];
|
||||
SkillValue mWatchedSkills[27];
|
||||
|
||||
DynamicStat<float> mWatchedHealth;
|
||||
DynamicStat<float> mWatchedMagicka;
|
||||
DynamicStat<float> mWatchedFatigue;
|
||||
|
||||
int mWatchedLevel;
|
||||
|
||||
float mWatchedTimeToStartDrowning;
|
||||
|
||||
bool mWatchedStatsEmpty;
|
||||
bool mUpdatePlayer;
|
||||
bool mClassSelected;
|
||||
bool mRaceSelected;
|
||||
|
@ -68,10 +54,6 @@ namespace MWMechanics
|
|||
virtual void drop(const MWWorld::CellStore *cellStore) override;
|
||||
///< Deregister all objects in the given cell.
|
||||
|
||||
virtual void watchActor(const MWWorld::Ptr& ptr) override;
|
||||
///< On each update look for changes in a previously registered actor and update the
|
||||
/// GUI accordingly.
|
||||
|
||||
virtual void update (float duration, bool paused) override;
|
||||
///< Update objects
|
||||
///
|
||||
|
|
|
@ -11,12 +11,14 @@ namespace MWMechanics
|
|||
float mPosition[3];
|
||||
float mRotation[3];
|
||||
float mSpeedFactor;
|
||||
bool mIsStrafing;
|
||||
|
||||
Movement()
|
||||
{
|
||||
mPosition[0] = mPosition[1] = mPosition[2] = 0.0f;
|
||||
mRotation[0] = mRotation[1] = mRotation[2] = 0.0f;
|
||||
mSpeedFactor = 1.f;
|
||||
mIsStrafing = false;
|
||||
}
|
||||
|
||||
osg::Vec3f asVec3()
|
||||
|
|
|
@ -481,13 +481,24 @@ bool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int
|
|||
|
||||
const ESM::RankData& rankData = faction.mData.mRankData[rank];
|
||||
|
||||
if (*iter<rankData.mSkill1)
|
||||
if (*iter<rankData.mPrimarySkill)
|
||||
return false;
|
||||
|
||||
if (skills.size() < 2)
|
||||
return true;
|
||||
|
||||
return *++iter>=rankData.mSkill2;
|
||||
iter++;
|
||||
if (*iter<rankData.mFavouredSkill)
|
||||
return false;
|
||||
|
||||
if (skills.size() < 3)
|
||||
return true;
|
||||
|
||||
iter++;
|
||||
if (*iter<rankData.mFavouredSkill)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MWMechanics::NpcStats::isWerewolf() const
|
||||
|
|
|
@ -1102,7 +1102,6 @@ void LocalPlayer::setClass()
|
|||
{
|
||||
charClass.mData.mIsPlayable = 0x1;
|
||||
MWBase::Environment::get().getMechanicsManager()->setPlayerClass(charClass);
|
||||
MWBase::Environment::get().getWindowManager()->setPlayerClass(charClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1111,7 +1110,6 @@ void LocalPlayer::setClass()
|
|||
if (existingCharClass)
|
||||
{
|
||||
MWBase::Environment::get().getMechanicsManager()->setPlayerClass(charClass.mId);
|
||||
MWBase::Environment::get().getWindowManager()->setPlayerClass(charClass);
|
||||
}
|
||||
else
|
||||
LOG_APPEND(TimedLog::LOG_INFO, "- Ignored invalid default class %s", charClass.mId.c_str());
|
||||
|
|
|
@ -84,12 +84,12 @@ namespace MWPhysics
|
|||
assert (mShapeInstance->getCollisionShape()->isCompound());
|
||||
|
||||
btCompoundShape* compound = static_cast<btCompoundShape*>(mShapeInstance->getCollisionShape());
|
||||
for (std::map<int, int>::const_iterator it = mShapeInstance->mAnimatedShapes.begin(); it != mShapeInstance->mAnimatedShapes.end(); ++it)
|
||||
for (const auto& shape : mShapeInstance->mAnimatedShapes)
|
||||
{
|
||||
int recIndex = it->first;
|
||||
int shapeIndex = it->second;
|
||||
int recIndex = shape.first;
|
||||
int shapeIndex = shape.second;
|
||||
|
||||
std::map<int, osg::NodePath>::iterator nodePathFound = mRecIndexToNodePath.find(recIndex);
|
||||
auto nodePathFound = mRecIndexToNodePath.find(recIndex);
|
||||
if (nodePathFound == mRecIndexToNodePath.end())
|
||||
{
|
||||
NifOsg::FindGroupByRecIndex visitor(recIndex);
|
||||
|
@ -104,7 +104,7 @@ namespace MWPhysics
|
|||
}
|
||||
osg::NodePath nodePath = visitor.mFoundPath;
|
||||
nodePath.erase(nodePath.begin());
|
||||
nodePathFound = mRecIndexToNodePath.insert(std::make_pair(recIndex, nodePath)).first;
|
||||
nodePathFound = mRecIndexToNodePath.emplace(recIndex, nodePath).first;
|
||||
}
|
||||
|
||||
osg::NodePath& nodePath = nodePathFound->second;
|
||||
|
|
|
@ -95,21 +95,21 @@ namespace MWPhysics
|
|||
if (mWaterCollisionObject.get())
|
||||
mCollisionWorld->removeCollisionObject(mWaterCollisionObject.get());
|
||||
|
||||
for (HeightFieldMap::iterator it = mHeightFields.begin(); it != mHeightFields.end(); ++it)
|
||||
for (auto& heightField : mHeightFields)
|
||||
{
|
||||
mCollisionWorld->removeCollisionObject(it->second->getCollisionObject());
|
||||
delete it->second;
|
||||
mCollisionWorld->removeCollisionObject(heightField.second->getCollisionObject());
|
||||
delete heightField.second;
|
||||
}
|
||||
|
||||
for (ObjectMap::iterator it = mObjects.begin(); it != mObjects.end(); ++it)
|
||||
for (auto& object : mObjects)
|
||||
{
|
||||
mCollisionWorld->removeCollisionObject(it->second->getCollisionObject());
|
||||
delete it->second;
|
||||
mCollisionWorld->removeCollisionObject(object.second->getCollisionObject());
|
||||
delete object.second;
|
||||
}
|
||||
|
||||
for (ActorMap::iterator it = mActors.begin(); it != mActors.end(); ++it)
|
||||
for (auto& actor : mActors)
|
||||
{
|
||||
delete it->second;
|
||||
delete actor.second;
|
||||
}
|
||||
|
||||
delete mCollisionWorld;
|
||||
|
@ -498,7 +498,7 @@ namespace MWPhysics
|
|||
return;
|
||||
|
||||
Object *obj = new Object(ptr, shapeInstance);
|
||||
mObjects.insert(std::make_pair(ptr, obj));
|
||||
mObjects.emplace(ptr, obj);
|
||||
|
||||
if (obj->isAnimated())
|
||||
mAnimatedObjects.insert(obj);
|
||||
|
@ -540,10 +540,10 @@ namespace MWPhysics
|
|||
map.erase(found);
|
||||
}
|
||||
|
||||
for (CollisionMap::iterator it = map.begin(); it != map.end(); ++it)
|
||||
for (auto& collision : map)
|
||||
{
|
||||
if (it->second == old)
|
||||
it->second = updated;
|
||||
if (collision.second == old)
|
||||
collision.second = updated;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -555,7 +555,7 @@ namespace MWPhysics
|
|||
Object* obj = found->second;
|
||||
obj->updatePtr(updated);
|
||||
mObjects.erase(found);
|
||||
mObjects.insert(std::make_pair(updated, obj));
|
||||
mObjects.emplace(updated, obj);
|
||||
}
|
||||
|
||||
ActorMap::iterator foundActor = mActors.find(old);
|
||||
|
@ -564,7 +564,7 @@ namespace MWPhysics
|
|||
Actor* actor = foundActor->second;
|
||||
actor->updatePtr(updated);
|
||||
mActors.erase(foundActor);
|
||||
mActors.insert(std::make_pair(updated, actor));
|
||||
mActors.emplace(updated, actor);
|
||||
}
|
||||
|
||||
updateCollisionMapPtr(mStandingCollisions, old, updated);
|
||||
|
@ -652,7 +652,8 @@ namespace MWPhysics
|
|||
}
|
||||
}
|
||||
|
||||
void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh) {
|
||||
void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh)
|
||||
{
|
||||
osg::ref_ptr<const Resource::BulletShape> shape = mShapeManager->getShape(mesh);
|
||||
if (!shape)
|
||||
return;
|
||||
|
@ -668,7 +669,7 @@ namespace MWPhysics
|
|||
}
|
||||
|
||||
Actor* actor = new Actor(ptr, shape, mCollisionWorld);
|
||||
mActors.insert(std::make_pair(ptr, actor));
|
||||
mActors.emplace(ptr, actor);
|
||||
}
|
||||
|
||||
bool PhysicsSystem::toggleCollisionMode()
|
||||
|
@ -688,17 +689,16 @@ namespace MWPhysics
|
|||
|
||||
void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &movement)
|
||||
{
|
||||
PtrVelocityList::iterator iter = mMovementQueue.begin();
|
||||
for(;iter != mMovementQueue.end();++iter)
|
||||
for(auto& movementItem : mMovementQueue)
|
||||
{
|
||||
if(iter->first == ptr)
|
||||
if (movementItem.first == ptr)
|
||||
{
|
||||
iter->second = movement;
|
||||
movementItem.second = movement;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mMovementQueue.push_back(std::make_pair(ptr, movement));
|
||||
mMovementQueue.emplace_back(ptr, movement);
|
||||
}
|
||||
|
||||
void PhysicsSystem::clearQueuedMovement()
|
||||
|
@ -727,27 +727,26 @@ namespace MWPhysics
|
|||
|
||||
const MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
PtrVelocityList::iterator iter = mMovementQueue.begin();
|
||||
for(;iter != mMovementQueue.end();++iter)
|
||||
for(auto& movementItem : mMovementQueue)
|
||||
{
|
||||
ActorMap::iterator foundActor = mActors.find(iter->first);
|
||||
ActorMap::iterator foundActor = mActors.find(movementItem.first);
|
||||
if (foundActor == mActors.end()) // actor was already removed from the scene
|
||||
continue;
|
||||
Actor* physicActor = foundActor->second;
|
||||
|
||||
float waterlevel = -std::numeric_limits<float>::max();
|
||||
const MWWorld::CellStore *cell = iter->first.getCell();
|
||||
const MWWorld::CellStore *cell = movementItem.first.getCell();
|
||||
if(cell->getCell()->hasWater())
|
||||
waterlevel = cell->getWaterLevel();
|
||||
|
||||
const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects();
|
||||
const MWMechanics::MagicEffects& effects = movementItem.first.getClass().getCreatureStats(movementItem.first).getMagicEffects();
|
||||
|
||||
bool waterCollision = false;
|
||||
if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())
|
||||
{
|
||||
if (!world->isUnderwater(iter->first.getCell(), osg::Vec3f(iter->first.getRefData().getPosition().asVec3())))
|
||||
if (!world->isUnderwater(movementItem.first.getCell(), osg::Vec3f(movementItem.first.getRefData().getPosition().asVec3())))
|
||||
waterCollision = true;
|
||||
else if (physicActor->getCollisionMode() && canMoveToWaterSurface(iter->first, waterlevel))
|
||||
else if (physicActor->getCollisionMode() && canMoveToWaterSurface(movementItem.first, waterlevel))
|
||||
{
|
||||
const osg::Vec3f actorPosition = physicActor->getPosition();
|
||||
physicActor->setPosition(osg::Vec3f(actorPosition.x(), actorPosition.y(), waterlevel));
|
||||
|
@ -759,8 +758,8 @@ namespace MWPhysics
|
|||
// Slow fall reduces fall speed by a factor of (effect magnitude / 200)
|
||||
float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f));
|
||||
|
||||
bool flying = world->isFlying(iter->first);
|
||||
bool swimming = world->isSwimming(iter->first);
|
||||
bool flying = world->isFlying(movementItem.first);
|
||||
bool swimming = world->isSwimming(movementItem.first);
|
||||
|
||||
bool wasOnGround = physicActor->getOnGround();
|
||||
osg::Vec3f position = physicActor->getPosition();
|
||||
|
@ -768,7 +767,7 @@ namespace MWPhysics
|
|||
bool positionChanged = false;
|
||||
for (int i=0; i<numSteps; ++i)
|
||||
{
|
||||
position = MovementSolver::move(position, physicActor->getPtr(), physicActor, iter->second, mPhysicsDt,
|
||||
position = MovementSolver::move(position, physicActor->getPtr(), physicActor, movementItem.second, mPhysicsDt,
|
||||
flying, waterlevel, slowFall, mCollisionWorld, mStandingCollisions);
|
||||
if (position != physicActor->getPosition())
|
||||
positionChanged = true;
|
||||
|
@ -782,14 +781,14 @@ namespace MWPhysics
|
|||
|
||||
float heightDiff = position.z() - oldHeight;
|
||||
|
||||
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
|
||||
MWMechanics::CreatureStats& stats = movementItem.first.getClass().getCreatureStats(movementItem.first);
|
||||
bool isStillOnGround = (numSteps > 0 && wasOnGround && physicActor->getOnGround());
|
||||
if (isStillOnGround || flying || swimming || slowFall < 1)
|
||||
stats.land(iter->first == player && (flying || swimming));
|
||||
stats.land(movementItem.first == player && (flying || swimming));
|
||||
else if (heightDiff < 0)
|
||||
stats.addToFallHeight(-heightDiff);
|
||||
|
||||
mMovementResults.push_back(std::make_pair(iter->first, interpolated));
|
||||
mMovementResults.emplace_back(movementItem.first, interpolated);
|
||||
}
|
||||
|
||||
mMovementQueue.clear();
|
||||
|
@ -823,9 +822,9 @@ namespace MWPhysics
|
|||
|
||||
bool PhysicsSystem::isActorStandingOn(const MWWorld::Ptr &actor, const MWWorld::ConstPtr &object) const
|
||||
{
|
||||
for (CollisionMap::const_iterator it = mStandingCollisions.begin(); it != mStandingCollisions.end(); ++it)
|
||||
for (const auto& standingActor : mStandingCollisions)
|
||||
{
|
||||
if (it->first == actor && it->second == object)
|
||||
if (standingActor.first == actor && standingActor.second == object)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -833,10 +832,10 @@ namespace MWPhysics
|
|||
|
||||
void PhysicsSystem::getActorsStandingOn(const MWWorld::ConstPtr &object, std::vector<MWWorld::Ptr> &out) const
|
||||
{
|
||||
for (CollisionMap::const_iterator it = mStandingCollisions.begin(); it != mStandingCollisions.end(); ++it)
|
||||
for (const auto& standingActor : mStandingCollisions)
|
||||
{
|
||||
if (it->second == object)
|
||||
out.push_back(it->first);
|
||||
if (standingActor.second == object)
|
||||
out.push_back(standingActor.first);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -621,6 +621,8 @@ namespace MWRender
|
|||
, mTextKeyListener(nullptr)
|
||||
, mHeadYawRadians(0.f)
|
||||
, mHeadPitchRadians(0.f)
|
||||
, mUpperBodyYawRadians(0.f)
|
||||
, mLegsYawRadians(0.f)
|
||||
, mHasMagicEffects(false)
|
||||
, mAlpha(1.f)
|
||||
{
|
||||
|
@ -1334,13 +1336,36 @@ namespace MWRender
|
|||
|
||||
updateEffects();
|
||||
|
||||
const float epsilon = 0.001f;
|
||||
float yawOffset = 0;
|
||||
if (mRootController)
|
||||
{
|
||||
bool enable = std::abs(mLegsYawRadians) > epsilon;
|
||||
mRootController->setEnabled(enable);
|
||||
if (enable)
|
||||
{
|
||||
mRootController->setRotate(osg::Quat(mLegsYawRadians, osg::Vec3f(0,0,1)));
|
||||
yawOffset = mLegsYawRadians;
|
||||
}
|
||||
}
|
||||
if (mSpineController)
|
||||
{
|
||||
float yaw = mUpperBodyYawRadians - yawOffset;
|
||||
bool enable = std::abs(yaw) > epsilon;
|
||||
mSpineController->setEnabled(enable);
|
||||
if (enable)
|
||||
{
|
||||
mSpineController->setRotate(osg::Quat(yaw, osg::Vec3f(0,0,1)));
|
||||
yawOffset = mUpperBodyYawRadians;
|
||||
}
|
||||
}
|
||||
if (mHeadController)
|
||||
{
|
||||
const float epsilon = 0.001f;
|
||||
bool enable = (std::abs(mHeadPitchRadians) > epsilon || std::abs(mHeadYawRadians) > epsilon);
|
||||
float yaw = mHeadYawRadians - yawOffset;
|
||||
bool enable = (std::abs(mHeadPitchRadians) > epsilon || std::abs(yaw) > epsilon);
|
||||
mHeadController->setEnabled(enable);
|
||||
if (enable)
|
||||
mHeadController->setRotate(osg::Quat(mHeadPitchRadians, osg::Vec3f(1,0,0)) * osg::Quat(mHeadYawRadians, osg::Vec3f(0,0,1)));
|
||||
mHeadController->setRotate(osg::Quat(mHeadPitchRadians, osg::Vec3f(1,0,0)) * osg::Quat(yaw, osg::Vec3f(0,0,1)));
|
||||
}
|
||||
|
||||
// Scripted animations should not cause movement
|
||||
|
@ -1460,6 +1485,8 @@ namespace MWRender
|
|||
{
|
||||
if (mLightListCallback)
|
||||
mObjectRoot->removeCullCallback(mLightListCallback);
|
||||
if (mTransparencyUpdater)
|
||||
mObjectRoot->removeCullCallback(mTransparencyUpdater);
|
||||
previousStateset = mObjectRoot->getStateSet();
|
||||
mObjectRoot->getParent(0)->removeChild(mObjectRoot);
|
||||
}
|
||||
|
@ -1551,6 +1578,8 @@ namespace MWRender
|
|||
if (!mLightListCallback)
|
||||
mLightListCallback = new SceneUtil::LightListCallback;
|
||||
mObjectRoot->addCullCallback(mLightListCallback);
|
||||
if (mTransparencyUpdater)
|
||||
mObjectRoot->addCullCallback(mTransparencyUpdater);
|
||||
}
|
||||
|
||||
osg::Group* Animation::getObjectRoot()
|
||||
|
@ -1801,13 +1830,17 @@ namespace MWRender
|
|||
|
||||
void Animation::addControllers()
|
||||
{
|
||||
mHeadController = nullptr;
|
||||
mHeadController = addRotateController("bip01 head");
|
||||
mSpineController = addRotateController("bip01 spine1");
|
||||
mRootController = addRotateController("bip01");
|
||||
}
|
||||
|
||||
NodeMap::const_iterator found = getNodeMap().find("bip01 head");
|
||||
if (found == getNodeMap().end())
|
||||
return;
|
||||
|
||||
osg::MatrixTransform* node = found->second;
|
||||
RotateController* Animation::addRotateController(std::string bone)
|
||||
{
|
||||
auto iter = getNodeMap().find(bone);
|
||||
if (iter == getNodeMap().end())
|
||||
return nullptr;
|
||||
osg::MatrixTransform* node = iter->second;
|
||||
|
||||
bool foundKeyframeCtrl = false;
|
||||
osg::Callback* cb = node->getUpdateCallback();
|
||||
|
@ -1820,13 +1853,15 @@ namespace MWRender
|
|||
}
|
||||
cb = cb->getNestedCallback();
|
||||
}
|
||||
|
||||
// Without KeyframeController the orientation will not be reseted each frame, so
|
||||
// RotateController shouldn't be used for such nodes.
|
||||
if (!foundKeyframeCtrl)
|
||||
return;
|
||||
return nullptr;
|
||||
|
||||
mHeadController = new RotateController(mObjectRoot.get());
|
||||
node->addUpdateCallback(mHeadController);
|
||||
mActiveControllers.emplace_back(node, mHeadController);
|
||||
RotateController* controller = new RotateController(mObjectRoot.get());
|
||||
node->addUpdateCallback(controller);
|
||||
mActiveControllers.emplace_back(node, controller);
|
||||
return controller;
|
||||
}
|
||||
|
||||
void Animation::setHeadPitch(float pitchRadians)
|
||||
|
|
|
@ -267,8 +267,15 @@ protected:
|
|||
TextKeyListener* mTextKeyListener;
|
||||
|
||||
osg::ref_ptr<RotateController> mHeadController;
|
||||
osg::ref_ptr<RotateController> mSpineController;
|
||||
osg::ref_ptr<RotateController> mRootController;
|
||||
float mHeadYawRadians;
|
||||
float mHeadPitchRadians;
|
||||
float mUpperBodyYawRadians;
|
||||
float mLegsYawRadians;
|
||||
|
||||
RotateController* addRotateController(std::string bone);
|
||||
|
||||
bool mHasMagicEffects;
|
||||
|
||||
osg::ref_ptr<SceneUtil::LightSource> mGlowLight;
|
||||
|
@ -477,6 +484,12 @@ public:
|
|||
virtual void setHeadYaw(float yawRadians);
|
||||
virtual float getHeadPitch() const;
|
||||
virtual float getHeadYaw() const;
|
||||
|
||||
virtual void setUpperBodyYawRadians(float v) { mUpperBodyYawRadians = v; }
|
||||
virtual void setLegsYawRadians(float v) { mLegsYawRadians = v; }
|
||||
virtual float getUpperBodyYawRadians() const { return mUpperBodyYawRadians; }
|
||||
virtual float getLegsYawRadians() const { return mLegsYawRadians; }
|
||||
|
||||
virtual void setAccurateAiming(bool enabled) {}
|
||||
virtual bool canBeHarvested() const { return false; }
|
||||
|
||||
|
|
|
@ -7,9 +7,13 @@
|
|||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/refdata.hpp"
|
||||
|
||||
#include "../mwmechanics/drawstate.hpp"
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
|
||||
#include "npcanimation.hpp"
|
||||
|
||||
namespace
|
||||
|
@ -52,11 +56,14 @@ namespace MWRender
|
|||
mFurthest(800.f),
|
||||
mIsNearest(false),
|
||||
mHeight(124.f),
|
||||
mMaxCameraDistance(192.f),
|
||||
mBaseCameraDistance(192.f),
|
||||
mVanityToggleQueued(false),
|
||||
mVanityToggleQueuedValue(false),
|
||||
mViewModeToggleQueued(false),
|
||||
mCameraDistance(0.f)
|
||||
mCameraDistance(0.f),
|
||||
mThirdPersonMode(ThirdPersonViewMode::Standard),
|
||||
mOverShoulderOffset(osg::Vec2f(30.0f, -10.0f)),
|
||||
mSmoothTransitionToCombatMode(0.f)
|
||||
{
|
||||
mVanity.enabled = false;
|
||||
mVanity.allowed = true;
|
||||
|
@ -68,7 +75,7 @@ namespace MWRender
|
|||
mMainCam.yaw = 0.f;
|
||||
mMainCam.offset = 400.f;
|
||||
|
||||
mCameraDistance = mMaxCameraDistance;
|
||||
mCameraDistance = mBaseCameraDistance;
|
||||
|
||||
mUpdateCallback = new UpdateRenderCameraCallback(this);
|
||||
mCamera->addUpdateCallback(mUpdateCallback);
|
||||
|
@ -84,7 +91,7 @@ namespace MWRender
|
|||
return mTrackingPtr;
|
||||
}
|
||||
|
||||
osg::Vec3d Camera::getFocalPoint()
|
||||
osg::Vec3d Camera::getFocalPoint() const
|
||||
{
|
||||
const osg::Node* trackNode = mTrackingNode;
|
||||
if (!trackNode)
|
||||
|
@ -96,22 +103,54 @@ namespace MWRender
|
|||
|
||||
osg::Vec3d position = worldMat.getTrans();
|
||||
if (!isFirstPerson())
|
||||
{
|
||||
position.z() += mHeight * mHeightScale;
|
||||
|
||||
// We subtract 10.f here and add it within focalPointOffset in order to avoid camera clipping through ceiling.
|
||||
// Needed because character's head can be a bit higher than collision area.
|
||||
position.z() -= 10.f;
|
||||
|
||||
position += getFocalPointOffset() + mFocalPointAdjustment;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
osg::Vec3d Camera::getFocalPointOffset() const
|
||||
{
|
||||
osg::Vec3d offset(0, 0, 10.f);
|
||||
if (mThirdPersonMode == ThirdPersonViewMode::OverShoulder && !mPreviewMode && !mVanity.enabled)
|
||||
{
|
||||
float horizontalOffset = mOverShoulderOffset.x() * (1.f - mSmoothTransitionToCombatMode);
|
||||
float verticalOffset = mSmoothTransitionToCombatMode * 15.f + (1.f - mSmoothTransitionToCombatMode) * mOverShoulderOffset.y();
|
||||
|
||||
offset.x() += horizontalOffset * cos(getYaw());
|
||||
offset.y() += horizontalOffset * sin(getYaw());
|
||||
offset.z() += verticalOffset;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Camera::getPosition(osg::Vec3d &focal, osg::Vec3d &camera) const
|
||||
{
|
||||
focal = getFocalPoint();
|
||||
osg::Vec3d offset(0,0,0);
|
||||
if (!isFirstPerson())
|
||||
{
|
||||
osg::Quat orient = osg::Quat(getPitch(), osg::Vec3d(1,0,0)) * osg::Quat(getYaw(), osg::Vec3d(0,0,1));
|
||||
offset = orient * osg::Vec3d(0.f, -mCameraDistance, 0.f);
|
||||
}
|
||||
camera = focal + offset;
|
||||
}
|
||||
|
||||
void Camera::updateCamera(osg::Camera *cam)
|
||||
{
|
||||
if (mTrackingPtr.isEmpty())
|
||||
return;
|
||||
|
||||
osg::Vec3d position = getFocalPoint();
|
||||
osg::Vec3d focal, position;
|
||||
getPosition(focal, position);
|
||||
|
||||
osg::Quat orient = osg::Quat(getPitch(), osg::Vec3d(1,0,0)) * osg::Quat(getYaw(), osg::Vec3d(0,0,1));
|
||||
|
||||
osg::Vec3d offset = orient * osg::Vec3d(0, isFirstPerson() ? 0 : -mCameraDistance, 0);
|
||||
position += offset;
|
||||
|
||||
osg::Vec3d forward = orient * osg::Vec3d(0,1,0);
|
||||
osg::Vec3d up = orient * osg::Vec3d(0,0,1);
|
||||
|
||||
|
@ -164,14 +203,38 @@ namespace MWRender
|
|||
if (paused)
|
||||
return;
|
||||
|
||||
// only show the crosshair in game mode and in first person mode.
|
||||
// only show the crosshair in game mode
|
||||
MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
|
||||
wm->showCrosshair(!wm->isGuiMode() && (mFirstPersonView && !mVanity.enabled && !mPreviewMode));
|
||||
wm->showCrosshair(!wm->isGuiMode() && !mVanity.enabled && !mPreviewMode
|
||||
&& (mFirstPersonView || mThirdPersonMode != ThirdPersonViewMode::Standard));
|
||||
|
||||
if(mVanity.enabled)
|
||||
{
|
||||
rotateCamera(0.f, osg::DegreesToRadians(3.f * duration), true);
|
||||
}
|
||||
|
||||
updateSmoothTransitionToCombatMode(duration);
|
||||
}
|
||||
|
||||
void Camera::setOverShoulderOffset(float horizontal, float vertical)
|
||||
{
|
||||
mOverShoulderOffset = osg::Vec2f(horizontal, vertical);
|
||||
}
|
||||
|
||||
void Camera::updateSmoothTransitionToCombatMode(float duration)
|
||||
{
|
||||
bool combatMode = true;
|
||||
if (mTrackingPtr.getClass().isActor())
|
||||
combatMode = mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing;
|
||||
float speed = ((combatMode ? 1.f : 0.f) - mSmoothTransitionToCombatMode) * 5;
|
||||
if (speed != 0)
|
||||
speed += speed > 0 ? 1 : -1;
|
||||
|
||||
mSmoothTransitionToCombatMode += speed * duration;
|
||||
if (mSmoothTransitionToCombatMode > 1)
|
||||
mSmoothTransitionToCombatMode = 1;
|
||||
if (mSmoothTransitionToCombatMode < 0)
|
||||
mSmoothTransitionToCombatMode = 0;
|
||||
}
|
||||
|
||||
void Camera::toggleViewMode(bool force)
|
||||
|
@ -186,6 +249,9 @@ namespace MWRender
|
|||
else
|
||||
mViewModeToggleQueued = false;
|
||||
|
||||
if (mTrackingPtr.getClass().isActor())
|
||||
mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).setSideMovementAngle(0);
|
||||
|
||||
mFirstPersonView = !mFirstPersonView;
|
||||
processViewChange();
|
||||
}
|
||||
|
@ -259,7 +325,7 @@ namespace MWRender
|
|||
mAnimation->setFirstPersonOffset(osg::Vec3f(0,0,-offset));
|
||||
}
|
||||
|
||||
float Camera::getYaw()
|
||||
float Camera::getYaw() const
|
||||
{
|
||||
if(mVanity.enabled || mPreviewMode)
|
||||
return mPreviewCam.yaw;
|
||||
|
@ -280,7 +346,7 @@ namespace MWRender
|
|||
}
|
||||
}
|
||||
|
||||
float Camera::getPitch()
|
||||
float Camera::getPitch() const
|
||||
{
|
||||
if (mVanity.enabled || mPreviewMode) {
|
||||
return mPreviewCam.pitch;
|
||||
|
@ -314,7 +380,7 @@ namespace MWRender
|
|||
return mCameraDistance;
|
||||
}
|
||||
|
||||
void Camera::setCameraDistance(float dist, bool adjust, bool override)
|
||||
void Camera::setBaseCameraDistance(float dist, bool adjust)
|
||||
{
|
||||
if(mFirstPersonView && !mPreviewMode && !mVanity.enabled)
|
||||
return;
|
||||
|
@ -322,34 +388,55 @@ namespace MWRender
|
|||
mIsNearest = false;
|
||||
|
||||
if (adjust)
|
||||
dist += mCameraDistance;
|
||||
{
|
||||
if (mVanity.enabled || mPreviewMode)
|
||||
dist += mCameraDistance;
|
||||
else
|
||||
dist += std::min(mCameraDistance - getCameraDistanceCorrection(), mBaseCameraDistance);
|
||||
}
|
||||
|
||||
if (dist >= mFurthest) {
|
||||
|
||||
if (dist >= mFurthest)
|
||||
dist = mFurthest;
|
||||
} else if (!override && dist < 10.f) {
|
||||
dist = 10.f;
|
||||
} else if (override && dist <= mNearest) {
|
||||
else if (dist <= mNearest)
|
||||
{
|
||||
dist = mNearest;
|
||||
mIsNearest = true;
|
||||
}
|
||||
mCameraDistance = dist;
|
||||
|
||||
if (override) {
|
||||
if (mVanity.enabled || mPreviewMode) {
|
||||
mPreviewCam.offset = mCameraDistance;
|
||||
} else if (!mFirstPersonView) {
|
||||
mMaxCameraDistance = mCameraDistance;
|
||||
}
|
||||
}
|
||||
if (mVanity.enabled || mPreviewMode)
|
||||
mPreviewCam.offset = dist;
|
||||
else if (!mFirstPersonView)
|
||||
mBaseCameraDistance = dist;
|
||||
setCameraDistance();
|
||||
}
|
||||
|
||||
void Camera::setCameraDistance(float dist, bool adjust)
|
||||
{
|
||||
if(mFirstPersonView && !mPreviewMode && !mVanity.enabled)
|
||||
return;
|
||||
|
||||
if (adjust) dist += mCameraDistance;
|
||||
|
||||
if (dist >= mFurthest)
|
||||
dist = mFurthest;
|
||||
else if (dist < 10.f)
|
||||
dist = 10.f;
|
||||
mCameraDistance = dist;
|
||||
}
|
||||
|
||||
float Camera::getCameraDistanceCorrection() const
|
||||
{
|
||||
return mThirdPersonMode != ThirdPersonViewMode::Standard ? std::max(-getPitch(), 0.f) * 50.f : 0;
|
||||
}
|
||||
|
||||
void Camera::setCameraDistance()
|
||||
{
|
||||
if (mVanity.enabled || mPreviewMode) {
|
||||
if (mVanity.enabled || mPreviewMode)
|
||||
mCameraDistance = mPreviewCam.offset;
|
||||
} else if (!mFirstPersonView) {
|
||||
mCameraDistance = mMaxCameraDistance;
|
||||
}
|
||||
else if (!mFirstPersonView)
|
||||
mCameraDistance = mBaseCameraDistance + getCameraDistanceCorrection();
|
||||
mFocalPointAdjustment = osg::Vec3d();
|
||||
}
|
||||
|
||||
void Camera::setAnimation(NpcAnimation *anim)
|
||||
|
@ -382,22 +469,12 @@ namespace MWRender
|
|||
rotateCamera(getPitch(), getYaw(), false);
|
||||
}
|
||||
|
||||
void Camera::getPosition(osg::Vec3f &focal, osg::Vec3f &camera)
|
||||
{
|
||||
focal = getFocalPoint();
|
||||
|
||||
osg::Quat orient = osg::Quat(getPitch(), osg::Vec3d(1,0,0)) * osg::Quat(getYaw(), osg::Vec3d(0,0,1));
|
||||
|
||||
osg::Vec3d offset = orient * osg::Vec3d(0, isFirstPerson() ? 0 : -mCameraDistance, 0);
|
||||
camera = focal + offset;
|
||||
}
|
||||
|
||||
bool Camera::isVanityOrPreviewModeEnabled()
|
||||
bool Camera::isVanityOrPreviewModeEnabled() const
|
||||
{
|
||||
return mPreviewMode || mVanity.enabled;
|
||||
}
|
||||
|
||||
bool Camera::isNearest()
|
||||
bool Camera::isNearest() const
|
||||
{
|
||||
return mIsNearest;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,10 @@ namespace MWRender
|
|||
/// \brief Camera control
|
||||
class Camera
|
||||
{
|
||||
public:
|
||||
enum class ThirdPersonViewMode {Standard, OverShoulder};
|
||||
|
||||
private:
|
||||
struct CamData {
|
||||
float pitch, yaw, offset;
|
||||
};
|
||||
|
@ -45,7 +49,7 @@ namespace MWRender
|
|||
bool enabled, allowed;
|
||||
} mVanity;
|
||||
|
||||
float mHeight, mMaxCameraDistance;
|
||||
float mHeight, mBaseCameraDistance;
|
||||
CamData mMainCam, mPreviewCam;
|
||||
|
||||
bool mVanityToggleQueued;
|
||||
|
@ -54,6 +58,16 @@ namespace MWRender
|
|||
|
||||
float mCameraDistance;
|
||||
|
||||
ThirdPersonViewMode mThirdPersonMode;
|
||||
osg::Vec2f mOverShoulderOffset;
|
||||
osg::Vec3d mFocalPointAdjustment;
|
||||
|
||||
// Makes sense only if mThirdPersonMode is OverShoulder. Can be in range [0, 1].
|
||||
// Used for smooth transition from non-combat camera position (0) to combat camera position (1).
|
||||
float mSmoothTransitionToCombatMode;
|
||||
void updateSmoothTransitionToCombatMode(float duration);
|
||||
float getCameraDistanceCorrection() const;
|
||||
|
||||
osg::ref_ptr<osg::NodeCallback> mUpdateCallback;
|
||||
|
||||
public:
|
||||
|
@ -62,6 +76,9 @@ namespace MWRender
|
|||
|
||||
MWWorld::Ptr getTrackingPtr() const;
|
||||
|
||||
void setThirdPersonViewMode(ThirdPersonViewMode mode) { mThirdPersonMode = mode; }
|
||||
void setOverShoulderOffset(float horizontal, float vertical);
|
||||
|
||||
/// Update the view matrix of \a cam
|
||||
void updateCamera(osg::Camera* cam);
|
||||
|
||||
|
@ -72,10 +89,10 @@ namespace MWRender
|
|||
/// \param rot Rotation angles in radians
|
||||
void rotateCamera(float pitch, float yaw, bool adjust);
|
||||
|
||||
float getYaw();
|
||||
float getYaw() const;
|
||||
void setYaw(float angle);
|
||||
|
||||
float getPitch();
|
||||
float getPitch() const;
|
||||
void setPitch(float angle);
|
||||
|
||||
/// Attach camera to object
|
||||
|
@ -100,27 +117,32 @@ namespace MWRender
|
|||
|
||||
void update(float duration, bool paused=false);
|
||||
|
||||
/// Set base camera distance for current mode. Don't work on 1st person view.
|
||||
/// \param adjust Indicates should distance be adjusted or set.
|
||||
void setBaseCameraDistance(float dist, bool adjust = false);
|
||||
|
||||
/// Set camera distance for current mode. Don't work on 1st person view.
|
||||
/// \param adjust Indicates should distance be adjusted or set.
|
||||
/// \param override If true new distance will be used as default.
|
||||
/// If false, default distance can be restored with setCameraDistance().
|
||||
void setCameraDistance(float dist, bool adjust = false, bool override = true);
|
||||
/// Default distance can be restored with setCameraDistance().
|
||||
void setCameraDistance(float dist, bool adjust = false);
|
||||
|
||||
/// Restore default camera distance for current mode.
|
||||
/// Restore default camera distance and offset for current mode.
|
||||
void setCameraDistance();
|
||||
|
||||
float getCameraDistance() const;
|
||||
|
||||
void setAnimation(NpcAnimation *anim);
|
||||
|
||||
osg::Vec3d getFocalPoint();
|
||||
osg::Vec3d getFocalPoint() const;
|
||||
osg::Vec3d getFocalPointOffset() const;
|
||||
void adjustFocalPoint(osg::Vec3d adjustment) { mFocalPointAdjustment = adjustment; }
|
||||
|
||||
/// Stores focal and camera world positions in passed arguments
|
||||
void getPosition(osg::Vec3f &focal, osg::Vec3f &camera);
|
||||
void getPosition(osg::Vec3d &focal, osg::Vec3d &camera) const;
|
||||
|
||||
bool isVanityOrPreviewModeEnabled();
|
||||
bool isVanityOrPreviewModeEnabled() const;
|
||||
|
||||
bool isNearest();
|
||||
bool isNearest() const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -194,8 +194,8 @@ namespace MWRender
|
|||
if (!matrixTransform) return;
|
||||
|
||||
osg::Matrix worldToLocal = osg::Matrix::identity();
|
||||
for (auto node : mNodePath)
|
||||
if (const osg::Transform* t = node->asTransform())
|
||||
for (auto pathNode : mNodePath)
|
||||
if (const osg::Transform* t = pathNode->asTransform())
|
||||
t->computeWorldToLocalMatrix(worldToLocal, nullptr);
|
||||
worldToLocal = osg::Matrix::orthoNormal(worldToLocal);
|
||||
|
||||
|
@ -428,7 +428,7 @@ namespace MWRender
|
|||
|
||||
if (activeGrid)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
|
||||
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
||||
for (auto ref : getRefTracker().mBlacklist)
|
||||
refs.erase(ref);
|
||||
}
|
||||
|
@ -464,7 +464,7 @@ namespace MWRender
|
|||
float dSqr = (viewPoint - pos).length2();
|
||||
if (!activeGrid)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSizeCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(mSizeCacheMutex);
|
||||
SizeCache::iterator found = mSizeCache.find(pair.first);
|
||||
if (found != mSizeCache.end() && found->second < dSqr*minSize*minSize)
|
||||
continue;
|
||||
|
@ -501,7 +501,7 @@ namespace MWRender
|
|||
}
|
||||
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
|
||||
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
||||
if (getRefTracker().mDisabled.count(pair.first))
|
||||
continue;
|
||||
}
|
||||
|
@ -509,7 +509,7 @@ namespace MWRender
|
|||
float radius2 = cnode->getBound().radius2() * ref.mScale*ref.mScale;
|
||||
if (radius2 < dSqr*minSize*minSize && !activeGrid)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSizeCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(mSizeCacheMutex);
|
||||
mSizeCache[pair.first] = radius2;
|
||||
continue;
|
||||
}
|
||||
|
@ -685,7 +685,7 @@ namespace MWRender
|
|||
return false;
|
||||
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
|
||||
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
||||
if (enabled && !getWritableRefTracker().mDisabled.erase(refnum)) return false;
|
||||
if (!enabled && !getWritableRefTracker().mDisabled.insert(refnum).second) return false;
|
||||
if (mRefTrackerLocked) return false;
|
||||
|
@ -706,7 +706,7 @@ namespace MWRender
|
|||
return false;
|
||||
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
|
||||
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
||||
if (!getWritableRefTracker().mBlacklist.insert(refnum).second) return false;
|
||||
if (mRefTrackerLocked) return false;
|
||||
}
|
||||
|
@ -724,7 +724,7 @@ namespace MWRender
|
|||
|
||||
void ObjectPaging::clear()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
|
||||
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
||||
mRefTrackerNew.mDisabled.clear();
|
||||
mRefTrackerNew.mBlacklist.clear();
|
||||
mRefTrackerLocked = true;
|
||||
|
@ -734,7 +734,7 @@ namespace MWRender
|
|||
{
|
||||
if (!mRefTrackerLocked) return false;
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
|
||||
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
||||
mRefTrackerLocked = false;
|
||||
if (mRefTracker == mRefTrackerNew)
|
||||
return false;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <components/resource/resourcemanager.hpp>
|
||||
#include <components/esm/loadcell.hpp>
|
||||
|
||||
#include <OpenThreads/Mutex>
|
||||
#include <mutex>
|
||||
|
||||
namespace Resource
|
||||
{
|
||||
|
@ -58,7 +58,7 @@ namespace MWRender
|
|||
float mMinSizeMergeFactor;
|
||||
float mMinSizeCostMultiplier;
|
||||
|
||||
OpenThreads::Mutex mRefTrackerMutex;
|
||||
std::mutex mRefTrackerMutex;
|
||||
struct RefTracker
|
||||
{
|
||||
std::set<ESM::RefNum> mDisabled;
|
||||
|
@ -72,7 +72,7 @@ namespace MWRender
|
|||
const RefTracker& getRefTracker() const { return mRefTracker; }
|
||||
RefTracker& getWritableRefTracker() { return mRefTrackerLocked ? mRefTrackerNew : mRefTracker; }
|
||||
|
||||
OpenThreads::Mutex mSizeCacheMutex;
|
||||
std::mutex mSizeCacheMutex;
|
||||
typedef std::map<ESM::RefNum, float> SizeCache;
|
||||
SizeCache mSizeCache;
|
||||
};
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <limits>
|
||||
#include <cstdlib>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include <osg/Light>
|
||||
#include <osg/LightModel>
|
||||
|
@ -364,6 +366,7 @@ namespace MWRender
|
|||
float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera");
|
||||
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
|
||||
mStateUpdater->setFogEnd(mViewDistance);
|
||||
updateThirdPersonViewMode();
|
||||
|
||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
|
||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
|
||||
|
@ -379,6 +382,19 @@ namespace MWRender
|
|||
mWorkQueue = nullptr;
|
||||
}
|
||||
|
||||
void RenderingManager::updateThirdPersonViewMode()
|
||||
{
|
||||
if (Settings::Manager::getBool("view over shoulder", "Camera"))
|
||||
mCamera->setThirdPersonViewMode(Camera::ThirdPersonViewMode::OverShoulder);
|
||||
else
|
||||
mCamera->setThirdPersonViewMode(Camera::ThirdPersonViewMode::Standard);
|
||||
|
||||
std::stringstream offset(Settings::Manager::getString("view over shoulder offset", "Camera"));
|
||||
float horizontal = 30.f, vertical = -10.f;
|
||||
offset >> horizontal >> vertical;
|
||||
mCamera->setOverShoulderOffset(horizontal, vertical);
|
||||
}
|
||||
|
||||
osgUtil::IncrementalCompileOperation* RenderingManager::getIncrementalCompileOperation()
|
||||
{
|
||||
return mViewer->getIncrementalCompileOperation();
|
||||
|
@ -616,7 +632,7 @@ namespace MWRender
|
|||
|
||||
mCamera->update(dt, paused);
|
||||
|
||||
osg::Vec3f focal, cameraPos;
|
||||
osg::Vec3d focal, cameraPos;
|
||||
mCamera->getPosition(focal, cameraPos);
|
||||
mCurrentCameraPos = cameraPos;
|
||||
|
||||
|
@ -695,24 +711,24 @@ namespace MWRender
|
|||
|
||||
virtual void operator () (osg::RenderInfo& renderInfo) const
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame)
|
||||
{
|
||||
mDone = true;
|
||||
mCondition.signal();
|
||||
mCondition.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void waitTillDone()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
if (mDone)
|
||||
return;
|
||||
mCondition.wait(&mMutex);
|
||||
mCondition.wait(lock);
|
||||
}
|
||||
|
||||
mutable OpenThreads::Condition mCondition;
|
||||
mutable OpenThreads::Mutex mMutex;
|
||||
mutable std::condition_variable mCondition;
|
||||
mutable std::mutex mMutex;
|
||||
mutable bool mDone;
|
||||
unsigned int mFrame;
|
||||
};
|
||||
|
@ -1323,13 +1339,18 @@ namespace MWRender
|
|||
{
|
||||
if(mCamera->isNearest() && dist > 0.f)
|
||||
mCamera->toggleViewMode();
|
||||
else if (override)
|
||||
mCamera->setBaseCameraDistance(-dist / 120.f * 10, adjust);
|
||||
else
|
||||
mCamera->setCameraDistance(-dist / 120.f * 10, adjust, override);
|
||||
mCamera->setCameraDistance(-dist / 120.f * 10, adjust);
|
||||
}
|
||||
else if(mCamera->isFirstPerson() && dist < 0.f)
|
||||
{
|
||||
mCamera->toggleViewMode();
|
||||
mCamera->setCameraDistance(0.f, false, override);
|
||||
if (override)
|
||||
mCamera->setBaseCameraDistance(0.f, false);
|
||||
else
|
||||
mCamera->setCameraDistance(0.f, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1376,7 +1397,7 @@ namespace MWRender
|
|||
void RenderingManager::changeVanityModeScale(float factor)
|
||||
{
|
||||
if(mCamera->isVanityOrPreviewModeEnabled())
|
||||
mCamera->setCameraDistance(-factor/120.f*10, true, true);
|
||||
mCamera->setBaseCameraDistance(-factor/120.f*10, true);
|
||||
}
|
||||
|
||||
void RenderingManager::overrideFieldOfView(float val)
|
||||
|
|
|
@ -252,6 +252,7 @@ namespace MWRender
|
|||
void updateTextureFiltering();
|
||||
void updateAmbient();
|
||||
void setFogColor(const osg::Vec4f& color);
|
||||
void updateThirdPersonViewMode();
|
||||
|
||||
void reportStats() const;
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
#include <memory>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -11,11 +15,6 @@
|
|||
#include <components/misc/constants.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
|
||||
#include <OpenThreads/Thread>
|
||||
#include <OpenThreads/Condition>
|
||||
#include <OpenThreads/Mutex>
|
||||
#include <OpenThreads/ScopedLock>
|
||||
|
||||
#include "openal_output.hpp"
|
||||
#include "sound_decoder.hpp"
|
||||
#include "sound.hpp"
|
||||
|
@ -309,31 +308,33 @@ const ALfloat OpenAL_SoundStream::sBufferLength = 0.125f;
|
|||
//
|
||||
// A background streaming thread (keeps active streams processed)
|
||||
//
|
||||
struct OpenAL_Output::StreamThread : public OpenThreads::Thread {
|
||||
struct OpenAL_Output::StreamThread
|
||||
{
|
||||
typedef std::vector<OpenAL_SoundStream*> StreamVec;
|
||||
StreamVec mStreams;
|
||||
|
||||
std::atomic<bool> mQuitNow;
|
||||
OpenThreads::Mutex mMutex;
|
||||
OpenThreads::Condition mCondVar;
|
||||
std::mutex mMutex;
|
||||
std::condition_variable mCondVar;
|
||||
std::thread mThread;
|
||||
|
||||
StreamThread()
|
||||
: mQuitNow(false)
|
||||
, mThread([this] { run(); })
|
||||
{
|
||||
start();
|
||||
}
|
||||
~StreamThread()
|
||||
{
|
||||
mQuitNow = true;
|
||||
mMutex.lock(); mMutex.unlock();
|
||||
mCondVar.broadcast();
|
||||
join();
|
||||
mCondVar.notify_all();
|
||||
mThread.join();
|
||||
}
|
||||
|
||||
// thread entry point
|
||||
virtual void run()
|
||||
void run()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
while(!mQuitNow)
|
||||
{
|
||||
StreamVec::iterator iter = mStreams.begin();
|
||||
|
@ -345,30 +346,30 @@ struct OpenAL_Output::StreamThread : public OpenThreads::Thread {
|
|||
++iter;
|
||||
}
|
||||
|
||||
mCondVar.wait(&mMutex, 50);
|
||||
mCondVar.wait_for(lock, std::chrono::milliseconds(50));
|
||||
}
|
||||
}
|
||||
|
||||
void add(OpenAL_SoundStream *stream)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
if(std::find(mStreams.begin(), mStreams.end(), stream) == mStreams.end())
|
||||
{
|
||||
mStreams.push_back(stream);
|
||||
mCondVar.broadcast();
|
||||
mCondVar.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void remove(OpenAL_SoundStream *stream)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
StreamVec::iterator iter = std::find(mStreams.begin(), mStreams.end(), stream);
|
||||
if(iter != mStreams.end()) mStreams.erase(iter);
|
||||
}
|
||||
|
||||
void removeAll()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mStreams.clear();
|
||||
}
|
||||
|
||||
|
@ -1341,7 +1342,7 @@ double OpenAL_Output::getStreamOffset(Stream *sound)
|
|||
{
|
||||
if(!sound->mHandle) return 0.0;
|
||||
OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mStreamThread->mMutex);
|
||||
std::lock_guard<std::mutex> lock(mStreamThread->mMutex);
|
||||
return stream->getStreamOffset();
|
||||
}
|
||||
|
||||
|
@ -1349,7 +1350,7 @@ float OpenAL_Output::getStreamLoudness(Stream *sound)
|
|||
{
|
||||
if(!sound->mHandle) return 0.0;
|
||||
OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mStreamThread->mMutex);
|
||||
std::lock_guard<std::mutex> lock(mStreamThread->mMutex);
|
||||
return stream->getCurrentLoudness();
|
||||
}
|
||||
|
||||
|
@ -1357,7 +1358,7 @@ bool OpenAL_Output::isStreamPlaying(Stream *sound)
|
|||
{
|
||||
if(!sound->mHandle) return false;
|
||||
OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mStreamThread->mMutex);
|
||||
std::lock_guard<std::mutex> lock(mStreamThread->mMutex);
|
||||
return stream->isPlaying();
|
||||
}
|
||||
|
||||
|
|
71
apps/openmw/mwsound/regionsoundselector.cpp
Normal file
71
apps/openmw/mwsound/regionsoundselector.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include "regionsoundselector.hpp"
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
namespace
|
||||
{
|
||||
int addChance(int result, const ESM::Region::SoundRef &v)
|
||||
{
|
||||
return result + v.mChance;
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<std::string> RegionSoundSelector::getNextRandom(float duration, const std::string& regionName,
|
||||
const MWBase::World& world)
|
||||
{
|
||||
mTimePassed += duration;
|
||||
|
||||
if (mTimePassed < mTimeToNextEnvSound)
|
||||
return {};
|
||||
|
||||
const float a = Misc::Rng::rollClosedProbability();
|
||||
// NOTE: We should use the "Minimum Time Between Environmental Sounds" and
|
||||
// "Maximum Time Between Environmental Sounds" fallback settings here.
|
||||
mTimeToNextEnvSound = 5.0f * a + 15.0f * (1.0f - a);
|
||||
mTimePassed = 0;
|
||||
|
||||
if (mLastRegionName != regionName)
|
||||
{
|
||||
mLastRegionName = regionName;
|
||||
mSumChance = 0;
|
||||
}
|
||||
|
||||
const ESM::Region* const region = world.getStore().get<ESM::Region>().search(mLastRegionName);
|
||||
|
||||
if (region == nullptr)
|
||||
return {};
|
||||
|
||||
if (mSumChance == 0)
|
||||
{
|
||||
mSumChance = std::accumulate(region->mSoundList.begin(), region->mSoundList.end(), 0, addChance);
|
||||
if (mSumChance == 0)
|
||||
return {};
|
||||
}
|
||||
|
||||
const int r = Misc::Rng::rollDice(mSumChance);
|
||||
int pos = 0;
|
||||
|
||||
const auto isSelected = [&] (const ESM::Region::SoundRef& sound)
|
||||
{
|
||||
if (r - pos < sound.mChance)
|
||||
return true;
|
||||
pos += sound.mChance;
|
||||
return false;
|
||||
};
|
||||
|
||||
const auto it = std::find_if(region->mSoundList.begin(), region->mSoundList.end(), isSelected);
|
||||
|
||||
if (it == region->mSoundList.end())
|
||||
return {};
|
||||
|
||||
return it->mSound;
|
||||
}
|
||||
}
|
29
apps/openmw/mwsound/regionsoundselector.hpp
Normal file
29
apps/openmw/mwsound/regionsoundselector.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef GAME_SOUND_REGIONSOUNDSELECTOR_H
|
||||
#define GAME_SOUND_REGIONSOUNDSELECTOR_H
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace MWBase
|
||||
{
|
||||
class World;
|
||||
}
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class RegionSoundSelector
|
||||
{
|
||||
public:
|
||||
boost::optional<std::string> getNextRandom(float duration, const std::string& regionName,
|
||||
const MWBase::World& world);
|
||||
|
||||
private:
|
||||
float mTimeToNextEnvSound = 0.0f;
|
||||
int mSumChance = 0;
|
||||
std::string mLastRegionName;
|
||||
float mTimePassed = 0.0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -30,17 +30,32 @@
|
|||
|
||||
namespace MWSound
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr float sMinUpdateInterval = 1.0f / 30.0f;
|
||||
|
||||
WaterSoundUpdaterSettings makeWaterSoundUpdaterSettings()
|
||||
{
|
||||
WaterSoundUpdaterSettings settings;
|
||||
|
||||
settings.mNearWaterRadius = Fallback::Map::getInt("Water_NearWaterRadius");
|
||||
settings.mNearWaterPoints = Fallback::Map::getInt("Water_NearWaterPoints");
|
||||
settings.mNearWaterIndoorTolerance = Fallback::Map::getFloat("Water_NearWaterIndoorTolerance");
|
||||
settings.mNearWaterOutdoorTolerance = Fallback::Map::getFloat("Water_NearWaterOutdoorTolerance");
|
||||
settings.mNearWaterIndoorID = Misc::StringUtils::lowerCase(Fallback::Map::getString("Water_NearWaterIndoorID"));
|
||||
settings.mNearWaterOutdoorID = Misc::StringUtils::lowerCase(Fallback::Map::getString("Water_NearWaterOutdoorID"));
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
// For combining PlayMode and Type flags
|
||||
inline int operator|(PlayMode a, Type b) { return static_cast<int>(a) | static_cast<int>(b); }
|
||||
|
||||
SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound)
|
||||
: mVFS(vfs)
|
||||
, mOutput(new DEFAULT_OUTPUT(*this))
|
||||
, mMasterVolume(1.0f)
|
||||
, mSFXVolume(1.0f)
|
||||
, mMusicVolume(1.0f)
|
||||
, mVoiceVolume(1.0f)
|
||||
, mFootstepsVolume(1.0f)
|
||||
, mWaterSoundUpdater(makeWaterSoundUpdaterSettings())
|
||||
, mSoundBuffers(new SoundBufferList::element_type())
|
||||
, mBufferCacheSize(0)
|
||||
, mSounds(new std::deque<Sound>())
|
||||
|
@ -54,24 +69,6 @@ namespace MWSound
|
|||
, mNearWaterSound(nullptr)
|
||||
, mPlaybackPaused(false)
|
||||
{
|
||||
mMasterVolume = Settings::Manager::getFloat("master volume", "Sound");
|
||||
mMasterVolume = std::min(std::max(mMasterVolume, 0.0f), 1.0f);
|
||||
mSFXVolume = Settings::Manager::getFloat("sfx volume", "Sound");
|
||||
mSFXVolume = std::min(std::max(mSFXVolume, 0.0f), 1.0f);
|
||||
mMusicVolume = Settings::Manager::getFloat("music volume", "Sound");
|
||||
mMusicVolume = std::min(std::max(mMusicVolume, 0.0f), 1.0f);
|
||||
mVoiceVolume = Settings::Manager::getFloat("voice volume", "Sound");
|
||||
mVoiceVolume = std::min(std::max(mVoiceVolume, 0.0f), 1.0f);
|
||||
mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound");
|
||||
mFootstepsVolume = std::min(std::max(mFootstepsVolume, 0.0f), 1.0f);
|
||||
|
||||
mNearWaterRadius = Fallback::Map::getInt("Water_NearWaterRadius");
|
||||
mNearWaterPoints = Fallback::Map::getInt("Water_NearWaterPoints");
|
||||
mNearWaterIndoorTolerance = Fallback::Map::getFloat("Water_NearWaterIndoorTolerance");
|
||||
mNearWaterOutdoorTolerance = Fallback::Map::getFloat("Water_NearWaterOutdoorTolerance");
|
||||
mNearWaterIndoorID = Misc::StringUtils::lowerCase(Fallback::Map::getString("Water_NearWaterIndoorID"));
|
||||
mNearWaterOutdoorID = Misc::StringUtils::lowerCase(Fallback::Map::getString("Water_NearWaterOutdoorID"));
|
||||
|
||||
mBufferCacheMin = std::max(Settings::Manager::getInt("buffer cache min", "Sound"), 1);
|
||||
mBufferCacheMax = std::max(Settings::Manager::getInt("buffer cache max", "Sound"), 1);
|
||||
mBufferCacheMax *= 1024*1024;
|
||||
|
@ -338,26 +335,7 @@ namespace MWSound
|
|||
// Gets the combined volume settings for the given sound type
|
||||
float SoundManager::volumeFromType(Type type) const
|
||||
{
|
||||
float volume = mMasterVolume;
|
||||
switch(type)
|
||||
{
|
||||
case Type::Sfx:
|
||||
volume *= mSFXVolume;
|
||||
break;
|
||||
case Type::Voice:
|
||||
volume *= mVoiceVolume;
|
||||
break;
|
||||
case Type::Foot:
|
||||
volume *= mFootstepsVolume;
|
||||
break;
|
||||
case Type::Music:
|
||||
volume *= mMusicVolume;
|
||||
break;
|
||||
case Type::Movie:
|
||||
case Type::Mask:
|
||||
break;
|
||||
}
|
||||
return volume;
|
||||
return mVolumeSettings.getVolumeFromType(type);
|
||||
}
|
||||
|
||||
void SoundManager::stopMusic()
|
||||
|
@ -767,23 +745,12 @@ namespace MWSound
|
|||
}
|
||||
}
|
||||
|
||||
void SoundManager::stopSound(const std::string& soundId)
|
||||
{
|
||||
if(!mOutput->isInitialized())
|
||||
return;
|
||||
|
||||
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
|
||||
if (!sfx) return;
|
||||
|
||||
stopSound(sfx, MWWorld::ConstPtr());
|
||||
}
|
||||
|
||||
void SoundManager::stopSound3D(const MWWorld::ConstPtr &ptr, const std::string& soundId)
|
||||
{
|
||||
if(!mOutput->isInitialized())
|
||||
return;
|
||||
|
||||
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
|
||||
Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId));
|
||||
if (!sfx) return;
|
||||
|
||||
stopSound(sfx, ptr);
|
||||
|
@ -907,151 +874,85 @@ namespace MWSound
|
|||
|
||||
void SoundManager::updateRegionSound(float duration)
|
||||
{
|
||||
static float sTimeToNextEnvSound = 0.0f;
|
||||
static int total = 0;
|
||||
static std::string regionName = "";
|
||||
static float sTimePassed = 0.0;
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::ConstPtr player = world->getPlayerPtr();
|
||||
const ESM::Cell *cell = player.getCell()->getCell();
|
||||
|
||||
sTimePassed += duration;
|
||||
if(!cell->isExterior() || sTimePassed < sTimeToNextEnvSound)
|
||||
if (!cell->isExterior())
|
||||
return;
|
||||
|
||||
float a = Misc::Rng::rollClosedProbability();
|
||||
// NOTE: We should use the "Minimum Time Between Environmental Sounds" and
|
||||
// "Maximum Time Between Environmental Sounds" fallback settings here.
|
||||
sTimeToNextEnvSound = 5.0f*a + 15.0f*(1.0f-a);
|
||||
sTimePassed = 0;
|
||||
|
||||
if(regionName != cell->mRegion)
|
||||
{
|
||||
regionName = cell->mRegion;
|
||||
total = 0;
|
||||
}
|
||||
|
||||
const ESM::Region *regn = world->getStore().get<ESM::Region>().search(regionName);
|
||||
if(regn == nullptr)
|
||||
return;
|
||||
|
||||
if(total == 0)
|
||||
{
|
||||
for(const ESM::Region::SoundRef &sndref : regn->mSoundList)
|
||||
total += (int)sndref.mChance;
|
||||
if(total == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
int r = Misc::Rng::rollDice(total);
|
||||
int pos = 0;
|
||||
|
||||
for(const ESM::Region::SoundRef &sndref : regn->mSoundList)
|
||||
{
|
||||
if(r - pos < sndref.mChance)
|
||||
{
|
||||
playSound(sndref.mSound, 1.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
pos += sndref.mChance;
|
||||
}
|
||||
if (const auto next = mRegionSoundSelector.getNextRandom(duration, cell->mRegion, *world))
|
||||
playSound(*next, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void SoundManager::updateWaterSound(float /*duration*/)
|
||||
void SoundManager::updateWaterSound()
|
||||
{
|
||||
static const ESM::Cell *LastCell;
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::ConstPtr player = world->getPlayerPtr();
|
||||
osg::Vec3f pos = player.getRefData().getPosition().asVec3();
|
||||
const ESM::Cell *curcell = player.getCell()->getCell();
|
||||
const auto update = mWaterSoundUpdater.update(player, *world);
|
||||
|
||||
float volume = 0.0f;
|
||||
const std::string& soundId = player.getCell()->isExterior() ? mNearWaterOutdoorID : mNearWaterIndoorID;
|
||||
WaterSoundAction action;
|
||||
Sound_Buffer* sfx;
|
||||
std::tie(action, sfx) = getWaterSoundAction(update, curcell);
|
||||
|
||||
if (!mListenerUnderwater)
|
||||
switch (action)
|
||||
{
|
||||
if (curcell->hasWater())
|
||||
{
|
||||
float dist = std::abs(player.getCell()->getWaterLevel() - pos.z());
|
||||
|
||||
if (player.getCell()->isExterior() && dist < mNearWaterOutdoorTolerance)
|
||||
{
|
||||
volume = (mNearWaterOutdoorTolerance - dist) / mNearWaterOutdoorTolerance;
|
||||
|
||||
if (mNearWaterPoints > 1)
|
||||
{
|
||||
int underwaterPoints = 0;
|
||||
|
||||
float step = mNearWaterRadius * 2.0f / (mNearWaterPoints - 1);
|
||||
|
||||
for (int x = 0; x < mNearWaterPoints; x++)
|
||||
{
|
||||
for (int y = 0; y < mNearWaterPoints; y++)
|
||||
{
|
||||
float height = world->getTerrainHeightAt(
|
||||
osg::Vec3f(pos.x() - mNearWaterRadius + x*step, pos.y() - mNearWaterRadius + y*step, 0.0f));
|
||||
|
||||
if (height < 0)
|
||||
underwaterPoints++;
|
||||
}
|
||||
}
|
||||
|
||||
volume *= underwaterPoints * 2.0f / (mNearWaterPoints*mNearWaterPoints);
|
||||
}
|
||||
}
|
||||
else if (!player.getCell()->isExterior() && dist < mNearWaterIndoorTolerance)
|
||||
{
|
||||
volume = (mNearWaterIndoorTolerance - dist) / mNearWaterIndoorTolerance;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
volume = 1.0f;
|
||||
|
||||
volume = std::min(volume, 1.0f);
|
||||
|
||||
if (mNearWaterSound)
|
||||
{
|
||||
if (volume == 0.0f)
|
||||
{
|
||||
case WaterSoundAction::DoNothing:
|
||||
break;
|
||||
case WaterSoundAction::SetVolume:
|
||||
mNearWaterSound->setVolume(update.mVolume * sfx->mVolume);
|
||||
break;
|
||||
case WaterSoundAction::FinishSound:
|
||||
mOutput->finishSound(mNearWaterSound);
|
||||
mNearWaterSound = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool soundIdChanged = false;
|
||||
|
||||
Sound_Buffer *sfx = lookupSound(soundId);
|
||||
if(LastCell != curcell)
|
||||
{
|
||||
LastCell = curcell;
|
||||
SoundMap::const_iterator snditer = mActiveSounds.find(MWWorld::Ptr());
|
||||
if(snditer != mActiveSounds.end())
|
||||
{
|
||||
SoundBufferRefPairList::const_iterator pairiter = std::find_if(
|
||||
snditer->second.begin(), snditer->second.end(),
|
||||
[this](const SoundBufferRefPairList::value_type &item) -> bool
|
||||
{ return mNearWaterSound == item.first; }
|
||||
);
|
||||
if (pairiter != snditer->second.end() && pairiter->second != sfx)
|
||||
soundIdChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(soundIdChanged)
|
||||
{
|
||||
break;
|
||||
case WaterSoundAction::PlaySound:
|
||||
if (mNearWaterSound)
|
||||
mOutput->finishSound(mNearWaterSound);
|
||||
mNearWaterSound = playSound(soundId, volume, 1.0f, Type::Sfx, PlayMode::Loop);
|
||||
}
|
||||
else if (sfx)
|
||||
mNearWaterSound->setVolume(volume * sfx->mVolume);
|
||||
}
|
||||
mNearWaterSound = playSound(update.mId, update.mVolume, 1.0f, Type::Sfx, PlayMode::Loop);
|
||||
break;
|
||||
}
|
||||
else if (volume > 0.0f)
|
||||
|
||||
mLastCell = curcell;
|
||||
}
|
||||
|
||||
std::pair<SoundManager::WaterSoundAction, Sound_Buffer*> SoundManager::getWaterSoundAction(
|
||||
const WaterSoundUpdate& update, const ESM::Cell* cell) const
|
||||
{
|
||||
if (mNearWaterSound)
|
||||
{
|
||||
LastCell = curcell;
|
||||
mNearWaterSound = playSound(soundId, volume, 1.0f, Type::Sfx, PlayMode::Loop);
|
||||
if (update.mVolume == 0.0f)
|
||||
return {WaterSoundAction::FinishSound, nullptr};
|
||||
|
||||
bool soundIdChanged = false;
|
||||
|
||||
Sound_Buffer* sfx = lookupSound(update.mId);
|
||||
if (mLastCell != cell)
|
||||
{
|
||||
const auto snditer = mActiveSounds.find(MWWorld::ConstPtr());
|
||||
if (snditer != mActiveSounds.end())
|
||||
{
|
||||
const auto pairiter = std::find_if(
|
||||
snditer->second.begin(), snditer->second.end(),
|
||||
[this](const SoundBufferRefPairList::value_type &item) -> bool
|
||||
{ return mNearWaterSound == item.first; }
|
||||
);
|
||||
if (pairiter != snditer->second.end() && pairiter->second != sfx)
|
||||
soundIdChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (soundIdChanged)
|
||||
return {WaterSoundAction::PlaySound, nullptr};
|
||||
|
||||
if (sfx)
|
||||
return {WaterSoundAction::SetVolume, sfx};
|
||||
}
|
||||
else if (update.mVolume > 0.0f)
|
||||
return {WaterSoundAction::PlaySound, nullptr};
|
||||
|
||||
return {WaterSoundAction::DoNothing, nullptr};
|
||||
}
|
||||
|
||||
void SoundManager::updateSounds(float duration)
|
||||
|
@ -1065,13 +966,11 @@ namespace MWSound
|
|||
mSaySoundsQueue.erase(queuesayiter++);
|
||||
}
|
||||
|
||||
static float timePassed = 0.0;
|
||||
|
||||
timePassed += duration;
|
||||
if(timePassed < (1.0f/30.0f))
|
||||
mTimePassed += duration;
|
||||
if (mTimePassed < sMinUpdateInterval)
|
||||
return;
|
||||
duration = timePassed;
|
||||
timePassed = 0.0f;
|
||||
duration = mTimePassed;
|
||||
mTimePassed = 0.0f;
|
||||
|
||||
// Make sure music is still playing
|
||||
if(!isMusicPlaying() && !mCurrentPlaylist.empty())
|
||||
|
@ -1215,7 +1114,7 @@ namespace MWSound
|
|||
mMusic->updateFade(duration);
|
||||
|
||||
mOutput->updateStream(mMusic);
|
||||
|
||||
|
||||
if (mMusic->getRealVolume() <= 0.f)
|
||||
{
|
||||
streamMusicFull(mNextMusic);
|
||||
|
@ -1235,18 +1134,14 @@ namespace MWSound
|
|||
MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
updateRegionSound(duration);
|
||||
updateWaterSound(duration);
|
||||
updateWaterSound();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SoundManager::processChangedSettings(const Settings::CategorySettingVector& settings)
|
||||
{
|
||||
mMasterVolume = Settings::Manager::getFloat("master volume", "Sound");
|
||||
mMusicVolume = Settings::Manager::getFloat("music volume", "Sound");
|
||||
mSFXVolume = Settings::Manager::getFloat("sfx volume", "Sound");
|
||||
mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound");
|
||||
mVoiceVolume = Settings::Manager::getFloat("voice volume", "Sound");
|
||||
mVolumeSettings.update();
|
||||
|
||||
if(!mOutput->isInitialized())
|
||||
return;
|
||||
|
@ -1292,6 +1187,8 @@ namespace MWSound
|
|||
mListenerUp = up;
|
||||
|
||||
mListenerUnderwater = underwater;
|
||||
|
||||
mWaterSoundUpdater.setUnderwater(underwater);
|
||||
}
|
||||
|
||||
void SoundManager::updatePtr(const MWWorld::ConstPtr &old, const MWWorld::ConstPtr &updated)
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
#include "regionsoundselector.hpp"
|
||||
#include "watersoundupdater.hpp"
|
||||
#include "type.hpp"
|
||||
#include "volumesettings.hpp"
|
||||
|
||||
namespace VFS
|
||||
{
|
||||
class Manager;
|
||||
|
@ -22,6 +27,7 @@ namespace VFS
|
|||
namespace ESM
|
||||
{
|
||||
struct Sound;
|
||||
struct Cell;
|
||||
}
|
||||
|
||||
namespace MWSound
|
||||
|
@ -53,18 +59,10 @@ namespace MWSound
|
|||
std::unordered_map<std::string, std::vector<int>> mMusicToPlay; // A list with music files not yet played
|
||||
std::string mLastPlayedMusic; // The music file that was last played
|
||||
|
||||
float mMasterVolume;
|
||||
float mSFXVolume;
|
||||
float mMusicVolume;
|
||||
float mVoiceVolume;
|
||||
float mFootstepsVolume;
|
||||
VolumeSettings mVolumeSettings;
|
||||
|
||||
WaterSoundUpdater mWaterSoundUpdater;
|
||||
|
||||
int mNearWaterRadius;
|
||||
int mNearWaterPoints;
|
||||
float mNearWaterIndoorTolerance;
|
||||
float mNearWaterOutdoorTolerance;
|
||||
std::string mNearWaterIndoorID;
|
||||
std::string mNearWaterOutdoorID;
|
||||
typedef std::unique_ptr<std::deque<Sound_Buffer> > SoundBufferList;
|
||||
// List of sound buffers, grown as needed. New enties are added to the
|
||||
// back, allowing existing Sound_Buffer references/pointers to remain
|
||||
|
@ -115,6 +113,12 @@ namespace MWSound
|
|||
std::string mNextMusic;
|
||||
bool mPlaybackPaused;
|
||||
|
||||
RegionSoundSelector mRegionSoundSelector;
|
||||
|
||||
float mTimePassed = 0;
|
||||
|
||||
const ESM::Cell *mLastCell = nullptr;
|
||||
|
||||
Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound);
|
||||
|
||||
Sound_Buffer *lookupSound(const std::string &soundId) const;
|
||||
|
@ -134,11 +138,22 @@ namespace MWSound
|
|||
|
||||
void updateSounds(float duration);
|
||||
void updateRegionSound(float duration);
|
||||
void updateWaterSound(float duration);
|
||||
void updateWaterSound();
|
||||
void updateMusic(float duration);
|
||||
|
||||
float volumeFromType(Type type) const;
|
||||
|
||||
enum class WaterSoundAction
|
||||
{
|
||||
DoNothing,
|
||||
SetVolume,
|
||||
FinishSound,
|
||||
PlaySound,
|
||||
};
|
||||
|
||||
std::pair<WaterSoundAction, Sound_Buffer*> getWaterSoundAction(const WaterSoundUpdate& update,
|
||||
const ESM::Cell* cell) const;
|
||||
|
||||
SoundManager(const SoundManager &rhs);
|
||||
SoundManager& operator=(const SoundManager &rhs);
|
||||
|
||||
|
@ -233,9 +248,6 @@ namespace MWSound
|
|||
virtual void stopSound(const MWWorld::CellStore *cell);
|
||||
///< Stop all sounds for the given cell.
|
||||
|
||||
virtual void stopSound(const std::string& soundId);
|
||||
///< Stop a non-3d looping sound
|
||||
|
||||
virtual void fadeOutSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float duration);
|
||||
///< Fade out given sound (that is already playing) of given object
|
||||
///< @param reference Reference to object, whose sound is faded out
|
||||
|
|
17
apps/openmw/mwsound/type.hpp
Normal file
17
apps/openmw/mwsound/type.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef GAME_SOUND_TYPE_H
|
||||
#define GAME_SOUND_TYPE_H
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
Sfx = 1 << 4, /* Normal SFX sound */
|
||||
Voice = 1 << 5, /* Voice sound */
|
||||
Foot = 1 << 6, /* Footstep sound */
|
||||
Music = 1 << 7, /* Music track */
|
||||
Movie = 1 << 8, /* Movie audio track */
|
||||
Mask = Sfx | Voice | Foot | Music | Movie
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
56
apps/openmw/mwsound/volumesettings.cpp
Normal file
56
apps/openmw/mwsound/volumesettings.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#include "volumesettings.hpp"
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
namespace
|
||||
{
|
||||
float clamp(float value)
|
||||
{
|
||||
return std::max(0.0f, std::min(1.0f, value));
|
||||
}
|
||||
}
|
||||
|
||||
VolumeSettings::VolumeSettings()
|
||||
: mMasterVolume(clamp(Settings::Manager::getFloat("master volume", "Sound"))),
|
||||
mSFXVolume(clamp(Settings::Manager::getFloat("sfx volume", "Sound"))),
|
||||
mMusicVolume(clamp(Settings::Manager::getFloat("music volume", "Sound"))),
|
||||
mVoiceVolume(clamp(Settings::Manager::getFloat("voice volume", "Sound"))),
|
||||
mFootstepsVolume(clamp(Settings::Manager::getFloat("footsteps volume", "Sound")))
|
||||
{
|
||||
}
|
||||
|
||||
float VolumeSettings::getVolumeFromType(Type type) const
|
||||
{
|
||||
float volume = mMasterVolume;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case Type::Sfx:
|
||||
volume *= mSFXVolume;
|
||||
break;
|
||||
case Type::Voice:
|
||||
volume *= mVoiceVolume;
|
||||
break;
|
||||
case Type::Foot:
|
||||
volume *= mFootstepsVolume;
|
||||
break;
|
||||
case Type::Music:
|
||||
volume *= mMusicVolume;
|
||||
break;
|
||||
case Type::Movie:
|
||||
case Type::Mask:
|
||||
break;
|
||||
}
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
void VolumeSettings::update()
|
||||
{
|
||||
*this = VolumeSettings();
|
||||
}
|
||||
}
|
26
apps/openmw/mwsound/volumesettings.hpp
Normal file
26
apps/openmw/mwsound/volumesettings.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef GAME_SOUND_VOLUMESETTINGS_H
|
||||
#define GAME_SOUND_VOLUMESETTINGS_H
|
||||
|
||||
#include "type.hpp"
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class VolumeSettings
|
||||
{
|
||||
public:
|
||||
VolumeSettings();
|
||||
|
||||
float getVolumeFromType(Type type) const;
|
||||
|
||||
void update();
|
||||
|
||||
private:
|
||||
float mMasterVolume;
|
||||
float mSFXVolume;
|
||||
float mMusicVolume;
|
||||
float mVoiceVolume;
|
||||
float mFootstepsVolume;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
71
apps/openmw/mwsound/watersoundupdater.cpp
Normal file
71
apps/openmw/mwsound/watersoundupdater.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include "watersoundupdater.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include <components/esm/loadcell.hpp>
|
||||
|
||||
#include <osg/Vec3f>
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
WaterSoundUpdater::WaterSoundUpdater(const WaterSoundUpdaterSettings& settings)
|
||||
: mSettings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
WaterSoundUpdate WaterSoundUpdater::update(const MWWorld::ConstPtr& player, const MWBase::World& world) const
|
||||
{
|
||||
WaterSoundUpdate result;
|
||||
|
||||
result.mId = player.getCell()->isExterior() ? mSettings.mNearWaterOutdoorID : mSettings.mNearWaterIndoorID;
|
||||
result.mVolume = std::min(1.0f, getVolume(player, world));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float WaterSoundUpdater::getVolume(const MWWorld::ConstPtr& player, const MWBase::World& world) const
|
||||
{
|
||||
if (mListenerUnderwater)
|
||||
return 1.0f;
|
||||
|
||||
const MWWorld::CellStore& cell = *player.getCell();
|
||||
|
||||
if (!cell.getCell()->hasWater())
|
||||
return 0.0f;
|
||||
|
||||
const osg::Vec3f pos = player.getRefData().getPosition().asVec3();
|
||||
const float dist = std::abs(cell.getWaterLevel() - pos.z());
|
||||
|
||||
if (cell.isExterior() && dist < mSettings.mNearWaterOutdoorTolerance)
|
||||
{
|
||||
if (mSettings.mNearWaterPoints <= 1)
|
||||
return (mSettings.mNearWaterOutdoorTolerance - dist) / mSettings.mNearWaterOutdoorTolerance;
|
||||
|
||||
const float step = mSettings.mNearWaterRadius * 2.0f / (mSettings.mNearWaterPoints - 1);
|
||||
|
||||
int underwaterPoints = 0;
|
||||
|
||||
for (int x = 0; x < mSettings.mNearWaterPoints; x++)
|
||||
{
|
||||
for (int y = 0; y < mSettings.mNearWaterPoints; y++)
|
||||
{
|
||||
const float terrainX = pos.x() - mSettings.mNearWaterRadius + x * step;
|
||||
const float terrainY = pos.y() - mSettings.mNearWaterRadius + y * step;
|
||||
const float height = world.getTerrainHeightAt(osg::Vec3f(terrainX, terrainY, 0.0f));
|
||||
|
||||
if (height < 0)
|
||||
underwaterPoints++;
|
||||
}
|
||||
}
|
||||
|
||||
return underwaterPoints * 2.0f / (mSettings.mNearWaterPoints * mSettings.mNearWaterPoints);
|
||||
}
|
||||
|
||||
if (!cell.isExterior() && dist < mSettings.mNearWaterIndoorTolerance)
|
||||
return (mSettings.mNearWaterIndoorTolerance - dist) / mSettings.mNearWaterIndoorTolerance;
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
54
apps/openmw/mwsound/watersoundupdater.hpp
Normal file
54
apps/openmw/mwsound/watersoundupdater.hpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef GAME_SOUND_WATERSOUNDUPDATER_H
|
||||
#define GAME_SOUND_WATERSOUNDUPDATER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace MWBase
|
||||
{
|
||||
class World;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class ConstPtr;
|
||||
}
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
struct WaterSoundUpdaterSettings
|
||||
{
|
||||
int mNearWaterRadius;
|
||||
int mNearWaterPoints;
|
||||
float mNearWaterIndoorTolerance;
|
||||
float mNearWaterOutdoorTolerance;
|
||||
std::string mNearWaterIndoorID;
|
||||
std::string mNearWaterOutdoorID;
|
||||
};
|
||||
|
||||
struct WaterSoundUpdate
|
||||
{
|
||||
std::string mId;
|
||||
float mVolume;
|
||||
};
|
||||
|
||||
class WaterSoundUpdater
|
||||
{
|
||||
public:
|
||||
explicit WaterSoundUpdater(const WaterSoundUpdaterSettings& settings);
|
||||
|
||||
WaterSoundUpdate update(const MWWorld::ConstPtr& player, const MWBase::World& world) const;
|
||||
|
||||
void setUnderwater(bool value)
|
||||
{
|
||||
mListenerUnderwater = value;
|
||||
}
|
||||
|
||||
private:
|
||||
const WaterSoundUpdaterSettings mSettings;
|
||||
bool mListenerUnderwater = false;
|
||||
|
||||
float getVolume(const MWWorld::ConstPtr& player, const MWBase::World& world) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,6 +1,8 @@
|
|||
#include "scene.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
||||
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
|
||||
|
@ -798,15 +800,12 @@ namespace MWWorld
|
|||
player.getClass().adjustPosition(player, true);
|
||||
}
|
||||
|
||||
MWBase::MechanicsManager *mechMgr =
|
||||
MWBase::Environment::get().getMechanicsManager();
|
||||
|
||||
mechMgr->updateCell(old, player);
|
||||
mechMgr->watchActor(player);
|
||||
MWBase::Environment::get().getMechanicsManager()->updateCell(old, player);
|
||||
MWBase::Environment::get().getWindowManager()->watchActor(player);
|
||||
|
||||
mPhysics->updatePtr(old, player);
|
||||
|
||||
MWBase::Environment::get().getWorld()->adjustSky();
|
||||
world->adjustSky();
|
||||
|
||||
mLastPlayerPos = player.getRefData().getPosition().asVec3();
|
||||
}
|
||||
|
@ -1215,7 +1214,7 @@ namespace MWWorld
|
|||
}
|
||||
else
|
||||
loadingListener->setProgress(0);
|
||||
OpenThreads::Thread::microSleep(5000);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2055,15 +2055,35 @@ namespace MWWorld
|
|||
int nightEye = static_cast<int>(player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::NightEye).getMagnitude());
|
||||
mRendering->setNightEyeFactor(std::min(1.f, (nightEye/100.f)));
|
||||
|
||||
mRendering->getCamera()->setCameraDistance();
|
||||
auto* camera = mRendering->getCamera();
|
||||
camera->setCameraDistance();
|
||||
if(!mRendering->getCamera()->isFirstPerson())
|
||||
{
|
||||
osg::Vec3f focal, camera;
|
||||
mRendering->getCamera()->getPosition(focal, camera);
|
||||
float radius = mRendering->getNearClipDistance()*2.5f;
|
||||
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castSphere(focal, camera, radius);
|
||||
float cameraObstacleLimit = mRendering->getNearClipDistance() * 2.5f;
|
||||
float focalObstacleLimit = std::max(cameraObstacleLimit, 10.0f);
|
||||
|
||||
// Adjust focal point.
|
||||
osg::Vec3d focal = camera->getFocalPoint();
|
||||
osg::Vec3d focalOffset = camera->getFocalPointOffset();
|
||||
float offsetLen = focalOffset.length();
|
||||
if (offsetLen > 0)
|
||||
{
|
||||
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castSphere(focal - focalOffset, focal, focalObstacleLimit);
|
||||
if (result.mHit)
|
||||
{
|
||||
double adjustmentCoef = -(result.mHitPos + result.mHitNormal * focalObstacleLimit - focal).length() / offsetLen;
|
||||
if (adjustmentCoef < -1)
|
||||
adjustmentCoef = -1;
|
||||
camera->adjustFocalPoint(focalOffset * adjustmentCoef);
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust camera position.
|
||||
osg::Vec3d cameraPos;
|
||||
camera->getPosition(focal, cameraPos);
|
||||
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castSphere(focal, cameraPos, cameraObstacleLimit);
|
||||
if (result.mHit)
|
||||
mRendering->getCamera()->setCameraDistance((result.mHitPos - focal).length() - radius, false, false);
|
||||
mRendering->getCamera()->setCameraDistance((result.mHitPos + result.mHitNormal * cameraObstacleLimit - focal).length(), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2688,7 +2708,7 @@ namespace MWWorld
|
|||
rotateObject(player, 0.f, 0.f, 0.f, MWBase::RotationFlag_inverseOrder | MWBase::RotationFlag_adjust);
|
||||
|
||||
MWBase::Environment::get().getMechanicsManager()->add(getPlayerPtr());
|
||||
MWBase::Environment::get().getMechanicsManager()->watchActor(getPlayerPtr());
|
||||
MWBase::Environment::get().getWindowManager()->watchActor(getPlayerPtr());
|
||||
|
||||
std::string model = getPlayerPtr().getClass().getModel(getPlayerPtr());
|
||||
model = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS());
|
||||
|
|
|
@ -127,25 +127,24 @@ bool Wizard::IniSettings::writeFile(const QString &path, QTextStream &stream)
|
|||
QString key(fullKey.at(1));
|
||||
|
||||
int index = buffer.lastIndexOf(section);
|
||||
if (index != -1) {
|
||||
// Look for the next section
|
||||
index = buffer.indexOf(QLatin1Char('['), index + 1);
|
||||
|
||||
if (index == -1 ) {
|
||||
// We are at the last section, append it to the bottom of the file
|
||||
buffer.append(QString("\n%1=%2").arg(key, i.value().toString()));
|
||||
mSettings.remove(i.key());
|
||||
continue;
|
||||
} else {
|
||||
// Not at last section, add the key at the index
|
||||
buffer.insert(index - 1, QString("\n%1=%2").arg(key, i.value().toString()));
|
||||
mSettings.remove(i.key());
|
||||
}
|
||||
|
||||
} else {
|
||||
if (index == -1) {
|
||||
// Add the section to the end of the file, because it's not found
|
||||
buffer.append(QString("\n%1\n").arg(section));
|
||||
i.previous();
|
||||
index = buffer.lastIndexOf(section);
|
||||
}
|
||||
|
||||
// Look for the next section
|
||||
index = buffer.indexOf(QLatin1Char('['), index + 1);
|
||||
|
||||
if (index == -1 ) {
|
||||
// We are at the last section, append it to the bottom of the file
|
||||
buffer.append(QString("\n%1=%2").arg(key, i.value().toString()));
|
||||
mSettings.remove(i.key());
|
||||
continue;
|
||||
} else {
|
||||
// Not at last section, add the key at the index
|
||||
buffer.insert(index - 1, QString("\n%1=%2").arg(key, i.value().toString()));
|
||||
mSettings.remove(i.key());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -120,9 +120,10 @@ void Wizard::MainWizard::addLogText(const QString &text)
|
|||
QTextStream out(&file);
|
||||
|
||||
if (!text.isEmpty())
|
||||
out << text << endl;
|
||||
|
||||
// file.close();
|
||||
{
|
||||
out << text << "\n";
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
void Wizard::MainWizard::setupGameSettings()
|
||||
|
|
|
@ -163,7 +163,7 @@ macro (openmw_add_executable target)
|
|||
|
||||
if (MSVC)
|
||||
if (CMAKE_VERSION VERSION_GREATER 3.8 OR CMAKE_VERSION VERSION_EQUAL 3.8)
|
||||
set_target_properties(${target} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(TargetDir)")
|
||||
set_target_properties(${target} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$<TARGET_FILE_DIR:${target}>")
|
||||
endif (CMAKE_VERSION VERSION_GREATER 3.8 OR CMAKE_VERSION VERSION_EQUAL 3.8)
|
||||
endif (MSVC)
|
||||
endmacro (openmw_add_executable)
|
||||
|
|
|
@ -2,11 +2,8 @@
|
|||
#include "launchersettings.hpp"
|
||||
|
||||
#include <QTextCodec>
|
||||
#include <QTextStream>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
|
@ -105,9 +102,9 @@ bool Config::GameSettings::readUserFile(QTextStream &stream)
|
|||
return readFile(stream, mUserSettings);
|
||||
}
|
||||
|
||||
bool Config::GameSettings::readFile(QTextStream &stream, QMap<QString, QString> &settings)
|
||||
bool Config::GameSettings::readFile(QTextStream &stream, QMultiMap<QString, QString> &settings)
|
||||
{
|
||||
QMap<QString, QString> cache;
|
||||
QMultiMap<QString, QString> cache;
|
||||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
|
@ -151,7 +148,7 @@ bool Config::GameSettings::readFile(QTextStream &stream, QMap<QString, QString>
|
|||
values.append(settings.values(key));
|
||||
|
||||
if (!values.contains(value)) {
|
||||
cache.insertMulti(key, value);
|
||||
cache.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +365,7 @@ bool Config::GameSettings::writeFileWithComments(QFile &file)
|
|||
*iter = QString(); // assume no match
|
||||
QString key = settingRegex.cap(1);
|
||||
QString keyVal = settingRegex.cap(1)+"="+settingRegex.cap(2);
|
||||
QMap<QString, QString>::const_iterator i = mUserSettings.find(key);
|
||||
QMultiMap<QString, QString>::const_iterator i = mUserSettings.find(key);
|
||||
while (i != mUserSettings.end() && i.key() == key)
|
||||
{
|
||||
QString settingLine = i.key() + "=" + i.value();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QMap>
|
||||
#include <QMultiMap>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
|
@ -31,7 +31,9 @@ namespace Config
|
|||
|
||||
inline void setValue(const QString &key, const QString &value)
|
||||
{
|
||||
mSettings.remove(key);
|
||||
mSettings.insert(key, value);
|
||||
mUserSettings.remove(key);
|
||||
mUserSettings.insert(key, value);
|
||||
}
|
||||
|
||||
|
@ -39,11 +41,11 @@ namespace Config
|
|||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insertMulti(key, value);
|
||||
mSettings.insert(key, value);
|
||||
|
||||
values = mUserSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mUserSettings.insertMulti(key, value);
|
||||
mUserSettings.insert(key, value);
|
||||
}
|
||||
|
||||
inline void remove(const QString &key)
|
||||
|
@ -63,7 +65,7 @@ namespace Config
|
|||
QStringList values(const QString &key, const QStringList &defaultValues = QStringList()) const;
|
||||
|
||||
bool readFile(QTextStream &stream);
|
||||
bool readFile(QTextStream &stream, QMap<QString, QString> &settings);
|
||||
bool readFile(QTextStream &stream, QMultiMap<QString, QString> &settings);
|
||||
bool readUserFile(QTextStream &stream);
|
||||
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
@ -78,8 +80,8 @@ namespace Config
|
|||
Files::ConfigurationManager &mCfgMgr;
|
||||
|
||||
void validatePaths();
|
||||
QMap<QString, QString> mSettings;
|
||||
QMap<QString, QString> mUserSettings;
|
||||
QMultiMap<QString, QString> mSettings;
|
||||
QMultiMap<QString, QString> mUserSettings;
|
||||
|
||||
QStringList mDataDirs;
|
||||
QString mDataLocal;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <QTextStream>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
#include <QMultiMap>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
@ -22,7 +22,7 @@ Config::LauncherSettings::~LauncherSettings()
|
|||
|
||||
QStringList Config::LauncherSettings::subKeys(const QString &key)
|
||||
{
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
QMultiMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
QStringList keys = settings.uniqueKeys();
|
||||
|
||||
QRegExp keyRe("(.+)/");
|
||||
|
@ -54,7 +54,7 @@ bool Config::LauncherSettings::writeFile(QTextStream &stream)
|
|||
{
|
||||
QString sectionPrefix;
|
||||
QRegExp sectionRe("([^/]+)/(.+)$");
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
QMultiMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
|
||||
QMapIterator<QString, QString> i(settings);
|
||||
i.toBack();
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
namespace Config
|
||||
{
|
||||
class LauncherSettings : public SettingsBase<QMap<QString, QString> >
|
||||
class LauncherSettings : public SettingsBase<QMultiMap<QString, QString> >
|
||||
{
|
||||
public:
|
||||
LauncherSettings();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
#include <QMultiMap>
|
||||
|
||||
namespace Config
|
||||
{
|
||||
|
@ -33,7 +33,7 @@ namespace Config
|
|||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insertMulti(key, value);
|
||||
mSettings.insert(key, value);
|
||||
}
|
||||
|
||||
inline void setMultiValueEnabled(bool enable)
|
||||
|
@ -83,8 +83,9 @@ namespace Config
|
|||
|
||||
if (!values.contains(value)) {
|
||||
if (mMultiValue) {
|
||||
cache.insertMulti(key, value);
|
||||
cache.insert(key, value);
|
||||
} else {
|
||||
cache.remove(key);
|
||||
cache.insert(key, value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ namespace ESM
|
|||
for (int i=0; i<10; ++i)
|
||||
{
|
||||
mData.mRankData[i].mAttribute1 = mData.mRankData[i].mAttribute2 = 0;
|
||||
mData.mRankData[i].mSkill1 = mData.mRankData[i].mSkill2 = 0;
|
||||
mData.mRankData[i].mPrimarySkill = mData.mRankData[i].mFavouredSkill = 0;
|
||||
mData.mRankData[i].mFactReaction = 0;
|
||||
|
||||
mRanks[i].clear();
|
||||
|
|
|
@ -19,10 +19,11 @@ struct RankData
|
|||
{
|
||||
int mAttribute1, mAttribute2; // Attribute level
|
||||
|
||||
int mSkill1, mSkill2; // Skill level (faction skills given in
|
||||
// Skill level (faction skills given in
|
||||
// skillID below.) You need one skill at
|
||||
// level 'skill1' and two skills at level
|
||||
// 'skill2' to advance to this rank.
|
||||
// level 'mPrimarySkill' and two skills at level
|
||||
// 'mFavouredSkill' to advance to this rank.
|
||||
int mPrimarySkill, mFavouredSkill;
|
||||
|
||||
int mFactReaction; // Reaction from faction members
|
||||
};
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
#include <set>
|
||||
|
||||
#include <OpenThreads/ScopedLock>
|
||||
|
||||
#include <osg/Image>
|
||||
#include <osg/Plane>
|
||||
|
||||
|
@ -548,7 +546,7 @@ namespace ESMTerrain
|
|||
|
||||
Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mLayerInfoMutex);
|
||||
std::lock_guard<std::mutex> lock(mLayerInfoMutex);
|
||||
|
||||
// Already have this cached?
|
||||
std::map<std::string, Terrain::LayerInfo>::iterator found = mLayerInfoMap.find(texture);
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
#define COMPONENTS_ESM_TERRAIN_STORAGE_H
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <OpenThreads/Mutex>
|
||||
#include <mutex>
|
||||
|
||||
#include <components/terrain/storage.hpp>
|
||||
|
||||
|
@ -138,7 +137,7 @@ namespace ESMTerrain
|
|||
std::string getTextureName (UniqueTextureId id);
|
||||
|
||||
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
|
||||
OpenThreads::Mutex mLayerInfoMutex;
|
||||
std::mutex mLayerInfoMutex;
|
||||
|
||||
std::string mNormalMapPattern;
|
||||
std::string mNormalHeightMapPattern;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "nifloader.hpp"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <osg/Matrixf>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Geometry>
|
||||
|
@ -923,15 +925,18 @@ namespace NifOsg
|
|||
osg::BoundingBox box;
|
||||
|
||||
int i=0;
|
||||
for (std::vector<Nif::NiParticleSystemController::Particle>::const_iterator it = partctrl->particles.begin();
|
||||
i<particledata->activeCount && it != partctrl->particles.end(); ++it, ++i)
|
||||
for (const auto& particle : partctrl->particles)
|
||||
{
|
||||
const Nif::NiParticleSystemController::Particle& particle = *it;
|
||||
if (i++ >= particledata->activeCount)
|
||||
break;
|
||||
|
||||
if (particle.lifespan <= 0)
|
||||
continue;
|
||||
|
||||
ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime));
|
||||
|
||||
osgParticle::Particle* created = partsys->createParticle(&particletemplate);
|
||||
created->setLifeTime(std::max(0.f, particle.lifespan));
|
||||
created->setLifeTime(particle.lifespan);
|
||||
|
||||
// Note this position and velocity is not correct for a particle system with absolute reference frame,
|
||||
// which can not be done in this loader since we are not attached to the scene yet. Will be fixed up post-load in the SceneManager.
|
||||
|
@ -970,6 +975,8 @@ namespace NifOsg
|
|||
osgParticle::ConstantRateCounter* counter = new osgParticle::ConstantRateCounter;
|
||||
if (partctrl->emitFlags & Nif::NiParticleSystemController::NoAutoAdjust)
|
||||
counter->setNumberOfParticlesPerSecondToCreate(partctrl->emitRate);
|
||||
else if (partctrl->lifetime == 0 && partctrl->lifetimeRandom == 0)
|
||||
counter->setNumberOfParticlesPerSecondToCreate(0);
|
||||
else
|
||||
counter->setNumberOfParticlesPerSecondToCreate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2));
|
||||
|
||||
|
@ -1725,8 +1732,8 @@ namespace NifOsg
|
|||
{
|
||||
typedef std::set<osg::ref_ptr<Attribute>, CompareStateAttribute> Cache;
|
||||
static Cache sCache;
|
||||
static OpenThreads::Mutex sMutex;
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(sMutex);
|
||||
static std::mutex sMutex;
|
||||
std::lock_guard<std::mutex> lock(sMutex);
|
||||
typename Cache::iterator found = sCache.find(attr);
|
||||
if (found == sCache.end())
|
||||
found = sCache.insert(attr).first;
|
||||
|
|
|
@ -125,7 +125,7 @@ void ParticleShooter::shoot(osgParticle::Particle *particle) const
|
|||
particle->setVelocity(dir * vel);
|
||||
|
||||
// Not supposed to set this here, but there doesn't seem to be a better way of doing it
|
||||
particle->setLifeTime(mLifetime + mLifetimeRandom * Misc::Rng::rollClosedProbability());
|
||||
particle->setLifeTime(std::max(std::numeric_limits<float>::epsilon(), mLifetime + mLifetimeRandom * Misc::Rng::rollClosedProbability()));
|
||||
}
|
||||
|
||||
GrowFadeAffector::GrowFadeAffector(float growTime, float fadeTime)
|
||||
|
@ -184,6 +184,7 @@ ParticleColorAffector::ParticleColorAffector(const ParticleColorAffector ©,
|
|||
|
||||
void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* dt */)
|
||||
{
|
||||
assert(particle->getLifeTime() > 0);
|
||||
float time = static_cast<float>(particle->getAge()/particle->getLifeTime());
|
||||
osg::Vec4f color = mData.interpKey(time);
|
||||
float alpha = color.a();
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace Resource
|
|||
{
|
||||
std::vector<osg::ref_ptr<osg::Object> > objectsToRemove;
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
|
||||
// Remove unreferenced entries from object cache
|
||||
ObjectCacheMap::iterator oitr = _objectCache.begin();
|
||||
|
@ -45,7 +45,7 @@ namespace Resource
|
|||
|
||||
void MultiObjectCache::clear()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
_objectCache.clear();
|
||||
}
|
||||
|
||||
|
@ -56,13 +56,13 @@ namespace Resource
|
|||
OSG_ALWAYS << " trying to add NULL object to cache for " << filename << std::endl;
|
||||
return;
|
||||
}
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
_objectCache.insert(std::make_pair(filename, object));
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Object> MultiObjectCache::takeFromObjectCache(const std::string &fileName)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
ObjectCacheMap::iterator found = _objectCache.find(fileName);
|
||||
if (found == _objectCache.end())
|
||||
return osg::ref_ptr<osg::Object>();
|
||||
|
@ -76,7 +76,7 @@ namespace Resource
|
|||
|
||||
void MultiObjectCache::releaseGLObjects(osg::State *state)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
|
||||
for(ObjectCacheMap::iterator itr = _objectCache.begin();
|
||||
itr != _objectCache.end();
|
||||
|
@ -89,7 +89,7 @@ namespace Resource
|
|||
|
||||
unsigned int MultiObjectCache::getCacheSize() const
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
return _objectCache.size();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Referenced>
|
||||
|
@ -43,7 +44,7 @@ namespace Resource
|
|||
typedef std::multimap<std::string, osg::ref_ptr<osg::Object> > ObjectCacheMap;
|
||||
|
||||
ObjectCacheMap _objectCache;
|
||||
mutable OpenThreads::Mutex _objectCacheMutex;
|
||||
mutable std::mutex _objectCacheMutex;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
|
@ -53,7 +54,7 @@ class GenericObjectCache : public osg::Referenced
|
|||
void updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime)
|
||||
{
|
||||
// look for objects with external references and update their time stamp.
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
for(typename ObjectCacheMap::iterator itr=_objectCache.begin(); itr!=_objectCache.end(); ++itr)
|
||||
{
|
||||
// If ref count is greater than 1, the object has an external reference.
|
||||
|
@ -71,7 +72,7 @@ class GenericObjectCache : public osg::Referenced
|
|||
{
|
||||
std::vector<osg::ref_ptr<osg::Object> > objectsToRemove;
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
// Remove expired entries from object cache
|
||||
typename ObjectCacheMap::iterator oitr = _objectCache.begin();
|
||||
while(oitr != _objectCache.end())
|
||||
|
@ -92,21 +93,21 @@ class GenericObjectCache : public osg::Referenced
|
|||
/** Remove all objects in the cache regardless of having external references or expiry times.*/
|
||||
void clear()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
_objectCache.clear();
|
||||
}
|
||||
|
||||
/** Add a key,object,timestamp triple to the Registry::ObjectCache.*/
|
||||
void addEntryToObjectCache(const KeyType& key, osg::Object* object, double timestamp = 0.0)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
_objectCache[key]=ObjectTimeStampPair(object,timestamp);
|
||||
}
|
||||
|
||||
/** Remove Object from cache.*/
|
||||
void removeFromObjectCache(const KeyType& key)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
typename ObjectCacheMap::iterator itr = _objectCache.find(key);
|
||||
if (itr!=_objectCache.end()) _objectCache.erase(itr);
|
||||
}
|
||||
|
@ -114,7 +115,7 @@ class GenericObjectCache : public osg::Referenced
|
|||
/** Get an ref_ptr<Object> from the object cache*/
|
||||
osg::ref_ptr<osg::Object> getRefFromObjectCache(const KeyType& key)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
typename ObjectCacheMap::iterator itr = _objectCache.find(key);
|
||||
if (itr!=_objectCache.end())
|
||||
return itr->second.first;
|
||||
|
@ -124,7 +125,7 @@ class GenericObjectCache : public osg::Referenced
|
|||
/** Check if an object is in the cache, and if it is, update its usage time stamp. */
|
||||
bool checkInObjectCache(const KeyType& key, double timeStamp)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
typename ObjectCacheMap::iterator itr = _objectCache.find(key);
|
||||
if (itr!=_objectCache.end())
|
||||
{
|
||||
|
@ -137,7 +138,7 @@ class GenericObjectCache : public osg::Referenced
|
|||
/** call releaseGLObjects on all objects attached to the object cache.*/
|
||||
void releaseGLObjects(osg::State* state)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
for(typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr)
|
||||
{
|
||||
osg::Object* object = itr->second.first.get();
|
||||
|
@ -148,7 +149,7 @@ class GenericObjectCache : public osg::Referenced
|
|||
/** call node->accept(nv); for all nodes in the objectCache. */
|
||||
void accept(osg::NodeVisitor& nv)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
for(typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr)
|
||||
{
|
||||
osg::Object* object = itr->second.first.get();
|
||||
|
@ -165,7 +166,7 @@ class GenericObjectCache : public osg::Referenced
|
|||
template <class Functor>
|
||||
void call(Functor& f)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
for (typename ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it)
|
||||
f(it->first, it->second.first.get());
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ class GenericObjectCache : public osg::Referenced
|
|||
/** Get the number of objects in the cache. */
|
||||
unsigned int getCacheSize() const
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
return _objectCache.size();
|
||||
}
|
||||
|
||||
|
@ -185,7 +186,7 @@ class GenericObjectCache : public osg::Referenced
|
|||
typedef std::map<KeyType, ObjectTimeStampPair > ObjectCacheMap;
|
||||
|
||||
ObjectCacheMap _objectCache;
|
||||
mutable OpenThreads::Mutex _objectCacheMutex;
|
||||
mutable std::mutex _objectCacheMutex;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ namespace Resource
|
|||
|
||||
void clearCache()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_listMutex);
|
||||
std::lock_guard<OpenThreads::Mutex> lock(_listMutex);
|
||||
_sharedTextureList.clear();
|
||||
_sharedStateSetList.clear();
|
||||
}
|
||||
|
@ -625,7 +625,7 @@ namespace Resource
|
|||
|
||||
mShaderManager->releaseGLObjects(state);
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSharedStateMutex);
|
||||
std::lock_guard<std::mutex> lock(mSharedStateMutex);
|
||||
mSharedStateManager->releaseGLObjects(state);
|
||||
}
|
||||
|
||||
|
@ -717,7 +717,7 @@ namespace Resource
|
|||
|
||||
if (mIncrementalCompileOperation)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*mIncrementalCompileOperation->getToCompiledMutex());
|
||||
std::lock_guard<OpenThreads::Mutex> lock(*mIncrementalCompileOperation->getToCompiledMutex());
|
||||
osgUtil::IncrementalCompileOperation::CompileSets& sets = mIncrementalCompileOperation->getToCompile();
|
||||
for(osgUtil::IncrementalCompileOperation::CompileSets::iterator it = sets.begin(); it != sets.end();)
|
||||
{
|
||||
|
@ -738,7 +738,7 @@ namespace Resource
|
|||
{
|
||||
ResourceManager::clearCache();
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSharedStateMutex);
|
||||
std::lock_guard<std::mutex> lock(mSharedStateMutex);
|
||||
mSharedStateManager->clearCache();
|
||||
mInstanceCache->clear();
|
||||
}
|
||||
|
@ -747,12 +747,12 @@ namespace Resource
|
|||
{
|
||||
if (mIncrementalCompileOperation)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*mIncrementalCompileOperation->getToCompiledMutex());
|
||||
std::lock_guard<OpenThreads::Mutex> lock(*mIncrementalCompileOperation->getToCompiledMutex());
|
||||
stats->setAttribute(frameNumber, "Compiling", mIncrementalCompileOperation->getToCompile().size());
|
||||
}
|
||||
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSharedStateMutex);
|
||||
std::lock_guard<std::mutex> lock(mSharedStateMutex);
|
||||
stats->setAttribute(frameNumber, "Texture", mSharedStateManager->getNumSharedTextures());
|
||||
stats->setAttribute(frameNumber, "StateSet", mSharedStateManager->getNumSharedStateSets());
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Node>
|
||||
|
@ -159,7 +160,7 @@ namespace Resource
|
|||
osg::ref_ptr<MultiObjectCache> mInstanceCache;
|
||||
|
||||
osg::ref_ptr<Resource::SharedStateManager> mSharedStateManager;
|
||||
mutable OpenThreads::Mutex mSharedStateMutex;
|
||||
mutable std::mutex mSharedStateMutex;
|
||||
|
||||
Resource::ImageManager* mImageManager;
|
||||
Resource::NifFileManager* mNifFileManager;
|
||||
|
|
|
@ -25,7 +25,7 @@ StatsHandler::StatsHandler():
|
|||
_statsWidth(1280.0f),
|
||||
_statsHeight(1024.0f),
|
||||
_font(""),
|
||||
_characterSize(20.0f)
|
||||
_characterSize(18.0f)
|
||||
{
|
||||
_camera = new osg::Camera;
|
||||
_camera->getOrCreateStateSet()->setGlobalDefaults();
|
||||
|
@ -45,6 +45,8 @@ Profiler::Profiler()
|
|||
else
|
||||
_font = "";
|
||||
|
||||
_characterSize = 18;
|
||||
|
||||
setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3);
|
||||
}
|
||||
|
||||
|
|
|
@ -891,7 +891,7 @@ MWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData
|
|||
|
||||
MWShadowTechnique::ViewDependentData* MWShadowTechnique::getViewDependentData(osgUtil::CullVisitor* cv)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_viewDependentDataMapMutex);
|
||||
std::lock_guard<std::mutex> lock(_viewDependentDataMapMutex);
|
||||
ViewDependentDataMap::iterator itr = _viewDependentDataMap.find(cv);
|
||||
if (itr!=_viewDependentDataMap.end()) return itr->second.get();
|
||||
|
||||
|
@ -1343,7 +1343,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||
std::string validRegionUniformName = "validRegionMatrix" + std::to_string(sm_i);
|
||||
osg::ref_ptr<osg::Uniform> validRegionUniform;
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_accessUniformsAndProgramMutex);
|
||||
std::lock_guard<std::mutex> lock(_accessUniformsAndProgramMutex);
|
||||
|
||||
for (auto uniform : _uniforms)
|
||||
{
|
||||
|
@ -1467,7 +1467,7 @@ void MWShadowTechnique::createShaders()
|
|||
|
||||
unsigned int _baseTextureUnit = 0;
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_accessUniformsAndProgramMutex);
|
||||
std::lock_guard<std::mutex> lock(_accessUniformsAndProgramMutex);
|
||||
|
||||
_shadowCastingStateSet = new osg::StateSet;
|
||||
|
||||
|
@ -2980,7 +2980,7 @@ osg::StateSet* MWShadowTechnique::selectStateSetForRenderingShadow(ViewDependent
|
|||
|
||||
osg::ref_ptr<osg::StateSet> stateset = vdd.getStateSet();
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_accessUniformsAndProgramMutex);
|
||||
std::lock_guard<std::mutex> lock(_accessUniformsAndProgramMutex);
|
||||
|
||||
vdd.getStateSet()->clear();
|
||||
|
||||
|
@ -3057,7 +3057,7 @@ void MWShadowTechnique::resizeGLObjectBuffers(unsigned int /*maxSize*/)
|
|||
|
||||
void MWShadowTechnique::releaseGLObjects(osg::State* state) const
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_viewDependentDataMapMutex);
|
||||
std::lock_guard<std::mutex> lock(_viewDependentDataMapMutex);
|
||||
for(ViewDependentDataMap::const_iterator itr = _viewDependentDataMap.begin();
|
||||
itr != _viewDependentDataMap.end();
|
||||
++itr)
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#ifndef COMPONENTS_SCENEUTIL_MWSHADOWTECHNIQUE_H
|
||||
#define COMPONENTS_SCENEUTIL_MWSHADOWTECHNIQUE_H 1
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <osg/Camera>
|
||||
#include <osg/Material>
|
||||
#include <osg/MatrixTransform>
|
||||
|
@ -234,7 +236,7 @@ namespace SceneUtil {
|
|||
virtual ~MWShadowTechnique();
|
||||
|
||||
typedef std::map< osgUtil::CullVisitor*, osg::ref_ptr<ViewDependentData> > ViewDependentDataMap;
|
||||
mutable OpenThreads::Mutex _viewDependentDataMapMutex;
|
||||
mutable std::mutex _viewDependentDataMapMutex;
|
||||
ViewDependentDataMap _viewDependentDataMap;
|
||||
|
||||
osg::ref_ptr<osg::StateSet> _shadowRecievingPlaceholderStateSet;
|
||||
|
@ -245,7 +247,7 @@ namespace SceneUtil {
|
|||
osg::ref_ptr<osg::Texture2D> _fallbackShadowMapTexture;
|
||||
|
||||
typedef std::vector< osg::ref_ptr<osg::Uniform> > Uniforms;
|
||||
mutable OpenThreads::Mutex _accessUniformsAndProgramMutex;
|
||||
mutable std::mutex _accessUniformsAndProgramMutex;
|
||||
Uniforms _uniforms;
|
||||
osg::ref_ptr<osg::Program> _program;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace SceneUtil
|
|||
}
|
||||
}
|
||||
|
||||
osg::StateSet* stateset = mStateSets[nv->getTraversalNumber()%2];
|
||||
osg::ref_ptr<osg::StateSet> stateset = mStateSets[nv->getTraversalNumber()%2];
|
||||
apply(stateset, nv);
|
||||
|
||||
if (!isCullVisitor)
|
||||
|
|
|
@ -2,69 +2,55 @@
|
|||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
|
||||
void WorkItem::waitTillDone()
|
||||
{
|
||||
if (mDone > 0)
|
||||
if (mDone)
|
||||
return;
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
while (mDone == 0)
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
while (!mDone)
|
||||
{
|
||||
mCondition.wait(&mMutex);
|
||||
mCondition.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
void WorkItem::signalDone()
|
||||
{
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
mDone.exchange(1);
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
mDone = true;
|
||||
}
|
||||
mCondition.broadcast();
|
||||
}
|
||||
|
||||
WorkItem::WorkItem()
|
||||
{
|
||||
}
|
||||
|
||||
WorkItem::~WorkItem()
|
||||
{
|
||||
mCondition.notify_all();
|
||||
}
|
||||
|
||||
bool WorkItem::isDone() const
|
||||
{
|
||||
return (mDone > 0);
|
||||
return mDone;
|
||||
}
|
||||
|
||||
WorkQueue::WorkQueue(int workerThreads)
|
||||
: mIsReleased(false)
|
||||
{
|
||||
for (int i=0; i<workerThreads; ++i)
|
||||
{
|
||||
WorkThread* thread = new WorkThread(this);
|
||||
mThreads.push_back(thread);
|
||||
thread->startThread();
|
||||
}
|
||||
mThreads.emplace_back(std::make_unique<WorkThread>(*this));
|
||||
}
|
||||
|
||||
WorkQueue::~WorkQueue()
|
||||
{
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
while (!mQueue.empty())
|
||||
mQueue.pop_back();
|
||||
mIsReleased = true;
|
||||
mCondition.broadcast();
|
||||
mCondition.notify_all();
|
||||
}
|
||||
|
||||
for (unsigned int i=0; i<mThreads.size(); ++i)
|
||||
{
|
||||
mThreads[i]->join();
|
||||
delete mThreads[i];
|
||||
}
|
||||
mThreads.clear();
|
||||
}
|
||||
|
||||
void WorkQueue::addWorkItem(osg::ref_ptr<WorkItem> item, bool front)
|
||||
|
@ -75,20 +61,20 @@ void WorkQueue::addWorkItem(osg::ref_ptr<WorkItem> item, bool front)
|
|||
return;
|
||||
}
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
if (front)
|
||||
mQueue.push_front(item);
|
||||
else
|
||||
mQueue.push_back(item);
|
||||
mCondition.signal();
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
osg::ref_ptr<WorkItem> WorkQueue::removeWorkItem()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
while (mQueue.empty() && !mIsReleased)
|
||||
{
|
||||
mCondition.wait(&mMutex);
|
||||
mCondition.wait(lock);
|
||||
}
|
||||
if (!mQueue.empty())
|
||||
{
|
||||
|
@ -102,27 +88,28 @@ osg::ref_ptr<WorkItem> WorkQueue::removeWorkItem()
|
|||
|
||||
unsigned int WorkQueue::getNumItems() const
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
return mQueue.size();
|
||||
}
|
||||
|
||||
unsigned int WorkQueue::getNumActiveThreads() const
|
||||
{
|
||||
unsigned int count = 0;
|
||||
for (unsigned int i=0; i<mThreads.size(); ++i)
|
||||
{
|
||||
if (mThreads[i]->isActive())
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
return std::accumulate(mThreads.begin(), mThreads.end(), 0u,
|
||||
[] (auto r, const auto& t) { return r + t->isActive(); });
|
||||
}
|
||||
|
||||
WorkThread::WorkThread(WorkQueue *workQueue)
|
||||
: mWorkQueue(workQueue)
|
||||
WorkThread::WorkThread(WorkQueue& workQueue)
|
||||
: mWorkQueue(&workQueue)
|
||||
, mActive(false)
|
||||
, mThread([this] { run(); })
|
||||
{
|
||||
}
|
||||
|
||||
WorkThread::~WorkThread()
|
||||
{
|
||||
mThread.join();
|
||||
}
|
||||
|
||||
void WorkThread::run()
|
||||
{
|
||||
while (true)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue