mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-09-26 21:53:07 -04:00
Merge branch 'nifisjustice' into 'master'
NIF updates See merge request OpenMW/openmw!394
This commit is contained in:
commit
6844800124
@ -320,7 +320,7 @@ void NiSkinData::read(NIFStream *nif)
|
||||
|
||||
int boneNum = nif->getInt();
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))
|
||||
nif->skip(4); // NiSkinPartition link
|
||||
partitions.read(nif);
|
||||
|
||||
// Has vertex weights flag
|
||||
if (nif->getVersion() > NIFStream::generateVersion(4,2,1,0) && !nif->getBoolean())
|
||||
@ -345,6 +345,69 @@ void NiSkinData::read(NIFStream *nif)
|
||||
}
|
||||
}
|
||||
|
||||
void NiSkinData::post(NIFFile *nif)
|
||||
{
|
||||
partitions.post(nif);
|
||||
}
|
||||
|
||||
void NiSkinPartition::read(NIFStream *nif)
|
||||
{
|
||||
unsigned int num = nif->getUInt();
|
||||
data.resize(num);
|
||||
for (auto& partition : data)
|
||||
partition.read(nif);
|
||||
}
|
||||
|
||||
void NiSkinPartition::Partition::read(NIFStream *nif)
|
||||
{
|
||||
unsigned short numVertices = nif->getUShort();
|
||||
unsigned short numTriangles = nif->getUShort();
|
||||
unsigned short numBones = nif->getUShort();
|
||||
unsigned short numStrips = nif->getUShort();
|
||||
unsigned short bonesPerVertex = nif->getUShort();
|
||||
if (numBones)
|
||||
nif->getUShorts(bones, numBones);
|
||||
|
||||
bool hasVertexMap = true;
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
hasVertexMap = nif->getBoolean();
|
||||
if (hasVertexMap && numVertices)
|
||||
nif->getUShorts(vertexMap, numVertices);
|
||||
|
||||
bool hasVertexWeights = true;
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
hasVertexWeights = nif->getBoolean();
|
||||
if (hasVertexWeights && numVertices && bonesPerVertex)
|
||||
nif->getFloats(weights, numVertices * bonesPerVertex);
|
||||
|
||||
std::vector<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();
|
||||
@ -392,4 +455,29 @@ void NiPalette::read(NIFStream *nif)
|
||||
colors[i] = nif->getUInt() | alphaMask;
|
||||
}
|
||||
|
||||
void NiStringPalette::read(NIFStream *nif)
|
||||
{
|
||||
unsigned int size = nif->getUInt();
|
||||
if (!size)
|
||||
return;
|
||||
std::vector<char> source;
|
||||
nif->getChars(source, size);
|
||||
if (nif->getUInt() != size)
|
||||
nif->file->warn("Failed size check in NiStringPalette");
|
||||
if (source[source.size()-1] != '\0')
|
||||
source.emplace_back('\0');
|
||||
const char* buffer = source.data();
|
||||
while (static_cast<size_t>(buffer - source.data()) < source.size())
|
||||
{
|
||||
palette.emplace_back(buffer);
|
||||
buffer += palette.back().size() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
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::vector<std::string> palette;
|
||||
void read(NIFStream *nif) override;
|
||||
};
|
||||
|
||||
struct NiBoolData : public Record
|
||||
{
|
||||
ByteKeyMapPtr mKeyList;
|
||||
void read(NIFStream *nif) override;
|
||||
};
|
||||
|
||||
} // Namespace
|
||||
#endif
|
||||
|
@ -1,9 +1,12 @@
|
||||
#include "niffile.hpp"
|
||||
#include "effect.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
||||
@ -113,6 +116,9 @@ 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 };
|
||||
return factory;
|
||||
}
|
||||
|
||||
@ -137,15 +143,46 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
||||
|
||||
// Check the header string
|
||||
std::string head = nif.getVersionString();
|
||||
if(head.compare(0, 22, "NetImmerse File Format") != 0)
|
||||
static const std::array<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)
|
||||
{
|
||||
static const bool ignoreUnsupported = Settings::Manager::getBool("load unsupported nif files", "Models");
|
||||
if (ignoreUnsupported)
|
||||
warn("Unsupported NIF version: " + printVersion(ver) + ". Proceed with caution!");
|
||||
else
|
||||
fail("Unsupported NIF version: " + printVersion(ver));
|
||||
}
|
||||
|
||||
// NIF data endianness
|
||||
if (ver >= NIFStream::generateVersion(20,0,0,4))
|
||||
@ -245,6 +282,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;
|
||||
|
@ -109,7 +109,10 @@ enum RecordType
|
||||
RC_NiVectorExtraData,
|
||||
RC_NiColorExtraData,
|
||||
RC_NiFloatExtraData,
|
||||
RC_NiFloatsExtraData
|
||||
RC_NiFloatsExtraData,
|
||||
RC_NiStringPalette,
|
||||
RC_NiBoolData,
|
||||
RC_NiSkinPartition
|
||||
};
|
||||
|
||||
/// Base class for all records
|
||||
|
@ -143,6 +143,8 @@ class NiAutoNormalParticlesData;
|
||||
class NiPalette;
|
||||
struct NiParticleModifier;
|
||||
struct NiLinesData;
|
||||
struct NiBoolData;
|
||||
struct NiSkinPartition;
|
||||
|
||||
using NodePtr = RecordPtrT<Node>;
|
||||
using ExtraPtr = RecordPtrT<Extra>;
|
||||
@ -166,6 +168,8 @@ 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 NodeList = RecordListT<Node>;
|
||||
using PropertyList = RecordListT<Property>;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -149,10 +140,12 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
||||
|
||||
if (findBoundingBox(node))
|
||||
{
|
||||
const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents);
|
||||
const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate);
|
||||
std::unique_ptr<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();
|
||||
|
||||
@ -383,7 +376,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons
|
||||
if (!mAvoidStaticMesh)
|
||||
mAvoidStaticMesh.reset(new btTriangleMesh(false));
|
||||
|
||||
fillTriangleMeshWithTransform(*mAvoidStaticMesh, nifNode, transform);
|
||||
fillTriangleMesh(*mAvoidStaticMesh, nifNode, transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -391,7 +384,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons
|
||||
mStaticMesh.reset(new btTriangleMesh(false));
|
||||
|
||||
// Static shape, just transform all vertices into position
|
||||
fillTriangleMeshWithTransform(*mStaticMesh, nifNode, transform);
|
||||
fillTriangleMesh(*mStaticMesh, nifNode, transform);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
@ -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)
|
||||
{
|
||||
@ -1175,7 +1199,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);
|
||||
@ -1220,7 +1244,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);
|
||||
|
@ -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.
|
@ -945,3 +945,8 @@ lineofsight keep inactive cache = 0
|
||||
|
||||
# Defer bounding boxes update until collision detection.
|
||||
defer aabb update = true
|
||||
|
||||
[Models]
|
||||
# Attempt to load any valid NIF file regardless of its version and track the progress.
|
||||
# Loading arbitrary meshes is not advised and may cause instability.
|
||||
load unsupported nif files = false
|
||||
|
Loading…
x
Reference in New Issue
Block a user