From ce2bcba5d41631a63ae24d0491578f0ddf187cd2 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 16 Dec 2020 23:44:15 +0000 Subject: [PATCH 001/132] Replace deprecated alpha test in shader visitor --- components/CMakeLists.txt | 2 +- components/sceneutil/mwshadowtechnique.cpp | 19 ++- components/sceneutil/mwshadowtechnique.hpp | 4 +- components/sceneutil/shadowsbin.cpp | 57 ++++++- components/sceneutil/shadowsbin.hpp | 27 ++-- components/shader/removedalphafunc.cpp | 27 ++++ components/shader/removedalphafunc.hpp | 40 +++++ components/shader/shadervisitor.cpp | 172 +++++++++++++++++---- components/shader/shadervisitor.hpp | 4 + files/shaders/CMakeLists.txt | 1 + files/shaders/alpha.glsl | 38 +++++ files/shaders/objects_fragment.glsl | 4 + files/shaders/shadowcasting_fragment.glsl | 7 +- 13 files changed, 342 insertions(+), 60 deletions(-) create mode 100644 components/shader/removedalphafunc.cpp create mode 100644 components/shader/removedalphafunc.hpp create mode 100644 files/shaders/alpha.glsl diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 3c1899037..45864a6e8 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -46,7 +46,7 @@ add_component_dir (resource ) add_component_dir (shader - shadermanager shadervisitor + shadermanager shadervisitor removedalphafunc ) add_component_dir (sceneutil diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index c49a14777..31267f75c 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -278,7 +278,7 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) static osg::ref_ptr ss; if (!ss) { - ShadowsBinAdder adder("ShadowsBin"); + ShadowsBinAdder adder("ShadowsBin", _vdsm->getCastingPrograms()); ss = new osg::StateSet; ss->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "ShadowsBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS); } @@ -882,11 +882,15 @@ void SceneUtil::MWShadowTechnique::disableFrontFaceCulling() void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & shaderManager) { // This can't be part of the constructor as OSG mandates that there be a trivial constructor available - - _castingProgram = new osg::Program(); - _castingProgram->addShader(shaderManager.getShader("shadowcasting_vertex.glsl", Shader::ShaderManager::DefineMap(), osg::Shader::VERTEX)); - _castingProgram->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", Shader::ShaderManager::DefineMap(), osg::Shader::FRAGMENT)); + osg::ref_ptr castingVertexShader = shaderManager.getShader("shadowcasting_vertex.glsl", {}, osg::Shader::VERTEX); + for (int alphaFunc = GL_NEVER; alphaFunc <= GL_ALWAYS; ++alphaFunc) + { + auto& program = _castingPrograms[alphaFunc - GL_NEVER]; + program = new osg::Program(); + program->addShader(castingVertexShader); + program->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", { {"alphaFunc", std::to_string(alphaFunc)} }, osg::Shader::FRAGMENT)); + } } MWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData(osgUtil::CullVisitor* /*cv*/) @@ -1604,10 +1608,11 @@ void MWShadowTechnique::createShaders() } - if (!_castingProgram) + if (!_castingPrograms[GL_ALWAYS - GL_NEVER]) OSG_NOTICE << "Shadow casting shader has not been set up. Remember to call setupCastingShader(Shader::ShaderManager &)" << std::endl; - _shadowCastingStateSet->setAttributeAndModes(_castingProgram, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + // Always use the GL_ALWAYS shader as the shadows bin will change it if necessary + _shadowCastingStateSet->setAttributeAndModes(_castingPrograms[GL_ALWAYS - GL_NEVER], osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); // The casting program uses a sampler, so to avoid undefined behaviour, we must bind a dummy texture in case no other is supplied _shadowCastingStateSet->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON); _shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true)); diff --git a/components/sceneutil/mwshadowtechnique.hpp b/components/sceneutil/mwshadowtechnique.hpp index 5125247dd..96b1c7d0e 100644 --- a/components/sceneutil/mwshadowtechnique.hpp +++ b/components/sceneutil/mwshadowtechnique.hpp @@ -215,6 +215,8 @@ namespace SceneUtil { virtual void createShaders(); + virtual std::array, GL_ALWAYS - GL_NEVER> getCastingPrograms() const { return _castingPrograms; } + virtual bool selectActiveLights(osgUtil::CullVisitor* cv, ViewDependentData* vdd) const; virtual osg::Polytope computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight); @@ -288,7 +290,7 @@ namespace SceneUtil { }; osg::ref_ptr _debugHud; - osg::ref_ptr _castingProgram; + std::array, GL_ALWAYS - GL_NEVER> _castingPrograms; }; } diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp index 520ad0362..9e6d461ae 100644 --- a/components/sceneutil/shadowsbin.cpp +++ b/components/sceneutil/shadowsbin.cpp @@ -1,7 +1,9 @@ #include "shadowsbin.hpp" #include #include +#include #include +#include #include using namespace osgUtil; @@ -40,6 +42,10 @@ namespace namespace SceneUtil { +std::array, GL_ALWAYS - GL_NEVER> ShadowsBin::sCastingPrograms = { + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr +}; + ShadowsBin::ShadowsBin() { mNoTestStateSet = new osg::StateSet; @@ -49,6 +55,12 @@ ShadowsBin::ShadowsBin() mShaderAlphaTestStateSet = new osg::StateSet; mShaderAlphaTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", true)); mShaderAlphaTestStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); + + for (size_t i = 0; i < sCastingPrograms.size(); ++i) + { + mAlphaFuncShaders[i] = new osg::StateSet; + mAlphaFuncShaders[i]->setAttribute(sCastingPrograms[i], osg::StateAttribute::ON | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE); + } } StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set& uninterestingCache) @@ -71,7 +83,6 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un continue; accumulateModeState(ss, state.mAlphaBlend, state.mAlphaBlendOverride, GL_BLEND); - accumulateModeState(ss, state.mAlphaTest, state.mAlphaTestOverride, GL_ALPHA_TEST); const osg::StateSet::AttributeList& attributes = ss->getAttributeList(); osg::StateSet::AttributeList::const_iterator found = attributes.find(std::make_pair(osg::StateAttribute::MATERIAL, 0)); @@ -83,6 +94,14 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un state.mMaterial = nullptr; } + found = attributes.find(std::make_pair(osg::StateAttribute::ALPHAFUNC, 0)); + if (found != attributes.end()) + { + // As force shaders is on, we know this is really a RemovedAlphaFunc + const osg::StateSet::RefAttributePair& rap = found->second; + accumulateState(state.mAlphaFunc, static_cast(rap.first.get()), state.mAlphaFuncOverride, rap.second); + } + // osg::FrontFace specifies triangle winding, not front-face culling. We can't safely reparent anything under it. found = attributes.find(std::make_pair(osg::StateAttribute::FRONTFACE, 0)); if (found != attributes.end()) @@ -113,16 +132,44 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un leaf->_parent = sg_new; sg_new->_leaves.push_back(leaf); } - return sg_new; + sg = sg_new; + } + + // GL_ALWAYS is set by default by mwshadowtechnique + if (state.mAlphaFunc && state.mAlphaFunc->getFunction() != GL_ALWAYS) + { + sg_new = sg->find_or_insert(mAlphaFuncShaders[state.mAlphaFunc->getFunction() - GL_NEVER]); + for (RenderLeaf* leaf : sg->_leaves) + { + leaf->_parent = sg_new; + sg_new->_leaves.push_back(leaf); + } + sg = sg_new; } + return sg; } +void ShadowsBin::addPrototype(const std::string & name, const std::array, GL_ALWAYS - GL_NEVER>& castingPrograms) +{ + sCastingPrograms = castingPrograms; + osg::ref_ptr bin(new ShadowsBin); + osgUtil::RenderBin::addRenderBinPrototype(name, bin); +} + +inline bool ShadowsBin::State::needTexture() const +{ + return mAlphaBlend || (mAlphaFunc && mAlphaFunc->getFunction() != GL_ALWAYS); +} + bool ShadowsBin::State::needShadows() const { - if (!mMaterial) - return true; - return materialNeedShadows(mMaterial); + if (mAlphaFunc && mAlphaFunc->getFunction() == GL_NEVER) + return false; + // other alpha func + material combinations might be skippable + if (mAlphaBlend && mMaterial) + return materialNeedShadows(mMaterial); + return true; } void ShadowsBin::sortImplementation() diff --git a/components/sceneutil/shadowsbin.hpp b/components/sceneutil/shadowsbin.hpp index cc6fd3525..7247a9af4 100644 --- a/components/sceneutil/shadowsbin.hpp +++ b/components/sceneutil/shadowsbin.hpp @@ -1,11 +1,13 @@ #ifndef OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H #define OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H +#include #include #include namespace osg { class Material; + class AlphaFunc; } namespace SceneUtil @@ -15,8 +17,12 @@ namespace SceneUtil class ShadowsBin : public osgUtil::RenderBin { private: + static std::array, GL_ALWAYS - GL_NEVER> sCastingPrograms; + osg::ref_ptr mNoTestStateSet; osg::ref_ptr mShaderAlphaTestStateSet; + + std::array, GL_ALWAYS - GL_NEVER> mAlphaFuncShaders; public: META_Object(SceneUtil, ShadowsBin) ShadowsBin(); @@ -24,6 +30,7 @@ namespace SceneUtil : osgUtil::RenderBin(rhs, copyop) , mNoTestStateSet(rhs.mNoTestStateSet) , mShaderAlphaTestStateSet(rhs.mShaderAlphaTestStateSet) + , mAlphaFuncShaders(rhs.mAlphaFuncShaders) {} void sortImplementation() override; @@ -33,8 +40,8 @@ namespace SceneUtil State() : mAlphaBlend(false) , mAlphaBlendOverride(false) - , mAlphaTest(false) - , mAlphaTestOverride(false) + , mAlphaFunc(nullptr) + , mAlphaFuncOverride(false) , mMaterial(nullptr) , mMaterialOverride(false) , mImportantState(false) @@ -42,33 +49,29 @@ namespace SceneUtil bool mAlphaBlend; bool mAlphaBlendOverride; - bool mAlphaTest; - bool mAlphaTestOverride; + osg::AlphaFunc* mAlphaFunc; + bool mAlphaFuncOverride; osg::Material* mMaterial; bool mMaterialOverride; bool mImportantState; - bool needTexture() const { return mAlphaBlend || mAlphaTest; } + bool needTexture() const; bool needShadows() const; // A state is interesting if there's anything about it that might affect whether we can optimise child state bool interesting() const { - return !needShadows() || needTexture() || mAlphaBlendOverride || mAlphaTestOverride || mMaterialOverride || mImportantState; + return !needShadows() || needTexture() || mAlphaBlendOverride || mAlphaFuncOverride || mMaterialOverride || mImportantState; } }; osgUtil::StateGraph* cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set& uninteresting); - static void addPrototype(const std::string& name) - { - osg::ref_ptr bin (new ShadowsBin); - osgUtil::RenderBin::addRenderBinPrototype(name, bin); - } + static void addPrototype(const std::string& name, const std::array, GL_ALWAYS - GL_NEVER>& castingPrograms); }; class ShadowsBinAdder { public: - ShadowsBinAdder(const std::string& name){ ShadowsBin::addPrototype(name); } + ShadowsBinAdder(const std::string& name, const std::array, GL_ALWAYS - GL_NEVER>& castingPrograms){ ShadowsBin::addPrototype(name, castingPrograms); } }; } diff --git a/components/shader/removedalphafunc.cpp b/components/shader/removedalphafunc.cpp new file mode 100644 index 000000000..15d0fb0f1 --- /dev/null +++ b/components/shader/removedalphafunc.cpp @@ -0,0 +1,27 @@ +#include "removedalphafunc.hpp" + +#include + +#include + +namespace Shader +{ + std::array, GL_ALWAYS - GL_NEVER> RemovedAlphaFunc::sInstances{ + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr + }; + + osg::ref_ptr RemovedAlphaFunc::getInstance(GLenum func) + { + assert(func >= GL_NEVER && func <= GL_ALWAYS); + if (!sInstances[func - GL_NEVER]) + sInstances[func - GL_NEVER] = new RemovedAlphaFunc(static_cast(func), 1.0); + return sInstances[func - GL_NEVER]; + } + + void RemovedAlphaFunc::apply(osg::State & state) const + { + // Hopefully, anything genuinely requiring the default alpha func of GL_ALWAYS explicitly sets it + if (!state.getGlobalDefaultAttribute(ALPHAFUNC)->getType() != getType()) + state.setGlobalDefaultAttribute(static_cast(cloneType())); + } +} diff --git a/components/shader/removedalphafunc.hpp b/components/shader/removedalphafunc.hpp new file mode 100644 index 000000000..a8165b0dc --- /dev/null +++ b/components/shader/removedalphafunc.hpp @@ -0,0 +1,40 @@ +#ifndef OPENMW_COMPONENTS_REMOVEDALPHAFUNC_H +#define OPENMW_COMPONENTS_REMOVEDALPHAFUNC_H + +#include + +#include + +namespace Shader +{ + // State attribute used when shader visitor replaces the deprecated alpha function with a shader + // Prevents redundant glAlphaFunc calls and lets the shadowsbin know the stateset had alpha testing + class RemovedAlphaFunc : public osg::AlphaFunc + { + public: + // Get a singleton-like instance with the right func (but a default threshold) + static osg::ref_ptr getInstance(GLenum func); + + RemovedAlphaFunc() + : osg::AlphaFunc() + {} + + RemovedAlphaFunc(ComparisonFunction func, float ref) + : osg::AlphaFunc(func, ref) + {} + + RemovedAlphaFunc(const RemovedAlphaFunc& raf, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) + : osg::AlphaFunc(raf, copyop) + {} + + META_StateAttribute(Shader, RemovedAlphaFunc, ALPHAFUNC); + + void apply(osg::State& state) const override; + + protected: + virtual ~RemovedAlphaFunc() = default; + + static std::array, GL_ALWAYS - GL_NEVER> sInstances; + }; +} +#endif //OPENMW_COMPONENTS_REMOVEDALPHAFUNC_H diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index e908b6aaa..aeec391c6 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -1,5 +1,6 @@ #include "shadervisitor.hpp" +#include #include #include #include @@ -13,6 +14,7 @@ #include #include +#include "removedalphafunc.hpp" #include "shadermanager.hpp" namespace Shader @@ -22,6 +24,9 @@ namespace Shader : mShaderRequired(false) , mColorMode(0) , mMaterialOverridden(false) + , mAlphaTestOverridden(false) + , mAlphaFunc(GL_ALWAYS) + , mAlphaRef(1.0) , mNormalHeight(false) , mTexStageRequiringTangents(-1) , mNode(nullptr) @@ -76,6 +81,34 @@ namespace Shader return newStateSet.get(); } + osg::UserDataContainer* getWritableUserDataContainer(osg::Object& object) + { + if (!object.getUserDataContainer()) + return object.getOrCreateUserDataContainer(); + + osg::ref_ptr newUserData = static_cast(object.getUserDataContainer()->clone(osg::CopyOp::SHALLOW_COPY)); + object.setUserDataContainer(newUserData); + return newUserData.get(); + } + + osg::StateSet* getRemovedState(osg::StateSet& stateSet) + { + if (!stateSet.getUserDataContainer()) + return nullptr; + + return static_cast(stateSet.getUserDataContainer()->getUserObject("removedState")); + } + + void updateRemovedState(osg::UserDataContainer& userData, osg::StateSet* stateSet) + { + unsigned int index = userData.getUserObjectIndex("removedState"); + if (index < userData.getNumUserObjects()) + userData.setUserObject(index, stateSet); + else + userData.addUserObject(stateSet); + stateSet->setName("removedState"); + } + const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap", "envMap", "specularMap", "decalMap", "bumpMap" }; bool isTextureNameRecognized(const std::string& name) { @@ -234,49 +267,67 @@ namespace Shader } const osg::StateSet::AttributeList& attributes = stateset->getAttributeList(); - for (osg::StateSet::AttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it) + osg::StateSet::AttributeList removedAttributes; + osg::ref_ptr removedState; + if (removedState = getRemovedState(*stateset)) + removedAttributes = removedState->getAttributeList(); + for (const auto& attributeMap : { attributes, removedAttributes }) { - if (it->first.first == osg::StateAttribute::MATERIAL) + for (osg::StateSet::AttributeList::const_iterator it = attributeMap.begin(); it != attributeMap.end(); ++it) { - // This should probably be moved out of ShaderRequirements and be applied directly now it's a uniform instead of a define - if (!mRequirements.back().mMaterialOverridden || it->second.second & osg::StateAttribute::PROTECTED) + if (it->first.first == osg::StateAttribute::MATERIAL) { - if (it->second.second & osg::StateAttribute::OVERRIDE) - mRequirements.back().mMaterialOverridden = true; + // This should probably be moved out of ShaderRequirements and be applied directly now it's a uniform instead of a define + if (!mRequirements.back().mMaterialOverridden || it->second.second & osg::StateAttribute::PROTECTED) + { + if (it->second.second & osg::StateAttribute::OVERRIDE) + mRequirements.back().mMaterialOverridden = true; - const osg::Material* mat = static_cast(it->second.first.get()); + const osg::Material* mat = static_cast(it->second.first.get()); - if (!writableStateSet) - writableStateSet = getWritableStateSet(node); + if (!writableStateSet) + writableStateSet = getWritableStateSet(node); - int colorMode; - switch (mat->getColorMode()) - { - case osg::Material::OFF: - colorMode = 0; - break; - case osg::Material::EMISSION: - colorMode = 1; - break; - default: - case osg::Material::AMBIENT_AND_DIFFUSE: - colorMode = 2; - break; - case osg::Material::AMBIENT: - colorMode = 3; - break; - case osg::Material::DIFFUSE: - colorMode = 4; - break; - case osg::Material::SPECULAR: - colorMode = 5; - break; + int colorMode; + switch (mat->getColorMode()) + { + case osg::Material::OFF: + colorMode = 0; + break; + case osg::Material::EMISSION: + colorMode = 1; + break; + default: + case osg::Material::AMBIENT_AND_DIFFUSE: + colorMode = 2; + break; + case osg::Material::AMBIENT: + colorMode = 3; + break; + case osg::Material::DIFFUSE: + colorMode = 4; + break; + case osg::Material::SPECULAR: + colorMode = 5; + break; + } + + mRequirements.back().mColorMode = colorMode; } + } + else if (it->first.first == osg::StateAttribute::ALPHAFUNC) + { + if (!mRequirements.back().mAlphaTestOverridden || it->second.second & osg::StateAttribute::PROTECTED) + { + if (it->second.second & osg::StateAttribute::OVERRIDE) + mRequirements.back().mAlphaTestOverridden = true; - mRequirements.back().mColorMode = colorMode; + const osg::AlphaFunc* alpha = static_cast(it->second.first.get()); + mRequirements.back().mAlphaFunc = alpha->getFunction(); + mRequirements.back().mAlphaRef = alpha->getReferenceValue(); + } } } - // Eventually, move alpha testing to discard in shader adn remove deprecated state here } } @@ -322,6 +373,42 @@ namespace Shader writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode)); + defineMap["alphaFunc"] = std::to_string(reqs.mAlphaFunc); + if (reqs.mAlphaFunc != osg::AlphaFunc::ALWAYS) + { + writableStateSet->addUniform(new osg::Uniform("alphaRef", reqs.mAlphaRef)); + + // back up removed state in case recreateShaders gets rid of the shader later + osg::ref_ptr removedState; + if ((removedState = getRemovedState(*writableStateSet)) && !mAllowedToModifyStateSets) + removedState = new osg::StateSet(*removedState, osg::CopyOp::SHALLOW_COPY); + if (!removedState) + removedState = new osg::StateSet(); + + if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT) + removedState->setMode(GL_ALPHA_TEST, writableStateSet->getMode(GL_ALPHA_TEST)); + // This disables the deprecated fixed-function alpha test + writableStateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED); + + const auto* alphaFunc = writableStateSet->getAttributePair(osg::StateAttribute::ALPHAFUNC); + if (alphaFunc) + removedState->setAttribute(alphaFunc->first, alphaFunc->second); + // This prevents redundant glAlphaFunc calls while letting the shadows bin still see the test + writableStateSet->setAttribute(RemovedAlphaFunc::getInstance(reqs.mAlphaFunc), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + + if (!removedState->getModeList().empty() || !removedState->getAttributeList().empty()) + { + // user data is normally shallow copied so shared with the original stateset + osg::ref_ptr writableUserData; + if (mAllowedToModifyStateSets) + writableUserData = writableStateSet->getOrCreateUserDataContainer(); + else + writableUserData = getWritableUserDataContainer(*writableStateSet); + + updateRemovedState(*writableUserData, removedState); + } + } + osg::ref_ptr vertexShader (mShaderManager.getShader(mDefaultVsTemplate, defineMap, osg::Shader::VERTEX)); osg::ref_ptr fragmentShader (mShaderManager.getShader(mDefaultFsTemplate, defineMap, osg::Shader::FRAGMENT)); @@ -347,6 +434,25 @@ namespace Shader writableStateSet = getWritableStateSet(node); writableStateSet->removeAttribute(osg::StateAttribute::PROGRAM); + + osg::ref_ptr removedState; + if (removedState = getRemovedState(*writableStateSet)) + { + // user data is normally shallow copied so shared with the original stateset + osg::ref_ptr writableUserData; + if (mAllowedToModifyStateSets) + writableUserData = writableStateSet->getUserDataContainer(); + else + writableUserData = getWritableUserDataContainer(*writableStateSet); + unsigned int index = writableUserData->getUserObjectIndex("removedState"); + writableUserData->removeUserObject(index); + + for (const auto& [mode, value] : removedState->getModeList()) + writableStateSet->setMode(mode, value); + + for (const auto& attribute : removedState->getAttributeList()) + writableStateSet->setAttribute(attribute.second.first, attribute.second.second); + } } bool ShaderVisitor::adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs) diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index 6031dbfe6..2ba18107b 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -79,6 +79,10 @@ namespace Shader int mColorMode; bool mMaterialOverridden; + bool mAlphaTestOverridden; + + GLenum mAlphaFunc; + float mAlphaRef; bool mNormalHeight; // true if normal map has height info in alpha channel diff --git a/files/shaders/CMakeLists.txt b/files/shaders/CMakeLists.txt index 8012c2bc1..16aedd16d 100644 --- a/files/shaders/CMakeLists.txt +++ b/files/shaders/CMakeLists.txt @@ -10,6 +10,7 @@ set(SHADER_FILES water_vertex.glsl water_fragment.glsl water_nm.png + alpha.glsl objects_vertex.glsl objects_fragment.glsl terrain_vertex.glsl diff --git a/files/shaders/alpha.glsl b/files/shaders/alpha.glsl new file mode 100644 index 000000000..9dee7bd31 --- /dev/null +++ b/files/shaders/alpha.glsl @@ -0,0 +1,38 @@ + +#define FUNC_NEVER 0x0200 +#define FUNC_LESS 0x0201 +#define FUNC_EQUAL 0x0202 +#define FUNC_LEQUAL 0x0203 +#define FUNC_GREATER 0x0204 +#define FUNC_NOTEQUAL 0x0205 +#define FUNC_GEQUAL 0x0206 +#define FUNC_ALWAYS 0x0207 + +#if @alphaFunc != FUNC_ALWAYS && @alphaFunc != FUNC_NEVER +uniform float alphaRef; +#endif + +void alphaTest() +{ + #if @alphaFunc == FUNC_NEVER + discard; + #elif @alphaFunc == FUNC_LESS + if (gl_FragData[0].a > alphaRef) + discard; + #elif @alphaFunc == FUNC_EQUAL + if (gl_FragData[0].a != alphaRef) + discard; + #elif @alphaFunc == FUNC_LEQUAL + if (gl_FragData[0].a >= alphaRef) + discard; + #elif @alphaFunc == FUNC_GREATER + if (gl_FragData[0].a < alphaRef) + discard; + #elif @alphaFunc == FUNC_NOTEQUAL + if (gl_FragData[0].a == alphaRef) + discard; + #elif @alphaFunc == FUNC_GEQUAL + if (gl_FragData[0].a <= alphaRef) + discard; + #endif +} \ No newline at end of file diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index d5716c378..ac631308c 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -67,6 +67,7 @@ varying vec3 passNormal; #include "shadows_fragment.glsl" #include "lighting.glsl" #include "parallax.glsl" +#include "alpha.glsl" void main() { @@ -108,6 +109,7 @@ void main() #if @diffuseMap gl_FragData[0] = texture2D(diffuseMap, adjustedDiffuseUV); + alphaTest(); #else gl_FragData[0] = vec4(1.0); #endif @@ -164,6 +166,8 @@ void main() gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor, shadowing); #endif + alphaTest(); + #if @envMap && !@preLightEnv gl_FragData[0].xyz += texture2D(envMap, envTexCoordGen).xyz * envMapColor.xyz * envLuma; #endif diff --git a/files/shaders/shadowcasting_fragment.glsl b/files/shaders/shadowcasting_fragment.glsl index a5410d008..8c53c542b 100644 --- a/files/shaders/shadowcasting_fragment.glsl +++ b/files/shaders/shadowcasting_fragment.glsl @@ -8,6 +8,8 @@ varying float alphaPassthrough; uniform bool useDiffuseMapForShadowAlpha; uniform bool alphaTestShadows; +#include "alpha.glsl" + void main() { gl_FragData[0].rgb = vec3(1.0); @@ -16,7 +18,10 @@ void main() else gl_FragData[0].a = alphaPassthrough; - // Prevent translucent things casting shadow (including the player using an invisibility effect). For now, rely on the deprecated FF test for non-blended stuff. + alphaTest(); + + // Prevent translucent things casting shadow (including the player using an invisibility effect). + // This replaces alpha blending, which obviously doesn't work with depth buffers if (alphaTestShadows && gl_FragData[0].a <= 0.5) discard; } From a0800715887135576ff0f863df00c897481b28b8 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 18 Dec 2020 00:02:51 +0000 Subject: [PATCH 002/132] Set default state sensibly --- apps/openmw/mwrender/renderingmanager.cpp | 5 +++++ components/shader/removedalphafunc.cpp | 7 ------- components/shader/removedalphafunc.hpp | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 217f0b73c..9cb5146dc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -32,6 +32,8 @@ #include #include #include + +#include #include #include @@ -376,6 +378,9 @@ namespace MWRender mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance)); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("simpleWater", false)); + // Hopefully, anything genuinely requiring the default alpha func of GL_ALWAYS explicitly sets it + mRootNode->getOrCreateStateSet()->setAttribute(Shader::RemovedAlphaFunc::getInstance(GL_GREATER)); + mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near"); mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far"); updateProjectionMatrix(); diff --git a/components/shader/removedalphafunc.cpp b/components/shader/removedalphafunc.cpp index 15d0fb0f1..0da36ab48 100644 --- a/components/shader/removedalphafunc.cpp +++ b/components/shader/removedalphafunc.cpp @@ -17,11 +17,4 @@ namespace Shader sInstances[func - GL_NEVER] = new RemovedAlphaFunc(static_cast(func), 1.0); return sInstances[func - GL_NEVER]; } - - void RemovedAlphaFunc::apply(osg::State & state) const - { - // Hopefully, anything genuinely requiring the default alpha func of GL_ALWAYS explicitly sets it - if (!state.getGlobalDefaultAttribute(ALPHAFUNC)->getType() != getType()) - state.setGlobalDefaultAttribute(static_cast(cloneType())); - } } diff --git a/components/shader/removedalphafunc.hpp b/components/shader/removedalphafunc.hpp index a8165b0dc..1aa9978cf 100644 --- a/components/shader/removedalphafunc.hpp +++ b/components/shader/removedalphafunc.hpp @@ -29,7 +29,7 @@ namespace Shader META_StateAttribute(Shader, RemovedAlphaFunc, ALPHAFUNC); - void apply(osg::State& state) const override; + void apply(osg::State& state) const override {} protected: virtual ~RemovedAlphaFunc() = default; From a4f32a469efbc7744146b3ab7d2c75d6e2fa91e9 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 18 Dec 2020 01:36:20 +0000 Subject: [PATCH 003/132] Work around Nvidia driver bug https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.1.20.pdf Section 4.1.3 says that hexadecimal integer literals are supported, but Nvidia have never read a specification since their founding, so their engineers didn't know that hexadecimal integer literals are requires to be supported to advertise support OpenGL versions with GLSL support. --- files/shaders/alpha.glsl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/files/shaders/alpha.glsl b/files/shaders/alpha.glsl index 9dee7bd31..0c69df0ab 100644 --- a/files/shaders/alpha.glsl +++ b/files/shaders/alpha.glsl @@ -1,12 +1,12 @@ -#define FUNC_NEVER 0x0200 -#define FUNC_LESS 0x0201 -#define FUNC_EQUAL 0x0202 -#define FUNC_LEQUAL 0x0203 -#define FUNC_GREATER 0x0204 -#define FUNC_NOTEQUAL 0x0205 -#define FUNC_GEQUAL 0x0206 -#define FUNC_ALWAYS 0x0207 +#define FUNC_NEVER 512 // 0x0200 +#define FUNC_LESS 513 // 0x0201 +#define FUNC_EQUAL 514 // 0x0202 +#define FUNC_LEQUAL 515 // 0x0203 +#define FUNC_GREATER 516 // 0x0204 +#define FUNC_NOTEQUAL 517 // 0x0205 +#define FUNC_GEQUAL 518 // 0x0206 +#define FUNC_ALWAYS 519 // 0x0207 #if @alphaFunc != FUNC_ALWAYS && @alphaFunc != FUNC_NEVER uniform float alphaRef; From 05ad44d0b1527339f8da788898cf9fa1ab92ba84 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 18 Dec 2020 01:44:46 +0000 Subject: [PATCH 004/132] Set correct array size --- components/sceneutil/mwshadowtechnique.hpp | 4 ++-- components/sceneutil/shadowsbin.cpp | 6 +++--- components/sceneutil/shadowsbin.hpp | 8 ++++---- components/shader/removedalphafunc.cpp | 4 ++-- components/shader/removedalphafunc.hpp | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/components/sceneutil/mwshadowtechnique.hpp b/components/sceneutil/mwshadowtechnique.hpp index 96b1c7d0e..7ffe687b4 100644 --- a/components/sceneutil/mwshadowtechnique.hpp +++ b/components/sceneutil/mwshadowtechnique.hpp @@ -215,7 +215,7 @@ namespace SceneUtil { virtual void createShaders(); - virtual std::array, GL_ALWAYS - GL_NEVER> getCastingPrograms() const { return _castingPrograms; } + virtual std::array, GL_ALWAYS - GL_NEVER + 1> getCastingPrograms() const { return _castingPrograms; } virtual bool selectActiveLights(osgUtil::CullVisitor* cv, ViewDependentData* vdd) const; @@ -290,7 +290,7 @@ namespace SceneUtil { }; osg::ref_ptr _debugHud; - std::array, GL_ALWAYS - GL_NEVER> _castingPrograms; + std::array, GL_ALWAYS - GL_NEVER + 1> _castingPrograms; }; } diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp index 9e6d461ae..de778f14b 100644 --- a/components/sceneutil/shadowsbin.cpp +++ b/components/sceneutil/shadowsbin.cpp @@ -42,8 +42,8 @@ namespace namespace SceneUtil { -std::array, GL_ALWAYS - GL_NEVER> ShadowsBin::sCastingPrograms = { - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr +std::array, GL_ALWAYS - GL_NEVER + 1> ShadowsBin::sCastingPrograms = { + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; ShadowsBin::ShadowsBin() @@ -150,7 +150,7 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un return sg; } -void ShadowsBin::addPrototype(const std::string & name, const std::array, GL_ALWAYS - GL_NEVER>& castingPrograms) +void ShadowsBin::addPrototype(const std::string & name, const std::array, GL_ALWAYS - GL_NEVER + 1>& castingPrograms) { sCastingPrograms = castingPrograms; osg::ref_ptr bin(new ShadowsBin); diff --git a/components/sceneutil/shadowsbin.hpp b/components/sceneutil/shadowsbin.hpp index 7247a9af4..d8bdd8607 100644 --- a/components/sceneutil/shadowsbin.hpp +++ b/components/sceneutil/shadowsbin.hpp @@ -17,12 +17,12 @@ namespace SceneUtil class ShadowsBin : public osgUtil::RenderBin { private: - static std::array, GL_ALWAYS - GL_NEVER> sCastingPrograms; + static std::array, GL_ALWAYS - GL_NEVER + 1> sCastingPrograms; osg::ref_ptr mNoTestStateSet; osg::ref_ptr mShaderAlphaTestStateSet; - std::array, GL_ALWAYS - GL_NEVER> mAlphaFuncShaders; + std::array, GL_ALWAYS - GL_NEVER + 1> mAlphaFuncShaders; public: META_Object(SceneUtil, ShadowsBin) ShadowsBin(); @@ -65,13 +65,13 @@ namespace SceneUtil osgUtil::StateGraph* cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set& uninteresting); - static void addPrototype(const std::string& name, const std::array, GL_ALWAYS - GL_NEVER>& castingPrograms); + static void addPrototype(const std::string& name, const std::array, GL_ALWAYS - GL_NEVER + 1>& castingPrograms); }; class ShadowsBinAdder { public: - ShadowsBinAdder(const std::string& name, const std::array, GL_ALWAYS - GL_NEVER>& castingPrograms){ ShadowsBin::addPrototype(name, castingPrograms); } + ShadowsBinAdder(const std::string& name, const std::array, GL_ALWAYS - GL_NEVER + 1>& castingPrograms){ ShadowsBin::addPrototype(name, castingPrograms); } }; } diff --git a/components/shader/removedalphafunc.cpp b/components/shader/removedalphafunc.cpp index 0da36ab48..6b701b890 100644 --- a/components/shader/removedalphafunc.cpp +++ b/components/shader/removedalphafunc.cpp @@ -6,8 +6,8 @@ namespace Shader { - std::array, GL_ALWAYS - GL_NEVER> RemovedAlphaFunc::sInstances{ - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr + std::array, GL_ALWAYS - GL_NEVER + 1> RemovedAlphaFunc::sInstances{ + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; osg::ref_ptr RemovedAlphaFunc::getInstance(GLenum func) diff --git a/components/shader/removedalphafunc.hpp b/components/shader/removedalphafunc.hpp index 1aa9978cf..c744391e5 100644 --- a/components/shader/removedalphafunc.hpp +++ b/components/shader/removedalphafunc.hpp @@ -34,7 +34,7 @@ namespace Shader protected: virtual ~RemovedAlphaFunc() = default; - static std::array, GL_ALWAYS - GL_NEVER> sInstances; + static std::array, GL_ALWAYS - GL_NEVER + 1> sInstances; }; } #endif //OPENMW_COMPONENTS_REMOVEDALPHAFUNC_H From 0b5d5eab4c997789b1a3eab5b9da8406f6fc1552 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 18 Dec 2020 02:11:51 +0000 Subject: [PATCH 005/132] Move is faster --- components/sceneutil/shadowsbin.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp index de778f14b..312d4226b 100644 --- a/components/sceneutil/shadowsbin.cpp +++ b/components/sceneutil/shadowsbin.cpp @@ -127,11 +127,9 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un if (state.mAlphaBlend) { sg_new = sg->find_or_insert(mShaderAlphaTestStateSet); - for (RenderLeaf* leaf : sg->_leaves) - { + sg_new->_leaves = std::move(sg->_leaves); + for (RenderLeaf* leaf : sg_new->_leaves) leaf->_parent = sg_new; - sg_new->_leaves.push_back(leaf); - } sg = sg_new; } @@ -139,11 +137,9 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un if (state.mAlphaFunc && state.mAlphaFunc->getFunction() != GL_ALWAYS) { sg_new = sg->find_or_insert(mAlphaFuncShaders[state.mAlphaFunc->getFunction() - GL_NEVER]); - for (RenderLeaf* leaf : sg->_leaves) - { + sg_new->_leaves = std::move(sg->_leaves); + for (RenderLeaf* leaf : sg_new->_leaves) leaf->_parent = sg_new; - sg_new->_leaves.push_back(leaf); - } sg = sg_new; } From 2ecd00b6db190476f4f8d713044fd3c42dd5f220 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 18 Dec 2020 16:31:21 +0000 Subject: [PATCH 006/132] Don't accidentally enable alpha testing for all shadow casting --- apps/openmw/mwrender/renderingmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9cb5146dc..736e4f2ee 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -379,7 +379,7 @@ namespace MWRender mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("simpleWater", false)); // Hopefully, anything genuinely requiring the default alpha func of GL_ALWAYS explicitly sets it - mRootNode->getOrCreateStateSet()->setAttribute(Shader::RemovedAlphaFunc::getInstance(GL_GREATER)); + mRootNode->getOrCreateStateSet()->setAttribute(Shader::RemovedAlphaFunc::getInstance(GL_ALWAYS)); mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near"); mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far"); From cc2ce9fa3e49b86fe1501eb2fe3b26e80918c995 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 19 Dec 2020 21:57:42 +0000 Subject: [PATCH 007/132] Explicitly default-construct array The docs seem to imply this is automatic when the array contains a class-type, which osg::ref_ptr is, but I got a crash log that doesn't make sense if that's true. --- components/sceneutil/mwshadowtechnique.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 31267f75c..39e47b0ea 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -782,13 +782,15 @@ void MWShadowTechnique::ViewDependentData::releaseGLObjects(osg::State* state) c MWShadowTechnique::MWShadowTechnique(): ShadowTechnique(), _enableShadows(false), - _debugHud(nullptr) + _debugHud(nullptr), + _castingPrograms{ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr } { _shadowRecievingPlaceholderStateSet = new osg::StateSet; } MWShadowTechnique::MWShadowTechnique(const MWShadowTechnique& vdsm, const osg::CopyOp& copyop): ShadowTechnique(vdsm,copyop) + , _castingPrograms(vdsm._castingPrograms) { _shadowRecievingPlaceholderStateSet = new osg::StateSet; _enableShadows = vdsm._enableShadows; From 0e4e8eb0f3af47ea236dac8c7372da97eb39dbdb Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 20 Dec 2020 01:22:14 +0000 Subject: [PATCH 008/132] Add glDebugGroup support --- components/debug/gldebug.cpp | 306 +++++++++++++++++++++++------------ components/debug/gldebug.hpp | 60 +++++++ 2 files changed, 267 insertions(+), 99 deletions(-) diff --git a/components/debug/gldebug.cpp b/components/debug/gldebug.cpp index 3c5ec728a..22d7bbe68 100644 --- a/components/debug/gldebug.cpp +++ b/components/debug/gldebug.cpp @@ -38,127 +38,235 @@ either expressed or implied, of the FreeBSD Project. // OpenGL constants not provided by OSG: #include -void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) +namespace Debug { -#ifdef GL_DEBUG_OUTPUT - std::string srcStr; - switch (source) + + void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) { - case GL_DEBUG_SOURCE_API: - srcStr = "API"; - break; - case GL_DEBUG_SOURCE_WINDOW_SYSTEM: - srcStr = "WINDOW_SYSTEM"; - break; - case GL_DEBUG_SOURCE_SHADER_COMPILER: - srcStr = "SHADER_COMPILER"; - break; - case GL_DEBUG_SOURCE_THIRD_PARTY: - srcStr = "THIRD_PARTY"; - break; - case GL_DEBUG_SOURCE_APPLICATION: - srcStr = "APPLICATION"; - break; - case GL_DEBUG_SOURCE_OTHER: - srcStr = "OTHER"; - break; - default: - srcStr = "UNDEFINED"; - break; +#ifdef GL_DEBUG_OUTPUT + std::string srcStr; + switch (source) + { + case GL_DEBUG_SOURCE_API: + srcStr = "API"; + break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: + srcStr = "WINDOW_SYSTEM"; + break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: + srcStr = "SHADER_COMPILER"; + break; + case GL_DEBUG_SOURCE_THIRD_PARTY: + srcStr = "THIRD_PARTY"; + break; + case GL_DEBUG_SOURCE_APPLICATION: + srcStr = "APPLICATION"; + break; + case GL_DEBUG_SOURCE_OTHER: + srcStr = "OTHER"; + break; + default: + srcStr = "UNDEFINED"; + break; + } + + std::string typeStr; + + Level logSeverity = Warning; + switch (type) + { + case GL_DEBUG_TYPE_ERROR: + typeStr = "ERROR"; + logSeverity = Error; + break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + typeStr = "DEPRECATED_BEHAVIOR"; + break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + typeStr = "UNDEFINED_BEHAVIOR"; + break; + case GL_DEBUG_TYPE_PORTABILITY: + typeStr = "PORTABILITY"; + break; + case GL_DEBUG_TYPE_PERFORMANCE: + typeStr = "PERFORMANCE"; + break; + case GL_DEBUG_TYPE_OTHER: + typeStr = "OTHER"; + break; + default: + typeStr = "UNDEFINED"; + break; + } + + Log(logSeverity) << "OpenGL " << typeStr << " [" << srcStr << "]: " << message; +#endif } - std::string typeStr; + class PushDebugGroup + { + public: + static std::unique_ptr sInstance; + + void (GL_APIENTRY * glPushDebugGroup) (GLenum source, GLuint id, GLsizei length, const GLchar * message); + + void (GL_APIENTRY * glPopDebugGroup) (void); - Debug::Level logSeverity = Debug::Warning; - switch (type) + bool valid() + { + return glPushDebugGroup && glPopDebugGroup; + } + }; + + std::unique_ptr PushDebugGroup::sInstance{ std::make_unique() }; + + EnableGLDebugOperation::EnableGLDebugOperation() : osg::GraphicsOperation("EnableGLDebugOperation", false) { - case GL_DEBUG_TYPE_ERROR: - typeStr = "ERROR"; - logSeverity = Debug::Error; - break; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: - typeStr = "DEPRECATED_BEHAVIOR"; - break; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: - typeStr = "UNDEFINED_BEHAVIOR"; - break; - case GL_DEBUG_TYPE_PORTABILITY: - typeStr = "PORTABILITY"; - break; - case GL_DEBUG_TYPE_PERFORMANCE: - typeStr = "PERFORMANCE"; - break; - case GL_DEBUG_TYPE_OTHER: - typeStr = "OTHER"; - break; - default: - typeStr = "UNDEFINED"; - break; } - Log(logSeverity) << "OpenGL " << typeStr << " [" << srcStr << "]: " << message; + void EnableGLDebugOperation::operator()(osg::GraphicsContext* graphicsContext) + { +#ifdef GL_DEBUG_OUTPUT + OpenThreads::ScopedLock lock(mMutex); + + unsigned int contextID = graphicsContext->getState()->getContextID(); + + typedef void (GL_APIENTRY *DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); + typedef void (GL_APIENTRY *GLDebugMessageControlFunction)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); + typedef void (GL_APIENTRY *GLDebugMessageCallbackFunction)(DEBUGPROC, const void* userParam); + + GLDebugMessageControlFunction glDebugMessageControl = nullptr; + GLDebugMessageCallbackFunction glDebugMessageCallback = nullptr; + + if (osg::isGLExtensionSupported(contextID, "GL_KHR_debug")) + { + osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallback"); + osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControl"); + osg::setGLExtensionFuncPtr(PushDebugGroup::sInstance->glPushDebugGroup, "glPushDebugGroup"); + osg::setGLExtensionFuncPtr(PushDebugGroup::sInstance->glPopDebugGroup, "glPopDebugGroup"); + } + else if (osg::isGLExtensionSupported(contextID, "GL_ARB_debug_output")) + { + osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackARB"); + osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlARB"); + } + else if (osg::isGLExtensionSupported(contextID, "GL_AMD_debug_output")) + { + osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackAMD"); + osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlAMD"); + } + + if (glDebugMessageCallback && glDebugMessageControl) + { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, true); + glDebugMessageCallback(debugCallback, nullptr); + + Log(Info) << "OpenGL debug callback attached."; + } + else #endif -} + Log(Error) << "Unable to attach OpenGL debug callback."; + } -void enableGLDebugExtension(unsigned int contextID) -{ -#ifdef GL_DEBUG_OUTPUT - typedef void (GL_APIENTRY *DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); - typedef void (GL_APIENTRY *GLDebugMessageControlFunction)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); - typedef void (GL_APIENTRY *GLDebugMessageCallbackFunction)(DEBUGPROC, const void* userParam); - - GLDebugMessageControlFunction glDebugMessageControl = nullptr; - GLDebugMessageCallbackFunction glDebugMessageCallback = nullptr; - - if (osg::isGLExtensionSupported(contextID, "GL_KHR_debug")) + bool shouldDebugOpenGL() + { + const char* env = std::getenv("OPENMW_DEBUG_OPENGL"); + if (!env) + return false; + std::string str(env); + if (str.length() == 0) + return true; + + return str.find("OFF") == std::string::npos && str.find("0") == std::string::npos && str.find("NO") == std::string::npos; + } + + DebugGroup::DebugGroup(const std::string & message, GLuint id) + : mSource(GL_DEBUG_SOURCE_APPLICATION) + , mId(id) + , mMessage(message) { - osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallback"); - osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControl"); } - else if (osg::isGLExtensionSupported(contextID, "GL_ARB_debug_output")) + + DebugGroup::DebugGroup(const DebugGroup & debugGroup, const osg::CopyOp & copyop) + : osg::StateAttribute(debugGroup, copyop) + , mSource(debugGroup.mSource) + , mId(debugGroup.mId) + , mMessage(debugGroup.mMessage) { - osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackARB"); - osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlARB"); } - else if (osg::isGLExtensionSupported(contextID, "GL_AMD_debug_output")) + + void DebugGroup::apply(osg::State & state) const { - osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackAMD"); - osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlAMD"); + if (!PushDebugGroup::sInstance->valid()) + { + Log(Error) << "OpenGL debug groups not supported on this system, or OPENMW_DEBUG_OPENGL environment variable not set."; + return; + } + + auto& attributeVec = state.getAttributeVec(this); + auto& lastAppliedStack = sLastAppliedStack[state.getContextID()]; + + size_t firstNonMatch = 0; + while (firstNonMatch < lastAppliedStack.size() + && ((firstNonMatch < attributeVec.size() && lastAppliedStack[firstNonMatch] == attributeVec[firstNonMatch].first) + || lastAppliedStack[firstNonMatch] == this)) + firstNonMatch++; + + for (size_t i = lastAppliedStack.size(); i > firstNonMatch; --i) + lastAppliedStack[i - 1]->pop(state); + lastAppliedStack.resize(firstNonMatch); + + lastAppliedStack.reserve(attributeVec.size()); + for (size_t i = firstNonMatch; i < attributeVec.size(); ++i) + { + const DebugGroup* group = static_cast(attributeVec[i].first); + group->push(state); + lastAppliedStack.push_back(group); + } + if (!(lastAppliedStack.back() == this)) + { + push(state); + lastAppliedStack.push_back(this); + } } - if (glDebugMessageCallback && glDebugMessageControl) + int DebugGroup::compare(const StateAttribute & sa) const { - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, true); - glDebugMessageCallback(debugCallback, nullptr); + COMPARE_StateAttribute_Types(DebugGroup, sa); + + COMPARE_StateAttribute_Parameter(mSource); + COMPARE_StateAttribute_Parameter(mId); + COMPARE_StateAttribute_Parameter(mMessage); - Log(Debug::Info) << "OpenGL debug callback attached."; + return 0; } - else -#endif - Log(Debug::Error) << "Unable to attach OpenGL debug callback."; -} -Debug::EnableGLDebugOperation::EnableGLDebugOperation() : osg::GraphicsOperation("EnableGLDebugOperation", false) -{ -} + void DebugGroup::releaseGLObjects(osg::State * state) const + { + if (state) + sLastAppliedStack.erase(state->getContextID()); + else + sLastAppliedStack.clear(); + } -void Debug::EnableGLDebugOperation::operator()(osg::GraphicsContext* graphicsContext) -{ - OpenThreads::ScopedLock lock(mMutex); + bool DebugGroup::isValid() const + { + return mSource || mId || mMessage.length(); + } - unsigned int contextID = graphicsContext->getState()->getContextID(); - enableGLDebugExtension(contextID); -} + void DebugGroup::push(osg::State & state) const + { + if (isValid()) + PushDebugGroup::sInstance->glPushDebugGroup(mSource, mId, mMessage.size(), mMessage.c_str()); + } + + void DebugGroup::pop(osg::State & state) const + { + if (isValid()) + PushDebugGroup::sInstance->glPopDebugGroup(); + } + + std::map> DebugGroup::sLastAppliedStack{}; -bool Debug::shouldDebugOpenGL() -{ - const char* env = std::getenv("OPENMW_DEBUG_OPENGL"); - if (!env) - return false; - std::string str(env); - if (str.length() == 0) - return true; - - return str.find("OFF") == std::string::npos && str.find("0") == std::string::npos && str.find("NO") == std::string::npos; } diff --git a/components/debug/gldebug.hpp b/components/debug/gldebug.hpp index 8be747afe..b6f32c9cf 100644 --- a/components/debug/gldebug.hpp +++ b/components/debug/gldebug.hpp @@ -17,5 +17,65 @@ namespace Debug }; bool shouldDebugOpenGL(); + + + /* + Debug groups allow rendering to be annotated, making debugging via APITrace/CodeXL/NSight etc. much clearer. + + Because I've not thought of a quick and clean way of doing it without incurring a small performance cost, + there are no uses of this class checked in. For now, add annotations locally when you need them. + + To use this class, add it to a StateSet just like any other StateAttribute. Prefer the string-only constructor. + You'll need OPENMW_DEBUG_OPENGL set to true, or shouldDebugOpenGL() redefined to just return true as otherwise + the extension function pointers won't get set up. That can maybe be cleaned up in the future. + + Beware that consecutive identical debug groups (i.e. pointers match) won't always get applied due to OSG thinking + it's already applied them. Either avoid nesting the same object, add dummy groups so they're not consecutive, or + ensure the leaf group isn't identical to its parent. + */ + class DebugGroup : public osg::StateAttribute + { + public: + DebugGroup() + : mSource(0) + , mId(0) + , mMessage("") + {} + + DebugGroup(GLenum source, GLuint id, const std::string& message) + : mSource(source) + , mId(id) + , mMessage(message) + {} + + DebugGroup(const std::string& message, GLuint id = 0); + + DebugGroup(const DebugGroup& debugGroup, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); + + META_StateAttribute(Debug, DebugGroup, osg::StateAttribute::Type(101)); + + void apply(osg::State& state) const override; + + int compare(const StateAttribute& sa) const override; + + void releaseGLObjects(osg::State* state = nullptr) const override; + + virtual bool isValid() const; + + protected: + virtual ~DebugGroup() = default; + + virtual void push(osg::State& state) const; + + virtual void pop(osg::State& state) const; + + GLenum mSource; + GLuint mId; + std::string mMessage; + + static std::map> sLastAppliedStack; + + friend EnableGLDebugOperation; + }; } #endif From 657da50d996d304cca4ee27884c46a290bb05223 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 20 Dec 2020 01:36:34 +0000 Subject: [PATCH 009/132] Ensure GL_BLEND is disabled when drawing shadow maps --- components/sceneutil/shadowsbin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp index 312d4226b..8b5c788dd 100644 --- a/components/sceneutil/shadowsbin.cpp +++ b/components/sceneutil/shadowsbin.cpp @@ -54,7 +54,7 @@ ShadowsBin::ShadowsBin() mShaderAlphaTestStateSet = new osg::StateSet; mShaderAlphaTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", true)); - mShaderAlphaTestStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); + mShaderAlphaTestStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE); for (size_t i = 0; i < sCastingPrograms.size(); ++i) { From 7e045cff7560a16015ab60b984d1713fb549c42e Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 20 Dec 2020 01:51:45 +0000 Subject: [PATCH 010/132] #include --- components/debug/gldebug.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/debug/gldebug.cpp b/components/debug/gldebug.cpp index 22d7bbe68..ee7dd608e 100644 --- a/components/debug/gldebug.cpp +++ b/components/debug/gldebug.cpp @@ -32,6 +32,7 @@ either expressed or implied, of the FreeBSD Project. #include "gldebug.hpp" #include +#include #include From a36ed5f129ff4cb008c29413d1683da4fac59e9a Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 23 Dec 2020 00:23:49 +0000 Subject: [PATCH 011/132] Optimise out redundant call We already had the results --- components/sceneutil/shadowsbin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp index 8b5c788dd..a2424fe36 100644 --- a/components/sceneutil/shadowsbin.cpp +++ b/components/sceneutil/shadowsbin.cpp @@ -27,9 +27,9 @@ namespace osg::StateSet::ModeList::const_iterator mf = l.find(mode); if (mf == l.end()) return; - int flags = mf->second; + unsigned int flags = mf->second; bool newValue = flags & osg::StateAttribute::ON; - accumulateState(currentValue, newValue, isOverride, ss->getMode(mode)); + accumulateState(currentValue, newValue, isOverride, flags); } inline bool materialNeedShadows(osg::Material* m) From 11b4af49ce2166057c68994bf8258a253e504137 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 23 Dec 2020 01:24:15 +0000 Subject: [PATCH 012/132] Allow shadowsbin to optimise clockwise-wound meshes when face culling is off --- components/sceneutil/mwshadowtechnique.cpp | 6 +++++ components/sceneutil/shadowsbin.cpp | 31 +++++++++++++++++----- components/sceneutil/shadowsbin.hpp | 2 +- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 39e47b0ea..cfc94ba86 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -870,7 +870,10 @@ void SceneUtil::MWShadowTechnique::enableFrontFaceCulling() _useFrontFaceCulling = true; if (_shadowCastingStateSet) + { _shadowCastingStateSet->setAttribute(new osg::CullFace(osg::CullFace::FRONT), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + _shadowCastingStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + } } void SceneUtil::MWShadowTechnique::disableFrontFaceCulling() @@ -878,7 +881,10 @@ void SceneUtil::MWShadowTechnique::disableFrontFaceCulling() _useFrontFaceCulling = false; if (_shadowCastingStateSet) + { + _shadowCastingStateSet->removeAttribute(osg::StateAttribute::CULLFACE); _shadowCastingStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); + } } void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & shaderManager) diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp index a2424fe36..26cbd58c3 100644 --- a/components/sceneutil/shadowsbin.cpp +++ b/components/sceneutil/shadowsbin.cpp @@ -63,7 +63,7 @@ ShadowsBin::ShadowsBin() } } -StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set& uninterestingCache) +StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set& uninterestingCache, bool cullFaceOverridden) { std::vector return_path; State state; @@ -102,10 +102,13 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un accumulateState(state.mAlphaFunc, static_cast(rap.first.get()), state.mAlphaFuncOverride, rap.second); } - // osg::FrontFace specifies triangle winding, not front-face culling. We can't safely reparent anything under it. - found = attributes.find(std::make_pair(osg::StateAttribute::FRONTFACE, 0)); - if (found != attributes.end()) - state.mImportantState = true; + if (!cullFaceOverridden) + { + // osg::FrontFace specifies triangle winding, not front-face culling. We can't safely reparent anything under it unless GL_CULL_FACE is off or we flip face culling. + found = attributes.find(std::make_pair(osg::StateAttribute::FRONTFACE, 0)); + if (found != attributes.end()) + state.mImportantState = true; + } if ((*itr) != sg && !state.interesting()) uninterestingCache.insert(*itr); @@ -188,7 +191,21 @@ void ShadowsBin::sortImplementation() return; } StateGraph* noTestRoot = root->find_or_insert(mNoTestStateSet.get()); - // root is now a stategraph with useDiffuseMapForShadowAlpha disabled but minimal other state + // noTestRoot is now a stategraph with useDiffuseMapForShadowAlpha disabled but minimal other state + + bool cullFaceOverridden = false; + while (root = root->_parent) + { + if (!root->getStateSet()) + continue; + unsigned int cullFaceFlags = root->getStateSet()->getMode(GL_CULL_FACE); + if (cullFaceFlags & osg::StateAttribute::OVERRIDE && !(cullFaceFlags & osg::StateAttribute::ON)) + { + cullFaceOverridden = true; + break; + } + } + noTestRoot->_leaves.reserve(_stateGraphList.size()); StateGraphList newList; std::unordered_set uninterestingCache; @@ -197,7 +214,7 @@ void ShadowsBin::sortImplementation() // Render leaves which shouldn't use the diffuse map for shadow alpha but do cast shadows become children of root, so graph is now empty. Don't add to newList. // Graphs containing just render leaves which don't cast shadows are discarded. Don't add to newList. // Graphs containing other leaves need to be in newList. - StateGraph* graphToAdd = cullStateGraph(graph, noTestRoot, uninterestingCache); + StateGraph* graphToAdd = cullStateGraph(graph, noTestRoot, uninterestingCache, cullFaceOverridden); if (graphToAdd) newList.push_back(graphToAdd); } diff --git a/components/sceneutil/shadowsbin.hpp b/components/sceneutil/shadowsbin.hpp index d8bdd8607..1c63caf4b 100644 --- a/components/sceneutil/shadowsbin.hpp +++ b/components/sceneutil/shadowsbin.hpp @@ -63,7 +63,7 @@ namespace SceneUtil } }; - osgUtil::StateGraph* cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set& uninteresting); + osgUtil::StateGraph* cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set& uninteresting, bool cullFaceOverridden); static void addPrototype(const std::string& name, const std::array, GL_ALWAYS - GL_NEVER + 1>& castingPrograms); }; From 8c3a786e5408cc8d2e857afc02ff1590c0ca8ef9 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 24 Dec 2020 00:32:15 +0000 Subject: [PATCH 013/132] Unconditionally disable alpha testing when shaders are used --- components/shader/shadervisitor.cpp | 45 +++++++++++++++-------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index aeec391c6..7db993316 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -374,39 +374,40 @@ namespace Shader writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode)); defineMap["alphaFunc"] = std::to_string(reqs.mAlphaFunc); + + // back up removed state in case recreateShaders gets rid of the shader later + osg::ref_ptr removedState; + if ((removedState = getRemovedState(*writableStateSet)) && !mAllowedToModifyStateSets) + removedState = new osg::StateSet(*removedState, osg::CopyOp::SHALLOW_COPY); + if (!removedState) + removedState = new osg::StateSet(); + if (reqs.mAlphaFunc != osg::AlphaFunc::ALWAYS) { writableStateSet->addUniform(new osg::Uniform("alphaRef", reqs.mAlphaRef)); - // back up removed state in case recreateShaders gets rid of the shader later - osg::ref_ptr removedState; - if ((removedState = getRemovedState(*writableStateSet)) && !mAllowedToModifyStateSets) - removedState = new osg::StateSet(*removedState, osg::CopyOp::SHALLOW_COPY); - if (!removedState) - removedState = new osg::StateSet(); - - if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT) - removedState->setMode(GL_ALPHA_TEST, writableStateSet->getMode(GL_ALPHA_TEST)); - // This disables the deprecated fixed-function alpha test - writableStateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED); - const auto* alphaFunc = writableStateSet->getAttributePair(osg::StateAttribute::ALPHAFUNC); if (alphaFunc) removedState->setAttribute(alphaFunc->first, alphaFunc->second); // This prevents redundant glAlphaFunc calls while letting the shadows bin still see the test writableStateSet->setAttribute(RemovedAlphaFunc::getInstance(reqs.mAlphaFunc), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + } - if (!removedState->getModeList().empty() || !removedState->getAttributeList().empty()) - { - // user data is normally shallow copied so shared with the original stateset - osg::ref_ptr writableUserData; - if (mAllowedToModifyStateSets) - writableUserData = writableStateSet->getOrCreateUserDataContainer(); - else - writableUserData = getWritableUserDataContainer(*writableStateSet); + if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT) + removedState->setMode(GL_ALPHA_TEST, writableStateSet->getMode(GL_ALPHA_TEST)); + // This disables the deprecated fixed-function alpha test + writableStateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED); - updateRemovedState(*writableUserData, removedState); - } + if (!removedState->getModeList().empty() || !removedState->getAttributeList().empty()) + { + // user data is normally shallow copied so shared with the original stateset + osg::ref_ptr writableUserData; + if (mAllowedToModifyStateSets) + writableUserData = writableStateSet->getOrCreateUserDataContainer(); + else + writableUserData = getWritableUserDataContainer(*writableStateSet); + + updateRemovedState(*writableUserData, removedState); } osg::ref_ptr vertexShader (mShaderManager.getShader(mDefaultVsTemplate, defineMap, osg::Shader::VERTEX)); From 8f4b856b449f4ad15e002dbb2afa88cd10ace588 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 26 Dec 2020 22:45:53 +0000 Subject: [PATCH 014/132] Initial A2C implementation --- apps/openmw/mwrender/renderingmanager.cpp | 1 + components/resource/scenemanager.cpp | 6 ++ components/resource/scenemanager.hpp | 3 + components/sceneutil/mwshadowtechnique.cpp | 2 +- components/shader/shadervisitor.cpp | 13 ++++ components/shader/shadervisitor.hpp | 4 ++ .../reference/modding/settings/shaders.rst | 11 ++++ files/settings-default.cfg | 5 ++ files/shaders/alpha.glsl | 60 +++++++++++++------ 9 files changed, 86 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 736e4f2ee..a60077a16 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -219,6 +219,7 @@ namespace MWRender resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders")); resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders")); resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders")); + resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders")); osg::ref_ptr sceneRoot = new SceneUtil::LightManager; sceneRoot->setLightingMask(Mask_Lighting); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 2630cd453..5f7116722 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -290,6 +290,11 @@ namespace Resource mApplyLightingToEnvMaps = apply; } + void SceneManager::setConvertAlphaTestToAlphaToCoverage(bool convert) + { + mConvertAlphaTestToAlphaToCoverage = convert; + } + SceneManager::~SceneManager() { // this has to be defined in the .cpp file as we can't delete incomplete types @@ -769,6 +774,7 @@ namespace Resource shaderVisitor->setAutoUseSpecularMaps(mAutoUseSpecularMaps); shaderVisitor->setSpecularMapPattern(mSpecularMapPattern); shaderVisitor->setApplyLightingToEnvMaps(mApplyLightingToEnvMaps); + shaderVisitor->setConvertAlphaTestToAlphaToCoverage(mConvertAlphaTestToAlphaToCoverage); return shaderVisitor; } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index fd75070a1..aae7d143f 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -75,6 +75,8 @@ namespace Resource void setApplyLightingToEnvMaps(bool apply); + void setConvertAlphaTestToAlphaToCoverage(bool convert); + void setShaderPath(const std::string& path); /// Check if a given scene is loaded and if so, update its usage timestamp to prevent it from being unloaded @@ -159,6 +161,7 @@ namespace Resource bool mAutoUseSpecularMaps; std::string mSpecularMapPattern; bool mApplyLightingToEnvMaps; + bool mConvertAlphaTestToAlphaToCoverage; osg::ref_ptr mInstanceCache; diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index cfc94ba86..c781318fa 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -897,7 +897,7 @@ void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & sh auto& program = _castingPrograms[alphaFunc - GL_NEVER]; program = new osg::Program(); program->addShader(castingVertexShader); - program->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", { {"alphaFunc", std::to_string(alphaFunc)} }, osg::Shader::FRAGMENT)); + program->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", { {"alphaFunc", std::to_string(alphaFunc)}, {"alphaToCoverage", "0"} }, osg::Shader::FRAGMENT)); } } diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 7db993316..4762549a7 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -382,6 +383,7 @@ namespace Shader if (!removedState) removedState = new osg::StateSet(); + defineMap["alphaToCoverage"] = "0"; if (reqs.mAlphaFunc != osg::AlphaFunc::ALWAYS) { writableStateSet->addUniform(new osg::Uniform("alphaRef", reqs.mAlphaRef)); @@ -391,6 +393,12 @@ namespace Shader removedState->setAttribute(alphaFunc->first, alphaFunc->second); // This prevents redundant glAlphaFunc calls while letting the shadows bin still see the test writableStateSet->setAttribute(RemovedAlphaFunc::getInstance(reqs.mAlphaFunc), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + + if (mConvertAlphaTestToAlphaToCoverage) + { + writableStateSet->setMode(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB, osg::StateAttribute::ON); + defineMap["alphaToCoverage"] = "1"; + } } if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT) @@ -581,4 +589,9 @@ namespace Shader mApplyLightingToEnvMaps = apply; } + void ShaderVisitor::setConvertAlphaTestToAlphaToCoverage(bool convert) + { + mConvertAlphaTestToAlphaToCoverage = convert; + } + } diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index 2ba18107b..606e06df9 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -40,6 +40,8 @@ namespace Shader void setApplyLightingToEnvMaps(bool apply); + void setConvertAlphaTestToAlphaToCoverage(bool convert); + void apply(osg::Node& node) override; void apply(osg::Drawable& drawable) override; @@ -63,6 +65,8 @@ namespace Shader bool mApplyLightingToEnvMaps; + bool mConvertAlphaTestToAlphaToCoverage; + ShaderManager& mShaderManager; Resource::ImageManager& mImageManager; diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index e23cc3d54..49ada897e 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -146,3 +146,14 @@ radial fog By default, the fog becomes thicker proportionally to your distance from the clipping plane set at the clipping distance, which causes distortion at the edges of the screen. This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV. Note that the rendering will act as if you have 'force shaders' option enabled with this on, which means that shaders will be used to render all objects and the terrain. + +convert alpha test to alpha-to-coverage +--------------------------------------- + +:Type: boolean +:Range: True/False +:Default: False + +Convert the alpha test (cutout/punchthrough alpha) to alpha-to-coverage. +This allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. +When MSAA is off, this setting will have no visible effect, but might have a performance cost. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c8805faa3..6629cb503 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -442,6 +442,11 @@ apply lighting to environment maps = false # This makes fogging independent from the viewing angle. Shaders will be used to render all objects. radial fog = false +# Convert the alpha test (cutout/punchthrough alpha) to alpha-to-coverage. +# This allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. +# When MSAA is off, this setting will have no visible effect, but might have a performance cost. +convert alpha test to alpha-to-coverage = false + [Input] # Capture control of the cursor prevent movement outside the window. diff --git a/files/shaders/alpha.glsl b/files/shaders/alpha.glsl index 0c69df0ab..b1c7ed149 100644 --- a/files/shaders/alpha.glsl +++ b/files/shaders/alpha.glsl @@ -14,25 +14,49 @@ uniform float alphaRef; void alphaTest() { - #if @alphaFunc == FUNC_NEVER - discard; - #elif @alphaFunc == FUNC_LESS - if (gl_FragData[0].a > alphaRef) - discard; - #elif @alphaFunc == FUNC_EQUAL - if (gl_FragData[0].a != alphaRef) - discard; - #elif @alphaFunc == FUNC_LEQUAL - if (gl_FragData[0].a >= alphaRef) - discard; - #elif @alphaFunc == FUNC_GREATER - if (gl_FragData[0].a < alphaRef) - discard; - #elif @alphaFunc == FUNC_NOTEQUAL - if (gl_FragData[0].a == alphaRef) + #if @alphaToCoverage + float coverageAlpha = (gl_FragData[0].a - alphaRef) / max(fwidth(gl_FragData[0].a), 0.0001) + 0.5; + + // Some functions don't make sense with A2C or are a pain to think about and no meshes use them anyway + // Use regular alpha testing in such cases until someone complains. + #if @alphaFunc == FUNC_NEVER discard; - #elif @alphaFunc == FUNC_GEQUAL - if (gl_FragData[0].a <= alphaRef) + #elif @alphaFunc == FUNC_LESS + gl_FragData[0].a = 1.0 - coverageAlpha; + #elif @alphaFunc == FUNC_EQUAL + if (gl_FragData[0].a != alphaRef) + discard; + #elif @alphaFunc == FUNC_LEQUAL + gl_FragData[0].a = 1.0 - coverageAlpha; + #elif @alphaFunc == FUNC_GREATER + gl_FragData[0].a = coverageAlpha; + #elif @alphaFunc == FUNC_NOTEQUAL + if (gl_FragData[0].a == alphaRef) + discard; + #elif @alphaFunc == FUNC_GEQUAL + gl_FragData[0].a = coverageAlpha; + #endif + #else + #if @alphaFunc == FUNC_NEVER discard; + #elif @alphaFunc == FUNC_LESS + if (gl_FragData[0].a > alphaRef) + discard; + #elif @alphaFunc == FUNC_EQUAL + if (gl_FragData[0].a != alphaRef) + discard; + #elif @alphaFunc == FUNC_LEQUAL + if (gl_FragData[0].a >= alphaRef) + discard; + #elif @alphaFunc == FUNC_GREATER + if (gl_FragData[0].a < alphaRef) + discard; + #elif @alphaFunc == FUNC_NOTEQUAL + if (gl_FragData[0].a == alphaRef) + discard; + #elif @alphaFunc == FUNC_GEQUAL + if (gl_FragData[0].a <= alphaRef) + discard; + #endif #endif } \ No newline at end of file From 54d465e3dcb5e5f98cbe12b8f85e6abc394968ea Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 27 Dec 2020 03:07:04 +0000 Subject: [PATCH 015/132] Do all alpha testing early and only once --- files/shaders/objects_fragment.glsl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index c79ad5e19..995c2adaf 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -109,11 +109,14 @@ void main() #if @diffuseMap gl_FragData[0] = texture2D(diffuseMap, adjustedDiffuseUV); - alphaTest(); #else gl_FragData[0] = vec4(1.0); #endif + vec4 diffuseColor = getDiffuseColor(); + gl_FragData[0].a *= diffuseColor.a; + alphaTest(); + #if @detailMap gl_FragData[0].xyz *= texture2D(detailMap, detailMapUV).xyz * 2.0; #endif @@ -152,10 +155,6 @@ void main() #endif - vec4 diffuseColor = getDiffuseColor(); - gl_FragData[0].a *= diffuseColor.a; - alphaTest(); - float shadowing = unshadowedLightRatio(linearDepth); vec3 lighting; #if !PER_PIXEL_LIGHTING From 54853380cdbdeb76195244a5f85ac75a956dd005 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Mon, 28 Dec 2020 22:39:09 +0000 Subject: [PATCH 016/132] Enable multisampling in RTTs to support A2C --- apps/openmw/mwrender/characterpreview.cpp | 3 ++- apps/openmw/mwrender/localmap.cpp | 12 +++++++++++- apps/openmw/mwrender/water.cpp | 24 +++++++++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 89db3e5f4..061f4d509 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -135,7 +136,7 @@ namespace MWRender mCamera->setProjectionMatrixAsPerspective(fovYDegrees, sizeX/static_cast(sizeY), 0.1f, 10000.f); // zNear and zFar are autocomputed mCamera->setViewport(0, 0, sizeX, sizeY); mCamera->setRenderOrder(osg::Camera::PRE_RENDER); - mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture); + mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture, 0, 0, false, Settings::Manager::getInt("antialiasing", "Video")); mCamera->setName("CharacterPreview"); mCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); mCamera->setCullMask(~(Mask_UpdateVisitor)); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 401e21ae4..90c85f39f 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -233,7 +233,17 @@ void LocalMap::setupRenderToTexture(osg::ref_ptr camera, int x, int texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); - camera->attach(osg::Camera::COLOR_BUFFER, texture); + unsigned int samples = 0; + unsigned int colourSamples = 0; + if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders")) + { + // Alpha-to-coverage requires a multisampled framebuffer. + // OSG will set that up automatically and resolve it to the specified single-sample texture for us. + // For some reason, two samples are needed, at least with some drivers. + samples = 2; + colourSamples = 1; + } + camera->attach(osg::Camera::COLOR_BUFFER, texture, 0, 0, false, samples, colourSamples); camera->addChild(mSceneRoot); mRoot->addChild(camera); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index b9018e0a2..3d3ec4ef6 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -271,7 +271,17 @@ public: mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mRefractionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); - attach(osg::Camera::COLOR_BUFFER, mRefractionTexture); + unsigned int samples = 0; + unsigned int colourSamples = 0; + if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders")) + { + // Alpha-to-coverage requires a multisampled framebuffer. + // OSG will set that up automatically and resolve it to the specified single-sample texture for us. + // For some reason, two samples are needed, at least with some drivers. + samples = 2; + colourSamples = 1; + } + attach(osg::Camera::COLOR_BUFFER, mRefractionTexture, 0, 0, false, samples, colourSamples); mRefractionDepthTexture = new osg::Texture2D; mRefractionDepthTexture->setTextureSize(rttSize, rttSize); @@ -356,7 +366,17 @@ public: mReflectionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mReflectionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); - attach(osg::Camera::COLOR_BUFFER, mReflectionTexture); + unsigned int samples = 0; + unsigned int colourSamples = 0; + if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders")) + { + // Alpha-to-coverage requires a multisampled framebuffer. + // OSG will set that up automatically and resolve it to the specified single-sample texture for us. + // For some reason, two samples are needed, at least with some drivers. + samples = 2; + colourSamples = 1; + } + attach(osg::Camera::COLOR_BUFFER, mReflectionTexture, 0, 0, false, samples, colourSamples); // XXX: should really flip the FrontFace on each renderable instead of forcing clockwise. osg::ref_ptr frontFace (new osg::FrontFace); From 9b99c76032bb96002fae6a5d67a41423a935119f Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 1 Jan 2021 16:01:33 +0000 Subject: [PATCH 017/132] Clamp alpha reference to avoid degenerate case --- files/shaders/alpha.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/shaders/alpha.glsl b/files/shaders/alpha.glsl index b1c7ed149..cb38831c5 100644 --- a/files/shaders/alpha.glsl +++ b/files/shaders/alpha.glsl @@ -15,7 +15,7 @@ uniform float alphaRef; void alphaTest() { #if @alphaToCoverage - float coverageAlpha = (gl_FragData[0].a - alphaRef) / max(fwidth(gl_FragData[0].a), 0.0001) + 0.5; + float coverageAlpha = (gl_FragData[0].a - clamp(alphaRef, 0.0001, 0.9999)) / max(fwidth(gl_FragData[0].a), 0.0001) + 0.5; // Some functions don't make sense with A2C or are a pain to think about and no meshes use them anyway // Use regular alpha testing in such cases until someone complains. From c75d7ceada68c67ba62cb6111a850d9f34aa2f19 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 2 Jan 2021 02:50:19 +0000 Subject: [PATCH 018/132] Ensure alpha test is off by default --- apps/openmw/mwrender/renderingmanager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a60077a16..567cd88ac 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -381,6 +381,8 @@ namespace MWRender // Hopefully, anything genuinely requiring the default alpha func of GL_ALWAYS explicitly sets it mRootNode->getOrCreateStateSet()->setAttribute(Shader::RemovedAlphaFunc::getInstance(GL_ALWAYS)); + // The transparent renderbin sets alpha testing on because that was faster on old GPUs. It's now slower and breaks things. + mRootNode->getOrCreateStateSet()->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF); mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near"); mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far"); From 69386df03602b6c1f1b4acc0e72d31f84dcb538d Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 2 Jan 2021 18:27:25 +0000 Subject: [PATCH 019/132] Correct alpha testing functions --- files/shaders/alpha.glsl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/files/shaders/alpha.glsl b/files/shaders/alpha.glsl index cb38831c5..2551a0205 100644 --- a/files/shaders/alpha.glsl +++ b/files/shaders/alpha.glsl @@ -40,22 +40,22 @@ void alphaTest() #if @alphaFunc == FUNC_NEVER discard; #elif @alphaFunc == FUNC_LESS - if (gl_FragData[0].a > alphaRef) + if (gl_FragData[0].a >= alphaRef) discard; #elif @alphaFunc == FUNC_EQUAL if (gl_FragData[0].a != alphaRef) discard; #elif @alphaFunc == FUNC_LEQUAL - if (gl_FragData[0].a >= alphaRef) + if (gl_FragData[0].a > alphaRef) discard; #elif @alphaFunc == FUNC_GREATER - if (gl_FragData[0].a < alphaRef) + if (gl_FragData[0].a <= alphaRef) discard; #elif @alphaFunc == FUNC_NOTEQUAL if (gl_FragData[0].a == alphaRef) discard; #elif @alphaFunc == FUNC_GEQUAL - if (gl_FragData[0].a <= alphaRef) + if (gl_FragData[0].a < alphaRef) discard; #endif #endif From f2eed5594a1ebd46909312eb6bf76742214853ab Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 2 Jan 2021 18:27:46 +0000 Subject: [PATCH 020/132] Don't use A2C when MSAA is off --- apps/openmw/mwrender/localmap.cpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwrender/water.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 90c85f39f..28b7c816b 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -235,7 +235,7 @@ void LocalMap::setupRenderToTexture(osg::ref_ptr camera, int x, int unsigned int samples = 0; unsigned int colourSamples = 0; - if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders")) + if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) { // Alpha-to-coverage requires a multisampled framebuffer. // OSG will set that up automatically and resolve it to the specified single-sample texture for us. diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 567cd88ac..17a6527fc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -219,7 +219,7 @@ namespace MWRender resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders")); resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders")); resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders")); - resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders")); + resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1); osg::ref_ptr sceneRoot = new SceneUtil::LightManager; sceneRoot->setLightingMask(Mask_Lighting); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 3d3ec4ef6..895524881 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -273,7 +273,7 @@ public: unsigned int samples = 0; unsigned int colourSamples = 0; - if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders")) + if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) { // Alpha-to-coverage requires a multisampled framebuffer. // OSG will set that up automatically and resolve it to the specified single-sample texture for us. @@ -368,7 +368,7 @@ public: unsigned int samples = 0; unsigned int colourSamples = 0; - if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders")) + if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) { // Alpha-to-coverage requires a multisampled framebuffer. // OSG will set that up automatically and resolve it to the specified single-sample texture for us. From e3fd5efcfefc22d9323463c7f274252b895c1b01 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 2 Jan 2021 19:09:06 +0000 Subject: [PATCH 021/132] Disable A2C for alpha-blended drawables --- components/shader/shadervisitor.cpp | 14 +++++++++++++- components/shader/shadervisitor.hpp | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 4762549a7..165be6745 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -26,8 +26,10 @@ namespace Shader , mColorMode(0) , mMaterialOverridden(false) , mAlphaTestOverridden(false) + , mAlphaBlendOverridden(false) , mAlphaFunc(GL_ALWAYS) , mAlphaRef(1.0) + , mAlphaBlend(false) , mNormalHeight(false) , mTexStageRequiringTangents(-1) , mNode(nullptr) @@ -330,6 +332,15 @@ namespace Shader } } } + + unsigned int alphaBlend = stateset->getMode(GL_BLEND); + if (alphaBlend != osg::StateAttribute::INHERIT && (!mRequirements.back().mAlphaBlendOverridden || alphaBlend & osg::StateAttribute::PROTECTED)) + { + if (alphaBlend & osg::StateAttribute::OVERRIDE) + mRequirements.back().mAlphaBlendOverridden = true; + + mRequirements.back().mAlphaBlend = alphaBlend & osg::StateAttribute::ON; + } } void ShaderVisitor::pushRequirements(osg::Node& node) @@ -394,7 +405,8 @@ namespace Shader // This prevents redundant glAlphaFunc calls while letting the shadows bin still see the test writableStateSet->setAttribute(RemovedAlphaFunc::getInstance(reqs.mAlphaFunc), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - if (mConvertAlphaTestToAlphaToCoverage) + // Blending won't work with A2C as we use the alpha channel for coverage. gl_SampleCoverage from ARB_sample_shading would save the day, but requires GLSL 130 + if (mConvertAlphaTestToAlphaToCoverage && !reqs.mAlphaBlend) { writableStateSet->setMode(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB, osg::StateAttribute::ON); defineMap["alphaToCoverage"] = "1"; diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index 606e06df9..9daeab29b 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -84,9 +84,11 @@ namespace Shader bool mMaterialOverridden; bool mAlphaTestOverridden; + bool mAlphaBlendOverridden; GLenum mAlphaFunc; float mAlphaRef; + bool mAlphaBlend; bool mNormalHeight; // true if normal map has height info in alpha channel From d061ae809601ae2b258fe6627d456c236f93ffc9 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 2 Jan 2021 23:27:28 +0000 Subject: [PATCH 022/132] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9f3d1816..214159db1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,7 @@ Feature #2404: Levelled List can not be placed into a container Feature #2686: Timestamps in openmw.log Feature #4894: Consider actors as obstacles for pathfinding + Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing Feature #5043: Head Bobbing Feature #5199: Improve Scene Colors Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher From d13459ecf98c4dfe0949d10ad4b5dec73817ab62 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 5 Jan 2021 22:21:54 +0000 Subject: [PATCH 023/132] Scale mipmap alpha to preserve coverage --- files/shaders/alpha.glsl | 23 +++++++++++++++++++++++ files/shaders/objects_fragment.glsl | 3 +++ files/shaders/shadowcasting_fragment.glsl | 2 ++ 3 files changed, 28 insertions(+) diff --git a/files/shaders/alpha.glsl b/files/shaders/alpha.glsl index 2551a0205..6ead9e6ca 100644 --- a/files/shaders/alpha.glsl +++ b/files/shaders/alpha.glsl @@ -12,6 +12,29 @@ uniform float alphaRef; #endif +float mipmapLevel(vec2 scaleduv) +{ + vec2 dUVdx = dFdx(scaleduv); + vec2 dUVdy = dFdy(scaleduv); + float maxDUVSquared = max(dot(dUVdx, dUVdx), dot(dUVdy, dUVdy)); + return max(0.0, 0.5 * log2(maxDUVSquared)); +} + +float coveragePreservingAlphaScale(sampler2D diffuseMap, vec2 uv) +{ + #if @alphaFunc != FUNC_ALWAYS && @alphaFunc != FUNC_NEVER + vec2 textureSize; + #ifdef GL_EXT_gpu_shader4 + textureSize = textureSize2D(diffuseMap, 0); + #else + textureSize = 256.0; + #endif + return 1.0 + mipmapLevel(uv * textureSize) * 0.25; + #else + return 1.0; + #endif +} + void alphaTest() { #if @alphaToCoverage diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 995c2adaf..c5f397789 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -1,5 +1,7 @@ #version 120 +#extension EXT_gpu_shader4: enable + #if @diffuseMap uniform sampler2D diffuseMap; varying vec2 diffuseMapUV; @@ -109,6 +111,7 @@ void main() #if @diffuseMap gl_FragData[0] = texture2D(diffuseMap, adjustedDiffuseUV); + gl_FragData[0].a *= coveragePreservingAlphaScale(diffuseMap, adjustedDiffuseUV); #else gl_FragData[0] = vec4(1.0); #endif diff --git a/files/shaders/shadowcasting_fragment.glsl b/files/shaders/shadowcasting_fragment.glsl index 8c53c542b..ea8a63313 100644 --- a/files/shaders/shadowcasting_fragment.glsl +++ b/files/shaders/shadowcasting_fragment.glsl @@ -1,5 +1,7 @@ #version 120 +#extension EXT_gpu_shader4: enable + uniform sampler2D diffuseMap; varying vec2 diffuseMapUV; From 4ed32520018dff5debf8d66ff7e1d7aa367a6c3b Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 7 Jan 2021 18:13:51 +0000 Subject: [PATCH 024/132] Check for EXT_gpu_shader4 CPU-side Mesa lies and always defines GL_EXT_gpu_shader4 even when the extension isn't present. --- apps/openmw/mwrender/renderingmanager.cpp | 1 + components/sceneutil/mwshadowtechnique.cpp | 7 ++++++- components/shader/shadervisitor.cpp | 7 +++++++ files/shaders/alpha.glsl | 4 ++-- files/shaders/objects_fragment.glsl | 4 +++- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 17a6527fc..95e98f55b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -252,6 +252,7 @@ namespace MWRender globalDefines["clamp"] = Settings::Manager::getBool("clamp lighting", "Shaders") ? "1" : "0"; globalDefines["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0"; globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0"; + globalDefines["useGPUShader4"] = "0"; // It is unnecessary to stop/start the viewer as no frames are being rendered yet. mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines); diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index c781318fa..41e64e124 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -892,12 +892,17 @@ void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & sh // This can't be part of the constructor as OSG mandates that there be a trivial constructor available osg::ref_ptr castingVertexShader = shaderManager.getShader("shadowcasting_vertex.glsl", {}, osg::Shader::VERTEX); + osg::ref_ptr exts = osg::GLExtensions::Get(0, false); + std::string useGPUShader4 = exts && exts->isGpuShader4Supported ? "1" : "0"; for (int alphaFunc = GL_NEVER; alphaFunc <= GL_ALWAYS; ++alphaFunc) { auto& program = _castingPrograms[alphaFunc - GL_NEVER]; program = new osg::Program(); program->addShader(castingVertexShader); - program->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", { {"alphaFunc", std::to_string(alphaFunc)}, {"alphaToCoverage", "0"} }, osg::Shader::FRAGMENT)); + program->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", { {"alphaFunc", std::to_string(alphaFunc)}, + {"alphaToCoverage", "0"}, + {"useGPUShader4", useGPUShader4} + }, osg::Shader::FRAGMENT)); } } diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 165be6745..d719daec2 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -411,6 +412,12 @@ namespace Shader writableStateSet->setMode(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB, osg::StateAttribute::ON); defineMap["alphaToCoverage"] = "1"; } + + // Preventing alpha tested stuff shrinking as lower mip levels are used requires knowing the texture size + osg::ref_ptr exts = osg::GLExtensions::Get(0, false); + if (exts && exts->isGpuShader4Supported) + defineMap["useGPUShader4"] = "1"; + // We could fall back to a texture size uniform if EXT_gpu_shader4 is missing } if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT) diff --git a/files/shaders/alpha.glsl b/files/shaders/alpha.glsl index 6ead9e6ca..05be801e9 100644 --- a/files/shaders/alpha.glsl +++ b/files/shaders/alpha.glsl @@ -24,10 +24,10 @@ float coveragePreservingAlphaScale(sampler2D diffuseMap, vec2 uv) { #if @alphaFunc != FUNC_ALWAYS && @alphaFunc != FUNC_NEVER vec2 textureSize; - #ifdef GL_EXT_gpu_shader4 + #if @useGPUShader4 textureSize = textureSize2D(diffuseMap, 0); #else - textureSize = 256.0; + textureSize = vec2(256.0); #endif return 1.0 + mipmapLevel(uv * textureSize) * 0.25; #else diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index c5f397789..37cca273f 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -1,6 +1,8 @@ #version 120 -#extension EXT_gpu_shader4: enable +#if @useGPUShader4 + #extension EXT_gpu_shader4: require +#endif #if @diffuseMap uniform sampler2D diffuseMap; From 0068c7bb252428571c1f6ae4ca57220873d0c701 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 8 Jan 2021 17:32:15 +0000 Subject: [PATCH 025/132] Spec says GL_ --- files/shaders/objects_fragment.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 37cca273f..693c04584 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -1,7 +1,7 @@ #version 120 #if @useGPUShader4 - #extension EXT_gpu_shader4: require + #extension GL_EXT_gpu_shader4: require #endif #if @diffuseMap From b8ee32e3173c9226d92cf0d9f60a07fe1c75a479 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 14 Feb 2021 23:32:04 +0000 Subject: [PATCH 026/132] Support A2C for groundcover --- files/shaders/groundcover_fragment.glsl | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/files/shaders/groundcover_fragment.glsl b/files/shaders/groundcover_fragment.glsl index 77fd32e58..c3339ba76 100644 --- a/files/shaders/groundcover_fragment.glsl +++ b/files/shaders/groundcover_fragment.glsl @@ -30,11 +30,7 @@ centroid varying vec3 shadowDiffuseLighting; #include "shadows_fragment.glsl" #include "lighting.glsl" - -float calc_coverage(float a, float alpha_ref, float falloff_rate) -{ - return clamp(falloff_rate * (a - alpha_ref) + alpha_ref, 0.0, 1.0); -} +#include "alpha.glsl" void main() { @@ -55,12 +51,13 @@ void main() gl_FragData[0] = vec4(1.0); #endif - gl_FragData[0].a = calc_coverage(gl_FragData[0].a, 128.0/255.0, 4.0); - - float shadowing = unshadowedLightRatio(linearDepth); if (euclideanDepth > @groundcoverFadeStart) gl_FragData[0].a *= 1.0-smoothstep(@groundcoverFadeStart, @groundcoverFadeEnd, euclideanDepth); + alphaTest(); + + float shadowing = unshadowedLightRatio(linearDepth); + vec3 lighting; #if !PER_PIXEL_LIGHTING lighting = passLighting + shadowDiffuseLighting * shadowing; From 9be258d26009ed7a3a1d03ca7d0d66455fd7beca Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 19 Feb 2021 19:59:48 +0000 Subject: [PATCH 027/132] Make it possible to reinstate FFP state easily --- apps/openmw/mwrender/groundcover.cpp | 2 ++ components/resource/scenemanager.cpp | 6 +++++ components/resource/scenemanager.hpp | 7 ++++- components/shader/shadervisitor.cpp | 39 ++++++++++++++++++++++++++++ components/shader/shadervisitor.hpp | 11 ++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/groundcover.cpp b/apps/openmw/mwrender/groundcover.cpp index 049118c90..4390dae22 100644 --- a/apps/openmw/mwrender/groundcover.cpp +++ b/apps/openmw/mwrender/groundcover.cpp @@ -258,6 +258,8 @@ namespace MWRender // 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); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 9d36f1cae..e46ce2016 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -257,6 +257,12 @@ namespace Resource node->accept(*shaderVisitor); } + void SceneManager::reinstateRemovedState(osg::ref_ptr node) + { + osg::ref_ptr reinstateRemovedStateVisitor = new Shader::ReinstateRemovedStateVisitor(false); + node->accept(*reinstateRemovedStateVisitor); + } + void SceneManager::setClampLighting(bool clamp) { mClampLighting = clamp; diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 0648ce06f..bf69a8c4b 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -75,9 +75,14 @@ namespace Resource Shader::ShaderManager& getShaderManager(); - /// Re-create shaders for this node, need to call this if texture stages or vertex color mode have changed. + /// Re-create shaders for this node, need to call this if alpha testing, texture stages or vertex color mode have changed. void recreateShaders(osg::ref_ptr node, const std::string& shaderPrefix = "objects", bool translucentFramebuffer = false, bool forceShadersForNode = false); + /// Applying shaders to a node may replace some fixed-function state. + /// This restores it. + /// When editing such state, it should be reinstated before the edits, and shaders should be recreated afterwards. + void reinstateRemovedState(osg::ref_ptr node); + /// @see ShaderVisitor::setForceShaders void setForceShaders(bool force); bool getForceShaders() const; diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index a79e24f8b..782ffa016 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -614,4 +614,43 @@ namespace Shader mTranslucentFramebuffer = translucent; } + ReinstateRemovedStateVisitor::ReinstateRemovedStateVisitor(bool allowedToModifyStateSets) + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mAllowedToModifyStateSets(allowedToModifyStateSets) + { + } + + void ReinstateRemovedStateVisitor::apply(osg::Node& node) + { + if (node.getStateSet()) + { + osg::ref_ptr removedState = getRemovedState(*node.getStateSet()); + if (removedState) + { + osg::ref_ptr writableStateSet; + if (mAllowedToModifyStateSets) + writableStateSet = node.getStateSet(); + else + writableStateSet = getWritableStateSet(node); + + // user data is normally shallow copied so shared with the original stateset + osg::ref_ptr writableUserData; + if (mAllowedToModifyStateSets) + writableUserData = writableStateSet->getUserDataContainer(); + else + writableUserData = getWritableUserDataContainer(*writableStateSet); + unsigned int index = writableUserData->getUserObjectIndex("removedState"); + writableUserData->removeUserObject(index); + + for (const auto&[mode, value] : removedState->getModeList()) + writableStateSet->setMode(mode, value); + + for (const auto& attribute : removedState->getAttributeList()) + writableStateSet->setAttribute(attribute.second.first, attribute.second.second); + } + } + + traverse(node); + } + } diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index daf42ff26..f7c6f8312 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -112,6 +112,17 @@ namespace Shader bool adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs); }; + class ReinstateRemovedStateVisitor : public osg::NodeVisitor + { + public: + ReinstateRemovedStateVisitor(bool allowedToModifyStateSets); + + void apply(osg::Node& node) override; + + private: + bool mAllowedToModifyStateSets; + }; + } #endif From 153ab57ae38f4930d8e60312b1edd437b8ea0163 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 24 Feb 2021 17:45:44 +0000 Subject: [PATCH 028/132] Make assignment in while loop condition obviously intentional --- components/sceneutil/shadowsbin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp index 26cbd58c3..5a4096f5c 100644 --- a/components/sceneutil/shadowsbin.cpp +++ b/components/sceneutil/shadowsbin.cpp @@ -185,7 +185,7 @@ void ShadowsBin::sortImplementation() root = root->_parent; const osg::StateSet* ss = root->getStateSet(); if (ss->getMode(GL_NORMALIZE) & osg::StateAttribute::ON // that is root stategraph of renderingmanager cpp - || ss->getAttribute(osg::StateAttribute::VIEWPORT)) // fallback to rendertargets sg just in case + || ss->getAttribute(osg::StateAttribute::VIEWPORT)) // fallback to rendertarget's sg just in case break; if (!root->_parent) return; @@ -194,7 +194,7 @@ void ShadowsBin::sortImplementation() // noTestRoot is now a stategraph with useDiffuseMapForShadowAlpha disabled but minimal other state bool cullFaceOverridden = false; - while (root = root->_parent) + while ((root = root->_parent)) { if (!root->getStateSet()) continue; From 4ed67d8597520a0a4e24213125550e7b5e1b2ef9 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 24 Feb 2021 18:01:06 +0000 Subject: [PATCH 029/132] Improve A2C setting name --- apps/openmw/mwrender/localmap.cpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwrender/water.cpp | 4 ++-- docs/source/reference/modding/settings/shaders.rst | 4 ++-- files/settings-default.cfg | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index f4845fa2b..a487fb1e9 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -239,7 +239,7 @@ void LocalMap::setupRenderToTexture(osg::ref_ptr camera, int x, int unsigned int samples = 0; unsigned int colourSamples = 0; - if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) + if (Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) { // Alpha-to-coverage requires a multisampled framebuffer. // OSG will set that up automatically and resolve it to the specified single-sample texture for us. diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 4770746c7..9f89d1bff 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -213,7 +213,7 @@ namespace MWRender resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders")); resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders")); resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders")); - resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1); + resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1); osg::ref_ptr sceneRoot = new SceneUtil::LightManager; sceneRoot->setLightingMask(Mask_Lighting); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index ed1fcb98f..0ab3de7ef 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -273,7 +273,7 @@ public: unsigned int samples = 0; unsigned int colourSamples = 0; - if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) + if (Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) { // Alpha-to-coverage requires a multisampled framebuffer. // OSG will set that up automatically and resolve it to the specified single-sample texture for us. @@ -368,7 +368,7 @@ public: unsigned int samples = 0; unsigned int colourSamples = 0; - if (Settings::Manager::getBool("convert alpha test to alpha-to-coverage", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) + if (Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) { // Alpha-to-coverage requires a multisampled framebuffer. // OSG will set that up automatically and resolve it to the specified single-sample texture for us. diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index 657f7bfc6..acc848299 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -148,13 +148,13 @@ By default, the fog becomes thicker proportionally to your distance from the cli This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV. Note that the rendering will act as if you have 'force shaders' option enabled with this on, which means that shaders will be used to render all objects and the terrain. -convert alpha test to alpha-to-coverage +antialias alpha test --------------------------------------- :Type: boolean :Range: True/False :Default: False -Convert the alpha test (cutout/punchthrough alpha) to alpha-to-coverage. +Convert the alpha test (cutout/punchthrough alpha) to alpha-to-coverage when :ref:`antialiasing` is on. This allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. When MSAA is off, this setting will have no visible effect, but might have a performance cost. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 9c8835646..d439c4635 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -445,7 +445,7 @@ radial fog = false # Convert the alpha test (cutout/punchthrough alpha) to alpha-to-coverage. # This allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. # When MSAA is off, this setting will have no visible effect, but might have a performance cost. -convert alpha test to alpha-to-coverage = false +antialias alpha test = false [Input] From f5a87ee46def4892ee736790fced2435cfe63dd4 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 26 Feb 2021 19:01:27 +0000 Subject: [PATCH 030/132] Refactor out duplicated RTT setup code --- apps/openmw/mwrender/localmap.cpp | 13 ++----------- apps/openmw/mwrender/water.cpp | 25 +++---------------------- components/sceneutil/util.cpp | 16 ++++++++++++++++ components/sceneutil/util.hpp | 4 ++++ 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index a487fb1e9..64931aa88 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "../mwbase/environment.hpp" @@ -237,17 +238,7 @@ void LocalMap::setupRenderToTexture(osg::ref_ptr camera, int x, int texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); - unsigned int samples = 0; - unsigned int colourSamples = 0; - if (Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) - { - // Alpha-to-coverage requires a multisampled framebuffer. - // OSG will set that up automatically and resolve it to the specified single-sample texture for us. - // For some reason, two samples are needed, at least with some drivers. - samples = 2; - colourSamples = 1; - } - camera->attach(osg::Camera::COLOR_BUFFER, texture, 0, 0, false, samples, colourSamples); + SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, texture); camera->addChild(mSceneRoot); mRoot->addChild(camera); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 0ab3de7ef..71b1b4985 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -271,17 +272,7 @@ public: mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mRefractionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); - unsigned int samples = 0; - unsigned int colourSamples = 0; - if (Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) - { - // Alpha-to-coverage requires a multisampled framebuffer. - // OSG will set that up automatically and resolve it to the specified single-sample texture for us. - // For some reason, two samples are needed, at least with some drivers. - samples = 2; - colourSamples = 1; - } - attach(osg::Camera::COLOR_BUFFER, mRefractionTexture, 0, 0, false, samples, colourSamples); + SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(this, osg::Camera::COLOR_BUFFER, mRefractionTexture); mRefractionDepthTexture = new osg::Texture2D; mRefractionDepthTexture->setTextureSize(rttSize, rttSize); @@ -366,17 +357,7 @@ public: mReflectionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mReflectionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); - unsigned int samples = 0; - unsigned int colourSamples = 0; - if (Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) - { - // Alpha-to-coverage requires a multisampled framebuffer. - // OSG will set that up automatically and resolve it to the specified single-sample texture for us. - // For some reason, two samples are needed, at least with some drivers. - samples = 2; - colourSamples = 1; - } - attach(osg::Camera::COLOR_BUFFER, mReflectionTexture, 0, 0, false, samples, colourSamples); + SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(this, osg::Camera::COLOR_BUFFER, mReflectionTexture); // XXX: should really flip the FrontFace on each renderable instead of forcing clockwise. osg::ref_ptr frontFace (new osg::FrontFace); diff --git a/components/sceneutil/util.cpp b/components/sceneutil/util.cpp index 2c0d8efa0..8a381681b 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -11,6 +11,7 @@ #include #include +#include namespace SceneUtil { @@ -260,4 +261,19 @@ osg::ref_ptr addEnchantedGlow(osg::ref_ptr node, Resourc return glowUpdater; } +bool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg::Camera::BufferComponent buffer, osg::Texture * texture, unsigned int level, unsigned int face, bool mipMapGeneration) +{ + unsigned int samples = 0; + unsigned int colourSamples = 0; + if (Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) + { + // Alpha-to-coverage requires a multisampled framebuffer. + // OSG will set that up automatically and resolve it to the specified single-sample texture for us. + // For some reason, two samples are needed, at least with some drivers. + samples = 2; + colourSamples = 1; + } + camera->attach(buffer, texture, level, face, mipMapGeneration, samples, colourSamples); +} + } diff --git a/components/sceneutil/util.hpp b/components/sceneutil/util.hpp index 303d609f5..8103ed87a 100644 --- a/components/sceneutil/util.hpp +++ b/components/sceneutil/util.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -60,6 +61,9 @@ namespace SceneUtil bool hasUserDescription(const osg::Node* node, const std::string pattern); osg::ref_ptr addEnchantedGlow(osg::ref_ptr node, Resource::ResourceSystem* resourceSystem, osg::Vec4f glowColor, float glowDuration=-1); + + // Alpha-to-coverage requires a multisampled framebuffer, so we need to set that up for RTTs + bool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg::Camera::BufferComponent buffer, osg::Texture* texture, unsigned int level = 0, unsigned int face = 0, bool mipMapGeneration = false); } #endif From 46a1715d8a63230c04755a39c34cddae8f6e6fb2 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 26 Feb 2021 20:10:58 +0000 Subject: [PATCH 031/132] Actually return something --- components/sceneutil/util.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/sceneutil/util.cpp b/components/sceneutil/util.cpp index 8a381681b..fa3c7d26d 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -265,7 +265,8 @@ bool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg:: { unsigned int samples = 0; unsigned int colourSamples = 0; - if (Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1) + bool addMSAAIntermediateTarget = Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1; + if (addMSAAIntermediateTarget) { // Alpha-to-coverage requires a multisampled framebuffer. // OSG will set that up automatically and resolve it to the specified single-sample texture for us. @@ -274,6 +275,7 @@ bool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg:: colourSamples = 1; } camera->attach(buffer, texture, level, face, mipMapGeneration, samples, colourSamples); + return addMSAAIntermediateTarget; } } From 044e7840726b19bc06b792954b9763238e9661a7 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Wed, 27 Jan 2021 00:10:50 +0000 Subject: [PATCH 032/132] gl4es: Delay feature detection until a context exists gl4es feature detection does not work reliably with EGL. If a context already exists, gl4es can instead reliably detect the underlying GLES features from the context itself. This requires gl4es to be configured with: -DNOEGL=ON -DNO_LOADER=ON -DNO_INIT_CONSTRUCTOR=ON This also requires gl4es to have this fix: https://github.com/ptitSeb/gl4es/pull/271 --- CMakeLists.txt | 5 ++++ components/CMakeLists.txt | 2 +- components/sdlutil/gl4es_init.cpp | 36 ++++++++++++++++++++++++ components/sdlutil/gl4es_init.h | 13 +++++++++ components/sdlutil/sdlgraphicswindow.cpp | 10 ++++++- 5 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 components/sdlutil/gl4es_init.cpp create mode 100644 components/sdlutil/gl4es_init.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fdac00d1e..14820b075 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,11 @@ endif() # Detect OS include(cmake/OSIdentity.cmake) +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) +endif() + # Apps and tools option(BUILD_OPENMW "Build OpenMW" ON) option(BUILD_LAUNCHER "Build Launcher" ON) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2120afc1a..95150a597 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -140,7 +140,7 @@ add_component_dir (fontloader ) add_component_dir (sdlutil - sdlgraphicswindow imagetosurface sdlinputwrapper sdlvideowrapper events sdlcursormanager + gl4es_init sdlgraphicswindow imagetosurface sdlinputwrapper sdlvideowrapper events sdlcursormanager ) add_component_dir (version diff --git a/components/sdlutil/gl4es_init.cpp b/components/sdlutil/gl4es_init.cpp new file mode 100644 index 000000000..bf9e007b6 --- /dev/null +++ b/components/sdlutil/gl4es_init.cpp @@ -0,0 +1,36 @@ +// EGL does not work reliably for feature detection. +// Instead, we initialize gl4es manually. +#ifdef OPENMW_GL4ES_MANUAL_INIT +#include "gl4es_init.h" + +// For glHint +#include + +extern "C" { + +#include +#include + +static SDL_Window *gWindow; + +void openmw_gl4es_GetMainFBSize(int *width, int *height) +{ + SDL_GetWindowSize(gWindow, width, height); +} + +void openmw_gl4es_init(SDL_Window *window) +{ + gWindow = window; + set_getprocaddress(SDL_GL_GetProcAddress); + set_getmainfbsize(openmw_gl4es_GetMainFBSize); + initialize_gl4es(); + + // merge glBegin/glEnd in beams and console + glHint(GL_BEGINEND_HINT_GL4ES, 1); + // dxt unpacked to 16-bit looks ugly + glHint(GL_AVOID16BITS_HINT_GL4ES, 1); +} + +} // extern "C" + +#endif // OPENMW_GL4ES_MANUAL_INIT diff --git a/components/sdlutil/gl4es_init.h b/components/sdlutil/gl4es_init.h new file mode 100644 index 000000000..11371e7ae --- /dev/null +++ b/components/sdlutil/gl4es_init.h @@ -0,0 +1,13 @@ +#ifndef OPENMW_COMPONENTS_SDLUTIL_GL4ES_INIT_H +#define OPENMW_COMPONENTS_SDLUTIL_GL4ES_INIT_H +#ifdef OPENMW_GL4ES_MANUAL_INIT +#include + +// Must be called once SDL video mode has been set, +// which creates a context. +// +// GL4ES can then query the context for features and extensions. +extern "C" void openmw_gl4es_init(SDL_Window *window); + +#endif // OPENMW_GL4ES_MANUAL_INIT +#endif // OPENMW_COMPONENTS_SDLUTIL_GL4ES_INIT_H diff --git a/components/sdlutil/sdlgraphicswindow.cpp b/components/sdlutil/sdlgraphicswindow.cpp index ad7ecd9ae..43284c216 100644 --- a/components/sdlutil/sdlgraphicswindow.cpp +++ b/components/sdlutil/sdlgraphicswindow.cpp @@ -2,6 +2,10 @@ #include +#ifdef OPENMW_GL4ES_MANUAL_INIT +#include "gl4es_init.h" +#endif + namespace SDLUtil { @@ -91,7 +95,7 @@ void GraphicsWindowSDL2::init() SDL_Window *oldWin = SDL_GL_GetCurrentWindow(); SDL_GLContext oldCtx = SDL_GL_GetCurrentContext(); -#if defined(ANDROID) +#if defined(ANDROID) || defined(OPENMW_GL4ES_MANUAL_INIT) int major = 1; int minor = 1; char *ver = getenv("OPENMW_GLES_VERSION"); @@ -116,6 +120,10 @@ void GraphicsWindowSDL2::init() return; } +#ifdef OPENMW_GL4ES_MANUAL_INIT + openmw_gl4es_init(mWindow); +#endif + setSwapInterval(_traits->vsync); // Update traits with what we've actually been given From 4495b67d77c958a9c33c70111dac04613a8f9701 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 23 Jan 2021 13:49:16 +0000 Subject: [PATCH 033/132] MyGUI HEAD compatibility Makes OpenMW compatible with the current MyGUI HEAD at: https://github.com/MyGUI/mygui/commit/f93d4fb614f843390cfe3492586466dc8d06c4b3 Refs #5806 --- apps/openmw/mwgui/bookpage.hpp | 2 +- components/myguiplatform/myguicompat.h | 12 ++++++++++ components/myguiplatform/myguidatamanager.cpp | 8 +++---- components/myguiplatform/myguidatamanager.hpp | 11 +++++---- .../myguiplatform/myguirendermanager.cpp | 15 ++++++++++-- .../myguiplatform/myguirendermanager.hpp | 14 ++++++++--- components/myguiplatform/myguitexture.cpp | 19 ++++----------- components/myguiplatform/myguitexture.hpp | 23 ++++++++++++++----- components/myguiplatform/scalinglayer.cpp | 6 +++-- 9 files changed, 72 insertions(+), 38 deletions(-) create mode 100644 components/myguiplatform/myguicompat.h diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index 4e49b8f67..f9fd93089 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -53,7 +53,7 @@ namespace MWGui { static const int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); - MyGUI::GlyphInfo* gi = font->getGlyphInfo(ch); + const MyGUI::GlyphInfo* gi = font->getGlyphInfo(ch); if (gi) { const float scale = font->getDefaultHeight() / (float) fontHeight; diff --git a/components/myguiplatform/myguicompat.h b/components/myguiplatform/myguicompat.h new file mode 100644 index 000000000..04ca11a79 --- /dev/null +++ b/components/myguiplatform/myguicompat.h @@ -0,0 +1,12 @@ +#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUICOMPAT_H +#define OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUICOMPAT_H + +#include + +#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0) + #define OPENMW_MYGUI_CONST_GETTER_3_4_1 const +#else + #define OPENMW_MYGUI_CONST_GETTER_3_4_1 +#endif + +#endif // OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUICOMPAT_H diff --git a/components/myguiplatform/myguidatamanager.cpp b/components/myguiplatform/myguidatamanager.cpp index c90e09221..0310e996b 100644 --- a/components/myguiplatform/myguidatamanager.cpp +++ b/components/myguiplatform/myguidatamanager.cpp @@ -15,7 +15,7 @@ void DataManager::setResourcePath(const std::string &path) mResourcePath = path; } -MyGUI::IDataStream *DataManager::getData(const std::string &name) +MyGUI::IDataStream *DataManager::getData(const std::string &name) OPENMW_MYGUI_CONST_GETTER_3_4_1 { std::string fullpath = getDataPath(name); std::unique_ptr stream; @@ -34,13 +34,13 @@ void DataManager::freeData(MyGUI::IDataStream *data) delete data; } -bool DataManager::isDataExist(const std::string &name) +bool DataManager::isDataExist(const std::string &name) OPENMW_MYGUI_CONST_GETTER_3_4_1 { std::string fullpath = mResourcePath + "/" + name; return boost::filesystem::exists(fullpath); } -const MyGUI::VectorString &DataManager::getDataListNames(const std::string &pattern) +const MyGUI::VectorString &DataManager::getDataListNames(const std::string &pattern) OPENMW_MYGUI_CONST_GETTER_3_4_1 { // TODO: pattern matching (unused?) static MyGUI::VectorString strings; @@ -49,7 +49,7 @@ const MyGUI::VectorString &DataManager::getDataListNames(const std::string &patt return strings; } -const std::string &DataManager::getDataPath(const std::string &name) +const std::string &DataManager::getDataPath(const std::string &name) OPENMW_MYGUI_CONST_GETTER_3_4_1 { static std::string result; result.clear(); diff --git a/components/myguiplatform/myguidatamanager.hpp b/components/myguiplatform/myguidatamanager.hpp index a97c6ad2e..ca2f94899 100644 --- a/components/myguiplatform/myguidatamanager.hpp +++ b/components/myguiplatform/myguidatamanager.hpp @@ -3,10 +3,11 @@ #include +#include "myguicompat.h" + namespace osgMyGUI { - class DataManager : public MyGUI::DataManager { public: @@ -18,7 +19,7 @@ public: /** Get data stream from specified resource name. @param _name Resource name (usually file name). */ - MyGUI::IDataStream* getData(const std::string& _name) override; + MyGUI::IDataStream* getData(const std::string& _name) OPENMW_MYGUI_CONST_GETTER_3_4_1 override; /** Free data stream. @param _data Data stream. @@ -28,18 +29,18 @@ public: /** Is data with specified name exist. @param _name Resource name. */ - bool isDataExist(const std::string& _name) override; + bool isDataExist(const std::string& _name) OPENMW_MYGUI_CONST_GETTER_3_4_1 override; /** Get all data names with names that matches pattern. @param _pattern Pattern to match (for example "*.layout"). */ - const MyGUI::VectorString& getDataListNames(const std::string& _pattern) override; + const MyGUI::VectorString& getDataListNames(const std::string& _pattern) OPENMW_MYGUI_CONST_GETTER_3_4_1 override; /** Get full path to data. @param _name Resource name. @return Return full path to specified data. */ - const std::string& getDataPath(const std::string& _name) override; + const std::string& getDataPath(const std::string& _name) OPENMW_MYGUI_CONST_GETTER_3_4_1 override; private: std::string mResourcePath; diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index dc771f11f..1813c9e01 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -14,6 +14,7 @@ #include +#include "myguicompat.h" #include "myguitexture.hpp" #define MYGUI_PLATFORM_LOG_SECTION "Platform" @@ -263,7 +264,7 @@ public: osg::VertexBufferObject* getVertexBuffer(); void setVertexCount(size_t count) override; - size_t getVertexCount() override; + size_t getVertexCount() OPENMW_MYGUI_CONST_GETTER_3_4_1 override; MyGUI::Vertex *lock() override; void unlock() override; @@ -290,7 +291,7 @@ void OSGVertexBuffer::setVertexCount(size_t count) mNeedVertexCount = count; } -size_t OSGVertexBuffer::getVertexCount() +size_t OSGVertexBuffer::getVertexCount() OPENMW_MYGUI_CONST_GETTER_3_4_1 { return mNeedVertexCount; } @@ -560,4 +561,14 @@ bool RenderManager::checkTexture(MyGUI::ITexture* _texture) return true; } +#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0) +void RenderManager::registerShader( + const std::string& _shaderName, + const std::string& _vertexProgramFile, + const std::string& _fragmentProgramFile) +{ + MYGUI_PLATFORM_LOG(Warning, "osgMyGUI::RenderManager::registerShader is not implemented"); +} +#endif + } diff --git a/components/myguiplatform/myguirendermanager.hpp b/components/myguiplatform/myguirendermanager.hpp index 72abebd18..3c3fb672d 100644 --- a/components/myguiplatform/myguirendermanager.hpp +++ b/components/myguiplatform/myguirendermanager.hpp @@ -5,6 +5,8 @@ #include +#include "myguicompat.h" + namespace Resource { class ImageManager; @@ -70,7 +72,8 @@ public: const MyGUI::IntSize& getViewSize() const override { return mViewSize; } /** @see RenderManager::getVertexFormat */ - MyGUI::VertexColourType getVertexFormat() override { return mVertexFormat; } + MyGUI::VertexColourType getVertexFormat() OPENMW_MYGUI_CONST_GETTER_3_4_1 override + { return mVertexFormat; } /** @see RenderManager::isFormatSupported */ bool isFormatSupported(MyGUI::PixelFormat format, MyGUI::TextureUsage usage) override; @@ -102,17 +105,22 @@ public: void setInjectState(osg::StateSet* stateSet); /** @see IRenderTarget::getInfo */ - const MyGUI::RenderTargetInfo& getInfo() override { return mInfo; } + const MyGUI::RenderTargetInfo& getInfo() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mInfo; } bool checkTexture(MyGUI::ITexture* _texture); // setViewSize() is a part of MyGUI::RenderManager interface since 3.4.0 release -#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,4,0) +#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3, 4, 0) void setViewSize(int width, int height); #else void setViewSize(int width, int height) override; #endif + // registerShader() is a part of MyGUI::RenderManager interface since 3.4.1 release +#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0) + void registerShader(const std::string& _shaderName, const std::string& _vertexProgramFile, const std::string& _fragmentProgramFile) override; +#endif + /*internal:*/ void collectDrawCalls(); diff --git a/components/myguiplatform/myguitexture.cpp b/components/myguiplatform/myguitexture.cpp index 598f5a14e..ce7332cc7 100644 --- a/components/myguiplatform/myguitexture.cpp +++ b/components/myguiplatform/myguitexture.cpp @@ -115,16 +115,6 @@ namespace osgMyGUI Log(Debug::Warning) << "Would save image to file " << fname; } - int OSGTexture::getWidth() - { - return mWidth; - } - - int OSGTexture::getHeight() - { - return mHeight; - } - void *OSGTexture::lock(MyGUI::TextureUsage /*access*/) { if (!mTexture.valid()) @@ -167,15 +157,14 @@ namespace osgMyGUI mLockedImage = nullptr; } - bool OSGTexture::isLocked() - { - return mLockedImage.valid(); - } - // Render-to-texture not currently implemented. MyGUI::IRenderTarget* OSGTexture::getRenderTarget() { return nullptr; } +#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0) + void OSGTexture::setShader(const std::string& _shaderName) + { Log(Debug::Warning) << "OSGTexture::setShader is not implemented"; } +#endif } diff --git a/components/myguiplatform/myguitexture.hpp b/components/myguiplatform/myguitexture.hpp index 6baeb7459..a34f1b762 100644 --- a/components/myguiplatform/myguitexture.hpp +++ b/components/myguiplatform/myguitexture.hpp @@ -5,6 +5,12 @@ #include +#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0) + #define OPENMW_MYGUI_CONST_GETTER_3_4_1 const +#else + #define OPENMW_MYGUI_CONST_GETTER_3_4_1 +#endif + namespace osg { class Image; @@ -47,17 +53,22 @@ namespace osgMyGUI void* lock(MyGUI::TextureUsage access) override; void unlock() override; - bool isLocked() override; + bool isLocked() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mLockedImage.valid(); } - int getWidth() override; - int getHeight() override; + int getWidth() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mWidth; } + int getHeight() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mHeight; } - MyGUI::PixelFormat getFormat() override { return mFormat; } - MyGUI::TextureUsage getUsage() override { return mUsage; } - size_t getNumElemBytes() override { return mNumElemBytes; } + MyGUI::PixelFormat getFormat() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mFormat; } + MyGUI::TextureUsage getUsage() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mUsage; } + size_t getNumElemBytes() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mNumElemBytes; } MyGUI::IRenderTarget *getRenderTarget() override; + // setShader() is a part of MyGUI::RenderManager interface since 3.4.1 release +#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0) + void setShader(const std::string& _shaderName) override; +#endif + /*internal:*/ osg::Texture2D *getTexture() const { return mTexture.get(); } }; diff --git a/components/myguiplatform/scalinglayer.cpp b/components/myguiplatform/scalinglayer.cpp index 07a5161b2..99ed6d07a 100644 --- a/components/myguiplatform/scalinglayer.cpp +++ b/components/myguiplatform/scalinglayer.cpp @@ -3,6 +3,8 @@ #include #include +#include "myguicompat.h" + namespace osgMyGUI { @@ -37,7 +39,7 @@ namespace osgMyGUI mTarget->doRender(_buffer, _texture, _count); } - const MyGUI::RenderTargetInfo& getInfo() override + const MyGUI::RenderTargetInfo& getInfo() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { mInfo = mTarget->getInfo(); mInfo.hOffset = mHOffset; @@ -51,7 +53,7 @@ namespace osgMyGUI MyGUI::IRenderTarget* mTarget; MyGUI::IntSize mViewSize; float mHOffset, mVOffset; - MyGUI::RenderTargetInfo mInfo; + mutable MyGUI::RenderTargetInfo mInfo; }; MyGUI::ILayerItem *ScalingLayer::getLayerItemByPoint(int _left, int _top) const From 8a1644885bda27a65c86133b01737e8adcd51fa9 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 20 Feb 2021 11:20:49 +0000 Subject: [PATCH 034/132] MyGUI: Bump version to 3.4.1 Also set MYGUI_DONT_USE_OBSOLETE --- extern/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index b4182a105..d746183b0 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -59,6 +59,7 @@ if(NOT OPENMW_USE_SYSTEM_MYGUI) set(MYGUI_BUILD_DEMOS OFF CACHE BOOL "") set(MYGUI_BUILD_PLUGINS OFF CACHE BOOL "") set(MYGUI_BUILD_TOOLS OFF CACHE BOOL "") + set(MYGUI_DONT_USE_OBSOLETE ON CACHE BOOL "") if(MYGUI_STATIC) set(BUILD_SHARED_LIBS OFF) @@ -68,8 +69,8 @@ if(NOT OPENMW_USE_SYSTEM_MYGUI) include(FetchContent) FetchContent_Declare(mygui - URL https://github.com/MyGUI/mygui/archive/MyGUI3.4.0.zip - URL_HASH MD5=9e990a4240430cbf567bfe73488a274e + URL https://github.com/MyGUI/mygui/archive/MyGUI3.4.1.zip + URL_HASH MD5=952d4033854612c99a5d9bf4b8550c26 SOURCE_DIR fetched/mygui ) FetchContent_MakeAvailableExcludeFromAll(mygui) From 28cb14289ae97f77ca802fda44256c090f714cd1 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 21 Feb 2021 01:03:36 +0100 Subject: [PATCH 035/132] initial attempt at FindRecastNavigation.cmake --- CI/install_debian_deps.sh | 1 + CMakeLists.txt | 4 + cmake/FindRecastNavigation.cmake | 143 +++++++++++++++++++++++++++++++ components/CMakeLists.txt | 5 +- 4 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 cmake/FindRecastNavigation.cmake diff --git a/CI/install_debian_deps.sh b/CI/install_debian_deps.sh index 82d2ff681..3e7ab7fca 100755 --- a/CI/install_debian_deps.sh +++ b/CI/install_debian_deps.sh @@ -23,6 +23,7 @@ declare -rA GROUPED_DEPS=( libsdl2-dev libqt5opengl5-dev libopenal-dev libunshield-dev libtinyxml-dev libbullet-dev liblz4-dev libpng-dev libjpeg-dev " + # TODO: add librecastnavigation-dev when debian is ready # These dependencies can alternatively be built and linked statically. [openmw-deps-dynamic]="libmygui-dev libopenscenegraph-dev" diff --git a/CMakeLists.txt b/CMakeLists.txt index fdac00d1e..405f4e874 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,10 @@ option(MYGUI_STATIC "Link static build of Mygui into the binaries" ${_mygui_stat option(OPENMW_USE_SYSTEM_RECASTNAVIGATION "Use system provided recastnavigation library" OFF) if(OPENMW_USE_SYSTEM_RECASTNAVIGATION) set(_recastnavigation_static_default OFF) + + find_package(RecastNavigation REQUIRED) + include_directories(SYSTEM ${RecastNavigation_INCLUDE_DIRS}) + else() set(_recastnavigation_static_default ON) endif() diff --git a/cmake/FindRecastNavigation.cmake b/cmake/FindRecastNavigation.cmake new file mode 100644 index 000000000..ae85faaaa --- /dev/null +++ b/cmake/FindRecastNavigation.cmake @@ -0,0 +1,143 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindRecastNavigation +------- + +Find the RecastNavigation include directory and library. + +Use this module by invoking find_package with the form:: + +.. code-block:: cmake + + find_package(RecastNavigation + [version] # Minimum version e.g. 1.8.0 + [REQUIRED] # Fail with error if RECASTNAV is not found + ) + +Imported targets +^^^^^^^^^^^^^^^^ + +This module defines the following :prop_tgt:`IMPORTED` targets: + +.. variable:: RecastNavigation::Recast + + Imported target for using the RECASTNAV library, if found. + +Result variables +^^^^^^^^^^^^^^^^ + +.. variable:: RECASTNAV_FOUND + + Set to true if RECASTNAV library found, otherwise false or undefined. + +.. variable:: RECASTNAV_INCLUDE_DIRS + + Paths to include directories listed in one variable for use by RECASTNAV client. + +.. variable:: RECASTNAV_LIBRARIES + + Paths to libraries to linked against to use RECASTNAV. + +.. variable:: RECAST_VERSION + + The version string of RECASTNAV found. + +Cache variables +^^^^^^^^^^^^^^^ + +For users who wish to edit and control the module behavior, this module +reads hints about search locations from the following variables:: + +.. variable:: RECASTNAV_INCLUDE_DIR + + Path to RECASTNAV include directory with ``Recast.h`` header. + +.. variable:: RECASTNAV_LIBRARY + + Path to RECASTNAV library to be linked. + +NOTE: The variables above should not usually be used in CMakeLists.txt files! + +#]=======================================================================] + +### Find library ############################################################## + +if(NOT RECASTNAV_LIBRARY) + find_library(RECASTNAV_LIBRARY_RELEASE NAMES Recast) + find_library(RECASTNAV_LIBRARY_DEBUG NAMES Recastd) + +# TODO: figure out a way to get Recast, Detour and DebugUtils libs together +# find_library(RECAST_LIBRARY_RELEASE NAMES Recast) +# find_library(RECAST_LIBRARY_DEBUG NAMES Recastd) + +# find_library(DETOUR_LIBRARY_RELEASE NAMES Detour) +# find_library(DETOUR_LIBRARY_DEBUG NAMES Detourd) + +# SET(RECASTNAV_LIBRARY_RELEASE ${RECAST_LIBRARY_RELEASE} ${DETOUR_LIBRARY_RELEASE}) +# SET(RECASTNAV_LIBRARY_DEBUG ${RECAST_LIBRARY_DEBUG} ${DETOUR_LIBRARY_DEBUG}) + + include(SelectLibraryConfigurations) + select_library_configurations(RECASTNAV) +else() + file(TO_CMAKE_PATH "${RECASTNAV_LIBRARY}" RECASTNAV_LIBRARY) +endif() + +### Find include directory #################################################### +find_path(RECASTNAV_INCLUDE_DIR NAMES Recast.h PATH_SUFFIXES include RECASTNAV include/recastnavigation) + +if(RECASTNAV_INCLUDE_DIR AND EXISTS "${RECASTNAV_INCLUDE_DIR}/Recast.h") + file(STRINGS "${RECASTNAV_INCLUDE_DIR}/Recast.h" _Recast_h_contents + REGEX "#define RECAST_VERSION_[A-Z]+[ ]+[0-9]+") + string(REGEX REPLACE "#define RECAST_VERSION_MAJOR[ ]+([0-9]+).+" "\\1" + RECAST_VERSION_MAJOR "${_Recast_h_contents}") + string(REGEX REPLACE ".+#define RECAST_VERSION_MINOR[ ]+([0-9]+).+" "\\1" + RECAST_VERSION_MINOR "${_Recast_h_contents}") + string(REGEX REPLACE ".+#define RECAST_VERSION_RELEASE[ ]+([0-9]+).*" "\\1" + RECAST_VERSION_RELEASE "${_Recast_h_contents}") + set(RECAST_VERSION "${RECAST_VERSION_MAJOR}.${RECAST_VERSION_MINOR}.${RECAST_VERSION_RELEASE}") + unset(_Recast_h_contents) +endif() + +#TODO: they don't include a version yet +set(RECAST_VERSION "1.5.1") + +### Set result variables ###################################################### +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(RecastNavigation DEFAULT_MSG + RECASTNAV_LIBRARY RECASTNAV_INCLUDE_DIR RECAST_VERSION) + +mark_as_advanced(RECASTNAV_INCLUDE_DIR RECASTNAV_LIBRARY) + +set(RECASTNAV_LIBRARIES ${RECASTNAV_LIBRARY}) +set(RECASTNAV_INCLUDE_DIRS ${RECASTNAV_INCLUDE_DIR}) + +### Import targets ############################################################ +if(RECASTNAV_FOUND) + if(NOT TARGET RecastNavigation::Recast) + add_library(RecastNavigation::Recast UNKNOWN IMPORTED) + set_target_properties(RecastNavigation::Recast PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + INTERFACE_INCLUDE_DIRECTORIES "${RECASTNAV_INCLUDE_DIR}") + + if(RECASTNAV_LIBRARY_RELEASE) + set_property(TARGET RecastNavigation::Recast APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(RecastNavigation::Recast PROPERTIES + IMPORTED_LOCATION_RELEASE "${RECASTNAV_LIBRARY_RELEASE}") + endif() + + if(RECASTNAV_LIBRARY_DEBUG) + set_property(TARGET RecastNavigation::Recast APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(RecastNavigation::Recast PROPERTIES + IMPORTED_LOCATION_DEBUG "${RECASTNAV_LIBRARY_DEBUG}") + endif() + + if(NOT RECASTNAV_LIBRARY_RELEASE AND NOT RECASTNAV_LIBRARY_DEBUG) + set_property(TARGET RecastNavigation::Recast APPEND PROPERTY + IMPORTED_LOCATION "${RECASTNAV_LIBRARY}") + endif() + endif() +endif() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2120afc1a..349f1cd79 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -241,8 +241,9 @@ target_link_libraries(components ${OPENGL_gl_LIBRARY} ${MyGUI_LIBRARIES} LZ4::LZ4 - RecastNavigation::DebugUtils - RecastNavigation::Detour +# TODO: uncomment these when ready +# RecastNavigation::DebugUtils +# RecastNavigation::Detour RecastNavigation::Recast ) From ed74834e01aa68c75d36857833dbf0f177fa7556 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 21 Feb 2021 21:19:11 +0100 Subject: [PATCH 036/132] put back the recast stuff --- components/CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 349f1cd79..2120afc1a 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -241,9 +241,8 @@ target_link_libraries(components ${OPENGL_gl_LIBRARY} ${MyGUI_LIBRARIES} LZ4::LZ4 -# TODO: uncomment these when ready -# RecastNavigation::DebugUtils -# RecastNavigation::Detour + RecastNavigation::DebugUtils + RecastNavigation::Detour RecastNavigation::Recast ) From c4064fca0c7335b20f87579b5a848910fac51db0 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 2 Mar 2021 23:11:06 +0100 Subject: [PATCH 037/132] include feedback and add DebugUtils and Detour --- CMakeLists.txt | 3 - cmake/FindRecastNavigation.cmake | 95 +++++++++++++++++++++++++++----- 2 files changed, 80 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 405f4e874..055f981aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,10 +130,7 @@ option(MYGUI_STATIC "Link static build of Mygui into the binaries" ${_mygui_stat option(OPENMW_USE_SYSTEM_RECASTNAVIGATION "Use system provided recastnavigation library" OFF) if(OPENMW_USE_SYSTEM_RECASTNAVIGATION) set(_recastnavigation_static_default OFF) - find_package(RecastNavigation REQUIRED) - include_directories(SYSTEM ${RecastNavigation_INCLUDE_DIRS}) - else() set(_recastnavigation_static_default ON) endif() diff --git a/cmake/FindRecastNavigation.cmake b/cmake/FindRecastNavigation.cmake index ae85faaaa..b0ce629a9 100644 --- a/cmake/FindRecastNavigation.cmake +++ b/cmake/FindRecastNavigation.cmake @@ -62,30 +62,38 @@ NOTE: The variables above should not usually be used in CMakeLists.txt files! #]=======================================================================] -### Find library ############################################################## +### Find libraries ############################################################## if(NOT RECASTNAV_LIBRARY) find_library(RECASTNAV_LIBRARY_RELEASE NAMES Recast) find_library(RECASTNAV_LIBRARY_DEBUG NAMES Recastd) - -# TODO: figure out a way to get Recast, Detour and DebugUtils libs together -# find_library(RECAST_LIBRARY_RELEASE NAMES Recast) -# find_library(RECAST_LIBRARY_DEBUG NAMES Recastd) - -# find_library(DETOUR_LIBRARY_RELEASE NAMES Detour) -# find_library(DETOUR_LIBRARY_DEBUG NAMES Detourd) - -# SET(RECASTNAV_LIBRARY_RELEASE ${RECAST_LIBRARY_RELEASE} ${DETOUR_LIBRARY_RELEASE}) -# SET(RECASTNAV_LIBRARY_DEBUG ${RECAST_LIBRARY_DEBUG} ${DETOUR_LIBRARY_DEBUG}) - include(SelectLibraryConfigurations) select_library_configurations(RECASTNAV) else() file(TO_CMAKE_PATH "${RECASTNAV_LIBRARY}" RECASTNAV_LIBRARY) endif() +if(NOT DETOUR_LIBRARY) + find_library(DETOUR_LIBRARY_RELEASE NAMES Detour) + find_library(DETOUR_LIBRARY_DEBUG NAMES Detourd) + include(SelectLibraryConfigurations) + select_library_configurations(DETOUR) +else() + file(TO_CMAKE_PATH "${DETOUR_LIBRARY}" DETOUR_LIBRARY) +endif() + +if(NOT DEBUGUTILS_LIBRARY) + find_library(DEBUGUTILS_LIBRARY_RELEASE NAMES DebugUtils) + find_library(DEBUGUTILS_LIBRARY_DEBUG NAMES DebugUtilsd) + include(SelectLibraryConfigurations) + select_library_configurations(DEBUGUTILS) +else() + file(TO_CMAKE_PATH "${DEBUGUTILS_LIBRARY}" DEBUGUTILS_LIBRARY) +endif() + ### Find include directory #################################################### find_path(RECASTNAV_INCLUDE_DIR NAMES Recast.h PATH_SUFFIXES include RECASTNAV include/recastnavigation) +mark_as_advanced(RECASTNAV_INCLUDE_DIR RECASTNAV_LIBRARY) if(RECASTNAV_INCLUDE_DIR AND EXISTS "${RECASTNAV_INCLUDE_DIR}/Recast.h") file(STRINGS "${RECASTNAV_INCLUDE_DIR}/Recast.h" _Recast_h_contents @@ -108,13 +116,17 @@ include(FindPackageHandleStandardArgs) find_package_handle_standard_args(RecastNavigation DEFAULT_MSG RECASTNAV_LIBRARY RECASTNAV_INCLUDE_DIR RECAST_VERSION) -mark_as_advanced(RECASTNAV_INCLUDE_DIR RECASTNAV_LIBRARY) - set(RECASTNAV_LIBRARIES ${RECASTNAV_LIBRARY}) set(RECASTNAV_INCLUDE_DIRS ${RECASTNAV_INCLUDE_DIR}) +set(DETOUR_LIBRARIES ${DETOUR_LIBRARY}) +set(DETOUR_INCLUDE_DIRS ${RECASTNAV_INCLUDE_DIR}) + +set(DEBUGUTILS_LIBRARIES ${DEBUGUTILS_LIBRARY}) +set(DEBUGUTILS_INCLUDE_DIRS ${RECASTNAV_INCLUDE_DIR}) + ### Import targets ############################################################ -if(RECASTNAV_FOUND) +if(RecastNavigation_FOUND) if(NOT TARGET RecastNavigation::Recast) add_library(RecastNavigation::Recast UNKNOWN IMPORTED) set_target_properties(RecastNavigation::Recast PROPERTIES @@ -140,4 +152,57 @@ if(RECASTNAV_FOUND) IMPORTED_LOCATION "${RECASTNAV_LIBRARY}") endif() endif() + + if(NOT TARGET RecastNavigation::Detour) + add_library(RecastNavigation::Detour UNKNOWN IMPORTED) + set_target_properties(RecastNavigation::Detour PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + INTERFACE_INCLUDE_DIRECTORIES "${DETOUR_INCLUDE_DIR}") + + if(RECASTNAV_LIBRARY_RELEASE) + set_property(TARGET RecastNavigation::Detour APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(RecastNavigation::Detour PROPERTIES + IMPORTED_LOCATION_RELEASE "${DETOUR_LIBRARY_RELEASE}") + endif() + + if(RECASTNAV_LIBRARY_DEBUG) + set_property(TARGET RecastNavigation::Detour APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(RecastNavigation::Detour PROPERTIES + IMPORTED_LOCATION_DEBUG "${DETOUR_LIBRARY_DEBUG}") + endif() + + if(NOT RECASTNAV_LIBRARY_RELEASE AND NOT RECASTNAV_LIBRARY_DEBUG) + set_property(TARGET RecastNavigation::Detour APPEND PROPERTY + IMPORTED_LOCATION "${DETOUR_LIBRARY}") + endif() + endif() + + if(NOT TARGET RecastNavigation::DebugUtils) + add_library(RecastNavigation::DebugUtils UNKNOWN IMPORTED) + set_target_properties(RecastNavigation::DebugUtils PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + INTERFACE_INCLUDE_DIRECTORIES "${DEBUGUTILS_INCLUDE_DIR}") + + if(RECASTNAV_LIBRARY_RELEASE) + set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(RecastNavigation::DebugUtils PROPERTIES + IMPORTED_LOCATION_RELEASE "${DEBUGUTILS_LIBRARY_RELEASE}") + endif() + + if(RECASTNAV_LIBRARY_DEBUG) + set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(RecastNavigation::DebugUtils PROPERTIES + IMPORTED_LOCATION_DEBUG "${DEBUGUTILS_LIBRARY_DEBUG}") + endif() + + if(NOT RECASTNAV_LIBRARY_RELEASE AND NOT RECASTNAV_LIBRARY_DEBUG) + set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY + IMPORTED_LOCATION "${DEBUGUTILS_LIBRARY}") + endif() + endif() + endif() From 2bc5a44e15162ee6557d441310080d36bc6e1787 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 3 Mar 2021 11:38:28 +0100 Subject: [PATCH 038/132] Added copyright, refactored to be more clear and marked certain things as advanced. --- cmake/FindRecastNavigation.cmake | 85 +++++++++++++++++--------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/cmake/FindRecastNavigation.cmake b/cmake/FindRecastNavigation.cmake index b0ce629a9..ee6122e5d 100644 --- a/cmake/FindRecastNavigation.cmake +++ b/cmake/FindRecastNavigation.cmake @@ -1,6 +1,6 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. - +# Copyright 2021 Bret Curtis for OpenMW #[=======================================================================[.rst: FindRecastNavigation ------- @@ -13,7 +13,7 @@ Use this module by invoking find_package with the form:: find_package(RecastNavigation [version] # Minimum version e.g. 1.8.0 - [REQUIRED] # Fail with error if RECASTNAV is not found + [REQUIRED] # Fail with error if RECAST is not found ) Imported targets @@ -23,26 +23,26 @@ This module defines the following :prop_tgt:`IMPORTED` targets: .. variable:: RecastNavigation::Recast - Imported target for using the RECASTNAV library, if found. + Imported target for using the RECAST library, if found. Result variables ^^^^^^^^^^^^^^^^ -.. variable:: RECASTNAV_FOUND +.. variable:: RECAST_FOUND - Set to true if RECASTNAV library found, otherwise false or undefined. + Set to true if RECAST library found, otherwise false or undefined. -.. variable:: RECASTNAV_INCLUDE_DIRS +.. variable:: RECAST_INCLUDE_DIRS - Paths to include directories listed in one variable for use by RECASTNAV client. + Paths to include directories listed in one variable for use by RECAST client. -.. variable:: RECASTNAV_LIBRARIES +.. variable:: RECAST_LIBRARIES - Paths to libraries to linked against to use RECASTNAV. + Paths to libraries to linked against to use RECAST. .. variable:: RECAST_VERSION - The version string of RECASTNAV found. + The version string of RECAST found. Cache variables ^^^^^^^^^^^^^^^ @@ -50,13 +50,13 @@ Cache variables For users who wish to edit and control the module behavior, this module reads hints about search locations from the following variables:: -.. variable:: RECASTNAV_INCLUDE_DIR +.. variable:: RECAST_INCLUDE_DIR - Path to RECASTNAV include directory with ``Recast.h`` header. + Path to RECAST include directory with ``Recast.h`` header. -.. variable:: RECASTNAV_LIBRARY +.. variable:: RECAST_LIBRARY - Path to RECASTNAV library to be linked. + Path to RECAST library to be linked. NOTE: The variables above should not usually be used in CMakeLists.txt files! @@ -64,13 +64,14 @@ NOTE: The variables above should not usually be used in CMakeLists.txt files! ### Find libraries ############################################################## -if(NOT RECASTNAV_LIBRARY) - find_library(RECASTNAV_LIBRARY_RELEASE NAMES Recast) - find_library(RECASTNAV_LIBRARY_DEBUG NAMES Recastd) +if(NOT RECAST_LIBRARY) + find_library(RECAST_LIBRARY_RELEASE NAMES Recast) + find_library(RECAST_LIBRARY_DEBUG NAMES Recastd) include(SelectLibraryConfigurations) - select_library_configurations(RECASTNAV) + select_library_configurations(RECAST) + mark_as_advanced(RECAST_LIBRARY_RELEASE RECAST_LIBRARY_DEBUG) else() - file(TO_CMAKE_PATH "${RECASTNAV_LIBRARY}" RECASTNAV_LIBRARY) + file(TO_CMAKE_PATH "${RECAST_LIBRARY}" RECAST_LIBRARY) endif() if(NOT DETOUR_LIBRARY) @@ -78,6 +79,7 @@ if(NOT DETOUR_LIBRARY) find_library(DETOUR_LIBRARY_DEBUG NAMES Detourd) include(SelectLibraryConfigurations) select_library_configurations(DETOUR) + mark_as_advanced(DETOUR_LIBRARY_RELEASE DETOUR_LIBRARY_DEBUG) else() file(TO_CMAKE_PATH "${DETOUR_LIBRARY}" DETOUR_LIBRARY) endif() @@ -87,16 +89,17 @@ if(NOT DEBUGUTILS_LIBRARY) find_library(DEBUGUTILS_LIBRARY_DEBUG NAMES DebugUtilsd) include(SelectLibraryConfigurations) select_library_configurations(DEBUGUTILS) + mark_as_advanced(DEBUGUTILS_LIBRARY_RELEASE DEBUGUTILS_LIBRARY_DEBUG) else() file(TO_CMAKE_PATH "${DEBUGUTILS_LIBRARY}" DEBUGUTILS_LIBRARY) endif() ### Find include directory #################################################### -find_path(RECASTNAV_INCLUDE_DIR NAMES Recast.h PATH_SUFFIXES include RECASTNAV include/recastnavigation) -mark_as_advanced(RECASTNAV_INCLUDE_DIR RECASTNAV_LIBRARY) +find_path(RECAST_INCLUDE_DIR NAMES Recast.h PATH_SUFFIXES include RECAST include/recastnavigation) +mark_as_advanced(RECAST_INCLUDE_DIR) -if(RECASTNAV_INCLUDE_DIR AND EXISTS "${RECASTNAV_INCLUDE_DIR}/Recast.h") - file(STRINGS "${RECASTNAV_INCLUDE_DIR}/Recast.h" _Recast_h_contents +if(RECAST_INCLUDE_DIR AND EXISTS "${RECAST_INCLUDE_DIR}/Recast.h") + file(STRINGS "${RECAST_INCLUDE_DIR}/Recast.h" _Recast_h_contents REGEX "#define RECAST_VERSION_[A-Z]+[ ]+[0-9]+") string(REGEX REPLACE "#define RECAST_VERSION_MAJOR[ ]+([0-9]+).+" "\\1" RECAST_VERSION_MAJOR "${_Recast_h_contents}") @@ -114,16 +117,16 @@ set(RECAST_VERSION "1.5.1") ### Set result variables ###################################################### include(FindPackageHandleStandardArgs) find_package_handle_standard_args(RecastNavigation DEFAULT_MSG - RECASTNAV_LIBRARY RECASTNAV_INCLUDE_DIR RECAST_VERSION) + RECAST_LIBRARY RECAST_INCLUDE_DIR RECAST_VERSION) -set(RECASTNAV_LIBRARIES ${RECASTNAV_LIBRARY}) -set(RECASTNAV_INCLUDE_DIRS ${RECASTNAV_INCLUDE_DIR}) +set(RECAST_LIBRARIES ${RECAST_LIBRARY}) +set(RECAST_INCLUDE_DIRS ${RECAST_INCLUDE_DIR}) set(DETOUR_LIBRARIES ${DETOUR_LIBRARY}) -set(DETOUR_INCLUDE_DIRS ${RECASTNAV_INCLUDE_DIR}) +set(DETOUR_INCLUDE_DIRS ${RECAST_INCLUDE_DIR}) set(DEBUGUTILS_LIBRARIES ${DEBUGUTILS_LIBRARY}) -set(DEBUGUTILS_INCLUDE_DIRS ${RECASTNAV_INCLUDE_DIR}) +set(DEBUGUTILS_INCLUDE_DIRS ${RECAST_INCLUDE_DIR}) ### Import targets ############################################################ if(RecastNavigation_FOUND) @@ -131,25 +134,25 @@ if(RecastNavigation_FOUND) add_library(RecastNavigation::Recast UNKNOWN IMPORTED) set_target_properties(RecastNavigation::Recast PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" - INTERFACE_INCLUDE_DIRECTORIES "${RECASTNAV_INCLUDE_DIR}") + INTERFACE_INCLUDE_DIRECTORIES "${RECAST_INCLUDE_DIR}") - if(RECASTNAV_LIBRARY_RELEASE) + if(RECAST_LIBRARY_RELEASE) set_property(TARGET RecastNavigation::Recast APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_target_properties(RecastNavigation::Recast PROPERTIES - IMPORTED_LOCATION_RELEASE "${RECASTNAV_LIBRARY_RELEASE}") + IMPORTED_LOCATION_RELEASE "${RECAST_LIBRARY_RELEASE}") endif() - if(RECASTNAV_LIBRARY_DEBUG) + if(RECAST_LIBRARY_DEBUG) set_property(TARGET RecastNavigation::Recast APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_target_properties(RecastNavigation::Recast PROPERTIES - IMPORTED_LOCATION_DEBUG "${RECASTNAV_LIBRARY_DEBUG}") + IMPORTED_LOCATION_DEBUG "${RECAST_LIBRARY_DEBUG}") endif() - if(NOT RECASTNAV_LIBRARY_RELEASE AND NOT RECASTNAV_LIBRARY_DEBUG) + if(NOT RECAST_LIBRARY_RELEASE AND NOT RECAST_LIBRARY_DEBUG) set_property(TARGET RecastNavigation::Recast APPEND PROPERTY - IMPORTED_LOCATION "${RECASTNAV_LIBRARY}") + IMPORTED_LOCATION "${RECAST_LIBRARY}") endif() endif() @@ -159,21 +162,21 @@ if(RecastNavigation_FOUND) IMPORTED_LINK_INTERFACE_LANGUAGES "C" INTERFACE_INCLUDE_DIRECTORIES "${DETOUR_INCLUDE_DIR}") - if(RECASTNAV_LIBRARY_RELEASE) + if(RECAST_LIBRARY_RELEASE) set_property(TARGET RecastNavigation::Detour APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_target_properties(RecastNavigation::Detour PROPERTIES IMPORTED_LOCATION_RELEASE "${DETOUR_LIBRARY_RELEASE}") endif() - if(RECASTNAV_LIBRARY_DEBUG) + if(RECAST_LIBRARY_DEBUG) set_property(TARGET RecastNavigation::Detour APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_target_properties(RecastNavigation::Detour PROPERTIES IMPORTED_LOCATION_DEBUG "${DETOUR_LIBRARY_DEBUG}") endif() - if(NOT RECASTNAV_LIBRARY_RELEASE AND NOT RECASTNAV_LIBRARY_DEBUG) + if(NOT RECAST_LIBRARY_RELEASE AND NOT RECAST_LIBRARY_DEBUG) set_property(TARGET RecastNavigation::Detour APPEND PROPERTY IMPORTED_LOCATION "${DETOUR_LIBRARY}") endif() @@ -185,21 +188,21 @@ if(RecastNavigation_FOUND) IMPORTED_LINK_INTERFACE_LANGUAGES "C" INTERFACE_INCLUDE_DIRECTORIES "${DEBUGUTILS_INCLUDE_DIR}") - if(RECASTNAV_LIBRARY_RELEASE) + if(RECAST_LIBRARY_RELEASE) set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_target_properties(RecastNavigation::DebugUtils PROPERTIES IMPORTED_LOCATION_RELEASE "${DEBUGUTILS_LIBRARY_RELEASE}") endif() - if(RECASTNAV_LIBRARY_DEBUG) + if(RECAST_LIBRARY_DEBUG) set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_target_properties(RecastNavigation::DebugUtils PROPERTIES IMPORTED_LOCATION_DEBUG "${DEBUGUTILS_LIBRARY_DEBUG}") endif() - if(NOT RECASTNAV_LIBRARY_RELEASE AND NOT RECASTNAV_LIBRARY_DEBUG) + if(NOT RECAST_LIBRARY_RELEASE AND NOT RECAST_LIBRARY_DEBUG) set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY IMPORTED_LOCATION "${DEBUGUTILS_LIBRARY}") endif() From 5e91af230d604a23268d2d22b71a59b4bed14f81 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Wed, 3 Mar 2021 21:13:05 +0300 Subject: [PATCH 039/132] Reset effect icon transparency when they're hidden (#5877) --- CHANGELOG.md | 1 + apps/openmw/mwgui/spellicons.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a46f47f53..8b839c172 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -104,6 +104,7 @@ Bug #5840: GetSoundPlaying "Health Damage" doesn't play when NPC hits target with shield effect ( vanilla engine behavior ) Bug #5841: Can't Cast Zero Cost Spells When Magicka is < 0 Bug #5871: The console appears if you type the Russian letter "Ё" in the name of the enchantment + Bug #5877: Effects appearing with empty icon Feature #390: 3rd person look "over the shoulder" Feature #1536: Show more information about level on menu Feature #2386: Distant Statics in the form of Object Paging diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index e6a10ee32..405abfbae 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -181,7 +181,9 @@ namespace MWGui } else if (mWidgetMap.find(effectId) != mWidgetMap.end()) { - mWidgetMap[effectId]->setVisible(false); + MyGUI::ImageBox* image = mWidgetMap[effectId]; + image->setVisible(false); + image->setAlpha(1.f); } } From fc329050b34524ac5e9e52e3fa6d0245189806bd Mon Sep 17 00:00:00 2001 From: Dobrohotov Alexei Date: Wed, 3 Mar 2021 22:04:10 +0300 Subject: [PATCH 040/132] AIPursue: Pursue the player until LOS is established (#5869) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/aipursue.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a46f47f53..84580d9fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,6 +103,7 @@ Bug #5838: Local map and other menus become blank in some locations while playing Wizards' Islands mod. Bug #5840: GetSoundPlaying "Health Damage" doesn't play when NPC hits target with shield effect ( vanilla engine behavior ) Bug #5841: Can't Cast Zero Cost Spells When Magicka is < 0 + Bug #5869: Guards can initiate arrest dialogue behind locked doors Bug #5871: The console appears if you type the Russian letter "Ё" in the name of the enchantment Feature #390: 3rd person look "over the shoulder" Feature #1536: Show more information about level on menu diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index bfe860d6d..5af73887c 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -53,9 +53,14 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte const float pathTolerance = 100.f; - if (pathTo(actor, dest, duration, pathTolerance) && - std::abs(dest.z() - actorPos.z()) < pathTolerance) // check the true distance in case the target is far away in Z-direction + // check the true distance in case the target is far away in Z-direction + bool reached = pathTo(actor, dest, duration, pathTolerance) && + std::abs(dest.z() - actorPos.z()) < pathTolerance; + + if (reached) { + if (!MWBase::Environment::get().getWorld()->getLOS(target, actor)) + return false; MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, actor); //Arrest player when reached return true; } From 741584472e19ebe21ab8a10cc91848ddd78e44b8 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Thu, 4 Mar 2021 10:39:54 +0100 Subject: [PATCH 041/132] another cleanup to FindRecastNavigation.cmake --- cmake/FindRecastNavigation.cmake | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmake/FindRecastNavigation.cmake b/cmake/FindRecastNavigation.cmake index ee6122e5d..9ff845f32 100644 --- a/cmake/FindRecastNavigation.cmake +++ b/cmake/FindRecastNavigation.cmake @@ -162,21 +162,21 @@ if(RecastNavigation_FOUND) IMPORTED_LINK_INTERFACE_LANGUAGES "C" INTERFACE_INCLUDE_DIRECTORIES "${DETOUR_INCLUDE_DIR}") - if(RECAST_LIBRARY_RELEASE) + if(DETOUR_LIBRARY_RELEASE) set_property(TARGET RecastNavigation::Detour APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_target_properties(RecastNavigation::Detour PROPERTIES IMPORTED_LOCATION_RELEASE "${DETOUR_LIBRARY_RELEASE}") endif() - if(RECAST_LIBRARY_DEBUG) + if(DETOUR_LIBRARY_DEBUG) set_property(TARGET RecastNavigation::Detour APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_target_properties(RecastNavigation::Detour PROPERTIES IMPORTED_LOCATION_DEBUG "${DETOUR_LIBRARY_DEBUG}") endif() - if(NOT RECAST_LIBRARY_RELEASE AND NOT RECAST_LIBRARY_DEBUG) + if(NOT DETOUR_LIBRARY_RELEASE AND NOT DETOUR_LIBRARY_DEBUG) set_property(TARGET RecastNavigation::Detour APPEND PROPERTY IMPORTED_LOCATION "${DETOUR_LIBRARY}") endif() @@ -188,21 +188,21 @@ if(RecastNavigation_FOUND) IMPORTED_LINK_INTERFACE_LANGUAGES "C" INTERFACE_INCLUDE_DIRECTORIES "${DEBUGUTILS_INCLUDE_DIR}") - if(RECAST_LIBRARY_RELEASE) + if(DEBUGUTILS_LIBRARY_RELEASE) set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_target_properties(RecastNavigation::DebugUtils PROPERTIES IMPORTED_LOCATION_RELEASE "${DEBUGUTILS_LIBRARY_RELEASE}") endif() - if(RECAST_LIBRARY_DEBUG) + if(DEBUGUTILS_LIBRARY_DEBUG) set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_target_properties(RecastNavigation::DebugUtils PROPERTIES IMPORTED_LOCATION_DEBUG "${DEBUGUTILS_LIBRARY_DEBUG}") endif() - if(NOT RECAST_LIBRARY_RELEASE AND NOT RECAST_LIBRARY_DEBUG) + if(NOT DEBUGUTILS_LIBRARY_RELEASE AND NOT DEBUGUTILS_LIBRARY_DEBUG) set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY IMPORTED_LOCATION "${DEBUGUTILS_LIBRARY}") endif() From b47573f36bffe9c22488af79822cd747fbac0968 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 4 Mar 2021 18:12:08 +0100 Subject: [PATCH 042/132] Fix recastnavigation debug library suffixes --- cmake/FindRecastNavigation.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/FindRecastNavigation.cmake b/cmake/FindRecastNavigation.cmake index 9ff845f32..01e4b3fbe 100644 --- a/cmake/FindRecastNavigation.cmake +++ b/cmake/FindRecastNavigation.cmake @@ -66,7 +66,7 @@ NOTE: The variables above should not usually be used in CMakeLists.txt files! if(NOT RECAST_LIBRARY) find_library(RECAST_LIBRARY_RELEASE NAMES Recast) - find_library(RECAST_LIBRARY_DEBUG NAMES Recastd) + find_library(RECAST_LIBRARY_DEBUG NAMES Recast-d) include(SelectLibraryConfigurations) select_library_configurations(RECAST) mark_as_advanced(RECAST_LIBRARY_RELEASE RECAST_LIBRARY_DEBUG) @@ -76,7 +76,7 @@ endif() if(NOT DETOUR_LIBRARY) find_library(DETOUR_LIBRARY_RELEASE NAMES Detour) - find_library(DETOUR_LIBRARY_DEBUG NAMES Detourd) + find_library(DETOUR_LIBRARY_DEBUG NAMES Detour-d) include(SelectLibraryConfigurations) select_library_configurations(DETOUR) mark_as_advanced(DETOUR_LIBRARY_RELEASE DETOUR_LIBRARY_DEBUG) @@ -86,7 +86,7 @@ endif() if(NOT DEBUGUTILS_LIBRARY) find_library(DEBUGUTILS_LIBRARY_RELEASE NAMES DebugUtils) - find_library(DEBUGUTILS_LIBRARY_DEBUG NAMES DebugUtilsd) + find_library(DEBUGUTILS_LIBRARY_DEBUG NAMES DebugUtils-d) include(SelectLibraryConfigurations) select_library_configurations(DEBUGUTILS) mark_as_advanced(DEBUGUTILS_LIBRARY_RELEASE DEBUGUTILS_LIBRARY_DEBUG) From b9575180b56d9dd8159e3b8ad7a4232414df2155 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 3 Mar 2021 18:15:26 +0100 Subject: [PATCH 043/132] Support custom recastnavigation system path --- cmake/FindRecastNavigation.cmake | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cmake/FindRecastNavigation.cmake b/cmake/FindRecastNavigation.cmake index 01e4b3fbe..8728a5e94 100644 --- a/cmake/FindRecastNavigation.cmake +++ b/cmake/FindRecastNavigation.cmake @@ -65,8 +65,8 @@ NOTE: The variables above should not usually be used in CMakeLists.txt files! ### Find libraries ############################################################## if(NOT RECAST_LIBRARY) - find_library(RECAST_LIBRARY_RELEASE NAMES Recast) - find_library(RECAST_LIBRARY_DEBUG NAMES Recast-d) + find_library(RECAST_LIBRARY_RELEASE NAMES Recast HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib) + find_library(RECAST_LIBRARY_DEBUG NAMES Recast-d HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib) include(SelectLibraryConfigurations) select_library_configurations(RECAST) mark_as_advanced(RECAST_LIBRARY_RELEASE RECAST_LIBRARY_DEBUG) @@ -75,8 +75,8 @@ else() endif() if(NOT DETOUR_LIBRARY) - find_library(DETOUR_LIBRARY_RELEASE NAMES Detour) - find_library(DETOUR_LIBRARY_DEBUG NAMES Detour-d) + find_library(DETOUR_LIBRARY_RELEASE NAMES Detour HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib) + find_library(DETOUR_LIBRARY_DEBUG NAMES Detour-d HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib) include(SelectLibraryConfigurations) select_library_configurations(DETOUR) mark_as_advanced(DETOUR_LIBRARY_RELEASE DETOUR_LIBRARY_DEBUG) @@ -85,8 +85,8 @@ else() endif() if(NOT DEBUGUTILS_LIBRARY) - find_library(DEBUGUTILS_LIBRARY_RELEASE NAMES DebugUtils) - find_library(DEBUGUTILS_LIBRARY_DEBUG NAMES DebugUtils-d) + find_library(DEBUGUTILS_LIBRARY_RELEASE NAMES DebugUtils HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib) + find_library(DEBUGUTILS_LIBRARY_DEBUG NAMES DebugUtils-d HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib) include(SelectLibraryConfigurations) select_library_configurations(DEBUGUTILS) mark_as_advanced(DEBUGUTILS_LIBRARY_RELEASE DEBUGUTILS_LIBRARY_DEBUG) @@ -95,7 +95,7 @@ else() endif() ### Find include directory #################################################### -find_path(RECAST_INCLUDE_DIR NAMES Recast.h PATH_SUFFIXES include RECAST include/recastnavigation) +find_path(RECAST_INCLUDE_DIR NAMES Recast.h HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES include RECAST include/recastnavigation) mark_as_advanced(RECAST_INCLUDE_DIR) if(RECAST_INCLUDE_DIR AND EXISTS "${RECAST_INCLUDE_DIR}/Recast.h") From 8ae4ee291f4fe1783223c875173e66ee2b1781dd Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 4 Mar 2021 22:52:03 +0100 Subject: [PATCH 044/132] Attempt to fix restocking items in old saves --- apps/openmw/mwworld/cellstore.cpp | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index cc614133a..73f69e220 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -104,6 +104,45 @@ namespace } } + template + void fixRestockingImpl(const T* base, RecordType& state) + { + // Workaround for old saves not containing negative quantities + for(const auto& baseItem : base->mInventory.mList) + { + if(baseItem.mCount < 0) + { + for(auto& item : state.mInventory.mItems) + { + if(item.mCount > 0 && Misc::StringUtils::ciEqual(baseItem.mItem, item.mRef.mRefID)) + item.mCount = -item.mCount; + } + } + } + } + + template + void fixRestocking(const T* base, RecordType& state) + {} + + template<> + void fixRestocking<>(const ESM::Creature* base, ESM::CreatureState& state) + { + fixRestockingImpl(base, state); + } + + template<> + void fixRestocking<>(const ESM::NPC* base, ESM::NpcState& state) + { + fixRestockingImpl(base, state); + } + + template<> + void fixRestocking<>(const ESM::Container* base, ESM::ContainerState& state) + { + fixRestockingImpl(base, state); + } + template void readReferenceCollection (ESM::ESMReader& reader, MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap, MWWorld::CellStore* cellstore) @@ -134,6 +173,9 @@ namespace if (!record) return; + if (state.mVersion < 15) + fixRestocking(record, state); + if (state.mRef.mRefNum.hasContentFile()) { for (typename MWWorld::CellRefList::List::iterator iter (collection.mList.begin()); From 41b3090a4910f406154e283f425b4242fde692dd Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 5 Mar 2021 14:27:01 +0400 Subject: [PATCH 045/132] Fix formatting in the groundcover docs --- docs/source/reference/modding/extended.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/reference/modding/extended.rst b/docs/source/reference/modding/extended.rst index 98b3e7f00..cd739cb1b 100644 --- a/docs/source/reference/modding/extended.rst +++ b/docs/source/reference/modding/extended.rst @@ -306,8 +306,11 @@ For example, we do not need to have collision or animation objects for groundcov do not need to render groundcover on the map, do not need to render it for the whole visible area (which can be very large with Distant Terrain). It allows to increase performance a lot. General advices to create assets for this feature: + 1. Alpha properties from Nif files are not used, a unified alpha settings are used (alpha testing, "greater of equal" function, 128/255 threshold). + 2. Use a single NiTriShape in groundocver mesh, or at least use same properties (texture, alpha, material, etc), so OpenMW can merge them on the fly. Otherwise animations may not work properly. + 3. Smooth fading does not work for meshes, which have textures without alpha (e.g. rock). Groundcover mods can be registered in the openmw.cfg via "groundcover" entries instead of "content" ones: From caa8b0ca1a7444a960160ffb11e315eca53be2fc Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 5 Mar 2021 13:03:21 +0100 Subject: [PATCH 046/132] give qt5 a show for brew --- CI/before_install.osx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index ba0ff00ca..d05415820 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -7,7 +7,7 @@ HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.9 || tru # Some of these tools can come from places other than brew, so check before installing command -v ccache >/dev/null 2>&1 || brew install ccache command -v cmake >/dev/null 2>&1 || brew install cmake -command -v qmake >/dev/null 2>&1 || brew install qt +command -v qmake >/dev/null 2>&1 || brew install qt5 curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-f8918dd.zip -o ~/openmw-deps.zip unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null From 17059cefea659458e2550355593cc7bb47fbca5e Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 5 Mar 2021 13:07:26 +0100 Subject: [PATCH 047/132] give qt@5 a try --- CI/before_install.osx.sh | 2 +- CI/before_script.osx.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index d05415820..9adf4dfa5 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -7,7 +7,7 @@ HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.9 || tru # Some of these tools can come from places other than brew, so check before installing command -v ccache >/dev/null 2>&1 || brew install ccache command -v cmake >/dev/null 2>&1 || brew install cmake -command -v qmake >/dev/null 2>&1 || brew install qt5 +command -v qmake >/dev/null 2>&1 || brew install qt@5 curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-f8918dd.zip -o ~/openmw-deps.zip unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index 8f9be16e1..36fa79299 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -4,7 +4,7 @@ export CXX=clang++ export CC=clang DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps" -QT_PATH=$(brew --prefix qt) +QT_PATH=$(brew --prefix qt@5) CCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache mkdir build cd build From 87ede9284bafd264d0d8371cf9f15c0f87d6203f Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 5 Mar 2021 13:08:38 +0100 Subject: [PATCH 048/132] be more explicit --- CI/before_install.osx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 9adf4dfa5..49b2fbe16 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/sh -ex # workaround python issue on travis HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.8 || true From 8fc0f965bb04de2f857a9c567699545e92aa1179 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 5 Mar 2021 13:10:22 +0100 Subject: [PATCH 049/132] which qmake? --- CI/before_install.osx.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 49b2fbe16..d8832d90e 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -9,5 +9,8 @@ command -v ccache >/dev/null 2>&1 || brew install ccache command -v cmake >/dev/null 2>&1 || brew install cmake command -v qmake >/dev/null 2>&1 || brew install qt@5 +qmake --version +which qmake + curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-f8918dd.zip -o ~/openmw-deps.zip unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null From f03beb796359af18637ed3b8e1dbe822803f55df Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 5 Mar 2021 13:12:42 +0100 Subject: [PATCH 050/132] uninstall qt@6 --- CI/before_install.osx.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index d8832d90e..9d0774d3c 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -3,6 +3,7 @@ # workaround python issue on travis HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.8 || true HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.9 || true +HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies qt@6 || true # Some of these tools can come from places other than brew, so check before installing command -v ccache >/dev/null 2>&1 || brew install ccache From 53cb20454ff9249dda40e30c0f3d1bc5a4590f1c Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 5 Mar 2021 13:20:03 +0100 Subject: [PATCH 051/132] add path hack only to verify, will remove later --- CI/before_install.osx.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 9d0774d3c..b38e04a06 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -10,6 +10,7 @@ command -v ccache >/dev/null 2>&1 || brew install ccache command -v cmake >/dev/null 2>&1 || brew install cmake command -v qmake >/dev/null 2>&1 || brew install qt@5 +export PATH="/usr/local/opt/qt@5/bin:$PATH" qmake --version which qmake From 9fbb530dde4d95e1881a5a83b12125ede46e786a Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 5 Mar 2021 13:25:46 +0100 Subject: [PATCH 052/132] be explicit and print version information to verification --- CI/before_install.osx.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index b38e04a06..6dad0abc1 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -9,10 +9,11 @@ HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies qt@6 || true command -v ccache >/dev/null 2>&1 || brew install ccache command -v cmake >/dev/null 2>&1 || brew install cmake command -v qmake >/dev/null 2>&1 || brew install qt@5 +export PATH="/usr/local/opt/qt@5/bin:$PATH" # needed to use qmake in none default path as qt now points to qt6 -export PATH="/usr/local/opt/qt@5/bin:$PATH" +ccache --version +cmake --version qmake --version -which qmake curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-f8918dd.zip -o ~/openmw-deps.zip unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null From 2bfee281fdf533fbc9577ee4c9a22795f2e4c265 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Thu, 25 Feb 2021 23:12:14 +0000 Subject: [PATCH 053/132] Merge branch 'restore_caster' into 'master' Restore projectile caster from savegame (#5860) See merge request OpenMW/openmw!616 (cherry picked from commit d595c7adb0fb45eafed6d3d0403ad640a91411ed) c5426bec In the savegame, projectile caster is identified by its actor id. When --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwphysics/physicssystem.cpp | 13 ++++++++++++- apps/openmw/mwphysics/physicssystem.hpp | 1 + apps/openmw/mwstate/statemanagerimp.cpp | 2 ++ apps/openmw/mwworld/projectilemanager.cpp | 23 +++++++++++++++++++++++ apps/openmw/mwworld/projectilemanager.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 1 + 8 files changed, 47 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 194e8f695..27980f070 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -539,6 +539,7 @@ namespace MWBase virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0; virtual void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile, const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) = 0; + virtual void updateProjectilesCasters() = 0; virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 382b348b9..b2decde2f 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -604,7 +604,9 @@ namespace MWPhysics return object->getCollisionObject(); return nullptr; }(); - assert(caster); + + if (caster == nullptr) + Log(Debug::Warning) << "No caster for projectile " << projectileId; ProjectileConvexCallback resultCallback(caster, btFrom, btTo, projectile); resultCallback.m_collisionFilterMask = 0xff; @@ -695,6 +697,15 @@ namespace MWPhysics return mProjectileId; } + void PhysicsSystem::setCaster(int projectileId, const MWWorld::Ptr& caster) + { + const auto foundProjectile = mProjectiles.find(projectileId); + assert(foundProjectile != mProjectiles.end()); + auto* projectile = foundProjectile->second.get(); + + projectile->setCaster(caster); + } + bool PhysicsSystem::toggleCollisionMode() { ActorMap::iterator found = mActors.find(MWMechanics::getPlayer()); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 57ebbadbb..80b2d98bc 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -125,6 +125,7 @@ namespace MWPhysics void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius, bool canTraverseWater); + void setCaster(int projectileId, const MWWorld::Ptr& caster); void updateProjectile(const int projectileId, const osg::Vec3f &position) const; void removeProjectile(const int projectileId); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 72e4b1ae0..f605344bf 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -539,6 +539,8 @@ void MWState::StateManager::loadGame (const Character *character, const std::str MWBase::Environment::get().getWorld()->changeToCell(cell->getCell()->getCellId(), pos, true, false); } + MWBase::Environment::get().getWorld()->updateProjectilesCasters(); + // Vanilla MW will restart startup scripts when a save game is loaded. This is unintuitive, // but some mods may be using it as a reload detector. MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 3ffc7bb95..f483905dd 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -352,6 +352,29 @@ namespace MWWorld mProjectiles.push_back(state); } + void ProjectileManager::updateCasters() + { + for (auto& state : mProjectiles) + mPhysics->setCaster(state.mProjectileId, state.getCaster()); + + for (auto& state : mMagicBolts) + { + // casters are identified by actor id in the savegame. objects doesn't have one so they can't be identified back. + // TODO: should object-type caster be restored from savegame? + if (state.mActorId == -1) + continue; + + auto caster = state.getCaster(); + if (caster.isEmpty()) + { + Log(Debug::Error) << "Couldn't find caster with ID " << state.mActorId; + cleanupMagicBolt(state); + continue; + } + mPhysics->setCaster(state.mProjectileId, caster); + } + } + void ProjectileManager::update(float dt) { periodicCleanup(dt); diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index c047d90dd..e4bcae1ae 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -54,6 +54,8 @@ namespace MWWorld void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& pos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength); + void updateCasters(); + void update(float dt); void processHits(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b609f325c..01b90e907 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3186,6 +3186,11 @@ namespace MWWorld mProjectileManager->launchMagicBolt(spellId, caster, fallbackDirection); } + void World::updateProjectilesCasters() + { + mProjectileManager->updateCasters(); + } + class ApplyLoopingParticlesVisitor : public MWMechanics::EffectSourceVisitor { private: diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index bc2baafd8..23153a31c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -644,6 +644,7 @@ namespace MWWorld void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) override; void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile, const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) override; + void updateProjectilesCasters() override; void applyLoopingParticles(const MWWorld::Ptr& ptr) override; From 31d8ce266b3f8f726b49f484e7fd1a8f5bec63f1 Mon Sep 17 00:00:00 2001 From: fredzio Date: Sat, 6 Mar 2021 09:46:37 +0100 Subject: [PATCH 054/132] Close a race between main and physics threads when actor is positioned by scripts. When a position is forced, the actor position in physics subsystem is overriden. The background physics thread is not made aware of this, its result are simply discarded. There is a short window where this doesn't work (in this example, actor is at A and script moves it to B) 1) actor position is set to B. (among others, Actor::mPosition is set to B) 2) physics thread reset Actor::mPosition with stale value (around A) 3) main thread read simulation result, reset Actor::mSkipSimulation flag => actor is at B 4) physics thread fetch latest Actor::mPosition value, which is around A 5) main thread read simulation result, actor is around A To avoid this situation, do not perform 2) until after 3) occurs. This way, at 4) starts the simulation with up-to-date Actor::mPosition --- apps/openmw/mwphysics/actor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 3b52ee934..bbec4d445 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -174,6 +174,9 @@ osg::Vec3f Actor::getCollisionObjectPosition() const bool Actor::setPosition(const osg::Vec3f& position) { std::scoped_lock lock(mPositionMutex); + // position is being forced, ignore simulation results until we sync up + if (mSkipSimulation) + return false; bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged; mPreviousPosition = mPosition + mPositionOffset; mPosition = position + mPositionOffset; From 455be9dbbbeccfcbb9a222aa94777d1c79bcf687 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 6 Mar 2021 19:21:23 +0000 Subject: [PATCH 055/132] Fix linking with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON` 1. CMake's built-in OSG finder does not use pkgconfig, so we have to manually ensure the order is correct for inter-library dependencies. https://gitlab.kitware.com/cmake/cmake/-/issues/21701 2. OSG plugin pkgconfig files are missing dependencies on the underlying libraries (e.g. freetype, png, jpeg), so we have to link them manually. https://github.com/openscenegraph/OpenSceneGraph/issues/1052 --- apps/opencs/CMakeLists.txt | 23 +++++++++++++++++------ apps/openmw/CMakeLists.txt | 22 +++++++++++++++++----- components/CMakeLists.txt | 26 ++++++++++++++++---------- 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 8be1fa4d3..65575580a 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -215,12 +215,16 @@ if(APPLE) endif(APPLE) target_link_libraries(openmw-cs - ${OSG_LIBRARIES} - ${OSGTEXT_LIBRARIES} - ${OSGUTIL_LIBRARIES} + # CMake's built-in OSG finder does not use pkgconfig, so we have to + # manually ensure the order is correct for inter-library dependencies. + # This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`. + # https://gitlab.kitware.com/cmake/cmake/-/issues/21701 ${OSGVIEWER_LIBRARIES} - ${OSGGA_LIBRARIES} ${OSGFX_LIBRARIES} + ${OSGGA_LIBRARIES} + ${OSGUTIL_LIBRARIES} + ${OSGTEXT_LIBRARIES} + ${OSG_LIBRARIES} ${EXTERN_OSGQT_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} @@ -233,17 +237,24 @@ if(OSG_STATIC) add_library(openmw_cs_osg_plugins INTERFACE) foreach(_plugin ${USED_OSG_PLUGINS}) string(TOUPPER ${_plugin} _plugin_uc) - if (${_plugin_uc}_LIBRARY MATCHES "[/.]") + if(OPENMW_USE_SYSTEM_OSG) list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY}) else() list(APPEND _osg_plugins_static_files $) + target_link_libraries(openmw_cs_osg_plugins INTERFACE $) + add_dependencies(openmw_cs_osg_plugins ${${_plugin_uc}_LIBRARY}) endif() - target_link_libraries(openmw_cs_osg_plugins INTERFACE ${${_plugin_uc}_LIBRARY}) 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) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index dda517c44..5e5bb8a9f 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -122,13 +122,18 @@ include_directories( ) target_link_libraries(openmw - ${OSG_LIBRARIES} + # CMake's built-in OSG finder does not use pkgconfig, so we have to + # manually ensure the order is correct for inter-library dependencies. + # This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`. + # https://gitlab.kitware.com/cmake/cmake/-/issues/21701 ${OSGPARTICLE_LIBRARIES} - ${OSGUTIL_LIBRARIES} - ${OSGDB_LIBRARIES} ${OSGVIEWER_LIBRARIES} ${OSGGA_LIBRARIES} ${OSGSHADOW_LIBRARIES} + ${OSGDB_LIBRARIES} + ${OSGUTIL_LIBRARIES} + ${OSG_LIBRARIES} + ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} @@ -148,17 +153,24 @@ if(OSG_STATIC) add_library(openmw_osg_plugins INTERFACE) foreach(_plugin ${USED_OSG_PLUGINS}) string(TOUPPER ${_plugin} _plugin_uc) - if (${_plugin_uc}_LIBRARY MATCHES "[/.]") + if(OPENMW_USE_SYSTEM_OSG) list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY}) else() list(APPEND _osg_plugins_static_files $) + target_link_libraries(openmw_osg_plugins INTERFACE $) + add_dependencies(openmw_osg_plugins ${${_plugin_uc}_LIBRARY}) endif() - target_link_libraries(openmw_osg_plugins INTERFACE ${${_plugin_uc}_LIBRARY}) 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) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2120afc1a..4da75c97c 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -223,20 +223,26 @@ include_directories(${BULLET_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR}) target_link_libraries(components - ${Boost_SYSTEM_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${Boost_IOSTREAMS_LIBRARY} - ${OSG_LIBRARIES} - ${OPENTHREADS_LIBRARIES} + # CMake's built-in OSG finder does not use pkgconfig, so we have to + # manually ensure the order is correct for inter-library dependencies. + # This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`. + # https://gitlab.kitware.com/cmake/cmake/-/issues/21701 ${OSGPARTICLE_LIBRARIES} - ${OSGUTIL_LIBRARIES} - ${OSGDB_LIBRARIES} ${OSGVIEWER_LIBRARIES} - ${OSGTEXT_LIBRARIES} - ${OSGGA_LIBRARIES} ${OSGSHADOW_LIBRARIES} ${OSGANIMATION_LIBRARIES} + ${OSGGA_LIBRARIES} + ${OSGTEXT_LIBRARIES} + ${OSGDB_LIBRARIES} + ${OSGUTIL_LIBRARIES} + ${OSG_LIBRARIES} + ${OPENTHREADS_LIBRARIES} + + ${Boost_SYSTEM_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_IOSTREAMS_LIBRARY} + ${SDL2_LIBRARIES} ${OPENGL_gl_LIBRARY} ${MyGUI_LIBRARIES} From b7076549a393f3e1e87dfc750b40246fd21e08d7 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 24 Jan 2021 06:11:44 +0000 Subject: [PATCH 056/132] osg-ffmpeg-videoplayer: Fix crash on ARM osg-ffmpeg-videoplayer handled frame allocation incorrectly. It used a `vector` as its buffer, meaning the addresses could did not respect alignment. Instead, changes it to use `AVFrame` as buffers, allocated via `av_image_alloc`. We also now only allocate the buffer once, instead of on every frame, which should improve the framerate of videos. Fixes the following crash on startup on ARM: > Invalid address alignment (signal 7) Fixes #5807 --- extern/osg-ffmpeg-videoplayer/videostate.cpp | 45 ++++++++++++++------ extern/osg-ffmpeg-videoplayer/videostate.hpp | 3 +- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/extern/osg-ffmpeg-videoplayer/videostate.cpp b/extern/osg-ffmpeg-videoplayer/videostate.cpp index 5858d985a..cdfdc8ad5 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.cpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -49,7 +50,7 @@ VideoState::VideoState() , av_sync_type(AV_SYNC_DEFAULT) , audio_st(nullptr) , video_st(nullptr), frame_last_pts(0.0) - , video_clock(0.0), sws_context(nullptr), rgbaFrame(nullptr), pictq_size(0) + , video_clock(0.0), sws_context(nullptr), pictq_size(0) , pictq_rindex(0), pictq_windex(0) , mSeekRequested(false) , mSeekPos(0) @@ -220,7 +221,7 @@ void VideoState::video_display(VideoPicture *vp) osg::ref_ptr image = new osg::Image; image->setImage(this->video_ctx->width, this->video_ctx->height, - 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, &vp->data[0], osg::Image::NO_DELETE); + 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, vp->rgbaFrame->data[0], osg::Image::NO_DELETE); mTexture->setImage(image); } @@ -308,11 +309,8 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) } vp->pts = pts; - vp->data.resize(this->video_ctx->width * this->video_ctx->height * 4); - - uint8_t *dst[4] = { &vp->data[0], nullptr, nullptr, nullptr }; sws_scale(this->sws_context, pFrame->data, pFrame->linesize, - 0, this->video_ctx->height, dst, this->rgbaFrame->linesize); + 0, this->video_ctx->height, vp->rgbaFrame->data, vp->rgbaFrame->linesize); // now we inform our display thread that we have a pic ready this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_ARRAY_SIZE; @@ -364,9 +362,6 @@ public: pFrame = av_frame_alloc(); - self->rgbaFrame = av_frame_alloc(); - av_image_alloc(self->rgbaFrame->data, self->rgbaFrame->linesize, self->video_ctx->width, self->video_ctx->height, AV_PIX_FMT_RGBA, 1); - while(self->videoq.get(packet, self) >= 0) { if(packet->data == flush_pkt.data) @@ -407,10 +402,7 @@ public: av_packet_unref(packet); - av_free(pFrame); - - av_freep(&self->rgbaFrame->data[0]); - av_free(self->rgbaFrame); + av_frame_free(&pFrame); } private: @@ -630,6 +622,25 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) return -1; } + // Allocate RGBA frame queue. + for (std::size_t i = 0; i < VIDEO_PICTURE_ARRAY_SIZE; ++i) { + AVFrame *frame = av_frame_alloc(); + if (frame == nullptr) { + std::cerr << "av_frame_alloc failed" << std::endl; + return -1; + } + + constexpr AVPixelFormat kPixFmt = AV_PIX_FMT_RGBA; + frame->format = kPixFmt; + frame->width = this->video_ctx->width; + frame->height = this->video_ctx->height; + if (av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, kPixFmt, 1) < 0) { + std::cerr << "av_image_alloc failed" << std::endl; + return -1; + } + this->pictq[i].rgbaFrame = frame; + } + this->video_thread.reset(new VideoThread(this)); break; @@ -771,6 +782,14 @@ void VideoState::deinit() mTexture->setImage(nullptr); mTexture = nullptr; } + + // Dellocate RGBA frame queue. + for (std::size_t i = 0; i < VIDEO_PICTURE_ARRAY_SIZE; ++i) { + if (this->pictq[i].rgbaFrame == nullptr) continue; + av_freep(&this->pictq[i].rgbaFrame->data[0]); + av_frame_free(&this->pictq[i].rgbaFrame); + } + } double VideoState::get_external_clock() diff --git a/extern/osg-ffmpeg-videoplayer/videostate.hpp b/extern/osg-ffmpeg-videoplayer/videostate.hpp index 641fce04b..ed226bcf2 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.hpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.hpp @@ -95,7 +95,7 @@ struct VideoPicture { VideoPicture() : pts(0.0) { } - std::vector data; + AVFrame* rgbaFrame = nullptr; double pts; }; @@ -160,7 +160,6 @@ struct VideoState { PacketQueue videoq; SwsContext* sws_context; VideoPicture pictq[VIDEO_PICTURE_ARRAY_SIZE]; - AVFrame* rgbaFrame; // used as buffer for the frame converted from its native format to RGBA int pictq_size, pictq_rindex, pictq_windex; std::mutex pictq_mutex; std::condition_variable pictq_cond; From 482f04c83678515927ce7368f0dd8a10afc1b76e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 7 Mar 2021 12:08:43 +0400 Subject: [PATCH 057/132] Use correct mCursorActive flag initial value --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7da8cb7bd..e82ba8c1d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -172,7 +172,7 @@ namespace MWGui , mWerewolfOverlayEnabled(Settings::Manager::getBool ("werewolf overlay", "GUI")) , mHudEnabled(true) , mCursorVisible(true) - , mCursorActive(false) + , mCursorActive(true) , mPlayerBounty(-1) , mGui(nullptr) , mGuiModes() From eb93fdfbea6fc1c7a96c7b734a02fe2b7fb51464 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 7 Mar 2021 03:57:54 +0000 Subject: [PATCH 058/132] Use unique_ptr with custom deleter for VideoPicture::rgbaFrame --- extern/osg-ffmpeg-videoplayer/videostate.cpp | 20 ++++++++++++-------- extern/osg-ffmpeg-videoplayer/videostate.hpp | 6 +++++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/extern/osg-ffmpeg-videoplayer/videostate.cpp b/extern/osg-ffmpeg-videoplayer/videostate.cpp index cdfdc8ad5..0380c82b0 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.cpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.cpp @@ -156,6 +156,12 @@ void PacketQueue::clear() this->mutex.unlock (); } +void VideoPicture::AVFrameDeleter::operator()(AVFrame* frame) const +{ + av_freep(frame->data); + av_frame_free(&frame); +} + int VideoState::istream_read(void *user_data, uint8_t *buf, int buf_size) { try @@ -623,8 +629,9 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) } // Allocate RGBA frame queue. - for (std::size_t i = 0; i < VIDEO_PICTURE_ARRAY_SIZE; ++i) { - AVFrame *frame = av_frame_alloc(); + for (std::size_t i = 0; i < VIDEO_PICTURE_ARRAY_SIZE; ++i) + { + std::unique_ptr frame{av_frame_alloc()}; if (frame == nullptr) { std::cerr << "av_frame_alloc failed" << std::endl; return -1; @@ -638,7 +645,7 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) std::cerr << "av_image_alloc failed" << std::endl; return -1; } - this->pictq[i].rgbaFrame = frame; + this->pictq[i].rgbaFrame = std::move(frame); } this->video_thread.reset(new VideoThread(this)); @@ -784,11 +791,8 @@ void VideoState::deinit() } // Dellocate RGBA frame queue. - for (std::size_t i = 0; i < VIDEO_PICTURE_ARRAY_SIZE; ++i) { - if (this->pictq[i].rgbaFrame == nullptr) continue; - av_freep(&this->pictq[i].rgbaFrame->data[0]); - av_frame_free(&this->pictq[i].rgbaFrame); - } + for (std::size_t i = 0; i < VIDEO_PICTURE_ARRAY_SIZE; ++i) + this->pictq[i].rgbaFrame = nullptr; } diff --git a/extern/osg-ffmpeg-videoplayer/videostate.hpp b/extern/osg-ffmpeg-videoplayer/videostate.hpp index ed226bcf2..b54c03cb9 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.hpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.hpp @@ -95,7 +95,11 @@ struct VideoPicture { VideoPicture() : pts(0.0) { } - AVFrame* rgbaFrame = nullptr; + struct AVFrameDeleter { + void operator()(AVFrame* frame) const; + }; + + std::unique_ptr rgbaFrame; double pts; }; From eb07818f130ec0e6b0b29d9928d9d0cd2dafabbd Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 7 Mar 2021 20:58:09 +0100 Subject: [PATCH 059/132] Ignore agents without bounding boxes --- components/detournavigator/navigatorimpl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index c47cf9766..142ba590d 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -18,6 +18,8 @@ namespace DetourNavigator void NavigatorImpl::addAgent(const osg::Vec3f& agentHalfExtents) { + if(agentHalfExtents.length2() <= 0) + return; ++mAgents[agentHalfExtents]; mNavMeshManager.addAgent(agentHalfExtents); } From 58d33aa95b729d81c236a587b59352c6a7c7f017 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Mon, 8 Mar 2021 03:16:55 +0000 Subject: [PATCH 060/132] AV: Fix all memory leaks The most substantial memory leak came from `PacketQueue::get` not unreferencing its argument packet. Other leaks came from using `av_free` instead of type-specific free functions. Also modifies `PacketQueue::put` for readability. --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 10 +++---- .../osg-ffmpeg-videoplayer/audiodecoder.cpp | 4 +-- extern/osg-ffmpeg-videoplayer/videostate.cpp | 28 +++++++++---------- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 95ed9eeed..84ea30b3c 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -287,9 +287,9 @@ void FFmpeg_Decoder::close() mStream = nullptr; av_packet_unref(&mPacket); - av_freep(&mFrame); - swr_free(&mSwr); av_freep(&mDataBuf); + av_frame_free(&mFrame); + swr_free(&mSwr); if(mFormatCtx) { @@ -302,11 +302,9 @@ void FFmpeg_Decoder::close() // if (mFormatCtx->pb->buffer != nullptr) { - av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = nullptr; + av_freep(&mFormatCtx->pb->buffer); } - av_free(mFormatCtx->pb); - mFormatCtx->pb = nullptr; + avio_context_free(&mFormatCtx->pb); } avformat_close_input(&mFormatCtx); } diff --git a/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp b/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp index 337382679..c32794d2a 100644 --- a/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp +++ b/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp @@ -91,7 +91,7 @@ MovieAudioDecoder::~MovieAudioDecoder() if(mAudioContext) avcodec_free_context(&mAudioContext); - av_freep(&mFrame); + av_frame_free(&mFrame); av_freep(&mDataBuf); } @@ -222,7 +222,7 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame, int &sample_skip) return result; } - av_packet_unref(&mPacket); + av_packet_unref(pkt); mGetNextPacket = true; /* next packet */ diff --git a/extern/osg-ffmpeg-videoplayer/videostate.cpp b/extern/osg-ffmpeg-videoplayer/videostate.cpp index 0380c82b0..7e523e054 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.cpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.cpp @@ -83,10 +83,11 @@ void PacketQueue::put(AVPacket *pkt) pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); if(!pkt1) throw std::bad_alloc(); - if(pkt != &flush_pkt && !pkt->buf && av_packet_ref(&pkt1->pkt, pkt) < 0) - throw std::runtime_error("Failed to duplicate packet"); + if(pkt == &flush_pkt) + pkt1->pkt = *pkt; + else + av_packet_move_ref(&pkt1->pkt, pkt); - pkt1->pkt = *pkt; pkt1->next = nullptr; this->mutex.lock (); @@ -117,7 +118,8 @@ int PacketQueue::get(AVPacket *pkt, VideoState *is) this->nb_packets--; this->size -= pkt1->pkt.size; - *pkt = pkt1->pkt; + av_packet_unref(pkt); + av_packet_move_ref(pkt, &pkt1->pkt); av_free(pkt1); return 1; @@ -364,6 +366,7 @@ public: { VideoState* self = mVideoState; AVPacket pkt1, *packet = &pkt1; + av_init_packet(packet); AVFrame *pFrame; pFrame = av_frame_alloc(); @@ -436,6 +439,7 @@ public: AVFormatContext *pFormatCtx = self->format_ctx; AVPacket pkt1, *packet = &pkt1; + av_init_packet(packet); try { @@ -691,16 +695,13 @@ void VideoState::init(std::shared_ptr inputstream, const std::stri { if (this->format_ctx->pb != nullptr) { - av_free(this->format_ctx->pb->buffer); - this->format_ctx->pb->buffer = nullptr; - - av_free(this->format_ctx->pb); - this->format_ctx->pb = nullptr; + av_freep(&this->format_ctx->pb->buffer); + avio_context_free(&this->format_ctx->pb); } } // "Note that a user-supplied AVFormatContext will be freed on failure." this->format_ctx = nullptr; - av_free(ioCtx); + avio_context_free(&ioCtx); throw std::runtime_error("Failed to open video input"); } @@ -774,11 +775,8 @@ void VideoState::deinit() /// if (this->format_ctx->pb != nullptr) { - av_free(this->format_ctx->pb->buffer); - this->format_ctx->pb->buffer = nullptr; - - av_free(this->format_ctx->pb); - this->format_ctx->pb = nullptr; + av_freep(&this->format_ctx->pb->buffer); + avio_context_free(&this->format_ctx->pb); } avformat_close_input(&this->format_ctx); } From 36bac353df3ccf0b6d21d591a994ac4922b367e6 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Mon, 8 Mar 2021 04:00:11 +0000 Subject: [PATCH 061/132] AV: Handle varying video frame dimensions --- extern/osg-ffmpeg-videoplayer/videostate.cpp | 60 ++++++++++++-------- extern/osg-ffmpeg-videoplayer/videostate.hpp | 6 ++ 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/extern/osg-ffmpeg-videoplayer/videostate.cpp b/extern/osg-ffmpeg-videoplayer/videostate.cpp index 7e523e054..a3a1e4318 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.cpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.cpp @@ -158,6 +158,33 @@ void PacketQueue::clear() this->mutex.unlock (); } +int VideoPicture::set_dimensions(int w, int h) { + if (this->rgbaFrame != nullptr && this->rgbaFrame->width == w && + this->rgbaFrame->height == h) { + return 0; + } + + std::unique_ptr frame{ + av_frame_alloc()}; + if (frame == nullptr) { + std::cerr << "av_frame_alloc failed" << std::endl; + return -1; + } + + constexpr AVPixelFormat kPixFmt = AV_PIX_FMT_RGBA; + frame->format = kPixFmt; + frame->width = w; + frame->height = h; + if (av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, + kPixFmt, 1) < 0) { + std::cerr << "av_image_alloc failed" << std::endl; + return -1; + } + + this->rgbaFrame = std::move(frame); + return 0; +} + void VideoPicture::AVFrameDeleter::operator()(AVFrame* frame) const { av_freep(frame->data); @@ -305,18 +332,25 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) // Convert the image into RGBA format // TODO: we could do this in a pixel shader instead, if the source format // matches a commonly used format (ie YUV420P) - if(this->sws_context == nullptr) + const int w = pFrame->width; + const int h = pFrame->height; + if(this->sws_context == nullptr || this->sws_context_w != w || this->sws_context_h != h) { - int w = this->video_ctx->width; - int h = this->video_ctx->height; + if (this->sws_context != nullptr) + sws_freeContext(this->sws_context); this->sws_context = sws_getContext(w, h, this->video_ctx->pix_fmt, w, h, AV_PIX_FMT_RGBA, SWS_BICUBIC, nullptr, nullptr, nullptr); if(this->sws_context == nullptr) throw std::runtime_error("Cannot initialize the conversion context!\n"); + this->sws_context_w = w; + this->sws_context_h = h; } vp->pts = pts; + if (vp->set_dimensions(w, h) < 0) + return -1; + sws_scale(this->sws_context, pFrame->data, pFrame->linesize, 0, this->video_ctx->height, vp->rgbaFrame->data, vp->rgbaFrame->linesize); @@ -632,26 +666,6 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) return -1; } - // Allocate RGBA frame queue. - for (std::size_t i = 0; i < VIDEO_PICTURE_ARRAY_SIZE; ++i) - { - std::unique_ptr frame{av_frame_alloc()}; - if (frame == nullptr) { - std::cerr << "av_frame_alloc failed" << std::endl; - return -1; - } - - constexpr AVPixelFormat kPixFmt = AV_PIX_FMT_RGBA; - frame->format = kPixFmt; - frame->width = this->video_ctx->width; - frame->height = this->video_ctx->height; - if (av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, kPixFmt, 1) < 0) { - std::cerr << "av_image_alloc failed" << std::endl; - return -1; - } - this->pictq[i].rgbaFrame = std::move(frame); - } - this->video_thread.reset(new VideoThread(this)); break; diff --git a/extern/osg-ffmpeg-videoplayer/videostate.hpp b/extern/osg-ffmpeg-videoplayer/videostate.hpp index b54c03cb9..015656084 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.hpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.hpp @@ -99,6 +99,11 @@ struct VideoPicture { void operator()(AVFrame* frame) const; }; + // Sets frame dimensions. + // Must be called before writing to `rgbaFrame`. + // Return -1 on error. + int set_dimensions(int w, int h); + std::unique_ptr rgbaFrame; double pts; }; @@ -163,6 +168,7 @@ struct VideoState { double video_clock; /// Date: Mon, 8 Mar 2021 04:16:33 +0000 Subject: [PATCH 062/132] AV: Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e4b70780..97634cedf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,7 @@ Bug #5739: Saving and loading the save a second or two before hitting the ground doesn't count fall damage Bug #5758: Paralyzed actors behavior is inconsistent with vanilla Bug #5762: Movement solver is insufficiently robust + Bug #5807: Video decoding crash on ARM Bug #5821: NPCs from mods getting removed if mod order was changed Bug #5835: OpenMW doesn't accept negative values for NPC's hello, alarm, fight, and flee Bug #5836: OpenMW dialogue/greeting/voice filter doesn't accept negative Ai values for NPC's hello, alarm, fight, and flee From d805886de70eec037db6cd49a9433caa3070bd8a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 8 Mar 2021 10:58:51 +0400 Subject: [PATCH 063/132] Double-buffer shader water stateset (bug #5026) --- CHANGELOG.md | 1 + apps/openmw/mwrender/renderingmanager.cpp | 4 +- apps/openmw/mwrender/sky.cpp | 31 ++++--------- apps/openmw/mwrender/sky.hpp | 8 ++-- apps/openmw/mwrender/water.cpp | 55 ++++++++++++++++++----- apps/openmw/mwrender/water.hpp | 7 ++- 6 files changed, 62 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e4b70780..58a09429b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ Bug #4764: Data race in osg ParticleSystem Bug #4765: Data race in ChunkManager -> Array::setBinding Bug #4774: Guards are ignorant of an invisible player that tries to attack them + Bug #5026: Data races with rain intensity uniform set by sky and used by water Bug #5101: Hostile followers travel with the player Bug #5108: Savegame bloating due to inefficient fog textures format Bug #5165: Active spells should use real time intead of timestamps diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a3aee5c0f..452104642 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -370,7 +370,6 @@ namespace MWRender mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); mSky->setCamera(mViewer->getCamera()); - mSky->setRainIntensityUniform(mWater->getRainIntensityUniform()); source->setStateSetModes(*mRootNode->getOrCreateStateSet(), osg::StateAttribute::ON); @@ -658,6 +657,9 @@ namespace MWRender mUnrefQueue->flush(mWorkQueue.get()); + float rainIntensity = mSky->getEffectFade(); + mWater->setRainIntensity(rainIntensity); + if (!paused) { mEffectManager->update(dt); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 389944000..d6b1f9066 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -1113,7 +1113,6 @@ private: SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneManager) : mSceneManager(sceneManager) , mCamera(nullptr) - , mRainIntensityUniform(nullptr) , mAtmosphereNightRoll(0.f) , mCreated(false) , mIsStorm(false) @@ -1163,11 +1162,6 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana mUnderwaterSwitch = new UnderwaterSwitchCallback(skyroot); } -void SkyManager::setRainIntensityUniform(osg::Uniform *uniform) -{ - mRainIntensityUniform = uniform; -} - void SkyManager::create() { assert(!mCreated); @@ -1576,30 +1570,21 @@ bool SkyManager::isEnabled() return mEnabled; } -bool SkyManager::hasRain() +bool SkyManager::hasRain() const { return mRainNode != nullptr; } -void SkyManager::update(float duration) +float SkyManager::getEffectFade() const { - if (!mEnabled) - { - if (mRainIntensityUniform) - mRainIntensityUniform->set(0.f); - - return; - } - - if (mRainIntensityUniform) - { - float rainIntensity = 0.f; - if (!mIsStorm && (hasRain() || mParticleNode)) - rainIntensity = mEffectFade; + if (mEnabled && !mIsStorm && (hasRain() || mParticleNode)) + return mEffectFade; - mRainIntensityUniform->set(rainIntensity); - } + return 0.f; +} +void SkyManager::update(float duration) +{ switchUnderwaterRain(); if (mIsStorm) diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index f32c40192..081221398 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -7,7 +7,6 @@ #include #include -#include namespace osg { @@ -157,7 +156,9 @@ namespace MWRender bool isEnabled(); - bool hasRain(); + bool hasRain() const; + + float getEffectFade() const; void setRainSpeed(float speed); @@ -180,8 +181,6 @@ namespace MWRender void setCamera(osg::Camera *camera); - void setRainIntensityUniform(osg::Uniform *uniform); - float getBaseWindSpeed() const; private: @@ -196,7 +195,6 @@ namespace MWRender Resource::SceneManager* mSceneManager; osg::Camera *mCamera; - osg::Uniform *mRainIntensityUniform; osg::ref_ptr mRootNode; osg::ref_ptr mEarlyRenderBinRoot; diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 1cc5a3cb7..af24a8648 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -208,6 +208,37 @@ public: } }; +class RainIntensityUpdater : public SceneUtil::StateSetUpdater +{ +public: + RainIntensityUpdater() + : mRainIntensity(0.f) + { + } + + void setRainIntensity(float rainIntensity) + { + mRainIntensity = rainIntensity; + } + +protected: + void setDefaults(osg::StateSet* stateset) override + { + osg::ref_ptr rainIntensityUniform = new osg::Uniform("rainIntensity", 0.0f); + stateset->addUniform(rainIntensityUniform.get()); + } + + void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) override + { + osg::ref_ptr rainIntensityUniform = stateset->getUniform("rainIntensity"); + if (rainIntensityUniform != nullptr) + rainIntensityUniform->set(mRainIntensity); + } + +private: + float mRainIntensity; +}; + osg::ref_ptr readPngImage (const std::string& file) { // use boost in favor of osgDB::readImage, to handle utf-8 path issues on Windows @@ -431,7 +462,8 @@ public: Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem *resourceSystem, osgUtil::IncrementalCompileOperation *ico, const std::string& resourcePath) - : mParent(parent) + : mRainIntensityUpdater(nullptr) + , mParent(parent) , mSceneRoot(sceneRoot) , mResourceSystem(resourceSystem) , mResourcePath(resourcePath) @@ -463,8 +495,6 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem setHeight(mTop); - mRainIntensityUniform = new osg::Uniform("rainIntensity",(float) 0.0); - updateWaterMaterial(); if (ico) @@ -494,11 +524,6 @@ void Water::setCullCallback(osg::Callback* callback) } } -osg::Uniform *Water::getRainIntensityUniform() -{ - return mRainIntensityUniform.get(); -} - void Water::updateWaterMaterial() { if (mReflection) @@ -556,6 +581,8 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha) osg::ref_ptr stateset = SceneUtil::createSimpleWaterStateSet(alpha, MWRender::RenderBin_Water); node->setStateSet(stateset); + node->setUpdateCallback(nullptr); + mRainIntensityUpdater = nullptr; // Add animated textures std::vector > textures; @@ -639,15 +666,15 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R shaderStateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); - shaderStateset->addUniform(mRainIntensityUniform.get()); - osg::ref_ptr program (new osg::Program); program->addShader(vertexShader); program->addShader(fragmentShader); shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON); node->setStateSet(shaderStateset); - node->setUpdateCallback(nullptr); + + mRainIntensityUpdater = new RainIntensityUpdater(); + node->setUpdateCallback(mRainIntensityUpdater); } void Water::processChangedSettings(const Settings::CategorySettingVector& settings) @@ -730,6 +757,12 @@ void Water::setHeight(const float height) mRefraction->setWaterLevel(mTop); } +void Water::setRainIntensity(float rainIntensity) +{ + if (mRainIntensityUpdater) + mRainIntensityUpdater->setRainIntensity(rainIntensity); +} + void Water::update(float dt) { mSimulation->update(dt); diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 3787ef426..ec7dc95db 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -46,11 +45,12 @@ namespace MWRender class Refraction; class Reflection; class RippleSimulation; + class RainIntensityUpdater; /// Water rendering class Water { - osg::ref_ptr mRainIntensityUniform; + osg::ref_ptr mRainIntensityUpdater; osg::ref_ptr mParent; osg::ref_ptr mSceneRoot; @@ -112,6 +112,7 @@ namespace MWRender void changeCell(const MWWorld::CellStore* store); void setHeight(const float height); + void setRainIntensity(const float rainIntensity); void update(float dt); @@ -119,8 +120,6 @@ namespace MWRender osg::Camera *getRefractionCamera(); void processChangedSettings(const Settings::CategorySettingVector& settings); - - osg::Uniform *getRainIntensityUniform(); }; } From 1db7d2ec4e78582f47a2187b5371f10018b28698 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Mon, 8 Mar 2021 19:29:34 +0000 Subject: [PATCH 064/132] Restore compatibility with FFMpeg < 57.80.100 This should fix macOS Travis build broken by 58d33aa95b729d81c236a587b59352c6a7c7f017 --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 4 ++++ extern/osg-ffmpeg-videoplayer/videostate.cpp | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 84ea30b3c..ec36a5cdf 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -304,7 +304,11 @@ void FFmpeg_Decoder::close() { av_freep(&mFormatCtx->pb->buffer); } +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100) avio_context_free(&mFormatCtx->pb); +#else + av_freep(&mFormatCtx->pb); +#endif } avformat_close_input(&mFormatCtx); } diff --git a/extern/osg-ffmpeg-videoplayer/videostate.cpp b/extern/osg-ffmpeg-videoplayer/videostate.cpp index a3a1e4318..7c4bddb01 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.cpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.cpp @@ -710,12 +710,20 @@ void VideoState::init(std::shared_ptr inputstream, const std::stri if (this->format_ctx->pb != nullptr) { av_freep(&this->format_ctx->pb->buffer); +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100) avio_context_free(&this->format_ctx->pb); +#else + av_freep(&this->format_ctx->pb); +#endif } } // "Note that a user-supplied AVFormatContext will be freed on failure." this->format_ctx = nullptr; +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100) avio_context_free(&ioCtx); +#else + av_freep(&ioCtx); +#endif throw std::runtime_error("Failed to open video input"); } @@ -790,7 +798,11 @@ void VideoState::deinit() if (this->format_ctx->pb != nullptr) { av_freep(&this->format_ctx->pb->buffer); +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100) avio_context_free(&this->format_ctx->pb); +#else + av_freep(&this->format_ctx->pb); +#endif } avformat_close_input(&this->format_ctx); } From 38679013fe6e426e3609ff833c1b55b02e0e644a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 10 Mar 2021 19:10:17 +0400 Subject: [PATCH 065/132] Give meaningful name to the mEffectFade --- apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwrender/sky.cpp | 14 +++++++------- apps/openmw/mwrender/sky.hpp | 6 +++--- apps/openmw/mwworld/weather.cpp | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 452104642..a6062f444 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -657,7 +657,7 @@ namespace MWRender mUnrefQueue->flush(mWorkQueue.get()); - float rainIntensity = mSky->getEffectFade(); + float rainIntensity = mSky->getPrecipitationAlpha(); mWater->setRainIntensity(rainIntensity); if (!paused) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index d6b1f9066..ddd4a0660 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -1138,7 +1138,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana , mBaseWindSpeed(0.f) , mEnabled(true) , mSunEnabled(true) - , mEffectFade(0.f) + , mPrecipitationAlpha(0.f) { osg::ref_ptr skyroot (new CameraRelativeTransform); skyroot->setName("Sky Root"); @@ -1516,7 +1516,7 @@ void SkyManager::createRain() osg::ref_ptr program (new osgParticle::ModularProgram); program->addOperator(new WrapAroundOperator(mCamera,rainRange)); - program->addOperator(new WeatherAlphaOperator(mEffectFade, true)); + program->addOperator(new WeatherAlphaOperator(mPrecipitationAlpha, true)); program->setParticleSystem(mRainParticleSystem); mRainNode->addChild(program); @@ -1575,10 +1575,10 @@ bool SkyManager::hasRain() const return mRainNode != nullptr; } -float SkyManager::getEffectFade() const +float SkyManager::getPrecipitationAlpha() const { if (mEnabled && !mIsStorm && (hasRain() || mParticleNode)) - return mEffectFade; + return mPrecipitationAlpha; return 0.f; } @@ -1722,7 +1722,7 @@ void SkyManager::setWeather(const WeatherResult& weather) SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::shared_ptr(new SceneUtil::FrameTimeSource)); mParticleEffect->accept(assignVisitor); - AlphaFader::SetupVisitor alphaFaderSetupVisitor(mEffectFade); + AlphaFader::SetupVisitor alphaFaderSetupVisitor(mPrecipitationAlpha); mParticleEffect->accept(alphaFaderSetupVisitor); @@ -1739,7 +1739,7 @@ void SkyManager::setWeather(const WeatherResult& weather) osg::ref_ptr program (new osgParticle::ModularProgram); if (!mIsStorm) program->addOperator(new WrapAroundOperator(mCamera,osg::Vec3(1024,1024,800))); - program->addOperator(new WeatherAlphaOperator(mEffectFade, false)); + program->addOperator(new WeatherAlphaOperator(mPrecipitationAlpha, false)); program->setParticleSystem(ps); mParticleNode->addChild(program); } @@ -1828,7 +1828,7 @@ void SkyManager::setWeather(const WeatherResult& weather) mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0 : 0); - mEffectFade = weather.mEffectFade; + mPrecipitationAlpha = weather.mPrecipitationAlpha; } float SkyManager::getBaseWindSpeed() const diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 081221398..f8c501dda 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -87,7 +87,7 @@ namespace MWRender std::string mParticleEffect; std::string mRainEffect; - float mEffectFade; + float mPrecipitationAlpha; float mRainDiameter; float mRainMinHeight; @@ -158,7 +158,7 @@ namespace MWRender bool hasRain() const; - float getEffectFade() const; + float getPrecipitationAlpha() const; void setRainSpeed(float speed); @@ -269,7 +269,7 @@ namespace MWRender bool mEnabled; bool mSunEnabled; - float mEffectFade; + float mPrecipitationAlpha; osg::Vec4f mMoonScriptColor; }; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 415e69d20..4bdd784db 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -1129,7 +1129,7 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam mResult.mGlareView = current.mGlareView; mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; mResult.mAmbientSoundVolume = 1.f; - mResult.mEffectFade = 1.f; + mResult.mPrecipitationAlpha = 1.f; mResult.mIsStorm = current.mIsStorm; @@ -1236,7 +1236,7 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const mResult.mRainSpeed = current.mRainSpeed; mResult.mRainEntranceSpeed = current.mRainEntranceSpeed; mResult.mAmbientSoundVolume = 1 - factor / threshold; - mResult.mEffectFade = mResult.mAmbientSoundVolume; + mResult.mPrecipitationAlpha = mResult.mAmbientSoundVolume; mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; mResult.mRainDiameter = current.mRainDiameter; mResult.mRainMinHeight = current.mRainMinHeight; @@ -1251,7 +1251,7 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const mResult.mRainSpeed = other.mRainSpeed; mResult.mRainEntranceSpeed = other.mRainEntranceSpeed; mResult.mAmbientSoundVolume = (factor - threshold) / (1 - threshold); - mResult.mEffectFade = mResult.mAmbientSoundVolume; + mResult.mPrecipitationAlpha = mResult.mAmbientSoundVolume; mResult.mAmbientLoopSoundID = other.mAmbientLoopSoundID; mResult.mRainDiameter = other.mRainDiameter; From cb2cbb4181c47261a3611e10ed259111f15880c6 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 10 Mar 2021 22:07:14 +0000 Subject: [PATCH 066/132] Convert blending factors properly for the character preview --- apps/openmw/mwrender/characterpreview.cpp | 38 ++++++++++++++----- .../myguiplatform/myguirendermanager.cpp | 11 ++++++ 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 633c8fcb7..59dfd944e 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -84,7 +84,8 @@ namespace MWRender }; - // Set up alpha blending to Additive mode to avoid issues caused by transparent objects writing onto the alpha value of the FBO + // Set up alpha blending mode to avoid issues caused by transparent objects writing onto the alpha value of the FBO + // This makes the RTT have premultiplied alpha, though, so the source blend factor must be GL_ONE when it's applied class SetUpBlendVisitor : public osg::NodeVisitor { public: @@ -94,22 +95,40 @@ namespace MWRender void apply(osg::Node& node) override { - if (osg::StateSet* stateset = node.getStateSet()) + if (osg::ref_ptr stateset = node.getStateSet()) { + osg::ref_ptr newStateSet; if (stateset->getAttribute(osg::StateAttribute::BLENDFUNC) || stateset->getBinNumber() == osg::StateSet::TRANSPARENT_BIN) { - osg::ref_ptr newStateSet = new osg::StateSet(*stateset, osg::CopyOp::SHALLOW_COPY); osg::BlendFunc* blendFunc = static_cast(stateset->getAttribute(osg::StateAttribute::BLENDFUNC)); - osg::ref_ptr newBlendFunc = blendFunc ? new osg::BlendFunc(*blendFunc) : new osg::BlendFunc; - newBlendFunc->setDestinationAlpha(osg::BlendFunc::ONE); - newStateSet->setAttribute(newBlendFunc, osg::StateAttribute::ON); - node.setStateSet(newStateSet); + + if (blendFunc) + { + newStateSet = new osg::StateSet(*stateset, osg::CopyOp::SHALLOW_COPY); + node.setStateSet(newStateSet); + osg::ref_ptr newBlendFunc = new osg::BlendFunc(*blendFunc); + newStateSet->setAttribute(newBlendFunc, osg::StateAttribute::ON); + // I *think* (based on some by-hand maths) that the RGB and dest alpha factors are unchanged, and only dest determines source alpha factor + // This has the benefit of being idempotent if we assume nothing used glBlendFuncSeparate before we touched it + if (blendFunc->getDestination() == osg::BlendFunc::ONE_MINUS_SRC_ALPHA) + newBlendFunc->setSourceAlpha(osg::BlendFunc::ONE); + else if (blendFunc->getDestination() == osg::BlendFunc::ONE) + newBlendFunc->setSourceAlpha(osg::BlendFunc::ZERO); + // Other setups barely exist in the wild and aren't worth supporting as they're not equippable gear + else + Log(Debug::Info) << "Unable to adjust blend mode for character preview. Source factor 0x" << std::hex << blendFunc->getSource() << ", destination factor 0x" << blendFunc->getDestination() << std::dec; + } } if (stateset->getMode(GL_BLEND) & osg::StateAttribute::ON) { + if (!newStateSet) + { + newStateSet = new osg::StateSet(*stateset, osg::CopyOp::SHALLOW_COPY); + node.setStateSet(newStateSet); + } // Disable noBlendAlphaEnv - stateset->setTextureMode(7, GL_TEXTURE_2D, osg::StateAttribute::OFF); - stateset->addUniform(mNoAlphaUniform); + newStateSet->setTextureMode(7, GL_TEXTURE_2D, osg::StateAttribute::OFF); + newStateSet->addUniform(mNoAlphaUniform); } } traverse(node); @@ -134,6 +153,7 @@ namespace MWRender mTexture->setInternalFormat(GL_RGBA); mTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + mTexture->setUserValue("premultiplied alpha", true); mCamera = new osg::Camera; // hints that the camera is not relative to the master camera diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 1813c9e01..6f9ee8195 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -14,6 +14,8 @@ #include +#include + #include "myguicompat.h" #include "myguitexture.hpp" @@ -439,14 +441,23 @@ void RenderManager::doRender(MyGUI::IVertexBuffer *buffer, MyGUI::ITexture *text batch.mVertexBuffer = static_cast(buffer)->getVertexBuffer(); batch.mArray = static_cast(buffer)->getVertexArray(); static_cast(buffer)->markUsed(); + bool premultipliedAlpha = false; if (texture) { batch.mTexture = static_cast(texture)->getTexture(); if (batch.mTexture->getDataVariance() == osg::Object::DYNAMIC) mDrawable->setDataVariance(osg::Object::DYNAMIC); // only for this frame, reset in begin() + batch.mTexture->getUserValue("premultiplied alpha", premultipliedAlpha); } if (mInjectState) batch.mStateSet = mInjectState; + else if (premultipliedAlpha) + { + // This is hacky, but MyGUI made it impossible to use a custom layer for a nested node, so state couldn't be injected 'properly' + osg::ref_ptr stateSet = new osg::StateSet(); + stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::ONE, osg::BlendFunc::ONE_MINUS_SRC_ALPHA)); + batch.mStateSet = stateSet; + } mDrawable->addBatch(batch); } From d52ae28b7a35eae90f63e96e0f24452608483c7f Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 11 Mar 2021 00:28:14 +0000 Subject: [PATCH 067/132] Fix linking on MacOS --- components/myguiplatform/myguirendermanager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 6f9ee8195..77a5ee533 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include From 64ddb4c1b0180e9cf37ba376a928404aca3ec185 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 11 Mar 2021 01:01:55 +0000 Subject: [PATCH 068/132] Fix linking on MacOS --- apps/openmw/mwrender/characterpreview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 59dfd944e..7ebc964fe 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include From 63958c6d612c7f6c14a36f56d69ce239d5c66d65 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 11 Mar 2021 10:00:11 +0400 Subject: [PATCH 069/132] Changelog fixes --- CHANGELOG.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae0324531..53cce8dc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,6 @@ 0.47.0 ------ - Bug #832: OpenMW-CS: Handle deleted references Bug #1662: Qt4 and Windows binaries crash if there's a non-ASCII character in a file path/config path Bug #1901: Actors colliding behaviour is different from vanilla Bug #1952: Incorrect particle lighting @@ -9,7 +8,7 @@ Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #2473: Unable to overstock merchants Bug #2798: Mutable ESM records - Bug #2976 [reopened]: Issues combining settings from the command line and both config files + Bug #2976: [reopened]: Issues combining settings from the command line and both config files Bug #3137: Walking into a wall prevents jumping Bug #3372: Projectiles and magic bolts go through moving targets Bug #3676: NiParticleColorModifier isn't applied properly @@ -24,7 +23,7 @@ Bug #4201: Projectile-projectile collision Bug #4247: Cannot walk up stairs in Ebonheart docks Bug #4357: OpenMW-CS: TopicInfos index sorting and rearranging isn't fully functional - Bug #4363: Editor: Defect in Clone Function for Dialogue Info records + Bug #4363: OpenMW-CS: Defect in Clone Function for Dialogue Info records Bug #4447: Actor collision capsule shape allows looking through some walls Bug #4465: Collision shape overlapping causes twitching Bug #4476: Abot Gondoliers: player hangs in air during scenic travel @@ -45,11 +44,11 @@ Bug #5367: Selecting a spell on an enchanted item per hotkey always plays the equip sound Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures Bug #5370: Opening an unlocked but trapped door uses the key - Bug #5384: openmw-cs: deleting an instance requires reload of scene window to show in editor + Bug #5384: OpenMW-CS: Deleting an instance requires reload of scene window to show in editor Bug #5387: Move/MoveWorld don't update the object's cell properly Bug #5391: Races Redone 1.2 bodies don't show on the inventory Bug #5397: NPC greeting does not reset if you leave + reenter area - Bug #5400: Editor: Verifier checks race of non-skin bodyparts + Bug #5400: OpenMW-CS: Verifier checks race of non-skin bodyparts Bug #5403: Enchantment effect doesn't show on an enemy during death animation Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work Bug #5416: Junk non-node records before the root node are not handled gracefully @@ -71,6 +70,7 @@ Bug #5499: Faction advance is available when requirements not met Bug #5502: Dead zone for analogue stick movement is too small Bug #5507: Sound volume is not clamped on ingame settings update + Bug #5525: Case-insensitive search in the inventory window does not work with non-ASCII characters Bug #5531: Actors flee using current rotation by axis x Bug #5539: Window resize breaks when going from a lower resolution to full screen resolution Bug #5548: Certain exhausted topics can be highlighted again even though there's no new dialogue @@ -94,7 +94,7 @@ Bug #5703: OpenMW-CS menu system crashing on XFCE Bug #5706: AI sequences stop looping after the saved game is reloaded Bug #5713: OpenMW-CS: Collada models are corrupted in Qt-based scene view - Bug #5731: Editor: skirts are invisible on characters + Bug #5731: OpenMW-CS: skirts are invisible on characters Bug #5739: Saving and loading the save a second or two before hitting the ground doesn't count fall damage Bug #5758: Paralyzed actors behavior is inconsistent with vanilla Bug #5762: Movement solver is insufficiently robust @@ -109,6 +109,7 @@ Bug #5871: The console appears if you type the Russian letter "Ё" in the name of the enchantment Bug #5877: Effects appearing with empty icon Feature #390: 3rd person look "over the shoulder" + Feature #832: OpenMW-CS: Handle deleted references Feature #1536: Show more information about level on menu Feature #2386: Distant Statics in the form of Object Paging Feature #2404: Levelled List can not be placed into a container @@ -117,7 +118,7 @@ Feature #4894: Consider actors as obstacles for pathfinding Feature #4977: Use the "default icon.tga" when an item's icon is not found Feature #5043: Head Bobbing - Feature #5199: Improve Scene Colors + Feature #5199: OpenMW-CS: Improve scene view colors Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5445: Handle NiLines @@ -126,7 +127,6 @@ Feature #5486: Fixes trainers to choose their training skills based on their base skill points Feature #5519: Code Patch tab in launcher Feature #5524: Resume failed script execution after reload - Feature #5525: Search fields tweaks (utf-8) Feature #5545: Option to allow stealing from an unconscious NPC during combat Feature #5563: Run physics update in background thread Feature #5579: MCP SetAngle enhancement From 39b7260ab469528e815e041979222821da6ce28d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 11 Mar 2021 11:46:44 +0400 Subject: [PATCH 070/132] Fix a crash during new game --- apps/openmw/mwrender/sky.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index ddd4a0660..f82641676 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -1585,6 +1585,9 @@ float SkyManager::getPrecipitationAlpha() const void SkyManager::update(float duration) { + if (!mEnabled) + return; + switchUnderwaterRain(); if (mIsStorm) From 351d11449b8d0fba2940a0a2c237312527eac50d Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Thu, 11 Mar 2021 23:27:42 +0000 Subject: [PATCH 071/132] Avoid heightfield conversion in newer Bullet Takes advantage of the direct `float` support implemented in https://github.com/bulletphysics/bullet3/pull/3293 --- apps/openmw/mwphysics/heightfield.cpp | 14 ++++++++++++++ apps/openmw/mwphysics/heightfield.hpp | 2 ++ 2 files changed, 16 insertions(+) diff --git a/apps/openmw/mwphysics/heightfield.cpp b/apps/openmw/mwphysics/heightfield.cpp index 34127fe3a..e210bc390 100644 --- a/apps/openmw/mwphysics/heightfield.cpp +++ b/apps/openmw/mwphysics/heightfield.cpp @@ -10,6 +10,12 @@ #include +#if BT_BULLET_VERSION < 310 +// Older Bullet versions only support `btScalar` heightfields. +// Our heightfield data is `float`. +// +// These functions handle conversion from `float` to `double` when +// `btScalar` is `double` (`BT_USE_DOUBLE_PRECISION`). namespace { template @@ -40,14 +46,18 @@ namespace return btScalarHeights.data(); } } +#endif namespace MWPhysics { HeightField::HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject, PhysicsTaskScheduler* scheduler) : mHoldObject(holdObject) +#if BT_BULLET_VERSION < 310 , mHeights(makeHeights(heights, sqrtVerts)) +#endif , mTaskScheduler(scheduler) { +#if BT_BULLET_VERSION < 310 mShape = std::make_unique( sqrtVerts, sqrtVerts, getHeights(heights, mHeights), @@ -55,6 +65,10 @@ namespace MWPhysics minH, maxH, 2, PHY_FLOAT, false ); +#else + mShape = std::make_unique( + sqrtVerts, sqrtVerts, heights, minH, maxH, 2, false); +#endif mShape->setUseDiamondSubdivision(true); mShape->setLocalScaling(btVector3(triSize, triSize, 1)); diff --git a/apps/openmw/mwphysics/heightfield.hpp b/apps/openmw/mwphysics/heightfield.hpp index c76f8b943..93b2733f3 100644 --- a/apps/openmw/mwphysics/heightfield.hpp +++ b/apps/openmw/mwphysics/heightfield.hpp @@ -34,7 +34,9 @@ namespace MWPhysics std::unique_ptr mShape; std::unique_ptr mCollisionObject; osg::ref_ptr mHoldObject; +#if BT_BULLET_VERSION < 310 std::vector mHeights; +#endif PhysicsTaskScheduler* mTaskScheduler; From fe6b990f2e255897f47c719d5dbdf5dd9dda08f2 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 28 Feb 2021 17:03:11 +0000 Subject: [PATCH 072/132] Update Bullet to improve performance Updates Bullet to include https://github.com/bulletphysics/bullet3/pull/3287 This massively improves heightfield collision detection performance in some areas. E.g. with single-threaded bullet in the `--skip-menu` starting area on my test desktop: 30 FPS -> 60 FPS --- extern/CMakeLists.txt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index d746183b0..bf18e4136 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -31,15 +31,11 @@ if(NOT OPENMW_USE_SYSTEM_BULLET) set(USE_DOUBLE_PRECISION ${BULLET_USE_DOUBLES} CACHE BOOL "") set(BULLET2_MULTITHREADING ON CACHE BOOL "") - # Version 3.08 with the following changes: - # 1. Fixes the linking of Threads: - # https://github.com/bulletphysics/bullet3/pull/3237 - # 2. Removes ~300 MiB of files not used here: - # rm -rf build3 data docs examples test Doxyfile + # master on 12 Mar 2021 include(FetchContent) FetchContent_Declare(bullet - URL https://github.com/glebm/bullet3/archive/ed5256454f4f84bd2c1728c88ddb0405d614e7d2.zip - URL_HASH MD5=e3c94fac35a7be885ad8843f828a0f96 + URL https://github.com/bulletphysics/bullet3/archive/87e668f6b2a883b4ef63db8a07c8e9283916e9d9.zip + URL_HASH MD5=9f13246439968494c2b595cf412d83c8 SOURCE_DIR fetched/bullet ) FetchContent_MakeAvailableExcludeFromAll(bullet) From f09b0fc1bd63aed88020ccf1ea575307848deed0 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 12 Mar 2021 17:21:36 +0000 Subject: [PATCH 073/132] Put groundcover alphafunc where shader visitor can see it I'd already made this change so don't know why it disappeared instead of being included in b8ee32e3 --- apps/openmw/mwrender/groundcover.cpp | 4 ++++ apps/openmw/mwrender/renderingmanager.cpp | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/groundcover.cpp b/apps/openmw/mwrender/groundcover.cpp index 4390dae22..0baa85c52 100644 --- a/apps/openmw/mwrender/groundcover.cpp +++ b/apps/openmw/mwrender/groundcover.cpp @@ -1,5 +1,6 @@ #include "groundcover.hpp" +#include #include #include @@ -265,6 +266,9 @@ namespace MWRender group->addChild(node); } + // Force a unified alpha handling instead of data from meshes + osg::ref_ptr alpha = new osg::AlphaFunc(osg::AlphaFunc::GEQUAL, 128.f / 255.f); + group->getOrCreateStateSet()->setAttributeAndModes(alpha.get(), osg::StateAttribute::ON); group->getBound(); group->setNodeMask(Mask_Groundcover); mSceneManager->recreateShaders(group, "groundcover", false, true); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9f89d1bff..fd912aa2d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -312,10 +311,6 @@ namespace MWRender groundcoverRoot->setName("Groundcover Root"); sceneRoot->addChild(groundcoverRoot); - // Force a unified alpha handling instead of data from meshes - osg::ref_ptr alpha = new osg::AlphaFunc(osg::AlphaFunc::GEQUAL, 128.f/255.f); - groundcoverRoot->getOrCreateStateSet()->setAttributeAndModes(alpha.get(), osg::StateAttribute::ON); - mGroundcoverUpdater = new GroundcoverUpdater; groundcoverRoot->addUpdateCallback(mGroundcoverUpdater); From 31032664064b36709db603bd055316d5fc780edd Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 13 Mar 2021 01:31:35 +0000 Subject: [PATCH 074/132] Add correct preprocessor check when enabling extension --- files/shaders/groundcover_fragment.glsl | 4 ++++ files/shaders/shadowcasting_fragment.glsl | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/files/shaders/groundcover_fragment.glsl b/files/shaders/groundcover_fragment.glsl index c3339ba76..bc84ded61 100644 --- a/files/shaders/groundcover_fragment.glsl +++ b/files/shaders/groundcover_fragment.glsl @@ -1,5 +1,9 @@ #version 120 +#if @useGPUShader4 + #extension EXT_gpu_shader4: require +#endif + #define GROUNDCOVER #if @diffuseMap diff --git a/files/shaders/shadowcasting_fragment.glsl b/files/shaders/shadowcasting_fragment.glsl index ea8a63313..a83e155ca 100644 --- a/files/shaders/shadowcasting_fragment.glsl +++ b/files/shaders/shadowcasting_fragment.glsl @@ -1,6 +1,8 @@ #version 120 -#extension EXT_gpu_shader4: enable +#if @useGPUShader4 + #extension EXT_gpu_shader4: require +#endif uniform sampler2D diffuseMap; varying vec2 diffuseMapUV; From 03b86c232be6788443f687cd58acf98794e43db1 Mon Sep 17 00:00:00 2001 From: fredzio Date: Sat, 13 Mar 2021 09:52:05 +0100 Subject: [PATCH 075/132] Apply the position offset even if the simulation is not performed because we're too fast. --- apps/openmw/mwphysics/actor.cpp | 11 +++++++++++ apps/openmw/mwphysics/actor.hpp | 7 +++++++ apps/openmw/mwphysics/mtphysics.cpp | 1 + apps/openmw/mwphysics/physicssystem.cpp | 4 ++++ 4 files changed, 23 insertions(+) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index bbec4d445..06abe7240 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -190,6 +190,17 @@ void Actor::adjustPosition(const osg::Vec3f& offset) mPositionOffset += offset; } +void Actor::applyOffsetChange() +{ + if (mPositionOffset.length() == 0) + return; + mWorldPosition += mPositionOffset; + mPosition += mPositionOffset; + mPreviousPosition += mPositionOffset; + mPositionOffset = osg::Vec3f(); + mWorldPositionChanged = true; +} + osg::Vec3f Actor::getPosition() const { return mPosition; diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 9d129f2ba..031125f40 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -96,9 +96,16 @@ namespace MWPhysics * Returns true if the new position is different. */ bool setPosition(const osg::Vec3f& position); + + // force set actor position to be as in Ptr::RefData void updatePosition(); + + // register a position offset that will be applied during simulation. void adjustPosition(const osg::Vec3f& offset); + // apply position offset. Can't be called during simulation + void applyOffsetChange(); + osg::Vec3f getPosition() const; osg::Vec3f getPreviousPosition() const; diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index 754bb60af..2b0db5b82 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -492,6 +492,7 @@ namespace MWPhysics if (actor->setPosition(actorData.mPosition)) { std::scoped_lock lock(mCollisionWorldMutex); + actorData.mPosition = actor->getPosition(); // account for potential position change made by script actor->updateCollisionObjectPosition(); mCollisionWorld->updateSingleAabb(actor->getCollisionObject()); } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index b2decde2f..dc9ab629a 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -961,6 +961,10 @@ namespace MWPhysics void ActorFrameData::updatePosition() { mActorRaw->updateWorldPosition(); + // If physics runs "fast enough", position are interpolated without simulation + // By calling this here, we are sure that offsets are applied at least once per frame, + // regardless of simulation speed. + mActorRaw->applyOffsetChange(); mPosition = mActorRaw->getPosition(); if (mMoveToWaterSurface) { From cb39f8fb0123b08dbdaafd0baa915cdf2ffbd405 Mon Sep 17 00:00:00 2001 From: fredzio Date: Sat, 13 Mar 2021 09:53:21 +0100 Subject: [PATCH 076/132] Use moveObjectBy in SetPos --- .../mwscript/transformationextensions.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 103c6629d..e8b406977 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -270,20 +270,17 @@ namespace MWScript Interpreter::Type_Float pos = runtime[0].mFloat; runtime.pop(); - float ax = ptr.getRefData().getPosition().pos[0]; - float ay = ptr.getRefData().getPosition().pos[1]; - float az = ptr.getRefData().getPosition().pos[2]; - // Note: SetPos does not skip weather transitions in vanilla engine, so we do not call setTeleported(true) here. - MWWorld::Ptr updated = ptr; + const auto curPos = ptr.getRefData().getPosition().asVec3(); + auto newPos = curPos; if(axis == "x") { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr,pos,ay,az,true); + newPos[0] = pos; } else if(axis == "y") { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,pos,az,true); + newPos[1] = pos; } else if(axis == "z") { @@ -292,20 +289,21 @@ namespace MWScript { float terrainHeight = -std::numeric_limits::max(); if (ptr.getCell()->isExterior()) - terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(osg::Vec3f(ax, ay, az)); + terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(curPos); if (pos < terrainHeight) pos = terrainHeight; } - updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos,true); + newPos[2] = pos; } else { return; } - dynamic_cast(runtime.getContext()).updatePtr(ptr,updated); + dynamic_cast(runtime.getContext()).updatePtr(ptr, + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos)); } }; From 36cea2073f598cc9768f88419a9e4d14b68c0682 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 13 Mar 2021 12:10:14 +0000 Subject: [PATCH 077/132] Update MyGUI Includes https://github.com/MyGUI/mygui/commit/f01cba4bb392ae990893442f5bcdf05bfc7a1656 Fixes https://gitlab.com/OpenMW/openmw/-/issues/5897 --- extern/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index bf18e4136..7e9f189fe 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -63,10 +63,11 @@ if(NOT OPENMW_USE_SYSTEM_MYGUI) set(BUILD_SHARED_LIBS ON) endif() + # master on 13 Mar 2021 include(FetchContent) FetchContent_Declare(mygui - URL https://github.com/MyGUI/mygui/archive/MyGUI3.4.1.zip - URL_HASH MD5=952d4033854612c99a5d9bf4b8550c26 + URL https://github.com/MyGUI/mygui/archive/59c1388b942721887d18743ada15f1906ff11a1f.zip + URL_HASH MD5=0a64c9cccc8f96dc8c08172175e68e1c SOURCE_DIR fetched/mygui ) FetchContent_MakeAvailableExcludeFromAll(mygui) From e30a59772c6d18f1953b0e4b6afc55be439e7c04 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 8 Jan 2021 22:18:37 +0100 Subject: [PATCH 078/132] Remove DEFAULT_DECODER macros --- apps/openmw/mwsound/ffmpeg_decoder.hpp | 6 ++---- apps/openmw/mwsound/soundmanagerimp.cpp | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 92d046d85..f099c831c 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -69,15 +69,13 @@ namespace MWSound FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs); FFmpeg_Decoder(const FFmpeg_Decoder &rhs); - FFmpeg_Decoder(const VFS::Manager* vfs); public: + explicit FFmpeg_Decoder(const VFS::Manager* vfs); + virtual ~FFmpeg_Decoder(); friend class SoundManager; }; -#ifndef DEFAULT_DECODER -#define DEFAULT_DECODER (::MWSound::FFmpeg_Decoder) -#endif } #endif diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 44465e5a7..740167b0a 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -117,7 +117,7 @@ namespace MWSound // Return a new decoder instance, used as needed by the output implementations DecoderPtr SoundManager::getDecoder() { - return DecoderPtr(new DEFAULT_DECODER (mVFS)); + return std::make_shared(mVFS); } DecoderPtr SoundManager::loadVoice(const std::string &voicefile) From b0311ce9f1ed2c562fac6f8b99127dfbf8a79cf8 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 26 Jan 2021 00:48:45 +0100 Subject: [PATCH 079/132] Remove DEFAULT_OUTPUT macros --- apps/openmw/mwsound/openal_output.hpp | 3 --- apps/openmw/mwsound/soundmanagerimp.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index d9ca924a7..2a19e6768 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -98,9 +98,6 @@ namespace MWSound OpenAL_Output(SoundManager &mgr); virtual ~OpenAL_Output(); }; -#ifndef DEFAULT_OUTPUT -#define DEFAULT_OUTPUT(x) ::MWSound::OpenAL_Output((x)) -#endif } #endif diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 740167b0a..6f029d0f6 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -54,7 +54,7 @@ namespace MWSound SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound) : mVFS(vfs) - , mOutput(new DEFAULT_OUTPUT(*this)) + , mOutput(new OpenAL_Output(*this)) , mWaterSoundUpdater(makeWaterSoundUpdaterSettings()) , mSoundBuffers(*vfs, *mOutput) , mListenerUnderwater(false) From 9275dd2dcb6499ee23aba7f6ec056552352034aa Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 24 Jan 2021 21:53:23 +0100 Subject: [PATCH 080/132] Avoid virtual dispatch in SoundManager dtor --- apps/openmw/mwsound/soundmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 6f029d0f6..fc9735d57 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -109,7 +109,7 @@ namespace MWSound SoundManager::~SoundManager() { - clear(); + SoundManager::clear(); mSoundBuffers.clear(); mOutput.reset(); } From 4d48c81998978f58e4386ea1e7c907ad95850694 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 13 Mar 2021 22:28:13 +0400 Subject: [PATCH 081/132] Add move assignment operator and move constructor for the ESM::Variant --- components/esm/variant.cpp | 21 +++++++++++++++++++++ components/esm/variant.hpp | 2 ++ 2 files changed, 23 insertions(+) diff --git a/components/esm/variant.cpp b/components/esm/variant.cpp index 6ff31dc44..cbcb6e002 100644 --- a/components/esm/variant.cpp +++ b/components/esm/variant.cpp @@ -62,10 +62,31 @@ ESM::Variant& ESM::Variant::operator= (const Variant& variant) return *this; } +ESM::Variant& ESM::Variant::operator= (Variant&& variant) +{ + if (&variant!=this) + { + delete mData; + + mType = variant.mType; + mData = variant.mData; + + variant.mData = nullptr; + } + + return *this; +} + ESM::Variant::Variant (const Variant& variant) : mType (variant.mType), mData (variant.mData ? variant.mData->clone() : nullptr) {} +ESM::Variant::Variant(Variant&& variant) +: mType (variant.mType), mData (variant.mData) +{ + variant.mData = nullptr; +} + ESM::VarType ESM::Variant::getType() const { return mType; diff --git a/components/esm/variant.hpp b/components/esm/variant.hpp index 5f179a7bd..20b3aa76e 100644 --- a/components/esm/variant.hpp +++ b/components/esm/variant.hpp @@ -46,8 +46,10 @@ namespace ESM ~Variant(); Variant& operator= (const Variant& variant); + Variant& operator= (Variant && variant); Variant (const Variant& variant); + Variant (Variant&& variant); VarType getType() const; From 447c3b94898178e04e4b73af5d79838c72cf7ddf Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Sat, 13 Mar 2021 21:51:48 +0100 Subject: [PATCH 082/132] Fix 5846 --- apps/openmw/mwmechanics/character.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 172ddb25c..99f8632f3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2920,8 +2920,8 @@ void CharacterController::updateHeadTracking(float duration) if (!head) return; - float zAngleRadians = 0.f; - float xAngleRadians = 0.f; + double zAngleRadians = 0.f; + double xAngleRadians = 0.f; if (!mHeadTrackTarget.isEmpty()) { @@ -2954,15 +2954,16 @@ void CharacterController::updateHeadTracking(float duration) const osg::Vec3f actorDirection = mPtr.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0); zAngleRadians = std::atan2(actorDirection.x(), actorDirection.y()) - std::atan2(direction.x(), direction.y()); + zAngleRadians = Misc::normalizeAngle(zAngleRadians - mAnimation->getHeadYaw()) + mAnimation->getHeadYaw(); + zAngleRadians *= (1 - direction.z() * direction.z()); xAngleRadians = std::asin(direction.z()); } const double xLimit = osg::DegreesToRadians(40.0); const double zLimit = osg::DegreesToRadians(30.0); double zLimitOffset = mAnimation->getUpperBodyYawRadians(); - xAngleRadians = osg::clampBetween(Misc::normalizeAngle(xAngleRadians), -xLimit, xLimit); - zAngleRadians = osg::clampBetween(Misc::normalizeAngle(zAngleRadians), - -zLimit + zLimitOffset, zLimit + zLimitOffset); + xAngleRadians = osg::clampBetween(xAngleRadians, -xLimit, xLimit); + zAngleRadians = osg::clampBetween(zAngleRadians, -zLimit + zLimitOffset, zLimit + zLimitOffset); float factor = duration*5; factor = std::min(factor, 1.f); From 34af58f53fcea6e5dc402c0c9da4286a81ed333b Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 13 Mar 2021 23:02:48 +0000 Subject: [PATCH 083/132] Use correct extension name. Like 0068c7bb252428571c1f6ae4ca57220873d0c701, but for other shaders, too --- files/shaders/groundcover_fragment.glsl | 2 +- files/shaders/shadowcasting_fragment.glsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/files/shaders/groundcover_fragment.glsl b/files/shaders/groundcover_fragment.glsl index bc84ded61..f25b7d487 100644 --- a/files/shaders/groundcover_fragment.glsl +++ b/files/shaders/groundcover_fragment.glsl @@ -1,7 +1,7 @@ #version 120 #if @useGPUShader4 - #extension EXT_gpu_shader4: require + #extension GL_EXT_gpu_shader4: require #endif #define GROUNDCOVER diff --git a/files/shaders/shadowcasting_fragment.glsl b/files/shaders/shadowcasting_fragment.glsl index a83e155ca..07fad047e 100644 --- a/files/shaders/shadowcasting_fragment.glsl +++ b/files/shaders/shadowcasting_fragment.glsl @@ -1,7 +1,7 @@ #version 120 #if @useGPUShader4 - #extension EXT_gpu_shader4: require + #extension GL_EXT_gpu_shader4: require #endif uniform sampler2D diffuseMap; From 6255b0492b7397f4075eac4fc3aee0feda0d29c1 Mon Sep 17 00:00:00 2001 From: "glassmancody.info" Date: Sat, 13 Mar 2021 15:43:46 -0800 Subject: [PATCH 084/132] Resolve crashes on exit with visible MyGUI widgets --- CHANGELOG.md | 1 + apps/openmw/mwgui/layout.cpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3563007cb..face13d19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,7 @@ Bug #5869: Guards can initiate arrest dialogue behind locked doors Bug #5871: The console appears if you type the Russian letter "Ё" in the name of the enchantment Bug #5877: Effects appearing with empty icon + Bug #5899: Visible modal windows and dropdowns crashing game on exit Feature #390: 3rd person look "over the shoulder" Feature #832: OpenMW-CS: Handle deleted references Feature #1536: Show more information about level on menu diff --git a/apps/openmw/mwgui/layout.cpp b/apps/openmw/mwgui/layout.cpp index ae1c09659..9b9b9537f 100644 --- a/apps/openmw/mwgui/layout.cpp +++ b/apps/openmw/mwgui/layout.cpp @@ -35,6 +35,7 @@ namespace MWGui void Layout::shutdown() { + setVisible(false); MyGUI::Gui::getInstance().destroyWidget(mMainWidget); mListWindowRoot.clear(); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e82ba8c1d..62b3d32d0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -503,8 +503,6 @@ namespace MWGui { mStatsWatcher.reset(); - mKeyboardNavigation.reset(); - MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); @@ -523,6 +521,8 @@ namespace MWGui delete mCursorManager; delete mToolTips; + mKeyboardNavigation.reset(); + cleanupGarbage(); mFontLoader.reset(); From 9466d6a409d3ca8318ae70462d8e01fbc79a3ba4 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 14 Mar 2021 03:42:23 +0000 Subject: [PATCH 085/132] Hide macro usage from Macs where it isn't defined --- components/debug/gldebug.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/debug/gldebug.cpp b/components/debug/gldebug.cpp index ee7dd608e..37829a8c1 100644 --- a/components/debug/gldebug.cpp +++ b/components/debug/gldebug.cpp @@ -183,7 +183,11 @@ namespace Debug } DebugGroup::DebugGroup(const std::string & message, GLuint id) + #ifdef GL_DEBUG_OUTPUT : mSource(GL_DEBUG_SOURCE_APPLICATION) + #else + : mSource(0x824A) + #endif , mId(id) , mMessage(message) { From 918fd174f33ce2024a238e94ccfd790e828cc39b Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 14 Mar 2021 12:39:55 +0000 Subject: [PATCH 086/132] Set `MYGUI_DONT_USE_OBSOLETE=OFF` Fixes https://gitlab.com/OpenMW/openmw/-/issues/5896 --- extern/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index bf18e4136..613de8fa8 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -55,7 +55,10 @@ if(NOT OPENMW_USE_SYSTEM_MYGUI) set(MYGUI_BUILD_DEMOS OFF CACHE BOOL "") set(MYGUI_BUILD_PLUGINS OFF CACHE BOOL "") set(MYGUI_BUILD_TOOLS OFF CACHE BOOL "") - set(MYGUI_DONT_USE_OBSOLETE ON CACHE BOOL "") + + # We appear to be using some obsolete properties in the XML. + # See https://gitlab.com/OpenMW/openmw/-/issues/5896 + set(MYGUI_DONT_USE_OBSOLETE OFF CACHE BOOL "") if(MYGUI_STATIC) set(BUILD_SHARED_LIBS OFF) From 19ad7d7f0cd77984ed4628ac539cce9a0c502039 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sat, 13 Mar 2021 15:24:45 +0100 Subject: [PATCH 087/132] Resolve #5895 by setting the initial mOnGround state to false; we do this because the movement solver runs one frame behind so when we run through the loop the first time we assume we are on the ground even though we may be 400 units in the air. --- apps/openmw/mwphysics/actor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 06abe7240..87698ef37 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -72,6 +72,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic updatePosition(); addCollisionMask(getCollisionMask()); updateCollisionObjectPosition(); + mOnGround.store(false, std::memory_order_release); } Actor::~Actor() From 1479f987934240075ba76ddf0836857c3d209edf Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 14 Mar 2021 01:02:44 +0100 Subject: [PATCH 088/132] hacky solution with debug; seems that player is added before cell so tracing down will not find anything --- apps/openmw/mwphysics/actor.cpp | 1 - apps/openmw/mwphysics/physicssystem.cpp | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 87698ef37..06abe7240 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -72,7 +72,6 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic updatePosition(); addCollisionMask(getCollisionMask()); updateCollisionObjectPosition(); - mOnGround.store(false, std::memory_order_release); } Actor::~Actor() diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index dc9ab629a..e24aa3500 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -680,7 +681,24 @@ namespace MWPhysics return; auto actor = std::make_shared(ptr, shape, mTaskScheduler.get()); + + if (ptr == MWMechanics::getPlayer()) + { + osg::Vec3f offset = actor->getCollisionObjectPosition() - ptr.getRefData().getPosition().asVec3(); + std::cout << "DEBUG Player position: " << actor->getPosition() << std::endl; + auto terrainHeight = std::max(0.f, MWBase::Environment::get().getWorld()->getTerrainHeightAt(actor->getPosition())); + std::cout << "DEBUG terrain height " << terrainHeight << std::endl; + ActorTracer tracer; + tracer.findGround(actor.get(), actor->getPosition() + offset, actor->getPosition() + offset - osg::Vec3f(0, 0, 100.f), mCollisionWorld.get()); + std::cout << "DEBUG mFraction " << tracer.mFraction << std::endl; // it seems that player is loaded before the cell so we can't find the ground. + + std::cout << "DEBUG calculation :: " << actor->getPosition().z() - terrainHeight << std::endl; + if (actor->getPosition().z() - terrainHeight > 300.f) + actor->setOnGround(false); + } + mActors.emplace(ptr, std::move(actor)); + } int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius, bool canTraverseWater) From c067782814d39f274c9f25451ebb7b9769dbac6d Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 14 Mar 2021 02:39:18 +0100 Subject: [PATCH 089/132] proper fix that traces down the player when a cell is loaded; we also only run once if the current cell being loaded is the one that the player is in. --- apps/openmw/mwphysics/physicssystem.cpp | 16 ---------------- apps/openmw/mwworld/scene.cpp | 5 ++++- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index e24aa3500..fcb85900b 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -682,21 +681,6 @@ namespace MWPhysics auto actor = std::make_shared(ptr, shape, mTaskScheduler.get()); - if (ptr == MWMechanics::getPlayer()) - { - osg::Vec3f offset = actor->getCollisionObjectPosition() - ptr.getRefData().getPosition().asVec3(); - std::cout << "DEBUG Player position: " << actor->getPosition() << std::endl; - auto terrainHeight = std::max(0.f, MWBase::Environment::get().getWorld()->getTerrainHeightAt(actor->getPosition())); - std::cout << "DEBUG terrain height " << terrainHeight << std::endl; - ActorTracer tracer; - tracer.findGround(actor.get(), actor->getPosition() + offset, actor->getPosition() + offset - osg::Vec3f(0, 0, 100.f), mCollisionWorld.get()); - std::cout << "DEBUG mFraction " << tracer.mFraction << std::endl; // it seems that player is loaded before the cell so we can't find the ground. - - std::cout << "DEBUG calculation :: " << actor->getPosition().z() - terrainHeight << std::endl; - if (actor->getPosition().z() - terrainHeight > 300.f) - actor->setOnGround(false); - } - mActors.emplace(ptr, std::move(actor)); } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 427ee3aa8..ac01c8893 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -447,11 +447,14 @@ namespace MWWorld mPhysics->disableWater(); const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if (player.getCell() == cell) { + mPhysics->traceDown(player, player.getRefData().getPosition().asVec3(), 10.f); + } + navigator->update(player.getRefData().getPosition().asVec3()); if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) { - mRendering.configureAmbient(cell->getCell()); } } From fff1df9ee46bce05cc1a68e163e137d6fdc19c88 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 14 Mar 2021 02:40:32 +0100 Subject: [PATCH 090/132] revert some blank lines --- apps/openmw/mwphysics/physicssystem.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index fcb85900b..dc9ab629a 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -680,9 +680,7 @@ namespace MWPhysics return; auto actor = std::make_shared(ptr, shape, mTaskScheduler.get()); - mActors.emplace(ptr, std::move(actor)); - } int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius, bool canTraverseWater) From 49545e6d299a706e6adcce6246b40441aa433c6a Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 14 Mar 2021 13:40:48 +0100 Subject: [PATCH 091/132] add comments as to why we need to check that the player is grounded or not; only run once during initial cell loading --- apps/openmw/mwworld/scene.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ac01c8893..31d83dce3 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -447,7 +447,9 @@ namespace MWWorld mPhysics->disableWater(); const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - if (player.getCell() == cell) { + + // By default the player is grounded, with the scene fully loaded, we validate and correct this. + if (player.getCell() == cell) { // Only run once, during initial cell load. mPhysics->traceDown(player, player.getRefData().getPosition().asVec3(), 10.f); } From 9fc0649fb6a03aec0f3b725373046cb8f325434b Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 14 Mar 2021 18:08:52 +0100 Subject: [PATCH 092/132] a better check to avoid the mCell assertion, so compariing nullptr to current cell will refurn false anyway --- apps/openmw/mwworld/scene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 31d83dce3..140d69e6b 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -449,7 +449,7 @@ namespace MWWorld const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // By default the player is grounded, with the scene fully loaded, we validate and correct this. - if (player.getCell() == cell) { // Only run once, during initial cell load. + if (player.mCell == cell) { // Only run once, during initial cell load. mPhysics->traceDown(player, player.getRefData().getPosition().asVec3(), 10.f); } From c2580d60e9cc398b4b7a8290e6830a7531c17a57 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 14 Mar 2021 19:32:03 +0100 Subject: [PATCH 093/132] Register copied Spells with SpellList --- apps/openmw/mwmechanics/spells.cpp | 8 ++++++++ apps/openmw/mwmechanics/spells.hpp | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index d292c015d..0af74e01b 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -24,6 +24,14 @@ namespace MWMechanics { } + Spells::Spells(const Spells& spells) : mSpellList(spells.mSpellList), mSpells(spells.mSpells), + mSelectedSpell(spells.mSelectedSpell), mUsedPowers(spells.mUsedPowers), + mSpellsChanged(spells.mSpellsChanged), mEffects(spells.mEffects), mSourcedEffects(spells.mSourcedEffects) + { + if(mSpellList) + mSpellList->addListener(this); + } + std::map::const_iterator Spells::begin() const { return mSpells.begin(); diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index 2f4049d2e..3df89a537 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -57,6 +57,10 @@ namespace MWMechanics Spells(); + Spells(const Spells&); + + Spells(const Spells&&) = delete; + ~Spells(); static bool hasCorprusEffect(const ESM::Spell *spell); From 40c989d732635633406de1b6d13d16156cb2052d Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 14 Mar 2021 22:11:18 +0100 Subject: [PATCH 094/132] allman style --- apps/openmw/mwworld/scene.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 140d69e6b..3f050ba36 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -449,7 +449,8 @@ namespace MWWorld const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // By default the player is grounded, with the scene fully loaded, we validate and correct this. - if (player.mCell == cell) { // Only run once, during initial cell load. + if (player.mCell == cell) // Only run once, during initial cell load. + { mPhysics->traceDown(player, player.getRefData().getPosition().asVec3(), 10.f); } From 952b31ac5c60c576dcbe3a17e44aaa21364dcdee Mon Sep 17 00:00:00 2001 From: Dobrohotov Alexei Date: Mon, 15 Mar 2021 00:43:47 +0300 Subject: [PATCH 095/132] NiZBufferProperty: handle depth test flag (bug #5902) --- CHANGELOG.md | 1 + components/nifosg/nifloader.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index face13d19..02dc9dc96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,7 @@ Bug #5871: The console appears if you type the Russian letter "Ё" in the name of the enchantment Bug #5877: Effects appearing with empty icon Bug #5899: Visible modal windows and dropdowns crashing game on exit + Bug #5902: NiZBufferProperty is unable to disable the depth test Feature #390: 3rd person look "over the shoulder" Feature #832: OpenMW-CS: Handle deleted references Feature #1536: Show more information about level on menu diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 902f15fb3..3e34969f4 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1725,11 +1725,16 @@ namespace NifOsg case Nif::RC_NiZBufferProperty: { const Nif::NiZBufferProperty* zprop = static_cast(property); - // VER_MW doesn't support a DepthFunction according to NifSkope + osg::StateSet* stateset = node->getOrCreateStateSet(); + // Depth test flag + stateset->setMode(GL_DEPTH_TEST, zprop->flags&1 ? osg::StateAttribute::ON + : osg::StateAttribute::OFF); osg::ref_ptr depth = new osg::Depth; + // Depth write flag depth->setWriteMask((zprop->flags>>1)&1); + // Morrowind ignores depth test function depth = shareAttribute(depth); - node->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); + stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); break; } // OSG groups the material properties that NIFs have separate, so we have to parse them all again when one changed From ba74fbf30ed8709f973c05511adc1b4234af71db Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Mon, 15 Mar 2021 22:35:13 +0000 Subject: [PATCH 096/132] Fix MyGUI log Also actually print an error to the regular log when the MyGUI log can't be opened so we notice if we kill it again in under five years. --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- components/myguiplatform/myguiloglistener.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 62b3d32d0..8e4ea4e54 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -189,7 +189,7 @@ namespace MWGui { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale); - mGuiPlatform->initialise(resourcePath, logpath); + mGuiPlatform->initialise(resourcePath, (boost::filesystem::path(logpath) / "MyGUI.log").generic_string()); mGui = new MyGUI::Gui; mGui->initialise(""); diff --git a/components/myguiplatform/myguiloglistener.cpp b/components/myguiplatform/myguiloglistener.cpp index a22ac8fc5..74b4b3081 100644 --- a/components/myguiplatform/myguiloglistener.cpp +++ b/components/myguiplatform/myguiloglistener.cpp @@ -2,11 +2,15 @@ #include +#include + namespace osgMyGUI { void CustomLogListener::open() { mStream.open(boost::filesystem::path(mFileName), std::ios_base::out); + if (!mStream.is_open()) + Log(Debug::Error) << "Unable to create MyGUI log with path " << mFileName; } void CustomLogListener::close() From f460ab215298d611e7d9438df6b01175f58eb072 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Tue, 16 Mar 2021 01:34:27 +0000 Subject: [PATCH 097/132] MSVC: Fix build with vcpkg's boost boost-zlib is not present (nor needed) in vcpkg version of boost There, it is part of boost-iostreams instead. This was previously reported in: https://gitlab.com/OpenMW/openmw/-/merge_requests/213#note_348625016 --- CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa1194205..ad35d1b0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -344,7 +344,9 @@ set(BOOST_COMPONENTS system filesystem program_options iostreams) if(WIN32) set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) if(MSVC) - set(BOOST_COMPONENTS ${BOOST_COMPONENTS} zlib) + # boost-zlib is not present (nor needed) in vcpkg version of boost. + # there, it is part of boost-iostreams instead. + set(BOOST_OPTIONAL_COMPONENTS zlib) endif(MSVC) endif(WIN32) @@ -354,7 +356,7 @@ endif() set(Boost_NO_BOOST_CMAKE ON) -find_package(Boost 1.6.2 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) +find_package(Boost 1.6.2 REQUIRED COMPONENTS ${BOOST_COMPONENTS} OPTIONAL_COMPONENTS ${BOOST_OPTIONAL_COMPONENTS}) if(OPENMW_USE_SYSTEM_MYGUI) find_package(MyGUI 3.2.2 REQUIRED) endif() From c68e047f19b4f9f5973af5f9b1c3db80b932eead Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 16 Mar 2021 13:21:35 +0400 Subject: [PATCH 098/132] Remove invalid MyGUI properties from layout files --- files/mygui/openmw_settings_window.layout | 1 - files/mygui/openmw_tooltips.layout | 5 ----- 2 files changed, 6 deletions(-) diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index b57d362ed..babb5c28f 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -302,7 +302,6 @@ - diff --git a/files/mygui/openmw_tooltips.layout b/files/mygui/openmw_tooltips.layout index bb505d253..ce927038c 100644 --- a/files/mygui/openmw_tooltips.layout +++ b/files/mygui/openmw_tooltips.layout @@ -193,7 +193,6 @@ - @@ -248,7 +247,6 @@ - @@ -269,9 +267,6 @@ - - - From 493659d4f93f8490be66918fdbc8154d96902810 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Tue, 16 Mar 2021 02:56:33 +0000 Subject: [PATCH 099/132] MSVC: extern/ tweaks to make it build Not everything is supported but it does build with the following CMakeSettings.json variables and dependencies from vcpkg: "variables": [ { "name": "OPENMW_USE_SYSTEM_BULLET", "value": "False", "type": "BOOL" }, { "name": "OPENMW_USE_SYSTEM_MYGUI", "value": "False", "type": "BOOL" }, { "name": "OPENMW_USE_SYSTEM_OSG", "value": "False", "type": "BOOL" }, { "name": "BULLET_STATIC", "value": "True", "type": "BOOL" }, { "name": "OSG_STATIC", "value": "False", "type": "BOOL" }, { "name": "MYGUI_STATIC", "value": "False", "type": "BOOL" } ], What works: it builds What does not work: Not all DLLs are copied into the output directory with this set up (SDL2, MyGUI, Bullet, OSG, are not copied). --- CMakeLists.txt | 6 ++++++ extern/CMakeLists.txt | 43 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ad35d1b0e..08bdbce8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,6 +115,12 @@ option(SDL2_STATIC "Link static build of SDL into the binaries" FALSE) option(QT_STATIC "Link static build of QT into the binaries" FALSE) option(OPENMW_USE_SYSTEM_BULLET "Use system provided bullet physics library" ON) +if(OPENMW_USE_SYSTEM_BULLET) + set(_bullet_static_default OFF) +else() + set(_bullet_static_default ON) +endif() +option(BULLET_STATIC "Link static build of Bullet into the binaries" ${_bullet_static_default}) option(OPENMW_USE_SYSTEM_OSG "Use system provided OpenSceneGraph libraries" ON) if(OPENMW_USE_SYSTEM_OSG) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 613de8fa8..8766e51cc 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -31,6 +31,15 @@ if(NOT OPENMW_USE_SYSTEM_BULLET) set(USE_DOUBLE_PRECISION ${BULLET_USE_DOUBLES} CACHE BOOL "") set(BULLET2_MULTITHREADING ON CACHE BOOL "") + if(BULLET_STATIC) + set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) + else() + set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE) + if(MSVC) + set(USE_MSVC_RUNTIME_LIBRARY_DLL ON CACHE BOOL "" FORCE) + endif() + endif() + # master on 12 Mar 2021 include(FetchContent) FetchContent_Declare(bullet @@ -61,9 +70,9 @@ if(NOT OPENMW_USE_SYSTEM_MYGUI) set(MYGUI_DONT_USE_OBSOLETE OFF CACHE BOOL "") if(MYGUI_STATIC) - set(BUILD_SHARED_LIBS OFF) + set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) else() - set(BUILD_SHARED_LIBS ON) + set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE) endif() include(FetchContent) @@ -81,8 +90,6 @@ endif() if(NOT OPENMW_USE_SYSTEM_OSG) cmake_minimum_required(VERSION 3.11) # for FetchContent - set(DYNAMIC_OPENTHREADS OFF CACHE BOOL "") - set(DYNAMIC_OPENSCENEGRAPH OFF CACHE BOOL "") set(BUILD_OSG_APPLICATIONS OFF CACHE BOOL "") set(BUILD_OSG_DEPRECATED_SERIALIZERS OFF CACHE BOOL "") set(OSG_FIND_3RD_PARTY_DEPS OFF CACHE BOOL "") @@ -104,9 +111,33 @@ if(NOT OPENMW_USE_SYSTEM_OSG) set(OPENGL_PROFILE "GL2" CACHE STRING "") if(OSG_STATIC) - set(BUILD_SHARED_LIBS OFF) + set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) + set(DYNAMIC_OPENTHREADS OFF CACHE BOOL "" FORCE) + set(DYNAMIC_OPENSCENEGRAPH OFF CACHE BOOL "" FORCE) else() - set(BUILD_SHARED_LIBS ON) + set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE) + set(DYNAMIC_OPENTHREADS ON CACHE BOOL "" FORCE) + set(DYNAMIC_OPENSCENEGRAPH ON CACHE BOOL "" FORCE) + endif() + mark_as_advanced(DYNAMIC_OPENTHREADS DYNAMIC_OPENSCENEGRAPH) + + if(WIN32) + # OSG here inherits C++17 language level because it doesn't specify its own. + # + # OSG's `using namespace std` interferes with Windows header files. + # + # See https://developercommunity.visualstudio.com/t/error-c2872-byte-ambiguous-symbol/93889 + # + # An alternative way to work around this without changing the language level is: + # + # add_compile_definitions(_HAS_STD_BYTE=0) + # + # TODO: Put OSG into its own scope so that this does not leak into Recast below. + set(CMAKE_CXX_STANDARD 11) + + if(MSVC) + set(OSG_MSVC_VERSIONED_DLL OFF CACHE BOOL "") + endif() endif() # branch OpenSceneGraph-3.6 on 23 Jan 2021. From b9c2f6ea1a1a07015bdee3df57d47b02af09a53f Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Tue, 16 Mar 2021 19:55:15 +0000 Subject: [PATCH 100/132] Minor cleanup: Remove `using namespace std` I came across these while trying to figure why MSVC build triggers https://developercommunity.visualstudio.com/t/error-c2872-byte-ambiguous-symbol/93889 In the end, the issue was not in openmw but in OSG, but it's good to clean up here anyway. --- components/bsa/bsa_file.cpp | 7 +++--- components/esm/esmreader.cpp | 6 ++--- components/to_utf8/gen_iconv.cpp | 41 ++++++++++++++++---------------- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 3fd74dd83..f6220b7ce 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -28,12 +28,11 @@ #include #include -using namespace std; using namespace Bsa; /// Error handling -void BSAFile::fail(const string &msg) +void BSAFile::fail(const std::string &msg) { throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + mFilename); } @@ -160,7 +159,7 @@ int BSAFile::getIndex(const char *str) const } /// Open an archive file. -void BSAFile::open(const string &file) +void BSAFile::open(const std::string &file) { mFilename = file; readHeader(); @@ -171,7 +170,7 @@ Files::IStreamPtr BSAFile::getFile(const char *file) assert(file); int i = getIndex(file); if(i == -1) - fail("File not found: " + string(file)); + fail("File not found: " + std::string(file)); const FileStruct &fs = mFiles[i]; diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 1b6eca734..4e7dce876 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -357,16 +357,14 @@ std::string ESMReader::getString(int size) void ESMReader::fail(const std::string &msg) { - using namespace std; - - stringstream ss; + std::stringstream ss; ss << "ESM Error: " << msg; ss << "\n File: " << mCtx.filename; ss << "\n Record: " << mCtx.recName.toString(); ss << "\n Subrecord: " << mCtx.subName.toString(); if (mEsm.get()) - ss << "\n Offset: 0x" << hex << mEsm->tellg(); + ss << "\n Offset: 0x" << std::hex << mEsm->tellg(); throw std::runtime_error(ss.str()); } diff --git a/components/to_utf8/gen_iconv.cpp b/components/to_utf8/gen_iconv.cpp index 8198b305d..f2d9a42f1 100644 --- a/components/to_utf8/gen_iconv.cpp +++ b/components/to_utf8/gen_iconv.cpp @@ -1,20 +1,19 @@ // This program generates the file tables_gen.hpp #include -using namespace std; #include #include -void tab() { cout << " "; } +void tab() { std::cout << " "; } // write one number with a space in front of it and a comma after it void num(char i, bool last) { // Convert i to its integer value, i.e. -128 to 127. Printing it directly // would result in non-printable characters in the source code, which is bad. - cout << " " << static_cast(i); - if(!last) cout << ","; + std::cout << " " << static_cast(i); + if(!last) std::cout << ","; } // Write one table entry (UTF8 value), 1-5 bytes @@ -27,9 +26,9 @@ void writeChar(char *value, int length, bool last, const std::string &comment="" num(value[i], last && i==4); if(comment != "") - cout << " // " << comment; + std::cout << " // " << comment; - cout << endl; + std::cout << std::endl; } // What to write on missing characters @@ -46,7 +45,7 @@ void writeMissing(bool last) int write_table(const std::string &charset, const std::string &tableName) { // Write table header - cout << "static signed char " << tableName << "[] =\n{\n"; + std::cout << "static signed char " << tableName << "[] =\n{\n"; // Open conversion system iconv_t cd = iconv_open ("UTF-8", charset.c_str()); @@ -74,7 +73,7 @@ int write_table(const std::string &charset, const std::string &tableName) iconv_close (cd); // Finish table - cout << "};\n"; + std::cout << "};\n"; return 0; } @@ -82,37 +81,37 @@ int write_table(const std::string &charset, const std::string &tableName) int main() { // Write header guard - cout << "#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H\n#define COMPONENTS_TOUTF8_TABLE_GEN_H\n\n"; + std::cout << "#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H\n#define COMPONENTS_TOUTF8_TABLE_GEN_H\n\n"; // Write namespace - cout << "namespace ToUTF8\n{\n\n"; + std::cout << "namespace ToUTF8\n{\n\n"; // Central European and Eastern European languages that use Latin script, such as // Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian. - cout << "\n/// Central European and Eastern European languages that use Latin script," - "\n/// such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian," - "\n/// Serbian (Latin script), Romanian and Albanian." - "\n"; + std::cout << "\n/// Central European and Eastern European languages that use Latin script," + "\n/// such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian," + "\n/// Serbian (Latin script), Romanian and Albanian." + "\n"; write_table("WINDOWS-1250", "windows_1250"); // Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages - cout << "\n/// Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic" - "\n/// and other languages" - "\n"; + std::cout << "\n/// Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic" + "\n/// and other languages" + "\n"; write_table("WINDOWS-1251", "windows_1251"); // English - cout << "\n/// Latin alphabet used by English and some other Western languages" - "\n"; + std::cout << "\n/// Latin alphabet used by English and some other Western languages" + "\n"; write_table("WINDOWS-1252", "windows_1252"); write_table("CP437", "cp437"); // Close namespace - cout << "\n}\n\n"; + std::cout << "\n}\n\n"; // Close header guard - cout << "#endif\n\n"; + std::cout << "#endif\n\n"; return 0; } From 162b25c1808b61103be37f406e77e637e90ab15d Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 17 Mar 2021 01:46:04 +0000 Subject: [PATCH 101/132] Fix sunglare on Mesa The sunglare works by comparing an occlusion query with depth testing on against one with depth testing off to determine if there's anything closer to the camera than the maximum depth buffer value. For the depth- tested query, the depth range is set from 1 to 1 so it's always drawn at the maximum distance. Originally, we had the depth function set to LESS, meaning that the query would always fail as 1 is not less than 1, but also glPolygonOffset was used to move the query by "the smallest value that is guaranteed to produce a resolvable offset for a given implementation" closer to the camera. While other driver and hardware combinations do that, Mesa seems to be clamping to the depth range, so still failing. Instead, it's simpler to just get rid of the polygon offset and change the depth test to LEQUAL as 1 *is* less than or equal to 1, but not than any other possible depth buffer value. --- apps/openmw/mwrender/sky.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index f82641676..5a6ec06e5 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -497,8 +497,6 @@ public: // Disable writing to the color buffer. We are using this geometry for visibility tests only. osg::ref_ptr colormask (new osg::ColorMask(0, 0, 0, 0)); stateset->setAttributeAndModes(colormask, osg::StateAttribute::ON); - osg::ref_ptr po (new osg::PolygonOffset( -1., -1. )); - stateset->setAttributeAndModes(po, osg::StateAttribute::ON); mTransform->addChild(queryNode); @@ -595,7 +593,7 @@ private: if (queryVisible) { osg::ref_ptr depth (new osg::Depth); - depth->setFunction(osg::Depth::LESS); + depth->setFunction(osg::Depth::LEQUAL); // This is a trick to make fragments written by the query always use the maximum depth value, // without having to retrieve the current far clipping distance. // We want the sun glare to be "infinitely" far away. From 3ad1040271094aecb07e8447a3f107fa8bc0db72 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 17 Mar 2021 02:00:21 +0000 Subject: [PATCH 102/132] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02dc9dc96..0891ecba9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,6 +110,7 @@ Bug #5877: Effects appearing with empty icon Bug #5899: Visible modal windows and dropdowns crashing game on exit Bug #5902: NiZBufferProperty is unable to disable the depth test + Bug #5906: Sunglare doesn't work with Mesa drivers and AMD GPUs Feature #390: 3rd person look "over the shoulder" Feature #832: OpenMW-CS: Handle deleted references Feature #1536: Show more information about level on menu From 1c296a1a78c4147835273aa6a4f7c08df8ef61cb Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 17 Mar 2021 18:11:36 +0000 Subject: [PATCH 103/132] Deploy Qt style DLL --- CI/before_script.msvc.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 0c26801cc..4eda627ac 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -325,6 +325,16 @@ add_qt_platform_dlls() { QT_PLATFORMS[$CONFIG]="${QT_PLATFORMS[$CONFIG]} $@" } +declare -A QT_STYLES +QT_STYLES["Release"]="" +QT_STYLES["Debug"]="" +QT_STYLES["RelWithDebInfo"]="" +add_qt_style_dlls() { + local CONFIG=$1 + shift + QT_STYLES[$CONFIG]="${QT_STYLES[$CONFIG]} $@" +} + if [ -z $PLATFORM ]; then PLATFORM="$(uname -m)" fi @@ -868,6 +878,7 @@ fi fi add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll add_qt_platform_dlls $CONFIGURATION "$(pwd)/plugins/platforms/qwindows${DLLSUFFIX}.dll" + add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll" done echo Done. else @@ -883,6 +894,7 @@ fi DIR=$(windowsPathAsUnix "${QT_SDK}") add_runtime_dlls $CONFIGURATION "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll add_qt_platform_dlls $CONFIGURATION "${DIR}/plugins/platforms/qwindows${DLLSUFFIX}.dll" + add_qt_style_dlls $CONFIGURATION "${DIR}/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll" done echo Done. fi @@ -1060,6 +1072,13 @@ fi cp "$DLL" "${DLL_PREFIX}platforms" done echo + echo "- Qt Style DLLs..." + mkdir -p ${DLL_PREFIX}styles + for DLL in ${QT_STYLES[$CONFIGURATION]}; do + echo " $(basename $DLL)" + cp "$DLL" "${DLL_PREFIX}styles" + done + echo done #fi From b38a81760069f9218411fb7657ce981a24356716 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 17 Mar 2021 23:29:48 +0000 Subject: [PATCH 104/132] Ensure vswhere finds us a single suitable MSVC installation Also document the numerous arguments to achieve this. --- CI/before_script.msvc.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 0c26801cc..71a6b79c4 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -1067,7 +1067,13 @@ if [ -n "$ACTIVATE_MSVC" ]; then echo -n "- Activating MSVC in the current shell... " command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; } - MSVC_INSTALLATION_PATH=$(vswhere -products '*' -version "[$MSVC_REAL_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) + # There are so many arguments now that I'm going to document them: + # * products: allow Visual Studio or standalone build tools + # * version: obvious. Awk helps make a version range by adding one. + # * property installationPath: only give the installation path. + # * latest: return only one result if several candidates exist. Prefer the last installed/updated + # * requires: make sure it's got the MSVC compiler instead of, for example, just the .NET compiler. The .x86.x64 suffix means it's for either, not that it's the x64 on x86 cross compiler as you always get both + MSVC_INSTALLATION_PATH=$(vswhere -products '*' -version "[$MSVC_REAL_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64) if [ -z "$MSVC_INSTALLATION_PATH" ]; then echo "vswhere was unable to find MSVC $MSVC_DISPLAY_YEAR" wrappedExit 1 From a22f6b24d539c0ae773dbc9f1966eb122d79194b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 18 Mar 2021 11:47:06 +0400 Subject: [PATCH 105/132] Init animation key struct before usage --- components/nif/nifkey.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nif/nifkey.hpp b/components/nif/nifkey.hpp index de2fa31c8..91869ff84 100644 --- a/components/nif/nifkey.hpp +++ b/components/nif/nifkey.hpp @@ -77,7 +77,7 @@ struct KeyMapT { mInterpolationType = nif->getUInt(); - KeyT key; + KeyType key = {}; NIFStream &nifReference = *nif; if (mInterpolationType == InterpolationType_Linear From 1471ef003a6110c21fb1f76e057aea11c4664821 Mon Sep 17 00:00:00 2001 From: wareya Date: Thu, 18 Mar 2021 13:53:00 -0400 Subject: [PATCH 106/132] fix async physics interpolation --- apps/openmw/mwphysics/mtphysics.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index 2b0db5b82..1fa6251f8 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -186,9 +186,11 @@ namespace MWPhysics mPostStepBarrier = std::make_unique(mNumThreads, [&]() { if (mRemainingSteps) + { --mRemainingSteps; + updateActorsPositions(); + } mNextJob.store(0, std::memory_order_release); - updateActorsPositions(); }); mPostSimBarrier = std::make_unique(mNumThreads, [&]() From 54daa234bd9124cd187eae6576b044eb849f377d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 19 Mar 2021 11:56:14 +0400 Subject: [PATCH 107/132] Reset watched stats upon reload or new game start --- apps/openmw/mwgui/statswatcher.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apps/openmw/mwgui/statswatcher.hpp b/apps/openmw/mwgui/statswatcher.hpp index 41ab4fd25..353779d87 100644 --- a/apps/openmw/mwgui/statswatcher.hpp +++ b/apps/openmw/mwgui/statswatcher.hpp @@ -63,6 +63,8 @@ namespace MWGui void watchActor(const MWWorld::Ptr& ptr); MWWorld::Ptr getWatchedActor() const { return mWatched; } + + void forceUpdate() { mWatchedStatsEmpty = true; } }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8e4ea4e54..54c09e00f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -495,6 +495,8 @@ namespace MWGui } else allow(GW_ALL); + + mStatsWatcher->forceUpdate(); } WindowManager::~WindowManager() From 845e3944d6d61488f024d73923dae975a3d12387 Mon Sep 17 00:00:00 2001 From: wareya Date: Fri, 19 Mar 2021 17:54:08 -0400 Subject: [PATCH 108/132] make refraction more visible even at a distance --- files/shaders/water_fragment.glsl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index d9b9463ad..5f6d8c6ac 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -236,7 +236,11 @@ void main(void) if (cameraPos.z < 0.0) refraction = clamp(refraction * 1.5, 0.0, 1.0); else - refraction = mix(refraction, waterColor, clamp(depthSampleDistorted/VISIBILITY, 0.0, 1.0)); + { + vec3 refractionA = mix(refraction, waterColor, clamp(depthSampleDistorted/VISIBILITY, 0.0, 1.0)); + vec3 refractionB = mix(refraction, waterColor, clamp(realWaterDepth/VISIBILITY, 0.0, 1.0)); + refraction = mix(refractionA, refractionB, 0.8); + } // sunlight scattering // normal for sunlight scattering From bf336e4cb45575b67903e611675d0b356d79d8cd Mon Sep 17 00:00:00 2001 From: wareya Date: Fri, 19 Mar 2021 18:51:52 -0400 Subject: [PATCH 109/132] make sun scattering color stop being an ugly radioactive green --- files/shaders/water_fragment.glsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index 5f6d8c6ac..b7c748751 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -29,9 +29,6 @@ const float BUMP_RAIN = 2.5; const float REFL_BUMP = 0.10; // reflection distortion amount const float REFR_BUMP = 0.07; // refraction distortion amount -const float SCATTER_AMOUNT = 0.3; // amount of sunlight scattering -const vec3 SCATTER_COLOUR = vec3(0.0,1.0,0.95); // colour of sunlight scattering - const vec3 SUN_EXT = vec3(0.45, 0.55, 0.68); //sunlight extinction const float SPEC_HARDNESS = 256.0; // specular highlights hardness @@ -43,6 +40,9 @@ const float WIND_SPEED = 0.2f; const vec3 WATER_COLOR = vec3(0.090195, 0.115685, 0.12745); +const float SCATTER_AMOUNT = 0.5; // amount of sunlight scattering +const vec3 SCATTER_COLOUR = WATER_COLOR * 8.0; // colour of sunlight scattering + // ---------------- rain ripples related stuff --------------------- const float RAIN_RIPPLE_GAPS = 5.0; From 1a4e9df707a4efe2cf092a252ccb66781906e466 Mon Sep 17 00:00:00 2001 From: wareya Date: Fri, 19 Mar 2021 20:12:40 -0400 Subject: [PATCH 110/132] add bit to suppress coastline artifacts at more of a distance --- files/shaders/water_fragment.glsl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index b7c748751..db208ea42 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -34,6 +34,7 @@ const vec3 SUN_EXT = vec3(0.45, 0.55, 0.68); //sunlight extinction const float SPEC_HARDNESS = 256.0; // specular highlights hardness const float BUMP_SUPPRESS_DEPTH = 300.0; // at what water depth bumpmap will be suppressed for reflections and refractions (prevents artifacts at shores) +const float BUMP_SUPPRESS_DEPTH_SS = 1000.0; // modifier using screenspace depth (helps prevent same artifacts but at higher distances) const vec2 WIND_DIR = vec2(0.5f, -0.8f); const float WIND_SPEED = 0.2f; @@ -218,7 +219,10 @@ void main(void) float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-screenCoordsOffset).x) * radialise; float surfaceDepth = linearizeDepth(gl_FragCoord.z) * radialise; float realWaterDepth = depthSample - surfaceDepth; // undistorted water depth in view direction, independent of frustum - screenCoordsOffset *= clamp(realWaterDepth / BUMP_SUPPRESS_DEPTH,0,1); + screenCoordsOffset *= clamp( + realWaterDepth / (BUMP_SUPPRESS_DEPTH + * max(1, depthSample / BUMP_SUPPRESS_DEPTH_SS)) // suppress more at distance + ,0 ,1); #endif // reflection vec3 reflection = texture2D(reflectionMap, screenCoords + screenCoordsOffset).rgb; From 675c0ab72fd365c3699a68081636db7d64dd175e Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 19 Mar 2021 23:23:26 +0100 Subject: [PATCH 111/132] Apply uniform random deviation to AI reaction timer This allows to distribute AI reaction calls over time. Before this change actors appearing at the same frame will react in the same frame over and over because AI reaction period is constant. It creates a non-uniform CPU usage over frames. If a single frame has too many AI reactions it may cause stuttering when there are too many actors on a scene for current system. Another concern is a synchronization of actions between creatures and NPC. They start to go or hit at the same frame that is unnatural. --- apps/openmw/mwmechanics/aicombat.cpp | 15 ++-------- apps/openmw/mwmechanics/aicombat.hpp | 4 +-- apps/openmw/mwmechanics/aipackage.cpp | 9 ++---- apps/openmw/mwmechanics/aipackage.hpp | 5 ++-- apps/openmw/mwmechanics/aitimer.hpp | 26 +++++++++++++++++ apps/openmw/mwmechanics/aiwander.cpp | 11 ++----- apps/openmw/mwmechanics/aiwander.hpp | 4 +-- components/misc/rng.cpp | 5 ++++ components/misc/rng.hpp | 2 ++ components/misc/timer.hpp | 42 +++++++++++++++++++++++++++ 10 files changed, 90 insertions(+), 33 deletions(-) create mode 100644 apps/openmw/mwmechanics/aitimer.hpp create mode 100644 components/misc/timer.hpp diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 58a908672..51fcb92c1 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -146,19 +146,10 @@ namespace MWMechanics } storage.mActionCooldown -= duration; - float& timerReact = storage.mTimerReact; - if (timerReact < AI_REACTION_TIME) - { - timerReact += duration; - } - else - { - timerReact = 0; - if (attack(actor, target, storage, characterController)) - return true; - } + if (storage.mReaction.update(duration) == Misc::TimerStatus::Waiting) + return false; - return false; + return attack(actor, target, storage, characterController); } bool AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController) diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 3a77aa8e8..0f42c6e2d 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -10,6 +10,7 @@ #include "pathfinding.hpp" #include "movement.hpp" #include "obstacle.hpp" +#include "aitimer.hpp" namespace ESM { @@ -27,7 +28,7 @@ namespace MWMechanics struct AiCombatStorage : AiTemporaryBase { float mAttackCooldown; - float mTimerReact; + AiReactionTimer mReaction; float mTimerCombatMove; bool mReadyToAttack; bool mAttack; @@ -60,7 +61,6 @@ namespace MWMechanics AiCombatStorage(): mAttackCooldown(0.0f), - mTimerReact(AI_REACTION_TIME), mTimerCombatMove(0.0f), mReadyToAttack(false), mAttack(false), diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 99132b711..00e1c9c3a 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -28,7 +28,6 @@ MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) : mTypeId(typeId), mOptions(options), - mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild mTargetActorRefId(""), mTargetActorId(-1), mRotateOnTheRunChecks(0), @@ -64,7 +63,7 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const void MWMechanics::AiPackage::reset() { // reset all members - mTimer = AI_REACTION_TIME + 1.0f; + mReaction.reset(); mIsShortcutting = false; mShortcutProhibited = false; mShortcutFailPos = osg::Vec3f(); @@ -75,7 +74,7 @@ void MWMechanics::AiPackage::reset() bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& dest, float duration, float destTolerance) { - mTimer += duration; //Update timer + const Misc::TimerStatus timerStatus = mReaction.update(duration); const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); //position of the actor MWBase::World* world = MWBase::Environment::get().getWorld(); @@ -98,7 +97,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& const bool isDestReached = (distToTarget <= destTolerance); const bool actorCanMoveByZ = canActorMoveByZAxis(actor); - if (!isDestReached && mTimer > AI_REACTION_TIME) + if (!isDestReached && timerStatus == Misc::TimerStatus::Elapsed) { if (actor.getClass().isBipedal(actor)) openDoors(actor); @@ -142,8 +141,6 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go } } - - mTimer = 0; } const float actorTolerance = 2 * actor.getClass().getMaxSpeed(actor) * duration diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 4201de5c8..81b09c8b9 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -10,6 +10,7 @@ #include "obstacle.hpp" #include "aistate.hpp" #include "aipackagetypeid.hpp" +#include "aitimer.hpp" namespace MWWorld { @@ -28,8 +29,6 @@ namespace ESM namespace MWMechanics { - const float AI_REACTION_TIME = 0.25f; - class CharacterController; class PathgridGraph; @@ -158,7 +157,7 @@ namespace MWMechanics PathFinder mPathFinder; ObstacleCheck mObstacleCheck; - float mTimer; + AiReactionTimer mReaction; std::string mTargetActorRefId; mutable int mTargetActorId; diff --git a/apps/openmw/mwmechanics/aitimer.hpp b/apps/openmw/mwmechanics/aitimer.hpp new file mode 100644 index 000000000..804cda1bd --- /dev/null +++ b/apps/openmw/mwmechanics/aitimer.hpp @@ -0,0 +1,26 @@ +#ifndef OPENMW_MECHANICS_AITIMER_H +#define OPENMW_MECHANICS_AITIMER_H + +#include +#include + +namespace MWMechanics +{ + constexpr float AI_REACTION_TIME = 0.25f; + + class AiReactionTimer + { + public: + static constexpr float sDeviation = 0.1f; + + Misc::TimerStatus update(float duration) { return mImpl.update(duration); } + + void reset() { mImpl.reset(Misc::Rng::deviate(0, sDeviation)); } + + private: + Misc::DeviatingPeriodicTimer mImpl {AI_REACTION_TIME, sDeviation, + Misc::Rng::deviate(0, sDeviation)}; + }; +} + +#endif diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 375209a25..72b8757bf 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -223,15 +223,10 @@ namespace MWMechanics doPerFrameActionsForState(actor, duration, storage); - float& lastReaction = storage.mReaction; - lastReaction += duration; - if (AI_REACTION_TIME <= lastReaction) - { - lastReaction = 0; - return reactionTimeActions(actor, storage, pos); - } - else + if (storage.mReaction.update(duration) == Misc::TimerStatus::Waiting) return false; + + return reactionTimeActions(actor, storage, pos); } bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 8e718061e..52a926d14 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -10,6 +10,7 @@ #include "pathfinding.hpp" #include "obstacle.hpp" #include "aistate.hpp" +#include "aitimer.hpp" namespace ESM { @@ -25,7 +26,7 @@ namespace MWMechanics /// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive. struct AiWanderStorage : AiTemporaryBase { - float mReaction; // update some actions infrequently + AiReactionTimer mReaction; // AiWander states enum WanderState @@ -57,7 +58,6 @@ namespace MWMechanics int mStuckCount; AiWanderStorage(): - mReaction(0), mState(Wander_ChooseAction), mIsWanderingManually(false), mCanWanderAlongPathGrid(true), diff --git a/components/misc/rng.cpp b/components/misc/rng.cpp index 23d820448..4805f0d91 100644 --- a/components/misc/rng.cpp +++ b/components/misc/rng.cpp @@ -47,4 +47,9 @@ namespace Misc { return static_cast(std::chrono::high_resolution_clock::now().time_since_epoch().count()); } + + float Rng::deviate(float mean, float deviation, Seed& seed) + { + return std::uniform_real_distribution(mean - deviation, mean + deviation)(seed.mGenerator); + } } diff --git a/components/misc/rng.hpp b/components/misc/rng.hpp index 8efca438d..998ac0d53 100644 --- a/components/misc/rng.hpp +++ b/components/misc/rng.hpp @@ -42,6 +42,8 @@ public: /// returns default seed for RNG static unsigned int generateDefaultSeed(); + + static float deviate(float mean, float deviation, Seed& seed = getSeed()); }; } diff --git a/components/misc/timer.hpp b/components/misc/timer.hpp new file mode 100644 index 000000000..81a2ca073 --- /dev/null +++ b/components/misc/timer.hpp @@ -0,0 +1,42 @@ +#ifndef OPENMW_COMPONENTS_MISC_TIMER_H +#define OPENMW_COMPONENTS_MISC_TIMER_H + +#include "rng.hpp" + +namespace Misc +{ + enum class TimerStatus + { + Waiting, + Elapsed, + }; + + class DeviatingPeriodicTimer + { + public: + explicit DeviatingPeriodicTimer(float period, float deviation, float timeLeft) + : mPeriod(period), mDeviation(deviation), mTimeLeft(timeLeft) + {} + + TimerStatus update(float duration) + { + if (mTimeLeft > 0) + { + mTimeLeft -= duration; + return TimerStatus::Waiting; + } + + mTimeLeft = Rng::deviate(mPeriod, mDeviation); + return TimerStatus::Elapsed; + } + + void reset(float timeLeft) { mTimeLeft = timeLeft; } + + private: + const float mPeriod; + const float mDeviation; + float mTimeLeft; + }; +} + +#endif From 62c0ecbbd0556b16db5a01798f41b4e94921565e Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 20 Mar 2021 01:50:51 +0100 Subject: [PATCH 112/132] Separate engage combat timer for each actor Use DeviatingPeriodicTimer to distribute calls over time. This reduces stuttering and make AI more natural. --- apps/openmw/mwmechanics/actor.hpp | 8 ++++++++ apps/openmw/mwmechanics/actors.cpp | 8 +++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/actor.hpp b/apps/openmw/mwmechanics/actor.hpp index 287ca420f..be4f42537 100644 --- a/apps/openmw/mwmechanics/actor.hpp +++ b/apps/openmw/mwmechanics/actor.hpp @@ -5,6 +5,8 @@ #include "../mwmechanics/actorutil.hpp" +#include + namespace MWRender { class Animation; @@ -41,12 +43,18 @@ namespace MWMechanics bool isTurningToPlayer() const; void setTurningToPlayer(bool turning); + Misc::TimerStatus updateEngageCombatTimer(float duration) + { + return mEngageCombat.update(duration); + } + private: std::unique_ptr mCharacterController; int mGreetingTimer{0}; float mTargetAngleRadians{0.f}; GreetingState mGreetingState{Greet_None}; bool mIsTurningToPlayer{false}; + Misc::DeviatingPeriodicTimer mEngageCombat{1.0f, 0.25f, Misc::Rng::deviate(0, 0.25f)}; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c0a137158..4379521a4 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1901,14 +1901,11 @@ namespace MWMechanics { if(!paused) { - static float timerUpdateAITargets = 0; static float timerUpdateHeadTrack = 0; static float timerUpdateEquippedLight = 0; static float timerUpdateHello = 0; const float updateEquippedLightInterval = 1.0f; - // target lists get updated once every 1.0 sec - if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0; if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0; if (timerUpdateHello >= 0.25f) timerUpdateHello = 0; if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0; @@ -1961,6 +1958,8 @@ namespace MWMechanics iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); + const Misc::TimerStatus engageCombatTimerStatus = iter->second->updateEngageCombatTimer(duration); + // For dead actors we need to update looping spell particles if (iter->first.getClass().getCreatureStats(iter->first).isDead()) { @@ -1989,7 +1988,7 @@ namespace MWMechanics } if (aiActive && inProcessingRange) { - if (timerUpdateAITargets == 0) + if (engageCombatTimerStatus == Misc::TimerStatus::Elapsed) { if (!isPlayer) adjustCommandedActor(iter->first); @@ -2076,7 +2075,6 @@ namespace MWMechanics if (avoidCollisions) predictAndAvoidCollisions(); - timerUpdateAITargets += duration; timerUpdateHeadTrack += duration; timerUpdateEquippedLight += duration; timerUpdateHello += duration; From 40265bf1181167d927f3870b9d5127e310ce49f7 Mon Sep 17 00:00:00 2001 From: wareya Date: Sat, 20 Mar 2021 21:14:56 -0400 Subject: [PATCH 113/132] make unstucking slightly smarter (can turn itself off, also acts like flat ground) --- apps/openmw/mwphysics/actor.hpp | 21 +++++++++++++++++++ apps/openmw/mwphysics/movementsolver.cpp | 26 +++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 031125f40..472a79bff 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -159,6 +159,24 @@ namespace MWPhysics MWWorld::Ptr getStandingOnPtr() const; void setStandingOnPtr(const MWWorld::Ptr& ptr); + unsigned int getStuckFrames() const + { + return mStuckFrames; + } + void setStuckFrames(unsigned int frames) + { + mStuckFrames = frames; + } + + const osg::Vec3f &getLastStuckPosition() const + { + return mLastStuckPosition; + } + void setLastStuckPosition(osg::Vec3f position) + { + mLastStuckPosition = position; + } + private: MWWorld::Ptr mStandingOnPtr; /// Removes then re-adds the collision object to the dynamics world @@ -192,6 +210,9 @@ namespace MWPhysics btTransform mLocalTransform; mutable std::mutex mPositionMutex; + unsigned int mStuckFrames; + osg::Vec3f mLastStuckPosition; + osg::Vec3f mForce; std::atomic mOnGround; std::atomic mOnSlope; diff --git a/apps/openmw/mwphysics/movementsolver.cpp b/apps/openmw/mwphysics/movementsolver.cpp index 5f0322a1f..a393ec578 100644 --- a/apps/openmw/mwphysics/movementsolver.cpp +++ b/apps/openmw/mwphysics/movementsolver.cpp @@ -204,7 +204,7 @@ namespace MWPhysics osg::Vec3f lastSlideNormalFallback(0,0,1); bool forceGroundTest = false; - for (int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations) + for (int iterations = 0; iterations < sMaxIterations && remainingTime > 0.0001f; ++iterations) { osg::Vec3f nextpos = newPosition + velocity * remainingTime; @@ -394,6 +394,12 @@ namespace MWPhysics isOnGround = false; } } + // forcibly treat stuck actors as if they're on flat ground because buggy collisions when inside of things can/will break ground detection + if(physicActor->getStuckFrames() > 0) + { + isOnGround = true; + isOnSlope = false; + } } if((isOnGround && !isOnSlope) || newPosition.z() < swimlevel || actor.mFlying) @@ -437,6 +443,17 @@ namespace MWPhysics auto* collisionObject = physicActor->getCollisionObject(); auto tempPosition = actor.mPosition; + if(physicActor->getStuckFrames() >= 10) + { + if((physicActor->getLastStuckPosition() - actor.mPosition).length2() < 100) + return; + else + { + physicActor->setStuckFrames(0); + physicActor->setLastStuckPosition({0, 0, 0}); + } + } + // use vanilla-accurate collision hull position hack (do same hitbox offset hack as movement solver) // if vanilla compatibility didn't matter, the "correct" collision hull position would be physicActor->getScaledMeshTranslation() const auto verticalHalfExtent = osg::Vec3f(0.0, 0.0, physicActor->getHalfExtents().z()); @@ -470,6 +487,8 @@ namespace MWPhysics auto contactCallback = gatherContacts({0.0, 0.0, 0.0}); if(contactCallback.mDistance < -sAllowedPenetration) { + physicActor->setStuckFrames(physicActor->getStuckFrames() + 1); + physicActor->setLastStuckPosition(actor.mPosition); // we are; try moving it out of the world auto positionDelta = contactCallback.mContactSum; // limit rejection delta to the largest known individual rejections @@ -502,6 +521,11 @@ namespace MWPhysics } } } + else + { + physicActor->setStuckFrames(0); + physicActor->setLastStuckPosition({0, 0, 0}); + } collisionObject->setWorldTransform(oldTransform); actor.mPosition = tempPosition; From e722c99e6284d5f3611b746e5e6cc562c28b8fa4 Mon Sep 17 00:00:00 2001 From: wareya Date: Sun, 21 Mar 2021 08:57:15 -0400 Subject: [PATCH 114/132] forgot to initialize these variables --- apps/openmw/mwphysics/actor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 06abe7240..905034cde 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -22,6 +22,7 @@ namespace MWPhysics Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, PhysicsTaskScheduler* scheduler) : mStandingOnPtr(nullptr), mCanWaterWalk(false), mWalkingOnWater(false) , mCollisionObject(nullptr), mMeshTranslation(shape->mCollisionBox.center), mHalfExtents(shape->mCollisionBox.extents) + , mStuckFrames(0), mLastStuckPosition{0, 0, 0} , mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false) , mInternalCollisionMode(true) , mExternalCollisionMode(true) From 7bbbe40abe042c1931823d68f0d769f40a5f8695 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Sun, 21 Mar 2021 13:56:56 +0100 Subject: [PATCH 115/132] "static const" -> "static constexpr" in headers --- apps/opencs/model/prefs/shortcutsetting.hpp | 2 +- apps/openmw/mwgui/sortfilteritemmodel.hpp | 28 ++++++------- apps/openmw/mwgui/widgets.hpp | 2 +- apps/openmw/mwmechanics/creaturestats.hpp | 2 +- apps/openmw/mwmechanics/obstacle.hpp | 2 +- apps/openmw/mwphysics/constants.hpp | 20 ++++----- apps/openmw/mwrender/animation.hpp | 2 +- apps/openmw/mwscript/ref.hpp | 4 +- apps/openmw/mwworld/containerstore.hpp | 36 ++++++++-------- apps/openmw/mwworld/inventorystore.hpp | 46 ++++++++++----------- 10 files changed, 72 insertions(+), 72 deletions(-) diff --git a/apps/opencs/model/prefs/shortcutsetting.hpp b/apps/opencs/model/prefs/shortcutsetting.hpp index a0c588b42..52298232e 100644 --- a/apps/opencs/model/prefs/shortcutsetting.hpp +++ b/apps/opencs/model/prefs/shortcutsetting.hpp @@ -34,7 +34,7 @@ namespace CSMPrefs void storeValue(const QKeySequence& sequence); void resetState(); - static const int MaxKeys = 4; + static constexpr int MaxKeys = 4; QPushButton* mButton; diff --git a/apps/openmw/mwgui/sortfilteritemmodel.hpp b/apps/openmw/mwgui/sortfilteritemmodel.hpp index fa70a0edd..64a01f71b 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.hpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.hpp @@ -35,20 +35,20 @@ namespace MWGui bool onDropItem(const MWWorld::Ptr &item, int count) override; bool onTakeItem(const MWWorld::Ptr &item, int count) override; - static const int Category_Weapon = (1<<1); - static const int Category_Apparel = (1<<2); - static const int Category_Misc = (1<<3); - static const int Category_Magic = (1<<4); - static const int Category_All = 255; - - static const int Filter_OnlyIngredients = (1<<0); - static const int Filter_OnlyEnchanted = (1<<1); - static const int Filter_OnlyEnchantable = (1<<2); - static const int Filter_OnlyChargedSoulstones = (1<<3); - static const int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action - static const int Filter_OnlyRepairable = (1<<5); - static const int Filter_OnlyRechargable = (1<<6); - static const int Filter_OnlyRepairTools = (1<<7); + static constexpr int Category_Weapon = (1<<1); + static constexpr int Category_Apparel = (1<<2); + static constexpr int Category_Misc = (1<<3); + static constexpr int Category_Magic = (1<<4); + static constexpr int Category_All = 255; + + static constexpr int Filter_OnlyIngredients = (1<<0); + static constexpr int Filter_OnlyEnchanted = (1<<1); + static constexpr int Filter_OnlyEnchantable = (1<<2); + static constexpr int Filter_OnlyChargedSoulstones = (1<<3); + static constexpr int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action + static constexpr int Filter_OnlyRepairable = (1<<5); + static constexpr int Filter_OnlyRechargable = (1<<6); + static constexpr int Filter_OnlyRepairTools = (1<<7); private: diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 731a41a35..3c5528715 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -268,7 +268,7 @@ namespace MWGui void initialiseOverride() override; private: - static const int sIconOffset = 24; + static constexpr int sIconOffset = 24; void updateWidgets(); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index b2c0aec98..e09f5197e 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -24,7 +24,7 @@ namespace MWMechanics { struct CorprusStats { - static const int sWorseningPeriod = 24; + static constexpr int sWorseningPeriod = 24; int mWorsenings[ESM::Attribute::Length]; MWWorld::TimeStamp mNextWorsening; diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 6c2197d81..b574bab67 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -12,7 +12,7 @@ namespace MWMechanics { struct Movement; - static const int NUM_EVADE_DIRECTIONS = 4; + static constexpr int NUM_EVADE_DIRECTIONS = 4; /// tests actor's proximity to a closed door by default bool proximityToDoor(const MWWorld::Ptr& actor, float minDist); diff --git a/apps/openmw/mwphysics/constants.hpp b/apps/openmw/mwphysics/constants.hpp index c6f2f3b53..d552ed49e 100644 --- a/apps/openmw/mwphysics/constants.hpp +++ b/apps/openmw/mwphysics/constants.hpp @@ -3,24 +3,24 @@ namespace MWPhysics { - static const float sStepSizeUp = 34.0f; - static const float sStepSizeDown = 62.0f; + static constexpr float sStepSizeUp = 34.0f; + static constexpr float sStepSizeDown = 62.0f; - static const float sMinStep = 10.0f; // hack to skip over tiny unwalkable slopes - static const float sMinStep2 = 20.0f; // hack to skip over shorter but longer/wider/further unwalkable slopes + static constexpr float sMinStep = 10.0f; // hack to skip over tiny unwalkable slopes + static constexpr float sMinStep2 = 20.0f; // hack to skip over shorter but longer/wider/further unwalkable slopes // whether to do the above stairstepping logic hacks to work around bad morrowind assets - disabling causes problems but improves performance - static const bool sDoExtraStairHacks = true; + static constexpr bool sDoExtraStairHacks = true; - static const float sGroundOffset = 1.0f; - static const float sMaxSlope = 49.0f; + static constexpr float sGroundOffset = 1.0f; + static constexpr float sMaxSlope = 49.0f; // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. - static const int sMaxIterations = 8; + static constexpr int sMaxIterations = 8; // Allows for more precise movement solving without getting stuck or snagging too easily. - static const float sCollisionMargin = 0.1; + static constexpr float sCollisionMargin = 0.1; // Allow for a small amount of penetration to prevent numerical precision issues from causing the "unstuck"ing code to run unnecessarily // Currently set to 0 because having the "unstuck"ing code run whenever possible prevents some glitchy snagging issues - static const float sAllowedPenetration = 0.0; + static constexpr float sAllowedPenetration = 0.0; } #endif diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index ebfe8a2e5..04c5825c9 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -102,7 +102,7 @@ public: BlendMask_All = BlendMask_LowerBody | BlendMask_UpperBody }; /* This is the number of *discrete* blend masks. */ - static const size_t sNumBlendMasks = 4; + static constexpr size_t sNumBlendMasks = 4; /// Holds an animation priority value for each BoneGroup. struct AnimPriority diff --git a/apps/openmw/mwscript/ref.hpp b/apps/openmw/mwscript/ref.hpp index e572f5147..c52b419c1 100644 --- a/apps/openmw/mwscript/ref.hpp +++ b/apps/openmw/mwscript/ref.hpp @@ -14,7 +14,7 @@ namespace MWScript { struct ExplicitRef { - static const bool implicit = false; + static constexpr bool implicit = false; MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required = true, bool activeOnly = false) const; @@ -22,7 +22,7 @@ namespace MWScript struct ImplicitRef { - static const bool implicit = true; + static constexpr bool implicit = true; MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required = true, bool activeOnly = false) const; diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index e0843efba..882f5efc4 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -73,22 +73,22 @@ namespace MWWorld { public: - static const int Type_Potion = 0x0001; - static const int Type_Apparatus = 0x0002; - static const int Type_Armor = 0x0004; - static const int Type_Book = 0x0008; - static const int Type_Clothing = 0x0010; - static const int Type_Ingredient = 0x0020; - static const int Type_Light = 0x0040; - static const int Type_Lockpick = 0x0080; - static const int Type_Miscellaneous = 0x0100; - static const int Type_Probe = 0x0200; - static const int Type_Repair = 0x0400; - static const int Type_Weapon = 0x0800; - - static const int Type_Last = Type_Weapon; - - static const int Type_All = 0xffff; + static constexpr int Type_Potion = 0x0001; + static constexpr int Type_Apparatus = 0x0002; + static constexpr int Type_Armor = 0x0004; + static constexpr int Type_Book = 0x0008; + static constexpr int Type_Clothing = 0x0010; + static constexpr int Type_Ingredient = 0x0020; + static constexpr int Type_Light = 0x0040; + static constexpr int Type_Lockpick = 0x0080; + static constexpr int Type_Miscellaneous = 0x0100; + static constexpr int Type_Probe = 0x0200; + static constexpr int Type_Repair = 0x0400; + static constexpr int Type_Weapon = 0x0800; + + static constexpr int Type_Last = Type_Weapon; + + static constexpr int Type_All = 0xffff; static const std::string sGoldId; @@ -265,13 +265,13 @@ namespace MWWorld template struct IsConvertible { - static const bool value = true; + static constexpr bool value = true; }; template struct IsConvertible { - static const bool value = false; + static constexpr bool value = false; }; template diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index bfe0a9992..6809e63b2 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -42,29 +42,29 @@ namespace MWWorld { public: - static const int Slot_Helmet = 0; - static const int Slot_Cuirass = 1; - static const int Slot_Greaves = 2; - static const int Slot_LeftPauldron = 3; - static const int Slot_RightPauldron = 4; - static const int Slot_LeftGauntlet = 5; - static const int Slot_RightGauntlet = 6; - static const int Slot_Boots = 7; - static const int Slot_Shirt = 8; - static const int Slot_Pants = 9; - static const int Slot_Skirt = 10; - static const int Slot_Robe = 11; - static const int Slot_LeftRing = 12; - static const int Slot_RightRing = 13; - static const int Slot_Amulet = 14; - static const int Slot_Belt = 15; - static const int Slot_CarriedRight = 16; - static const int Slot_CarriedLeft = 17; - static const int Slot_Ammunition = 18; - - static const int Slots = 19; - - static const int Slot_NoSlot = -1; + static constexpr int Slot_Helmet = 0; + static constexpr int Slot_Cuirass = 1; + static constexpr int Slot_Greaves = 2; + static constexpr int Slot_LeftPauldron = 3; + static constexpr int Slot_RightPauldron = 4; + static constexpr int Slot_LeftGauntlet = 5; + static constexpr int Slot_RightGauntlet = 6; + static constexpr int Slot_Boots = 7; + static constexpr int Slot_Shirt = 8; + static constexpr int Slot_Pants = 9; + static constexpr int Slot_Skirt = 10; + static constexpr int Slot_Robe = 11; + static constexpr int Slot_LeftRing = 12; + static constexpr int Slot_RightRing = 13; + static constexpr int Slot_Amulet = 14; + static constexpr int Slot_Belt = 15; + static constexpr int Slot_CarriedRight = 16; + static constexpr int Slot_CarriedLeft = 17; + static constexpr int Slot_Ammunition = 18; + + static constexpr int Slots = 19; + + static constexpr int Slot_NoSlot = -1; private: From 209e7718a8e246a07e3afb8ffe40859f21df4594 Mon Sep 17 00:00:00 2001 From: "Hristos N. Triantafillou" Date: Sun, 21 Mar 2021 15:37:48 -0500 Subject: [PATCH 116/132] Clarify the requirements of a data folder The current text could be interpreted to mean that a data folder _must_ have a plugin, but this isn't the case. This added text clarifies that a plugin or resources are needed. --- docs/source/reference/modding/mod-install.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/reference/modding/mod-install.rst b/docs/source/reference/modding/mod-install.rst index 2c883aa59..c15d1b268 100644 --- a/docs/source/reference/modding/mod-install.rst +++ b/docs/source/reference/modding/mod-install.rst @@ -11,6 +11,7 @@ Install #. Locate the plugin files, ``.esp`` or ``.omwaddon``, or possibly ``.esm``. The folder containing the plugin files we will call your *data folder* #. Check that all resource folders (``Meshes``, ``Textures``, etc.) containing additional resource files (the actual meshes, textures, etc.) are in the *data folder*. + #. Note that not all mods have a plugin, and not all mods have resources, but they must at minimum have one or the other. .. note:: There may be multiple levels of folders, but the location of the plugins must be the same as the resource folders. From 63f01d8c5f3dcc86826acd36ca244a17e845201c Mon Sep 17 00:00:00 2001 From: wareya Date: Sun, 21 Mar 2021 20:45:46 +0000 Subject: [PATCH 117/132] Prevent physics death spiral by falling back to true delta time when needed --- CHANGELOG.md | 2 + apps/openmw/mwphysics/mtphysics.cpp | 77 ++++++++++++++++++++++++- apps/openmw/mwphysics/mtphysics.hpp | 13 ++++- apps/openmw/mwphysics/physicssystem.cpp | 13 ++--- apps/openmw/mwphysics/physicssystem.hpp | 2 +- components/misc/budgetmeasurement.hpp | 42 ++++++++++++++ 6 files changed, 134 insertions(+), 15 deletions(-) create mode 100644 components/misc/budgetmeasurement.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 0891ecba9..4ac01c00f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ Bug #5424: Creatures do not headtrack player Bug #5425: Poison effect only appears for one frame Bug #5427: GetDistance unknown ID error is misleading + Bug #5431: Physics performance degradation after a specific number of actors on a scene Bug #5435: Enemies can't hurt the player when collision is off Bug #5441: Enemies can't push a player character when in critical strike stance Bug #5451: Magic projectiles don't disappear with the caster @@ -143,6 +144,7 @@ Feature #5730: Add graphic herbalism option to the launcher and documents Feature #5771: ori command should report where a mesh is loaded from and whether the x version is used. Feature #5813: Instanced groundcover support + Feature #5910: Fall back to delta time when physics can't keep up Task #5480: Drop Qt4 support Task #5520: Improve cell name autocompleter implementation Task #5844: Update 'toggle sneak' documentation diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index 1fa6251f8..11eb7f909 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -101,7 +101,7 @@ namespace osg::Vec3f interpolateMovements(MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt) { - const float interpolationFactor = timeAccum / physicsDt; + const float interpolationFactor = std::clamp(timeAccum / physicsDt, 0.0f, 1.0f); return actorData.mPosition * interpolationFactor + actorData.mActorRaw->getPreviousPosition() * (1.f - interpolationFactor); } @@ -138,7 +138,8 @@ namespace namespace MWPhysics { PhysicsTaskScheduler::PhysicsTaskScheduler(float physicsDt, std::shared_ptr collisionWorld) - : mPhysicsDt(physicsDt) + : mDefaultPhysicsDt(physicsDt) + , mPhysicsDt(physicsDt) , mTimeAccum(0.f) , mCollisionWorld(std::move(collisionWorld)) , mNumJobs(0) @@ -152,6 +153,11 @@ namespace MWPhysics , mNextLOS(0) , mFrameNumber(0) , mTimer(osg::Timer::instance()) + , mPrevStepCount(1) + , mBudget(physicsDt) + , mAsyncBudget(0.0f) + , mBudgetCursor(0) + , mAsyncStartTime(0) , mTimeBegin(0) , mTimeEnd(0) , mFrameStart(0) @@ -220,13 +226,61 @@ namespace MWPhysics thread.join(); } - const std::vector& PhysicsTaskScheduler::moveActors(int numSteps, float timeAccum, std::vector&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) + std::tuple PhysicsTaskScheduler::calculateStepConfig(float timeAccum) const + { + int maxAllowedSteps = 2; + int numSteps = timeAccum / mDefaultPhysicsDt; + + // adjust maximum step count based on whether we're likely physics bottlenecked or not + // if maxAllowedSteps ends up higher than numSteps, we will not invoke delta time + // if it ends up lower than numSteps, but greater than 1, we will run a number of true delta time physics steps that we expect to be within budget + // if it ends up lower than numSteps and also 1, we will run a single delta time physics step + // if we did not do this, and had a fixed step count limit, + // we would have an unnecessarily low render framerate if we were only physics bottlenecked, + // and we would be unnecessarily invoking true delta time if we were only render bottlenecked + + // get physics timing stats + float budgetMeasurement = std::max(mBudget.get(), mAsyncBudget.get()); + // time spent per step in terms of the intended physics framerate + budgetMeasurement /= mDefaultPhysicsDt; + // ensure sane minimum value + budgetMeasurement = std::max(0.00001f, budgetMeasurement); + // we're spending almost or more than realtime per physics frame; limit to a single step + if (budgetMeasurement > 0.95) + maxAllowedSteps = 1; + // physics is fairly cheap; limit based on expense + if (budgetMeasurement < 0.5) + maxAllowedSteps = std::ceil(1.0/budgetMeasurement); + // limit to a reasonable amount + maxAllowedSteps = std::min(10, maxAllowedSteps); + + // fall back to delta time for this frame if fixed timestep physics would fall behind + float actualDelta = mDefaultPhysicsDt; + if (numSteps > maxAllowedSteps) + { + numSteps = maxAllowedSteps; + // ensure that we do not simulate a frame ahead when doing delta time; this reduces stutter and latency + // this causes interpolation to 100% use the most recent physics result when true delta time is happening + // and we deliberately simulate up to exactly the timestamp that we want to render + actualDelta = timeAccum/float(numSteps+1); + // actually: if this results in a per-step delta less than the target physics steptime, clamp it + // this might reintroduce some stutter, but only comes into play in obscure cases + // (because numSteps is originally based on mDefaultPhysicsDt, this won't cause us to overrun) + actualDelta = std::max(actualDelta, mDefaultPhysicsDt); + } + + return std::make_tuple(numSteps, actualDelta); + } + + const std::vector& PhysicsTaskScheduler::moveActors(float & timeAccum, std::vector&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) { // This function run in the main thread. // While the mSimulationMutex is held, background physics threads can't run. std::unique_lock lock(mSimulationMutex); + double timeStart = mTimer->tick(); + mMovedActors.clear(); // start by finishing previous background computation @@ -251,14 +305,21 @@ namespace MWPhysics mMovedActors.emplace_back(data.mActorRaw->getPtr()); } } + if(mAdvanceSimulation) + mAsyncBudget.update(mTimer->delta_s(mAsyncStartTime, mTimeEnd), mPrevStepCount, mBudgetCursor); updateStats(frameStart, frameNumber, stats); } + auto [numSteps, newDelta] = calculateStepConfig(timeAccum); + timeAccum -= numSteps*newDelta; + // init for (auto& data : actorsData) data.updatePosition(); + mPrevStepCount = numSteps; mRemainingSteps = numSteps; mTimeAccum = timeAccum; + mPhysicsDt = newDelta; mActorsFrameData = std::move(actorsData); mAdvanceSimulation = (mRemainingSteps != 0); mNewFrame = true; @@ -269,20 +330,30 @@ namespace MWPhysics if (mAdvanceSimulation) mWorldFrameData = std::make_unique(); + if (mAdvanceSimulation) + mBudgetCursor += 1; + if (mNumThreads == 0) { syncComputation(); + if(mAdvanceSimulation) + mBudget.update(mTimer->delta_s(timeStart, mTimer->tick()), numSteps, mBudgetCursor); return mMovedActors; } + mAsyncStartTime = mTimer->tick(); lock.unlock(); mHasJob.notify_all(); + if (mAdvanceSimulation) + mBudget.update(mTimer->delta_s(timeStart, mTimer->tick()), 1, mBudgetCursor); return mMovedActors; } const std::vector& PhysicsTaskScheduler::resetSimulation(const ActorMap& actors) { std::unique_lock lock(mSimulationMutex); + mBudget.reset(mDefaultPhysicsDt); + mAsyncBudget.reset(0.0f); mMovedActors.clear(); mActorsFrameData.clear(); for (const auto& [_, actor] : actors) diff --git a/apps/openmw/mwphysics/mtphysics.hpp b/apps/openmw/mwphysics/mtphysics.hpp index b35ebd5ee..137755c21 100644 --- a/apps/openmw/mwphysics/mtphysics.hpp +++ b/apps/openmw/mwphysics/mtphysics.hpp @@ -13,6 +13,7 @@ #include "physicssystem.hpp" #include "ptrholder.hpp" +#include "components/misc/budgetmeasurement.hpp" namespace Misc { @@ -32,7 +33,7 @@ namespace MWPhysics /// @param timeAccum accumulated time from previous run to interpolate movements /// @param actorsData per actor data needed to compute new positions /// @return new position of each actor - const std::vector& moveActors(int numSteps, float timeAccum, std::vector&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); + const std::vector& moveActors(float & timeAccum, std::vector&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); const std::vector& resetSimulation(const ActorMap& actors); @@ -58,11 +59,13 @@ namespace MWPhysics void updateAabbs(); void updatePtrAabb(const std::weak_ptr& ptr); void updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); + std::tuple calculateStepConfig(float timeAccum) const; std::unique_ptr mWorldFrameData; std::vector mActorsFrameData; std::vector mMovedActors; - const float mPhysicsDt; + float mDefaultPhysicsDt; + float mPhysicsDt; float mTimeAccum; std::shared_ptr mCollisionWorld; std::vector mLOSCache; @@ -94,6 +97,12 @@ namespace MWPhysics unsigned int mFrameNumber; const osg::Timer* mTimer; + + int mPrevStepCount; + Misc::BudgetMeasurement mBudget; + Misc::BudgetMeasurement mAsyncBudget; + unsigned int mBudgetCursor; + osg::Timer_t mAsyncStartTime; osg::Timer_t mTimeBegin; osg::Timer_t mTimeEnd; osg::Timer_t mFrameStart; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index dc9ab629a..65ed13f80 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -744,19 +744,14 @@ namespace MWPhysics { mTimeAccum += dt; - const int maxAllowedSteps = 20; - int numSteps = mTimeAccum / mPhysicsDt; - numSteps = std::min(numSteps, maxAllowedSteps); - - mTimeAccum -= numSteps * mPhysicsDt; - if (skipSimulation) return mTaskScheduler->resetSimulation(mActors); - return mTaskScheduler->moveActors(numSteps, mTimeAccum, prepareFrameData(numSteps), frameStart, frameNumber, stats); + // modifies mTimeAccum + return mTaskScheduler->moveActors(mTimeAccum, prepareFrameData(mTimeAccum >= mPhysicsDt), frameStart, frameNumber, stats); } - std::vector PhysicsSystem::prepareFrameData(int numSteps) + std::vector PhysicsSystem::prepareFrameData(bool willSimulate) { std::vector actorsFrameData; actorsFrameData.reserve(mMovementQueue.size()); @@ -796,7 +791,7 @@ namespace MWPhysics // Ue current value only if we don't advance the simulation. Otherwise we might get a stale value. MWWorld::Ptr standingOn; - if (numSteps == 0) + if (!willSimulate) standingOn = physicActor->getStandingOnPtr(); actorsFrameData.emplace_back(std::move(physicActor), standingOn, moveToWaterSurface, movement, slowFall, waterlevel); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 80b2d98bc..354823986 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -252,7 +252,7 @@ namespace MWPhysics void updateWater(); - std::vector prepareFrameData(int numSteps); + std::vector prepareFrameData(bool willSimulate); osg::ref_ptr mUnrefQueue; diff --git a/components/misc/budgetmeasurement.hpp b/components/misc/budgetmeasurement.hpp new file mode 100644 index 000000000..3d56477af --- /dev/null +++ b/components/misc/budgetmeasurement.hpp @@ -0,0 +1,42 @@ +#ifndef OPENMW_COMPONENTS_MISC_BUDGETMEASUREMENT_H +#define OPENMW_COMPONENTS_MISC_BUDGETMEASUREMENT_H + + +namespace Misc +{ + +class BudgetMeasurement +{ + std::array mBudgetHistory; + std::array mBudgetStepCount; + +public: + BudgetMeasurement(const float default_expense) + { + mBudgetHistory = {default_expense, default_expense, default_expense, default_expense}; + mBudgetStepCount = {1, 1, 1, 1}; + } + + void reset(const float default_expense) + { + mBudgetHistory = {default_expense, default_expense, default_expense, default_expense}; + mBudgetStepCount = {1, 1, 1, 1}; + } + + void update(double delta, unsigned int stepCount, size_t cursor) + { + mBudgetHistory[cursor%4] = delta; + mBudgetStepCount[cursor%4] = stepCount; + } + + double get() const + { + float sum = (mBudgetHistory[0] + mBudgetHistory[1] + mBudgetHistory[2] + mBudgetHistory[3]); + unsigned int stepCountSum = (mBudgetStepCount[0] + mBudgetStepCount[1] + mBudgetStepCount[2] + mBudgetStepCount[3]); + return sum/float(stepCountSum); + } +}; + +} + +#endif From 4862e8c8f492548f9e8b3c4e0d1aa962179b0f2e Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 21 Mar 2021 21:30:17 +0000 Subject: [PATCH 118/132] Bump aqt version --- CI/before_script.msvc.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index dba87128a..b2b6483ec 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -848,9 +848,11 @@ fi wrappedExit 1 fi - if ! [ -e "aqt-venv/${VENV_BIN_DIR}/aqt" ]; then + # check version + pip list | grep 'aqtinstall\s*1.1.3' + if [ $? -ne 0 ]; then echo " Installing aqt wheel into virtualenv..." - run_cmd "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall==0.9.2 + run_cmd "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall==1.1.3 fi popd > /dev/null From 2fdbe9b3f6a5b47ae692148dd0a8651c2836d5f6 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Thu, 17 Dec 2020 00:46:09 +0300 Subject: [PATCH 119/132] Handle BSShader[PP/No]LightingProperty --- components/nif/niffile.cpp | 2 + components/nif/property.cpp | 32 ++++- components/nif/property.hpp | 42 ++++++- components/nif/record.hpp | 4 +- components/nifosg/nifloader.cpp | 139 ++++++++++++++++++++++ components/resource/scenemanager.cpp | 2 +- components/shader/shadervisitor.cpp | 17 ++- components/shader/shadervisitor.hpp | 5 +- files/shaders/CMakeLists.txt | 4 + files/shaders/nv_default_fragment.glsl | 106 +++++++++++++++++ files/shaders/nv_default_vertex.glsl | 58 +++++++++ files/shaders/nv_nolighting_fragment.glsl | 55 +++++++++ files/shaders/nv_nolighting_vertex.glsl | 49 ++++++++ files/shaders/objects_fragment.glsl | 5 +- files/shaders/objects_vertex.glsl | 4 +- 15 files changed, 509 insertions(+), 15 deletions(-) create mode 100644 files/shaders/nv_default_fragment.glsl create mode 100644 files/shaders/nv_default_vertex.glsl create mode 100644 files/shaders/nv_nolighting_fragment.glsl create mode 100644 files/shaders/nv_nolighting_vertex.glsl diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 665533c91..3e226b35e 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -133,6 +133,8 @@ static std::map makeFactory() factory["BSShaderTextureSet"] = {&construct , RC_BSShaderTextureSet }; factory["BSLODTriShape"] = {&construct , RC_BSLODTriShape }; factory["BSShaderProperty"] = {&construct , RC_BSShaderProperty }; + factory["BSShaderPPLightingProperty"] = {&construct , RC_BSShaderPPLightingProperty }; + factory["BSShaderNoLightingProperty"] = {&construct , RC_BSShaderNoLightingProperty }; return factory; } diff --git a/components/nif/property.cpp b/components/nif/property.cpp index d5357e123..1d2dd885d 100644 --- a/components/nif/property.cpp +++ b/components/nif/property.cpp @@ -118,6 +118,34 @@ void BSShaderLightingProperty::read(NIFStream *nif) clamp = nif->getUInt(); } +void BSShaderPPLightingProperty::read(NIFStream *nif) +{ + BSShaderLightingProperty::read(nif); + textureSet.read(nif); + if (nif->getBethVersion() <= 14) + return; + refraction.strength = nif->getFloat(); + refraction.period = nif->getInt(); + if (nif->getBethVersion() <= 24) + return; + parallax.passes = nif->getFloat(); + parallax.scale = nif->getFloat(); +} + +void BSShaderPPLightingProperty::post(NIFFile *nif) +{ + BSShaderLightingProperty::post(nif); + textureSet.post(nif); +} + +void BSShaderNoLightingProperty::read(NIFStream *nif) +{ + BSShaderLightingProperty::read(nif); + filename = nif->getSizedString(); + if (nif->getBethVersion() >= 27) + falloffParams = nif->getVector4(); +} + void NiFogProperty::read(NIFStream *nif) { Property::read(nif); @@ -137,8 +165,8 @@ void S_MaterialProperty::read(NIFStream *nif) emissive = nif->getVector3(); glossiness = nif->getFloat(); alpha = nif->getFloat(); - if (nif->getBethVersion() > 21) - emissive *= nif->getFloat(); + if (nif->getBethVersion() >= 22) + emissiveMult = nif->getFloat(); } void S_VertexColorProperty::read(NIFStream *nif) diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 008e84515..9c76f6d6e 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -118,6 +118,18 @@ struct NiShadeProperty : public Property struct BSShaderProperty : public NiShadeProperty { + enum BSShaderType + { + SHADER_TALL_GRASS = 0, + SHADER_DEFAULT = 1, + SHADER_SKY = 10, + SHADER_SKIN = 14, + SHADER_WATER = 17, + SHADER_LIGHTING30 = 29, + SHADER_TILE = 32, + SHADER_NOLIGHTING = 33 + }; + unsigned int type{0u}, flags1{0u}, flags2{0u}; float envMapIntensity{0.f}; void read(NIFStream *nif) override; @@ -129,6 +141,34 @@ struct BSShaderLightingProperty : public BSShaderProperty void read(NIFStream *nif) override; }; +struct BSShaderPPLightingProperty : public BSShaderLightingProperty +{ + BSShaderTextureSetPtr textureSet; + struct RefractionSettings + { + float strength{0.f}; + int period{0}; + }; + struct ParallaxSettings + { + float passes{0.f}; + float scale{0.f}; + }; + RefractionSettings refraction; + ParallaxSettings parallax; + + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + +struct BSShaderNoLightingProperty : public BSShaderLightingProperty +{ + std::string filename; + osg::Vec4f falloffParams; + + void read(NIFStream *nif) override; +}; + struct NiDitherProperty : public Property { unsigned short flags; @@ -193,7 +233,7 @@ struct S_MaterialProperty // The vector components are R,G,B osg::Vec3f ambient{1.f,1.f,1.f}, diffuse{1.f,1.f,1.f}; osg::Vec3f specular, emissive; - float glossiness{0.f}, alpha{0.f}; + float glossiness{0.f}, alpha{0.f}, emissiveMult{1.f}; void read(NIFStream *nif); }; diff --git a/components/nif/record.hpp b/components/nif/record.hpp index efacd8246..ed97acabc 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -122,7 +122,9 @@ enum RecordType RC_NiColorInterpolator, RC_BSShaderTextureSet, RC_BSLODTriShape, - RC_BSShaderProperty + RC_BSShaderProperty, + RC_BSShaderPPLightingProperty, + RC_BSShaderNoLightingProperty }; /// Base class for all records diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 3e34969f4..ae1726f06 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1672,6 +1672,85 @@ namespace NifOsg handleTextureControllers(texprop, composite, imageManager, stateset, animflags); } + void handleTextureSet(const Nif::BSShaderTextureSet* textureSet, unsigned int clamp, const std::string& nodeName, osg::StateSet* stateset, Resource::ImageManager* imageManager, std::vector& boundTextures) + { + if (!boundTextures.empty()) + { + for (unsigned int i = 0; i < boundTextures.size(); ++i) + stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF); + boundTextures.clear(); + } + + const unsigned int uvSet = 0; + + for (size_t i = 0; i < textureSet->textures.size(); ++i) + { + if (textureSet->textures[i].empty()) + continue; + switch(i) + { + case Nif::BSShaderTextureSet::TextureType_Base: + case Nif::BSShaderTextureSet::TextureType_Normal: + case Nif::BSShaderTextureSet::TextureType_Glow: + break; + default: + { + Log(Debug::Info) << "Unhandled texture stage " << i << " on shape \"" << nodeName << "\" in " << mFilename; + continue; + } + } + std::string filename = Misc::ResourceHelpers::correctTexturePath(textureSet->textures[i], imageManager->getVFS()); + osg::ref_ptr image = imageManager->getImage(filename); + osg::ref_ptr texture2d = new osg::Texture2D(image); + if (image) + texture2d->setTextureSize(image->s(), image->t()); + bool wrapT = clamp & 0x1; + bool wrapS = (clamp >> 1) & 0x1; + texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + unsigned int texUnit = boundTextures.size(); + stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); + // BSShaderTextureSet presence means there's no need for FFP support for the affected node + switch (i) + { + case Nif::BSShaderTextureSet::TextureType_Base: + texture2d->setName("diffuseMap"); + break; + case Nif::BSShaderTextureSet::TextureType_Normal: + texture2d->setName("normalMap"); + break; + case Nif::BSShaderTextureSet::TextureType_Glow: + texture2d->setName("emissiveMap"); + break; + } + boundTextures.emplace_back(uvSet); + } + } + + const std::string& getNVShaderPrefix(unsigned int type) const + { + static const std::map mapping = + { + {Nif::BSShaderProperty::SHADER_TALL_GRASS, std::string()}, + {Nif::BSShaderProperty::SHADER_DEFAULT, "nv_default"}, + {Nif::BSShaderProperty::SHADER_SKY, std::string()}, + {Nif::BSShaderProperty::SHADER_SKIN, std::string()}, + {Nif::BSShaderProperty::SHADER_WATER, std::string()}, + {Nif::BSShaderProperty::SHADER_LIGHTING30, std::string()}, + {Nif::BSShaderProperty::SHADER_TILE, std::string()}, + {Nif::BSShaderProperty::SHADER_NOLIGHTING, "nv_nolighting"}, + }; + auto prefix = mapping.find(type); + if (prefix == mapping.end()) + Log(Debug::Warning) << "Unknown shader type " << type << " in " << mFilename; + else if (prefix->second.empty()) + Log(Debug::Warning) << "Unhandled shader type " << type << " in " << mFilename; + else + return prefix->second; + + return mapping.at(Nif::BSShaderProperty::SHADER_DEFAULT); + } + void handleProperty(const Nif::Property *property, osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector& boundTextures, int animflags) { @@ -1757,6 +1836,63 @@ namespace NifOsg handleTextureProperty(texprop, node->getName(), stateset, composite, imageManager, boundTextures, animflags); break; } + case Nif::RC_BSShaderPPLightingProperty: + { + auto texprop = static_cast(property); + bool shaderRequired = true; + node->setUserValue("shaderPrefix", getNVShaderPrefix(texprop->type)); + node->setUserValue("shaderRequired", shaderRequired); + osg::StateSet* stateset = node->getOrCreateStateSet(); + if (!texprop->textureSet.empty()) + { + auto textureSet = texprop->textureSet.getPtr(); + handleTextureSet(textureSet, texprop->clamp, node->getName(), stateset, imageManager, boundTextures); + } + handleTextureControllers(texprop, composite, imageManager, stateset, animflags); + break; + } + case Nif::RC_BSShaderNoLightingProperty: + { + auto texprop = static_cast(property); + bool shaderRequired = true; + node->setUserValue("shaderPrefix", getNVShaderPrefix(texprop->type)); + node->setUserValue("shaderRequired", shaderRequired); + osg::StateSet* stateset = node->getOrCreateStateSet(); + if (!texprop->filename.empty()) + { + if (!boundTextures.empty()) + { + for (unsigned int i = 0; i < boundTextures.size(); ++i) + stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF); + boundTextures.clear(); + } + std::string filename = Misc::ResourceHelpers::correctTexturePath(texprop->filename, imageManager->getVFS()); + osg::ref_ptr image = imageManager->getImage(filename); + osg::ref_ptr texture2d = new osg::Texture2D(image); + texture2d->setName("diffuseMap"); + if (image) + texture2d->setTextureSize(image->s(), image->t()); + bool wrapT = texprop->clamp & 0x1; + bool wrapS = (texprop->clamp >> 1) & 0x1; + texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + const unsigned int texUnit = 0; + const unsigned int uvSet = 0; + stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); + boundTextures.push_back(uvSet); + } + if (mBethVersion >= 27) + { + stateset->addUniform(new osg::Uniform("useFalloff", true)); + stateset->addUniform(new osg::Uniform("falloffParams", texprop->falloffParams)); + } + else + { + stateset->addUniform(new osg::Uniform("useFalloff", false)); + } + handleTextureControllers(texprop, composite, imageManager, stateset, animflags); + break; + } // unused by mw case Nif::RC_NiShadeProperty: case Nif::RC_NiDitherProperty: @@ -1809,6 +1945,7 @@ namespace NifOsg bool hasMatCtrl = false; int lightmode = 1; + float emissiveMult = 1.f; for (const Nif::Property* property : properties) { @@ -1828,6 +1965,7 @@ namespace NifOsg mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.diffuse, matprop->data.alpha)); mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.ambient, 1.f)); mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.emissive, 1.f)); + emissiveMult = matprop->data.emissiveMult; mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.specular, 1.f)); mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness); @@ -1948,6 +2086,7 @@ namespace NifOsg mat = shareAttribute(mat); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); + stateset->addUniform(new osg::Uniform("emissiveMult", emissiveMult)); } }; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index e46ce2016..66d48f971 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -774,7 +774,7 @@ namespace Resource Shader::ShaderVisitor *SceneManager::createShaderVisitor(const std::string& shaderPrefix, bool translucentFramebuffer) { - Shader::ShaderVisitor* shaderVisitor = new Shader::ShaderVisitor(*mShaderManager.get(), *mImageManager, shaderPrefix+"_vertex.glsl", shaderPrefix+"_fragment.glsl"); + Shader::ShaderVisitor* shaderVisitor = new Shader::ShaderVisitor(*mShaderManager.get(), *mImageManager, shaderPrefix); shaderVisitor->setForceShaders(mForceShaders); shaderVisitor->setAutoUseNormalMaps(mAutoUseNormalMaps); shaderVisitor->setNormalMapPattern(mNormalMapPattern); diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index eae9ad2db..1af9f94c5 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -42,7 +42,7 @@ namespace Shader } - ShaderVisitor::ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string &defaultVsTemplate, const std::string &defaultFsTemplate) + ShaderVisitor::ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string &defaultShaderPrefix) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mForceShaders(false) , mAllowedToModifyStateSets(true) @@ -52,8 +52,7 @@ namespace Shader , mTranslucentFramebuffer(false) , mShaderManager(shaderManager) , mImageManager(imageManager) - , mDefaultVsTemplate(defaultVsTemplate) - , mDefaultFsTemplate(defaultFsTemplate) + , mDefaultShaderPrefix(defaultShaderPrefix) { mRequirements.emplace_back(); } @@ -129,6 +128,10 @@ namespace Shader if (mAllowedToModifyStateSets) writableStateSet = node.getStateSet(); const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList(); + bool shaderRequired = false; + if (node.getUserValue("shaderRequired", shaderRequired) && shaderRequired) + mRequirements.back().mShaderRequired = true; + if (!texAttributes.empty()) { const osg::Texture* diffuseMap = nullptr; @@ -440,8 +443,12 @@ namespace Shader defineMap["translucentFramebuffer"] = mTranslucentFramebuffer ? "1" : "0"; - osg::ref_ptr vertexShader (mShaderManager.getShader(mDefaultVsTemplate, defineMap, osg::Shader::VERTEX)); - osg::ref_ptr fragmentShader (mShaderManager.getShader(mDefaultFsTemplate, defineMap, osg::Shader::FRAGMENT)); + std::string shaderPrefix; + if (!node.getUserValue("shaderPrefix", shaderPrefix)) + shaderPrefix = mDefaultShaderPrefix; + + osg::ref_ptr vertexShader (mShaderManager.getShader(shaderPrefix + "_vertex.glsl", defineMap, osg::Shader::VERTEX)); + osg::ref_ptr fragmentShader (mShaderManager.getShader(shaderPrefix + "_fragment.glsl", defineMap, osg::Shader::FRAGMENT)); if (vertexShader && fragmentShader) { diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index f7c6f8312..30ff41a33 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -17,7 +17,7 @@ namespace Shader class ShaderVisitor : public osg::NodeVisitor { public: - ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string& defaultVsTemplate, const std::string& defaultFsTemplate); + ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string& defaultShaderPrefix); /// By default, only bump mapped objects will have a shader added to them. /// Setting force = true will cause all objects to render using shaders, regardless of having a bump map. @@ -104,8 +104,7 @@ namespace Shader }; std::vector mRequirements; - std::string mDefaultVsTemplate; - std::string mDefaultFsTemplate; + std::string mDefaultShaderPrefix; void createProgram(const ShaderRequirements& reqs); void ensureFFP(osg::Node& node); diff --git a/files/shaders/CMakeLists.txt b/files/shaders/CMakeLists.txt index 7250aa372..e06dfe56a 100644 --- a/files/shaders/CMakeLists.txt +++ b/files/shaders/CMakeLists.txt @@ -26,6 +26,10 @@ set(SHADER_FILES shadowcasting_vertex.glsl shadowcasting_fragment.glsl vertexcolors.glsl + nv_default_vertex.glsl + nv_default_fragment.glsl + nv_nolighting_vertex.glsl + nv_nolighting_fragment.glsl ) copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}") diff --git a/files/shaders/nv_default_fragment.glsl b/files/shaders/nv_default_fragment.glsl new file mode 100644 index 000000000..03fa378a6 --- /dev/null +++ b/files/shaders/nv_default_fragment.glsl @@ -0,0 +1,106 @@ +#version 120 + +#if @useGPUShader4 + #extension GL_EXT_gpu_shader4: require +#endif + +#if @diffuseMap +uniform sampler2D diffuseMap; +varying vec2 diffuseMapUV; +#endif + +#if @emissiveMap +uniform sampler2D emissiveMap; +varying vec2 emissiveMapUV; +#endif + +#if @normalMap +uniform sampler2D normalMap; +varying vec2 normalMapUV; +varying vec4 passTangent; +#endif + +uniform bool noAlpha; + +varying float euclideanDepth; +varying float linearDepth; + +#define PER_PIXEL_LIGHTING 1 + +varying vec3 passViewPos; +varying vec3 passNormal; + +#include "vertexcolors.glsl" +#include "shadows_fragment.glsl" +#include "lighting.glsl" +#include "alpha.glsl" + +uniform float emissiveMult; + +void main() +{ +#if @diffuseMap + gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV); + gl_FragData[0].a *= coveragePreservingAlphaScale(diffuseMap, adjustedDiffuseUV); +#else + gl_FragData[0] = vec4(1.0); +#endif + + vec4 diffuseColor = getDiffuseColor(); + gl_FragData[0].a *= diffuseColor.a; + alphaTest(); + +#if @normalMap + vec4 normalTex = texture2D(normalMap, normalMapUV); + // Must flip Y for DirectX format normal maps + normalTex.y = 1.0 - normalTex.y; + + vec3 normalizedNormal = normalize(passNormal); + vec3 normalizedTangent = normalize(passTangent.xyz); + vec3 binormal = cross(normalizedTangent, normalizedNormal) * passTangent.w; + mat3 tbnTranspose = mat3(normalizedTangent, binormal, normalizedNormal); + + vec3 viewNormal = gl_NormalMatrix * normalize(tbnTranspose * (normalTex.xyz * 2.0 - 1.0)); +#else + vec3 viewNormal = gl_NormalMatrix * normalize(passNormal); +#endif + + float shadowing = unshadowedLightRatio(linearDepth); + vec3 diffuseLight, ambientLight; + doLighting(passViewPos, normalize(viewNormal), shadowing, diffuseLight, ambientLight); + vec3 emission = getEmissionColor().xyz * emissiveMult; +#if @emissiveMap + emission *= texture2D(emissiveMap, emissiveMapUV).xyz; +#endif + vec3 lighting = diffuseColor.xyz * diffuseLight + getAmbientColor().xyz * ambientLight + emission; + +#if @clamp + lighting = clamp(lighting, vec3(0.0), vec3(1.0)); +#else + lighting = max(lighting, 0.0); +#endif + + gl_FragData[0].xyz *= lighting; + + float shininess = gl_FrontMaterial.shininess; + vec3 matSpec = getSpecularColor().xyz; +#if @normalMap + matSpec *= normalTex.a; +#endif + + if (matSpec != vec3(0.0)) + gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; +#if @radialFog + float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#else + float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#endif + gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); + +#if @translucentFramebuffer + if (noAlpha) + gl_FragData[0].a = 1.0; +#endif + + applyShadowDebugOverlay(); +} diff --git a/files/shaders/nv_default_vertex.glsl b/files/shaders/nv_default_vertex.glsl new file mode 100644 index 000000000..7c9d434f1 --- /dev/null +++ b/files/shaders/nv_default_vertex.glsl @@ -0,0 +1,58 @@ +#version 120 + +#if @diffuseMap +varying vec2 diffuseMapUV; +#endif + +#if @emissiveMap +varying vec2 emissiveMapUV; +#endif + +#if @normalMap +varying vec2 normalMapUV; +varying vec4 passTangent; +#endif + +varying float euclideanDepth; +varying float linearDepth; + +varying vec3 passViewPos; +varying vec3 passNormal; + +#define PER_PIXEL_LIGHTING 1 + +#include "vertexcolors.glsl" +#include "shadows_vertex.glsl" +#include "lighting.glsl" + +void main(void) +{ + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + + vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); + gl_ClipVertex = viewPos; + euclideanDepth = length(viewPos.xyz); + linearDepth = gl_Position.z; + +#if @diffuseMap + diffuseMapUV = (gl_TextureMatrix[@diffuseMapUV] * gl_MultiTexCoord@diffuseMapUV).xy; +#endif + +#if @emissiveMap + emissiveMapUV = (gl_TextureMatrix[@emissiveMapUV] * gl_MultiTexCoord@emissiveMapUV).xy; +#endif + +#if @normalMap + normalMapUV = (gl_TextureMatrix[@normalMapUV] * gl_MultiTexCoord@normalMapUV).xy; + passTangent = gl_MultiTexCoord7.xyzw; +#endif + + passColor = gl_Color; + passViewPos = viewPos.xyz; + passNormal = gl_Normal.xyz; + +#if @shadows_enabled + vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); + setupShadowCoords(viewPos, viewNormal); +#endif +} diff --git a/files/shaders/nv_nolighting_fragment.glsl b/files/shaders/nv_nolighting_fragment.glsl new file mode 100644 index 000000000..27679a069 --- /dev/null +++ b/files/shaders/nv_nolighting_fragment.glsl @@ -0,0 +1,55 @@ +#version 120 + +#if @useGPUShader4 + #extension GL_EXT_gpu_shader4: require +#endif + +#if @diffuseMap +uniform sampler2D diffuseMap; +varying vec2 diffuseMapUV; +#endif + +uniform bool noAlpha; + +#if @radialFog +varying float euclideanDepth; +#else +varying float linearDepth; +#endif + +uniform bool useFalloff; + +varying float passFalloff; + +#include "vertexcolors.glsl" +#include "alpha.glsl" + +void main() +{ +#if @diffuseMap + gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV); + gl_FragData[0].a *= coveragePreservingAlphaScale(diffuseMap, diffuseMapUV); +#else + gl_FragData[0] = vec4(1.0); +#endif + + gl_FragData[0] *= getDiffuseColor(); + + if (useFalloff) + gl_FragData[0].a *= passFalloff; + + alphaTest(); + +#if @radialFog + float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#else + float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#endif + +#if @translucentFramebuffer + if (noAlpha) + gl_FragData[0].a = 1.0; +#endif + + gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); +} diff --git a/files/shaders/nv_nolighting_vertex.glsl b/files/shaders/nv_nolighting_vertex.glsl new file mode 100644 index 000000000..275f1e573 --- /dev/null +++ b/files/shaders/nv_nolighting_vertex.glsl @@ -0,0 +1,49 @@ +#version 120 + +#if @diffuseMap +varying vec2 diffuseMapUV; +#endif + +#if @radialFog +varying float euclideanDepth; +#else +varying float linearDepth; +#endif + +uniform bool useFalloff; +uniform vec4 falloffParams; + +varying vec3 passViewPos; +varying float passFalloff; + +#include "vertexcolors.glsl" + +void main(void) +{ + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + + vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); + gl_ClipVertex = viewPos; +#if @radialFog + euclideanDepth = length(viewPos.xyz); +#else + linearDepth = gl_Position.z; +#endif + +#if @diffuseMap + diffuseMapUV = (gl_TextureMatrix[@diffuseMapUV] * gl_MultiTexCoord@diffuseMapUV).xy; +#endif + + passColor = gl_Color; + if (useFalloff) + { + vec3 viewNormal = gl_NormalMatrix * normalize(gl_Normal.xyz); + vec3 viewDir = normalize(viewPos.xyz); + float viewAngle = abs(dot(viewNormal, viewDir)); + passFalloff = smoothstep(falloffParams.y, falloffParams.x, viewAngle); + } + else + { + passFalloff = 1.0; + } +} diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index e0d7833c9..6b67be937 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -64,6 +64,8 @@ varying float linearDepth; #if !PER_PIXEL_LIGHTING centroid varying vec3 passLighting; centroid varying vec3 shadowDiffuseLighting; +#else +uniform float emissiveMult; #endif varying vec3 passViewPos; varying vec3 passNormal; @@ -168,7 +170,8 @@ void main() #else vec3 diffuseLight, ambientLight; doLighting(passViewPos, normalize(viewNormal), shadowing, diffuseLight, ambientLight); - lighting = diffuseColor.xyz * diffuseLight + getAmbientColor().xyz * ambientLight + getEmissionColor().xyz; + vec3 emission = getEmissionColor().xyz * emissiveMult; + lighting = diffuseColor.xyz * diffuseLight + getAmbientColor().xyz * ambientLight + emission; #endif #if @clamp diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index 15467933b..bf5bdb40c 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -45,6 +45,7 @@ varying float linearDepth; #if !PER_PIXEL_LIGHTING centroid varying vec3 passLighting; centroid varying vec3 shadowDiffuseLighting; +uniform float emissiveMult; #endif varying vec3 passViewPos; varying vec3 passNormal; @@ -114,7 +115,8 @@ void main(void) #if !PER_PIXEL_LIGHTING vec3 diffuseLight, ambientLight; doLighting(viewPos.xyz, viewNormal, diffuseLight, ambientLight, shadowDiffuseLighting); - passLighting = getDiffuseColor().xyz * diffuseLight + getAmbientColor().xyz * ambientLight + getEmissionColor().xyz; + vec3 emission = getEmissionColor().xyz * emissiveMult; + passLighting = getDiffuseColor().xyz * diffuseLight + getAmbientColor().xyz * ambientLight + emission; shadowDiffuseLighting *= getDiffuseColor().xyz; #endif From 580fa78034faaa6b51ca0ae6fdd631ef2e0ef69f Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Mon, 22 Mar 2021 20:43:34 +0100 Subject: [PATCH 120/132] Don't purge summon effects with invalid creature ids --- apps/openmw/mwmechanics/summoning.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index 0f699ccad..9f65f3d6c 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -145,6 +145,12 @@ namespace MWMechanics for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) { + if(it->second == -1) + { + // Keep the spell effect active if we failed to spawn anything + it++; + continue; + } MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second); if (ptr.isEmpty() || (ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished())) { From 8e9bd5c0bd78da03ed9ae8c4fb1145c306afc2c5 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Mon, 22 Mar 2021 20:44:13 +0100 Subject: [PATCH 121/132] Don't throw an exception when equipping a bound item fails --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/actors.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ac01c00f..6c36ce3eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,6 +112,7 @@ Bug #5899: Visible modal windows and dropdowns crashing game on exit Bug #5902: NiZBufferProperty is unable to disable the depth test Bug #5906: Sunglare doesn't work with Mesa drivers and AMD GPUs + Bug #5912: ImprovedBound mod doesn't work Feature #390: 3rd person look "over the shoulder" Feature #832: OpenMW-CS: Handle deleted references Feature #1536: Show more information about level on menu diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c0a137158..a1e2d2a72 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -346,7 +346,11 @@ namespace MWMechanics if (actor != MWMechanics::getPlayer()) return; - MWWorld::Ptr newItem = *store.getSlot(slot); + MWWorld::Ptr newItem; + auto it = store.getSlot(slot); + // Equip can fail because beast races cannot equip boots/helmets + if(it != store.end()) + newItem = *it; if (newItem.isEmpty() || boundPtr != newItem) return; From 313355cb3d72ba2ddd4b1557d801a0852151bf2f Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 23 Mar 2021 21:40:32 +0100 Subject: [PATCH 122/132] Fix default max tile size in navigator doc --- docs/source/reference/modding/settings/navigator.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/reference/modding/settings/navigator.rst b/docs/source/reference/modding/settings/navigator.rst index af40ac750..fcef549d0 100644 --- a/docs/source/reference/modding/settings/navigator.rst +++ b/docs/source/reference/modding/settings/navigator.rst @@ -305,7 +305,7 @@ tile size :Type: integer :Range: > 0 -:Default: 64 +:Default: 128 The width and height of each tile. From 39c0ce9ddf5d1c85c2e4cac4eb6b1f6ad980f2d0 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 23 Mar 2021 23:15:13 +0100 Subject: [PATCH 123/132] Build limited path for far destinations When distance between start and end point is greater than max radius of area possibly covered by navmesh there is no way to find path via navmesh. Also if distance is greater than cell size navmesh might not exists withing mentioned area because cell is not loaded therefore navmesh is not generated. So minumum of these values is used to limit max path distance. Assuming that path actually exists it's possible to build path to the edge of a circle. When actor reaches initial edge path is built further. However it will not be optimal. --- apps/openmw/mwmechanics/aipackage.cpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 17 +++++++++++++++++ apps/openmw/mwmechanics/pathfinding.hpp | 4 ++++ components/detournavigator/navigator.hpp | 2 ++ components/detournavigator/navigatorimpl.cpp | 6 ++++++ components/detournavigator/navigatorimpl.hpp | 2 ++ components/detournavigator/navigatorstub.hpp | 5 +++++ components/detournavigator/settingsutils.hpp | 12 +++++++++++- 8 files changed, 48 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 00e1c9c3a..214aad320 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -114,7 +114,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& if (wasShortcutting || doesPathNeedRecalc(dest, actor)) // if need to rebuild path { const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor); - mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()), + mPathFinder.buildLimitedPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()), pathfindingHalfExtents, getNavigatorFlags(actor), getAreaCosts(actor)); mRotateOnTheRunChecks = 3; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 93ae90547..781b897a7 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -442,4 +442,21 @@ namespace MWMechanics std::copy(prePath.rbegin(), prePath.rend(), std::front_inserter(mPath)); } + + void PathFinder::buildLimitedPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, + const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, + const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts) + { + const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); + const auto maxDistance = std::min( + navigator->getMaxNavmeshAreaRealRadius(), + static_cast(Constants::CellSizeInUnits) + ); + const auto startToEnd = endPoint - startPoint; + const auto distance = startToEnd.length(); + if (distance <= maxDistance) + return buildPath(actor, startPoint, endPoint, cell, pathgridGraph, halfExtents, flags, areaCosts); + const auto end = startPoint + startToEnd * maxDistance / distance; + buildPath(actor, startPoint, end, cell, pathgridGraph, halfExtents, flags, areaCosts); + } } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index b5c376b8c..ed88a57ca 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -101,6 +101,10 @@ namespace MWMechanics void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts); + void buildLimitedPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, + const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, + const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts); + /// Remove front point if exist and within tolerance void update(const osg::Vec3f& position, float pointTolerance, float destinationTolerance, bool shortenIfAlmostStraight, bool canMoveByZ); diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index ef61f78c6..d08bfa640 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -235,6 +235,8 @@ namespace DetourNavigator const osg::Vec3f& end, const Flags includeFlags) const; virtual RecastMeshTiles getRecastMeshTiles() = 0; + + virtual float getMaxNavmeshAreaRealRadius() const = 0; }; } diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 142ba590d..abfb20ba8 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -213,4 +213,10 @@ namespace DetourNavigator ++it; } } + + float NavigatorImpl::getMaxNavmeshAreaRealRadius() const + { + const auto& settings = getSettings(); + return getRealTileSize(settings) * getMaxNavmeshAreaRadius(settings); + } } diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index e197c71b7..74fff0dea 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -60,6 +60,8 @@ namespace DetourNavigator RecastMeshTiles getRecastMeshTiles() override; + float getMaxNavmeshAreaRealRadius() const override; + private: Settings mSettings; NavMeshManager mNavMeshManager; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index f1f9e06ef..f6892bf1b 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -92,6 +92,11 @@ namespace DetourNavigator return {}; } + float getMaxNavmeshAreaRealRadius() const override + { + return std::numeric_limits::max(); + } + private: Settings mDefaultSettings {}; SharedNavMeshCacheItem mEmptyNavMeshCacheItem; diff --git a/components/detournavigator/settingsutils.hpp b/components/detournavigator/settingsutils.hpp index 39ffc03d1..8f1c96e28 100644 --- a/components/detournavigator/settingsutils.hpp +++ b/components/detournavigator/settingsutils.hpp @@ -1,4 +1,4 @@ -#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGSUTILS_H +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGSUTILS_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGSUTILS_H #include "settings.hpp" @@ -89,6 +89,16 @@ namespace DetourNavigator transform.getOrigin() + btVector3(0, 0, getSwimLevel(settings, agentHalfExtentsZ) - agentHalfExtentsZ) ); } + + inline float getRealTileSize(const Settings& settings) + { + return settings.mTileSize * settings.mCellSize / settings.mRecastScaleFactor; + } + + inline float getMaxNavmeshAreaRadius(const Settings& settings) + { + return std::floor(std::sqrt(settings.mMaxTilesNumber / osg::PI)) - 1; + } } #endif From f32e1790bcdf1b85e655d0a8ae34a45aa463ed4d Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 23 Mar 2021 23:48:11 +0100 Subject: [PATCH 124/132] Add half extents to AiEscord max distance For actors with big bounding box given constants may not work properly like it's not possible to get close enough to actor from a given angle to make it move. --- apps/openmw/mwmechanics/aiescort.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 5dc1e44db..8c5230221 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -82,14 +82,16 @@ namespace MWMechanics mRemainingDuration = mDuration; return true; } - mMaxDist = 450; + const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); + mMaxDist = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())) + 450.0f; } else { // Stop moving if the player is too far away MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); actor.getClass().getMovementSettings(actor).mPosition[1] = 0; - mMaxDist = 250; + const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); + mMaxDist = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())) + 250.0f; } return false; From 453e94ea9fa31a0949b89b52edba4516586fc2ba Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 23 Mar 2021 23:59:45 +0100 Subject: [PATCH 125/132] Use half extents for destination distance tolerance in AiEscort For actors moving in water destination may be located at such z coordinate that they can't reach. --- apps/openmw/mwmechanics/aiescort.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 8c5230221..75c046110 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -73,25 +73,25 @@ namespace MWMechanics const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mTargetActorRefId, false); const osg::Vec3f leaderPos = actor.getRefData().getPosition().asVec3(); const osg::Vec3f followerPos = follower.getRefData().getPosition().asVec3(); + const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); + const float maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())); if ((leaderPos - followerPos).length2() <= mMaxDist * mMaxDist) { const osg::Vec3f dest(mX, mY, mZ); - if (pathTo(actor, dest, duration)) //Returns true on path complete + if (pathTo(actor, dest, duration, maxHalfExtent)) //Returns true on path complete { mRemainingDuration = mDuration; return true; } - const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); - mMaxDist = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())) + 450.0f; + mMaxDist = maxHalfExtent + 450.0f; } else { // Stop moving if the player is too far away MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); actor.getClass().getMovementSettings(actor).mPosition[1] = 0; - const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); - mMaxDist = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())) + 250.0f; + mMaxDist = maxHalfExtent + 250.0f; } return false; From e8c2cd15f09bc23bfa6d639bfd59e90ca5302d18 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 24 Mar 2021 00:03:22 +0100 Subject: [PATCH 126/132] Add #5914 to the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c36ce3eb..f8d3a65f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,7 @@ Bug #5902: NiZBufferProperty is unable to disable the depth test Bug #5906: Sunglare doesn't work with Mesa drivers and AMD GPUs Bug #5912: ImprovedBound mod doesn't work + Bug #5914: BM: The Swimmer can't reach destination Feature #390: 3rd person look "over the shoulder" Feature #832: OpenMW-CS: Handle deleted references Feature #1536: Show more information about level on menu From cf52bee188b1d16e524a17b80dd870a8b94d6d56 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 24 Mar 2021 16:32:15 +0400 Subject: [PATCH 127/132] Fix build with OSG 3.4 --- components/shader/shadervisitor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 1af9f94c5..612c9011d 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include From 91bca0cb1fbcf4ed08acb9dfdfedde63a7bdcf06 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 24 Mar 2021 13:28:34 -0700 Subject: [PATCH 128/132] attempt to fix build issue --- CI/before_script.msvc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index b2b6483ec..93d995c99 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -849,8 +849,8 @@ fi fi # check version - pip list | grep 'aqtinstall\s*1.1.3' - if [ $? -ne 0 ]; then + aqt-venv/${VENV_BIN_DIR}/pip list | grep 'aqtinstall\s*1.1.3' || [[ $? -ne 0 ]] + if [[ $? -eq 0 ]]; then echo " Installing aqt wheel into virtualenv..." run_cmd "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall==1.1.3 fi From 72a2e3722ef0d9c273679df8ee4e5d85afa0245b Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 24 Mar 2021 14:01:40 -0700 Subject: [PATCH 129/132] update syntax --- CI/before_script.msvc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 93d995c99..e1afdb059 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -849,8 +849,8 @@ fi fi # check version - aqt-venv/${VENV_BIN_DIR}/pip list | grep 'aqtinstall\s*1.1.3' || [[ $? -ne 0 ]] - if [[ $? -eq 0 ]]; then + aqt-venv/${VENV_BIN_DIR}/pip list | grep 'aqtinstall\s*1.1.3' || [ $? -ne 0 ] + if [ $? -eq 0 ]; then echo " Installing aqt wheel into virtualenv..." run_cmd "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall==1.1.3 fi From e56efdd562eb9eefcce5ab536373f219ceb55b64 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 23 Mar 2021 15:52:37 -0700 Subject: [PATCH 130/132] change aim calculation --- apps/openmw/mwworld/worldimp.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 01b90e907..e5fa7322c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3867,11 +3867,14 @@ namespace MWWorld return false; } - osg::Vec3f World::aimToTarget(const ConstPtr &actor, const MWWorld::ConstPtr& target) + osg::Vec3f World::aimToTarget(const ConstPtr &actor, const ConstPtr &target) { osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3(); - weaponPos.z() += mPhysics->getHalfExtents(actor).z(); - osg::Vec3f targetPos = mPhysics->getCollisionObjectPosition(target); + osg::Vec3f weaponHalfExtents = mPhysics->getHalfExtents(actor); + osg::Vec3f targetPos = target.getRefData().getPosition().asVec3(); + osg::Vec3f targetHalfExtents = mPhysics->getHalfExtents(target); + weaponPos.z() += weaponHalfExtents.z() * 2 * 0.75; // projectilemanager.cpp spawns bolts at 0.75 actor height + targetPos.z() += targetHalfExtents.z(); return (targetPos - weaponPos); } From 2cd96e56d5c6f5359f4f2e45d0b5e37572c7fc3e Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 24 Mar 2021 10:21:37 -0700 Subject: [PATCH 131/132] create constant and use constant in other parts of the code base --- AUTHORS.md | 1 + CHANGELOG.md | 1 + apps/openmw/mwmechanics/aicast.cpp | 6 ++++-- apps/openmw/mwworld/projectilemanager.cpp | 3 +-- apps/openmw/mwworld/worldimp.cpp | 2 +- components/misc/constants.hpp | 3 +++ 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 53efdd286..b30168fd8 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -131,6 +131,7 @@ Programmers Martin Otto (MAtahualpa) Mateusz Kołaczek (PL_kolek) Mateusz Malisz (malice) + Max Henzerling (SaintMercury) megaton Michael Hogan (Xethik) Michael Mc Donnell diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c36ce3eb..fe5364312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,7 @@ Bug #5656: Sneaking characters block hits while standing Bug #5661: Region sounds don't play at the right interval Bug #5675: OpenMW-cs. FRMR subrecords are saved with the wrong MastIdx + Bug #5680: Enemy AI incorrectly aims projectiles Bug #5681: Player character can clip or pass through bridges instead of colliding against them Bug #5687: Bound items covering the same inventory slot expiring at the same time freezes the game Bug #5688: Water shader broken indoors with enable indoor shadows = false diff --git a/apps/openmw/mwmechanics/aicast.cpp b/apps/openmw/mwmechanics/aicast.cpp index af3aac340..630c04a6a 100644 --- a/apps/openmw/mwmechanics/aicast.cpp +++ b/apps/openmw/mwmechanics/aicast.cpp @@ -1,5 +1,7 @@ #include "aicast.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" @@ -54,12 +56,12 @@ bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::Charac if (target != actor && target.getClass().isActor()) { osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(target); - targetPos.z() += halfExtents.z() * 2 * 0.75f; + targetPos.z() += halfExtents.z() * 2 * Constants::TorsoHeight; } osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3(); osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); - actorPos.z() += halfExtents.z() * 2 * 0.75f; + actorPos.z() += halfExtents.z() * 2 * Constants::TorsoHeight; osg::Vec3f dir = targetPos - actorPos; diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index f483905dd..b463fc09e 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -265,9 +265,8 @@ namespace MWWorld osg::Vec3f pos = caster.getRefData().getPosition().asVec3(); if (caster.getClass().isActor()) { - // Spawn at 0.75 * ActorHeight // Note: we ignore the collision box offset, this is required to make some flying creatures work as intended. - pos.z() += mPhysics->getRenderingHalfExtents(caster).z() * 2 * 0.75; + pos.z() += mPhysics->getRenderingHalfExtents(caster).z() * 2 * Constants::TorsoHeight; } if (MWBase::Environment::get().getWorld()->isUnderwater(caster.getCell(), pos)) // Underwater casting not possible diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e5fa7322c..b37d326f0 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3873,7 +3873,7 @@ namespace MWWorld osg::Vec3f weaponHalfExtents = mPhysics->getHalfExtents(actor); osg::Vec3f targetPos = target.getRefData().getPosition().asVec3(); osg::Vec3f targetHalfExtents = mPhysics->getHalfExtents(target); - weaponPos.z() += weaponHalfExtents.z() * 2 * 0.75; // projectilemanager.cpp spawns bolts at 0.75 actor height + weaponPos.z() += weaponHalfExtents.z() * 2 * Constants::TorsoHeight; targetPos.z() += targetHalfExtents.z(); return (targetPos - weaponPos); } diff --git a/components/misc/constants.hpp b/components/misc/constants.hpp index 1053b1c56..bfd3933fc 100644 --- a/components/misc/constants.hpp +++ b/components/misc/constants.hpp @@ -33,6 +33,9 @@ const std::string NightDayLabel = "NightDaySwitch"; // A label to mark visual switches for herbalism feature const std::string HerbalismLabel = "HerbalismSwitch"; +// Percentage height at which projectiles are spawned from an actor +const float TorsoHeight = 0.75f; + } #endif From dc09616e59a2b81f6fa9d44708f47cbf09757def Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 24 Mar 2021 11:15:09 -0700 Subject: [PATCH 132/132] change bugfix name to be same as ticket name --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe5364312..d0217d835 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,7 +88,7 @@ Bug #5656: Sneaking characters block hits while standing Bug #5661: Region sounds don't play at the right interval Bug #5675: OpenMW-cs. FRMR subrecords are saved with the wrong MastIdx - Bug #5680: Enemy AI incorrectly aims projectiles + Bug #5680: Bull Netches incorrectly aim over the player character's head and always miss Bug #5681: Player character can clip or pass through bridges instead of colliding against them Bug #5687: Bound items covering the same inventory slot expiring at the same time freezes the game Bug #5688: Water shader broken indoors with enable indoor shadows = false