mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-06 09:45:32 +00:00
Merge branch 'dehardcodebaseanim' into 'master'
Dehardcode Base_animation and improve Collada support See merge request OpenMW/openmw!510
This commit is contained in:
commit
c33b2e0100
13 changed files with 199 additions and 48 deletions
|
@ -418,10 +418,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;
|
||||
}
|
||||
|
@ -431,12 +431,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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
|
||||
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
|
||||
#include <components/nifbullet/bulletnifloader.hpp>
|
||||
|
@ -145,11 +146,31 @@ 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
|
||||
NodeToShapeVisitor visitor;
|
||||
node->accept(visitor);
|
||||
shape = visitor.getShape();
|
||||
|
||||
// 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)
|
||||
return osg::ref_ptr<BulletShape>();
|
||||
{
|
||||
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), std::move(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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue