mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-10-05 09:26:30 +00:00
Add OpenMW commits up to 1 Apr 2019
# Conflicts: # .travis.yml # CMakeLists.txt # apps/openmw/engine.cpp # apps/openmw/mwdialogue/dialoguemanagerimp.cpp # apps/openmw/mwgui/jailscreen.cpp # apps/openmw/mwgui/trainingwindow.cpp # apps/openmw/mwgui/travelwindow.cpp # apps/openmw/mwgui/waitdialog.cpp
This commit is contained in:
commit
f671c0bddc
135 changed files with 1785 additions and 1303 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
Bug #2969: Scripted items can stack
|
Bug #2969: Scripted items can stack
|
||||||
Bug #2987: Editor: some chance and AI data fields can overflow
|
Bug #2987: Editor: some chance and AI data fields can overflow
|
||||||
|
Bug #3006: 'else if' operator breaks script compilation
|
||||||
|
Bug #3109: SetPos/Position handles actors differently
|
||||||
|
Bug #3282: Unintended behaviour when assigning F3 and Windows keys
|
||||||
Bug #3623: Fix HiDPI on Windows
|
Bug #3623: Fix HiDPI on Windows
|
||||||
Bug #3733: Normal maps are inverted on mirrored UVs
|
Bug #3733: Normal maps are inverted on mirrored UVs
|
||||||
Bug #3765: DisableTeleporting makes Mark/Recall/Intervention effects undetectable
|
Bug #3765: DisableTeleporting makes Mark/Recall/Intervention effects undetectable
|
||||||
|
@ -18,6 +21,7 @@
|
||||||
Bug #4720: Inventory avatar has shield with two-handed weapon during [un]equipping animation
|
Bug #4720: Inventory avatar has shield with two-handed weapon during [un]equipping animation
|
||||||
Bug #4723: ResetActors command works incorrectly
|
Bug #4723: ResetActors command works incorrectly
|
||||||
Bug #4745: Editor: Interior cell lighting field values are not displayed as colors
|
Bug #4745: Editor: Interior cell lighting field values are not displayed as colors
|
||||||
|
Bug #4736: LandTexture records overrides do not work
|
||||||
Bug #4746: Non-solid player can't run or sneak
|
Bug #4746: Non-solid player can't run or sneak
|
||||||
Bug #4747: Bones are not read from X.NIF file for NPC animation
|
Bug #4747: Bones are not read from X.NIF file for NPC animation
|
||||||
Bug #4750: Sneaking doesn't work in first person view if the player is in attack ready state
|
Bug #4750: Sneaking doesn't work in first person view if the player is in attack ready state
|
||||||
|
@ -32,15 +36,26 @@
|
||||||
Bug #4813: Creatures with known file but no "Sound Gen Creature" assigned use default sounds
|
Bug #4813: Creatures with known file but no "Sound Gen Creature" assigned use default sounds
|
||||||
Bug #4815: "Finished" journal entry with lower index doesn't close journal, SetJournalIndex closes journal
|
Bug #4815: "Finished" journal entry with lower index doesn't close journal, SetJournalIndex closes journal
|
||||||
Bug #4820: Spell absorption is broken
|
Bug #4820: Spell absorption is broken
|
||||||
|
Bug #4823: Jail progress bar works incorrectly
|
||||||
Bug #4827: NiUVController is handled incorrectly
|
Bug #4827: NiUVController is handled incorrectly
|
||||||
Bug #4828: Potion looping effects VFX are not shown for NPCs
|
Bug #4828: Potion looping effects VFX are not shown for NPCs
|
||||||
Bug #4837: CTD when a mesh with NiLODNode root node with particles is loaded
|
Bug #4837: CTD when a mesh with NiLODNode root node with particles is loaded
|
||||||
|
Bug #4841: Russian localization ignores implicit keywords
|
||||||
Bug #4860: Actors outside of processing range visible for one frame after spawning
|
Bug #4860: Actors outside of processing range visible for one frame after spawning
|
||||||
|
Bug #4867: Arbitrary text after local variable declarations breaks script compilation
|
||||||
Bug #4876: AI ratings handling inconsistencies
|
Bug #4876: AI ratings handling inconsistencies
|
||||||
|
Bug #4877: Startup script executes only on a new game start
|
||||||
Bug #4888: Global variable stray explicit reference calls break script compilation
|
Bug #4888: Global variable stray explicit reference calls break script compilation
|
||||||
|
Bug #4896: Title screen music doesn't loop
|
||||||
|
Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5
|
||||||
|
Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used.
|
||||||
|
Bug #4922: Werewolves can not attack if the transformation happens during attack
|
||||||
|
Bug #4938: Strings from subrecords with actually empty headers can't be empty
|
||||||
|
Bug #4942: Hand-to-Hand attack type is chosen randomly when "always use best attack" is turned off
|
||||||
Feature #2229: Improve pathfinding AI
|
Feature #2229: Improve pathfinding AI
|
||||||
Feature #3442: Default values for fallbacks from ini file
|
Feature #3442: Default values for fallbacks from ini file
|
||||||
Feature #3610: Option to invert X axis
|
Feature #3610: Option to invert X axis
|
||||||
|
Feature #3893: Implicit target for "set" function in console
|
||||||
Feature #3980: In-game option to disable controller
|
Feature #3980: In-game option to disable controller
|
||||||
Feature #4209: Editor: Faction rank sub-table
|
Feature #4209: Editor: Faction rank sub-table
|
||||||
Feature #4673: Weapon sheathing
|
Feature #4673: Weapon sheathing
|
||||||
|
@ -52,6 +67,7 @@
|
||||||
Feature #4887: Add openmw command option to set initial random seed
|
Feature #4887: Add openmw command option to set initial random seed
|
||||||
Feature #4890: Make Distant Terrain configurable
|
Feature #4890: Make Distant Terrain configurable
|
||||||
Task #4686: Upgrade media decoder to a more current FFmpeg API
|
Task #4686: Upgrade media decoder to a more current FFmpeg API
|
||||||
|
Task #4695: Optimize Distant Terrain memory consumption
|
||||||
|
|
||||||
0.45.0
|
0.45.0
|
||||||
------
|
------
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
brew update
|
brew update
|
||||||
brew unlink cmake || true
|
|
||||||
brew install https://gist.githubusercontent.com/nikolaykasyanov/f36da224bdef42025e480f99fa21a82d/raw/7dd8b5ed2750198757f81c6bc6456e03541999bd/cmake.rb
|
|
||||||
brew switch cmake 3.12.4
|
|
||||||
brew outdated pkgconfig || brew upgrade pkgconfig
|
brew outdated pkgconfig || brew upgrade pkgconfig
|
||||||
brew install qt
|
brew install qt
|
||||||
|
brew install ccache
|
||||||
|
|
||||||
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-110f3d3.zip -o ~/openmw-deps.zip
|
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-110f3d3.zip -o ~/openmw-deps.zip
|
||||||
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||||
|
|
|
@ -4,12 +4,15 @@ export CXX=clang++
|
||||||
export CC=clang
|
export CC=clang
|
||||||
|
|
||||||
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
||||||
QT_PATH=`brew --prefix qt`
|
QT_PATH=$(brew --prefix qt)
|
||||||
|
CCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
cmake \
|
cmake \
|
||||||
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
|
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
|
||||||
|
-D CMAKE_C_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE" \
|
||||||
|
-D CMAKE_CXX_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE" \
|
||||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.9" \
|
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.9" \
|
||||||
-D CMAKE_OSX_SYSROOT="macosx10.14" \
|
-D CMAKE_OSX_SYSROOT="macosx10.14" \
|
||||||
-D CMAKE_BUILD_TYPE=Release \
|
-D CMAKE_BUILD_TYPE=Release \
|
||||||
|
|
|
@ -267,11 +267,12 @@ endif()
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
||||||
|
|
||||||
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX osgShadow)
|
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX osgShadow)
|
||||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
|
include_directories(SYSTEM ${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||||
|
|
||||||
set(USED_OSG_PLUGINS
|
set(USED_OSG_PLUGINS
|
||||||
osgdb_bmp
|
osgdb_bmp
|
||||||
osgdb_dds
|
osgdb_dds
|
||||||
|
osgdb_freetype
|
||||||
osgdb_jpeg
|
osgdb_jpeg
|
||||||
osgdb_osg
|
osgdb_osg
|
||||||
osgdb_png
|
osgdb_png
|
||||||
|
@ -858,8 +859,8 @@ endif()
|
||||||
|
|
||||||
# Apple bundling
|
# Apple bundling
|
||||||
if (OPENMW_OSX_DEPLOYMENT AND APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
if (OPENMW_OSX_DEPLOYMENT AND APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
||||||
if (${CMAKE_MAJOR_VERSION} STREQUAL "3" AND ${CMAKE_MINOR_VERSION} STREQUAL "13")
|
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND CMAKE_VERSION VERSION_LESS 3.13.4)
|
||||||
message(FATAL_ERROR "macOS packaging is broken in CMake 3.13.*, see https://gitlab.com/OpenMW/openmw/issues/4767. Please use an older version like 3.12.4")
|
message(FATAL_ERROR "macOS packaging is broken in early CMake 3.13 releases, see https://gitlab.com/OpenMW/openmw/issues/4767. Please use at least 3.13.4 or an older version like 3.12.4")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
get_property(QT_COCOA_PLUGIN_PATH TARGET Qt5::QCocoaIntegrationPlugin PROPERTY LOCATION_RELEASE)
|
get_property(QT_COCOA_PLUGIN_PATH TARGET Qt5::QCocoaIntegrationPlugin PROPERTY LOCATION_RELEASE)
|
||||||
|
|
|
@ -739,7 +739,7 @@ void Record<ESM::Faction>::print()
|
||||||
std::cout << " Faction Reaction: "
|
std::cout << " Faction Reaction: "
|
||||||
<< mData.mData.mRankData[i].mFactReaction << std::endl;
|
<< mData.mData.mRankData[i].mFactReaction << std::endl;
|
||||||
}
|
}
|
||||||
for (const std::pair<std::string, int> &reaction : mData.mReactions)
|
for (const auto &reaction : mData.mReactions)
|
||||||
std::cout << " Reaction: " << reaction.second << " = " << reaction.first << std::endl;
|
std::cout << " Reaction: " << reaction.second << " = " << reaction.first << std::endl;
|
||||||
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,14 +102,6 @@ bool parseOptions (int argc, char** argv, std::vector<std::string>& files)
|
||||||
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).
|
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).
|
||||||
options(desc).positional(p).run();
|
options(desc).positional(p).run();
|
||||||
bpo::store(valid_opts, variables);
|
bpo::store(valid_opts, variables);
|
||||||
}
|
|
||||||
catch(std::exception &e)
|
|
||||||
{
|
|
||||||
std::cout << "ERROR parsing arguments: " << e.what() << "\n\n"
|
|
||||||
<< desc << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bpo::notify(variables);
|
bpo::notify(variables);
|
||||||
if (variables.count ("help"))
|
if (variables.count ("help"))
|
||||||
{
|
{
|
||||||
|
@ -121,6 +113,13 @@ bool parseOptions (int argc, char** argv, std::vector<std::string>& files)
|
||||||
files = variables["input-file"].as< std::vector<std::string> >();
|
files = variables["input-file"].as< std::vector<std::string> >();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch(std::exception &e)
|
||||||
|
{
|
||||||
|
std::cout << "ERROR parsing arguments: " << e.what() << "\n\n"
|
||||||
|
<< desc << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "No input files or directories specified!" << std::endl;
|
std::cout << "No input files or directories specified!" << std::endl;
|
||||||
std::cout << desc << std::endl;
|
std::cout << desc << std::endl;
|
||||||
|
|
|
@ -195,7 +195,7 @@ namespace CSMPrefs
|
||||||
// Only activate the best match; in exact conflicts, this will favor the first shortcut added.
|
// Only activate the best match; in exact conflicts, this will favor the first shortcut added.
|
||||||
if (!potentials.empty())
|
if (!potentials.empty())
|
||||||
{
|
{
|
||||||
std::sort(potentials.begin(), potentials.end(), ShortcutEventHandler::sort);
|
std::stable_sort(potentials.begin(), potentials.end(), ShortcutEventHandler::sort);
|
||||||
Shortcut* shortcut = potentials.front().second;
|
Shortcut* shortcut = potentials.front().second;
|
||||||
|
|
||||||
if (shortcut->getModifierStatus() && shortcut->getSecondaryMode() == Shortcut::SM_Replace)
|
if (shortcut->getModifierStatus() && shortcut->getSecondaryMode() == Shortcut::SM_Replace)
|
||||||
|
@ -325,7 +325,7 @@ namespace CSMPrefs
|
||||||
if (left.first == Matches_WithMod && right.first == Matches_NoMod)
|
if (left.first == Matches_WithMod && right.first == Matches_NoMod)
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return left.second->getPosition() >= right.second->getPosition();
|
return left.second->getPosition() > right.second->getPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutEventHandler::widgetDestroyed()
|
void ShortcutEventHandler::widgetDestroyed()
|
||||||
|
|
|
@ -1006,7 +1006,7 @@ void CSMWorld::Data::loadFallbackEntries()
|
||||||
std::make_pair("PrisonMarker", "marker_prison.nif")
|
std::make_pair("PrisonMarker", "marker_prison.nif")
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const std::pair<std::string, std::string> &marker : staticMarkers)
|
for (const auto &marker : staticMarkers)
|
||||||
{
|
{
|
||||||
if (mReferenceables.searchId (marker.first)==-1)
|
if (mReferenceables.searchId (marker.first)==-1)
|
||||||
{
|
{
|
||||||
|
@ -1020,7 +1020,7 @@ void CSMWorld::Data::loadFallbackEntries()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const std::pair<std::string, std::string> &marker : doorMarkers)
|
for (const auto &marker : doorMarkers)
|
||||||
{
|
{
|
||||||
if (mReferenceables.searchId (marker.first)==-1)
|
if (mReferenceables.searchId (marker.first)==-1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
@ -615,7 +616,39 @@ void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint)
|
||||||
}
|
}
|
||||||
else if (hint[0]=='r')
|
else if (hint[0]=='r')
|
||||||
{
|
{
|
||||||
/// \todo implement 'r' type hints
|
// syntax r:ref#number (e.g. r:ref#100)
|
||||||
|
char ignore;
|
||||||
|
|
||||||
|
std::istringstream stream (hint.c_str());
|
||||||
|
if (stream >> ignore) // ignore r
|
||||||
|
{
|
||||||
|
char ignore1; // : or ;
|
||||||
|
|
||||||
|
std::string refCode; // ref#number (e.g. ref#100)
|
||||||
|
|
||||||
|
while (stream >> ignore1 >> refCode) {}
|
||||||
|
|
||||||
|
//Find out cell coordinate
|
||||||
|
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
|
||||||
|
*mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_References));
|
||||||
|
int cellColumn = references.findColumnIndex(CSMWorld::Columns::ColumnId_Cell);
|
||||||
|
QVariant cell = references.data(references.getModelIndex(refCode, cellColumn)).value<QVariant>();
|
||||||
|
QString cellqs = cell.toString();
|
||||||
|
std::istringstream streamCellCoord (cellqs.toStdString().c_str());
|
||||||
|
|
||||||
|
if (streamCellCoord >> ignore) //ignore #
|
||||||
|
{
|
||||||
|
// Current coordinate
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
// Loop through all the coordinates to add them to selection
|
||||||
|
while (streamCellCoord >> x >> y)
|
||||||
|
selection.add (CSMWorld::CellCoordinates (x, y));
|
||||||
|
|
||||||
|
// Mark that camera needs setup
|
||||||
|
mCamPositionSet=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setCellSelection (selection);
|
setCellSelection (selection);
|
||||||
|
|
|
@ -18,10 +18,10 @@ namespace CSVRender
|
||||||
private:
|
private:
|
||||||
const CSMWorld::Data& mData;
|
const CSMWorld::Data& mData;
|
||||||
|
|
||||||
virtual osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY);
|
virtual osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY) override;
|
||||||
virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
|
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override;
|
||||||
|
|
||||||
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
|
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <components/detournavigator/navigator.hpp>
|
||||||
|
|
||||||
#include "mwinput/inputmanagerimp.hpp"
|
#include "mwinput/inputmanagerimp.hpp"
|
||||||
|
|
||||||
#include "mwgui/windowmanagerimp.hpp"
|
#include "mwgui/windowmanagerimp.hpp"
|
||||||
|
@ -289,6 +291,8 @@ bool OMW::Engine::frame(float frametime)
|
||||||
|
|
||||||
stats->setAttribute(frameNumber, "WorkQueue", mWorkQueue->getNumItems());
|
stats->setAttribute(frameNumber, "WorkQueue", mWorkQueue->getNumItems());
|
||||||
stats->setAttribute(frameNumber, "WorkThread", mWorkQueue->getNumActiveThreads());
|
stats->setAttribute(frameNumber, "WorkThread", mWorkQueue->getNumActiveThreads());
|
||||||
|
|
||||||
|
mEnvironment.getWorld()->getNavigator()->reportStats(frameNumber, *stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -313,6 +317,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
||||||
, mActivationDistanceOverride(-1)
|
, mActivationDistanceOverride(-1)
|
||||||
, mGrab(true)
|
, mGrab(true)
|
||||||
, mExportFonts(false)
|
, mExportFonts(false)
|
||||||
|
, mRandomSeed(0)
|
||||||
, mScriptContext (0)
|
, mScriptContext (0)
|
||||||
, mFSStrict (false)
|
, mFSStrict (false)
|
||||||
, mScriptBlacklistUse (true)
|
, mScriptBlacklistUse (true)
|
||||||
|
@ -667,7 +672,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
// Create the world
|
// Create the world
|
||||||
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
|
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
|
||||||
mFileCollections, mContentFiles, mEncoder, mFallbackMap,
|
mFileCollections, mContentFiles, mEncoder, mFallbackMap,
|
||||||
mActivationDistanceOverride, mCellName, mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
|
mActivationDistanceOverride, mCellName, mResDir.string(), mCfgMgr.getUserDataPath().string()));
|
||||||
mEnvironment.getWorld()->setupPlayer();
|
mEnvironment.getWorld()->setupPlayer();
|
||||||
input->setPlayer(&mEnvironment.getWorld()->getPlayer());
|
input->setPlayer(&mEnvironment.getWorld()->getPlayer());
|
||||||
|
|
||||||
|
@ -783,6 +788,9 @@ void OMW::Engine::go()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Log(Debug::Info) << "OSG version: " << osgGetVersion();
|
Log(Debug::Info) << "OSG version: " << osgGetVersion();
|
||||||
|
SDL_version sdlVersion;
|
||||||
|
SDL_GetVersion(&sdlVersion);
|
||||||
|
Log(Debug::Info) << "SDL version: " << (int)sdlVersion.major << "." << (int)sdlVersion.minor << "." << (int)sdlVersion.patch;
|
||||||
|
|
||||||
Misc::Rng::init(mRandomSeed);
|
Misc::Rng::init(mRandomSeed);
|
||||||
|
|
||||||
|
@ -855,22 +863,21 @@ void OMW::Engine::go()
|
||||||
{
|
{
|
||||||
// start in main menu
|
// start in main menu
|
||||||
mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
||||||
try
|
mEnvironment.getSoundManager()->playTitleMusic();
|
||||||
{
|
|
||||||
// Is there an ini setting for this filename or something?
|
|
||||||
mEnvironment.getSoundManager()->streamMusic("Special/morrowind title.mp3");
|
|
||||||
|
|
||||||
std::string logo = mFallbackMap["Movies_Morrowind_Logo"];
|
std::string logo = mFallbackMap["Movies_Morrowind_Logo"];
|
||||||
if (!logo.empty())
|
if (!logo.empty())
|
||||||
mEnvironment.getWindowManager()->playVideo(logo, true);
|
mEnvironment.getWindowManager()->playVideo(logo, true);
|
||||||
}
|
}
|
||||||
catch (...) {}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mEnvironment.getStateManager()->newGame (!mNewGame);
|
mEnvironment.getStateManager()->newGame (!mNewGame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mStartupScript.empty() && mEnvironment.getStateManager()->getState() == MWState::StateManager::State_Running)
|
||||||
|
{
|
||||||
|
mEnvironment.getWindowManager()->executeInConsole(mStartupScript);
|
||||||
|
}
|
||||||
|
|
||||||
// Start the main rendering loop
|
// Start the main rendering loop
|
||||||
osg::Timer frameTimer;
|
osg::Timer frameTimer;
|
||||||
double simulationTime = 0.0;
|
double simulationTime = 0.0;
|
||||||
|
|
|
@ -90,9 +90,9 @@ namespace MWBase
|
||||||
virtual void setPlayerClass (const ESM::Class& class_) = 0;
|
virtual void setPlayerClass (const ESM::Class& class_) = 0;
|
||||||
///< Set player class to custom class.
|
///< Set player class to custom class.
|
||||||
|
|
||||||
virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep) = 0;
|
virtual void restoreDynamicStats(MWWorld::Ptr actor, double hours, bool sleep) = 0;
|
||||||
|
|
||||||
virtual void rest(bool sleep) = 0;
|
virtual void rest(double hours, bool sleep) = 0;
|
||||||
///< If the player is sleeping or waiting, this should be called every hour.
|
///< If the player is sleeping or waiting, this should be called every hour.
|
||||||
/// @param sleep is the player sleeping or waiting?
|
/// @param sleep is the player sleeping or waiting?
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,9 @@ namespace MWBase
|
||||||
///< Start playing music from the selected folder
|
///< Start playing music from the selected folder
|
||||||
/// \param name of the folder that contains the playlist
|
/// \param name of the folder that contains the playlist
|
||||||
|
|
||||||
|
virtual void playTitleMusic() = 0;
|
||||||
|
///< Start playing title music
|
||||||
|
|
||||||
virtual void say(const MWWorld::ConstPtr &reference, const std::string& filename) = 0;
|
virtual void say(const MWWorld::ConstPtr &reference, const std::string& filename) = 0;
|
||||||
///< Make an actor say some text.
|
///< Make an actor say some text.
|
||||||
/// \param filename name of a sound file in "Sound/" in the data directory.
|
/// \param filename name of a sound file in "Sound/" in the data directory.
|
||||||
|
|
|
@ -406,7 +406,7 @@ namespace MWBase
|
||||||
virtual void deleteObject (const MWWorld::Ptr& ptr) = 0;
|
virtual void deleteObject (const MWWorld::Ptr& ptr) = 0;
|
||||||
virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0;
|
virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0;
|
||||||
|
|
||||||
virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0;
|
virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z, bool moveToActive=false) = 0;
|
||||||
///< @return an updated Ptr in case the Ptr's cell changes
|
///< @return an updated Ptr in case the Ptr's cell changes
|
||||||
|
|
||||||
virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0;
|
virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0;
|
||||||
|
@ -760,7 +760,7 @@ namespace MWBase
|
||||||
|
|
||||||
virtual bool isPlayerInJail() const = 0;
|
virtual bool isPlayerInJail() const = 0;
|
||||||
|
|
||||||
virtual void rest() = 0;
|
virtual void rest(double hours) = 0;
|
||||||
|
|
||||||
virtual void setPlayerTraveling(bool traveling) = 0;
|
virtual void setPlayerTraveling(bool traveling) = 0;
|
||||||
virtual bool isPlayerTraveling() const = 0;
|
virtual bool isPlayerTraveling() const = 0;
|
||||||
|
|
|
@ -131,9 +131,6 @@ namespace MWDialogue
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (tok->isImplicitKeyword() && mTranslationDataStorage.hasTranslation())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (mActorKnownTopics.count( topicId ))
|
if (mActorKnownTopics.count( topicId ))
|
||||||
mKnownTopics.insert( topicId );
|
mKnownTopics.insert( topicId );
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ namespace MWDialogue
|
||||||
Token(const std::string & text, Type type) : mText(text), mType(type) {}
|
Token(const std::string & text, Type type) : mText(text), mType(type) {}
|
||||||
|
|
||||||
bool isExplicitLink() { return mType == ExplicitLink; }
|
bool isExplicitLink() { return mType == ExplicitLink; }
|
||||||
bool isImplicitKeyword() { return mType == ImplicitKeyword; }
|
|
||||||
|
|
||||||
std::string mText;
|
std::string mText;
|
||||||
Type mType;
|
Type mType;
|
||||||
|
|
|
@ -11,10 +11,12 @@
|
||||||
#include "../mwscript/extensions.hpp"
|
#include "../mwscript/extensions.hpp"
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/scriptmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
@ -173,6 +175,12 @@ namespace MWGui
|
||||||
print("> " + command + "\n");
|
print("> " + command + "\n");
|
||||||
|
|
||||||
Compiler::Locals locals;
|
Compiler::Locals locals;
|
||||||
|
if (!mPtr.isEmpty())
|
||||||
|
{
|
||||||
|
std::string script = mPtr.getClass().getScript(mPtr);
|
||||||
|
if (!script.empty())
|
||||||
|
locals = MWBase::Environment::get().getScriptManager()->getLocals(script);
|
||||||
|
}
|
||||||
Compiler::Output output (locals);
|
Compiler::Output output (locals);
|
||||||
|
|
||||||
if (compile (command + "\n", output))
|
if (compile (command + "\n", output))
|
||||||
|
|
|
@ -122,8 +122,7 @@ namespace MWGui
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for (int i=0; i<mDays*24; ++i)
|
MWBase::Environment::get().getMechanicsManager()->rest(mDays * 24, true);
|
||||||
MWBase::Environment::get().getMechanicsManager()->rest(true);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp change (major)
|
Start of tes3mp change (major)
|
||||||
|
|
|
@ -304,11 +304,10 @@ namespace MWGui
|
||||||
|
|
||||||
MyGUI::IntSize requested = button->getRequestedSize();
|
MyGUI::IntSize requested = button->getRequestedSize();
|
||||||
|
|
||||||
|
button->setImageCoord(MyGUI::IntCoord(0, 0, requested.width, requested.height));
|
||||||
// Trim off some of the excessive padding
|
// Trim off some of the excessive padding
|
||||||
// TODO: perhaps do this within ImageButton?
|
// TODO: perhaps do this within ImageButton?
|
||||||
int trim = 8;
|
int height = requested.height-16;
|
||||||
button->setImageCoord(MyGUI::IntCoord(0, trim, requested.width, requested.height-trim));
|
|
||||||
int height = requested.height-trim*2;
|
|
||||||
button->setImageTile(MyGUI::IntSize(requested.width, height));
|
button->setImageTile(MyGUI::IntSize(requested.width, height));
|
||||||
button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, height);
|
button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, height);
|
||||||
curH += height;
|
curH += height;
|
||||||
|
|
|
@ -244,8 +244,7 @@ namespace MWGui
|
||||||
map->setNeedMouseFocus(false);
|
map->setNeedMouseFocus(false);
|
||||||
fog->setNeedMouseFocus(false);
|
fog->setNeedMouseFocus(false);
|
||||||
|
|
||||||
mMapWidgets.push_back(map);
|
mMaps.emplace_back(map, fog);
|
||||||
mFogWidgets.push_back(fog);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,36 +264,37 @@ namespace MWGui
|
||||||
|
|
||||||
void LocalMapBase::applyFogOfWar()
|
void LocalMapBase::applyFogOfWar()
|
||||||
{
|
{
|
||||||
TextureVector fogTextures;
|
|
||||||
for (int mx=0; mx<mNumCells; ++mx)
|
for (int mx=0; mx<mNumCells; ++mx)
|
||||||
{
|
{
|
||||||
for (int my=0; my<mNumCells; ++my)
|
for (int my=0; my<mNumCells; ++my)
|
||||||
{
|
{
|
||||||
int x = mCurX + (mx - mCellDistance);
|
int x = mCurX + (mx - mCellDistance);
|
||||||
int y = mCurY + (-1*(my - mCellDistance));
|
int y = mCurY + (-1*(my - mCellDistance));
|
||||||
MyGUI::ImageBox* fog = mFogWidgets[my + mNumCells*mx];
|
|
||||||
|
MapEntry& entry = mMaps[my + mNumCells*mx];
|
||||||
|
MyGUI::ImageBox* fog = entry.mFogWidget;
|
||||||
|
|
||||||
if (!mFogOfWarToggled || !mFogOfWarEnabled)
|
if (!mFogOfWarToggled || !mFogOfWarEnabled)
|
||||||
{
|
{
|
||||||
fog->setImageTexture("");
|
fog->setImageTexture("");
|
||||||
|
entry.mFogTexture.reset();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> tex = mLocalMapRender->getFogOfWarTexture(x, y);
|
osg::ref_ptr<osg::Texture2D> tex = mLocalMapRender->getFogOfWarTexture(x, y);
|
||||||
if (tex)
|
if (tex)
|
||||||
{
|
{
|
||||||
std::shared_ptr<MyGUI::ITexture> myguitex (new osgMyGUI::OSGTexture(tex));
|
entry.mFogTexture.reset(new osgMyGUI::OSGTexture(tex));
|
||||||
fog->setRenderItemTexture(myguitex.get());
|
fog->setRenderItemTexture(entry.mFogTexture.get());
|
||||||
fog->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));
|
fog->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));
|
||||||
fogTextures.push_back(myguitex);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
fog->setImageTexture("black");
|
fog->setImageTexture("black");
|
||||||
|
entry.mFogTexture.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Move the textures we just set into mFogTextures, and move the previous textures into fogTextures, for deletion when this function ends.
|
|
||||||
// Note, above we need to ensure that all widgets are getting a new texture set, lest we delete textures that are still in use.
|
|
||||||
mFogTextures.swap(fogTextures);
|
|
||||||
|
|
||||||
redraw();
|
redraw();
|
||||||
}
|
}
|
||||||
|
@ -428,7 +428,6 @@ namespace MWGui
|
||||||
applyFogOfWar();
|
applyFogOfWar();
|
||||||
|
|
||||||
// Update the map textures
|
// Update the map textures
|
||||||
TextureVector textures;
|
|
||||||
for (int mx=0; mx<mNumCells; ++mx)
|
for (int mx=0; mx<mNumCells; ++mx)
|
||||||
{
|
{
|
||||||
for (int my=0; my<mNumCells; ++my)
|
for (int my=0; my<mNumCells; ++my)
|
||||||
|
@ -436,21 +435,23 @@ namespace MWGui
|
||||||
int mapX = x + (mx - mCellDistance);
|
int mapX = x + (mx - mCellDistance);
|
||||||
int mapY = y + (-1*(my - mCellDistance));
|
int mapY = y + (-1*(my - mCellDistance));
|
||||||
|
|
||||||
MyGUI::ImageBox* box = mMapWidgets[my + mNumCells*mx];
|
MapEntry& entry = mMaps[my + mNumCells*mx];
|
||||||
|
MyGUI::ImageBox* box = entry.mMapWidget;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> texture = mLocalMapRender->getMapTexture(mapX, mapY);
|
osg::ref_ptr<osg::Texture2D> texture = mLocalMapRender->getMapTexture(mapX, mapY);
|
||||||
if (texture)
|
if (texture)
|
||||||
{
|
{
|
||||||
std::shared_ptr<MyGUI::ITexture> guiTex (new osgMyGUI::OSGTexture(texture));
|
entry.mMapTexture.reset(new osgMyGUI::OSGTexture(texture));
|
||||||
textures.push_back(guiTex);
|
box->setRenderItemTexture(entry.mMapTexture.get());
|
||||||
box->setRenderItemTexture(guiTex.get());
|
|
||||||
box->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
|
box->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
box->setRenderItemTexture(nullptr);
|
box->setRenderItemTexture(nullptr);
|
||||||
|
entry.mMapTexture.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mMapTextures.swap(textures);
|
|
||||||
|
|
||||||
// Delay the door markers update until scripts have been given a chance to run.
|
// Delay the door markers update until scripts have been given a chance to run.
|
||||||
// If we don't do this, door markers that should be disabled will still appear on the map.
|
// If we don't do this, door markers that should be disabled will still appear on the map.
|
||||||
|
@ -896,22 +897,13 @@ namespace MWGui
|
||||||
|
|
||||||
void MapWindow::cellExplored(int x, int y)
|
void MapWindow::cellExplored(int x, int y)
|
||||||
{
|
{
|
||||||
mQueuedToExplore.push_back(std::make_pair(x,y));
|
mGlobalMapRender->cleanupCameras();
|
||||||
|
mGlobalMapRender->exploreCell(x, y, mLocalMapRender->getMapTexture(x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapWindow::onFrame(float dt)
|
void MapWindow::onFrame(float dt)
|
||||||
{
|
{
|
||||||
LocalMapBase::onFrame(dt);
|
LocalMapBase::onFrame(dt);
|
||||||
|
|
||||||
mGlobalMapRender->cleanupCameras();
|
|
||||||
|
|
||||||
for (CellId& cellId : mQueuedToExplore)
|
|
||||||
{
|
|
||||||
mGlobalMapRender->exploreCell(cellId.first, cellId.second, mLocalMapRender->getMapTexture(cellId.first, cellId.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
mQueuedToExplore.clear();
|
|
||||||
|
|
||||||
NoDrop::onFrame(dt);
|
NoDrop::onFrame(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1128,8 +1120,8 @@ namespace MWGui
|
||||||
NoDrop::setAlpha(alpha);
|
NoDrop::setAlpha(alpha);
|
||||||
// can't allow showing map with partial transparency, as the fog of war will also go transparent
|
// can't allow showing map with partial transparency, as the fog of war will also go transparent
|
||||||
// and reveal parts of the map you shouldn't be able to see
|
// and reveal parts of the map you shouldn't be able to see
|
||||||
for (MyGUI::ImageBox* widget : mMapWidgets)
|
for (MapEntry& entry : mMaps)
|
||||||
widget->setVisible(alpha == 1);
|
entry.mMapWidget->setVisible(alpha == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapWindow::customMarkerCreated(MyGUI::Widget *marker)
|
void MapWindow::customMarkerCreated(MyGUI::Widget *marker)
|
||||||
|
|
|
@ -148,12 +148,17 @@ namespace MWGui
|
||||||
// Stores markers that were placed by a player. May be shared between multiple map views.
|
// Stores markers that were placed by a player. May be shared between multiple map views.
|
||||||
CustomMarkerCollection& mCustomMarkers;
|
CustomMarkerCollection& mCustomMarkers;
|
||||||
|
|
||||||
std::vector<MyGUI::ImageBox*> mMapWidgets;
|
struct MapEntry
|
||||||
std::vector<MyGUI::ImageBox*> mFogWidgets;
|
{
|
||||||
|
MapEntry(MyGUI::ImageBox* mapWidget, MyGUI::ImageBox* fogWidget)
|
||||||
|
: mMapWidget(mapWidget), mFogWidget(fogWidget) {}
|
||||||
|
|
||||||
typedef std::vector<std::shared_ptr<MyGUI::ITexture> > TextureVector;
|
MyGUI::ImageBox* mMapWidget;
|
||||||
TextureVector mMapTextures;
|
MyGUI::ImageBox* mFogWidget;
|
||||||
TextureVector mFogTextures;
|
std::shared_ptr<MyGUI::ITexture> mMapTexture;
|
||||||
|
std::shared_ptr<MyGUI::ITexture> mFogTexture;
|
||||||
|
};
|
||||||
|
std::vector<MapEntry> mMaps;
|
||||||
|
|
||||||
// Keep track of created marker widgets, just to easily remove them later.
|
// Keep track of created marker widgets, just to easily remove them later.
|
||||||
std::vector<MyGUI::Widget*> mDoorMarkerWidgets;
|
std::vector<MyGUI::Widget*> mDoorMarkerWidgets;
|
||||||
|
@ -333,10 +338,6 @@ namespace MWGui
|
||||||
typedef std::pair<int, int> CellId;
|
typedef std::pair<int, int> CellId;
|
||||||
std::set<CellId> mMarkers;
|
std::set<CellId> mMarkers;
|
||||||
|
|
||||||
// Cells that should be explored in the next frame (i.e. their map revealed on the global map)
|
|
||||||
// We can't do this immediately, because the map update is not immediate either (see mNeedMapUpdate in scene.cpp)
|
|
||||||
std::vector<CellId> mQueuedToExplore;
|
|
||||||
|
|
||||||
MyGUI::Button* mEventBoxGlobal;
|
MyGUI::Button* mEventBoxGlobal;
|
||||||
MyGUI::Button* mEventBoxLocal;
|
MyGUI::Button* mEventBoxLocal;
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,8 @@ namespace MWGui
|
||||||
|
|
||||||
MyGUI::ScrollBar* scroll = current->castType<MyGUI::ScrollBar>();
|
MyGUI::ScrollBar* scroll = current->castType<MyGUI::ScrollBar>();
|
||||||
std::string valueStr;
|
std::string valueStr;
|
||||||
if (getSettingValueType(current) == "Float")
|
std::string valueType = getSettingValueType(current);
|
||||||
|
if (valueType == "Float" || valueType == "Integer")
|
||||||
{
|
{
|
||||||
// TODO: ScrollBar isn't meant for this. should probably use a dedicated FloatSlider widget
|
// TODO: ScrollBar isn't meant for this. should probably use a dedicated FloatSlider widget
|
||||||
float min,max;
|
float min,max;
|
||||||
|
@ -438,14 +439,18 @@ namespace MWGui
|
||||||
if (getSettingType(scroller) == "Slider")
|
if (getSettingType(scroller) == "Slider")
|
||||||
{
|
{
|
||||||
std::string valueStr;
|
std::string valueStr;
|
||||||
if (getSettingValueType(scroller) == "Float")
|
std::string valueType = getSettingValueType(scroller);
|
||||||
|
if (valueType == "Float" || valueType == "Integer")
|
||||||
{
|
{
|
||||||
float value = pos / float(scroller->getScrollRange()-1);
|
float value = pos / float(scroller->getScrollRange()-1);
|
||||||
|
|
||||||
float min,max;
|
float min,max;
|
||||||
getSettingMinMax(scroller, min, max);
|
getSettingMinMax(scroller, min, max);
|
||||||
value = min + (max-min) * value;
|
value = min + (max-min) * value;
|
||||||
|
if (valueType == "Float")
|
||||||
Settings::Manager::setFloat(getSettingName(scroller), getSettingCategory(scroller), value);
|
Settings::Manager::setFloat(getSettingName(scroller), getSettingCategory(scroller), value);
|
||||||
|
else
|
||||||
|
Settings::Manager::setInt(getSettingName(scroller), getSettingCategory(scroller), (int)value);
|
||||||
valueStr = MyGUI::utility::toString(int(value));
|
valueStr = MyGUI::utility::toString(int(value));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -79,14 +79,11 @@ namespace MWGui
|
||||||
static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fMagicStartIconBlink")->mValue.getFloat();
|
static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fMagicStartIconBlink")->mValue.getFloat();
|
||||||
|
|
||||||
std::vector<MagicEffectInfo>& effectInfos = effectInfoPair.second;
|
std::vector<MagicEffectInfo>& effectInfos = effectInfoPair.second;
|
||||||
bool addNewLine = true;
|
bool addNewLine = false;
|
||||||
for (const MagicEffectInfo& effectInfo : effectInfos)
|
for (const MagicEffectInfo& effectInfo : effectInfos)
|
||||||
{
|
{
|
||||||
if (addNewLine)
|
if (addNewLine)
|
||||||
{
|
|
||||||
sourcesDescription += "\n";
|
sourcesDescription += "\n";
|
||||||
addNewLine = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if at least one of the effect sources is permanent, the effect will never wear off
|
// if at least one of the effect sources is permanent, the effect will never wear off
|
||||||
if (effectInfo.mPermanent)
|
if (effectInfo.mPermanent)
|
||||||
|
@ -161,6 +158,8 @@ namespace MWGui
|
||||||
sourcesDescription += MWGui::ToolTips::toString(duration) + "s";
|
sourcesDescription += MWGui::ToolTips::toString(duration) + "s";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addNewLine = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remainingDuration > 0.f)
|
if (remainingDuration > 0.f)
|
||||||
|
|
|
@ -169,8 +169,7 @@ namespace MWGui
|
||||||
npcStats.setGoldPool(npcStats.getGoldPool() + price);
|
npcStats.setGoldPool(npcStats.getGoldPool() + price);
|
||||||
|
|
||||||
// advance time
|
// advance time
|
||||||
MWBase::Environment::get().getMechanicsManager()->rest(false);
|
MWBase::Environment::get().getMechanicsManager()->rest(2, false);
|
||||||
MWBase::Environment::get().getMechanicsManager()->rest(false);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp change (major)
|
Start of tes3mp change (major)
|
||||||
|
|
|
@ -174,10 +174,7 @@ namespace MWGui
|
||||||
ESM::Position playerPos = player.getRefData().getPosition();
|
ESM::Position playerPos = player.getRefData().getPosition();
|
||||||
float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length();
|
float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length();
|
||||||
int hours = static_cast<int>(d /MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fTravelTimeMult")->mValue.getFloat());
|
int hours = static_cast<int>(d /MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fTravelTimeMult")->mValue.getFloat());
|
||||||
for(int i = 0;i < hours;i++)
|
MWBase::Environment::get().getMechanicsManager()->rest(hours, true);
|
||||||
{
|
|
||||||
MWBase::Environment::get().getMechanicsManager ()->rest (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp change (major)
|
Start of tes3mp change (major)
|
||||||
|
|
|
@ -271,7 +271,7 @@ namespace MWGui
|
||||||
void WaitDialog::onWaitingProgressChanged(int cur, int total)
|
void WaitDialog::onWaitingProgressChanged(int cur, int total)
|
||||||
{
|
{
|
||||||
mProgressBar.setProgress(cur, total);
|
mProgressBar.setProgress(cur, total);
|
||||||
MWBase::Environment::get().getMechanicsManager()->rest(mSleeping);
|
MWBase::Environment::get().getMechanicsManager()->rest(1, mSleeping);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp change (major)
|
Start of tes3mp change (major)
|
||||||
|
|
|
@ -1070,9 +1070,6 @@ namespace MWGui
|
||||||
|
|
||||||
updateMap();
|
updateMap();
|
||||||
|
|
||||||
if (!mMap->isVisible())
|
|
||||||
mMap->onFrame(frameDuration);
|
|
||||||
|
|
||||||
mHud->onFrame(frameDuration);
|
mHud->onFrame(frameDuration);
|
||||||
|
|
||||||
mDebugWindow->onFrame(frameDuration);
|
mDebugWindow->onFrame(frameDuration);
|
||||||
|
|
|
@ -208,8 +208,6 @@ namespace MWInput
|
||||||
|
|
||||||
void InputManager::handleGuiArrowKey(int action)
|
void InputManager::handleGuiArrowKey(int action)
|
||||||
{
|
{
|
||||||
// Temporary shut-down of this function until deemed necessary.
|
|
||||||
return;
|
|
||||||
if (SDL_IsTextInputActive())
|
if (SDL_IsTextInputActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -414,7 +412,8 @@ namespace MWInput
|
||||||
case A_MoveRight:
|
case A_MoveRight:
|
||||||
case A_MoveForward:
|
case A_MoveForward:
|
||||||
case A_MoveBackward:
|
case A_MoveBackward:
|
||||||
handleGuiArrowKey(action);
|
// Temporary shut-down of this function until deemed necessary.
|
||||||
|
//handleGuiArrowKey(action);
|
||||||
break;
|
break;
|
||||||
case A_Journal:
|
case A_Journal:
|
||||||
toggleJournal ();
|
toggleJournal ();
|
||||||
|
@ -1905,6 +1904,17 @@ namespace MWInput
|
||||||
MWBase::Environment::get().getWindowManager ()->notifyInputActionBound ();
|
MWBase::Environment::get().getWindowManager ()->notifyInputActionBound ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disallow binding reserved keys
|
||||||
|
if (key == SDL_SCANCODE_F3 || key == SDL_SCANCODE_F4 || key == SDL_SCANCODE_F10 || key == SDL_SCANCODE_F11)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifndef __APPLE__
|
||||||
|
// Disallow binding Windows/Meta keys
|
||||||
|
if (key == SDL_SCANCODE_LGUI || key == SDL_SCANCODE_RGUI)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
if(!mDetectingKeyboard)
|
if(!mDetectingKeyboard)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -147,23 +147,45 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
|
||||||
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
|
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
|
||||||
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
|
||||||
bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).getMagnitude() > 0;
|
|
||||||
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
|
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
|
||||||
|
|
||||||
health = 0.1f * endurance;
|
health = 0.1f * endurance;
|
||||||
|
|
||||||
magicka = 0;
|
|
||||||
if (!stunted)
|
|
||||||
{
|
|
||||||
float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat ();
|
float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat ();
|
||||||
magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
|
magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
float mRemainingTime;
|
||||||
|
|
||||||
|
GetStuntedMagickaDuration(const MWWorld::Ptr& actor)
|
||||||
|
: mRemainingTime(0.f){}
|
||||||
|
|
||||||
|
virtual void visit (MWMechanics::EffectKey key,
|
||||||
|
const std::string& sourceName, const std::string& sourceId, int casterActorId,
|
||||||
|
float magnitude, float remainingTime = -1, float totalTime = -1)
|
||||||
|
{
|
||||||
|
if (mRemainingTime == -1) return;
|
||||||
|
|
||||||
|
if (key.mId == ESM::MagicEffect::StuntedMagicka)
|
||||||
|
{
|
||||||
|
if (totalTime == -1)
|
||||||
|
{
|
||||||
|
mRemainingTime = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingTime > mRemainingTime)
|
||||||
|
mRemainingTime = remainingTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class SoulTrap : public MWMechanics::EffectSourceVisitor
|
class SoulTrap : public MWMechanics::EffectSourceVisitor
|
||||||
{
|
{
|
||||||
MWWorld::Ptr mCreature;
|
MWWorld::Ptr mCreature;
|
||||||
|
@ -611,7 +633,7 @@ namespace MWMechanics
|
||||||
creatureStats.setMagicka(magicka);
|
creatureStats.setMagicka(magicka);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, bool sleep)
|
void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, double hours, bool sleep)
|
||||||
{
|
{
|
||||||
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
|
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
|
||||||
if (stats.isDead())
|
if (stats.isDead())
|
||||||
|
@ -625,13 +647,37 @@ namespace MWMechanics
|
||||||
getRestorationPerHourOfSleep(ptr, health, magicka);
|
getRestorationPerHourOfSleep(ptr, health, magicka);
|
||||||
|
|
||||||
DynamicStat<float> stat = stats.getHealth();
|
DynamicStat<float> stat = stats.getHealth();
|
||||||
stat.setCurrent(stat.getCurrent() + health);
|
stat.setCurrent(stat.getCurrent() + health * hours);
|
||||||
stats.setHealth(stat);
|
stats.setHealth(stat);
|
||||||
|
|
||||||
|
double restoreHours = hours;
|
||||||
|
bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).getMagnitude() > 0;
|
||||||
|
if (stunted)
|
||||||
|
{
|
||||||
|
// Stunted Magicka effect should be taken into account.
|
||||||
|
GetStuntedMagickaDuration visitor(ptr);
|
||||||
|
stats.getActiveSpells().visitEffectSources(visitor);
|
||||||
|
stats.getSpells().visitEffectSources(visitor);
|
||||||
|
if (ptr.getClass().hasInventoryStore(ptr))
|
||||||
|
ptr.getClass().getInventoryStore(ptr).visitEffectSources(visitor);
|
||||||
|
|
||||||
|
// Take a maximum remaining duration of Stunted Magicka effects (-1 is a constant one) in game hours.
|
||||||
|
if (visitor.mRemainingTime > 0)
|
||||||
|
{
|
||||||
|
double timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
||||||
|
restoreHours = std::max(0.0, hours - visitor.mRemainingTime * timeScale / 3600.f);
|
||||||
|
}
|
||||||
|
else if (visitor.mRemainingTime == -1)
|
||||||
|
restoreHours = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restoreHours > 0)
|
||||||
|
{
|
||||||
stat = stats.getMagicka();
|
stat = stats.getMagicka();
|
||||||
stat.setCurrent(stat.getCurrent() + magicka);
|
stat.setCurrent(stat.getCurrent() + magicka * restoreHours);
|
||||||
stats.setMagicka(stat);
|
stats.setMagicka(stat);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Current fatigue can be above base value due to a fortify effect.
|
// Current fatigue can be above base value due to a fortify effect.
|
||||||
// In that case stop here and don't try to restore.
|
// In that case stop here and don't try to restore.
|
||||||
|
@ -653,7 +699,7 @@ namespace MWMechanics
|
||||||
float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance);
|
float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance);
|
||||||
x *= fEndFatigueMult * endurance;
|
x *= fEndFatigueMult * endurance;
|
||||||
|
|
||||||
fatigue.setCurrent (fatigue.getCurrent() + 3600 * x);
|
fatigue.setCurrent (fatigue.getCurrent() + 3600 * x * hours);
|
||||||
stats.setFatigue (fatigue);
|
stats.setFatigue (fatigue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1925,9 +1971,9 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actors::rest(bool sleep)
|
void Actors::rest(double hours, bool sleep)
|
||||||
{
|
{
|
||||||
float duration = 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
float duration = hours * 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
||||||
const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||||
const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
|
const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
|
||||||
|
|
||||||
|
@ -1937,7 +1983,7 @@ namespace MWMechanics
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!sleep || iter->first == player)
|
if (!sleep || iter->first == player)
|
||||||
restoreDynamicStats(iter->first, sleep);
|
restoreDynamicStats(iter->first, hours, sleep);
|
||||||
|
|
||||||
if ((!iter->first.getRefData().getBaseNode()) ||
|
if ((!iter->first.getRefData().getBaseNode()) ||
|
||||||
(playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > mActorsProcessingRange*mActorsProcessingRange)
|
(playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > mActorsProcessingRange*mActorsProcessingRange)
|
||||||
|
@ -2049,11 +2095,12 @@ namespace MWMechanics
|
||||||
getRestorationPerHourOfSleep(ptr, healthPerHour, magickaPerHour);
|
getRestorationPerHourOfSleep(ptr, healthPerHour, magickaPerHour);
|
||||||
|
|
||||||
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
||||||
|
bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).getMagnitude() > 0;
|
||||||
|
|
||||||
float healthHours = healthPerHour > 0
|
float healthHours = healthPerHour > 0
|
||||||
? (stats.getHealth().getModified() - stats.getHealth().getCurrent()) / healthPerHour
|
? (stats.getHealth().getModified() - stats.getHealth().getCurrent()) / healthPerHour
|
||||||
: 1.0f;
|
: 1.0f;
|
||||||
float magickaHours = magickaPerHour > 0
|
float magickaHours = magickaPerHour > 0 && !stunted
|
||||||
? (stats.getMagicka().getModified() - stats.getMagicka().getCurrent()) / magickaPerHour
|
? (stats.getMagicka().getModified() - stats.getMagicka().getCurrent()) / magickaPerHour
|
||||||
: 1.0f;
|
: 1.0f;
|
||||||
|
|
||||||
|
|
|
@ -121,13 +121,13 @@ namespace MWMechanics
|
||||||
void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
|
void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
|
||||||
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance);
|
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance);
|
||||||
|
|
||||||
void rest(bool sleep);
|
void rest(double hours, bool sleep);
|
||||||
///< Update actors while the player is waiting or sleeping. This should be called every hour.
|
///< Update actors while the player is waiting or sleeping.
|
||||||
|
|
||||||
void updateSneaking(CharacterController* ctrl, float duration);
|
void updateSneaking(CharacterController* ctrl, float duration);
|
||||||
///< Update the sneaking indicator state according to the given player character controller.
|
///< Update the sneaking indicator state according to the given player character controller.
|
||||||
|
|
||||||
void restoreDynamicStats(const MWWorld::Ptr& actor, bool sleep);
|
void restoreDynamicStats(const MWWorld::Ptr& actor, double hours, bool sleep);
|
||||||
|
|
||||||
int getHoursToRest(const MWWorld::Ptr& ptr) const;
|
int getHoursToRest(const MWWorld::Ptr& ptr) const;
|
||||||
///< Calculate how many hours the given actor needs to rest in order to be fully healed
|
///< Calculate how many hours the given actor needs to rest in order to be fully healed
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
|
|
||||||
|
#include "../mwphysics/collisiontype.hpp"
|
||||||
|
|
||||||
#include "pathgrid.hpp"
|
#include "pathgrid.hpp"
|
||||||
#include "creaturestats.hpp"
|
#include "creaturestats.hpp"
|
||||||
#include "steering.hpp"
|
#include "steering.hpp"
|
||||||
|
@ -45,6 +47,16 @@ namespace MWMechanics
|
||||||
std::string("idle9"),
|
std::string("idle9"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
inline int getCountBeforeReset(const MWWorld::ConstPtr& actor)
|
||||||
|
{
|
||||||
|
if (actor.getClass().isPureWaterCreature(actor) || actor.getClass().isPureFlyingCreature(actor))
|
||||||
|
return 1;
|
||||||
|
return COUNT_BEFORE_RESET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
||||||
mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle),
|
mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle),
|
||||||
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)),
|
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)),
|
||||||
|
@ -298,7 +310,8 @@ namespace MWMechanics
|
||||||
const auto currentPosition = actor.getRefData().getPosition().asVec3();
|
const auto currentPosition = actor.getRefData().getPosition().asVec3();
|
||||||
|
|
||||||
std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here
|
std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here
|
||||||
bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
|
const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
|
||||||
|
const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);
|
||||||
do {
|
do {
|
||||||
// Determine a random location within radius of original position
|
// Determine a random location within radius of original position
|
||||||
const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance;
|
const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance;
|
||||||
|
@ -309,13 +322,22 @@ namespace MWMechanics
|
||||||
mDestination = osg::Vec3f(destinationX, destinationY, destinationZ);
|
mDestination = osg::Vec3f(destinationX, destinationY, destinationZ);
|
||||||
|
|
||||||
// Check if land creature will walk onto water or if water creature will swim onto land
|
// Check if land creature will walk onto water or if water creature will swim onto land
|
||||||
if ((!isWaterCreature && !destinationIsAtWater(actor, mDestination)) ||
|
if (!isWaterCreature && destinationIsAtWater(actor, mDestination))
|
||||||
(isWaterCreature && !destinationThroughGround(currentPosition, mDestination)))
|
continue;
|
||||||
|
|
||||||
|
if ((isWaterCreature || isFlyingCreature) && destinationThroughGround(currentPosition, mDestination))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (isWaterCreature || isFlyingCreature)
|
||||||
|
{
|
||||||
|
mPathFinder.buildStraightPath(mDestination);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
|
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
|
||||||
mPathFinder.buildPath(actor, currentPosition, mDestination, actor.getCell(),
|
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents,
|
||||||
getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor));
|
getNavigatorFlags(actor));
|
||||||
mPathFinder.addPointToPath(mDestination);
|
}
|
||||||
|
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
|
@ -323,8 +345,8 @@ namespace MWMechanics
|
||||||
mHasDestination = true;
|
mHasDestination = true;
|
||||||
mUsePathgrid = false;
|
mUsePathgrid = false;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
break;
|
||||||
} while (--attempts);
|
} while (--attempts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,8 +364,10 @@ namespace MWMechanics
|
||||||
* Returns true if the start to end point travels through a collision point (land).
|
* Returns true if the start to end point travels through a collision point (land).
|
||||||
*/
|
*/
|
||||||
bool AiWander::destinationThroughGround(const osg::Vec3f& startPoint, const osg::Vec3f& destination) {
|
bool AiWander::destinationThroughGround(const osg::Vec3f& startPoint, const osg::Vec3f& destination) {
|
||||||
|
const int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door;
|
||||||
return MWBase::Environment::get().getWorld()->castRay(startPoint.x(), startPoint.y(), startPoint.z(),
|
return MWBase::Environment::get().getWorld()->castRay(startPoint.x(), startPoint.y(), startPoint.z(),
|
||||||
destination.x(), destination.y(), destination.z());
|
destination.x(), destination.y(), destination.z(),
|
||||||
|
mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {
|
void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {
|
||||||
|
@ -479,7 +503,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
// if stuck for sufficiently long, act like current location was the destination
|
// if stuck for sufficiently long, act like current location was the destination
|
||||||
if (storage.mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset
|
if (storage.mStuckCount >= getCountBeforeReset(actor)) // something has gone wrong, reset
|
||||||
{
|
{
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
stopWalking(actor, storage);
|
stopWalking(actor, storage);
|
||||||
|
|
|
@ -1719,23 +1719,25 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
|
||||||
/*
|
/*
|
||||||
End of tes3mp change (major)
|
End of tes3mp change (major)
|
||||||
*/
|
*/
|
||||||
{
|
|
||||||
if (isWeapon)
|
|
||||||
{
|
{
|
||||||
if (Settings::Manager::getBool("best attack", "Game"))
|
if (Settings::Manager::getBool("best attack", "Game"))
|
||||||
|
{
|
||||||
|
if (isWeapon)
|
||||||
{
|
{
|
||||||
MWWorld::ConstContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
MWWorld::ConstContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||||
mAttackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase);
|
mAttackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
setAttackTypeBasedOnMovement();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// There is no "best attack" for Hand-to-Hand
|
// There is no "best attack" for Hand-to-Hand
|
||||||
setAttackTypeRandomly(mAttackType);
|
setAttackTypeRandomly(mAttackType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setAttackTypeBasedOnMovement();
|
||||||
|
}
|
||||||
|
}
|
||||||
// else if (mPtr != getPlayer()) use mAttackType set by AiCombat
|
// else if (mPtr != getPlayer()) use mAttackType set by AiCombat
|
||||||
startKey = mAttackType+" start";
|
startKey = mAttackType+" start";
|
||||||
stopKey = mAttackType+" min attack";
|
stopKey = mAttackType+" min attack";
|
||||||
|
@ -2697,6 +2699,13 @@ void CharacterController::forceStateUpdate()
|
||||||
return;
|
return;
|
||||||
clearAnimQueue();
|
clearAnimQueue();
|
||||||
|
|
||||||
|
// Make sure we canceled the current attack or spellcasting,
|
||||||
|
// because we disabled attack animations anyway.
|
||||||
|
mCastingManualSpell = false;
|
||||||
|
mAttackingOrSpell = false;
|
||||||
|
if (mUpperBodyState != UpperCharState_Nothing)
|
||||||
|
mUpperBodyState = UpperCharState_WeapEquiped;
|
||||||
|
|
||||||
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
||||||
|
|
||||||
if(mDeathState != CharState_None)
|
if(mDeathState != CharState_None)
|
||||||
|
|
|
@ -303,7 +303,9 @@ namespace MWMechanics
|
||||||
void MechanicsManager::advanceTime (float duration)
|
void MechanicsManager::advanceTime (float duration)
|
||||||
{
|
{
|
||||||
// Uses ingame time, but scaled to real time
|
// Uses ingame time, but scaled to real time
|
||||||
duration /= MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
const float timeScaleFactor = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
||||||
|
if (timeScaleFactor != 0.0f)
|
||||||
|
duration /= timeScaleFactor;
|
||||||
MWWorld::Ptr player = getPlayer();
|
MWWorld::Ptr player = getPlayer();
|
||||||
player.getClass().getInventoryStore(player).rechargeItems(duration);
|
player.getClass().getInventoryStore(player).rechargeItems(duration);
|
||||||
}
|
}
|
||||||
|
@ -491,17 +493,17 @@ namespace MWMechanics
|
||||||
return mActors.isSneaking(ptr);
|
return mActors.isSneaking(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MechanicsManager::rest(bool sleep)
|
void MechanicsManager::rest(double hours, bool sleep)
|
||||||
{
|
{
|
||||||
if (sleep)
|
if (sleep)
|
||||||
MWBase::Environment::get().getWorld()->rest();
|
MWBase::Environment::get().getWorld()->rest(hours);
|
||||||
|
|
||||||
mActors.rest(sleep);
|
mActors.rest(hours, sleep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MechanicsManager::restoreDynamicStats(MWWorld::Ptr actor, bool sleep)
|
void MechanicsManager::restoreDynamicStats(MWWorld::Ptr actor, double hours, bool sleep)
|
||||||
{
|
{
|
||||||
mActors.restoreDynamicStats(actor, sleep);
|
mActors.restoreDynamicStats(actor, hours, sleep);
|
||||||
}
|
}
|
||||||
|
|
||||||
int MechanicsManager::getHoursToRest() const
|
int MechanicsManager::getHoursToRest() const
|
||||||
|
|
|
@ -95,9 +95,9 @@ namespace MWMechanics
|
||||||
virtual void setPlayerClass (const ESM::Class& class_) override;
|
virtual void setPlayerClass (const ESM::Class& class_) override;
|
||||||
///< Set player class to custom class.
|
///< Set player class to custom class.
|
||||||
|
|
||||||
virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep) override;
|
virtual void restoreDynamicStats(MWWorld::Ptr actor, double hours, bool sleep) override;
|
||||||
|
|
||||||
virtual void rest(bool sleep) override;
|
virtual void rest(double hours, bool sleep) override;
|
||||||
///< If the player is sleeping or waiting, this should be called every hour.
|
///< If the player is sleeping or waiting, this should be called every hour.
|
||||||
/// @param sleep is the player sleeping or waiting?
|
/// @param sleep is the player sleeping or waiting?
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
|
|
||||||
|
@ -121,17 +123,19 @@ namespace MWMechanics
|
||||||
* u = how long to move sideways
|
* u = how long to move sideways
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration, float scaleMinimumDistance)
|
void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration)
|
||||||
{
|
{
|
||||||
const MWWorld::Class& cls = actor.getClass();
|
const ESM::Position pos = actor.getRefData().getPosition();
|
||||||
ESM::Position pos = actor.getRefData().getPosition();
|
|
||||||
|
|
||||||
if(mDistSameSpot == -1)
|
if (mDistSameSpot == -1)
|
||||||
mDistSameSpot = DIST_SAME_SPOT * cls.getSpeed(actor) * scaleMinimumDistance;
|
{
|
||||||
|
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
|
||||||
|
mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor) + 1.2 * std::max(halfExtents.x(), halfExtents.y());
|
||||||
|
}
|
||||||
|
|
||||||
float distSameSpot = mDistSameSpot * duration;
|
const float distSameSpot = mDistSameSpot * duration;
|
||||||
|
const float squaredMovedDistance = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2();
|
||||||
bool samePosition = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2() < distSameSpot * distSameSpot;
|
const bool samePosition = squaredMovedDistance < distSameSpot * distSameSpot;
|
||||||
|
|
||||||
// update position
|
// update position
|
||||||
mPrevX = pos.pos[0];
|
mPrevX = pos.pos[0];
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace MWMechanics
|
||||||
bool isEvading() const;
|
bool isEvading() const;
|
||||||
|
|
||||||
// Updates internal state, call each frame for moving actor
|
// Updates internal state, call each frame for moving actor
|
||||||
void update(const MWWorld::Ptr& actor, float duration, float scaleMinimumDistance = 1.0f);
|
void update(const MWWorld::Ptr& actor, float duration);
|
||||||
|
|
||||||
// change direction to try to fix "stuck" actor
|
// change direction to try to fix "stuck" actor
|
||||||
void takeEvasiveAction(MWMechanics::Movement& actorMovement) const;
|
void takeEvasiveAction(MWMechanics::Movement& actorMovement) const;
|
||||||
|
|
|
@ -269,6 +269,13 @@ namespace MWMechanics
|
||||||
mPath.pop_front();
|
mPath.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PathFinder::buildStraightPath(const osg::Vec3f& endPoint)
|
||||||
|
{
|
||||||
|
mPath.clear();
|
||||||
|
mPath.push_back(endPoint);
|
||||||
|
mConstructed = true;
|
||||||
|
}
|
||||||
|
|
||||||
void PathFinder::buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
void PathFinder::buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
||||||
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph)
|
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph)
|
||||||
{
|
{
|
||||||
|
@ -280,6 +287,16 @@ namespace MWMechanics
|
||||||
mConstructed = true;
|
mConstructed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
|
||||||
|
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags)
|
||||||
|
{
|
||||||
|
mPath.clear();
|
||||||
|
|
||||||
|
buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath));
|
||||||
|
|
||||||
|
mConstructed = true;
|
||||||
|
}
|
||||||
|
|
||||||
void PathFinder::buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
void PathFinder::buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
||||||
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
||||||
const DetourNavigator::Flags flags)
|
const DetourNavigator::Flags flags)
|
||||||
|
|
|
@ -72,9 +72,14 @@ namespace MWMechanics
|
||||||
mCell = nullptr;
|
mCell = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void buildStraightPath(const osg::Vec3f& endPoint);
|
||||||
|
|
||||||
void buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
void buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
||||||
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);
|
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);
|
||||||
|
|
||||||
|
void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
|
||||||
|
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags);
|
||||||
|
|
||||||
void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
||||||
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
||||||
const DetourNavigator::Flags flags);
|
const DetourNavigator::Flags flags);
|
||||||
|
|
|
@ -170,7 +170,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
float rating = rateEffects(enchantment->mEffects, actor, enemy);
|
float rating = rateEffects(enchantment->mEffects, actor, enemy);
|
||||||
|
|
||||||
rating *= 2; // prefer rechargable magic items over spells
|
rating *= 1.25f; // prefer rechargable magic items over spells
|
||||||
return rating;
|
return rating;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,9 @@ namespace MWMechanics
|
||||||
value = ref->mBase->mData.mCombat;
|
value = ref->mBase->mData.mCombat;
|
||||||
}
|
}
|
||||||
|
|
||||||
rating *= getHitChance(actor, enemy, value) / 100.f;
|
// Take hit chance in account, but do not allow rating become negative.
|
||||||
|
float chance = getHitChance(actor, enemy, value) / 100.f;
|
||||||
|
rating *= std::min(1.f, std::max(0.01f, chance));
|
||||||
|
|
||||||
if (weapon->mData.mType < ESM::Weapon::Arrow)
|
if (weapon->mData.mType < ESM::Weapon::Arrow)
|
||||||
rating *= weapon->mData.mSpeed;
|
rating *= weapon->mData.mSpeed;
|
||||||
|
|
|
@ -242,6 +242,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
|
||||||
{
|
{
|
||||||
osg::Vec4f glowColor = getEnchantmentColor(*weapon);
|
osg::Vec4f glowColor = getEnchantmentColor(*weapon);
|
||||||
mScabbard = getWeaponPart(mesh, boneName, isEnchanted, &glowColor);
|
mScabbard = getWeaponPart(mesh, boneName, isEnchanted, &glowColor);
|
||||||
|
if (mScabbard)
|
||||||
resetControllers(mScabbard->getNode());
|
resetControllers(mScabbard->getNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1798,17 +1798,13 @@ namespace MWRender
|
||||||
material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,alpha));
|
material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,alpha));
|
||||||
material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
|
material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
|
||||||
stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||||
|
stateset->addUniform(new osg::Uniform("colorMode", 0), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||||
mObjectRoot->setStateSet(stateset);
|
mObjectRoot->setStateSet(stateset);
|
||||||
|
|
||||||
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mObjectRoot->setStateSet(nullptr);
|
mObjectRoot->setStateSet(nullptr);
|
||||||
|
|
||||||
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setRenderBin();
|
setRenderBin();
|
||||||
|
|
|
@ -329,7 +329,7 @@ namespace MWRender
|
||||||
camera->setViewMatrix(osg::Matrix::identity());
|
camera->setViewMatrix(osg::Matrix::identity());
|
||||||
camera->setProjectionMatrix(osg::Matrix::identity());
|
camera->setProjectionMatrix(osg::Matrix::identity());
|
||||||
camera->setProjectionResizePolicy(osg::Camera::FIXED);
|
camera->setProjectionResizePolicy(osg::Camera::FIXED);
|
||||||
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
camera->setRenderOrder(osg::Camera::PRE_RENDER, 1); // Make sure the global map is rendered after the local map
|
||||||
y = mHeight - y - height; // convert top-left origin to bottom-left
|
y = mHeight - y - height; // convert top-left origin to bottom-left
|
||||||
camera->setViewport(x, y, width, height);
|
camera->setViewport(x, y, width, height);
|
||||||
|
|
||||||
|
|
|
@ -12,16 +12,15 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
|
|
||||||
LandManager::LandManager(int loadFlags)
|
LandManager::LandManager(int loadFlags)
|
||||||
: ResourceManager(nullptr)
|
: GenericResourceManager<std::pair<int, int> >(nullptr)
|
||||||
, mLoadFlags(loadFlags)
|
, mLoadFlags(loadFlags)
|
||||||
{
|
{
|
||||||
|
mCache = new CacheType;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<ESMTerrain::LandObject> LandManager::getLand(int x, int y)
|
osg::ref_ptr<ESMTerrain::LandObject> LandManager::getLand(int x, int y)
|
||||||
{
|
{
|
||||||
std::string idstr = std::to_string(x) + " " + std::to_string(y);
|
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(std::make_pair(x,y));
|
||||||
|
|
||||||
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(idstr);
|
|
||||||
if (obj)
|
if (obj)
|
||||||
return static_cast<ESMTerrain::LandObject*>(obj.get());
|
return static_cast<ESMTerrain::LandObject*>(obj.get());
|
||||||
else
|
else
|
||||||
|
@ -30,7 +29,7 @@ osg::ref_ptr<ESMTerrain::LandObject> LandManager::getLand(int x, int y)
|
||||||
if (!land)
|
if (!land)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
osg::ref_ptr<ESMTerrain::LandObject> landObj (new ESMTerrain::LandObject(land, mLoadFlags));
|
osg::ref_ptr<ESMTerrain::LandObject> landObj (new ESMTerrain::LandObject(land, mLoadFlags));
|
||||||
mCache->addEntryToObjectCache(idstr, landObj.get());
|
mCache->addEntryToObjectCache(std::make_pair(x,y), landObj.get());
|
||||||
return landObj;
|
return landObj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <osg/Object>
|
#include <osg/Object>
|
||||||
|
|
||||||
|
#include <components/resource/objectcache.hpp>
|
||||||
#include <components/resource/resourcemanager.hpp>
|
#include <components/resource/resourcemanager.hpp>
|
||||||
#include <components/esmterrain/storage.hpp>
|
#include <components/esmterrain/storage.hpp>
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ namespace ESM
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
|
||||||
class LandManager : public Resource::ResourceManager
|
class LandManager : public Resource::GenericResourceManager<std::pair<int, int> >
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LandManager(int loadFlags);
|
LandManager(int loadFlags);
|
||||||
|
|
|
@ -613,7 +613,8 @@ void LocalMap::updatePlayer (const osg::Vec3f& position, const osg::Quat& orient
|
||||||
if (!segment.mFogOfWarImage || !segment.mMapTexture)
|
if (!segment.mFogOfWarImage || !segment.mMapTexture)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
unsigned char* data = segment.mFogOfWarImage->data();
|
uint32_t* data = (uint32_t*)segment.mFogOfWarImage->data();
|
||||||
|
bool changed = false;
|
||||||
for (int texV = 0; texV<sFogOfWarResolution; ++texV)
|
for (int texV = 0; texV<sFogOfWarResolution; ++texV)
|
||||||
{
|
{
|
||||||
for (int texU = 0; texU<sFogOfWarResolution; ++texU)
|
for (int texU = 0; texU<sFogOfWarResolution; ++texU)
|
||||||
|
@ -625,16 +626,24 @@ void LocalMap::updatePlayer (const osg::Vec3f& position, const osg::Quat& orient
|
||||||
uint8_t alpha = (clr >> 24);
|
uint8_t alpha = (clr >> 24);
|
||||||
|
|
||||||
alpha = std::min( alpha, (uint8_t) (std::max(0.f, std::min(1.f, (sqrDist/sqrExploreRadius)))*255) );
|
alpha = std::min( alpha, (uint8_t) (std::max(0.f, std::min(1.f, (sqrDist/sqrExploreRadius)))*255) );
|
||||||
*(uint32_t*)data = (uint32_t) (alpha << 24);
|
uint32_t val = (uint32_t) (alpha << 24);
|
||||||
|
if ( *data != val)
|
||||||
|
{
|
||||||
|
*data = val;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
data += 4;
|
++data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
segment.mHasFogState = true;
|
segment.mHasFogState = true;
|
||||||
segment.mFogOfWarImage->dirty();
|
segment.mFogOfWarImage->dirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalMap::MapSegment::MapSegment()
|
LocalMap::MapSegment::MapSegment()
|
||||||
|
|
|
@ -54,10 +54,8 @@ std::string getVampireHead(const std::string& race, bool female)
|
||||||
if (sVampireMapping.find(thisCombination) == sVampireMapping.end())
|
if (sVampireMapping.find(thisCombination) == sVampireMapping.end())
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
|
for (const ESM::BodyPart& bodypart : store.get<ESM::BodyPart>())
|
||||||
for(MWWorld::Store<ESM::BodyPart>::iterator it = partStore.begin(); it != partStore.end(); ++it)
|
|
||||||
{
|
{
|
||||||
const ESM::BodyPart& bodypart = *it;
|
|
||||||
if (!bodypart.mData.mVampire)
|
if (!bodypart.mData.mVampire)
|
||||||
continue;
|
continue;
|
||||||
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
|
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
|
||||||
|
@ -68,12 +66,11 @@ std::string getVampireHead(const std::string& race, bool female)
|
||||||
continue;
|
continue;
|
||||||
if (!Misc::StringUtils::ciEqual(bodypart.mRace, race))
|
if (!Misc::StringUtils::ciEqual(bodypart.mRace, race))
|
||||||
continue;
|
continue;
|
||||||
sVampireMapping[thisCombination] = &*it;
|
sVampireMapping[thisCombination] = &bodypart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sVampireMapping.find(thisCombination) == sVampireMapping.end())
|
sVampireMapping.emplace(thisCombination, nullptr);
|
||||||
sVampireMapping[thisCombination] = nullptr;
|
|
||||||
|
|
||||||
const ESM::BodyPart* bodyPart = sVampireMapping[thisCombination];
|
const ESM::BodyPart* bodyPart = sVampireMapping[thisCombination];
|
||||||
if (!bodyPart)
|
if (!bodyPart)
|
||||||
|
@ -238,6 +235,18 @@ void HeadAnimationTime::setBlinkStop(float value)
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
NpcAnimation::NpcType NpcAnimation::getNpcType()
|
||||||
|
{
|
||||||
|
const MWWorld::Class &cls = mPtr.getClass();
|
||||||
|
NpcAnimation::NpcType curType = Type_Normal;
|
||||||
|
if (cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0)
|
||||||
|
curType = Type_Vampire;
|
||||||
|
if (cls.getNpcStats(mPtr).isWerewolf())
|
||||||
|
curType = Type_Werewolf;
|
||||||
|
|
||||||
|
return curType;
|
||||||
|
}
|
||||||
|
|
||||||
static NpcAnimation::PartBoneMap createPartListMap()
|
static NpcAnimation::PartBoneMap createPartListMap()
|
||||||
{
|
{
|
||||||
NpcAnimation::PartBoneMap result;
|
NpcAnimation::PartBoneMap result;
|
||||||
|
@ -283,7 +292,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> par
|
||||||
mViewMode(viewMode),
|
mViewMode(viewMode),
|
||||||
mShowWeapons(false),
|
mShowWeapons(false),
|
||||||
mShowCarriedLeft(true),
|
mShowCarriedLeft(true),
|
||||||
mNpcType(Type_Normal),
|
mNpcType(getNpcType()),
|
||||||
mFirstPersonFieldOfView(firstPersonFieldOfView),
|
mFirstPersonFieldOfView(firstPersonFieldOfView),
|
||||||
mSoundsDisabled(disableSounds),
|
mSoundsDisabled(disableSounds),
|
||||||
mAccurateAiming(false),
|
mAccurateAiming(false),
|
||||||
|
@ -431,41 +440,39 @@ void NpcAnimation::updateNpcBase()
|
||||||
|
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);
|
const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);
|
||||||
bool isWerewolf = (mNpcType == Type_Werewolf);
|
NpcType curType = getNpcType();
|
||||||
bool isVampire = (mNpcType == Type_Vampire);
|
bool isWerewolf = (curType == Type_Werewolf);
|
||||||
|
bool isVampire = (curType == Type_Vampire);
|
||||||
bool isFemale = !mNpc->isMale();
|
bool isFemale = !mNpc->isMale();
|
||||||
|
|
||||||
if (isWerewolf)
|
mHeadModel.clear();
|
||||||
|
mHairModel.clear();
|
||||||
|
|
||||||
|
std::string headName = isWerewolf ? "WerewolfHead" : mNpc->mHead;
|
||||||
|
std::string hairName = isWerewolf ? "WerewolfHair" : mNpc->mHair;
|
||||||
|
|
||||||
|
if (!headName.empty())
|
||||||
{
|
{
|
||||||
mHeadModel = "meshes\\" + store.get<ESM::BodyPart>().find("WerewolfHead")->mModel;
|
const ESM::BodyPart* bp = store.get<ESM::BodyPart>().search(headName);
|
||||||
mHairModel = "meshes\\" + store.get<ESM::BodyPart>().find("WerewolfHair")->mModel;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mHeadModel = "";
|
|
||||||
const std::string& vampireHead = getVampireHead(mNpc->mRace, isFemale);
|
|
||||||
if (isVampire && !vampireHead.empty())
|
|
||||||
mHeadModel = vampireHead;
|
|
||||||
else if (!mNpc->mHead.empty())
|
|
||||||
{
|
|
||||||
const ESM::BodyPart* bp = store.get<ESM::BodyPart>().search(mNpc->mHead);
|
|
||||||
if (bp)
|
if (bp)
|
||||||
mHeadModel = "meshes\\" + bp->mModel;
|
mHeadModel = "meshes\\" + bp->mModel;
|
||||||
else
|
else
|
||||||
Log(Debug::Warning) << "Warning: Failed to load body part '" << mNpc->mHead << "'";
|
Log(Debug::Warning) << "Warning: Failed to load body part '" << headName << "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
mHairModel = "";
|
if (!hairName.empty())
|
||||||
if (!mNpc->mHair.empty())
|
|
||||||
{
|
{
|
||||||
const ESM::BodyPart* bp = store.get<ESM::BodyPart>().search(mNpc->mHair);
|
const ESM::BodyPart* bp = store.get<ESM::BodyPart>().search(hairName);
|
||||||
if (bp)
|
if (bp)
|
||||||
mHairModel = "meshes\\" + bp->mModel;
|
mHairModel = "meshes\\" + bp->mModel;
|
||||||
else
|
else
|
||||||
Log(Debug::Warning) << "Warning: Failed to load body part '" << mNpc->mHair << "'";
|
Log(Debug::Warning) << "Warning: Failed to load body part '" << hairName << "'";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& vampireHead = getVampireHead(mNpc->mRace, isFemale);
|
||||||
|
if (!isWerewolf && isVampire && !vampireHead.empty())
|
||||||
|
mHeadModel = vampireHead;
|
||||||
|
|
||||||
bool is1stPerson = mViewMode == VM_FirstPerson;
|
bool is1stPerson = mViewMode == VM_FirstPerson;
|
||||||
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
||||||
|
|
||||||
|
@ -473,7 +480,7 @@ void NpcAnimation::updateNpcBase()
|
||||||
defaultSkeleton = Misc::ResourceHelpers::correctActorModelPath(defaultSkeleton, mResourceSystem->getVFS());
|
defaultSkeleton = Misc::ResourceHelpers::correctActorModelPath(defaultSkeleton, mResourceSystem->getVFS());
|
||||||
|
|
||||||
std::string smodel = defaultSkeleton;
|
std::string smodel = defaultSkeleton;
|
||||||
if (!is1stPerson && !isWerewolf & !mNpc->mModel.empty())
|
if (!is1stPerson && !isWerewolf && !mNpc->mModel.empty())
|
||||||
smodel = Misc::ResourceHelpers::correctActorModelPath("meshes\\" + mNpc->mModel, mResourceSystem->getVFS());
|
smodel = Misc::ResourceHelpers::correctActorModelPath("meshes\\" + mNpc->mModel, mResourceSystem->getVFS());
|
||||||
|
|
||||||
setObjectRoot(smodel, true, true, false);
|
setObjectRoot(smodel, true, true, false);
|
||||||
|
@ -517,14 +524,7 @@ void NpcAnimation::updateParts()
|
||||||
if (!mObjectRoot.get())
|
if (!mObjectRoot.get())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const MWWorld::Class &cls = mPtr.getClass();
|
NpcType curType = getNpcType();
|
||||||
|
|
||||||
NpcType curType = Type_Normal;
|
|
||||||
if (cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0)
|
|
||||||
curType = Type_Vampire;
|
|
||||||
if (cls.getNpcStats(mPtr).isWerewolf())
|
|
||||||
curType = Type_Werewolf;
|
|
||||||
|
|
||||||
if (curType != mNpcType)
|
if (curType != mNpcType)
|
||||||
{
|
{
|
||||||
mNpcType = curType;
|
mNpcType = curType;
|
||||||
|
@ -632,7 +632,7 @@ void NpcAnimation::updateParts()
|
||||||
showWeapons(mShowWeapons);
|
showWeapons(mShowWeapons);
|
||||||
showCarriedLeft(mShowCarriedLeft);
|
showCarriedLeft(mShowCarriedLeft);
|
||||||
|
|
||||||
bool isWerewolf = (mNpcType == Type_Werewolf);
|
bool isWerewolf = (getNpcType() == Type_Werewolf);
|
||||||
std::string race = (isWerewolf ? "werewolf" : Misc::StringUtils::lowerCase(mNpc->mRace));
|
std::string race = (isWerewolf ? "werewolf" : Misc::StringUtils::lowerCase(mNpc->mRace));
|
||||||
|
|
||||||
const std::vector<const ESM::BodyPart*> &parts = getBodyParts(race, !mNpc->isMale(), mViewMode == VM_FirstPerson, isWerewolf);
|
const std::vector<const ESM::BodyPart*> &parts = getBodyParts(race, !mNpc->isMale(), mViewMode == VM_FirstPerson, isWerewolf);
|
||||||
|
@ -649,9 +649,6 @@ void NpcAnimation::updateParts()
|
||||||
|
|
||||||
if (wasArrowAttached)
|
if (wasArrowAttached)
|
||||||
attachArrow();
|
attachArrow();
|
||||||
|
|
||||||
if (mAlpha != 1.f)
|
|
||||||
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -730,10 +727,7 @@ void NpcAnimation::removePartGroup(int group)
|
||||||
|
|
||||||
bool NpcAnimation::isFirstPersonPart(const ESM::BodyPart* bodypart)
|
bool NpcAnimation::isFirstPersonPart(const ESM::BodyPart* bodypart)
|
||||||
{
|
{
|
||||||
return (bodypart->mId.size() >= 3)
|
return bodypart->mId.size() >= 3 && bodypart->mId.substr(bodypart->mId.size()-3, 3) == "1st";
|
||||||
&& bodypart->mId[bodypart->mId.size()-3] == '1'
|
|
||||||
&& bodypart->mId[bodypart->mId.size()-2] == 's'
|
|
||||||
&& bodypart->mId[bodypart->mId.size()-1] == 't';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NpcAnimation::isFemalePart(const ESM::BodyPart* bodypart)
|
bool NpcAnimation::isFemalePart(const ESM::BodyPart* bodypart)
|
||||||
|
@ -793,16 +787,16 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g
|
||||||
osg::Object* obj = node->getUserDataContainer()->getUserObject(i);
|
osg::Object* obj = node->getUserDataContainer()->getUserObject(i);
|
||||||
if (NifOsg::TextKeyMapHolder* keys = dynamic_cast<NifOsg::TextKeyMapHolder*>(obj))
|
if (NifOsg::TextKeyMapHolder* keys = dynamic_cast<NifOsg::TextKeyMapHolder*>(obj))
|
||||||
{
|
{
|
||||||
for (NifOsg::TextKeyMap::const_iterator it = keys->mTextKeys.begin(); it != keys->mTextKeys.end(); ++it)
|
for (const auto &key : keys->mTextKeys)
|
||||||
{
|
{
|
||||||
if (Misc::StringUtils::ciEqual(it->second, "talk: start"))
|
if (Misc::StringUtils::ciEqual(key.second, "talk: start"))
|
||||||
mHeadAnimationTime->setTalkStart(it->first);
|
mHeadAnimationTime->setTalkStart(key.first);
|
||||||
if (Misc::StringUtils::ciEqual(it->second, "talk: stop"))
|
if (Misc::StringUtils::ciEqual(key.second, "talk: stop"))
|
||||||
mHeadAnimationTime->setTalkStop(it->first);
|
mHeadAnimationTime->setTalkStop(key.first);
|
||||||
if (Misc::StringUtils::ciEqual(it->second, "blink: start"))
|
if (Misc::StringUtils::ciEqual(key.second, "blink: start"))
|
||||||
mHeadAnimationTime->setBlinkStart(it->first);
|
mHeadAnimationTime->setBlinkStart(key.first);
|
||||||
if (Misc::StringUtils::ciEqual(it->second, "blink: stop"))
|
if (Misc::StringUtils::ciEqual(key.second, "blink: stop"))
|
||||||
mHeadAnimationTime->setBlinkStop(it->first);
|
mHeadAnimationTime->setBlinkStop(key.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -828,16 +822,15 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::
|
||||||
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
|
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
|
||||||
|
|
||||||
const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : "";
|
const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : "";
|
||||||
std::vector<ESM::PartReference>::const_iterator part(parts.begin());
|
for(const ESM::PartReference& part : parts)
|
||||||
for(;part != parts.end();++part)
|
|
||||||
{
|
{
|
||||||
const ESM::BodyPart *bodypart = 0;
|
const ESM::BodyPart *bodypart = nullptr;
|
||||||
if(!mNpc->isMale() && !part->mFemale.empty())
|
if(!mNpc->isMale() && !part.mFemale.empty())
|
||||||
{
|
{
|
||||||
bodypart = partStore.search(part->mFemale+ext);
|
bodypart = partStore.search(part.mFemale+ext);
|
||||||
if(!bodypart && mViewMode == VM_FirstPerson)
|
if(!bodypart && mViewMode == VM_FirstPerson)
|
||||||
{
|
{
|
||||||
bodypart = partStore.search(part->mFemale);
|
bodypart = partStore.search(part.mFemale);
|
||||||
if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand ||
|
if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand ||
|
||||||
bodypart->mData.mPart == ESM::BodyPart::MP_Wrist ||
|
bodypart->mData.mPart == ESM::BodyPart::MP_Wrist ||
|
||||||
bodypart->mData.mPart == ESM::BodyPart::MP_Forearm ||
|
bodypart->mData.mPart == ESM::BodyPart::MP_Forearm ||
|
||||||
|
@ -845,14 +838,14 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::
|
||||||
bodypart = nullptr;
|
bodypart = nullptr;
|
||||||
}
|
}
|
||||||
else if (!bodypart)
|
else if (!bodypart)
|
||||||
Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mFemale << "'";
|
Log(Debug::Warning) << "Warning: Failed to find body part '" << part.mFemale << "'";
|
||||||
}
|
}
|
||||||
if(!bodypart && !part->mMale.empty())
|
if(!bodypart && !part.mMale.empty())
|
||||||
{
|
{
|
||||||
bodypart = partStore.search(part->mMale+ext);
|
bodypart = partStore.search(part.mMale+ext);
|
||||||
if(!bodypart && mViewMode == VM_FirstPerson)
|
if(!bodypart && mViewMode == VM_FirstPerson)
|
||||||
{
|
{
|
||||||
bodypart = partStore.search(part->mMale);
|
bodypart = partStore.search(part.mMale);
|
||||||
if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand ||
|
if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand ||
|
||||||
bodypart->mData.mPart == ESM::BodyPart::MP_Wrist ||
|
bodypart->mData.mPart == ESM::BodyPart::MP_Wrist ||
|
||||||
bodypart->mData.mPart == ESM::BodyPart::MP_Forearm ||
|
bodypart->mData.mPart == ESM::BodyPart::MP_Forearm ||
|
||||||
|
@ -860,13 +853,13 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::
|
||||||
bodypart = nullptr;
|
bodypart = nullptr;
|
||||||
}
|
}
|
||||||
else if (!bodypart)
|
else if (!bodypart)
|
||||||
Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mMale << "'";
|
Log(Debug::Warning) << "Warning: Failed to find body part '" << part.mMale << "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(bodypart)
|
if(bodypart)
|
||||||
addOrReplaceIndividualPart((ESM::PartReferenceType)part->mPart, group, priority, "meshes\\"+bodypart->mModel, enchantedGlow, glowColor);
|
addOrReplaceIndividualPart((ESM::PartReferenceType)part.mPart, group, priority, "meshes\\"+bodypart->mModel, enchantedGlow, glowColor);
|
||||||
else
|
else
|
||||||
reserveIndividualPart((ESM::PartReferenceType)part->mPart, group, priority);
|
reserveIndividualPart((ESM::PartReferenceType)part.mPart, group, priority);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -885,7 +878,7 @@ void NpcAnimation::addControllers()
|
||||||
osg::MatrixTransform* node = found->second.get();
|
osg::MatrixTransform* node = found->second.get();
|
||||||
mFirstPersonNeckController = new NeckController(mObjectRoot.get());
|
mFirstPersonNeckController = new NeckController(mObjectRoot.get());
|
||||||
node->addUpdateCallback(mFirstPersonNeckController);
|
node->addUpdateCallback(mFirstPersonNeckController);
|
||||||
mActiveControllers.insert(std::make_pair(node, mFirstPersonNeckController));
|
mActiveControllers.emplace(node, mFirstPersonNeckController);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mViewMode == VM_Normal)
|
else if (mViewMode == VM_Normal)
|
||||||
|
@ -918,9 +911,6 @@ void NpcAnimation::showWeapons(bool showWeapon)
|
||||||
attachArrow();
|
attachArrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Note: we will need to recreate shaders later if we use weapon sheathing anyway, so there is no point to update them here
|
|
||||||
if (mAlpha != 1.f && !mWeaponSheathing)
|
|
||||||
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -932,10 +922,6 @@ void NpcAnimation::showWeapons(bool showWeapon)
|
||||||
|
|
||||||
updateHolsteredWeapon(!mShowWeapons);
|
updateHolsteredWeapon(!mShowWeapons);
|
||||||
updateQuiver();
|
updateQuiver();
|
||||||
|
|
||||||
// Recreate shaders for invisible actors, otherwise sheath nodes will be visible
|
|
||||||
if (mAlpha != 1.f && mWeaponSheathing)
|
|
||||||
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NpcAnimation::showCarriedLeft(bool show)
|
void NpcAnimation::showCarriedLeft(bool show)
|
||||||
|
@ -953,8 +939,6 @@ void NpcAnimation::showCarriedLeft(bool show)
|
||||||
if (iter->getTypeName() == typeid(ESM::Light).name() && mObjectParts[ESM::PRT_Shield])
|
if (iter->getTypeName() == typeid(ESM::Light).name() && mObjectParts[ESM::PRT_Shield])
|
||||||
addExtraLight(mObjectParts[ESM::PRT_Shield]->getNode()->asGroup(), iter->get<ESM::Light>()->mBase);
|
addExtraLight(mObjectParts[ESM::PRT_Shield]->getNode()->asGroup(), iter->get<ESM::Light>()->mBase);
|
||||||
}
|
}
|
||||||
if (mAlpha != 1.f)
|
|
||||||
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
removeIndividualPart(ESM::PRT_Shield);
|
removeIndividualPart(ESM::PRT_Shield);
|
||||||
|
@ -1087,40 +1071,39 @@ const std::vector<const ESM::BodyPart *>& NpcAnimation::getBodyParts(const std::
|
||||||
std::vector<const ESM::BodyPart*>& parts = sRaceMapping[std::make_pair(race, flags)];
|
std::vector<const ESM::BodyPart*>& parts = sRaceMapping[std::make_pair(race, flags)];
|
||||||
|
|
||||||
typedef std::multimap<ESM::BodyPart::MeshPart,ESM::PartReferenceType> BodyPartMapType;
|
typedef std::multimap<ESM::BodyPart::MeshPart,ESM::PartReferenceType> BodyPartMapType;
|
||||||
static BodyPartMapType sBodyPartMap;
|
static const BodyPartMapType sBodyPartMap =
|
||||||
if(sBodyPartMap.empty())
|
|
||||||
{
|
{
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Neck, ESM::PRT_Neck));
|
{ESM::BodyPart::MP_Neck, ESM::PRT_Neck},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass));
|
{ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Groin, ESM::PRT_Groin));
|
{ESM::BodyPart::MP_Groin, ESM::PRT_Groin},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_RHand));
|
{ESM::BodyPart::MP_Hand, ESM::PRT_RHand},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_LHand));
|
{ESM::BodyPart::MP_Hand, ESM::PRT_LHand},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist));
|
{ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist));
|
{ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm));
|
{ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm));
|
{ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm));
|
{ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm));
|
{ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_RFoot));
|
{ESM::BodyPart::MP_Foot, ESM::PRT_RFoot},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_LFoot));
|
{ESM::BodyPart::MP_Foot, ESM::PRT_LFoot},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle));
|
{ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle));
|
{ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_RKnee));
|
{ESM::BodyPart::MP_Knee, ESM::PRT_RKnee},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_LKnee));
|
{ESM::BodyPart::MP_Knee, ESM::PRT_LKnee},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg));
|
{ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg));
|
{ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg},
|
||||||
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail));
|
{ESM::BodyPart::MP_Tail, ESM::PRT_Tail}
|
||||||
}
|
};
|
||||||
|
|
||||||
parts.resize(ESM::PRT_Count, nullptr);
|
parts.resize(ESM::PRT_Count, nullptr);
|
||||||
|
|
||||||
|
if (werewolf)
|
||||||
|
return parts;
|
||||||
|
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
|
|
||||||
for(MWWorld::Store<ESM::BodyPart>::iterator it = partStore.begin(); it != partStore.end(); ++it)
|
for(const ESM::BodyPart& bodypart : store.get<ESM::BodyPart>())
|
||||||
{
|
{
|
||||||
if(werewolf)
|
|
||||||
break;
|
|
||||||
const ESM::BodyPart& bodypart = *it;
|
|
||||||
if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
|
if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
|
||||||
continue;
|
continue;
|
||||||
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
|
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
|
||||||
|
|
|
@ -74,6 +74,8 @@ private:
|
||||||
|
|
||||||
void updateNpcBase();
|
void updateNpcBase();
|
||||||
|
|
||||||
|
NpcType getNpcType();
|
||||||
|
|
||||||
PartHolderPtr insertBoundedPart(const std::string &model, const std::string &bonename,
|
PartHolderPtr insertBoundedPart(const std::string &model, const std::string &bonename,
|
||||||
const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=nullptr);
|
const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=nullptr);
|
||||||
|
|
||||||
|
|
|
@ -314,6 +314,7 @@ namespace MWRender
|
||||||
|
|
||||||
mTerrain->setDefaultViewer(mViewer->getCamera());
|
mTerrain->setDefaultViewer(mViewer->getCamera());
|
||||||
mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells"));
|
mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells"));
|
||||||
|
mTerrain->setWorkQueue(mWorkQueue.get());
|
||||||
|
|
||||||
mCamera.reset(new Camera(mViewer->getCamera()));
|
mCamera.reset(new Camera(mViewer->getCamera()));
|
||||||
|
|
||||||
|
@ -371,8 +372,10 @@ namespace MWRender
|
||||||
|
|
||||||
mNearClip = Settings::Manager::getFloat("near clip", "Camera");
|
mNearClip = Settings::Manager::getFloat("near clip", "Camera");
|
||||||
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
|
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
|
||||||
mFieldOfView = Settings::Manager::getFloat("field of view", "Camera");
|
float fov = Settings::Manager::getFloat("field of view", "Camera");
|
||||||
mFirstPersonFieldOfView = Settings::Manager::getFloat("first person field of view", "Camera");
|
mFieldOfView = std::min(std::max(1.f, fov), 179.f);
|
||||||
|
float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera");
|
||||||
|
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
|
||||||
mStateUpdater->setFogEnd(mViewDistance);
|
mStateUpdater->setFogEnd(mViewDistance);
|
||||||
|
|
||||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
|
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
|
||||||
|
@ -1198,6 +1201,12 @@ namespace MWRender
|
||||||
|
|
||||||
mUniformNear->set(mNearClip);
|
mUniformNear->set(mNearClip);
|
||||||
mUniformFar->set(mViewDistance);
|
mUniformFar->set(mViewDistance);
|
||||||
|
|
||||||
|
// Since our fog is not radial yet, we should take FOV in account, otherwise terrain near viewing distance may disappear.
|
||||||
|
// Limit FOV here just for sure, otherwise viewing distance can be too high.
|
||||||
|
fov = std::min(mFieldOfView, 140.f);
|
||||||
|
float distanceMult = std::cos(osg::DegreesToRadians(fov)/2.f);
|
||||||
|
mTerrain->setViewDistance(mViewDistance * (distanceMult ? 1.f/distanceMult : 1.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::updateTextureFiltering()
|
void RenderingManager::updateTextureFiltering()
|
||||||
|
@ -1446,8 +1455,8 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto locked = it->second.lockConst();
|
const auto locked = it->second->lockConst();
|
||||||
mNavMesh->update(locked->getValue(), mNavMeshNumber, locked->getGeneration(),
|
mNavMesh->update(locked->getImpl(), mNavMeshNumber, locked->getGeneration(),
|
||||||
locked->getNavMeshRevision(), mNavigator.getSettings());
|
locked->getNavMeshRevision(), mNavigator.getSettings());
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
|
|
|
@ -22,6 +22,15 @@ namespace MWRender
|
||||||
mResourceSystem->removeResourceManager(mLandManager.get());
|
mResourceSystem->removeResourceManager(mLandManager.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TerrainStorage::hasData(int cellX, int cellY)
|
||||||
|
{
|
||||||
|
const MWWorld::ESMStore &esmStore =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
|
||||||
|
const ESM::Land* land = esmStore.get<ESM::Land>().search(cellX, cellY);
|
||||||
|
return land != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
|
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
|
||||||
{
|
{
|
||||||
minX = 0, minY = 0, maxX = 0, maxY = 0;
|
minX = 0, minY = 0, maxX = 0, maxY = 0;
|
||||||
|
|
|
@ -20,11 +20,13 @@ namespace MWRender
|
||||||
TerrainStorage(Resource::ResourceSystem* resourceSystem, const std::string& normalMapPattern = "", const std::string& normalHeightMapPatteern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false);
|
TerrainStorage(Resource::ResourceSystem* resourceSystem, const std::string& normalMapPattern = "", const std::string& normalHeightMapPatteern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false);
|
||||||
~TerrainStorage();
|
~TerrainStorage();
|
||||||
|
|
||||||
virtual osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY);
|
virtual osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY) override;
|
||||||
virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
|
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override;
|
||||||
|
|
||||||
|
virtual bool hasData(int cellX, int cellY) override;
|
||||||
|
|
||||||
/// Get bounds of the whole terrain in cell units
|
/// Get bounds of the whole terrain in cell units
|
||||||
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
|
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override;
|
||||||
|
|
||||||
LandManager* getLandManager() const;
|
LandManager* getLandManager() const;
|
||||||
|
|
||||||
|
|
|
@ -285,11 +285,11 @@ namespace MWScript
|
||||||
MWWorld::Ptr updated = ptr;
|
MWWorld::Ptr updated = ptr;
|
||||||
if(axis == "x")
|
if(axis == "x")
|
||||||
{
|
{
|
||||||
updated = MWBase::Environment::get().getWorld()->moveObject(ptr,pos,ay,az);
|
updated = MWBase::Environment::get().getWorld()->moveObject(ptr,pos,ay,az,true);
|
||||||
}
|
}
|
||||||
else if(axis == "y")
|
else if(axis == "y")
|
||||||
{
|
{
|
||||||
updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,pos,az);
|
updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,pos,az,true);
|
||||||
}
|
}
|
||||||
else if(axis == "z")
|
else if(axis == "z")
|
||||||
{
|
{
|
||||||
|
@ -304,7 +304,7 @@ namespace MWScript
|
||||||
pos = terrainHeight;
|
pos = terrainHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos);
|
updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos,true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw std::runtime_error ("invalid axis: " + axis);
|
throw std::runtime_error ("invalid axis: " + axis);
|
||||||
|
@ -447,7 +447,7 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z);
|
ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z, true);
|
||||||
}
|
}
|
||||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);
|
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);
|
||||||
|
|
||||||
|
|
|
@ -96,25 +96,25 @@ bool FFmpeg_Decoder::getAVAudioData()
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if(mPacket.size == 0 && !getNextPacket())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Decode some data, and check for errors */
|
/* Decode some data, and check for errors */
|
||||||
int ret = 0;
|
int ret = avcodec_receive_frame(mCodecCtx, mFrame);
|
||||||
ret = avcodec_receive_frame(mCodecCtx, mFrame);
|
|
||||||
if (ret == 0)
|
|
||||||
got_frame = true;
|
|
||||||
if (ret == AVERROR(EAGAIN))
|
if (ret == AVERROR(EAGAIN))
|
||||||
ret = 0;
|
{
|
||||||
if (ret == 0)
|
if (mPacket.size == 0 && !getNextPacket())
|
||||||
|
return false;
|
||||||
ret = avcodec_send_packet(mCodecCtx, &mPacket);
|
ret = avcodec_send_packet(mCodecCtx, &mPacket);
|
||||||
if (ret < 0 && ret != AVERROR(EAGAIN))
|
av_packet_unref(&mPacket);
|
||||||
|
if (ret == 0)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ret != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
av_packet_unref(&mPacket);
|
av_packet_unref(&mPacket);
|
||||||
|
|
||||||
if (!got_frame || mFrame->nb_samples == 0)
|
if (mFrame->nb_samples == 0)
|
||||||
continue;
|
continue;
|
||||||
|
got_frame = true;
|
||||||
|
|
||||||
if(mSwr)
|
if(mSwr)
|
||||||
{
|
{
|
||||||
|
@ -138,7 +138,7 @@ bool FFmpeg_Decoder::getAVAudioData()
|
||||||
else
|
else
|
||||||
mFrameData = &mFrame->data[0];
|
mFrameData = &mFrame->data[0];
|
||||||
|
|
||||||
} while(!got_frame || mFrame->nb_samples == 0);
|
} while(!got_frame);
|
||||||
mNextPts += (double)mFrame->nb_samples / mCodecCtx->sample_rate;
|
mNextPts += (double)mFrame->nb_samples / mCodecCtx->sample_rate;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -471,6 +471,36 @@ namespace MWSound
|
||||||
startRandomTitle();
|
startRandomTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SoundManager::playTitleMusic()
|
||||||
|
{
|
||||||
|
if (mCurrentPlaylist == "Title")
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mMusicFiles.find("Title") == mMusicFiles.end())
|
||||||
|
{
|
||||||
|
std::vector<std::string> filelist;
|
||||||
|
const std::map<std::string, VFS::File*>& index = mVFS->getIndex();
|
||||||
|
// Is there an ini setting for this filename or something?
|
||||||
|
std::string filename = "music/special/morrowind title.mp3";
|
||||||
|
auto found = index.find(filename);
|
||||||
|
if (found != index.end())
|
||||||
|
{
|
||||||
|
filelist.emplace_back(found->first);
|
||||||
|
mMusicFiles["Title"] = filelist;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(Debug::Warning) << "Title music not found";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mMusicFiles["Title"].empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mCurrentPlaylist = "Title";
|
||||||
|
startRandomTitle();
|
||||||
|
}
|
||||||
|
|
||||||
void SoundManager::say(const MWWorld::ConstPtr &ptr, const std::string &filename)
|
void SoundManager::say(const MWWorld::ConstPtr &ptr, const std::string &filename)
|
||||||
{
|
{
|
||||||
|
@ -1122,10 +1152,10 @@ namespace MWSound
|
||||||
if(!mOutput->isInitialized())
|
if(!mOutput->isInitialized())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
updateSounds(duration);
|
||||||
if (MWBase::Environment::get().getStateManager()->getState()!=
|
if (MWBase::Environment::get().getStateManager()->getState()!=
|
||||||
MWBase::StateManager::State_NoGame)
|
MWBase::StateManager::State_NoGame)
|
||||||
{
|
{
|
||||||
updateSounds(duration);
|
|
||||||
updateRegionSound(duration);
|
updateRegionSound(duration);
|
||||||
updateWaterSound(duration);
|
updateWaterSound(duration);
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,6 +168,9 @@ namespace MWSound
|
||||||
///< Start playing music from the selected folder
|
///< Start playing music from the selected folder
|
||||||
/// \param name of the folder that contains the playlist
|
/// \param name of the folder that contains the playlist
|
||||||
|
|
||||||
|
virtual void playTitleMusic();
|
||||||
|
///< Start playing title music
|
||||||
|
|
||||||
virtual void say(const MWWorld::ConstPtr &reference, const std::string& filename);
|
virtual void say(const MWWorld::ConstPtr &reference, const std::string& filename);
|
||||||
///< Make an actor say some text.
|
///< Make an actor say some text.
|
||||||
/// \param filename name of a sound file in "Sound/" in the data directory.
|
/// \param filename name of a sound file in "Sound/" in the data directory.
|
||||||
|
|
|
@ -381,7 +381,7 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)
|
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)
|
||||||
{
|
{
|
||||||
mWorld->preload(mTerrainViews[i], mPreloadPositions[i]);
|
mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort);
|
||||||
mTerrainViews[i]->reset(0);
|
mTerrainViews[i]->reset(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,16 +150,16 @@ MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name)
|
||||||
return &result->second;
|
return &result->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::Cells::rest ()
|
void MWWorld::Cells::rest (double hours)
|
||||||
{
|
{
|
||||||
for (auto &interior : mInteriors)
|
for (auto &interior : mInteriors)
|
||||||
{
|
{
|
||||||
interior.second.rest();
|
interior.second.rest(hours);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &exterior : mExteriors)
|
for (auto &exterior : mExteriors)
|
||||||
{
|
{
|
||||||
exterior.second.rest();
|
exterior.second.rest(hours);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace MWWorld
|
||||||
|
|
||||||
/// @note name must be lower case
|
/// @note name must be lower case
|
||||||
Ptr getPtr (const std::string& name);
|
Ptr getPtr (const std::string& name);
|
||||||
void rest ();
|
void rest (double hours);
|
||||||
|
|
||||||
/// Get all Ptrs referencing \a name in exterior cells
|
/// Get all Ptrs referencing \a name in exterior cells
|
||||||
/// @note Due to the current implementation of getPtr this only supports one Ptr per cell.
|
/// @note Due to the current implementation of getPtr this only supports one Ptr per cell.
|
||||||
|
|
|
@ -1152,7 +1152,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellStore::rest()
|
void CellStore::rest(double hours)
|
||||||
{
|
{
|
||||||
if (mState == State_Loaded)
|
if (mState == State_Loaded)
|
||||||
{
|
{
|
||||||
|
@ -1161,7 +1161,7 @@ namespace MWWorld
|
||||||
Ptr ptr = getCurrentPtr(&*it);
|
Ptr ptr = getCurrentPtr(&*it);
|
||||||
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, true);
|
MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, hours, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
|
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
|
||||||
|
@ -1169,7 +1169,7 @@ namespace MWWorld
|
||||||
Ptr ptr = getCurrentPtr(&*it);
|
Ptr ptr = getCurrentPtr(&*it);
|
||||||
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, true);
|
MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, hours, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ namespace MWWorld
|
||||||
/// @return updated MWWorld::Ptr with the new CellStore pointer set.
|
/// @return updated MWWorld::Ptr with the new CellStore pointer set.
|
||||||
MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo);
|
MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo);
|
||||||
|
|
||||||
void rest();
|
void rest(double hours);
|
||||||
|
|
||||||
/// Make a copy of the given object and insert it into this cell.
|
/// Make a copy of the given object and insert it into this cell.
|
||||||
/// @note If you get a linker error here, this means the given type can not be inserted into a cell.
|
/// @note If you get a linker error here, this means the given type can not be inserted into a cell.
|
||||||
|
|
|
@ -360,7 +360,7 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
if (const auto object = mPhysics->getObject(ptr))
|
if (const auto object = mPhysics->getObject(ptr))
|
||||||
navigator->removeObject(DetourNavigator::ObjectId(object));
|
navigator->removeObject(DetourNavigator::ObjectId(object));
|
||||||
else if (const auto actor = mPhysics->getActor(ptr))
|
else if (mPhysics->getActor(ptr))
|
||||||
{
|
{
|
||||||
navigator->removeAgent(world->getPathfindingHalfExtents(ptr));
|
navigator->removeAgent(world->getPathfindingHalfExtents(ptr));
|
||||||
mRendering.removeActorPath(ptr);
|
mRendering.removeActorPath(ptr);
|
||||||
|
@ -892,7 +892,7 @@ namespace MWWorld
|
||||||
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||||
navigator->update(player.getRefData().getPosition().asVec3());
|
navigator->update(player.getRefData().getPosition().asVec3());
|
||||||
}
|
}
|
||||||
else if (const auto actor = mPhysics->getActor(ptr))
|
else if (mPhysics->getActor(ptr))
|
||||||
{
|
{
|
||||||
navigator->removeAgent(MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(ptr));
|
navigator->removeAgent(MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(ptr));
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,6 +387,21 @@ namespace MWWorld
|
||||||
|
|
||||||
assert(plugin < mStatic.size());
|
assert(plugin < mStatic.size());
|
||||||
|
|
||||||
|
// Replace texture for records with given ID and index from all plugins.
|
||||||
|
for (unsigned int i=0; i<mStatic.size(); i++)
|
||||||
|
{
|
||||||
|
ESM::LandTexture* tex = const_cast<ESM::LandTexture*>(search(lt.mIndex, i));
|
||||||
|
if (tex)
|
||||||
|
{
|
||||||
|
const std::string texId = Misc::StringUtils::lowerCase(tex->mId);
|
||||||
|
const std::string ltId = Misc::StringUtils::lowerCase(lt.mId);
|
||||||
|
if (texId == ltId)
|
||||||
|
{
|
||||||
|
tex->mTexture = lt.mTexture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LandTextureList <exl = mStatic[plugin];
|
LandTextureList <exl = mStatic[plugin];
|
||||||
if(lt.mIndex + 1 > (int)ltexl.size())
|
if(lt.mIndex + 1 > (int)ltexl.size())
|
||||||
ltexl.resize(lt.mIndex+1);
|
ltexl.resize(lt.mIndex+1);
|
||||||
|
|
|
@ -178,12 +178,12 @@ namespace MWWorld
|
||||||
const Files::Collections& fileCollections,
|
const Files::Collections& fileCollections,
|
||||||
const std::vector<std::string>& contentFiles,
|
const std::vector<std::string>& contentFiles,
|
||||||
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap,
|
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap,
|
||||||
int activationDistanceOverride, const std::string& startCell, const std::string& startupScript,
|
int activationDistanceOverride, const std::string& startCell,
|
||||||
const std::string& resourcePath, const std::string& userDataPath)
|
const std::string& resourcePath, const std::string& userDataPath)
|
||||||
: mResourceSystem(resourceSystem), mFallback(fallbackMap), mLocalScripts (mStore),
|
: mResourceSystem(resourceSystem), mFallback(fallbackMap), mLocalScripts (mStore),
|
||||||
mSky (true), mCells (mStore, mEsm),
|
mSky (true), mCells (mStore, mEsm),
|
||||||
mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mUserDataPath(userDataPath),
|
mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mUserDataPath(userDataPath),
|
||||||
mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript),
|
mActivationDistanceOverride (activationDistanceOverride),
|
||||||
mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true),
|
mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true),
|
||||||
mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0),
|
mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0),
|
||||||
mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f)
|
mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f)
|
||||||
|
@ -337,9 +337,6 @@ namespace MWWorld
|
||||||
if (!mPhysics->toggleCollisionMode())
|
if (!mPhysics->toggleCollisionMode())
|
||||||
mPhysics->toggleCollisionMode();
|
mPhysics->toggleCollisionMode();
|
||||||
|
|
||||||
if (!mStartupScript.empty())
|
|
||||||
MWBase::Environment::get().getWindowManager()->executeInConsole(mStartupScript);
|
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->updatePlayer();
|
MWBase::Environment::get().getWindowManager()->updatePlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,7 +500,7 @@ namespace MWWorld
|
||||||
gmst["iWereWolfBounty"] = ESM::Variant(1000);
|
gmst["iWereWolfBounty"] = ESM::Variant(1000);
|
||||||
gmst["fCombatDistanceWerewolfMod"] = ESM::Variant(0.3f);
|
gmst["fCombatDistanceWerewolfMod"] = ESM::Variant(0.3f);
|
||||||
|
|
||||||
for (const std::pair<std::string, ESM::Variant> ¶ms : gmst)
|
for (const auto ¶ms : gmst)
|
||||||
{
|
{
|
||||||
if (!mStore.get<ESM::GameSetting>().search(params.first))
|
if (!mStore.get<ESM::GameSetting>().search(params.first))
|
||||||
{
|
{
|
||||||
|
@ -533,7 +530,7 @@ namespace MWWorld
|
||||||
globals["crimegoldturnin"] = ESM::Variant(0);
|
globals["crimegoldturnin"] = ESM::Variant(0);
|
||||||
globals["pchasturnin"] = ESM::Variant(0);
|
globals["pchasturnin"] = ESM::Variant(0);
|
||||||
|
|
||||||
for (const std::pair<std::string, ESM::Variant> ¶ms : globals)
|
for (const auto ¶ms : globals)
|
||||||
{
|
{
|
||||||
if (!mStore.get<ESM::Global>().search(params.first))
|
if (!mStore.get<ESM::Global>().search(params.first))
|
||||||
{
|
{
|
||||||
|
@ -552,7 +549,7 @@ namespace MWWorld
|
||||||
statics["templemarker"] = "marker_temple.nif";
|
statics["templemarker"] = "marker_temple.nif";
|
||||||
statics["travelmarker"] = "marker_travel.nif";
|
statics["travelmarker"] = "marker_travel.nif";
|
||||||
|
|
||||||
for (const std::pair<std::string, std::string> ¶ms : statics)
|
for (const auto ¶ms : statics)
|
||||||
{
|
{
|
||||||
if (!mStore.get<ESM::Static>().search(params.first))
|
if (!mStore.get<ESM::Static>().search(params.first))
|
||||||
{
|
{
|
||||||
|
@ -566,7 +563,7 @@ namespace MWWorld
|
||||||
std::map<std::string, std::string> doors;
|
std::map<std::string, std::string> doors;
|
||||||
doors["prisonmarker"] = "marker_prison.nif";
|
doors["prisonmarker"] = "marker_prison.nif";
|
||||||
|
|
||||||
for (const std::pair<std::string, std::string> ¶ms : doors)
|
for (const auto ¶ms : doors)
|
||||||
{
|
{
|
||||||
if (!mStore.get<ESM::Door>().search(params.first))
|
if (!mStore.get<ESM::Door>().search(params.first))
|
||||||
{
|
{
|
||||||
|
@ -1512,23 +1509,24 @@ namespace MWWorld
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr World::moveObjectImp(const Ptr& ptr, float x, float y, float z, bool movePhysics)
|
MWWorld::Ptr World::moveObjectImp(const Ptr& ptr, float x, float y, float z, bool movePhysics, bool moveToActive)
|
||||||
{
|
{
|
||||||
CellStore *cell = ptr.getCell();
|
|
||||||
|
|
||||||
if (cell->isExterior()) {
|
|
||||||
int cellX, cellY;
|
int cellX, cellY;
|
||||||
positionToIndex(x, y, cellX, cellY);
|
positionToIndex(x, y, cellX, cellY);
|
||||||
|
|
||||||
cell = getExterior(cellX, cellY);
|
CellStore* cell = ptr.getCell();
|
||||||
}
|
CellStore* newCell = getExterior(cellX, cellY);
|
||||||
|
bool isCellActive = getPlayerPtr().getCell()->isExterior() && mWorldScene->isCellActive(*newCell);
|
||||||
|
|
||||||
|
if (cell->isExterior() || (moveToActive && isCellActive && ptr.getClass().isActor()))
|
||||||
|
cell = newCell;
|
||||||
|
|
||||||
return moveObject(ptr, cell, x, y, z, movePhysics);
|
return moveObject(ptr, cell, x, y, z, movePhysics);
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z)
|
MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z, bool moveToActive)
|
||||||
{
|
{
|
||||||
return moveObjectImp(ptr, x, y, z);
|
return moveObjectImp(ptr, x, y, z, true, moveToActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::scaleObject (const Ptr& ptr, float scale)
|
void World::scaleObject (const Ptr& ptr, float scale)
|
||||||
|
@ -3698,9 +3696,9 @@ namespace MWWorld
|
||||||
return closestMarker;
|
return closestMarker;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::rest()
|
void World::rest(double hours)
|
||||||
{
|
{
|
||||||
mCells.rest();
|
mCells.rest(hours);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::teleportToClosestMarker (const MWWorld::Ptr& ptr,
|
void World::teleportToClosestMarker (const MWWorld::Ptr& ptr,
|
||||||
|
|
|
@ -120,8 +120,6 @@ namespace MWWorld
|
||||||
|
|
||||||
int mActivationDistanceOverride;
|
int mActivationDistanceOverride;
|
||||||
|
|
||||||
std::string mStartupScript;
|
|
||||||
|
|
||||||
std::map<MWWorld::Ptr, int> mDoorStates;
|
std::map<MWWorld::Ptr, int> mDoorStates;
|
||||||
///< only holds doors that are currently moving. 1 = opening, 2 = closing
|
///< only holds doors that are currently moving. 1 = opening, 2 = closing
|
||||||
|
|
||||||
|
@ -132,7 +130,7 @@ namespace MWWorld
|
||||||
|
|
||||||
void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, bool adjust);
|
void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, bool adjust);
|
||||||
|
|
||||||
Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z, bool movePhysics=true);
|
Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false);
|
||||||
///< @return an updated Ptr in case the Ptr's cell changes
|
///< @return an updated Ptr in case the Ptr's cell changes
|
||||||
|
|
||||||
Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos);
|
Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos);
|
||||||
|
@ -211,7 +209,7 @@ namespace MWWorld
|
||||||
const Files::Collections& fileCollections,
|
const Files::Collections& fileCollections,
|
||||||
const std::vector<std::string>& contentFiles,
|
const std::vector<std::string>& contentFiles,
|
||||||
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap,
|
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap,
|
||||||
int activationDistanceOverride, const std::string& startCell, const std::string& startupScript, const std::string& resourcePath, const std::string& userDataPath);
|
int activationDistanceOverride, const std::string& startCell, const std::string& resourcePath, const std::string& userDataPath);
|
||||||
|
|
||||||
virtual ~World();
|
virtual ~World();
|
||||||
|
|
||||||
|
@ -499,7 +497,7 @@ namespace MWWorld
|
||||||
|
|
||||||
void undeleteObject (const Ptr& ptr) override;
|
void undeleteObject (const Ptr& ptr) override;
|
||||||
|
|
||||||
MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z) override;
|
MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z, bool moveToActive=false) override;
|
||||||
///< @return an updated Ptr in case the Ptr's cell changes
|
///< @return an updated Ptr in case the Ptr's cell changes
|
||||||
|
|
||||||
MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override;
|
MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override;
|
||||||
|
@ -737,7 +735,7 @@ namespace MWWorld
|
||||||
RestPermitted canRest() const override;
|
RestPermitted canRest() const override;
|
||||||
///< check if the player is allowed to rest
|
///< check if the player is allowed to rest
|
||||||
|
|
||||||
void rest() override;
|
void rest(double hours) override;
|
||||||
|
|
||||||
/// \todo Probably shouldn't be here
|
/// \todo Probably shouldn't be here
|
||||||
MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override;
|
MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override;
|
||||||
|
|
|
@ -80,14 +80,6 @@ namespace
|
||||||
EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), NavigatorException);
|
EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), NavigatorException);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavigatorTest, find_path_for_removed_agent_should_return_empty)
|
|
||||||
{
|
|
||||||
mNavigator->addAgent(mAgentHalfExtents);
|
|
||||||
mNavigator->removeAgent(mAgentHalfExtents);
|
|
||||||
mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut);
|
|
||||||
EXPECT_EQ(mPath, std::deque<osg::Vec3f>());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavigatorTest, add_agent_should_count_each_agent)
|
TEST_F(DetourNavigatorNavigatorTest, add_agent_should_count_each_agent)
|
||||||
{
|
{
|
||||||
mNavigator->addAgent(mAgentHalfExtents);
|
mNavigator->addAgent(mAgentHalfExtents);
|
||||||
|
|
|
@ -125,7 +125,7 @@ namespace Compiler
|
||||||
|
|
||||||
if (loop.size()!=loop2.size())
|
if (loop.size()!=loop2.size())
|
||||||
throw std::logic_error (
|
throw std::logic_error (
|
||||||
"internal compiler error: failed to generate a while loop");
|
"Internal compiler error: failed to generate a while loop");
|
||||||
|
|
||||||
std::copy (loop2.begin(), loop2.end(), std::back_inserter (mCode));
|
std::copy (loop2.begin(), loop2.end(), std::back_inserter (mCode));
|
||||||
|
|
||||||
|
@ -179,6 +179,14 @@ namespace Compiler
|
||||||
scanner.scan (mLineParser);
|
scanner.scan (mLineParser);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (mState==IfElseJunkState)
|
||||||
|
{
|
||||||
|
getErrorHandler().warning ("Extra text after else", loc);
|
||||||
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
|
scanner.scan (skip);
|
||||||
|
mState = IfElseBodyState;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return Parser::parseName (name, loc, scanner);
|
return Parser::parseName (name, loc, scanner);
|
||||||
}
|
}
|
||||||
|
@ -207,8 +215,7 @@ namespace Compiler
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState ||
|
else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState)
|
||||||
mState==IfElseJunkState)
|
|
||||||
{
|
{
|
||||||
if (parseIfBody (keyword, loc, scanner))
|
if (parseIfBody (keyword, loc, scanner))
|
||||||
return true;
|
return true;
|
||||||
|
@ -218,6 +225,14 @@ namespace Compiler
|
||||||
if ( parseWhileBody (keyword, loc, scanner))
|
if ( parseWhileBody (keyword, loc, scanner))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (mState==IfElseJunkState)
|
||||||
|
{
|
||||||
|
getErrorHandler().warning ("Extra text after else", loc);
|
||||||
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
|
scanner.scan (skip);
|
||||||
|
mState = IfElseBodyState;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return Parser::parseKeyword (keyword, loc, scanner);
|
return Parser::parseKeyword (keyword, loc, scanner);
|
||||||
}
|
}
|
||||||
|
@ -250,8 +265,9 @@ namespace Compiler
|
||||||
default: ;
|
default: ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (code==Scanner::S_open && mState==IfElseJunkState)
|
else if (mState==IfElseJunkState)
|
||||||
{
|
{
|
||||||
|
getErrorHandler().warning ("Extra text after else", loc);
|
||||||
SkipParser skip (getErrorHandler(), getContext());
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
scanner.scan (skip);
|
scanner.scan (skip);
|
||||||
mState = IfElseBodyState;
|
mState = IfElseBodyState;
|
||||||
|
|
|
@ -22,20 +22,20 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke
|
||||||
char type = mLocals.getType (name2);
|
char type = mLocals.getType (name2);
|
||||||
|
|
||||||
if (type!=' ')
|
if (type!=' ')
|
||||||
{
|
getErrorHandler().warning ("Local variable re-declaration", loc);
|
||||||
/// \todo add option to make re-declared local variables an error
|
else
|
||||||
getErrorHandler().warning ("ignoring local variable re-declaration",
|
|
||||||
loc);
|
|
||||||
|
|
||||||
mState = State_End;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mLocals.declare (mType, name2);
|
mLocals.declare (mType, name2);
|
||||||
|
|
||||||
mState = State_End;
|
mState = State_End;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (mState==State_End)
|
||||||
|
{
|
||||||
|
getErrorHandler().warning ("Extra text after local variable declaration", loc);
|
||||||
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
|
scanner.scan (skip);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return Parser::parseName (name, loc, scanner);
|
return Parser::parseName (name, loc, scanner);
|
||||||
}
|
}
|
||||||
|
@ -61,17 +61,31 @@ bool Compiler::DeclarationParser::parseKeyword (int keyword, const TokenLoc& loc
|
||||||
else if (mState==State_Name)
|
else if (mState==State_Name)
|
||||||
{
|
{
|
||||||
// allow keywords to be used as local variable names. MW script compiler, you suck!
|
// allow keywords to be used as local variable names. MW script compiler, you suck!
|
||||||
/// \todo option to disable this atrocity.
|
|
||||||
return parseName (loc.mLiteral, loc, scanner);
|
return parseName (loc.mLiteral, loc, scanner);
|
||||||
}
|
}
|
||||||
|
else if (mState==State_End)
|
||||||
|
{
|
||||||
|
getErrorHandler().warning ("Extra text after local variable declaration", loc);
|
||||||
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
|
scanner.scan (skip);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return Parser::parseKeyword (keyword, loc, scanner);
|
return Parser::parseKeyword (keyword, loc, scanner);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
bool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||||
{
|
{
|
||||||
if (code==Scanner::S_newline && mState==State_End)
|
if (mState==State_End)
|
||||||
|
{
|
||||||
|
if (code!=Scanner::S_newline)
|
||||||
|
{
|
||||||
|
getErrorHandler().warning ("Extra text after local variable declaration", loc);
|
||||||
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
|
scanner.scan (skip);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return Parser::parseSpecial (code, loc, scanner);
|
return Parser::parseSpecial (code, loc, scanner);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual const char *what() const throw() { return "compile error";}
|
virtual const char *what() const throw() { return "Compile error";}
|
||||||
///< Return error message
|
///< Return error message
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual const char *what() const throw() { return "can't read file"; }
|
virtual const char *what() const throw() { return "Can't read file"; }
|
||||||
///< Return error message
|
///< Return error message
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual const char *what() const throw() { return "end of file"; }
|
virtual const char *what() const throw() { return "End of file"; }
|
||||||
///< Return error message
|
///< Return error message
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ namespace Compiler
|
||||||
else if (t1=='f' || t2=='f')
|
else if (t1=='f' || t2=='f')
|
||||||
mOperands.push_back ('f');
|
mOperands.push_back ('f');
|
||||||
else
|
else
|
||||||
throw std::logic_error ("failed to determine result operand type");
|
throw std::logic_error ("Failed to determine result operand type");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprParser::pop()
|
void ExprParser::pop()
|
||||||
|
@ -158,7 +158,7 @@ namespace Compiler
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
throw std::logic_error ("unknown operator");
|
throw std::logic_error ("Unknown operator");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +287,7 @@ namespace Compiler
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mExplicit.clear();
|
mExplicit.clear();
|
||||||
getErrorHandler().warning ("Ignoring stray explicit reference", loc);
|
getErrorHandler().warning ("Stray explicit reference", loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,7 +430,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
if (!hasExplicit)
|
if (!hasExplicit)
|
||||||
{
|
{
|
||||||
getErrorHandler().warning ("ignoring stray explicit reference", loc);
|
getErrorHandler().warning ("Stray explicit reference", loc);
|
||||||
mExplicit.clear();
|
mExplicit.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -735,13 +735,13 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
if (mOperands.empty() && mOperators.empty())
|
if (mOperands.empty() && mOperators.empty())
|
||||||
{
|
{
|
||||||
getErrorHandler().error ("missing expression", mTokenLoc);
|
getErrorHandler().error ("Missing expression", mTokenLoc);
|
||||||
return 'l';
|
return 'l';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mNextOperand || mOperands.empty())
|
if (mNextOperand || mOperands.empty())
|
||||||
{
|
{
|
||||||
getErrorHandler().error ("syntax error in expression", mTokenLoc);
|
getErrorHandler().error ("Syntax error in expression", mTokenLoc);
|
||||||
return 'l';
|
return 'l';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -799,7 +799,7 @@ namespace Compiler
|
||||||
++optionalCount;
|
++optionalCount;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
getErrorHandler().warning ("ignoring extra argument",
|
getErrorHandler().warning ("Extra argument",
|
||||||
stringParser.getTokenLoc());
|
stringParser.getTokenLoc());
|
||||||
}
|
}
|
||||||
else if (*iter=='X')
|
else if (*iter=='X')
|
||||||
|
@ -813,7 +813,7 @@ namespace Compiler
|
||||||
if (parser.isEmpty())
|
if (parser.isEmpty())
|
||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
getErrorHandler().warning("ignoring extra argument", parser.getTokenLoc());
|
getErrorHandler().warning("Extra argument", parser.getTokenLoc());
|
||||||
}
|
}
|
||||||
else if (*iter=='z')
|
else if (*iter=='z')
|
||||||
{
|
{
|
||||||
|
@ -825,7 +825,7 @@ namespace Compiler
|
||||||
if (discardParser.isEmpty())
|
if (discardParser.isEmpty())
|
||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
getErrorHandler().warning("ignoring extra argument", discardParser.getTokenLoc());
|
getErrorHandler().warning("Extra argument", discardParser.getTokenLoc());
|
||||||
}
|
}
|
||||||
else if (*iter=='j')
|
else if (*iter=='j')
|
||||||
{
|
{
|
||||||
|
|
|
@ -356,7 +356,7 @@ namespace Compiler
|
||||||
opcodePlaySound3DExplicit);
|
opcodePlaySound3DExplicit);
|
||||||
extensions.registerInstruction ("playsound3dvp", "cff", opcodePlaySound3DVP,
|
extensions.registerInstruction ("playsound3dvp", "cff", opcodePlaySound3DVP,
|
||||||
opcodePlaySound3DVPExplicit);
|
opcodePlaySound3DVPExplicit);
|
||||||
extensions.registerInstruction ("playloopsound3d", "c", opcodePlayLoopSound3D,
|
extensions.registerInstruction ("playloopsound3d", "cXX", opcodePlayLoopSound3D,
|
||||||
opcodePlayLoopSound3DExplicit);
|
opcodePlayLoopSound3DExplicit);
|
||||||
extensions.registerInstruction ("playloopsound3dvp", "cff", opcodePlayLoopSound3DVP,
|
extensions.registerInstruction ("playloopsound3dvp", "cff", opcodePlayLoopSound3DVP,
|
||||||
opcodePlayLoopSound3DVPExplicit);
|
opcodePlayLoopSound3DVPExplicit);
|
||||||
|
|
|
@ -98,7 +98,7 @@ namespace Compiler
|
||||||
if (mState == BeginState)
|
if (mState == BeginState)
|
||||||
{
|
{
|
||||||
if (code != Scanner::S_newline)
|
if (code != Scanner::S_newline)
|
||||||
reportWarning ("Ignoring stray special character before begin statement", loc);
|
reportWarning ("Stray special character before begin statement", loc);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ bool Compiler::JunkParser::parseName (const std::string& name, const TokenLoc& l
|
||||||
bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||||
{
|
{
|
||||||
if (keyword==mIgnoreKeyword)
|
if (keyword==mIgnoreKeyword)
|
||||||
reportWarning ("ignoring found junk", loc);
|
reportWarning ("Ignoring found junk", loc);
|
||||||
else
|
else
|
||||||
scanner.putbackKeyword (keyword, loc);
|
scanner.putbackKeyword (keyword, loc);
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scann
|
||||||
bool Compiler::JunkParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
bool Compiler::JunkParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||||
{
|
{
|
||||||
if (code==Scanner::S_member)
|
if (code==Scanner::S_member)
|
||||||
reportWarning ("ignoring found junk", loc);
|
reportWarning ("Ignoring found junk", loc);
|
||||||
else
|
else
|
||||||
scanner.putbackSpecial (code, loc);
|
scanner.putbackSpecial (code, loc);
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace Compiler
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
throw std::runtime_error ("unknown expression result type");
|
throw std::runtime_error ("Unknown expression result type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
if (mState==PotentialEndState)
|
if (mState==PotentialEndState)
|
||||||
{
|
{
|
||||||
getErrorHandler().warning ("ignoring stray string argument", loc);
|
getErrorHandler().warning ("Stray string argument", loc);
|
||||||
mState = EndState;
|
mState = EndState;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ namespace Compiler
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getErrorHandler().error ("unknown variable", loc);
|
getErrorHandler().error ("Unknown variable", loc);
|
||||||
SkipParser skip (getErrorHandler(), getContext());
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
scanner.scan (skip);
|
scanner.scan (skip);
|
||||||
return false;
|
return false;
|
||||||
|
@ -233,7 +233,7 @@ namespace Compiler
|
||||||
|
|
||||||
if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to)
|
if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to)
|
||||||
{
|
{
|
||||||
getErrorHandler().warning ("unknown variable, ignoring set instruction", loc);
|
getErrorHandler().warning ("Unknown variable", loc);
|
||||||
SkipParser skip (getErrorHandler(), getContext());
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
scanner.scan (skip);
|
scanner.scan (skip);
|
||||||
return false;
|
return false;
|
||||||
|
@ -286,7 +286,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
if (!hasExplicit && mState==ExplicitState)
|
if (!hasExplicit && mState==ExplicitState)
|
||||||
{
|
{
|
||||||
getErrorHandler().warning ("ignoring stray explicit reference", loc);
|
getErrorHandler().warning ("Stray explicit reference", loc);
|
||||||
mExplicit.clear();
|
mExplicit.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
if (!hasExplicit && !mExplicit.empty())
|
if (!hasExplicit && !mExplicit.empty())
|
||||||
{
|
{
|
||||||
getErrorHandler().warning ("ignoring stray explicit reference", loc);
|
getErrorHandler().warning ("Stray explicit reference", loc);
|
||||||
mExplicit.clear();
|
mExplicit.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +360,7 @@ namespace Compiler
|
||||||
if (mState==ExplicitState)
|
if (mState==ExplicitState)
|
||||||
{
|
{
|
||||||
// drop stray explicit reference
|
// drop stray explicit reference
|
||||||
getErrorHandler().warning ("ignoring stray explicit reference", loc);
|
getErrorHandler().warning ("Stray explicit reference", loc);
|
||||||
mState = BeginState;
|
mState = BeginState;
|
||||||
mExplicit.clear();
|
mExplicit.clear();
|
||||||
}
|
}
|
||||||
|
@ -375,8 +375,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
if (!getContext().canDeclareLocals())
|
if (!getContext().canDeclareLocals())
|
||||||
{
|
{
|
||||||
getErrorHandler().error (
|
getErrorHandler().error("Local variables cannot be declared in this context", loc);
|
||||||
"local variables can't be declared in this context", loc);
|
|
||||||
SkipParser skip (getErrorHandler(), getContext());
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
scanner.scan (skip);
|
scanner.scan (skip);
|
||||||
return true;
|
return true;
|
||||||
|
@ -412,19 +411,19 @@ namespace Compiler
|
||||||
|
|
||||||
case Scanner::K_else:
|
case Scanner::K_else:
|
||||||
|
|
||||||
getErrorHandler().warning ("ignoring stray else", loc);
|
getErrorHandler().warning ("Stray else", loc);
|
||||||
mState = EndState;
|
mState = EndState;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Scanner::K_endif:
|
case Scanner::K_endif:
|
||||||
|
|
||||||
getErrorHandler().warning ("ignoring stray endif", loc);
|
getErrorHandler().warning ("Stray endif", loc);
|
||||||
mState = EndState;
|
mState = EndState;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Scanner::K_begin:
|
case Scanner::K_begin:
|
||||||
|
|
||||||
getErrorHandler().warning ("ignoring stray begin", loc);
|
getErrorHandler().warning ("Stray begin", loc);
|
||||||
mState = EndState;
|
mState = EndState;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -491,7 +490,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
if (mState==EndState && code==Scanner::S_open)
|
if (mState==EndState && code==Scanner::S_open)
|
||||||
{
|
{
|
||||||
getErrorHandler().warning ("ignoring stray '[' or '(' at the end of the line",
|
getErrorHandler().warning ("Stray '[' or '(' at the end of the line",
|
||||||
loc);
|
loc);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -508,7 +507,7 @@ namespace Compiler
|
||||||
|
|
||||||
if (code==Scanner::S_ref && mState==SetPotentialMemberVarState)
|
if (code==Scanner::S_ref && mState==SetPotentialMemberVarState)
|
||||||
{
|
{
|
||||||
getErrorHandler().warning ("Ignoring stray explicit reference", loc);
|
getErrorHandler().warning ("Stray explicit reference", loc);
|
||||||
mState = SetState;
|
mState = SetState;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace Compiler
|
||||||
case 'f': return mFloats;
|
case 'f': return mFloats;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::logic_error ("unknown variable type");
|
throw std::logic_error ("Unknown variable type");
|
||||||
}
|
}
|
||||||
|
|
||||||
int Locals::searchIndex (char type, const std::string& name) const
|
int Locals::searchIndex (char type, const std::string& name) const
|
||||||
|
@ -48,7 +48,7 @@ namespace Compiler
|
||||||
case 'f': return mFloats;
|
case 'f': return mFloats;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::logic_error ("unknown variable type");
|
throw std::logic_error ("Unknown variable type");
|
||||||
}
|
}
|
||||||
|
|
||||||
char Locals::getType (const std::string& name) const
|
char Locals::getType (const std::string& name) const
|
||||||
|
|
|
@ -158,7 +158,7 @@ namespace Compiler
|
||||||
TokenLoc loc (mLoc);
|
TokenLoc loc (mLoc);
|
||||||
mLoc.mLiteral.clear();
|
mLoc.mLiteral.clear();
|
||||||
|
|
||||||
mErrorHandler.error ("syntax error", loc);
|
mErrorHandler.error ("Syntax error", loc);
|
||||||
throw SourceException();
|
throw SourceException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,7 +521,7 @@ namespace Compiler
|
||||||
else if (c == '<' || c == '>') // Treat <> and << as <
|
else if (c == '<' || c == '>') // Treat <> and << as <
|
||||||
{
|
{
|
||||||
special = S_cmpLT;
|
special = S_cmpLT;
|
||||||
mErrorHandler.warning (std::string("invalid operator <") + c + ", treating it as <", mLoc);
|
mErrorHandler.warning ("Invalid operator, treating it as <", mLoc);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -549,7 +549,7 @@ namespace Compiler
|
||||||
else if (c == '<' || c == '>') // Treat >< and >> as >
|
else if (c == '<' || c == '>') // Treat >< and >> as >
|
||||||
{
|
{
|
||||||
special = S_cmpGT;
|
special = S_cmpGT;
|
||||||
mErrorHandler.warning (std::string("invalid operator >") + c + ", treating it as >", mLoc);
|
mErrorHandler.warning ("Invalid operator, treating it as >", mLoc);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
#include <osg/Stats>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
using DetourNavigator::ChangeType;
|
using DetourNavigator::ChangeType;
|
||||||
|
@ -102,6 +104,20 @@ namespace DetourNavigator
|
||||||
mDone.wait(lock, [&] { return mJobs.empty(); });
|
mDone.wait(lock, [&] { return mJobs.empty(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const
|
||||||
|
{
|
||||||
|
std::size_t jobs = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
jobs = mJobs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.setAttribute(frameNumber, "NavMesh UpdateJobs", jobs);
|
||||||
|
|
||||||
|
mNavMeshTilesCache.reportStats(frameNumber, stats);
|
||||||
|
}
|
||||||
|
|
||||||
void AsyncNavMeshUpdater::process() throw()
|
void AsyncNavMeshUpdater::process() throw()
|
||||||
{
|
{
|
||||||
log("start process jobs");
|
log("start process jobs");
|
||||||
|
@ -129,12 +145,17 @@ namespace DetourNavigator
|
||||||
|
|
||||||
const auto firstStart = setFirstStart(start);
|
const auto firstStart = setFirstStart(start);
|
||||||
|
|
||||||
|
const auto navMeshCacheItem = job.mNavMeshCacheItem.lock();
|
||||||
|
|
||||||
|
if (!navMeshCacheItem)
|
||||||
|
return true;
|
||||||
|
|
||||||
const auto recastMesh = mRecastMeshManager.get().getMesh(job.mChangedTile);
|
const auto recastMesh = mRecastMeshManager.get().getMesh(job.mChangedTile);
|
||||||
const auto playerTile = *mPlayerTile.lockConst();
|
const auto playerTile = *mPlayerTile.lockConst();
|
||||||
const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile);
|
const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile);
|
||||||
|
|
||||||
const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile,
|
const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile,
|
||||||
offMeshConnections, mSettings, job.mNavMeshCacheItem, mNavMeshTilesCache);
|
offMeshConnections, mSettings, navMeshCacheItem, mNavMeshTilesCache);
|
||||||
|
|
||||||
const auto finish = std::chrono::steady_clock::now();
|
const auto finish = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
@ -143,7 +164,7 @@ namespace DetourNavigator
|
||||||
using FloatMs = std::chrono::duration<float, std::milli>;
|
using FloatMs = std::chrono::duration<float, std::milli>;
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto locked = job.mNavMeshCacheItem.lockConst();
|
const auto locked = navMeshCacheItem->lockConst();
|
||||||
log("cache updated for agent=", job.mAgentHalfExtents, " status=", status,
|
log("cache updated for agent=", job.mAgentHalfExtents, " status=", status,
|
||||||
" generation=", locked->getGeneration(),
|
" generation=", locked->getGeneration(),
|
||||||
" revision=", locked->getNavMeshRevision(),
|
" revision=", locked->getNavMeshRevision(),
|
||||||
|
@ -157,9 +178,7 @@ namespace DetourNavigator
|
||||||
boost::optional<AsyncNavMeshUpdater::Job> AsyncNavMeshUpdater::getNextJob()
|
boost::optional<AsyncNavMeshUpdater::Job> AsyncNavMeshUpdater::getNextJob()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
if (mJobs.empty())
|
if (!mHasJob.wait_for(lock, std::chrono::milliseconds(10), [&] { return !mJobs.empty(); }))
|
||||||
mHasJob.wait_for(lock, std::chrono::milliseconds(10));
|
|
||||||
if (mJobs.empty())
|
|
||||||
{
|
{
|
||||||
mFirstStart.lock()->reset();
|
mFirstStart.lock()->reset();
|
||||||
mDone.notify_all();
|
mDone.notify_all();
|
||||||
|
@ -194,7 +213,8 @@ namespace DetourNavigator
|
||||||
writeToFile(*recastMesh, mSettings.get().mRecastMeshPathPrefix + std::to_string(job.mChangedTile.x())
|
writeToFile(*recastMesh, mSettings.get().mRecastMeshPathPrefix + std::to_string(job.mChangedTile.x())
|
||||||
+ "_" + std::to_string(job.mChangedTile.y()) + "_", recastMeshRevision);
|
+ "_" + std::to_string(job.mChangedTile.y()) + "_", recastMeshRevision);
|
||||||
if (mSettings.get().mEnableWriteNavMeshToFile)
|
if (mSettings.get().mEnableWriteNavMeshToFile)
|
||||||
writeToFile(job.mNavMeshCacheItem.lockConst()->getValue(), mSettings.get().mNavMeshPathPrefix, navMeshRevision);
|
if (const auto shared = job.mNavMeshCacheItem.lock())
|
||||||
|
writeToFile(shared->lockConst()->getImpl(), mSettings.get().mNavMeshPathPrefix, navMeshRevision);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point AsyncNavMeshUpdater::setFirstStart(const std::chrono::steady_clock::time_point& value)
|
std::chrono::steady_clock::time_point AsyncNavMeshUpdater::setFirstStart(const std::chrono::steady_clock::time_point& value)
|
||||||
|
|
|
@ -44,11 +44,13 @@ namespace DetourNavigator
|
||||||
|
|
||||||
void wait();
|
void wait();
|
||||||
|
|
||||||
|
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Job
|
struct Job
|
||||||
{
|
{
|
||||||
osg::Vec3f mAgentHalfExtents;
|
osg::Vec3f mAgentHalfExtents;
|
||||||
SharedNavMeshCacheItem mNavMeshCacheItem;
|
std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem;
|
||||||
TilePosition mChangedTile;
|
TilePosition mChangedTile;
|
||||||
unsigned mTryNumber;
|
unsigned mTryNumber;
|
||||||
ChangeType mChangeType;
|
ChangeType mChangeType;
|
||||||
|
@ -72,7 +74,7 @@ namespace DetourNavigator
|
||||||
std::reference_wrapper<TileCachedRecastMeshManager> mRecastMeshManager;
|
std::reference_wrapper<TileCachedRecastMeshManager> mRecastMeshManager;
|
||||||
std::reference_wrapper<OffMeshConnectionsManager> mOffMeshConnectionsManager;
|
std::reference_wrapper<OffMeshConnectionsManager> mOffMeshConnectionsManager;
|
||||||
std::atomic_bool mShouldStop;
|
std::atomic_bool mShouldStop;
|
||||||
std::mutex mMutex;
|
mutable std::mutex mMutex;
|
||||||
std::condition_variable mHasJob;
|
std::condition_variable mHasJob;
|
||||||
std::condition_variable mDone;
|
std::condition_variable mDone;
|
||||||
Jobs mJobs;
|
Jobs mJobs;
|
||||||
|
|
|
@ -25,8 +25,6 @@ namespace
|
||||||
{
|
{
|
||||||
using namespace DetourNavigator;
|
using namespace DetourNavigator;
|
||||||
|
|
||||||
static const int doNotTransferOwnership = 0;
|
|
||||||
|
|
||||||
void initPolyMeshDetail(rcPolyMeshDetail& value)
|
void initPolyMeshDetail(rcPolyMeshDetail& value)
|
||||||
{
|
{
|
||||||
value.meshes = nullptr;
|
value.meshes = nullptr;
|
||||||
|
@ -441,56 +439,7 @@ namespace
|
||||||
return NavMeshData(navMeshData, navMeshDataSize);
|
return NavMeshData(navMeshData, navMeshDataSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateNavMeshStatusBuilder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
UpdateNavMeshStatusBuilder() = default;
|
|
||||||
|
|
||||||
UpdateNavMeshStatusBuilder removed(bool value)
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
set(UpdateNavMeshStatus::removed);
|
|
||||||
else
|
|
||||||
unset(UpdateNavMeshStatus::removed);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateNavMeshStatusBuilder added(bool value)
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
set(UpdateNavMeshStatus::added);
|
|
||||||
else
|
|
||||||
unset(UpdateNavMeshStatus::added);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateNavMeshStatusBuilder failed(bool value)
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
set(UpdateNavMeshStatus::failed);
|
|
||||||
else
|
|
||||||
unset(UpdateNavMeshStatus::failed);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateNavMeshStatus getResult() const
|
|
||||||
{
|
|
||||||
return mResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
UpdateNavMeshStatus mResult = UpdateNavMeshStatus::ignored;
|
|
||||||
|
|
||||||
void set(UpdateNavMeshStatus value)
|
|
||||||
{
|
|
||||||
mResult = static_cast<UpdateNavMeshStatus>(static_cast<unsigned>(mResult) | static_cast<unsigned>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
void unset(UpdateNavMeshStatus value)
|
|
||||||
{
|
|
||||||
mResult = static_cast<UpdateNavMeshStatus>(static_cast<unsigned>(mResult) & ~static_cast<unsigned>(value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
unsigned long getMinValuableBitsNumber(const T value)
|
unsigned long getMinValuableBitsNumber(const T value)
|
||||||
|
@ -500,49 +449,6 @@ namespace
|
||||||
++power;
|
++power;
|
||||||
return power;
|
return power;
|
||||||
}
|
}
|
||||||
|
|
||||||
dtStatus addTile(dtNavMesh& navMesh, const NavMeshData& navMeshData)
|
|
||||||
{
|
|
||||||
const dtTileRef lastRef = 0;
|
|
||||||
dtTileRef* const result = nullptr;
|
|
||||||
return navMesh.addTile(navMeshData.mValue.get(), navMeshData.mSize,
|
|
||||||
doNotTransferOwnership, lastRef, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus addTile(dtNavMesh& navMesh, const NavMeshTilesCache::Value& cachedNavMeshData)
|
|
||||||
{
|
|
||||||
const dtTileRef lastRef = 0;
|
|
||||||
dtTileRef* const result = nullptr;
|
|
||||||
return navMesh.addTile(cachedNavMeshData.get().mValue, cachedNavMeshData.get().mSize,
|
|
||||||
doNotTransferOwnership, lastRef, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
UpdateNavMeshStatus replaceTile(const SharedNavMeshCacheItem& navMeshCacheItem,
|
|
||||||
const TilePosition& changedTile, T&& navMeshData)
|
|
||||||
{
|
|
||||||
const auto locked = navMeshCacheItem.lock();
|
|
||||||
auto& navMesh = locked->getValue();
|
|
||||||
const int layer = 0;
|
|
||||||
const auto tileRef = navMesh.getTileRefAt(changedTile.x(), changedTile.y(), layer);
|
|
||||||
unsigned char** const data = nullptr;
|
|
||||||
int* const dataSize = nullptr;
|
|
||||||
const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, data, dataSize));
|
|
||||||
const auto addStatus = addTile(navMesh, navMeshData);
|
|
||||||
|
|
||||||
if (dtStatusSucceed(addStatus))
|
|
||||||
{
|
|
||||||
locked->setUsedTile(changedTile, std::forward<T>(navMeshData));
|
|
||||||
return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (removed)
|
|
||||||
locked->removeUsedTile(changedTile);
|
|
||||||
log("failed to add tile with status=", WriteDtStatus {addStatus});
|
|
||||||
return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
|
@ -591,26 +497,13 @@ namespace DetourNavigator
|
||||||
" playerTile=", playerTile,
|
" playerTile=", playerTile,
|
||||||
" changedTileDistance=", getDistance(changedTile, playerTile));
|
" changedTileDistance=", getDistance(changedTile, playerTile));
|
||||||
|
|
||||||
const auto params = *navMeshCacheItem.lockConst()->getValue().getParams();
|
const auto params = *navMeshCacheItem->lockConst()->getImpl().getParams();
|
||||||
const osg::Vec3f origin(params.orig[0], params.orig[1], params.orig[2]);
|
const osg::Vec3f origin(params.orig[0], params.orig[1], params.orig[2]);
|
||||||
|
|
||||||
const auto x = changedTile.x();
|
|
||||||
const auto y = changedTile.y();
|
|
||||||
|
|
||||||
const auto removeTile = [&] {
|
|
||||||
const auto locked = navMeshCacheItem.lock();
|
|
||||||
auto& navMesh = locked->getValue();
|
|
||||||
const auto tileRef = navMesh.getTileRefAt(x, y, 0);
|
|
||||||
const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, nullptr, nullptr));
|
|
||||||
if (removed)
|
|
||||||
locked->removeUsedTile(changedTile);
|
|
||||||
return UpdateNavMeshStatusBuilder().removed(removed).getResult();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!recastMesh)
|
if (!recastMesh)
|
||||||
{
|
{
|
||||||
log("ignore add tile: recastMesh is null");
|
log("ignore add tile: recastMesh is null");
|
||||||
return removeTile();
|
return navMeshCacheItem->lock()->removeTile(changedTile);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto recastMeshBounds = recastMesh->getBounds();
|
auto recastMeshBounds = recastMesh->getBounds();
|
||||||
|
@ -625,13 +518,13 @@ namespace DetourNavigator
|
||||||
if (isEmpty(recastMeshBounds))
|
if (isEmpty(recastMeshBounds))
|
||||||
{
|
{
|
||||||
log("ignore add tile: recastMesh is empty");
|
log("ignore add tile: recastMesh is empty");
|
||||||
return removeTile();
|
return navMeshCacheItem->lock()->removeTile(changedTile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shouldAddTile(changedTile, playerTile, std::min(settings.mMaxTilesNumber, params.maxTiles)))
|
if (!shouldAddTile(changedTile, playerTile, std::min(settings.mMaxTilesNumber, params.maxTiles)))
|
||||||
{
|
{
|
||||||
log("ignore add tile: too far from player");
|
log("ignore add tile: too far from player");
|
||||||
return removeTile();
|
return navMeshCacheItem->lock()->removeTile(changedTile);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cachedNavMeshData = navMeshTilesCache.get(agentHalfExtents, changedTile, *recastMesh, offMeshConnections);
|
auto cachedNavMeshData = navMeshTilesCache.get(agentHalfExtents, changedTile, *recastMesh, offMeshConnections);
|
||||||
|
@ -648,7 +541,7 @@ namespace DetourNavigator
|
||||||
if (!navMeshData.mValue)
|
if (!navMeshData.mValue)
|
||||||
{
|
{
|
||||||
log("ignore add tile: NavMeshData is null");
|
log("ignore add tile: NavMeshData is null");
|
||||||
return removeTile();
|
return navMeshCacheItem->lock()->removeTile(changedTile);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -665,10 +558,10 @@ namespace DetourNavigator
|
||||||
if (!cachedNavMeshData)
|
if (!cachedNavMeshData)
|
||||||
{
|
{
|
||||||
log("cache overflow");
|
log("cache overflow");
|
||||||
return replaceTile(navMeshCacheItem, changedTile, std::move(navMeshData));
|
return navMeshCacheItem->lock()->updateTile(changedTile, std::move(navMeshData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return replaceTile(navMeshCacheItem, changedTile, std::move(cachedNavMeshData));
|
return navMeshCacheItem->lock()->updateTile(changedTile, std::move(cachedNavMeshData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,21 +20,6 @@ namespace DetourNavigator
|
||||||
class RecastMesh;
|
class RecastMesh;
|
||||||
struct Settings;
|
struct Settings;
|
||||||
|
|
||||||
enum class UpdateNavMeshStatus : unsigned
|
|
||||||
{
|
|
||||||
ignored = 0,
|
|
||||||
removed = 1 << 0,
|
|
||||||
added = 1 << 1,
|
|
||||||
replaced = removed | added,
|
|
||||||
failed = 1 << 2,
|
|
||||||
lost = removed | failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool isSuccess(UpdateNavMeshStatus value)
|
|
||||||
{
|
|
||||||
return (static_cast<unsigned>(value) & static_cast<unsigned>(UpdateNavMeshStatus::failed)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float getLength(const osg::Vec2i& value)
|
inline float getLength(const osg::Vec2i& value)
|
||||||
{
|
{
|
||||||
return std::sqrt(float(osg::square(value.x()) + osg::square(value.y())));
|
return std::sqrt(float(osg::square(value.x()) + osg::square(value.y())));
|
||||||
|
|
|
@ -174,7 +174,7 @@ namespace DetourNavigator
|
||||||
if (!navMesh)
|
if (!navMesh)
|
||||||
return out;
|
return out;
|
||||||
const auto settings = getSettings();
|
const auto settings = getSettings();
|
||||||
return findSmoothPath(navMesh.lock()->getValue(), toNavMeshCoordinates(settings, agentHalfExtents),
|
return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents),
|
||||||
toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start),
|
toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start),
|
||||||
toNavMeshCoordinates(settings, end), includeFlags, settings, out);
|
toNavMeshCoordinates(settings, end), includeFlags, settings, out);
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,9 @@ namespace DetourNavigator
|
||||||
*/
|
*/
|
||||||
virtual std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const = 0;
|
virtual std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const = 0;
|
||||||
|
|
||||||
virtual Settings getSettings() const = 0;
|
virtual const Settings& getSettings() const = 0;
|
||||||
|
|
||||||
|
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,10 @@ namespace DetourNavigator
|
||||||
void NavigatorImpl::removeAgent(const osg::Vec3f& agentHalfExtents)
|
void NavigatorImpl::removeAgent(const osg::Vec3f& agentHalfExtents)
|
||||||
{
|
{
|
||||||
const auto it = mAgents.find(agentHalfExtents);
|
const auto it = mAgents.find(agentHalfExtents);
|
||||||
if (it == mAgents.end() || --it->second)
|
if (it == mAgents.end())
|
||||||
return;
|
return;
|
||||||
mAgents.erase(it);
|
if (it->second > 0)
|
||||||
mNavMeshManager.reset(agentHalfExtents);
|
--it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NavigatorImpl::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform)
|
bool NavigatorImpl::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform)
|
||||||
|
@ -113,6 +113,7 @@ namespace DetourNavigator
|
||||||
|
|
||||||
void NavigatorImpl::update(const osg::Vec3f& playerPosition)
|
void NavigatorImpl::update(const osg::Vec3f& playerPosition)
|
||||||
{
|
{
|
||||||
|
removeUnusedNavMeshes();
|
||||||
for (const auto& v : mAgents)
|
for (const auto& v : mAgents)
|
||||||
mNavMeshManager.update(playerPosition, v.first);
|
mNavMeshManager.update(playerPosition, v.first);
|
||||||
}
|
}
|
||||||
|
@ -132,11 +133,16 @@ namespace DetourNavigator
|
||||||
return mNavMeshManager.getNavMeshes();
|
return mNavMeshManager.getNavMeshes();
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings NavigatorImpl::getSettings() const
|
const Settings& NavigatorImpl::getSettings() const
|
||||||
{
|
{
|
||||||
return mSettings;
|
return mSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavigatorImpl::reportStats(unsigned int frameNumber, osg::Stats& stats) const
|
||||||
|
{
|
||||||
|
mNavMeshManager.reportStats(frameNumber, stats);
|
||||||
|
}
|
||||||
|
|
||||||
void NavigatorImpl::updateAvoidShapeId(const ObjectId id, const ObjectId avoidId)
|
void NavigatorImpl::updateAvoidShapeId(const ObjectId id, const ObjectId avoidId)
|
||||||
{
|
{
|
||||||
updateId(id, avoidId, mWaterIds);
|
updateId(id, avoidId, mWaterIds);
|
||||||
|
@ -156,4 +162,15 @@ namespace DetourNavigator
|
||||||
inserted.first->second = updateId;
|
inserted.first->second = updateId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavigatorImpl::removeUnusedNavMeshes()
|
||||||
|
{
|
||||||
|
for (auto it = mAgents.begin(); it != mAgents.end();)
|
||||||
|
{
|
||||||
|
if (it->second == 0 && mNavMeshManager.reset(it->first))
|
||||||
|
it = mAgents.erase(it);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,9 @@ namespace DetourNavigator
|
||||||
|
|
||||||
std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const override;
|
std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const override;
|
||||||
|
|
||||||
Settings getSettings() const override;
|
const Settings& getSettings() const override;
|
||||||
|
|
||||||
|
void reportStats(unsigned int frameNumber, osg::Stats& stats) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Settings mSettings;
|
Settings mSettings;
|
||||||
|
@ -58,6 +60,7 @@ namespace DetourNavigator
|
||||||
void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId);
|
void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId);
|
||||||
void updateWaterShapeId(const ObjectId id, const ObjectId waterId);
|
void updateWaterShapeId(const ObjectId id, const ObjectId waterId);
|
||||||
void updateId(const ObjectId id, const ObjectId waterId, std::unordered_map<ObjectId, ObjectId>& ids);
|
void updateId(const ObjectId id, const ObjectId waterId, std::unordered_map<ObjectId, ObjectId>& ids);
|
||||||
|
void removeUnusedNavMeshes();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
struct NavigatorStub final : public Navigator
|
class NavigatorStub final : public Navigator
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
NavigatorStub() = default;
|
NavigatorStub() = default;
|
||||||
|
|
||||||
void addAgent(const osg::Vec3f& /*agentHalfExtents*/) override {}
|
void addAgent(const osg::Vec3f& /*agentHalfExtents*/) override {}
|
||||||
|
@ -65,7 +66,7 @@ namespace DetourNavigator
|
||||||
|
|
||||||
SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override
|
SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override
|
||||||
{
|
{
|
||||||
return SharedNavMeshCacheItem();
|
return mEmptyNavMeshCacheItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const override
|
std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const override
|
||||||
|
@ -73,10 +74,16 @@ namespace DetourNavigator
|
||||||
return std::map<osg::Vec3f, SharedNavMeshCacheItem>();
|
return std::map<osg::Vec3f, SharedNavMeshCacheItem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings getSettings() const override
|
const Settings& getSettings() const override
|
||||||
{
|
{
|
||||||
return Settings {};
|
return mDefaultSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reportStats(unsigned int /*frameNumber*/, osg::Stats& /*stats*/) const override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Settings mDefaultSettings {};
|
||||||
|
SharedNavMeshCacheItem mEmptyNavMeshCacheItem;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,29 +4,113 @@
|
||||||
#include "sharednavmesh.hpp"
|
#include "sharednavmesh.hpp"
|
||||||
#include "tileposition.hpp"
|
#include "tileposition.hpp"
|
||||||
#include "navmeshtilescache.hpp"
|
#include "navmeshtilescache.hpp"
|
||||||
|
#include "dtstatus.hpp"
|
||||||
|
|
||||||
#include <components/misc/guarded.hpp>
|
#include <components/misc/guarded.hpp>
|
||||||
|
|
||||||
|
#include <DetourNavMesh.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
|
enum class UpdateNavMeshStatus : unsigned
|
||||||
|
{
|
||||||
|
ignored = 0,
|
||||||
|
removed = 1 << 0,
|
||||||
|
added = 1 << 1,
|
||||||
|
replaced = removed | added,
|
||||||
|
failed = 1 << 2,
|
||||||
|
lost = removed | failed,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool isSuccess(UpdateNavMeshStatus value)
|
||||||
|
{
|
||||||
|
return (static_cast<unsigned>(value) & static_cast<unsigned>(UpdateNavMeshStatus::failed)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateNavMeshStatusBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UpdateNavMeshStatusBuilder() = default;
|
||||||
|
|
||||||
|
UpdateNavMeshStatusBuilder removed(bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
set(UpdateNavMeshStatus::removed);
|
||||||
|
else
|
||||||
|
unset(UpdateNavMeshStatus::removed);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateNavMeshStatusBuilder added(bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
set(UpdateNavMeshStatus::added);
|
||||||
|
else
|
||||||
|
unset(UpdateNavMeshStatus::added);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateNavMeshStatusBuilder failed(bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
set(UpdateNavMeshStatus::failed);
|
||||||
|
else
|
||||||
|
unset(UpdateNavMeshStatus::failed);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateNavMeshStatus getResult() const
|
||||||
|
{
|
||||||
|
return mResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
UpdateNavMeshStatus mResult = UpdateNavMeshStatus::ignored;
|
||||||
|
|
||||||
|
void set(UpdateNavMeshStatus value)
|
||||||
|
{
|
||||||
|
mResult = static_cast<UpdateNavMeshStatus>(static_cast<unsigned>(mResult) | static_cast<unsigned>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void unset(UpdateNavMeshStatus value)
|
||||||
|
{
|
||||||
|
mResult = static_cast<UpdateNavMeshStatus>(static_cast<unsigned>(mResult) & ~static_cast<unsigned>(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline unsigned char* getRawData(NavMeshData& navMeshData)
|
||||||
|
{
|
||||||
|
return navMeshData.mValue.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned char* getRawData(NavMeshTilesCache::Value& cachedNavMeshData)
|
||||||
|
{
|
||||||
|
return cachedNavMeshData.get().mValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getSize(const NavMeshData& navMeshData)
|
||||||
|
{
|
||||||
|
return navMeshData.mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getSize(const NavMeshTilesCache::Value& cachedNavMeshData)
|
||||||
|
{
|
||||||
|
return cachedNavMeshData.get().mSize;
|
||||||
|
}
|
||||||
|
|
||||||
class NavMeshCacheItem
|
class NavMeshCacheItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NavMeshCacheItem(const NavMeshPtr& value, std::size_t generation)
|
NavMeshCacheItem(const NavMeshPtr& impl, std::size_t generation)
|
||||||
: mValue(value), mGeneration(generation), mNavMeshRevision(0)
|
: mImpl(impl), mGeneration(generation), mNavMeshRevision(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const dtNavMesh& getValue() const
|
const dtNavMesh& getImpl() const
|
||||||
{
|
{
|
||||||
return *mValue;
|
return *mImpl;
|
||||||
}
|
|
||||||
|
|
||||||
dtNavMesh& getValue()
|
|
||||||
{
|
|
||||||
return *mValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t getGeneration() const
|
std::size_t getGeneration() const
|
||||||
|
@ -39,6 +123,38 @@ namespace DetourNavigator
|
||||||
return mNavMeshRevision;
|
return mNavMeshRevision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
UpdateNavMeshStatus updateTile(const TilePosition& position, T&& navMeshData)
|
||||||
|
{
|
||||||
|
const auto removed = removeTileImpl(position);
|
||||||
|
const auto addStatus = addTileImpl(getRawData(navMeshData), getSize(navMeshData));
|
||||||
|
if (dtStatusSucceed(addStatus))
|
||||||
|
{
|
||||||
|
setUsedTile(position, std::forward<T>(navMeshData));
|
||||||
|
return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (removed)
|
||||||
|
removeUsedTile(position);
|
||||||
|
return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateNavMeshStatus removeTile(const TilePosition& position)
|
||||||
|
{
|
||||||
|
const auto removed = dtStatusSucceed(removeTileImpl(position));
|
||||||
|
if (removed)
|
||||||
|
removeUsedTile(position);
|
||||||
|
return UpdateNavMeshStatusBuilder().removed(removed).getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
NavMeshPtr mImpl;
|
||||||
|
std::size_t mGeneration;
|
||||||
|
std::size_t mNavMeshRevision;
|
||||||
|
std::map<TilePosition, std::pair<NavMeshTilesCache::Value, NavMeshData>> mUsedTiles;
|
||||||
|
|
||||||
void setUsedTile(const TilePosition& tilePosition, NavMeshTilesCache::Value value)
|
void setUsedTile(const TilePosition& tilePosition, NavMeshTilesCache::Value value)
|
||||||
{
|
{
|
||||||
mUsedTiles[tilePosition] = std::make_pair(std::move(value), NavMeshData());
|
mUsedTiles[tilePosition] = std::make_pair(std::move(value), NavMeshData());
|
||||||
|
@ -57,14 +173,26 @@ namespace DetourNavigator
|
||||||
++mNavMeshRevision;
|
++mNavMeshRevision;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
dtStatus addTileImpl(unsigned char* data, int size)
|
||||||
NavMeshPtr mValue;
|
{
|
||||||
std::size_t mGeneration;
|
const int doNotTransferOwnership = 0;
|
||||||
std::size_t mNavMeshRevision;
|
const dtTileRef lastRef = 0;
|
||||||
std::map<TilePosition, std::pair<NavMeshTilesCache::Value, NavMeshData>> mUsedTiles;
|
dtTileRef* const result = nullptr;
|
||||||
|
return mImpl->addTile(data, size, doNotTransferOwnership, lastRef, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
dtStatus removeTileImpl(const TilePosition& position)
|
||||||
|
{
|
||||||
|
const int layer = 0;
|
||||||
|
const auto tileRef = mImpl->getTileRefAt(position.x(), position.y(), layer);
|
||||||
|
unsigned char** const data = nullptr;
|
||||||
|
int* const dataSize = nullptr;
|
||||||
|
return mImpl->removeTile(tileRef, data, dataSize);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using SharedNavMeshCacheItem = Misc::SharedGuarded<NavMeshCacheItem>;
|
using GuardedNavMeshCacheItem = Misc::ScopeGuarded<NavMeshCacheItem>;
|
||||||
|
using SharedNavMeshCacheItem = std::shared_ptr<GuardedNavMeshCacheItem>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -16,6 +16,21 @@ namespace
|
||||||
{
|
{
|
||||||
return current == add ? current : ChangeType::mixed;
|
return current == add ? current : ChangeType::mixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Safely reset shared_ptr with definite underlying object destrutor call.
|
||||||
|
/// Assuming there is another thread holding copy of this shared_ptr or weak_ptr to this shared_ptr.
|
||||||
|
template <class T>
|
||||||
|
bool resetIfUnique(std::shared_ptr<T>& ptr)
|
||||||
|
{
|
||||||
|
const std::weak_ptr<T> weak(ptr);
|
||||||
|
ptr.reset();
|
||||||
|
if (auto shared = weak.lock())
|
||||||
|
{
|
||||||
|
ptr = std::move(shared);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
|
@ -79,13 +94,22 @@ namespace DetourNavigator
|
||||||
if (cached != mCache.end())
|
if (cached != mCache.end())
|
||||||
return;
|
return;
|
||||||
mCache.insert(std::make_pair(agentHalfExtents,
|
mCache.insert(std::make_pair(agentHalfExtents,
|
||||||
std::make_shared<NavMeshCacheItem>(makeEmptyNavMesh(mSettings), ++mGenerationCounter)));
|
std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), ++mGenerationCounter)));
|
||||||
log("cache add for agent=", agentHalfExtents);
|
log("cache add for agent=", agentHalfExtents);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMeshManager::reset(const osg::Vec3f& agentHalfExtents)
|
bool NavMeshManager::reset(const osg::Vec3f& agentHalfExtents)
|
||||||
{
|
{
|
||||||
|
const auto it = mCache.find(agentHalfExtents);
|
||||||
|
if (it == mCache.end())
|
||||||
|
return true;
|
||||||
|
if (!resetIfUnique(it->second))
|
||||||
|
return false;
|
||||||
mCache.erase(agentHalfExtents);
|
mCache.erase(agentHalfExtents);
|
||||||
|
mChangedTiles.erase(agentHalfExtents);
|
||||||
|
mPlayerTile.erase(agentHalfExtents);
|
||||||
|
mLastRecastMeshManagerRevision.erase(agentHalfExtents);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMeshManager::addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end)
|
void NavMeshManager::addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end)
|
||||||
|
@ -139,8 +163,8 @@ namespace DetourNavigator
|
||||||
}
|
}
|
||||||
const auto changedTiles = mChangedTiles.find(agentHalfExtents);
|
const auto changedTiles = mChangedTiles.find(agentHalfExtents);
|
||||||
{
|
{
|
||||||
const auto locked = cached.lock();
|
const auto locked = cached->lockConst();
|
||||||
const auto& navMesh = locked->getValue();
|
const auto& navMesh = locked->getImpl();
|
||||||
if (changedTiles != mChangedTiles.end())
|
if (changedTiles != mChangedTiles.end())
|
||||||
{
|
{
|
||||||
for (const auto& tile : changedTiles->second)
|
for (const auto& tile : changedTiles->second)
|
||||||
|
@ -152,10 +176,6 @@ namespace DetourNavigator
|
||||||
else
|
else
|
||||||
tileToPost->second = addChangeType(tileToPost->second, tile.second);
|
tileToPost->second = addChangeType(tileToPost->second, tile.second);
|
||||||
}
|
}
|
||||||
for (const auto& tile : tilesToPost)
|
|
||||||
changedTiles->second.erase(tile.first);
|
|
||||||
if (changedTiles->second.empty())
|
|
||||||
mChangedTiles.erase(changedTiles);
|
|
||||||
}
|
}
|
||||||
const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles);
|
const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles);
|
||||||
mRecastMeshManager.forEachTilePosition([&] (const TilePosition& tile)
|
mRecastMeshManager.forEachTilePosition([&] (const TilePosition& tile)
|
||||||
|
@ -171,6 +191,8 @@ namespace DetourNavigator
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, tilesToPost);
|
mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, tilesToPost);
|
||||||
|
if (changedTiles != mChangedTiles.end())
|
||||||
|
changedTiles->second.clear();
|
||||||
log("cache update posted for agent=", agentHalfExtents,
|
log("cache update posted for agent=", agentHalfExtents,
|
||||||
" playerTile=", lastPlayerTile->second,
|
" playerTile=", lastPlayerTile->second,
|
||||||
" recastMeshManagerRevision=", lastRevision);
|
" recastMeshManagerRevision=", lastRevision);
|
||||||
|
@ -191,6 +213,11 @@ namespace DetourNavigator
|
||||||
return mCache;
|
return mCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavMeshManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const
|
||||||
|
{
|
||||||
|
mAsyncNavMeshUpdater.reportStats(frameNumber, stats);
|
||||||
|
}
|
||||||
|
|
||||||
void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform,
|
void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform,
|
||||||
const ChangeType changeType)
|
const ChangeType changeType)
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace DetourNavigator
|
||||||
|
|
||||||
bool removeWater(const osg::Vec2i& cellPosition);
|
bool removeWater(const osg::Vec2i& cellPosition);
|
||||||
|
|
||||||
void reset(const osg::Vec3f& agentHalfExtents);
|
bool reset(const osg::Vec3f& agentHalfExtents);
|
||||||
|
|
||||||
void addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end);
|
void addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end);
|
||||||
|
|
||||||
|
@ -50,6 +50,8 @@ namespace DetourNavigator
|
||||||
|
|
||||||
std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const;
|
std::map<osg::Vec3f, SharedNavMeshCacheItem> getNavMeshes() const;
|
||||||
|
|
||||||
|
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Settings& mSettings;
|
const Settings& mSettings;
|
||||||
TileCachedRecastMeshManager mRecastMeshManager;
|
TileCachedRecastMeshManager mRecastMeshManager;
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
#include "navmeshtilescache.hpp"
|
#include "navmeshtilescache.hpp"
|
||||||
#include "exceptions.hpp"
|
#include "exceptions.hpp"
|
||||||
|
|
||||||
|
#include <osg/Stats>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
|
@ -61,8 +65,7 @@ namespace DetourNavigator
|
||||||
if (tileValues == agentValues->second.end())
|
if (tileValues == agentValues->second.end())
|
||||||
return Value();
|
return Value();
|
||||||
|
|
||||||
// TODO: use different function to make key to avoid unnecessary std::string allocation
|
const auto tile = tileValues->second.mMap.find(RecastMeshKeyView(recastMesh, offMeshConnections));
|
||||||
const auto tile = tileValues->second.mMap.find(makeNavMeshKey(recastMesh, offMeshConnections));
|
|
||||||
if (tile == tileValues->second.mMap.end())
|
if (tile == tileValues->second.mMap.end())
|
||||||
return Value();
|
return Value();
|
||||||
|
|
||||||
|
@ -85,7 +88,7 @@ namespace DetourNavigator
|
||||||
if (navMeshSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize))
|
if (navMeshSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize))
|
||||||
return Value();
|
return Value();
|
||||||
|
|
||||||
const auto navMeshKey = makeNavMeshKey(recastMesh, offMeshConnections);
|
auto navMeshKey = makeNavMeshKey(recastMesh, offMeshConnections);
|
||||||
const auto itemSize = navMeshSize + 2 * navMeshKey.size();
|
const auto itemSize = navMeshSize + 2 * navMeshKey.size();
|
||||||
|
|
||||||
if (itemSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize))
|
if (itemSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize))
|
||||||
|
@ -94,9 +97,8 @@ namespace DetourNavigator
|
||||||
while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize)
|
while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize)
|
||||||
removeLeastRecentlyUsed();
|
removeLeastRecentlyUsed();
|
||||||
|
|
||||||
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, navMeshKey);
|
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(navMeshKey));
|
||||||
// TODO: use std::string_view or some alternative to avoid navMeshKey copy into both mFreeItems and mValues
|
const auto emplaced = mValues[agentHalfExtents][changedTile].mMap.emplace(iterator->mNavMeshKey, iterator);
|
||||||
const auto emplaced = mValues[agentHalfExtents][changedTile].mMap.emplace(navMeshKey, iterator);
|
|
||||||
|
|
||||||
if (!emplaced.second)
|
if (!emplaced.second)
|
||||||
{
|
{
|
||||||
|
@ -113,6 +115,24 @@ namespace DetourNavigator
|
||||||
return Value(*this, iterator);
|
return Value(*this, iterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavMeshTilesCache::reportStats(unsigned int frameNumber, osg::Stats& stats) const
|
||||||
|
{
|
||||||
|
std::size_t navMeshCacheSize = 0;
|
||||||
|
std::size_t usedNavMeshTiles = 0;
|
||||||
|
std::size_t cachedNavMeshTiles = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
navMeshCacheSize = mUsedNavMeshDataSize;
|
||||||
|
usedNavMeshTiles = mBusyItems.size();
|
||||||
|
cachedNavMeshTiles = mFreeItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.setAttribute(frameNumber, "NavMesh CacheSize", navMeshCacheSize);
|
||||||
|
stats.setAttribute(frameNumber, "NavMesh UsedTiles", usedNavMeshTiles);
|
||||||
|
stats.setAttribute(frameNumber, "NavMesh CachedTiles", cachedNavMeshTiles);
|
||||||
|
}
|
||||||
|
|
||||||
void NavMeshTilesCache::removeLeastRecentlyUsed()
|
void NavMeshTilesCache::removeLeastRecentlyUsed()
|
||||||
{
|
{
|
||||||
const auto& item = mFreeItems.back();
|
const auto& item = mFreeItems.back();
|
||||||
|
@ -131,9 +151,10 @@ namespace DetourNavigator
|
||||||
|
|
||||||
mUsedNavMeshDataSize -= getSize(item);
|
mUsedNavMeshDataSize -= getSize(item);
|
||||||
mFreeNavMeshDataSize -= getSize(item);
|
mFreeNavMeshDataSize -= getSize(item);
|
||||||
mFreeItems.pop_back();
|
|
||||||
|
|
||||||
tileValues->second.mMap.erase(value);
|
tileValues->second.mMap.erase(value);
|
||||||
|
mFreeItems.pop_back();
|
||||||
|
|
||||||
if (!tileValues->second.mMap.empty())
|
if (!tileValues->second.mMap.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -163,4 +184,69 @@ namespace DetourNavigator
|
||||||
mFreeItems.splice(mFreeItems.begin(), mBusyItems, iterator);
|
mFreeItems.splice(mFreeItems.begin(), mBusyItems, iterator);
|
||||||
mFreeNavMeshDataSize += getSize(*iterator);
|
mFreeNavMeshDataSize += getSize(*iterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct CompareBytes
|
||||||
|
{
|
||||||
|
const char* mRhsIt;
|
||||||
|
const char* mRhsEnd;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
int operator ()(const std::vector<T>& lhs)
|
||||||
|
{
|
||||||
|
const auto lhsBegin = reinterpret_cast<const char*>(lhs.data());
|
||||||
|
const auto lhsEnd = reinterpret_cast<const char*>(lhs.data() + lhs.size());
|
||||||
|
const auto lhsSize = static_cast<std::ptrdiff_t>(lhsEnd - lhsBegin);
|
||||||
|
const auto rhsSize = static_cast<std::ptrdiff_t>(mRhsEnd - mRhsIt);
|
||||||
|
|
||||||
|
if (lhsBegin == nullptr || mRhsIt == nullptr)
|
||||||
|
{
|
||||||
|
if (lhsSize < rhsSize)
|
||||||
|
return -1;
|
||||||
|
else if (lhsSize > rhsSize)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto size = std::min(lhsSize, rhsSize);
|
||||||
|
|
||||||
|
if (const auto result = std::memcmp(lhsBegin, mRhsIt, size))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (lhsSize > rhsSize)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
mRhsIt += size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int NavMeshTilesCache::RecastMeshKeyView::compare(const std::string& other) const
|
||||||
|
{
|
||||||
|
CompareBytes compareBytes {other.data(), other.data() + other.size()};
|
||||||
|
|
||||||
|
if (const auto result = compareBytes(mRecastMesh.get().getIndices()))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (const auto result = compareBytes(mRecastMesh.get().getVertices()))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (const auto result = compareBytes(mRecastMesh.get().getAreaTypes()))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (const auto result = compareBytes(mRecastMesh.get().getWater()))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (const auto result = compareBytes(mOffMeshConnections.get()))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (compareBytes.mRhsIt < compareBytes.mRhsEnd)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,12 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace osg
|
||||||
|
{
|
||||||
|
class Stats;
|
||||||
|
}
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
|
@ -104,14 +110,68 @@ namespace DetourNavigator
|
||||||
const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections,
|
const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections,
|
||||||
NavMeshData&& value);
|
NavMeshData&& value);
|
||||||
|
|
||||||
|
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
class KeyView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KeyView() = default;
|
||||||
|
|
||||||
|
KeyView(const std::string& value)
|
||||||
|
: mValue(&value) {}
|
||||||
|
|
||||||
|
const std::string& getValue() const
|
||||||
|
{
|
||||||
|
assert(mValue);
|
||||||
|
return *mValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int compare(const std::string& other) const
|
||||||
|
{
|
||||||
|
assert(mValue);
|
||||||
|
return mValue->compare(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isLess(const KeyView& other) const
|
||||||
|
{
|
||||||
|
assert(mValue);
|
||||||
|
return other.compare(*mValue) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator <(const KeyView& lhs, const KeyView& rhs)
|
||||||
|
{
|
||||||
|
return lhs.isLess(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string* mValue = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RecastMeshKeyView : public KeyView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RecastMeshKeyView(const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections)
|
||||||
|
: mRecastMesh(recastMesh), mOffMeshConnections(offMeshConnections) {}
|
||||||
|
|
||||||
|
int compare(const std::string& other) const override;
|
||||||
|
|
||||||
|
bool isLess(const KeyView& other) const override
|
||||||
|
{
|
||||||
|
return compare(other.getValue()) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::reference_wrapper<const RecastMesh> mRecastMesh;
|
||||||
|
std::reference_wrapper<const std::vector<OffMeshConnection>> mOffMeshConnections;
|
||||||
|
};
|
||||||
|
|
||||||
struct TileMap
|
struct TileMap
|
||||||
{
|
{
|
||||||
std::map<std::string, ItemIterator> mMap;
|
std::map<KeyView, ItemIterator> mMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::mutex mMutex;
|
mutable std::mutex mMutex;
|
||||||
std::size_t mMaxNavMeshDataSize;
|
std::size_t mMaxNavMeshDataSize;
|
||||||
std::size_t mUsedNavMeshDataSize;
|
std::size_t mUsedNavMeshDataSize;
|
||||||
std::size_t mFreeNavMeshDataSize;
|
std::size_t mFreeNavMeshDataSize;
|
||||||
|
|
|
@ -61,8 +61,8 @@ namespace DetourNavigator
|
||||||
for (const auto& tile : currentTiles)
|
for (const auto& tile : currentTiles)
|
||||||
if (!newTiles.count(tile) && removeTile(id, tile, tiles.get()))
|
if (!newTiles.count(tile) && removeTile(id, tile, tiles.get()))
|
||||||
changedTiles.push_back(tile);
|
changedTiles.push_back(tile);
|
||||||
std::swap(currentTiles, newTiles);
|
|
||||||
}
|
}
|
||||||
|
std::swap(currentTiles, newTiles);
|
||||||
if (!changedTiles.empty())
|
if (!changedTiles.empty())
|
||||||
++mRevision;
|
++mRevision;
|
||||||
return changedTiles;
|
return changedTiles;
|
||||||
|
|
|
@ -127,7 +127,7 @@ std::string ESMReader::getHString()
|
||||||
// them. For some reason, they break the rules, and contain a byte
|
// them. For some reason, they break the rules, and contain a byte
|
||||||
// (value 0) even if the header says there is no data. If
|
// (value 0) even if the header says there is no data. If
|
||||||
// Morrowind accepts it, so should we.
|
// Morrowind accepts it, so should we.
|
||||||
if (mCtx.leftSub == 0)
|
if (mCtx.leftSub == 0 && mCtx.leftRec != 0)
|
||||||
{
|
{
|
||||||
// Skip the following zero byte
|
// Skip the following zero byte
|
||||||
mCtx.leftRec--;
|
mCtx.leftRec--;
|
||||||
|
|
|
@ -11,18 +11,9 @@ class ESMWriter;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Texture used for texturing landscape.
|
* Texture used for texturing landscape.
|
||||||
*
|
* They are indexed by 'num', but still use 'id' to override base records.
|
||||||
* They are probably indexed by 'num', not 'id', but I don't know for
|
* Original editor even does not allow to create new records with existing ID's.
|
||||||
* sure. And num is not unique between files, so one option is to keep
|
* TODO: currently OpenMW-CS does not allow to override LTEX records at all.
|
||||||
* a separate list for each input file (that has LTEX records, of
|
|
||||||
* course.) We also need to resolve references to already existing
|
|
||||||
* land textures to save space.
|
|
||||||
|
|
||||||
* I'm not sure if it is even possible to override existing land
|
|
||||||
* textures, probably not. I'll have to try it, and have to mimic the
|
|
||||||
* behaviour of morrowind. First, check what you are allowed to do in
|
|
||||||
* the editor. Then make an esp which changes a commonly used land
|
|
||||||
* texture, and see if it affects the game.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct LandTexture
|
struct LandTexture
|
||||||
|
|
|
@ -104,7 +104,7 @@ namespace ESM
|
||||||
}
|
}
|
||||||
|
|
||||||
mScriptData.resize(subSize);
|
mScriptData.resize(subSize);
|
||||||
esm.getExact(&mScriptData[0], mScriptData.size());
|
esm.getExact(mScriptData.data(), mScriptData.size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESM::FourCC<'S','C','T','X'>::value:
|
case ESM::FourCC<'S','C','T','X'>::value:
|
||||||
|
@ -156,7 +156,7 @@ namespace ESM
|
||||||
}
|
}
|
||||||
|
|
||||||
esm.startSubRecord("SCDT");
|
esm.startSubRecord("SCDT");
|
||||||
esm.write(reinterpret_cast<const char * >(&mScriptData[0]), mData.mScriptDataSize);
|
esm.write(reinterpret_cast<const char *>(mScriptData.data()), mData.mScriptDataSize);
|
||||||
esm.endRecord("SCDT");
|
esm.endRecord("SCDT");
|
||||||
|
|
||||||
esm.writeHNOString("SCTX", mScriptText);
|
esm.writeHNOString("SCTX", mScriptText);
|
||||||
|
|
|
@ -46,19 +46,6 @@ namespace ESMTerrain
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const ESM::Land::LandData *LandObject::getData(int flags) const
|
|
||||||
{
|
|
||||||
if ((mData.mDataLoaded & flags) != flags)
|
|
||||||
return nullptr;
|
|
||||||
return &mData;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LandObject::getPlugin() const
|
|
||||||
{
|
|
||||||
return mLand->mPlugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const float defaultHeight = ESM::Land::DEFAULT_HEIGHT;
|
const float defaultHeight = ESM::Land::DEFAULT_HEIGHT;
|
||||||
|
|
||||||
Storage::Storage(const VFS::Manager *vfs, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps)
|
Storage::Storage(const VFS::Manager *vfs, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps)
|
||||||
|
@ -158,7 +145,7 @@ namespace ESMTerrain
|
||||||
normal.normalize();
|
normal.normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::fixColour (osg::Vec4f& color, int cellX, int cellY, int col, int row, LandCache& cache)
|
void Storage::fixColour (osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache)
|
||||||
{
|
{
|
||||||
if (col == ESM::Land::LAND_SIZE-1)
|
if (col == ESM::Land::LAND_SIZE-1)
|
||||||
{
|
{
|
||||||
|
@ -175,22 +162,22 @@ namespace ESMTerrain
|
||||||
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0;
|
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0;
|
||||||
if (data)
|
if (data)
|
||||||
{
|
{
|
||||||
color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
|
color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3];
|
||||||
color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
|
color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1];
|
||||||
color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f;
|
color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
color.r() = 1;
|
color.r() = 255;
|
||||||
color.g() = 1;
|
color.g() = 255;
|
||||||
color.b() = 1;
|
color.b() = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center,
|
void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center,
|
||||||
osg::ref_ptr<osg::Vec3Array> positions,
|
osg::ref_ptr<osg::Vec3Array> positions,
|
||||||
osg::ref_ptr<osg::Vec3Array> normals,
|
osg::ref_ptr<osg::Vec3Array> normals,
|
||||||
osg::ref_ptr<osg::Vec4Array> colours)
|
osg::ref_ptr<osg::Vec4ubArray> colours)
|
||||||
{
|
{
|
||||||
// LOD level n means every 2^n-th vertex is kept
|
// LOD level n means every 2^n-th vertex is kept
|
||||||
size_t increment = static_cast<size_t>(1) << lodLevel;
|
size_t increment = static_cast<size_t>(1) << lodLevel;
|
||||||
|
@ -207,7 +194,7 @@ namespace ESMTerrain
|
||||||
colours->resize(numVerts*numVerts);
|
colours->resize(numVerts*numVerts);
|
||||||
|
|
||||||
osg::Vec3f normal;
|
osg::Vec3f normal;
|
||||||
osg::Vec4f color;
|
osg::Vec4ub color;
|
||||||
|
|
||||||
float vertY = 0;
|
float vertY = 0;
|
||||||
float vertX = 0;
|
float vertX = 0;
|
||||||
|
@ -295,20 +282,20 @@ namespace ESMTerrain
|
||||||
if (colourData)
|
if (colourData)
|
||||||
{
|
{
|
||||||
for (int i=0; i<3; ++i)
|
for (int i=0; i<3; ++i)
|
||||||
color[i] = colourData->mColours[srcArrayIndex+i] / 255.f;
|
color[i] = colourData->mColours[srcArrayIndex+i];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
color.r() = 1;
|
color.r() = 255;
|
||||||
color.g() = 1;
|
color.g() = 255;
|
||||||
color.b() = 1;
|
color.b() = 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlike normals, colors mostly connect seamlessly between cells, but not always...
|
// Unlike normals, colors mostly connect seamlessly between cells, but not always...
|
||||||
if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
|
if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
|
||||||
fixColour(color, cellX, cellY, col, row, cache);
|
fixColour(color, cellX, cellY, col, row, cache);
|
||||||
|
|
||||||
color.a() = 1;
|
color.a() = 255;
|
||||||
|
|
||||||
(*colours)[static_cast<unsigned int>(vertX*numVerts + vertY)] = color;
|
(*colours)[static_cast<unsigned int>(vertX*numVerts + vertY)] = color;
|
||||||
|
|
||||||
|
@ -388,8 +375,7 @@ namespace ESMTerrain
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::getBlendmaps(float chunkSize, const osg::Vec2f &chunkCenter,
|
void Storage::getBlendmaps(float chunkSize, const osg::Vec2f &chunkCenter, ImageVector &blendmaps, std::vector<Terrain::LayerInfo> &layerList)
|
||||||
bool pack, ImageVector &blendmaps, std::vector<Terrain::LayerInfo> &layerList)
|
|
||||||
{
|
{
|
||||||
osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize/2.f, chunkSize/2.f);
|
osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize/2.f, chunkSize/2.f);
|
||||||
int cellX = static_cast<int>(std::floor(origin.x()));
|
int cellX = static_cast<int>(std::floor(origin.x()));
|
||||||
|
@ -429,11 +415,8 @@ namespace ESMTerrain
|
||||||
layerList.push_back(getLayerInfo(getTextureName(*it)));
|
layerList.push_back(getLayerInfo(getTextureName(*it)));
|
||||||
}
|
}
|
||||||
|
|
||||||
int numTextures = textureIndices.size();
|
// size-1 since the base layer doesn't need blending
|
||||||
// numTextures-1 since the base layer doesn't need blending
|
int numBlendmaps = textureIndices.size() - 1;
|
||||||
int numBlendmaps = pack ? static_cast<int>(std::ceil((numTextures - 1) / 4.f)) : (numTextures - 1);
|
|
||||||
|
|
||||||
int channels = pack ? 4 : 1;
|
|
||||||
|
|
||||||
// Second iteration - create and fill in the blend maps
|
// Second iteration - create and fill in the blend maps
|
||||||
const int blendmapSize = (realTextureSize-1) * chunkSize + 1;
|
const int blendmapSize = (realTextureSize-1) * chunkSize + 1;
|
||||||
|
@ -443,10 +426,8 @@ namespace ESMTerrain
|
||||||
|
|
||||||
for (int i=0; i<numBlendmaps; ++i)
|
for (int i=0; i<numBlendmaps; ++i)
|
||||||
{
|
{
|
||||||
GLenum format = pack ? GL_RGBA : GL_ALPHA;
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Image> image (new osg::Image);
|
osg::ref_ptr<osg::Image> image (new osg::Image);
|
||||||
image->allocateImage(blendmapImageSize, blendmapImageSize, 1, format, GL_UNSIGNED_BYTE);
|
image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE);
|
||||||
unsigned char* pData = image->data();
|
unsigned char* pData = image->data();
|
||||||
|
|
||||||
for (int y=0; y<blendmapSize; ++y)
|
for (int y=0; y<blendmapSize; ++y)
|
||||||
|
@ -456,18 +437,16 @@ namespace ESMTerrain
|
||||||
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart, cache);
|
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart, cache);
|
||||||
assert(textureIndicesMap.find(id) != textureIndicesMap.end());
|
assert(textureIndicesMap.find(id) != textureIndicesMap.end());
|
||||||
int layerIndex = textureIndicesMap.find(id)->second;
|
int layerIndex = textureIndicesMap.find(id)->second;
|
||||||
int blendIndex = (pack ? static_cast<int>(std::floor((layerIndex - 1) / 4.f)) : layerIndex - 1);
|
|
||||||
int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0;
|
|
||||||
|
|
||||||
int alpha = (blendIndex == i) ? 255 : 0;
|
int alpha = (layerIndex == i+1) ? 255 : 0;
|
||||||
|
|
||||||
int realY = (blendmapSize - y - 1)*imageScaleFactor;
|
int realY = (blendmapSize - y - 1)*imageScaleFactor;
|
||||||
int realX = x*imageScaleFactor;
|
int realX = x*imageScaleFactor;
|
||||||
|
|
||||||
pData[((realY+0)*blendmapImageSize + realX + 0)*channels + channel] = alpha;
|
pData[(realY+0)*blendmapImageSize + realX + 0] = alpha;
|
||||||
pData[((realY+1)*blendmapImageSize + realX + 0)*channels + channel] = alpha;
|
pData[(realY+1)*blendmapImageSize + realX + 0] = alpha;
|
||||||
pData[((realY+0)*blendmapImageSize + realX + 1)*channels + channel] = alpha;
|
pData[(realY+0)*blendmapImageSize + realX + 1] = alpha;
|
||||||
pData[((realY+1)*blendmapImageSize + realX + 1)*channels + channel] = alpha;
|
pData[(realY+1)*blendmapImageSize + realX + 1] = alpha;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
blendmaps.push_back(image);
|
blendmaps.push_back(image);
|
||||||
|
|
|
@ -29,8 +29,17 @@ namespace ESMTerrain
|
||||||
|
|
||||||
META_Object(ESMTerrain, LandObject)
|
META_Object(ESMTerrain, LandObject)
|
||||||
|
|
||||||
const ESM::Land::LandData* getData(int flags) const;
|
inline const ESM::Land::LandData* getData(int flags) const
|
||||||
int getPlugin() const;
|
{
|
||||||
|
if ((mData.mDataLoaded & flags) != flags)
|
||||||
|
return nullptr;
|
||||||
|
return &mData;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getPlugin() const
|
||||||
|
{
|
||||||
|
return mLand->mPlugin;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const ESM::Land* mLand;
|
const ESM::Land* mLand;
|
||||||
|
@ -75,7 +84,7 @@ namespace ESMTerrain
|
||||||
virtual void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center,
|
virtual void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center,
|
||||||
osg::ref_ptr<osg::Vec3Array> positions,
|
osg::ref_ptr<osg::Vec3Array> positions,
|
||||||
osg::ref_ptr<osg::Vec3Array> normals,
|
osg::ref_ptr<osg::Vec3Array> normals,
|
||||||
osg::ref_ptr<osg::Vec4Array> colours);
|
osg::ref_ptr<osg::Vec4ubArray> colours);
|
||||||
|
|
||||||
/// Create textures holding layer blend values for a terrain chunk.
|
/// Create textures holding layer blend values for a terrain chunk.
|
||||||
/// @note The terrain chunk shouldn't be larger than one cell since otherwise we might
|
/// @note The terrain chunk shouldn't be larger than one cell since otherwise we might
|
||||||
|
@ -83,13 +92,9 @@ namespace ESMTerrain
|
||||||
/// @note May be called from background threads.
|
/// @note May be called from background threads.
|
||||||
/// @param chunkSize size of the terrain chunk in cell units
|
/// @param chunkSize size of the terrain chunk in cell units
|
||||||
/// @param chunkCenter center of the chunk in cell units
|
/// @param chunkCenter center of the chunk in cell units
|
||||||
/// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) -
|
|
||||||
/// otherwise, each texture contains blend values for one layer only. Shader-based rendering
|
|
||||||
/// can utilize packing, FFP can't.
|
|
||||||
/// @param blendmaps created blendmaps will be written here
|
/// @param blendmaps created blendmaps will be written here
|
||||||
/// @param layerList names of the layer textures used will be written here
|
/// @param layerList names of the layer textures used will be written here
|
||||||
virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, bool pack,
|
virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
||||||
ImageVector& blendmaps,
|
|
||||||
std::vector<Terrain::LayerInfo>& layerList);
|
std::vector<Terrain::LayerInfo>& layerList);
|
||||||
|
|
||||||
virtual float getHeightAt (const osg::Vec3f& worldPos);
|
virtual float getHeightAt (const osg::Vec3f& worldPos);
|
||||||
|
@ -105,21 +110,20 @@ namespace ESMTerrain
|
||||||
private:
|
private:
|
||||||
const VFS::Manager* mVFS;
|
const VFS::Manager* mVFS;
|
||||||
|
|
||||||
void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache);
|
inline void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache);
|
||||||
void fixColour (osg::Vec4f& colour, int cellX, int cellY, int col, int row, LandCache& cache);
|
inline void fixColour (osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache);
|
||||||
void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache);
|
inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache);
|
||||||
|
|
||||||
float getVertexHeight (const ESM::Land::LandData* data, int x, int y);
|
inline float getVertexHeight (const ESM::Land::LandData* data, int x, int y);
|
||||||
|
|
||||||
const LandObject* getLand(int cellX, int cellY, LandCache& cache);
|
inline const LandObject* getLand(int cellX, int cellY, LandCache& cache);
|
||||||
|
|
||||||
// Since plugins can define new texture palettes, we need to know the plugin index too
|
// Since plugins can define new texture palettes, we need to know the plugin index too
|
||||||
// in order to retrieve the correct texture name.
|
// in order to retrieve the correct texture name.
|
||||||
// pair <texture id, plugin id>
|
// pair <texture id, plugin id>
|
||||||
typedef std::pair<short, short> UniqueTextureId;
|
typedef std::pair<short, short> UniqueTextureId;
|
||||||
|
|
||||||
UniqueTextureId getVtexIndexAt(int cellX, int cellY,
|
inline UniqueTextureId getVtexIndexAt(int cellX, int cellY, int x, int y, LandCache&);
|
||||||
int x, int y, LandCache&);
|
|
||||||
std::string getTextureName (UniqueTextureId id);
|
std::string getTextureName (UniqueTextureId id);
|
||||||
|
|
||||||
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
|
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
|
||||||
|
|
|
@ -83,38 +83,6 @@ namespace Misc
|
||||||
std::mutex mMutex;
|
std::mutex mMutex;
|
||||||
T mValue;
|
T mValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
|
||||||
class SharedGuarded
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SharedGuarded()
|
|
||||||
: mMutex(std::make_shared<std::mutex>()), mValue()
|
|
||||||
{}
|
|
||||||
|
|
||||||
SharedGuarded(std::shared_ptr<T> value)
|
|
||||||
: mMutex(std::make_shared<std::mutex>()), mValue(std::move(value))
|
|
||||||
{}
|
|
||||||
|
|
||||||
Locked<T> lock() const
|
|
||||||
{
|
|
||||||
return Locked<T>(*mMutex, *mValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
Locked<const T> lockConst() const
|
|
||||||
{
|
|
||||||
return Locked<const T>(*mMutex, *mValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
operator bool() const
|
|
||||||
{
|
|
||||||
return static_cast<bool>(mValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<std::mutex> mMutex;
|
|
||||||
std::shared_ptr<T> mValue;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -120,7 +120,7 @@ void NiRotatingParticlesData::read(NIFStream *nif)
|
||||||
|
|
||||||
void NiPosData::read(NIFStream *nif)
|
void NiPosData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
mKeyList.reset(new Vector3KeyMap);
|
mKeyList = std::make_shared<Vector3KeyMap>();
|
||||||
mKeyList->read(nif);
|
mKeyList->read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,14 +128,14 @@ void NiUVData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
for(int i = 0;i < 4;i++)
|
for(int i = 0;i < 4;i++)
|
||||||
{
|
{
|
||||||
mKeyList[i].reset(new FloatKeyMap);
|
mKeyList[i] = std::make_shared<FloatKeyMap>();
|
||||||
mKeyList[i]->read(nif);
|
mKeyList[i]->read(nif);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiFloatData::read(NIFStream *nif)
|
void NiFloatData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
mKeyList.reset(new FloatKeyMap);
|
mKeyList = std::make_shared<FloatKeyMap>();
|
||||||
mKeyList->read(nif);
|
mKeyList->read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ void NiPixelData::read(NIFStream *nif)
|
||||||
|
|
||||||
void NiColorData::read(NIFStream *nif)
|
void NiColorData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
mKeyMap.reset(new Vector4KeyMap);
|
mKeyMap = std::make_shared<Vector4KeyMap>();
|
||||||
mKeyMap->read(nif);
|
mKeyMap->read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ void NiMorphData::read(NIFStream *nif)
|
||||||
mMorphs.resize(morphCount);
|
mMorphs.resize(morphCount);
|
||||||
for(int i = 0;i < morphCount;i++)
|
for(int i = 0;i < morphCount;i++)
|
||||||
{
|
{
|
||||||
mMorphs[i].mKeyFrames.reset(new FloatKeyMap);
|
mMorphs[i].mKeyFrames = std::make_shared<FloatKeyMap>();
|
||||||
mMorphs[i].mKeyFrames->read(nif, true);
|
mMorphs[i].mKeyFrames->read(nif, true);
|
||||||
nif->getVector3s(mMorphs[i].mVertices, vertCount);
|
nif->getVector3s(mMorphs[i].mVertices, vertCount);
|
||||||
}
|
}
|
||||||
|
@ -239,22 +239,22 @@ void NiMorphData::read(NIFStream *nif)
|
||||||
|
|
||||||
void NiKeyframeData::read(NIFStream *nif)
|
void NiKeyframeData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
mRotations.reset(new QuaternionKeyMap);
|
mRotations = std::make_shared<QuaternionKeyMap>();
|
||||||
mRotations->read(nif);
|
mRotations->read(nif);
|
||||||
if(mRotations->mInterpolationType == Vector3KeyMap::sXYZInterpolation)
|
if(mRotations->mInterpolationType == Vector3KeyMap::sXYZInterpolation)
|
||||||
{
|
{
|
||||||
//Chomp unused float
|
//Chomp unused float
|
||||||
nif->getFloat();
|
nif->getFloat();
|
||||||
mXRotations.reset(new FloatKeyMap);
|
mXRotations = std::make_shared<FloatKeyMap>();
|
||||||
mYRotations.reset(new FloatKeyMap);
|
mYRotations = std::make_shared<FloatKeyMap>();
|
||||||
mZRotations.reset(new FloatKeyMap);
|
mZRotations = std::make_shared<FloatKeyMap>();
|
||||||
mXRotations->read(nif, true);
|
mXRotations->read(nif, true);
|
||||||
mYRotations->read(nif, true);
|
mYRotations->read(nif, true);
|
||||||
mZRotations->read(nif, true);
|
mZRotations->read(nif, true);
|
||||||
}
|
}
|
||||||
mTranslations.reset(new Vector3KeyMap);
|
mTranslations = std::make_shared<Vector3KeyMap>();
|
||||||
mTranslations->read(nif);
|
mTranslations->read(nif);
|
||||||
mScales.reset(new FloatKeyMap);
|
mScales = std::make_shared<FloatKeyMap>();
|
||||||
mScales->read(nif);
|
mScales->read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -311,10 +311,11 @@ void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
|
||||||
RollController::RollController(const Nif::NiFloatData *data)
|
RollController::RollController(const Nif::NiFloatData *data)
|
||||||
: mData(data->mKeyList, 1.f)
|
: mData(data->mKeyList, 1.f)
|
||||||
|
, mStartingTime(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
RollController::RollController()
|
RollController::RollController() : mStartingTime(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,165 +0,0 @@
|
||||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
|
|
||||||
*
|
|
||||||
* This library is open source and may be redistributed and/or modified under
|
|
||||||
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
|
|
||||||
* (at your option) any later version. The full license is in LICENSE file
|
|
||||||
* included with this distribution, and on the openscenegraph.org website.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* OpenSceneGraph Public License for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "objectcache.hpp"
|
|
||||||
|
|
||||||
#include <osg/Object>
|
|
||||||
#include <osg/Node>
|
|
||||||
|
|
||||||
namespace Resource
|
|
||||||
{
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// ObjectCache
|
|
||||||
//
|
|
||||||
ObjectCache::ObjectCache():
|
|
||||||
osg::Referenced(true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectCache::~ObjectCache()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectCache::addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp)
|
|
||||||
{
|
|
||||||
if (!object)
|
|
||||||
{
|
|
||||||
OSG_ALWAYS << " trying to add NULL object to cache for " << filename << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
|
||||||
_objectCache[filename]=ObjectTimeStampPair(object,timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Object> ObjectCache::getRefFromObjectCache(const std::string& fileName)
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
|
||||||
ObjectCacheMap::iterator itr = _objectCache.find(fileName);
|
|
||||||
if (itr!=_objectCache.end())
|
|
||||||
{
|
|
||||||
return itr->second.first;
|
|
||||||
}
|
|
||||||
else return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ObjectCache::checkInObjectCache(const std::string &fileName, double timeStamp)
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
|
||||||
ObjectCacheMap::iterator itr = _objectCache.find(fileName);
|
|
||||||
if (itr!=_objectCache.end())
|
|
||||||
{
|
|
||||||
itr->second.second = timeStamp;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectCache::updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime)
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
|
||||||
|
|
||||||
// look for objects with external references and update their time stamp.
|
|
||||||
for(ObjectCacheMap::iterator itr=_objectCache.begin();
|
|
||||||
itr!=_objectCache.end();
|
|
||||||
++itr)
|
|
||||||
{
|
|
||||||
// If ref count is greater than 1, the object has an external reference.
|
|
||||||
// If the timestamp is yet to be initialized, it needs to be updated too.
|
|
||||||
if (itr->second.first->referenceCount()>1 || itr->second.second == 0.0)
|
|
||||||
{
|
|
||||||
// So update it.
|
|
||||||
itr->second.second = referenceTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectCache::removeExpiredObjectsInCache(double expiryTime)
|
|
||||||
{
|
|
||||||
std::vector<osg::ref_ptr<osg::Object> > objectsToRemove;
|
|
||||||
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
|
||||||
|
|
||||||
// Remove expired entries from object cache
|
|
||||||
ObjectCacheMap::iterator oitr = _objectCache.begin();
|
|
||||||
while(oitr != _objectCache.end())
|
|
||||||
{
|
|
||||||
if (oitr->second.second<=expiryTime)
|
|
||||||
{
|
|
||||||
objectsToRemove.push_back(oitr->second.first);
|
|
||||||
_objectCache.erase(oitr++);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++oitr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// note, actual unref happens outside of the lock
|
|
||||||
objectsToRemove.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectCache::removeFromObjectCache(const std::string& fileName)
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
|
||||||
ObjectCacheMap::iterator itr = _objectCache.find(fileName);
|
|
||||||
if (itr!=_objectCache.end()) _objectCache.erase(itr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectCache::clear()
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
|
||||||
_objectCache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectCache::releaseGLObjects(osg::State* state)
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
|
||||||
|
|
||||||
for(ObjectCacheMap::iterator itr = _objectCache.begin();
|
|
||||||
itr != _objectCache.end();
|
|
||||||
++itr)
|
|
||||||
{
|
|
||||||
osg::Object* object = itr->second.first.get();
|
|
||||||
object->releaseGLObjects(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectCache::accept(osg::NodeVisitor &nv)
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
|
||||||
|
|
||||||
for(ObjectCacheMap::iterator itr = _objectCache.begin();
|
|
||||||
itr != _objectCache.end();
|
|
||||||
++itr)
|
|
||||||
{
|
|
||||||
osg::Object* object = itr->second.first.get();
|
|
||||||
if (object)
|
|
||||||
{
|
|
||||||
osg::Node* node = dynamic_cast<osg::Node*>(object);
|
|
||||||
if (node)
|
|
||||||
node->accept(nv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int ObjectCache::getCacheSize() const
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
|
||||||
return _objectCache.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +1,8 @@
|
||||||
// Resource ObjectCache for OpenMW, forked from osgDB ObjectCache by Robert Osfield, see copyright notice below.
|
// Resource ObjectCache for OpenMW, forked from osgDB ObjectCache by Robert Osfield, see copyright notice below.
|
||||||
// The main change from the upstream version is that removeExpiredObjectsInCache no longer keeps a lock while the unref happens.
|
// Changes:
|
||||||
|
// - removeExpiredObjectsInCache no longer keeps a lock while the unref happens.
|
||||||
|
// - template allows customized KeyType.
|
||||||
|
// - objects with uninitialized time stamp are not removed.
|
||||||
|
|
||||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
|
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
|
||||||
*
|
*
|
||||||
|
@ -19,6 +22,7 @@
|
||||||
|
|
||||||
#include <osg/Referenced>
|
#include <osg/Referenced>
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
#include <osg/Node>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -32,11 +36,13 @@ namespace osg
|
||||||
|
|
||||||
namespace Resource {
|
namespace Resource {
|
||||||
|
|
||||||
class ObjectCache : public osg::Referenced
|
template <typename KeyType>
|
||||||
|
class GenericObjectCache : public osg::Referenced
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ObjectCache();
|
GenericObjectCache()
|
||||||
|
: osg::Referenced(true) {}
|
||||||
|
|
||||||
/** For each object in the cache which has an reference count greater than 1
|
/** For each object in the cache which has an reference count greater than 1
|
||||||
* (and therefore referenced by elsewhere in the application) set the time stamp
|
* (and therefore referenced by elsewhere in the application) set the time stamp
|
||||||
|
@ -44,59 +50,149 @@ class ObjectCache : public osg::Referenced
|
||||||
* This would typically be called once per frame by applications which are doing database paging,
|
* This would typically be called once per frame by applications which are doing database paging,
|
||||||
* and need to prune objects that are no longer required.
|
* and need to prune objects that are no longer required.
|
||||||
* The time used should be taken from the FrameStamp::getReferenceTime().*/
|
* The time used should be taken from the FrameStamp::getReferenceTime().*/
|
||||||
void updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime);
|
void updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime)
|
||||||
|
{
|
||||||
|
// look for objects with external references and update their time stamp.
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::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.
|
||||||
|
// If the timestamp is yet to be initialized, it needs to be updated too.
|
||||||
|
if (itr->second.first->referenceCount()>1 || itr->second.second == 0.0)
|
||||||
|
itr->second.second = referenceTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Removed object in the cache which have a time stamp at or before the specified expiry time.
|
/** Removed object in the cache which have a time stamp at or before the specified expiry time.
|
||||||
* This would typically be called once per frame by applications which are doing database paging,
|
* This would typically be called once per frame by applications which are doing database paging,
|
||||||
* and need to prune objects that are no longer required, and called after the a called
|
* and need to prune objects that are no longer required, and called after the a called
|
||||||
* after the call to updateTimeStampOfObjectsInCacheWithExternalReferences(expirtyTime).*/
|
* after the call to updateTimeStampOfObjectsInCacheWithExternalReferences(expirtyTime).*/
|
||||||
void removeExpiredObjectsInCache(double expiryTime);
|
void removeExpiredObjectsInCache(double expiryTime)
|
||||||
|
{
|
||||||
|
std::vector<osg::ref_ptr<osg::Object> > objectsToRemove;
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||||
|
// Remove expired entries from object cache
|
||||||
|
typename ObjectCacheMap::iterator oitr = _objectCache.begin();
|
||||||
|
while(oitr != _objectCache.end())
|
||||||
|
{
|
||||||
|
if (oitr->second.second<=expiryTime)
|
||||||
|
{
|
||||||
|
objectsToRemove.push_back(oitr->second.first);
|
||||||
|
_objectCache.erase(oitr++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++oitr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// note, actual unref happens outside of the lock
|
||||||
|
objectsToRemove.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/** Remove all objects in the cache regardless of having external references or expiry times.*/
|
/** Remove all objects in the cache regardless of having external references or expiry times.*/
|
||||||
void clear();
|
void clear()
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||||
|
_objectCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/** Add a filename,object,timestamp triple to the Registry::ObjectCache.*/
|
/** Add a key,object,timestamp triple to the Registry::ObjectCache.*/
|
||||||
void addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp = 0.0);
|
void addEntryToObjectCache(const KeyType& key, osg::Object* object, double timestamp = 0.0)
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||||
|
_objectCache[key]=ObjectTimeStampPair(object,timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
/** Remove Object from cache.*/
|
/** Remove Object from cache.*/
|
||||||
void removeFromObjectCache(const std::string& fileName);
|
void removeFromObjectCache(const KeyType& key)
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||||
|
typename ObjectCacheMap::iterator itr = _objectCache.find(key);
|
||||||
|
if (itr!=_objectCache.end()) _objectCache.erase(itr);
|
||||||
|
}
|
||||||
|
|
||||||
/** Get an ref_ptr<Object> from the object cache*/
|
/** Get an ref_ptr<Object> from the object cache*/
|
||||||
osg::ref_ptr<osg::Object> getRefFromObjectCache(const std::string& fileName);
|
osg::ref_ptr<osg::Object> getRefFromObjectCache(const KeyType& key)
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||||
|
typename ObjectCacheMap::iterator itr = _objectCache.find(key);
|
||||||
|
if (itr!=_objectCache.end())
|
||||||
|
return itr->second.first;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** Check if an object is in the cache, and if it is, update its usage time stamp. */
|
/** Check if an object is in the cache, and if it is, update its usage time stamp. */
|
||||||
bool checkInObjectCache(const std::string& fileName, double timeStamp);
|
bool checkInObjectCache(const KeyType& key, double timeStamp)
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||||
|
typename ObjectCacheMap::iterator itr = _objectCache.find(key);
|
||||||
|
if (itr!=_objectCache.end())
|
||||||
|
{
|
||||||
|
itr->second.second = timeStamp;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** call releaseGLObjects on all objects attached to the object cache.*/
|
/** call releaseGLObjects on all objects attached to the object cache.*/
|
||||||
void releaseGLObjects(osg::State* state);
|
void releaseGLObjects(osg::State* state)
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||||
|
for(typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr)
|
||||||
|
{
|
||||||
|
osg::Object* object = itr->second.first.get();
|
||||||
|
object->releaseGLObjects(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** call node->accept(nv); for all nodes in the objectCache. */
|
/** call node->accept(nv); for all nodes in the objectCache. */
|
||||||
void accept(osg::NodeVisitor& nv);
|
void accept(osg::NodeVisitor& nv)
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||||
|
for(typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr)
|
||||||
|
{
|
||||||
|
osg::Object* object = itr->second.first.get();
|
||||||
|
if (object)
|
||||||
|
{
|
||||||
|
osg::Node* node = dynamic_cast<osg::Node*>(object);
|
||||||
|
if (node)
|
||||||
|
node->accept(nv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** call operator()(osg::Object*) for each object in the cache. */
|
/** call operator()(osg::Object*) for each object in the cache. */
|
||||||
template <class Functor>
|
template <class Functor>
|
||||||
void call(Functor& f)
|
void call(Functor& f)
|
||||||
{
|
{
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||||
for (ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it)
|
for (typename ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it)
|
||||||
f(it->second.first.get());
|
f(it->second.first.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the number of objects in the cache. */
|
/** Get the number of objects in the cache. */
|
||||||
unsigned int getCacheSize() const;
|
unsigned int getCacheSize() const
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||||
|
return _objectCache.size();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual ~ObjectCache();
|
virtual ~GenericObjectCache() {}
|
||||||
|
|
||||||
typedef std::pair<osg::ref_ptr<osg::Object>, double > ObjectTimeStampPair;
|
typedef std::pair<osg::ref_ptr<osg::Object>, double > ObjectTimeStampPair;
|
||||||
typedef std::map<std::string, ObjectTimeStampPair > ObjectCacheMap;
|
typedef std::map<KeyType, ObjectTimeStampPair > ObjectCacheMap;
|
||||||
|
|
||||||
ObjectCacheMap _objectCache;
|
ObjectCacheMap _objectCache;
|
||||||
mutable OpenThreads::Mutex _objectCacheMutex;
|
mutable OpenThreads::Mutex _objectCacheMutex;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ObjectCache : public GenericObjectCache<std::string>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
#include "resourcemanager.hpp"
|
|
||||||
|
|
||||||
#include "objectcache.hpp"
|
|
||||||
|
|
||||||
namespace Resource
|
|
||||||
{
|
|
||||||
|
|
||||||
ResourceManager::ResourceManager(const VFS::Manager *vfs)
|
|
||||||
: mVFS(vfs)
|
|
||||||
, mCache(new Resource::ObjectCache)
|
|
||||||
, mExpiryDelay(0.0)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceManager::~ResourceManager()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResourceManager::updateCache(double referenceTime)
|
|
||||||
{
|
|
||||||
mCache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);
|
|
||||||
mCache->removeExpiredObjectsInCache(referenceTime - mExpiryDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResourceManager::clearCache()
|
|
||||||
{
|
|
||||||
mCache->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResourceManager::setExpiryDelay(double expiryDelay)
|
|
||||||
{
|
|
||||||
mExpiryDelay = expiryDelay;
|
|
||||||
}
|
|
||||||
|
|
||||||
const VFS::Manager* ResourceManager::getVFS() const
|
|
||||||
{
|
|
||||||
return mVFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResourceManager::releaseGLObjects(osg::State *state)
|
|
||||||
{
|
|
||||||
mCache->releaseGLObjects(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue