mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 08:53:52 +00:00
Merge branch 'nifdefinitions' into 'master'
Introduce some extended NIF definitions See merge request OpenMW/openmw!319
This commit is contained in:
commit
72ab340374
15 changed files with 505 additions and 107 deletions
|
@ -244,6 +244,7 @@ namespace
|
|||
void init(Nif::Named& value)
|
||||
{
|
||||
value.extra = Nif::ExtraPtr(nullptr);
|
||||
value.extralist = Nif::ExtraList();
|
||||
value.controller = Nif::ControllerPtr(nullptr);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,12 +14,18 @@ namespace Nif
|
|||
class Extra : public Record
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
ExtraPtr next; // Next extra data record in the list
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
next.read(nif);
|
||||
nif->getUInt(); // Size of the record
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||
name = nif->getString();
|
||||
else if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||
{
|
||||
next.read(nif);
|
||||
nif->getUInt(); // Size of the record
|
||||
}
|
||||
}
|
||||
|
||||
void post(NIFFile *nif) { next.post(nif); }
|
||||
|
@ -44,18 +50,23 @@ class Named : public Record
|
|||
public:
|
||||
std::string name;
|
||||
ExtraPtr extra;
|
||||
ExtraList extralist;
|
||||
ControllerPtr controller;
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
name = nif->getString();
|
||||
extra.read(nif);
|
||||
if (nif->getVersion() < NIFStream::generateVersion(10,0,1,0))
|
||||
extra.read(nif);
|
||||
else
|
||||
extralist.read(nif);
|
||||
controller.read(nif);
|
||||
}
|
||||
|
||||
void post(NIFFile *nif)
|
||||
{
|
||||
extra.post(nif);
|
||||
extralist.post(nif);
|
||||
controller.post(nif);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,16 +14,31 @@ namespace Nif
|
|||
if (external)
|
||||
filename = nif->getString();
|
||||
else
|
||||
internal = nif->getChar();
|
||||
|
||||
if (!external && internal)
|
||||
{
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,3))
|
||||
internal = nif->getChar();
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
filename = nif->getString(); // Original file path of the internal texture
|
||||
}
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,3))
|
||||
{
|
||||
if (!external && internal)
|
||||
data.read(nif);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.read(nif);
|
||||
}
|
||||
|
||||
pixel = nif->getUInt();
|
||||
mipmap = nif->getUInt();
|
||||
alpha = nif->getUInt();
|
||||
|
||||
nif->getChar(); // always 1
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,103))
|
||||
nif->getBoolean(); // Direct rendering
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,4))
|
||||
nif->getBoolean(); // NiPersistentSrcTextureRendererData is used instead of NiPixelData
|
||||
}
|
||||
|
||||
void NiSourceTexture::post(NIFFile *nif)
|
||||
|
@ -79,6 +94,12 @@ namespace Nif
|
|||
NiParticleModifier::read(nif);
|
||||
|
||||
mBounceFactor = nif->getFloat();
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(4,2,2,0))
|
||||
{
|
||||
// Unused in NifSkope. Need to figure out what these do.
|
||||
/*bool spawnOnCollision = */nif->getBoolean();
|
||||
/*bool dieOnCollision = */nif->getBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
void NiPlanarCollider::read(NIFStream *nif)
|
||||
|
|
|
@ -97,7 +97,10 @@ namespace Nif
|
|||
// 01: Diffuse
|
||||
// 10: Specular
|
||||
// 11: Emissive
|
||||
targetColor = (flags >> 4) & 3;
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
targetColor = nif->getUShort() & 3;
|
||||
else
|
||||
targetColor = (flags >> 4) & 3;
|
||||
data.read(nif);
|
||||
}
|
||||
|
||||
|
@ -110,6 +113,8 @@ namespace Nif
|
|||
void NiLookAtController::read(NIFStream *nif)
|
||||
{
|
||||
Controller::read(nif);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
lookAtFlags = nif->getUShort();
|
||||
target.read(nif);
|
||||
}
|
||||
|
||||
|
@ -192,6 +197,8 @@ namespace Nif
|
|||
void NiGeomMorpherController::read(NIFStream *nif)
|
||||
{
|
||||
Controller::read(nif);
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_OB_OLD)
|
||||
/*bool updateNormals = !!*/nif->getUShort();
|
||||
data.read(nif);
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
|
||||
/*bool alwaysActive = */nif->getChar(); // Always 0
|
||||
|
@ -219,8 +226,11 @@ namespace Nif
|
|||
{
|
||||
Controller::read(nif);
|
||||
mTexSlot = nif->getUInt();
|
||||
/*unknown=*/nif->getUInt();/*0?*/
|
||||
mDelta = nif->getFloat();
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103))
|
||||
{
|
||||
timeStart = nif->getFloat();
|
||||
mDelta = nif->getFloat();
|
||||
}
|
||||
mSources.read(nif);
|
||||
}
|
||||
|
||||
|
|
|
@ -118,6 +118,7 @@ class NiLookAtController : public Controller
|
|||
{
|
||||
public:
|
||||
NodePtr target;
|
||||
unsigned short lookAtFlags{0};
|
||||
|
||||
void read(NIFStream *nif);
|
||||
void post(NIFFile *nif);
|
||||
|
|
|
@ -33,13 +33,33 @@ void NiSkinInstance::post(NIFFile *nif)
|
|||
|
||||
void NiGeometryData::read(NIFStream *nif)
|
||||
{
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,114))
|
||||
nif->getInt(); // Group ID. (Almost?) always 0.
|
||||
|
||||
int verts = nif->getUShort();
|
||||
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
nif->skip(2); // Keep flags and compress flags
|
||||
|
||||
if (nif->getBoolean())
|
||||
nif->getVector3s(vertices, verts);
|
||||
|
||||
unsigned int dataFlags = 0;
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||
dataFlags = nif->getUShort();
|
||||
|
||||
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
|
||||
nif->getUInt(); // Material CRC
|
||||
|
||||
if (nif->getBoolean())
|
||||
{
|
||||
nif->getVector3s(normals, verts);
|
||||
if (dataFlags & 0x1000)
|
||||
{
|
||||
nif->getVector3s(tangents, verts);
|
||||
nif->getVector3s(bitangents, verts);
|
||||
}
|
||||
}
|
||||
|
||||
center = nif->getVector3();
|
||||
radius = nif->getFloat();
|
||||
|
@ -47,14 +67,27 @@ void NiGeometryData::read(NIFStream *nif)
|
|||
if (nif->getBoolean())
|
||||
nif->getVector4s(colors, verts);
|
||||
|
||||
// In Morrowind this field only corresponds to the number of UV sets.
|
||||
// NifTools research is inaccurate.
|
||||
int uvs = nif->getUShort();
|
||||
|
||||
if(nif->getInt())
|
||||
// Only the first 6 bits are used as a count. I think the rest are
|
||||
// flags of some sort.
|
||||
unsigned int numUVs = dataFlags;
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||
{
|
||||
uvlist.resize(uvs);
|
||||
for(int i = 0;i < uvs;i++)
|
||||
numUVs = nif->getUShort();
|
||||
// In Morrowind this field only corresponds to the number of UV sets.
|
||||
// NifTools research is inaccurate.
|
||||
if (nif->getVersion() > NIFFile::NIFVersion::VER_MW)
|
||||
numUVs &= 0x3f;
|
||||
}
|
||||
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > 0)
|
||||
numUVs &= 0x1;
|
||||
|
||||
bool hasUVs = true;
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW)
|
||||
hasUVs = nif->getBoolean();
|
||||
if (hasUVs)
|
||||
{
|
||||
uvlist.resize(numUVs);
|
||||
for (unsigned int i = 0; i < numUVs; i++)
|
||||
{
|
||||
nif->getVector2s(uvlist[i], verts);
|
||||
// flip the texture coordinates to convert them to the OpenGL convention of bottom-left image origin
|
||||
|
@ -64,6 +97,12 @@ void NiGeometryData::read(NIFStream *nif)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||
nif->getUShort(); // Consistency flags
|
||||
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20,0,0,4))
|
||||
nif->skip(4); // Additional data
|
||||
}
|
||||
|
||||
void NiTriShapeData::read(NIFStream *nif)
|
||||
|
@ -75,13 +114,17 @@ void NiTriShapeData::read(NIFStream *nif)
|
|||
// We have three times as many vertices as triangles, so this
|
||||
// is always equal to tris*3.
|
||||
int cnt = nif->getInt();
|
||||
nif->getUShorts(triangles, cnt);
|
||||
bool hasTriangles = true;
|
||||
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD)
|
||||
hasTriangles = nif->getBoolean();
|
||||
if (hasTriangles)
|
||||
nif->getUShorts(triangles, cnt);
|
||||
|
||||
// Read the match list, which lists the vertices that are equal to
|
||||
// vertices. We don't actually need need this for anything, so
|
||||
// just skip it.
|
||||
int verts = nif->getUShort();
|
||||
for(int i=0;i < verts;i++)
|
||||
unsigned short verts = nif->getUShort();
|
||||
for (unsigned short i=0; i < verts; i++)
|
||||
{
|
||||
// Number of vertices matching vertex 'i'
|
||||
int num = nif->getUShort();
|
||||
|
@ -101,7 +144,11 @@ void NiTriStripsData::read(NIFStream *nif)
|
|||
std::vector<unsigned short> lengths;
|
||||
nif->getUShorts(lengths, numStrips);
|
||||
|
||||
if (!numStrips)
|
||||
// "Has Strips" flag. Exceptionally useful.
|
||||
bool hasStrips = false;
|
||||
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD)
|
||||
hasStrips = nif->getBoolean();
|
||||
if (!hasStrips || !numStrips)
|
||||
return;
|
||||
|
||||
strips.resize(numStrips);
|
||||
|
@ -140,27 +187,37 @@ void NiAutoNormalParticlesData::read(NIFStream *nif)
|
|||
NiGeometryData::read(nif);
|
||||
|
||||
// Should always match the number of vertices
|
||||
numParticles = nif->getUShort();
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW)
|
||||
numParticles = nif->getUShort();
|
||||
|
||||
particleRadius = nif->getFloat();
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))
|
||||
std::fill(particleRadii.begin(), particleRadii.end(), nif->getFloat());
|
||||
else if (nif->getBoolean())
|
||||
nif->getFloats(particleRadii, vertices.size());
|
||||
activeCount = nif->getUShort();
|
||||
|
||||
// Particle sizes
|
||||
if (nif->getBoolean())
|
||||
{
|
||||
// Particle sizes
|
||||
nif->getFloats(sizes, vertices.size());
|
||||
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0) && nif->getBoolean())
|
||||
nif->getQuaternions(rotations, vertices.size());
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20,0,0,4))
|
||||
{
|
||||
if (nif->getBoolean())
|
||||
nif->getFloats(rotationAngles, vertices.size());
|
||||
if (nif->getBoolean())
|
||||
nif->getVector3s(rotationAxes, vertices.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void NiRotatingParticlesData::read(NIFStream *nif)
|
||||
{
|
||||
NiAutoNormalParticlesData::read(nif);
|
||||
|
||||
if (nif->getBoolean())
|
||||
{
|
||||
// Rotation quaternions.
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0) && nif->getBoolean())
|
||||
nif->getQuaternions(rotations, vertices.size());
|
||||
}
|
||||
}
|
||||
|
||||
void NiPosData::read(NIFStream *nif)
|
||||
|
@ -188,12 +245,27 @@ void NiPixelData::read(NIFStream *nif)
|
|||
{
|
||||
fmt = (Format)nif->getUInt();
|
||||
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
colorMask[i] = nif->getUInt();
|
||||
bpp = nif->getUInt();
|
||||
if (nif->getVersion() < NIFStream::generateVersion(10,4,0,2))
|
||||
{
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
colorMask[i] = nif->getUInt();
|
||||
bpp = nif->getUInt();
|
||||
nif->skip(8); // "Old Fast Compare". Whatever that means.
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
pixelTiling = nif->getUInt();
|
||||
}
|
||||
else // TODO: see if anything from here needs to be implemented
|
||||
{
|
||||
bpp = nif->getChar();
|
||||
nif->skip(4); // Renderer hint
|
||||
nif->skip(4); // Extra data
|
||||
nif->skip(4); // Flags
|
||||
pixelTiling = nif->getUInt();
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20,3,0,4))
|
||||
sRGB = nif->getBoolean();
|
||||
nif->skip(4*10); // Channel data
|
||||
}
|
||||
|
||||
// 8 bytes of "Old Fast Compare". Whatever that means.
|
||||
nif->skip(8);
|
||||
palette.read(nif);
|
||||
|
||||
numberOfMipmaps = nif->getUInt();
|
||||
|
@ -213,8 +285,10 @@ void NiPixelData::read(NIFStream *nif)
|
|||
|
||||
// Read the data
|
||||
unsigned int numPixels = nif->getUInt();
|
||||
if (numPixels)
|
||||
nif->getUChars(data, numPixels);
|
||||
bool hasFaces = nif->getVersion() >= NIFStream::generateVersion(10,4,0,2);
|
||||
unsigned int numFaces = hasFaces ? nif->getUInt() : 1;
|
||||
if (numPixels && numFaces)
|
||||
nif->getUChars(data, numPixels * numFaces);
|
||||
}
|
||||
|
||||
void NiPixelData::post(NIFFile *nif)
|
||||
|
@ -249,6 +323,10 @@ void NiSkinData::read(NIFStream *nif)
|
|||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))
|
||||
nif->skip(4); // NiSkinPartition link
|
||||
|
||||
// Has vertex weights flag
|
||||
if (nif->getVersion() > NIFStream::generateVersion(4,2,1,0) && !nif->getBoolean())
|
||||
return;
|
||||
|
||||
bones.resize(boneNum);
|
||||
for (BoneInfo &bi : bones)
|
||||
{
|
||||
|
@ -272,7 +350,7 @@ void NiMorphData::read(NIFStream *nif)
|
|||
{
|
||||
int morphCount = nif->getInt();
|
||||
int vertCount = nif->getInt();
|
||||
/*relative targets?*/nif->getChar();
|
||||
nif->getChar(); // Relative targets, always 1
|
||||
|
||||
mMorphs.resize(morphCount);
|
||||
for(int i = 0;i < morphCount;i++)
|
||||
|
@ -290,7 +368,8 @@ void NiKeyframeData::read(NIFStream *nif)
|
|||
if(mRotations->mInterpolationType == InterpolationType_XYZ)
|
||||
{
|
||||
//Chomp unused float
|
||||
nif->getFloat();
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))
|
||||
nif->getFloat();
|
||||
mXRotations = std::make_shared<FloatKeyMap>();
|
||||
mYRotations = std::make_shared<FloatKeyMap>();
|
||||
mZRotations = std::make_shared<FloatKeyMap>();
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Nif
|
|||
class NiGeometryData : public Record
|
||||
{
|
||||
public:
|
||||
std::vector<osg::Vec3f> vertices, normals;
|
||||
std::vector<osg::Vec3f> vertices, normals, tangents, bitangents;
|
||||
std::vector<osg::Vec4f> colors;
|
||||
std::vector< std::vector<osg::Vec2f> > uvlist;
|
||||
osg::Vec3f center;
|
||||
|
@ -73,13 +73,15 @@ struct NiLinesData : public NiGeometryData
|
|||
class NiAutoNormalParticlesData : public NiGeometryData
|
||||
{
|
||||
public:
|
||||
int numParticles;
|
||||
int numParticles{0};
|
||||
|
||||
float particleRadius;
|
||||
|
||||
int activeCount;
|
||||
|
||||
std::vector<float> sizes;
|
||||
std::vector<float> particleRadii, sizes, rotationAngles;
|
||||
std::vector<osg::Quat> rotations;
|
||||
std::vector<osg::Vec3f> rotationAxes;
|
||||
|
||||
void read(NIFStream *nif);
|
||||
};
|
||||
|
@ -87,8 +89,6 @@ public:
|
|||
class NiRotatingParticlesData : public NiAutoNormalParticlesData
|
||||
{
|
||||
public:
|
||||
std::vector<osg::Quat> rotations;
|
||||
|
||||
void read(NIFStream *nif);
|
||||
};
|
||||
|
||||
|
@ -133,7 +133,8 @@ public:
|
|||
Format fmt;
|
||||
|
||||
unsigned int colorMask[4];
|
||||
unsigned int bpp;
|
||||
unsigned int bpp, pixelTiling{0};
|
||||
bool sRGB{false};
|
||||
|
||||
NiPalettePtr palette;
|
||||
unsigned int numberOfMipmaps;
|
||||
|
|
|
@ -28,6 +28,10 @@ void NiTextureEffect::read(NIFStream *nif)
|
|||
// Texture Filtering
|
||||
nif->skip(4);
|
||||
|
||||
// Max anisotropy samples
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20,5,0,4))
|
||||
nif->skip(2);
|
||||
|
||||
clamp = nif->getUInt();
|
||||
|
||||
textureType = (TextureType)nif->getUInt();
|
||||
|
@ -36,14 +40,12 @@ void NiTextureEffect::read(NIFStream *nif)
|
|||
|
||||
texture.read(nif);
|
||||
|
||||
/*
|
||||
byte = 0
|
||||
vector4 = [1,0,0,0]
|
||||
short = 0
|
||||
short = -75
|
||||
short = 0
|
||||
*/
|
||||
nif->skip(23);
|
||||
nif->skip(1); // Use clipping plane
|
||||
nif->skip(16); // Clipping plane dimensions vector
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,2,0,0))
|
||||
nif->skip(4); // PS2-specific shorts
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,1,0,12))
|
||||
nif->skip(2); // Unknown short
|
||||
}
|
||||
|
||||
void NiTextureEffect::post(NIFFile *nif)
|
||||
|
|
|
@ -34,6 +34,9 @@ struct NiDynamicEffect : public Node
|
|||
void read(NIFStream *nif)
|
||||
{
|
||||
Node::read(nif);
|
||||
if (nif->getVersion() >= nif->generateVersion(10,1,0,106)
|
||||
&& nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)
|
||||
nif->getBoolean(); // Switch state
|
||||
unsigned int numAffectedNodes = nif->getUInt();
|
||||
for (unsigned int i=0; i<numAffectedNodes; ++i)
|
||||
nif->getUInt(); // ref to another Node
|
||||
|
|
|
@ -139,15 +139,77 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
|||
// 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));
|
||||
|
||||
// NIF data endianness
|
||||
if (ver >= NIFStream::generateVersion(20,0,0,4))
|
||||
{
|
||||
unsigned char endianness = nif.getChar();
|
||||
if (endianness == 0)
|
||||
fail("Big endian NIF files are unsupported");
|
||||
}
|
||||
|
||||
// User version
|
||||
if (ver > NIFStream::generateVersion(10,0,1,8))
|
||||
userVer = nif.getUInt();
|
||||
|
||||
// Number of records
|
||||
size_t recNum = nif.getUInt();
|
||||
records.resize(recNum);
|
||||
|
||||
// Bethesda stream header
|
||||
// It contains Bethesda format version and (useless) export information
|
||||
if (ver == VER_OB_OLD ||
|
||||
(userVer >= 3 && ((ver == VER_OB || ver == VER_BGS)
|
||||
|| (ver >= NIFStream::generateVersion(10,1,0,0) && ver <= NIFStream::generateVersion(20,0,0,4) && userVer <= 11))))
|
||||
{
|
||||
bethVer = nif.getUInt();
|
||||
nif.getExportString(); // Author
|
||||
if (bethVer > BETHVER_FO4)
|
||||
nif.getUInt(); // Unknown
|
||||
nif.getExportString(); // Process script
|
||||
nif.getExportString(); // Export script
|
||||
if (bethVer == BETHVER_FO4)
|
||||
nif.getExportString(); // Max file path
|
||||
}
|
||||
|
||||
std::vector<std::string> recTypes;
|
||||
std::vector<unsigned short> recTypeIndices;
|
||||
|
||||
const bool hasRecTypeListings = ver >= NIFStream::generateVersion(5,0,0,1);
|
||||
if (hasRecTypeListings)
|
||||
{
|
||||
unsigned short recTypeNum = nif.getUShort();
|
||||
if (recTypeNum) // Record type list
|
||||
nif.getSizedStrings(recTypes, recTypeNum);
|
||||
if (recNum) // Record type mapping for each record
|
||||
nif.getUShorts(recTypeIndices, recNum);
|
||||
if (ver >= NIFStream::generateVersion(5,0,0,6)) // Groups
|
||||
{
|
||||
if (ver >= NIFStream::generateVersion(20,1,0,1)) // String table
|
||||
{
|
||||
if (ver >= NIFStream::generateVersion(20,2,0,5) && recNum) // Record sizes
|
||||
{
|
||||
std::vector<unsigned int> recSizes; // Currently unused
|
||||
nif.getUInts(recSizes, recNum);
|
||||
}
|
||||
unsigned int stringNum = nif.getUInt();
|
||||
nif.getUInt(); // Max string length
|
||||
if (stringNum)
|
||||
nif.getSizedStrings(strings, stringNum);
|
||||
}
|
||||
std::vector<unsigned int> groups; // Currently unused
|
||||
unsigned int groupNum = nif.getUInt();
|
||||
if (groupNum)
|
||||
nif.getUInts(groups, groupNum);
|
||||
}
|
||||
}
|
||||
|
||||
const bool hasRecordSeparators = ver >= NIFStream::generateVersion(10,0,0,0) && ver < NIFStream::generateVersion(10,2,0,0);
|
||||
for(size_t i = 0;i < recNum;i++)
|
||||
{
|
||||
Record *r = nullptr;
|
||||
|
||||
std::string rec = nif.getString();
|
||||
std::string rec = hasRecTypeListings ? recTypes[recTypeIndices[i]] : nif.getString();
|
||||
if(rec.empty())
|
||||
{
|
||||
std::stringstream error;
|
||||
|
@ -155,6 +217,17 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
|||
fail(error.str());
|
||||
}
|
||||
|
||||
// Record separator. Some Havok records in Oblivion do not have it.
|
||||
if (hasRecordSeparators && rec.compare(0, 3, "bhk"))
|
||||
{
|
||||
if (nif.getInt())
|
||||
{
|
||||
std::stringstream warning;
|
||||
warning << "Record number " << i << " out of " << recNum << " is preceded by a non-zero separator.";
|
||||
warn(warning.str());
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string,RecordFactoryEntry>::const_iterator entry = factories.find(rec);
|
||||
|
||||
if (entry != factories.end())
|
||||
|
|
|
@ -25,18 +25,19 @@ namespace Nif
|
|||
return t;
|
||||
}
|
||||
|
||||
///Currently specific for 4.0.0.2 and earlier
|
||||
///Booleans in 4.0.0.2 (Morrowind format) and earlier are 4 byte, while in 4.1.0.0+ they're 1 byte.
|
||||
bool NIFStream::getBoolean()
|
||||
{
|
||||
return getInt() != 0;
|
||||
return getVersion() < generateVersion(4,1,0,0) ? getInt() != 0 : getChar() != 0;
|
||||
}
|
||||
|
||||
///Read in a string, either from the string table using the index (currently absent) or from the stream using the specified length
|
||||
///Read in a string, either from the string table using the index or from the stream using the specified length
|
||||
std::string NIFStream::getString()
|
||||
{
|
||||
return getSizedString();
|
||||
return getVersion() < generateVersion(20,1,0,1) ? getSizedString() : file->getString(getUInt());
|
||||
}
|
||||
|
||||
|
||||
// Convenience utility functions: get the versions of the currently read file
|
||||
unsigned int NIFStream::getVersion() const { return file->getVersion(); }
|
||||
unsigned int NIFStream::getUserVersion() const { return file->getBethVersion(); }
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
PropertyList props;
|
||||
|
||||
// Bounding box info
|
||||
bool hasBounds;
|
||||
bool hasBounds{false};
|
||||
osg::Vec3f boundPos;
|
||||
Matrix3 boundRot;
|
||||
osg::Vec3f boundXYZ; // Box size
|
||||
|
@ -39,12 +39,15 @@ public:
|
|||
{
|
||||
Named::read(nif);
|
||||
|
||||
flags = nif->getUShort();
|
||||
flags = nif->getBethVersion() <= 26 ? nif->getUShort() : nif->getUInt();
|
||||
trafo = nif->getTrafo();
|
||||
velocity = nif->getVector3();
|
||||
props.read(nif);
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||
velocity = nif->getVector3();
|
||||
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
|
||||
props.read(nif);
|
||||
|
||||
hasBounds = nif->getBoolean();
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||
hasBounds = nif->getBoolean();
|
||||
if(hasBounds)
|
||||
{
|
||||
nif->getInt(); // always 1
|
||||
|
@ -52,6 +55,9 @@ public:
|
|||
boundRot = nif->getMatrix3();
|
||||
boundXYZ = nif->getVector3();
|
||||
}
|
||||
// Reference to the collision object in Gamebryo files.
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||
nif->skip(4);
|
||||
|
||||
parent = nullptr;
|
||||
|
||||
|
@ -102,7 +108,8 @@ struct NiNode : Node
|
|||
{
|
||||
Node::read(nif);
|
||||
children.read(nif);
|
||||
effects.read(nif);
|
||||
if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)
|
||||
effects.read(nif);
|
||||
|
||||
// Discard transformations for the root node, otherwise some meshes
|
||||
// occasionally get wrong orientation. Only for NiNode-s for now, but
|
||||
|
@ -130,7 +137,39 @@ struct NiNode : Node
|
|||
|
||||
struct NiGeometry : Node
|
||||
{
|
||||
struct MaterialData
|
||||
{
|
||||
std::vector<std::string> materialNames;
|
||||
std::vector<int> materialExtraData;
|
||||
unsigned int activeMaterial{0};
|
||||
bool materialNeedsUpdate{false};
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))
|
||||
return;
|
||||
unsigned int numMaterials = 0;
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(20,1,0,3))
|
||||
numMaterials = nif->getBoolean(); // Has Shader
|
||||
else if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5))
|
||||
numMaterials = nif->getUInt();
|
||||
if (numMaterials)
|
||||
{
|
||||
nif->getStrings(materialNames, numMaterials);
|
||||
nif->getInts(materialExtraData, numMaterials);
|
||||
}
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5))
|
||||
activeMaterial = nif->getUInt();
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
|
||||
{
|
||||
materialNeedsUpdate = nif->getBoolean();
|
||||
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
|
||||
nif->skip(8);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NiSkinInstancePtr skin;
|
||||
MaterialData materialData;
|
||||
};
|
||||
|
||||
struct NiTriShape : NiGeometry
|
||||
|
@ -149,6 +188,7 @@ struct NiTriShape : NiGeometry
|
|||
Node::read(nif);
|
||||
data.read(nif);
|
||||
skin.read(nif);
|
||||
materialData.read(nif);
|
||||
}
|
||||
|
||||
void post(NIFFile *nif)
|
||||
|
@ -170,6 +210,7 @@ struct NiTriStrips : NiGeometry
|
|||
Node::read(nif);
|
||||
data.read(nif);
|
||||
skin.read(nif);
|
||||
materialData.read(nif);
|
||||
}
|
||||
|
||||
void post(NIFFile *nif)
|
||||
|
@ -207,6 +248,8 @@ struct NiCamera : Node
|
|||
{
|
||||
struct Camera
|
||||
{
|
||||
unsigned short cameraFlags{0};
|
||||
|
||||
// Camera frustrum
|
||||
float left, right, top, bottom, nearDist, farDist;
|
||||
|
||||
|
@ -216,15 +259,21 @@ struct NiCamera : Node
|
|||
// Level of detail modifier
|
||||
float LOD;
|
||||
|
||||
// Orthographic projection usage flag
|
||||
bool orthographic{false};
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
cameraFlags = nif->getUShort();
|
||||
left = nif->getFloat();
|
||||
right = nif->getFloat();
|
||||
top = nif->getFloat();
|
||||
bottom = nif->getFloat();
|
||||
nearDist = nif->getFloat();
|
||||
farDist = nif->getFloat();
|
||||
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
orthographic = nif->getBoolean();
|
||||
vleft = nif->getFloat();
|
||||
vright = nif->getFloat();
|
||||
vtop = nif->getFloat();
|
||||
|
@ -243,6 +292,8 @@ struct NiCamera : Node
|
|||
|
||||
nif->getInt(); // -1
|
||||
nif->getInt(); // 0
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(4,2,1,0))
|
||||
nif->getInt(); // 0
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -285,11 +336,14 @@ struct NiRotatingParticles : Node
|
|||
// A node used as the base to switch between child nodes, such as for LOD levels.
|
||||
struct NiSwitchNode : public NiNode
|
||||
{
|
||||
unsigned int switchFlags{0};
|
||||
unsigned int initialIndex;
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
NiNode::read(nif);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
switchFlags = nif->getUShort();
|
||||
initialIndex = nif->getUInt();
|
||||
}
|
||||
};
|
||||
|
@ -310,6 +364,12 @@ struct NiLODNode : public NiSwitchNode
|
|||
NiSwitchNode::read(nif);
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))
|
||||
lodCenter = nif->getVector3();
|
||||
else if (nif->getVersion() > NIFStream::generateVersion(10,0,1,0))
|
||||
{
|
||||
nif->skip(4); // NiLODData, unsupported at the moment
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int numLodLevels = nif->getUInt();
|
||||
for (unsigned int i=0; i<numLodLevels; ++i)
|
||||
{
|
||||
|
|
|
@ -6,25 +6,44 @@
|
|||
namespace Nif
|
||||
{
|
||||
|
||||
void Property::read(NIFStream *nif)
|
||||
{
|
||||
Named::read(nif);
|
||||
flags = nif->getUShort();
|
||||
}
|
||||
|
||||
void NiTexturingProperty::Texture::read(NIFStream *nif)
|
||||
{
|
||||
inUse = nif->getBoolean();
|
||||
if(!inUse) return;
|
||||
|
||||
texture.read(nif);
|
||||
clamp = nif->getUInt();
|
||||
nif->skip(4); // Filter mode. Ignoring because global filtering settings are more sensible
|
||||
uvSet = nif->getUInt();
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||
{
|
||||
clamp = nif->getInt();
|
||||
nif->skip(4); // Filter mode. Ignoring because global filtering settings are more sensible
|
||||
}
|
||||
else
|
||||
{
|
||||
clamp = nif->getUShort() & 0xF;
|
||||
}
|
||||
// Max anisotropy. I assume we'll always only use the global anisotropy setting.
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20,5,0,4))
|
||||
nif->getUShort();
|
||||
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||
uvSet = nif->getUInt();
|
||||
|
||||
// Two PS2-specific shorts.
|
||||
nif->skip(4);
|
||||
nif->skip(2); // Unknown short
|
||||
if (nif->getVersion() < NIFStream::generateVersion(10,4,0,2))
|
||||
nif->skip(4);
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,1,0,18))
|
||||
nif->skip(2); // Unknown short
|
||||
else if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
||||
{
|
||||
if (nif->getBoolean()) // Has texture transform
|
||||
{
|
||||
nif->getVector2(); // UV translation
|
||||
nif->getVector2(); // UV scale
|
||||
nif->getFloat(); // W axis rotation
|
||||
nif->getUInt(); // Transform method
|
||||
nif->getVector2(); // Texture rotation origin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NiTexturingProperty::Texture::post(NIFFile *nif)
|
||||
|
@ -35,7 +54,10 @@ void NiTexturingProperty::Texture::post(NIFFile *nif)
|
|||
void NiTexturingProperty::read(NIFStream *nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
apply = nif->getUInt();
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD || nif->getVersion() >= NIFStream::generateVersion(20,1,0,2))
|
||||
flags = nif->getUShort();
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(20,1,0,1))
|
||||
apply = nif->getUInt();
|
||||
|
||||
unsigned int numTextures = nif->getUInt();
|
||||
|
||||
|
@ -51,32 +73,53 @@ void NiTexturingProperty::read(NIFStream *nif)
|
|||
envMapLumaBias = nif->getVector2();
|
||||
bumpMapMatrix = nif->getVector4();
|
||||
}
|
||||
else if (i == 7 && textures[7].inUse && nif->getVersion() >= NIFStream::generateVersion(20,2,0,5))
|
||||
/*float parallaxOffset = */nif->getFloat();
|
||||
}
|
||||
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||
{
|
||||
unsigned int numShaderTextures = nif->getUInt();
|
||||
shaderTextures.resize(numShaderTextures);
|
||||
for (unsigned int i = 0; i < numShaderTextures; i++)
|
||||
{
|
||||
shaderTextures[i].read(nif);
|
||||
if (shaderTextures[i].inUse)
|
||||
nif->getUInt(); // Unique identifier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NiTexturingProperty::post(NIFFile *nif)
|
||||
{
|
||||
Property::post(nif);
|
||||
for(int i = 0;i < 7;i++)
|
||||
for (size_t i = 0; i < textures.size(); i++)
|
||||
textures[i].post(nif);
|
||||
for (size_t i = 0; i < shaderTextures.size(); i++)
|
||||
shaderTextures[i].post(nif);
|
||||
}
|
||||
|
||||
void NiFogProperty::read(NIFStream *nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
|
||||
mFlags = nif->getUShort();
|
||||
mFogDepth = nif->getFloat();
|
||||
mColour = nif->getVector3();
|
||||
}
|
||||
|
||||
void S_MaterialProperty::read(NIFStream *nif)
|
||||
{
|
||||
ambient = nif->getVector3();
|
||||
diffuse = nif->getVector3();
|
||||
if (nif->getBethVersion() < 26)
|
||||
{
|
||||
ambient = nif->getVector3();
|
||||
diffuse = nif->getVector3();
|
||||
}
|
||||
specular = nif->getVector3();
|
||||
emissive = nif->getVector3();
|
||||
glossiness = nif->getFloat();
|
||||
alpha = nif->getFloat();
|
||||
if (nif->getBethVersion() > 21)
|
||||
emissive *= nif->getFloat();
|
||||
}
|
||||
|
||||
void S_VertexColorProperty::read(NIFStream *nif)
|
||||
|
@ -92,14 +135,29 @@ void S_AlphaProperty::read(NIFStream *nif)
|
|||
|
||||
void S_StencilProperty::read(NIFStream *nif)
|
||||
{
|
||||
enabled = nif->getChar();
|
||||
compareFunc = nif->getInt();
|
||||
stencilRef = nif->getUInt();
|
||||
stencilMask = nif->getUInt();
|
||||
failAction = nif->getInt();
|
||||
zFailAction = nif->getInt();
|
||||
zPassAction = nif->getInt();
|
||||
drawMode = nif->getInt();
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||
{
|
||||
enabled = nif->getChar();
|
||||
compareFunc = nif->getInt();
|
||||
stencilRef = nif->getUInt();
|
||||
stencilMask = nif->getUInt();
|
||||
failAction = nif->getInt();
|
||||
zFailAction = nif->getInt();
|
||||
zPassAction = nif->getInt();
|
||||
drawMode = nif->getInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned short flags = nif->getUShort();
|
||||
enabled = flags & 0x1;
|
||||
failAction = (flags >> 1) & 0x7;
|
||||
zFailAction = (flags >> 4) & 0x7;
|
||||
zPassAction = (flags >> 7) & 0x7;
|
||||
drawMode = (flags >> 10) & 0x3;
|
||||
compareFunc = (flags >> 12) & 0x7;
|
||||
stencilRef = nif->getUInt();
|
||||
stencilMask = nif->getUInt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -29,18 +29,13 @@
|
|||
namespace Nif
|
||||
{
|
||||
|
||||
class Property : public Named
|
||||
{
|
||||
public:
|
||||
// The meaning of these depends on the actual property type.
|
||||
unsigned int flags;
|
||||
|
||||
void read(NIFStream *nif);
|
||||
};
|
||||
class Property : public Named { };
|
||||
|
||||
class NiTexturingProperty : public Property
|
||||
{
|
||||
public:
|
||||
unsigned short flags{0u};
|
||||
|
||||
// A sub-texture
|
||||
struct Texture
|
||||
{
|
||||
|
@ -92,6 +87,7 @@ public:
|
|||
};
|
||||
|
||||
std::vector<Texture> textures;
|
||||
std::vector<Texture> shaderTextures;
|
||||
|
||||
osg::Vec2f envMapLumaBias;
|
||||
osg::Vec4f bumpMapMatrix;
|
||||
|
@ -103,28 +99,81 @@ public:
|
|||
class NiFogProperty : public Property
|
||||
{
|
||||
public:
|
||||
unsigned short mFlags;
|
||||
float mFogDepth;
|
||||
osg::Vec3f mColour;
|
||||
|
||||
void read(NIFStream *nif);
|
||||
};
|
||||
|
||||
// These contain no other data than the 'flags' field in Property
|
||||
class NiShadeProperty : public Property { };
|
||||
class NiDitherProperty : public Property { };
|
||||
class NiZBufferProperty : public Property { };
|
||||
class NiSpecularProperty : public Property { };
|
||||
class NiWireframeProperty : public Property { };
|
||||
// These contain no other data than the 'flags' field
|
||||
struct NiShadeProperty : public Property
|
||||
{
|
||||
unsigned short flags{0u};
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
|
||||
flags = nif->getUShort();
|
||||
}
|
||||
};
|
||||
|
||||
struct NiDitherProperty : public Property
|
||||
{
|
||||
unsigned short flags;
|
||||
void read(NIFStream* nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
flags = nif->getUShort();
|
||||
}
|
||||
};
|
||||
|
||||
struct NiZBufferProperty : public Property
|
||||
{
|
||||
unsigned short flags;
|
||||
unsigned int testFunction;
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
flags = nif->getUShort();
|
||||
testFunction = (flags >> 2) & 0x7;
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(4,1,0,12) && nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||
testFunction = nif->getUInt();
|
||||
}
|
||||
};
|
||||
|
||||
struct NiSpecularProperty : public Property
|
||||
{
|
||||
unsigned short flags;
|
||||
void read(NIFStream* nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
flags = nif->getUShort();
|
||||
}
|
||||
};
|
||||
|
||||
struct NiWireframeProperty : public Property
|
||||
{
|
||||
unsigned short flags;
|
||||
void read(NIFStream* nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
flags = nif->getUShort();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// The rest are all struct-based
|
||||
template <typename T>
|
||||
struct StructPropT : Property
|
||||
{
|
||||
T data;
|
||||
unsigned short flags;
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
flags = nif->getUShort();
|
||||
data.read(nif);
|
||||
}
|
||||
};
|
||||
|
@ -132,7 +181,8 @@ struct StructPropT : Property
|
|||
struct S_MaterialProperty
|
||||
{
|
||||
// The vector components are R,G,B
|
||||
osg::Vec3f ambient, diffuse, specular, emissive;
|
||||
osg::Vec3f ambient{1.f,1.f,1.f}, diffuse{1.f,1.f,1.f};
|
||||
osg::Vec3f specular, emissive;
|
||||
float glossiness, alpha;
|
||||
|
||||
void read(NIFStream *nif);
|
||||
|
@ -246,9 +296,35 @@ struct S_StencilProperty
|
|||
};
|
||||
|
||||
class NiAlphaProperty : public StructPropT<S_AlphaProperty> { };
|
||||
class NiMaterialProperty : public StructPropT<S_MaterialProperty> { };
|
||||
class NiVertexColorProperty : public StructPropT<S_VertexColorProperty> { };
|
||||
class NiStencilProperty : public StructPropT<S_StencilProperty> { };
|
||||
struct NiStencilProperty : public Property
|
||||
{
|
||||
S_StencilProperty data;
|
||||
unsigned short flags{0u};
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
|
||||
flags = nif->getUShort();
|
||||
data.read(nif);
|
||||
}
|
||||
};
|
||||
|
||||
struct NiMaterialProperty : public Property
|
||||
{
|
||||
S_MaterialProperty data;
|
||||
unsigned short flags{0u};
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
Property::read(nif);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(3,0,0,0)
|
||||
&& nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
|
||||
flags = nif->getUShort();
|
||||
data.read(nif);
|
||||
}
|
||||
};
|
||||
|
||||
} // Namespace
|
||||
#endif
|
||||
|
|
|
@ -1732,7 +1732,7 @@ namespace NifOsg
|
|||
osg::StateSet* stateset = node->getOrCreateStateSet();
|
||||
|
||||
// Specular lighting is enabled by default, but there's a quirk...
|
||||
int specFlags = 1;
|
||||
bool specEnabled = true;
|
||||
osg::ref_ptr<osg::Material> mat (new osg::Material);
|
||||
mat->setColorMode(hasVertexColors ? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF);
|
||||
|
||||
|
@ -1751,7 +1751,8 @@ namespace NifOsg
|
|||
case Nif::RC_NiSpecularProperty:
|
||||
{
|
||||
// Specular property can turn specular lighting off.
|
||||
specFlags = property->flags;
|
||||
auto specprop = static_cast<const Nif::NiSpecularProperty*>(property);
|
||||
specEnabled = specprop->flags & 1;
|
||||
break;
|
||||
}
|
||||
case Nif::RC_NiMaterialProperty:
|
||||
|
@ -1835,7 +1836,7 @@ namespace NifOsg
|
|||
}
|
||||
|
||||
// While NetImmerse and Gamebryo support specular lighting, Morrowind has its support disabled.
|
||||
if (mVersion <= Nif::NIFFile::NIFVersion::VER_MW || specFlags == 0)
|
||||
if (mVersion <= Nif::NIFFile::NIFVersion::VER_MW || !specEnabled)
|
||||
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f,0.f,0.f,0.f));
|
||||
|
||||
if (lightmode == 0)
|
||||
|
|
Loading…
Reference in a new issue