From ce2bcba5d41631a63ae24d0491578f0ddf187cd2 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 16 Dec 2020 23:44:15 +0000 Subject: [PATCH 001/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] #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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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/116] 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 becccf3b5e30cff03a358295cf6dd1f547542022 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 10 Feb 2021 22:34:15 +0100 Subject: [PATCH 026/116] Make actor flee from a combat when cannot reach a target --- apps/openmw/mwmechanics/aicombat.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index b98d5ef49..4b0128119 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -262,6 +262,25 @@ namespace MWMechanics // start new attack storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat); } + else if (!isRangedCombat && !mPathFinder.isPathConstructed() && storage.mCurrentAction->isAttackingOrSpell()) + { + const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); + const osg::Vec3f destination = target.getRefData().getPosition().asVec3(); + const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); + mPathFinder.buildPath(actor, position, destination, actor.getCell(), getPathGridGraph(actor.getCell()), + halfExtents, getNavigatorFlags(actor), getAreaCosts(actor)); + + if (!mPathFinder.isPathConstructed()) + { + storage.stopAttack(); + characterController.setAttackingOrSpell(false); + currentAction.reset(new ActionFlee()); + actionCooldown = currentAction->getActionCooldown(); + storage.startFleeing(); + MWBase::Environment::get().getDialogueManager()->say(actor, "flee"); + } + } + return false; } From 8dba61f7aed8072edac0755454e0df7edd269531 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 14 Feb 2021 04:07:08 +0100 Subject: [PATCH 027/116] Use navmesh raycast to find reachable position around target --- apps/openmw/mwmechanics/aicombat.cpp | 80 ++++++++++++++----- apps/openmw/mwmechanics/aicombat.hpp | 7 +- .../detournavigator/navigator.cpp | 22 +++++ components/CMakeLists.txt | 1 + components/detournavigator/navigator.cpp | 16 ++++ components/detournavigator/navigator.hpp | 11 +++ components/detournavigator/raycast.cpp | 44 ++++++++++ components/detournavigator/raycast.hpp | 19 +++++ 8 files changed, 178 insertions(+), 22 deletions(-) create mode 100644 components/detournavigator/raycast.cpp create mode 100644 components/detournavigator/raycast.hpp diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 4b0128119..58a908672 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "../mwphysics/collisiontype.hpp" @@ -127,10 +128,11 @@ namespace MWMechanics { //Update every frame. UpdateLOS uses a timer, so the LOS check does not happen every frame. updateLOS(actor, target, duration, storage); - float targetReachedTolerance = 0.0f; - if (storage.mLOS) - targetReachedTolerance = storage.mAttackRange; - const bool is_target_reached = pathTo(actor, target.getRefData().getPosition().asVec3(), duration, targetReachedTolerance); + const float targetReachedTolerance = storage.mLOS && !storage.mUseCustomDestination + ? storage.mAttackRange : 0.0f; + const osg::Vec3f destination = storage.mUseCustomDestination + ? storage.mCustomDestination : target.getRefData().getPosition().asVec3(); + const bool is_target_reached = pathTo(actor, destination, duration, targetReachedTolerance); if (is_target_reached) storage.mReadyToAttack = true; } @@ -232,8 +234,8 @@ namespace MWMechanics const ESM::Weapon* weapon = currentAction->getWeapon(); ESM::Position pos = actor.getRefData().getPosition(); - osg::Vec3f vActorPos(pos.asVec3()); - osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3()); + const osg::Vec3f vActorPos(pos.asVec3()); + const osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3()); osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target); float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target); @@ -243,9 +245,7 @@ namespace MWMechanics if (isRangedCombat) { // rotate actor taking into account target movement direction and projectile speed - osg::Vec3f& lastTargetPos = storage.mLastTargetPos; - vAimDir = AimDirToMovingTarget(actor, target, lastTargetPos, AI_REACTION_TIME, (weapon ? weapon->mData.mType : 0), storage.mStrength); - lastTargetPos = vTargetPos; + vAimDir = AimDirToMovingTarget(actor, target, storage.mLastTargetPos, AI_REACTION_TIME, (weapon ? weapon->mData.mType : 0), storage.mStrength); storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir); storage.mMovement.mRotation[2] = getZAngleToDir(vAimDir); @@ -256,28 +256,66 @@ namespace MWMechanics storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated } + storage.mLastTargetPos = vTargetPos; + if (storage.mReadyToAttack) { storage.startCombatMove(isRangedCombat, distToTarget, rangeAttack, actor, target); // start new attack storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat); } - else if (!isRangedCombat && !mPathFinder.isPathConstructed() && storage.mCurrentAction->isAttackingOrSpell()) + + // If actor uses custom destination it has to try to rebuild path because environment can change + // (door is opened between actor and target) or target position has changed and current custom destination + // is not good enough to attack target. + if (storage.mCurrentAction->isAttackingOrSpell() + && ((!storage.mReadyToAttack && !mPathFinder.isPathConstructed()) + || (storage.mUseCustomDestination && (storage.mCustomDestination - vTargetPos).length() > rangeAttack))) { - const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); - const osg::Vec3f destination = target.getRefData().getPosition().asVec3(); - const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); - mPathFinder.buildPath(actor, position, destination, actor.getCell(), getPathGridGraph(actor.getCell()), - halfExtents, getNavigatorFlags(actor), getAreaCosts(actor)); + // Try to build path to the target. + const auto halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); + const auto navigatorFlags = getNavigatorFlags(actor); + const auto areaCosts = getAreaCosts(actor); + const auto pathGridGraph = getPathGridGraph(actor.getCell()); + mPathFinder.buildPath(actor, vActorPos, vTargetPos, actor.getCell(), pathGridGraph, halfExtents, navigatorFlags, areaCosts); if (!mPathFinder.isPathConstructed()) { - storage.stopAttack(); - characterController.setAttackingOrSpell(false); - currentAction.reset(new ActionFlee()); - actionCooldown = currentAction->getActionCooldown(); - storage.startFleeing(); - MWBase::Environment::get().getDialogueManager()->say(actor, "flee"); + // If there is no path, try to find a point on a line from the actor position to target projected + // on navmesh to attack the target from there. + const MWBase::World* world = MWBase::Environment::get().getWorld(); + const auto halfExtents = world->getPathfindingHalfExtents(actor); + const auto navigator = world->getNavigator(); + const auto navigatorFlags = getNavigatorFlags(actor); + const auto areaCosts = getAreaCosts(actor); + const auto hit = navigator->raycast(halfExtents, vActorPos, vTargetPos, navigatorFlags); + + if (hit.has_value() && (*hit - vTargetPos).length() <= rangeAttack) + { + // If the point is close enough, try to find a path to that point. + mPathFinder.buildPath(actor, vActorPos, *hit, actor.getCell(), pathGridGraph, halfExtents, navigatorFlags, areaCosts); + if (mPathFinder.isPathConstructed()) + { + // If path to that point is found use it as custom destination. + storage.mCustomDestination = *hit; + storage.mUseCustomDestination = true; + } + } + + if (!mPathFinder.isPathConstructed()) + { + storage.mUseCustomDestination = false; + storage.stopAttack(); + characterController.setAttackingOrSpell(false); + currentAction.reset(new ActionFlee()); + actionCooldown = currentAction->getActionCooldown(); + storage.startFleeing(); + MWBase::Environment::get().getDialogueManager()->say(actor, "flee"); + } + } + else + { + storage.mUseCustomDestination = false; } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 64645ca94..3a77aa8e8 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -55,6 +55,9 @@ namespace MWMechanics float mFleeBlindRunTimer; ESM::Pathgrid::Point mFleeDest; + bool mUseCustomDestination; + osg::Vec3f mCustomDestination; + AiCombatStorage(): mAttackCooldown(0.0f), mTimerReact(AI_REACTION_TIME), @@ -74,7 +77,9 @@ namespace MWMechanics mFleeState(FleeState_None), mLOS(false), mUpdateLOSTimer(0.0f), - mFleeBlindRunTimer(0.0f) + mFleeBlindRunTimer(0.0f), + mUseCustomDestination(false), + mCustomDestination() {} void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target); diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index ae345d187..c6d093d5f 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -802,4 +802,26 @@ namespace EXPECT_GT(duration, mSettings.mMinUpdateInterval) << std::chrono::duration_cast>(duration).count() << " ms"; } + + TEST_F(DetourNavigatorNavigatorTest, update_then_raycast_should_return_position) + { + const std::array heightfieldData {{ + 0, 0, 0, 0, 0, + 0, -25, -25, -25, -25, + 0, -25, -100, -100, -100, + 0, -25, -100, -100, -100, + 0, -25, -100, -100, -100, + }}; + btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + shape.setLocalScaling(btVector3(128, 128, 1)); + + mNavigator->addAgent(mAgentHalfExtents); + mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); + mNavigator->update(mPlayerPosition); + mNavigator->wait(); + + const auto result = mNavigator->raycast(mAgentHalfExtents, mStart, mEnd, Flag_walk); + + ASSERT_THAT(result, Optional(Vec3fEq(mEnd.x(), mEnd.y(), 1.87719))); + } } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 832fc611f..44813180e 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -181,6 +181,7 @@ add_component_dir(detournavigator settings navigator findrandompointaroundcircle + raycast ) set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui diff --git a/components/detournavigator/navigator.cpp b/components/detournavigator/navigator.cpp index 658e539ad..700217c52 100644 --- a/components/detournavigator/navigator.cpp +++ b/components/detournavigator/navigator.cpp @@ -1,5 +1,6 @@ #include "findrandompointaroundcircle.hpp" #include "navigator.hpp" +#include "raycast.hpp" namespace DetourNavigator { @@ -17,4 +18,19 @@ namespace DetourNavigator return std::optional(); return std::optional(fromNavMeshCoordinates(settings, *result)); } + + std::optional Navigator::raycast(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start, + const osg::Vec3f& end, const Flags includeFlags) const + { + const auto navMesh = getNavMesh(agentHalfExtents); + if (navMesh == nullptr) + return {}; + const auto settings = getSettings(); + const auto result = DetourNavigator::raycast(navMesh->lockConst()->getImpl(), + toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, start), + toNavMeshCoordinates(settings, end), includeFlags, settings); + if (!result) + return {}; + return fromNavMeshCoordinates(settings, *result); + } } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index a79aa59d4..ef61f78c6 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -223,6 +223,17 @@ namespace DetourNavigator std::optional findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const; + /** + * @brief raycast finds farest navmesh point from start on a line from start to end that has path from start. + * @param agentHalfExtents allows to find navmesh for given actor. + * @param start of the line + * @param end of the line + * @param includeFlags setup allowed surfaces for actor to walk. + * @return not empty optional with position if point is found and empty optional if point is not found. + */ + std::optional raycast(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start, + const osg::Vec3f& end, const Flags includeFlags) const; + virtual RecastMeshTiles getRecastMeshTiles() = 0; }; } diff --git a/components/detournavigator/raycast.cpp b/components/detournavigator/raycast.cpp new file mode 100644 index 000000000..86fabe9c1 --- /dev/null +++ b/components/detournavigator/raycast.cpp @@ -0,0 +1,44 @@ +#include "raycast.hpp" +#include "settings.hpp" +#include "findsmoothpath.hpp" + +#include +#include +#include + +#include + +namespace DetourNavigator +{ + std::optional raycast(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, + const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const Settings& settings) + { + dtNavMeshQuery navMeshQuery; + if (!initNavMeshQuery(navMeshQuery, navMesh, settings.mMaxNavMeshQueryNodes)) + return {}; + + dtQueryFilter queryFilter; + queryFilter.setIncludeFlags(includeFlags); + + dtPolyRef ref = 0; + if (dtStatus status = navMeshQuery.findNearestPoly(start.ptr(), halfExtents.ptr(), &queryFilter, &ref, nullptr); + dtStatusFailed(status) || ref == 0) + return {}; + + const unsigned options = 0; + std::array path; + dtRaycastHit hit; + hit.path = path.data(); + hit.maxPath = path.size(); + if (dtStatus status = navMeshQuery.raycast(ref, start.ptr(), end.ptr(), &queryFilter, options, &hit); + dtStatusFailed(status) || hit.pathCount == 0) + return {}; + + osg::Vec3f hitPosition; + if (dtStatus status = navMeshQuery.closestPointOnPoly(path[hit.pathCount - 1], end.ptr(), hitPosition.ptr(), nullptr); + dtStatusFailed(status)) + return {}; + + return hitPosition; + } +} diff --git a/components/detournavigator/raycast.hpp b/components/detournavigator/raycast.hpp new file mode 100644 index 000000000..ddf61b49f --- /dev/null +++ b/components/detournavigator/raycast.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RAYCAST_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RAYCAST_H + +#include "flags.hpp" + +#include +#include + +class dtNavMesh; + +namespace DetourNavigator +{ + struct Settings; + + std::optional raycast(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, + const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const Settings& settings); +} + +#endif From b8ee32e3173c9226d92cf0d9f60a07fe1c75a479 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 14 Feb 2021 23:32:04 +0000 Subject: [PATCH 028/116] 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 029/116] 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 030/116] 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 031/116] 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 b7535ad9fb1022206a5e4b09d640ea7b5ea412ae Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 25 Feb 2021 11:33:48 +0400 Subject: [PATCH 032/116] Remove outdated entries from gitignore --- .gitignore | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.gitignore b/.gitignore index 8112f683a..1905957d9 100644 --- a/.gitignore +++ b/.gitignore @@ -84,13 +84,3 @@ moc_*.cxx *.[ao] *.so venv/ - -## recastnavigation unused files -extern/recastnavigation/.travis.yml -extern/recastnavigation/CONTRIBUTING.md -extern/recastnavigation/Docs/ -extern/recastnavigation/Doxyfile -extern/recastnavigation/README.md -extern/recastnavigation/RecastDemo/ -extern/recastnavigation/Tests/ -extern/recastnavigation/appveyor.yml From a616af822eee470a823529342f46db77caf3e8df Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 25 Feb 2021 19:33:11 +0100 Subject: [PATCH 033/116] Fix Ctrl U/W behaviour to work with unicode text --- apps/openmw/mwgui/console.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 89ba5d388..d0d3b87a6 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -249,7 +249,7 @@ namespace MWGui size_t length = mCommandLine->getTextCursor() - max; if(length > 0) { - std::string text = caption; + auto text = caption; text.erase(max, length); mCommandLine->setCaption(text); mCommandLine->setTextCursor(max); @@ -259,7 +259,7 @@ namespace MWGui { if(mCommandLine->getTextCursor() > 0) { - std::string text = mCommandLine->getCaption(); + auto text = mCommandLine->getCaption(); text.erase(0, mCommandLine->getTextCursor()); mCommandLine->setCaption(text); mCommandLine->setTextCursor(0); From 6e0f070cd1c8cd3b9aa0a9fdf7acd4093b0ec15c Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 25 Feb 2021 21:37:21 +0100 Subject: [PATCH 034/116] Don't assume unmapped keycodes outside the extended ASCII range are unprintable --- apps/openmw/mwinput/keyboardmanager.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index db047a342..854085846 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -39,12 +39,14 @@ namespace MWInput && MWBase::Environment::get().getWindowManager()->isConsoleMode()) SDL_StopTextInput(); - bool consumed = false; + bool consumed = SDL_IsTextInputActive() && // Little trick to check if key is printable + (!(SDLK_SCANCODE_MASK & arg.keysym.sym) && + (std::isprint(arg.keysym.sym) || + // Don't trust isprint for symbols outside the extended ASCII range + (kc == MyGUI::KeyCode::None && arg.keysym.sym > 0xff))); if (kc != MyGUI::KeyCode::None && !mBindingsManager->isDetectingBindingState()) { - consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(kc, 0, arg.repeat); - if (SDL_IsTextInputActive() && // Little trick to check if key is printable - (!(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym))) + if (MWBase::Environment::get().getWindowManager()->injectKeyPress(kc, 0, arg.repeat)) consumed = true; mBindingsManager->setPlayerControlsEnabled(!consumed); } From 1a423860b553de9853d98d890d92e31ad236a0d2 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 25 Feb 2021 21:45:15 +0100 Subject: [PATCH 035/116] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index acfdc9e50..a46f47f53 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 #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 Feature #2386: Distant Statics in the form of Object Paging From 13c5b1b0c54a66ed07d18d527cd8e48bab9f3e47 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Fri, 26 Feb 2021 10:45:32 +0000 Subject: [PATCH 036/116] CMake: Fix support for OSG static lib paths `$` fails if the argument is already a path (instead of a library name). This happens when a static library is found via `osg_find_library`. If the argument contains `/` or `.`, do not use `$` --- apps/opencs/CMakeLists.txt | 6 +++++- apps/openmw/CMakeLists.txt | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 17b3375f5..8be1fa4d3 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -233,7 +233,11 @@ if(OSG_STATIC) add_library(openmw_cs_osg_plugins INTERFACE) foreach(_plugin ${USED_OSG_PLUGINS}) string(TOUPPER ${_plugin} _plugin_uc) - list(APPEND _osg_plugins_static_files $) + if (${_plugin_uc}_LIBRARY MATCHES "[/.]") + list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY}) + else() + list(APPEND _osg_plugins_static_files $) + endif() target_link_libraries(openmw_cs_osg_plugins INTERFACE ${${_plugin_uc}_LIBRARY}) endforeach() # We use --whole-archive because OSG plugins use registration. diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 1c11825ef..dda517c44 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -148,7 +148,11 @@ if(OSG_STATIC) add_library(openmw_osg_plugins INTERFACE) foreach(_plugin ${USED_OSG_PLUGINS}) string(TOUPPER ${_plugin} _plugin_uc) - list(APPEND _osg_plugins_static_files $) + if (${_plugin_uc}_LIBRARY MATCHES "[/.]") + list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY}) + else() + list(APPEND _osg_plugins_static_files $) + endif() target_link_libraries(openmw_osg_plugins INTERFACE ${${_plugin_uc}_LIBRARY}) endforeach() # We use --whole-archive because OSG plugins use registration. From 21a70b7d2b6d3d7697e847045fff39034d341168 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Fri, 26 Feb 2021 09:54:02 +0000 Subject: [PATCH 037/116] heighfield: buildAccellerator() This enables accelleration of heightfield collisions. Unfortunately, `btHeightfieldTerrainShape::processAllTriangle` does not yet use the accellerator data, so this change does not improve performance yet but might do so in future bullet versions. References: * Accellerator introduced in: https://github.com/bulletphysics/bullet3/pull/2062 * Feature request to use the accellerator in `processAllTriangle`: https://github.com/bulletphysics/bullet3/issues/3276 --- apps/openmw/mwphysics/heightfield.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwphysics/heightfield.cpp b/apps/openmw/mwphysics/heightfield.cpp index 436cdfe8f..cbcc9d024 100644 --- a/apps/openmw/mwphysics/heightfield.cpp +++ b/apps/openmw/mwphysics/heightfield.cpp @@ -58,6 +58,12 @@ namespace MWPhysics mShape->setUseDiamondSubdivision(true); mShape->setLocalScaling(btVector3(triSize, triSize, 1)); + // Enables acceleration of heighfield collissions. + // + // Bullet does not yet use this in the most time-consuming method, `btHeightfieldTerrainShape::processAllTriangle`. + // See https://github.com/bulletphysics/bullet3/issues/3276 + mShape->buildAccelerator(); + btTransform transform(btQuaternion::getIdentity(), btVector3((x+0.5f) * triSize * (sqrtVerts-1), (y+0.5f) * triSize * (sqrtVerts-1), From f5a87ee46def4892ee736790fced2435cfe63dd4 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 26 Feb 2021 19:01:27 +0000 Subject: [PATCH 038/116] 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 16e8cf89afd9a146ba3bdb345f3c46fe37e4b006 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 26 Feb 2021 20:10:03 +0000 Subject: [PATCH 039/116] Fix compilation of tests with MSVC --- components/bullethelpers/operators.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/bullethelpers/operators.hpp b/components/bullethelpers/operators.hpp index dd2ec8017..9250563ae 100644 --- a/components/bullethelpers/operators.hpp +++ b/components/bullethelpers/operators.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include From 46a1715d8a63230c04755a39c34cddae8f6e6fb2 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 26 Feb 2021 20:10:58 +0000 Subject: [PATCH 040/116] 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 041/116] 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 fdca76ce86f54e899e40f205b8d88b782062ab38 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 28 Feb 2021 20:42:23 +0100 Subject: [PATCH 042/116] Remove unused includes --- .../detournavigator/tilecachedrecastmeshmanager.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index eac3c024f..be5209001 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -4,10 +4,6 @@ #include #include -#include -#include -#include -#include #include #include From fac57597481ff8cb11559f49f04430a3ad3c7f0d Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 28 Feb 2021 20:42:10 +0100 Subject: [PATCH 043/116] Set bounding min and max height for btHeightfieldTerrainShape They must be set for proper AABB based filtering. Use +-max(abs(min), abs(max)) to make sure bullet does not shift coordinates by z. --- .../detournavigator/navigator.cpp | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index c6d093d5f..d377aed1e 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -76,6 +76,17 @@ namespace } }; + template + btHeightfieldTerrainShape makeSquareHeightfieldTerrainShape(const std::array& values, + btScalar heightScale = 1, int upAxis = 2, PHY_ScalarType heightDataType = PHY_FLOAT, bool flipQuadEdges = false) + { + const int width = static_cast(std::sqrt(size)); + const btScalar min = *std::min_element(values.begin(), values.end()); + const btScalar max = *std::max_element(values.begin(), values.end()); + const btScalar greater = std::max(std::abs(min), std::abs(max)); + return btHeightfieldTerrainShape(width, width, values.data(), heightScale, -greater, greater, upAxis, heightDataType, flipQuadEdges); + } + TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty) { EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), @@ -108,7 +119,7 @@ namespace 0, -25, -100, -100, -100, 0, -25, -100, -100, -100, }}; - btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); shape.setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentHalfExtents); @@ -154,7 +165,7 @@ namespace 0, -25, -100, -100, -100, 0, -25, -100, -100, -100, }}; - btHeightfieldTerrainShape heightfieldShape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape heightfieldShape = makeSquareHeightfieldTerrainShape(heightfieldData); heightfieldShape.setLocalScaling(btVector3(128, 128, 1)); btBoxShape boxShape(btVector3(20, 20, 100)); @@ -238,7 +249,7 @@ namespace 0, -25, -100, -100, -100, 0, -25, -100, -100, -100, }}; - btHeightfieldTerrainShape heightfieldShape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape heightfieldShape = makeSquareHeightfieldTerrainShape(heightfieldData); heightfieldShape.setLocalScaling(btVector3(128, 128, 1)); btBoxShape boxShape(btVector3(20, 20, 100)); @@ -325,7 +336,7 @@ namespace 0, -25, -100, -100, -100, 0, -25, -100, -100, -100, }}; - btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); shape.setLocalScaling(btVector3(128, 128, 1)); const std::array heightfieldData2 {{ @@ -335,7 +346,7 @@ namespace -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, }}; - btHeightfieldTerrainShape shape2(5, 5, heightfieldData2.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shape2 = makeSquareHeightfieldTerrainShape(heightfieldData2); shape2.setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentHalfExtents); @@ -382,7 +393,7 @@ namespace 0, -25, -100, -100, -100, 0, -25, -100, -100, -100, }}; - btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); shape.setLocalScaling(btVector3(128, 128, 1)); std::array heightfieldDataAvoid {{ @@ -392,7 +403,7 @@ namespace -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, }}; - btHeightfieldTerrainShape shapeAvoid(5, 5, heightfieldDataAvoid.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shapeAvoid = makeSquareHeightfieldTerrainShape(heightfieldDataAvoid); shapeAvoid.setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentHalfExtents); @@ -439,7 +450,7 @@ namespace -50, -100, -150, -100, -100, 0, -50, -100, -100, -100, }}; - btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); shape.setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentHalfExtents); @@ -487,7 +498,7 @@ namespace 0, -100, -100, -100, -100, -100, 0, 0, 0, 0, 0, 0, 0, 0, }}; - btHeightfieldTerrainShape shape(7, 7, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); shape.setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentHalfExtents); @@ -534,7 +545,7 @@ namespace 0, -100, -100, -100, -100, -100, 0, 0, 0, 0, 0, 0, 0, 0, }}; - btHeightfieldTerrainShape shape(7, 7, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); shape.setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentHalfExtents); @@ -581,7 +592,7 @@ namespace 0, -100, -100, -100, -100, -100, 0, 0, 0, 0, 0, 0, 0, 0, }}; - btHeightfieldTerrainShape shape(7, 7, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); shape.setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentHalfExtents); @@ -626,7 +637,7 @@ namespace 0, -25, -100, -100, -100, 0, -25, -100, -100, -100, }}; - btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); shape.setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentHalfExtents); @@ -680,7 +691,7 @@ namespace 0, -25, -100, -100, -100, 0, -25, -100, -100, -100, }}; - btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); shape.setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentHalfExtents); @@ -711,7 +722,7 @@ namespace 0, -25, -100, -100, -100, 0, -25, -100, -100, -100, }}; - btHeightfieldTerrainShape heightfieldShape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape heightfieldShape = makeSquareHeightfieldTerrainShape(heightfieldData); heightfieldShape.setLocalScaling(btVector3(128, 128, 1)); const std::vector boxShapes(100, btVector3(20, 20, 100)); @@ -812,7 +823,7 @@ namespace 0, -25, -100, -100, -100, 0, -25, -100, -100, -100, }}; - btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); shape.setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentHalfExtents); From 1fb442e701213fbfec7857ea3eb34749fb6fa1f7 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Mon, 1 Mar 2021 08:12:43 +0000 Subject: [PATCH 044/116] heightfield: Only `buildAccelerator` on Bullet 2.89+ Fixes #5874 --- apps/openmw/mwphysics/heightfield.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwphysics/heightfield.cpp b/apps/openmw/mwphysics/heightfield.cpp index cbcc9d024..34127fe3a 100644 --- a/apps/openmw/mwphysics/heightfield.cpp +++ b/apps/openmw/mwphysics/heightfield.cpp @@ -58,11 +58,14 @@ namespace MWPhysics mShape->setUseDiamondSubdivision(true); mShape->setLocalScaling(btVector3(triSize, triSize, 1)); - // Enables acceleration of heighfield collissions. +#if BT_BULLET_VERSION >= 289 + // Accelerates some collision tests. // - // Bullet does not yet use this in the most time-consuming method, `btHeightfieldTerrainShape::processAllTriangle`. - // See https://github.com/bulletphysics/bullet3/issues/3276 + // Note: The accelerator data structure in Bullet is only used + // in some operations. This could be improved, see: + // https://github.com/bulletphysics/bullet3/issues/3276 mShape->buildAccelerator(); +#endif btTransform transform(btQuaternion::getIdentity(), btVector3((x+0.5f) * triSize * (sqrtVerts-1), From 023bc80f550bc936b936888d50cdf170f6736ebe Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Mon, 1 Mar 2021 21:37:30 +0000 Subject: [PATCH 045/116] Don't let elemental shields harm the player in god mode --- apps/openmw/mwmechanics/combat.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 183845b8c..7c625019d 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -305,6 +305,10 @@ namespace MWMechanics void applyElementalShields(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim) { + // Don't let elemental shields harm the player in god mode. + bool godmode = attacker == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); + if (godmode) + return; for (int i=0; i<3; ++i) { float magnitude = victim.getClass().getCreatureStats(victim).getMagicEffects().get(ESM::MagicEffect::FireShield+i).getMagnitude(); From 4495b67d77c958a9c33c70111dac04613a8f9701 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 23 Jan 2021 13:49:16 +0000 Subject: [PATCH 046/116] 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 047/116] 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 048/116] 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 049/116] 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 050/116] 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 051/116] 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 052/116] 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 053/116] 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 054/116] 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 055/116] 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 056/116] 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 057/116] 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 058/116] 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 059/116] 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 060/116] 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 061/116] 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 062/116] 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 063/116] 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 064/116] 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 065/116] 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 066/116] 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 067/116] 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 068/116] 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 069/116] 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 070/116] 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 071/116] 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 072/116] 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 073/116] 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 074/116] 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 075/116] 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 076/116] 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 077/116] 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 078/116] 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 079/116] 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 080/116] 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 081/116] 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 082/116] 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 083/116] 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 084/116] 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 085/116] 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 086/116] 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 087/116] 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 088/116] 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 089/116] 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 4d48c81998978f58e4386ea1e7c907ad95850694 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 13 Mar 2021 22:28:13 +0400 Subject: [PATCH 090/116] 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 091/116] 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 092/116] 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 093/116] 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 094/116] 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 095/116] 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 096/116] 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 097/116] 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 098/116] 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 099/116] 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 100/116] 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 101/116] 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 102/116] 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 103/116] 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 104/116] 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 105/116] 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 106/116] 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 107/116] 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 108/116] 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 109/116] 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 110/116] 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 111/116] 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 112/116] 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 113/116] 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 114/116] 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 115/116] 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 116/116] 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()