mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 03:15:32 +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
|
||||
libavcodec-dev, libavformat-dev, libavutil-dev, libswresample-dev, libswscale-dev,
|
||||
# Audio, Video and Misc. deps
|
||||
libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, liblz4-dev
|
||||
libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, liblz4-dev,
|
||||
# The other ones from OpenMW ppa
|
||||
libbullet-dev, libopenscenegraph-3.4-dev, libmygui-dev,
|
||||
# tes3mp stuff
|
||||
|
@ -71,11 +71,11 @@ before_script:
|
|||
- ./CI/before_script.${TRAVIS_OS_NAME}.sh
|
||||
script:
|
||||
- cd ./build
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ${ANALYZE} make -j3; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ] && [ "${BUILD_TESTS_ONLY}" ]; then ./openmw_test_suite; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||
- ${ANALYZE} make -j3; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ] && [ "${BUILD_TESTS_ONLY}" ]; then ./openmw_test_suite; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||
- cd "${TRAVIS_BUILD_DIR}"
|
||||
- ccache -s
|
||||
#deploy:
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
Bug #2976 [reopened]: Issues combining settings from the command line and both config files
|
||||
Bug #3676: NiParticleColorModifier isn't applied properly
|
||||
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
||||
Bug #3789: Crash in visitEffectSources while in battle
|
||||
Bug #3862: Random container contents behave differently than vanilla
|
||||
Bug #3929: Leveled list merchant containers respawn on barter
|
||||
Bug #4021: Attributes and skills are not stored as floats
|
||||
Bug #4055: Local scripts don't inherit variables from their base record
|
||||
Bug #4083: Door animation freezes when colliding with actors
|
||||
Bug #4623: Corprus implementation is incorrect
|
||||
Bug #4631: Setting MSAA level too high doesn't fall back to highest supported level
|
||||
Bug #4764: Data race in osg ParticleSystem
|
||||
|
|
|
@ -5,8 +5,5 @@ command -v ccache >/dev/null 2>&1 || brew install ccache
|
|||
command -v cmake >/dev/null 2>&1 || brew install cmake
|
||||
command -v qmake >/dev/null 2>&1 || brew install qt
|
||||
|
||||
brew link --overwrite lz4 # overwrite system lz4; use brew
|
||||
brew reinstall lz4
|
||||
|
||||
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-ef2462c.zip -o ~/openmw-deps.zip
|
||||
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-f8918dd.zip -o ~/openmw-deps.zip
|
||||
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||
|
|
|
@ -25,5 +25,6 @@ cmake \
|
|||
-D BUILD_BSATOOL=TRUE \
|
||||
-D BUILD_ESSIMPORTER=TRUE \
|
||||
-D BUILD_NIFTEST=TRUE \
|
||||
-D BULLET_USE_DOUBLES=TRUE \
|
||||
-G"Unix Makefiles" \
|
||||
..
|
||||
|
|
|
@ -132,6 +132,7 @@ int main(int argc, char **argv)
|
|||
if(!parseOptions (argc, argv, files))
|
||||
return 1;
|
||||
|
||||
Nif::NIFFile::setLoadUnsupportedFiles(true);
|
||||
// std::cout << "Reading Files" << std::endl;
|
||||
for(std::vector<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)
|
||||
{
|
||||
CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||
const MagicEffects &effects = creatureStats.getMagicEffects();
|
||||
bool godmode = ptr == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
||||
|
||||
applyCureEffects(ptr);
|
||||
|
||||
bool wasDead = creatureStats.isDead();
|
||||
|
||||
if (duration > 0)
|
||||
|
@ -1810,6 +1852,9 @@ namespace MWMechanics
|
|||
|
||||
void Actors::predictAndAvoidCollisions()
|
||||
{
|
||||
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
||||
return;
|
||||
|
||||
const float minGap = 10.f;
|
||||
const float maxDistForPartialAvoiding = 200.f;
|
||||
const float maxDistForStrictAvoiding = 100.f;
|
||||
|
|
|
@ -226,6 +226,7 @@ namespace MWMechanics
|
|||
|
||||
private:
|
||||
void updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl);
|
||||
void applyCureEffects (const MWWorld::Ptr& actor);
|
||||
|
||||
PtrActorMap mActors;
|
||||
float mTimerDisposeSummonsCorpses;
|
||||
|
|
|
@ -217,40 +217,6 @@ namespace MWMechanics
|
|||
break;
|
||||
}
|
||||
|
||||
case ESM::MagicEffect::CurePoison:
|
||||
actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison);
|
||||
break;
|
||||
case ESM::MagicEffect::CureParalyzation:
|
||||
actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze);
|
||||
break;
|
||||
case ESM::MagicEffect::CureCommonDisease:
|
||||
actor.getClass().getCreatureStats(actor).getSpells().purgeCommonDisease();
|
||||
break;
|
||||
case ESM::MagicEffect::CureBlightDisease:
|
||||
actor.getClass().getCreatureStats(actor).getSpells().purgeBlightDisease();
|
||||
break;
|
||||
case ESM::MagicEffect::CureCorprusDisease:
|
||||
actor.getClass().getCreatureStats(actor).getSpells().purgeCorprusDisease();
|
||||
break;
|
||||
case ESM::MagicEffect::RemoveCurse:
|
||||
actor.getClass().getCreatureStats(actor).getSpells().purgeCurses();
|
||||
break;
|
||||
/*
|
||||
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:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace MWMechanics
|
|||
struct EffectKey;
|
||||
|
||||
/// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed
|
||||
/// Note: this function works in loop, so magic effects should not be removed here to avoid iterator invalidation.
|
||||
/// @return Was the effect a tickable effect with a magnitude?
|
||||
bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey& effectKey, float magnitude);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
||||
|
||||
#include "components/misc/convert.hpp"
|
||||
|
||||
#include "ptrholder.hpp"
|
||||
|
||||
namespace MWPhysics
|
||||
|
@ -20,7 +22,7 @@ namespace MWPhysics
|
|||
collisionObject = col1Wrap->m_collisionObject;
|
||||
PtrHolder* holder = static_cast<PtrHolder*>(collisionObject->getUserPointer());
|
||||
if (holder)
|
||||
mResult.push_back(holder->getPtr());
|
||||
mResult.emplace_back(ContactPoint{holder->getPtr(), Misc::Convert::toOsg(cp.m_positionWorldOnB), Misc::Convert::toOsg(cp.m_normalWorldOnB)});
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include "physicssystem.hpp"
|
||||
|
||||
class btCollisionObject;
|
||||
struct btCollisionObjectWrapper;
|
||||
|
||||
|
@ -23,7 +25,7 @@ namespace MWPhysics
|
|||
const btCollisionObjectWrapper* col0Wrap,int partId0,int index0,
|
||||
const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) override;
|
||||
|
||||
std::vector<MWWorld::Ptr> mResult;
|
||||
std::vector<ContactPoint> mResult;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -427,15 +427,15 @@ namespace MWPhysics
|
|||
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;
|
||||
|
||||
ObjectMap::const_iterator found = mObjects.find(ptr);
|
||||
auto found = mObjects.find(ptr);
|
||||
if (found != mObjects.end())
|
||||
me = found->second->getCollisionObject();
|
||||
else
|
||||
return std::vector<MWWorld::Ptr>();
|
||||
return {};
|
||||
|
||||
ContactTestResultCallback resultCallback (me);
|
||||
resultCallback.m_collisionFilterGroup = collisionGroup;
|
||||
|
@ -444,6 +444,14 @@ namespace MWPhysics
|
|||
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)
|
||||
{
|
||||
ActorMap::iterator found = mActors.find(ptr);
|
||||
|
|
|
@ -57,6 +57,13 @@ namespace MWPhysics
|
|||
class Actor;
|
||||
class PhysicsTaskScheduler;
|
||||
|
||||
struct ContactPoint
|
||||
{
|
||||
MWWorld::Ptr mObject;
|
||||
osg::Vec3f mPoint;
|
||||
osg::Vec3f mNormal;
|
||||
};
|
||||
|
||||
struct LOSRequest
|
||||
{
|
||||
LOSRequest(const std::weak_ptr<Actor>& a1, const std::weak_ptr<Actor>& a2);
|
||||
|
@ -145,6 +152,7 @@ namespace MWPhysics
|
|||
void debugDraw();
|
||||
|
||||
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);
|
||||
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::ConstPtr& actor,
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/resource/keyframemanager.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
@ -37,8 +36,6 @@
|
|||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
@ -493,9 +490,8 @@ namespace MWRender
|
|||
class TransparencyUpdater : public SceneUtil::StateSetUpdater
|
||||
{
|
||||
public:
|
||||
TransparencyUpdater(const float alpha, osg::ref_ptr<osg::Uniform> shadowUniform)
|
||||
TransparencyUpdater(const float alpha)
|
||||
: mAlpha(alpha)
|
||||
, mShadowUniform(shadowUniform)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -509,9 +505,6 @@ namespace MWRender
|
|||
{
|
||||
osg::BlendFunc* blendfunc (new osg::BlendFunc);
|
||||
stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
// TODO: don't do this anymore once custom shadow renderbin is handling it
|
||||
if (mShadowUniform)
|
||||
stateset->addUniform(mShadowUniform);
|
||||
|
||||
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
|
||||
stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
|
||||
|
@ -533,7 +526,6 @@ namespace MWRender
|
|||
|
||||
private:
|
||||
float mAlpha;
|
||||
osg::ref_ptr<osg::Uniform> mShadowUniform;
|
||||
};
|
||||
|
||||
struct Animation::AnimSource
|
||||
|
@ -1773,7 +1765,7 @@ namespace MWRender
|
|||
{
|
||||
if (mTransparencyUpdater == nullptr)
|
||||
{
|
||||
mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform());
|
||||
mTransparencyUpdater = new TransparencyUpdater(alpha);
|
||||
mObjectRoot->addCullCallback(mTransparencyUpdater);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -216,6 +216,7 @@ namespace MWRender
|
|||
resourceSystem->getSceneManager()->setNormalHeightMapPattern(Settings::Manager::getString("normal height map pattern", "Shaders"));
|
||||
resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders"));
|
||||
resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders"));
|
||||
resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders"));
|
||||
|
||||
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager;
|
||||
sceneRoot->setLightingMask(Mask_Lighting);
|
||||
|
@ -361,6 +362,7 @@ namespace MWRender
|
|||
mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater));
|
||||
NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor);
|
||||
NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect);
|
||||
Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models"));
|
||||
|
||||
mNearClip = Settings::Manager::getFloat("near clip", "Camera");
|
||||
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
|
||||
|
|
|
@ -1771,21 +1771,28 @@ namespace MWWorld
|
|||
float minRot = door.getCellRef().getPosition().rot[2];
|
||||
float maxRot = minRot + osg::DegreesToRadians(90.f);
|
||||
|
||||
float diff = duration * osg::DegreesToRadians(90.f);
|
||||
float targetRot = std::min(std::max(minRot, oldRot + diff * (state == MWWorld::DoorState::Opening ? 1 : -1)), maxRot);
|
||||
float diff = duration * osg::DegreesToRadians(90.f) * (state == MWWorld::DoorState::Opening ? 1 : -1);
|
||||
float targetRot = std::min(std::max(minRot, oldRot + diff), maxRot);
|
||||
rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot, MWBase::RotationFlag_none);
|
||||
|
||||
bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot;
|
||||
|
||||
/// \todo should use convexSweepTest here
|
||||
bool collisionWithActor = false;
|
||||
std::vector<MWWorld::Ptr> collisions = mPhysics->getCollisions(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor);
|
||||
for (MWWorld::Ptr& ptr : collisions)
|
||||
for (auto& [ptr, point, normal] : mPhysics->getCollisionsPoints(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor))
|
||||
{
|
||||
|
||||
if (ptr.getClass().isActor())
|
||||
{
|
||||
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;
|
||||
|
||||
|
||||
// Collided with actor, ask actor to try to avoid door
|
||||
if(ptr != getPlayerPtr() )
|
||||
{
|
||||
|
|
|
@ -373,6 +373,7 @@ namespace
|
|||
TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default)
|
||||
{
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -422,11 +423,13 @@ namespace
|
|||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -444,12 +447,14 @@ namespace
|
|||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -467,16 +472,19 @@ namespace
|
|||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
mNiNode.hasBounds = true;
|
||||
mNiNode.boundXYZ = osg::Vec3f(4, 5, 6);
|
||||
mNiNode.boundPos = osg::Vec3f(-4, -5, -6);
|
||||
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNiNode.bounds.box.extents = osg::Vec3f(4, 5, 6);
|
||||
mNiNode.bounds.box.center = osg::Vec3f(-4, -5, -6);
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -494,20 +502,24 @@ namespace
|
|||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
mNode2.hasBounds = true;
|
||||
mNode2.boundXYZ = osg::Vec3f(4, 5, 6);
|
||||
mNode2.boundPos = osg::Vec3f(-4, -5, -6);
|
||||
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
|
||||
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
|
||||
|
||||
mNiNode.hasBounds = true;
|
||||
mNiNode.boundXYZ = osg::Vec3f(7, 8, 9);
|
||||
mNiNode.boundPos = osg::Vec3f(-7, -8, -9);
|
||||
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9);
|
||||
mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9);
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -524,21 +536,25 @@ namespace
|
|||
TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_second_with_flag_should_use_second_bounds)
|
||||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
mNode2.hasBounds = true;
|
||||
mNode2.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||
mNode2.boundXYZ = osg::Vec3f(4, 5, 6);
|
||||
mNode2.boundPos = osg::Vec3f(-4, -5, -6);
|
||||
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
|
||||
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
|
||||
|
||||
mNiNode.hasBounds = true;
|
||||
mNiNode.boundXYZ = osg::Vec3f(7, 8, 9);
|
||||
mNiNode.boundPos = osg::Vec3f(-7, -8, -9);
|
||||
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9);
|
||||
mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9);
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -555,8 +571,9 @@ namespace
|
|||
TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounds_but_without_flag_should_return_shape_with_bounds_but_with_null_collision_shape)
|
||||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
|
||||
|
@ -588,8 +605,9 @@ namespace
|
|||
TEST_F(TestBulletNifLoader, for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape)
|
||||
{
|
||||
mNiTriShape.hasBounds = true;
|
||||
mNiTriShape.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNiTriShape.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNiTriShape.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNiTriShape.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNiTriShape.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));
|
||||
|
|
|
@ -52,7 +52,7 @@ add_component_dir (shader
|
|||
add_component_dir (sceneutil
|
||||
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
|
||||
lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer
|
||||
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh
|
||||
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin
|
||||
)
|
||||
|
||||
add_component_dir (nif
|
||||
|
|
|
@ -11,11 +11,22 @@
|
|||
namespace Debug
|
||||
{
|
||||
#ifdef _WIN32
|
||||
bool isRedirected(DWORD nStdHandle)
|
||||
{
|
||||
DWORD fileType = GetFileType(GetStdHandle(nStdHandle));
|
||||
|
||||
return (fileType == FILE_TYPE_DISK) || (fileType == FILE_TYPE_PIPE);
|
||||
}
|
||||
|
||||
bool attachParentConsole()
|
||||
{
|
||||
if (GetConsoleWindow() != nullptr)
|
||||
return true;
|
||||
|
||||
bool inRedirected = isRedirected(STD_INPUT_HANDLE);
|
||||
bool outRedirected = isRedirected(STD_OUTPUT_HANDLE);
|
||||
bool errRedirected = isRedirected(STD_ERROR_HANDLE);
|
||||
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS))
|
||||
{
|
||||
fflush(stdout);
|
||||
|
@ -24,12 +35,21 @@ namespace Debug
|
|||
std::cerr.flush();
|
||||
|
||||
// this looks dubious but is really the right way
|
||||
_wfreopen(L"CON", L"w", stdout);
|
||||
_wfreopen(L"CON", L"w", stderr);
|
||||
_wfreopen(L"CON", L"r", stdin);
|
||||
freopen("CON", "w", stdout);
|
||||
freopen("CON", "w", stderr);
|
||||
freopen("CON", "r", stdin);
|
||||
if (!inRedirected)
|
||||
{
|
||||
_wfreopen(L"CON", L"r", stdin);
|
||||
freopen("CON", "r", stdin);
|
||||
}
|
||||
if (!outRedirected)
|
||||
{
|
||||
_wfreopen(L"CON", L"w", stdout);
|
||||
freopen("CON", "w", stdout);
|
||||
}
|
||||
if (!errRedirected)
|
||||
{
|
||||
_wfreopen(L"CON", L"w", stderr);
|
||||
freopen("CON", "w", stderr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -92,6 +92,8 @@ namespace Nif
|
|||
void NiMaterialColorController::read(NIFStream *nif)
|
||||
{
|
||||
Controller::read(nif);
|
||||
if (nif->getVersion() > NIFStream::generateVersion(10,1,0,103))
|
||||
interpolator.read(nif);
|
||||
// Two bits that correspond to the controlled material color.
|
||||
// 00: Ambient
|
||||
// 01: Diffuse
|
||||
|
@ -101,12 +103,14 @@ namespace Nif
|
|||
targetColor = nif->getUShort() & 3;
|
||||
else
|
||||
targetColor = (flags >> 4) & 3;
|
||||
data.read(nif);
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))
|
||||
data.read(nif);
|
||||
}
|
||||
|
||||
void NiMaterialColorController::post(NIFFile *nif)
|
||||
{
|
||||
Controller::post(nif);
|
||||
interpolator.post(nif);
|
||||
data.post(nif);
|
||||
}
|
||||
|
||||
|
@ -161,25 +165,33 @@ namespace Nif
|
|||
void NiKeyframeController::read(NIFStream *nif)
|
||||
{
|
||||
Controller::read(nif);
|
||||
data.read(nif);
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))
|
||||
data.read(nif);
|
||||
else
|
||||
interpolator.read(nif);
|
||||
}
|
||||
|
||||
void NiKeyframeController::post(NIFFile *nif)
|
||||
{
|
||||
Controller::post(nif);
|
||||
data.post(nif);
|
||||
interpolator.post(nif);
|
||||
}
|
||||
|
||||
void NiFloatInterpController::read(NIFStream *nif)
|
||||
{
|
||||
Controller::read(nif);
|
||||
data.read(nif);
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))
|
||||
data.read(nif);
|
||||
else
|
||||
interpolator.read(nif);
|
||||
}
|
||||
|
||||
void NiFloatInterpController::post(NIFFile *nif)
|
||||
{
|
||||
Controller::post(nif);
|
||||
data.post(nif);
|
||||
interpolator.post(nif);
|
||||
}
|
||||
|
||||
void NiGeomMorpherController::read(NIFStream *nif)
|
||||
|
@ -189,13 +201,34 @@ namespace Nif
|
|||
/*bool updateNormals = !!*/nif->getUShort();
|
||||
data.read(nif);
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
|
||||
{
|
||||
/*bool alwaysActive = */nif->getChar(); // Always 0
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,106))
|
||||
{
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||
{
|
||||
interpolators.read(nif);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,2,0,0) && nif->getBethVersion() > 9)
|
||||
{
|
||||
unsigned int numUnknown = nif->getUInt();
|
||||
nif->skip(4 * numUnknown);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle weighted interpolators
|
||||
unsigned int numInterps = nif->getUInt();
|
||||
nif->skip(8 * numInterps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NiGeomMorpherController::post(NIFFile *nif)
|
||||
{
|
||||
Controller::post(nif);
|
||||
data.post(nif);
|
||||
interpolators.post(nif);
|
||||
}
|
||||
|
||||
void NiVisController::read(NIFStream *nif)
|
||||
|
@ -213,6 +246,8 @@ namespace Nif
|
|||
void NiFlipController::read(NIFStream *nif)
|
||||
{
|
||||
Controller::read(nif);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,2,0,0))
|
||||
mInterpolator.read(nif);
|
||||
mTexSlot = nif->getUInt();
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))
|
||||
{
|
||||
|
@ -225,7 +260,69 @@ namespace Nif
|
|||
void NiFlipController::post(NIFFile *nif)
|
||||
{
|
||||
Controller::post(nif);
|
||||
mInterpolator.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
|
||||
{
|
||||
public:
|
||||
NiPoint3InterpolatorPtr interpolator;
|
||||
NiPosDataPtr data;
|
||||
unsigned int targetColor;
|
||||
|
||||
|
@ -138,6 +139,7 @@ class NiKeyframeController : public Controller
|
|||
{
|
||||
public:
|
||||
NiKeyframeDataPtr data;
|
||||
NiTransformInterpolatorPtr interpolator;
|
||||
|
||||
void read(NIFStream *nif) override;
|
||||
void post(NIFFile *nif) override;
|
||||
|
@ -146,6 +148,7 @@ public:
|
|||
struct NiFloatInterpController : public Controller
|
||||
{
|
||||
NiFloatDataPtr data;
|
||||
NiFloatInterpolatorPtr interpolator;
|
||||
|
||||
void read(NIFStream *nif) override;
|
||||
void post(NIFFile *nif) override;
|
||||
|
@ -158,6 +161,7 @@ class NiGeomMorpherController : public Controller
|
|||
{
|
||||
public:
|
||||
NiMorphDataPtr data;
|
||||
NiFloatInterpolatorList interpolators;
|
||||
|
||||
void read(NIFStream *nif) override;
|
||||
void post(NIFFile *nif) override;
|
||||
|
@ -175,6 +179,7 @@ public:
|
|||
class NiFlipController : public Controller
|
||||
{
|
||||
public:
|
||||
NiFloatInterpolatorPtr mInterpolator;
|
||||
int mTexSlot; // NiTexturingProperty::TextureType
|
||||
float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources
|
||||
NiSourceTextureList mSources;
|
||||
|
@ -183,5 +188,47 @@ public:
|
|||
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
|
||||
#endif
|
||||
|
|
|
@ -6,6 +6,8 @@ namespace Nif
|
|||
void NiSkinInstance::read(NIFStream *nif)
|
||||
{
|
||||
data.read(nif);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,101))
|
||||
partitions.read(nif);
|
||||
root.read(nif);
|
||||
bones.read(nif);
|
||||
}
|
||||
|
@ -13,6 +15,7 @@ void NiSkinInstance::read(NIFStream *nif)
|
|||
void NiSkinInstance::post(NIFFile *nif)
|
||||
{
|
||||
data.post(nif);
|
||||
partitions.post(nif);
|
||||
root.post(nif);
|
||||
bones.post(nif);
|
||||
|
||||
|
@ -320,7 +323,7 @@ void NiSkinData::read(NIFStream *nif)
|
|||
|
||||
int boneNum = nif->getInt();
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))
|
||||
nif->skip(4); // NiSkinPartition link
|
||||
partitions.read(nif);
|
||||
|
||||
// Has vertex weights flag
|
||||
if (nif->getVersion() > NIFStream::generateVersion(4,2,1,0) && !nif->getBoolean())
|
||||
|
@ -345,6 +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)
|
||||
{
|
||||
int morphCount = nif->getInt();
|
||||
|
@ -355,7 +421,7 @@ void NiMorphData::read(NIFStream *nif)
|
|||
for(int i = 0;i < morphCount;i++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -392,4 +458,17 @@ void NiPalette::read(NIFStream *nif)
|
|||
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
|
||||
|
|
|
@ -174,6 +174,7 @@ class NiSkinInstance : public Record
|
|||
{
|
||||
public:
|
||||
NiSkinDataPtr data;
|
||||
NiSkinPartitionPtr partitions;
|
||||
NodePtr root;
|
||||
NodeList bones;
|
||||
|
||||
|
@ -200,6 +201,25 @@ public:
|
|||
|
||||
Transformation trafo;
|
||||
std::vector<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;
|
||||
};
|
||||
|
@ -240,5 +260,17 @@ public:
|
|||
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
|
||||
#endif
|
||||
|
|
|
@ -80,5 +80,11 @@ void NiFloatsExtraData::read(NIFStream *nif)
|
|||
nif->getFloats(data, num);
|
||||
}
|
||||
|
||||
void BSBound::read(NIFStream *nif)
|
||||
{
|
||||
Extra::read(nif);
|
||||
center = nif->getVector3();
|
||||
halfExtents = nif->getVector3();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -109,5 +109,12 @@ struct NiFloatsExtraData : public Extra
|
|||
void read(NIFStream *nif) override;
|
||||
};
|
||||
|
||||
struct BSBound : public Extra
|
||||
{
|
||||
osg::Vec3f center, halfExtents;
|
||||
|
||||
void read(NIFStream *nif) override;
|
||||
};
|
||||
|
||||
} // Namespace
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "niffile.hpp"
|
||||
#include "effect.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
||||
|
@ -113,6 +114,19 @@ static std::map<std::string,RecordFactoryEntry> makeFactory()
|
|||
factory["NiColorExtraData"] = {&construct <NiVectorExtraData> , RC_NiColorExtraData };
|
||||
factory["NiFloatExtraData"] = {&construct <NiFloatExtraData> , RC_NiFloatExtraData };
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -137,15 +151,45 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
|||
|
||||
// Check the header string
|
||||
std::string head = nif.getVersionString();
|
||||
if(head.compare(0, 22, "NetImmerse File Format") != 0)
|
||||
static const std::array<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);
|
||||
|
||||
supported = false;
|
||||
|
||||
// Get BCD version
|
||||
ver = nif.getUInt();
|
||||
// 4.0.0.0 is an older, practically identical version of the format.
|
||||
// It's not used by Morrowind assets but Morrowind supports it.
|
||||
if(ver != NIFStream::generateVersion(4,0,0,0) && ver != VER_MW)
|
||||
fail("Unsupported NIF version: " + printVersion(ver));
|
||||
static const std::array<uint32_t, 2> supportedVers =
|
||||
{
|
||||
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
|
||||
if (ver >= NIFStream::generateVersion(20,0,0,4))
|
||||
|
@ -245,6 +289,9 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
|||
else
|
||||
fail("Unknown record type " + rec);
|
||||
|
||||
if (!supported)
|
||||
Log(Debug::Verbose) << "NIF Debug: Reading record of type " << rec << ", index " << i << " (" << filename << ")";
|
||||
|
||||
assert(r != nullptr);
|
||||
assert(r->recType != RC_MISSING);
|
||||
r->recName = rec;
|
||||
|
@ -286,4 +333,11 @@ bool NIFFile::getUseSkinning() const
|
|||
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;
|
||||
|
||||
static bool sLoadUnsupportedFiles;
|
||||
|
||||
/// Parse the file
|
||||
void parse(Files::IStreamPtr stream);
|
||||
|
||||
|
@ -149,6 +151,8 @@ public:
|
|||
|
||||
/// Get the Bethesda version of the NIF format used
|
||||
unsigned int getBethVersion() const override { return bethVer; }
|
||||
|
||||
static void setLoadUnsupportedFiles(bool load);
|
||||
};
|
||||
using NIFFilePtr = std::shared_ptr<const Nif::NIFFile>;
|
||||
|
||||
|
|
|
@ -52,16 +52,27 @@ struct KeyMapT {
|
|||
MapType mKeys;
|
||||
|
||||
//Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html)
|
||||
void read(NIFStream *nif, bool force=false)
|
||||
void read(NIFStream *nif, bool force = false, bool morph = false)
|
||||
{
|
||||
assert(nif);
|
||||
|
||||
mInterpolationType = InterpolationType_Unknown;
|
||||
|
||||
if (morph && nif->getVersion() >= NIFStream::generateVersion(10,1,0,106))
|
||||
nif->getString(); // Frame name
|
||||
|
||||
size_t count = nif->getUInt();
|
||||
if(count == 0 && !force)
|
||||
if (count == 0 && !force && !morph)
|
||||
return;
|
||||
|
||||
if (morph && nif->getVersion() > NIFStream::generateVersion(10,1,0,0))
|
||||
{
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,104) &&
|
||||
nif->getVersion() <= NIFStream::generateVersion(20,1,0,2) && nif->getBethVersion() < 10)
|
||||
nif->getFloat(); // Legacy weight
|
||||
return;
|
||||
}
|
||||
|
||||
mKeys.clear();
|
||||
|
||||
mInterpolationType = nif->getUInt();
|
||||
|
|
|
@ -16,6 +16,117 @@ namespace Nif
|
|||
|
||||
struct NiNode;
|
||||
|
||||
struct NiBoundingVolume
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
SPHERE_BV = 0,
|
||||
BOX_BV = 1,
|
||||
CAPSULE_BV = 2,
|
||||
LOZENGE_BV = 3,
|
||||
UNION_BV = 4,
|
||||
HALFSPACE_BV = 5
|
||||
};
|
||||
|
||||
struct NiSphereBV
|
||||
{
|
||||
osg::Vec3f center;
|
||||
float radius{0.f};
|
||||
};
|
||||
|
||||
struct NiBoxBV
|
||||
{
|
||||
osg::Vec3f center;
|
||||
Matrix3 axis;
|
||||
osg::Vec3f extents;
|
||||
};
|
||||
|
||||
struct NiCapsuleBV
|
||||
{
|
||||
osg::Vec3f center, axis;
|
||||
float extent{0.f}, radius{0.f};
|
||||
};
|
||||
|
||||
struct NiLozengeBV
|
||||
{
|
||||
float radius{0.f}, extent0{0.f}, extent1{0.f};
|
||||
osg::Vec3f center, axis0, axis1;
|
||||
};
|
||||
|
||||
struct NiHalfSpaceBV
|
||||
{
|
||||
osg::Vec3f center, normal;
|
||||
};
|
||||
|
||||
unsigned int type;
|
||||
NiSphereBV sphere;
|
||||
NiBoxBV box;
|
||||
NiCapsuleBV capsule;
|
||||
NiLozengeBV lozenge;
|
||||
std::vector<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
|
||||
parent node (unless it's the root), and transformation (location
|
||||
and rotation) relative to it's parent.
|
||||
|
@ -31,9 +142,7 @@ public:
|
|||
|
||||
// Bounding box info
|
||||
bool hasBounds{false};
|
||||
osg::Vec3f boundPos;
|
||||
Matrix3 boundRot;
|
||||
osg::Vec3f boundXYZ; // Box size
|
||||
NiBoundingVolume bounds;
|
||||
|
||||
void read(NIFStream *nif) override
|
||||
{
|
||||
|
@ -48,13 +157,8 @@ public:
|
|||
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||
hasBounds = nif->getBoolean();
|
||||
if(hasBounds)
|
||||
{
|
||||
nif->getInt(); // always 1
|
||||
boundPos = nif->getVector3();
|
||||
boundRot = nif->getMatrix3();
|
||||
boundXYZ = nif->getVector3();
|
||||
}
|
||||
if (hasBounds)
|
||||
bounds.read(nif);
|
||||
// Reference to the collision object in Gamebryo files.
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||
nif->skip(4);
|
||||
|
|
|
@ -109,7 +109,17 @@ enum RecordType
|
|||
RC_NiVectorExtraData,
|
||||
RC_NiColorExtraData,
|
||||
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
|
||||
|
|
|
@ -143,6 +143,11 @@ class NiAutoNormalParticlesData;
|
|||
class NiPalette;
|
||||
struct NiParticleModifier;
|
||||
struct NiLinesData;
|
||||
struct NiBoolData;
|
||||
struct NiSkinPartition;
|
||||
struct NiFloatInterpolator;
|
||||
struct NiPoint3Interpolator;
|
||||
struct NiTransformInterpolator;
|
||||
|
||||
using NodePtr = RecordPtrT<Node>;
|
||||
using ExtraPtr = RecordPtrT<Extra>;
|
||||
|
@ -166,11 +171,17 @@ using NiRotatingParticlesDataPtr = RecordPtrT<NiRotatingParticlesData>;
|
|||
using NiAutoNormalParticlesDataPtr = RecordPtrT<NiAutoNormalParticlesData>;
|
||||
using NiPalettePtr = RecordPtrT<NiPalette>;
|
||||
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 PropertyList = RecordListT<Property>;
|
||||
using ExtraList = RecordListT<Extra>;
|
||||
using NiSourceTextureList = RecordListT<NiSourceTexture>;
|
||||
using NiFloatInterpolatorList = RecordListT<NiFloatInterpolator>;
|
||||
|
||||
} // Namespace
|
||||
#endif
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/misc/convert.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include <components/nif/node.hpp>
|
||||
|
@ -24,11 +25,6 @@ osg::Matrixf getWorldTransform(const Nif::Node *node)
|
|||
return node->trafo.toMatrix();
|
||||
}
|
||||
|
||||
btVector3 getbtVector(const osg::Vec3f &v)
|
||||
{
|
||||
return btVector3(v.x(), v.y(), v.z());
|
||||
}
|
||||
|
||||
bool pathFileNameStartsWithX(const std::string& path)
|
||||
{
|
||||
const std::size_t slashpos = path.find_last_of("/\\");
|
||||
|
@ -36,7 +32,7 @@ bool pathFileNameStartsWithX(const std::string& path)
|
|||
return letterPos < path.size() && (path[letterPos] == 'x' || path[letterPos] == 'X');
|
||||
}
|
||||
|
||||
void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriShapeData& data, const osg::Matrixf &transform)
|
||||
void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data, const osg::Matrixf &transform)
|
||||
{
|
||||
mesh.preallocateVertices(static_cast<int>(data.vertices.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)
|
||||
{
|
||||
mesh.addTriangle(
|
||||
getbtVector(vertices[triangles[i + 0]] * transform),
|
||||
getbtVector(vertices[triangles[i + 1]] * transform),
|
||||
getbtVector(vertices[triangles[i + 2]] * transform)
|
||||
Misc::Convert::toBullet(vertices[triangles[i + 0]] * transform),
|
||||
Misc::Convert::toBullet(vertices[triangles[i + 1]] * transform),
|
||||
Misc::Convert::toBullet(vertices[triangles[i + 2]] * transform)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, const osg::Matrixf &transform)
|
||||
void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, const osg::Matrixf &transform)
|
||||
{
|
||||
const std::vector<osg::Vec3f> &vertices = data.vertices;
|
||||
const std::vector<std::vector<unsigned short>> &strips = data.strips;
|
||||
if (vertices.empty() || strips.empty())
|
||||
return;
|
||||
mesh.preallocateVertices(static_cast<int>(data.vertices.size()));
|
||||
mesh.preallocateVertices(static_cast<int>(vertices.size()));
|
||||
int numTriangles = 0;
|
||||
for (const std::vector<unsigned short>& strip : strips)
|
||||
{
|
||||
|
@ -88,17 +84,17 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD
|
|||
if (i%2==0)
|
||||
{
|
||||
mesh.addTriangle(
|
||||
getbtVector(vertices[a] * transform),
|
||||
getbtVector(vertices[b] * transform),
|
||||
getbtVector(vertices[c] * transform)
|
||||
Misc::Convert::toBullet(vertices[a] * transform),
|
||||
Misc::Convert::toBullet(vertices[b] * transform),
|
||||
Misc::Convert::toBullet(vertices[c] * transform)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
mesh.addTriangle(
|
||||
getbtVector(vertices[a] * transform),
|
||||
getbtVector(vertices[c] * transform),
|
||||
getbtVector(vertices[b] * transform)
|
||||
Misc::Convert::toBullet(vertices[a] * transform),
|
||||
Misc::Convert::toBullet(vertices[c] * transform),
|
||||
Misc::Convert::toBullet(vertices[b] * transform)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -106,17 +102,12 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD
|
|||
}
|
||||
}
|
||||
|
||||
void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::Node* nifNode, const osg::Matrixf &transform)
|
||||
void fillTriangleMesh(btTriangleMesh& mesh, const Nif::Node* nifNode, const osg::Matrixf &transform = osg::Matrixf())
|
||||
{
|
||||
if (nifNode->recType == Nif::RC_NiTriShape)
|
||||
fillTriangleMeshWithTransform(mesh, static_cast<const Nif::NiTriShape*>(nifNode)->data.get(), transform);
|
||||
else // if (nifNode->recType == Nif::RC_NiTriStrips)
|
||||
fillTriangleMeshWithTransform(mesh, static_cast<const Nif::NiTriStrips*>(nifNode)->data.get(), transform);
|
||||
}
|
||||
|
||||
void fillTriangleMesh(btTriangleMesh& mesh, const Nif::Node* node)
|
||||
{
|
||||
fillTriangleMeshWithTransform(mesh, node, osg::Matrixf());
|
||||
fillTriangleMesh(mesh, static_cast<const Nif::NiTriShape*>(nifNode)->data.get(), transform);
|
||||
else if (nifNode->recType == Nif::RC_NiTriStrips)
|
||||
fillTriangleMesh(mesh, static_cast<const Nif::NiTriStrips*>(nifNode)->data.get(), transform);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -141,18 +132,21 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
|||
if ((node = dynamic_cast<Nif::Node*>(r)))
|
||||
break;
|
||||
}
|
||||
const std::string filename = nif.getFilename();
|
||||
if (!node)
|
||||
{
|
||||
warn("Found no root nodes in NIF.");
|
||||
warn("Found no root nodes in NIF file " + filename);
|
||||
return mShape;
|
||||
}
|
||||
|
||||
if (findBoundingBox(node))
|
||||
if (findBoundingBox(node, filename))
|
||||
{
|
||||
const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents);
|
||||
const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate);
|
||||
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();
|
||||
transform.setOrigin(getbtVector(mShape->mCollisionBoxTranslate));
|
||||
transform.setOrigin(origin);
|
||||
compound->addChildShape(transform, boxShape.get());
|
||||
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).
|
||||
// assume all nodes in the file will be animated
|
||||
const auto filename = nif.getFilename();
|
||||
const bool isAnimated = pathFileNameStartsWithX(filename);
|
||||
|
||||
handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated);
|
||||
|
@ -201,12 +194,25 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
|||
|
||||
// Find a boundingBox in the node hierarchy.
|
||||
// Return: use bounding box for collision?
|
||||
bool BulletNifLoader::findBoundingBox(const Nif::Node* node)
|
||||
bool BulletNifLoader::findBoundingBox(const Nif::Node* node, const std::string& filename)
|
||||
{
|
||||
if (node->hasBounds)
|
||||
{
|
||||
mShape->mCollisionBoxHalfExtents = node->boundXYZ;
|
||||
mShape->mCollisionBoxTranslate = node->boundPos;
|
||||
unsigned int type = node->bounds.type;
|
||||
switch (type)
|
||||
{
|
||||
case Nif::NiBoundingVolume::Type::BOX_BV:
|
||||
mShape->mCollisionBoxHalfExtents = node->bounds.box.extents;
|
||||
mShape->mCollisionBoxTranslate = node->bounds.box.center;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
std::stringstream warning;
|
||||
warning << "Unsupported NiBoundingVolume type " << type << " in node " << node->recIndex;
|
||||
warning << " in file " << filename;
|
||||
warn(warning.str());
|
||||
}
|
||||
}
|
||||
|
||||
if (node->flags & Nif::NiNode::Flag_BBoxCollision)
|
||||
{
|
||||
|
@ -222,7 +228,7 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node)
|
|||
{
|
||||
if(!list[i].empty())
|
||||
{
|
||||
bool found = findBoundingBox (list[i].getPtr());
|
||||
bool found = findBoundingBox (list[i].getPtr(), filename);
|
||||
if (found)
|
||||
return true;
|
||||
}
|
||||
|
@ -383,7 +389,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons
|
|||
if (!mAvoidStaticMesh)
|
||||
mAvoidStaticMesh.reset(new btTriangleMesh(false));
|
||||
|
||||
fillTriangleMeshWithTransform(*mAvoidStaticMesh, nifNode, transform);
|
||||
fillTriangleMesh(*mAvoidStaticMesh, nifNode, transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -391,7 +397,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons
|
|||
mStaticMesh.reset(new btTriangleMesh(false));
|
||||
|
||||
// Static shape, just transform all vertices into position
|
||||
fillTriangleMeshWithTransform(*mStaticMesh, nifNode, transform);
|
||||
fillTriangleMesh(*mStaticMesh, nifNode, transform);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class BulletNifLoader
|
|||
public:
|
||||
void warn(const std::string &msg)
|
||||
{
|
||||
Log(Debug::Warning) << "NIFLoader: Warn:" << msg;
|
||||
Log(Debug::Warning) << "NIFLoader: Warn: " << msg;
|
||||
}
|
||||
|
||||
void fail(const std::string &msg)
|
||||
|
@ -52,7 +52,7 @@ public:
|
|||
osg::ref_ptr<Resource::BulletShape> load(const Nif::File& file);
|
||||
|
||||
private:
|
||||
bool findBoundingBox(const Nif::Node* node);
|
||||
bool findBoundingBox(const Nif::Node* node, const std::string& filename);
|
||||
|
||||
void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode,
|
||||
bool isAnimated=false, bool autogenerated=false, bool avoid=false);
|
||||
|
|
|
@ -92,6 +92,26 @@ KeyframeController::KeyframeController(const Nif::NiKeyframeData *data)
|
|||
{
|
||||
}
|
||||
|
||||
KeyframeController::KeyframeController(const Nif::NiTransformInterpolator* interpolator)
|
||||
: mRotations(interpolator->data->mRotations, interpolator->defaultRot)
|
||||
, mXRotations(interpolator->data->mXRotations, 0.f)
|
||||
, mYRotations(interpolator->data->mYRotations, 0.f)
|
||||
, mZRotations(interpolator->data->mZRotations, 0.f)
|
||||
, mTranslations(interpolator->data->mTranslations, interpolator->defaultPos)
|
||||
, mScales(interpolator->data->mScales, interpolator->defaultScale)
|
||||
{
|
||||
}
|
||||
|
||||
KeyframeController::KeyframeController(const float scale, const osg::Vec3f& pos, const osg::Quat& rot)
|
||||
: mRotations(Nif::QuaternionKeyMapPtr(), rot)
|
||||
, mXRotations(Nif::FloatKeyMapPtr(), 0.f)
|
||||
, mYRotations(Nif::FloatKeyMapPtr(), 0.f)
|
||||
, mZRotations(Nif::FloatKeyMapPtr(), 0.f)
|
||||
, mTranslations(Nif::Vector3KeyMapPtr(), pos)
|
||||
, mScales(Nif::FloatKeyMapPtr(), scale)
|
||||
{
|
||||
}
|
||||
|
||||
osg::Quat KeyframeController::getXYZRotation(float time) const
|
||||
{
|
||||
float xrot = 0, yrot = 0, zrot = 0;
|
||||
|
@ -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)
|
||||
mKeyFrames.emplace_back(data->mMorphs[i].mKeyFrames);
|
||||
if (ctrl->interpolators.length() == 0)
|
||||
{
|
||||
if (ctrl->data.empty())
|
||||
return;
|
||||
for (const auto& morph : ctrl->data->mMorphs)
|
||||
mKeyFrames.emplace_back(morph.mKeyFrames);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < ctrl->interpolators.length(); ++i)
|
||||
{
|
||||
if (!ctrl->interpolators[i].empty())
|
||||
mKeyFrames.emplace_back(ctrl->interpolators[i].getPtr());
|
||||
else
|
||||
mKeyFrames.emplace_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable)
|
||||
|
@ -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)
|
||||
: osg::NodeCallback(copy, copyop)
|
||||
, 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)
|
||||
: mData(data->mKeyList, 1.f)
|
||||
, 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)
|
||||
: mData(data->mKeyList, osg::Vec3f(1,1,1))
|
||||
, 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)
|
||||
, 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)
|
||||
|
@ -463,14 +518,19 @@ FlipController::FlipController(const FlipController ©, const osg::CopyOp &co
|
|||
, mTexSlot(copy.mTexSlot)
|
||||
, mDelta(copy.mDelta)
|
||||
, mTextures(copy.mTextures)
|
||||
, mData(copy.mData)
|
||||
{
|
||||
}
|
||||
|
||||
void FlipController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)
|
||||
{
|
||||
if (hasInput() && mDelta != 0 && !mTextures.empty())
|
||||
if (hasInput() && !mTextures.empty())
|
||||
{
|
||||
int curTexture = int(getInputValue(nv) / mDelta) % mTextures.size();
|
||||
int curTexture = 0;
|
||||
if (mDelta != 0)
|
||||
curTexture = int(getInputValue(nv) / mDelta) % mTextures.size();
|
||||
else
|
||||
curTexture = int(mData.interpKey(getInputValue(nv))) % mTextures.size();
|
||||
stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <components/sceneutil/statesetupdater.hpp>
|
||||
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
|
||||
#include <osg/Texture2D>
|
||||
|
||||
|
@ -59,6 +60,31 @@ namespace NifOsg
|
|||
|
||||
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())
|
||||
: mKeys(keys)
|
||||
, mDefaultVal(defaultVal)
|
||||
|
@ -188,7 +214,7 @@ namespace NifOsg
|
|||
class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller
|
||||
{
|
||||
public:
|
||||
GeomMorpherController(const Nif::NiMorphData* data);
|
||||
GeomMorpherController(const Nif::NiGeomMorpherController* ctrl);
|
||||
GeomMorpherController();
|
||||
GeomMorpherController(const GeomMorpherController& copy, const osg::CopyOp& copyop);
|
||||
|
||||
|
@ -203,7 +229,14 @@ namespace NifOsg
|
|||
class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller
|
||||
{
|
||||
public:
|
||||
// This is used if there's no interpolator but there is data (Morrowind meshes).
|
||||
KeyframeController(const Nif::NiKeyframeData *data);
|
||||
// This is used if the interpolator has data.
|
||||
KeyframeController(const Nif::NiTransformInterpolator* interpolator);
|
||||
// This is used if there are default values available (e.g. from a data-less interpolator).
|
||||
// If there's neither keyframe data nor an interpolator a KeyframeController must not be created.
|
||||
KeyframeController(const float scale, const osg::Vec3f& pos, const osg::Quat& rot);
|
||||
|
||||
KeyframeController();
|
||||
KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop);
|
||||
|
||||
|
@ -272,6 +305,7 @@ namespace NifOsg
|
|||
|
||||
public:
|
||||
RollController(const Nif::NiFloatData *data);
|
||||
RollController(const Nif::NiFloatInterpolator* interpolator);
|
||||
RollController() = default;
|
||||
RollController(const RollController& copy, const osg::CopyOp& copyop);
|
||||
|
||||
|
@ -287,6 +321,7 @@ namespace NifOsg
|
|||
osg::ref_ptr<const osg::Material> mBaseMaterial;
|
||||
public:
|
||||
AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial);
|
||||
AlphaController(const Nif::NiFloatInterpolator* interpolator, const osg::Material* baseMaterial);
|
||||
AlphaController();
|
||||
AlphaController(const AlphaController& copy, const osg::CopyOp& copyop);
|
||||
|
||||
|
@ -308,6 +343,7 @@ namespace NifOsg
|
|||
Emissive = 3
|
||||
};
|
||||
MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial);
|
||||
MaterialColorController(const Nif::NiPoint3Interpolator* interpolator, TargetColor color, const osg::Material* baseMaterial);
|
||||
MaterialColorController();
|
||||
MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop);
|
||||
|
||||
|
@ -329,6 +365,7 @@ namespace NifOsg
|
|||
int mTexSlot{0};
|
||||
float mDelta{0.f};
|
||||
std::vector<osg::ref_ptr<osg::Texture2D> > mTextures;
|
||||
FloatInterpolator mData;
|
||||
|
||||
public:
|
||||
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.
|
||||
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::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
|
||||
|
||||
if(key->data.empty())
|
||||
if (key->data.empty() && key->interpolator.empty())
|
||||
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)));
|
||||
|
||||
if (!target.mKeyframeControllers.emplace(strdata->string, callback).second)
|
||||
|
@ -528,7 +540,19 @@ namespace NifOsg
|
|||
// - finding a random child NiNode in NiBspArrayController
|
||||
node->setUserValue("recIndex", nifNode->recIndex);
|
||||
|
||||
std::vector<Nif::ExtraPtr> extraCollection;
|
||||
|
||||
for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->next)
|
||||
extraCollection.emplace_back(e);
|
||||
|
||||
for (size_t i = 0; i < nifNode->extralist.length(); ++i)
|
||||
{
|
||||
Nif::ExtraPtr e = nifNode->extralist[i];
|
||||
if (!e.empty())
|
||||
extraCollection.emplace_back(e);
|
||||
}
|
||||
|
||||
for (const auto& e : extraCollection)
|
||||
{
|
||||
if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys)
|
||||
{
|
||||
|
@ -584,7 +608,7 @@ namespace NifOsg
|
|||
|
||||
applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags);
|
||||
|
||||
const bool isGeometry = nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines;
|
||||
const bool isGeometry = isTypeGeometry(nifNode->recType);
|
||||
|
||||
if (isGeometry && !skipMeshes)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
|
||||
|
@ -710,9 +752,9 @@ namespace NifOsg
|
|||
if (ctrl->recType == Nif::RC_NiKeyframeController)
|
||||
{
|
||||
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
|
||||
if (key->data.empty())
|
||||
if (key->data.empty() && key->interpolator.empty())
|
||||
continue;
|
||||
osg::ref_ptr<KeyframeController> callback(new KeyframeController(key->data.getPtr()));
|
||||
osg::ref_ptr<KeyframeController> callback(handleKeyframeController(key));
|
||||
setupController(key, callback, animflags);
|
||||
node->addUpdateCallback(callback);
|
||||
isAnimated = true;
|
||||
|
@ -739,9 +781,13 @@ namespace NifOsg
|
|||
else if (ctrl->recType == Nif::RC_NiRollController)
|
||||
{
|
||||
const Nif::NiRollController *rollctrl = static_cast<const Nif::NiRollController*>(ctrl.getPtr());
|
||||
if (rollctrl->data.empty())
|
||||
if (rollctrl->data.empty() && rollctrl->interpolator.empty())
|
||||
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);
|
||||
node->addUpdateCallback(callback);
|
||||
isAnimated = true;
|
||||
|
@ -767,19 +813,27 @@ namespace NifOsg
|
|||
if (ctrl->recType == Nif::RC_NiAlphaController)
|
||||
{
|
||||
const Nif::NiAlphaController* alphactrl = static_cast<const Nif::NiAlphaController*>(ctrl.getPtr());
|
||||
if (alphactrl->data.empty())
|
||||
if (alphactrl->data.empty() && alphactrl->interpolator.empty())
|
||||
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);
|
||||
composite->addController(osgctrl);
|
||||
}
|
||||
else if (ctrl->recType == Nif::RC_NiMaterialColorController)
|
||||
{
|
||||
const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr());
|
||||
if (matctrl->data.empty())
|
||||
if (matctrl->data.empty() && matctrl->interpolator.empty())
|
||||
continue;
|
||||
osg::ref_ptr<MaterialColorController> osgctrl;
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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::Geometry> geom (new osg::Geometry);
|
||||
handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags);
|
||||
|
@ -1190,7 +1244,7 @@ namespace NifOsg
|
|||
continue;
|
||||
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);
|
||||
drawable->setUpdateCallback(morphctrl);
|
||||
break;
|
||||
|
@ -1220,7 +1274,7 @@ namespace NifOsg
|
|||
void handleSkinnedGeometry(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::Geometry> geometry (new osg::Geometry);
|
||||
handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags);
|
||||
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);
|
||||
|
|
|
@ -220,6 +220,7 @@ namespace Resource
|
|||
, mClampLighting(true)
|
||||
, mAutoUseNormalMaps(false)
|
||||
, mAutoUseSpecularMaps(false)
|
||||
, mApplyLightingToEnvMaps(false)
|
||||
, mInstanceCache(new MultiObjectCache)
|
||||
, mSharedStateManager(new SharedStateManager)
|
||||
, mImageManager(imageManager)
|
||||
|
@ -284,6 +285,11 @@ namespace Resource
|
|||
mSpecularMapPattern = pattern;
|
||||
}
|
||||
|
||||
void SceneManager::setApplyLightingToEnvMaps(bool apply)
|
||||
{
|
||||
mApplyLightingToEnvMaps = apply;
|
||||
}
|
||||
|
||||
SceneManager::~SceneManager()
|
||||
{
|
||||
// this has to be defined in the .cpp file as we can't delete incomplete types
|
||||
|
@ -770,6 +776,7 @@ namespace Resource
|
|||
shaderVisitor->setNormalHeightMapPattern(mNormalHeightMapPattern);
|
||||
shaderVisitor->setAutoUseSpecularMaps(mAutoUseSpecularMaps);
|
||||
shaderVisitor->setSpecularMapPattern(mSpecularMapPattern);
|
||||
shaderVisitor->setApplyLightingToEnvMaps(mApplyLightingToEnvMaps);
|
||||
return shaderVisitor;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,8 @@ namespace Resource
|
|||
|
||||
void setSpecularMapPattern(const std::string& pattern);
|
||||
|
||||
void setApplyLightingToEnvMaps(bool apply);
|
||||
|
||||
void setShaderPath(const std::string& path);
|
||||
|
||||
/// Check if a given scene is loaded and if so, update its usage timestamp to prevent it from being unloaded
|
||||
|
@ -156,6 +158,7 @@ namespace Resource
|
|||
std::string mNormalHeightMapPattern;
|
||||
bool mAutoUseSpecularMaps;
|
||||
std::string mSpecularMapPattern;
|
||||
bool mApplyLightingToEnvMaps;
|
||||
|
||||
osg::ref_ptr<MultiObjectCache> mInstanceCache;
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <osg/Depth>
|
||||
|
||||
#include <sstream>
|
||||
#include "shadowsbin.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -273,10 +274,20 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||
cv->pushCullingSet();
|
||||
}
|
||||
#endif
|
||||
// bin has to go inside camera cull or the rendertexture stage will override it
|
||||
static osg::ref_ptr<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())
|
||||
{
|
||||
_vdsm->getShadowedScene()->osg::Group::traverse(*nv);
|
||||
}
|
||||
cv->popStateSet();
|
||||
#if 1
|
||||
if (!_polytope.empty())
|
||||
{
|
||||
|
@ -555,6 +566,7 @@ MWShadowTechnique::ShadowData::ShadowData(MWShadowTechnique::ViewDependentData*
|
|||
_camera = new osg::Camera;
|
||||
_camera->setName("ShadowCamera");
|
||||
_camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
|
||||
_camera->setImplicitBufferAttachmentMask(0, 0);
|
||||
|
||||
//_camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
|
||||
_camera->setClearColor(osg::Vec4(0.0f,0.0f,0.0f,0.0f));
|
||||
|
@ -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_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*/)
|
||||
|
@ -1583,17 +1586,14 @@ void MWShadowTechnique::createShaders()
|
|||
_shadowCastingStateSet->setAttributeAndModes(_castingProgram, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||
// The casting program uses a sampler, so to avoid undefined behaviour, we must bind a dummy texture in case no other is supplied
|
||||
_shadowCastingStateSet->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON);
|
||||
_shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false));
|
||||
_shadowCastingStateSet->addUniform(_shadowMapAlphaTestDisableUniform);
|
||||
_shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true));
|
||||
_shadowCastingStateSet->addUniform(new osg::Uniform("alphaTestShadows", false));
|
||||
osg::ref_ptr<osg::Depth> depth = new osg::Depth;
|
||||
depth->setWriteMask(true);
|
||||
_shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
_shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON);
|
||||
|
||||
_shadowCastingStateSet->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "RenderBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS);
|
||||
|
||||
// TODO: compare performance when alpha testing is handled here versus using a discard in the fragment shader
|
||||
// TODO: compare performance when we set a bunch of GL state to the default here with OVERRIDE set so that there are fewer pointless state switches
|
||||
}
|
||||
|
||||
osg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight)
|
||||
|
|
|
@ -288,7 +288,6 @@ namespace SceneUtil {
|
|||
|
||||
osg::ref_ptr<DebugHUD> _debugHud;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const osg::ref_ptr<osg::Uniform> getShadowMapAlphaTestEnableUniform();
|
||||
const osg::ref_ptr<osg::Uniform> getShadowMapAlphaTestDisableUniform();
|
||||
|
||||
private:
|
||||
std::string mPath;
|
||||
|
||||
|
@ -63,9 +60,6 @@ namespace Shader
|
|||
ProgramMap mPrograms;
|
||||
|
||||
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);
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include "shadervisitor.hpp"
|
||||
|
||||
#include <osg/AlphaFunc>
|
||||
#include <osg/BlendFunc>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Material>
|
||||
#include <osg/Texture>
|
||||
|
@ -14,7 +12,6 @@
|
|||
#include <components/vfs/manager.hpp>
|
||||
#include <components/sceneutil/riggeometry.hpp>
|
||||
#include <components/sceneutil/morphgeometry.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "shadermanager.hpp"
|
||||
|
||||
|
@ -25,7 +22,6 @@ namespace Shader
|
|||
: mShaderRequired(false)
|
||||
, mColorMode(0)
|
||||
, mMaterialOverridden(false)
|
||||
, mBlendFuncOverridden(false)
|
||||
, mNormalHeight(false)
|
||||
, mTexStageRequiringTangents(-1)
|
||||
, mNode(nullptr)
|
||||
|
@ -43,6 +39,7 @@ namespace Shader
|
|||
, mAllowedToModifyStateSets(true)
|
||||
, mAutoUseNormalMaps(false)
|
||||
, mAutoUseSpecularMaps(false)
|
||||
, mApplyLightingToEnvMaps(false)
|
||||
, mShaderManager(shaderManager)
|
||||
, mImageManager(imageManager)
|
||||
, mDefaultVsTemplate(defaultVsTemplate)
|
||||
|
@ -144,11 +141,9 @@ namespace Shader
|
|||
// Bump maps are off by default as well
|
||||
writableStateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON);
|
||||
}
|
||||
else if (texName == "envMap")
|
||||
else if (texName == "envMap" && mApplyLightingToEnvMaps)
|
||||
{
|
||||
static const bool preLightEnv = Settings::Manager::getBool("apply lighting to environment maps", "Shaders");
|
||||
if (preLightEnv)
|
||||
mRequirements.back().mShaderRequired = true;
|
||||
mRequirements.back().mShaderRequired = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -233,14 +228,11 @@ namespace Shader
|
|||
if (!writableStateSet)
|
||||
writableStateSet = getWritableStateSet(node);
|
||||
// We probably shouldn't construct a new version of this each time as Uniforms use pointer comparison for early-out.
|
||||
// Also it should probably belong to the shader manager
|
||||
// Also it should probably belong to the shader manager or be applied by the shadows bin
|
||||
writableStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true));
|
||||
}
|
||||
}
|
||||
|
||||
bool alphaSettingsChanged = false;
|
||||
bool alphaTestShadows = false;
|
||||
|
||||
const osg::StateSet::AttributeList& attributes = stateset->getAttributeList();
|
||||
for (osg::StateSet::AttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it)
|
||||
{
|
||||
|
@ -284,28 +276,8 @@ namespace Shader
|
|||
mRequirements.back().mColorMode = colorMode;
|
||||
}
|
||||
}
|
||||
else if (it->first.first == osg::StateAttribute::BLENDFUNC)
|
||||
{
|
||||
if (!mRequirements.back().mBlendFuncOverridden || it->second.second & osg::StateAttribute::PROTECTED)
|
||||
{
|
||||
if (it->second.second & osg::StateAttribute::OVERRIDE)
|
||||
mRequirements.back().mBlendFuncOverridden = true;
|
||||
|
||||
const osg::BlendFunc* blend = static_cast<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
|
||||
}
|
||||
// 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)
|
||||
|
@ -477,4 +449,9 @@ namespace Shader
|
|||
mSpecularMapPattern = pattern;
|
||||
}
|
||||
|
||||
void ShaderVisitor::setApplyLightingToEnvMaps(bool apply)
|
||||
{
|
||||
mApplyLightingToEnvMaps = apply;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ namespace Shader
|
|||
|
||||
void setSpecularMapPattern(const std::string& pattern);
|
||||
|
||||
void setApplyLightingToEnvMaps(bool apply);
|
||||
|
||||
void apply(osg::Node& node) override;
|
||||
|
||||
void apply(osg::Drawable& drawable) override;
|
||||
|
@ -59,6 +61,8 @@ namespace Shader
|
|||
bool mAutoUseSpecularMaps;
|
||||
std::string mSpecularMapPattern;
|
||||
|
||||
bool mApplyLightingToEnvMaps;
|
||||
|
||||
ShaderManager& mShaderManager;
|
||||
Resource::ImageManager& mImageManager;
|
||||
|
||||
|
@ -75,7 +79,6 @@ namespace Shader
|
|||
int mColorMode;
|
||||
|
||||
bool mMaterialOverridden;
|
||||
bool mBlendFuncOverridden;
|
||||
|
||||
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
|
||||
navigator
|
||||
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
|
||||
|
||||
[Physics]
|
||||
# how much background thread to use in the physics solver. 0 to disable (i.e solver run in the main thread)
|
||||
# Set the number of background threads used for physics.
|
||||
# If no background threads are used, physics calculations are processed in the main thread
|
||||
# and the settings below have no effect.
|
||||
async num threads = 0
|
||||
|
||||
# maintain a cache of lineofsight request in the bacground physics thread
|
||||
# determines for how much frames an inactive lineofsight request should be kept updated in the cache
|
||||
# -1 to disable (i.e the LOS will be calculated only on request)
|
||||
# Set the number of frames an inactive line-of-sight request will be kept
|
||||
# refreshed in the background physics thread cache.
|
||||
# If this is set to -1, line-of-sight requests are never cached.
|
||||
lineofsight keep inactive cache = 0
|
||||
|
||||
# wether to defer aabb update till before collision detection
|
||||
# Defer bounding boxes update until collision detection.
|
||||
defer aabb update = true
|
||||
|
||||
[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