From d340224c95862be8bf49ca31b0db7d5651d96411 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 01/37] shadowsbin for gl state reduction --- components/CMakeLists.txt | 2 +- components/sceneutil/mwshadowtechnique.cpp | 17 ++- components/sceneutil/shadowsbin.cpp | 133 +++++++++++++++++++++ components/sceneutil/shadowsbin.hpp | 58 +++++++++ components/shader/shadervisitor.cpp | 7 -- 5 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 components/sceneutil/shadowsbin.cpp create mode 100644 components/sceneutil/shadowsbin.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 412717aaa..eaf9a6dda 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -51,7 +51,7 @@ add_component_dir (shader add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer - actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique + actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique shadowsbin ) add_component_dir (nif diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index f31d2faef..bc0bcdf33 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -24,6 +24,7 @@ #include #include +#include "shadowsbin.hpp" namespace { @@ -272,10 +273,20 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) cv->pushCullingSet(); } #endif + // bin has to go inside camera cull or the rendertexture stage will override it + static osg::ref_ptr ss; + if (!ss) + { + ShadowsBinAdder adder("ShadowsBin"); + ss = new osg::StateSet; + ss->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "ShadowsBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS); + } + cv->pushStateSet(ss); if (_vdsm->getShadowedScene()) { _vdsm->getShadowedScene()->osg::Group::traverse(*nv); } + cv->popStateSet(); #if 1 if (!_polytope.empty()) { @@ -1569,14 +1580,10 @@ void MWShadowTechnique::createShaders() _shadowCastingStateSet->setAttributeAndModes(_castingProgram, 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", false)); - + _shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true)); _shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON); - _shadowCastingStateSet->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "RenderBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS); - // TODO: compare performance when alpha testing is handled here versus using a discard in the fragment shader - // TODO: compare performance when we set a bunch of GL state to the default here with OVERRIDE set so that there are fewer pointless state switches } osg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight) diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp new file mode 100644 index 000000000..2bf2f06f6 --- /dev/null +++ b/components/sceneutil/shadowsbin.cpp @@ -0,0 +1,133 @@ +#include "shadowsbin.hpp" +#include +#include +#include +#include + +using namespace osgUtil; + +namespace +{ + template + inline void accumulateState(T& currentValue, T newValue, bool& isOverride, unsigned int overrideFlags) + { + if (isOverride && !(overrideFlags & osg::StateAttribute::PROTECTED)) return; + + if (overrideFlags & osg::StateAttribute::OVERRIDE) + isOverride = true; + + currentValue = newValue; + } + + inline void accumulateModeState(const osg::StateSet* ss, bool& currentValue, bool& isOverride, int mode) + { + const osg::StateSet::ModeList& l = ss->getModeList(); + osg::StateSet::ModeList::const_iterator mf = l.find(mode); + if (mf == l.end()) + return; + int flags = mf->second; + bool newValue = flags & osg::StateAttribute::ON; + accumulateState(currentValue, newValue, isOverride, ss->getMode(mode)); + } + + inline bool materialNeedShadows(osg::Material* m) + { + return m->getDiffuse(osg::Material::FRONT).a() > 0.5; + } +} + +namespace SceneUtil +{ + +ShadowsBin::ShadowsBin() +{ + mStateSet = new osg::StateSet; + mStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false)); +} + +bool ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set& uninteresting) +{ + std::vector return_path; + State state; + StateGraph* sg_new = sg; + do + { + if (uninteresting.find(sg_new) != uninteresting.end()) + break; + return_path.push_back(sg_new); + sg_new = sg_new->_parent; + } while (sg_new && sg_new != root); + for(std::vector::reverse_iterator itr=return_path.rbegin(); itr!=return_path.rend(); ++itr) + { + const osg::StateSet* ss = (*itr)->getStateSet(); + if (!ss) continue; + accumulateModeState(ss, state.mAlphaBlend, state.mAlphaBlendOverride, GL_BLEND); + accumulateModeState(ss, state.mAlphaTest, state.mAlphaTestOverride, GL_ALPHA_TEST); + const osg::StateSet::AttributeList& l = ss->getAttributeList(); + osg::StateSet::AttributeList::const_iterator f = l.find(std::make_pair(osg::StateAttribute::MATERIAL, 0)); + if (f != l.end()) + { + const osg::StateSet::RefAttributePair* rap = &f->second; + accumulateState(state.mMaterial, static_cast(rap->first.get()), state.mMaterialOverride, rap->second); + if (state.mMaterial && !materialNeedShadows(state.mMaterial)) + state.mMaterial = nullptr; + } + f = l.find(std::make_pair(osg::StateAttribute::FRONTFACE, 0)); + if (f != l.end()) + state.mImportantState = true; + if ((*itr) != sg && !state.interesting()) + uninteresting.insert(*itr); + } + + if (!state.needShadows()) + return true; + + if (!state.needTexture() && !state.mImportantState) + { + for (RenderLeaf* leaf : sg->_leaves) + { + leaf->_parent = root; + root->_leaves.push_back(leaf); + } + return true; + } + return false; +} + +bool ShadowsBin::State::needShadows() const +{ + if (!mMaterial) + return true; + return materialNeedShadows(mMaterial); +} + +void ShadowsBin::sortImplementation() +{ + if (!_stateGraphList.size()) + return; + StateGraph* root = _stateGraphList[0]; + while (root->_parent) + { + 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 + break; + if (!root->_parent) + return; + } + root = root->find_or_insert(mStateSet.get()); + root->_leaves.reserve(_stateGraphList.size()); + StateGraphList newList; + std::unordered_set uninteresting; + for (StateGraph* graph : _stateGraphList) + { + if (!cullStateGraph(graph, root, uninteresting)) + newList.push_back(graph); + } + if (!root->_leaves.empty()) + newList.push_back(root); + _stateGraphList = newList; +} + +} diff --git a/components/sceneutil/shadowsbin.hpp b/components/sceneutil/shadowsbin.hpp new file mode 100644 index 000000000..05926bace --- /dev/null +++ b/components/sceneutil/shadowsbin.hpp @@ -0,0 +1,58 @@ +#ifndef OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H +#define OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H +#include +#include + +namespace osg +{ + class Material; +} + +namespace SceneUtil +{ + + /// renderbin which culls redundent state for shadows rendering + class ShadowsBin : public osgUtil::RenderBin + { + private: + osg::ref_ptr mStateSet; + public: + META_Object(SceneUtil, ShadowsBin) + ShadowsBin(); + ShadowsBin(const ShadowsBin& rhs, const osg::CopyOp& copyop) : osgUtil::RenderBin(rhs, copyop), mStateSet(rhs.mStateSet) {} + + virtual void sortImplementation(); + + struct State + { + State():mAlphaBlend(false),mAlphaBlendOverride(false),mAlphaTest(false),mAlphaTestOverride(false),mMaterial(nullptr),mMaterialOverride(false),mImportantState(false){} + bool mAlphaBlend; + bool mAlphaBlendOverride; + bool mAlphaTest; + bool mAlphaTestOverride; + osg::Material* mMaterial; + bool mMaterialOverride; + bool mImportantState; + bool needTexture() const { return mAlphaBlend || mAlphaTest; } + bool needShadows() const; + bool interesting() const { return !needShadows() || needTexture() || mAlphaBlendOverride || mAlphaTestOverride || mMaterialOverride || mImportantState; } + }; + + bool 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); + } + }; + + class ShadowsBinAdder + { + public: + ShadowsBinAdder(const std::string& name){ ShadowsBin::addPrototype(name); } + }; + +} + +#endif diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index e40cc255b..365cc5b66 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -205,13 +205,6 @@ namespace Shader mRequirements.back().mShaderRequired = true; } } - - if (diffuseMap) - { - if (!writableStateSet) - writableStateSet = getWritableStateSet(node); - writableStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true)); - } } const osg::StateSet::AttributeList& attributes = stateset->getAttributeList(); From 449506fef155f1d0d4bcaa833604409babfe7bf8 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 31 Oct 2020 19:06:20 +0000 Subject: [PATCH 02/37] Attempt to explain what shadowsbin is doing --- components/sceneutil/shadowsbin.cpp | 44 ++++++++++++++++++++--------- components/sceneutil/shadowsbin.hpp | 19 +++++++------ 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp index 2bf2f06f6..e0a141ffb 100644 --- a/components/sceneutil/shadowsbin.cpp +++ b/components/sceneutil/shadowsbin.cpp @@ -32,6 +32,7 @@ namespace inline bool materialNeedShadows(osg::Material* m) { + // I'm pretty sure this needs to check the colour mode - vertex colours might override this value. return m->getDiffuse(osg::Material::FRONT).a() > 0.5; } } @@ -45,38 +46,45 @@ ShadowsBin::ShadowsBin() mStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false)); } -bool ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set& uninteresting) +bool ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set& uninterestingCache) { std::vector return_path; State state; StateGraph* sg_new = sg; do { - if (uninteresting.find(sg_new) != uninteresting.end()) + if (uninterestingCache.find(sg_new) != uninterestingCache.end()) break; return_path.push_back(sg_new); sg_new = sg_new->_parent; } while (sg_new && sg_new != root); - for(std::vector::reverse_iterator itr=return_path.rbegin(); itr!=return_path.rend(); ++itr) + + for(auto itr=return_path.rbegin(); itr!=return_path.rend(); ++itr) { const osg::StateSet* ss = (*itr)->getStateSet(); - if (!ss) continue; + if (!ss) + continue; + accumulateModeState(ss, state.mAlphaBlend, state.mAlphaBlendOverride, GL_BLEND); accumulateModeState(ss, state.mAlphaTest, state.mAlphaTestOverride, GL_ALPHA_TEST); - const osg::StateSet::AttributeList& l = ss->getAttributeList(); - osg::StateSet::AttributeList::const_iterator f = l.find(std::make_pair(osg::StateAttribute::MATERIAL, 0)); - if (f != l.end()) + + const osg::StateSet::AttributeList& attributes = ss->getAttributeList(); + osg::StateSet::AttributeList::const_iterator found = attributes.find(std::make_pair(osg::StateAttribute::MATERIAL, 0)); + if (found != attributes.end()) { - const osg::StateSet::RefAttributePair* rap = &f->second; - accumulateState(state.mMaterial, static_cast(rap->first.get()), state.mMaterialOverride, rap->second); + const osg::StateSet::RefAttributePair& rap = found->second; + accumulateState(state.mMaterial, static_cast(rap.first.get()), state.mMaterialOverride, rap.second); if (state.mMaterial && !materialNeedShadows(state.mMaterial)) state.mMaterial = nullptr; } - f = l.find(std::make_pair(osg::StateAttribute::FRONTFACE, 0)); - if (f != l.end()) + + // 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 ((*itr) != sg && !state.interesting()) - uninteresting.insert(*itr); + uninterestingCache.insert(*itr); } if (!state.needShadows()) @@ -103,6 +111,10 @@ bool ShadowsBin::State::needShadows() const void ShadowsBin::sortImplementation() { + // The cull visitor contains a stategraph. + // When a stateset is pushed, it's added/found as a child of the current stategraph node, then that node becomes the new current stategraph node. + // When a drawable is added, the current stategraph node is added to the current renderbin (if it's not there already) and the drawable is added as a renderleaf to the stategraph + // This means our list only contains stategraph nodes with directly-attached renderleaves, but they might have parents with more state set that needs to be considered. if (!_stateGraphList.size()) return; StateGraph* root = _stateGraphList[0]; @@ -117,12 +129,16 @@ void ShadowsBin::sortImplementation() return; } root = root->find_or_insert(mStateSet.get()); + // root is now a stategraph with useDiffuseMapForShadowAlpha disabled but minimal other state root->_leaves.reserve(_stateGraphList.size()); StateGraphList newList; - std::unordered_set uninteresting; + std::unordered_set uninterestingCache; for (StateGraph* graph : _stateGraphList) { - if (!cullStateGraph(graph, root, uninteresting)) + // 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. + if (!cullStateGraph(graph, root, uninterestingCache)) newList.push_back(graph); } if (!root->_leaves.empty()) diff --git a/components/sceneutil/shadowsbin.hpp b/components/sceneutil/shadowsbin.hpp index 05926bace..6302d5974 100644 --- a/components/sceneutil/shadowsbin.hpp +++ b/components/sceneutil/shadowsbin.hpp @@ -17,11 +17,11 @@ namespace SceneUtil private: osg::ref_ptr mStateSet; public: - META_Object(SceneUtil, ShadowsBin) - ShadowsBin(); - ShadowsBin(const ShadowsBin& rhs, const osg::CopyOp& copyop) : osgUtil::RenderBin(rhs, copyop), mStateSet(rhs.mStateSet) {} + META_Object(SceneUtil, ShadowsBin) + ShadowsBin(); + ShadowsBin(const ShadowsBin& rhs, const osg::CopyOp& copyop) : osgUtil::RenderBin(rhs, copyop), mStateSet(rhs.mStateSet) {} - virtual void sortImplementation(); + virtual void sortImplementation(); struct State { @@ -35,16 +35,17 @@ namespace SceneUtil bool mImportantState; bool needTexture() const { return mAlphaBlend || mAlphaTest; } 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; } }; bool 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) + { + osg::ref_ptr bin (new ShadowsBin); + osgUtil::RenderBin::addRenderBinPrototype(name, bin); + } }; class ShadowsBinAdder From 18e38d8810be4e397861d0ce8adad4747353dbd3 Mon Sep 17 00:00:00 2001 From: fredzio Date: Sun, 1 Nov 2020 22:30:48 +0100 Subject: [PATCH 03/37] Do not block a door when it turns away. --- .../mwphysics/contacttestresultcallback.cpp | 4 +++- .../mwphysics/contacttestresultcallback.hpp | 4 +++- apps/openmw/mwphysics/physicssystem.cpp | 14 +++++++++++--- apps/openmw/mwphysics/physicssystem.hpp | 8 ++++++++ apps/openmw/mwworld/worldimp.cpp | 15 ++++++++++----- 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwphysics/contacttestresultcallback.cpp b/apps/openmw/mwphysics/contacttestresultcallback.cpp index f8209e363..5829ee02f 100644 --- a/apps/openmw/mwphysics/contacttestresultcallback.cpp +++ b/apps/openmw/mwphysics/contacttestresultcallback.cpp @@ -2,6 +2,8 @@ #include +#include "components/misc/convert.hpp" + #include "ptrholder.hpp" namespace MWPhysics @@ -20,7 +22,7 @@ namespace MWPhysics collisionObject = col1Wrap->m_collisionObject; PtrHolder* holder = static_cast(collisionObject->getUserPointer()); if (holder) - mResult.push_back(holder->getPtr()); + mResult.emplace_back(ContactPoint{holder->getPtr(), Misc::Convert::toOsg(cp.m_positionWorldOnB), Misc::Convert::toOsg(cp.m_normalWorldOnB)}); return 0.f; } diff --git a/apps/openmw/mwphysics/contacttestresultcallback.hpp b/apps/openmw/mwphysics/contacttestresultcallback.hpp index 03fc36299..fbe12d5dc 100644 --- a/apps/openmw/mwphysics/contacttestresultcallback.hpp +++ b/apps/openmw/mwphysics/contacttestresultcallback.hpp @@ -7,6 +7,8 @@ #include "../mwworld/ptr.hpp" +#include "physicssystem.hpp" + class btCollisionObject; struct btCollisionObjectWrapper; @@ -23,7 +25,7 @@ namespace MWPhysics const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) override; - std::vector mResult; + std::vector mResult; }; } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 513e8d59b..9a777bd45 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -403,15 +403,15 @@ namespace MWPhysics return osg::Vec3f(); } - std::vector PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const + std::vector PhysicsSystem::getCollisionsPoints(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const { btCollisionObject* me = nullptr; - ObjectMap::const_iterator found = mObjects.find(ptr); + auto found = mObjects.find(ptr); if (found != mObjects.end()) me = found->second->getCollisionObject(); else - return std::vector(); + return {}; ContactTestResultCallback resultCallback (me); resultCallback.m_collisionFilterGroup = collisionGroup; @@ -420,6 +420,14 @@ namespace MWPhysics return resultCallback.mResult; } + std::vector PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const + { + std::vector actors; + for (auto& [actor, point, normal] : getCollisionsPoints(ptr, collisionGroup, collisionMask)) + actors.emplace_back(actor); + return actors; + } + osg::Vec3f PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight) { ActorMap::iterator found = mActors.find(ptr); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 31707465b..36ef762d3 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -57,6 +57,13 @@ namespace MWPhysics class Actor; class PhysicsTaskScheduler; + struct ContactPoint + { + MWWorld::Ptr mObject; + osg::Vec3f mPoint; + osg::Vec3f mNormal; + }; + struct LOSRequest { LOSRequest(const std::weak_ptr& a1, const std::weak_ptr& a2); @@ -145,6 +152,7 @@ namespace MWPhysics void debugDraw(); std::vector getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; ///< get handles this object collides with + std::vector getCollisionsPoints(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight); std::pair getHitContact(const MWWorld::ConstPtr& actor, diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index dbe6af326..095bb1454 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1577,21 +1577,26 @@ namespace MWWorld float minRot = door.getCellRef().getPosition().rot[2]; float maxRot = minRot + osg::DegreesToRadians(90.f); - float diff = duration * osg::DegreesToRadians(90.f); - float targetRot = std::min(std::max(minRot, oldRot + diff * (state == MWWorld::DoorState::Opening ? 1 : -1)), maxRot); + float diff = duration * osg::DegreesToRadians(90.f) * (state == MWWorld::DoorState::Opening ? 1 : -1); + float targetRot = std::min(std::max(minRot, oldRot + diff), maxRot); rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot, MWBase::RotationFlag_none); bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot; /// \todo should use convexSweepTest here bool collisionWithActor = false; - std::vector collisions = mPhysics->getCollisions(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor); - for (MWWorld::Ptr& ptr : collisions) + for (auto& [ptr, point, normal] : mPhysics->getCollisionsPoints(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor)) { + if (ptr.getClass().isActor()) { + osg::Vec3f direction = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * point - point; + direction.normalize(); + if (direction * normal < 0) // door is turning away from actor + continue; + collisionWithActor = true; - + // Collided with actor, ask actor to try to avoid door if(ptr != getPlayerPtr() ) { From ecd10a731eb5c4abb1f6b24e33f0abd57ade395a Mon Sep 17 00:00:00 2001 From: Frederic Chardon Date: Mon, 2 Nov 2020 13:52:36 +0100 Subject: [PATCH 04/37] Compute the rotation normal relative to the door axe, not the world. --- apps/openmw/mwworld/worldimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 095bb1454..be32765ad 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1590,8 +1590,10 @@ namespace MWWorld if (ptr.getClass().isActor()) { - osg::Vec3f direction = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * point - point; + auto localPoint = objPos.asVec3() - point; + osg::Vec3f direction = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * localPoint - localPoint; direction.normalize(); + mPhysics->reportCollision(Misc::Convert::toBullet(point), Misc::Convert::toBullet(normal)); if (direction * normal < 0) // door is turning away from actor continue; From c1d56d94c4b3329b50afd0863f0284ef7753a7bc Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 2 Nov 2020 17:51:36 +0400 Subject: [PATCH 05/37] Do not remove active effects in loop (bug #3789) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 31 +++++++++++++++++++++ apps/openmw/mwmechanics/tickableeffects.cpp | 18 ------------ apps/openmw/mwmechanics/tickableeffects.hpp | 1 + 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 304892ca4..5bda1ff5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Bug #2976 [reopened]: Issues combining settings from the command line and both config files Bug #3676: NiParticleColorModifier isn't applied properly Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects + Bug #3789: Crash in visitEffectSources while in battle Bug #3862: Random container contents behave differently than vanilla Bug #3929: Leveled list merchant containers respawn on barter Bug #4021: Attributes and skills are not stored as floats diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 81b3a353d..beab36cbf 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -381,6 +381,37 @@ namespace MWMechanics target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude, true); return true; } + else if (target.getClass().isActor() && effectId == ESM::MagicEffect::CurePoison) + { + target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); + return true; + } + else if (target.getClass().isActor() && effectId == ESM::MagicEffect::CureParalyzation) + { + target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze); + return true; + } + else if (target.getClass().isActor() && effectId == ESM::MagicEffect::CureCommonDisease) + { + target.getClass().getCreatureStats(target).getSpells().purgeCommonDisease(); + return true; + } + else if (target.getClass().isActor() && effectId == ESM::MagicEffect::CureBlightDisease) + { + target.getClass().getCreatureStats(target).getSpells().purgeBlightDisease(); + return true; + } + else if (target.getClass().isActor() && effectId == ESM::MagicEffect::CureCorprusDisease) + { + target.getClass().getCreatureStats(target).getActiveSpells().purgeCorprusDisease(); + target.getClass().getCreatureStats(target).getSpells().purgeCorprusDisease(); + return true; + } + else if (target.getClass().isActor() && effectId == ESM::MagicEffect::RemoveCurse) + { + target.getClass().getCreatureStats(target).getSpells().purgeCurses(); + return true; + } else if (target.getClass().isActor() && target == getPlayer()) { MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mCaster); diff --git a/apps/openmw/mwmechanics/tickableeffects.cpp b/apps/openmw/mwmechanics/tickableeffects.cpp index fa3b6ac20..5056179f8 100644 --- a/apps/openmw/mwmechanics/tickableeffects.cpp +++ b/apps/openmw/mwmechanics/tickableeffects.cpp @@ -205,24 +205,6 @@ namespace MWMechanics break; } - case ESM::MagicEffect::CurePoison: - actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); - break; - case ESM::MagicEffect::CureParalyzation: - actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze); - break; - case ESM::MagicEffect::CureCommonDisease: - actor.getClass().getCreatureStats(actor).getSpells().purgeCommonDisease(); - break; - case ESM::MagicEffect::CureBlightDisease: - actor.getClass().getCreatureStats(actor).getSpells().purgeBlightDisease(); - break; - case ESM::MagicEffect::CureCorprusDisease: - actor.getClass().getCreatureStats(actor).getSpells().purgeCorprusDisease(); - break; - case ESM::MagicEffect::RemoveCurse: - actor.getClass().getCreatureStats(actor).getSpells().purgeCurses(); - break; default: return false; } diff --git a/apps/openmw/mwmechanics/tickableeffects.hpp b/apps/openmw/mwmechanics/tickableeffects.hpp index c4abed6a3..ccd42ca19 100644 --- a/apps/openmw/mwmechanics/tickableeffects.hpp +++ b/apps/openmw/mwmechanics/tickableeffects.hpp @@ -12,6 +12,7 @@ namespace MWMechanics struct EffectKey; /// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed + /// Note: this function works in loop, so magic effects should not be removed here to avoid iterator invalidation. /// @return Was the effect a tickable effect with a magnitude? bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey& effectKey, float magnitude); } From 0e971dccf02b669bda291be91b10025aba5511c0 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 4 Nov 2020 13:33:54 +0400 Subject: [PATCH 06/37] Rework cure effects --- apps/openmw/mwmechanics/actors.cpp | 42 ++++++++++++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 31 ----------------- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a3865983e..8f3675ef7 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -916,12 +916,54 @@ namespace MWMechanics } }; + void Actors::applyCureEffects(const MWWorld::Ptr& actor) + { + CreatureStats &creatureStats = actor.getClass().getCreatureStats(actor); + const MagicEffects &effects = creatureStats.getMagicEffects(); + + if (effects.get(ESM::MagicEffect::CurePoison).getModifier() > 0) + { + creatureStats.getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); + creatureStats.getSpells().purgeEffect(ESM::MagicEffect::Poison); + if (actor.getClass().hasInventoryStore(actor)) + actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Poison); + } + else if (effects.get(ESM::MagicEffect::CureParalyzation).getModifier() > 0) + { + creatureStats.getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze); + creatureStats.getSpells().purgeEffect(ESM::MagicEffect::Paralyze); + if (actor.getClass().hasInventoryStore(actor)) + actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Paralyze); + } + else if (effects.get(ESM::MagicEffect::CureCommonDisease).getModifier() > 0) + { + creatureStats.getSpells().purgeCommonDisease(); + } + else if (effects.get(ESM::MagicEffect::CureBlightDisease).getModifier() > 0) + { + creatureStats.getSpells().purgeBlightDisease(); + } + else if (effects.get(ESM::MagicEffect::CureCorprusDisease).getModifier() > 0) + { + creatureStats.getActiveSpells().purgeCorprusDisease(); + creatureStats.getSpells().purgeCorprusDisease(); + if (actor.getClass().hasInventoryStore(actor)) + actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Corprus, true); + } + else if (effects.get(ESM::MagicEffect::RemoveCurse).getModifier() > 0) + { + creatureStats.getSpells().purgeCurses(); + } + } + void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration) { CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr); const MagicEffects &effects = creatureStats.getMagicEffects(); bool godmode = ptr == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); + applyCureEffects(ptr); + bool wasDead = creatureStats.isDead(); if (duration > 0) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 9299d468c..453540001 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -206,6 +206,7 @@ namespace MWMechanics private: void updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl); + void applyCureEffects (const MWWorld::Ptr& actor); PtrActorMap mActors; float mTimerDisposeSummonsCorpses; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index beab36cbf..81b3a353d 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -381,37 +381,6 @@ namespace MWMechanics target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude, true); return true; } - else if (target.getClass().isActor() && effectId == ESM::MagicEffect::CurePoison) - { - target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); - return true; - } - else if (target.getClass().isActor() && effectId == ESM::MagicEffect::CureParalyzation) - { - target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze); - return true; - } - else if (target.getClass().isActor() && effectId == ESM::MagicEffect::CureCommonDisease) - { - target.getClass().getCreatureStats(target).getSpells().purgeCommonDisease(); - return true; - } - else if (target.getClass().isActor() && effectId == ESM::MagicEffect::CureBlightDisease) - { - target.getClass().getCreatureStats(target).getSpells().purgeBlightDisease(); - return true; - } - else if (target.getClass().isActor() && effectId == ESM::MagicEffect::CureCorprusDisease) - { - target.getClass().getCreatureStats(target).getActiveSpells().purgeCorprusDisease(); - target.getClass().getCreatureStats(target).getSpells().purgeCorprusDisease(); - return true; - } - else if (target.getClass().isActor() && effectId == ESM::MagicEffect::RemoveCurse) - { - target.getClass().getCreatureStats(target).getSpells().purgeCurses(); - return true; - } else if (target.getClass().isActor() && target == getPlayer()) { MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mCaster); From 64ff09883f9245d96ec16d36154865a04f15541e Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Thu, 5 Nov 2020 13:30:56 +0100 Subject: [PATCH 07/37] Add #4083 to the changelog. The entry for the release changelog is going to be included in my upcoming MR which will update the whole release changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12dd019d8..1f19783ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Bug #3929: Leveled list merchant containers respawn on barter Bug #4021: Attributes and skills are not stored as floats Bug #4055: Local scripts don't inherit variables from their base record + Bug #4083: Door animation freezes when colliding with actors Bug #4623: Corprus implementation is incorrect Bug #4631: Setting MSAA level too high doesn't fall back to highest supported level Bug #4764: Data race in osg ParticleSystem From 9859565f8dbc00fbec9518618345e232f4fc4708 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sat, 7 Nov 2020 02:18:36 +0300 Subject: [PATCH 08/37] Improve grammar in [Physics] category of the default settings --- files/settings-default.cfg | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 143523750..b9fb2fbf7 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -933,13 +933,15 @@ object shadows = false enable indoor shadows = true [Physics] -# how much background thread to use in the physics solver. 0 to disable (i.e solver run in the main thread) +# Set the number of background threads used for physics. +# If no background threads are used, physics calculations are processed in the main thread +# and the settings below have no effect. async num threads = 0 -# maintain a cache of lineofsight request in the bacground physics thread -# determines for how much frames an inactive lineofsight request should be kept updated in the cache -# -1 to disable (i.e the LOS will be calculated only on request) +# Set the number of frames an inactive line-of-sight request will be kept +# refreshed in the background physics thread cache. +# If this is set to -1, line-of-sight requests are never cached. lineofsight keep inactive cache = 0 -# wether to defer aabb update till before collision detection +# Defer bounding boxes update until collision detection. defer aabb update = true From 7e63afdecfb9b453c566d127c40f68fe8c3aeb0c Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sat, 7 Nov 2020 02:05:16 +0300 Subject: [PATCH 09/37] Allow loading arbitrary NIF files --- components/nif/niffile.cpp | 43 +++++++++++++++++-- .../reference/modding/settings/index.rst | 1 + .../reference/modding/settings/models.rst | 27 ++++++++++++ files/settings-default.cfg | 5 +++ 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 docs/source/reference/modding/settings/models.rst diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 5642dfebb..098f6aba2 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -1,9 +1,12 @@ #include "niffile.hpp" #include "effect.hpp" +#include #include #include +#include + namespace Nif { @@ -137,15 +140,46 @@ void NIFFile::parse(Files::IStreamPtr stream) // Check the header string std::string head = nif.getVersionString(); - if(head.compare(0, 22, "NetImmerse File Format") != 0) + static const std::array verStrings = + { + "NetImmerse File Format", + "Gamebryo File Format" + }; + bool supported = false; + for (const std::string& verString : verStrings) + { + supported = (head.compare(0, verString.size(), verString) == 0); + if (supported) + break; + } + if (!supported) fail("Invalid NIF header: " + head); + supported = false; + // Get BCD version ver = nif.getUInt(); // 4.0.0.0 is an older, practically identical version of the format. // It's not used by Morrowind assets but Morrowind supports it. - if(ver != NIFStream::generateVersion(4,0,0,0) && ver != VER_MW) - fail("Unsupported NIF version: " + printVersion(ver)); + static const std::array supportedVers = + { + NIFStream::generateVersion(4,0,0,0), + VER_MW + }; + for (uint32_t supportedVer : supportedVers) + { + supported = (ver == supportedVer); + if (supported) + break; + } + if (!supported) + { + static const bool ignoreUnsupported = Settings::Manager::getBool("load unsupported nif files", "Models"); + if (ignoreUnsupported) + warn("Unsupported NIF version: " + printVersion(ver) + ". Proceed with caution!"); + else + fail("Unsupported NIF version: " + printVersion(ver)); + } // NIF data endianness if (ver >= NIFStream::generateVersion(20,0,0,4)) @@ -245,6 +279,9 @@ void NIFFile::parse(Files::IStreamPtr stream) else fail("Unknown record type " + rec); + if (!supported) + Log(Debug::Verbose) << "NIF Debug: Reading record of type " << rec << ", index " << i << " (" << filename << ")"; + assert(r != nullptr); assert(r->recType != RC_MISSING); r->recName = rec; diff --git a/docs/source/reference/modding/settings/index.rst b/docs/source/reference/modding/settings/index.rst index 2261fe8e1..586a99dbb 100644 --- a/docs/source/reference/modding/settings/index.rst +++ b/docs/source/reference/modding/settings/index.rst @@ -58,3 +58,4 @@ The ranges included with each setting are the physically possible ranges, not re windows navigator physics + models diff --git a/docs/source/reference/modding/settings/models.rst b/docs/source/reference/modding/settings/models.rst new file mode 100644 index 000000000..da56a1709 --- /dev/null +++ b/docs/source/reference/modding/settings/models.rst @@ -0,0 +1,27 @@ +Models Settings +############### + +load unsupported nif files +-------------------------- + +:Type: boolean +:Range: True/False +:Default: False + +At the moment OpenMW's NIF loader is only tailored to load models +that can also load in Morrowind. + +However, you can enable its limited and experimental support for updates in +the definitions of record types from later NIF revisions by toggling on +this setting. + +You must keep in mind that loading unsupported NIF files may fail, +and the degree of this failure may vary. In milder cases, OpenMW will reject +the file anyway because it lacks a definition for a certain record type +that the file may use. In more severe cases OpenMW's +incomplete understanding of a record type can lead to memory corruption, +crashes or even freezes. Don't enable this if you're not sure that +you know what you're doing. + +To help debug possible issues OpenMW will log its progress in loading +every file that uses an unsupported NIF version. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index b9fb2fbf7..0b307ba09 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -945,3 +945,8 @@ lineofsight keep inactive cache = 0 # Defer bounding boxes update until collision detection. defer aabb update = true + +[Models] +# Attempt to load any valid NIF file regardless of its version and track the progress. +# Loading arbitrary meshes is not advised and may cause instability. +load unsupported nif files = false From c79f50965229c3773069bea851d49e0f96bf76fc Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sat, 7 Nov 2020 03:33:04 +0300 Subject: [PATCH 10/37] Misc NIF loader improvements Bullet NIF loader cleanup Collect all extra records of a node Remove code duplication in geometry type detection in OSG-side NIF loader --- components/nifbullet/bulletnifloader.cpp | 53 ++++++++++-------------- components/nifosg/nifloader.cpp | 30 ++++++++++++-- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 15834ffad..5b531121e 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -24,11 +25,6 @@ osg::Matrixf getWorldTransform(const Nif::Node *node) return node->trafo.toMatrix(); } -btVector3 getbtVector(const osg::Vec3f &v) -{ - return btVector3(v.x(), v.y(), v.z()); -} - bool pathFileNameStartsWithX(const std::string& path) { const std::size_t slashpos = path.find_last_of("/\\"); @@ -36,7 +32,7 @@ bool pathFileNameStartsWithX(const std::string& path) return letterPos < path.size() && (path[letterPos] == 'x' || path[letterPos] == 'X'); } -void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriShapeData& data, const osg::Matrixf &transform) +void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data, const osg::Matrixf &transform) { mesh.preallocateVertices(static_cast(data.vertices.size())); mesh.preallocateIndices(static_cast(data.triangles.size())); @@ -47,20 +43,20 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriShapeDa for (std::size_t i = 0; i < triangles.size(); i += 3) { mesh.addTriangle( - getbtVector(vertices[triangles[i + 0]] * transform), - getbtVector(vertices[triangles[i + 1]] * transform), - getbtVector(vertices[triangles[i + 2]] * transform) + Misc::Convert::toBullet(vertices[triangles[i + 0]] * transform), + Misc::Convert::toBullet(vertices[triangles[i + 1]] * transform), + Misc::Convert::toBullet(vertices[triangles[i + 2]] * transform) ); } } -void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, const osg::Matrixf &transform) +void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, const osg::Matrixf &transform) { const std::vector &vertices = data.vertices; const std::vector> &strips = data.strips; if (vertices.empty() || strips.empty()) return; - mesh.preallocateVertices(static_cast(data.vertices.size())); + mesh.preallocateVertices(static_cast(vertices.size())); int numTriangles = 0; for (const std::vector& strip : strips) { @@ -88,17 +84,17 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD if (i%2==0) { mesh.addTriangle( - getbtVector(vertices[a] * transform), - getbtVector(vertices[b] * transform), - getbtVector(vertices[c] * transform) + Misc::Convert::toBullet(vertices[a] * transform), + Misc::Convert::toBullet(vertices[b] * transform), + Misc::Convert::toBullet(vertices[c] * transform) ); } else { mesh.addTriangle( - getbtVector(vertices[a] * transform), - getbtVector(vertices[c] * transform), - getbtVector(vertices[b] * transform) + Misc::Convert::toBullet(vertices[a] * transform), + Misc::Convert::toBullet(vertices[c] * transform), + Misc::Convert::toBullet(vertices[b] * transform) ); } } @@ -106,17 +102,12 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD } } -void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::Node* nifNode, const osg::Matrixf &transform) +void fillTriangleMesh(btTriangleMesh& mesh, const Nif::Node* nifNode, const osg::Matrixf &transform = osg::Matrixf()) { if (nifNode->recType == Nif::RC_NiTriShape) - fillTriangleMeshWithTransform(mesh, static_cast(nifNode)->data.get(), transform); - else // if (nifNode->recType == Nif::RC_NiTriStrips) - fillTriangleMeshWithTransform(mesh, static_cast(nifNode)->data.get(), transform); -} - -void fillTriangleMesh(btTriangleMesh& mesh, const Nif::Node* node) -{ - fillTriangleMeshWithTransform(mesh, node, osg::Matrixf()); + fillTriangleMesh(mesh, static_cast(nifNode)->data.get(), transform); + else if (nifNode->recType == Nif::RC_NiTriStrips) + fillTriangleMesh(mesh, static_cast(nifNode)->data.get(), transform); } } @@ -149,10 +140,12 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) if (findBoundingBox(node)) { + const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents); + const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate); std::unique_ptr compound (new btCompoundShape); - std::unique_ptr boxShape(new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents))); + std::unique_ptr boxShape(new btBoxShape(halfExtents)); btTransform transform = btTransform::getIdentity(); - transform.setOrigin(getbtVector(mShape->mCollisionBoxTranslate)); + transform.setOrigin(origin); compound->addChildShape(transform, boxShape.get()); boxShape.release(); @@ -383,7 +376,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons if (!mAvoidStaticMesh) mAvoidStaticMesh.reset(new btTriangleMesh(false)); - fillTriangleMeshWithTransform(*mAvoidStaticMesh, nifNode, transform); + fillTriangleMesh(*mAvoidStaticMesh, nifNode, transform); } else { @@ -391,7 +384,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons mStaticMesh.reset(new btTriangleMesh(false)); // Static shape, just transform all vertices into position - fillTriangleMeshWithTransform(*mStaticMesh, nifNode, transform); + fillTriangleMesh(*mStaticMesh, nifNode, transform); } } diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 7a592fb4a..805283b42 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -60,6 +60,18 @@ namespace } } + bool isTypeGeometry(int type) + { + switch (type) + { + case Nif::RC_NiTriShape: + case Nif::RC_NiTriStrips: + case Nif::RC_NiLines: + return true; + } + return false; + } + // Collect all properties affecting the given drawable that should be handled on drawable basis rather than on the node hierarchy above it. void collectDrawableProperties(const Nif::Node* nifNode, std::vector& out) { @@ -528,7 +540,19 @@ namespace NifOsg // - finding a random child NiNode in NiBspArrayController node->setUserValue("recIndex", nifNode->recIndex); + std::vector extraCollection; + for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->next) + extraCollection.emplace_back(e); + + for (size_t i = 0; i < nifNode->extralist.length(); ++i) + { + Nif::ExtraPtr e = nifNode->extralist[i]; + if (!e.empty()) + extraCollection.emplace_back(e); + } + + for (const auto& e : extraCollection) { if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys) { @@ -584,7 +608,7 @@ namespace NifOsg applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags); - const bool isGeometry = nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines; + const bool isGeometry = isTypeGeometry(nifNode->recType); if (isGeometry && !skipMeshes) { @@ -1175,7 +1199,7 @@ namespace NifOsg void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines); + assert(isTypeGeometry(nifNode->recType)); osg::ref_ptr drawable; osg::ref_ptr geom (new osg::Geometry); handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags); @@ -1220,7 +1244,7 @@ namespace NifOsg void handleSkinnedGeometry(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines); + assert(isTypeGeometry(nifNode->recType)); osg::ref_ptr geometry (new osg::Geometry); handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags); osg::ref_ptr rig(new SceneUtil::RigGeometry); From afea11b70a851e6d56c7e8da355c533b73659b15 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sat, 7 Nov 2020 03:40:21 +0300 Subject: [PATCH 11/37] Read NiStringPalette and NiBoolData --- components/nif/data.cpp | 25 +++++++++++++++++++++++++ components/nif/data.hpp | 12 ++++++++++++ components/nif/niffile.cpp | 2 ++ components/nif/record.hpp | 4 +++- components/nif/recordptr.hpp | 2 ++ 5 files changed, 44 insertions(+), 1 deletion(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 5b0b99c53..4df464fdf 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -392,4 +392,29 @@ void NiPalette::read(NIFStream *nif) colors[i] = nif->getUInt() | alphaMask; } +void NiStringPalette::read(NIFStream *nif) +{ + unsigned int size = nif->getUInt(); + if (!size) + return; + std::vector source; + nif->getChars(source, size); + if (nif->getUInt() != size) + nif->file->warn("Failed size check in NiStringPalette"); + if (source[source.size()-1] != '\0') + source.emplace_back('\0'); + const char* buffer = source.data(); + while (static_cast(buffer - source.data()) < source.size()) + { + palette.emplace_back(buffer); + buffer += palette.back().size() + 1; + } +} + +void NiBoolData::read(NIFStream *nif) +{ + mKeyList = std::make_shared(); + mKeyList->read(nif); +} + } // Namespace diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 0ba544645..d0ea150e0 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -240,5 +240,17 @@ public: void read(NIFStream *nif) override; }; +struct NiStringPalette : public Record +{ + std::vector palette; + void read(NIFStream *nif) override; +}; + +struct NiBoolData : public Record +{ + ByteKeyMapPtr mKeyList; + void read(NIFStream *nif) override; +}; + } // Namespace #endif diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 098f6aba2..b50b602d5 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -116,6 +116,8 @@ static std::map makeFactory() factory["NiColorExtraData"] = {&construct , RC_NiColorExtraData }; factory["NiFloatExtraData"] = {&construct , RC_NiFloatExtraData }; factory["NiFloatsExtraData"] = {&construct , RC_NiFloatsExtraData }; + factory["NiStringPalette"] = {&construct , RC_NiStringPalette }; + factory["NiBoolData"] = {&construct , RC_NiBoolData }; return factory; } diff --git a/components/nif/record.hpp b/components/nif/record.hpp index eb26b0cce..0e8b80ffa 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -109,7 +109,9 @@ enum RecordType RC_NiVectorExtraData, RC_NiColorExtraData, RC_NiFloatExtraData, - RC_NiFloatsExtraData + RC_NiFloatsExtraData, + RC_NiStringPalette, + RC_NiBoolData }; /// Base class for all records diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 57607cb6a..d0b3d00ab 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -143,6 +143,7 @@ class NiAutoNormalParticlesData; class NiPalette; struct NiParticleModifier; struct NiLinesData; +struct NiBoolData; using NodePtr = RecordPtrT; using ExtraPtr = RecordPtrT; @@ -166,6 +167,7 @@ using NiRotatingParticlesDataPtr = RecordPtrT; using NiAutoNormalParticlesDataPtr = RecordPtrT; using NiPalettePtr = RecordPtrT; using NiParticleModifierPtr = RecordPtrT; +using NiBoolDataPtr = RecordPtrT; using NodeList = RecordListT; using PropertyList = RecordListT; From a38c6294250b043cb6180670956e5af12ffb99fa Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sat, 7 Nov 2020 04:04:46 +0300 Subject: [PATCH 12/37] Read NiSkinPartition --- components/nif/data.cpp | 65 +++++++++++++++++++++++++++++++++++- components/nif/data.hpp | 20 +++++++++++ components/nif/niffile.cpp | 1 + components/nif/record.hpp | 3 +- components/nif/recordptr.hpp | 2 ++ 5 files changed, 89 insertions(+), 2 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 4df464fdf..6bbab9786 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -320,7 +320,7 @@ void NiSkinData::read(NIFStream *nif) int boneNum = nif->getInt(); if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,1,0,0)) - nif->skip(4); // NiSkinPartition link + partitions.read(nif); // Has vertex weights flag if (nif->getVersion() > NIFStream::generateVersion(4,2,1,0) && !nif->getBoolean()) @@ -345,6 +345,69 @@ void NiSkinData::read(NIFStream *nif) } } +void NiSkinData::post(NIFFile *nif) +{ + partitions.post(nif); +} + +void NiSkinPartition::read(NIFStream *nif) +{ + unsigned int num = nif->getUInt(); + data.resize(num); + for (auto& partition : data) + partition.read(nif); +} + +void NiSkinPartition::Partition::read(NIFStream *nif) +{ + unsigned short numVertices = nif->getUShort(); + unsigned short numTriangles = nif->getUShort(); + unsigned short numBones = nif->getUShort(); + unsigned short numStrips = nif->getUShort(); + unsigned short bonesPerVertex = nif->getUShort(); + if (numBones) + nif->getUShorts(bones, numBones); + + bool hasVertexMap = true; + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0)) + hasVertexMap = nif->getBoolean(); + if (hasVertexMap && numVertices) + nif->getUShorts(vertexMap, numVertices); + + bool hasVertexWeights = true; + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0)) + hasVertexWeights = nif->getBoolean(); + if (hasVertexWeights && numVertices && bonesPerVertex) + nif->getFloats(weights, numVertices * bonesPerVertex); + + std::vector stripLengths; + if (numStrips) + nif->getUShorts(stripLengths, numStrips); + + bool hasFaces = true; + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0)) + hasFaces = nif->getBoolean(); + if (hasFaces) + { + if (numStrips) + { + strips.resize(numStrips); + for (unsigned short i = 0; i < numStrips; i++) + nif->getUShorts(strips[i], stripLengths[i]); + } + else if (numTriangles) + nif->getUShorts(triangles, numTriangles * 3); + } + bool hasBoneIndices = nif->getChar() != 0; + if (hasBoneIndices && numVertices && bonesPerVertex) + nif->getChars(boneIndices, numVertices * bonesPerVertex); + if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3) + { + nif->getChar(); // LOD level + nif->getBoolean(); // Global VB + } +} + void NiMorphData::read(NIFStream *nif) { int morphCount = nif->getInt(); diff --git a/components/nif/data.hpp b/components/nif/data.hpp index d0ea150e0..4d13afb9d 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -174,6 +174,7 @@ class NiSkinInstance : public Record { public: NiSkinDataPtr data; + NiSkinPartitionPtr partitions; NodePtr root; NodeList bones; @@ -200,6 +201,25 @@ public: Transformation trafo; std::vector bones; + NiSkinPartitionPtr partitions; + + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + +struct NiSkinPartition : public Record +{ + struct Partition + { + std::vector bones; + std::vector vertexMap; + std::vector weights; + std::vector> strips; + std::vector triangles; + std::vector boneIndices; + void read(NIFStream *nif); + }; + std::vector data; void read(NIFStream *nif) override; }; diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index b50b602d5..04cac535c 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -118,6 +118,7 @@ static std::map makeFactory() factory["NiFloatsExtraData"] = {&construct , RC_NiFloatsExtraData }; factory["NiStringPalette"] = {&construct , RC_NiStringPalette }; factory["NiBoolData"] = {&construct , RC_NiBoolData }; + factory["NiSkinPartition"] = {&construct , RC_NiSkinPartition }; return factory; } diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 0e8b80ffa..2a6be58e2 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -111,7 +111,8 @@ enum RecordType RC_NiFloatExtraData, RC_NiFloatsExtraData, RC_NiStringPalette, - RC_NiBoolData + RC_NiBoolData, + RC_NiSkinPartition }; /// Base class for all records diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index d0b3d00ab..2748f4073 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -144,6 +144,7 @@ class NiPalette; struct NiParticleModifier; struct NiLinesData; struct NiBoolData; +struct NiSkinPartition; using NodePtr = RecordPtrT; using ExtraPtr = RecordPtrT; @@ -168,6 +169,7 @@ using NiAutoNormalParticlesDataPtr = RecordPtrT; using NiPalettePtr = RecordPtrT; using NiParticleModifierPtr = RecordPtrT; using NiBoolDataPtr = RecordPtrT; +using NiSkinPartitionPtr = RecordPtrT; using NodeList = RecordListT; using PropertyList = RecordListT; From 96769ab4a5a1c8df30842497a2e94be7f83a780d Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sun, 8 Nov 2020 13:53:52 +0300 Subject: [PATCH 13/37] Try to reword unsupported NIF loading docs --- .../reference/modding/settings/models.rst | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/source/reference/modding/settings/models.rst b/docs/source/reference/modding/settings/models.rst index da56a1709..b0da374d6 100644 --- a/docs/source/reference/modding/settings/models.rst +++ b/docs/source/reference/modding/settings/models.rst @@ -8,20 +8,24 @@ load unsupported nif files :Range: True/False :Default: False -At the moment OpenMW's NIF loader is only tailored to load models -that can also load in Morrowind. +Allow the engine to load arbitrary NIF files as long as they appear to be valid. -However, you can enable its limited and experimental support for updates in -the definitions of record types from later NIF revisions by toggling on -this setting. +OpenMW has limited and **experimental** support for NIF files +that Morrowind itself cannot load, which normally goes unused. -You must keep in mind that loading unsupported NIF files may fail, -and the degree of this failure may vary. In milder cases, OpenMW will reject -the file anyway because it lacks a definition for a certain record type -that the file may use. In more severe cases OpenMW's -incomplete understanding of a record type can lead to memory corruption, -crashes or even freezes. Don't enable this if you're not sure that -you know what you're doing. +If enabled, this setting allows the NIF loader to make use of that functionality. + +.. warning:: + You must keep in mind that since the mentioned support is experimental, + loading unsupported NIF files may fail, and the degree of this failure may vary. + + In milder cases, OpenMW will reject the file anyway because + it lacks a definition for a certain record type that the file may use. + + In more severe cases OpenMW's incomplete understanding of a record type + can lead to memory corruption, freezes or even crashes. + + **Do not enable** this if you're not so sure that you know what you're doing. To help debug possible issues OpenMW will log its progress in loading every file that uses an unsupported NIF version. From 1d07361f08233dce6e02e73fae51859861430b14 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Mon, 9 Nov 2020 00:19:35 +0300 Subject: [PATCH 14/37] Read a few more NIF types BSXFlags, NiTransformData, BSBound, BSFadeNode, bhkBlendController --- components/nif/controller.cpp | 6 ++++++ components/nif/controller.hpp | 5 +++++ components/nif/extra.cpp | 6 ++++++ components/nif/extra.hpp | 7 +++++++ components/nif/niffile.cpp | 5 +++++ components/nif/record.hpp | 5 ++++- 6 files changed, 33 insertions(+), 1 deletion(-) diff --git a/components/nif/controller.cpp b/components/nif/controller.cpp index 07699239e..1f1dd01c1 100644 --- a/components/nif/controller.cpp +++ b/components/nif/controller.cpp @@ -228,4 +228,10 @@ namespace Nif mSources.post(nif); } + void bhkBlendController::read(NIFStream *nif) + { + Controller::read(nif); + nif->getUInt(); // Zero + } + } diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 54cf9bf8f..ac6783b8d 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -183,5 +183,10 @@ public: void post(NIFFile *nif) override; }; +struct bhkBlendController : public Controller +{ + void read(NIFStream *nif) override; +}; + } // Namespace #endif diff --git a/components/nif/extra.cpp b/components/nif/extra.cpp index d08e5d738..eeaf9d3ac 100644 --- a/components/nif/extra.cpp +++ b/components/nif/extra.cpp @@ -80,5 +80,11 @@ void NiFloatsExtraData::read(NIFStream *nif) nif->getFloats(data, num); } +void BSBound::read(NIFStream *nif) +{ + Extra::read(nif); + center = nif->getVector3(); + halfExtents = nif->getVector3(); +} } diff --git a/components/nif/extra.hpp b/components/nif/extra.hpp index 3be627004..5d7aa0c3b 100644 --- a/components/nif/extra.hpp +++ b/components/nif/extra.hpp @@ -109,5 +109,12 @@ struct NiFloatsExtraData : public Extra void read(NIFStream *nif) override; }; +struct BSBound : public Extra +{ + osg::Vec3f center, halfExtents; + + void read(NIFStream *nif) override; +}; + } // Namespace #endif diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 04cac535c..8214a8288 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -119,6 +119,11 @@ static std::map makeFactory() factory["NiStringPalette"] = {&construct , RC_NiStringPalette }; factory["NiBoolData"] = {&construct , RC_NiBoolData }; factory["NiSkinPartition"] = {&construct , RC_NiSkinPartition }; + factory["BSXFlags"] = {&construct , RC_BSXFlags }; + factory["BSBound"] = {&construct , RC_BSBound }; + factory["NiTransformData"] = {&construct , RC_NiKeyframeData }; + factory["BSFadeNode"] = {&construct , RC_NiNode }; + factory["bhkBlendController"] = {&construct , RC_bhkBlendController }; return factory; } diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 2a6be58e2..d3ee01fd4 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -112,7 +112,10 @@ enum RecordType RC_NiFloatsExtraData, RC_NiStringPalette, RC_NiBoolData, - RC_NiSkinPartition + RC_NiSkinPartition, + RC_BSXFlags, + RC_BSBound, + RC_bhkBlendController }; /// Base class for all records From b52357409011ce7b19bf9df8b554975a7fcf9e7c Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Mon, 9 Nov 2020 13:53:58 +0300 Subject: [PATCH 15/37] Remove shader visitor settings manager dependency --- apps/openmw/mwrender/renderingmanager.cpp | 1 + components/resource/scenemanager.cpp | 7 +++++++ components/resource/scenemanager.hpp | 3 +++ components/shader/shadervisitor.cpp | 13 ++++++++----- components/shader/shadervisitor.hpp | 4 ++++ 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 0aef614c8..8e5a01eaf 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -216,6 +216,7 @@ namespace MWRender resourceSystem->getSceneManager()->setNormalHeightMapPattern(Settings::Manager::getString("normal height map pattern", "Shaders")); 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")); osg::ref_ptr sceneRoot = new SceneUtil::LightManager; sceneRoot->setLightingMask(Mask_Lighting); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 571ea6d0d..44ba7e687 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -220,6 +220,7 @@ namespace Resource , mClampLighting(true) , mAutoUseNormalMaps(false) , mAutoUseSpecularMaps(false) + , mApplyLightingToEnvMaps(false) , mInstanceCache(new MultiObjectCache) , mSharedStateManager(new SharedStateManager) , mImageManager(imageManager) @@ -284,6 +285,11 @@ namespace Resource mSpecularMapPattern = pattern; } + void SceneManager::setApplyLightingToEnvMaps(bool apply) + { + mApplyLightingToEnvMaps = apply; + } + SceneManager::~SceneManager() { // this has to be defined in the .cpp file as we can't delete incomplete types @@ -770,6 +776,7 @@ namespace Resource shaderVisitor->setNormalHeightMapPattern(mNormalHeightMapPattern); shaderVisitor->setAutoUseSpecularMaps(mAutoUseSpecularMaps); shaderVisitor->setSpecularMapPattern(mSpecularMapPattern); + shaderVisitor->setApplyLightingToEnvMaps(mApplyLightingToEnvMaps); return shaderVisitor; } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 0ee32a9b7..8df556158 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -73,6 +73,8 @@ namespace Resource void setSpecularMapPattern(const std::string& pattern); + void setApplyLightingToEnvMaps(bool apply); + 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 @@ -156,6 +158,7 @@ namespace Resource std::string mNormalHeightMapPattern; bool mAutoUseSpecularMaps; std::string mSpecularMapPattern; + bool mApplyLightingToEnvMaps; osg::ref_ptr mInstanceCache; diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 0eab2fd89..e11f09930 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include "shadermanager.hpp" @@ -43,6 +42,7 @@ namespace Shader , mAllowedToModifyStateSets(true) , mAutoUseNormalMaps(false) , mAutoUseSpecularMaps(false) + , mApplyLightingToEnvMaps(false) , mShaderManager(shaderManager) , mImageManager(imageManager) , mDefaultVsTemplate(defaultVsTemplate) @@ -144,11 +144,9 @@ namespace Shader // Bump maps are off by default as well writableStateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON); } - else if (texName == "envMap") + else if (texName == "envMap" && mApplyLightingToEnvMaps) { - static const bool preLightEnv = Settings::Manager::getBool("apply lighting to environment maps", "Shaders"); - if (preLightEnv) - mRequirements.back().mShaderRequired = true; + mRequirements.back().mShaderRequired = true; } } else @@ -477,4 +475,9 @@ namespace Shader mSpecularMapPattern = pattern; } + void ShaderVisitor::setApplyLightingToEnvMaps(bool apply) + { + mApplyLightingToEnvMaps = apply; + } + } diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index bf1022180..3089845a3 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -38,6 +38,8 @@ namespace Shader void setSpecularMapPattern(const std::string& pattern); + void setApplyLightingToEnvMaps(bool apply); + void apply(osg::Node& node) override; void apply(osg::Drawable& drawable) override; @@ -59,6 +61,8 @@ namespace Shader bool mAutoUseSpecularMaps; std::string mSpecularMapPattern; + bool mApplyLightingToEnvMaps; + ShaderManager& mShaderManager; Resource::ImageManager& mImageManager; From c857588ee9234972918d727ed076269f43abcaa4 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Mon, 9 Nov 2020 14:22:48 +0300 Subject: [PATCH 16/37] Remove NIFFile settings manager dependency --- apps/niftest/niftest.cpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 1 + components/nif/niffile.cpp | 12 ++++++++---- components/nif/niffile.hpp | 4 ++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index f848ae330..e9484d5f5 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -132,6 +132,7 @@ int main(int argc, char **argv) if(!parseOptions (argc, argv, files)) return 1; + Nif::NIFFile::setLoadUnsupportedFiles(true); // std::cout << "Reading Files" << std::endl; for(std::vector::const_iterator it=files.begin(); it!=files.end(); ++it) { diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 0aef614c8..068b3e46c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -361,6 +361,7 @@ namespace MWRender mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater)); NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor); NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect); + Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models")); mNearClip = Settings::Manager::getFloat("near clip", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 8214a8288..06b4a89d0 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -5,8 +5,6 @@ #include #include -#include - namespace Nif { @@ -182,8 +180,7 @@ void NIFFile::parse(Files::IStreamPtr stream) } if (!supported) { - static const bool ignoreUnsupported = Settings::Manager::getBool("load unsupported nif files", "Models"); - if (ignoreUnsupported) + if (sLoadUnsupportedFiles) warn("Unsupported NIF version: " + printVersion(ver) + ". Proceed with caution!"); else fail("Unsupported NIF version: " + printVersion(ver)); @@ -331,4 +328,11 @@ bool NIFFile::getUseSkinning() const return mUseSkinning; } +bool NIFFile::sLoadUnsupportedFiles = false; + +void NIFFile::setLoadUnsupportedFiles(bool load) +{ + sLoadUnsupportedFiles = load; +} + } diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 9d8edac26..c6dd8af75 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -62,6 +62,8 @@ class NIFFile final : public File bool mUseSkinning = false; + static bool sLoadUnsupportedFiles; + /// Parse the file void parse(Files::IStreamPtr stream); @@ -149,6 +151,8 @@ public: /// Get the Bethesda version of the NIF format used unsigned int getBethVersion() const override { return bethVer; } + + static void setLoadUnsupportedFiles(bool load); }; using NIFFilePtr = std::shared_ptr; From b8ed3b0059758a9f0dfd87ef4dd1430e1ac0dca4 Mon Sep 17 00:00:00 2001 From: corristo Date: Tue, 10 Nov 2020 08:21:46 +0000 Subject: [PATCH 17/37] [macOS, CI] Update dependencies, don't use lz4 from Homebrew Also, enable double precision support for Bullet. --- CI/before_install.osx.sh | 5 +---- CI/before_script.osx.sh | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index fd77e0693..85434fa06 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -5,8 +5,5 @@ 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 -brew link --overwrite lz4 # overwrite system lz4; use brew -brew reinstall lz4 - -curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-ef2462c.zip -o ~/openmw-deps.zip +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 e11ef1b58..8f9be16e1 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -25,5 +25,6 @@ cmake \ -D BUILD_BSATOOL=TRUE \ -D BUILD_ESSIMPORTER=TRUE \ -D BUILD_NIFTEST=TRUE \ +-D BULLET_USE_DOUBLES=TRUE \ -G"Unix Makefiles" \ .. From f3f5dcb016507e3661583c40661137dc53200b83 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Tue, 10 Nov 2020 22:30:44 +0300 Subject: [PATCH 18/37] Read a few more NIF types NiFloatInterpolator, NiPoint3Interpolator, NiTransformInterpolator, NiBoolInterpolator Update a few existing controller records Update NiSkinInstance --- components/nif/controller.cpp | 96 +++++++++++++++++++++++++++++++- components/nif/controller.hpp | 42 ++++++++++++++ components/nif/data.cpp | 5 +- components/nif/niffile.cpp | 5 ++ components/nif/nifkey.hpp | 15 ++++- components/nif/record.hpp | 6 +- components/nif/recordptr.hpp | 7 +++ components/nifosg/controller.cpp | 53 ++++++++++++++++-- components/nifosg/controller.hpp | 41 ++++++++++++++ components/nifosg/nifloader.cpp | 50 +++++++++++++---- 10 files changed, 299 insertions(+), 21 deletions(-) diff --git a/components/nif/controller.cpp b/components/nif/controller.cpp index 1f1dd01c1..2cd61950b 100644 --- a/components/nif/controller.cpp +++ b/components/nif/controller.cpp @@ -92,6 +92,8 @@ namespace Nif void NiMaterialColorController::read(NIFStream *nif) { Controller::read(nif); + if (nif->getVersion() > NIFStream::generateVersion(10,1,0,103)) + interpolator.read(nif); // Two bits that correspond to the controlled material color. // 00: Ambient // 01: Diffuse @@ -101,12 +103,14 @@ namespace Nif targetColor = nif->getUShort() & 3; else targetColor = (flags >> 4) & 3; - data.read(nif); + if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103)) + data.read(nif); } void NiMaterialColorController::post(NIFFile *nif) { Controller::post(nif); + interpolator.post(nif); data.post(nif); } @@ -161,25 +165,33 @@ namespace Nif void NiKeyframeController::read(NIFStream *nif) { Controller::read(nif); - data.read(nif); + if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103)) + data.read(nif); + else + interpolator.read(nif); } void NiKeyframeController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); + interpolator.post(nif); } void NiFloatInterpController::read(NIFStream *nif) { Controller::read(nif); - data.read(nif); + if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103)) + data.read(nif); + else + interpolator.read(nif); } void NiFloatInterpController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); + interpolator.post(nif); } void NiGeomMorpherController::read(NIFStream *nif) @@ -189,13 +201,33 @@ namespace Nif /*bool updateNormals = !!*/nif->getUShort(); data.read(nif); if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW) + { /*bool alwaysActive = */nif->getChar(); // Always 0 + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,106)) + { + if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) + { + interpolators.read(nif); + if (nif->getVersion() >= NIFStream::generateVersion(10,2,0,0) && nif->getBethVersion() > 9) + { + unsigned int numUnknown = nif->getUInt(); + nif->skip(4 * numUnknown); + } + } + else + { + unsigned int numInterps = nif->getUInt(); + nif->skip(8 * numInterps); + } + } + } } void NiGeomMorpherController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); + interpolators.post(nif); } void NiVisController::read(NIFStream *nif) @@ -213,6 +245,8 @@ namespace Nif void NiFlipController::read(NIFStream *nif) { Controller::read(nif); + if (nif->getVersion() >= NIFStream::generateVersion(10,2,0,0)) + mInterpolator.read(nif); mTexSlot = nif->getUInt(); if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103)) { @@ -225,6 +259,7 @@ namespace Nif void NiFlipController::post(NIFFile *nif) { Controller::post(nif); + mInterpolator.post(nif); mSources.post(nif); } @@ -234,4 +269,59 @@ namespace Nif nif->getUInt(); // Zero } + void NiPoint3Interpolator::read(NIFStream *nif) + { + defaultVal = nif->getVector3(); + data.read(nif); + } + + void NiPoint3Interpolator::post(NIFFile *nif) + { + data.post(nif); + } + + void NiBoolInterpolator::read(NIFStream *nif) + { + defaultVal = nif->getBoolean(); + data.read(nif); + } + + void NiBoolInterpolator::post(NIFFile *nif) + { + data.post(nif); + } + + void NiFloatInterpolator::read(NIFStream *nif) + { + defaultVal = nif->getFloat(); + data.read(nif); + } + + void NiFloatInterpolator::post(NIFFile *nif) + { + data.post(nif); + } + + void NiTransformInterpolator::read(NIFStream *nif) + { + defaultPos = nif->getVector3(); + defaultRot = nif->getQuaternion(); + defaultScale = nif->getFloat(); + if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,109)) + { + if (!nif->getBoolean()) + defaultPos = osg::Vec3f(); + if (!nif->getBoolean()) + defaultRot = osg::Quat(); + if (!nif->getBoolean()) + defaultScale = 1.f; + } + data.read(nif); + } + + void NiTransformInterpolator::post(NIFFile *nif) + { + data.post(nif); + } + } diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index ac6783b8d..6b84d3749 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -83,6 +83,7 @@ using NiBSPArrayController = NiParticleSystemController; class NiMaterialColorController : public Controller { public: + NiPoint3InterpolatorPtr interpolator; NiPosDataPtr data; unsigned int targetColor; @@ -138,6 +139,7 @@ class NiKeyframeController : public Controller { public: NiKeyframeDataPtr data; + NiTransformInterpolatorPtr interpolator; void read(NIFStream *nif) override; void post(NIFFile *nif) override; @@ -146,6 +148,7 @@ public: struct NiFloatInterpController : public Controller { NiFloatDataPtr data; + NiFloatInterpolatorPtr interpolator; void read(NIFStream *nif) override; void post(NIFFile *nif) override; @@ -158,6 +161,7 @@ class NiGeomMorpherController : public Controller { public: NiMorphDataPtr data; + NiFloatInterpolatorList interpolators; void read(NIFStream *nif) override; void post(NIFFile *nif) override; @@ -175,6 +179,7 @@ public: class NiFlipController : public Controller { public: + NiFloatInterpolatorPtr mInterpolator; int mTexSlot; // NiTexturingProperty::TextureType float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources NiSourceTextureList mSources; @@ -188,5 +193,42 @@ struct bhkBlendController : public Controller void read(NIFStream *nif) override; }; +struct Interpolator : public Record { }; + +struct NiPoint3Interpolator : public Interpolator +{ + osg::Vec3f defaultVal; + NiPosDataPtr data; + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + +struct NiBoolInterpolator : public Interpolator +{ + bool defaultVal; + NiBoolDataPtr data; + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + +struct NiFloatInterpolator : public Interpolator +{ + float defaultVal; + NiFloatDataPtr data; + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + +struct NiTransformInterpolator : public Interpolator +{ + osg::Vec3f defaultPos; + osg::Quat defaultRot; + float defaultScale; + NiKeyframeDataPtr data; + + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + } // Namespace #endif diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 6bbab9786..1eb5c40fe 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -6,6 +6,8 @@ namespace Nif void NiSkinInstance::read(NIFStream *nif) { data.read(nif); + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,101)) + partitions.read(nif); root.read(nif); bones.read(nif); } @@ -13,6 +15,7 @@ void NiSkinInstance::read(NIFStream *nif) void NiSkinInstance::post(NIFFile *nif) { data.post(nif); + partitions.post(nif); root.post(nif); bones.post(nif); @@ -418,7 +421,7 @@ void NiMorphData::read(NIFStream *nif) for(int i = 0;i < morphCount;i++) { mMorphs[i].mKeyFrames = std::make_shared(); - mMorphs[i].mKeyFrames->read(nif, true); + mMorphs[i].mKeyFrames->read(nif, true, /*morph*/true); nif->getVector3s(mMorphs[i].mVertices, vertCount); } } diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 06b4a89d0..7915908d1 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -122,6 +122,11 @@ static std::map makeFactory() factory["NiTransformData"] = {&construct , RC_NiKeyframeData }; factory["BSFadeNode"] = {&construct , RC_NiNode }; factory["bhkBlendController"] = {&construct , RC_bhkBlendController }; + factory["NiFloatInterpolator"] = {&construct , RC_NiFloatInterpolator }; + factory["NiBoolInterpolator"] = {&construct , RC_NiBoolInterpolator }; + factory["NiPoint3Interpolator"] = {&construct , RC_NiPoint3Interpolator }; + factory["NiTransformController"] = {&construct , RC_NiKeyframeController }; + factory["NiTransformInterpolator"] = {&construct , RC_NiTransformInterpolator }; return factory; } diff --git a/components/nif/nifkey.hpp b/components/nif/nifkey.hpp index 4c10327e1..de2fa31c8 100644 --- a/components/nif/nifkey.hpp +++ b/components/nif/nifkey.hpp @@ -52,16 +52,27 @@ struct KeyMapT { MapType mKeys; //Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html) - void read(NIFStream *nif, bool force=false) + void read(NIFStream *nif, bool force = false, bool morph = false) { assert(nif); mInterpolationType = InterpolationType_Unknown; + if (morph && nif->getVersion() >= NIFStream::generateVersion(10,1,0,106)) + nif->getString(); // Frame name + size_t count = nif->getUInt(); - if(count == 0 && !force) + if (count == 0 && !force && !morph) return; + if (morph && nif->getVersion() > NIFStream::generateVersion(10,1,0,0)) + { + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,104) && + nif->getVersion() <= NIFStream::generateVersion(20,1,0,2) && nif->getBethVersion() < 10) + nif->getFloat(); // Legacy weight + return; + } + mKeys.clear(); mInterpolationType = nif->getUInt(); diff --git a/components/nif/record.hpp b/components/nif/record.hpp index d3ee01fd4..c5773643a 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -115,7 +115,11 @@ enum RecordType RC_NiSkinPartition, RC_BSXFlags, RC_BSBound, - RC_bhkBlendController + RC_bhkBlendController, + RC_NiFloatInterpolator, + RC_NiPoint3Interpolator, + RC_NiBoolInterpolator, + RC_NiTransformInterpolator, }; /// Base class for all records diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 2748f4073..b40a17583 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -145,6 +145,9 @@ struct NiParticleModifier; struct NiLinesData; struct NiBoolData; struct NiSkinPartition; +struct NiFloatInterpolator; +struct NiPoint3Interpolator; +struct NiTransformInterpolator; using NodePtr = RecordPtrT; using ExtraPtr = RecordPtrT; @@ -170,11 +173,15 @@ using NiPalettePtr = RecordPtrT; using NiParticleModifierPtr = RecordPtrT; using NiBoolDataPtr = RecordPtrT; using NiSkinPartitionPtr = RecordPtrT; +using NiFloatInterpolatorPtr = RecordPtrT; +using NiPoint3InterpolatorPtr = RecordPtrT; +using NiTransformInterpolatorPtr = RecordPtrT; using NodeList = RecordListT; using PropertyList = RecordListT; using ExtraList = RecordListT; using NiSourceTextureList = RecordListT; +using NiFloatInterpolatorList = RecordListT; } // Namespace #endif diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 9c654b9d1..c0f7522d5 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -92,6 +92,26 @@ KeyframeController::KeyframeController(const Nif::NiKeyframeData *data) { } +KeyframeController::KeyframeController(const Nif::NiTransformInterpolator* interpolator) + : mRotations(interpolator->data->mRotations, interpolator->defaultRot) + , mXRotations(interpolator->data->mXRotations, 0.f) + , mYRotations(interpolator->data->mYRotations, 0.f) + , mZRotations(interpolator->data->mZRotations, 0.f) + , mTranslations(interpolator->data->mTranslations, interpolator->defaultPos) + , mScales(interpolator->data->mScales, interpolator->defaultScale) +{ +} + +KeyframeController::KeyframeController(const float scale, const osg::Vec3f& pos, const osg::Quat& rot) + : mRotations(Nif::QuaternionKeyMapPtr(), rot) + , mXRotations(Nif::FloatKeyMapPtr(), 0.f) + , mYRotations(Nif::FloatKeyMapPtr(), 0.f) + , mZRotations(Nif::FloatKeyMapPtr(), 0.f) + , mTranslations(Nif::Vector3KeyMapPtr(), pos) + , mScales(Nif::FloatKeyMapPtr(), scale) +{ +} + osg::Quat KeyframeController::getXYZRotation(float time) const { float xrot = 0, yrot = 0, zrot = 0; @@ -313,6 +333,11 @@ RollController::RollController(const Nif::NiFloatData *data) { } +RollController::RollController(const Nif::NiFloatInterpolator* interpolator) + : mData(interpolator) +{ +} + RollController::RollController(const RollController ©, const osg::CopyOp ©op) : osg::NodeCallback(copy, copyop) , Controller(copy) @@ -344,6 +369,10 @@ void RollController::operator() (osg::Node* node, osg::NodeVisitor* nv) } } +AlphaController::AlphaController() +{ +} + AlphaController::AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial) : mData(data->mKeyList, 1.f) , mBaseMaterial(baseMaterial) @@ -351,7 +380,9 @@ AlphaController::AlphaController(const Nif::NiFloatData *data, const osg::Materi } -AlphaController::AlphaController() +AlphaController::AlphaController(const Nif::NiFloatInterpolator* interpolator, const osg::Material* baseMaterial) + : mData(interpolator) + , mBaseMaterial(baseMaterial) { } @@ -379,6 +410,10 @@ void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) } } +MaterialColorController::MaterialColorController() +{ +} + MaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial) : mData(data->mKeyList, osg::Vec3f(1,1,1)) , mTargetColor(color) @@ -386,7 +421,10 @@ MaterialColorController::MaterialColorController(const Nif::NiPosData *data, Tar { } -MaterialColorController::MaterialColorController() +MaterialColorController::MaterialColorController(const Nif::NiPoint3Interpolator* interpolator, TargetColor color, const osg::Material* baseMaterial) + : mData(interpolator) + , mTargetColor(color) + , mBaseMaterial(baseMaterial) { } @@ -448,6 +486,8 @@ FlipController::FlipController(const Nif::NiFlipController *ctrl, const std::vec , mDelta(ctrl->mDelta) , mTextures(textures) { + if (!ctrl->mInterpolator.empty()) + mData = ctrl->mInterpolator.getPtr(); } FlipController::FlipController(int texSlot, float delta, const std::vector >& textures) @@ -463,14 +503,19 @@ FlipController::FlipController(const FlipController ©, const osg::CopyOp &co , mTexSlot(copy.mTexSlot) , mDelta(copy.mDelta) , mTextures(copy.mTextures) + , mData(copy.mData) { } void FlipController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv) { - if (hasInput() && mDelta != 0 && !mTextures.empty()) + if (hasInput() && !mTextures.empty()) { - int curTexture = int(getInputValue(nv) / mDelta) % mTextures.size(); + int curTexture = 0; + if (mDelta != 0) + curTexture = int(getInputValue(nv) / mDelta) % mTextures.size(); + else + curTexture = int(mData.interpKey(getInputValue(nv))) % mTextures.size(); stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]); } } diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index c087e635f..fa154d207 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -58,6 +58,8 @@ namespace NifOsg using ValueT = typename MapT::ValueType; ValueInterpolator() = default; + ValueInterpolator(const Nif::NiFloatInterpolator* interpolator) = delete; + ValueInterpolator(const Nif::NiPoint3Interpolator* interpolator) = delete; ValueInterpolator(std::shared_ptr keys, ValueT defaultVal = ValueT()) : mKeys(keys) @@ -161,6 +163,34 @@ namespace NifOsg using Vec3Interpolator = ValueInterpolator; using Vec4Interpolator = ValueInterpolator; + template<> + inline FloatInterpolator::ValueInterpolator(const Nif::NiFloatInterpolator* interpolator) + : mDefaultVal(interpolator->defaultVal) + { + if (interpolator->data.empty()) + return; + mKeys = interpolator->data->mKeyList; + if (mKeys) + { + mLastLowKey = mKeys->mKeys.end(); + mLastHighKey = mKeys->mKeys.end(); + } + } + + template<> + inline Vec3Interpolator::ValueInterpolator(const Nif::NiPoint3Interpolator* interpolator) + : mDefaultVal(interpolator->defaultVal) + { + if (interpolator->data.empty()) + return; + mKeys = interpolator->data->mKeyList; + if (mKeys) + { + mLastLowKey = mKeys->mKeys.end(); + mLastHighKey = mKeys->mKeys.end(); + } + } + class ControllerFunction : public SceneUtil::ControllerFunction { private: @@ -203,7 +233,14 @@ namespace NifOsg class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller { public: + // This is used if there's no interpolator but there is data (Morrowind meshes). KeyframeController(const Nif::NiKeyframeData *data); + // This is used if the interpolator has data. + KeyframeController(const Nif::NiTransformInterpolator* interpolator); + // This is used if there are default values available (e.g. from a data-less interpolator). + // If there's neither keyframe data nor an interpolator a KeyframeController must not be created. + KeyframeController(const float scale, const osg::Vec3f& pos, const osg::Quat& rot); + KeyframeController(); KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop); @@ -272,6 +309,7 @@ namespace NifOsg public: RollController(const Nif::NiFloatData *data); + RollController(const Nif::NiFloatInterpolator* interpolator); RollController() = default; RollController(const RollController& copy, const osg::CopyOp& copyop); @@ -287,6 +325,7 @@ namespace NifOsg osg::ref_ptr mBaseMaterial; public: AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial); + AlphaController(const Nif::NiFloatInterpolator* interpolator, const osg::Material* baseMaterial); AlphaController(); AlphaController(const AlphaController& copy, const osg::CopyOp& copyop); @@ -308,6 +347,7 @@ namespace NifOsg Emissive = 3 }; MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial); + MaterialColorController(const Nif::NiPoint3Interpolator* interpolator, TargetColor color, const osg::Material* baseMaterial); MaterialColorController(); MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop); @@ -329,6 +369,7 @@ namespace NifOsg int mTexSlot{0}; float mDelta{0.f}; std::vector > mTextures; + FloatInterpolator mData; public: FlipController(const Nif::NiFlipController* ctrl, const std::vector >& textures); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 805283b42..030252412 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -281,10 +281,10 @@ namespace NifOsg const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if(key->data.empty()) + if (key->data.empty() && key->interpolator.empty()) continue; - osg::ref_ptr callback(new NifOsg::KeyframeController(key->data.getPtr())); + osg::ref_ptr callback(handleKeyframeController(key)); callback->setFunction(std::shared_ptr(new NifOsg::ControllerFunction(key))); if (!target.mKeyframeControllers.emplace(strdata->string, callback).second) @@ -725,6 +725,24 @@ namespace NifOsg } } + static osg::ref_ptr handleKeyframeController(const Nif::NiKeyframeController* keyctrl) + { + osg::ref_ptr ctrl; + if (!keyctrl->interpolator.empty()) + { + const Nif::NiTransformInterpolator* interp = keyctrl->interpolator.getPtr(); + if (!interp->data.empty()) + ctrl = new NifOsg::KeyframeController(interp); + else + ctrl = new NifOsg::KeyframeController(interp->defaultScale, interp->defaultPos, interp->defaultRot); + } + else if (!keyctrl->data.empty()) + { + ctrl = new NifOsg::KeyframeController(keyctrl->data.getPtr()); + } + return ctrl; + } + void handleNodeControllers(const Nif::Node* nifNode, osg::Node* node, int animflags, bool& isAnimated) { for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) @@ -734,9 +752,9 @@ namespace NifOsg if (ctrl->recType == Nif::RC_NiKeyframeController) { const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if (key->data.empty()) + if (key->data.empty() && key->interpolator.empty()) continue; - osg::ref_ptr callback(new KeyframeController(key->data.getPtr())); + osg::ref_ptr callback(handleKeyframeController(key)); setupController(key, callback, animflags); node->addUpdateCallback(callback); isAnimated = true; @@ -763,9 +781,13 @@ namespace NifOsg else if (ctrl->recType == Nif::RC_NiRollController) { const Nif::NiRollController *rollctrl = static_cast(ctrl.getPtr()); - if (rollctrl->data.empty()) + if (rollctrl->data.empty() && rollctrl->interpolator.empty()) continue; - osg::ref_ptr callback(new RollController(rollctrl->data.getPtr())); + osg::ref_ptr callback; + if (!rollctrl->interpolator.empty()) + callback = new RollController(rollctrl->interpolator.getPtr()); + else // if (!rollctrl->data.empty()) + callback = new RollController(rollctrl->data.getPtr()); setupController(rollctrl, callback, animflags); node->addUpdateCallback(callback); isAnimated = true; @@ -791,19 +813,27 @@ namespace NifOsg if (ctrl->recType == Nif::RC_NiAlphaController) { const Nif::NiAlphaController* alphactrl = static_cast(ctrl.getPtr()); - if (alphactrl->data.empty()) + if (alphactrl->data.empty() && alphactrl->interpolator.empty()) continue; - osg::ref_ptr osgctrl(new AlphaController(alphactrl->data.getPtr(), baseMaterial)); + osg::ref_ptr osgctrl; + if (!alphactrl->interpolator.empty()) + osgctrl = new AlphaController(alphactrl->interpolator.getPtr(), baseMaterial); + else // if (!alphactrl->data.empty()) + osgctrl = new AlphaController(alphactrl->data.getPtr(), baseMaterial); setupController(alphactrl, osgctrl, animflags); composite->addController(osgctrl); } else if (ctrl->recType == Nif::RC_NiMaterialColorController) { const Nif::NiMaterialColorController* matctrl = static_cast(ctrl.getPtr()); - if (matctrl->data.empty()) + if (matctrl->data.empty() && matctrl->interpolator.empty()) continue; + osg::ref_ptr osgctrl; auto targetColor = static_cast(matctrl->targetColor); - osg::ref_ptr osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor, baseMaterial)); + if (!matctrl->interpolator.empty()) + osgctrl = new MaterialColorController(matctrl->interpolator.getPtr(), targetColor, baseMaterial); + else // if (!matctrl->data.empty()) + osgctrl = new MaterialColorController(matctrl->data.getPtr(), targetColor, baseMaterial); setupController(matctrl, osgctrl, animflags); composite->addController(osgctrl); } From a46699fb1e91a0fa21cdb2a1c78eaa134246d5ee Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Tue, 10 Nov 2020 23:23:11 +0300 Subject: [PATCH 19/37] Handle NiGeomMorpherController interpolator list --- components/nif/controller.cpp | 1 + components/nifosg/controller.cpp | 21 ++++++++++++++++++--- components/nifosg/controller.hpp | 2 +- components/nifosg/nifloader.cpp | 2 +- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/components/nif/controller.cpp b/components/nif/controller.cpp index 2cd61950b..1e909120e 100644 --- a/components/nif/controller.cpp +++ b/components/nif/controller.cpp @@ -216,6 +216,7 @@ namespace Nif } else { + // TODO: handle weighted interpolators unsigned int numInterps = nif->getUInt(); nif->skip(8 * numInterps); } diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index c0f7522d5..64e9f7de6 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -197,10 +197,25 @@ GeomMorpherController::GeomMorpherController(const GeomMorpherController ©, { } -GeomMorpherController::GeomMorpherController(const Nif::NiMorphData *data) +GeomMorpherController::GeomMorpherController(const Nif::NiGeomMorpherController* ctrl) { - for (unsigned int i=0; imMorphs.size(); ++i) - mKeyFrames.emplace_back(data->mMorphs[i].mKeyFrames); + if (ctrl->interpolators.length() == 0) + { + if (ctrl->data.empty()) + return; + for (const auto& morph : ctrl->data->mMorphs) + mKeyFrames.emplace_back(morph.mKeyFrames); + } + else + { + for (size_t i = 0; i < ctrl->interpolators.length(); ++i) + { + if (!ctrl->interpolators[i].empty()) + mKeyFrames.emplace_back(ctrl->interpolators[i].getPtr()); + else + mKeyFrames.emplace_back(); + } + } } void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable) diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index fa154d207..dd3068ac8 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -218,7 +218,7 @@ namespace NifOsg class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller { public: - GeomMorpherController(const Nif::NiMorphData* data); + GeomMorpherController(const Nif::NiGeomMorpherController* ctrl); GeomMorpherController(); GeomMorpherController(const GeomMorpherController& copy, const osg::CopyOp& copyop); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 030252412..a5a61b317 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1244,7 +1244,7 @@ namespace NifOsg continue; drawable = handleMorphGeometry(nimorphctrl, geom, parentNode, composite, boundTextures, animflags); - osg::ref_ptr morphctrl = new GeomMorpherController(nimorphctrl->data.getPtr()); + osg::ref_ptr morphctrl = new GeomMorpherController(nimorphctrl); setupController(ctrl.getPtr(), morphctrl, animflags); drawable->setUpdateCallback(morphctrl); break; From 5d046bc95dfc9e848b03772f4e53521fd2a1c343 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 11 Nov 2020 14:58:06 +0000 Subject: [PATCH 20/37] Mark override --- components/sceneutil/shadowsbin.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sceneutil/shadowsbin.hpp b/components/sceneutil/shadowsbin.hpp index 6302d5974..6c35e1779 100644 --- a/components/sceneutil/shadowsbin.hpp +++ b/components/sceneutil/shadowsbin.hpp @@ -21,7 +21,7 @@ namespace SceneUtil ShadowsBin(); ShadowsBin(const ShadowsBin& rhs, const osg::CopyOp& copyop) : osgUtil::RenderBin(rhs, copyop), mStateSet(rhs.mStateSet) {} - virtual void sortImplementation(); + void sortImplementation() override; struct State { From 72f7e6a702cef495159869e3bde2de1d4627c7b8 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 12 Nov 2020 00:26:30 +0000 Subject: [PATCH 21/37] Handle all shadow alpha uniforms in shadowsbin --- apps/openmw/mwrender/animation.cpp | 12 ++------ components/sceneutil/mwshadowtechnique.cpp | 11 +------- components/sceneutil/mwshadowtechnique.hpp | 1 - components/sceneutil/shadowsbin.cpp | 33 ++++++++++++++-------- components/sceneutil/shadowsbin.hpp | 7 +++-- components/shader/shadermanager.cpp | 10 ------- components/shader/shadermanager.hpp | 6 ---- components/shader/shadervisitor.cpp | 28 +----------------- components/shader/shadervisitor.hpp | 1 - 9 files changed, 29 insertions(+), 80 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 29645a7c1..94a521960 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -37,8 +36,6 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" @@ -493,9 +490,8 @@ namespace MWRender class TransparencyUpdater : public SceneUtil::StateSetUpdater { public: - TransparencyUpdater(const float alpha, osg::ref_ptr shadowUniform) + TransparencyUpdater(const float alpha) : mAlpha(alpha) - , mShadowUniform(shadowUniform) { } @@ -509,9 +505,6 @@ namespace MWRender { osg::BlendFunc* blendfunc (new osg::BlendFunc); stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); - // TODO: don't do this anymore once custom shadow renderbin is handling it - if (mShadowUniform) - stateset->addUniform(mShadowUniform); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); @@ -533,7 +526,6 @@ namespace MWRender private: float mAlpha; - osg::ref_ptr mShadowUniform; }; struct Animation::AnimSource @@ -1773,7 +1765,7 @@ namespace MWRender { if (mTransparencyUpdater == nullptr) { - mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform()); + mTransparencyUpdater = new TransparencyUpdater(alpha); mObjectRoot->addCullCallback(mTransparencyUpdater); } else diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index c8a241c36..06a1efd2a 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -885,15 +885,6 @@ void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & sh _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)); - - _shadowMapAlphaTestDisableUniform = shaderManager.getShadowMapAlphaTestDisableUniform(); - _shadowMapAlphaTestDisableUniform->setName("alphaTestShadows"); - _shadowMapAlphaTestDisableUniform->setType(osg::Uniform::BOOL); - _shadowMapAlphaTestDisableUniform->set(false); - - shaderManager.getShadowMapAlphaTestEnableUniform()->setName("alphaTestShadows"); - shaderManager.getShadowMapAlphaTestEnableUniform()->setType(osg::Uniform::BOOL); - shaderManager.getShadowMapAlphaTestEnableUniform()->set(true); } MWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData(osgUtil::CullVisitor* /*cv*/) @@ -1595,7 +1586,7 @@ void MWShadowTechnique::createShaders() // 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)); - _shadowCastingStateSet->addUniform(_shadowMapAlphaTestDisableUniform); + _shadowCastingStateSet->addUniform(new osg::Uniform("alphaTestShadows", false)); osg::ref_ptr depth = new osg::Depth; depth->setWriteMask(true); _shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); diff --git a/components/sceneutil/mwshadowtechnique.hpp b/components/sceneutil/mwshadowtechnique.hpp index 23a8e009a..a7208cfa6 100644 --- a/components/sceneutil/mwshadowtechnique.hpp +++ b/components/sceneutil/mwshadowtechnique.hpp @@ -288,7 +288,6 @@ namespace SceneUtil { osg::ref_ptr _debugHud; osg::ref_ptr _castingProgram; - osg::ref_ptr _shadowMapAlphaTestDisableUniform; }; } diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp index e0a141ffb..4c0c78cab 100644 --- a/components/sceneutil/shadowsbin.cpp +++ b/components/sceneutil/shadowsbin.cpp @@ -42,11 +42,16 @@ namespace SceneUtil ShadowsBin::ShadowsBin() { - mStateSet = new osg::StateSet; - mStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false)); + mNoTestStateSet = new osg::StateSet; + mNoTestStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false)); + mNoTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", false)); + + mShaderAlphaTestStateSet = new osg::StateSet; + mShaderAlphaTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", true)); + mShaderAlphaTestStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); } -bool ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set& uninterestingCache) +StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set& uninterestingCache) { std::vector return_path; State state; @@ -88,7 +93,7 @@ bool ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered } if (!state.needShadows()) - return true; + return nullptr; if (!state.needTexture() && !state.mImportantState) { @@ -97,9 +102,12 @@ bool ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered leaf->_parent = root; root->_leaves.push_back(leaf); } - return true; + return nullptr; } - return false; + + if (state.mAlphaBlend) + return sg->find_or_insert(mShaderAlphaTestStateSet); + return sg; } bool ShadowsBin::State::needShadows() const @@ -128,9 +136,9 @@ void ShadowsBin::sortImplementation() if (!root->_parent) return; } - root = root->find_or_insert(mStateSet.get()); + StateGraph* noTestRoot = root->find_or_insert(mNoTestStateSet.get()); // root is now a stategraph with useDiffuseMapForShadowAlpha disabled but minimal other state - root->_leaves.reserve(_stateGraphList.size()); + noTestRoot->_leaves.reserve(_stateGraphList.size()); StateGraphList newList; std::unordered_set uninterestingCache; for (StateGraph* graph : _stateGraphList) @@ -138,11 +146,12 @@ 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. - if (!cullStateGraph(graph, root, uninterestingCache)) - newList.push_back(graph); + StateGraph* graphToAdd = cullStateGraph(graph, noTestRoot, uninterestingCache); + if (graphToAdd) + newList.push_back(graphToAdd); } - if (!root->_leaves.empty()) - newList.push_back(root); + if (!noTestRoot->_leaves.empty()) + newList.push_back(noTestRoot); _stateGraphList = newList; } diff --git a/components/sceneutil/shadowsbin.hpp b/components/sceneutil/shadowsbin.hpp index 6c35e1779..13f68ec90 100644 --- a/components/sceneutil/shadowsbin.hpp +++ b/components/sceneutil/shadowsbin.hpp @@ -15,11 +15,12 @@ namespace SceneUtil class ShadowsBin : public osgUtil::RenderBin { private: - osg::ref_ptr mStateSet; + osg::ref_ptr mNoTestStateSet; + osg::ref_ptr mShaderAlphaTestStateSet; public: META_Object(SceneUtil, ShadowsBin) ShadowsBin(); - ShadowsBin(const ShadowsBin& rhs, const osg::CopyOp& copyop) : osgUtil::RenderBin(rhs, copyop), mStateSet(rhs.mStateSet) {} + ShadowsBin(const ShadowsBin& rhs, const osg::CopyOp& copyop) : osgUtil::RenderBin(rhs, copyop), mNoTestStateSet(rhs.mNoTestStateSet), mShaderAlphaTestStateSet(rhs.mShaderAlphaTestStateSet) {} void sortImplementation() override; @@ -39,7 +40,7 @@ namespace SceneUtil bool interesting() const { return !needShadows() || needTexture() || mAlphaBlendOverride || mAlphaTestOverride || mMaterialOverride || mImportantState; } }; - bool cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set& uninteresting); + osgUtil::StateGraph* cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set& uninteresting); static void addPrototype(const std::string& name) { diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index bfaa11282..788a8720b 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -384,14 +384,4 @@ namespace Shader program.second->releaseGLObjects(state); } - const osg::ref_ptr ShaderManager::getShadowMapAlphaTestEnableUniform() - { - return mShadowMapAlphaTestEnableUniform; - } - - const osg::ref_ptr ShaderManager::getShadowMapAlphaTestDisableUniform() - { - return mShadowMapAlphaTestDisableUniform; - } - } diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index ed5bbc907..13db30b01 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -43,9 +43,6 @@ namespace Shader void releaseGLObjects(osg::State* state); - const osg::ref_ptr getShadowMapAlphaTestEnableUniform(); - const osg::ref_ptr getShadowMapAlphaTestDisableUniform(); - private: std::string mPath; @@ -63,9 +60,6 @@ namespace Shader ProgramMap mPrograms; std::mutex mMutex; - - const osg::ref_ptr mShadowMapAlphaTestEnableUniform = new osg::Uniform(); - const osg::ref_ptr mShadowMapAlphaTestDisableUniform = new osg::Uniform(); }; bool parseFors(std::string& source, const std::string& templateName); diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index c30307f29..f17c4a80c 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -1,7 +1,5 @@ #include "shadervisitor.hpp" -#include -#include #include #include #include @@ -25,7 +23,6 @@ namespace Shader : mShaderRequired(false) , mColorMode(0) , mMaterialOverridden(false) - , mBlendFuncOverridden(false) , mNormalHeight(false) , mTexStageRequiringTangents(-1) , mNode(nullptr) @@ -233,14 +230,11 @@ namespace Shader if (!writableStateSet) writableStateSet = getWritableStateSet(node); // We probably shouldn't construct a new version of this each time as Uniforms use pointer comparison for early-out. - // Also it should probably belong to the shader manager + // Also it should probably belong to the shader manager or be applied by the shadows bin writableStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true)); } } - bool alphaSettingsChanged = false; - bool alphaTestShadows = false; - const osg::StateSet::AttributeList& attributes = stateset->getAttributeList(); for (osg::StateSet::AttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it) { @@ -284,28 +278,8 @@ namespace Shader mRequirements.back().mColorMode = colorMode; } } - else if (it->first.first == osg::StateAttribute::BLENDFUNC) - { - if (!mRequirements.back().mBlendFuncOverridden || it->second.second & osg::StateAttribute::PROTECTED) - { - if (it->second.second & osg::StateAttribute::OVERRIDE) - mRequirements.back().mBlendFuncOverridden = true; - - const osg::BlendFunc* blend = static_cast(it->second.first.get()); - if (blend->getSource() == osg::BlendFunc::SRC_ALPHA || blend->getSource() == osg::BlendFunc::SRC_COLOR) - alphaTestShadows = true; - alphaSettingsChanged = true; - } - } // Eventually, move alpha testing to discard in shader adn remove deprecated state here } - // we don't need to check for glEnable/glDisable of blending as we always set it at the same time - if (alphaSettingsChanged) - { - if (!writableStateSet) - writableStateSet = getWritableStateSet(node); - writableStateSet->addUniform(alphaTestShadows ? mShaderManager.getShadowMapAlphaTestEnableUniform() : mShaderManager.getShadowMapAlphaTestDisableUniform()); - } } void ShaderVisitor::pushRequirements(osg::Node& node) diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index bf1022180..ae157b976 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -75,7 +75,6 @@ namespace Shader int mColorMode; bool mMaterialOverridden; - bool mBlendFuncOverridden; bool mNormalHeight; // true if normal map has height info in alpha channel From 296dce470a6b2fe26894daaf5d9b3b3e6923a787 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 12 Nov 2020 16:58:29 +0000 Subject: [PATCH 22/37] Spelling fix --- components/sceneutil/shadowsbin.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sceneutil/shadowsbin.hpp b/components/sceneutil/shadowsbin.hpp index 13f68ec90..86fd47cc0 100644 --- a/components/sceneutil/shadowsbin.hpp +++ b/components/sceneutil/shadowsbin.hpp @@ -11,7 +11,7 @@ namespace osg namespace SceneUtil { - /// renderbin which culls redundent state for shadows rendering + /// renderbin which culls redundant state for shadow map rendering class ShadowsBin : public osgUtil::RenderBin { private: From cdbf19a5086e33ee790be9854a366b4ef65e928d Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 12 Nov 2020 17:04:23 +0000 Subject: [PATCH 23/37] Tidy up run-on lines --- components/sceneutil/shadowsbin.hpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/components/sceneutil/shadowsbin.hpp b/components/sceneutil/shadowsbin.hpp index 86fd47cc0..cc6fd3525 100644 --- a/components/sceneutil/shadowsbin.hpp +++ b/components/sceneutil/shadowsbin.hpp @@ -20,13 +20,26 @@ namespace SceneUtil public: META_Object(SceneUtil, ShadowsBin) ShadowsBin(); - ShadowsBin(const ShadowsBin& rhs, const osg::CopyOp& copyop) : osgUtil::RenderBin(rhs, copyop), mNoTestStateSet(rhs.mNoTestStateSet), mShaderAlphaTestStateSet(rhs.mShaderAlphaTestStateSet) {} + ShadowsBin(const ShadowsBin& rhs, const osg::CopyOp& copyop) + : osgUtil::RenderBin(rhs, copyop) + , mNoTestStateSet(rhs.mNoTestStateSet) + , mShaderAlphaTestStateSet(rhs.mShaderAlphaTestStateSet) + {} void sortImplementation() override; struct State { - State():mAlphaBlend(false),mAlphaBlendOverride(false),mAlphaTest(false),mAlphaTestOverride(false),mMaterial(nullptr),mMaterialOverride(false),mImportantState(false){} + State() + : mAlphaBlend(false) + , mAlphaBlendOverride(false) + , mAlphaTest(false) + , mAlphaTestOverride(false) + , mMaterial(nullptr) + , mMaterialOverride(false) + , mImportantState(false) + {} + bool mAlphaBlend; bool mAlphaBlendOverride; bool mAlphaTest; @@ -37,7 +50,10 @@ namespace SceneUtil bool needTexture() const { return mAlphaBlend || mAlphaTest; } 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; } + bool interesting() const + { + return !needShadows() || needTexture() || mAlphaBlendOverride || mAlphaTestOverride || mMaterialOverride || mImportantState; + } }; osgUtil::StateGraph* cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set& uninteresting); From 55f65752fd3b39bb12660c4f77da3f53bf044ac1 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 12 Nov 2020 17:55:10 +0000 Subject: [PATCH 24/37] Don't bind unnecessary colour buffer when drawing shadow maps --- components/sceneutil/mwshadowtechnique.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 76eaf0bb7..3f60f79b9 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -555,6 +555,7 @@ MWShadowTechnique::ShadowData::ShadowData(MWShadowTechnique::ViewDependentData* _camera = new osg::Camera; _camera->setName("ShadowCamera"); _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); + _camera->setImplicitBufferAttachmentMask(0, 0); //_camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); _camera->setClearColor(osg::Vec4(0.0f,0.0f,0.0f,0.0f)); From d8d435196226f4750d9797936d8fc939bdb2db60 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 12 Nov 2020 19:45:32 +0100 Subject: [PATCH 25/37] fix MSVC2017 builds --- components/nifosg/controller.hpp | 63 +++++++++++++++++--------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index dd3068ac8..f08f28612 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -10,6 +10,7 @@ #include #include +#include #include @@ -58,8 +59,38 @@ namespace NifOsg using ValueT = typename MapT::ValueType; ValueInterpolator() = default; - ValueInterpolator(const Nif::NiFloatInterpolator* interpolator) = delete; - ValueInterpolator(const Nif::NiPoint3Interpolator* interpolator) = delete; + + template + inline ValueInterpolator(typename std::enable_if< + std::is_same::value, + const Nif::NiFloatInterpolator + >::type * interpolator) : mDefaultVal(interpolator->defaultVal) + { + if (interpolator->data.empty()) + return; + mKeys = interpolator->data->mKeyList; + if (mKeys) + { + mLastLowKey = mKeys->mKeys.end(); + mLastHighKey = mKeys->mKeys.end(); + } + }; + + template + inline ValueInterpolator(typename std::enable_if< + std::is_same::value, + const Nif::NiPoint3Interpolator + >::type * interpolator) : mDefaultVal(interpolator->defaultVal) + { + if (interpolator->data.empty()) + return; + mKeys = interpolator->data->mKeyList; + if (mKeys) + { + mLastLowKey = mKeys->mKeys.end(); + mLastHighKey = mKeys->mKeys.end(); + } + } ValueInterpolator(std::shared_ptr keys, ValueT defaultVal = ValueT()) : mKeys(keys) @@ -163,34 +194,6 @@ namespace NifOsg using Vec3Interpolator = ValueInterpolator; using Vec4Interpolator = ValueInterpolator; - template<> - inline FloatInterpolator::ValueInterpolator(const Nif::NiFloatInterpolator* interpolator) - : mDefaultVal(interpolator->defaultVal) - { - if (interpolator->data.empty()) - return; - mKeys = interpolator->data->mKeyList; - if (mKeys) - { - mLastLowKey = mKeys->mKeys.end(); - mLastHighKey = mKeys->mKeys.end(); - } - } - - template<> - inline Vec3Interpolator::ValueInterpolator(const Nif::NiPoint3Interpolator* interpolator) - : mDefaultVal(interpolator->defaultVal) - { - if (interpolator->data.empty()) - return; - mKeys = interpolator->data->mKeyList; - if (mKeys) - { - mLastLowKey = mKeys->mKeys.end(); - mLastHighKey = mKeys->mKeys.end(); - } - } - class ControllerFunction : public SceneUtil::ControllerFunction { private: From d8897c4509ac06e9b59685c32c019320e7b1fa87 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 12 Nov 2020 20:46:45 +0100 Subject: [PATCH 26/37] remove inline and use enable_if_t --- components/nifosg/controller.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index f08f28612..17ad9564c 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -61,10 +61,10 @@ namespace NifOsg ValueInterpolator() = default; template - inline ValueInterpolator(typename std::enable_if< + ValueInterpolator(typename std::enable_if_t< std::is_same::value, const Nif::NiFloatInterpolator - >::type * interpolator) : mDefaultVal(interpolator->defaultVal) + > * interpolator) : mDefaultVal(interpolator->defaultVal) { if (interpolator->data.empty()) return; @@ -77,10 +77,10 @@ namespace NifOsg }; template - inline ValueInterpolator(typename std::enable_if< + ValueInterpolator(typename std::enable_if_t< std::is_same::value, const Nif::NiPoint3Interpolator - >::type * interpolator) : mDefaultVal(interpolator->defaultVal) + > * interpolator) : mDefaultVal(interpolator->defaultVal) { if (interpolator->data.empty()) return; From fcfd340c697afdaee2fd989e7d782283c929eade Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 12 Nov 2020 23:23:40 +0000 Subject: [PATCH 27/37] Actually copy alpha blended drawables to the new stategraph --- components/sceneutil/shadowsbin.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp index 4c0c78cab..520ad0362 100644 --- a/components/sceneutil/shadowsbin.cpp +++ b/components/sceneutil/shadowsbin.cpp @@ -106,7 +106,15 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un } if (state.mAlphaBlend) - return sg->find_or_insert(mShaderAlphaTestStateSet); + { + sg_new = sg->find_or_insert(mShaderAlphaTestStateSet); + for (RenderLeaf* leaf : sg->_leaves) + { + leaf->_parent = sg_new; + sg_new->_leaves.push_back(leaf); + } + return sg_new; + } return sg; } From dee91d12c2ee9633a20e37c4beb1a54982bad95d Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 13 Nov 2020 16:26:08 +0100 Subject: [PATCH 28/37] Update .travis.yml --- .travis.yml | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2f457798a..fb029fcf3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,13 +2,7 @@ language: cpp branches: only: - master - - coverity_scan - /openmw-.*$/ -env: - global: - # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created - # via the "travis encrypt" command using the project repo's public key - - secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE=" cache: ccache addons: apt: @@ -27,28 +21,17 @@ addons: # The other ones from OpenMW ppa libbullet-dev, libopenscenegraph-dev, libmygui-dev ] - coverity_scan: # TODO: currently takes too long, disabled openmw/openmw-cs for now. - project: - name: "OpenMW/openmw" - description: "" - branch_pattern: coverity_scan - notification_email: 1122069+psi29a@users.noreply.github.com - build_command_prepend: "cov-configure --comptype gcc --compiler gcc-5 --template; cmake . -DBUILD_OPENMW=FALSE -DBUILD_OPENCS=FALSE" - build_command: "make VERBOSE=1 -j3" matrix: include: - name: OpenMW (all) on MacOS 10.15 with Xcode 11.6 os: osx osx_image: xcode11.6 - if: branch != coverity_scan - name: OpenMW (all) on Ubuntu Focal with GCC os: linux dist: focal - if: branch != coverity_scan - name: OpenMW (tests only) on Ubuntu Focal with GCC os: linux dist: focal - if: branch != coverity_scan env: - BUILD_TESTS_ONLY: 1 - name: OpenMW (openmw) on Ubuntu Focal with Clang's Static Analysis @@ -57,16 +40,7 @@ matrix: env: - MATRIX_EVAL="CC=clang && CXX=clang++" - ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++" - if: branch != coverity_scan compiler: clang - - name: OpenMW Components Coverity Scan - os: linux - dist: focal - if: branch = coverity_scan -# allow_failures: -# - name: OpenMW (openmw) on Ubuntu Focal with GCC-10 -# env: -# - MATRIX_EVAL="CC=gcc-10 && CXX=g++-10" before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi From df2ae6e86651935c4c3c95bec84bedcb9d011230 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 13 Nov 2020 16:29:11 +0100 Subject: [PATCH 29/37] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fb029fcf3..8ec35a671 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: cpp +dist: focal branches: only: - master @@ -8,7 +9,6 @@ addons: apt: sources: - sourceline: 'ppa:openmw/openmw' - # - ubuntu-toolchain-r-test # for GCC-10 packages: [ # Dev build-essential, cmake, clang-tools, ccache, From e15716eb0c09dcaa834a99982c169042feaa9bc7 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 13 Nov 2020 16:38:53 +0100 Subject: [PATCH 30/37] Update .travis.yml --- .travis.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8ec35a671..956997f25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: cpp +os: [ linux, osx ] dist: focal branches: only: @@ -17,7 +18,7 @@ addons: # FFmpeg libavcodec-dev, libavformat-dev, libavutil-dev, libswresample-dev, libswscale-dev, # Audio, Video and Misc. deps - libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, liblz4-dev + libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, liblz4-dev, # The other ones from OpenMW ppa libbullet-dev, libopenscenegraph-dev, libmygui-dev ] @@ -44,17 +45,17 @@ matrix: before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_install.${TRAVIS_OS_NAME}.sh; fi + - ./CI/before_install.${TRAVIS_OS_NAME}.sh before_script: - ccache -z - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_script.${TRAVIS_OS_NAME}.sh; fi + - ./CI/before_script.${TRAVIS_OS_NAME}.sh script: - cd ./build - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ${ANALYZE} make -j3; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ] && [ "${BUILD_TESTS_ONLY}" ]; then ./openmw_test_suite; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi + - ${ANALYZE} make -j3; + - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi + - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi + - if [ "${TRAVIS_OS_NAME}" = "linux" ] && [ "${BUILD_TESTS_ONLY}" ]; then ./openmw_test_suite; fi + - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi - cd "${TRAVIS_BUILD_DIR}" - ccache -s deploy: From 94c89e6d5ef170933e2d791be6bab5e119982397 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Fri, 13 Nov 2020 16:41:45 +0100 Subject: [PATCH 31/37] check ValueTs --- components/nifosg/controller.hpp | 35 +++++++++++++------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 17ad9564c..a29fabefd 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -60,11 +60,20 @@ namespace NifOsg ValueInterpolator() = default; - template - ValueInterpolator(typename std::enable_if_t< - std::is_same::value, - const Nif::NiFloatInterpolator - > * interpolator) : mDefaultVal(interpolator->defaultVal) + template< + class T, + typename = std::enable_if_t< + std::conjunction_v< + std::disjunction< + std::is_same, + std::is_same + >, + std::is_same + >, + T + > + > + ValueInterpolator(const T* interpolator) : mDefaultVal(interpolator->defaultVal) { if (interpolator->data.empty()) return; @@ -76,22 +85,6 @@ namespace NifOsg } }; - template - ValueInterpolator(typename std::enable_if_t< - std::is_same::value, - const Nif::NiPoint3Interpolator - > * interpolator) : mDefaultVal(interpolator->defaultVal) - { - if (interpolator->data.empty()) - return; - mKeys = interpolator->data->mKeyList; - if (mKeys) - { - mLastLowKey = mKeys->mKeys.end(); - mLastHighKey = mKeys->mKeys.end(); - } - } - ValueInterpolator(std::shared_ptr keys, ValueT defaultVal = ValueT()) : mKeys(keys) , mDefaultVal(defaultVal) From 8b0475037d30453dad616ca69be3b090331c0bd7 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 13 Nov 2020 16:42:10 +0100 Subject: [PATCH 32/37] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 956997f25..54ef00698 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,7 +48,7 @@ before_install: - ./CI/before_install.${TRAVIS_OS_NAME}.sh before_script: - ccache -z - - ./CI/before_script.${TRAVIS_OS_NAME}.sh + - ./CI/before_script.${TRAVIS_OS_NAME}.sh script: - cd ./build - ${ANALYZE} make -j3; From 68836aa0fd35a8c3938177a16d197ca9d5d4224d Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 13 Nov 2020 16:44:06 +0100 Subject: [PATCH 33/37] Update .travis.yml --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 54ef00698..e84a9ad71 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,4 @@ language: cpp -os: [ linux, osx ] -dist: focal branches: only: - master From 8a6d3d1b4f2836e09d663354a4074d8bdce0e46d Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Fri, 13 Nov 2020 22:53:12 +0300 Subject: [PATCH 34/37] Minor fixes Fix extra semicolon Disable collision avoidance if AI is disabled --- apps/openmw/mwmechanics/actors.cpp | 3 +++ components/nifosg/controller.hpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 8f3675ef7..047741bac 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1731,6 +1731,9 @@ namespace MWMechanics void Actors::predictAndAvoidCollisions() { + if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) + return; + const float minGap = 10.f; const float maxDistForPartialAvoiding = 200.f; const float maxDistForStrictAvoiding = 100.f; diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index a29fabefd..996e4ef97 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -83,7 +83,7 @@ namespace NifOsg mLastLowKey = mKeys->mKeys.end(); mLastHighKey = mKeys->mKeys.end(); } - }; + } ValueInterpolator(std::shared_ptr keys, ValueT defaultVal = ValueT()) : mKeys(keys) From 117697ea227933cf8c3a6c4b784bc8d659991559 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sat, 14 Nov 2020 01:12:32 +0300 Subject: [PATCH 35/37] Fix NiStringPalette loading --- components/nif/data.cpp | 16 ++-------------- components/nif/data.hpp | 2 +- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 1eb5c40fe..235825e4a 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -460,21 +460,9 @@ void NiPalette::read(NIFStream *nif) void NiStringPalette::read(NIFStream *nif) { - unsigned int size = nif->getUInt(); - if (!size) - return; - std::vector source; - nif->getChars(source, size); - if (nif->getUInt() != size) + palette = nif->getString(); + if (nif->getUInt() != palette.size()) nif->file->warn("Failed size check in NiStringPalette"); - if (source[source.size()-1] != '\0') - source.emplace_back('\0'); - const char* buffer = source.data(); - while (static_cast(buffer - source.data()) < source.size()) - { - palette.emplace_back(buffer); - buffer += palette.back().size() + 1; - } } void NiBoolData::read(NIFStream *nif) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 4d13afb9d..35f329573 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -262,7 +262,7 @@ public: struct NiStringPalette : public Record { - std::vector palette; + std::string palette; void read(NIFStream *nif) override; }; From 89d73c5fc74ecc1a2f8c4b807f06967d5e83a960 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 14 Nov 2020 02:04:46 +0000 Subject: [PATCH 36/37] Only reroute stdout etc. to new console if not already redirected This should fix the issue where Windows Release builds (compiled as /SUBSYSTEM:WINDOWS instead of /SUBSYSTEM:CONSOLE) can't have their output redirected. Basically, a console application creates a console if not given one, so you get a console window behind OpenMW while it's running. It was decided that this was ugly, so we set Release builds to be windows applications, which don't get an automatic console and don't automatically connect to a console if given one anyway. Of course, we still wanted to actually be able to print to a console if given one, so we manually attach to the parent process' console if it exists, then reopen the standard streams connected to CON, the Windows pseudo-file representing the current console. This is a little like connecting a second wire into a dumb terminal in that you're pumping characters into the display rather than onto a pipeline, so output can't be redirected. It turns out, though, that if a /SUBSYSTEM:WINDOWS application has its standard streams redirected by the calling process, it still gets its handles as normal, so everything starts off connected just how we want it and we were clobbering this good setup with the straight-to-console fix. All we need to do to fix that is check if we've got valid standard handles and that they go somewhere useful, and if so, avoid reopening them once the console is attached. Simples. --- components/debug/debugging.cpp | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index dfed077e3..c4f3af307 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -11,11 +11,22 @@ namespace Debug { #ifdef _WIN32 + bool isRedirected(DWORD nStdHandle) + { + DWORD fileType = GetFileType(GetStdHandle(nStdHandle)); + + return (fileType == FILE_TYPE_DISK) || (fileType == FILE_TYPE_PIPE); + } + bool attachParentConsole() { if (GetConsoleWindow() != nullptr) return true; + bool inRedirected = isRedirected(STD_INPUT_HANDLE); + bool outRedirected = isRedirected(STD_OUTPUT_HANDLE); + bool errRedirected = isRedirected(STD_ERROR_HANDLE); + if (AttachConsole(ATTACH_PARENT_PROCESS)) { fflush(stdout); @@ -24,12 +35,21 @@ namespace Debug std::cerr.flush(); // this looks dubious but is really the right way - _wfreopen(L"CON", L"w", stdout); - _wfreopen(L"CON", L"w", stderr); - _wfreopen(L"CON", L"r", stdin); - freopen("CON", "w", stdout); - freopen("CON", "w", stderr); - freopen("CON", "r", stdin); + if (!inRedirected) + { + _wfreopen(L"CON", L"r", stdin); + freopen("CON", "r", stdin); + } + if (!outRedirected) + { + _wfreopen(L"CON", L"w", stdout); + freopen("CON", "w", stdout); + } + if (!errRedirected) + { + _wfreopen(L"CON", L"w", stderr); + freopen("CON", "w", stderr); + } return true; } From df9667e92348a9fadb4615c911ef54ef266ee05a Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sat, 14 Nov 2020 03:42:15 +0300 Subject: [PATCH 37/37] Read NIF bounding volume data correctly --- .../nifloader/testbulletnifloader.cpp | 66 ++++++---- components/nif/node.hpp | 124 ++++++++++++++++-- components/nifbullet/bulletnifloader.cpp | 27 +++- components/nifbullet/bulletnifloader.hpp | 4 +- 4 files changed, 178 insertions(+), 43 deletions(-) diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 72dcd3066..7da8f0fd5 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -373,6 +373,7 @@ namespace TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default) { EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -422,11 +423,13 @@ namespace { mNode.hasBounds = true; mNode.flags |= Nif::NiNode::Flag_BBoxCollision; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -444,12 +447,14 @@ namespace { mNode.hasBounds = true; mNode.flags |= Nif::NiNode::Flag_BBoxCollision; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -467,16 +472,19 @@ namespace { mNode.hasBounds = true; mNode.flags |= Nif::NiNode::Flag_BBoxCollision; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNiNode.hasBounds = true; - mNiNode.boundXYZ = osg::Vec3f(4, 5, 6); - mNiNode.boundPos = osg::Vec3f(-4, -5, -6); + mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNiNode.bounds.box.extents = osg::Vec3f(4, 5, 6); + mNiNode.bounds.box.center = osg::Vec3f(-4, -5, -6); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -494,20 +502,24 @@ namespace { mNode.hasBounds = true; mNode.flags |= Nif::NiNode::Flag_BBoxCollision; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNode2.hasBounds = true; - mNode2.boundXYZ = osg::Vec3f(4, 5, 6); - mNode2.boundPos = osg::Vec3f(-4, -5, -6); + mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6); + mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6); mNiNode.hasBounds = true; - mNiNode.boundXYZ = osg::Vec3f(7, 8, 9); - mNiNode.boundPos = osg::Vec3f(-7, -8, -9); + mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9); + mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -524,21 +536,25 @@ namespace TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_second_with_flag_should_use_second_bounds) { mNode.hasBounds = true; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNode2.hasBounds = true; mNode2.flags |= Nif::NiNode::Flag_BBoxCollision; - mNode2.boundXYZ = osg::Vec3f(4, 5, 6); - mNode2.boundPos = osg::Vec3f(-4, -5, -6); + mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6); + mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6); mNiNode.hasBounds = true; - mNiNode.boundXYZ = osg::Vec3f(7, 8, 9); - mNiNode.boundPos = osg::Vec3f(-7, -8, -9); + mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9); + mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -555,8 +571,9 @@ namespace TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounds_but_without_flag_should_return_shape_with_bounds_but_with_null_collision_shape) { mNode.hasBounds = true; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); @@ -588,8 +605,9 @@ namespace TEST_F(TestBulletNifLoader, for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape) { mNiTriShape.hasBounds = true; - mNiTriShape.boundXYZ = osg::Vec3f(1, 2, 3); - mNiTriShape.boundPos = osg::Vec3f(-1, -2, -3); + mNiTriShape.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNiTriShape.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNiTriShape.bounds.box.center = osg::Vec3f(-1, -2, -3); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 72adfe06c..0b958d2c2 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -16,6 +16,117 @@ namespace Nif struct NiNode; +struct NiBoundingVolume +{ + enum Type + { + SPHERE_BV = 0, + BOX_BV = 1, + CAPSULE_BV = 2, + LOZENGE_BV = 3, + UNION_BV = 4, + HALFSPACE_BV = 5 + }; + + struct NiSphereBV + { + osg::Vec3f center; + float radius{0.f}; + }; + + struct NiBoxBV + { + osg::Vec3f center; + Matrix3 axis; + osg::Vec3f extents; + }; + + struct NiCapsuleBV + { + osg::Vec3f center, axis; + float extent{0.f}, radius{0.f}; + }; + + struct NiLozengeBV + { + float radius{0.f}, extent0{0.f}, extent1{0.f}; + osg::Vec3f center, axis0, axis1; + }; + + struct NiHalfSpaceBV + { + osg::Vec3f center, normal; + }; + + unsigned int type; + NiSphereBV sphere; + NiBoxBV box; + NiCapsuleBV capsule; + NiLozengeBV lozenge; + std::vector children; + NiHalfSpaceBV plane; + void read(NIFStream* nif) + { + type = nif->getUInt(); + switch (type) + { + case SPHERE_BV: + { + sphere.center = nif->getVector3(); + sphere.radius = nif->getFloat(); + break; + } + case BOX_BV: + { + box.center = nif->getVector3(); + box.axis = nif->getMatrix3(); + box.extents = nif->getVector3(); + break; + } + case CAPSULE_BV: + { + capsule.center = nif->getVector3(); + capsule.axis = nif->getVector3(); + capsule.extent = nif->getFloat(); + capsule.radius = nif->getFloat(); + break; + } + case LOZENGE_BV: + { + lozenge.radius = nif->getFloat(); + lozenge.extent0 = nif->getFloat(); + lozenge.extent1 = nif->getFloat(); + lozenge.center = nif->getVector3(); + lozenge.axis0 = nif->getVector3(); + lozenge.axis1 = nif->getVector3(); + break; + } + case UNION_BV: + { + unsigned int numChildren = nif->getUInt(); + if (numChildren == 0) + break; + children.resize(numChildren); + for (NiBoundingVolume& child : children) + child.read(nif); + break; + } + case HALFSPACE_BV: + { + plane.center = nif->getVector3(); + plane.normal = nif->getVector3(); + break; + } + default: + { + std::stringstream error; + error << "Unhandled NiBoundingVolume type: " << type; + nif->file->fail(error.str()); + } + } + } +}; + /** A Node is an object that's part of the main NIF tree. It has parent node (unless it's the root), and transformation (location and rotation) relative to it's parent. @@ -31,9 +142,7 @@ public: // Bounding box info bool hasBounds{false}; - osg::Vec3f boundPos; - Matrix3 boundRot; - osg::Vec3f boundXYZ; // Box size + NiBoundingVolume bounds; void read(NIFStream *nif) override { @@ -48,13 +157,8 @@ public: if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0)) hasBounds = nif->getBoolean(); - if(hasBounds) - { - nif->getInt(); // always 1 - boundPos = nif->getVector3(); - boundRot = nif->getMatrix3(); - boundXYZ = nif->getVector3(); - } + if (hasBounds) + bounds.read(nif); // Reference to the collision object in Gamebryo files. if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0)) nif->skip(4); diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 5b531121e..b1461e536 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -132,13 +132,14 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) if ((node = dynamic_cast(r))) break; } + const std::string filename = nif.getFilename(); if (!node) { - warn("Found no root nodes in NIF."); + warn("Found no root nodes in NIF file " + filename); return mShape; } - if (findBoundingBox(node)) + if (findBoundingBox(node, filename)) { const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents); const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate); @@ -158,7 +159,6 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource). // assume all nodes in the file will be animated - const auto filename = nif.getFilename(); const bool isAnimated = pathFileNameStartsWithX(filename); handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated); @@ -194,12 +194,25 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) // Find a boundingBox in the node hierarchy. // Return: use bounding box for collision? -bool BulletNifLoader::findBoundingBox(const Nif::Node* node) +bool BulletNifLoader::findBoundingBox(const Nif::Node* node, const std::string& filename) { if (node->hasBounds) { - mShape->mCollisionBoxHalfExtents = node->boundXYZ; - mShape->mCollisionBoxTranslate = node->boundPos; + unsigned int type = node->bounds.type; + switch (type) + { + case Nif::NiBoundingVolume::Type::BOX_BV: + mShape->mCollisionBoxHalfExtents = node->bounds.box.extents; + mShape->mCollisionBoxTranslate = node->bounds.box.center; + break; + default: + { + std::stringstream warning; + warning << "Unsupported NiBoundingVolume type " << type << " in node " << node->recIndex; + warning << " in file " << filename; + warn(warning.str()); + } + } if (node->flags & Nif::NiNode::Flag_BBoxCollision) { @@ -215,7 +228,7 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node) { if(!list[i].empty()) { - bool found = findBoundingBox (list[i].getPtr()); + bool found = findBoundingBox (list[i].getPtr(), filename); if (found) return true; } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index e423e5149..054b33fed 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -40,7 +40,7 @@ class BulletNifLoader public: void warn(const std::string &msg) { - Log(Debug::Warning) << "NIFLoader: Warn:" << msg; + Log(Debug::Warning) << "NIFLoader: Warn: " << msg; } void fail(const std::string &msg) @@ -52,7 +52,7 @@ public: osg::ref_ptr load(const Nif::File& file); private: - bool findBoundingBox(const Nif::Node* node); + bool findBoundingBox(const Nif::Node* node, const std::string& filename); void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false, bool avoid=false);