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,60 +433,63 @@ 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;
bool hasVertex = flags & BSVertexDesc::VertexAttribute::Vertex;
if (vertexFlag == BSVertexDesc::VertexAttribute::Vertex) bool hasTangent = flags & BSVertexDesc::VertexAttribute::Tangents;
{ bool hasUV = flags & BSVertexDesc::VertexAttribute::UVs;
nif->read(mVertex); bool hasNormal = flags & BSVertexDesc::VertexAttribute::Normals;
} bool hasVertexColor = flags & BSVertexDesc::VertexAttribute::Vertex_Colors;
bool hasSkinData = flags & BSVertexDesc::VertexAttribute::Skinned;
if ((vertexFlag | tangentsFlag) bool hasEyeData = flags & BSVertexDesc::VertexAttribute::Eye_Data;
== (BSVertexDesc::VertexAttribute::Vertex | BSVertexDesc::VertexAttribute::Tangents))
if (hasVertex)
{ {
nif->read(mBitangentX); if (fullPrecision)
} {
nif->read(mVertex);
if ((vertexFlag | tangentsFlag) == BSVertexDesc::VertexAttribute::Vertex) if (hasTangent)
{ nif->read(mBitangentX);
nif->read(mUnusedW); 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); nif->readArray(mUV);
}
if (normalsFlag) if (hasNormal)
{ {
nif->readArray(mNormal); nif->readArray(mNormal);
nif->read(mBitangentY); nif->read(mBitangentY);
if (hasTangent)
{
nif->readArray(mTangent);
nif->read(mBitangentZ);
}
} }
if ((normalsFlag | tangentsFlag) if (hasVertexColor)
== (BSVertexDesc::VertexAttribute::Normals | BSVertexDesc::VertexAttribute::Tangents)) nif->readArray(mVertColor);
{
nif->readArray(mTangent);
nif->read(mBitangentZ);
}
if (flags & BSVertexDesc::VertexAttribute::Vertex_Colors)
{
nif->readArray(mVertColors);
}
if (flags & BSVertexDesc::VertexAttribute::Skinned) 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->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
{ {
if (nif->get<bool>()) // Has texture transform nif->read(mHasTransform);
{ if (mHasTransform)
nif->getVector2(); // UV translation mTransform.read(nif);
nif->getVector2(); // UV scale
nif->getFloat(); // W axis rotation
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();
if (!numTextures) mTextures.resize(nif->get<uint32_t>());
return; for (size_t i = 0; i < mTextures.size(); i++)
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)
{
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); NiProperty::read(nif);
mFlags = nif->getUShort();
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;
compareFunc = (flags >> 12) & 0x7;
stencilRef = nif->getUInt();
stencilMask = nif->getUInt();
} }
} }
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 #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; } struct NiTexturingProperty : NiProperty
bool wrapS() const { return (clamp >> 1) & 1; } {
enum class ApplyMode : uint32_t
{
Replace = 0,
Decal = 1,
Modulate = 2,
Hilight = 3, // PS2-specific?
Hilight2 = 4, // Used for Oblivion parallax
}; };
/* Apply mode: enum TextureType
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
{ {
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;
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::Vec2f mEnvMapLumaBias;
osg::Vec4f bumpMapMatrix; osg::Vec4f mBumpMapMatrix;
float mParallaxOffset;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
struct NiFogProperty : public Property struct NiShadeProperty : NiProperty
{ {
unsigned short mFlags; uint16_t mFlags{ 0u };
float mFogDepth;
osg::Vec3f mColour;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
// These contain no other data than the 'flags' field enum class BSShaderType : uint32_t
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
{ {
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,
};
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; void read(NIFStream* nif) override;
bool specular() const { return flags1 & 1; } // These flags are shared between BSShader and BSLightingShader
bool doubleSided() const { return (flags2 >> 4) & 1; } // Shader-specific flag methods must be handled on per-record basis
bool treeAnim() const { return (flags2 >> 29) & 1; } bool specular() const { return mShaderFlags1 & BSSFlag1_Specular; }
bool decal() const { return (flags1 >> 26) & 1; } 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; void read(NIFStream* nif) override;
bool wrapT() const { return clamp & 1; } bool wrapT() const { return mClamp & 1; }
bool wrapS() const { return (clamp >> 1) & 1; } bool wrapS() const { return mClamp & 2; }
}; };
struct BSShaderPPLightingProperty : public BSShaderLightingProperty struct BSShaderPPLightingProperty : BSShaderLightingProperty
{ {
BSShaderTextureSetPtr textureSet; BSShaderTextureSetPtr mTextureSet;
struct RefractionSettings BSSPRefractionParams mRefraction;
{ BSSPParallaxParams mParallax;
float strength{ 0.f }; osg::Vec4f mEmissiveColor;
int period{ 0 };
};
struct ParallaxSettings
{
float passes{ 0.f };
float scale{ 0.f };
};
RefractionSettings refraction;
ParallaxSettings parallax;
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; float mLumEmittance;
void read(NIFStream* nif) override float mExposureOffset;
{ float mFinalExposureMin, mFinalExposureMax;
Property::read(nif);
flags = nif->getUShort(); void read(NIFStream* nif);
}
}; };
struct NiZBufferProperty : public Property struct BSSPWetnessParams
{ {
unsigned short flags; float mSpecScale;
unsigned int testFunction; float mSpecPower;
void read(NIFStream* nif) override float mMinVar;
{ float mEnvMapScale;
Property::read(nif); float mFresnelPower;
flags = nif->getUShort(); float mMetalness;
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; }
bool depthWrite() const { return (flags >> 1) & 1; } void read(NIFStream* nif);
}; };
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; }
};
struct NiDitherProperty : NiProperty
{
uint16_t mFlags;
void read(NIFStream* nif); 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 NiAlphaProperty : public StructPropT<S_AlphaProperty> struct NiSpecularProperty : NiProperty
{ {
bool useAlphaBlending() const { return flags & 1; } bool mEnable;
int sourceBlendMode() const { return (flags >> 1) & 0xF; }
int destinationBlendMode() const { return (flags >> 5) & 0xF; }
bool noSorter() const { return (flags >> 13) & 1; }
bool useAlphaTesting() const { return (flags >> 9) & 1; } void read(NIFStream* nif) override;
int alphaTestMode() const { return (flags >> 10) & 0x7; }
}; };
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_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