Merge branch 'nifcouriersix' into 'master'

Modernize NIF loader, part 6

See merge request OpenMW/openmw!3435
macos_ci_fix
psi29a 1 year ago
commit 3ae189dda1

@ -38,7 +38,7 @@ bool isNIF(const std::filesystem::path& filename)
/// See if the file has the "bsa" extension.
bool isBSA(const std::filesystem::path& filename)
{
return hasExtension(filename, ".bsa");
return hasExtension(filename, ".bsa") || hasExtension(filename, ".ba2");
}
std::unique_ptr<VFS::Archive> makeBsaArchive(const std::filesystem::path& path)
@ -216,7 +216,7 @@ int main(int argc, char** argv)
else
{
std::cerr << "ERROR: \"" << Files::pathToUnicodeString(path)
<< "\" is not a nif file, bsa file, or directory!" << std::endl;
<< "\" is not a nif file, bsa/ba2 file, or directory!" << std::endl;
}
}
catch (std::exception& e)

@ -187,10 +187,10 @@ osg::Group {
init(node);
Nif::BSShaderPPLightingProperty property;
property.recType = Nif::RC_BSShaderPPLightingProperty;
property.textureSet = nullptr;
property.mTextureSet = nullptr;
property.mController = nullptr;
property.type = GetParam().mShaderType;
node.mProperties.push_back(Nif::RecordPtrT<Nif::Property>(&property));
property.mType = GetParam().mShaderType;
node.mProperties.push_back(Nif::RecordPtrT<Nif::NiProperty>(&property));
Nif::NIFFile file("test.nif");
file.mRoots.push_back(&node);
auto result = Loader::load(file, &mImageManager);
@ -217,8 +217,8 @@ osg::Group {
property.recType = Nif::RC_BSLightingShaderProperty;
property.mTextureSet = nullptr;
property.mController = nullptr;
property.type = GetParam().mShaderType;
node.mProperties.push_back(Nif::RecordPtrT<Nif::Property>(&property));
property.mType = GetParam().mShaderType;
node.mProperties.push_back(Nif::RecordPtrT<Nif::NiProperty>(&property));
Nif::NIFFile file("test.nif");
file.mRoots.push_back(&node);
auto result = Loader::load(file, &mImageManager);

@ -26,27 +26,27 @@ namespace Nif
{
NiDynamicEffect::read(nif);
mDimmer = nif->getFloat();
mAmbient = nif->getVector3();
mDiffuse = nif->getVector3();
mSpecular = nif->getVector3();
nif->read(mDimmer);
nif->read(mAmbient);
nif->read(mDiffuse);
nif->read(mSpecular);
}
void NiPointLight::read(NIFStream* nif)
{
NiLight::read(nif);
mConstantAttenuation = nif->getFloat();
mLinearAttenuation = nif->getFloat();
mQuadraticAttenuation = nif->getFloat();
nif->read(mConstantAttenuation);
nif->read(mLinearAttenuation);
nif->read(mQuadraticAttenuation);
}
void NiSpotLight::read(NIFStream* nif)
{
NiPointLight::read(nif);
mCutoff = nif->getFloat();
mExponent = nif->getFloat();
nif->read(mCutoff);
nif->read(mExponent);
}
void NiTextureEffect::read(NIFStream* nif)

@ -146,16 +146,6 @@ namespace Nif
/// Read a sequence of null-terminated strings
std::string getStringPalette();
/// DEPRECATED: Use read() or get()
char getChar() { return get<char>(); }
unsigned short getUShort() { return get<unsigned short>(); }
int getInt() { return get<int>(); }
unsigned int getUInt() { return get<unsigned int>(); }
float getFloat() { return get<float>(); }
osg::Vec2f getVector2() { return get<osg::Vec2f>(); }
osg::Vec3f getVector3() { return get<osg::Vec3f>(); }
osg::Vec4f getVector4() { return get<osg::Vec4f>(); }
};
template <>

@ -433,60 +433,63 @@ namespace Nif
void BSVertexData::read(NIFStream* nif, uint16_t flags)
{
uint16_t vertexFlag = flags & BSVertexDesc::VertexAttribute::Vertex;
uint16_t tangentsFlag = flags & BSVertexDesc::VertexAttribute::Tangents;
uint16_t UVsFlag = flags & BSVertexDesc::VertexAttribute::UVs;
uint16_t normalsFlag = flags & BSVertexDesc::VertexAttribute::Normals;
if (vertexFlag == BSVertexDesc::VertexAttribute::Vertex)
{
nif->read(mVertex);
}
if ((vertexFlag | tangentsFlag)
== (BSVertexDesc::VertexAttribute::Vertex | BSVertexDesc::VertexAttribute::Tangents))
bool fullPrecision = true;
if (nif->getBethVersion() != NIFFile::BethVersion::BETHVER_SSE)
fullPrecision = flags & BSVertexDesc::VertexAttribute::Full_Precision;
bool hasVertex = flags & BSVertexDesc::VertexAttribute::Vertex;
bool hasTangent = flags & BSVertexDesc::VertexAttribute::Tangents;
bool hasUV = flags & BSVertexDesc::VertexAttribute::UVs;
bool hasNormal = flags & BSVertexDesc::VertexAttribute::Normals;
bool hasVertexColor = flags & BSVertexDesc::VertexAttribute::Vertex_Colors;
bool hasSkinData = flags & BSVertexDesc::VertexAttribute::Skinned;
bool hasEyeData = flags & BSVertexDesc::VertexAttribute::Eye_Data;
if (hasVertex)
{
nif->read(mBitangentX);
}
if ((vertexFlag | tangentsFlag) == BSVertexDesc::VertexAttribute::Vertex)
{
nif->read(mUnusedW);
if (fullPrecision)
{
nif->read(mVertex);
if (hasTangent)
nif->read(mBitangentX);
else
nif->skip(4); // Unused
}
else
{
nif->readArray(mHalfVertex);
if (hasTangent)
nif->read(mHalfBitangentX);
else
nif->skip(2); // Unused
}
}
if (UVsFlag == BSVertexDesc::VertexAttribute::UVs)
{
if (hasUV)
nif->readArray(mUV);
}
if (normalsFlag)
if (hasNormal)
{
nif->readArray(mNormal);
nif->read(mBitangentY);
if (hasTangent)
{
nif->readArray(mTangent);
nif->read(mBitangentZ);
}
}
if ((normalsFlag | tangentsFlag)
== (BSVertexDesc::VertexAttribute::Normals | BSVertexDesc::VertexAttribute::Tangents))
{
nif->readArray(mTangent);
nif->read(mBitangentZ);
}
if (flags & BSVertexDesc::VertexAttribute::Vertex_Colors)
{
nif->readArray(mVertColors);
}
if (hasVertexColor)
nif->readArray(mVertColor);
if (flags & BSVertexDesc::VertexAttribute::Skinned)
if (hasSkinData)
{
nif->readArray(mBoneWeights);
nif->readArray(mBoneIndices);
}
if (flags & BSVertexDesc::VertexAttribute::Eye_Data)
{
if (hasEyeData)
nif->read(mEyeData);
}
}
void BSValueNode::read(NIFStream* nif)

@ -82,7 +82,7 @@ namespace Nif
uint32_t mFlags;
NiTransform mTransform;
osg::Vec3f mVelocity;
PropertyList mProperties;
NiPropertyList mProperties;
BoundingVolume mBounds;
NiCollisionObjectPtr mCollision;
// Parent nodes for the node. Only types derived from NiNode can be parents.
@ -339,14 +339,15 @@ namespace Nif
struct BSVertexData
{
osg::Vec3f mVertex;
std::array<Misc::float16_t, 3> mHalfVertex;
float mBitangentX;
uint32_t mUnusedW;
Misc::float16_t mHalfBitangentX;
std::array<Misc::float16_t, 2> mUV;
std::array<char, 3> mNormal;
char mBitangentY;
std::array<char, 3> mTangent;
char mBitangentZ;
std::array<char, 4> mVertColors;
std::array<char, 4> mVertColor;
std::array<Misc::float16_t, 4> mBoneWeights;
std::array<char, 4> mBoneIndices;
float mEyeData;

@ -6,192 +6,368 @@
namespace Nif
{
void NiTextureTransform::read(NIFStream* nif)
{
nif->read(mOffset);
nif->read(mScale);
nif->read(mRotation);
mTransformMethod = static_cast<Method>(nif->get<uint32_t>());
nif->read(mOrigin);
}
void NiTexturingProperty::Texture::read(NIFStream* nif)
{
nif->read(inUse);
if (!inUse)
nif->read(mEnabled);
if (!mEnabled)
return;
texture.read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
mSourceTexture.read(nif);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
{
clamp = nif->getInt();
nif->skip(4); // Filter mode. Ignoring because global filtering settings are more sensible
nif->read(mClamp);
nif->read(mFilter);
}
else
{
clamp = nif->getUShort() & 0xF;
uint16_t flags;
nif->read(flags);
mClamp = flags & 0xF;
mFilter = (flags >> 4) & 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();
nif->read(mMaxAnisotropy);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
uvSet = nif->getUInt();
nif->read(mUVSet);
// Two PS2-specific shorts.
if (nif->getVersion() < NIFStream::generateVersion(10, 4, 0, 2))
// PS2 filtering settings
if (nif->getVersion() <= NIFStream::generateVersion(10, 4, 0, 1))
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->getVersion() <= NIFStream::generateVersion(4, 1, 0, 12))
nif->skip(2); // Unknown
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
{
if (nif->get<bool>()) // 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
}
nif->read(mHasTransform);
if (mHasTransform)
mTransform.read(nif);
}
}
void NiTexturingProperty::Texture::post(Reader& nif)
{
texture.post(nif);
mSourceTexture.post(nif);
}
void NiTexturingProperty::read(NIFStream* nif)
{
Property::read(nif);
NiProperty::read(nif);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD
|| nif->getVersion() >= NIFStream::generateVersion(20, 1, 0, 2))
flags = nif->getUShort();
nif->read(mFlags);
if (nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 1))
apply = nif->getUInt();
unsigned int numTextures = nif->getUInt();
mApplyMode = static_cast<ApplyMode>(nif->get<uint32_t>());
if (!numTextures)
return;
textures.resize(numTextures);
for (unsigned int i = 0; i < numTextures; i++)
mTextures.resize(nif->get<uint32_t>());
for (size_t i = 0; i < mTextures.size(); i++)
{
textures[i].read(nif);
if (i == 5 && textures[5].inUse) // Bump map settings
mTextures[i].read(nif);
if (i == 5 && mTextures[5].mEnabled)
{
envMapLumaBias = nif->getVector2();
bumpMapMatrix = nif->getVector4();
nif->read(mEnvMapLumaBias);
nif->read(mBumpMapMatrix);
}
else if (i == 7 && textures[7].inUse && nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
/*float parallaxOffset = */ nif->getFloat();
else if (i == 7 && mTextures[7].mEnabled && nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
nif->read(mParallaxOffset);
}
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++)
mShaderTextures.resize(nif->get<uint32_t>());
mShaderIds.resize(mShaderTextures.size());
for (size_t i = 0; i < mShaderTextures.size(); i++)
{
shaderTextures[i].read(nif);
if (shaderTextures[i].inUse)
nif->getUInt(); // Unique identifier
mShaderTextures[i].read(nif);
if (mShaderTextures[i].mEnabled)
nif->read(mShaderIds[i]);
}
}
}
void NiTexturingProperty::post(Reader& nif)
{
Property::post(nif);
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);
NiProperty::post(nif);
for (Texture& tex : mTextures)
tex.post(nif);
for (Texture& tex : mShaderTextures)
tex.post(nif);
}
void BSSPParallaxParams::read(NIFStream* nif)
{
nif->read(mMaxPasses);
nif->read(mScale);
}
void BSSPRefractionParams::read(NIFStream* nif)
{
nif->read(mStrength);
nif->read(mPeriod);
}
void BSShaderProperty::read(NIFStream* nif)
{
if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_F76 && recType == RC_BSLightingShaderProperty)
nif->read(mType);
NiShadeProperty::read(nif);
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
if (nif->getUserVersion() <= 11)
{
type = nif->getUInt();
flags1 = nif->getUInt();
flags2 = nif->getUInt();
envMapIntensity = nif->getFloat();
nif->read(mType);
nif->read(mShaderFlags1);
nif->read(mShaderFlags2);
nif->read(mEnvMapScale);
return;
}
if (!mName.empty() && nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76)
return;
if (nif->getBethVersion() <= 131)
{
nif->read(mShaderFlags1);
nif->read(mShaderFlags2);
}
else
{
uint32_t numShaderFlags1 = 0, numShaderFlags2 = 0;
nif->read(numShaderFlags1);
if (nif->getBethVersion() >= 152)
nif->read(numShaderFlags2);
nif->readVector(mShaderFlags1Hashes, numShaderFlags1);
nif->readVector(mShaderFlags2Hashes, numShaderFlags2);
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76 && recType == RC_BSLightingShaderProperty)
{
nif->read(mType);
// Remap FO76+ shader types to FO4 system so that we can actually use them
// TODO: NifTools spec doesn't do anything about the misplaced EyeEnvmap. Bug or feature?
switch (mType)
{
case 3:
mType = static_cast<uint32_t>(BSLightingShaderType::ShaderType_FaceTint);
break;
case 4:
mType = static_cast<uint32_t>(BSLightingShaderType::ShaderType_SkinTint);
break;
case 5:
mType = static_cast<uint32_t>(BSLightingShaderType::ShaderType_HairTint);
break;
case 12:
mType = static_cast<uint32_t>(BSLightingShaderType::ShaderType_EyeEnvmap);
break;
case 17:
mType = static_cast<uint32_t>(BSLightingShaderType::ShaderType_Terrain);
break;
default:
break;
}
}
}
nif->read(mUVOffset);
nif->read(mUVScale);
}
void BSShaderLightingProperty::read(NIFStream* nif)
{
BSShaderProperty::read(nif);
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
clamp = nif->getUInt();
if (nif->getUserVersion() <= 11)
nif->read(mClamp);
}
void BSShaderPPLightingProperty::read(NIFStream* nif)
{
BSShaderLightingProperty::read(nif);
textureSet.read(nif);
if (nif->getBethVersion() <= 14)
return;
refraction.strength = nif->getFloat();
refraction.period = nif->getInt();
if (nif->getBethVersion() <= 24)
return;
parallax.passes = nif->getFloat();
parallax.scale = nif->getFloat();
mTextureSet.read(nif);
if (nif->getUserVersion() == 11)
{
if (nif->getBethVersion() >= 15)
mRefraction.read(nif);
if (nif->getBethVersion() >= 25)
mParallax.read(nif);
}
else if (nif->getUserVersion() >= 12)
nif->read(mEmissiveColor);
}
void BSShaderPPLightingProperty::post(Reader& nif)
{
BSShaderLightingProperty::post(nif);
textureSet.post(nif);
mTextureSet.post(nif);
}
void BSShaderNoLightingProperty::read(NIFStream* nif)
{
BSShaderLightingProperty::read(nif);
filename = nif->getSizedString();
mFilename = nif->getSizedString();
if (nif->getBethVersion() >= 27)
falloffParams = nif->getVector4();
nif->read(mFalloffParams);
}
void BSSPLuminanceParams::read(NIFStream* nif)
{
nif->read(mLumEmittance);
nif->read(mExposureOffset);
nif->read(mFinalExposureMin);
nif->read(mFinalExposureMax);
}
void BSSPWetnessParams::read(NIFStream* nif)
{
nif->read(mSpecScale);
nif->read(mSpecPower);
nif->read(mMinVar);
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_FO4)
nif->read(mEnvMapScale);
nif->read(mFresnelPower);
nif->read(mMetalness);
if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO4)
nif->skip(4); // Unknown
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76)
nif->skip(4); // Unknown
}
void BSSPMLParallaxParams::read(NIFStream* nif)
{
nif->read(mInnerLayerThickness);
nif->read(mRefractionScale);
nif->read(mInnerLayerTextureScale);
nif->read(mEnvMapScale);
}
void BSSPTranslucencyParams::read(NIFStream* nif)
{
nif->read(mSubsurfaceColor);
nif->read(mTransmissiveScale);
nif->read(mTurbulence);
nif->read(mThickObject);
nif->read(mMixAlbedo);
}
void BSLightingShaderProperty::read(NIFStream* nif)
{
type = nif->getUInt();
BSShaderProperty::read(nif);
flags1 = nif->getUInt();
flags2 = nif->getUInt();
nif->skip(8); // UV offset
nif->skip(8); // UV scale
if (!mName.empty() && nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76)
return;
mTextureSet.read(nif);
mEmissive = nif->getVector3();
mEmissiveMult = nif->getFloat();
mClamp = nif->getUInt();
mAlpha = nif->getFloat();
nif->getFloat(); // Refraction strength
mGlossiness = nif->getFloat();
mSpecular = nif->getVector3();
mSpecStrength = nif->getFloat();
nif->skip(8); // Lighting effects
switch (static_cast<BSLightingShaderType>(type))
nif->read(mEmissive);
nif->read(mEmissiveMult);
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4)
nif->read(mRootMaterial);
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_STF)
nif->skip(4); // Unknown float
nif->read(mClamp);
nif->read(mAlpha);
nif->read(mRefractionStrength);
if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)
nif->read(mGlossiness);
else
nif->read(mSmoothness);
nif->read(mSpecular);
nif->read(mSpecStrength);
if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)
nif->readArray(mLightingEffects);
else if (nif->getBethVersion() <= 139)
{
nif->read(mSubsurfaceRolloff);
nif->read(mRimlightPower);
if (mRimlightPower == std::numeric_limits<float>::max())
nif->read(mBacklightPower);
}
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4)
{
nif->read(mGrayscaleToPaletteScale);
nif->read(mFresnelPower);
mWetness.read(nif);
}
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_STF)
mLuminance.read(nif);
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76)
{
nif->read(mDoTranslucency);
if (mDoTranslucency)
mTranslucency.read(nif);
if (nif->get<uint8_t>() != 0)
{
mTextureArrays.resize(nif->get<uint32_t>());
for (std::vector<std::string>& textureArray : mTextureArrays)
nif->getSizedStrings(textureArray, nif->get<uint32_t>());
}
}
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_STF)
{
nif->skip(4); // Unknown
nif->skip(4); // Unknown
nif->skip(2); // Unknown
}
switch (static_cast<BSLightingShaderType>(mType))
{
case BSLightingShaderType::ShaderType_EnvMap:
nif->skip(4); // Environment map scale
if (nif->getBethVersion() <= 139)
nif->read(mEnvMapScale);
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4)
{
nif->read(mUseSSR);
nif->read(mWetnessUseSSR);
}
break;
case BSLightingShaderType::ShaderType_SkinTint:
nif->read(mSkinTintColor);
if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO4)
nif->read(mSkinTintAlpha);
break;
case BSLightingShaderType::ShaderType_HairTint:
nif->skip(12); // Tint color
nif->read(mHairTintColor);
break;
case BSLightingShaderType::ShaderType_ParallaxOcc:
nif->skip(4); // Max passes
nif->skip(4); // Scale
mParallax.read(nif);
break;
case BSLightingShaderType::ShaderType_MultiLayerParallax:
nif->skip(4); // Inner layer thickness
nif->skip(4); // Refraction scale
nif->skip(8); // Inner layer texture scale
nif->skip(4); // Environment map strength
mMultiLayerParallax.read(nif);
break;
case BSLightingShaderType::ShaderType_SparkleSnow:
nif->skip(16); // Sparkle parameters
nif->read(mSparkle);
break;
case BSLightingShaderType::ShaderType_EyeEnvmap:
nif->skip(4); // Cube map scale
nif->skip(12); // Left eye cube map offset
nif->skip(12); // Right eye cube map offset
nif->read(mCubeMapScale);
nif->read(mLeftEyeReflectionCenter);
nif->read(mRightEyeReflectionCenter);
break;
default:
break;
@ -201,97 +377,185 @@ namespace Nif
void BSLightingShaderProperty::post(Reader& nif)
{
BSShaderProperty::post(nif);
mTextureSet.post(nif);
}
void BSEffectShaderProperty::read(NIFStream* nif)
{
BSShaderProperty::read(nif);
flags1 = nif->getUInt();
flags2 = nif->getUInt();
mUVOffset = nif->getVector2();
mUVScale = nif->getVector2();
if (!mName.empty() && nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76)
return;
mSourceTexture = nif->getSizedString();
unsigned int miscParams = nif->getUInt();
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_STF)
nif->skip(4); // Unknown
uint32_t miscParams = nif->get<uint32_t>();
mClamp = miscParams & 0xFF;
mLightingInfluence = (miscParams >> 8) & 0xFF;
mEnvMapMinLOD = (miscParams >> 16) & 0xFF;
mFalloffParams = nif->getVector4();
mBaseColor = nif->getVector4();
mBaseColorScale = nif->getFloat();
mFalloffDepth = nif->getFloat();
nif->read(mFalloffParams);
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76)
nif->read(mRefractionPower);
nif->read(mBaseColor);
nif->read(mBaseColorScale);
nif->read(mFalloffDepth);
mGreyscaleTexture = nif->getSizedString();
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4)
{
mEnvMapTexture = nif->getSizedString();
mNormalTexture = nif->getSizedString();
mEnvMaskTexture = nif->getSizedString();
nif->read(mEnvMapScale);
}
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76)
{
nif->read(mRefractionPower);
mReflectanceTexture = nif->getSizedString();
mLightingTexture = nif->getSizedString();
nif->read(mEmittanceColor);
mEmitGradientTexture = nif->getSizedString();
mLuminance.read(nif);
}
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_STF)
{
nif->skip(7); // Unknown bytes
nif->skip(6 * sizeof(float)); // Unknown floats
nif->skip(1); // Unknown byte
}
}
void NiAlphaProperty::read(NIFStream* nif)
{
NiProperty::read(nif);
nif->read(mFlags);
nif->read(mThreshold);
}
void NiDitherProperty::read(NIFStream* nif)
{
NiProperty::read(nif);
nif->read(mFlags);
}
void NiFogProperty::read(NIFStream* nif)
{
Property::read(nif);
mFlags = nif->getUShort();
mFogDepth = nif->getFloat();
mColour = nif->getVector3();
NiProperty::read(nif);
nif->read(mFlags);
nif->read(mFogDepth);
nif->read(mColour);
}
void S_MaterialProperty::read(NIFStream* nif)
void NiMaterialProperty::read(NIFStream* nif)
{
NiProperty::read(nif);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
nif->read(mFlags);
if (nif->getBethVersion() < 26)
{
ambient = nif->getVector3();
diffuse = nif->getVector3();
nif->read(mAmbient);
nif->read(mDiffuse);
}
specular = nif->getVector3();
emissive = nif->getVector3();
glossiness = nif->getFloat();
alpha = nif->getFloat();
nif->read(mSpecular);
nif->read(mEmissive);
nif->read(mGlossiness);
nif->read(mAlpha);
if (nif->getBethVersion() >= 22)
emissiveMult = nif->getFloat();
nif->read(mEmissiveMult);
}
void NiVertexColorProperty::read(NIFStream* nif)
void NiShadeProperty::read(NIFStream* nif)
{
NiProperty::read(nif);
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
nif->read(mFlags);
}
void NiSpecularProperty::read(NIFStream* nif)
{
NiProperty::read(nif);
mEnable = nif->get<uint16_t>() & 1;
}
void NiStencilProperty::read(NIFStream* nif)
{
Property::read(nif);
mFlags = nif->getUShort();
NiProperty::read(nif);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
{
mVertexMode = static_cast<VertexMode>(nif->getUInt());
mLightingMode = static_cast<LightMode>(nif->getUInt());
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
nif->read(mFlags);
mEnabled = nif->get<uint8_t>() != 0;
mTestFunction = static_cast<TestFunc>(nif->get<uint32_t>());
nif->read(mStencilRef);
nif->read(mStencilMask);
mFailAction = static_cast<Action>(nif->get<uint32_t>());
mZFailAction = static_cast<Action>(nif->get<uint32_t>());
mPassAction = static_cast<Action>(nif->get<uint32_t>());
mDrawMode = static_cast<DrawMode>(nif->get<uint32_t>());
}
else
{
mVertexMode = static_cast<VertexMode>((mFlags >> 4) & 0x3);
mLightingMode = static_cast<LightMode>((mFlags >> 3) & 0x1);
nif->read(mFlags);
mEnabled = mFlags & 0x1;
mFailAction = static_cast<Action>((mFlags >> 1) & 0x7);
mZFailAction = static_cast<Action>((mFlags >> 4) & 0x7);
mPassAction = static_cast<Action>((mFlags >> 7) & 0x7);
mDrawMode = static_cast<DrawMode>((mFlags >> 10) & 0x3);
mTestFunction = static_cast<TestFunc>((mFlags >> 12) & 0x7);
nif->read(mStencilRef);
nif->read(mStencilMask);
}
}
void S_AlphaProperty::read(NIFStream* nif)
void NiVertexColorProperty::read(NIFStream* nif)
{
threshold = nif->getChar();
}
NiProperty::read(nif);
void S_StencilProperty::read(NIFStream* nif)
{
nif->read(mFlags);
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();
mVertexMode = static_cast<VertexMode>(nif->get<uint32_t>());
mLightingMode = static_cast<LightMode>(nif->get<uint32_t>());
}
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();
mVertexMode = static_cast<VertexMode>((mFlags >> 4) & 0x3);
mLightingMode = static_cast<LightMode>((mFlags >> 3) & 0x1);
}
}
void NiWireframeProperty::read(NIFStream* nif)
{
NiProperty::read(nif);
mEnable = nif->get<uint16_t>() & 1;
}
void NiZBufferProperty::read(NIFStream* nif)
{
NiProperty::read(nif);
nif->read(mFlags);
if (nif->getVersion() >= NIFStream::generateVersion(4, 1, 0, 12)
&& nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
nif->read(mTestFunction);
else
mTestFunction = (mFlags >> 2) & 0x7;
}
}

@ -1,26 +1,3 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: https://openmw.org/
This file (property.h) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
https://www.gnu.org/licenses/ .
*/
#ifndef OPENMW_COMPONENTS_NIF_PROPERTY_HPP
#define OPENMW_COMPONENTS_NIF_PROPERTY_HPP
@ -29,57 +6,42 @@
namespace Nif
{
struct Property : public NiObjectNET
struct NiProperty : NiObjectNET
{
};
struct NiTexturingProperty : public Property
struct NiTextureTransform
{
unsigned short flags{ 0u };
// A sub-texture
struct Texture
enum class Method : uint32_t
{
/* Clamp mode
0 - clampS clampT
1 - clampS wrapT
2 - wrapS clampT
3 - wrapS wrapT
*/
bool inUse;
NiSourceTexturePtr texture;
// Back = inverse of mOrigin.
// FromMaya = inverse of the V axis with a positive translation along V of 1 unit.
MayaLegacy = 0, // mOrigin * mRotation * Back * mOffset * mScale
Max = 1, // mOrigin * mScale * mRotation * mOffset * Back
Maya = 2, // mOrigin * mRotation * Back * FromMaya * mOffset * mScale
};
unsigned int clamp, uvSet;
osg::Vec2f mOffset;
osg::Vec2f mScale;
float mRotation;
Method mTransformMethod;
osg::Vec2f mOrigin;
void read(NIFStream* nif);
void post(Reader& nif);
void read(NIFStream* nif);
};
bool wrapT() const { return clamp & 1; }
bool wrapS() const { return (clamp >> 1) & 1; }
struct NiTexturingProperty : NiProperty
{
enum class ApplyMode : uint32_t
{
Replace = 0,
Decal = 1,
Modulate = 2,
Hilight = 3, // PS2-specific?
Hilight2 = 4, // Used for Oblivion parallax
};
/* Apply mode:
0 - replace
1 - decal
2 - modulate
3 - hilight // These two are for PS2 only?
4 - hilight2
*/
unsigned int apply{ 0 };
/*
* The textures in this list are as follows:
*
* 0 - Base texture
* 1 - Dark texture
* 2 - Detail texture
* 3 - Gloss texture
* 4 - Glow texture
* 5 - Bump map texture
* 6 - Decal texture
*/
enum TextureType : uint32_t
enum TextureType
{
BaseTexture = 0,
DarkTexture = 1,
@ -90,38 +52,48 @@ namespace Nif
DecalTexture = 6,
};
std::vector<Texture> textures;
std::vector<Texture> shaderTextures;
// A sub-texture
struct Texture
{
bool mEnabled;
NiSourceTexturePtr mSourceTexture;
uint32_t mClamp;
uint32_t mFilter;
uint16_t mMaxAnisotropy;
uint32_t mUVSet;
bool mHasTransform;
NiTextureTransform mTransform;
void read(NIFStream* nif);
void post(Reader& nif);
bool wrapT() const { return mClamp & 1; }
bool wrapS() const { return mClamp & 2; }
};
uint16_t mFlags{ 0u };
ApplyMode mApplyMode{ ApplyMode::Modulate };
std::vector<Texture> mTextures;
std::vector<Texture> mShaderTextures;
std::vector<uint32_t> mShaderIds;
osg::Vec2f envMapLumaBias;
osg::Vec4f bumpMapMatrix;
osg::Vec2f mEnvMapLumaBias;
osg::Vec4f mBumpMapMatrix;
float mParallaxOffset;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiFogProperty : public Property
struct NiShadeProperty : NiProperty
{
unsigned short mFlags;
float mFogDepth;
osg::Vec3f mColour;
uint16_t mFlags{ 0u };
void read(NIFStream* nif) override;
};
// These contain no other data than the 'flags' field
struct NiShadeProperty : public Property
{
unsigned short flags{ 0u };
void read(NIFStream* nif) override
{
Property::read(nif);
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
flags = nif->getUShort();
}
};
enum class BSShaderType : unsigned int
enum class BSShaderType : uint32_t
{
ShaderType_TallGrass = 0,
ShaderType_Default = 1,
@ -133,56 +105,73 @@ namespace Nif
ShaderType_NoLighting = 33
};
struct BSShaderProperty : public NiShadeProperty
enum BSShaderFlags1
{
unsigned int type{ 0u }, flags1{ 0u }, flags2{ 0u };
float envMapIntensity{ 0.f };
BSSFlag1_Specular = 0x00000001,
BSSFlag1_Decal = 0x04000000,
};
struct BSSPParallaxParams
{
float mMaxPasses{ 4.f };
float mScale{ 1.f };
void read(NIFStream* nif);
};
struct BSSPRefractionParams
{
float mStrength{ 0.f };
int32_t mPeriod{ 0 };
void read(NIFStream* nif);
};
struct BSShaderProperty : NiShadeProperty
{
uint32_t mType{ 0u }, mShaderFlags1{ 0u }, mShaderFlags2{ 0u };
float mEnvMapScale{ 0.f };
std::vector<uint32_t> mShaderFlags1Hashes, mShaderFlags2Hashes;
osg::Vec2f mUVOffset, mUVScale;
void read(NIFStream* nif) override;
bool specular() const { return flags1 & 1; }
bool doubleSided() const { return (flags2 >> 4) & 1; }
bool treeAnim() const { return (flags2 >> 29) & 1; }
bool decal() const { return (flags1 >> 26) & 1; }
// These flags are shared between BSShader and BSLightingShader
// Shader-specific flag methods must be handled on per-record basis
bool specular() const { return mShaderFlags1 & BSSFlag1_Specular; }
bool decal() const { return mShaderFlags1 & BSSFlag1_Decal; }
};
struct BSShaderLightingProperty : public BSShaderProperty
struct BSShaderLightingProperty : BSShaderProperty
{
unsigned int clamp{ 0u };
uint32_t mClamp{ 3 };
void read(NIFStream* nif) override;
bool wrapT() const { return clamp & 1; }
bool wrapS() const { return (clamp >> 1) & 1; }
bool wrapT() const { return mClamp & 1; }
bool wrapS() const { return mClamp & 2; }
};
struct BSShaderPPLightingProperty : public BSShaderLightingProperty
struct BSShaderPPLightingProperty : BSShaderLightingProperty
{
BSShaderTextureSetPtr textureSet;
struct RefractionSettings
{
float strength{ 0.f };
int period{ 0 };
};
struct ParallaxSettings
{
float passes{ 0.f };
float scale{ 0.f };
};
RefractionSettings refraction;
ParallaxSettings parallax;
BSShaderTextureSetPtr mTextureSet;
BSSPRefractionParams mRefraction;
BSSPParallaxParams mParallax;
osg::Vec4f mEmissiveColor;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct BSShaderNoLightingProperty : public BSShaderLightingProperty
struct BSShaderNoLightingProperty : BSShaderLightingProperty
{
std::string filename;
osg::Vec4f falloffParams;
std::string mFilename;
osg::Vec4f mFalloffParams;
void read(NIFStream* nif) override;
};
enum class BSLightingShaderType : unsigned int
enum class BSLightingShaderType : uint32_t
{
ShaderType_Default = 0,
ShaderType_EnvMap = 1,
@ -204,120 +193,157 @@ namespace Nif
ShaderType_Cloud = 17,
ShaderType_LODNoise = 18,
ShaderType_MultitexLandLODBlend = 19,
ShaderType_Dismemberment = 20
ShaderType_Dismemberment = 20,
ShaderType_Terrain = 21, // FO76+, technically 17
};
struct BSLightingShaderProperty : public BSShaderProperty
enum BSLightingShaderFlags1
{
BSShaderTextureSetPtr mTextureSet;
unsigned int mClamp{ 0u };
float mAlpha;
float mGlossiness;
osg::Vec3f mEmissive, mSpecular;
float mEmissiveMult, mSpecStrength;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
BSLSFlag1_Falloff = 0x00000040,
};
struct BSEffectShaderProperty : public BSShaderProperty
enum BSLightingShaderFlags2
{
osg::Vec2f mUVOffset, mUVScale;
std::string mSourceTexture;
unsigned char mClamp;
unsigned char mLightingInfluence;
unsigned char mEnvMapMinLOD;
osg::Vec4f mFalloffParams;
osg::Vec4f mBaseColor;
float mBaseColorScale;
float mFalloffDepth;
std::string mGreyscaleTexture;
void read(NIFStream* nif) override;
bool useFalloff() const { return (flags >> 6) & 1; }
BSLSFlag2_DoubleSided = 0x00000010,
BSLSFlag2_TreeAnim = 0x20000000,
};
struct NiDitherProperty : public Property
struct BSSPLuminanceParams
{
unsigned short flags;
void read(NIFStream* nif) override
{
Property::read(nif);
flags = nif->getUShort();
}
float mLumEmittance;
float mExposureOffset;
float mFinalExposureMin, mFinalExposureMax;
void read(NIFStream* nif);
};
struct NiZBufferProperty : public Property
struct BSSPWetnessParams
{
unsigned short flags;
unsigned int testFunction;
void read(NIFStream* nif) override
{
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();
}
bool depthTest() const { return flags & 1; }
float mSpecScale;
float mSpecPower;
float mMinVar;
float mEnvMapScale;
float mFresnelPower;
float mMetalness;
bool depthWrite() const { return (flags >> 1) & 1; }
void read(NIFStream* nif);
};
struct NiSpecularProperty : public Property
struct BSSPMLParallaxParams
{
unsigned short flags;
void read(NIFStream* nif) override
{
Property::read(nif);
flags = nif->getUShort();
}
float mInnerLayerThickness;
float mRefractionScale;
osg::Vec2f mInnerLayerTextureScale;
float mEnvMapScale;
bool isEnabled() const { return flags & 1; }
void read(NIFStream* nif);
};
struct NiWireframeProperty : public Property
struct BSSPTranslucencyParams
{
unsigned short flags;
void read(NIFStream* nif) override
{
Property::read(nif);
flags = nif->getUShort();
}
osg::Vec3f mSubsurfaceColor;
float mTransmissiveScale;
float mTurbulence;
bool mThickObject;
bool mMixAlbedo;
bool isEnabled() const { return flags & 1; }
void read(NIFStream* nif);
};
// The rest are all struct-based
template <typename T>
struct StructPropT : Property
struct BSLightingShaderProperty : BSShaderProperty
{
T data;
unsigned short flags;
BSShaderTextureSetPtr mTextureSet;
osg::Vec3f mEmissive;
float mEmissiveMult;
std::string mRootMaterial;
uint32_t mClamp;
float mAlpha;
float mRefractionStrength;
float mGlossiness{ 80.f };
float mSmoothness{ 1.f };
osg::Vec3f mSpecular;
float mSpecStrength;
std::array<float, 2> mLightingEffects;
float mSubsurfaceRolloff;
float mRimlightPower;
float mBacklightPower;
float mGrayscaleToPaletteScale{ 1.f };
float mFresnelPower{ 5.f };
BSSPWetnessParams mWetness;
bool mDoTranslucency{ false };
BSSPTranslucencyParams mTranslucency;
std::vector<std::vector<std::string>> mTextureArrays;
BSSPLuminanceParams mLuminance;
bool mUseSSR;
bool mWetnessUseSSR;
osg::Vec3f mSkinTintColor;
float mSkinTintAlpha{ 1.f };
osg::Vec3f mHairTintColor;
BSSPParallaxParams mParallax;
BSSPMLParallaxParams mMultiLayerParallax;
osg::Vec4f mSparkle;
float mCubeMapScale;
osg::Vec3f mLeftEyeReflectionCenter;
osg::Vec3f mRightEyeReflectionCenter;
void read(NIFStream* nif) override
{
Property::read(nif);
flags = nif->getUShort();
data.read(nif);
}
void read(NIFStream* nif) override;
void post(Reader& nif) override;
bool doubleSided() const { return mShaderFlags2 & BSLSFlag2_DoubleSided; }
bool treeAnim() const { return mShaderFlags2 & BSLSFlag2_TreeAnim; }
};
struct S_MaterialProperty
struct BSEffectShaderProperty : BSShaderProperty
{
// The vector components are R,G,B
osg::Vec3f ambient{ 1.f, 1.f, 1.f }, diffuse{ 1.f, 1.f, 1.f };
osg::Vec3f specular, emissive;
float glossiness{ 0.f }, alpha{ 0.f }, emissiveMult{ 1.f };
std::string mSourceTexture;
uint8_t mClamp;
uint8_t mLightingInfluence;
uint8_t mEnvMapMinLOD;
osg::Vec4f mFalloffParams;
float mRefractionPower;
osg::Vec4f mBaseColor;
float mBaseColorScale;
float mFalloffDepth;
std::string mGreyscaleTexture;
std::string mEnvMapTexture;
std::string mNormalTexture;
std::string mEnvMaskTexture;
float mEnvMapScale;
std::string mReflectanceTexture;
std::string mLightingTexture;
osg::Vec3f mEmittanceColor;
std::string mEmitGradientTexture;
BSSPLuminanceParams mLuminance;
void read(NIFStream* nif);
void read(NIFStream* nif) override;
bool useFalloff() const { return mShaderFlags1 & BSLSFlag1_Falloff; }
bool doubleSided() const { return mShaderFlags2 & BSLSFlag2_DoubleSided; }
bool treeAnim() const { return mShaderFlags2 & BSLSFlag2_TreeAnim; }
};
struct S_AlphaProperty
struct NiAlphaProperty : NiProperty
{
enum Flags
{
Flag_Blending = 0x0001,
Flag_Testing = 0x0200,
Flag_NoSorter = 0x2000,
};
uint16_t mFlags;
uint8_t mThreshold;
void read(NIFStream* nif) override;
bool useAlphaBlending() const { return mFlags & Flag_Blending; }
bool useAlphaTesting() const { return mFlags & Flag_Testing; }
bool noSorter() const { return mFlags & Flag_NoSorter; }
/*
NiAlphaProperty blend modes (glBlendFunc):
0000 GL_ONE
@ -346,119 +372,132 @@ namespace Nif
http://niftools.sourceforge.net/doc/nif/NiAlphaProperty.html
*/
// Tested against when certain flags are set (see above.)
unsigned char threshold;
int sourceBlendMode() const { return (mFlags >> 1) & 0xF; }
int destinationBlendMode() const { return (mFlags >> 5) & 0xF; }
int alphaTestMode() const { return (mFlags >> 10) & 0x7; }
};
struct NiDitherProperty : NiProperty
{
uint16_t mFlags;
void read(NIFStream* nif);
void read(NIFStream* nif) override;
};
/*
Docs taken from:
http://niftools.sourceforge.net/doc/nif/NiStencilProperty.html
*/
struct S_StencilProperty
struct NiFogProperty : NiProperty
{
// Is stencil test enabled?
unsigned char enabled;
uint16_t mFlags;
float mFogDepth;
osg::Vec3f mColour;
/*
0 TEST_NEVER
1 TEST_LESS
2 TEST_EQUAL
3 TEST_LESS_EQUAL
4 TEST_GREATER
5 TEST_NOT_EQUAL
6 TEST_GREATER_EQUAL
7 TEST_NEVER (though nifskope comment says TEST_ALWAYS, but ingame it is TEST_NEVER)
*/
int compareFunc;
unsigned stencilRef;
unsigned stencilMask;
/*
Stencil test fail action, depth test fail action and depth test pass action:
0 ACTION_KEEP
1 ACTION_ZERO
2 ACTION_REPLACE
3 ACTION_INCREMENT
4 ACTION_DECREMENT
5 ACTION_INVERT
*/
int failAction;
int zFailAction;
int zPassAction;
/*
Face draw mode:
0 DRAW_CCW_OR_BOTH
1 DRAW_CCW [default]
2 DRAW_CW
3 DRAW_BOTH
*/
int drawMode;
void read(NIFStream* nif) override;
};
void read(NIFStream* nif);
struct NiMaterialProperty : NiProperty
{
uint16_t mFlags{ 0u };
osg::Vec3f mAmbient{ 1.f, 1.f, 1.f };
osg::Vec3f mDiffuse{ 1.f, 1.f, 1.f };
osg::Vec3f mSpecular;
osg::Vec3f mEmissive;
float mGlossiness{ 0.f };
float mAlpha{ 0.f };
float mEmissiveMult{ 1.f };
void read(NIFStream* nif) override;
};
struct NiAlphaProperty : public StructPropT<S_AlphaProperty>
struct NiSpecularProperty : NiProperty
{
bool useAlphaBlending() const { return flags & 1; }
int sourceBlendMode() const { return (flags >> 1) & 0xF; }
int destinationBlendMode() const { return (flags >> 5) & 0xF; }
bool noSorter() const { return (flags >> 13) & 1; }
bool mEnable;
bool useAlphaTesting() const { return (flags >> 9) & 1; }
int alphaTestMode() const { return (flags >> 10) & 0x7; }
void read(NIFStream* nif) override;
};
struct NiVertexColorProperty : public Property
struct NiStencilProperty : NiProperty
{
enum class VertexMode : unsigned int
enum class TestFunc : uint32_t
{
Never = 0,
Less = 1,
Equal = 2,
LessEqual = 3,
Greater = 4,
NotEqual = 5,
GreaterEqual = 6,
Always = 7,
};
enum class Action : uint32_t
{
Keep = 0,
Zero = 1,
Replace = 2,
Increment = 3,
Decrement = 4,
Invert = 5,
};
enum class DrawMode : uint32_t
{
Default = 0,
CounterClockwise = 1,
Clockwise = 2,
Both = 3,
};
uint16_t mFlags{ 0u };
bool mEnabled;
TestFunc mTestFunction;
uint32_t mStencilRef;
uint32_t mStencilMask;
Action mFailAction;
Action mZFailAction;
Action mPassAction;
DrawMode mDrawMode;
void read(NIFStream* nif) override;
};
struct NiVertexColorProperty : NiProperty
{
enum class VertexMode : uint32_t
{
VertMode_SrcIgnore = 0,
VertMode_SrcEmissive = 1,
VertMode_SrcAmbDif = 2
};
enum class LightMode : unsigned int
enum class LightMode : uint32_t
{
LightMode_Emissive = 0,
LightMode_EmiAmbDif = 1
};
unsigned short mFlags;
uint16_t mFlags;
VertexMode mVertexMode;
LightMode mLightingMode;
void read(NIFStream* nif) override;
};
struct NiStencilProperty : public Property
struct NiWireframeProperty : NiProperty
{
S_StencilProperty data;
unsigned short flags{ 0u };
bool mEnable;
void read(NIFStream* nif) override
{
Property::read(nif);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
flags = nif->getUShort();
data.read(nif);
}
void read(NIFStream* nif) override;
};
struct NiMaterialProperty : public Property
struct NiZBufferProperty : NiProperty
{
S_MaterialProperty data;
unsigned short flags{ 0u };
uint16_t mFlags;
uint32_t mTestFunction;
void read(NIFStream* nif) override
{
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);
}
void read(NIFStream* nif) override;
bool depthTest() const { return mFlags & 1; }
bool depthWrite() const { return mFlags & 2; }
};
} // Namespace
}
#endif

@ -112,7 +112,7 @@ namespace Nif
struct NiAVObject;
struct Extra;
struct Property;
struct NiProperty;
struct NiUVData;
struct NiPosData;
struct NiVisData;
@ -191,7 +191,7 @@ namespace Nif
using BSMultiBoundDataPtr = RecordPtrT<BSMultiBoundData>;
using NiAVObjectList = RecordListT<NiAVObject>;
using PropertyList = RecordListT<Property>;
using NiPropertyList = RecordListT<NiProperty>;
using ExtraList = RecordListT<Extra>;
using NiSourceTextureList = RecordListT<NiSourceTexture>;
using NiInterpolatorList = RecordListT<NiInterpolator>;

@ -103,7 +103,7 @@ namespace
// 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::NiAVObject* nifNode, const Nif::Parent* parent, std::vector<const Nif::Property*>& out)
const Nif::NiAVObject* nifNode, const Nif::Parent* parent, std::vector<const Nif::NiProperty*>& out)
{
if (parent != nullptr)
collectDrawableProperties(&parent->mNiNode, parent->mParent, out);
@ -412,7 +412,7 @@ namespace NifOsg
{
const Nif::NiStencilProperty* stencilprop
= static_cast<const Nif::NiStencilProperty*>(property.getPtr());
if (stencilprop->data.enabled != 0)
if (stencilprop->mEnabled)
{
hasStencilProperty = true;
break;
@ -959,7 +959,7 @@ namespace NifOsg
}
}
void handleMaterialControllers(const Nif::Property* materialProperty,
void handleMaterialControllers(const Nif::NiProperty* materialProperty,
SceneUtil::CompositeStateSetUpdater* composite, int animflags, const osg::Material* baseMaterial)
{
for (Nif::NiTimeControllerPtr ctrl = materialProperty->mController; !ctrl.empty(); ctrl = ctrl->mNext)
@ -1008,8 +1008,9 @@ namespace NifOsg
}
}
void handleTextureControllers(const Nif::Property* texProperty, SceneUtil::CompositeStateSetUpdater* composite,
Resource::ImageManager* imageManager, osg::StateSet* stateset, int animflags)
void handleTextureControllers(const Nif::NiProperty* texProperty,
SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager,
osg::StateSet* stateset, int animflags)
{
for (Nif::NiTimeControllerPtr ctrl = texProperty->mController; !ctrl.empty(); ctrl = ctrl->mNext)
{
@ -1316,7 +1317,7 @@ namespace NifOsg
// localToWorldMatrix for transforming to particle space
handleParticlePrograms(partctrl->mModifier, partctrl->mCollider, parentNode, partsys.get(), rf);
std::vector<const Nif::Property*> drawableProps;
std::vector<const Nif::NiProperty*> drawableProps;
collectDrawableProperties(nifNode, parent, drawableProps);
applyDrawableProperties(parentNode, drawableProps, composite, true, animflags);
@ -1462,7 +1463,7 @@ namespace NifOsg
// - if there are no vertex colors, we need to disable colorMode.
// - there are 3 "overlapping" nif properties that all affect the osg::Material, handling them
// above the actual renderable would be tedious.
std::vector<const Nif::Property*> drawableProps;
std::vector<const Nif::NiProperty*> drawableProps;
collectDrawableProperties(nifNode, parent, drawableProps);
applyDrawableProperties(parentNode, drawableProps, composite, !niGeometryData->mColors.empty(), animflags);
}
@ -1616,50 +1617,54 @@ namespace NifOsg
}
}
osg::Stencil::Function getStencilFunction(int func)
osg::Stencil::Function getStencilFunction(Nif::NiStencilProperty::TestFunc func)
{
using TestFunc = Nif::NiStencilProperty::TestFunc;
switch (func)
{
case 0:
case TestFunc::Never:
return osg::Stencil::NEVER;
case 1:
case TestFunc::Less:
return osg::Stencil::LESS;
case 2:
case TestFunc::Equal:
return osg::Stencil::EQUAL;
case 3:
case TestFunc::LessEqual:
return osg::Stencil::LEQUAL;
case 4:
case TestFunc::Greater:
return osg::Stencil::GREATER;
case 5:
case TestFunc::NotEqual:
return osg::Stencil::NOTEQUAL;
case 6:
case TestFunc::GreaterEqual:
return osg::Stencil::GEQUAL;
case 7:
case TestFunc::Always:
return osg::Stencil::ALWAYS;
default:
Log(Debug::Info) << "Unexpected stencil function: " << func << " in " << mFilename;
Log(Debug::Info) << "Unexpected stencil function: " << static_cast<uint32_t>(func) << " in "
<< mFilename;
return osg::Stencil::NEVER;
}
}
osg::Stencil::Operation getStencilOperation(int op)
osg::Stencil::Operation getStencilOperation(Nif::NiStencilProperty::Action op)
{
using Action = Nif::NiStencilProperty::Action;
switch (op)
{
case 0:
case Action::Keep:
return osg::Stencil::KEEP;
case 1:
case Action::Zero:
return osg::Stencil::ZERO;
case 2:
case Action::Replace:
return osg::Stencil::REPLACE;
case 3:
case Action::Increment:
return osg::Stencil::INCR;
case 4:
case Action::Decrement:
return osg::Stencil::DECR;
case 5:
case Action::Invert:
return osg::Stencil::INVERT;
default:
Log(Debug::Info) << "Unexpected stencil operation: " << op << " in " << mFilename;
Log(Debug::Info) << "Unexpected stencil operation: " << static_cast<uint32_t>(op) << " in "
<< mFilename;
return osg::Stencil::KEEP;
}
}
@ -1827,9 +1832,9 @@ namespace NifOsg
// If this loop is changed such that the base texture isn't guaranteed to end up in texture unit 0, the
// shadow casting shader will need to be updated accordingly.
for (size_t i = 0; i < texprop->textures.size(); ++i)
for (size_t i = 0; i < texprop->mTextures.size(); ++i)
{
if (texprop->textures[i].inUse
if (texprop->mTextures[i].mEnabled
|| (i == Nif::NiTexturingProperty::BaseTexture && !texprop->mController.empty()))
{
switch (i)
@ -1854,10 +1859,10 @@ namespace NifOsg
unsigned int uvSet = 0;
// create a new texture, will later attempt to share using the SharedStateManager
osg::ref_ptr<osg::Texture2D> texture2d;
if (texprop->textures[i].inUse)
if (texprop->mTextures[i].mEnabled)
{
const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i];
if (tex.texture.empty() && texprop->mController.empty())
const Nif::NiTexturingProperty::Texture& tex = texprop->mTextures[i];
if (tex.mSourceTexture.empty() && texprop->mController.empty())
{
if (i == 0)
Log(Debug::Warning) << "Base texture is in use but empty on shape \"" << nodeName
@ -1865,9 +1870,9 @@ namespace NifOsg
continue;
}
if (!tex.texture.empty())
if (!tex.mSourceTexture.empty())
{
const Nif::NiSourceTexture* st = tex.texture.getPtr();
const Nif::NiSourceTexture* st = tex.mSourceTexture.getPtr();
osg::ref_ptr<osg::Image> image = handleSourceTexture(st, imageManager);
texture2d = new osg::Texture2D(image);
if (image)
@ -1878,7 +1883,7 @@ namespace NifOsg
handleTextureWrapping(texture2d, tex.wrapS(), tex.wrapT());
uvSet = tex.uvSet;
uvSet = tex.mUVSet;
}
else
{
@ -1926,10 +1931,10 @@ namespace NifOsg
// Bump maps offset the environment map.
// Set this texture to Off by default since we can't render it with the fixed-function pipeline
stateset->setTextureMode(texUnit, GL_TEXTURE_2D, osg::StateAttribute::OFF);
osg::Matrix2 bumpMapMatrix(texprop->bumpMapMatrix.x(), texprop->bumpMapMatrix.y(),
texprop->bumpMapMatrix.z(), texprop->bumpMapMatrix.w());
osg::Matrix2 bumpMapMatrix(texprop->mBumpMapMatrix.x(), texprop->mBumpMapMatrix.y(),
texprop->mBumpMapMatrix.z(), texprop->mBumpMapMatrix.w());
stateset->addUniform(new osg::Uniform("bumpMapMatrix", bumpMapMatrix));
stateset->addUniform(new osg::Uniform("envMapLumaBias", texprop->envMapLumaBias));
stateset->addUniform(new osg::Uniform("envMapLumaBias", texprop->mEnvMapLumaBias));
}
else if (i == Nif::NiTexturingProperty::GlossTexture)
{
@ -2098,6 +2103,7 @@ namespace NifOsg
case Nif::BSLightingShaderType::ShaderType_LODNoise:
case Nif::BSLightingShaderType::ShaderType_MultitexLandLODBlend:
case Nif::BSLightingShaderType::ShaderType_Dismemberment:
case Nif::BSLightingShaderType::ShaderType_Terrain:
Log(Debug::Warning) << "Unhandled BSLightingShaderType " << type << " in " << mFilename;
return "bs/default";
}
@ -2105,7 +2111,7 @@ namespace NifOsg
return "bs/default";
}
void handleProperty(const Nif::Property* property, osg::Node* node,
void handleProperty(const Nif::NiProperty* property, osg::Node* node,
SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager,
std::vector<unsigned int>& boundTextures, int animflags, bool hasStencilProperty)
{
@ -2114,14 +2120,17 @@ namespace NifOsg
case Nif::RC_NiStencilProperty:
{
const Nif::NiStencilProperty* stencilprop = static_cast<const Nif::NiStencilProperty*>(property);
osg::ref_ptr<osg::FrontFace> frontFace = new osg::FrontFace;
switch (stencilprop->data.drawMode)
using DrawMode = Nif::NiStencilProperty::DrawMode;
switch (stencilprop->mDrawMode)
{
case 2:
case DrawMode::Clockwise:
frontFace->setMode(osg::FrontFace::CLOCKWISE);
break;
case 0:
case 1:
case DrawMode::Default:
case DrawMode::CounterClockwise:
case DrawMode::Both:
default:
frontFace->setMode(osg::FrontFace::COUNTER_CLOCKWISE);
break;
@ -2130,20 +2139,20 @@ namespace NifOsg
osg::StateSet* stateset = node->getOrCreateStateSet();
stateset->setAttribute(frontFace, osg::StateAttribute::ON);
stateset->setMode(GL_CULL_FACE,
stencilprop->data.drawMode == 3 ? osg::StateAttribute::OFF : osg::StateAttribute::ON);
if (stencilprop->mDrawMode == DrawMode::Both)
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
else
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
if (stencilprop->data.enabled != 0)
if (stencilprop->mEnabled)
{
mHasStencilProperty = true;
osg::ref_ptr<osg::Stencil> stencil = new osg::Stencil;
stencil->setFunction(getStencilFunction(stencilprop->data.compareFunc),
stencilprop->data.stencilRef, stencilprop->data.stencilMask);
stencil->setStencilFailOperation(getStencilOperation(stencilprop->data.failAction));
stencil->setStencilPassAndDepthFailOperation(
getStencilOperation(stencilprop->data.zFailAction));
stencil->setStencilPassAndDepthPassOperation(
getStencilOperation(stencilprop->data.zPassAction));
stencil->setFunction(getStencilFunction(stencilprop->mTestFunction), stencilprop->mStencilRef,
stencilprop->mStencilMask);
stencil->setStencilFailOperation(getStencilOperation(stencilprop->mFailAction));
stencil->setStencilPassAndDepthFailOperation(getStencilOperation(stencilprop->mZFailAction));
stencil->setStencilPassAndDepthPassOperation(getStencilOperation(stencilprop->mPassAction));
stencil = shareAttribute(stencil);
stateset->setAttributeAndModes(stencil, osg::StateAttribute::ON);
@ -2155,7 +2164,7 @@ namespace NifOsg
const Nif::NiWireframeProperty* wireprop = static_cast<const Nif::NiWireframeProperty*>(property);
osg::ref_ptr<osg::PolygonMode> mode = new osg::PolygonMode;
mode->setMode(osg::PolygonMode::FRONT_AND_BACK,
wireprop->isEnabled() ? osg::PolygonMode::LINE : osg::PolygonMode::FILL);
wireprop->mEnable ? osg::PolygonMode::LINE : osg::PolygonMode::FILL);
mode = shareAttribute(mode);
node->getOrCreateStateSet()->setAttributeAndModes(mode, osg::StateAttribute::ON);
break;
@ -2202,18 +2211,16 @@ namespace NifOsg
{
auto texprop = static_cast<const Nif::BSShaderPPLightingProperty*>(property);
bool shaderRequired = true;
node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->type)));
node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->mType)));
node->setUserValue("shaderRequired", shaderRequired);
osg::StateSet* stateset = node->getOrCreateStateSet();
if (!texprop->textureSet.empty())
if (!texprop->mTextureSet.empty())
{
auto textureSet = texprop->textureSet.getPtr();
auto textureSet = texprop->mTextureSet.getPtr();
handleTextureSet(
textureSet, texprop->clamp, node->getName(), stateset, imageManager, boundTextures);
textureSet, texprop->mClamp, node->getName(), stateset, imageManager, boundTextures);
}
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
if (texprop->doubleSided())
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
break;
}
case Nif::RC_BSShaderNoLightingProperty:
@ -2221,10 +2228,10 @@ namespace NifOsg
auto texprop = static_cast<const Nif::BSShaderNoLightingProperty*>(property);
bool shaderRequired = true;
bool useFalloff = false;
node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->type)));
node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->mType)));
node->setUserValue("shaderRequired", shaderRequired);
osg::StateSet* stateset = node->getOrCreateStateSet();
if (!texprop->filename.empty())
if (!texprop->mFilename.empty())
{
if (!boundTextures.empty())
{
@ -2233,7 +2240,7 @@ namespace NifOsg
boundTextures.clear();
}
std::string filename
= Misc::ResourceHelpers::correctTexturePath(texprop->filename, imageManager->getVFS());
= Misc::ResourceHelpers::correctTexturePath(texprop->mFilename, imageManager->getVFS());
osg::ref_ptr<osg::Image> image = imageManager->getImage(filename);
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
texture2d->setName("diffuseMap");
@ -2247,20 +2254,18 @@ namespace NifOsg
if (mBethVersion >= 27)
{
useFalloff = true;
stateset->addUniform(new osg::Uniform("falloffParams", texprop->falloffParams));
stateset->addUniform(new osg::Uniform("falloffParams", texprop->mFalloffParams));
}
}
stateset->addUniform(new osg::Uniform("useFalloff", useFalloff));
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
if (texprop->doubleSided())
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
break;
}
case Nif::RC_BSLightingShaderProperty:
{
auto texprop = static_cast<const Nif::BSLightingShaderProperty*>(property);
bool shaderRequired = true;
node->setUserValue("shaderPrefix", std::string(getBSLightingShaderPrefix(texprop->type)));
node->setUserValue("shaderPrefix", std::string(getBSLightingShaderPrefix(texprop->mType)));
node->setUserValue("shaderRequired", shaderRequired);
osg::StateSet* stateset = node->getOrCreateStateSet();
if (!texprop->mTextureSet.empty())
@ -2365,7 +2370,7 @@ namespace NifOsg
return *found;
}
void applyDrawableProperties(osg::Node* node, const std::vector<const Nif::Property*>& properties,
void applyDrawableProperties(osg::Node* node, const std::vector<const Nif::NiProperty*>& properties,
SceneUtil::CompositeStateSetUpdater* composite, bool hasVertexColors, int animflags)
{
// Specular lighting is enabled by default, but there's a quirk...
@ -2390,7 +2395,7 @@ namespace NifOsg
float emissiveMult = 1.f;
float specStrength = 1.f;
for (const Nif::Property* property : properties)
for (const Nif::NiProperty* property : properties)
{
switch (property->recType)
{
@ -2399,21 +2404,20 @@ namespace NifOsg
// Specular property can turn specular lighting off.
// FIXME: NiMaterialColorController doesn't care about this.
auto specprop = static_cast<const Nif::NiSpecularProperty*>(property);
specEnabled = specprop->isEnabled();
specEnabled = specprop->mEnable;
break;
}
case Nif::RC_NiMaterialProperty:
{
const Nif::NiMaterialProperty* matprop = static_cast<const Nif::NiMaterialProperty*>(property);
mat->setDiffuse(
osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.diffuse, matprop->data.alpha));
mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.ambient, 1.f));
mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.emissive, 1.f));
emissiveMult = matprop->data.emissiveMult;
mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->mDiffuse, matprop->mAlpha));
mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->mAmbient, 1.f));
mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->mEmissive, 1.f));
emissiveMult = matprop->mEmissiveMult;
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.specular, 1.f));
mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness);
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->mSpecular, 1.f));
mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->mGlossiness);
if (!matprop->mController.empty())
{
@ -2428,29 +2432,31 @@ namespace NifOsg
const Nif::NiVertexColorProperty* vertprop
= static_cast<const Nif::NiVertexColorProperty*>(property);
using VertexMode = Nif::NiVertexColorProperty::VertexMode;
switch (vertprop->mVertexMode)
{
case Nif::NiVertexColorProperty::VertexMode::VertMode_SrcIgnore:
case VertexMode::VertMode_SrcIgnore:
{
mat->setColorMode(osg::Material::OFF);
break;
}
case Nif::NiVertexColorProperty::VertexMode::VertMode_SrcEmissive:
case VertexMode::VertMode_SrcEmissive:
{
mat->setColorMode(osg::Material::EMISSION);
break;
}
case Nif::NiVertexColorProperty::VertexMode::VertMode_SrcAmbDif:
case VertexMode::VertMode_SrcAmbDif:
{
lightmode = vertprop->mLightingMode;
using LightMode = Nif::NiVertexColorProperty::LightMode;
switch (lightmode)
{
case Nif::NiVertexColorProperty::LightMode::LightMode_Emissive:
case LightMode::LightMode_Emissive:
{
mat->setColorMode(osg::Material::OFF);
break;
}
case Nif::NiVertexColorProperty::LightMode::LightMode_EmiAmbDif:
case LightMode::LightMode_EmiAmbDif:
default:
{
mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
@ -2503,7 +2509,7 @@ namespace NifOsg
if (alphaprop->useAlphaTesting())
{
osg::ref_ptr<osg::AlphaFunc> alphaFunc(new osg::AlphaFunc(
getTestMode(alphaprop->alphaTestMode()), alphaprop->data.threshold / 255.f));
getTestMode(alphaprop->alphaTestMode()), alphaprop->mThreshold / 255.f));
alphaFunc = shareAttribute(alphaFunc);
node->getOrCreateStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON);
}

Loading…
Cancel
Save