mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 19:19:56 +00:00
Add OpenMW commits up to 14 Nov 2020
# Conflicts: # .travis.yml # apps/openmw/mwmechanics/tickableeffects.cpp
This commit is contained in:
commit
676481d061
49 changed files with 1192 additions and 246 deletions
12
.travis.yml
12
.travis.yml
|
@ -24,7 +24,7 @@ addons:
|
||||||
# FFmpeg
|
# FFmpeg
|
||||||
libavcodec-dev, libavformat-dev, libavutil-dev, libswresample-dev, libswscale-dev,
|
libavcodec-dev, libavformat-dev, libavutil-dev, libswresample-dev, libswscale-dev,
|
||||||
# Audio, Video and Misc. deps
|
# 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
|
# The other ones from OpenMW ppa
|
||||||
libbullet-dev, libopenscenegraph-3.4-dev, libmygui-dev,
|
libbullet-dev, libopenscenegraph-3.4-dev, libmygui-dev,
|
||||||
# tes3mp stuff
|
# tes3mp stuff
|
||||||
|
@ -71,11 +71,11 @@ before_script:
|
||||||
- ./CI/before_script.${TRAVIS_OS_NAME}.sh
|
- ./CI/before_script.${TRAVIS_OS_NAME}.sh
|
||||||
script:
|
script:
|
||||||
- cd ./build
|
- cd ./build
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ${ANALYZE} make -j3; fi
|
- ${ANALYZE} make -j3; fi
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
- if [ "${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 [ "${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 [ "${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
|
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||||
- cd "${TRAVIS_BUILD_DIR}"
|
- cd "${TRAVIS_BUILD_DIR}"
|
||||||
- ccache -s
|
- ccache -s
|
||||||
#deploy:
|
#deploy:
|
||||||
|
|
|
@ -10,10 +10,12 @@
|
||||||
Bug #2976 [reopened]: Issues combining settings from the command line and both config files
|
Bug #2976 [reopened]: Issues combining settings from the command line and both config files
|
||||||
Bug #3676: NiParticleColorModifier isn't applied properly
|
Bug #3676: NiParticleColorModifier isn't applied properly
|
||||||
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
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 #3862: Random container contents behave differently than vanilla
|
||||||
Bug #3929: Leveled list merchant containers respawn on barter
|
Bug #3929: Leveled list merchant containers respawn on barter
|
||||||
Bug #4021: Attributes and skills are not stored as floats
|
Bug #4021: Attributes and skills are not stored as floats
|
||||||
Bug #4055: Local scripts don't inherit variables from their base record
|
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 #4623: Corprus implementation is incorrect
|
||||||
Bug #4631: Setting MSAA level too high doesn't fall back to highest supported level
|
Bug #4631: Setting MSAA level too high doesn't fall back to highest supported level
|
||||||
Bug #4764: Data race in osg ParticleSystem
|
Bug #4764: Data race in osg ParticleSystem
|
||||||
|
|
|
@ -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 cmake >/dev/null 2>&1 || brew install cmake
|
||||||
command -v qmake >/dev/null 2>&1 || brew install qt
|
command -v qmake >/dev/null 2>&1 || brew install qt
|
||||||
|
|
||||||
brew link --overwrite lz4 # overwrite system lz4; use brew
|
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-f8918dd.zip -o ~/openmw-deps.zip
|
||||||
brew reinstall lz4
|
|
||||||
|
|
||||||
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-ef2462c.zip -o ~/openmw-deps.zip
|
|
||||||
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||||
|
|
|
@ -25,5 +25,6 @@ cmake \
|
||||||
-D BUILD_BSATOOL=TRUE \
|
-D BUILD_BSATOOL=TRUE \
|
||||||
-D BUILD_ESSIMPORTER=TRUE \
|
-D BUILD_ESSIMPORTER=TRUE \
|
||||||
-D BUILD_NIFTEST=TRUE \
|
-D BUILD_NIFTEST=TRUE \
|
||||||
|
-D BULLET_USE_DOUBLES=TRUE \
|
||||||
-G"Unix Makefiles" \
|
-G"Unix Makefiles" \
|
||||||
..
|
..
|
||||||
|
|
|
@ -132,6 +132,7 @@ int main(int argc, char **argv)
|
||||||
if(!parseOptions (argc, argv, files))
|
if(!parseOptions (argc, argv, files))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
Nif::NIFFile::setLoadUnsupportedFiles(true);
|
||||||
// std::cout << "Reading Files" << std::endl;
|
// std::cout << "Reading Files" << std::endl;
|
||||||
for(std::vector<std::string>::const_iterator it=files.begin(); it!=files.end(); ++it)
|
for(std::vector<std::string>::const_iterator it=files.begin(); it!=files.end(); ++it)
|
||||||
{
|
{
|
||||||
|
|
|
@ -961,12 +961,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)
|
void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration)
|
||||||
{
|
{
|
||||||
CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
|
CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||||
const MagicEffects &effects = creatureStats.getMagicEffects();
|
const MagicEffects &effects = creatureStats.getMagicEffects();
|
||||||
bool godmode = ptr == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
bool godmode = ptr == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
||||||
|
|
||||||
|
applyCureEffects(ptr);
|
||||||
|
|
||||||
bool wasDead = creatureStats.isDead();
|
bool wasDead = creatureStats.isDead();
|
||||||
|
|
||||||
if (duration > 0)
|
if (duration > 0)
|
||||||
|
@ -1810,6 +1852,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
void Actors::predictAndAvoidCollisions()
|
void Actors::predictAndAvoidCollisions()
|
||||||
{
|
{
|
||||||
|
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
||||||
|
return;
|
||||||
|
|
||||||
const float minGap = 10.f;
|
const float minGap = 10.f;
|
||||||
const float maxDistForPartialAvoiding = 200.f;
|
const float maxDistForPartialAvoiding = 200.f;
|
||||||
const float maxDistForStrictAvoiding = 100.f;
|
const float maxDistForStrictAvoiding = 100.f;
|
||||||
|
|
|
@ -226,6 +226,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl);
|
void updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl);
|
||||||
|
void applyCureEffects (const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
PtrActorMap mActors;
|
PtrActorMap mActors;
|
||||||
float mTimerDisposeSummonsCorpses;
|
float mTimerDisposeSummonsCorpses;
|
||||||
|
|
|
@ -217,40 +217,6 @@ namespace MWMechanics
|
||||||
break;
|
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;
|
|
||||||
/*
|
|
||||||
Start of tes3mp addition
|
|
||||||
|
|
||||||
Don't apply paralysis to DedicatedPlayers and DedicatedActors unilterally on this client
|
|
||||||
*/
|
|
||||||
case ESM::MagicEffect::Paralyze:
|
|
||||||
{
|
|
||||||
if (mwmp::PlayerList::isDedicatedPlayer(actor) || mwmp::Main::get().getCellController()->isDedicatedActor(actor))
|
|
||||||
{
|
|
||||||
actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
End of tes3mp addition
|
|
||||||
*/
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ namespace MWMechanics
|
||||||
struct EffectKey;
|
struct EffectKey;
|
||||||
|
|
||||||
/// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed
|
/// 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?
|
/// @return Was the effect a tickable effect with a magnitude?
|
||||||
bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey& effectKey, float magnitude);
|
bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey& effectKey, float magnitude);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
||||||
|
|
||||||
|
#include "components/misc/convert.hpp"
|
||||||
|
|
||||||
#include "ptrholder.hpp"
|
#include "ptrholder.hpp"
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
|
@ -20,7 +22,7 @@ namespace MWPhysics
|
||||||
collisionObject = col1Wrap->m_collisionObject;
|
collisionObject = col1Wrap->m_collisionObject;
|
||||||
PtrHolder* holder = static_cast<PtrHolder*>(collisionObject->getUserPointer());
|
PtrHolder* holder = static_cast<PtrHolder*>(collisionObject->getUserPointer());
|
||||||
if (holder)
|
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;
|
return 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
#include "physicssystem.hpp"
|
||||||
|
|
||||||
class btCollisionObject;
|
class btCollisionObject;
|
||||||
struct btCollisionObjectWrapper;
|
struct btCollisionObjectWrapper;
|
||||||
|
|
||||||
|
@ -23,7 +25,7 @@ namespace MWPhysics
|
||||||
const btCollisionObjectWrapper* col0Wrap,int partId0,int index0,
|
const btCollisionObjectWrapper* col0Wrap,int partId0,int index0,
|
||||||
const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) override;
|
const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) override;
|
||||||
|
|
||||||
std::vector<MWWorld::Ptr> mResult;
|
std::vector<ContactPoint> mResult;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -427,15 +427,15 @@ namespace MWPhysics
|
||||||
return osg::Vec3f();
|
return osg::Vec3f();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<MWWorld::Ptr> PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const
|
std::vector<ContactPoint> PhysicsSystem::getCollisionsPoints(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const
|
||||||
{
|
{
|
||||||
btCollisionObject* me = nullptr;
|
btCollisionObject* me = nullptr;
|
||||||
|
|
||||||
ObjectMap::const_iterator found = mObjects.find(ptr);
|
auto found = mObjects.find(ptr);
|
||||||
if (found != mObjects.end())
|
if (found != mObjects.end())
|
||||||
me = found->second->getCollisionObject();
|
me = found->second->getCollisionObject();
|
||||||
else
|
else
|
||||||
return std::vector<MWWorld::Ptr>();
|
return {};
|
||||||
|
|
||||||
ContactTestResultCallback resultCallback (me);
|
ContactTestResultCallback resultCallback (me);
|
||||||
resultCallback.m_collisionFilterGroup = collisionGroup;
|
resultCallback.m_collisionFilterGroup = collisionGroup;
|
||||||
|
@ -444,6 +444,14 @@ namespace MWPhysics
|
||||||
return resultCallback.mResult;
|
return resultCallback.mResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<MWWorld::Ptr> PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const
|
||||||
|
{
|
||||||
|
std::vector<MWWorld::Ptr> 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)
|
osg::Vec3f PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight)
|
||||||
{
|
{
|
||||||
ActorMap::iterator found = mActors.find(ptr);
|
ActorMap::iterator found = mActors.find(ptr);
|
||||||
|
|
|
@ -57,6 +57,13 @@ namespace MWPhysics
|
||||||
class Actor;
|
class Actor;
|
||||||
class PhysicsTaskScheduler;
|
class PhysicsTaskScheduler;
|
||||||
|
|
||||||
|
struct ContactPoint
|
||||||
|
{
|
||||||
|
MWWorld::Ptr mObject;
|
||||||
|
osg::Vec3f mPoint;
|
||||||
|
osg::Vec3f mNormal;
|
||||||
|
};
|
||||||
|
|
||||||
struct LOSRequest
|
struct LOSRequest
|
||||||
{
|
{
|
||||||
LOSRequest(const std::weak_ptr<Actor>& a1, const std::weak_ptr<Actor>& a2);
|
LOSRequest(const std::weak_ptr<Actor>& a1, const std::weak_ptr<Actor>& a2);
|
||||||
|
@ -145,6 +152,7 @@ namespace MWPhysics
|
||||||
void debugDraw();
|
void debugDraw();
|
||||||
|
|
||||||
std::vector<MWWorld::Ptr> getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; ///< get handles this object collides with
|
std::vector<MWWorld::Ptr> getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; ///< get handles this object collides with
|
||||||
|
std::vector<ContactPoint> getCollisionsPoints(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const;
|
||||||
osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight);
|
osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight);
|
||||||
|
|
||||||
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::ConstPtr& actor,
|
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::ConstPtr& actor,
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/resource/keyframemanager.hpp>
|
#include <components/resource/keyframemanager.hpp>
|
||||||
#include <components/resource/resourcesystem.hpp>
|
|
||||||
|
|
||||||
#include <components/misc/constants.hpp>
|
#include <components/misc/constants.hpp>
|
||||||
#include <components/misc/resourcehelpers.hpp>
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
|
@ -37,8 +36,6 @@
|
||||||
|
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
#include <components/shader/shadermanager.hpp>
|
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
@ -493,9 +490,8 @@ namespace MWRender
|
||||||
class TransparencyUpdater : public SceneUtil::StateSetUpdater
|
class TransparencyUpdater : public SceneUtil::StateSetUpdater
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TransparencyUpdater(const float alpha, osg::ref_ptr<osg::Uniform> shadowUniform)
|
TransparencyUpdater(const float alpha)
|
||||||
: mAlpha(alpha)
|
: mAlpha(alpha)
|
||||||
, mShadowUniform(shadowUniform)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,9 +505,6 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
osg::BlendFunc* blendfunc (new osg::BlendFunc);
|
osg::BlendFunc* blendfunc (new osg::BlendFunc);
|
||||||
stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
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->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
|
||||||
stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
|
stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
|
||||||
|
@ -533,7 +526,6 @@ namespace MWRender
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float mAlpha;
|
float mAlpha;
|
||||||
osg::ref_ptr<osg::Uniform> mShadowUniform;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Animation::AnimSource
|
struct Animation::AnimSource
|
||||||
|
@ -1773,7 +1765,7 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
if (mTransparencyUpdater == nullptr)
|
if (mTransparencyUpdater == nullptr)
|
||||||
{
|
{
|
||||||
mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform());
|
mTransparencyUpdater = new TransparencyUpdater(alpha);
|
||||||
mObjectRoot->addCullCallback(mTransparencyUpdater);
|
mObjectRoot->addCullCallback(mTransparencyUpdater);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -216,6 +216,7 @@ namespace MWRender
|
||||||
resourceSystem->getSceneManager()->setNormalHeightMapPattern(Settings::Manager::getString("normal height map pattern", "Shaders"));
|
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()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders"));
|
||||||
resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "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<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager;
|
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager;
|
||||||
sceneRoot->setLightingMask(Mask_Lighting);
|
sceneRoot->setLightingMask(Mask_Lighting);
|
||||||
|
@ -361,6 +362,7 @@ namespace MWRender
|
||||||
mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater));
|
mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater));
|
||||||
NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor);
|
NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor);
|
||||||
NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect);
|
NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect);
|
||||||
|
Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models"));
|
||||||
|
|
||||||
mNearClip = Settings::Manager::getFloat("near clip", "Camera");
|
mNearClip = Settings::Manager::getFloat("near clip", "Camera");
|
||||||
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
|
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
|
||||||
|
|
|
@ -1771,21 +1771,28 @@ namespace MWWorld
|
||||||
float minRot = door.getCellRef().getPosition().rot[2];
|
float minRot = door.getCellRef().getPosition().rot[2];
|
||||||
float maxRot = minRot + osg::DegreesToRadians(90.f);
|
float maxRot = minRot + osg::DegreesToRadians(90.f);
|
||||||
|
|
||||||
float diff = duration * osg::DegreesToRadians(90.f);
|
float diff = duration * osg::DegreesToRadians(90.f) * (state == MWWorld::DoorState::Opening ? 1 : -1);
|
||||||
float targetRot = std::min(std::max(minRot, oldRot + diff * (state == MWWorld::DoorState::Opening ? 1 : -1)), maxRot);
|
float targetRot = std::min(std::max(minRot, oldRot + diff), maxRot);
|
||||||
rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot, MWBase::RotationFlag_none);
|
rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot, MWBase::RotationFlag_none);
|
||||||
|
|
||||||
bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot;
|
bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot;
|
||||||
|
|
||||||
/// \todo should use convexSweepTest here
|
/// \todo should use convexSweepTest here
|
||||||
bool collisionWithActor = false;
|
bool collisionWithActor = false;
|
||||||
std::vector<MWWorld::Ptr> collisions = mPhysics->getCollisions(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor);
|
for (auto& [ptr, point, normal] : mPhysics->getCollisionsPoints(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor))
|
||||||
for (MWWorld::Ptr& ptr : collisions)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
if (ptr.getClass().isActor())
|
if (ptr.getClass().isActor())
|
||||||
{
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
collisionWithActor = true;
|
collisionWithActor = true;
|
||||||
|
|
||||||
// Collided with actor, ask actor to try to avoid door
|
// Collided with actor, ask actor to try to avoid door
|
||||||
if(ptr != getPlayerPtr() )
|
if(ptr != getPlayerPtr() )
|
||||||
{
|
{
|
||||||
|
|
|
@ -373,6 +373,7 @@ namespace
|
||||||
TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default)
|
TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default)
|
||||||
{
|
{
|
||||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0));
|
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0));
|
||||||
|
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||||
const auto result = mLoader.load(mNifFile);
|
const auto result = mLoader.load(mNifFile);
|
||||||
|
|
||||||
Resource::BulletShape expected;
|
Resource::BulletShape expected;
|
||||||
|
@ -422,11 +423,13 @@ namespace
|
||||||
{
|
{
|
||||||
mNode.hasBounds = true;
|
mNode.hasBounds = true;
|
||||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
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, numRoots()).WillOnce(Return(1));
|
||||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
|
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
|
||||||
|
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||||
const auto result = mLoader.load(mNifFile);
|
const auto result = mLoader.load(mNifFile);
|
||||||
|
|
||||||
Resource::BulletShape expected;
|
Resource::BulletShape expected;
|
||||||
|
@ -444,12 +447,14 @@ namespace
|
||||||
{
|
{
|
||||||
mNode.hasBounds = true;
|
mNode.hasBounds = true;
|
||||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
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>({Nif::NodePtr(&mNode)}));
|
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
|
||||||
|
|
||||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||||
|
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||||
const auto result = mLoader.load(mNifFile);
|
const auto result = mLoader.load(mNifFile);
|
||||||
|
|
||||||
Resource::BulletShape expected;
|
Resource::BulletShape expected;
|
||||||
|
@ -467,16 +472,19 @@ namespace
|
||||||
{
|
{
|
||||||
mNode.hasBounds = true;
|
mNode.hasBounds = true;
|
||||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||||
|
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||||
|
|
||||||
mNiNode.hasBounds = true;
|
mNiNode.hasBounds = true;
|
||||||
mNiNode.boundXYZ = osg::Vec3f(4, 5, 6);
|
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNiNode.boundPos = osg::Vec3f(-4, -5, -6);
|
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>({Nif::NodePtr(&mNode)}));
|
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
|
||||||
|
|
||||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||||
|
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||||
const auto result = mLoader.load(mNifFile);
|
const auto result = mLoader.load(mNifFile);
|
||||||
|
|
||||||
Resource::BulletShape expected;
|
Resource::BulletShape expected;
|
||||||
|
@ -494,20 +502,24 @@ namespace
|
||||||
{
|
{
|
||||||
mNode.hasBounds = true;
|
mNode.hasBounds = true;
|
||||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||||
|
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||||
|
|
||||||
mNode2.hasBounds = true;
|
mNode2.hasBounds = true;
|
||||||
mNode2.boundXYZ = osg::Vec3f(4, 5, 6);
|
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNode2.boundPos = osg::Vec3f(-4, -5, -6);
|
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
|
||||||
|
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
|
||||||
|
|
||||||
mNiNode.hasBounds = true;
|
mNiNode.hasBounds = true;
|
||||||
mNiNode.boundXYZ = osg::Vec3f(7, 8, 9);
|
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNiNode.boundPos = osg::Vec3f(-7, -8, -9);
|
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>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
|
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
|
||||||
|
|
||||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||||
|
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||||
const auto result = mLoader.load(mNifFile);
|
const auto result = mLoader.load(mNifFile);
|
||||||
|
|
||||||
Resource::BulletShape expected;
|
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)
|
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.hasBounds = true;
|
||||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||||
|
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||||
|
|
||||||
mNode2.hasBounds = true;
|
mNode2.hasBounds = true;
|
||||||
mNode2.flags |= Nif::NiNode::Flag_BBoxCollision;
|
mNode2.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||||
mNode2.boundXYZ = osg::Vec3f(4, 5, 6);
|
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNode2.boundPos = osg::Vec3f(-4, -5, -6);
|
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
|
||||||
|
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
|
||||||
|
|
||||||
mNiNode.hasBounds = true;
|
mNiNode.hasBounds = true;
|
||||||
mNiNode.boundXYZ = osg::Vec3f(7, 8, 9);
|
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNiNode.boundPos = osg::Vec3f(-7, -8, -9);
|
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>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
|
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
|
||||||
|
|
||||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||||
|
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||||
const auto result = mLoader.load(mNifFile);
|
const auto result = mLoader.load(mNifFile);
|
||||||
|
|
||||||
Resource::BulletShape expected;
|
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)
|
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.hasBounds = true;
|
||||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
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, numRoots()).WillOnce(Return(1));
|
||||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
|
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)
|
TEST_F(TestBulletNifLoader, for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape)
|
||||||
{
|
{
|
||||||
mNiTriShape.hasBounds = true;
|
mNiTriShape.hasBounds = true;
|
||||||
mNiTriShape.boundXYZ = osg::Vec3f(1, 2, 3);
|
mNiTriShape.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||||
mNiTriShape.boundPos = osg::Vec3f(-1, -2, -3);
|
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, numRoots()).WillOnce(Return(1));
|
||||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));
|
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));
|
||||||
|
|
|
@ -52,7 +52,7 @@ add_component_dir (shader
|
||||||
add_component_dir (sceneutil
|
add_component_dir (sceneutil
|
||||||
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
|
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
|
||||||
lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer
|
lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer
|
||||||
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh
|
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (nif
|
add_component_dir (nif
|
||||||
|
|
|
@ -11,11 +11,22 @@
|
||||||
namespace Debug
|
namespace Debug
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
bool isRedirected(DWORD nStdHandle)
|
||||||
|
{
|
||||||
|
DWORD fileType = GetFileType(GetStdHandle(nStdHandle));
|
||||||
|
|
||||||
|
return (fileType == FILE_TYPE_DISK) || (fileType == FILE_TYPE_PIPE);
|
||||||
|
}
|
||||||
|
|
||||||
bool attachParentConsole()
|
bool attachParentConsole()
|
||||||
{
|
{
|
||||||
if (GetConsoleWindow() != nullptr)
|
if (GetConsoleWindow() != nullptr)
|
||||||
return true;
|
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))
|
if (AttachConsole(ATTACH_PARENT_PROCESS))
|
||||||
{
|
{
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
@ -24,12 +35,21 @@ namespace Debug
|
||||||
std::cerr.flush();
|
std::cerr.flush();
|
||||||
|
|
||||||
// this looks dubious but is really the right way
|
// this looks dubious but is really the right way
|
||||||
_wfreopen(L"CON", L"w", stdout);
|
if (!inRedirected)
|
||||||
_wfreopen(L"CON", L"w", stderr);
|
{
|
||||||
_wfreopen(L"CON", L"r", stdin);
|
_wfreopen(L"CON", L"r", stdin);
|
||||||
freopen("CON", "w", stdout);
|
freopen("CON", "r", stdin);
|
||||||
freopen("CON", "w", stderr);
|
}
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,8 @@ namespace Nif
|
||||||
void NiMaterialColorController::read(NIFStream *nif)
|
void NiMaterialColorController::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Controller::read(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.
|
// Two bits that correspond to the controlled material color.
|
||||||
// 00: Ambient
|
// 00: Ambient
|
||||||
// 01: Diffuse
|
// 01: Diffuse
|
||||||
|
@ -101,12 +103,14 @@ namespace Nif
|
||||||
targetColor = nif->getUShort() & 3;
|
targetColor = nif->getUShort() & 3;
|
||||||
else
|
else
|
||||||
targetColor = (flags >> 4) & 3;
|
targetColor = (flags >> 4) & 3;
|
||||||
data.read(nif);
|
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))
|
||||||
|
data.read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiMaterialColorController::post(NIFFile *nif)
|
void NiMaterialColorController::post(NIFFile *nif)
|
||||||
{
|
{
|
||||||
Controller::post(nif);
|
Controller::post(nif);
|
||||||
|
interpolator.post(nif);
|
||||||
data.post(nif);
|
data.post(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,25 +165,33 @@ namespace Nif
|
||||||
void NiKeyframeController::read(NIFStream *nif)
|
void NiKeyframeController::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Controller::read(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)
|
void NiKeyframeController::post(NIFFile *nif)
|
||||||
{
|
{
|
||||||
Controller::post(nif);
|
Controller::post(nif);
|
||||||
data.post(nif);
|
data.post(nif);
|
||||||
|
interpolator.post(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiFloatInterpController::read(NIFStream *nif)
|
void NiFloatInterpController::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Controller::read(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)
|
void NiFloatInterpController::post(NIFFile *nif)
|
||||||
{
|
{
|
||||||
Controller::post(nif);
|
Controller::post(nif);
|
||||||
data.post(nif);
|
data.post(nif);
|
||||||
|
interpolator.post(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiGeomMorpherController::read(NIFStream *nif)
|
void NiGeomMorpherController::read(NIFStream *nif)
|
||||||
|
@ -189,13 +201,34 @@ namespace Nif
|
||||||
/*bool updateNormals = !!*/nif->getUShort();
|
/*bool updateNormals = !!*/nif->getUShort();
|
||||||
data.read(nif);
|
data.read(nif);
|
||||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
|
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
|
||||||
|
{
|
||||||
/*bool alwaysActive = */nif->getChar(); // Always 0
|
/*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
|
||||||
|
{
|
||||||
|
// TODO: handle weighted interpolators
|
||||||
|
unsigned int numInterps = nif->getUInt();
|
||||||
|
nif->skip(8 * numInterps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiGeomMorpherController::post(NIFFile *nif)
|
void NiGeomMorpherController::post(NIFFile *nif)
|
||||||
{
|
{
|
||||||
Controller::post(nif);
|
Controller::post(nif);
|
||||||
data.post(nif);
|
data.post(nif);
|
||||||
|
interpolators.post(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiVisController::read(NIFStream *nif)
|
void NiVisController::read(NIFStream *nif)
|
||||||
|
@ -213,6 +246,8 @@ namespace Nif
|
||||||
void NiFlipController::read(NIFStream *nif)
|
void NiFlipController::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Controller::read(nif);
|
Controller::read(nif);
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,2,0,0))
|
||||||
|
mInterpolator.read(nif);
|
||||||
mTexSlot = nif->getUInt();
|
mTexSlot = nif->getUInt();
|
||||||
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))
|
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))
|
||||||
{
|
{
|
||||||
|
@ -225,7 +260,69 @@ namespace Nif
|
||||||
void NiFlipController::post(NIFFile *nif)
|
void NiFlipController::post(NIFFile *nif)
|
||||||
{
|
{
|
||||||
Controller::post(nif);
|
Controller::post(nif);
|
||||||
|
mInterpolator.post(nif);
|
||||||
mSources.post(nif);
|
mSources.post(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bhkBlendController::read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Controller::read(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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,7 @@ using NiBSPArrayController = NiParticleSystemController;
|
||||||
class NiMaterialColorController : public Controller
|
class NiMaterialColorController : public Controller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
NiPoint3InterpolatorPtr interpolator;
|
||||||
NiPosDataPtr data;
|
NiPosDataPtr data;
|
||||||
unsigned int targetColor;
|
unsigned int targetColor;
|
||||||
|
|
||||||
|
@ -138,6 +139,7 @@ class NiKeyframeController : public Controller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NiKeyframeDataPtr data;
|
NiKeyframeDataPtr data;
|
||||||
|
NiTransformInterpolatorPtr interpolator;
|
||||||
|
|
||||||
void read(NIFStream *nif) override;
|
void read(NIFStream *nif) override;
|
||||||
void post(NIFFile *nif) override;
|
void post(NIFFile *nif) override;
|
||||||
|
@ -146,6 +148,7 @@ public:
|
||||||
struct NiFloatInterpController : public Controller
|
struct NiFloatInterpController : public Controller
|
||||||
{
|
{
|
||||||
NiFloatDataPtr data;
|
NiFloatDataPtr data;
|
||||||
|
NiFloatInterpolatorPtr interpolator;
|
||||||
|
|
||||||
void read(NIFStream *nif) override;
|
void read(NIFStream *nif) override;
|
||||||
void post(NIFFile *nif) override;
|
void post(NIFFile *nif) override;
|
||||||
|
@ -158,6 +161,7 @@ class NiGeomMorpherController : public Controller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NiMorphDataPtr data;
|
NiMorphDataPtr data;
|
||||||
|
NiFloatInterpolatorList interpolators;
|
||||||
|
|
||||||
void read(NIFStream *nif) override;
|
void read(NIFStream *nif) override;
|
||||||
void post(NIFFile *nif) override;
|
void post(NIFFile *nif) override;
|
||||||
|
@ -175,6 +179,7 @@ public:
|
||||||
class NiFlipController : public Controller
|
class NiFlipController : public Controller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
NiFloatInterpolatorPtr mInterpolator;
|
||||||
int mTexSlot; // NiTexturingProperty::TextureType
|
int mTexSlot; // NiTexturingProperty::TextureType
|
||||||
float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources
|
float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources
|
||||||
NiSourceTextureList mSources;
|
NiSourceTextureList mSources;
|
||||||
|
@ -183,5 +188,47 @@ public:
|
||||||
void post(NIFFile *nif) override;
|
void post(NIFFile *nif) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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
|
} // Namespace
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -6,6 +6,8 @@ namespace Nif
|
||||||
void NiSkinInstance::read(NIFStream *nif)
|
void NiSkinInstance::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
data.read(nif);
|
data.read(nif);
|
||||||
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,101))
|
||||||
|
partitions.read(nif);
|
||||||
root.read(nif);
|
root.read(nif);
|
||||||
bones.read(nif);
|
bones.read(nif);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +15,7 @@ void NiSkinInstance::read(NIFStream *nif)
|
||||||
void NiSkinInstance::post(NIFFile *nif)
|
void NiSkinInstance::post(NIFFile *nif)
|
||||||
{
|
{
|
||||||
data.post(nif);
|
data.post(nif);
|
||||||
|
partitions.post(nif);
|
||||||
root.post(nif);
|
root.post(nif);
|
||||||
bones.post(nif);
|
bones.post(nif);
|
||||||
|
|
||||||
|
@ -320,7 +323,7 @@ void NiSkinData::read(NIFStream *nif)
|
||||||
|
|
||||||
int boneNum = nif->getInt();
|
int boneNum = nif->getInt();
|
||||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))
|
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
|
// Has vertex weights flag
|
||||||
if (nif->getVersion() > NIFStream::generateVersion(4,2,1,0) && !nif->getBoolean())
|
if (nif->getVersion() > NIFStream::generateVersion(4,2,1,0) && !nif->getBoolean())
|
||||||
|
@ -345,6 +348,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<unsigned short> 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)
|
void NiMorphData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
int morphCount = nif->getInt();
|
int morphCount = nif->getInt();
|
||||||
|
@ -355,7 +421,7 @@ void NiMorphData::read(NIFStream *nif)
|
||||||
for(int i = 0;i < morphCount;i++)
|
for(int i = 0;i < morphCount;i++)
|
||||||
{
|
{
|
||||||
mMorphs[i].mKeyFrames = std::make_shared<FloatKeyMap>();
|
mMorphs[i].mKeyFrames = std::make_shared<FloatKeyMap>();
|
||||||
mMorphs[i].mKeyFrames->read(nif, true);
|
mMorphs[i].mKeyFrames->read(nif, true, /*morph*/true);
|
||||||
nif->getVector3s(mMorphs[i].mVertices, vertCount);
|
nif->getVector3s(mMorphs[i].mVertices, vertCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,4 +458,17 @@ void NiPalette::read(NIFStream *nif)
|
||||||
colors[i] = nif->getUInt() | alphaMask;
|
colors[i] = nif->getUInt() | alphaMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NiStringPalette::read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
palette = nif->getString();
|
||||||
|
if (nif->getUInt() != palette.size())
|
||||||
|
nif->file->warn("Failed size check in NiStringPalette");
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiBoolData::read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
mKeyList = std::make_shared<ByteKeyMap>();
|
||||||
|
mKeyList->read(nif);
|
||||||
|
}
|
||||||
|
|
||||||
} // Namespace
|
} // Namespace
|
||||||
|
|
|
@ -174,6 +174,7 @@ class NiSkinInstance : public Record
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NiSkinDataPtr data;
|
NiSkinDataPtr data;
|
||||||
|
NiSkinPartitionPtr partitions;
|
||||||
NodePtr root;
|
NodePtr root;
|
||||||
NodeList bones;
|
NodeList bones;
|
||||||
|
|
||||||
|
@ -200,6 +201,25 @@ public:
|
||||||
|
|
||||||
Transformation trafo;
|
Transformation trafo;
|
||||||
std::vector<BoneInfo> bones;
|
std::vector<BoneInfo> bones;
|
||||||
|
NiSkinPartitionPtr partitions;
|
||||||
|
|
||||||
|
void read(NIFStream *nif) override;
|
||||||
|
void post(NIFFile *nif) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiSkinPartition : public Record
|
||||||
|
{
|
||||||
|
struct Partition
|
||||||
|
{
|
||||||
|
std::vector<unsigned short> bones;
|
||||||
|
std::vector<unsigned short> vertexMap;
|
||||||
|
std::vector<float> weights;
|
||||||
|
std::vector<std::vector<unsigned short>> strips;
|
||||||
|
std::vector<unsigned short> triangles;
|
||||||
|
std::vector<char> boneIndices;
|
||||||
|
void read(NIFStream *nif);
|
||||||
|
};
|
||||||
|
std::vector<Partition> data;
|
||||||
|
|
||||||
void read(NIFStream *nif) override;
|
void read(NIFStream *nif) override;
|
||||||
};
|
};
|
||||||
|
@ -240,5 +260,17 @@ public:
|
||||||
void read(NIFStream *nif) override;
|
void read(NIFStream *nif) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct NiStringPalette : public Record
|
||||||
|
{
|
||||||
|
std::string palette;
|
||||||
|
void read(NIFStream *nif) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NiBoolData : public Record
|
||||||
|
{
|
||||||
|
ByteKeyMapPtr mKeyList;
|
||||||
|
void read(NIFStream *nif) override;
|
||||||
|
};
|
||||||
|
|
||||||
} // Namespace
|
} // Namespace
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -80,5 +80,11 @@ void NiFloatsExtraData::read(NIFStream *nif)
|
||||||
nif->getFloats(data, num);
|
nif->getFloats(data, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BSBound::read(NIFStream *nif)
|
||||||
|
{
|
||||||
|
Extra::read(nif);
|
||||||
|
center = nif->getVector3();
|
||||||
|
halfExtents = nif->getVector3();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,5 +109,12 @@ struct NiFloatsExtraData : public Extra
|
||||||
void read(NIFStream *nif) override;
|
void read(NIFStream *nif) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BSBound : public Extra
|
||||||
|
{
|
||||||
|
osg::Vec3f center, halfExtents;
|
||||||
|
|
||||||
|
void read(NIFStream *nif) override;
|
||||||
|
};
|
||||||
|
|
||||||
} // Namespace
|
} // Namespace
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "niffile.hpp"
|
#include "niffile.hpp"
|
||||||
#include "effect.hpp"
|
#include "effect.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
@ -113,6 +114,19 @@ static std::map<std::string,RecordFactoryEntry> makeFactory()
|
||||||
factory["NiColorExtraData"] = {&construct <NiVectorExtraData> , RC_NiColorExtraData };
|
factory["NiColorExtraData"] = {&construct <NiVectorExtraData> , RC_NiColorExtraData };
|
||||||
factory["NiFloatExtraData"] = {&construct <NiFloatExtraData> , RC_NiFloatExtraData };
|
factory["NiFloatExtraData"] = {&construct <NiFloatExtraData> , RC_NiFloatExtraData };
|
||||||
factory["NiFloatsExtraData"] = {&construct <NiFloatsExtraData> , RC_NiFloatsExtraData };
|
factory["NiFloatsExtraData"] = {&construct <NiFloatsExtraData> , RC_NiFloatsExtraData };
|
||||||
|
factory["NiStringPalette"] = {&construct <NiStringPalette> , RC_NiStringPalette };
|
||||||
|
factory["NiBoolData"] = {&construct <NiBoolData> , RC_NiBoolData };
|
||||||
|
factory["NiSkinPartition"] = {&construct <NiSkinPartition> , RC_NiSkinPartition };
|
||||||
|
factory["BSXFlags"] = {&construct <NiIntegerExtraData> , RC_BSXFlags };
|
||||||
|
factory["BSBound"] = {&construct <BSBound> , RC_BSBound };
|
||||||
|
factory["NiTransformData"] = {&construct <NiKeyframeData> , RC_NiKeyframeData };
|
||||||
|
factory["BSFadeNode"] = {&construct <NiNode> , RC_NiNode };
|
||||||
|
factory["bhkBlendController"] = {&construct <bhkBlendController> , RC_bhkBlendController };
|
||||||
|
factory["NiFloatInterpolator"] = {&construct <NiFloatInterpolator> , RC_NiFloatInterpolator };
|
||||||
|
factory["NiBoolInterpolator"] = {&construct <NiBoolInterpolator> , RC_NiBoolInterpolator };
|
||||||
|
factory["NiPoint3Interpolator"] = {&construct <NiPoint3Interpolator> , RC_NiPoint3Interpolator };
|
||||||
|
factory["NiTransformController"] = {&construct <NiKeyframeController> , RC_NiKeyframeController };
|
||||||
|
factory["NiTransformInterpolator"] = {&construct <NiTransformInterpolator> , RC_NiTransformInterpolator };
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,15 +151,45 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
||||||
|
|
||||||
// Check the header string
|
// Check the header string
|
||||||
std::string head = nif.getVersionString();
|
std::string head = nif.getVersionString();
|
||||||
if(head.compare(0, 22, "NetImmerse File Format") != 0)
|
static const std::array<std::string, 2> 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);
|
fail("Invalid NIF header: " + head);
|
||||||
|
|
||||||
|
supported = false;
|
||||||
|
|
||||||
// Get BCD version
|
// Get BCD version
|
||||||
ver = nif.getUInt();
|
ver = nif.getUInt();
|
||||||
// 4.0.0.0 is an older, practically identical version of the format.
|
// 4.0.0.0 is an older, practically identical version of the format.
|
||||||
// It's not used by Morrowind assets but Morrowind supports it.
|
// It's not used by Morrowind assets but Morrowind supports it.
|
||||||
if(ver != NIFStream::generateVersion(4,0,0,0) && ver != VER_MW)
|
static const std::array<uint32_t, 2> supportedVers =
|
||||||
fail("Unsupported NIF version: " + printVersion(ver));
|
{
|
||||||
|
NIFStream::generateVersion(4,0,0,0),
|
||||||
|
VER_MW
|
||||||
|
};
|
||||||
|
for (uint32_t supportedVer : supportedVers)
|
||||||
|
{
|
||||||
|
supported = (ver == supportedVer);
|
||||||
|
if (supported)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!supported)
|
||||||
|
{
|
||||||
|
if (sLoadUnsupportedFiles)
|
||||||
|
warn("Unsupported NIF version: " + printVersion(ver) + ". Proceed with caution!");
|
||||||
|
else
|
||||||
|
fail("Unsupported NIF version: " + printVersion(ver));
|
||||||
|
}
|
||||||
|
|
||||||
// NIF data endianness
|
// NIF data endianness
|
||||||
if (ver >= NIFStream::generateVersion(20,0,0,4))
|
if (ver >= NIFStream::generateVersion(20,0,0,4))
|
||||||
|
@ -245,6 +289,9 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
||||||
else
|
else
|
||||||
fail("Unknown record type " + rec);
|
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 != nullptr);
|
||||||
assert(r->recType != RC_MISSING);
|
assert(r->recType != RC_MISSING);
|
||||||
r->recName = rec;
|
r->recName = rec;
|
||||||
|
@ -286,4 +333,11 @@ bool NIFFile::getUseSkinning() const
|
||||||
return mUseSkinning;
|
return mUseSkinning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NIFFile::sLoadUnsupportedFiles = false;
|
||||||
|
|
||||||
|
void NIFFile::setLoadUnsupportedFiles(bool load)
|
||||||
|
{
|
||||||
|
sLoadUnsupportedFiles = load;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,8 @@ class NIFFile final : public File
|
||||||
|
|
||||||
bool mUseSkinning = false;
|
bool mUseSkinning = false;
|
||||||
|
|
||||||
|
static bool sLoadUnsupportedFiles;
|
||||||
|
|
||||||
/// Parse the file
|
/// Parse the file
|
||||||
void parse(Files::IStreamPtr stream);
|
void parse(Files::IStreamPtr stream);
|
||||||
|
|
||||||
|
@ -149,6 +151,8 @@ public:
|
||||||
|
|
||||||
/// Get the Bethesda version of the NIF format used
|
/// Get the Bethesda version of the NIF format used
|
||||||
unsigned int getBethVersion() const override { return bethVer; }
|
unsigned int getBethVersion() const override { return bethVer; }
|
||||||
|
|
||||||
|
static void setLoadUnsupportedFiles(bool load);
|
||||||
};
|
};
|
||||||
using NIFFilePtr = std::shared_ptr<const Nif::NIFFile>;
|
using NIFFilePtr = std::shared_ptr<const Nif::NIFFile>;
|
||||||
|
|
||||||
|
|
|
@ -52,16 +52,27 @@ struct KeyMapT {
|
||||||
MapType mKeys;
|
MapType mKeys;
|
||||||
|
|
||||||
//Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html)
|
//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);
|
assert(nif);
|
||||||
|
|
||||||
mInterpolationType = InterpolationType_Unknown;
|
mInterpolationType = InterpolationType_Unknown;
|
||||||
|
|
||||||
|
if (morph && nif->getVersion() >= NIFStream::generateVersion(10,1,0,106))
|
||||||
|
nif->getString(); // Frame name
|
||||||
|
|
||||||
size_t count = nif->getUInt();
|
size_t count = nif->getUInt();
|
||||||
if(count == 0 && !force)
|
if (count == 0 && !force && !morph)
|
||||||
return;
|
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();
|
mKeys.clear();
|
||||||
|
|
||||||
mInterpolationType = nif->getUInt();
|
mInterpolationType = nif->getUInt();
|
||||||
|
|
|
@ -16,6 +16,117 @@ namespace Nif
|
||||||
|
|
||||||
struct NiNode;
|
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<NiBoundingVolume> 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
|
/** 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
|
parent node (unless it's the root), and transformation (location
|
||||||
and rotation) relative to it's parent.
|
and rotation) relative to it's parent.
|
||||||
|
@ -31,9 +142,7 @@ public:
|
||||||
|
|
||||||
// Bounding box info
|
// Bounding box info
|
||||||
bool hasBounds{false};
|
bool hasBounds{false};
|
||||||
osg::Vec3f boundPos;
|
NiBoundingVolume bounds;
|
||||||
Matrix3 boundRot;
|
|
||||||
osg::Vec3f boundXYZ; // Box size
|
|
||||||
|
|
||||||
void read(NIFStream *nif) override
|
void read(NIFStream *nif) override
|
||||||
{
|
{
|
||||||
|
@ -48,13 +157,8 @@ public:
|
||||||
|
|
||||||
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||||
hasBounds = nif->getBoolean();
|
hasBounds = nif->getBoolean();
|
||||||
if(hasBounds)
|
if (hasBounds)
|
||||||
{
|
bounds.read(nif);
|
||||||
nif->getInt(); // always 1
|
|
||||||
boundPos = nif->getVector3();
|
|
||||||
boundRot = nif->getMatrix3();
|
|
||||||
boundXYZ = nif->getVector3();
|
|
||||||
}
|
|
||||||
// Reference to the collision object in Gamebryo files.
|
// Reference to the collision object in Gamebryo files.
|
||||||
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||||
nif->skip(4);
|
nif->skip(4);
|
||||||
|
|
|
@ -109,7 +109,17 @@ enum RecordType
|
||||||
RC_NiVectorExtraData,
|
RC_NiVectorExtraData,
|
||||||
RC_NiColorExtraData,
|
RC_NiColorExtraData,
|
||||||
RC_NiFloatExtraData,
|
RC_NiFloatExtraData,
|
||||||
RC_NiFloatsExtraData
|
RC_NiFloatsExtraData,
|
||||||
|
RC_NiStringPalette,
|
||||||
|
RC_NiBoolData,
|
||||||
|
RC_NiSkinPartition,
|
||||||
|
RC_BSXFlags,
|
||||||
|
RC_BSBound,
|
||||||
|
RC_bhkBlendController,
|
||||||
|
RC_NiFloatInterpolator,
|
||||||
|
RC_NiPoint3Interpolator,
|
||||||
|
RC_NiBoolInterpolator,
|
||||||
|
RC_NiTransformInterpolator,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base class for all records
|
/// Base class for all records
|
||||||
|
|
|
@ -143,6 +143,11 @@ class NiAutoNormalParticlesData;
|
||||||
class NiPalette;
|
class NiPalette;
|
||||||
struct NiParticleModifier;
|
struct NiParticleModifier;
|
||||||
struct NiLinesData;
|
struct NiLinesData;
|
||||||
|
struct NiBoolData;
|
||||||
|
struct NiSkinPartition;
|
||||||
|
struct NiFloatInterpolator;
|
||||||
|
struct NiPoint3Interpolator;
|
||||||
|
struct NiTransformInterpolator;
|
||||||
|
|
||||||
using NodePtr = RecordPtrT<Node>;
|
using NodePtr = RecordPtrT<Node>;
|
||||||
using ExtraPtr = RecordPtrT<Extra>;
|
using ExtraPtr = RecordPtrT<Extra>;
|
||||||
|
@ -166,11 +171,17 @@ using NiRotatingParticlesDataPtr = RecordPtrT<NiRotatingParticlesData>;
|
||||||
using NiAutoNormalParticlesDataPtr = RecordPtrT<NiAutoNormalParticlesData>;
|
using NiAutoNormalParticlesDataPtr = RecordPtrT<NiAutoNormalParticlesData>;
|
||||||
using NiPalettePtr = RecordPtrT<NiPalette>;
|
using NiPalettePtr = RecordPtrT<NiPalette>;
|
||||||
using NiParticleModifierPtr = RecordPtrT<NiParticleModifier>;
|
using NiParticleModifierPtr = RecordPtrT<NiParticleModifier>;
|
||||||
|
using NiBoolDataPtr = RecordPtrT<NiBoolData>;
|
||||||
|
using NiSkinPartitionPtr = RecordPtrT<NiSkinPartition>;
|
||||||
|
using NiFloatInterpolatorPtr = RecordPtrT<NiFloatInterpolator>;
|
||||||
|
using NiPoint3InterpolatorPtr = RecordPtrT<NiPoint3Interpolator>;
|
||||||
|
using NiTransformInterpolatorPtr = RecordPtrT<NiTransformInterpolator>;
|
||||||
|
|
||||||
using NodeList = RecordListT<Node>;
|
using NodeList = RecordListT<Node>;
|
||||||
using PropertyList = RecordListT<Property>;
|
using PropertyList = RecordListT<Property>;
|
||||||
using ExtraList = RecordListT<Extra>;
|
using ExtraList = RecordListT<Extra>;
|
||||||
using NiSourceTextureList = RecordListT<NiSourceTexture>;
|
using NiSourceTextureList = RecordListT<NiSourceTexture>;
|
||||||
|
using NiFloatInterpolatorList = RecordListT<NiFloatInterpolator>;
|
||||||
|
|
||||||
} // Namespace
|
} // Namespace
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
#include <components/misc/convert.hpp>
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
#include <components/nif/node.hpp>
|
#include <components/nif/node.hpp>
|
||||||
|
@ -24,11 +25,6 @@ osg::Matrixf getWorldTransform(const Nif::Node *node)
|
||||||
return node->trafo.toMatrix();
|
return node->trafo.toMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
btVector3 getbtVector(const osg::Vec3f &v)
|
|
||||||
{
|
|
||||||
return btVector3(v.x(), v.y(), v.z());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pathFileNameStartsWithX(const std::string& path)
|
bool pathFileNameStartsWithX(const std::string& path)
|
||||||
{
|
{
|
||||||
const std::size_t slashpos = path.find_last_of("/\\");
|
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');
|
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<int>(data.vertices.size()));
|
mesh.preallocateVertices(static_cast<int>(data.vertices.size()));
|
||||||
mesh.preallocateIndices(static_cast<int>(data.triangles.size()));
|
mesh.preallocateIndices(static_cast<int>(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)
|
for (std::size_t i = 0; i < triangles.size(); i += 3)
|
||||||
{
|
{
|
||||||
mesh.addTriangle(
|
mesh.addTriangle(
|
||||||
getbtVector(vertices[triangles[i + 0]] * transform),
|
Misc::Convert::toBullet(vertices[triangles[i + 0]] * transform),
|
||||||
getbtVector(vertices[triangles[i + 1]] * transform),
|
Misc::Convert::toBullet(vertices[triangles[i + 1]] * transform),
|
||||||
getbtVector(vertices[triangles[i + 2]] * 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<osg::Vec3f> &vertices = data.vertices;
|
const std::vector<osg::Vec3f> &vertices = data.vertices;
|
||||||
const std::vector<std::vector<unsigned short>> &strips = data.strips;
|
const std::vector<std::vector<unsigned short>> &strips = data.strips;
|
||||||
if (vertices.empty() || strips.empty())
|
if (vertices.empty() || strips.empty())
|
||||||
return;
|
return;
|
||||||
mesh.preallocateVertices(static_cast<int>(data.vertices.size()));
|
mesh.preallocateVertices(static_cast<int>(vertices.size()));
|
||||||
int numTriangles = 0;
|
int numTriangles = 0;
|
||||||
for (const std::vector<unsigned short>& strip : strips)
|
for (const std::vector<unsigned short>& strip : strips)
|
||||||
{
|
{
|
||||||
|
@ -88,17 +84,17 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD
|
||||||
if (i%2==0)
|
if (i%2==0)
|
||||||
{
|
{
|
||||||
mesh.addTriangle(
|
mesh.addTriangle(
|
||||||
getbtVector(vertices[a] * transform),
|
Misc::Convert::toBullet(vertices[a] * transform),
|
||||||
getbtVector(vertices[b] * transform),
|
Misc::Convert::toBullet(vertices[b] * transform),
|
||||||
getbtVector(vertices[c] * transform)
|
Misc::Convert::toBullet(vertices[c] * transform)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mesh.addTriangle(
|
mesh.addTriangle(
|
||||||
getbtVector(vertices[a] * transform),
|
Misc::Convert::toBullet(vertices[a] * transform),
|
||||||
getbtVector(vertices[c] * transform),
|
Misc::Convert::toBullet(vertices[c] * transform),
|
||||||
getbtVector(vertices[b] * 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)
|
if (nifNode->recType == Nif::RC_NiTriShape)
|
||||||
fillTriangleMeshWithTransform(mesh, static_cast<const Nif::NiTriShape*>(nifNode)->data.get(), transform);
|
fillTriangleMesh(mesh, static_cast<const Nif::NiTriShape*>(nifNode)->data.get(), transform);
|
||||||
else // if (nifNode->recType == Nif::RC_NiTriStrips)
|
else if (nifNode->recType == Nif::RC_NiTriStrips)
|
||||||
fillTriangleMeshWithTransform(mesh, static_cast<const Nif::NiTriStrips*>(nifNode)->data.get(), transform);
|
fillTriangleMesh(mesh, static_cast<const Nif::NiTriStrips*>(nifNode)->data.get(), transform);
|
||||||
}
|
|
||||||
|
|
||||||
void fillTriangleMesh(btTriangleMesh& mesh, const Nif::Node* node)
|
|
||||||
{
|
|
||||||
fillTriangleMeshWithTransform(mesh, node, osg::Matrixf());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -141,18 +132,21 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
||||||
if ((node = dynamic_cast<Nif::Node*>(r)))
|
if ((node = dynamic_cast<Nif::Node*>(r)))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
const std::string filename = nif.getFilename();
|
||||||
if (!node)
|
if (!node)
|
||||||
{
|
{
|
||||||
warn("Found no root nodes in NIF.");
|
warn("Found no root nodes in NIF file " + filename);
|
||||||
return mShape;
|
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);
|
||||||
std::unique_ptr<btCompoundShape> compound (new btCompoundShape);
|
std::unique_ptr<btCompoundShape> compound (new btCompoundShape);
|
||||||
std::unique_ptr<btBoxShape> boxShape(new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents)));
|
std::unique_ptr<btBoxShape> boxShape(new btBoxShape(halfExtents));
|
||||||
btTransform transform = btTransform::getIdentity();
|
btTransform transform = btTransform::getIdentity();
|
||||||
transform.setOrigin(getbtVector(mShape->mCollisionBoxTranslate));
|
transform.setOrigin(origin);
|
||||||
compound->addChildShape(transform, boxShape.get());
|
compound->addChildShape(transform, boxShape.get());
|
||||||
boxShape.release();
|
boxShape.release();
|
||||||
|
|
||||||
|
@ -165,7 +159,6 @@ osg::ref_ptr<Resource::BulletShape> 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).
|
// 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
|
// assume all nodes in the file will be animated
|
||||||
const auto filename = nif.getFilename();
|
|
||||||
const bool isAnimated = pathFileNameStartsWithX(filename);
|
const bool isAnimated = pathFileNameStartsWithX(filename);
|
||||||
|
|
||||||
handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated);
|
handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated);
|
||||||
|
@ -201,12 +194,25 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
||||||
|
|
||||||
// Find a boundingBox in the node hierarchy.
|
// Find a boundingBox in the node hierarchy.
|
||||||
// Return: use bounding box for collision?
|
// 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)
|
if (node->hasBounds)
|
||||||
{
|
{
|
||||||
mShape->mCollisionBoxHalfExtents = node->boundXYZ;
|
unsigned int type = node->bounds.type;
|
||||||
mShape->mCollisionBoxTranslate = node->boundPos;
|
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)
|
if (node->flags & Nif::NiNode::Flag_BBoxCollision)
|
||||||
{
|
{
|
||||||
|
@ -222,7 +228,7 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node)
|
||||||
{
|
{
|
||||||
if(!list[i].empty())
|
if(!list[i].empty())
|
||||||
{
|
{
|
||||||
bool found = findBoundingBox (list[i].getPtr());
|
bool found = findBoundingBox (list[i].getPtr(), filename);
|
||||||
if (found)
|
if (found)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -383,7 +389,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons
|
||||||
if (!mAvoidStaticMesh)
|
if (!mAvoidStaticMesh)
|
||||||
mAvoidStaticMesh.reset(new btTriangleMesh(false));
|
mAvoidStaticMesh.reset(new btTriangleMesh(false));
|
||||||
|
|
||||||
fillTriangleMeshWithTransform(*mAvoidStaticMesh, nifNode, transform);
|
fillTriangleMesh(*mAvoidStaticMesh, nifNode, transform);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -391,7 +397,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons
|
||||||
mStaticMesh.reset(new btTriangleMesh(false));
|
mStaticMesh.reset(new btTriangleMesh(false));
|
||||||
|
|
||||||
// Static shape, just transform all vertices into position
|
// Static shape, just transform all vertices into position
|
||||||
fillTriangleMeshWithTransform(*mStaticMesh, nifNode, transform);
|
fillTriangleMesh(*mStaticMesh, nifNode, transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ class BulletNifLoader
|
||||||
public:
|
public:
|
||||||
void warn(const std::string &msg)
|
void warn(const std::string &msg)
|
||||||
{
|
{
|
||||||
Log(Debug::Warning) << "NIFLoader: Warn:" << msg;
|
Log(Debug::Warning) << "NIFLoader: Warn: " << msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fail(const std::string &msg)
|
void fail(const std::string &msg)
|
||||||
|
@ -52,7 +52,7 @@ public:
|
||||||
osg::ref_ptr<Resource::BulletShape> load(const Nif::File& file);
|
osg::ref_ptr<Resource::BulletShape> load(const Nif::File& file);
|
||||||
|
|
||||||
private:
|
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,
|
void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode,
|
||||||
bool isAnimated=false, bool autogenerated=false, bool avoid=false);
|
bool isAnimated=false, bool autogenerated=false, bool avoid=false);
|
||||||
|
|
|
@ -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
|
osg::Quat KeyframeController::getXYZRotation(float time) const
|
||||||
{
|
{
|
||||||
float xrot = 0, yrot = 0, zrot = 0;
|
float xrot = 0, yrot = 0, zrot = 0;
|
||||||
|
@ -177,10 +197,25 @@ GeomMorpherController::GeomMorpherController(const GeomMorpherController ©,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
GeomMorpherController::GeomMorpherController(const Nif::NiMorphData *data)
|
GeomMorpherController::GeomMorpherController(const Nif::NiGeomMorpherController* ctrl)
|
||||||
{
|
{
|
||||||
for (unsigned int i=0; i<data->mMorphs.size(); ++i)
|
if (ctrl->interpolators.length() == 0)
|
||||||
mKeyFrames.emplace_back(data->mMorphs[i].mKeyFrames);
|
{
|
||||||
|
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)
|
void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable)
|
||||||
|
@ -313,6 +348,11 @@ RollController::RollController(const Nif::NiFloatData *data)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RollController::RollController(const Nif::NiFloatInterpolator* interpolator)
|
||||||
|
: mData(interpolator)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
RollController::RollController(const RollController ©, const osg::CopyOp ©op)
|
RollController::RollController(const RollController ©, const osg::CopyOp ©op)
|
||||||
: osg::NodeCallback(copy, copyop)
|
: osg::NodeCallback(copy, copyop)
|
||||||
, Controller(copy)
|
, Controller(copy)
|
||||||
|
@ -344,6 +384,10 @@ void RollController::operator() (osg::Node* node, osg::NodeVisitor* nv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AlphaController::AlphaController()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
AlphaController::AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial)
|
AlphaController::AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial)
|
||||||
: mData(data->mKeyList, 1.f)
|
: mData(data->mKeyList, 1.f)
|
||||||
, mBaseMaterial(baseMaterial)
|
, mBaseMaterial(baseMaterial)
|
||||||
|
@ -351,7 +395,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 +425,10 @@ void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaterialColorController::MaterialColorController()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
MaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial)
|
MaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial)
|
||||||
: mData(data->mKeyList, osg::Vec3f(1,1,1))
|
: mData(data->mKeyList, osg::Vec3f(1,1,1))
|
||||||
, mTargetColor(color)
|
, mTargetColor(color)
|
||||||
|
@ -386,7 +436,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 +501,8 @@ FlipController::FlipController(const Nif::NiFlipController *ctrl, const std::vec
|
||||||
, mDelta(ctrl->mDelta)
|
, mDelta(ctrl->mDelta)
|
||||||
, mTextures(textures)
|
, mTextures(textures)
|
||||||
{
|
{
|
||||||
|
if (!ctrl->mInterpolator.empty())
|
||||||
|
mData = ctrl->mInterpolator.getPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
FlipController::FlipController(int texSlot, float delta, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures)
|
FlipController::FlipController(int texSlot, float delta, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures)
|
||||||
|
@ -463,14 +518,19 @@ FlipController::FlipController(const FlipController ©, const osg::CopyOp &co
|
||||||
, mTexSlot(copy.mTexSlot)
|
, mTexSlot(copy.mTexSlot)
|
||||||
, mDelta(copy.mDelta)
|
, mDelta(copy.mDelta)
|
||||||
, mTextures(copy.mTextures)
|
, mTextures(copy.mTextures)
|
||||||
|
, mData(copy.mData)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlipController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)
|
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]);
|
stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <components/sceneutil/statesetupdater.hpp>
|
#include <components/sceneutil/statesetupdater.hpp>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include <osg/Texture2D>
|
#include <osg/Texture2D>
|
||||||
|
|
||||||
|
@ -59,6 +60,31 @@ namespace NifOsg
|
||||||
|
|
||||||
ValueInterpolator() = default;
|
ValueInterpolator() = default;
|
||||||
|
|
||||||
|
template<
|
||||||
|
class T,
|
||||||
|
typename = std::enable_if_t<
|
||||||
|
std::conjunction_v<
|
||||||
|
std::disjunction<
|
||||||
|
std::is_same<ValueT, float>,
|
||||||
|
std::is_same<ValueT, osg::Vec3f>
|
||||||
|
>,
|
||||||
|
std::is_same<decltype(T::defaultVal), ValueT>
|
||||||
|
>,
|
||||||
|
T
|
||||||
|
>
|
||||||
|
>
|
||||||
|
ValueInterpolator(const T* 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<const MapT> keys, ValueT defaultVal = ValueT())
|
ValueInterpolator(std::shared_ptr<const MapT> keys, ValueT defaultVal = ValueT())
|
||||||
: mKeys(keys)
|
: mKeys(keys)
|
||||||
, mDefaultVal(defaultVal)
|
, mDefaultVal(defaultVal)
|
||||||
|
@ -188,7 +214,7 @@ namespace NifOsg
|
||||||
class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller
|
class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GeomMorpherController(const Nif::NiMorphData* data);
|
GeomMorpherController(const Nif::NiGeomMorpherController* ctrl);
|
||||||
GeomMorpherController();
|
GeomMorpherController();
|
||||||
GeomMorpherController(const GeomMorpherController& copy, const osg::CopyOp& copyop);
|
GeomMorpherController(const GeomMorpherController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
|
@ -203,7 +229,14 @@ namespace NifOsg
|
||||||
class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller
|
class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
// This is used if there's no interpolator but there is data (Morrowind meshes).
|
||||||
KeyframeController(const Nif::NiKeyframeData *data);
|
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();
|
||||||
KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop);
|
KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
|
@ -272,6 +305,7 @@ namespace NifOsg
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RollController(const Nif::NiFloatData *data);
|
RollController(const Nif::NiFloatData *data);
|
||||||
|
RollController(const Nif::NiFloatInterpolator* interpolator);
|
||||||
RollController() = default;
|
RollController() = default;
|
||||||
RollController(const RollController& copy, const osg::CopyOp& copyop);
|
RollController(const RollController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
|
@ -287,6 +321,7 @@ namespace NifOsg
|
||||||
osg::ref_ptr<const osg::Material> mBaseMaterial;
|
osg::ref_ptr<const osg::Material> mBaseMaterial;
|
||||||
public:
|
public:
|
||||||
AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial);
|
AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial);
|
||||||
|
AlphaController(const Nif::NiFloatInterpolator* interpolator, const osg::Material* baseMaterial);
|
||||||
AlphaController();
|
AlphaController();
|
||||||
AlphaController(const AlphaController& copy, const osg::CopyOp& copyop);
|
AlphaController(const AlphaController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
|
@ -308,6 +343,7 @@ namespace NifOsg
|
||||||
Emissive = 3
|
Emissive = 3
|
||||||
};
|
};
|
||||||
MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial);
|
MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial);
|
||||||
|
MaterialColorController(const Nif::NiPoint3Interpolator* interpolator, TargetColor color, const osg::Material* baseMaterial);
|
||||||
MaterialColorController();
|
MaterialColorController();
|
||||||
MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop);
|
MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
|
@ -329,6 +365,7 @@ namespace NifOsg
|
||||||
int mTexSlot{0};
|
int mTexSlot{0};
|
||||||
float mDelta{0.f};
|
float mDelta{0.f};
|
||||||
std::vector<osg::ref_ptr<osg::Texture2D> > mTextures;
|
std::vector<osg::ref_ptr<osg::Texture2D> > mTextures;
|
||||||
|
FloatInterpolator mData;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FlipController(const Nif::NiFlipController* ctrl, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures);
|
FlipController(const Nif::NiFlipController* ctrl, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures);
|
||||||
|
|
|
@ -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.
|
// 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<const Nif::Property*>& out)
|
void collectDrawableProperties(const Nif::Node* nifNode, std::vector<const Nif::Property*>& out)
|
||||||
{
|
{
|
||||||
|
@ -269,10 +281,10 @@ namespace NifOsg
|
||||||
const Nif::NiStringExtraData *strdata = static_cast<const Nif::NiStringExtraData*>(extra.getPtr());
|
const Nif::NiStringExtraData *strdata = static_cast<const Nif::NiStringExtraData*>(extra.getPtr());
|
||||||
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
|
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
|
||||||
|
|
||||||
if(key->data.empty())
|
if (key->data.empty() && key->interpolator.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
osg::ref_ptr<NifOsg::KeyframeController> callback(new NifOsg::KeyframeController(key->data.getPtr()));
|
osg::ref_ptr<NifOsg::KeyframeController> callback(handleKeyframeController(key));
|
||||||
callback->setFunction(std::shared_ptr<NifOsg::ControllerFunction>(new NifOsg::ControllerFunction(key)));
|
callback->setFunction(std::shared_ptr<NifOsg::ControllerFunction>(new NifOsg::ControllerFunction(key)));
|
||||||
|
|
||||||
if (!target.mKeyframeControllers.emplace(strdata->string, callback).second)
|
if (!target.mKeyframeControllers.emplace(strdata->string, callback).second)
|
||||||
|
@ -528,7 +540,19 @@ namespace NifOsg
|
||||||
// - finding a random child NiNode in NiBspArrayController
|
// - finding a random child NiNode in NiBspArrayController
|
||||||
node->setUserValue("recIndex", nifNode->recIndex);
|
node->setUserValue("recIndex", nifNode->recIndex);
|
||||||
|
|
||||||
|
std::vector<Nif::ExtraPtr> extraCollection;
|
||||||
|
|
||||||
for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->next)
|
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)
|
if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys)
|
||||||
{
|
{
|
||||||
|
@ -584,7 +608,7 @@ namespace NifOsg
|
||||||
|
|
||||||
applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags);
|
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)
|
if (isGeometry && !skipMeshes)
|
||||||
{
|
{
|
||||||
|
@ -701,6 +725,24 @@ namespace NifOsg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static osg::ref_ptr<KeyframeController> handleKeyframeController(const Nif::NiKeyframeController* keyctrl)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<NifOsg::KeyframeController> 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)
|
void handleNodeControllers(const Nif::Node* nifNode, osg::Node* node, int animflags, bool& isAnimated)
|
||||||
{
|
{
|
||||||
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
|
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
|
||||||
|
@ -710,9 +752,9 @@ namespace NifOsg
|
||||||
if (ctrl->recType == Nif::RC_NiKeyframeController)
|
if (ctrl->recType == Nif::RC_NiKeyframeController)
|
||||||
{
|
{
|
||||||
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
|
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
|
||||||
if (key->data.empty())
|
if (key->data.empty() && key->interpolator.empty())
|
||||||
continue;
|
continue;
|
||||||
osg::ref_ptr<KeyframeController> callback(new KeyframeController(key->data.getPtr()));
|
osg::ref_ptr<KeyframeController> callback(handleKeyframeController(key));
|
||||||
setupController(key, callback, animflags);
|
setupController(key, callback, animflags);
|
||||||
node->addUpdateCallback(callback);
|
node->addUpdateCallback(callback);
|
||||||
isAnimated = true;
|
isAnimated = true;
|
||||||
|
@ -739,9 +781,13 @@ namespace NifOsg
|
||||||
else if (ctrl->recType == Nif::RC_NiRollController)
|
else if (ctrl->recType == Nif::RC_NiRollController)
|
||||||
{
|
{
|
||||||
const Nif::NiRollController *rollctrl = static_cast<const Nif::NiRollController*>(ctrl.getPtr());
|
const Nif::NiRollController *rollctrl = static_cast<const Nif::NiRollController*>(ctrl.getPtr());
|
||||||
if (rollctrl->data.empty())
|
if (rollctrl->data.empty() && rollctrl->interpolator.empty())
|
||||||
continue;
|
continue;
|
||||||
osg::ref_ptr<RollController> callback(new RollController(rollctrl->data.getPtr()));
|
osg::ref_ptr<RollController> 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);
|
setupController(rollctrl, callback, animflags);
|
||||||
node->addUpdateCallback(callback);
|
node->addUpdateCallback(callback);
|
||||||
isAnimated = true;
|
isAnimated = true;
|
||||||
|
@ -767,19 +813,27 @@ namespace NifOsg
|
||||||
if (ctrl->recType == Nif::RC_NiAlphaController)
|
if (ctrl->recType == Nif::RC_NiAlphaController)
|
||||||
{
|
{
|
||||||
const Nif::NiAlphaController* alphactrl = static_cast<const Nif::NiAlphaController*>(ctrl.getPtr());
|
const Nif::NiAlphaController* alphactrl = static_cast<const Nif::NiAlphaController*>(ctrl.getPtr());
|
||||||
if (alphactrl->data.empty())
|
if (alphactrl->data.empty() && alphactrl->interpolator.empty())
|
||||||
continue;
|
continue;
|
||||||
osg::ref_ptr<AlphaController> osgctrl(new AlphaController(alphactrl->data.getPtr(), baseMaterial));
|
osg::ref_ptr<AlphaController> 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);
|
setupController(alphactrl, osgctrl, animflags);
|
||||||
composite->addController(osgctrl);
|
composite->addController(osgctrl);
|
||||||
}
|
}
|
||||||
else if (ctrl->recType == Nif::RC_NiMaterialColorController)
|
else if (ctrl->recType == Nif::RC_NiMaterialColorController)
|
||||||
{
|
{
|
||||||
const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr());
|
const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr());
|
||||||
if (matctrl->data.empty())
|
if (matctrl->data.empty() && matctrl->interpolator.empty())
|
||||||
continue;
|
continue;
|
||||||
|
osg::ref_ptr<MaterialColorController> osgctrl;
|
||||||
auto targetColor = static_cast<MaterialColorController::TargetColor>(matctrl->targetColor);
|
auto targetColor = static_cast<MaterialColorController::TargetColor>(matctrl->targetColor);
|
||||||
osg::ref_ptr<MaterialColorController> 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);
|
setupController(matctrl, osgctrl, animflags);
|
||||||
composite->addController(osgctrl);
|
composite->addController(osgctrl);
|
||||||
}
|
}
|
||||||
|
@ -1175,7 +1229,7 @@ namespace NifOsg
|
||||||
|
|
||||||
void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, int animflags)
|
void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& 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<osg::Drawable> drawable;
|
osg::ref_ptr<osg::Drawable> drawable;
|
||||||
osg::ref_ptr<osg::Geometry> geom (new osg::Geometry);
|
osg::ref_ptr<osg::Geometry> geom (new osg::Geometry);
|
||||||
handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags);
|
handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags);
|
||||||
|
@ -1190,7 +1244,7 @@ namespace NifOsg
|
||||||
continue;
|
continue;
|
||||||
drawable = handleMorphGeometry(nimorphctrl, geom, parentNode, composite, boundTextures, animflags);
|
drawable = handleMorphGeometry(nimorphctrl, geom, parentNode, composite, boundTextures, animflags);
|
||||||
|
|
||||||
osg::ref_ptr<GeomMorpherController> morphctrl = new GeomMorpherController(nimorphctrl->data.getPtr());
|
osg::ref_ptr<GeomMorpherController> morphctrl = new GeomMorpherController(nimorphctrl);
|
||||||
setupController(ctrl.getPtr(), morphctrl, animflags);
|
setupController(ctrl.getPtr(), morphctrl, animflags);
|
||||||
drawable->setUpdateCallback(morphctrl);
|
drawable->setUpdateCallback(morphctrl);
|
||||||
break;
|
break;
|
||||||
|
@ -1220,7 +1274,7 @@ namespace NifOsg
|
||||||
void handleSkinnedGeometry(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite,
|
void handleSkinnedGeometry(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite,
|
||||||
const std::vector<unsigned int>& boundTextures, int animflags)
|
const std::vector<unsigned int>& 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<osg::Geometry> geometry (new osg::Geometry);
|
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
|
||||||
handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags);
|
handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags);
|
||||||
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);
|
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);
|
||||||
|
|
|
@ -220,6 +220,7 @@ namespace Resource
|
||||||
, mClampLighting(true)
|
, mClampLighting(true)
|
||||||
, mAutoUseNormalMaps(false)
|
, mAutoUseNormalMaps(false)
|
||||||
, mAutoUseSpecularMaps(false)
|
, mAutoUseSpecularMaps(false)
|
||||||
|
, mApplyLightingToEnvMaps(false)
|
||||||
, mInstanceCache(new MultiObjectCache)
|
, mInstanceCache(new MultiObjectCache)
|
||||||
, mSharedStateManager(new SharedStateManager)
|
, mSharedStateManager(new SharedStateManager)
|
||||||
, mImageManager(imageManager)
|
, mImageManager(imageManager)
|
||||||
|
@ -284,6 +285,11 @@ namespace Resource
|
||||||
mSpecularMapPattern = pattern;
|
mSpecularMapPattern = pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SceneManager::setApplyLightingToEnvMaps(bool apply)
|
||||||
|
{
|
||||||
|
mApplyLightingToEnvMaps = apply;
|
||||||
|
}
|
||||||
|
|
||||||
SceneManager::~SceneManager()
|
SceneManager::~SceneManager()
|
||||||
{
|
{
|
||||||
// this has to be defined in the .cpp file as we can't delete incomplete types
|
// 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->setNormalHeightMapPattern(mNormalHeightMapPattern);
|
||||||
shaderVisitor->setAutoUseSpecularMaps(mAutoUseSpecularMaps);
|
shaderVisitor->setAutoUseSpecularMaps(mAutoUseSpecularMaps);
|
||||||
shaderVisitor->setSpecularMapPattern(mSpecularMapPattern);
|
shaderVisitor->setSpecularMapPattern(mSpecularMapPattern);
|
||||||
|
shaderVisitor->setApplyLightingToEnvMaps(mApplyLightingToEnvMaps);
|
||||||
return shaderVisitor;
|
return shaderVisitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,8 @@ namespace Resource
|
||||||
|
|
||||||
void setSpecularMapPattern(const std::string& pattern);
|
void setSpecularMapPattern(const std::string& pattern);
|
||||||
|
|
||||||
|
void setApplyLightingToEnvMaps(bool apply);
|
||||||
|
|
||||||
void setShaderPath(const std::string& path);
|
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
|
/// 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;
|
std::string mNormalHeightMapPattern;
|
||||||
bool mAutoUseSpecularMaps;
|
bool mAutoUseSpecularMaps;
|
||||||
std::string mSpecularMapPattern;
|
std::string mSpecularMapPattern;
|
||||||
|
bool mApplyLightingToEnvMaps;
|
||||||
|
|
||||||
osg::ref_ptr<MultiObjectCache> mInstanceCache;
|
osg::ref_ptr<MultiObjectCache> mInstanceCache;
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <osg/Depth>
|
#include <osg/Depth>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include "shadowsbin.hpp"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -273,10 +274,20 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
cv->pushCullingSet();
|
cv->pushCullingSet();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
// bin has to go inside camera cull or the rendertexture stage will override it
|
||||||
|
static osg::ref_ptr<osg::StateSet> 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())
|
if (_vdsm->getShadowedScene())
|
||||||
{
|
{
|
||||||
_vdsm->getShadowedScene()->osg::Group::traverse(*nv);
|
_vdsm->getShadowedScene()->osg::Group::traverse(*nv);
|
||||||
}
|
}
|
||||||
|
cv->popStateSet();
|
||||||
#if 1
|
#if 1
|
||||||
if (!_polytope.empty())
|
if (!_polytope.empty())
|
||||||
{
|
{
|
||||||
|
@ -555,6 +566,7 @@ MWShadowTechnique::ShadowData::ShadowData(MWShadowTechnique::ViewDependentData*
|
||||||
_camera = new osg::Camera;
|
_camera = new osg::Camera;
|
||||||
_camera->setName("ShadowCamera");
|
_camera->setName("ShadowCamera");
|
||||||
_camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
|
_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(1.0f,1.0f,1.0f,1.0f));
|
||||||
_camera->setClearColor(osg::Vec4(0.0f,0.0f,0.0f,0.0f));
|
_camera->setClearColor(osg::Vec4(0.0f,0.0f,0.0f,0.0f));
|
||||||
|
@ -874,15 +886,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_vertex.glsl", Shader::ShaderManager::DefineMap(), osg::Shader::VERTEX));
|
||||||
_castingProgram->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", Shader::ShaderManager::DefineMap(), osg::Shader::FRAGMENT));
|
_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*/)
|
MWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData(osgUtil::CullVisitor* /*cv*/)
|
||||||
|
@ -1583,17 +1586,14 @@ void MWShadowTechnique::createShaders()
|
||||||
_shadowCastingStateSet->setAttributeAndModes(_castingProgram, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
_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
|
// 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->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON);
|
||||||
_shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false));
|
_shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true));
|
||||||
_shadowCastingStateSet->addUniform(_shadowMapAlphaTestDisableUniform);
|
_shadowCastingStateSet->addUniform(new osg::Uniform("alphaTestShadows", false));
|
||||||
osg::ref_ptr<osg::Depth> depth = new osg::Depth;
|
osg::ref_ptr<osg::Depth> depth = new osg::Depth;
|
||||||
depth->setWriteMask(true);
|
depth->setWriteMask(true);
|
||||||
_shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
_shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||||
_shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON);
|
_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 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)
|
osg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight)
|
||||||
|
|
|
@ -288,7 +288,6 @@ namespace SceneUtil {
|
||||||
|
|
||||||
osg::ref_ptr<DebugHUD> _debugHud;
|
osg::ref_ptr<DebugHUD> _debugHud;
|
||||||
osg::ref_ptr<osg::Program> _castingProgram;
|
osg::ref_ptr<osg::Program> _castingProgram;
|
||||||
osg::ref_ptr<osg::Uniform> _shadowMapAlphaTestDisableUniform;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
166
components/sceneutil/shadowsbin.cpp
Normal file
166
components/sceneutil/shadowsbin.cpp
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
#include "shadowsbin.hpp"
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <osg/StateSet>
|
||||||
|
#include <osg/Material>
|
||||||
|
#include <osgUtil/StateGraph>
|
||||||
|
|
||||||
|
using namespace osgUtil;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace SceneUtil
|
||||||
|
{
|
||||||
|
|
||||||
|
ShadowsBin::ShadowsBin()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set<StateGraph*>& uninterestingCache)
|
||||||
|
{
|
||||||
|
std::vector<StateGraph*> return_path;
|
||||||
|
State state;
|
||||||
|
StateGraph* sg_new = sg;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
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(auto 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& 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 = found->second;
|
||||||
|
accumulateState(state.mMaterial, static_cast<osg::Material*>(rap.first.get()), state.mMaterialOverride, rap.second);
|
||||||
|
if (state.mMaterial && !materialNeedShadows(state.mMaterial))
|
||||||
|
state.mMaterial = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
uninterestingCache.insert(*itr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.needShadows())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!state.needTexture() && !state.mImportantState)
|
||||||
|
{
|
||||||
|
for (RenderLeaf* leaf : sg->_leaves)
|
||||||
|
{
|
||||||
|
leaf->_parent = root;
|
||||||
|
root->_leaves.push_back(leaf);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.mAlphaBlend)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShadowsBin::State::needShadows() const
|
||||||
|
{
|
||||||
|
if (!mMaterial)
|
||||||
|
return true;
|
||||||
|
return materialNeedShadows(mMaterial);
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
StateGraph* noTestRoot = root->find_or_insert(mNoTestStateSet.get());
|
||||||
|
// root is now a stategraph with useDiffuseMapForShadowAlpha disabled but minimal other state
|
||||||
|
noTestRoot->_leaves.reserve(_stateGraphList.size());
|
||||||
|
StateGraphList newList;
|
||||||
|
std::unordered_set<StateGraph*> uninterestingCache;
|
||||||
|
for (StateGraph* graph : _stateGraphList)
|
||||||
|
{
|
||||||
|
// Render leaves which shouldn't use the diffuse map for shadow alpha but do cast shadows become children of root, so graph is now empty. Don't add to newList.
|
||||||
|
// Graphs containing just render leaves which don't cast shadows are discarded. Don't add to newList.
|
||||||
|
// Graphs containing other leaves need to be in newList.
|
||||||
|
StateGraph* graphToAdd = cullStateGraph(graph, noTestRoot, uninterestingCache);
|
||||||
|
if (graphToAdd)
|
||||||
|
newList.push_back(graphToAdd);
|
||||||
|
}
|
||||||
|
if (!noTestRoot->_leaves.empty())
|
||||||
|
newList.push_back(noTestRoot);
|
||||||
|
_stateGraphList = newList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
76
components/sceneutil/shadowsbin.hpp
Normal file
76
components/sceneutil/shadowsbin.hpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H
|
||||||
|
#define OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <osgUtil/RenderBin>
|
||||||
|
|
||||||
|
namespace osg
|
||||||
|
{
|
||||||
|
class Material;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace SceneUtil
|
||||||
|
{
|
||||||
|
|
||||||
|
/// renderbin which culls redundant state for shadow map rendering
|
||||||
|
class ShadowsBin : public osgUtil::RenderBin
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
osg::ref_ptr<osg::StateSet> mNoTestStateSet;
|
||||||
|
osg::ref_ptr<osg::StateSet> mShaderAlphaTestStateSet;
|
||||||
|
public:
|
||||||
|
META_Object(SceneUtil, ShadowsBin)
|
||||||
|
ShadowsBin();
|
||||||
|
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)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool mAlphaBlend;
|
||||||
|
bool mAlphaBlendOverride;
|
||||||
|
bool mAlphaTest;
|
||||||
|
bool mAlphaTestOverride;
|
||||||
|
osg::Material* mMaterial;
|
||||||
|
bool mMaterialOverride;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
osgUtil::StateGraph* cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set<osgUtil::StateGraph*>& uninteresting);
|
||||||
|
|
||||||
|
static void addPrototype(const std::string& name)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osgUtil::RenderBin> bin (new ShadowsBin);
|
||||||
|
osgUtil::RenderBin::addRenderBinPrototype(name, bin);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShadowsBinAdder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadowsBinAdder(const std::string& name){ ShadowsBin::addPrototype(name); }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -384,14 +384,4 @@ namespace Shader
|
||||||
program.second->releaseGLObjects(state);
|
program.second->releaseGLObjects(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
const osg::ref_ptr<osg::Uniform> ShaderManager::getShadowMapAlphaTestEnableUniform()
|
|
||||||
{
|
|
||||||
return mShadowMapAlphaTestEnableUniform;
|
|
||||||
}
|
|
||||||
|
|
||||||
const osg::ref_ptr<osg::Uniform> ShaderManager::getShadowMapAlphaTestDisableUniform()
|
|
||||||
{
|
|
||||||
return mShadowMapAlphaTestDisableUniform;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,9 +43,6 @@ namespace Shader
|
||||||
|
|
||||||
void releaseGLObjects(osg::State* state);
|
void releaseGLObjects(osg::State* state);
|
||||||
|
|
||||||
const osg::ref_ptr<osg::Uniform> getShadowMapAlphaTestEnableUniform();
|
|
||||||
const osg::ref_ptr<osg::Uniform> getShadowMapAlphaTestDisableUniform();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
|
|
||||||
|
@ -63,9 +60,6 @@ namespace Shader
|
||||||
ProgramMap mPrograms;
|
ProgramMap mPrograms;
|
||||||
|
|
||||||
std::mutex mMutex;
|
std::mutex mMutex;
|
||||||
|
|
||||||
const osg::ref_ptr<osg::Uniform> mShadowMapAlphaTestEnableUniform = new osg::Uniform();
|
|
||||||
const osg::ref_ptr<osg::Uniform> mShadowMapAlphaTestDisableUniform = new osg::Uniform();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool parseFors(std::string& source, const std::string& templateName);
|
bool parseFors(std::string& source, const std::string& templateName);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "shadervisitor.hpp"
|
#include "shadervisitor.hpp"
|
||||||
|
|
||||||
#include <osg/AlphaFunc>
|
|
||||||
#include <osg/BlendFunc>
|
|
||||||
#include <osg/Geometry>
|
#include <osg/Geometry>
|
||||||
#include <osg/Material>
|
#include <osg/Material>
|
||||||
#include <osg/Texture>
|
#include <osg/Texture>
|
||||||
|
@ -14,7 +12,6 @@
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
#include <components/sceneutil/riggeometry.hpp>
|
#include <components/sceneutil/riggeometry.hpp>
|
||||||
#include <components/sceneutil/morphgeometry.hpp>
|
#include <components/sceneutil/morphgeometry.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
|
||||||
|
|
||||||
#include "shadermanager.hpp"
|
#include "shadermanager.hpp"
|
||||||
|
|
||||||
|
@ -25,7 +22,6 @@ namespace Shader
|
||||||
: mShaderRequired(false)
|
: mShaderRequired(false)
|
||||||
, mColorMode(0)
|
, mColorMode(0)
|
||||||
, mMaterialOverridden(false)
|
, mMaterialOverridden(false)
|
||||||
, mBlendFuncOverridden(false)
|
|
||||||
, mNormalHeight(false)
|
, mNormalHeight(false)
|
||||||
, mTexStageRequiringTangents(-1)
|
, mTexStageRequiringTangents(-1)
|
||||||
, mNode(nullptr)
|
, mNode(nullptr)
|
||||||
|
@ -43,6 +39,7 @@ namespace Shader
|
||||||
, mAllowedToModifyStateSets(true)
|
, mAllowedToModifyStateSets(true)
|
||||||
, mAutoUseNormalMaps(false)
|
, mAutoUseNormalMaps(false)
|
||||||
, mAutoUseSpecularMaps(false)
|
, mAutoUseSpecularMaps(false)
|
||||||
|
, mApplyLightingToEnvMaps(false)
|
||||||
, mShaderManager(shaderManager)
|
, mShaderManager(shaderManager)
|
||||||
, mImageManager(imageManager)
|
, mImageManager(imageManager)
|
||||||
, mDefaultVsTemplate(defaultVsTemplate)
|
, mDefaultVsTemplate(defaultVsTemplate)
|
||||||
|
@ -144,11 +141,9 @@ namespace Shader
|
||||||
// Bump maps are off by default as well
|
// Bump maps are off by default as well
|
||||||
writableStateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON);
|
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");
|
mRequirements.back().mShaderRequired = true;
|
||||||
if (preLightEnv)
|
|
||||||
mRequirements.back().mShaderRequired = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -233,14 +228,11 @@ namespace Shader
|
||||||
if (!writableStateSet)
|
if (!writableStateSet)
|
||||||
writableStateSet = getWritableStateSet(node);
|
writableStateSet = getWritableStateSet(node);
|
||||||
// We probably shouldn't construct a new version of this each time as Uniforms use pointer comparison for early-out.
|
// 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));
|
writableStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool alphaSettingsChanged = false;
|
|
||||||
bool alphaTestShadows = false;
|
|
||||||
|
|
||||||
const osg::StateSet::AttributeList& attributes = stateset->getAttributeList();
|
const osg::StateSet::AttributeList& attributes = stateset->getAttributeList();
|
||||||
for (osg::StateSet::AttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it)
|
for (osg::StateSet::AttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it)
|
||||||
{
|
{
|
||||||
|
@ -284,28 +276,8 @@ namespace Shader
|
||||||
mRequirements.back().mColorMode = colorMode;
|
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<const osg::BlendFunc*>(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
|
// 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)
|
void ShaderVisitor::pushRequirements(osg::Node& node)
|
||||||
|
@ -477,4 +449,9 @@ namespace Shader
|
||||||
mSpecularMapPattern = pattern;
|
mSpecularMapPattern = pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShaderVisitor::setApplyLightingToEnvMaps(bool apply)
|
||||||
|
{
|
||||||
|
mApplyLightingToEnvMaps = apply;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ namespace Shader
|
||||||
|
|
||||||
void setSpecularMapPattern(const std::string& pattern);
|
void setSpecularMapPattern(const std::string& pattern);
|
||||||
|
|
||||||
|
void setApplyLightingToEnvMaps(bool apply);
|
||||||
|
|
||||||
void apply(osg::Node& node) override;
|
void apply(osg::Node& node) override;
|
||||||
|
|
||||||
void apply(osg::Drawable& drawable) override;
|
void apply(osg::Drawable& drawable) override;
|
||||||
|
@ -59,6 +61,8 @@ namespace Shader
|
||||||
bool mAutoUseSpecularMaps;
|
bool mAutoUseSpecularMaps;
|
||||||
std::string mSpecularMapPattern;
|
std::string mSpecularMapPattern;
|
||||||
|
|
||||||
|
bool mApplyLightingToEnvMaps;
|
||||||
|
|
||||||
ShaderManager& mShaderManager;
|
ShaderManager& mShaderManager;
|
||||||
Resource::ImageManager& mImageManager;
|
Resource::ImageManager& mImageManager;
|
||||||
|
|
||||||
|
@ -75,7 +79,6 @@ namespace Shader
|
||||||
int mColorMode;
|
int mColorMode;
|
||||||
|
|
||||||
bool mMaterialOverridden;
|
bool mMaterialOverridden;
|
||||||
bool mBlendFuncOverridden;
|
|
||||||
|
|
||||||
bool mNormalHeight; // true if normal map has height info in alpha channel
|
bool mNormalHeight; // true if normal map has height info in alpha channel
|
||||||
|
|
||||||
|
|
|
@ -58,3 +58,4 @@ The ranges included with each setting are the physically possible ranges, not re
|
||||||
windows
|
windows
|
||||||
navigator
|
navigator
|
||||||
physics
|
physics
|
||||||
|
models
|
||||||
|
|
31
docs/source/reference/modding/settings/models.rst
Normal file
31
docs/source/reference/modding/settings/models.rst
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
Models Settings
|
||||||
|
###############
|
||||||
|
|
||||||
|
load unsupported nif files
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: False
|
||||||
|
|
||||||
|
Allow the engine to load arbitrary NIF files as long as they appear to be valid.
|
||||||
|
|
||||||
|
OpenMW has limited and **experimental** support for NIF files
|
||||||
|
that Morrowind itself cannot load, which normally goes unused.
|
||||||
|
|
||||||
|
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.
|
|
@ -933,13 +933,20 @@ object shadows = false
|
||||||
enable indoor shadows = true
|
enable indoor shadows = true
|
||||||
|
|
||||||
[Physics]
|
[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
|
async num threads = 0
|
||||||
|
|
||||||
# maintain a cache of lineofsight request in the bacground physics thread
|
# Set the number of frames an inactive line-of-sight request will be kept
|
||||||
# determines for how much frames an inactive lineofsight request should be kept updated in the cache
|
# refreshed in the background physics thread cache.
|
||||||
# -1 to disable (i.e the LOS will be calculated only on request)
|
# If this is set to -1, line-of-sight requests are never cached.
|
||||||
lineofsight keep inactive cache = 0
|
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
|
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
|
||||||
|
|
Loading…
Reference in a new issue