Introduce some extended NIF definitions

pull/3011/head
Alexei Dobrohotov 4 years ago
parent 25b966d58d
commit 29ccb09da5

@ -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();
// 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))
{
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;
if(nif->getInt())
bool hasUVs = true;
if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW)
hasUVs = nif->getBoolean();
if (hasUVs)
{
uvlist.resize(uvs);
for(int i = 0;i < uvs;i++)
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…
Cancel
Save