1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-12-08 12:34:30 +00:00

Add OpenMW commits up to 2 May 2021

# Conflicts:
#   components/CMakeLists.txt
This commit is contained in:
David Cernat 2021-05-02 20:55:49 +02:00
commit a3f304107b
172 changed files with 3800 additions and 840 deletions

View file

@ -31,7 +31,8 @@ stages:
- build/install/
Coverity:
extends: .Debian
extends: .Debian_Image
stage: build
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
before_script:
@ -44,10 +45,10 @@ Coverity:
- cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc) openmw
after_script:
- tar cfz cov-int.tar.gz cov-int
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME \
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL \
--form file=@cov-int.tar.gz --form version="`git describe --tags`" \
--form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form file=@cov-int.tar.gz --form version="`git describe --tags`"
--form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
variables:
CC: gcc
CXX: g++
@ -119,7 +120,7 @@ Debian_Clang_tests:
.MacOS:
image: macos-11-xcode-12
tags:
- macos
- shared-macos-amd64
stage: build
only:
variables:
@ -128,12 +129,12 @@ Debian_Clang_tests:
paths:
- ccache/
script:
- rm -fr build/* # remove anything in the build directory
- rm -fr build # remove the build directory
- CI/before_install.osx.sh
- export CCACHE_BASEDIR="$(pwd)"
- export CCACHE_DIR="$(pwd)/ccache"
- mkdir -pv "${CCACHE_DIR}"
- ccache -z -M "${CCACHE_SIZE}"
- CI/before_install.osx.sh
- CI/before_script.osx.sh
- cd build; make -j $(sysctl -n hw.logicalcpu) package
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.dmg"; done
@ -154,6 +155,7 @@ macOS11_Xcode12:
macOS10.15_Xcode11:
extends: .MacOS
image: macos-10.15-xcode-11
allow_failure: true
cache:
key: macOS10.15_Xcode11.v1
variables:

View file

@ -49,6 +49,7 @@ Programmers
Cédric Mocquillon
Chris Boyce (slothlife)
Chris Robinson (KittyCat)
Cody Glassman (Wazabear)
Coleman Smith (olcoal)
Cory F. Cohen (cfcohen)
Cris Mihalache (Mirceam)
@ -89,6 +90,7 @@ Programmers
Internecine
Jackerty
Jacob Essex (Yacoby)
Jacob Turnbull (Tankinfrank)
Jake Westrip (16bitint)
James Carty (MrTopCat)
James Moore (moore.work)
@ -150,6 +152,7 @@ Programmers
Nathan Jeffords (blunted2night)
NeveHanter
Nialsy
Nick Crawford (nighthawk469)
Nikolay Kasyanov (corristo)
nobrakal
Nolan Poe (nopoe)

View file

@ -117,6 +117,8 @@
Bug #5914: BM: The Swimmer can't reach destination
Bug #5923: Clicking on empty spaces between journal entries might show random topics
Bug #5934: AddItem command doesn't accept negative values
Bug #5975: NIF controllers from sheath meshes are used
Bug #5995: NiUVController doesn't calculate the UV offset properly
Feature #390: 3rd person look "over the shoulder"
Feature #832: OpenMW-CS: Handle deleted references
Feature #1536: Show more information about level on menu
@ -127,6 +129,7 @@
Feature #3983: Wizard: Add link to buy Morrowind
Feature #4894: Consider actors as obstacles for pathfinding
Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing
Feature #4917: Do not trigger NavMesh update when RecastMesh update should not change NavMesh
Feature #4977: Use the "default icon.tga" when an item's icon is not found
Feature #5043: Head Bobbing
Feature #5199: OpenMW-CS: Improve scene view colors
@ -152,6 +155,7 @@
Feature #5771: ori command should report where a mesh is loaded from and whether the x version is used.
Feature #5813: Instanced groundcover support
Feature #5814: Bsatool should be able to create BSA archives, not only to extract it
Feature #5828: Support more than 8 lights
Feature #5910: Fall back to delta time when physics can't keep up
Task #5480: Drop Qt4 support
Task #5520: Improve cell name autocompleter implementation

View file

@ -30,55 +30,11 @@ command -v unixPathAsWindows >/dev/null 2>&1 || function unixPathAsWindows {
fi
}
function windowsSystemPathAsUnix {
if command -v cygpath >/dev/null 2>&1; then
cygpath -u -p $1
else
IFS=';' read -r -a paths <<< "$1"
declare -a convertedPaths
for entry in paths; do
convertedPaths+=(windowsPathAsUnix $entry)
done
convertedPath=printf ":%s" ${convertedPaths[@]}
echo ${convertedPath:1}
fi
}
# capture CMD environment so we know what's been changed
declare -A originalCmdEnv
originalIFS="$IFS"
IFS=$'\n\r'
for pair in $(cmd //c "set"); do
IFS='=' read -r -a separatedPair <<< "${pair}"
if [ ${#separatedPair[@]} -ne 2 ]; then
echo "Parsed '$pair' as ${#separatedPair[@]} parts, expected 2."
continue
fi
originalCmdEnv["${separatedPair[0]}"]="${separatedPair[1]}"
done
# capture CMD environment in a shell with MSVC activated
cmdEnv="$(cmd //c "$(unixPathAsWindows "$(dirname "${BASH_SOURCE[0]}")")\ActivateMSVC.bat" "&&" set)"
declare -A cmdEnvChanges
for pair in $cmdEnv; do
if [ -n "$pair" ]; then
IFS='=' read -r -a separatedPair <<< "${pair}"
if [ ${#separatedPair[@]} -ne 2 ]; then
echo "Parsed '$pair' as ${#separatedPair[@]} parts, expected 2."
continue
fi
key="${separatedPair[0]}"
value="${separatedPair[1]}"
if ! [ ${originalCmdEnv[$key]+_} ] || [ "${originalCmdEnv[$key]}" != "$value" ]; then
if [ $key != 'PATH' ] && [ $key != 'path' ] && [ $key != 'Path' ]; then
export "$key=$value"
else
export PATH=$(windowsSystemPathAsUnix $value)
fi
fi
fi
done
cmd //c "$(unixPathAsWindows "$(dirname "${BASH_SOURCE[0]}")")\ActivateMSVC.bat" "&&" "bash" "-c" "declare -px > declared_env.sh"
source ./declared_env.sh
rm declared_env.sh
MISSINGTOOLS=0
@ -93,6 +49,4 @@ if [ $MISSINGTOOLS -ne 0 ]; then
return 1
fi
IFS="$originalIFS"
restoreOldSettings
restoreOldSettings

View file

@ -1,9 +1,9 @@
#!/bin/sh -ex
# workaround python issue on travis
HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.8 || true
HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.9 || true
HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies qt@6 || true
[ -z "${TRAVIS}" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.8 || true
[ -z "${TRAVIS}" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.9 || true
[ -z "${TRAVIS}" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies qt@6 || true
# Some of these tools can come from places other than brew, so check before installing
command -v ccache >/dev/null 2>&1 || brew install ccache
@ -15,5 +15,5 @@ ccache --version
cmake --version
qmake --version
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-f8918dd.zip -o ~/openmw-deps.zip
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-8f5ef6e.zip -o ~/openmw-deps.zip
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null

View file

@ -34,6 +34,7 @@ option(BUILD_NIFTEST "Build nif file tester" ON)
option(BUILD_DOCS "Build documentation." OFF )
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
option(BUILD_BENCHMARKS "Build benchmarks with Google Benchmark" OFF)
option(BULLET_USE_DOUBLES "Use double precision for Bullet" ON)
option(BUILD_OPENMW_MP "Build OpenMW-MP" ON)
option(BUILD_BROWSER "Build tes3mp Server Browser" ON)
@ -533,10 +534,6 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter")
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 5.0)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override")
endif()
endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
# Extern
@ -614,6 +611,10 @@ if (BUILD_UNITTESTS)
add_subdirectory( apps/openmw_test_suite )
endif()
if (BUILD_BENCHMARKS)
add_subdirectory(apps/benchmarks)
endif()
if (WIN32)
if (MSVC)
if (OPENMW_MP_BUILD)
@ -644,64 +645,16 @@ if (WIN32)
# Play a bit with the warning levels
set(WARNINGS "/Wall") # Since windows can only disable specific warnings, not enable them
set(WARNINGS "/W4")
set(WARNINGS_DISABLE
# Warnings that aren't enabled normally and don't need to be enabled
# They're unneeded and sometimes completely retarded warnings that /Wall enables
# Not going to bother commenting them as they tend to warn on every standard library file
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625
4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045
# Warnings that are thrown on standard libraries and not OpenMW
4347 # Non-template function with same name and parameter count as template function
4365 # Variable signed/unsigned mismatch
4510 4512 # Unable to generate copy constructor/assignment operator as it's not public in the base
4706 # Assignment in conditional expression
4738 # Storing 32-bit float result in memory, possible loss of performance
4774 # Format string expected in argument is not a string literal
4986 # Undocumented warning that occurs in the crtdbg.h file
4987 # nonstandard extension used (triggered by setjmp.h)
4996 # Function was declared deprecated
# caused by OSG
4589 # Constructor of abstract class 'osg::Operation' ignores initializer for virtual base class 'osg::Referenced' (False warning)
# caused by boost
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
4643 # Forward declaring 'X' in namespace std is not permitted by the C++ Standard. (in *_std_fwd.h files)
5204 # Class has virtual functions, but its trivial destructor is not virtual
# caused by MyGUI
4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception'
4297 # function assumed not to throw an exception but does
# OpenMW specific warnings
4099 # Type mismatch, declared class or struct is defined with other type
4100 # Unreferenced formal parameter (-Wunused-parameter)
4101 # Unreferenced local variable (-Wunused-variable)
4127 # Conditional expression is constant
4242 # Storing value in a variable of a smaller type, possible loss of data
4244 # Storing value of one type in variable of another (size_t in int, for example)
4245 # Signed/unsigned mismatch
4267 # Conversion from 'size_t' to 'int', possible loss of data
4305 # Truncating value (double to float, for example)
4309 # Variable overflow, trying to store 128 in a signed char for example
4351 # New behavior: elements of array 'array' will be default initialized (desired behavior)
4355 # Using 'this' in member initialization list
4464 # relative include path contains '..'
4505 # Unreferenced local function has been removed
4701 # Potentially uninitialized local variable used
4702 # Unreachable code
4714 # function 'QString QString::trimmed(void) &&' marked as __forceinline not inlined
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
4996 # Function was declared deprecated
)
if (MSVC_VERSION GREATER 1800)
set(WARNINGS_DISABLE ${WARNINGS_DISABLE} 5026 5027
5031 # #pragma warning(pop): likely mismatch, popping warning state pushed in different file (config_begin.hpp, config_end.hpp)
)
endif()
if( "${MyGUI_VERSION}" VERSION_LESS_EQUAL "3.4.0" )
set(WARNINGS_DISABLE ${WARNINGS_DISABLE}
@ -709,6 +662,12 @@ if (WIN32)
)
endif()
if( "${MyGUI_VERSION}" VERSION_LESS_EQUAL "3.4.1" )
set(WARNINGS_DISABLE ${WARNINGS_DISABLE}
4275 # non dll-interface class 'MyGUI::delegates::IDelegateUnlink' used as base for dll-interface class 'MyGUI::Widget'
)
endif()
foreach(d ${WARNINGS_DISABLE})
set(WARNINGS "${WARNINGS} /wd${d}")
endforeach(d)

View file

@ -0,0 +1,34 @@
cmake_minimum_required(VERSION 3.11)
set(BENCHMARK_ENABLE_TESTING OFF)
set(BENCHMARK_ENABLE_INSTALL OFF)
set(BENCHMARK_ENABLE_GTEST_TESTS OFF)
set(SAVED_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "-Wsuggest-override" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "-Wundef" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
include(FetchContent)
FetchContent_Declare(benchmark
URL https://github.com/google/benchmark/archive/refs/tags/v1.5.2.zip
URL_HASH MD5=49395b757a7c4656d70f1328d93efd00
SOURCE_DIR fetched/benchmark
)
FetchContent_MakeAvailableExcludeFromAll(benchmark)
set(CMAKE_CXX_FLAGS "${SAVED_CMAKE_CXX_FLAGS}")
openmw_add_executable(openmw_detournavigator_navmeshtilescache_benchmark detournavigator/navmeshtilescache.cpp)
target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE -Wall)
target_compile_features(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE cxx_std_17)
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark benchmark::benchmark components)
if (UNIX AND NOT APPLE)
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark ${CMAKE_THREAD_LIBS_INIT})
endif()
if (MSVC)
if (CMAKE_CL_64)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj")
endif (CMAKE_CL_64)
endif (MSVC)

View file

@ -0,0 +1,215 @@
#include <benchmark/benchmark.h>
#include <components/detournavigator/navmeshtilescache.hpp>
#include <algorithm>
#include <random>
#include <iostream>
namespace
{
using namespace DetourNavigator;
struct Key
{
osg::Vec3f mAgentHalfExtents;
TilePosition mTilePosition;
RecastMesh mRecastMesh;
std::vector<OffMeshConnection> mOffMeshConnections;
};
struct Item
{
Key mKey;
NavMeshData mValue;
};
template <typename Random>
TilePosition generateTilePosition(int max, Random& random)
{
std::uniform_int_distribution<int> distribution(0, max);
return TilePosition(distribution(random), distribution(random));
}
template <typename Random>
osg::Vec3f generateAgentHalfExtents(float min, float max, Random& random)
{
std::uniform_int_distribution<int> distribution(min, max);
return osg::Vec3f(distribution(random), distribution(random), distribution(random));
}
template <typename OutputIterator, typename Random>
void generateVertices(OutputIterator out, std::size_t number, Random& random)
{
std::uniform_real_distribution<float> distribution(0.0, 1.0);
std::generate_n(out, 3 * (number - number % 3), [&] { return distribution(random); });
}
template <typename OutputIterator, typename Random>
void generateIndices(OutputIterator out, int max, std::size_t number, Random& random)
{
std::uniform_int_distribution<int> distribution(0, max);
std::generate_n(out, number - number % 3, [&] { return distribution(random); });
}
AreaType toAreaType(int index)
{
switch (index)
{
case 0: return AreaType_null;
case 1: return AreaType_water;
case 2: return AreaType_door;
case 3: return AreaType_pathgrid;
case 4: return AreaType_ground;
}
return AreaType_null;
}
template <typename Random>
AreaType generateAreaType(Random& random)
{
std::uniform_int_distribution<int> distribution(0, 4);
return toAreaType(distribution(random));;
}
template <typename OutputIterator, typename Random>
void generateAreaTypes(OutputIterator out, std::size_t triangles, Random& random)
{
std::generate_n(out, triangles, [&] { return generateAreaType(random); });
}
template <typename OutputIterator, typename Random>
void generateWater(OutputIterator out, std::size_t count, Random& random)
{
std::uniform_real_distribution<btScalar> distribution(0.0, 1.0);
std::generate_n(out, count, [&] {
const btVector3 shift(distribution(random), distribution(random), distribution(random));
return RecastMesh::Water {1, btTransform(btMatrix3x3::getIdentity(), shift)};
});
}
template <typename OutputIterator, typename Random>
void generateOffMeshConnection(OutputIterator out, std::size_t count, Random& random)
{
std::uniform_real_distribution<btScalar> distribution(0.0, 1.0);
std::generate_n(out, count, [&] {
const osg::Vec3f start(distribution(random), distribution(random), distribution(random));
const osg::Vec3f end(distribution(random), distribution(random), distribution(random));
return OffMeshConnection {start, end, generateAreaType(random)};
});
}
template <class Random>
Key generateKey(std::size_t triangles, Random& random)
{
const osg::Vec3f agentHalfExtents = generateAgentHalfExtents(0.5, 1.5, random);
const TilePosition tilePosition = generateTilePosition(10000, random);
const std::size_t generation = std::uniform_int_distribution<std::size_t>(0, 100)(random);
const std::size_t revision = std::uniform_int_distribution<std::size_t>(0, 10000)(random);
std::vector<float> vertices;
generateVertices(std::back_inserter(vertices), triangles * 1.98, random);
std::vector<int> indices;
generateIndices(std::back_inserter(indices), static_cast<int>(vertices.size() / 3) - 1, vertices.size() * 1.53, random);
std::vector<AreaType> areaTypes;
generateAreaTypes(std::back_inserter(areaTypes), indices.size() / 3, random);
std::vector<RecastMesh::Water> water;
generateWater(std::back_inserter(water), 2, random);
const std::size_t trianglesPerChunk = 256;
RecastMesh recastMesh(generation, revision, std::move(indices), std::move(vertices),
std::move(areaTypes), std::move(water), trianglesPerChunk);
std::vector<OffMeshConnection> offMeshConnections;
generateOffMeshConnection(std::back_inserter(offMeshConnections), 300, random);
return Key {agentHalfExtents, tilePosition, std::move(recastMesh), std::move(offMeshConnections)};
}
constexpr std::size_t trianglesPerTile = 310;
template <typename OutputIterator, typename Random>
void generateKeys(OutputIterator out, std::size_t count, Random& random)
{
std::generate_n(out, count, [&] { return generateKey(trianglesPerTile, random); });
}
template <typename OutputIterator, typename Random>
void fillCache(OutputIterator out, Random& random, NavMeshTilesCache& cache)
{
std::size_t size = cache.getStats().mNavMeshCacheSize;
while (true)
{
Key key = generateKey(trianglesPerTile, random);
cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData());
*out++ = std::move(key);
const std::size_t newSize = cache.getStats().mNavMeshCacheSize;
if (size >= newSize)
break;
size = newSize;
}
}
template <std::size_t maxCacheSize, int hitPercentage>
void getFromFilledCache(benchmark::State& state)
{
NavMeshTilesCache cache(maxCacheSize);
std::minstd_rand random;
std::vector<Key> keys;
fillCache(std::back_inserter(keys), random, cache);
generateKeys(std::back_inserter(keys), keys.size() * (100 - hitPercentage) / 100, random);
std::size_t n = 0;
while (state.KeepRunning())
{
const auto& key = keys[n++ % keys.size()];
const auto result = cache.get(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections);
benchmark::DoNotOptimize(result);
}
}
constexpr auto getFromFilledCache_1m_100hit = getFromFilledCache<1 * 1024 * 1024, 100>;
constexpr auto getFromFilledCache_4m_100hit = getFromFilledCache<4 * 1024 * 1024, 100>;
constexpr auto getFromFilledCache_16m_100hit = getFromFilledCache<16 * 1024 * 1024, 100>;
constexpr auto getFromFilledCache_64m_100hit = getFromFilledCache<64 * 1024 * 1024, 100>;
constexpr auto getFromFilledCache_1m_70hit = getFromFilledCache<1 * 1024 * 1024, 70>;
constexpr auto getFromFilledCache_4m_70hit = getFromFilledCache<4 * 1024 * 1024, 70>;
constexpr auto getFromFilledCache_16m_70hit = getFromFilledCache<16 * 1024 * 1024, 70>;
constexpr auto getFromFilledCache_64m_70hit = getFromFilledCache<64 * 1024 * 1024, 70>;
template <std::size_t maxCacheSize>
void setToBoundedNonEmptyCache(benchmark::State& state)
{
NavMeshTilesCache cache(maxCacheSize);
std::minstd_rand random;
std::vector<Key> keys;
fillCache(std::back_inserter(keys), random, cache);
generateKeys(std::back_inserter(keys), keys.size() * 2, random);
std::reverse(keys.begin(), keys.end());
std::size_t n = 0;
while (state.KeepRunning())
{
const auto& key = keys[n++ % keys.size()];
const auto result = cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData());
benchmark::DoNotOptimize(result);
}
}
constexpr auto setToBoundedNonEmptyCache_1m = setToBoundedNonEmptyCache<1 * 1024 * 1024>;
constexpr auto setToBoundedNonEmptyCache_4m = setToBoundedNonEmptyCache<4 * 1024 * 1024>;
constexpr auto setToBoundedNonEmptyCache_16m = setToBoundedNonEmptyCache<16 * 1024 * 1024>;
constexpr auto setToBoundedNonEmptyCache_64m = setToBoundedNonEmptyCache<64 * 1024 * 1024>;
} // namespace
BENCHMARK(getFromFilledCache_1m_100hit);
BENCHMARK(getFromFilledCache_4m_100hit);
BENCHMARK(getFromFilledCache_16m_100hit);
BENCHMARK(getFromFilledCache_64m_100hit);
BENCHMARK(getFromFilledCache_1m_70hit);
BENCHMARK(getFromFilledCache_4m_70hit);
BENCHMARK(getFromFilledCache_16m_70hit);
BENCHMARK(getFromFilledCache_64m_70hit);
BENCHMARK(setToBoundedNonEmptyCache_1m);
BENCHMARK(setToBoundedNonEmptyCache_4m);
BENCHMARK(setToBoundedNonEmptyCache_16m);
BENCHMARK(setToBoundedNonEmptyCache_64m);
BENCHMARK_MAIN();

View file

@ -322,7 +322,7 @@ int load(Arguments& info)
std::string filename = info.filename;
std::cout << "Loading file: " << filename << std::endl;
std::list<int> skipped;
std::list<uint32_t> skipped;
try {

View file

@ -11,6 +11,7 @@ namespace ESSImport
{
out.mId = Misc::StringUtils::lowerCase(scpt.mSCHD.mName.toString());
out.mRunning = scpt.mRunning;
out.mTargetRef.unset(); // TODO: convert target reference of global script
convertSCRI(scpt.mSCRI, out.mLocals);
}

View file

@ -19,6 +19,7 @@ namespace ESSImport
item.mCount = contItem.mCount;
item.mRelativeEquipmentSlot = -1;
item.mLockLevel = 0;
item.mRefNum.unset();
unsigned int itemCount = std::abs(item.mCount);
bool separateStacks = false;

View file

@ -57,7 +57,7 @@ int main(int argc, char** argv)
else
{
const std::string& ext = ".omwsave";
if (boost::filesystem::exists(boost::filesystem::path(outputFile))
if (bfs::exists(bfs::path(outputFile))
&& (outputFile.size() < ext.size() || outputFile.substr(outputFile.size()-ext.size()) != ext))
{
throw std::runtime_error("Output file already exists and does not end in .omwsave. Did you mean to use --compare?");

View file

@ -1,5 +1,7 @@
#include "advancedpage.hpp"
#include <array>
#include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp>
#include <QFileDialog>
@ -138,6 +140,13 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain");
viewingDistanceComboBox->setValue(convertToCells(mEngineSettings.getInt("viewing distance", "Camera")));
int lightingMethod = 1;
if (mEngineSettings.getString("lighting method", "Shaders") == "legacy")
lightingMethod = 0;
else if (mEngineSettings.getString("lighting method", "Shaders") == "shaders")
lightingMethod = 2;
lightingMethodComboBox->setCurrentIndex(lightingMethod);
}
// Audio
@ -194,6 +203,7 @@ bool Launcher::AdvancedPage::loadSettings()
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
scalingSpinBox->setValue(mEngineSettings.getFloat("scaling factor", "GUI"));
}
// Bug fixes
@ -288,6 +298,9 @@ void Launcher::AdvancedPage::saveSettings()
{
mEngineSettings.setInt("viewing distance", "Camera", convertToUnits(viewingDistance));
}
static std::array<std::string, 3> lightingMethodMap = {"legacy", "shaders compatibility", "shaders"};
mEngineSettings.setString("lighting method", "Shaders", lightingMethodMap[lightingMethodComboBox->currentIndex()]);
}
// Audio
@ -348,6 +361,9 @@ void Launcher::AdvancedPage::saveSettings()
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
float uiScalingFactor = scalingSpinBox->value();
if (uiScalingFactor != mEngineSettings.getFloat("scaling factor", "GUI"))
mEngineSettings.setFloat("scaling factor", "GUI", uiScalingFactor);
}
// Bug fixes

View file

@ -37,19 +37,25 @@ std::vector<const char *> Launcher::enumerateOpenALDevicesHrtf()
std::vector<const char *> ret;
ALCdevice *device = alcOpenDevice(nullptr);
if(device && alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
if(device)
{
LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;
void* funcPtr = alcGetProcAddress(device, "alcGetStringiSOFT");
memcpy(&alcGetStringiSOFT, &funcPtr, sizeof(funcPtr));
ALCint num_hrtf;
alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);
ret.reserve(num_hrtf);
for(ALCint i = 0;i < num_hrtf && i < 20;++i)
if(alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
{
const ALCchar *entry = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);
ret.emplace_back(entry);
LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;
void* funcPtr = alcGetProcAddress(device, "alcGetStringiSOFT");
memcpy(&alcGetStringiSOFT, &funcPtr, sizeof(funcPtr));
ALCint num_hrtf;
alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);
ret.reserve(num_hrtf);
for(ALCint i = 0;i < num_hrtf;++i)
{
const ALCchar *entry = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);
if(strcmp(entry, "") == 0)
break;
ret.emplace_back(entry);
}
}
alcCloseDevice(device);
}
return ret;
}
}

View file

@ -116,7 +116,7 @@ opencs_units (view/prefs
opencs_units (model/prefs
state setting intsetting doublesetting boolsetting enumsetting coloursetting shortcut
shortcuteventhandler shortcutmanager shortcutsetting modifiersetting
shortcuteventhandler shortcutmanager shortcutsetting modifiersetting stringsetting
)
opencs_units_noqt (model/prefs

View file

@ -13,8 +13,6 @@
namespace CSMPrefs
{
const int ShortcutSetting::MaxKeys;
ShortcutSetting::ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
const std::string& label)
: Setting(parent, values, mutex, key, label)

View file

@ -421,6 +421,16 @@ void CSMPrefs::State::declare()
declareSubcategory ("Script Editor");
declareShortcut ("script-editor-comment", "Comment Selection", QKeySequence());
declareShortcut ("script-editor-uncomment", "Uncomment Selection", QKeySequence());
declareCategory ("Models");
declareString ("baseanim", "base animations", "meshes/base_anim.nif").
setTooltip("3rd person base model with textkeys-data");
declareString ("baseanimkna", "base animations, kna", "meshes/base_animkna.nif").
setTooltip("3rd person beast race base model with textkeys-data");
declareString ("baseanimfemale", "base animations, female", "meshes/base_anim_female.nif").
setTooltip("3rd person female base model with textkeys-data");
declareString ("wolfskin", "base animations, wolf", "meshes/wolf/skin.nif").
setTooltip("3rd person werewolf skin");
}
void CSMPrefs::State::declareCategory (const std::string& key)
@ -557,6 +567,24 @@ CSMPrefs::ShortcutSetting& CSMPrefs::State::declareShortcut (const std::string&
return *setting;
}
CSMPrefs::StringSetting& CSMPrefs::State::declareString (const std::string& key, const std::string& label, std::string default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
setDefault (key, default_);
default_ = mSettings.getString (key, mCurrentCategory->second.getKey());
CSMPrefs::StringSetting *setting =
new CSMPrefs::StringSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,
default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
CSMPrefs::ModifierSetting& CSMPrefs::State::declareModifier(const std::string& key, const std::string& label,
int default_)
{

View file

@ -16,6 +16,7 @@
#include "category.hpp"
#include "setting.hpp"
#include "enumsetting.hpp"
#include "stringsetting.hpp"
#include "shortcutmanager.hpp"
class QColor;
@ -78,6 +79,8 @@ namespace CSMPrefs
ShortcutSetting& declareShortcut (const std::string& key, const std::string& label,
const QKeySequence& default_);
StringSetting& declareString (const std::string& key, const std::string& label, std::string default_);
ModifierSetting& declareModifier(const std::string& key, const std::string& label, int modifier_);
void declareSeparator();

View file

@ -0,0 +1,54 @@
#include "stringsetting.hpp"
#include <QLineEdit>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "state.hpp"
CSMPrefs::StringSetting::StringSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, std::string default_)
: Setting (parent, values, mutex, key, label), mDefault (default_), mWidget(nullptr)
{}
CSMPrefs::StringSetting& CSMPrefs::StringSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::StringSetting::makeWidgets (QWidget *parent)
{
mWidget = new QLineEdit (QString::fromUtf8 (mDefault.c_str()), parent);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
mWidget->setToolTip (tooltip);
}
connect (mWidget, SIGNAL (textChanged (QString)), this, SLOT (textChanged (QString)));
return std::make_pair (static_cast<QWidget *> (nullptr), mWidget);
}
void CSMPrefs::StringSetting::updateWidget()
{
if (mWidget)
{
mWidget->setText(QString::fromStdString(getValues().getString(getKey(), getParent()->getKey())));
}
}
void CSMPrefs::StringSetting::textChanged (const QString& text)
{
{
QMutexLocker lock (getMutex());
getValues().setString (getKey(), getParent()->getKey(), text.toStdString());
}
getParent()->getState()->update (*this);
}

View file

@ -0,0 +1,36 @@
#ifndef CSM_PREFS_StringSetting_H
#define CSM_PREFS_StringSetting_H
#include "setting.hpp"
class QLineEdit;
namespace CSMPrefs
{
class StringSetting : public Setting
{
Q_OBJECT
std::string mTooltip;
std::string mDefault;
QLineEdit* mWidget;
public:
StringSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, std::string default_);
StringSetting& setTooltip (const std::string& tooltip);
/// Return label, input widget.
std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent) override;
void updateWidget() override;
private slots:
void textChanged (const QString& text);
};
}
#endif

View file

@ -76,6 +76,7 @@ void CSMWorld::ImportLandTexturesCommand::redo()
}
std::vector<std::string> oldTextures;
oldTextures.reserve(texIndices.size());
for (int index : texIndices)
{
oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index));

View file

@ -83,6 +83,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
defines["clamp"] = "1"; // Clamp lighting
defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind
defines["radialFog"] = "0";
defines["lightingModel"] = "0";
for (const auto& define : shadowDefines)
defines[define.first] = define.second;
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
@ -985,20 +986,6 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, nullptr, &metaData));
}
// Fix uninitialized master data index
for (std::vector<ESM::Header::MasterData>::const_iterator masterData = mReader->getGameFiles().begin();
masterData != mReader->getGameFiles().end(); ++masterData)
{
std::map<std::string, int>::iterator nameResult = mContentFileNames.find(masterData->name);
if (nameResult != mContentFileNames.end())
{
ESM::Header::MasterData& hackedMasterData = const_cast<ESM::Header::MasterData&>(*masterData);
hackedMasterData.index = nameResult->second;
}
}
return mReader->getRecordCount();
}

View file

@ -201,7 +201,7 @@ QModelIndex CSMWorld::IdTree::parent (const QModelIndex& index) const
const std::pair<int, int>& address(unfoldIndexAddress(id));
if (address.first >= this->rowCount() || address.second >= this->columnCount())
throw "Parent index is not present in the model";
throw std::logic_error("Parent index is not present in the model");
return createIndex(address.first, address.second);
}
@ -216,7 +216,7 @@ unsigned int CSMWorld::IdTree::foldIndexAddress (const QModelIndex& index) const
std::pair< int, int > CSMWorld::IdTree::unfoldIndexAddress (unsigned int id) const
{
if (id == 0)
throw "Attempt to unfold index id of the top level data cell";
throw std::runtime_error("Attempt to unfold index id of the top level data cell");
--id;
int row = id / this->columnCount();

View file

@ -126,7 +126,7 @@ namespace CSVRender
{
// Try again without any mask
boundsVisitor.reset();
boundsVisitor.setTraversalMask(~0);
boundsVisitor.setTraversalMask(~0u);
root->accept(boundsVisitor);
// Last resort, set a default
@ -458,7 +458,7 @@ namespace CSVRender
, mDown(false)
, mRollLeft(false)
, mRollRight(false)
, mPickingMask(~0)
, mPickingMask(~0u)
, mCenter(0,0,0)
, mDistance(0)
, mOrbitSpeed(osg::PI / 4)

View file

@ -151,7 +151,7 @@ void CSVRender::CellArrow::buildShape()
osg::Vec4Array *colours = new osg::Vec4Array;
for (int i=0; i<6; ++i)
colours->push_back (osg::Vec4f (0.11, 0.6f, 0.95f, 1.0f));
colours->push_back (osg::Vec4f (0.11f, 0.6f, 0.95f, 1.0f));
for (int i=0; i<6; ++i)
colours->push_back (osg::Vec4f (0.08f, 0.44f, 0.7f, 1.0f));

View file

@ -8,7 +8,7 @@ namespace CSVRender
/// @note See the respective file in OpenMW (apps/openmw/mwrender/vismask.hpp)
/// for general usage hints about node masks.
/// @copydoc MWRender::VisMask
enum Mask
enum Mask : unsigned int
{
// elements that are part of the actual scene
Mask_Reference = 0x2,

View file

@ -120,7 +120,7 @@ void RenderWidget::flagAsModified()
mView->requestRedraw();
}
void RenderWidget::setVisibilityMask(int mask)
void RenderWidget::setVisibilityMask(unsigned int mask)
{
mView->getCamera()->setCullMask(mask | Mask_ParticleSystem | Mask_Lighting);
}

View file

@ -55,7 +55,7 @@ namespace CSVRender
/// Initiates a request to redraw the view
void flagAsModified();
void setVisibilityMask(int mask);
void setVisibilityMask(unsigned int mask);
osg::Camera *getCamera();

View file

@ -606,7 +606,7 @@ void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter;
}
catch (const std::exception& e)
catch (const std::exception&)
{
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
freeIndexFound = true;

View file

@ -163,7 +163,7 @@ void CSVRender::WorldspaceWidget::selectDefaultNavigationMode()
void CSVRender::WorldspaceWidget::centerOrbitCameraOnSelection()
{
std::vector<osg::ref_ptr<TagBase> > selection = getSelection(~0);
std::vector<osg::ref_ptr<TagBase> > selection = getSelection(~0u);
for (std::vector<osg::ref_ptr<TagBase> >::iterator it = selection.begin(); it!=selection.end(); ++it)
{

View file

@ -237,6 +237,8 @@ namespace MWBase
virtual bool getWorldMouseOver() = 0;
virtual float getScalingFactor() = 0;
virtual bool toggleFogOfWar() = 0;
virtual bool toggleFullHelp() = 0;

View file

@ -77,7 +77,7 @@ namespace MWClass
CreatureCustomData() = default;
CreatureCustomData(const CreatureCustomData& other);
CreatureCustomData(CreatureCustomData&& other) noexcept = default;
CreatureCustomData(CreatureCustomData&& other) = default;
CreatureCustomData& asCreatureCustomData() override
{

View file

@ -17,7 +17,7 @@ namespace MWDialogue
std::vector<Token> parseHyperText(const std::string & text)
{
std::vector<Token> result;
size_t pos_end, iteration_pos = 0;
size_t pos_end = std::string::npos, iteration_pos = 0;
for(;;)
{
size_t pos_begin = text.find('@', iteration_pos);

View file

@ -161,7 +161,7 @@ namespace MWGui
// We need this copy for when @# hyperlinks are replaced
std::string text = mText;
size_t pos_end;
size_t pos_end = std::string::npos;
for(;;)
{
size_t pos_begin = text.find('@');

View file

@ -81,13 +81,8 @@ namespace MWGui
, mLastYSize(0)
, mPreview(new MWRender::InventoryPreview(parent, resourceSystem, MWMechanics::getPlayer()))
, mTrading(false)
, mScaleFactor(1.0f)
, mUpdateTimer(0.f)
{
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
if (uiScale > 0.f)
mScaleFactor = uiScale;
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));
mPreview->rebuild();
@ -483,10 +478,11 @@ namespace MWGui
MyGUI::IntSize size = mAvatarImage->getSize();
int width = std::min(mPreview->getTextureWidth(), size.width);
int height = std::min(mPreview->getTextureHeight(), size.height);
mPreview->setViewport(int(width*mScaleFactor), int(height*mScaleFactor));
float scalingFactor = MWBase::Environment::get().getWindowManager()->getScalingFactor();
mPreview->setViewport(int(width*scalingFactor), int(height*scalingFactor));
mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f,
width*mScaleFactor/float(mPreview->getTextureWidth()), height*mScaleFactor/float(mPreview->getTextureHeight())));
width*scalingFactor/float(mPreview->getTextureWidth()), height*scalingFactor/float(mPreview->getTextureHeight())));
}
void InventoryWindow::onNameFilterChanged(MyGUI::EditBox* _sender)
@ -661,8 +657,9 @@ namespace MWGui
y = (mAvatarImage->getHeight()-1) - y;
// Scale coordinates
x = int(x*mScaleFactor);
y = int(y*mScaleFactor);
float scalingFactor = MWBase::Environment::get().getWindowManager()->getScalingFactor();
x = static_cast<int>(x*scalingFactor);
y = static_cast<int>(y*scalingFactor);
int slot = mPreview->getSlotSelected (x, y);

View file

@ -104,7 +104,6 @@ namespace MWGui
std::unique_ptr<MWRender::InventoryPreview> mPreview;
bool mTrading;
float mScaleFactor;
float mUpdateTimer;
void toggleMaximized();

View file

@ -129,7 +129,7 @@ struct JournalViewModelImpl : JournalViewModel
utf8text.replace(pos_begin, pos_end+1-pos_begin, displayName);
intptr_t value;
intptr_t value = 0;
if (mModel->mKeywordSearch.containsKeyword(topicName, value))
mHyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = value;
}

View file

@ -11,12 +11,16 @@
#include <iomanip>
#include <numeric>
#include <array>
#include <components/debug/debuglog.hpp>
#include <components/misc/stringops.hpp>
#include <components/misc/constants.hpp>
#include <components/widgets/sharedstatebutton.hpp>
#include <components/settings/settings.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -106,11 +110,24 @@ namespace
if (!widget->getUserString(settingMax).empty())
max = MyGUI::utility::parseFloat(widget->getUserString(settingMax));
}
void updateMaxLightsComboBox(MyGUI::ComboBox* box)
{
constexpr int min = 8;
constexpr int max = 32;
constexpr int increment = 8;
int maxLights = Settings::Manager::getInt("max lights", "Shaders");
// show increments of 8 in dropdown
if (maxLights >= min && maxLights <= max && !(maxLights % increment))
box->setIndexSelected((maxLights / increment)-1);
else
box->setIndexSelected(MyGUI::ITEM_NONE);
}
}
namespace MWGui
{
void SettingsWindow::configureWidgets(MyGUI::Widget* widget)
void SettingsWindow::configureWidgets(MyGUI::Widget* widget, bool init)
{
MyGUI::EnumeratorWidgetPtr widgets = widget->getEnumerator();
while (widgets.next())
@ -124,7 +141,8 @@ namespace MWGui
getSettingCategory(current))
? "#{sOn}" : "#{sOff}";
current->castType<MyGUI::Button>()->setCaptionWithReplacing(initialValue);
current->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
if (init)
current->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
}
if (type == sliderType)
{
@ -159,6 +177,12 @@ namespace MWGui
ss << std::fixed << std::setprecision(2) << value/Constants::CellSizeInUnits;
valueStr = ss.str();
}
else if (valueType == "Float")
{
std::stringstream ss;
ss << std::fixed << std::setprecision(2) << value;
valueStr = ss.str();
}
else
valueStr = MyGUI::utility::toString(int(value));
@ -173,12 +197,13 @@ namespace MWGui
valueStr = MyGUI::utility::toString(value);
scroll->setScrollPosition(value);
}
scroll->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
if (init)
scroll->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
if (scroll->getVisible())
updateSliderLabel(scroll, valueStr);
}
configureWidgets(current);
configureWidgets(current, init);
}
}
@ -205,7 +230,7 @@ namespace MWGui
getWidget(unusedSlider, widgetName);
unusedSlider->setVisible(false);
configureWidgets(mMainWidget);
configureWidgets(mMainWidget, true);
setTitle("#{sOptions}");
@ -222,6 +247,9 @@ namespace MWGui
getWidget(mControllerSwitch, "ControllerButton");
getWidget(mWaterTextureSize, "WaterTextureSize");
getWidget(mWaterReflectionDetail, "WaterReflectionDetail");
getWidget(mLightingMethodButton, "LightingMethodButton");
getWidget(mLightsResetButton, "LightsResetButton");
getWidget(mMaxLights, "MaxLights");
#ifndef WIN32
// hide gamma controls since it currently does not work under Linux
@ -247,6 +275,10 @@ namespace MWGui
mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged);
mWaterReflectionDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterReflectionDetailChanged);
mLightingMethodButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onLightingMethodButtonChanged);
mLightsResetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onLightsResetButtonClicked);
mMaxLights->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onMaxLightsChanged);
mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);
mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);
@ -292,6 +324,8 @@ namespace MWGui
waterReflectionDetail = std::min(5, std::max(0, waterReflectionDetail));
mWaterReflectionDetail->setIndexSelected(waterReflectionDetail);
updateMaxLightsComboBox(mMaxLights);
mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video"));
mKeyboardSwitch->setStateSelected(true);
@ -378,6 +412,54 @@ namespace MWGui
apply();
}
void SettingsWindow::onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos)
{
if (pos == MyGUI::ITEM_NONE)
return;
std::string message = "This change requires a restart to take effect.";
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, {"#{sOK}"}, true);
Settings::Manager::setString("lighting method", "Shaders", _sender->getItemNameAt(pos));
apply();
}
void SettingsWindow::onMaxLightsChanged(MyGUI::ComboBox* _sender, size_t pos)
{
int count = 8 * (pos + 1);
Settings::Manager::setInt("max lights", "Shaders", count);
apply();
configureWidgets(mMainWidget, false);
}
void SettingsWindow::onLightsResetButtonClicked(MyGUI::Widget* _sender)
{
std::vector<std::string> buttons = {"#{sYes}", "#{sNo}"};
std::string message = "Resets to default values, would you like to continue? Changes to lighting method will require a restart.";
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons, true);
int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton();
if (selectedButton == 1 || selectedButton == -1)
return;
constexpr std::array<const char*, 6> settings = {
"light bounds multiplier",
"maximum light distance",
"light fade start",
"minimum interior brightness",
"max lights",
"lighting method",
};
for (const auto& setting : settings)
Settings::Manager::setString(setting, "Shaders", Settings::Manager::mDefaultSettings[{"Shaders", setting}]);
mLightingMethodButton->setIndexSelected(mLightingMethodButton->findItemIndexWith(Settings::Manager::mDefaultSettings[{"Shaders", "lighting method"}]));
updateMaxLightsComboBox(mMaxLights);
apply();
configureWidgets(mMainWidget, false);
}
void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender)
{
std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On");
@ -480,6 +562,12 @@ namespace MWGui
ss << std::fixed << std::setprecision(2) << value/Constants::CellSizeInUnits;
valueStr = ss.str();
}
else if (valueType == "Float")
{
std::stringstream ss;
ss << std::fixed << std::setprecision(2) << value;
valueStr = ss.str();
}
else
valueStr = MyGUI::utility::toString(int(value));
}
@ -570,6 +658,30 @@ namespace MWGui
layoutControlsBox();
}
void SettingsWindow::updateLightSettings()
{
auto lightingMethod = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getLightingMethod();
std::string lightingMethodStr = SceneUtil::LightManager::getLightingMethodString(lightingMethod);
mLightingMethodButton->removeAllItems();
std::array<SceneUtil::LightingMethod, 3> methods = {
SceneUtil::LightingMethod::FFP,
SceneUtil::LightingMethod::PerObjectUniform,
SceneUtil::LightingMethod::SingleUBO,
};
for (const auto& method : methods)
{
if (!MWBase::Environment::get().getResourceSystem()->getSceneManager()->isSupportedLightingMethod(method))
continue;
mLightingMethodButton->addItem(SceneUtil::LightManager::getLightingMethodString(method));
}
mLightingMethodButton->setIndexSelected(mLightingMethodButton->findItemIndexWith(lightingMethodStr));
}
void SettingsWindow::layoutControlsBox()
{
const int h = 18;
@ -632,6 +744,7 @@ namespace MWGui
{
highlightCurrentResolution();
updateControlsBox();
updateLightSettings();
resetScrollbars();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton);
}

View file

@ -14,6 +14,8 @@ namespace MWGui
void updateControlsBox();
void updateLightSettings();
void onResChange(int, int) override { center(); }
protected:
@ -30,6 +32,10 @@ namespace MWGui
MyGUI::ComboBox* mWaterTextureSize;
MyGUI::ComboBox* mWaterReflectionDetail;
MyGUI::ComboBox* mMaxLights;
MyGUI::ComboBox* mLightingMethodButton;
MyGUI::Button* mLightsResetButton;
// controls
MyGUI::ScrollView* mControlsBox;
MyGUI::Button* mResetControlsButton;
@ -50,6 +56,10 @@ namespace MWGui
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos);
void onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos);
void onLightsResetButtonClicked(MyGUI::Widget* _sender);
void onMaxLightsChanged(MyGUI::ComboBox* _sender, size_t pos);
void onRebindAction(MyGUI::Widget* _sender);
void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel);
void onResetDefaultBindings(MyGUI::Widget* _sender);
@ -61,7 +71,7 @@ namespace MWGui
void apply();
void configureWidgets(MyGUI::Widget* widget);
void configureWidgets(MyGUI::Widget* widget, bool init);
void updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value);
void layoutControlsBox();

View file

@ -93,7 +93,7 @@ namespace MWGui
int windowHeight = window->getSize().height;
//initial values defined in openmw_stats_window.layout, if custom options are not present in .layout, a default is loaded
float leftPaneRatio = 0.44;
float leftPaneRatio = 0.44f;
if (mLeftPane->isUserString("LeftPaneRatio"))
leftPaneRatio = MyGUI::utility::parseFloat(mLeftPane->getUserString("LeftPaneRatio"));

View file

@ -273,7 +273,7 @@ namespace MWGui
std::string widgetName = userStringPair.first.substr(underscorePos+1, userStringPair.first.size()-(underscorePos+1));
type = "Property";
size_t caretPos = key.find("^");
size_t caretPos = key.find('^');
if (caretPos != std::string::npos)
{
type = key.substr(0, caretPos);

View file

@ -1,5 +1,6 @@
#include "windowmanagerimp.hpp"
#include <algorithm>
#include <cassert>
#include <chrono>
#include <thread>
@ -199,8 +200,8 @@ namespace MWGui
, mVersionDescription(versionDescription)
, mWindowVisible(true)
{
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale);
mScalingFactor = std::clamp(Settings::Manager::getFloat("scaling factor", "GUI"), 0.5f, 8.f);
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), mScalingFactor);
mGuiPlatform->initialise(resourcePath, (boost::filesystem::path(logpath) / "MyGUI.log").generic_string());
mGui = new MyGUI::Gui;
@ -211,7 +212,7 @@ namespace MWGui
MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag);
// Load fonts
mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS(), userDataPath));
mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS(), userDataPath, mScalingFactor));
mFontLoader->loadBitmapFonts(exportFonts);
//Register own widgets with MyGUI
@ -1105,8 +1106,9 @@ namespace MWGui
if(tag.compare(0, MyGuiPrefixLength, MyGuiPrefix) == 0)
{
tag = tag.substr(MyGuiPrefixLength, tag.length());
std::string settingSection = tag.substr(0, tag.find(","));
std::string settingTag = tag.substr(tag.find(",")+1, tag.length());
size_t comma_pos = tag.find(',');
std::string settingSection = tag.substr(0, comma_pos);
std::string settingTag = tag.substr(comma_pos+1, tag.length());
_result = Settings::Manager::getString(settingTag, settingSection);
}
@ -1415,6 +1417,11 @@ namespace MWGui
return mHud->getWorldMouseOver();
}
float WindowManager::getScalingFactor()
{
return mScalingFactor;
}
void WindowManager::executeInConsole (const std::string& path)
{
mConsole->executeFile (path);

View file

@ -274,6 +274,8 @@ namespace MWGui
bool getWorldMouseOver() override;
float getScalingFactor() override;
bool toggleFogOfWar() override;
bool toggleFullHelp() override; ///< show extra info in item tooltips (owner, script)
bool getFullHelp() const override;
@ -624,6 +626,8 @@ namespace MWGui
SDLUtil::VideoWrapper* mVideoWrapper;
float mScalingFactor;
/**
* Called when MyGUI tries to retrieve a tag's value. Tags must be denoted in #{tag} notation and will be replaced upon setting a user visible text/property.
* Supported syntax:

View file

@ -32,7 +32,6 @@ namespace MWInput
, mMouseManager(mouseManager)
, mJoystickEnabled (Settings::Manager::getBool("enable controller", "Input"))
, mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input"))
, mInvUiScalingFactor(1.f)
, mSneakToggleShortcutTimer(0.f)
, mGamepadZoom(0)
, mGamepadGuiCursorEnabled(true)
@ -69,10 +68,6 @@ namespace MWInput
}
}
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
if (uiScale > 0.f)
mInvUiScalingFactor = 1.f / uiScale;
float deadZoneRadius = Settings::Manager::getFloat("joystick dead zone", "Input");
deadZoneRadius = std::min(std::max(deadZoneRadius, 0.0f), 0.5f);
mBindingsManager->setJoystickDeadZone(deadZoneRadius);
@ -102,8 +97,10 @@ namespace MWInput
// We keep track of our own mouse position, so that moving the mouse while in
// game mode does not move the position of the GUI cursor
float xMove = xAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed;
float yMove = yAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed;
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
float xMove = xAxis * dt * 1500.0f / uiScale;
float yMove = yAxis * dt * 1500.0f / uiScale;
float mouseWheelMove = -zAxis * dt * 1500.0f;
if (xMove != 0 || yMove != 0 || mouseWheelMove != 0)
{

View file

@ -52,7 +52,6 @@ namespace MWInput
bool mJoystickEnabled;
float mGamepadCursorSpeed;
float mInvUiScalingFactor;
float mSneakToggleShortcutTimer;
float mGamepadZoom;
bool mGamepadGuiCursorEnabled;

View file

@ -29,22 +29,18 @@ namespace MWInput
, mCameraYMultiplier(Settings::Manager::getFloat("camera y multiplier", "Input"))
, mBindingsManager(bindingsManager)
, mInputWrapper(inputWrapper)
, mInvUiScalingFactor(1.f)
, mGuiCursorX(0)
, mGuiCursorY(0)
, mMouseWheel(0)
, mMouseLookEnabled(false)
, mGuiCursorEnabled(true)
{
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
if (uiScale > 0.f)
mInvUiScalingFactor = 1.f / uiScale;
int w,h;
SDL_GetWindowSize(window, &w, &h);
mGuiCursorX = mInvUiScalingFactor * w / 2.f;
mGuiCursorY = mInvUiScalingFactor * h / 2.f;
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
mGuiCursorX = w / (2.f * uiScale);
mGuiCursorY = h / (2.f * uiScale);
}
void MouseManager::processChangedSettings(const Settings::CategorySettingVector& changed)
@ -79,8 +75,9 @@ namespace MWInput
// We keep track of our own mouse position, so that moving the mouse while in
// game mode does not move the position of the GUI cursor
mGuiCursorX = static_cast<float>(arg.x) * mInvUiScalingFactor;
mGuiCursorY = static_cast<float>(arg.y) * mInvUiScalingFactor;
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
mGuiCursorX = static_cast<float>(arg.x) / uiScale;
mGuiCursorY = static_cast<float>(arg.y) / uiScale;
mMouseWheel = static_cast<int>(arg.z);
@ -249,6 +246,7 @@ namespace MWInput
void MouseManager::warpMouse()
{
mInputWrapper->warpMouse(static_cast<int>(mGuiCursorX / mInvUiScalingFactor), static_cast<int>(mGuiCursorY / mInvUiScalingFactor));
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
mInputWrapper->warpMouse(static_cast<int>(mGuiCursorX*uiScale), static_cast<int>(mGuiCursorY*uiScale));
}
}

View file

@ -47,7 +47,6 @@ namespace MWInput
BindingsManager* mBindingsManager;
SDLUtil::InputWrapper* mInputWrapper;
float mInvUiScalingFactor;
float mGuiCursorX;
float mGuiCursorY;

View file

@ -465,7 +465,8 @@ namespace MWMechanics
}
void Actors::updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance)
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance,
bool inCombatOrPursue)
{
if (!actor.getRefData().getBaseNode())
return;
@ -486,7 +487,7 @@ namespace MWMechanics
const osg::Vec3f actor2Pos(targetActor.getRefData().getPosition().asVec3());
float sqrDist = (actor1Pos - actor2Pos).length2();
if (sqrDist > std::min(maxDistance * maxDistance, sqrHeadTrackDistance))
if (sqrDist > std::min(maxDistance * maxDistance, sqrHeadTrackDistance) && !inCombatOrPursue)
return;
// stop tracking when target is behind the actor
@ -494,7 +495,7 @@ namespace MWMechanics
osg::Vec3f targetDirection(actor2Pos - actor1Pos);
actorDirection.z() = 0;
targetDirection.z() = 0;
if (actorDirection * targetDirection > 0
if ((actorDirection * targetDirection > 0 || inCombatOrPursue)
&& MWBase::Environment::get().getWorld()->getLOS(actor, targetActor) // check LOS and awareness last as it's the most expensive function
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(targetActor, actor))
{
@ -2194,28 +2195,25 @@ namespace MWMechanics
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
bool firstPersonPlayer = isPlayer && world->isFirstPerson();
bool inCombatOrPursue = stats.getAiSequence().isInCombat() || stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue);
MWWorld::Ptr activePackageTarget;
// 1. Unconsious actor can not track target
// 2. Actors in combat and pursue mode do not bother to headtrack
// 2. Actors in combat and pursue mode do not bother to headtrack anyone except their target
// 3. Player character does not use headtracking in the 1st-person view
if (!stats.getKnockedDown() && !firstPersonPlayer && !inCombatOrPursue)
if (!stats.getKnockedDown() && !firstPersonPlayer)
{
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
if (inCombatOrPursue)
activePackageTarget = stats.getAiSequence().getActivePackage().getTarget();
for (PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
{
if (it->first == iter->first)
continue;
updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance);
}
}
if (!stats.getKnockedDown() && !isPlayer && inCombatOrPursue)
{
// Actors in combat and pursue mode always look at their target.
for (const auto& package : stats.getAiSequence())
{
headTrackTarget = package->getTarget();
if (!headTrackTarget.isEmpty())
break;
if (inCombatOrPursue && it->first != activePackageTarget)
continue;
updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance, inCombatOrPursue);
}
}

View file

@ -131,7 +131,8 @@ namespace MWMechanics
void turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir);
void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance);
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance,
bool inCombatOrPursue);
void rest(double hours, bool sleep);
///< Update actors while the player is waiting or sleeping.

View file

@ -23,7 +23,7 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro
actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
actorClass.getMovementSettings(actor).mPosition[1] = 1;
smoothTurn(actor, -osg::PI / 2, 0);
smoothTurn(actor, static_cast<float>(-osg::PI_2), 0);
return false;
}

View file

@ -331,8 +331,9 @@ namespace MWMechanics
&& ((!storage.mReadyToAttack && !mPathFinder.isPathConstructed())
|| (storage.mUseCustomDestination && (storage.mCustomDestination - vTargetPos).length() > rangeAttack)))
{
const MWBase::World* world = MWBase::Environment::get().getWorld();
// Try to build path to the target.
const auto halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
const auto halfExtents = world->getPathfindingHalfExtents(actor);
const auto navigatorFlags = getNavigatorFlags(actor);
const auto areaCosts = getAreaCosts(actor);
const auto pathGridGraph = getPathGridGraph(actor.getCell());
@ -342,11 +343,7 @@ namespace MWMechanics
{
// If there is no path, try to find a point on a line from the actor position to target projected
// on navmesh to attack the target from there.
const MWBase::World* world = MWBase::Environment::get().getWorld();
const auto halfExtents = world->getPathfindingHalfExtents(actor);
const auto navigator = world->getNavigator();
const auto navigatorFlags = getNavigatorFlags(actor);
const auto areaCosts = getAreaCosts(actor);
const auto hit = navigator->raycast(halfExtents, vActorPos, vTargetPos, navigatorFlags);
if (hit.has_value() && (*hit - vTargetPos).length() <= rangeAttack)
@ -602,7 +599,7 @@ namespace MWMechanics
// Otherwise apply a random side step (kind of dodging) with some probability
// if actor is within range of target's weapon.
if (std::abs(angleToTarget) > osg::PI / 4)
moveDuration = 0.2;
moveDuration = 0.2f;
else if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25)
moveDuration = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();
if (moveDuration > 0)
@ -810,16 +807,18 @@ osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& t
float t_collision;
float projVelDirSquared = projSpeed * projSpeed - velPerp * velPerp;
osg::Vec3f vTargetMoveDirNormalized = vTargetMoveDir;
vTargetMoveDirNormalized.normalize();
float projDistDiff = vDirToTarget * vTargetMoveDirNormalized; // dot product
projDistDiff = std::sqrt(distToTarget * distToTarget - projDistDiff * projDistDiff);
if (projVelDirSquared > 0)
{
osg::Vec3f vTargetMoveDirNormalized = vTargetMoveDir;
vTargetMoveDirNormalized.normalize();
float projDistDiff = vDirToTarget * vTargetMoveDirNormalized; // dot product
projDistDiff = std::sqrt(distToTarget * distToTarget - projDistDiff * projDistDiff);
t_collision = projDistDiff / (std::sqrt(projVelDirSquared) - velDir);
else t_collision = 0; // speed of projectile is not enough to reach moving target
}
else
t_collision = 0; // speed of projectile is not enough to reach moving target
return vDirToTarget + vTargetMoveDir * t_collision;
}

View file

@ -25,6 +25,14 @@
#include <osg/Quat>
namespace
{
float divOrMax(float dividend, float divisor)
{
return divisor == 0 ? std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon() : dividend / divisor;
}
}
MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) :
mTypeId(typeId),
mOptions(options),
@ -416,14 +424,15 @@ DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld::
const MWWorld::Class& actorClass = actor.getClass();
DetourNavigator::Flags result = DetourNavigator::Flag_none;
if (actorClass.isPureWaterCreature(actor)
|| (getTypeId() != AiPackageTypeId::Wander
&& ((allowToFollowOverWaterSurface && getTypeId() == AiPackageTypeId::Follow)
|| actorClass.canSwim(actor)
|| hasWaterWalking(actor))))
if ((actorClass.isPureWaterCreature(actor)
|| (getTypeId() != AiPackageTypeId::Wander
&& ((allowToFollowOverWaterSurface && getTypeId() == AiPackageTypeId::Follow)
|| actorClass.canSwim(actor)
|| hasWaterWalking(actor)))
) && actorClass.getSwimSpeed(actor) > 0)
result |= DetourNavigator::Flag_swim;
if (actorClass.canWalk(actor))
if (actorClass.canWalk(actor) && actor.getClass().getWalkSpeed(actor) > 0)
result |= DetourNavigator::Flag_walk;
if (actorClass.isBipedal(actor) && getTypeId() != AiPackageTypeId::Wander)
@ -439,15 +448,15 @@ DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::P
const MWWorld::Class& actorClass = actor.getClass();
if (flags & DetourNavigator::Flag_swim)
costs.mWater = costs.mWater / actorClass.getSwimSpeed(actor);
costs.mWater = divOrMax(costs.mWater, actorClass.getSwimSpeed(actor));
if (flags & DetourNavigator::Flag_walk)
{
float walkCost;
if (getTypeId() == AiPackageTypeId::Wander)
walkCost = 1.0 / actorClass.getWalkSpeed(actor);
walkCost = divOrMax(1.0, actorClass.getWalkSpeed(actor));
else
walkCost = 1.0 / actorClass.getRunSpeed(actor);
walkCost = divOrMax(1.0, actorClass.getRunSpeed(actor));
costs.mDoor = costs.mDoor * walkCost;
costs.mPathgrid = costs.mPathgrid * walkCost;
costs.mGround = costs.mGround * walkCost;

View file

@ -1037,7 +1037,7 @@ void CharacterController::handleTextKey(const std::string &groupname, SceneUtil:
// The event can optionally contain volume and pitch modifiers
float volume=1.f, pitch=1.f;
if (soundgen.find(" ") != std::string::npos)
if (soundgen.find(' ') != std::string::npos)
{
std::vector<std::string> tokens;
split(soundgen, ' ', tokens);

View file

@ -153,23 +153,23 @@ namespace MWMechanics
void SpellList::addListener(Spells* spells)
{
for(const auto ptr : mListeners)
{
if(ptr == spells)
return;
}
if (std::find(mListeners.begin(), mListeners.end(), spells) != mListeners.end())
return;
mListeners.push_back(spells);
}
void SpellList::removeListener(Spells* spells)
{
for(auto it = mListeners.begin(); it != mListeners.end(); it++)
{
if(*it == spells)
{
mListeners.erase(it);
break;
}
}
const auto it = std::find(mListeners.begin(), mListeners.end(), spells);
if (it != mListeners.end())
mListeners.erase(it);
}
void SpellList::updateListener(Spells* before, Spells* after)
{
const auto it = std::find(mListeners.begin(), mListeners.end(), before);
if (it == mListeners.end())
return mListeners.push_back(after);
*it = after;
}
}

View file

@ -61,6 +61,8 @@ namespace MWMechanics
void removeListener(Spells* spells);
void updateListener(Spells* before, Spells* after);
const std::vector<std::string> getSpells() const;
};
}

View file

@ -43,6 +43,15 @@ namespace MWMechanics
mSpellList->addListener(this);
}
Spells::Spells(Spells&& spells) : mSpellList(std::move(spells.mSpellList)), mSpells(std::move(spells.mSpells)),
mSelectedSpell(std::move(spells.mSelectedSpell)), mUsedPowers(std::move(spells.mUsedPowers)),
mSpellsChanged(std::move(spells.mSpellsChanged)), mEffects(std::move(spells.mEffects)),
mSourcedEffects(std::move(spells.mSourcedEffects))
{
if (mSpellList)
mSpellList->updateListener(&spells, this);
}
std::map<const ESM::Spell*, SpellParams>::const_iterator Spells::begin() const
{
return mSpells.begin();

View file

@ -59,7 +59,7 @@ namespace MWMechanics
Spells(const Spells&);
Spells(const Spells&&) = delete;
Spells(Spells&& spells);
~Spells();

View file

@ -156,6 +156,7 @@ void Actor::updatePosition()
mPreviousPosition = mWorldPosition;
mPosition = mWorldPosition;
mSimulationPosition = mWorldPosition;
mPositionOffset = osg::Vec3f();
mStandingOnPtr = nullptr;
mSkipSimulation = true;
}
@ -231,7 +232,6 @@ void Actor::applyOffsetChange()
{
if (mPositionOffset.length() == 0)
return;
mWorldPosition += mPositionOffset;
mPosition += mPositionOffset;
mPreviousPosition += mPositionOffset;
mSimulationPosition += mPositionOffset;

View file

@ -17,10 +17,10 @@ namespace MWPhysics
// Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared.
static constexpr int sMaxIterations = 8;
// Allows for more precise movement solving without getting stuck or snagging too easily.
static constexpr float sCollisionMargin = 0.1;
static constexpr float sCollisionMargin = 0.1f;
// Allow for a small amount of penetration to prevent numerical precision issues from causing the "unstuck"ing code to run unnecessarily
// Currently set to 0 because having the "unstuck"ing code run whenever possible prevents some glitchy snagging issues
static constexpr float sAllowedPenetration = 0.0;
static constexpr float sAllowedPenetration = 0.0f;
}
#endif

View file

@ -636,9 +636,6 @@ namespace MWPhysics
return nullptr;
}();
if (caster == nullptr)
Log(Debug::Warning) << "No caster for projectile " << projectileId;
ProjectileConvexCallback resultCallback(caster, btFrom, btTo, projectile);
resultCallback.m_collisionFilterMask = 0xff;
resultCallback.m_collisionFilterGroup = CollisionType_Projectile;

View file

@ -60,14 +60,14 @@ namespace MWPhysics
// attempt 3: further, less tall fixed distance movement, same as above
// If you're making a full conversion you should purge the logic for attempts 2 and 3. Attempts 2 and 3 just try to work around problems with vanilla Morrowind assets.
int attempt = 0;
float downStepSize;
float downStepSize = 0;
while(attempt < 3)
{
attempt++;
if(attempt == 1)
tracerDest = tracerPos + toMove;
else if (!firstIteration || !sDoExtraStairHacks) // first attempt failed and not on first movement solver iteration, can't retry -- or we have extra hacks disabled
else if (!sDoExtraStairHacks) // early out if we have extra hacks disabled
{
return false;
}

View file

@ -358,6 +358,8 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
}
mScabbard = attachMesh(scabbardName, boneName);
if (mScabbard)
resetControllers(mScabbard->getNode());
osg::Group* weaponNode = getBoneByName("Bip01 Weapon");
if (!weaponNode)

View file

@ -499,6 +499,11 @@ namespace MWRender
mAlpha = alpha;
}
void setLightSource(const osg::ref_ptr<SceneUtil::LightSource>& lightSource)
{
mLightSource = lightSource;
}
protected:
void setDefaults(osg::StateSet* stateset) override
{
@ -521,10 +526,13 @@ namespace MWRender
{
osg::Material* material = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
material->setAlpha(osg::Material::FRONT_AND_BACK, mAlpha);
if (mLightSource)
mLightSource->setActorFade(mAlpha);
}
private:
float mAlpha;
osg::ref_ptr<SceneUtil::LightSource> mLightSource;
};
struct Animation::AnimSource
@ -1613,7 +1621,7 @@ namespace MWRender
{
bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();
SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior);
mExtraLightSource = SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior);
}
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture)
@ -1763,6 +1771,7 @@ namespace MWRender
if (mTransparencyUpdater == nullptr)
{
mTransparencyUpdater = new TransparencyUpdater(alpha);
mTransparencyUpdater->setLightSource(mExtraLightSource);
mObjectRoot->addCullCallback(mTransparencyUpdater);
}
else

View file

@ -278,6 +278,7 @@ protected:
osg::ref_ptr<SceneUtil::LightSource> mGlowLight;
osg::ref_ptr<SceneUtil::GlowUpdater> mGlowUpdater;
osg::ref_ptr<TransparencyUpdater> mTransparencyUpdater;
osg::ref_ptr<SceneUtil::LightSource> mExtraLightSource;
float mAlpha;

View file

@ -433,7 +433,7 @@ namespace MWRender
void Camera::setPitch(float angle)
{
const float epsilon = 0.000001f;
float limit = osg::PI_2 - epsilon;
float limit = static_cast<float>(osg::PI_2) - epsilon;
mPitch = osg::clampBetween(angle, -limit, limit);
}

View file

@ -174,7 +174,9 @@ namespace MWRender
mCamera->setNodeMask(Mask_RenderToTexture);
osg::ref_ptr<SceneUtil::LightManager> lightManager = new SceneUtil::LightManager;
bool ffp = mResourceSystem->getSceneManager()->getLightingMethod() == SceneUtil::LightingMethod::FFP;
osg::ref_ptr<SceneUtil::LightManager> lightManager = new SceneUtil::LightManager(ffp);
lightManager->setStartLight(1);
osg::ref_ptr<osg::StateSet> stateset = lightManager->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
@ -231,12 +233,22 @@ namespace MWRender
float positionZ = std::cos(altitude);
light->setPosition(osg::Vec4(positionX,positionY,positionZ, 0.0));
light->setDiffuse(osg::Vec4(diffuseR,diffuseG,diffuseB,1));
light->setAmbient(osg::Vec4(ambientR,ambientG,ambientB,1));
osg::Vec4 ambientRGBA = osg::Vec4(ambientR,ambientG,ambientB,1);
if (mResourceSystem->getSceneManager()->getForceShaders())
{
// When using shaders, we now skip the ambient sun calculation as this is the only place it's used.
// Using the scene ambient will give identical results.
lightmodel->setAmbientIntensity(ambientRGBA);
light->setAmbient(osg::Vec4(0,0,0,1));
}
else
light->setAmbient(ambientRGBA);
light->setSpecular(osg::Vec4(0,0,0,0));
light->setLightNum(0);
light->setConstantAttenuation(1.f);
light->setLinearAttenuation(0.f);
light->setQuadraticAttenuation(0.f);
lightManager->setSunlight(light);
osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource;
lightSource->setLight(light);
@ -414,7 +426,7 @@ namespace MWRender
visitor.setTraversalNumber(mDrawOnceCallback->getLastRenderedFrame());
osg::Node::NodeMask nodeMask = mCamera->getNodeMask();
mCamera->setNodeMask(~0);
mCamera->setNodeMask(~0u);
mCamera->accept(visitor);
mCamera->setNodeMask(nodeMask);

View file

@ -5,6 +5,7 @@
#include <osg/VertexAttribDivisor>
#include <components/esm/esmreader.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include "apps/openmw/mwworld/esmstore.hpp"
#include "apps/openmw/mwbase/environment.hpp"
@ -271,6 +272,8 @@ namespace MWRender
group->getOrCreateStateSet()->setAttributeAndModes(alpha.get(), osg::StateAttribute::ON);
group->getBound();
group->setNodeMask(Mask_Groundcover);
if (mSceneManager->getLightingMethod() != SceneUtil::LightingMethod::FFP)
group->setCullCallback(new SceneUtil::LightListCallback);
mSceneManager->recreateShaders(group, "groundcover", false, true);
return group;

View file

@ -19,9 +19,13 @@
#include <components/sceneutil/visitor.hpp>
#include <components/sceneutil/shadow.hpp>
#include <components/sceneutil/util.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/files/memorystream.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/resource/resourcesystem.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/cellstore.hpp"
@ -89,9 +93,8 @@ LocalMap::LocalMap(osg::Group* root)
, mInterior(false)
{
// Increase map resolution, if use UI scaling
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
if (uiScale > 0.f)
mMapResolution *= uiScale;
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
mMapResolution *= uiScale;
SceneUtil::FindByNameVisitor find("Scene Root");
mRoot->accept(find);
@ -220,6 +223,9 @@ osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);
// override sun for local map
SceneUtil::configureStateSetSunOverride(static_cast<SceneUtil::LightManager*>(mSceneRoot.get()), light, stateset);
camera->addChild(lightSource);
camera->setStateSet(stateset);
camera->setViewport(0, 0, mMapResolution, mMapResolution);

View file

@ -402,7 +402,7 @@ namespace MWRender
refs[ref.mRefNum] = ref;
}
}
catch (std::exception& e)
catch (std::exception&)
{
continue;
}

View file

@ -52,6 +52,7 @@
#include "../mwgui/loadingscreen.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "sky.hpp"
#include "effectmanager.hpp"
@ -195,14 +196,20 @@ namespace MWRender
, mWorkQueue(workQueue)
, mUnrefQueue(new SceneUtil::UnrefQueue)
, mNavigator(navigator)
, mMinimumAmbientLuminance(0.f)
, mNightEyeFactor(0.f)
, mFieldOfViewOverridden(false)
, mFieldOfViewOverride(0.f)
{
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
// Shadows and radial fog have problems with fixed-function mode
bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders") || Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows");
bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders")
|| Settings::Manager::getBool("force shaders", "Shaders")
|| Settings::Manager::getBool("enable shadows", "Shadows")
|| lightingMethod != SceneUtil::LightingMethod::FFP;
resourceSystem->getSceneManager()->setForceShaders(forceShaders);
// FIXME: calling dummy method because terrain needs to know whether lighting is clamped
resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders"));
@ -214,7 +221,13 @@ namespace MWRender
resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders"));
resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1);
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager;
// Let LightManager choose which backend to use based on our hint. For methods besides legacy lighting, this depends on support for various OpenGL extensions.
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(lightingMethod == SceneUtil::LightingMethod::FFP);
resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod());
resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod());
resourceSystem->getSceneManager()->setSupportedLightingMethods(sceneRoot->getSupportedLightingMethods());
mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f);
sceneRoot->setLightingMask(Mask_Lighting);
mSceneRoot = sceneRoot;
sceneRoot->setStartLight(1);
@ -236,6 +249,7 @@ namespace MWRender
mShadowManager.reset(new SceneUtil::ShadowManager(sceneRoot, mRootNode, shadowCastingTraversalMask, indoorShadowCastingTraversalMask, mResourceSystem->getSceneManager()->getShaderManager()));
Shader::ShaderManager::DefineMap shadowDefines = mShadowManager->getShadowDefines();
Shader::ShaderManager::DefineMap lightDefines = sceneRoot->getLightDefines();
Shader::ShaderManager::DefineMap globalDefines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
for (auto itr = shadowDefines.begin(); itr != shadowDefines.end(); itr++)
@ -247,9 +261,15 @@ namespace MWRender
globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0";
globalDefines["useGPUShader4"] = "0";
for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++)
globalDefines[itr->first] = itr->second;
// Refactor this at some point - most shaders don't care about these defines
float groundcoverDistance = (Constants::CellSizeInUnits * std::max(1, Settings::Manager::getInt("distance", "Groundcover")) - 1024) * 0.93;
globalDefines["groundcoverFadeStart"] = std::to_string(groundcoverDistance * 0.9f);
globalDefines["groundcoverFadeEnd"] = std::to_string(groundcoverDistance);
globalDefines["groundcoverStompMode"] = std::to_string(std::clamp(Settings::Manager::getInt("stomp mode", "Groundcover"), 0, 2));
globalDefines["groundcoverStompIntensity"] = std::to_string(std::clamp(Settings::Manager::getInt("stomp intensity", "Groundcover"), 0, 2));
// It is unnecessary to stop/start the viewer as no frames are being rendered yet.
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines);
@ -352,6 +372,7 @@ namespace MWRender
mSunLight->setAmbient(osg::Vec4f(0,0,0,1));
mSunLight->setSpecular(osg::Vec4f(0,0,0,0));
mSunLight->setConstantAttenuation(1.f);
sceneRoot->setSunlight(mSunLight);
sceneRoot->addChild(source);
sceneRoot->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
@ -520,7 +541,32 @@ namespace MWRender
void RenderingManager::configureAmbient(const ESM::Cell *cell)
{
setAmbientColour(SceneUtil::colourFromRGB(cell->mAmbi.mAmbient));
bool needsAdjusting = false;
if (mResourceSystem->getSceneManager()->getLightingMethod() != SceneUtil::LightingMethod::FFP)
needsAdjusting = !cell->isExterior() && !(cell->mData.mFlags & ESM::Cell::QuasiEx);
auto ambient = SceneUtil::colourFromRGB(cell->mAmbi.mAmbient);
if (needsAdjusting)
{
constexpr float pR = 0.2126;
constexpr float pG = 0.7152;
constexpr float pB = 0.0722;
// we already work in linear RGB so no conversions are needed for the luminosity function
float relativeLuminance = pR*ambient.r() + pG*ambient.g() + pB*ambient.b();
if (relativeLuminance < mMinimumAmbientLuminance)
{
// brighten ambient so it reaches the minimum threshold but no more, we want to mess with content data as least we can
float targetBrightnessIncreaseFactor = mMinimumAmbientLuminance / relativeLuminance;
if (ambient.r() == 0.f && ambient.g() == 0.f && ambient.b() == 0.f)
ambient = osg::Vec4(mMinimumAmbientLuminance, mMinimumAmbientLuminance, mMinimumAmbientLuminance, ambient.a());
else
ambient *= targetBrightnessIncreaseFactor;
}
}
setAmbientColour(ambient);
osg::Vec4f diffuse = SceneUtil::colourFromRGB(cell->mAmbi.mSunlight);
mSunLight->setDiffuse(diffuse);
@ -614,7 +660,7 @@ namespace MWRender
}
else if (mode == Render_Scene)
{
int mask = mViewer->getCamera()->getCullMask();
unsigned int mask = mViewer->getCamera()->getCullMask();
bool enabled = mask&Mask_Scene;
enabled = !enabled;
if (enabled)
@ -769,7 +815,7 @@ namespace MWRender
return false;
}
int maskBackup = mPlayerAnimation->getObjectRoot()->getNodeMask();
unsigned int maskBackup = mPlayerAnimation->getObjectRoot()->getNodeMask();
if (mCamera->isFirstPerson())
mPlayerAnimation->getObjectRoot()->setNodeMask(0);
@ -814,8 +860,7 @@ namespace MWRender
{
RenderingManager::RayResult result;
result.mHit = false;
result.mHitRefnum.mContentFile = -1;
result.mHitRefnum.mIndex = -1;
result.mHitRefnum.unset();
result.mRatio = 0;
if (intersector->containsIntersections())
{
@ -871,7 +916,7 @@ namespace MWRender
mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp());
mIntersectionVisitor->setIntersector(intersector);
int mask = ~0;
unsigned int mask = ~0u;
mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water|Mask_SimpleWater|Mask_Groundcover);
if (ignorePlayer)
mask &= ~(Mask_Player);
@ -1103,9 +1148,47 @@ namespace MWRender
else if (it->first == "General" && (it->second == "texture filter" ||
it->second == "texture mipmap" ||
it->second == "anisotropy"))
{
updateTextureFiltering();
}
else if (it->first == "Water")
{
mWater->processChangedSettings(changed);
}
else if (it->first == "Shaders" && it->second == "minimum interior brightness")
{
mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f);
if (MWMechanics::getPlayer().isInCell())
configureAmbient(MWMechanics::getPlayer().getCell()->getCell());
}
else if (it->first == "Shaders" && (it->second == "light bounds multiplier" ||
it->second == "maximum light distance" ||
it->second == "light fade start" ||
it->second == "max lights"))
{
auto* lightManager = static_cast<SceneUtil::LightManager*>(getLightRoot());
lightManager->processChangedSettings(changed);
if (it->second == "max lights" && !lightManager->usingFFP())
{
mViewer->stopThreading();
lightManager->updateMaxLights();
auto defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
for (const auto& [name, key] : lightManager->getLightDefines())
defines[name] = key;
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
mSceneRoot->removeUpdateCallback(mStateUpdater);
mStateUpdater = new StateUpdater;
mSceneRoot->addUpdateCallback(mStateUpdater);
mStateUpdater->setFogEnd(mViewDistance);
updateAmbient();
mViewer->startThreading();
}
}
}
}

View file

@ -296,6 +296,7 @@ namespace MWRender
osg::ref_ptr<StateUpdater> mStateUpdater;
osg::Vec4f mAmbientColor;
float mMinimumAmbientLuminance;
float mNightEyeFactor;
float mNearClip;

View file

@ -317,8 +317,8 @@ public:
if (cv->getCullingMode() & osg::CullSettings::FAR_PLANE_CULLING)
++numPlanes;
int mask = 0x1;
int resultMask = cv->getProjectionCullingStack().back().getFrustum().getResultMask();
unsigned int mask = 0x1;
unsigned int resultMask = cv->getProjectionCullingStack().back().getFrustum().getResultMask();
for (unsigned int i=0; i<cv->getProjectionCullingStack().back().getFrustum().getPlaneList().size(); ++i)
{
if (i >= numPlanes)
@ -441,7 +441,7 @@ private:
class CelestialBody
{
public:
CelestialBody(osg::Group* parentNode, float scaleFactor, int numUvSets, unsigned int visibleMask=~0)
CelestialBody(osg::Group* parentNode, float scaleFactor, int numUvSets, unsigned int visibleMask=~0u)
: mVisibleMask(visibleMask)
{
mGeom = createTexturedQuad(numUvSets);
@ -1315,7 +1315,8 @@ public:
while (callback)
{
if ((composite = dynamic_cast<SceneUtil::CompositeStateSetUpdater*>(callback)))
composite = dynamic_cast<SceneUtil::CompositeStateSetUpdater*>(callback);
if (composite)
break;
callback = callback->getNestedCallback();
@ -1624,7 +1625,7 @@ void SkyManager::setEnabled(bool enabled)
if (enabled && !mCreated)
create();
mRootNode->setNodeMask(enabled ? Mask_Sky : 0);
mRootNode->setNodeMask(enabled ? Mask_Sky : 0u);
mEnabled = enabled;
}
@ -1785,7 +1786,7 @@ void SkyManager::setWeather(const WeatherResult& weather)
mCloudUpdater->setOpacity((1.f-mCloudBlendFactor));
mCloudUpdater2->setOpacity(mCloudBlendFactor);
mCloudMesh2->setNodeMask(mCloudBlendFactor > 0.f ? ~0 : 0);
mCloudMesh2->setNodeMask(mCloudBlendFactor > 0.f ? ~0u : 0);
}
if (mCloudColour != weather.mFogColor)
@ -1830,7 +1831,7 @@ void SkyManager::setWeather(const WeatherResult& weather)
mAtmosphereNightUpdater->setFade(mStarsOpacity);
}
mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0 : 0);
mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0u : 0);
mPrecipitationAlpha = weather.mPrecipitationAlpha;
}

View file

@ -19,7 +19,7 @@ namespace MWRender
/// another mask, or what type of node this mask is usually set on.
/// @note The mask values are not serialized within models, nor used in any other way that would break backwards
/// compatibility if the enumeration values were to be changed. Feel free to change them when it makes sense.
enum VisMask
enum VisMask : unsigned int
{
Mask_UpdateVisitor = 0x1, // reserved for separating UpdateVisitors from CullVisitors

View file

@ -28,6 +28,7 @@
#include <components/sceneutil/shadow.hpp>
#include <components/sceneutil/util.hpp>
#include <components/sceneutil/waterutil.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/misc/constants.hpp>
@ -670,6 +671,9 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
osg::ref_ptr<osg::Program> program (new osg::Program);
program->addShader(vertexShader);
program->addShader(fragmentShader);
auto method = mResourceSystem->getSceneManager()->getLightingMethod();
if (method == SceneUtil::LightingMethod::SingleUBO)
program->addBindUniformBlock("LightBufferBinding", static_cast<int>(Shader::UBOBinding::LightBuffer));
shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);
node->setStateSet(shaderStateset);
@ -772,11 +776,11 @@ void Water::update(float dt)
void Water::updateVisible()
{
bool visible = mEnabled && mToggled;
mWaterNode->setNodeMask(visible ? ~0 : 0);
mWaterNode->setNodeMask(visible ? ~0u : 0u);
if (mRefraction)
mRefraction->setNodeMask(visible ? Mask_RenderToTexture : 0);
mRefraction->setNodeMask(visible ? Mask_RenderToTexture : 0u);
if (mReflection)
mReflection->setNodeMask(visible ? Mask_RenderToTexture : 0);
mReflection->setNodeMask(visible ? Mask_RenderToTexture : 0u);
}
bool Water::toggle()

View file

@ -84,7 +84,7 @@ namespace MWWorld
mTerrain->cacheCell(mTerrainView.get(), mX, mY);
mPreloadedObjects.insert(mLandManager->getLand(mX, mY));
}
catch(std::exception& e)
catch(std::exception&)
{
}
}
@ -127,7 +127,7 @@ namespace MWWorld
mPreloadedObjects.insert(mBulletShapeManager->getShape(mesh));
}
catch (std::exception& e)
catch (std::exception&)
{
// ignore error for now, would spam the log too much
// error will be shown when visiting the cell

View file

@ -157,7 +157,7 @@ MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReade
: mStore (store), mReader (reader),
mIdCacheIndex (0)
{
int cacheSize = std::max(Settings::Manager::getInt("pointers cache size", "Cells"), 0);
int cacheSize = std::clamp(Settings::Manager::getInt("pointers cache size", "Cells"), 40, 1000);
mIdCache = IdCache(cacheSize, std::pair<std::string, CellStore *> ("", (CellStore*)nullptr));
}

View file

@ -88,27 +88,13 @@ namespace
MWWorld::ResolutionListener::~ResolutionListener()
{
if(!mStore.mModified && mStore.mResolved && !mStore.mPtr.isEmpty())
try
{
try
{
for(const auto&& ptr : mStore)
ptr.getRefData().setCount(0);
}
catch(const std::exception& e)
{
Log(Debug::Warning) << "Failed to clear temporary container contents of " << mStore.mPtr.get<ESM::Container>()->mBase->mId << ": " << e.what();
}
mStore.fillNonRandom(mStore.mPtr.get<ESM::Container>()->mBase->mInventory, "", mStore.mSeed);
try
{
addScripts(mStore, mStore.mPtr.mCell);
}
catch(const std::exception& e)
{
Log(Debug::Warning) << "Failed to restart item scripts inside " << mStore.mPtr.get<ESM::Container>()->mBase->mId << ": " << e.what();
}
mStore.mResolved = false;
mStore.unresolve();
}
catch(const std::exception& e)
{
Log(Debug::Error) << "Failed to clear temporary container contents: " << e.what();
}
}
@ -767,6 +753,21 @@ MWWorld::ResolutionHandle MWWorld::ContainerStore::resolveTemporarily()
return {listener};
}
void MWWorld::ContainerStore::unresolve()
{
if (mModified)
return;
if (mResolved && !mPtr.isEmpty())
{
for(const auto&& ptr : *this)
ptr.getRefData().setCount(0);
fillNonRandom(mPtr.get<ESM::Container>()->mBase->mInventory, "", mSeed);
addScripts(*this, mPtr.mCell);
mResolved = false;
}
}
float MWWorld::ContainerStore::getWeight() const
{
if (!mWeightUpToDate)

View file

@ -261,6 +261,7 @@ namespace MWWorld
void resolve();
ResolutionHandle resolveTemporarily();
void unresolve();
friend class ContainerStoreIteratorBase<Ptr>;
friend class ContainerStoreIteratorBase<ConstPtr>;

View file

@ -63,9 +63,9 @@ namespace
// We will replace invalid entries by fixed ones
std::vector<ESM::NPC> npcsToReplace;
for (auto it : npcs)
for (auto npcIter : npcs)
{
ESM::NPC npc = it.second;
ESM::NPC npc = npcIter.second;
bool changed = false;
const std::string npcFaction = npc.mFaction;

View file

@ -1037,7 +1037,7 @@ namespace MWWorld
{
mSceneManager->getTemplate(mMesh);
}
catch (std::exception& e)
catch (std::exception&)
{
}
}
@ -1121,7 +1121,7 @@ namespace MWWorld
exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
}
}
catch (std::exception& e)
catch (std::exception&)
{
// ignore error for now, would spam the log too much
}

View file

@ -1444,11 +1444,12 @@ namespace MWWorld
MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive)
{
auto* actor = mPhysics->getActor(ptr);
osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec;
if (actor)
actor->adjustPosition(vec);
osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec;
return moveObject(ptr, newpos.x(), newpos.y(), newpos.z(), false, moveToActive && ptr != getPlayerPtr());
if (ptr.getClass().isActor())
return moveObject(ptr, newpos.x(), newpos.y(), newpos.z(), false, moveToActive && ptr != getPlayerPtr());
return moveObject(ptr, newpos.x(), newpos.y(), newpos.z());
}
void World::scaleObject (const Ptr& ptr, float scale)
@ -4387,7 +4388,7 @@ namespace MWWorld
if (!model.empty())
scene->preload(model, ref.getPtr().getClass().useAnim());
}
catch(std::exception& e)
catch(std::exception&)
{
}
}

View file

@ -835,4 +835,55 @@ namespace
ASSERT_THAT(result, Optional(Vec3fEq(mEnd.x(), mEnd.y(), 1.87719)));
}
TEST_F(DetourNavigatorNavigatorTest, update_for_oscillating_object_that_does_not_change_navmesh_should_not_trigger_navmesh_update)
{
const std::array<btScalar, 5 * 5> heightfieldData {{
0, 0, 0, 0, 0,
0, -25, -25, -25, -25,
0, -25, -100, -100, -100,
0, -25, -100, -100, -100,
0, -25, -100, -100, -100,
}};
btHeightfieldTerrainShape heightfieldShape = makeSquareHeightfieldTerrainShape(heightfieldData);
heightfieldShape.setLocalScaling(btVector3(128, 128, 1));
const btBoxShape oscillatingBoxShape(btVector3(20, 20, 20));
const btVector3 oscillatingBoxShapePosition(32, 32, 400);
const btBoxShape boderBoxShape(btVector3(50, 50, 50));
mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape,
btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition));
// add this box to make navmesh bound box independent from oscillatingBoxShape rotations
mNavigator->addObject(ObjectId(&boderBoxShape), boderBoxShape,
btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200)));
mNavigator->update(mPlayerPosition);
mNavigator->wait();
const auto navMeshes = mNavigator->getNavMeshes();
ASSERT_EQ(navMeshes.size(), 1);
{
const auto navMesh = navMeshes.begin()->second->lockConst();
ASSERT_EQ(navMesh->getGeneration(), 1);
ASSERT_EQ(navMesh->getNavMeshRevision(), 4);
}
for (int n = 0; n < 10; ++n)
{
const btTransform transform(btQuaternion(btVector3(0, 0, 1), n * 2 * osg::PI / 10),
oscillatingBoxShapePosition);
mNavigator->updateObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, transform);
mNavigator->update(mPlayerPosition);
mNavigator->wait();
}
ASSERT_EQ(navMeshes.size(), 1);
{
const auto navMesh = navMeshes.begin()->second->lockConst();
ASSERT_EQ(navMesh->getGeneration(), 1);
ASSERT_EQ(navMesh->getNavMeshRevision(), 4);
}
}
}

View file

@ -77,7 +77,7 @@ namespace
EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1}));
}
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_existing_element_should_throw_exception)
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_existing_element_should_return_cached_element)
{
const std::size_t navMeshDataSize = 1;
const std::size_t navMeshKeySize = cRecastMeshKeySize;
@ -87,10 +87,9 @@ namespace
NavMeshData anotherNavMeshData {anotherData, 1};
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));
EXPECT_THROW(
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(anotherNavMeshData)),
InvalidArgument
);
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(anotherNavMeshData));
ASSERT_TRUE(result);
EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1}));
}
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_should_return_cached_value)

View file

@ -89,7 +89,7 @@ add_component_dir (esmterrain
)
add_component_dir (misc
constants utf8stream stringops resourcehelpers rng messageformatparser weakcache
constants utf8stream stringops resourcehelpers rng messageformatparser weakcache thread
)
add_component_dir (debug
@ -265,6 +265,8 @@ if (BUILD_OPENMW OR BUILD_OPENCS)
navigator
findrandompointaroundcircle
raycast
navmeshtileview
oscillatingrecastmeshobject
)
endif()
# End of tes3mp change (major)

View file

@ -168,6 +168,14 @@ void BSAFile::readHeader()
fs.setNameInfos(namesOffset, &mStringBuf);
fs.hash = hashes[i];
if (namesOffset >= mStringBuf.size()) {
fail("Archive contains names offset outside itself");
}
const void* end = std::memchr(fs.name(), '\0', mStringBuf.size()-namesOffset);
if (!end) {
fail("Archive contains non-zero terminated string");
}
endOfNameBuffer = std::max(endOfNameBuffer, namesOffset + std::strlen(fs.name())+1);
assert(endOfNameBuffer <= mStringBuf.size());

View file

@ -35,7 +35,16 @@
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#if defined(_MSC_VER)
#pragma warning (push)
#pragma warning (disable : 4706)
#include <boost/iostreams/filter/zlib.hpp>
#pragma warning (pop)
#else
#include <boost/iostreams/filter/zlib.hpp>
#endif
#include <boost/iostreams/device/array.hpp>
#include <components/bsa/memorystream.hpp>

View file

@ -0,0 +1,29 @@
#ifndef OPENMW_COMPONENTS_BULLETHELPERS_AABB_H
#define OPENMW_COMPONENTS_BULLETHELPERS_AABB_H
#include <LinearMath/btVector3.h>
#include <LinearMath/btTransform.h>
#include <BulletCollision/CollisionShapes/btCollisionShape.h>
#include <BulletCollision/Gimpact/btBoxCollision.h>
inline bool operator==(const btAABB& lhs, const btAABB& rhs)
{
return lhs.m_min == rhs.m_min && lhs.m_max == rhs.m_max;
}
inline bool operator!=(const btAABB& lhs, const btAABB& rhs)
{
return !(lhs == rhs);
}
namespace BulletHelpers
{
inline btAABB getAabb(const btCollisionShape& shape, const btTransform& transform)
{
btAABB result;
shape.getAabb(transform, result.m_min, result.m_max);
return result;
}
}
#endif

View file

@ -43,7 +43,7 @@ namespace bfs = boost::filesystem;
#include <sys/user.h>
#endif
static const char crash_switch[] = "--cc-handle-crash";
#include "crashcatcher.hpp"
static const char fatal_err[] = "\n\n*** Fatal Error ***\n";
static const char pipe_err[] = "!!! Failed to create pipe\n";
@ -150,6 +150,9 @@ static void gdb_info(pid_t pid)
* So CoverityScan warning is valid only for ancient versions of stdlib.
*/
strcpy(respfile, "/tmp/gdb-respfile-XXXXXX");
#ifdef __COVERITY__
umask(0600);
#endif
if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != nullptr)
{
fprintf(f, "attach %d\n"

View file

@ -9,6 +9,8 @@
#define USE_CRASH_CATCHER 0
#endif
constexpr char crash_switch[] = "--cc-handle-crash";
#if USE_CRASH_CATCHER
extern void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath);
#else

View file

@ -136,7 +136,7 @@ namespace Crash
memset(mShm->mStartup.mLogFilePath, 0, sizeof(mShm->mStartup.mLogFilePath));
int length = crashLogPath.length();
if (length > MAX_LONG_PATH) length = MAX_LONG_PATH;
if (length >= MAX_LONG_PATH) length = MAX_LONG_PATH - 1;
strncpy(mShm->mStartup.mLogFilePath, crashLogPath.c_str(), length);
mShm->mStartup.mLogFilePath[length] = '\0';
@ -178,8 +178,6 @@ namespace Crash
sInstance->handleVectoredException(info);
_Exit(1);
return EXCEPTION_CONTINUE_SEARCH;
}
void CrashCatcher::handleVectoredException(PEXCEPTION_POINTERS info)

View file

@ -179,7 +179,12 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c
std::cerr.rdbuf (&sb);
#else
// Redirect cout and cerr to the log file
logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName));
// If we are collecting a stack trace, append to existing log file
std::ios_base::openmode mode = std::ios::out;
if(argc == 2 && strcmp(argv[1], crash_switch) == 0)
mode |= std::ios::app;
logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName), mode);
coutsb.open (Debug::Tee(logfile, oldcout));
cerrsb.open (Debug::Tee(logfile, oldcerr));

View file

@ -2,8 +2,10 @@
#include "debug.hpp"
#include "makenavmesh.hpp"
#include "settings.hpp"
#include "version.hpp"
#include <components/debug/debuglog.hpp>
#include <components/misc/thread.hpp>
#include <osg/Stats>
@ -135,6 +137,7 @@ namespace DetourNavigator
void AsyncNavMeshUpdater::process() noexcept
{
Log(Debug::Debug) << "Start process navigator jobs by thread=" << std::this_thread::get_id();
Misc::setCurrentThreadIdlePriority();
while (!mShouldStop)
{
try
@ -178,6 +181,19 @@ namespace DetourNavigator
const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile,
offMeshConnections, mSettings, navMeshCacheItem, mNavMeshTilesCache);
if (recastMesh != nullptr)
{
Version navMeshVersion;
{
const auto locked = navMeshCacheItem->lockConst();
navMeshVersion.mGeneration = locked->getGeneration();
navMeshVersion.mRevision = locked->getNavMeshRevision();
}
mRecastMeshManager.get().reportNavMeshChange(job.mChangedTile,
Version {recastMesh->getGeneration(), recastMesh->getRevision()},
navMeshVersion);
}
const auto finish = std::chrono::steady_clock::now();
writeDebugFiles(job, recastMesh.get());
@ -246,7 +262,7 @@ namespace DetourNavigator
if (jobs.top().mProcessTime > now)
return {};
Job job = std::move(jobs.top());
Job job = jobs.top();
jobs.pop();
if (changeLastUpdate && job.mChangeType == ChangeType::update)
@ -257,7 +273,7 @@ namespace DetourNavigator
if (it->second.empty())
pushed.erase(it);
return {std::move(job)};
return job;
}
void AsyncNavMeshUpdater::writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const

View file

@ -61,4 +61,9 @@ namespace DetourNavigator
{
return mImpl.isEmpty();
}
void CachedRecastMeshManager::reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion)
{
mImpl.reportNavMeshChange(recastMeshVersion, navMeshVersion);
}
}

View file

@ -2,6 +2,7 @@
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_CACHEDRECASTMESHMANAGER_H
#include "recastmeshmanager.hpp"
#include "version.hpp"
namespace DetourNavigator
{
@ -25,6 +26,8 @@ namespace DetourNavigator
bool isEmpty() const;
void reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion);
private:
RecastMeshManager mImpl;
std::shared_ptr<RecastMesh> mCached;

View file

@ -46,6 +46,9 @@ namespace DetourNavigator
ChunkyTriMesh(const std::vector<float>& verts, const std::vector<int>& tris,
const std::vector<AreaType>& flags, const std::size_t trisPerChunk);
ChunkyTriMesh(ChunkyTriMesh&&) = default;
ChunkyTriMesh& operator=(ChunkyTriMesh&&) = default;
ChunkyTriMesh(const ChunkyTriMesh&) = delete;
ChunkyTriMesh& operator=(const ChunkyTriMesh&) = delete;

View file

@ -576,17 +576,8 @@ namespace DetourNavigator
return navMeshCacheItem->lock()->removeTile(changedTile);
}
try
{
cachedNavMeshData = navMeshTilesCache.set(agentHalfExtents, changedTile, *recastMesh,
offMeshConnections, std::move(navMeshData));
}
catch (const InvalidArgument&)
{
cachedNavMeshData = navMeshTilesCache.get(agentHalfExtents, changedTile, *recastMesh,
offMeshConnections);
cached = static_cast<bool>(cachedNavMeshData);
}
cachedNavMeshData = navMeshTilesCache.set(agentHalfExtents, changedTile, *recastMesh,
offMeshConnections, std::move(navMeshData));
if (!cachedNavMeshData)
{

View file

@ -5,6 +5,7 @@
#include "tileposition.hpp"
#include "navmeshtilescache.hpp"
#include "dtstatus.hpp"
#include "navmeshtileview.hpp"
#include <components/misc/guarded.hpp>
@ -141,6 +142,12 @@ namespace DetourNavigator
template <class T>
UpdateNavMeshStatus updateTile(const TilePosition& position, T&& navMeshData)
{
const dtMeshTile* currentTile = getTile(position);
if (currentTile != nullptr
&& asNavMeshTileConstView(*currentTile) == asNavMeshTileConstView(getRawData(navMeshData)))
{
return UpdateNavMeshStatus::ignored;
}
const auto removed = removeTileImpl(position);
const auto addStatus = addTileImpl(getRawData(navMeshData), getSize(navMeshData));
if (dtStatusSucceed(addStatus))
@ -206,6 +213,12 @@ namespace DetourNavigator
int* const dataSize = nullptr;
return dtStatusSucceed(mImpl->removeTile(tileRef, data, dataSize));
}
const dtMeshTile* getTile(const TilePosition& position) const
{
const int layer = 0;
return mImpl->getTileAt(position.x(), position.y(), layer);
}
};
using GuardedNavMeshCacheItem = Misc::ScopeGuarded<NavMeshCacheItem>;

View file

@ -1,5 +1,4 @@
#include "navmeshtilescache.hpp"
#include "exceptions.hpp"
#include <osg/Stats>
@ -32,16 +31,8 @@ namespace DetourNavigator
++mGetCount;
const auto agentValues = mValues.find(agentHalfExtents);
if (agentValues == mValues.end())
return Value();
const auto tileValues = agentValues->second.find(changedTile);
if (tileValues == agentValues->second.end())
return Value();
const auto tile = tileValues->second.mMap.find(NavMeshKeyView(recastMesh, offMeshConnections));
if (tile == tileValues->second.mMap.end())
const auto tile = mValues.find(std::make_tuple(agentHalfExtents, changedTile, NavMeshKeyView(recastMesh, offMeshConnections)));
if (tile == mValues.end())
return Value();
acquireItemUnsafe(tile->second);
@ -71,76 +62,61 @@ namespace DetourNavigator
};
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(navMeshKey), itemSize);
const auto emplaced = mValues[agentHalfExtents][changedTile].mMap.emplace(iterator->mNavMeshKey, iterator);
const auto emplaced = mValues.emplace(std::make_tuple(agentHalfExtents, changedTile, NavMeshKeyRef(iterator->mNavMeshKey)), iterator);
if (!emplaced.second)
{
mFreeItems.erase(iterator);
throw InvalidArgument("Set existing cache value");
acquireItemUnsafe(emplaced.first->second);
++mGetCount;
++mHitCount;
return Value(*this, emplaced.first->second);
}
iterator->mNavMeshData = std::move(value);
++iterator->mUseCount;
mUsedNavMeshDataSize += itemSize;
mFreeNavMeshDataSize += itemSize;
acquireItemUnsafe(iterator);
mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator);
return Value(*this, iterator);
}
void NavMeshTilesCache::reportStats(unsigned int frameNumber, osg::Stats& stats) const
NavMeshTilesCache::Stats NavMeshTilesCache::getStats() const
{
std::size_t navMeshCacheSize = 0;
std::size_t usedNavMeshTiles = 0;
std::size_t cachedNavMeshTiles = 0;
std::size_t hitCount = 0;
std::size_t getCount = 0;
Stats result;
{
const std::lock_guard<std::mutex> lock(mMutex);
navMeshCacheSize = mUsedNavMeshDataSize;
usedNavMeshTiles = mBusyItems.size();
cachedNavMeshTiles = mFreeItems.size();
hitCount = mHitCount;
getCount = mGetCount;
result.mNavMeshCacheSize = mUsedNavMeshDataSize;
result.mUsedNavMeshTiles = mBusyItems.size();
result.mCachedNavMeshTiles = mFreeItems.size();
result.mHitCount = mHitCount;
result.mGetCount = mGetCount;
}
return result;
}
stats.setAttribute(frameNumber, "NavMesh CacheSize", navMeshCacheSize);
stats.setAttribute(frameNumber, "NavMesh UsedTiles", usedNavMeshTiles);
stats.setAttribute(frameNumber, "NavMesh CachedTiles", cachedNavMeshTiles);
stats.setAttribute(frameNumber, "NavMesh CacheHitRate", static_cast<double>(hitCount) / getCount * 100.0);
void NavMeshTilesCache::reportStats(unsigned int frameNumber, osg::Stats& out) const
{
const Stats stats = getStats();
out.setAttribute(frameNumber, "NavMesh CacheSize", stats.mNavMeshCacheSize);
out.setAttribute(frameNumber, "NavMesh UsedTiles", stats.mUsedNavMeshTiles);
out.setAttribute(frameNumber, "NavMesh CachedTiles", stats.mCachedNavMeshTiles);
out.setAttribute(frameNumber, "NavMesh CacheHitRate", static_cast<double>(stats.mHitCount) / stats.mGetCount * 100.0);
}
void NavMeshTilesCache::removeLeastRecentlyUsed()
{
const auto& item = mFreeItems.back();
const auto agentValues = mValues.find(item.mAgentHalfExtents);
if (agentValues == mValues.end())
return;
const auto tileValues = agentValues->second.find(item.mChangedTile);
if (tileValues == agentValues->second.end())
return;
const auto value = tileValues->second.mMap.find(item.mNavMeshKey);
if (value == tileValues->second.mMap.end())
const auto value = mValues.find(std::make_tuple(item.mAgentHalfExtents, item.mChangedTile, NavMeshKeyRef(item.mNavMeshKey)));
if (value == mValues.end())
return;
mUsedNavMeshDataSize -= item.mSize;
mFreeNavMeshDataSize -= item.mSize;
tileValues->second.mMap.erase(value);
mValues.erase(value);
mFreeItems.pop_back();
if (!tileValues->second.mMap.empty())
return;
agentValues->second.erase(tileValues);
if (!agentValues->second.empty())
return;
mValues.erase(agentValues);
}
void NavMeshTilesCache::acquireItemUnsafe(ItemIterator iterator)

View file

@ -110,6 +110,14 @@ namespace DetourNavigator
return lhs < rhs.mRef.get();
}
template <class L, class R>
inline bool operator <(const std::tuple<osg::Vec3f, TilePosition, L>& lhs, const std::tuple<osg::Vec3f, TilePosition, R>& rhs)
{
const auto left = std::tie(std::get<0>(lhs), std::get<1>(lhs));
const auto right = std::tie(std::get<0>(rhs), std::get<1>(rhs));
return std::tie(left, std::get<2>(lhs)) < std::tie(right, std::get<2>(rhs));
}
class NavMeshTilesCache
{
public:
@ -188,6 +196,15 @@ namespace DetourNavigator
ItemIterator mIterator;
};
struct Stats
{
std::size_t mNavMeshCacheSize;
std::size_t mUsedNavMeshTiles;
std::size_t mCachedNavMeshTiles;
std::size_t mHitCount;
std::size_t mGetCount;
};
NavMeshTilesCache(const std::size_t maxNavMeshDataSize);
Value get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,
@ -197,14 +214,11 @@ namespace DetourNavigator
const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections,
NavMeshData&& value);
Stats getStats() const;
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
private:
struct TileMap
{
std::map<NavMeshKeyRef, ItemIterator, std::less<>> mMap;
};
mutable std::mutex mMutex;
std::size_t mMaxNavMeshDataSize;
std::size_t mUsedNavMeshDataSize;
@ -213,7 +227,7 @@ namespace DetourNavigator
std::size_t mGetCount;
std::list<Item> mBusyItems;
std::list<Item> mFreeItems;
std::map<osg::Vec3f, std::map<TilePosition, TileMap>> mValues;
std::map<std::tuple<osg::Vec3f, TilePosition, NavMeshKeyRef>, ItemIterator, std::less<>> mValues;
void removeLeastRecentlyUsed();

View file

@ -0,0 +1,183 @@
#include "navmeshtileview.hpp"
#include <DetourCommon.h>
#include <DetourNavMesh.h>
#include <algorithm>
#include <cassert>
#include <stdexcept>
#include <tuple>
namespace
{
template <typename T>
struct Ref
{
T& mRef;
explicit Ref(T& ref) : mRef(ref) {}
friend bool operator==(const Ref& lhs, const Ref& rhs)
{
return lhs.mRef == rhs.mRef;
}
};
template <typename T, std::size_t size>
struct ArrayRef
{
T (&mRef)[size];
explicit ArrayRef(T (&ref)[size]) : mRef(ref) {}
friend bool operator==(const ArrayRef& lhs, const ArrayRef& rhs)
{
return std::equal(std::begin(lhs.mRef), std::end(lhs.mRef), std::begin(rhs.mRef));
}
};
template <typename T>
struct Span
{
T* mBegin;
T* mEnd;
explicit Span(T* data, int size) : mBegin(data), mEnd(data + static_cast<std::size_t>(size)) {}
friend bool operator==(const Span& lhs, const Span& rhs)
{
// size is already equal if headers are equal
assert((lhs.mEnd - lhs.mBegin) == (rhs.mEnd - rhs.mBegin));
return std::equal(lhs.mBegin, lhs.mEnd, rhs.mBegin);
}
};
auto makeTuple(const dtMeshHeader& v)
{
return std::tuple(
v.x,
v.y,
v.layer,
v.userId,
v.polyCount,
v.vertCount,
v.maxLinkCount,
v.detailMeshCount,
v.detailVertCount,
v.detailTriCount,
v.bvNodeCount,
v.offMeshConCount,
v.offMeshBase,
v.walkableHeight,
v.walkableRadius,
v.walkableClimb,
v.detailVertCount,
ArrayRef(v.bmin),
ArrayRef(v.bmax),
v.bvQuantFactor
);
}
auto makeTuple(const dtPoly& v)
{
return std::tuple(ArrayRef(v.verts), ArrayRef(v.neis), v.flags, v.vertCount, v.areaAndtype);
}
auto makeTuple(const dtPolyDetail& v)
{
return std::tuple(v.vertBase, v.triBase, v.vertCount, v.triCount);
}
auto makeTuple(const dtBVNode& v)
{
return std::tuple(ArrayRef(v.bmin), ArrayRef(v.bmax), v.i);
}
auto makeTuple(const dtOffMeshConnection& v)
{
return std::tuple(ArrayRef(v.pos), v.rad, v.poly, v.flags, v.side, v.userId);
}
auto makeTuple(const DetourNavigator::NavMeshTileConstView& v)
{
return std::tuple(
Ref(*v.mHeader),
Span(v.mPolys, v.mHeader->polyCount),
Span(v.mVerts, v.mHeader->vertCount),
Span(v.mDetailMeshes, v.mHeader->detailMeshCount),
Span(v.mDetailVerts, v.mHeader->detailVertCount),
Span(v.mDetailTris, v.mHeader->detailTriCount),
Span(v.mBvTree, v.mHeader->bvNodeCount),
Span(v.mOffMeshCons, v.mHeader->offMeshConCount)
);
}
}
template <class T>
inline auto operator==(const T& lhs, const T& rhs)
-> std::enable_if_t<std::is_same_v<std::void_t<decltype(makeTuple(lhs))>, void>, bool>
{
return makeTuple(lhs) == makeTuple(rhs);
}
namespace DetourNavigator
{
NavMeshTileConstView asNavMeshTileConstView(const unsigned char* data)
{
const dtMeshHeader* header = reinterpret_cast<const dtMeshHeader*>(data);
if (header->magic != DT_NAVMESH_MAGIC)
throw std::logic_error("Invalid navmesh magic");
if (header->version != DT_NAVMESH_VERSION)
throw std::logic_error("Invalid navmesh version");
// Similar code to https://github.com/recastnavigation/recastnavigation/blob/c5cbd53024c8a9d8d097a4371215e3342d2fdc87/Detour/Source/DetourNavMesh.cpp#L978-L996
const int headerSize = dtAlign4(sizeof(dtMeshHeader));
const int vertsSize = dtAlign4(sizeof(float) * 3 * header->vertCount);
const int polysSize = dtAlign4(sizeof(dtPoly) * header->polyCount);
const int linksSize = dtAlign4(sizeof(dtLink) * (header->maxLinkCount));
const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail) * header->detailMeshCount);
const int detailVertsSize = dtAlign4(sizeof(float) * 3 * header->detailVertCount);
const int detailTrisSize = dtAlign4(sizeof(unsigned char) * 4 * header->detailTriCount);
const int bvtreeSize = dtAlign4(sizeof(dtBVNode) * header->bvNodeCount);
const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection) * header->offMeshConCount);
const unsigned char* ptr = data + headerSize;
NavMeshTileConstView view;
view.mHeader = header;
view.mVerts = dtGetThenAdvanceBufferPointer<const float>(ptr, vertsSize);
view.mPolys = dtGetThenAdvanceBufferPointer<const dtPoly>(ptr, polysSize);
ptr += linksSize;
view.mDetailMeshes = dtGetThenAdvanceBufferPointer<const dtPolyDetail>(ptr, detailMeshesSize);
view.mDetailVerts = dtGetThenAdvanceBufferPointer<const float>(ptr, detailVertsSize);
view.mDetailTris = dtGetThenAdvanceBufferPointer<const unsigned char>(ptr, detailTrisSize);
view.mBvTree = dtGetThenAdvanceBufferPointer<const dtBVNode>(ptr, bvtreeSize);
view.mOffMeshCons = dtGetThenAdvanceBufferPointer<const dtOffMeshConnection>(ptr, offMeshLinksSize);
return view;
}
NavMeshTileConstView asNavMeshTileConstView(const dtMeshTile& tile)
{
NavMeshTileConstView view;
view.mHeader = tile.header;
view.mPolys = tile.polys;
view.mVerts = tile.verts;
view.mDetailMeshes = tile.detailMeshes;
view.mDetailVerts = tile.detailVerts;
view.mDetailTris = tile.detailTris;
view.mBvTree = tile.bvTree;
view.mOffMeshCons = tile.offMeshCons;
return view;
}
bool operator==(const NavMeshTileConstView& lhs, const NavMeshTileConstView& rhs)
{
return makeTuple(lhs) == makeTuple(rhs);
}
}

Some files were not shown because too many files have changed in this diff Show more