mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 19:19:56 +00:00
Add OpenMW commits up to 14 Feb 2021
# Conflicts: # apps/openmw/mwclass/door.cpp # apps/openmw/mwscript/aiextensions.cpp
This commit is contained in:
commit
7e188f2dd6
39 changed files with 451 additions and 137 deletions
|
@ -116,12 +116,13 @@ variables: &cs-targets
|
|||
- .\ActivateMSVC.ps1
|
||||
- cmake --build . --config $config --target ($targets.Split(','))
|
||||
- cd $config
|
||||
- echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
|
||||
- |
|
||||
if (Get-ChildItem -Recurse *.pdb) {
|
||||
7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb'
|
||||
7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb' CI-ID.txt
|
||||
Get-ChildItem -Recurse *.pdb | Remove-Item
|
||||
}
|
||||
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.zip '*'
|
||||
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}.zip '*'
|
||||
after_script:
|
||||
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
|
||||
cache:
|
||||
|
@ -206,12 +207,13 @@ Windows_Ninja_CS_RelWithDebInfo:
|
|||
- cd MSVC2019_64
|
||||
- cmake --build . --config $config --target ($targets.Split(','))
|
||||
- cd $config
|
||||
- echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
|
||||
- |
|
||||
if (Get-ChildItem -Recurse *.pdb) {
|
||||
7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb'
|
||||
7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb' CI-ID.txt
|
||||
Get-ChildItem -Recurse *.pdb | Remove-Item
|
||||
}
|
||||
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.zip '*'
|
||||
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}.zip '*'
|
||||
after_script:
|
||||
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
|
||||
cache:
|
||||
|
|
|
@ -215,6 +215,7 @@ Programmers
|
|||
Yohaulticetl
|
||||
Yuri Krupenin
|
||||
zelurker
|
||||
Noah Gooder
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
|
|
@ -98,6 +98,11 @@
|
|||
Bug #5758: Paralyzed actors behavior is inconsistent with vanilla
|
||||
Bug #5762: Movement solver is insufficiently robust
|
||||
Bug #5821: NPCs from mods getting removed if mod order was changed
|
||||
Bug #5835: OpenMW doesn't accept negative values for NPC's hello, alarm, fight, and flee
|
||||
Bug #5836: OpenMW dialogue/greeting/voice filter doesn't accept negative Ai values for NPC's hello, alarm, fight, and flee
|
||||
Bug #5838: Local map and other menus become blank in some locations while playing Wizards' Islands mod.
|
||||
Bug #5840: GetSoundPlaying "Health Damage" doesn't play when NPC hits target with shield effect ( vanilla engine behavior )
|
||||
Bug #5841: Can't Cast Zero Cost Spells When Magicka is < 0
|
||||
Feature #390: 3rd person look "over the shoulder"
|
||||
Feature #1536: Show more information about level on menu
|
||||
Feature #2386: Distant Statics in the form of Object Paging
|
||||
|
@ -105,6 +110,7 @@
|
|||
Feature #2686: Timestamps in openmw.log
|
||||
Feature #3171: OpenMW-CS: Instance drag selection
|
||||
Feature #4894: Consider actors as obstacles for pathfinding
|
||||
Feature #4977: Use the "default icon.tga" when an item's icon is not found
|
||||
Feature #5043: Head Bobbing
|
||||
Feature #5199: Improve Scene Colors
|
||||
Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher
|
||||
|
@ -130,6 +136,7 @@
|
|||
Feature #5813: Instanced groundcover support
|
||||
Task #5480: Drop Qt4 support
|
||||
Task #5520: Improve cell name autocompleter implementation
|
||||
Task #5844: Update 'toggle sneak' documentation
|
||||
|
||||
0.46.0
|
||||
------
|
||||
|
|
|
@ -218,6 +218,7 @@ namespace MWBase
|
|||
///
|
||||
/// \note If cell==0, the cell the player is currently in will be used instead to
|
||||
/// generate a name.
|
||||
virtual std::string getCellName(const ESM::Cell* cell) const = 0;
|
||||
|
||||
virtual void removeRefScript (MWWorld::RefData *ref) = 0;
|
||||
//< Remove the script attached to ref from mLocalScripts
|
||||
|
|
|
@ -379,45 +379,31 @@ namespace MWClass
|
|||
return info;
|
||||
}
|
||||
|
||||
std::string Door::getDestination (const MWWorld::LiveCellRef<ESM::Door>& door)
|
||||
std::string Door::getDestination(const MWWorld::LiveCellRef<ESM::Door>& door)
|
||||
{
|
||||
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
std::string dest;
|
||||
if (door.mRef.getDestCell() != "")
|
||||
std::string dest = door.mRef.getDestCell();
|
||||
if (dest.empty())
|
||||
{
|
||||
// door leads to an interior, use interior name as tooltip
|
||||
dest = door.mRef.getDestCell();
|
||||
|
||||
// door leads to exterior, use cell name (if any), otherwise translated region name
|
||||
int x, y;
|
||||
auto world = MWBase::Environment::get().getWorld();
|
||||
world->positionToIndex(door.mRef.getDoorDest().pos[0], door.mRef.getDoorDest().pos[1], x, y);
|
||||
const ESM::Cell* cell = world->getStore().get<ESM::Cell>().search(x, y);
|
||||
dest = world->getCellName(cell);
|
||||
}
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
Start of tes3mp addition
|
||||
|
||||
If there is a destination override in the mwmp::Worldstate for this door's original
|
||||
destination, use it
|
||||
*/
|
||||
if (mwmp::Main::get().getNetworking()->getWorldstate()->destinationOverrides.count(dest) != 0)
|
||||
else if (mwmp::Main::get().getNetworking()->getWorldstate()->destinationOverrides.count(dest) != 0)
|
||||
dest = mwmp::Main::get().getNetworking()->getWorldstate()->destinationOverrides[dest];
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
End of tes3mp addition
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
// door leads to exterior, use cell name (if any), otherwise translated region name
|
||||
int x,y;
|
||||
MWBase::Environment::get().getWorld()->positionToIndex (door.mRef.getDoorDest().pos[0], door.mRef.getDoorDest().pos[1], x, y);
|
||||
const ESM::Cell* cell = store.get<ESM::Cell>().find(x,y);
|
||||
if (cell->mName != "")
|
||||
dest = cell->mName;
|
||||
else
|
||||
{
|
||||
const ESM::Region* region =
|
||||
store.get<ESM::Region>().find(cell->mRegion);
|
||||
|
||||
//name as is, not a token
|
||||
return MyGUI::TextIterator::toTagsString(region->mName);
|
||||
}
|
||||
}
|
||||
|
||||
return "#{sCell=" + dest + "}";
|
||||
}
|
||||
|
|
|
@ -435,10 +435,10 @@ namespace MWClass
|
|||
{
|
||||
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
|
||||
|
||||
std::string model = "meshes\\base_anim.nif";
|
||||
std::string model = Settings::Manager::getString("baseanim", "Models");
|
||||
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
|
||||
if(race->mData.mFlags & ESM::Race::Beast)
|
||||
model = "meshes\\base_animkna.nif";
|
||||
model = Settings::Manager::getString("baseanimkna", "Models");
|
||||
|
||||
return model;
|
||||
}
|
||||
|
@ -448,12 +448,12 @@ namespace MWClass
|
|||
const MWWorld::LiveCellRef<ESM::NPC> *npc = ptr.get<ESM::NPC>();
|
||||
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().search(npc->mBase->mRace);
|
||||
if(race && race->mData.mFlags & ESM::Race::Beast)
|
||||
models.emplace_back("meshes\\base_animkna.nif");
|
||||
models.emplace_back(Settings::Manager::getString("baseanimkna", "Models"));
|
||||
|
||||
// keep these always loaded just in case
|
||||
models.emplace_back("meshes/xargonian_swimkna.nif");
|
||||
models.emplace_back("meshes/xbase_anim_female.nif");
|
||||
models.emplace_back("meshes/xbase_anim.nif");
|
||||
models.emplace_back(Settings::Manager::getString("xargonianswimkna", "Models"));
|
||||
models.emplace_back(Settings::Manager::getString("xbaseanimfemale", "Models"));
|
||||
models.emplace_back(Settings::Manager::getString("xbaseanim", "Models"));
|
||||
|
||||
if (!npc->mBase->mModel.empty())
|
||||
models.push_back("meshes/"+npc->mBase->mModel);
|
||||
|
|
|
@ -316,7 +316,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
|
|||
case SelectWrapper::Function_AiSetting:
|
||||
|
||||
return mActor.getClass().getCreatureStats (mActor).getAiSetting (
|
||||
(MWMechanics::CreatureStats::AiSetting)select.getArgument()).getModified();
|
||||
(MWMechanics::CreatureStats::AiSetting)select.getArgument()).getModified(false);
|
||||
|
||||
case SelectWrapper::Function_PcAttribute:
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
#include <MyGUI_TextBox.h>
|
||||
|
||||
// correctIconPath
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
|
@ -106,7 +109,10 @@ namespace MWGui
|
|||
std::string invIcon = ptr.getClass().getInventoryIcon(ptr);
|
||||
if (invIcon.empty())
|
||||
invIcon = "default icon.tga";
|
||||
setIcon(MWBase::Environment::get().getWindowManager()->correctIconPath(invIcon));
|
||||
invIcon = MWBase::Environment::get().getWindowManager()->correctIconPath(invIcon);
|
||||
if (!MWBase::Environment::get().getResourceSystem()->getVFS()->exists(invIcon))
|
||||
invIcon = MWBase::Environment::get().getWindowManager()->correctIconPath("default icon.tga");
|
||||
setIcon(invIcon);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -996,29 +996,29 @@ namespace MWMechanics
|
|||
if (actor.getClass().hasInventoryStore(actor))
|
||||
actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Poison);
|
||||
}
|
||||
else if (effects.get(ESM::MagicEffect::CureParalyzation).getModifier() > 0)
|
||||
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)
|
||||
if (effects.get(ESM::MagicEffect::CureCommonDisease).getModifier() > 0)
|
||||
{
|
||||
creatureStats.getSpells().purgeCommonDisease();
|
||||
}
|
||||
else if (effects.get(ESM::MagicEffect::CureBlightDisease).getModifier() > 0)
|
||||
if (effects.get(ESM::MagicEffect::CureBlightDisease).getModifier() > 0)
|
||||
{
|
||||
creatureStats.getSpells().purgeBlightDisease();
|
||||
}
|
||||
else if (effects.get(ESM::MagicEffect::CureCorprusDisease).getModifier() > 0)
|
||||
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)
|
||||
if (effects.get(ESM::MagicEffect::RemoveCurse).getModifier() > 0)
|
||||
{
|
||||
creatureStats.getSpells().purgeCurses();
|
||||
}
|
||||
|
|
|
@ -452,6 +452,8 @@ namespace MWMechanics
|
|||
MWMechanics::DynamicStat<float> health = attackerStats.getHealth();
|
||||
health.setCurrent(health.getCurrent() - x);
|
||||
attackerStats.setHealth(health);
|
||||
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(attacker, "Health Damage", 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ namespace MWMechanics
|
|||
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||
return 100;
|
||||
|
||||
if (checkMagicka && stats.getMagicka().getCurrent() < spell->mData.mCost)
|
||||
if (checkMagicka && spell->mData.mCost > 0 && stats.getMagicka().getCurrent() < spell->mData.mCost)
|
||||
return 0;
|
||||
|
||||
if (spell->mData.mFlags & ESM::Spell::F_Always)
|
||||
|
|
|
@ -18,8 +18,10 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
T Stat<T>::getModified() const
|
||||
T Stat<T>::getModified(bool capped) const
|
||||
{
|
||||
if(!capped)
|
||||
return mModified;
|
||||
return std::max(static_cast<T>(0), mModified);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace MWMechanics
|
|||
|
||||
const T& getBase() const;
|
||||
|
||||
T getModified() const;
|
||||
T getModified(bool capped = true) const;
|
||||
T getCurrentModified() const;
|
||||
T getModifier() const;
|
||||
T getCurrentModifier() const;
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace MWPhysics
|
|||
ContactCollectionCallback(const btCollisionObject * me, osg::Vec3f velocity) : mMe(me)
|
||||
{
|
||||
m_collisionFilterGroup = me->getBroadphaseHandle()->m_collisionFilterGroup;
|
||||
m_collisionFilterMask = me->getBroadphaseHandle()->m_collisionFilterMask;
|
||||
m_collisionFilterMask = me->getBroadphaseHandle()->m_collisionFilterMask & ~CollisionType_Projectile;
|
||||
mVelocity = Misc::Convert::toBullet(velocity);
|
||||
}
|
||||
btScalar addSingleResult(btManifoldPoint & contact, const btCollisionObjectWrapper * colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper * colObj1Wrap, int partId1, int index1) override
|
||||
|
|
|
@ -1500,7 +1500,7 @@ namespace MWRender
|
|||
MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
|
||||
if(ref->mBase->mFlags & ESM::Creature::Bipedal)
|
||||
{
|
||||
defaultSkeleton = "meshes\\xbase_anim.nif";
|
||||
defaultSkeleton = Settings::Manager::getString("xbaseanim", "Models");
|
||||
inject = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,6 +184,9 @@ namespace MWRender
|
|||
osg::ref_ptr<osg::Texture2D> dummyTexture = new osg::Texture2D();
|
||||
dummyTexture->setInternalFormat(GL_RED);
|
||||
dummyTexture->setTextureSize(1, 1);
|
||||
// This might clash with a shadow map, so make sure it doesn't cast shadows
|
||||
dummyTexture->setShadowComparison(true);
|
||||
dummyTexture->setShadowCompareFunc(osg::Texture::ShadowCompareFunc::ALWAYS);
|
||||
stateset->setTextureAttributeAndModes(7, dummyTexture, osg::StateAttribute::ON);
|
||||
stateset->setTextureAttribute(7, noBlendAlphaEnv, osg::StateAttribute::ON);
|
||||
stateset->addUniform(new osg::Uniform("noAlpha", true));
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <components/sceneutil/visitor.hpp>
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
#include <components/sceneutil/skeleton.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -35,7 +35,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr,
|
|||
setObjectRoot(model, false, false, true);
|
||||
|
||||
if((ref->mBase->mFlags&ESM::Creature::Bipedal))
|
||||
addAnimSource("meshes\\xbase_anim.nif", model);
|
||||
addAnimSource(Settings::Manager::getString("xbaseanim", "Models"), model);
|
||||
addAnimSource(model, model);
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const
|
|||
|
||||
if((ref->mBase->mFlags&ESM::Creature::Bipedal))
|
||||
{
|
||||
addAnimSource("meshes\\xbase_anim.nif", model);
|
||||
addAnimSource(Settings::Manager::getString("xbaseanim", "Models"), model);
|
||||
}
|
||||
addAnimSource(model, model);
|
||||
|
||||
|
|
|
@ -524,7 +524,7 @@ void NpcAnimation::updateNpcBase()
|
|||
|
||||
if(!is1stPerson)
|
||||
{
|
||||
const std::string base = "meshes\\xbase_anim.nif";
|
||||
const std::string base = Settings::Manager::getString("xbaseanim", "Models");
|
||||
if (smodel != base && !isWerewolf)
|
||||
addAnimSource(base, smodel);
|
||||
|
||||
|
@ -538,7 +538,7 @@ void NpcAnimation::updateNpcBase()
|
|||
}
|
||||
else
|
||||
{
|
||||
const std::string base = "meshes\\xbase_anim.1st.nif";
|
||||
const std::string base = Settings::Manager::getString("xbaseanim1st", "Models");
|
||||
if (smodel != base && !isWerewolf)
|
||||
addAnimSource(base, smodel);
|
||||
|
||||
|
|
|
@ -456,12 +456,15 @@ namespace MWRender
|
|||
mSky->listAssetsToPreload(workItem->mModels, workItem->mTextures);
|
||||
mWater->listAssetsToPreload(workItem->mTextures);
|
||||
|
||||
const char* basemodels[] = {"xbase_anim", "xbase_anim.1st", "xbase_anim_female", "xbase_animkna"};
|
||||
for (size_t i=0; i<sizeof(basemodels)/sizeof(basemodels[0]); ++i)
|
||||
{
|
||||
workItem->mModels.push_back(std::string("meshes/") + basemodels[i] + ".nif");
|
||||
workItem->mKeyframes.push_back(std::string("meshes/") + basemodels[i] + ".kf");
|
||||
}
|
||||
workItem->mModels.push_back(Settings::Manager::getString("xbaseanim", "Models"));
|
||||
workItem->mModels.push_back(Settings::Manager::getString("xbaseanim1st", "Models"));
|
||||
workItem->mModels.push_back(Settings::Manager::getString("xbaseanimfemale", "Models"));
|
||||
workItem->mModels.push_back(Settings::Manager::getString("xargonianswimkna", "Models"));
|
||||
|
||||
workItem->mKeyframes.push_back(Settings::Manager::getString("xbaseanimkf", "Models"));
|
||||
workItem->mKeyframes.push_back(Settings::Manager::getString("xbaseanim1stkf", "Models"));
|
||||
workItem->mKeyframes.push_back(Settings::Manager::getString("xbaseanimfemalekf", "Models"));
|
||||
workItem->mKeyframes.push_back(Settings::Manager::getString("xargonianswimknakf", "Models"));
|
||||
|
||||
workItem->mTextures.emplace_back("textures/_land_default.dds");
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ namespace MWScript
|
|||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
runtime.push(ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getModified());
|
||||
runtime.push(ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getModified(false));
|
||||
}
|
||||
};
|
||||
template<class R>
|
||||
|
@ -290,20 +290,20 @@ namespace MWScript
|
|||
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||
runtime.pop();
|
||||
|
||||
MWMechanics::Stat<int> stat = ptr.getClass().getCreatureStats(ptr).getAiSetting(mIndex);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Track the original stat value, to ensure we don't send repetitive packets to the server
|
||||
about its changes
|
||||
*/
|
||||
MWMechanics::Stat<int> stat = ptr.getClass().getCreatureStats(ptr).getAiSetting(mIndex);
|
||||
|
||||
int initialValue = stat.getBase();
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
stat.setModified(value, 0);
|
||||
ptr.getClass().getCreatureStats(ptr).setAiSetting(mIndex, value);
|
||||
ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, value);
|
||||
|
||||
/*
|
||||
|
|
|
@ -198,6 +198,9 @@ namespace MWScript
|
|||
.getCreatureStats(ptr)
|
||||
.getDynamic(mIndex)
|
||||
.getCurrent();
|
||||
// GetMagicka shouldn't return negative values
|
||||
if(mIndex == 1 && value < 0)
|
||||
value = 0;
|
||||
}
|
||||
runtime.push (value);
|
||||
}
|
||||
|
|
|
@ -723,13 +723,19 @@ namespace MWWorld
|
|||
{
|
||||
if (!cell)
|
||||
cell = mWorldScene->getCurrentCell();
|
||||
return getCellName(cell->getCell());
|
||||
}
|
||||
|
||||
if (!cell->getCell()->isExterior() || !cell->getCell()->mName.empty())
|
||||
return cell->getCell()->mName;
|
||||
std::string World::getCellName(const ESM::Cell* cell) const
|
||||
{
|
||||
if (cell)
|
||||
{
|
||||
if (!cell->isExterior() || !cell->mName.empty())
|
||||
return cell->mName;
|
||||
|
||||
if (const ESM::Region* region = mStore.get<ESM::Region>().search (cell->getCell()->mRegion))
|
||||
if (const ESM::Region* region = mStore.get<ESM::Region>().search (cell->mRegion))
|
||||
return region->mName;
|
||||
|
||||
}
|
||||
return mStore.get<ESM::GameSetting>().find ("sDefaultCellname")->mValue.getString();
|
||||
}
|
||||
|
||||
|
@ -3461,7 +3467,7 @@ namespace MWWorld
|
|||
// Check mana
|
||||
bool godmode = (isPlayer && mGodMode);
|
||||
MWMechanics::DynamicStat<float> magicka = stats.getMagicka();
|
||||
if (magicka.getCurrent() < spell->mData.mCost && !godmode)
|
||||
if (spell->mData.mCost > 0 && magicka.getCurrent() < spell->mData.mCost && !godmode)
|
||||
{
|
||||
message = "#{sMagicInsufficientSP}";
|
||||
fail = true;
|
||||
|
|
|
@ -315,6 +315,7 @@ namespace MWWorld
|
|||
///
|
||||
/// \note If cell==0, the cell the player is currently in will be used instead to
|
||||
/// generate a name.
|
||||
std::string getCellName(const ESM::Cell* cell) const override;
|
||||
|
||||
void removeRefScript (MWWorld::RefData *ref) override;
|
||||
//< Remove the script attached to ref from mLocalScripts
|
||||
|
|
|
@ -15,6 +15,7 @@ if (GTEST_FOUND AND GMOCK_FOUND)
|
|||
esm/test_fixed_string.cpp
|
||||
|
||||
misc/test_stringops.cpp
|
||||
misc/test_endianness.cpp
|
||||
|
||||
nifloader/testbulletnifloader.cpp
|
||||
|
||||
|
|
122
apps/openmw_test_suite/misc/test_endianness.cpp
Normal file
122
apps/openmw_test_suite/misc/test_endianness.cpp
Normal file
|
@ -0,0 +1,122 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include "components/misc/endianness.hpp"
|
||||
|
||||
struct EndiannessTest : public ::testing::Test {};
|
||||
|
||||
TEST_F(EndiannessTest, test_swap_endianness_inplace1)
|
||||
{
|
||||
uint8_t zero=0x00;
|
||||
uint8_t ff=0xFF;
|
||||
uint8_t fortytwo=0x42;
|
||||
uint8_t half=128;
|
||||
|
||||
Misc::swapEndiannessInplace(zero);
|
||||
EXPECT_EQ(zero, 0x00);
|
||||
|
||||
Misc::swapEndiannessInplace(ff);
|
||||
EXPECT_EQ(ff, 0xFF);
|
||||
|
||||
Misc::swapEndiannessInplace(fortytwo);
|
||||
EXPECT_EQ(fortytwo, 0x42);
|
||||
|
||||
Misc::swapEndiannessInplace(half);
|
||||
EXPECT_EQ(half, 128);
|
||||
}
|
||||
|
||||
TEST_F(EndiannessTest, test_swap_endianness_inplace2)
|
||||
{
|
||||
uint16_t zero = 0x0000;
|
||||
uint16_t ffff = 0xFFFF;
|
||||
uint16_t n12 = 0x0102;
|
||||
uint16_t fortytwo = 0x0042;
|
||||
|
||||
Misc::swapEndiannessInplace(zero);
|
||||
EXPECT_EQ(zero, 0x0000);
|
||||
Misc::swapEndiannessInplace(zero);
|
||||
EXPECT_EQ(zero, 0x0000);
|
||||
|
||||
Misc::swapEndiannessInplace(ffff);
|
||||
EXPECT_EQ(ffff, 0xFFFF);
|
||||
Misc::swapEndiannessInplace(ffff);
|
||||
EXPECT_EQ(ffff, 0xFFFF);
|
||||
|
||||
Misc::swapEndiannessInplace(n12);
|
||||
EXPECT_EQ(n12, 0x0201);
|
||||
Misc::swapEndiannessInplace(n12);
|
||||
EXPECT_EQ(n12, 0x0102);
|
||||
|
||||
Misc::swapEndiannessInplace(fortytwo);
|
||||
EXPECT_EQ(fortytwo, 0x4200);
|
||||
Misc::swapEndiannessInplace(fortytwo);
|
||||
EXPECT_EQ(fortytwo, 0x0042);
|
||||
}
|
||||
|
||||
TEST_F(EndiannessTest, test_swap_endianness_inplace4)
|
||||
{
|
||||
uint32_t zero = 0x00000000;
|
||||
uint32_t n1234 = 0x01020304;
|
||||
uint32_t ffff = 0xFFFFFFFF;
|
||||
|
||||
Misc::swapEndiannessInplace(zero);
|
||||
EXPECT_EQ(zero, 0x00000000);
|
||||
Misc::swapEndiannessInplace(zero);
|
||||
EXPECT_EQ(zero, 0x00000000);
|
||||
|
||||
Misc::swapEndiannessInplace(n1234);
|
||||
EXPECT_EQ(n1234, 0x04030201);
|
||||
Misc::swapEndiannessInplace(n1234);
|
||||
EXPECT_EQ(n1234, 0x01020304);
|
||||
|
||||
Misc::swapEndiannessInplace(ffff);
|
||||
EXPECT_EQ(ffff, 0xFFFFFFFF);
|
||||
Misc::swapEndiannessInplace(ffff);
|
||||
EXPECT_EQ(ffff, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
TEST_F(EndiannessTest, test_swap_endianness_inplace8)
|
||||
{
|
||||
uint64_t zero = 0x0000'0000'0000'0000;
|
||||
uint64_t n1234 = 0x0102'0304'0506'0708;
|
||||
uint64_t ffff = 0xFFFF'FFFF'FFFF'FFFF;
|
||||
|
||||
Misc::swapEndiannessInplace(zero);
|
||||
EXPECT_EQ(zero, 0x0000'0000'0000'0000);
|
||||
Misc::swapEndiannessInplace(zero);
|
||||
EXPECT_EQ(zero, 0x0000'0000'0000'0000);
|
||||
|
||||
Misc::swapEndiannessInplace(ffff);
|
||||
EXPECT_EQ(ffff, 0xFFFF'FFFF'FFFF'FFFF);
|
||||
Misc::swapEndiannessInplace(ffff);
|
||||
EXPECT_EQ(ffff, 0xFFFF'FFFF'FFFF'FFFF);
|
||||
|
||||
Misc::swapEndiannessInplace(n1234);
|
||||
EXPECT_EQ(n1234, 0x0807'0605'0403'0201);
|
||||
Misc::swapEndiannessInplace(n1234);
|
||||
EXPECT_EQ(n1234, 0x0102'0304'0506'0708);
|
||||
}
|
||||
|
||||
TEST_F(EndiannessTest, test_swap_endianness_inplace_float)
|
||||
{
|
||||
const uint32_t original = 0x4023d70a;
|
||||
const uint32_t expected = 0x0ad72340;
|
||||
|
||||
float number;
|
||||
memcpy(&number, &original, sizeof(original));
|
||||
|
||||
Misc::swapEndiannessInplace(number);
|
||||
|
||||
EXPECT_TRUE(!memcmp(&number, &expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
TEST_F(EndiannessTest, test_swap_endianness_inplace_double)
|
||||
{
|
||||
const uint64_t original = 0x040047ae147ae147ul;
|
||||
const uint64_t expected = 0x47e17a14ae470004ul;
|
||||
|
||||
double number;
|
||||
memcpy(&number, &original, sizeof(original));
|
||||
|
||||
Misc::swapEndiannessInplace(number);
|
||||
|
||||
EXPECT_TRUE(!memcmp(&number, &expected, sizeof(expected)) );
|
||||
}
|
|
@ -95,9 +95,9 @@ void Wizard::ExistingInstallationPage::on_browseButton_clicked()
|
|||
{
|
||||
QString selectedFile = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
tr("Select master file"),
|
||||
tr("Select Morrowind.esm (located in Data Files)"),
|
||||
QDir::currentPath(),
|
||||
QString(tr("Morrowind master file (*.esm)")),
|
||||
QString(tr("Morrowind master file (Morrowind.esm)")),
|
||||
nullptr,
|
||||
QFileDialog::DontResolveSymlinks);
|
||||
|
||||
|
@ -110,7 +110,18 @@ void Wizard::ExistingInstallationPage::on_browseButton_clicked()
|
|||
return;
|
||||
|
||||
if (!mWizard->findFiles(QLatin1String("Morrowind"), info.absolutePath()))
|
||||
return; // No valid Morrowind installation found
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error detecting Morrowind files"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(QObject::tr(
|
||||
"<b>Morrowind.bsa</b> is missing!<br>\
|
||||
Make sure your Morrowind installation is complete."
|
||||
));
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
QString path(QDir::toNativeSeparators(info.absolutePath()));
|
||||
QList<QListWidgetItem*> items = installationsList->findItems(path, Qt::MatchExactly);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#define COMPONENTS_MISC_ENDIANNESS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
|
@ -15,20 +17,26 @@ namespace Misc
|
|||
|
||||
if constexpr (sizeof(T) == 2)
|
||||
{
|
||||
uint16_t& v16 = *reinterpret_cast<uint16_t*>(&v);
|
||||
uint16_t v16;
|
||||
std::memcpy(&v16, &v, sizeof(T));
|
||||
v16 = (v16 >> 8) | (v16 << 8);
|
||||
std::memcpy(&v, &v16, sizeof(T));
|
||||
}
|
||||
if constexpr (sizeof(T) == 4)
|
||||
{
|
||||
uint32_t& v32 = *reinterpret_cast<uint32_t*>(&v);
|
||||
v32 = (v32 >> 24) | ((v32 >> 8) & 0xff00) | ((v32 & 0xff00) << 8) || v32 << 24;
|
||||
uint32_t v32;
|
||||
std::memcpy(&v32, &v, sizeof(T));
|
||||
v32 = (v32 >> 24) | ((v32 >> 8) & 0xff00) | ((v32 & 0xff00) << 8) | (v32 << 24);
|
||||
std::memcpy(&v, &v32, sizeof(T));
|
||||
}
|
||||
if constexpr (sizeof(T) == 8)
|
||||
{
|
||||
uint64_t& v64 = *reinterpret_cast<uint64_t*>(&v);
|
||||
uint64_t v64;
|
||||
std::memcpy(&v64, &v, sizeof(T));
|
||||
v64 = (v64 >> 56) | ((v64 & 0x00ff'0000'0000'0000) >> 40) | ((v64 & 0x0000'ff00'0000'0000) >> 24)
|
||||
| ((v64 & 0x0000'00ff'0000'0000) >> 8) | ((v64 & 0x0000'0000'ff00'0000) << 8)
|
||||
| ((v64 & 0x0000'0000'00ff'0000) << 24) | ((v64 & 0x0000'0000'0000'ff00) << 40) | (v64 << 56);
|
||||
std::memcpy(&v, &v64, sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
|
||||
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
|
||||
#include <components/nifbullet/bulletnifloader.hpp>
|
||||
|
@ -86,7 +87,17 @@ public:
|
|||
return osg::ref_ptr<BulletShape>();
|
||||
|
||||
osg::ref_ptr<BulletShape> shape (new BulletShape);
|
||||
shape->mCollisionShape = new TriangleMeshShape(mTriangleMesh.release(), true);
|
||||
btBvhTriangleMeshShape* triangleMeshShape = new TriangleMeshShape(mTriangleMesh.release(), true);
|
||||
btVector3 aabbMin = triangleMeshShape->getLocalAabbMin();
|
||||
btVector3 aabbMax = triangleMeshShape->getLocalAabbMax();
|
||||
shape->mCollisionBox.extents[0] = (aabbMax[0] - aabbMin[0]) / 2.0f;
|
||||
shape->mCollisionBox.extents[1] = (aabbMax[1] - aabbMin[1]) / 2.0f;
|
||||
shape->mCollisionBox.extents[2] = (aabbMax[2] - aabbMin[2]) / 2.0f;
|
||||
shape->mCollisionBox.center = osg::Vec3f( (aabbMax[0] + aabbMin[0]) / 2.0f,
|
||||
(aabbMax[1] + aabbMin[1]) / 2.0f,
|
||||
(aabbMax[2] + aabbMin[2]) / 2.0f );
|
||||
shape->mCollisionShape = triangleMeshShape;
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
|
@ -135,12 +146,32 @@ osg::ref_ptr<const BulletShape> BulletShapeManager::getShape(const std::string &
|
|||
|
||||
osg::ref_ptr<const osg::Node> constNode (mSceneManager->getTemplate(normalized));
|
||||
osg::ref_ptr<osg::Node> node (const_cast<osg::Node*>(constNode.get())); // const-trickery required because there is no const version of NodeVisitor
|
||||
|
||||
// Check first if there's a custom collision node
|
||||
unsigned int visitAllNodesMask = 0xffffffff;
|
||||
SceneUtil::FindByNameVisitor nameFinder("Collision");
|
||||
nameFinder.setTraversalMask(visitAllNodesMask);
|
||||
nameFinder.setNodeMaskOverride(visitAllNodesMask);
|
||||
node->accept(nameFinder);
|
||||
if (nameFinder.mFoundNode)
|
||||
{
|
||||
NodeToShapeVisitor visitor;
|
||||
visitor.setTraversalMask(visitAllNodesMask);
|
||||
visitor.setNodeMaskOverride(visitAllNodesMask);
|
||||
nameFinder.mFoundNode->accept(visitor);
|
||||
shape = visitor.getShape();
|
||||
}
|
||||
|
||||
// Generate a collision shape from the mesh
|
||||
if (!shape)
|
||||
{
|
||||
NodeToShapeVisitor visitor;
|
||||
node->accept(visitor);
|
||||
shape = visitor.getShape();
|
||||
if (!shape)
|
||||
return osg::ref_ptr<BulletShape>();
|
||||
}
|
||||
}
|
||||
|
||||
mCache->addEntryToObjectCache(normalized, shape);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <components/nifosg/nifloader.hpp>
|
||||
#include <components/sceneutil/keyframe.hpp>
|
||||
#include <components/sceneutil/osgacontroller.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "animation.hpp"
|
||||
#include "objectcache.hpp"
|
||||
|
@ -17,11 +18,13 @@
|
|||
namespace Resource
|
||||
{
|
||||
|
||||
RetrieveAnimationsVisitor::RetrieveAnimationsVisitor(SceneUtil::KeyframeHolder& target, osg::ref_ptr<osgAnimation::BasicAnimationManager> animationManager) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mTarget(target), mAnimationManager(animationManager) {}
|
||||
RetrieveAnimationsVisitor::RetrieveAnimationsVisitor(SceneUtil::KeyframeHolder& target, osg::ref_ptr<osgAnimation::BasicAnimationManager> animationManager,
|
||||
const std::string& normalized, const VFS::Manager* vfs) :
|
||||
osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mTarget(target), mAnimationManager(animationManager), mNormalized(normalized), mVFS(vfs) {}
|
||||
|
||||
void RetrieveAnimationsVisitor::apply(osg::Node& node)
|
||||
{
|
||||
if (node.libraryName() == std::string("osgAnimation") && node.className() == std::string("Bone") && node.getName() == std::string("bip01"))
|
||||
if (node.libraryName() == std::string("osgAnimation") && node.className() == std::string("Bone") && Misc::StringUtils::lowerCase(node.getName()) == std::string("bip01"))
|
||||
{
|
||||
osg::ref_ptr<SceneUtil::OsgAnimationController> callback = new SceneUtil::OsgAnimationController();
|
||||
|
||||
|
@ -38,27 +41,19 @@ namespace Resource
|
|||
|
||||
osg::ref_ptr<Resource::Animation> mergedAnimationTrack = new Resource::Animation;
|
||||
std::string animationName = animation->getName();
|
||||
std::string start = animationName + std::string(": start");
|
||||
std::string stop = animationName + std::string(": stop");
|
||||
mergedAnimationTrack->setName(animationName);
|
||||
|
||||
const osgAnimation::ChannelList& channels = animation->getChannels();
|
||||
for (const auto& channel: channels)
|
||||
{
|
||||
mergedAnimationTrack->addChannel(channel.get()->clone()); // is ->clone needed?
|
||||
}
|
||||
mergedAnimationTrack->setName(animation->getName());
|
||||
|
||||
callback->addMergedAnimationTrack(mergedAnimationTrack);
|
||||
|
||||
float startTime = animation->getStartTime();
|
||||
float stopTime = startTime + animation->getDuration();
|
||||
|
||||
// mTextKeys is a nif-thing, used by OpenMW's animation system
|
||||
// Format is likely "AnimationName: [Keyword_optional] [Start OR Stop]"
|
||||
// AnimationNames are keywords like idle2, idle3... AiPackages and various mechanics control which animations are played
|
||||
// Keywords can be stuff like Loop, Equip, Unequip, Block, InventoryHandtoHand, InventoryWeaponOneHand, PickProbe, Slash, Thrust, Chop... even "Slash Small Follow"
|
||||
mTarget.mTextKeys.emplace(startTime, std::move(start));
|
||||
mTarget.mTextKeys.emplace(stopTime, std::move(stop));
|
||||
|
||||
SceneUtil::EmulatedAnimation emulatedAnimation;
|
||||
emulatedAnimation.mStartTime = startTime;
|
||||
emulatedAnimation.mStopTime = stopTime;
|
||||
|
@ -66,12 +61,61 @@ namespace Resource
|
|||
emulatedAnimations.emplace_back(emulatedAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
// mTextKeys is a nif-thing, used by OpenMW's animation system
|
||||
// Format is likely "AnimationName: [Keyword_optional] [Start OR Stop]"
|
||||
// AnimationNames are keywords like idle2, idle3... AiPackages and various mechanics control which animations are played
|
||||
// Keywords can be stuff like Loop, Equip, Unequip, Block, InventoryHandtoHand, InventoryWeaponOneHand, PickProbe, Slash, Thrust, Chop... even "Slash Small Follow"
|
||||
// osgAnimation formats should have a .txt file with the same name, each line holding a textkey and whitespace separated time value
|
||||
// e.g. idle: start 0.0333
|
||||
try
|
||||
{
|
||||
Files::IStreamPtr textKeysFile = mVFS->get(changeFileExtension(mNormalized, "txt"));
|
||||
std::string line;
|
||||
while ( getline (*textKeysFile, line) )
|
||||
{
|
||||
mTarget.mTextKeys.emplace(parseTimeSignature(line), parseTextKey(line));
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Log(Debug::Warning) << "No textkey file found for " << mNormalized;
|
||||
}
|
||||
|
||||
callback->setEmulatedAnimations(emulatedAnimations);
|
||||
mTarget.mKeyframeControllers.emplace(node.getName(), callback);
|
||||
}
|
||||
|
||||
traverse(node);
|
||||
}
|
||||
|
||||
std::string RetrieveAnimationsVisitor::parseTextKey(const std::string& line)
|
||||
{
|
||||
size_t spacePos = line.find_last_of(' ');
|
||||
if (spacePos != std::string::npos)
|
||||
return line.substr(0, spacePos);
|
||||
return "";
|
||||
}
|
||||
|
||||
double RetrieveAnimationsVisitor::parseTimeSignature(const std::string& line)
|
||||
{
|
||||
size_t spacePos = line.find_last_of(' ');
|
||||
double time = 0.0;
|
||||
if (spacePos != std::string::npos && spacePos + 1 < line.size())
|
||||
time = std::stod(line.substr(spacePos + 1));
|
||||
return time;
|
||||
}
|
||||
|
||||
std::string RetrieveAnimationsVisitor::changeFileExtension(const std::string file, const std::string ext)
|
||||
{
|
||||
size_t extPos = file.find_last_of('.');
|
||||
if (extPos != std::string::npos && extPos+1 < file.size())
|
||||
{
|
||||
return file.substr(0, extPos + 1) + ext;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Resource
|
||||
|
@ -109,7 +153,7 @@ namespace Resource
|
|||
osg::ref_ptr<osgAnimation::BasicAnimationManager> bam = dynamic_cast<osgAnimation::BasicAnimationManager*> (scene->getUpdateCallback());
|
||||
if (bam)
|
||||
{
|
||||
Resource::RetrieveAnimationsVisitor rav(*loaded.get(), bam);
|
||||
Resource::RetrieveAnimationsVisitor rav(*loaded.get(), bam, normalized, mVFS);
|
||||
scene->accept(rav);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,21 @@ namespace Resource
|
|||
class RetrieveAnimationsVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
RetrieveAnimationsVisitor(SceneUtil::KeyframeHolder& target, osg::ref_ptr<osgAnimation::BasicAnimationManager> animationManager);
|
||||
RetrieveAnimationsVisitor(SceneUtil::KeyframeHolder& target, osg::ref_ptr<osgAnimation::BasicAnimationManager> animationManager,
|
||||
const std::string& normalized, const VFS::Manager* vfs);
|
||||
|
||||
virtual void apply(osg::Node& node) override;
|
||||
|
||||
private:
|
||||
|
||||
std::string changeFileExtension(const std::string file, const std::string ext);
|
||||
std::string parseTextKey(const std::string& line);
|
||||
double parseTimeSignature(const std::string& line);
|
||||
|
||||
SceneUtil::KeyframeHolder& mTarget;
|
||||
osg::ref_ptr<osgAnimation::BasicAnimationManager> mAnimationManager;
|
||||
std::string mNormalized;
|
||||
const VFS::Manager* mVFS;
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <components/sceneutil/util.hpp>
|
||||
#include <components/sceneutil/controller.hpp>
|
||||
#include <components/sceneutil/optimizer.hpp>
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
|
||||
#include <components/shader/shadervisitor.hpp>
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
|
@ -373,6 +374,14 @@ namespace Resource
|
|||
errormsg << "Error loading " << normalizedFilename << ": " << result.message() << " code " << result.status() << std::endl;
|
||||
throw std::runtime_error(errormsg.str());
|
||||
}
|
||||
|
||||
// Recognize and hide collision node
|
||||
unsigned int hiddenNodeMask = 0;
|
||||
SceneUtil::FindByNameVisitor nameFinder("Collision");
|
||||
result.getNode()->accept(nameFinder);
|
||||
if (nameFinder.mFoundNode)
|
||||
nameFinder.mFoundNode->setNodeMask(hiddenNodeMask);
|
||||
|
||||
return result.getNode();
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +399,8 @@ namespace Resource
|
|||
{
|
||||
const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm",
|
||||
"Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle",
|
||||
"Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "Arrow", "Camera"};
|
||||
"Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "Arrow", "Camera", "Collision", "Right_Wrist", "Left_Wrist",
|
||||
"Shield_Bone", "Right_Forearm", "Left_Forearm", "Right_Upper_Arm", "Left_Clavicle", "Weapon_Bone", "Root_Bone"};
|
||||
|
||||
reservedNames = std::vector<std::string>(reserved, reserved + sizeof(reserved)/sizeof(reserved[0]));
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "actorutil.hpp"
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
std::string getActorSkeleton(bool firstPerson, bool isFemale, bool isBeast, bool isWerewolf)
|
||||
|
@ -7,24 +9,24 @@ namespace SceneUtil
|
|||
if (!firstPerson)
|
||||
{
|
||||
if (isWerewolf)
|
||||
return "meshes\\wolf\\skin.nif";
|
||||
return Settings::Manager::getString("wolfskin", "Models");
|
||||
else if (isBeast)
|
||||
return "meshes\\base_animkna.nif";
|
||||
return Settings::Manager::getString("baseanimkna", "Models");
|
||||
else if (isFemale)
|
||||
return "meshes\\base_anim_female.nif";
|
||||
return Settings::Manager::getString("baseanimfemale", "Models");
|
||||
else
|
||||
return "meshes\\base_anim.nif";
|
||||
return Settings::Manager::getString("baseanim", "Models");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isWerewolf)
|
||||
return "meshes\\wolf\\skin.1st.nif";
|
||||
return Settings::Manager::getString("wolfskin1st", "Models");
|
||||
else if (isBeast)
|
||||
return "meshes\\base_animkna.1st.nif";
|
||||
return Settings::Manager::getString("baseanimkna1st", "Models");
|
||||
else if (isFemale)
|
||||
return "meshes\\base_anim_female.1st.nif";
|
||||
return Settings::Manager::getString("baseanimfemale1st", "Models");
|
||||
else
|
||||
return "meshes\\base_anim.1st.nif";
|
||||
return Settings::Manager::getString("xbaseanim1st", "Models");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -997,9 +997,9 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||
}
|
||||
|
||||
// 1. Traverse main scene graph
|
||||
cv.pushStateSet( _shadowRecievingPlaceholderStateSet.get() );
|
||||
|
||||
osg::ref_ptr<osgUtil::StateGraph> decoratorStateGraph = cv.getCurrentStateGraph();
|
||||
auto* shadowReceiverStateSet = vdd->getStateSet(cv.getTraversalNumber());
|
||||
shadowReceiverStateSet->clear();
|
||||
cv.pushStateSet(shadowReceiverStateSet);
|
||||
|
||||
cullShadowReceivingScene(&cv);
|
||||
|
||||
|
@ -1426,7 +1426,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||
|
||||
if (numValidShadows>0)
|
||||
{
|
||||
decoratorStateGraph->setStateSet(selectStateSetForRenderingShadow(*vdd, cv.getTraversalNumber()));
|
||||
prepareStateSetForRenderingShadow(*vdd, cv.getTraversalNumber());
|
||||
}
|
||||
|
||||
// OSG_NOTICE<<"End of shadow setup Projection matrix "<<*cv.getProjectionMatrix()<<std::endl;
|
||||
|
@ -3004,9 +3004,9 @@ void MWShadowTechnique::cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Ca
|
|||
return;
|
||||
}
|
||||
|
||||
osg::StateSet* MWShadowTechnique::selectStateSetForRenderingShadow(ViewDependentData& vdd, unsigned int traversalNumber) const
|
||||
osg::StateSet* MWShadowTechnique::prepareStateSetForRenderingShadow(ViewDependentData& vdd, unsigned int traversalNumber) const
|
||||
{
|
||||
OSG_INFO<<" selectStateSetForRenderingShadow() "<<vdd.getStateSet(traversalNumber)<<std::endl;
|
||||
OSG_INFO<<" prepareStateSetForRenderingShadow() "<<vdd.getStateSet(traversalNumber)<<std::endl;
|
||||
|
||||
osg::ref_ptr<osg::StateSet> stateset = vdd.getStateSet(traversalNumber);
|
||||
|
||||
|
|
|
@ -231,7 +231,7 @@ namespace SceneUtil {
|
|||
|
||||
virtual void cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Camera* camera) const;
|
||||
|
||||
virtual osg::StateSet* selectStateSetForRenderingShadow(ViewDependentData& vdd, unsigned int traversalNumber) const;
|
||||
virtual osg::StateSet* prepareStateSetForRenderingShadow(ViewDependentData& vdd, unsigned int traversalNumber) const;
|
||||
|
||||
protected:
|
||||
virtual ~MWShadowTechnique();
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <osgAnimation/UpdateMatrixTransform>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/resource/animation.hpp>
|
||||
#include <components/sceneutil/controller.hpp>
|
||||
#include <components/sceneutil/keyframe.hpp>
|
||||
|
@ -83,7 +84,7 @@ namespace SceneUtil
|
|||
{
|
||||
osgAnimation::UpdateMatrixTransform* umt = dynamic_cast<osgAnimation::UpdateMatrixTransform*>(cb);
|
||||
if (umt)
|
||||
if (node.getName() != "bip01") link(umt);
|
||||
if (Misc::StringUtils::lowerCase(node.getName()) != "bip01") link(umt);
|
||||
cb = cb->getNestedCallback();
|
||||
}
|
||||
|
||||
|
@ -102,10 +103,14 @@ namespace SceneUtil
|
|||
}
|
||||
|
||||
OsgAnimationController::OsgAnimationController(const OsgAnimationController ©, const osg::CopyOp ©op) : SceneUtil::KeyframeController(copy, copyop)
|
||||
, mMergedAnimationTracks(copy.mMergedAnimationTracks)
|
||||
, mEmulatedAnimations(copy.mEmulatedAnimations)
|
||||
{
|
||||
mLinker = nullptr;
|
||||
for (const auto& mergedAnimationTrack : copy.mMergedAnimationTracks)
|
||||
{
|
||||
Resource::Animation* copiedAnimationTrack = static_cast<Resource::Animation*>(mergedAnimationTrack.get()->clone(copyop));
|
||||
mMergedAnimationTracks.emplace_back(copiedAnimationTrack);
|
||||
}
|
||||
}
|
||||
|
||||
osg::Vec3f OsgAnimationController::getTranslation(float time) const
|
||||
|
|
|
@ -60,7 +60,20 @@ namespace SceneUtil
|
|||
void NodeMapVisitor::apply(osg::MatrixTransform& trans)
|
||||
{
|
||||
// Take transformation for first found node in file
|
||||
const std::string nodeName = Misc::StringUtils::lowerCase(trans.getName());
|
||||
std::string originalNodeName = Misc::StringUtils::lowerCase(trans.getName());
|
||||
|
||||
if (trans.libraryName() == std::string("osgAnimation"))
|
||||
{
|
||||
// Convert underscores to whitespaces as a workaround for Collada (OpenMW's animation system uses whitespace-separated names)
|
||||
std::string underscore = "_";
|
||||
std::size_t foundUnderscore = originalNodeName.find(underscore);
|
||||
|
||||
if (foundUnderscore != std::string::npos)
|
||||
std::replace(originalNodeName.begin(), originalNodeName.end(), '_', ' ');
|
||||
}
|
||||
|
||||
const std::string nodeName = originalNodeName;
|
||||
|
||||
mMap.emplace(nodeName, &trans);
|
||||
|
||||
traverse(trans);
|
||||
|
|
|
@ -38,7 +38,7 @@ This setting causes the behavior of the sneak key (bound to Ctrl by default)
|
|||
to toggle sneaking on and off rather than requiring the key to be held down while sneaking.
|
||||
Players that spend significant time sneaking may find the character easier to control with this option enabled.
|
||||
|
||||
This setting can only be configured by editing the settings configuration file.
|
||||
This setting can be toggled in the launcher under "Advanced" -> "Game Mechanics" -> "Toggle sneak".
|
||||
|
||||
always run
|
||||
----------
|
||||
|
|
|
@ -955,6 +955,51 @@ defer aabb update = true
|
|||
# Loading arbitrary meshes is not advised and may cause instability.
|
||||
load unsupported nif files = false
|
||||
|
||||
# 3rd person base animation model that looks also for the corresponding kf-file
|
||||
xbaseanim = meshes/xbase_anim.nif
|
||||
|
||||
# 3rd person base model with textkeys-data
|
||||
baseanim = meshes/base_anim.nif
|
||||
|
||||
# 1st person base animation model that looks also for corresponding kf-file
|
||||
xbaseanim1st = meshes/xbase_anim.1st.nif
|
||||
|
||||
# 3rd person beast race base model with textkeys-data
|
||||
baseanimkna = meshes/base_animkna.nif
|
||||
|
||||
# 1st person beast race base animation model
|
||||
baseanimkna1st = meshes/base_animkna.1st.nif
|
||||
|
||||
# 3rd person female base animation model
|
||||
xbaseanimfemale = meshes/xbase_anim_female.nif
|
||||
|
||||
# 3rd person female base model with textkeys-data
|
||||
baseanimfemale = meshes/base_anim_female.nif
|
||||
|
||||
# 1st person female base model with textkeys-data
|
||||
baseanimfemale1st = meshes/base_anim_female.1st.nif
|
||||
|
||||
# 3rd person werewolf skin
|
||||
wolfskin = meshes/wolf/skin.nif
|
||||
|
||||
# 1st person werewolf skin
|
||||
wolfskin1st = meshes/wolf/skin.1st.nif
|
||||
|
||||
# Argonian smimkna
|
||||
xargonianswimkna = meshes/xargonian_swimkna.nif
|
||||
|
||||
# File to load xbaseanim 3rd person animations
|
||||
xbaseanimkf = meshes/xbase_anim.kf
|
||||
|
||||
# File to load xbaseanim 3rd person animations
|
||||
xbaseanim1stkf = meshes/xbase_anim.1st.kf
|
||||
|
||||
# File to load xbaseanim animations from
|
||||
xbaseanimfemalekf = meshes/xbase_anim_female.kf
|
||||
|
||||
# File to load xargonianswimkna animations from
|
||||
xargonianswimknakf = meshes/xargonian_swimkna.kf
|
||||
|
||||
[Groundcover]
|
||||
|
||||
# enable separate groundcover handling
|
||||
|
|
|
@ -13,22 +13,13 @@ void perLight(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 vie
|
|||
#ifndef GROUNDCOVER
|
||||
lambert = max(lambert, 0.0);
|
||||
#else
|
||||
float eyeCosine = dot(normalize(viewPos), viewNormal.xyz);
|
||||
if (lambert < 0.0)
|
||||
{
|
||||
float cosine = dot(normalize(viewPos), normalize(viewNormal.xyz));
|
||||
if (lambert >= 0.0)
|
||||
cosine = -cosine;
|
||||
|
||||
float mult = 1.0;
|
||||
float divisor = 8.0;
|
||||
|
||||
if (cosine < 0.0 && cosine >= -1.0/divisor)
|
||||
mult = mix(1.0, 0.3, -cosine*divisor);
|
||||
else if (cosine < -1.0/divisor)
|
||||
mult = 0.3;
|
||||
|
||||
lambert *= mult;
|
||||
lambert = abs(lambert);
|
||||
lambert = -lambert;
|
||||
eyeCosine = -eyeCosine;
|
||||
}
|
||||
lambert *= clamp(-8.0 * (1.0 - 0.3) * eyeCosine + 1.0, 0.3, 1.0);
|
||||
#endif
|
||||
diffuseOut = gl_LightSource[lightIndex].diffuse.xyz * lambert;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue