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. /// See if the file has the "bsa" extension.
bool isBSA(const std::filesystem::path& filename) 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) std::unique_ptr<VFS::Archive> makeBsaArchive(const std::filesystem::path& path)
@ -216,7 +216,7 @@ int main(int argc, char** argv)
else else
{ {
std::cerr << "ERROR: \"" << Files::pathToUnicodeString(path) 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) catch (std::exception& e)

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

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

@ -146,16 +146,6 @@ namespace Nif
/// Read a sequence of null-terminated strings /// Read a sequence of null-terminated strings
std::string getStringPalette(); 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 <> template <>

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

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

@ -6,192 +6,368 @@
namespace Nif 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) void NiTexturingProperty::Texture::read(NIFStream* nif)
{ {
nif->read(inUse); nif->read(mEnabled);
if (!inUse) if (!mEnabled)
return; return;
texture.read(nif); if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
mSourceTexture.read(nif);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
{ {
clamp = nif->getInt(); nif->read(mClamp);
nif->skip(4); // Filter mode. Ignoring because global filtering settings are more sensible nif->read(mFilter);
} }
else 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)) if (nif->getVersion() >= NIFStream::generateVersion(20, 5, 0, 4))
nif->getUShort(); nif->read(mMaxAnisotropy);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
uvSet = nif->getUInt(); nif->read(mUVSet);
// Two PS2-specific shorts. // PS2 filtering settings
if (nif->getVersion() < NIFStream::generateVersion(10, 4, 0, 2)) if (nif->getVersion() <= NIFStream::generateVersion(10, 4, 0, 1))
nif->skip(4); nif->skip(4);
if (nif->getVersion() <= NIFStream::generateVersion(4, 1, 0, 18))
nif->skip(2); // Unknown short if (nif->getVersion() <= NIFStream::generateVersion(4, 1, 0, 12))
else if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) nif->skip(2); // Unknown
{
if (nif->get<bool>()) // Has texture transform if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
{ {
nif->getVector2(); // UV translation nif->read(mHasTransform);
nif->getVector2(); // UV scale if (mHasTransform)
nif->getFloat(); // W axis rotation mTransform.read(nif);
nif->getUInt(); // Transform method
nif->getVector2(); // Texture rotation origin
}
} }
} }
void NiTexturingProperty::Texture::post(Reader& nif) void NiTexturingProperty::Texture::post(Reader& nif)
{ {
texture.post(nif); mSourceTexture.post(nif);
} }
void NiTexturingProperty::read(NIFStream* nif) void NiTexturingProperty::read(NIFStream* nif)
{ {
Property::read(nif); NiProperty::read(nif);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD
|| nif->getVersion() >= NIFStream::generateVersion(20, 1, 0, 2)) || nif->getVersion() >= NIFStream::generateVersion(20, 1, 0, 2))
flags = nif->getUShort(); nif->read(mFlags);
if (nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 1)) if (nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 1))
apply = nif->getUInt(); mApplyMode = static_cast<ApplyMode>(nif->get<uint32_t>());
unsigned int numTextures = nif->getUInt(); mTextures.resize(nif->get<uint32_t>());
for (size_t i = 0; i < mTextures.size(); i++)
if (!numTextures)
return;
textures.resize(numTextures);
for (unsigned int i = 0; i < numTextures; i++)
{ {
textures[i].read(nif); mTextures[i].read(nif);
if (i == 5 && textures[5].inUse) // Bump map settings
if (i == 5 && mTextures[5].mEnabled)
{ {
envMapLumaBias = nif->getVector2(); nif->read(mEnvMapLumaBias);
bumpMapMatrix = nif->getVector4(); nif->read(mBumpMapMatrix);
} }
else if (i == 7 && textures[7].inUse && nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5)) else if (i == 7 && mTextures[7].mEnabled && nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
/*float parallaxOffset = */ nif->getFloat(); nif->read(mParallaxOffset);
} }
if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
{ {
unsigned int numShaderTextures = nif->getUInt(); mShaderTextures.resize(nif->get<uint32_t>());
shaderTextures.resize(numShaderTextures); mShaderIds.resize(mShaderTextures.size());
for (unsigned int i = 0; i < numShaderTextures; i++) for (size_t i = 0; i < mShaderTextures.size(); i++)
{ {
shaderTextures[i].read(nif); mShaderTextures[i].read(nif);
if (shaderTextures[i].inUse) if (mShaderTextures[i].mEnabled)
nif->getUInt(); // Unique identifier nif->read(mShaderIds[i]);
} }
} }
} }
void NiTexturingProperty::post(Reader& nif) void NiTexturingProperty::post(Reader& nif)
{ {
Property::post(nif); NiProperty::post(nif);
for (size_t i = 0; i < textures.size(); i++)
textures[i].post(nif); for (Texture& tex : mTextures)
for (size_t i = 0; i < shaderTextures.size(); i++) tex.post(nif);
shaderTextures[i].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) void BSShaderProperty::read(NIFStream* nif)
{ {
if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_F76 && recType == RC_BSLightingShaderProperty)
nif->read(mType);
NiShadeProperty::read(nif); NiShadeProperty::read(nif);
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
if (nif->getUserVersion() <= 11)
{ {
type = nif->getUInt(); nif->read(mType);
flags1 = nif->getUInt(); nif->read(mShaderFlags1);
flags2 = nif->getUInt(); nif->read(mShaderFlags2);
envMapIntensity = nif->getFloat(); 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) void BSShaderLightingProperty::read(NIFStream* nif)
{ {
BSShaderProperty::read(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) void BSShaderPPLightingProperty::read(NIFStream* nif)
{ {
BSShaderLightingProperty::read(nif); BSShaderLightingProperty::read(nif);
textureSet.read(nif);
if (nif->getBethVersion() <= 14) mTextureSet.read(nif);
return; if (nif->getUserVersion() == 11)
refraction.strength = nif->getFloat(); {
refraction.period = nif->getInt(); if (nif->getBethVersion() >= 15)
if (nif->getBethVersion() <= 24) mRefraction.read(nif);
return; if (nif->getBethVersion() >= 25)
parallax.passes = nif->getFloat(); mParallax.read(nif);
parallax.scale = nif->getFloat(); }
else if (nif->getUserVersion() >= 12)
nif->read(mEmissiveColor);
} }
void BSShaderPPLightingProperty::post(Reader& nif) void BSShaderPPLightingProperty::post(Reader& nif)
{ {
BSShaderLightingProperty::post(nif); BSShaderLightingProperty::post(nif);
textureSet.post(nif);
mTextureSet.post(nif);
} }
void BSShaderNoLightingProperty::read(NIFStream* nif) void BSShaderNoLightingProperty::read(NIFStream* nif)
{ {
BSShaderLightingProperty::read(nif); BSShaderLightingProperty::read(nif);
filename = nif->getSizedString();
mFilename = nif->getSizedString();
if (nif->getBethVersion() >= 27) 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) void BSLightingShaderProperty::read(NIFStream* nif)
{ {
type = nif->getUInt();
BSShaderProperty::read(nif); BSShaderProperty::read(nif);
flags1 = nif->getUInt();
flags2 = nif->getUInt(); if (!mName.empty() && nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76)
nif->skip(8); // UV offset return;
nif->skip(8); // UV scale
mTextureSet.read(nif); mTextureSet.read(nif);
mEmissive = nif->getVector3(); nif->read(mEmissive);
mEmissiveMult = nif->getFloat(); nif->read(mEmissiveMult);
mClamp = nif->getUInt();
mAlpha = nif->getFloat(); if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4)
nif->getFloat(); // Refraction strength nif->read(mRootMaterial);
mGlossiness = nif->getFloat();
mSpecular = nif->getVector3(); if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_STF)
mSpecStrength = nif->getFloat(); nif->skip(4); // Unknown float
nif->skip(8); // Lighting effects
switch (static_cast<BSLightingShaderType>(type)) 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: 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; break;
case BSLightingShaderType::ShaderType_SkinTint: case BSLightingShaderType::ShaderType_SkinTint:
nif->read(mSkinTintColor);
if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO4)
nif->read(mSkinTintAlpha);
break;
case BSLightingShaderType::ShaderType_HairTint: case BSLightingShaderType::ShaderType_HairTint:
nif->skip(12); // Tint color nif->read(mHairTintColor);
break; break;
case BSLightingShaderType::ShaderType_ParallaxOcc: case BSLightingShaderType::ShaderType_ParallaxOcc:
nif->skip(4); // Max passes mParallax.read(nif);
nif->skip(4); // Scale
break; break;
case BSLightingShaderType::ShaderType_MultiLayerParallax: case BSLightingShaderType::ShaderType_MultiLayerParallax:
nif->skip(4); // Inner layer thickness mMultiLayerParallax.read(nif);
nif->skip(4); // Refraction scale
nif->skip(8); // Inner layer texture scale
nif->skip(4); // Environment map strength
break; break;
case BSLightingShaderType::ShaderType_SparkleSnow: case BSLightingShaderType::ShaderType_SparkleSnow:
nif->skip(16); // Sparkle parameters nif->read(mSparkle);
break; break;
case BSLightingShaderType::ShaderType_EyeEnvmap: case BSLightingShaderType::ShaderType_EyeEnvmap:
nif->skip(4); // Cube map scale nif->read(mCubeMapScale);
nif->skip(12); // Left eye cube map offset nif->read(mLeftEyeReflectionCenter);
nif->skip(12); // Right eye cube map offset nif->read(mRightEyeReflectionCenter);
break; break;
default: default:
break; break;
@ -201,97 +377,185 @@ namespace Nif
void BSLightingShaderProperty::post(Reader& nif) void BSLightingShaderProperty::post(Reader& nif)
{ {
BSShaderProperty::post(nif); BSShaderProperty::post(nif);
mTextureSet.post(nif); mTextureSet.post(nif);
} }
void BSEffectShaderProperty::read(NIFStream* nif) void BSEffectShaderProperty::read(NIFStream* nif)
{ {
BSShaderProperty::read(nif); BSShaderProperty::read(nif);
flags1 = nif->getUInt();
flags2 = nif->getUInt(); if (!mName.empty() && nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76)
mUVOffset = nif->getVector2(); return;
mUVScale = nif->getVector2();
mSourceTexture = nif->getSizedString(); 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; mClamp = miscParams & 0xFF;
mLightingInfluence = (miscParams >> 8) & 0xFF; mLightingInfluence = (miscParams >> 8) & 0xFF;
mEnvMapMinLOD = (miscParams >> 16) & 0xFF; mEnvMapMinLOD = (miscParams >> 16) & 0xFF;
mFalloffParams = nif->getVector4(); nif->read(mFalloffParams);
mBaseColor = nif->getVector4();
mBaseColorScale = nif->getFloat(); if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76)
mFalloffDepth = nif->getFloat(); nif->read(mRefractionPower);
nif->read(mBaseColor);
nif->read(mBaseColorScale);
nif->read(mFalloffDepth);
mGreyscaleTexture = nif->getSizedString(); 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) void NiFogProperty::read(NIFStream* nif)
{ {
Property::read(nif); NiProperty::read(nif);
mFlags = nif->getUShort();
mFogDepth = nif->getFloat(); nif->read(mFlags);
mColour = nif->getVector3(); 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) if (nif->getBethVersion() < 26)
{ {
ambient = nif->getVector3(); nif->read(mAmbient);
diffuse = nif->getVector3(); nif->read(mDiffuse);
} }
specular = nif->getVector3(); nif->read(mSpecular);
emissive = nif->getVector3(); nif->read(mEmissive);
glossiness = nif->getFloat(); nif->read(mGlossiness);
alpha = nif->getFloat(); nif->read(mAlpha);
if (nif->getBethVersion() >= 22) if (nif->getBethVersion() >= 22)
emissiveMult = nif->getFloat(); nif->read(mEmissiveMult);
} }
void NiVertexColorProperty::read(NIFStream* nif) void NiShadeProperty::read(NIFStream* nif)
{ {
Property::read(nif); NiProperty::read(nif);
mFlags = nif->getUShort();
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)
{
NiProperty::read(nif);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
{ {
mVertexMode = static_cast<VertexMode>(nif->getUInt()); if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
mLightingMode = static_cast<LightMode>(nif->getUInt()); 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 else
{ {
mVertexMode = static_cast<VertexMode>((mFlags >> 4) & 0x3); nif->read(mFlags);
mLightingMode = static_cast<LightMode>((mFlags >> 3) & 0x1); 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) if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
{ {
enabled = nif->getChar(); mVertexMode = static_cast<VertexMode>(nif->get<uint32_t>());
compareFunc = nif->getInt(); mLightingMode = static_cast<LightMode>(nif->get<uint32_t>());
stencilRef = nif->getUInt();
stencilMask = nif->getUInt();
failAction = nif->getInt();
zFailAction = nif->getInt();
zPassAction = nif->getInt();
drawMode = nif->getInt();
} }
else else
{ {
unsigned short flags = nif->getUShort(); mVertexMode = static_cast<VertexMode>((mFlags >> 4) & 0x3);
enabled = flags & 0x1; mLightingMode = static_cast<LightMode>((mFlags >> 3) & 0x1);
failAction = (flags >> 1) & 0x7; }
zFailAction = (flags >> 4) & 0x7; }
zPassAction = (flags >> 7) & 0x7;
drawMode = (flags >> 10) & 0x3; void NiWireframeProperty::read(NIFStream* nif)
compareFunc = (flags >> 12) & 0x7; {
stencilRef = nif->getUInt(); NiProperty::read(nif);
stencilMask = nif->getUInt();
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 #ifndef OPENMW_COMPONENTS_NIF_PROPERTY_HPP
#define OPENMW_COMPONENTS_NIF_PROPERTY_HPP #define OPENMW_COMPONENTS_NIF_PROPERTY_HPP
@ -29,57 +6,42 @@
namespace Nif namespace Nif
{ {
struct Property : public NiObjectNET struct NiProperty : NiObjectNET
{ {
}; };
struct NiTexturingProperty : public Property struct NiTextureTransform
{ {
unsigned short flags{ 0u }; enum class Method : uint32_t
// A sub-texture
struct Texture
{ {
/* Clamp mode // Back = inverse of mOrigin.
0 - clampS clampT // FromMaya = inverse of the V axis with a positive translation along V of 1 unit.
1 - clampS wrapT MayaLegacy = 0, // mOrigin * mRotation * Back * mOffset * mScale
2 - wrapS clampT Max = 1, // mOrigin * mScale * mRotation * mOffset * Back
3 - wrapS wrapT Maya = 2, // mOrigin * mRotation * Back * FromMaya * mOffset * mScale
*/ };
bool inUse;
NiSourceTexturePtr texture;
unsigned int clamp, uvSet; osg::Vec2f mOffset;
osg::Vec2f mScale;
float mRotation;
Method mTransformMethod;
osg::Vec2f mOrigin;
void read(NIFStream* nif); void read(NIFStream* nif);
void post(Reader& nif);
bool wrapT() const { return clamp & 1; }
bool wrapS() const { return (clamp >> 1) & 1; }
}; };
/* Apply mode: struct NiTexturingProperty : NiProperty
0 - replace {
1 - decal enum class ApplyMode : uint32_t
2 - modulate {
3 - hilight // These two are for PS2 only? Replace = 0,
4 - hilight2 Decal = 1,
*/ Modulate = 2,
unsigned int apply{ 0 }; Hilight = 3, // PS2-specific?
Hilight2 = 4, // Used for Oblivion parallax
};
/* enum TextureType
* 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
{ {
BaseTexture = 0, BaseTexture = 0,
DarkTexture = 1, DarkTexture = 1,
@ -90,38 +52,48 @@ namespace Nif
DecalTexture = 6, DecalTexture = 6,
}; };
std::vector<Texture> textures; // A sub-texture
std::vector<Texture> shaderTextures; struct Texture
{
bool mEnabled;
NiSourceTexturePtr mSourceTexture;
uint32_t mClamp;
uint32_t mFilter;
uint16_t mMaxAnisotropy;
uint32_t mUVSet;
bool mHasTransform;
NiTextureTransform mTransform;
osg::Vec2f envMapLumaBias; void read(NIFStream* nif);
osg::Vec4f bumpMapMatrix; void post(Reader& nif);
void read(NIFStream* nif) override; bool wrapT() const { return mClamp & 1; }
void post(Reader& nif) override; bool wrapS() const { return mClamp & 2; }
}; };
struct NiFogProperty : public Property uint16_t mFlags{ 0u };
{ ApplyMode mApplyMode{ ApplyMode::Modulate };
unsigned short mFlags;
float mFogDepth; std::vector<Texture> mTextures;
osg::Vec3f mColour; std::vector<Texture> mShaderTextures;
std::vector<uint32_t> mShaderIds;
osg::Vec2f mEnvMapLumaBias;
osg::Vec4f mBumpMapMatrix;
float mParallaxOffset;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override;
}; };
// These contain no other data than the 'flags' field struct NiShadeProperty : NiProperty
struct NiShadeProperty : public Property
{
unsigned short flags{ 0u };
void read(NIFStream* nif) override
{ {
Property::read(nif); uint16_t mFlags{ 0u };
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
flags = nif->getUShort(); void read(NIFStream* nif) override;
}
}; };
enum class BSShaderType : unsigned int enum class BSShaderType : uint32_t
{ {
ShaderType_TallGrass = 0, ShaderType_TallGrass = 0,
ShaderType_Default = 1, ShaderType_Default = 1,
@ -133,56 +105,73 @@ namespace Nif
ShaderType_NoLighting = 33 ShaderType_NoLighting = 33
}; };
struct BSShaderProperty : public NiShadeProperty enum BSShaderFlags1
{ {
unsigned int type{ 0u }, flags1{ 0u }, flags2{ 0u }; BSSFlag1_Specular = 0x00000001,
float envMapIntensity{ 0.f }; BSSFlag1_Decal = 0x04000000,
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; }
}; };
struct BSShaderLightingProperty : public BSShaderProperty struct BSSPParallaxParams
{ {
unsigned int clamp{ 0u }; float mMaxPasses{ 4.f };
void read(NIFStream* nif) override; float mScale{ 1.f };
bool wrapT() const { return clamp & 1; } void read(NIFStream* nif);
bool wrapS() const { return (clamp >> 1) & 1; }
}; };
struct BSShaderPPLightingProperty : public BSShaderLightingProperty struct BSSPRefractionParams
{ {
BSShaderTextureSetPtr textureSet; float mStrength{ 0.f };
struct RefractionSettings int32_t mPeriod{ 0 };
void read(NIFStream* nif);
};
struct BSShaderProperty : NiShadeProperty
{ {
float strength{ 0.f }; uint32_t mType{ 0u }, mShaderFlags1{ 0u }, mShaderFlags2{ 0u };
int period{ 0 }; float mEnvMapScale{ 0.f };
std::vector<uint32_t> mShaderFlags1Hashes, mShaderFlags2Hashes;
osg::Vec2f mUVOffset, mUVScale;
void read(NIFStream* nif) override;
// 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 ParallaxSettings
struct BSShaderLightingProperty : BSShaderProperty
{ {
float passes{ 0.f }; uint32_t mClamp{ 3 };
float scale{ 0.f };
void read(NIFStream* nif) override;
bool wrapT() const { return mClamp & 1; }
bool wrapS() const { return mClamp & 2; }
}; };
RefractionSettings refraction;
ParallaxSettings parallax; struct BSShaderPPLightingProperty : BSShaderLightingProperty
{
BSShaderTextureSetPtr mTextureSet;
BSSPRefractionParams mRefraction;
BSSPParallaxParams mParallax;
osg::Vec4f mEmissiveColor;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
struct BSShaderNoLightingProperty : public BSShaderLightingProperty struct BSShaderNoLightingProperty : BSShaderLightingProperty
{ {
std::string filename; std::string mFilename;
osg::Vec4f falloffParams; osg::Vec4f mFalloffParams;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
enum class BSLightingShaderType : unsigned int enum class BSLightingShaderType : uint32_t
{ {
ShaderType_Default = 0, ShaderType_Default = 0,
ShaderType_EnvMap = 1, ShaderType_EnvMap = 1,
@ -204,120 +193,157 @@ namespace Nif
ShaderType_Cloud = 17, ShaderType_Cloud = 17,
ShaderType_LODNoise = 18, ShaderType_LODNoise = 18,
ShaderType_MultitexLandLODBlend = 19, ShaderType_MultitexLandLODBlend = 19,
ShaderType_Dismemberment = 20 ShaderType_Dismemberment = 20,
ShaderType_Terrain = 21, // FO76+, technically 17
}; };
struct BSLightingShaderProperty : public BSShaderProperty enum BSLightingShaderFlags1
{ {
BSShaderTextureSetPtr mTextureSet; BSLSFlag1_Falloff = 0x00000040,
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;
}; };
struct BSEffectShaderProperty : public BSShaderProperty enum BSLightingShaderFlags2
{ {
osg::Vec2f mUVOffset, mUVScale; BSLSFlag2_DoubleSided = 0x00000010,
std::string mSourceTexture; BSLSFlag2_TreeAnim = 0x20000000,
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; }
}; };
struct NiDitherProperty : public Property struct BSSPLuminanceParams
{
unsigned short flags;
void read(NIFStream* nif) override
{ {
Property::read(nif); float mLumEmittance;
flags = nif->getUShort(); 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); float mSpecScale;
flags = nif->getUShort(); float mSpecPower;
testFunction = (flags >> 2) & 0x7; float mMinVar;
if (nif->getVersion() >= NIFStream::generateVersion(4, 1, 0, 12) float mEnvMapScale;
&& nif->getVersion() <= NIFFile::NIFVersion::VER_OB) float mFresnelPower;
testFunction = nif->getUInt(); float mMetalness;
}
bool depthTest() const { return flags & 1; } void read(NIFStream* nif);
bool depthWrite() const { return (flags >> 1) & 1; }
}; };
struct NiSpecularProperty : public Property struct BSSPMLParallaxParams
{ {
unsigned short flags; float mInnerLayerThickness;
void read(NIFStream* nif) override float mRefractionScale;
{ osg::Vec2f mInnerLayerTextureScale;
Property::read(nif); float mEnvMapScale;
flags = nif->getUShort();
}
bool isEnabled() const { return flags & 1; } void read(NIFStream* nif);
}; };
struct NiWireframeProperty : public Property struct BSSPTranslucencyParams
{ {
unsigned short flags; osg::Vec3f mSubsurfaceColor;
void read(NIFStream* nif) override float mTransmissiveScale;
{ float mTurbulence;
Property::read(nif); bool mThickObject;
flags = nif->getUShort(); bool mMixAlbedo;
}
bool isEnabled() const { return flags & 1; } void read(NIFStream* nif);
}; };
// The rest are all struct-based struct BSLightingShaderProperty : BSShaderProperty
template <typename T>
struct StructPropT : Property
{ {
T data; BSShaderTextureSetPtr mTextureSet;
unsigned short flags; 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 void read(NIFStream* nif) override;
{ void post(Reader& nif) override;
Property::read(nif);
flags = nif->getUShort(); bool doubleSided() const { return mShaderFlags2 & BSLSFlag2_DoubleSided; }
data.read(nif); bool treeAnim() const { return mShaderFlags2 & BSLSFlag2_TreeAnim; }
}
}; };
struct S_MaterialProperty struct BSEffectShaderProperty : BSShaderProperty
{ {
// The vector components are R,G,B std::string mSourceTexture;
osg::Vec3f ambient{ 1.f, 1.f, 1.f }, diffuse{ 1.f, 1.f, 1.f }; uint8_t mClamp;
osg::Vec3f specular, emissive; uint8_t mLightingInfluence;
float glossiness{ 0.f }, alpha{ 0.f }, emissiveMult{ 1.f }; 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): NiAlphaProperty blend modes (glBlendFunc):
0000 GL_ONE 0000 GL_ONE
@ -346,119 +372,132 @@ namespace Nif
http://niftools.sourceforge.net/doc/nif/NiAlphaProperty.html http://niftools.sourceforge.net/doc/nif/NiAlphaProperty.html
*/ */
// Tested against when certain flags are set (see above.) int sourceBlendMode() const { return (mFlags >> 1) & 0xF; }
unsigned char threshold; int destinationBlendMode() const { return (mFlags >> 5) & 0xF; }
int alphaTestMode() const { return (mFlags >> 10) & 0x7; }
};
void read(NIFStream* nif); struct NiDitherProperty : NiProperty
{
uint16_t mFlags;
void read(NIFStream* nif) override;
}; };
/* struct NiFogProperty : NiProperty
Docs taken from:
http://niftools.sourceforge.net/doc/nif/NiStencilProperty.html
*/
struct S_StencilProperty
{ {
// Is stencil test enabled? uint16_t mFlags;
unsigned char enabled; float mFogDepth;
osg::Vec3f mColour;
/* void read(NIFStream* nif) override;
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); 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 NiSpecularProperty : NiProperty
{
bool mEnable;
void read(NIFStream* nif) override;
};
struct NiStencilProperty : NiProperty
{
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,
}; };
struct NiAlphaProperty : public StructPropT<S_AlphaProperty> enum class DrawMode : uint32_t
{ {
bool useAlphaBlending() const { return flags & 1; } Default = 0,
int sourceBlendMode() const { return (flags >> 1) & 0xF; } CounterClockwise = 1,
int destinationBlendMode() const { return (flags >> 5) & 0xF; } Clockwise = 2,
bool noSorter() const { return (flags >> 13) & 1; } Both = 3,
};
uint16_t mFlags{ 0u };
bool mEnabled;
TestFunc mTestFunction;
uint32_t mStencilRef;
uint32_t mStencilMask;
Action mFailAction;
Action mZFailAction;
Action mPassAction;
DrawMode mDrawMode;
bool useAlphaTesting() const { return (flags >> 9) & 1; } void read(NIFStream* nif) override;
int alphaTestMode() const { return (flags >> 10) & 0x7; }
}; };
struct NiVertexColorProperty : public Property struct NiVertexColorProperty : NiProperty
{ {
enum class VertexMode : unsigned int enum class VertexMode : uint32_t
{ {
VertMode_SrcIgnore = 0, VertMode_SrcIgnore = 0,
VertMode_SrcEmissive = 1, VertMode_SrcEmissive = 1,
VertMode_SrcAmbDif = 2 VertMode_SrcAmbDif = 2
}; };
enum class LightMode : unsigned int enum class LightMode : uint32_t
{ {
LightMode_Emissive = 0, LightMode_Emissive = 0,
LightMode_EmiAmbDif = 1 LightMode_EmiAmbDif = 1
}; };
unsigned short mFlags; uint16_t mFlags;
VertexMode mVertexMode; VertexMode mVertexMode;
LightMode mLightingMode; LightMode mLightingMode;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiStencilProperty : public Property struct NiWireframeProperty : NiProperty
{ {
S_StencilProperty data; bool mEnable;
unsigned short flags{ 0u };
void read(NIFStream* nif) override void read(NIFStream* nif) override;
{
Property::read(nif);
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
flags = nif->getUShort();
data.read(nif);
}
}; };
struct NiMaterialProperty : public Property struct NiZBufferProperty : NiProperty
{ {
S_MaterialProperty data; uint16_t mFlags;
unsigned short flags{ 0u }; uint32_t mTestFunction;
void read(NIFStream* nif) override void read(NIFStream* nif) override;
{
Property::read(nif); bool depthTest() const { return mFlags & 1; }
if (nif->getVersion() >= NIFStream::generateVersion(3, 0, 0, 0) bool depthWrite() const { return mFlags & 2; }
&& nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
flags = nif->getUShort();
data.read(nif);
}
}; };
} // Namespace }
#endif #endif

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

Loading…
Cancel
Save