mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-12-08 22:34:31 +00:00
Add OpenMW commits up to 2 May 2021
# Conflicts: # components/CMakeLists.txt
This commit is contained in:
commit
a3f304107b
172 changed files with 3800 additions and 840 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,71 +645,29 @@ 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}
|
||||
4866 # compiler may not enforce left-to-right evaluation order for call
|
||||
)
|
||||
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)
|
||||
|
|
|
|||
34
apps/benchmarks/CMakeLists.txt
Normal file
34
apps/benchmarks/CMakeLists.txt
Normal 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)
|
||||
215
apps/benchmarks/detournavigator/navmeshtilescache.cpp
Normal file
215
apps/benchmarks/detournavigator/navmeshtilescache.cpp
Normal 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();
|
||||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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?");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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_)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
54
apps/opencs/model/prefs/stringsetting.cpp
Normal file
54
apps/opencs/model/prefs/stringsetting.cpp
Normal 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);
|
||||
}
|
||||
36
apps/opencs/model/prefs/stringsetting.hpp
Normal file
36
apps/opencs/model/prefs/stringsetting.hpp
Normal 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
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -237,6 +237,8 @@ namespace MWBase
|
|||
|
||||
virtual bool getWorldMouseOver() = 0;
|
||||
|
||||
virtual float getScalingFactor() = 0;
|
||||
|
||||
virtual bool toggleFogOfWar() = 0;
|
||||
|
||||
virtual bool toggleFullHelp() = 0;
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ namespace MWClass
|
|||
|
||||
CreatureCustomData() = default;
|
||||
CreatureCustomData(const CreatureCustomData& other);
|
||||
CreatureCustomData(CreatureCustomData&& other) noexcept = default;
|
||||
CreatureCustomData(CreatureCustomData&& other) = default;
|
||||
|
||||
CreatureCustomData& asCreatureCustomData() override
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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('@');
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@ namespace MWGui
|
|||
std::unique_ptr<MWRender::InventoryPreview> mPreview;
|
||||
|
||||
bool mTrading;
|
||||
float mScaleFactor;
|
||||
float mUpdateTimer;
|
||||
|
||||
void toggleMaximized();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ namespace MWInput
|
|||
|
||||
bool mJoystickEnabled;
|
||||
float mGamepadCursorSpeed;
|
||||
float mInvUiScalingFactor;
|
||||
float mSneakToggleShortcutTimer;
|
||||
float mGamepadZoom;
|
||||
bool mGamepadGuiCursorEnabled;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ namespace MWInput
|
|||
|
||||
BindingsManager* mBindingsManager;
|
||||
SDLUtil::InputWrapper* mInputWrapper;
|
||||
float mInvUiScalingFactor;
|
||||
|
||||
float mGuiCursorX;
|
||||
float mGuiCursorY;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ namespace MWMechanics
|
|||
|
||||
void removeListener(Spells* spells);
|
||||
|
||||
void updateListener(Spells* before, Spells* after);
|
||||
|
||||
const std::vector<std::string> getSpells() const;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ namespace MWMechanics
|
|||
|
||||
Spells(const Spells&);
|
||||
|
||||
Spells(const Spells&&) = delete;
|
||||
Spells(Spells&& spells);
|
||||
|
||||
~Spells();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -402,7 +402,7 @@ namespace MWRender
|
|||
refs[ref.mRefNum] = ref;
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
catch (std::exception&)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -296,6 +296,7 @@ namespace MWRender
|
|||
osg::ref_ptr<StateUpdater> mStateUpdater;
|
||||
|
||||
osg::Vec4f mAmbientColor;
|
||||
float mMinimumAmbientLuminance;
|
||||
float mNightEyeFactor;
|
||||
|
||||
float mNearClip;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ namespace MWWorld
|
|||
|
||||
void resolve();
|
||||
ResolutionHandle resolveTemporarily();
|
||||
void unresolve();
|
||||
|
||||
friend class ContainerStoreIteratorBase<Ptr>;
|
||||
friend class ContainerStoreIteratorBase<ConstPtr>;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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&)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
29
components/bullethelpers/aabb.hpp
Normal file
29
components/bullethelpers/aabb.hpp
Normal 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
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -61,4 +61,9 @@ namespace DetourNavigator
|
|||
{
|
||||
return mImpl.isEmpty();
|
||||
}
|
||||
|
||||
void CachedRecastMeshManager::reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion)
|
||||
{
|
||||
mImpl.reportNavMeshChange(recastMeshVersion, navMeshVersion);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
183
components/detournavigator/navmeshtileview.cpp
Normal file
183
components/detournavigator/navmeshtileview.cpp
Normal 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
Loading…
Reference in a new issue