Merge branch 'master' into 'OpenCS-preserve-blocked'

# Conflicts:
#   CHANGELOG.md
pull/3130/head
psi29a 3 years ago
commit 18f5762a76

@ -0,0 +1,50 @@
name: CMake
on:
pull_request:
branches: [ master ]
env:
BUILD_TYPE: RelWithDebInfo
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Add OpenMW PPA Dependancies
run: sudo add-apt-repository ppa:openmw/openmw; sudo apt-get update
- name: Install Building Dependancies
run: sudo CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
- name: Prime ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${{ matrix.os }}-${{ env.BUILD_TYPE }}
max-size: 1000M
- name: Configure
run: cmake -S . -B . -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build . --config ${{env.BUILD_TYPE}} --parallel 3
- name: Install
shell: bash
run: cmake --install .
- name: Create Artifact
shell: bash
working-directory: install
run: |
ls -laR
7z a ../build_artifact.7z .
- name: Upload Artifact
uses: actions/upload-artifact@v1
with:
path: ./build_artifact.7z
name: build_artifact.7z

@ -223,6 +223,18 @@ variables: &tests-targets
- choco install ninja -y
- choco install python -y
- refreshenv
- |
function Make-SafeFileName {
param(
[Parameter(Mandatory=$true)]
[String]
$FileName
)
[IO.Path]::GetInvalidFileNameChars() | ForEach-Object {
$FileName = $FileName.Replace($_, '_')
}
return $FileName
}
stage: build
script:
- $time = (Get-Date -Format "HH:mm:ss")
@ -237,10 +249,10 @@ variables: &tests-targets
- Get-ChildItem -Recurse *.ilk | Remove-Item
- |
if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb' CI-ID.txt
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
Get-ChildItem -Recurse *.pdb | Remove-Item
}
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip '*'
- 7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip"))" '*'
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
after_script:
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
@ -326,6 +338,18 @@ Windows_Ninja_Tests_RelWithDebInfo:
- choco install vswhere -y
- choco install python -y
- refreshenv
- |
function Make-SafeFileName {
param(
[Parameter(Mandatory=$true)]
[String]
$FileName
)
[IO.Path]::GetInvalidFileNameChars() | ForEach-Object {
$FileName = $FileName.Replace($_, '_')
}
return $FileName
}
stage: build
script:
- $time = (Get-Date -Format "HH:mm:ss")
@ -339,10 +363,10 @@ Windows_Ninja_Tests_RelWithDebInfo:
- Get-ChildItem -Recurse *.ilk | Remove-Item
- |
if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb' CI-ID.txt
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
Get-ChildItem -Recurse *.pdb | Remove-Item
}
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip '*'
- 7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip"))" '*'
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
after_script:
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log

@ -44,6 +44,7 @@ Programmers
Austin Salgat (Salgat)
Ben Shealy (bentsherman)
Berulacks
Bo Svensson
Britt Mathis (galdor557)
Capostrophic
Carl Maxwell
@ -99,6 +100,7 @@ Programmers
James Stephens (james-h-stephens)
Jan-Peter Nilsson (peppe)
Jan Borsodi (am0s)
JanuarySnow
Jason Hooks (jhooks)
jeaye
jefetienne

@ -7,6 +7,7 @@
Bug #3846: Strings starting with "-" fail to compile if not enclosed in quotes
Bug #3905: Great House Dagoth issues
Bug #4203: Resurrecting an actor should close the loot GUI
Bug #4602: Robert's Bodies: crash inside createInstance()
Bug #4700: Editor: Incorrect command implementation
Bug #4744: Invisible particles must still be processed
Bug #4752: UpdateCellCommand doesn't undo properly
@ -36,6 +37,7 @@
Bug #6174: Spellmaking and Enchanting sliders differences from vanilla
Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
Bug #6197: Infinite Casting Loop
Bug #6273: Respawning NPCs rotation is inconsistent
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record
Feature #2780: A way to see current OpenMW version in the console
Feature #3616: Allow Zoom levels on the World Map
@ -47,7 +49,11 @@
Feature #6032: Reverse-z depth buffer
Feature #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly
Feature #6199: Support FBO Rendering
Editor: Preserve the "blocked" record flag for referenceable objects.
Feature #6251: OpenMW-CS: Set instance movement based on camera zoom
Feature #6288: Preserve the "blocked" record flag for referenceable objects.
Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings
Task #6264: Remove the old classes in animation.cpp
0.47.0
------

@ -14,6 +14,12 @@ if [[ "${BUILD_TESTS_ONLY}" ]]; then
BUILD_BENCHMARKS=ON
fi
CXX_FLAGS='-Werror -Wno-error=deprecated-declarations -Wno-error=nonnull -Wno-error=deprecated-copy'
if [[ "${CXX}" == 'clang++' ]]; then
CXX_FLAGS="${CXX_FLAGS} -Wno-error=unused-lambda-capture -Wno-error=gnu-zero-variadic-macro-arguments"
fi
declare -a CMAKE_CONF_OPTS=(
-DCMAKE_C_COMPILER="${CC:-/usr/bin/cc}"
-DCMAKE_CXX_COMPILER="${CXX:-/usr/bin/c++}"
@ -24,6 +30,8 @@ declare -a CMAKE_CONF_OPTS=(
-DBUILD_SHARED_LIBS=OFF
-DUSE_SYSTEM_TINYXML=ON
-DCMAKE_INSTALL_PREFIX=install
-DCMAKE_C_FLAGS='-Werror'
-DCMAKE_CXX_FLAGS="${CXX_FLAGS}"
)
if [[ $CI_OPENMW_USE_STATIC_DEPS ]]; then

@ -13,6 +13,11 @@ if(POLICY CMP0083)
cmake_policy(SET CMP0083 NEW)
endif()
# to link with freetype library
if(POLICY CMP0079)
cmake_policy(SET CMP0079 NEW)
endif()
option(OPENMW_GL4ES_MANUAL_INIT "Manually initialize gl4es. This is more reliable on platforms without a windowing system. Requires gl4es to be configured with -DNOEGL=ON -DNO_LOADER=ON -DNO_INIT_CONSTRUCTOR=ON." OFF)
if(OPENMW_GL4ES_MANUAL_INIT)
add_definitions(-DOPENMW_GL4ES_MANUAL_INIT)
@ -202,8 +207,6 @@ if (USE_QT)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
# Instruct CMake to run moc automatically when needed.
#set(CMAKE_AUTOMOC ON)
endif()
set(USED_OSG_COMPONENTS
@ -512,7 +515,7 @@ endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -pedantic -Wno-long-long")
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wundef -Wno-unused-parameter -pedantic -Wno-long-long ${CMAKE_CXX_FLAGS}")
add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS=ON )
if (APPLE)

@ -36,23 +36,6 @@ set(LAUNCHER_HEADER
)
# Headers that must be pre-processed
set(LAUNCHER_HEADER_MOC
datafilespage.hpp
graphicspage.hpp
maindialog.hpp
playpage.hpp
textslotmsgbox.hpp
settingspage.hpp
advancedpage.hpp
utils/cellnameloader.hpp
utils/textinputdialog.hpp
utils/profilescombobox.hpp
utils/lineedit.hpp
utils/openalutil.hpp
)
set(LAUNCHER_UI
${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui
${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui
@ -74,7 +57,6 @@ if(WIN32)
endif(WIN32)
QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
QT5_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
QT5_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
@ -109,4 +91,7 @@ if (BUILD_WITH_CODE_COVERAGE)
target_link_libraries(openmw-launcher gcov)
endif()
if(USE_QT)
set_property(TARGET openmw-launcher PROPERTY AUTOMOC ON)
endif(USE_QT)

@ -52,11 +52,8 @@ void readVFS(VFS::Archive* anArchive,std::string archivePath = "")
myManager.addArchive(anArchive);
myManager.buildIndex();
std::map<std::string, VFS::File*> files=myManager.getIndex();
for(auto it=files.begin(); it!=files.end(); ++it)
for(const auto& name : myManager.getRecursiveDirectoryIterator(""))
{
std::string name = it->first;
try{
if(isNIF(name))
{

@ -8,11 +8,11 @@ opencs_units (model/doc
document operation saving documentmanager loader runner operationholder
)
opencs_units_noqt (model/doc
opencs_units (model/doc
stage savingstate savingstages blacklist messages
)
opencs_hdrs_noqt (model/doc
opencs_hdrs (model/doc
state
)
@ -23,14 +23,14 @@ opencs_units (model/world
)
opencs_units_noqt (model/world
opencs_units (model/world
universalid record commands columnbase columnimp scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
idcompletionmanager metadata defaultgmsts infoselectwrapper commandmacro
)
opencs_hdrs_noqt (model/world
opencs_hdrs (model/world
columnimp idcollection collection info subcellcollection
)
@ -39,14 +39,14 @@ opencs_units (model/tools
tools reportmodel mergeoperation
)
opencs_units_noqt (model/tools
opencs_units (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
mergestages gmstcheck topicinfocheck journalcheck enchantmentcheck
)
opencs_hdrs_noqt (model/tools
opencs_hdrs (model/tools
mergestate
)
@ -57,11 +57,11 @@ opencs_units (view/doc
)
opencs_units_noqt (view/doc
opencs_units (view/doc
subviewfactory
)
opencs_hdrs_noqt (view/doc
opencs_hdrs (view/doc
subviewfactoryimp
)
@ -74,7 +74,7 @@ opencs_units (view/world
bodypartcreator landtexturecreator landcreator
)
opencs_units_noqt (view/world
opencs_units (view/world
subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator dialoguecreator idcompletiondelegate
colordelegate dragdroputils
@ -92,12 +92,12 @@ opencs_units (view/render
cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands
)
opencs_units_noqt (view/render
opencs_units (view/render
lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase
cellarrow cellmarker cellborder pathgrid
)
opencs_hdrs_noqt (view/render
opencs_hdrs (view/render
mask
)
@ -106,7 +106,7 @@ opencs_units (view/tools
reportsubview reporttable searchsubview searchbox merge
)
opencs_units_noqt (view/tools
opencs_units (view/tools
subviews
)
@ -119,11 +119,11 @@ opencs_units (model/prefs
shortcuteventhandler shortcutmanager shortcutsetting modifiersetting stringsetting
)
opencs_units_noqt (model/prefs
opencs_units (model/prefs
category
)
opencs_units_noqt (model/filter
opencs_units (model/filter
node unarynode narynode leafnode booleannode parser andnode ornode notnode textnode valuenode
)
@ -150,7 +150,6 @@ if(WIN32)
endif(WIN32)
qt5_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
qt5_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})
qt5_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
# for compiled .ui files
@ -232,31 +231,6 @@ target_link_libraries(openmw-cs
components_qt
)
if(OSG_STATIC)
unset(_osg_plugins_static_files)
add_library(openmw_cs_osg_plugins INTERFACE)
foreach(_plugin ${USED_OSG_PLUGINS})
string(TOUPPER ${_plugin} _plugin_uc)
if(OPENMW_USE_SYSTEM_OSG)
list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY})
else()
list(APPEND _osg_plugins_static_files $<TARGET_FILE:${${_plugin_uc}_LIBRARY}>)
target_link_libraries(openmw_cs_osg_plugins INTERFACE $<TARGET_PROPERTY:${${_plugin_uc}_LIBRARY},LINK_LIBRARIES>)
add_dependencies(openmw_cs_osg_plugins ${${_plugin_uc}_LIBRARY})
endif()
endforeach()
# We use --whole-archive because OSG plugins use registration.
get_whole_archive_options(_opts ${_osg_plugins_static_files})
target_link_options(openmw_cs_osg_plugins INTERFACE ${_opts})
target_link_libraries(openmw-cs openmw_cs_osg_plugins)
if(OPENMW_USE_SYSTEM_OSG)
# OSG plugin pkgconfig files are missing these dependencies.
# https://github.com/openscenegraph/OpenSceneGraph/issues/1052
target_link_libraries(openmw freetype jpeg png)
endif()
endif(OSG_STATIC)
target_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL)
if (WIN32)
@ -284,3 +258,7 @@ endif (MSVC)
if(APPLE)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT Bundle)
endif()
if(USE_QT)
set_property(TARGET openmw-cs PROPERTY AUTOMOC ON)
endif(USE_QT)

@ -9,6 +9,9 @@
#include "data.hpp"
#include <string>
#include <string_view>
namespace CSMWorld
{
const std::string& ActorAdapter::RaceData::getId() const
@ -121,7 +124,7 @@ namespace CSMWorld
return SceneUtil::getActorSkeleton(firstPerson, mFemale, beast, werewolf);
}
const std::string ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const
std::string_view ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const
{
auto it = mParts.find(index);
if (it == mParts.end())
@ -131,7 +134,7 @@ namespace CSMWorld
if (mFemale)
{
// Note: we should use male parts for females as fallback
const std::string femalePart = mRaceData->getFemalePart(index);
const std::string& femalePart = mRaceData->getFemalePart(index);
if (!femalePart.empty())
return femalePart;
}
@ -139,11 +142,10 @@ namespace CSMWorld
return mRaceData->getMalePart(index);
}
return "";
return {};
}
const std::string& partName = it->second.first;
return partName;
return it->second.first;
}
bool ActorAdapter::ActorData::hasDependency(const std::string& id) const

@ -4,6 +4,8 @@
#include <array>
#include <map>
#include <unordered_set>
#include <string>
#include <string_view>
#include <QObject>
#include <QModelIndex>
@ -93,7 +95,7 @@ namespace CSMWorld
/// Returns the skeleton the actor should use for attaching parts to
std::string getSkeleton() const;
/// Retrieves the associated actor part
const std::string getPart(ESM::PartReferenceType index) const;
std::string_view getPart(ESM::PartReferenceType index) const;
/// Checks if the actor has a data dependency
bool hasDependency(const std::string& id) const;

@ -9,6 +9,7 @@
#include <string>
#include <functional>
#include <memory>
#include <string_view>
#include <QVariant>
@ -153,7 +154,7 @@ namespace CSMWorld
///< Change the state of a record from base to modified, if it is not already.
/// \return True if the record was changed.
int searchId (const std::string& id) const override;
int searchId(std::string_view id) const override;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
@ -476,7 +477,7 @@ namespace CSMWorld
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::searchId (const std::string& id) const
int Collection<ESXRecordT, IdAccessorT>::searchId(std::string_view id) const
{
std::string id2 = Misc::StringUtils::lowerCase(id);

@ -4,6 +4,7 @@
#include <string>
#include <vector>
#include <memory>
#include <string_view>
#include "universalid.hpp"
#include "columns.hpp"
@ -61,7 +62,7 @@ namespace CSMWorld
UniversalId::Type type = UniversalId::Type_None) = 0;
///< \param type Will be ignored, unless the collection supports multiple record types
virtual int searchId (const std::string& id) const = 0;
virtual int searchId(std::string_view id) const = 0;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)

@ -392,7 +392,7 @@ int CSMWorld::Columns::getId (const std::string& name)
std::string name2 = Misc::StringUtils::lowerCase (name);
for (int i=0; sNames[i].mName; ++i)
if (Misc::StringUtils::ciEqual(sNames[i].mName, name2))
if (Misc::StringUtils::ciEqual(std::string_view(sNames[i].mName), name2))
return sNames[i].mId;
return -1;

@ -97,7 +97,7 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base)
}
}
int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::string& topic) const
int CSMWorld::InfoCollection::getInfoIndex(std::string_view id, std::string_view topic) const
{
// find the topic first
std::unordered_map<std::string, std::vector<std::pair<std::string, int> > >::const_iterator iter
@ -345,12 +345,12 @@ void CSMWorld::InfoCollection::appendBlankRecord (const std::string& id, Univer
insertRecord(std::move(record2), getInsertIndex(id, type, nullptr), type); // call InfoCollection::insertRecord()
}
int CSMWorld::InfoCollection::searchId (const std::string& id) const
int CSMWorld::InfoCollection::searchId(std::string_view id) const
{
std::string::size_type separator = id.find_last_of('#');
if (separator == std::string::npos)
throw std::runtime_error("invalid info ID: " + id);
throw std::runtime_error("invalid info ID: " + std::string(id));
return getInfoIndex(id.substr(separator+1), id.substr(0, separator));
}

@ -2,6 +2,7 @@
#define CSM_WOLRD_INFOCOLLECTION_H
#include <unordered_map>
#include <string_view>
#include "collection.hpp"
#include "info.hpp"
@ -43,7 +44,7 @@ namespace CSMWorld
void load (const Info& record, bool base);
int getInfoIndex (const std::string& id, const std::string& topic) const;
int getInfoIndex(std::string_view id, std::string_view topic) const;
///< Return index for record \a id or -1 (if not present; deleted records are considered)
///
/// \param id info ID without topic prefix
@ -79,7 +80,7 @@ namespace CSMWorld
void appendBlankRecord (const std::string& id,
UniversalId::Type type = UniversalId::Type_None) override;
int searchId (const std::string& id) const override;
int searchId(std::string_view id) const override;
void appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None) override;

@ -7,6 +7,8 @@
#include "universalid.hpp"
#include "record.hpp"
#include <string_view>
namespace CSMWorld
{
template<>
@ -261,7 +263,7 @@ void CSMWorld::RefCollection::cloneRecord (const std::string& origin,
insertRecord(std::move(copy), getAppendIndex(destination, type)); // call RefCollection::insertRecord()
}
int CSMWorld::RefCollection::searchId (const std::string& id) const
int CSMWorld::RefCollection::searchId(std::string_view id) const
{
return searchId(extractIdNum(id));
}

@ -2,6 +2,7 @@
#define CSM_WOLRD_REFCOLLECTION_H
#include <map>
#include <string_view>
#include "../doc/stage.hpp"
@ -56,7 +57,7 @@ namespace CSMWorld
const std::string& destination,
const UniversalId::Type type);
virtual int searchId (const std::string& id) const;
virtual int searchId(std::string_view id) const;
virtual void appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None);

@ -2,6 +2,7 @@
#include <stdexcept>
#include <memory>
#include <string_view>
#include <components/esm/esmreader.hpp>
@ -790,7 +791,7 @@ void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, Univer
mData.appendRecord (type, id, false);
}
int CSMWorld::RefIdCollection::searchId (const std::string& id) const
int CSMWorld::RefIdCollection::searchId(std::string_view id) const
{
RefIdData::LocalIndex localIndex = mData.searchId (id);

@ -4,6 +4,7 @@
#include <vector>
#include <map>
#include <deque>
#include <string_view>
#include "columnbase.hpp"
#include "collectionbase.hpp"
@ -85,7 +86,7 @@ namespace CSMWorld
void appendBlankRecord (const std::string& id, UniversalId::Type type) override;
///< \param type Will be ignored, unless the collection supports multiple record types
int searchId (const std::string& id) const override;
int searchId(std::string_view id) const override;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)

@ -2,6 +2,7 @@
#include <cassert>
#include <memory>
#include <string_view>
CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {}
@ -74,8 +75,7 @@ int CSMWorld::RefIdData::localToGlobalIndex (const LocalIndex& index)
return globalIndex;
}
CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId (
const std::string& id) const
CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId(std::string_view id) const
{
std::string id2 = Misc::StringUtils::lowerCase (id);

@ -5,6 +5,7 @@
#include <map>
#include <memory>
#include <cassert>
#include <string_view>
#include <components/esm/loadacti.hpp>
#include <components/esm/loadalch.hpp>
@ -277,7 +278,7 @@ namespace CSMWorld
int localToGlobalIndex (const LocalIndex& index) const;
LocalIndex searchId (const std::string& id) const;
LocalIndex searchId(std::string_view id) const;
void erase (int index, int count);

@ -3,6 +3,7 @@
#include <sstream>
#include <stdexcept>
#include <algorithm>
#include <string_view>
#include <components/vfs/manager.hpp>
@ -22,10 +23,8 @@ void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char * const *
size_t baseSize = mBaseDirectory.size();
const std::map<std::string, VFS::File*>& index = vfs->getIndex();
for (std::map<std::string, VFS::File*>::const_iterator it = index.begin(); it != index.end(); ++it)
for (const auto& filepath : vfs->getRecursiveDirectoryIterator(""))
{
std::string filepath = it->first;
if (filepath.size()<baseSize+1 ||
filepath.substr (0, baseSize)!=mBaseDirectory ||
(filepath[baseSize]!='/' && filepath[baseSize]!='\\'))
@ -83,7 +82,7 @@ int CSMWorld::Resources::getIndex (const std::string& id) const
return index;
}
int CSMWorld::Resources::searchId (const std::string& id) const
int CSMWorld::Resources::searchId(std::string_view id) const
{
std::string id2 = Misc::StringUtils::lowerCase (id);

@ -4,6 +4,7 @@
#include <string>
#include <map>
#include <vector>
#include <string_view>
#include "universalid.hpp"
@ -35,7 +36,7 @@ namespace CSMWorld
int getIndex (const std::string& id) const;
int searchId (const std::string& id) const;
int searchId(std::string_view id) const;
UniversalId::Type getType() const;
};

@ -96,7 +96,7 @@ namespace CSVRender
for (int i = 0; i < ESM::PRT_Count; ++i)
{
auto type = (ESM::PartReferenceType) i;
std::string partId = mActorData->getPart(type);
const std::string_view partId = mActorData->getPart(type);
attachBodyPart(type, getBodyPartMesh(partId));
}
}
@ -115,7 +115,7 @@ namespace CSVRender
}
}
std::string Actor::getBodyPartMesh(const std::string& bodyPartId)
std::string Actor::getBodyPartMesh(std::string_view bodyPartId)
{
const auto& bodyParts = mData.getBodyParts();

@ -2,6 +2,7 @@
#define OPENCS_VIEW_RENDER_ACTOR_H
#include <string>
#include <string_view>
#include <osg/ref_ptr>
@ -54,7 +55,7 @@ namespace CSVRender
void loadBodyParts();
void attachBodyPart(ESM::PartReferenceType, const std::string& mesh);
std::string getBodyPartMesh(const std::string& bodyPartId);
std::string getBodyPartMesh(std::string_view bodyPartId);
static const std::string MeshPrefix;

@ -297,6 +297,8 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos)
return false;
}
mObjectsAtDragStart.clear();
for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin());
iter!=selection.end(); ++iter)
{
@ -305,6 +307,12 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos)
if (mSubModeId == "move")
{
objectTag->mObject->setEdited (Object::Override_Position);
float x = objectTag->mObject->getPosition().pos[0];
float y = objectTag->mObject->getPosition().pos[1];
float z = objectTag->mObject->getPosition().pos[2];
osg::Vec3f thisPoint(x, y, z);
mDragStart = getMousePlaneCoords(pos, getProjectionSpaceCoords(thisPoint));
mObjectsAtDragStart.emplace_back(thisPoint);
mDragMode = DragMode_Move;
}
else if (mSubModeId == "rotate")
@ -392,29 +400,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getEdited (Mask_Reference);
if (mDragMode == DragMode_Move)
{
osg::Vec3f eye, centre, up;
getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up);
if (diffY)
{
offset += up * diffY * speedFactor;
}
if (diffX)
{
offset += ((centre-eye) ^ up) * diffX * speedFactor;
}
if (mDragAxis!=-1)
{
for (int i=0; i<3; ++i)
{
if (i!=mDragAxis)
offset[i] = 0;
}
}
}
if (mDragMode == DragMode_Move) {}
else if (mDragMode == DragMode_Rotate)
{
osg::Vec3f eye, centre, up;
@ -514,17 +500,32 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
return;
}
int i = 0;
// Apply
for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin()); iter!=selection.end(); ++iter)
for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin()); iter!=selection.end(); ++iter, i++)
{
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (iter->get()))
{
if (mDragMode == DragMode_Move)
{
ESM::Position position = objectTag->mObject->getPosition();
for (int i=0; i<3; ++i)
osg::Vec3f mousePos = getMousePlaneCoords(pos, getProjectionSpaceCoords(mDragStart));
float addToX = mousePos.x() - mDragStart.x();
float addToY = mousePos.y() - mDragStart.y();
float addToZ = mousePos.z() - mDragStart.z();
position.pos[0] = mObjectsAtDragStart[i].x() + addToX;
position.pos[1] = mObjectsAtDragStart[i].y() + addToY;
position.pos[2] = mObjectsAtDragStart[i].z() + addToZ;
// XYZ-locking
if (mDragAxis != -1)
{
position.pos[i] += offset[i];
for (int j = 0; j < 3; ++j)
{
if (j != mDragAxis)
position.pos[j] = mObjectsAtDragStart[i][j];
}
}
objectTag->mObject->setPosition(position.pos);
@ -608,6 +609,7 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos)
}
}
mObjectsAtDragStart.clear();
mDragMode = DragMode_None;
}
@ -634,8 +636,10 @@ void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor)
std::vector<osg::ref_ptr<TagBase> > selection =
getWorldspaceWidget().getEdited (Mask_Reference);
int j = 0;
for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin());
iter!=selection.end(); ++iter)
iter!=selection.end(); ++iter, j++)
{
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (iter->get()))
{
@ -643,6 +647,9 @@ void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor)
for (int i=0; i<3; ++i)
position.pos[i] += offset[i];
objectTag->mObject->setPosition (position.pos);
osg::Vec3f thisPoint(position.pos[0], position.pos[1], position.pos[2]);
mDragStart = getMousePlaneCoords(getWorldspaceWidget().mapFromGlobal(QCursor::pos()), getProjectionSpaceCoords(thisPoint));
mObjectsAtDragStart[j] = thisPoint;
}
}
}

@ -45,6 +45,8 @@ namespace CSVRender
bool mLocked;
float mUnitScaleDist;
osg::ref_ptr<osg::Group> mParentNode;
osg::Vec3f mDragStart;
std::vector<osg::Vec3f> mObjectsAtDragStart;
int getSubModeFromId (const std::string& id) const;

@ -1,5 +1,5 @@
#ifndef CSV_TOOLS_REPORTTABLE_H
#define CSV_TOOLS_REPORTTABLE_H
#ifndef CSV_TOOLS_MERGE_H
#define CSV_TOOLS_MERGE_H
#include <QWidget>

@ -36,7 +36,7 @@ void CSVTools::SearchSubView::replace (bool selection)
// in a single string.
for (std::vector<int>::const_reverse_iterator iter (indices.rbegin()); iter!=indices.rend(); ++iter)
{
CSMWorld::UniversalId id = model.getUniversalId (*iter);
const CSMWorld::UniversalId& id = model.getUniversalId (*iter);
CSMWorld::UniversalId::Type type = CSMWorld::UniversalId::getParentType (id.getType());

@ -22,7 +22,8 @@ CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUnd
std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listReferenceableTypes();
mType = new QComboBox (this);
mType->setMaxVisibleItems(20);
for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
iter!=types.end(); ++iter)
{
@ -31,7 +32,9 @@ CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUnd
mType->addItem (QIcon (id2.getIcon().c_str()), id2.getTypeName().c_str(),
static_cast<int> (id2.getType()));
}
mType->model()->sort(0);
insertBeforeButtons (mType, false);
connect (mType, SIGNAL (currentIndexChanged (int)), this, SLOT (setType (int)));

@ -155,31 +155,6 @@ target_link_libraries(openmw
${LUA_LIBRARIES}
)
if(OSG_STATIC)
unset(_osg_plugins_static_files)
add_library(openmw_osg_plugins INTERFACE)
foreach(_plugin ${USED_OSG_PLUGINS})
string(TOUPPER ${_plugin} _plugin_uc)
if(OPENMW_USE_SYSTEM_OSG)
list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY})
else()
list(APPEND _osg_plugins_static_files $<TARGET_FILE:${${_plugin_uc}_LIBRARY}>)
target_link_libraries(openmw_osg_plugins INTERFACE $<TARGET_PROPERTY:${${_plugin_uc}_LIBRARY},LINK_LIBRARIES>)
add_dependencies(openmw_osg_plugins ${${_plugin_uc}_LIBRARY})
endif()
endforeach()
# We use --whole-archive because OSG plugins use registration.
get_whole_archive_options(_opts ${_osg_plugins_static_files})
target_link_options(openmw_osg_plugins INTERFACE ${_opts})
target_link_libraries(openmw openmw_osg_plugins)
if(OPENMW_USE_SYSTEM_OSG)
# OSG plugin pkgconfig files are missing these dependencies.
# https://github.com/openscenegraph/OpenSceneGraph/issues/1052
target_link_libraries(openmw freetype jpeg png)
endif()
endif(OSG_STATIC)
if (ANDROID)
target_link_libraries(openmw EGL android log z)
endif (ANDROID)

@ -521,7 +521,7 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings)
throw std::runtime_error ("No default settings file found! Make sure the file \"defaults.bin\" was properly installed.");
// load user settings if they exist
const std::string settingspath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string();
std::string settingspath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string();
if (boost::filesystem::exists(settingspath))
settings.loadUser(settingspath);

@ -846,6 +846,7 @@ namespace MWClass
// Reset to original position
MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().asVec3());
MWBase::Environment::get().getWorld()->rotateObject(ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none);
}
}
}

@ -1397,6 +1397,7 @@ namespace MWClass
// Reset to original position
MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().asVec3());
MWBase::Environment::get().getWorld()->rotateObject(ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none);
}
}
}

@ -491,7 +491,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
{
add_partial_text();
stream.consume ();
mLine = nullptr, mRun = nullptr;
mLine = nullptr;
mRun = nullptr;
continue;
}
@ -551,7 +552,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
if (left + space_width + word_width > mPageWidth)
{
mLine = nullptr, mRun = nullptr, left = 0;
mLine = nullptr;
mRun = nullptr;
left = 0;
}
else
{

@ -517,6 +517,7 @@ namespace MWGui
std::vector<ESM::Skill::SkillEnum> CreateClassDialog::getMajorSkills() const
{
std::vector<ESM::Skill::SkillEnum> v;
v.reserve(5);
for(int i = 0; i < 5; i++)
{
v.push_back(mMajorSkill[i]->getSkillId());
@ -527,6 +528,7 @@ namespace MWGui
std::vector<ESM::Skill::SkillEnum> CreateClassDialog::getMinorSkills() const
{
std::vector<ESM::Skill::SkillEnum> v;
v.reserve(5);
for(int i=0; i < 5; i++)
{
v.push_back(mMinorSkill[i]->getSkillId());

@ -704,7 +704,7 @@ namespace MWGui
if (!MWBase::Environment::get().getWindowManager()->isAllowed(GW_Inventory))
return;
// make sure the object is of a type that can be picked up
std::string type = object.getTypeName();
const std::string& type = object.getTypeName();
if ( (type != typeid(ESM::Apparatus).name())
&& (type != typeid(ESM::Armor).name())
&& (type != typeid(ESM::Book).name())

@ -13,6 +13,7 @@
#include <MyGUI_Gui.h>
#include <MyGUI_TextBox.h>
#include <components/misc/pathhelpers.hpp>
#include <components/misc/rng.hpp>
#include <components/debug/debuglog.hpp>
#include <components/myguiplatform/myguitexture.hpp>
@ -66,35 +67,15 @@ namespace MWGui
void LoadingScreen::findSplashScreens()
{
const std::map<std::string, VFS::File*>& index = mResourceSystem->getVFS()->getIndex();
std::string pattern = "Splash/";
mResourceSystem->getVFS()->normalizeFilename(pattern);
auto isSupportedExtension = [](const std::string_view& ext) {
static const std::array<std::string, 7> supported_extensions{ {"tga", "dds", "ktx", "png", "bmp", "jpeg", "jpg"} };
return !ext.empty() && std::find(supported_extensions.begin(), supported_extensions.end(), ext) != supported_extensions.end();
};
/* priority given to the left */
const std::array<std::string, 7> supported_extensions {{".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}};
auto found = index.lower_bound(pattern);
while (found != index.end())
for (const auto& name : mResourceSystem->getVFS()->getRecursiveDirectoryIterator("Splash/"))
{
const std::string& name = found->first;
if (name.size() >= pattern.size() && name.substr(0, pattern.size()) == pattern)
{
size_t pos = name.find_last_of('.');
if (pos != std::string::npos)
{
for(auto const& extension: supported_extensions)
{
if (name.compare(pos, name.size() - pos, extension) == 0)
{
mSplashScreens.push_back(found->first);
break; /* based on priority */
}
}
}
}
else
break;
++found;
if (isSupportedExtension(Misc::getFileExtension(name)))
mSplashScreens.push_back(name);
}
if (mSplashScreens.empty())
Log(Debug::Warning) << "Warning: no splash screens found!";

@ -988,7 +988,8 @@ namespace MWGui
if (mInterior)
{
auto pos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition();
x = pos.x(), y = pos.y();
x = pos.x();
y = pos.y();
}
setGlobalMapPlayerPosition(x, y);
@ -1160,7 +1161,8 @@ namespace MWGui
void MapWindow::worldPosToGlobalMapImageSpace(float x, float y, float& imageX, float& imageY) const
{
mGlobalMapRender->worldPosToImageSpace(x, y, imageX, imageY);
imageX *= mGlobalMapZoom, imageY *= mGlobalMapZoom;
imageX *= mGlobalMapZoom;
imageY *= mGlobalMapZoom;
}
void MapWindow::updateCustomMarkers()

@ -376,7 +376,9 @@ namespace MWGui
{
for (const std::string& keyword : keywords)
{
if(Misc::StringUtils::ciEqual(MyGUI::LanguageManager::getInstance().replaceTags("#{" + keyword + "}"), button->getCaption()))
if (Misc::StringUtils::ciEqual(
MyGUI::LanguageManager::getInstance().replaceTags("#{" + keyword + "}").asUTF8(),
button->getCaption().asUTF8()))
{
return button;
}

@ -71,7 +71,7 @@ namespace MWLua
else
{
const std::string& recordId = std::get<std::string>(item);
if (old_it != store.end() && *old_it->getCellRef().getRefIdPtr() == recordId)
if (old_it != store.end() && old_it->getCellRef().getRefIdRef() == recordId)
return true; // already equipped
itemPtr = store.search(recordId);
if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0)

@ -284,7 +284,7 @@ namespace MWLua
std::shared_ptr<LocalScripts> scripts;
// When loading a game, it can be called before LuaManager::setPlayer,
// so we can't just check ptr == mPlayer here.
if (*ptr.getCellRef().getRefIdPtr() == "player")
if (ptr.getCellRef().getRefIdRef() == "player")
{
scripts = std::make_shared<PlayerScripts>(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry()));
scripts->addPackage("openmw.ui", mUserInterfacePackage);

@ -51,13 +51,13 @@ namespace MWLua
bool isMarker(const MWWorld::Ptr& ptr)
{
std::string_view id = *ptr.getCellRef().getRefIdPtr();
std::string_view id = ptr.getCellRef().getRefIdRef();
return id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker";
}
std::string_view getMWClassName(const MWWorld::Ptr& ptr)
{
if (*ptr.getCellRef().getRefIdPtr() == "player")
if (ptr.getCellRef().getRefIdRef() == "player")
return "Player";
if (isMarker(ptr))
return "Marker";
@ -71,7 +71,7 @@ namespace MWLua
res.append(" (");
res.append(getMWClassName(ptr));
res.append(", ");
res.append(*ptr.getCellRef().getRefIdPtr());
res.append(ptr.getCellRef().getRefIdRef());
res.append(")");
return res;
}

@ -1653,7 +1653,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
MWRender::Animation::BlendMask_All, false,
weapSpeed, startKey, stopKey,
0.0f, 0);
if(mAnimation->isPlaying(mCurrentWeapon))
if(mAnimation->getCurrentTime(mCurrentWeapon) != -1.f)
mUpperBodyState = UpperCharState_StartToMinAttack;
}
}

@ -206,9 +206,6 @@ namespace MWMechanics
endPointInLocalCoords,
startNode);
if (!endNode.second)
return;
// if it's shorter for actor to travel from start to end, than to travel from either
// start or end to nearest pathgrid point, just travel from start to end.
float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2();
@ -279,7 +276,8 @@ namespace MWMechanics
// unreachable pathgrid point.
//
// The AI routines will have to deal with such situations.
*out++ = endPoint;
if (endNode.second)
*out++ = endPoint;
}
float PathFinder::getZAngleToNext(float x, float y) const

@ -181,7 +181,7 @@ namespace MWPhysics
void PhysicsSystem::markAsNonSolid(const MWWorld::ConstPtr &ptr)
{
ObjectMap::iterator found = mObjects.find(ptr);
ObjectMap::iterator found = mObjects.find(ptr.mRef);
if (found == mObjects.end())
return;
@ -198,7 +198,7 @@ namespace MWPhysics
if (obj.isEmpty())
return true; // assume standing on terrain (which is a non-object, so not collision tracked)
ObjectMap::const_iterator foundObj = mObjects.find(obj);
ObjectMap::const_iterator foundObj = mObjects.find(obj.mRef);
if (foundObj == mObjects.end())
return false;
@ -374,8 +374,8 @@ namespace MWPhysics
bool PhysicsSystem::getLineOfSight(const MWWorld::ConstPtr &actor1, const MWWorld::ConstPtr &actor2) const
{
const auto it1 = mActors.find(actor1);
const auto it2 = mActors.find(actor2);
const auto it1 = mActors.find(actor1.mRef);
const auto it2 = mActors.find(actor2.mRef);
if (it1 == mActors.end() || it2 == mActors.end())
return false;
@ -441,7 +441,7 @@ namespace MWPhysics
{
btCollisionObject* me = nullptr;
auto found = mObjects.find(ptr);
auto found = mObjects.find(ptr.mRef);
if (found != mObjects.end())
me = found->second->getCollisionObject();
else
@ -464,7 +464,7 @@ namespace MWPhysics
osg::Vec3f PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight)
{
ActorMap::iterator found = mActors.find(ptr);
ActorMap::iterator found = mActors.find(ptr.mRef);
if (found == mActors.end())
return ptr.getRefData().getPosition().asVec3();
return MovementSolver::traceDown(ptr, position, found->second.get(), mCollisionWorld.get(), maxHeight);
@ -504,7 +504,7 @@ namespace MWPhysics
assert(!getObject(ptr));
auto obj = std::make_shared<Object>(ptr, shapeInstance, rotation, collisionType, mTaskScheduler.get());
mObjects.emplace(ptr, obj);
mObjects.emplace(ptr.mRef, obj);
if (obj->isAnimated())
mAnimatedObjects.insert(obj.get());
@ -512,8 +512,7 @@ namespace MWPhysics
void PhysicsSystem::remove(const MWWorld::Ptr &ptr)
{
ObjectMap::iterator found = mObjects.find(ptr);
if (found != mObjects.end())
if (auto found = mObjects.find(ptr.mRef); found != mObjects.end())
{
if (mUnrefQueue.get())
mUnrefQueue->push(found->second->getShapeInstance());
@ -522,11 +521,9 @@ namespace MWPhysics
mObjects.erase(found);
}
ActorMap::iterator foundActor = mActors.find(ptr);
if (foundActor != mActors.end())
else if (auto found = mActors.find(ptr.mRef); found != mActors.end())
{
mActors.erase(foundActor);
mActors.erase(found);
}
}
@ -539,22 +536,10 @@ namespace MWPhysics
void PhysicsSystem::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated)
{
ObjectMap::iterator found = mObjects.find(old);
if (found != mObjects.end())
{
auto obj = found->second;
obj->updatePtr(updated);
mObjects.erase(found);
mObjects.emplace(updated, std::move(obj));
}
auto actorNode = mActors.extract(old);
if (!actorNode.empty())
{
actorNode.key() = updated;
actorNode.mapped()->updatePtr(updated);
mActors.insert(std::move(actorNode));
}
if (auto found = mObjects.find(old.mRef); found != mObjects.end())
found->second->updatePtr(updated);
else if (auto found = mActors.find(old.mRef); found != mActors.end())
found->second->updatePtr(updated);
for (auto& [_, actor] : mActors)
{
@ -572,7 +557,7 @@ namespace MWPhysics
Actor *PhysicsSystem::getActor(const MWWorld::Ptr &ptr)
{
ActorMap::iterator found = mActors.find(ptr);
ActorMap::iterator found = mActors.find(ptr.mRef);
if (found != mActors.end())
return found->second.get();
return nullptr;
@ -580,7 +565,7 @@ namespace MWPhysics
const Actor *PhysicsSystem::getActor(const MWWorld::ConstPtr &ptr) const
{
ActorMap::const_iterator found = mActors.find(ptr);
ActorMap::const_iterator found = mActors.find(ptr.mRef);
if (found != mActors.end())
return found->second.get();
return nullptr;
@ -588,7 +573,7 @@ namespace MWPhysics
const Object* PhysicsSystem::getObject(const MWWorld::ConstPtr &ptr) const
{
ObjectMap::const_iterator found = mObjects.find(ptr);
ObjectMap::const_iterator found = mObjects.find(ptr.mRef);
if (found != mObjects.end())
return found->second.get();
return nullptr;
@ -604,20 +589,16 @@ namespace MWPhysics
void PhysicsSystem::updateScale(const MWWorld::Ptr &ptr)
{
ObjectMap::iterator found = mObjects.find(ptr);
if (found != mObjects.end())
if (auto found = mObjects.find(ptr.mRef); found != mObjects.end())
{
float scale = ptr.getCellRef().getScale();
found->second->setScale(scale);
mTaskScheduler->updateSingleAabb(found->second);
return;
}
ActorMap::iterator foundActor = mActors.find(ptr);
if (foundActor != mActors.end())
else if (auto found = mActors.find(ptr.mRef); found != mActors.end())
{
foundActor->second->updateScale();
mTaskScheduler->updateSingleAabb(foundActor->second);
return;
found->second->updateScale();
mTaskScheduler->updateSingleAabb(found->second);
}
}
@ -650,40 +631,32 @@ namespace MWPhysics
void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr, osg::Quat rotate)
{
ObjectMap::iterator found = mObjects.find(ptr);
if (found != mObjects.end())
if (auto found = mObjects.find(ptr.mRef); found != mObjects.end())
{
found->second->setRotation(rotate);
mTaskScheduler->updateSingleAabb(found->second);
return;
}
ActorMap::iterator foundActor = mActors.find(ptr);
if (foundActor != mActors.end())
else if (auto found = mActors.find(ptr.mRef); found != mActors.end())
{
if (!foundActor->second->isRotationallyInvariant())
if (!found->second->isRotationallyInvariant())
{
foundActor->second->setRotation(rotate);
mTaskScheduler->updateSingleAabb(foundActor->second);
found->second->setRotation(rotate);
mTaskScheduler->updateSingleAabb(found->second);
}
return;
}
}
void PhysicsSystem::updatePosition(const MWWorld::Ptr &ptr)
{
ObjectMap::iterator found = mObjects.find(ptr);
if (found != mObjects.end())
if (auto found = mObjects.find(ptr.mRef); found != mObjects.end())
{
found->second->updatePosition();
mTaskScheduler->updateSingleAabb(found->second);
return;
}
ActorMap::iterator foundActor = mActors.find(ptr);
if (foundActor != mActors.end())
else if (auto found = mActors.find(ptr.mRef); found != mActors.end())
{
foundActor->second->updatePosition();
mTaskScheduler->updateSingleAabb(foundActor->second, true);
return;
found->second->updatePosition();
mTaskScheduler->updateSingleAabb(found->second, true);
}
}
@ -710,7 +683,7 @@ namespace MWPhysics
auto actor = std::make_shared<Actor>(ptr, shape, mTaskScheduler.get(), canWaterWalk);
mActors.emplace(ptr, std::move(actor));
mActors.emplace(ptr.mRef, std::move(actor));
}
int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius, bool canTraverseWater)
@ -738,7 +711,7 @@ namespace MWPhysics
bool PhysicsSystem::toggleCollisionMode()
{
ActorMap::iterator found = mActors.find(MWMechanics::getPlayer());
ActorMap::iterator found = mActors.find(MWMechanics::getPlayer().mRef);
if (found != mActors.end())
{
bool cmode = found->second->getCollisionMode();
@ -753,7 +726,7 @@ namespace MWPhysics
void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity)
{
ActorMap::iterator found = mActors.find(ptr);
ActorMap::iterator found = mActors.find(ptr.mRef);
if (found != mActors.end())
found->second->setVelocity(velocity);
}
@ -770,13 +743,13 @@ namespace MWPhysics
framedata.first.reserve(mActors.size());
framedata.second.reserve(mActors.size());
const MWBase::World *world = MWBase::Environment::get().getWorld();
for (const auto& [actor, physicActor] : mActors)
for (const auto& [ref, physicActor] : mActors)
{
auto ptr = physicActor->getPtr();
if (!actor.getClass().isMobile(ptr))
if (!ptr.getClass().isMobile(ptr))
continue;
float waterlevel = -std::numeric_limits<float>::max();
const MWWorld::CellStore *cell = actor.getCell();
const MWWorld::CellStore *cell = ptr.getCell();
if(cell->getCell()->hasWater())
waterlevel = cell->getWaterLevel();
@ -786,7 +759,7 @@ namespace MWPhysics
bool waterCollision = false;
if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())
{
if (physicActor->getCollisionMode() || !world->isUnderwater(actor.getCell(), actor.getRefData().getPosition().asVec3()))
if (physicActor->getCollisionMode() || !world->isUnderwater(ptr.getCell(), ptr.getRefData().getPosition().asVec3()))
waterCollision = true;
}
@ -813,7 +786,7 @@ namespace MWPhysics
{
if (animatedObject->animateCollisionShapes())
{
auto obj = mObjects.find(animatedObject->getPtr());
auto obj = mObjects.find(animatedObject->getPtr().mRef);
assert(obj != mObjects.end());
mTaskScheduler->updateSingleAabb(obj->second);
}
@ -840,18 +813,26 @@ namespace MWPhysics
{
auto* player = getActor(MWMechanics::getPlayer());
auto* world = MWBase::Environment::get().getWorld();
for (auto& [ptr, physicActor] : mActors)
// copy new ptr position in temporary vector. player is handled separately as its movement might change active cell.
std::vector<std::pair<MWWorld::Ptr, osg::Vec3f>> newPositions;
newPositions.reserve(mActors.size() - 1);
for (const auto& [ptr, physicActor] : mActors)
{
if (physicActor.get() == player)
continue;
world->moveObject(physicActor->getPtr(), physicActor->getSimulationPosition(), false, false);
newPositions.emplace_back(physicActor->getPtr(), physicActor->getSimulationPosition());
}
for (auto& [ptr, pos] : newPositions)
world->moveObject(ptr, pos, false, false);
world->moveObject(player->getPtr(), player->getSimulationPosition(), false, false);
}
void PhysicsSystem::updateAnimatedCollisionShape(const MWWorld::Ptr& object)
{
ObjectMap::iterator found = mObjects.find(object);
ObjectMap::iterator found = mObjects.find(object.mRef);
if (found != mObjects.end())
if (found->second->animateCollisionShapes())
mTaskScheduler->updateSingleAabb(found->second);
@ -865,7 +846,7 @@ namespace MWPhysics
bool PhysicsSystem::isActorStandingOn(const MWWorld::Ptr &actor, const MWWorld::ConstPtr &object) const
{
const auto physActor = mActors.find(actor);
const auto physActor = mActors.find(actor.mRef);
if (physActor != mActors.end())
return physActor->second->getStandingOnPtr() == object;
return false;
@ -943,7 +924,7 @@ namespace MWPhysics
bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const
{
btCollisionObject* object = nullptr;
const auto it = mActors.find(ignore);
const auto it = mActors.find(ignore.mRef);
if (it != mActors.end())
object = it->second->getCollisionObject();
const auto bulletPosition = Misc::Convert::toBullet(position);

@ -5,6 +5,7 @@
#include <memory>
#include <map>
#include <set>
#include <unordered_map>
#include <algorithm>
#include <osg/Quat>
@ -56,7 +57,7 @@ namespace MWPhysics
class PhysicsTaskScheduler;
class Projectile;
using ActorMap = std::map<MWWorld::ConstPtr, std::shared_ptr<Actor>>;
using ActorMap = std::unordered_map<const MWWorld::LiveCellRefBase*, std::shared_ptr<Actor>>;
struct ContactPoint
{
@ -272,7 +273,7 @@ namespace MWPhysics
std::unique_ptr<Resource::BulletShapeManager> mShapeManager;
Resource::ResourceSystem* mResourceSystem;
using ObjectMap = std::map<MWWorld::ConstPtr, std::shared_ptr<Object>>;
using ObjectMap = std::unordered_map<const MWWorld::LiveCellRefBase*, std::shared_ptr<Object>>;
ObjectMap mObjects;
std::set<Object*> mAnimatedObjects; // stores pointers to elements in mObjects

@ -271,7 +271,7 @@ bool ActorAnimation::useShieldAnimations() const
return false;
}
osg::Group* ActorAnimation::getBoneByName(const std::string& boneName)
osg::Group* ActorAnimation::getBoneByName(const std::string& boneName) const
{
if (!mObjectRoot)
return nullptr;

@ -41,7 +41,7 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
bool updateCarriedLeftVisible(const int weaptype) const override;
protected:
osg::Group* getBoneByName(const std::string& boneName);
osg::Group* getBoneByName(const std::string& boneName) const;
virtual void updateHolsteredWeapon(bool showHolsteredWeapons);
virtual void updateHolsteredShield(bool showCarriedLeft);
virtual void updateQuiver();

@ -17,6 +17,7 @@
#include <components/resource/keyframemanager.hpp>
#include <components/misc/constants.hpp>
#include <components/misc/pathhelpers.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/sceneutil/keyframe.hpp>
@ -196,32 +197,6 @@ namespace
return 0.0f;
}
/// @brief Base class for visitors that remove nodes from a scene graph.
/// Subclasses need to fill the mToRemove vector.
/// To use, node->accept(removeVisitor); removeVisitor.remove();
class RemoveVisitor : public osg::NodeVisitor
{
public:
RemoveVisitor()
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
{
}
void remove()
{
for (RemoveVec::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it)
{
if (!it->second->removeChild(it->first))
Log(Debug::Error) << "Error removing " << it->first->getName();
}
}
protected:
// <node to remove, parent node to remove it from>
typedef std::vector<std::pair<osg::Node*, osg::Group*> > RemoveVec;
std::vector<std::pair<osg::Node*, osg::Group*> > mToRemove;
};
class GetExtendedBonesVisitor : public osg::NodeVisitor
{
public:
@ -244,7 +219,7 @@ namespace
std::vector<std::pair<osg::Node*, osg::Group*> > mFoundBones;
};
class RemoveFinishedCallbackVisitor : public RemoveVisitor
class RemoveFinishedCallbackVisitor : public SceneUtil::RemoveVisitor
{
public:
bool mHasMagicEffects;
@ -289,7 +264,7 @@ namespace
}
};
class RemoveCallbackVisitor : public RemoveVisitor
class RemoveCallbackVisitor : public SceneUtil::RemoveVisitor
{
public:
bool mHasMagicEffects;
@ -397,90 +372,6 @@ namespace
private:
int mEffectId;
};
// Removes all drawables from a graph.
class CleanObjectRootVisitor : public RemoveVisitor
{
public:
void apply(osg::Drawable& drw) override
{
applyDrawable(drw);
}
void apply(osg::Group& node) override
{
applyNode(node);
}
void apply(osg::MatrixTransform& node) override
{
applyNode(node);
}
void apply(osg::Node& node) override
{
applyNode(node);
}
void applyNode(osg::Node& node)
{
if (node.getStateSet())
node.setStateSet(nullptr);
if (node.getNodeMask() == 0x1 && node.getNumParents() == 1)
mToRemove.emplace_back(&node, node.getParent(0));
else
traverse(node);
}
void applyDrawable(osg::Node& node)
{
osg::NodePath::iterator parent = getNodePath().end()-2;
// We know that the parent is a Group because only Groups can have children.
osg::Group* parentGroup = static_cast<osg::Group*>(*parent);
// Try to prune nodes that would be empty after the removal
if (parent != getNodePath().begin())
{
// This could be extended to remove the parent's parent, and so on if they are empty as well.
// But for NIF files, there won't be a benefit since only TriShapes can be set to STATIC dataVariance.
osg::Group* parentParent = static_cast<osg::Group*>(*(parent - 1));
if (parentGroup->getNumChildren() == 1 && parentGroup->getDataVariance() == osg::Object::STATIC)
{
mToRemove.emplace_back(parentGroup, parentParent);
return;
}
}
mToRemove.emplace_back(&node, parentGroup);
}
};
class RemoveTriBipVisitor : public RemoveVisitor
{
public:
void apply(osg::Drawable& drw) override
{
applyImpl(drw);
}
void apply(osg::Group& node) override
{
traverse(node);
}
void apply(osg::MatrixTransform& node) override
{
traverse(node);
}
void applyImpl(osg::Node& node)
{
const std::string toFind = "tri bip";
if (Misc::StringUtils::ciCompareLen(node.getName(), toFind, toFind.size()) == 0)
{
osg::Group* parent = static_cast<osg::Group*>(*(getNodePath().end()-2));
// Not safe to remove in apply(), since the visitor is still iterating the child list
mToRemove.emplace_back(&node, parent);
}
}
};
}
namespace MWRender
@ -701,8 +592,6 @@ namespace MWRender
void Animation::loadAllAnimationsInFolder(const std::string &model, const std::string &baseModel)
{
const std::map<std::string, VFS::File*>& index = mResourceSystem->getVFS()->getIndex();
std::string animationPath = model;
if (animationPath.find("meshes") == 0)
{
@ -710,21 +599,10 @@ namespace MWRender
}
animationPath.replace(animationPath.size()-3, 3, "/");
mResourceSystem->getVFS()->normalizeFilename(animationPath);
std::map<std::string, VFS::File*>::const_iterator found = index.lower_bound(animationPath);
while (found != index.end())
for (const auto& name : mResourceSystem->getVFS()->getRecursiveDirectoryIterator(animationPath))
{
const std::string& name = found->first;
if (name.size() >= animationPath.size() && name.substr(0, animationPath.size()) == animationPath)
{
size_t pos = name.find_last_of('.');
if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".kf") == 0)
addSingleAnimSource(name, baseModel);
}
else
break;
++found;
if (Misc::getFileExtension(name) == "kf")
addSingleAnimSource(name, baseModel);
}
}
@ -1405,8 +1283,6 @@ namespace MWRender
if (model.empty())
return;
const std::map<std::string, VFS::File*>& index = resourceSystem->getVFS()->getIndex();
std::string animationPath = model;
if (animationPath.find("meshes") == 0)
{
@ -1414,21 +1290,10 @@ namespace MWRender
}
animationPath.replace(animationPath.size()-4, 4, "/");
resourceSystem->getVFS()->normalizeFilename(animationPath);
std::map<std::string, VFS::File*>::const_iterator found = index.lower_bound(animationPath);
while (found != index.end())
for (const auto& name : resourceSystem->getVFS()->getRecursiveDirectoryIterator(animationPath))
{
const std::string& name = found->first;
if (name.size() >= animationPath.size() && name.substr(0, animationPath.size()) == animationPath)
{
size_t pos = name.find_last_of('.');
if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".nif") == 0)
loadBonesFromFile(node, name, resourceSystem);
}
else
break;
++found;
if (Misc::getFileExtension(name) == "nif")
loadBonesFromFile(node, name, resourceSystem);
}
}

@ -158,7 +158,7 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)
try
{
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(itemModel);
osg::ref_ptr<const osg::Node> node = mResourceSystem->getSceneManager()->getTemplate(itemModel);
const NodeMap& nodeMap = getNodeMap();
NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename));

@ -1,6 +1,7 @@
#include "groundcover.hpp"
#include <osg/AlphaFunc>
#include <osg/BlendFunc>
#include <osg/Geometry>
#include <osg/VertexAttribDivisor>
@ -66,18 +67,6 @@ namespace MWRender
{
}
void apply(osg::Node& node) override
{
osg::ref_ptr<osg::StateSet> ss = node.getStateSet();
if (ss != nullptr)
{
ss->removeAttribute(osg::StateAttribute::MATERIAL);
removeAlpha(ss);
}
traverse(node);
}
void apply(osg::Geometry& geom) override
{
for (unsigned int i = 0; i < geom.getNumPrimitiveSets(); ++i)
@ -110,32 +99,14 @@ namespace MWRender
// Display lists do not support instancing in OSG 3.4
geom.setUseDisplayList(false);
geom.setUseVertexBufferObjects(true);
geom.setVertexAttribArray(6, transforms.get(), osg::Array::BIND_PER_VERTEX);
geom.setVertexAttribArray(7, rotations.get(), osg::Array::BIND_PER_VERTEX);
osg::ref_ptr<osg::StateSet> ss = geom.getOrCreateStateSet();
ss->setAttribute(new osg::VertexAttribDivisor(6, 1));
ss->setAttribute(new osg::VertexAttribDivisor(7, 1));
ss->removeAttribute(osg::StateAttribute::MATERIAL);
removeAlpha(ss);
traverse(geom);
}
private:
std::vector<Groundcover::GroundcoverEntry> mInstances;
osg::Vec3f mChunkPosition;
void removeAlpha(osg::StateSet* stateset)
{
// MGE uses default alpha settings for groundcover, so we can not rely on alpha properties
stateset->removeAttribute(osg::StateAttribute::ALPHAFUNC);
stateset->removeMode(GL_ALPHA_TEST);
stateset->removeAttribute(osg::StateAttribute::BLENDFUNC);
stateset->removeMode(GL_BLEND);
stateset->setRenderBinToInherit();
}
};
class DensityCalculator
@ -180,11 +151,11 @@ namespace MWRender
osg::ref_ptr<osg::Node> Groundcover::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
{
ChunkId id = std::make_tuple(center, size, activeGrid);
GroundcoverChunkId id = std::make_tuple(center, size);
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id);
if (obj)
return obj->asNode();
return static_cast<osg::Node*>(obj.get());
else
{
InstanceMap instances;
@ -196,10 +167,19 @@ namespace MWRender
}
Groundcover::Groundcover(Resource::SceneManager* sceneManager, float density)
: GenericResourceManager<ChunkId>(nullptr)
: GenericResourceManager<GroundcoverChunkId>(nullptr)
, mSceneManager(sceneManager)
, mDensity(density)
, mStateset(new osg::StateSet)
{
// MGE uses default alpha settings for groundcover, so we can not rely on alpha properties
// Force a unified alpha handling instead of data from meshes
osg::ref_ptr<osg::AlphaFunc> alpha = new osg::AlphaFunc(osg::AlphaFunc::GEQUAL, 128.f / 255.f);
mStateset->setAttributeAndModes(alpha.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
mStateset->setAttributeAndModes(new osg::BlendFunc, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
mStateset->setRenderBinDetails(0, "RenderBin", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
mStateset->setAttribute(new osg::VertexAttribDivisor(6, 1));
mStateset->setAttribute(new osg::VertexAttribDivisor(7, 1));
}
void Groundcover::collectInstances(InstanceMap& instances, float size, const osg::Vec2f& center)
@ -255,27 +235,23 @@ namespace MWRender
for (auto& pair : instances)
{
const osg::Node* temp = mSceneManager->getTemplate(pair.first);
osg::ref_ptr<osg::Node> node = static_cast<osg::Node*>(temp->clone(osg::CopyOp::DEEP_COPY_ALL&(~osg::CopyOp::DEEP_COPY_TEXTURES)));
osg::ref_ptr<osg::Node> node = static_cast<osg::Node*>(temp->clone(osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES|osg::CopyOp::DEEP_COPY_USERDATA|osg::CopyOp::DEEP_COPY_ARRAYS|osg::CopyOp::DEEP_COPY_PRIMITIVES));
// Keep link to original mesh to keep it in cache
group->getOrCreateUserDataContainer()->addUserObject(new Resource::TemplateRef(temp));
mSceneManager->reinstateRemovedState(node);
InstancingVisitor visitor(pair.second, worldCenter);
node->accept(visitor);
group->addChild(node);
}
// Force a unified alpha handling instead of data from meshes
osg::ref_ptr<osg::AlphaFunc> alpha = new osg::AlphaFunc(osg::AlphaFunc::GEQUAL, 128.f / 255.f);
group->getOrCreateStateSet()->setAttributeAndModes(alpha.get(), osg::StateAttribute::ON);
group->getBound();
group->setStateSet(mStateset);
group->setNodeMask(Mask_Groundcover);
if (mSceneManager->getLightingMethod() != SceneUtil::LightingMethod::FFP)
group->setCullCallback(new SceneUtil::LightListCallback);
mSceneManager->recreateShaders(group, "groundcover", false, true);
mSceneManager->shareState(group);
group->getBound();
return group;
}

@ -29,8 +29,8 @@ namespace MWRender
osg::Vec3f mPlayerPos;
};
typedef std::tuple<osg::Vec2f, float, bool> ChunkId; // Center, Size, ActiveGrid
class Groundcover : public Resource::GenericResourceManager<ChunkId>, public Terrain::QuadTreeWorld::ChunkManager
typedef std::tuple<osg::Vec2f, float> GroundcoverChunkId; // Center, Size
class Groundcover : public Resource::GenericResourceManager<GroundcoverChunkId>, public Terrain::QuadTreeWorld::ChunkManager
{
public:
Groundcover(Resource::SceneManager* sceneManager, float density);
@ -56,6 +56,7 @@ namespace MWRender
private:
Resource::SceneManager* mSceneManager;
float mDensity;
osg::ref_ptr<osg::StateSet> mStateset;
typedef std::map<std::string, std::vector<GroundcoverEntry>> InstanceMap;
osg::ref_ptr<osg::Node> createChunk(InstanceMap& instances, const osg::Vec2f& center);

@ -714,14 +714,14 @@ void NpcAnimation::updateParts()
PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const std::string& bonename, const std::string& bonefilter, bool enchantedGlow, osg::Vec4f* glowColor)
{
osg::ref_ptr<osg::Node> instance = mResourceSystem->getSceneManager()->getInstance(model);
osg::ref_ptr<const osg::Node> templateNode = mResourceSystem->getSceneManager()->getTemplate(model);
const NodeMap& nodeMap = getNodeMap();
NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename));
if (found == nodeMap.end())
throw std::runtime_error("Can't find attachment node " + bonename);
osg::ref_ptr<osg::Node> attached = SceneUtil::attach(instance, mObjectRoot, bonefilter, found->second);
osg::ref_ptr<osg::Node> attached = SceneUtil::attach(templateNode, mObjectRoot, bonefilter, found->second);
if (enchantedGlow)
mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, *glowColor);

@ -77,7 +77,7 @@ namespace MWRender
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id);
if (obj)
return obj->asNode();
return static_cast<osg::Node*>(obj.get());
else
{
osg::ref_ptr<osg::Node> node = createChunk(size, center, activeGrid, viewPoint, compile);

@ -373,7 +373,9 @@ namespace MWRender
mTerrainStorage.reset(new TerrainStorage(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps));
const float lodFactor = Settings::Manager::getFloat("lod factor", "Terrain");
if (Settings::Manager::getBool("distant terrain", "Terrain"))
bool groundcover = Settings::Manager::getBool("enabled", "Groundcover");
bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
if (distantTerrain || groundcover)
{
const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain");
int compMapPower = Settings::Manager::getInt("composite map level", "Terrain");
@ -398,41 +400,27 @@ namespace MWRender
mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells"));
mTerrain->setWorkQueue(mWorkQueue.get());
if (Settings::Manager::getBool("enabled", "Groundcover"))
{
osg::ref_ptr<osg::Group> groundcoverRoot = new osg::Group;
groundcoverRoot->setNodeMask(Mask_Groundcover);
groundcoverRoot->setName("Groundcover Root");
sceneRoot->addChild(groundcoverRoot);
mGroundcoverUpdater = new GroundcoverUpdater;
groundcoverRoot->addUpdateCallback(mGroundcoverUpdater);
float chunkSize = Settings::Manager::getFloat("min chunk size", "Groundcover");
if (chunkSize >= 1.0f)
chunkSize = 1.0f;
else if (chunkSize >= 0.5f)
chunkSize = 0.5f;
else if (chunkSize >= 0.25f)
chunkSize = 0.25f;
else if (chunkSize != 0.125f)
chunkSize = 0.125f;
osg::ref_ptr<SceneUtil::CompositeStateSetUpdater> composite = new SceneUtil::CompositeStateSetUpdater;
if (groundcover)
{
float density = Settings::Manager::getFloat("density", "Groundcover");
density = std::clamp(density, 0.f, 1.f);
mGroundcoverWorld.reset(new Terrain::QuadTreeWorld(groundcoverRoot, mTerrainStorage.get(), Mask_Groundcover, lodFactor, chunkSize));
mGroundcoverUpdater = new GroundcoverUpdater;
composite->addController(mGroundcoverUpdater);
mGroundcover.reset(new Groundcover(mResourceSystem->getSceneManager(), density));
static_cast<Terrain::QuadTreeWorld*>(mGroundcoverWorld.get())->addChunkManager(mGroundcover.get());
static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mGroundcover.get());
mResourceSystem->addResourceManager(mGroundcover.get());
// Groundcover it is handled in the same way indifferently from if it is from active grid or from distant cell.
// Use a stub grid to avoid splitting between chunks for active grid and chunks for distant cells.
mGroundcoverWorld->setActiveGrid(osg::Vec4i(0, 0, 0, 0));
float groundcoverDistance = std::max(0.f, Settings::Manager::getFloat("rendering distance", "Groundcover"));
mGroundcover->setViewDistance(groundcoverDistance);
}
mStateUpdater = new StateUpdater;
sceneRoot->addUpdateCallback(mStateUpdater);
composite->addController(mStateUpdater);
sceneRoot->addUpdateCallback(composite);
mSharedUniformStateUpdater = new SharedUniformStateUpdater;
rootNode->addUpdateCallback(mSharedUniformStateUpdater);
@ -693,8 +681,6 @@ namespace MWRender
if (store->getCell()->isExterior())
{
mTerrain->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
if (mGroundcoverWorld)
mGroundcoverWorld->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
}
}
void RenderingManager::removeCell(const MWWorld::CellStore *store)
@ -706,8 +692,6 @@ namespace MWRender
if (store->getCell()->isExterior())
{
mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
if (mGroundcoverWorld)
mGroundcoverWorld->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
}
mWater->removeCell(store);
@ -718,8 +702,6 @@ namespace MWRender
if (!enable)
mWater->setCullCallback(nullptr);
mTerrain->enable(enable);
if (mGroundcoverWorld)
mGroundcoverWorld->enable(enable);
}
void RenderingManager::setSkyEnabled(bool enabled)
@ -1179,12 +1161,6 @@ namespace MWRender
fov = std::min(mFieldOfView, 140.f);
float distanceMult = std::cos(osg::DegreesToRadians(fov)/2.f);
mTerrain->setViewDistance(mViewDistance * (distanceMult ? 1.f/distanceMult : 1.f));
if (mGroundcoverWorld)
{
float groundcoverDistance = std::max(0.f, Settings::Manager::getFloat("rendering distance", "Groundcover"));
mGroundcoverWorld->setViewDistance(groundcoverDistance * (distanceMult ? 1.f/distanceMult : 1.f));
}
}
void RenderingManager::updateTextureFiltering()

@ -262,8 +262,6 @@ namespace MWRender
osg::ref_ptr<osg::Group> mSceneRoot;
Resource::ResourceSystem* mResourceSystem;
osg::ref_ptr<GroundcoverUpdater> mGroundcoverUpdater;
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
@ -278,10 +276,10 @@ namespace MWRender
std::unique_ptr<Objects> mObjects;
std::unique_ptr<Water> mWater;
std::unique_ptr<Terrain::World> mTerrain;
std::unique_ptr<Terrain::World> mGroundcoverWorld;
std::unique_ptr<TerrainStorage> mTerrainStorage;
std::unique_ptr<ObjectPaging> mObjectPaging;
std::unique_ptr<Groundcover> mGroundcover;
osg::ref_ptr<GroundcoverUpdater> mGroundcoverUpdater;
std::unique_ptr<SkyManager> mSky;
std::unique_ptr<FogManager> mFog;
std::unique_ptr<ScreenshotManager> mScreenshotManager;

@ -191,6 +191,7 @@ namespace MWRender
screenshotH = screenshotW; // use square resolution for planet mapping
std::vector<osg::ref_ptr<osg::Image>> images;
images.reserve(6);
for (int i = 0; i < 6; ++i)
images.push_back(new osg::Image);

@ -33,7 +33,10 @@ namespace MWRender
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
{
minX = 0, minY = 0, maxX = 0, maxY = 0;
minX = 0;
minY = 0;
maxX = 0;
maxY = 0;
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();

@ -131,7 +131,7 @@ namespace MWSound
max = std::max(min, max);
Sound_Buffer& sfx = mSoundBuffers.emplace_back("Sound/" + sound.mSound, volume, min, max);
mVfs->normalizeFilename(sfx.mResourceName);
sfx.mResourceName = mVfs->normalizeFilename(sfx.mResourceName);
mBufferNameMap.emplace(soundId, &sfx);
return &sfx;

@ -294,20 +294,9 @@ namespace MWSound
if (mMusicFiles.find(playlist) == mMusicFiles.end())
{
std::vector<std::string> filelist;
const std::map<std::string, VFS::File*>& index = mVFS->getIndex();
std::string pattern = "Music/" + playlist;
mVFS->normalizeFilename(pattern);
std::map<std::string, VFS::File*>::const_iterator found = index.lower_bound(pattern);
while (found != index.end())
{
if (found->first.size() >= pattern.size() && found->first.substr(0, pattern.size()) == pattern)
filelist.push_back(found->first);
else
break;
++found;
}
for (const auto& name : mVFS->getRecursiveDirectoryIterator("Music/" + playlist))
filelist.push_back(name);
mMusicFiles[playlist] = filelist;
}
@ -327,13 +316,11 @@ namespace MWSound
if (mMusicFiles.find("Title") == mMusicFiles.end())
{
std::vector<std::string> filelist;
const std::map<std::string, VFS::File*>& index = mVFS->getIndex();
// Is there an ini setting for this filename or something?
std::string filename = "music/special/morrowind title.mp3";
auto found = index.find(filename);
if (found != index.end())
if (mVFS->exists(filename))
{
filelist.emplace_back(found->first);
filelist.emplace_back(filename);
mMusicFiles["Title"] = filelist;
}
else
@ -355,10 +342,7 @@ namespace MWSound
if(!mOutput->isInitialized())
return;
std::string voicefile = "Sound/"+filename;
mVFS->normalizeFilename(voicefile);
DecoderPtr decoder = loadVoice(voicefile);
DecoderPtr decoder = loadVoice(mVFS->normalizeFilename("Sound/" + filename));
if (!decoder)
return;
@ -389,10 +373,7 @@ namespace MWSound
if(!mOutput->isInitialized())
return;
std::string voicefile = "Sound/"+filename;
mVFS->normalizeFilename(voicefile);
DecoderPtr decoder = loadVoice(voicefile);
DecoderPtr decoder = loadVoice(mVFS->normalizeFilename("Sound/" + filename));
if (!decoder)
return;

@ -14,12 +14,36 @@
#include <components/terrain/world.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include <components/esm/loadcell.hpp>
#include <components/loadinglistener/reporter.hpp>
#include "../mwrender/landmanager.hpp"
#include "cellstore.hpp"
#include "class.hpp"
namespace
{
template <class Contained>
bool contains(const std::vector<MWWorld::CellPreloader::PositionCellGrid>& container,
const Contained& contained, float tolerance=1.f)
{
for (const auto& pos : contained)
{
bool found = false;
for (const auto& pos2 : container)
{
if ((pos.first-pos2.first).length2() < tolerance*tolerance && pos.second == pos2.second)
{
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
}
namespace MWWorld
{
@ -94,7 +118,6 @@ namespace MWWorld
{
mesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS());
bool animated = false;
size_t slashpos = mesh.find_last_of("/\\");
if (slashpos != std::string::npos && slashpos != mesh.size()-1)
{
@ -106,17 +129,11 @@ namespace MWWorld
{
kfname.replace(kfname.size()-4, 4, ".kf");
if (mSceneManager->getVFS()->exists(kfname))
{
mPreloadedObjects.insert(mKeyframeManager->get(kfname));
animated = true;
}
}
}
}
if (mPreloadInstances && animated)
mPreloadedObjects.insert(mSceneManager->cacheInstance(mesh));
else
mPreloadedObjects.insert(mSceneManager->getTemplate(mesh));
mPreloadedObjects.insert(mSceneManager->getTemplate(mesh));
if (mPreloadInstances)
mPreloadedObjects.insert(mBulletShapeManager->cacheInstance(mesh));
else
@ -157,8 +174,6 @@ namespace MWWorld
public:
TerrainPreloadItem(const std::vector<osg::ref_ptr<Terrain::View> >& views, Terrain::World* world, const std::vector<CellPreloader::PositionCellGrid>& preloadPositions)
: mAbort(false)
, mProgress(views.size())
, mProgressRange(0)
, mTerrainViews(views)
, mWorld(world)
, mPreloadPositions(preloadPositions)
@ -178,8 +193,9 @@ namespace MWWorld
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)
{
mTerrainViews[i]->reset();
mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort, mProgress[i], mProgressRange);
mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort, mLoadingReporter);
}
mLoadingReporter.complete();
}
void abort() override
@ -187,16 +203,17 @@ namespace MWWorld
mAbort = true;
}
int getProgress() const { return !mProgress.empty() ? mProgress[0].load() : 0; }
int getProgressRange() const { return !mProgress.empty() && mProgress[0].load() ? mProgressRange : 0; }
void wait(Loading::Listener& listener) const
{
mLoadingReporter.wait(listener);
}
private:
std::atomic<bool> mAbort;
std::vector<std::atomic<int>> mProgress;
int mProgressRange;
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
Terrain::World* mWorld;
std::vector<CellPreloader::PositionCellGrid> mPreloadPositions;
Loading::Reporter mLoadingReporter;
};
/// Worker thread item: update the resource system's cache, effectively deleting unused entries.
@ -230,6 +247,7 @@ namespace MWWorld
, mPreloadInstances(true)
, mLastResourceCacheUpdate(0.0)
, mStoreViewsFailCount(0)
, mLoadedTerrainTimestamp(0.0)
{
}
@ -375,7 +393,11 @@ namespace MWWorld
setTerrainPreloadPositions(std::vector<PositionCellGrid>());
}
else
{
mStoreViewsFailCount = 0;
mLoadedTerrainPositions = mTerrainPreloadPositions;
mLoadedTerrainTimestamp = timestamp;
}
mTerrainPreloadItem = nullptr;
}
}
@ -415,7 +437,7 @@ namespace MWWorld
mUnrefQueue = unrefQueue;
}
bool CellPreloader::syncTerrainLoad(const std::vector<CellPreloader::PositionCellGrid> &positions, int& progress, int& progressRange, double timestamp)
bool CellPreloader::syncTerrainLoad(const std::vector<CellPreloader::PositionCellGrid> &positions, double timestamp, Loading::Listener& listener)
{
if (!mTerrainPreloadItem)
return true;
@ -435,18 +457,15 @@ namespace MWWorld
}
else
{
progress = mTerrainPreloadItem->getProgress();
progressRange = mTerrainPreloadItem->getProgressRange();
return false;
mTerrainPreloadItem->wait(listener);
return true;
}
}
void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid *exceptPos)
{
const float resetThreshold = ESM::Land::REAL_SIZE;
for (const auto& pos : mTerrainPreloadPositions)
if (exceptPos && (pos.first-exceptPos->first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos->second)
return;
if (exceptPos && contains(mTerrainPreloadPositions, std::array {*exceptPos}, ESM::Land::REAL_SIZE))
return;
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
{
mTerrainPreloadItem->abort();
@ -455,28 +474,13 @@ namespace MWWorld
setTerrainPreloadPositions(std::vector<CellPreloader::PositionCellGrid>());
}
bool contains(const std::vector<CellPreloader::PositionCellGrid>& container, const std::vector<CellPreloader::PositionCellGrid>& contained)
{
for (const auto& pos : contained)
{
bool found = false;
for (const auto& pos2 : container)
{
if ((pos.first-pos2.first).length2() < 1 && pos.second == pos2.second)
{
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
void CellPreloader::setTerrainPreloadPositions(const std::vector<CellPreloader::PositionCellGrid> &positions)
{
if (positions.empty())
{
mTerrainPreloadPositions.clear();
mLoadedTerrainPositions.clear();
}
else if (contains(mTerrainPreloadPositions, positions))
return;
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
@ -503,5 +507,10 @@ namespace MWWorld
}
}
}
bool CellPreloader::isTerrainLoaded(const CellPreloader::PositionCellGrid &position, double referenceTime) const
{
return mLoadedTerrainTimestamp + mResourceSystem->getSceneManager()->getExpiryDelay() > referenceTime && contains(mLoadedTerrainPositions, std::array {position}, ESM::Land::REAL_SIZE);
}
}

@ -29,6 +29,11 @@ namespace MWRender
class LandManager;
}
namespace Loading
{
class Listener;
}
namespace MWWorld
{
class CellStore;
@ -72,8 +77,9 @@ namespace MWWorld
typedef std::pair<osg::Vec3f, osg::Vec4i> PositionCellGrid;
void setTerrainPreloadPositions(const std::vector<PositionCellGrid>& positions);
bool syncTerrainLoad(const std::vector<CellPreloader::PositionCellGrid> &positions, int& progress, int& progressRange, double timestamp);
bool syncTerrainLoad(const std::vector<CellPreloader::PositionCellGrid> &positions, double timestamp, Loading::Listener& listener);
void abortTerrainPreloadExcept(const PositionCellGrid *exceptPos);
bool isTerrainLoaded(const CellPreloader::PositionCellGrid &position, double referenceTime) const;
private:
Resource::ResourceSystem* mResourceSystem;
@ -114,6 +120,9 @@ namespace MWWorld
std::vector<PositionCellGrid> mTerrainPreloadPositions;
osg::ref_ptr<TerrainPreloadItem> mTerrainPreloadItem;
osg::ref_ptr<SceneUtil::WorkItem> mUpdateCacheItem;
std::vector<PositionCellGrid> mLoadedTerrainPositions;
double mLoadedTerrainTimestamp;
};
}

@ -48,11 +48,6 @@ namespace MWWorld
return mCellRef.mRefID;
}
const std::string* CellRef::getRefIdPtr() const
{
return &mCellRef.mRefID;
}
bool CellRef::getTeleport() const
{
return mCellRef.mTeleport;

@ -38,8 +38,8 @@ namespace MWWorld
// Id of object being referenced
std::string getRefId() const;
// Pointer to ID of the object being referenced
const std::string* getRefIdPtr() const;
// Reference to ID of the object being referenced
const std::string& getRefIdRef() const { return mCellRef.mRefID; }
// For doors - true if this door teleports to somewhere else, false
// if it should open through animation.

@ -36,7 +36,7 @@ namespace
}
bool cont = cell.second.forEach([&] (MWWorld::Ptr ptr)
{
if(*ptr.getCellRef().getRefIdPtr() == id)
if (ptr.getCellRef().getRefIdRef() == id)
{
return visitor(ptr);
}

@ -181,7 +181,7 @@ namespace
{
for (typename MWWorld::CellRefList<T>::List::iterator iter (collection.mList.begin());
iter!=collection.mList.end(); ++iter)
if (iter->mRef.getRefNum()==state.mRef.mRefNum && *iter->mRef.getRefIdPtr() == state.mRef.mRefID)
if (iter->mRef.getRefNum()==state.mRef.mRefNum && iter->mRef.getRefIdRef() == state.mRef.mRefID)
{
// overwrite existing reference
float oldscale = iter->mRef.getScale();
@ -417,7 +417,7 @@ namespace MWWorld
const std::string *mIdToFind;
bool operator()(const PtrType& ptr)
{
if (*ptr.getCellRef().getRefIdPtr() == *mIdToFind)
if (ptr.getCellRef().getRefIdRef() == *mIdToFind)
{
mFound = ptr;
return false;

@ -184,7 +184,7 @@ int MWWorld::ContainerStore::count(const std::string &id) const
{
int total=0;
for (const auto&& iter : *this)
if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))
if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefIdRef(), id))
total += iter.getRefData().getCount();
return total;
}
@ -249,7 +249,7 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2)
const MWWorld::Class& cls1 = ptr1.getClass();
const MWWorld::Class& cls2 = ptr2.getClass();
if (!Misc::StringUtils::ciEqual(ptr1.getCellRef().getRefId(), ptr2.getCellRef().getRefId()))
if (!Misc::StringUtils::ciEqual(ptr1.getCellRef().getRefIdRef(), ptr2.getCellRef().getRefIdRef()))
return false;
// If it has an enchantment, don't stack when some of the charge is already used
@ -364,7 +364,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr,
for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter)
{
if (Misc::StringUtils::ciEqual((*iter).getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId))
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefIdRef(), MWWorld::ContainerStore::sGoldId))
{
iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), realCount));
flagAsModified();
@ -465,7 +465,7 @@ int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const
int toRemove = count;
for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter)
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), itemId))
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefIdRef(), itemId))
toRemove -= remove(*iter, toRemove, actor, equipReplacement, resolveFirst);
flagAsModified();
@ -740,7 +740,7 @@ MWWorld::Ptr MWWorld::ContainerStore::findReplacement(const std::string& id)
for (auto&& iter : *this)
{
int iterHealth = iter.getClass().hasItemHealth(iter) ? iter.getClass().getItemHealth(iter) : 1;
if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))
if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefIdRef(), id))
{
// Prefer the stack with the lowest remaining uses
// Try to get item with zero durability only if there are no other items found

@ -143,7 +143,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr,
if (allowAutoEquip && actorPtr != MWMechanics::getPlayer()
&& actorPtr.getClass().isNpc() && !actorPtr.getClass().getNpcStats(actorPtr).isWerewolf())
{
std::string type = itemPtr.getTypeName();
const std::string& type = itemPtr.getTypeName();
if (type == typeid(ESM::Armor).name() || type == typeid(ESM::Clothing).name())
autoEquip(actorPtr);
}
@ -748,7 +748,7 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor
if (equipReplacement && wasEquipped && (actor != MWMechanics::getPlayer())
&& actor.getClass().isNpc() && !actor.getClass().getNpcStats(actor).isWerewolf())
{
std::string type = item.getTypeName();
const std::string& type = item.getTypeName();
if (type == typeid(ESM::Armor).name() || type == typeid(ESM::Clothing).name())
autoEquip(actor);
}

@ -628,7 +628,10 @@ namespace MWWorld
osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter);
mRendering.setActiveGrid(newGrid);
preloadTerrain(pos, true);
if (mRendering.pagingUnlockCache())
mPreloader->abortTerrainPreloadExcept(nullptr);
if (!mPreloader->isTerrainLoaded(std::make_pair(pos, newGrid), mRendering.getReferenceTime()))
preloadTerrain(pos, true);
mPagedRefs.clear();
mRendering.getPagedRefnums(newGrid, mPagedRefs);
@ -742,14 +745,12 @@ namespace MWWorld
{
loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")...");
CellStoreCollection::iterator iter = mActiveCells.begin();
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(it->mData.mX, it->mData.mY);
loadInactiveCell (cell, loadingListener, true);
activateCell (cell, loadingListener, false, true);
iter = mActiveCells.begin();
while (iter != mActiveCells.end())
auto iter = mInactiveCells.begin();
while (iter != mInactiveCells.end())
{
if (it->isExterior() && it->mData.mX == (*iter)->getCell()->getGridX() &&
it->mData.mY == (*iter)->getCell()->getGridY())
@ -796,8 +797,8 @@ namespace MWWorld
loadInactiveCell (cell, loadingListener, true);
activateCell (cell, loadingListener, false, true);
CellStoreCollection::iterator iter = mActiveCells.begin();
while (iter != mActiveCells.end())
auto iter = mInactiveCells.begin();
while (iter != mInactiveCells.end())
{
assert (!(*iter)->getCell()->isExterior());
@ -1243,32 +1244,16 @@ namespace MWWorld
{
std::vector<PositionCellGrid> vec;
vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
if (sync && mRendering.pagingUnlockCache())
mPreloader->abortTerrainPreloadExcept(nullptr);
else
mPreloader->abortTerrainPreloadExcept(&vec[0]);
mPreloader->abortTerrainPreloadExcept(&vec[0]);
mPreloader->setTerrainPreloadPositions(vec);
if (!sync) return;
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
Loading::ScopedLoad load(loadingListener);
int progress = 0, initialProgress = -1, progressRange = 0;
while (!mPreloader->syncTerrainLoad(vec, progress, progressRange, mRendering.getReferenceTime()))
{
if (initialProgress == -1)
{
loadingListener->setLabel("#{sLoadingMessage4}");
initialProgress = progress;
}
if (progress)
{
loadingListener->setProgressRange(std::max(0, progressRange-initialProgress));
loadingListener->setProgress(progress-initialProgress);
}
else
loadingListener->setProgress(0);
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
loadingListener->setLabel("#{sLoadingMessage4}");
while (!mPreloader->syncTerrainLoad(vec, mRendering.getReferenceTime(), *loadingListener)) {}
}
void Scene::reloadTerrain()

@ -520,7 +520,8 @@ namespace MWWorld
const ESM::Cell *Store<ESM::Cell>::search(int x, int y) const
{
ESM::Cell cell;
cell.mData.mX = x, cell.mData.mY = y;
cell.mData.mX = x;
cell.mData.mY = y;
std::pair<int, int> key(x, y);
DynamicExt::const_iterator it = mExt.find(key);
@ -538,7 +539,8 @@ namespace MWWorld
const ESM::Cell *Store<ESM::Cell>::searchStatic(int x, int y) const
{
ESM::Cell cell;
cell.mData.mX = x, cell.mData.mY = y;
cell.mData.mX = x;
cell.mData.mY = y;
std::pair<int, int> key(x, y);
DynamicExt::const_iterator it = mExt.find(key);

@ -428,7 +428,7 @@ namespace MWWorld
const ESM::WeaponType *search(const int id) const;
const ESM::WeaponType *find(const int id) const;
RecordId load(ESM::ESMReader &esm) override { return RecordId(nullptr, false); }
RecordId load(ESM::ESMReader &esm) override { return RecordId({}, false); }
ESM::WeaponType* insert(const ESM::WeaponType &weaponType);

@ -77,6 +77,14 @@ return {
EXPECT_EQ(LuaUtil::call(script1["get"]).get<int>(), 45);
}
TEST_F(LuaStateTest, ToString)
{
EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.sol(), 3.14)), "3.14");
EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.sol(), true)), "true");
EXPECT_EQ(LuaUtil::toString(sol::nil), "nil");
EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.sol(), "something")), "\"something\"");
}
TEST_F(LuaStateTest, ErrorHandling)
{
EXPECT_ERROR(mLua.runInNewSandbox("invalid.lua"), "[string \"invalid.lua\"]:1:");

@ -1,6 +1,7 @@
#include "gmock/gmock.h"
#include <gtest/gtest.h>
#include <components/lua/luastate.hpp>
#include <components/lua/utilpackage.hpp>
#include "testing_util.hpp"
@ -45,6 +46,7 @@ namespace
EXPECT_FLOAT_EQ(lua.safe_script("return v.y").get<float>(), 12);
EXPECT_FLOAT_EQ(lua.safe_script("return v.z").get<float>(), 13);
EXPECT_EQ(lua.safe_script("return tostring(v)").get<std::string>(), "(5, 12, 13)");
EXPECT_EQ(LuaUtil::toString(lua.safe_script("return v")), "(5, 12, 13)");
EXPECT_FLOAT_EQ(lua.safe_script("return util.vector3(4, 0, 3):length()").get<float>(), 5);
EXPECT_FLOAT_EQ(lua.safe_script("return util.vector3(4, 0, 3):length2()").get<float>(), 25);
EXPECT_FALSE(lua.safe_script("return util.vector3(1, 2, 3) == util.vector3(1, 3, 2)").get<bool>());

@ -1,6 +1,10 @@
#include <gtest/gtest.h>
#include "components/misc/stringops.hpp"
#include <string>
#include <string_view>
#include <type_traits>
struct PartialBinarySearchTest : public ::testing::Test
{
protected:
@ -12,10 +16,6 @@ struct PartialBinarySearchTest : public ::testing::Test
std::sort(mDataVec.begin(), mDataVec.end(), Misc::StringUtils::ciLess);
}
void TearDown() override
{
}
bool matches(const std::string& keyword)
{
return Misc::StringUtils::partialBinarySearch(mDataVec.begin(), mDataVec.end(), keyword) != mDataVec.end();
@ -51,3 +51,97 @@ TEST_F (PartialBinarySearchTest, ci_test)
std::string unicode1 = "\u04151 \u0418"; // CYRILLIC CAPITAL LETTER IE, CYRILLIC CAPITAL LETTER I
EXPECT_TRUE( Misc::StringUtils::lowerCase(unicode1) == unicode1 );
}
namespace
{
using ::Misc::StringUtils;
using namespace ::testing;
template <class T>
struct MiscStringUtilsCiEqualEmptyTest : Test {};
TYPED_TEST_SUITE_P(MiscStringUtilsCiEqualEmptyTest);
TYPED_TEST_P(MiscStringUtilsCiEqualEmptyTest, empty_strings_should_be_equal)
{
EXPECT_TRUE(StringUtils::ciEqual(typename TypeParam::first_type {}, typename TypeParam::second_type {}));
}
REGISTER_TYPED_TEST_SUITE_P(MiscStringUtilsCiEqualEmptyTest,
empty_strings_should_be_equal
);
using EmptyStringTypePairsTypes = Types<
std::pair<std::string, std::string>,
std::pair<std::string, std::string_view>,
std::pair<std::string, const char[1]>,
std::pair<std::string_view, std::string>,
std::pair<std::string_view, std::string_view>,
std::pair<std::string_view, const char[1]>,
std::pair<const char[1], std::string>,
std::pair<const char[1], std::string_view>,
std::pair<const char[1], const char[1]>
>;
INSTANTIATE_TYPED_TEST_SUITE_P(EmptyStringTypePairs, MiscStringUtilsCiEqualEmptyTest, EmptyStringTypePairsTypes);
template <class T>
struct MiscStringUtilsCiEqualNotEmptyTest : Test {};
TYPED_TEST_SUITE_P(MiscStringUtilsCiEqualNotEmptyTest);
using RawValue = const char[4];
constexpr RawValue foo = "foo";
constexpr RawValue fooUpper = "FOO";
constexpr RawValue bar = "bar";
template <typename T>
using Value = std::conditional_t<std::is_same_v<T, RawValue>, RawValue&, T>;
TYPED_TEST_P(MiscStringUtilsCiEqualNotEmptyTest, same_strings_should_be_equal)
{
const Value<typename TypeParam::first_type> a {foo};
const Value<typename TypeParam::second_type> b {foo};
EXPECT_TRUE(StringUtils::ciEqual(a, b)) << a << "\n" << b;
}
TYPED_TEST_P(MiscStringUtilsCiEqualNotEmptyTest, same_strings_with_different_case_sensetivity_should_be_equal)
{
const Value<typename TypeParam::first_type> a {foo};
const Value<typename TypeParam::second_type> b {fooUpper};
EXPECT_TRUE(StringUtils::ciEqual(a, b)) << a << "\n" << b;
}
TYPED_TEST_P(MiscStringUtilsCiEqualNotEmptyTest, different_strings_content_should_not_be_equal)
{
const Value<typename TypeParam::first_type> a {foo};
const Value<typename TypeParam::second_type> b {bar};
EXPECT_FALSE(StringUtils::ciEqual(a, b)) << a << "\n" << b;
}
REGISTER_TYPED_TEST_SUITE_P(MiscStringUtilsCiEqualNotEmptyTest,
same_strings_should_be_equal,
same_strings_with_different_case_sensetivity_should_be_equal,
different_strings_content_should_not_be_equal
);
using NotEmptyStringTypePairsTypes = Types<
std::pair<std::string, std::string>,
std::pair<std::string, std::string_view>,
std::pair<std::string, const char[4]>,
std::pair<std::string_view, std::string>,
std::pair<std::string_view, std::string_view>,
std::pair<std::string_view, const char[4]>,
std::pair<const char[4], std::string>,
std::pair<const char[4], std::string_view>,
std::pair<const char[4], const char[4]>
>;
INSTANTIATE_TYPED_TEST_SUITE_P(NotEmptyStringTypePairs, MiscStringUtilsCiEqualNotEmptyTest, NotEmptyStringTypePairsTypes);
TEST(MiscStringUtilsCiEqualTest, string_with_different_length_should_not_be_equal)
{
EXPECT_FALSE(StringUtils::ciEqual(std::string("a"), std::string("aa")));
}
}

@ -1,4 +1,3 @@
set(WIZARD
componentselectionpage.cpp
conclusionpage.cpp
@ -34,21 +33,6 @@ set(WIZARD_HEADER
utils/componentlistwidget.hpp
)
# Headers that must be pre-processed
set(WIZARD_HEADER_MOC
componentselectionpage.hpp
conclusionpage.hpp
existinginstallationpage.hpp
importpage.hpp
installationtargetpage.hpp
intropage.hpp
languageselectionpage.hpp
mainwizard.hpp
methodselectionpage.hpp
utils/componentlistwidget.hpp
)
set(WIZARD_UI
${CMAKE_SOURCE_DIR}/files/ui/wizard/componentselectionpage.ui
${CMAKE_SOURCE_DIR}/files/ui/wizard/conclusionpage.ui
@ -63,7 +47,6 @@ set(WIZARD_UI
if (OPENMW_USE_UNSHIELD)
set (WIZARD ${WIZARD} installationpage.cpp unshield/unshieldworker.cpp)
set (WIZARD_HEADER ${WIZARD_HEADER} installationpage.hpp unshield/unshieldworker.hpp)
set (WIZARD_HEADER_MOC ${WIZARD_HEADER_MOC} installationpage.hpp unshield/unshieldworker.hpp)
set (WIZARD_UI ${WIZARD_UI} ${CMAKE_SOURCE_DIR}/files/ui/wizard/installationpage.ui)
add_definitions(-DOPENMW_USE_UNSHIELD)
endif (OPENMW_USE_UNSHIELD)
@ -80,7 +63,6 @@ if(WIN32)
endif(WIN32)
QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/wizard/wizard.qrc)
QT5_WRAP_CPP(MOC_SRCS ${WIZARD_HEADER_MOC})
QT5_WRAP_UI(UI_HDRS ${WIZARD_UI})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
@ -94,7 +76,6 @@ openmw_add_executable(openmw-wizard
${WIZARD}
${WIZARD_HEADER}
${RCC_SRCS}
${MOC_SRCS}
${UI_HDRS}
)
@ -125,3 +106,7 @@ endif()
if (WIN32)
INSTALL(TARGETS openmw-wizard RUNTIME DESTINATION ".")
endif(WIN32)
if(USE_QT)
set_property(TARGET openmw-wizard PROPERTY AUTOMOC ON)
endif(USE_QT)

@ -80,10 +80,6 @@ foreach (f ${ALL})
list (APPEND files "${f}")
list (APPEND COMPONENT_QT_FILES "${f}")
endforeach (f)
file (GLOB MOC_H "${dir}/${u}.hpp")
foreach (fi ${MOC_H})
list (APPEND COMPONENT_MOC_FILES "${fi}")
endforeach (fi)
endforeach (u)
source_group ("components\\${dir}" FILES ${files})
endmacro (add_component_qt_dir)
@ -97,44 +93,21 @@ add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp")
add_file (${project} _SRC ${comp} "${dir}/${unit}.cpp")
endmacro (add_unit)
macro (add_qt_unit project dir unit)
add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp")
add_file (${project} _HDR_QT ${comp} "${dir}/${unit}.hpp")
add_file (${project} _SRC ${comp} "${dir}/${unit}.cpp")
endmacro (add_qt_unit)
macro (add_hdr project dir unit)
add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp")
endmacro (add_hdr)
macro (add_qt_hdr project dir unit)
add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp")
add_file (${project} _HDR_QT ${comp} "${dir}/${unit}.hpp")
endmacro (add_qt_hdr)
macro (opencs_units dir)
foreach (u ${ARGN})
add_qt_unit (OPENCS ${dir} ${u})
endforeach (u)
endmacro (opencs_units)
macro (opencs_units_noqt dir)
foreach (u ${ARGN})
add_unit (OPENCS ${dir} ${u})
endforeach (u)
endmacro (opencs_units_noqt)
endmacro (opencs_units)
macro (opencs_hdrs dir)
foreach (u ${ARGN})
add_qt_hdr (OPENCS ${dir} ${u})
endforeach (u)
endmacro (opencs_hdrs)
macro (opencs_hdrs_noqt dir)
foreach (u ${ARGN})
add_hdr (OPENCS ${dir} ${u})
endforeach (u)
endmacro (opencs_hdrs_noqt)
endmacro (opencs_hdrs)
include(CMakeParseArguments)

@ -197,6 +197,10 @@ add_component_dir(detournavigator
navmeshcacheitem
)
add_component_dir(loadinglistener
reporter
)
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
)
@ -222,7 +226,6 @@ if (USE_QT)
)
QT5_WRAP_UI(ESM_UI_HDR ${ESM_UI})
QT5_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES})
endif()
if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
@ -276,7 +279,7 @@ if (WIN32)
endif()
if (USE_QT)
add_library(components_qt STATIC ${COMPONENT_QT_FILES} ${MOC_SRCS} ${ESM_UI_HDR})
add_library(components_qt STATIC ${COMPONENT_QT_FILES} ${ESM_UI_HDR})
target_link_libraries(components_qt components Qt5::Widgets Qt5::Core)
target_compile_definitions(components_qt PRIVATE OPENMW_DOC_BASEURL="${OPENMW_DOC_BASEURL}")
endif()
@ -285,6 +288,15 @@ if (GIT_CHECKOUT)
add_dependencies (components git-version)
endif (GIT_CHECKOUT)
if (OSG_STATIC AND CMAKE_SYSTEM_NAME MATCHES "Linux")
find_package(X11 REQUIRED COMPONENTS Xinerama Xrandr)
target_link_libraries(components ${CMAKE_DL_LIBS} X11::X11 X11::Xinerama X11::Xrandr)
find_package(Fontconfig MODULE)
if(Fontconfig_FOUND)
target_link_libraries(components Fontconfig::Fontconfig)
endif()
endif()
if (WIN32)
target_link_libraries(components shlwapi)
endif()
@ -304,3 +316,35 @@ endif()
set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE)
target_compile_definitions(components PUBLIC BT_USE_DOUBLE_PRECISION)
if(OSG_STATIC)
unset(_osg_plugins_static_files)
add_library(components_osg_plugins INTERFACE)
foreach(_plugin ${USED_OSG_PLUGINS})
string(TOUPPER ${_plugin} _plugin_uc)
if(OPENMW_USE_SYSTEM_OSG)
list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY})
else()
list(APPEND _osg_plugins_static_files $<TARGET_FILE:${${_plugin_uc}_LIBRARY}>)
target_link_libraries(components_osg_plugins INTERFACE $<TARGET_PROPERTY:${${_plugin_uc}_LIBRARY},LINK_LIBRARIES>)
add_dependencies(components_osg_plugins ${${_plugin_uc}_LIBRARY})
endif()
endforeach()
# We use --whole-archive because OSG plugins use registration.
get_whole_archive_options(_opts ${_osg_plugins_static_files})
target_link_options(components_osg_plugins INTERFACE ${_opts})
target_link_libraries(components components_osg_plugins)
if(OPENMW_USE_SYSTEM_OSG)
# OSG plugin pkgconfig files are missing these dependencies.
# https://github.com/openscenegraph/OpenSceneGraph/issues/1052
find_package(Freetype REQUIRED)
find_package(JPEG REQUIRED)
find_package(PNG REQUIRED)
target_link_libraries(components Freetype::Freetype JPEG::JPEG PNG::PNG)
endif()
endif(OSG_STATIC)
if(USE_QT)
set_property(TARGET components_qt PROPERTY AUTOMOC ON)
endif(USE_QT)

@ -27,7 +27,7 @@ namespace DetourNavigator
std::optional<RemovedRecastMeshObject> CachedRecastMeshManager::removeObject(const ObjectId id)
{
const auto object = mImpl.removeObject(id);
auto object = mImpl.removeObject(id);
if (object)
mCached.lock()->reset();
return object;

@ -248,7 +248,7 @@ namespace DetourNavigator
const auto tile = tiles.find(tilePosition);
if (tile == tiles.end())
return std::optional<RemovedRecastMeshObject>();
const auto tileResult = tile->second->removeObject(id);
auto tileResult = tile->second->removeObject(id);
if (tile->second->isEmpty())
{
tiles.erase(tile);

@ -17,6 +17,7 @@
#include <components/vfs/manager.hpp>
#include <components/misc/pathhelpers.hpp>
#include <components/misc/stringops.hpp>
#include <components/myguiplatform/myguitexture.hpp>
@ -191,24 +192,10 @@ namespace Gui
void FontLoader::loadBitmapFonts(bool exportToFile)
{
const std::map<std::string, VFS::File*>& index = mVFS->getIndex();
std::string pattern = "Fonts/";
mVFS->normalizeFilename(pattern);
std::map<std::string, VFS::File*>::const_iterator found = index.lower_bound(pattern);
while (found != index.end())
for (const auto& name : mVFS->getRecursiveDirectoryIterator("Fonts/"))
{
const std::string& name = found->first;
if (name.size() >= pattern.size() && name.substr(0, pattern.size()) == pattern)
{
size_t pos = name.find_last_of('.');
if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".fnt") == 0)
loadBitmapFont(name, exportToFile);
}
else
break;
++found;
if (Misc::getFileExtension(name) == "fnt")
loadBitmapFont(name, exportToFile);
}
}

@ -0,0 +1,41 @@
#include "reporter.hpp"
#include "loadinglistener.hpp"
#include <condition_variable>
#include <cstddef>
#include <mutex>
namespace Loading
{
void Reporter::addTotal(std::size_t value)
{
const std::lock_guard lock(mMutex);
mTotal += value;
mUpdated.notify_all();
}
void Reporter::addProgress(std::size_t value)
{
const std::lock_guard lock(mMutex);
mProgress += value;
mUpdated.notify_all();
}
void Reporter::complete()
{
const std::lock_guard lock(mMutex);
mDone = true;
mUpdated.notify_all();
}
void Reporter::wait(Listener& listener) const
{
std::unique_lock lock(mMutex);
while (!mDone)
{
listener.setProgressRange(mTotal);
listener.setProgress(mProgress);
mUpdated.wait(lock);
}
}
}

@ -0,0 +1,32 @@
#ifndef COMPONENTS_LOADINGLISTENER_REPORTER_H
#define COMPONENTS_LOADINGLISTENER_REPORTER_H
#include <condition_variable>
#include <cstddef>
#include <mutex>
namespace Loading
{
class Listener;
class Reporter
{
public:
void addTotal(std::size_t value);
void addProgress(std::size_t value);
void complete();
void wait(Listener& listener) const;
private:
std::size_t mProgress = 0;
std::size_t mTotal = 0;
bool mDone = false;
mutable std::mutex mMutex;
mutable std::condition_variable mUpdated;
};
}
#endif

@ -167,4 +167,14 @@ namespace LuaUtil
#endif
}
std::string toString(const sol::object& obj)
{
if (obj == sol::nil)
return "nil";
else if (obj.get_type() == sol::type::string)
return "\"" + obj.as<std::string>() + "\"";
else
return call(sol::state_view(obj.lua_state())["tostring"], obj);
}
}

@ -103,6 +103,9 @@ namespace LuaUtil
return getFieldOrNil(table.as<sol::table>()[first], str...);
}
// String representation of a Lua object. Should be used for debugging/logging purposes only.
std::string toString(const sol::object&);
}
#endif // COMPONENTS_LUA_LUASTATE_H

@ -0,0 +1,19 @@
#ifndef OPENMW_COMPONENTS_MISC_PATHHELPERS_H
#define OPENMW_COMPONENTS_MISC_PATHHELPERS_H
#include <string>
namespace Misc
{
inline std::string_view getFileExtension(std::string_view file)
{
if (auto extPos = file.find_last_of('.'); extPos != std::string::npos)
{
file.remove_prefix(extPos + 1);
return file;
}
return {};
}
}
#endif

@ -4,6 +4,8 @@
#include <cctype>
#include <string>
#include <algorithm>
#include <string_view>
#include <iterator>
#include "utf8stream.hpp"
@ -109,18 +111,34 @@ public:
return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), ci());
}
static bool ciEqual(const std::string &x, const std::string &y) {
if (x.size() != y.size()) {
template <class X, class Y>
static bool ciEqual(const X& x, const Y& y)
{
if (std::size(x) != std::size(y))
return false;
}
std::string::const_iterator xit = x.begin();
std::string::const_iterator yit = y.begin();
for (; xit != x.end(); ++xit, ++yit) {
if (toLower(*xit) != toLower(*yit)) {
return false;
}
}
return true;
return std::equal(std::begin(x), std::end(x), std::begin(y),
[] (char l, char r) { return toLower(l) == toLower(r); });
}
template <std::size_t n>
static auto ciEqual(const char(& x)[n], const char(& y)[n])
{
static_assert(n > 0);
return ciEqual(std::string_view(x, n - 1), std::string_view(y, n - 1));
}
template <std::size_t n, class T>
static auto ciEqual(const char(& x)[n], const T& y)
{
static_assert(n > 0);
return ciEqual(std::string_view(x, n - 1), y);
}
template <std::size_t n, class T>
static auto ciEqual(const T& x, const char(& y)[n])
{
static_assert(n > 0);
return ciEqual(x, std::string_view(y, n - 1));
}
static int ciCompareLen(const std::string &x, const std::string &y, size_t len)
@ -157,9 +175,9 @@ public:
}
/// Returns lower case copy of input string
static std::string lowerCase(const std::string &in)
static std::string lowerCase(std::string_view in)
{
std::string out = in;
std::string out(in);
lowerCaseInPlace(out);
return out;
}

@ -58,7 +58,7 @@ btCollisionShape* BulletShape::duplicateCollisionShape(const btCollisionShape *s
for(int i = 0;i < numShapes;++i)
{
btCollisionShape *child = duplicateCollisionShape(comp->getChildShape(i));
btTransform trans = comp->getChildTransform(i);
const btTransform& trans = comp->getChildTransform(i);
newShape->addChildShape(trans, child);
}

@ -8,6 +8,7 @@
#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
#include <components/misc/pathhelpers.hpp>
#include <components/sceneutil/visitor.hpp>
#include <components/vfs/manager.hpp>
@ -121,8 +122,7 @@ BulletShapeManager::~BulletShapeManager()
osg::ref_ptr<const BulletShape> BulletShapeManager::getShape(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
const std::string normalized = mVFS->normalizeFilename(name);
osg::ref_ptr<BulletShape> shape;
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
@ -130,12 +130,7 @@ osg::ref_ptr<const BulletShape> BulletShapeManager::getShape(const std::string &
shape = osg::ref_ptr<BulletShape>(static_cast<BulletShape*>(obj.get()));
else
{
size_t extPos = normalized.find_last_of('.');
std::string ext;
if (extPos != std::string::npos && extPos+1 < normalized.size())
ext = normalized.substr(extPos+1);
if (ext == "nif")
if (Misc::getFileExtension(normalized) == "nif")
{
NifBullet::BulletNifLoader loader;
shape = loader.load(*mNifFileManager->get(normalized));
@ -180,8 +175,7 @@ osg::ref_ptr<const BulletShape> BulletShapeManager::getShape(const std::string &
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::cacheInstance(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
const std::string normalized = mVFS->normalizeFilename(name);
osg::ref_ptr<BulletShapeInstance> instance = createInstance(normalized);
if (instance)
@ -191,8 +185,7 @@ osg::ref_ptr<BulletShapeInstance> BulletShapeManager::cacheInstance(const std::s
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::getInstance(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
const std::string normalized = mVFS->normalizeFilename(name);
osg::ref_ptr<osg::Object> obj = mInstanceCache->takeFromObjectCache(normalized);
if (obj.get())

@ -4,6 +4,7 @@
#include <osgDB/Registry>
#include <components/debug/debuglog.hpp>
#include <components/misc/pathhelpers.hpp>
#include <components/vfs/manager.hpp>
#include "objectcache.hpp"
@ -83,8 +84,7 @@ namespace Resource
osg::ref_ptr<osg::Image> ImageManager::getImage(const std::string &filename)
{
std::string normalized = filename;
mVFS->normalizeFilename(normalized);
const std::string normalized = mVFS->normalizeFilename(filename);
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
if (obj)
@ -103,10 +103,7 @@ namespace Resource
return mWarningImage;
}
size_t extPos = normalized.find_last_of('.');
std::string ext;
if (extPos != std::string::npos && extPos+1 < normalized.size())
ext = normalized.substr(extPos+1);
const std::string ext(Misc::getFileExtension(normalized));
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext);
if (!reader)
{

@ -9,6 +9,7 @@
#include <components/nifosg/nifloader.hpp>
#include <components/sceneutil/keyframe.hpp>
#include <components/sceneutil/osgacontroller.hpp>
#include <components/misc/pathhelpers.hpp>
#include <components/misc/stringops.hpp>
#include "animation.hpp"
@ -30,7 +31,7 @@ namespace Resource
std::vector<SceneUtil::EmulatedAnimation> emulatedAnimations;
for (auto animation : mAnimationManager->getAnimationList())
for (const auto& animation : mAnimationManager->getAnimationList())
{
if (animation)
{
@ -133,8 +134,7 @@ namespace Resource
osg::ref_ptr<const SceneUtil::KeyframeHolder> KeyframeManager::get(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
const std::string normalized = mVFS->normalizeFilename(name);
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
if (obj)
@ -142,8 +142,7 @@ namespace Resource
else
{
osg::ref_ptr<SceneUtil::KeyframeHolder> loaded (new SceneUtil::KeyframeHolder);
std::string ext = Resource::getFileExtension(normalized);
if (ext == "kf")
if (Misc::getFileExtension(normalized) == "kf")
{
NifOsg::Loader::loadKf(Nif::NIFFilePtr(new Nif::NIFFile(mVFS->getNormalized(normalized), normalized)), *loaded.get());
}

@ -59,6 +59,7 @@ namespace Resource
/// How long to keep objects in cache after no longer being referenced.
void setExpiryDelay (double expiryDelay) override { mExpiryDelay = expiryDelay; }
float getExpiryDelay() const { return mExpiryDelay; }
const VFS::Manager* getVFS() const { return mVFS; }

@ -17,6 +17,7 @@
#include <components/nifosg/nifloader.hpp>
#include <components/nif/niffile.hpp>
#include <components/misc/pathhelpers.hpp>
#include <components/misc/stringops.hpp>
#include <components/vfs/manager.hpp>
@ -358,10 +359,7 @@ namespace Resource
bool SceneManager::checkLoaded(const std::string &name, double timeStamp)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
return mCache->checkInObjectCache(normalized, timeStamp);
return mCache->checkInObjectCache(mVFS->normalizeFilename(name), timeStamp);
}
/// @brief Callback to read image files from the VFS.
@ -391,12 +389,12 @@ namespace Resource
osg::ref_ptr<osg::Node> load (const std::string& normalizedFilename, const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager)
{
std::string ext = Resource::getFileExtension(normalizedFilename);
auto ext = Misc::getFileExtension(normalizedFilename);
if (ext == "nif")
return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), imageManager);
else
{
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext);
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(std::string(ext));
if (!reader)
{
std::stringstream errormsg;
@ -533,8 +531,7 @@ namespace Resource
osg::ref_ptr<const osg::Node> SceneManager::getTemplate(const std::string &name, bool compile)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
std::string normalized = mVFS->normalizeFilename(name);
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
if (obj)
@ -603,8 +600,7 @@ namespace Resource
osg::ref_ptr<osg::Node> SceneManager::cacheInstance(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
const std::string normalized = mVFS->normalizeFilename(name);
osg::ref_ptr<osg::Node> node = createInstance(normalized);
@ -642,8 +638,7 @@ namespace Resource
osg::ref_ptr<osg::Node> SceneManager::getInstance(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
const std::string normalized = mVFS->normalizeFilename(name);
osg::ref_ptr<osg::Object> obj = mInstanceCache->takeFromObjectCache(normalized);
if (obj.get())
@ -822,12 +817,4 @@ namespace Resource
shaderVisitor->setTranslucentFramebuffer(translucentFramebuffer);
return shaderVisitor;
}
std::string getFileExtension(const std::string& file)
{
size_t extPos = file.find_last_of('.');
if (extPos != std::string::npos && extPos+1 < file.size())
return file.substr(extPos+1);
return std::string();
}
}

@ -15,6 +15,7 @@
#include <components/sceneutil/skeleton.hpp>
#include "visitor.hpp"
#include "clone.hpp"
namespace SceneUtil
{
@ -49,10 +50,10 @@ namespace SceneUtil
return;
osg::Node* node = &drawable;
while (node->getNumParents())
for (auto it = getNodePath().rbegin()+1; it != getNodePath().rend(); ++it)
{
osg::Group* parent = node->getParent(0);
if (!parent || !filterMatches(parent->getName()))
osg::Node* parent = *it;
if (!filterMatches(parent->getName()))
break;
node = parent;
}
@ -63,12 +64,7 @@ namespace SceneUtil
{
for (const osg::ref_ptr<osg::Node>& node : mToCopy)
{
if (node->getNumParents() > 1)
Log(Debug::Error) << "Error CopyRigVisitor: node has " << node->getNumParents() << " parents";
while (node->getNumParents())
node->getParent(0)->removeChild(node);
mParent->addChild(node);
mParent->addChild(static_cast<osg::Node*>(node->clone(SceneUtil::CopyOp())));
}
mToCopy.clear();
}
@ -90,25 +86,28 @@ namespace SceneUtil
std::string mFilter2;
};
void mergeUserData(osg::UserDataContainer* source, osg::Object* target)
void mergeUserData(const osg::UserDataContainer* source, osg::Object* target)
{
if (!source)
return;
if (!target->getUserDataContainer())
target->setUserDataContainer(source);
target->setUserDataContainer(osg::clone(source, osg::CopyOp::SHALLOW_COPY));
else
{
for (unsigned int i=0; i<source->getNumUserObjects(); ++i)
target->getUserDataContainer()->addUserObject(source->getUserObject(i));
target->getUserDataContainer()->addUserObject(osg::clone(source->getUserObject(i), osg::CopyOp::SHALLOW_COPY));
}
}
osg::ref_ptr<osg::Node> attach(osg::ref_ptr<osg::Node> toAttach, osg::Node *master, const std::string &filter, osg::Group* attachNode)
osg::ref_ptr<osg::Node> attach(osg::ref_ptr<const osg::Node> toAttach, osg::Node *master, const std::string &filter, osg::Group* attachNode)
{
if (dynamic_cast<SceneUtil::Skeleton*>(toAttach.get()))
if (dynamic_cast<const SceneUtil::Skeleton*>(toAttach.get()))
{
osg::ref_ptr<osg::Group> handle = new osg::Group;
CopyRigVisitor copyVisitor(handle, filter);
toAttach->accept(copyVisitor);
const_cast<osg::Node*>(toAttach.get())->accept(copyVisitor);
copyVisitor.doCopy();
if (handle->getNumChildren() == 1)
@ -122,14 +121,16 @@ namespace SceneUtil
else
{
master->asGroup()->addChild(handle);
handle->setUserDataContainer(toAttach->getUserDataContainer());
mergeUserData(toAttach->getUserDataContainer(), handle);
return handle;
}
}
else
{
osg::ref_ptr<osg::Node> clonedToAttach = static_cast<osg::Node*>(toAttach->clone(SceneUtil::CopyOp()));
FindByNameVisitor findBoneOffset("BoneOffset");
toAttach->accept(findBoneOffset);
clonedToAttach->accept(findBoneOffset);
osg::ref_ptr<osg::PositionAttitudeTransform> trans;
@ -172,13 +173,13 @@ namespace SceneUtil
if (trans)
{
attachNode->addChild(trans);
trans->addChild(toAttach);
trans->addChild(clonedToAttach);
return trans;
}
else
{
attachNode->addChild(toAttach);
return toAttach;
attachNode->addChild(clonedToAttach);
return clonedToAttach;
}
}
}

@ -14,12 +14,12 @@ namespace osg
namespace SceneUtil
{
/// Attach parts of the \a toAttach scenegraph to the \a master scenegraph, using the specified filter and attachment node.
/// Clone and attach parts of the \a toAttach scenegraph to the \a master scenegraph, using the specified filter and attachment node.
/// If the \a toAttach scene graph contains skinned objects, we will attach only those (filtered by the \a filter).
/// Otherwise, just attach all of the toAttach scenegraph to the attachment node on the master scenegraph, with no filtering.
/// @note The master scene graph is expected to include a skeleton.
/// @return A newly created node that is directly attached to the master scene graph
osg::ref_ptr<osg::Node> attach(osg::ref_ptr<osg::Node> toAttach, osg::Node* master, const std::string& filter, osg::Group* attachNode);
osg::ref_ptr<osg::Node> attach(osg::ref_ptr<const osg::Node> toAttach, osg::Node* master, const std::string& filter, osg::Group* attachNode);
}

@ -2,8 +2,6 @@
#include <osg/StateSet>
#include <osgAnimation/Bone>
#include <osgAnimation/Skeleton>
#include <osgAnimation/MorphGeometry>
#include <osgAnimation/RigGeometry>
@ -35,11 +33,6 @@ namespace SceneUtil
mUpdaterToOldPs[cloned] = updater->getParticleSystem(0);
return cloned;
}
if (dynamic_cast<const osgAnimation::Bone*>(node) || dynamic_cast<const osgAnimation::Skeleton*>(node))
{
return osg::clone(node, *this);
}
return osg::CopyOp::operator()(node);
}

@ -54,6 +54,7 @@ ShadowsBin::ShadowsBin()
mShaderAlphaTestStateSet = new osg::StateSet;
mShaderAlphaTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", true));
mShaderAlphaTestStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true));
mShaderAlphaTestStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE);
for (size_t i = 0; i < sCastingPrograms.size(); ++i)

@ -9,6 +9,9 @@
#include <components/misc/stringops.hpp>
#include <cstring>
#include <string_view>
namespace SceneUtil
{
@ -24,7 +27,7 @@ namespace SceneUtil
void FindByClassVisitor::apply(osg::Node &node)
{
if (Misc::StringUtils::ciEqual(node.className(), mNameToFind))
if (Misc::StringUtils::ciEqual(std::string_view(node.className()), mNameToFind))
mFoundNodes.push_back(&node);
traverse(node);
@ -32,13 +35,13 @@ namespace SceneUtil
void FindByNameVisitor::apply(osg::Group &group)
{
if (!checkGroup(group))
if (!mFoundNode && !checkGroup(group))
traverse(group);
}
void FindByNameVisitor::apply(osg::MatrixTransform &node)
{
if (!checkGroup(node))
if (!mFoundNode && !checkGroup(node))
traverse(node);
}

@ -371,11 +371,10 @@ namespace Shader
void ShaderManager::setGlobalDefines(DefineMap & globalDefines)
{
mGlobalDefines = globalDefines;
for (auto shaderMapElement: mShaders)
for (const auto& [key, shader]: mShaders)
{
std::string templateId = shaderMapElement.first.first;
ShaderManager::DefineMap defines = shaderMapElement.first.second;
osg::ref_ptr<osg::Shader> shader = shaderMapElement.second;
std::string templateId = key.first;
ShaderManager::DefineMap defines = key.second;
if (shader == nullptr)
// I'm not sure how to handle a shader that was already broken as there's no way to get a potential replacement to the nodes that need it.
continue;
@ -391,13 +390,13 @@ namespace Shader
void ShaderManager::releaseGLObjects(osg::State *state)
{
std::lock_guard<std::mutex> lock(mMutex);
for (auto shader : mShaders)
for (const auto& [_, shader] : mShaders)
{
if (shader.second != nullptr)
shader.second->releaseGLObjects(state);
if (shader != nullptr)
shader->releaseGLObjects(state);
}
for (auto program : mPrograms)
program.second->releaseGLObjects(state);
for (const auto& [_, program] : mPrograms)
program->releaseGLObjects(state);
}
}

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

Loading…
Cancel
Save