diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 06f2110e69..004e45765c 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -38,7 +38,7 @@ bool isNIF(const std::filesystem::path& filename) /// See if the file has the "bsa" extension. bool isBSA(const std::filesystem::path& filename) { - return hasExtension(filename, ".bsa"); + return hasExtension(filename, ".bsa") || hasExtension(filename, ".ba2"); } std::unique_ptr makeBsaArchive(const std::filesystem::path& path) @@ -216,7 +216,7 @@ int main(int argc, char** argv) else { std::cerr << "ERROR: \"" << Files::pathToUnicodeString(path) - << "\" is not a nif file, bsa file, or directory!" << std::endl; + << "\" is not a nif file, bsa/ba2 file, or directory!" << std::endl; } } catch (std::exception& e) diff --git a/apps/openmw_test_suite/nifosg/testnifloader.cpp b/apps/openmw_test_suite/nifosg/testnifloader.cpp index c8bc57bf4b..a82fba15ca 100644 --- a/apps/openmw_test_suite/nifosg/testnifloader.cpp +++ b/apps/openmw_test_suite/nifosg/testnifloader.cpp @@ -187,10 +187,10 @@ osg::Group { init(node); Nif::BSShaderPPLightingProperty property; property.recType = Nif::RC_BSShaderPPLightingProperty; - property.textureSet = nullptr; + property.mTextureSet = nullptr; property.mController = nullptr; - property.type = GetParam().mShaderType; - node.mProperties.push_back(Nif::RecordPtrT(&property)); + property.mType = GetParam().mShaderType; + node.mProperties.push_back(Nif::RecordPtrT(&property)); Nif::NIFFile file("test.nif"); file.mRoots.push_back(&node); auto result = Loader::load(file, &mImageManager); @@ -217,8 +217,8 @@ osg::Group { property.recType = Nif::RC_BSLightingShaderProperty; property.mTextureSet = nullptr; property.mController = nullptr; - property.type = GetParam().mShaderType; - node.mProperties.push_back(Nif::RecordPtrT(&property)); + property.mType = GetParam().mShaderType; + node.mProperties.push_back(Nif::RecordPtrT(&property)); Nif::NIFFile file("test.nif"); file.mRoots.push_back(&node); auto result = Loader::load(file, &mImageManager); diff --git a/components/nif/effect.cpp b/components/nif/effect.cpp index 8db6de06d4..ae4c7947cb 100644 --- a/components/nif/effect.cpp +++ b/components/nif/effect.cpp @@ -26,27 +26,27 @@ namespace Nif { NiDynamicEffect::read(nif); - mDimmer = nif->getFloat(); - mAmbient = nif->getVector3(); - mDiffuse = nif->getVector3(); - mSpecular = nif->getVector3(); + nif->read(mDimmer); + nif->read(mAmbient); + nif->read(mDiffuse); + nif->read(mSpecular); } void NiPointLight::read(NIFStream* nif) { NiLight::read(nif); - mConstantAttenuation = nif->getFloat(); - mLinearAttenuation = nif->getFloat(); - mQuadraticAttenuation = nif->getFloat(); + nif->read(mConstantAttenuation); + nif->read(mLinearAttenuation); + nif->read(mQuadraticAttenuation); } void NiSpotLight::read(NIFStream* nif) { NiPointLight::read(nif); - mCutoff = nif->getFloat(); - mExponent = nif->getFloat(); + nif->read(mCutoff); + nif->read(mExponent); } void NiTextureEffect::read(NIFStream* nif) diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 8a4ec847fc..95205c4fda 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -146,16 +146,6 @@ namespace Nif /// Read a sequence of null-terminated strings std::string getStringPalette(); - - /// DEPRECATED: Use read() or get() - char getChar() { return get(); } - unsigned short getUShort() { return get(); } - int getInt() { return get(); } - unsigned int getUInt() { return get(); } - float getFloat() { return get(); } - osg::Vec2f getVector2() { return get(); } - osg::Vec3f getVector3() { return get(); } - osg::Vec4f getVector4() { return get(); } }; template <> diff --git a/components/nif/node.cpp b/components/nif/node.cpp index 328546b7c6..ac99a06a1b 100644 --- a/components/nif/node.cpp +++ b/components/nif/node.cpp @@ -433,60 +433,63 @@ namespace Nif void BSVertexData::read(NIFStream* nif, uint16_t flags) { - uint16_t vertexFlag = flags & BSVertexDesc::VertexAttribute::Vertex; - uint16_t tangentsFlag = flags & BSVertexDesc::VertexAttribute::Tangents; - uint16_t UVsFlag = flags & BSVertexDesc::VertexAttribute::UVs; - uint16_t normalsFlag = flags & BSVertexDesc::VertexAttribute::Normals; - - if (vertexFlag == BSVertexDesc::VertexAttribute::Vertex) - { - nif->read(mVertex); - } - - if ((vertexFlag | tangentsFlag) - == (BSVertexDesc::VertexAttribute::Vertex | BSVertexDesc::VertexAttribute::Tangents)) + bool fullPrecision = true; + if (nif->getBethVersion() != NIFFile::BethVersion::BETHVER_SSE) + fullPrecision = flags & BSVertexDesc::VertexAttribute::Full_Precision; + + bool hasVertex = flags & BSVertexDesc::VertexAttribute::Vertex; + bool hasTangent = flags & BSVertexDesc::VertexAttribute::Tangents; + bool hasUV = flags & BSVertexDesc::VertexAttribute::UVs; + bool hasNormal = flags & BSVertexDesc::VertexAttribute::Normals; + bool hasVertexColor = flags & BSVertexDesc::VertexAttribute::Vertex_Colors; + bool hasSkinData = flags & BSVertexDesc::VertexAttribute::Skinned; + bool hasEyeData = flags & BSVertexDesc::VertexAttribute::Eye_Data; + + if (hasVertex) { - nif->read(mBitangentX); - } - - if ((vertexFlag | tangentsFlag) == BSVertexDesc::VertexAttribute::Vertex) - { - nif->read(mUnusedW); + if (fullPrecision) + { + nif->read(mVertex); + if (hasTangent) + nif->read(mBitangentX); + else + nif->skip(4); // Unused + } + else + { + nif->readArray(mHalfVertex); + if (hasTangent) + nif->read(mHalfBitangentX); + else + nif->skip(2); // Unused + } } - if (UVsFlag == BSVertexDesc::VertexAttribute::UVs) - { + if (hasUV) nif->readArray(mUV); - } - if (normalsFlag) + if (hasNormal) { nif->readArray(mNormal); nif->read(mBitangentY); + if (hasTangent) + { + nif->readArray(mTangent); + nif->read(mBitangentZ); + } } - if ((normalsFlag | tangentsFlag) - == (BSVertexDesc::VertexAttribute::Normals | BSVertexDesc::VertexAttribute::Tangents)) - { - nif->readArray(mTangent); - nif->read(mBitangentZ); - } - - if (flags & BSVertexDesc::VertexAttribute::Vertex_Colors) - { - nif->readArray(mVertColors); - } + if (hasVertexColor) + nif->readArray(mVertColor); - if (flags & BSVertexDesc::VertexAttribute::Skinned) + if (hasSkinData) { nif->readArray(mBoneWeights); nif->readArray(mBoneIndices); } - if (flags & BSVertexDesc::VertexAttribute::Eye_Data) - { + if (hasEyeData) nif->read(mEyeData); - } } void BSValueNode::read(NIFStream* nif) diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 7d851051e0..0d2edad9a6 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -82,7 +82,7 @@ namespace Nif uint32_t mFlags; NiTransform mTransform; osg::Vec3f mVelocity; - PropertyList mProperties; + NiPropertyList mProperties; BoundingVolume mBounds; NiCollisionObjectPtr mCollision; // Parent nodes for the node. Only types derived from NiNode can be parents. @@ -339,14 +339,15 @@ namespace Nif struct BSVertexData { osg::Vec3f mVertex; + std::array mHalfVertex; float mBitangentX; - uint32_t mUnusedW; + Misc::float16_t mHalfBitangentX; std::array mUV; std::array mNormal; char mBitangentY; std::array mTangent; char mBitangentZ; - std::array mVertColors; + std::array mVertColor; std::array mBoneWeights; std::array mBoneIndices; float mEyeData; diff --git a/components/nif/property.cpp b/components/nif/property.cpp index 3569fd55cc..fbdb273cfd 100644 --- a/components/nif/property.cpp +++ b/components/nif/property.cpp @@ -6,192 +6,368 @@ namespace Nif { + void NiTextureTransform::read(NIFStream* nif) + { + nif->read(mOffset); + nif->read(mScale); + nif->read(mRotation); + mTransformMethod = static_cast(nif->get()); + nif->read(mOrigin); + } + void NiTexturingProperty::Texture::read(NIFStream* nif) { - nif->read(inUse); - if (!inUse) + nif->read(mEnabled); + if (!mEnabled) return; - texture.read(nif); + if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13)) + mSourceTexture.read(nif); + if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) { - clamp = nif->getInt(); - nif->skip(4); // Filter mode. Ignoring because global filtering settings are more sensible + nif->read(mClamp); + nif->read(mFilter); } else { - clamp = nif->getUShort() & 0xF; + uint16_t flags; + nif->read(flags); + mClamp = flags & 0xF; + mFilter = (flags >> 4) & 0xF; } - // Max anisotropy. I assume we'll always only use the global anisotropy setting. + if (nif->getVersion() >= NIFStream::generateVersion(20, 5, 0, 4)) - nif->getUShort(); + nif->read(mMaxAnisotropy); if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) - uvSet = nif->getUInt(); + nif->read(mUVSet); - // Two PS2-specific shorts. - if (nif->getVersion() < NIFStream::generateVersion(10, 4, 0, 2)) + // PS2 filtering settings + if (nif->getVersion() <= NIFStream::generateVersion(10, 4, 0, 1)) nif->skip(4); - if (nif->getVersion() <= NIFStream::generateVersion(4, 1, 0, 18)) - nif->skip(2); // Unknown short - else if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) + + if (nif->getVersion() <= NIFStream::generateVersion(4, 1, 0, 12)) + nif->skip(2); // Unknown + + if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) { - if (nif->get()) // Has texture transform - { - nif->getVector2(); // UV translation - nif->getVector2(); // UV scale - nif->getFloat(); // W axis rotation - nif->getUInt(); // Transform method - nif->getVector2(); // Texture rotation origin - } + nif->read(mHasTransform); + if (mHasTransform) + mTransform.read(nif); } } void NiTexturingProperty::Texture::post(Reader& nif) { - texture.post(nif); + mSourceTexture.post(nif); } void NiTexturingProperty::read(NIFStream* nif) { - Property::read(nif); + NiProperty::read(nif); + if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD || nif->getVersion() >= NIFStream::generateVersion(20, 1, 0, 2)) - flags = nif->getUShort(); + nif->read(mFlags); if (nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 1)) - apply = nif->getUInt(); - - unsigned int numTextures = nif->getUInt(); + mApplyMode = static_cast(nif->get()); - if (!numTextures) - return; - - textures.resize(numTextures); - for (unsigned int i = 0; i < numTextures; i++) + mTextures.resize(nif->get()); + for (size_t i = 0; i < mTextures.size(); i++) { - textures[i].read(nif); - if (i == 5 && textures[5].inUse) // Bump map settings + mTextures[i].read(nif); + + if (i == 5 && mTextures[5].mEnabled) { - envMapLumaBias = nif->getVector2(); - bumpMapMatrix = nif->getVector4(); + nif->read(mEnvMapLumaBias); + nif->read(mBumpMapMatrix); } - else if (i == 7 && textures[7].inUse && nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5)) - /*float parallaxOffset = */ nif->getFloat(); + else if (i == 7 && mTextures[7].mEnabled && nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5)) + nif->read(mParallaxOffset); } if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) { - unsigned int numShaderTextures = nif->getUInt(); - shaderTextures.resize(numShaderTextures); - for (unsigned int i = 0; i < numShaderTextures; i++) + mShaderTextures.resize(nif->get()); + mShaderIds.resize(mShaderTextures.size()); + for (size_t i = 0; i < mShaderTextures.size(); i++) { - shaderTextures[i].read(nif); - if (shaderTextures[i].inUse) - nif->getUInt(); // Unique identifier + mShaderTextures[i].read(nif); + if (mShaderTextures[i].mEnabled) + nif->read(mShaderIds[i]); } } } void NiTexturingProperty::post(Reader& nif) { - Property::post(nif); - for (size_t i = 0; i < textures.size(); i++) - textures[i].post(nif); - for (size_t i = 0; i < shaderTextures.size(); i++) - shaderTextures[i].post(nif); + NiProperty::post(nif); + + for (Texture& tex : mTextures) + tex.post(nif); + for (Texture& tex : mShaderTextures) + tex.post(nif); + } + + void BSSPParallaxParams::read(NIFStream* nif) + { + nif->read(mMaxPasses); + nif->read(mScale); + } + + void BSSPRefractionParams::read(NIFStream* nif) + { + nif->read(mStrength); + nif->read(mPeriod); } void BSShaderProperty::read(NIFStream* nif) { + if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_F76 && recType == RC_BSLightingShaderProperty) + nif->read(mType); + NiShadeProperty::read(nif); - if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) + + if (nif->getUserVersion() <= 11) { - type = nif->getUInt(); - flags1 = nif->getUInt(); - flags2 = nif->getUInt(); - envMapIntensity = nif->getFloat(); + nif->read(mType); + nif->read(mShaderFlags1); + nif->read(mShaderFlags2); + nif->read(mEnvMapScale); + return; + } + + if (!mName.empty() && nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76) + return; + + if (nif->getBethVersion() <= 131) + { + nif->read(mShaderFlags1); + nif->read(mShaderFlags2); + } + else + { + uint32_t numShaderFlags1 = 0, numShaderFlags2 = 0; + nif->read(numShaderFlags1); + if (nif->getBethVersion() >= 152) + nif->read(numShaderFlags2); + nif->readVector(mShaderFlags1Hashes, numShaderFlags1); + nif->readVector(mShaderFlags2Hashes, numShaderFlags2); + if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76 && recType == RC_BSLightingShaderProperty) + { + nif->read(mType); + + // Remap FO76+ shader types to FO4 system so that we can actually use them + // TODO: NifTools spec doesn't do anything about the misplaced EyeEnvmap. Bug or feature? + switch (mType) + { + case 3: + mType = static_cast(BSLightingShaderType::ShaderType_FaceTint); + break; + case 4: + mType = static_cast(BSLightingShaderType::ShaderType_SkinTint); + break; + case 5: + mType = static_cast(BSLightingShaderType::ShaderType_HairTint); + break; + case 12: + mType = static_cast(BSLightingShaderType::ShaderType_EyeEnvmap); + break; + case 17: + mType = static_cast(BSLightingShaderType::ShaderType_Terrain); + break; + default: + break; + } + } } + + nif->read(mUVOffset); + nif->read(mUVScale); } void BSShaderLightingProperty::read(NIFStream* nif) { BSShaderProperty::read(nif); - if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) - clamp = nif->getUInt(); + + if (nif->getUserVersion() <= 11) + nif->read(mClamp); } void BSShaderPPLightingProperty::read(NIFStream* nif) { BSShaderLightingProperty::read(nif); - textureSet.read(nif); - if (nif->getBethVersion() <= 14) - return; - refraction.strength = nif->getFloat(); - refraction.period = nif->getInt(); - if (nif->getBethVersion() <= 24) - return; - parallax.passes = nif->getFloat(); - parallax.scale = nif->getFloat(); + + mTextureSet.read(nif); + if (nif->getUserVersion() == 11) + { + if (nif->getBethVersion() >= 15) + mRefraction.read(nif); + if (nif->getBethVersion() >= 25) + mParallax.read(nif); + } + else if (nif->getUserVersion() >= 12) + nif->read(mEmissiveColor); } void BSShaderPPLightingProperty::post(Reader& nif) { BSShaderLightingProperty::post(nif); - textureSet.post(nif); + + mTextureSet.post(nif); } void BSShaderNoLightingProperty::read(NIFStream* nif) { BSShaderLightingProperty::read(nif); - filename = nif->getSizedString(); + + mFilename = nif->getSizedString(); if (nif->getBethVersion() >= 27) - falloffParams = nif->getVector4(); + nif->read(mFalloffParams); + } + + void BSSPLuminanceParams::read(NIFStream* nif) + { + nif->read(mLumEmittance); + nif->read(mExposureOffset); + nif->read(mFinalExposureMin); + nif->read(mFinalExposureMax); + } + + void BSSPWetnessParams::read(NIFStream* nif) + { + nif->read(mSpecScale); + nif->read(mSpecPower); + nif->read(mMinVar); + if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_FO4) + nif->read(mEnvMapScale); + nif->read(mFresnelPower); + nif->read(mMetalness); + if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO4) + nif->skip(4); // Unknown + if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76) + nif->skip(4); // Unknown + } + + void BSSPMLParallaxParams::read(NIFStream* nif) + { + nif->read(mInnerLayerThickness); + nif->read(mRefractionScale); + nif->read(mInnerLayerTextureScale); + nif->read(mEnvMapScale); + } + + void BSSPTranslucencyParams::read(NIFStream* nif) + { + nif->read(mSubsurfaceColor); + nif->read(mTransmissiveScale); + nif->read(mTurbulence); + nif->read(mThickObject); + nif->read(mMixAlbedo); } void BSLightingShaderProperty::read(NIFStream* nif) { - type = nif->getUInt(); BSShaderProperty::read(nif); - flags1 = nif->getUInt(); - flags2 = nif->getUInt(); - nif->skip(8); // UV offset - nif->skip(8); // UV scale + + if (!mName.empty() && nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76) + return; + mTextureSet.read(nif); - mEmissive = nif->getVector3(); - mEmissiveMult = nif->getFloat(); - mClamp = nif->getUInt(); - mAlpha = nif->getFloat(); - nif->getFloat(); // Refraction strength - mGlossiness = nif->getFloat(); - mSpecular = nif->getVector3(); - mSpecStrength = nif->getFloat(); - nif->skip(8); // Lighting effects - switch (static_cast(type)) + nif->read(mEmissive); + nif->read(mEmissiveMult); + + if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4) + nif->read(mRootMaterial); + + if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_STF) + nif->skip(4); // Unknown float + + nif->read(mClamp); + nif->read(mAlpha); + nif->read(mRefractionStrength); + + if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4) + nif->read(mGlossiness); + else + nif->read(mSmoothness); + + nif->read(mSpecular); + nif->read(mSpecStrength); + + if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4) + nif->readArray(mLightingEffects); + else if (nif->getBethVersion() <= 139) + { + nif->read(mSubsurfaceRolloff); + nif->read(mRimlightPower); + if (mRimlightPower == std::numeric_limits::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() != 0) + { + mTextureArrays.resize(nif->get()); + for (std::vector& textureArray : mTextureArrays) + nif->getSizedStrings(textureArray, nif->get()); + } + } + + if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_STF) + { + nif->skip(4); // Unknown + nif->skip(4); // Unknown + nif->skip(2); // Unknown + } + + switch (static_cast(mType)) { case BSLightingShaderType::ShaderType_EnvMap: - nif->skip(4); // Environment map scale + if (nif->getBethVersion() <= 139) + nif->read(mEnvMapScale); + if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4) + { + nif->read(mUseSSR); + nif->read(mWetnessUseSSR); + } break; case BSLightingShaderType::ShaderType_SkinTint: + nif->read(mSkinTintColor); + if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO4) + nif->read(mSkinTintAlpha); + break; case BSLightingShaderType::ShaderType_HairTint: - nif->skip(12); // Tint color + nif->read(mHairTintColor); break; case BSLightingShaderType::ShaderType_ParallaxOcc: - nif->skip(4); // Max passes - nif->skip(4); // Scale + mParallax.read(nif); break; case BSLightingShaderType::ShaderType_MultiLayerParallax: - nif->skip(4); // Inner layer thickness - nif->skip(4); // Refraction scale - nif->skip(8); // Inner layer texture scale - nif->skip(4); // Environment map strength + mMultiLayerParallax.read(nif); break; case BSLightingShaderType::ShaderType_SparkleSnow: - nif->skip(16); // Sparkle parameters + nif->read(mSparkle); break; case BSLightingShaderType::ShaderType_EyeEnvmap: - nif->skip(4); // Cube map scale - nif->skip(12); // Left eye cube map offset - nif->skip(12); // Right eye cube map offset + nif->read(mCubeMapScale); + nif->read(mLeftEyeReflectionCenter); + nif->read(mRightEyeReflectionCenter); break; default: break; @@ -201,97 +377,185 @@ namespace Nif void BSLightingShaderProperty::post(Reader& nif) { BSShaderProperty::post(nif); + mTextureSet.post(nif); } void BSEffectShaderProperty::read(NIFStream* nif) { BSShaderProperty::read(nif); - flags1 = nif->getUInt(); - flags2 = nif->getUInt(); - mUVOffset = nif->getVector2(); - mUVScale = nif->getVector2(); + + if (!mName.empty() && nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76) + return; + mSourceTexture = nif->getSizedString(); - unsigned int miscParams = nif->getUInt(); + + if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_STF) + nif->skip(4); // Unknown + + uint32_t miscParams = nif->get(); mClamp = miscParams & 0xFF; mLightingInfluence = (miscParams >> 8) & 0xFF; mEnvMapMinLOD = (miscParams >> 16) & 0xFF; - mFalloffParams = nif->getVector4(); - mBaseColor = nif->getVector4(); - mBaseColorScale = nif->getFloat(); - mFalloffDepth = nif->getFloat(); + nif->read(mFalloffParams); + + if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76) + nif->read(mRefractionPower); + + nif->read(mBaseColor); + nif->read(mBaseColorScale); + nif->read(mFalloffDepth); mGreyscaleTexture = nif->getSizedString(); + + if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4) + { + mEnvMapTexture = nif->getSizedString(); + mNormalTexture = nif->getSizedString(); + mEnvMaskTexture = nif->getSizedString(); + nif->read(mEnvMapScale); + } + + if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_F76) + { + nif->read(mRefractionPower); + mReflectanceTexture = nif->getSizedString(); + mLightingTexture = nif->getSizedString(); + nif->read(mEmittanceColor); + mEmitGradientTexture = nif->getSizedString(); + mLuminance.read(nif); + } + + if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_STF) + { + nif->skip(7); // Unknown bytes + nif->skip(6 * sizeof(float)); // Unknown floats + nif->skip(1); // Unknown byte + } + } + + void NiAlphaProperty::read(NIFStream* nif) + { + NiProperty::read(nif); + + nif->read(mFlags); + nif->read(mThreshold); + } + + void NiDitherProperty::read(NIFStream* nif) + { + NiProperty::read(nif); + + nif->read(mFlags); } void NiFogProperty::read(NIFStream* nif) { - Property::read(nif); - mFlags = nif->getUShort(); - mFogDepth = nif->getFloat(); - mColour = nif->getVector3(); + NiProperty::read(nif); + + nif->read(mFlags); + nif->read(mFogDepth); + nif->read(mColour); } - void S_MaterialProperty::read(NIFStream* nif) + void NiMaterialProperty::read(NIFStream* nif) { + NiProperty::read(nif); + + if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD) + nif->read(mFlags); if (nif->getBethVersion() < 26) { - ambient = nif->getVector3(); - diffuse = nif->getVector3(); + nif->read(mAmbient); + nif->read(mDiffuse); } - specular = nif->getVector3(); - emissive = nif->getVector3(); - glossiness = nif->getFloat(); - alpha = nif->getFloat(); + nif->read(mSpecular); + nif->read(mEmissive); + nif->read(mGlossiness); + nif->read(mAlpha); if (nif->getBethVersion() >= 22) - emissiveMult = nif->getFloat(); + nif->read(mEmissiveMult); } - void NiVertexColorProperty::read(NIFStream* nif) + void NiShadeProperty::read(NIFStream* nif) + { + NiProperty::read(nif); + + if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) + nif->read(mFlags); + } + + void NiSpecularProperty::read(NIFStream* nif) + { + NiProperty::read(nif); + + mEnable = nif->get() & 1; + } + + void NiStencilProperty::read(NIFStream* nif) { - Property::read(nif); - mFlags = nif->getUShort(); + NiProperty::read(nif); + if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) { - mVertexMode = static_cast(nif->getUInt()); - mLightingMode = static_cast(nif->getUInt()); + if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD) + nif->read(mFlags); + mEnabled = nif->get() != 0; + mTestFunction = static_cast(nif->get()); + nif->read(mStencilRef); + nif->read(mStencilMask); + mFailAction = static_cast(nif->get()); + mZFailAction = static_cast(nif->get()); + mPassAction = static_cast(nif->get()); + mDrawMode = static_cast(nif->get()); } else { - mVertexMode = static_cast((mFlags >> 4) & 0x3); - mLightingMode = static_cast((mFlags >> 3) & 0x1); + nif->read(mFlags); + mEnabled = mFlags & 0x1; + mFailAction = static_cast((mFlags >> 1) & 0x7); + mZFailAction = static_cast((mFlags >> 4) & 0x7); + mPassAction = static_cast((mFlags >> 7) & 0x7); + mDrawMode = static_cast((mFlags >> 10) & 0x3); + mTestFunction = static_cast((mFlags >> 12) & 0x7); + nif->read(mStencilRef); + nif->read(mStencilMask); } } - void S_AlphaProperty::read(NIFStream* nif) + void NiVertexColorProperty::read(NIFStream* nif) { - threshold = nif->getChar(); - } + NiProperty::read(nif); - void S_StencilProperty::read(NIFStream* nif) - { + nif->read(mFlags); if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) { - enabled = nif->getChar(); - compareFunc = nif->getInt(); - stencilRef = nif->getUInt(); - stencilMask = nif->getUInt(); - failAction = nif->getInt(); - zFailAction = nif->getInt(); - zPassAction = nif->getInt(); - drawMode = nif->getInt(); + mVertexMode = static_cast(nif->get()); + mLightingMode = static_cast(nif->get()); } else { - unsigned short flags = nif->getUShort(); - enabled = flags & 0x1; - failAction = (flags >> 1) & 0x7; - zFailAction = (flags >> 4) & 0x7; - zPassAction = (flags >> 7) & 0x7; - drawMode = (flags >> 10) & 0x3; - compareFunc = (flags >> 12) & 0x7; - stencilRef = nif->getUInt(); - stencilMask = nif->getUInt(); + mVertexMode = static_cast((mFlags >> 4) & 0x3); + mLightingMode = static_cast((mFlags >> 3) & 0x1); } } + void NiWireframeProperty::read(NIFStream* nif) + { + NiProperty::read(nif); + + mEnable = nif->get() & 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; + } + } diff --git a/components/nif/property.hpp b/components/nif/property.hpp index d9574f31d6..797e02c40d 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -1,26 +1,3 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2010 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: https://openmw.org/ - - This file (property.h) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - https://www.gnu.org/licenses/ . - - */ - #ifndef OPENMW_COMPONENTS_NIF_PROPERTY_HPP #define OPENMW_COMPONENTS_NIF_PROPERTY_HPP @@ -29,57 +6,42 @@ namespace Nif { - struct Property : public NiObjectNET + struct NiProperty : NiObjectNET { }; - struct NiTexturingProperty : public Property + struct NiTextureTransform { - unsigned short flags{ 0u }; - - // A sub-texture - struct Texture + enum class Method : uint32_t { - /* Clamp mode - 0 - clampS clampT - 1 - clampS wrapT - 2 - wrapS clampT - 3 - wrapS wrapT - */ - - bool inUse; - NiSourceTexturePtr texture; + // Back = inverse of mOrigin. + // FromMaya = inverse of the V axis with a positive translation along V of 1 unit. + MayaLegacy = 0, // mOrigin * mRotation * Back * mOffset * mScale + Max = 1, // mOrigin * mScale * mRotation * mOffset * Back + Maya = 2, // mOrigin * mRotation * Back * FromMaya * mOffset * mScale + }; - unsigned int clamp, uvSet; + osg::Vec2f mOffset; + osg::Vec2f mScale; + float mRotation; + Method mTransformMethod; + osg::Vec2f mOrigin; - void read(NIFStream* nif); - void post(Reader& nif); + void read(NIFStream* nif); + }; - bool wrapT() const { return clamp & 1; } - bool wrapS() const { return (clamp >> 1) & 1; } + struct NiTexturingProperty : NiProperty + { + enum class ApplyMode : uint32_t + { + Replace = 0, + Decal = 1, + Modulate = 2, + Hilight = 3, // PS2-specific? + Hilight2 = 4, // Used for Oblivion parallax }; - /* Apply mode: - 0 - replace - 1 - decal - 2 - modulate - 3 - hilight // These two are for PS2 only? - 4 - hilight2 - */ - unsigned int apply{ 0 }; - - /* - * The textures in this list are as follows: - * - * 0 - Base texture - * 1 - Dark texture - * 2 - Detail texture - * 3 - Gloss texture - * 4 - Glow texture - * 5 - Bump map texture - * 6 - Decal texture - */ - enum TextureType : uint32_t + enum TextureType { BaseTexture = 0, DarkTexture = 1, @@ -90,38 +52,48 @@ namespace Nif DecalTexture = 6, }; - std::vector textures; - std::vector shaderTextures; + // A sub-texture + struct Texture + { + bool mEnabled; + NiSourceTexturePtr mSourceTexture; + uint32_t mClamp; + uint32_t mFilter; + uint16_t mMaxAnisotropy; + uint32_t mUVSet; + bool mHasTransform; + NiTextureTransform mTransform; + + void read(NIFStream* nif); + void post(Reader& nif); + + bool wrapT() const { return mClamp & 1; } + bool wrapS() const { return mClamp & 2; } + }; + + uint16_t mFlags{ 0u }; + ApplyMode mApplyMode{ ApplyMode::Modulate }; + + std::vector mTextures; + std::vector mShaderTextures; + std::vector mShaderIds; - osg::Vec2f envMapLumaBias; - osg::Vec4f bumpMapMatrix; + osg::Vec2f mEnvMapLumaBias; + osg::Vec4f mBumpMapMatrix; + float mParallaxOffset; void read(NIFStream* nif) override; void post(Reader& nif) override; }; - struct NiFogProperty : public Property + struct NiShadeProperty : NiProperty { - unsigned short mFlags; - float mFogDepth; - osg::Vec3f mColour; + uint16_t mFlags{ 0u }; void read(NIFStream* nif) override; }; - // These contain no other data than the 'flags' field - struct NiShadeProperty : public Property - { - unsigned short flags{ 0u }; - void read(NIFStream* nif) override - { - Property::read(nif); - if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) - flags = nif->getUShort(); - } - }; - - enum class BSShaderType : unsigned int + enum class BSShaderType : uint32_t { ShaderType_TallGrass = 0, ShaderType_Default = 1, @@ -133,56 +105,73 @@ namespace Nif ShaderType_NoLighting = 33 }; - struct BSShaderProperty : public NiShadeProperty + enum BSShaderFlags1 { - unsigned int type{ 0u }, flags1{ 0u }, flags2{ 0u }; - float envMapIntensity{ 0.f }; + BSSFlag1_Specular = 0x00000001, + BSSFlag1_Decal = 0x04000000, + }; + + struct BSSPParallaxParams + { + float mMaxPasses{ 4.f }; + float mScale{ 1.f }; + + void read(NIFStream* nif); + }; + + struct BSSPRefractionParams + { + float mStrength{ 0.f }; + int32_t mPeriod{ 0 }; + + void read(NIFStream* nif); + }; + + struct BSShaderProperty : NiShadeProperty + { + uint32_t mType{ 0u }, mShaderFlags1{ 0u }, mShaderFlags2{ 0u }; + float mEnvMapScale{ 0.f }; + std::vector mShaderFlags1Hashes, mShaderFlags2Hashes; + osg::Vec2f mUVOffset, mUVScale; + void read(NIFStream* nif) override; - bool specular() const { return flags1 & 1; } - bool doubleSided() const { return (flags2 >> 4) & 1; } - bool treeAnim() const { return (flags2 >> 29) & 1; } - bool decal() const { return (flags1 >> 26) & 1; } + // These flags are shared between BSShader and BSLightingShader + // Shader-specific flag methods must be handled on per-record basis + bool specular() const { return mShaderFlags1 & BSSFlag1_Specular; } + bool decal() const { return mShaderFlags1 & BSSFlag1_Decal; } }; - struct BSShaderLightingProperty : public BSShaderProperty + struct BSShaderLightingProperty : BSShaderProperty { - unsigned int clamp{ 0u }; + uint32_t mClamp{ 3 }; + void read(NIFStream* nif) override; - bool wrapT() const { return clamp & 1; } - bool wrapS() const { return (clamp >> 1) & 1; } + bool wrapT() const { return mClamp & 1; } + bool wrapS() const { return mClamp & 2; } }; - struct BSShaderPPLightingProperty : public BSShaderLightingProperty + struct BSShaderPPLightingProperty : BSShaderLightingProperty { - BSShaderTextureSetPtr textureSet; - struct RefractionSettings - { - float strength{ 0.f }; - int period{ 0 }; - }; - struct ParallaxSettings - { - float passes{ 0.f }; - float scale{ 0.f }; - }; - RefractionSettings refraction; - ParallaxSettings parallax; + BSShaderTextureSetPtr mTextureSet; + BSSPRefractionParams mRefraction; + BSSPParallaxParams mParallax; + osg::Vec4f mEmissiveColor; void read(NIFStream* nif) override; void post(Reader& nif) override; }; - struct BSShaderNoLightingProperty : public BSShaderLightingProperty + struct BSShaderNoLightingProperty : BSShaderLightingProperty { - std::string filename; - osg::Vec4f falloffParams; + std::string mFilename; + osg::Vec4f mFalloffParams; void read(NIFStream* nif) override; }; - enum class BSLightingShaderType : unsigned int + enum class BSLightingShaderType : uint32_t { ShaderType_Default = 0, ShaderType_EnvMap = 1, @@ -204,120 +193,157 @@ namespace Nif ShaderType_Cloud = 17, ShaderType_LODNoise = 18, ShaderType_MultitexLandLODBlend = 19, - ShaderType_Dismemberment = 20 + ShaderType_Dismemberment = 20, + ShaderType_Terrain = 21, // FO76+, technically 17 }; - struct BSLightingShaderProperty : public BSShaderProperty + enum BSLightingShaderFlags1 { - BSShaderTextureSetPtr mTextureSet; - unsigned int mClamp{ 0u }; - float mAlpha; - float mGlossiness; - osg::Vec3f mEmissive, mSpecular; - float mEmissiveMult, mSpecStrength; - - void read(NIFStream* nif) override; - void post(Reader& nif) override; + BSLSFlag1_Falloff = 0x00000040, }; - struct BSEffectShaderProperty : public BSShaderProperty + enum BSLightingShaderFlags2 { - osg::Vec2f mUVOffset, mUVScale; - std::string mSourceTexture; - unsigned char mClamp; - unsigned char mLightingInfluence; - unsigned char mEnvMapMinLOD; - osg::Vec4f mFalloffParams; - osg::Vec4f mBaseColor; - float mBaseColorScale; - float mFalloffDepth; - std::string mGreyscaleTexture; - - void read(NIFStream* nif) override; - - bool useFalloff() const { return (flags >> 6) & 1; } + BSLSFlag2_DoubleSided = 0x00000010, + BSLSFlag2_TreeAnim = 0x20000000, }; - struct NiDitherProperty : public Property + struct BSSPLuminanceParams { - unsigned short flags; - void read(NIFStream* nif) override - { - Property::read(nif); - flags = nif->getUShort(); - } + float mLumEmittance; + float mExposureOffset; + float mFinalExposureMin, mFinalExposureMax; + + void read(NIFStream* nif); }; - struct NiZBufferProperty : public Property + struct BSSPWetnessParams { - unsigned short flags; - unsigned int testFunction; - void read(NIFStream* nif) override - { - Property::read(nif); - flags = nif->getUShort(); - testFunction = (flags >> 2) & 0x7; - if (nif->getVersion() >= NIFStream::generateVersion(4, 1, 0, 12) - && nif->getVersion() <= NIFFile::NIFVersion::VER_OB) - testFunction = nif->getUInt(); - } - - bool depthTest() const { return flags & 1; } + float mSpecScale; + float mSpecPower; + float mMinVar; + float mEnvMapScale; + float mFresnelPower; + float mMetalness; - bool depthWrite() const { return (flags >> 1) & 1; } + void read(NIFStream* nif); }; - struct NiSpecularProperty : public Property + struct BSSPMLParallaxParams { - unsigned short flags; - void read(NIFStream* nif) override - { - Property::read(nif); - flags = nif->getUShort(); - } + float mInnerLayerThickness; + float mRefractionScale; + osg::Vec2f mInnerLayerTextureScale; + float mEnvMapScale; - bool isEnabled() const { return flags & 1; } + void read(NIFStream* nif); }; - struct NiWireframeProperty : public Property + struct BSSPTranslucencyParams { - unsigned short flags; - void read(NIFStream* nif) override - { - Property::read(nif); - flags = nif->getUShort(); - } + osg::Vec3f mSubsurfaceColor; + float mTransmissiveScale; + float mTurbulence; + bool mThickObject; + bool mMixAlbedo; - bool isEnabled() const { return flags & 1; } + void read(NIFStream* nif); }; - // The rest are all struct-based - template - struct StructPropT : Property + struct BSLightingShaderProperty : BSShaderProperty { - T data; - unsigned short flags; + BSShaderTextureSetPtr mTextureSet; + osg::Vec3f mEmissive; + float mEmissiveMult; + std::string mRootMaterial; + uint32_t mClamp; + float mAlpha; + float mRefractionStrength; + float mGlossiness{ 80.f }; + float mSmoothness{ 1.f }; + osg::Vec3f mSpecular; + float mSpecStrength; + std::array mLightingEffects; + float mSubsurfaceRolloff; + float mRimlightPower; + float mBacklightPower; + float mGrayscaleToPaletteScale{ 1.f }; + float mFresnelPower{ 5.f }; + BSSPWetnessParams mWetness; + bool mDoTranslucency{ false }; + BSSPTranslucencyParams mTranslucency; + std::vector> mTextureArrays; + BSSPLuminanceParams mLuminance; + + bool mUseSSR; + bool mWetnessUseSSR; + + osg::Vec3f mSkinTintColor; + float mSkinTintAlpha{ 1.f }; + osg::Vec3f mHairTintColor; + + BSSPParallaxParams mParallax; + BSSPMLParallaxParams mMultiLayerParallax; + osg::Vec4f mSparkle; + + float mCubeMapScale; + osg::Vec3f mLeftEyeReflectionCenter; + osg::Vec3f mRightEyeReflectionCenter; - void read(NIFStream* nif) override - { - Property::read(nif); - flags = nif->getUShort(); - data.read(nif); - } + void read(NIFStream* nif) override; + void post(Reader& nif) override; + + bool doubleSided() const { return mShaderFlags2 & BSLSFlag2_DoubleSided; } + bool treeAnim() const { return mShaderFlags2 & BSLSFlag2_TreeAnim; } }; - struct S_MaterialProperty + struct BSEffectShaderProperty : BSShaderProperty { - // The vector components are R,G,B - osg::Vec3f ambient{ 1.f, 1.f, 1.f }, diffuse{ 1.f, 1.f, 1.f }; - osg::Vec3f specular, emissive; - float glossiness{ 0.f }, alpha{ 0.f }, emissiveMult{ 1.f }; + std::string mSourceTexture; + uint8_t mClamp; + uint8_t mLightingInfluence; + uint8_t mEnvMapMinLOD; + osg::Vec4f mFalloffParams; + float mRefractionPower; + osg::Vec4f mBaseColor; + float mBaseColorScale; + float mFalloffDepth; + std::string mGreyscaleTexture; + std::string mEnvMapTexture; + std::string mNormalTexture; + std::string mEnvMaskTexture; + float mEnvMapScale; + std::string mReflectanceTexture; + std::string mLightingTexture; + osg::Vec3f mEmittanceColor; + std::string mEmitGradientTexture; + BSSPLuminanceParams mLuminance; - void read(NIFStream* nif); + void read(NIFStream* nif) override; + + bool useFalloff() const { return mShaderFlags1 & BSLSFlag1_Falloff; } + bool doubleSided() const { return mShaderFlags2 & BSLSFlag2_DoubleSided; } + bool treeAnim() const { return mShaderFlags2 & BSLSFlag2_TreeAnim; } }; - struct S_AlphaProperty + struct NiAlphaProperty : NiProperty { + enum Flags + { + Flag_Blending = 0x0001, + Flag_Testing = 0x0200, + Flag_NoSorter = 0x2000, + }; + + uint16_t mFlags; + uint8_t mThreshold; + + void read(NIFStream* nif) override; + + bool useAlphaBlending() const { return mFlags & Flag_Blending; } + bool useAlphaTesting() const { return mFlags & Flag_Testing; } + bool noSorter() const { return mFlags & Flag_NoSorter; } + /* NiAlphaProperty blend modes (glBlendFunc): 0000 GL_ONE @@ -346,119 +372,132 @@ namespace Nif http://niftools.sourceforge.net/doc/nif/NiAlphaProperty.html */ - // Tested against when certain flags are set (see above.) - unsigned char threshold; + int sourceBlendMode() const { return (mFlags >> 1) & 0xF; } + int destinationBlendMode() const { return (mFlags >> 5) & 0xF; } + int alphaTestMode() const { return (mFlags >> 10) & 0x7; } + }; + + struct NiDitherProperty : NiProperty + { + uint16_t mFlags; - void read(NIFStream* nif); + void read(NIFStream* nif) override; }; - /* - Docs taken from: - http://niftools.sourceforge.net/doc/nif/NiStencilProperty.html - */ - struct S_StencilProperty + struct NiFogProperty : NiProperty { - // Is stencil test enabled? - unsigned char enabled; + uint16_t mFlags; + float mFogDepth; + osg::Vec3f mColour; - /* - 0 TEST_NEVER - 1 TEST_LESS - 2 TEST_EQUAL - 3 TEST_LESS_EQUAL - 4 TEST_GREATER - 5 TEST_NOT_EQUAL - 6 TEST_GREATER_EQUAL - 7 TEST_NEVER (though nifskope comment says TEST_ALWAYS, but ingame it is TEST_NEVER) - */ - int compareFunc; - unsigned stencilRef; - unsigned stencilMask; - /* - Stencil test fail action, depth test fail action and depth test pass action: - 0 ACTION_KEEP - 1 ACTION_ZERO - 2 ACTION_REPLACE - 3 ACTION_INCREMENT - 4 ACTION_DECREMENT - 5 ACTION_INVERT - */ - int failAction; - int zFailAction; - int zPassAction; - /* - Face draw mode: - 0 DRAW_CCW_OR_BOTH - 1 DRAW_CCW [default] - 2 DRAW_CW - 3 DRAW_BOTH - */ - int drawMode; + void read(NIFStream* nif) override; + }; - void read(NIFStream* nif); + struct NiMaterialProperty : NiProperty + { + uint16_t mFlags{ 0u }; + osg::Vec3f mAmbient{ 1.f, 1.f, 1.f }; + osg::Vec3f mDiffuse{ 1.f, 1.f, 1.f }; + osg::Vec3f mSpecular; + osg::Vec3f mEmissive; + float mGlossiness{ 0.f }; + float mAlpha{ 0.f }; + float mEmissiveMult{ 1.f }; + + void read(NIFStream* nif) override; }; - struct NiAlphaProperty : public StructPropT + struct NiSpecularProperty : NiProperty { - bool useAlphaBlending() const { return flags & 1; } - int sourceBlendMode() const { return (flags >> 1) & 0xF; } - int destinationBlendMode() const { return (flags >> 5) & 0xF; } - bool noSorter() const { return (flags >> 13) & 1; } + bool mEnable; - bool useAlphaTesting() const { return (flags >> 9) & 1; } - int alphaTestMode() const { return (flags >> 10) & 0x7; } + void read(NIFStream* nif) override; }; - struct NiVertexColorProperty : public Property + struct NiStencilProperty : NiProperty { - enum class VertexMode : unsigned int + enum class TestFunc : uint32_t + { + Never = 0, + Less = 1, + Equal = 2, + LessEqual = 3, + Greater = 4, + NotEqual = 5, + GreaterEqual = 6, + Always = 7, + }; + + enum class Action : uint32_t + { + Keep = 0, + Zero = 1, + Replace = 2, + Increment = 3, + Decrement = 4, + Invert = 5, + }; + + enum class DrawMode : uint32_t + { + Default = 0, + CounterClockwise = 1, + Clockwise = 2, + Both = 3, + }; + + uint16_t mFlags{ 0u }; + bool mEnabled; + TestFunc mTestFunction; + uint32_t mStencilRef; + uint32_t mStencilMask; + Action mFailAction; + Action mZFailAction; + Action mPassAction; + DrawMode mDrawMode; + + void read(NIFStream* nif) override; + }; + + struct NiVertexColorProperty : NiProperty + { + enum class VertexMode : uint32_t { VertMode_SrcIgnore = 0, VertMode_SrcEmissive = 1, VertMode_SrcAmbDif = 2 }; - enum class LightMode : unsigned int + enum class LightMode : uint32_t { LightMode_Emissive = 0, LightMode_EmiAmbDif = 1 }; - unsigned short mFlags; + uint16_t mFlags; VertexMode mVertexMode; LightMode mLightingMode; void read(NIFStream* nif) override; }; - struct NiStencilProperty : public Property + struct NiWireframeProperty : NiProperty { - S_StencilProperty data; - unsigned short flags{ 0u }; + bool mEnable; - void read(NIFStream* nif) override - { - Property::read(nif); - if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD) - flags = nif->getUShort(); - data.read(nif); - } + void read(NIFStream* nif) override; }; - struct NiMaterialProperty : public Property + struct NiZBufferProperty : NiProperty { - S_MaterialProperty data; - unsigned short flags{ 0u }; + uint16_t mFlags; + uint32_t mTestFunction; - void read(NIFStream* nif) override - { - Property::read(nif); - if (nif->getVersion() >= NIFStream::generateVersion(3, 0, 0, 0) - && nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD) - flags = nif->getUShort(); - data.read(nif); - } + void read(NIFStream* nif) override; + + bool depthTest() const { return mFlags & 1; } + bool depthWrite() const { return mFlags & 2; } }; -} // Namespace +} #endif diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 627e59cf0d..54ebe0e9a2 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -112,7 +112,7 @@ namespace Nif struct NiAVObject; struct Extra; - struct Property; + struct NiProperty; struct NiUVData; struct NiPosData; struct NiVisData; @@ -191,7 +191,7 @@ namespace Nif using BSMultiBoundDataPtr = RecordPtrT; using NiAVObjectList = RecordListT; - using PropertyList = RecordListT; + using NiPropertyList = RecordListT; using ExtraList = RecordListT; using NiSourceTextureList = RecordListT; using NiInterpolatorList = RecordListT; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 05f42db05a..7a8fd6afb2 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -103,7 +103,7 @@ namespace // Collect all properties affecting the given drawable that should be handled on drawable basis rather than on the // node hierarchy above it. void collectDrawableProperties( - const Nif::NiAVObject* nifNode, const Nif::Parent* parent, std::vector& out) + const Nif::NiAVObject* nifNode, const Nif::Parent* parent, std::vector& out) { if (parent != nullptr) collectDrawableProperties(&parent->mNiNode, parent->mParent, out); @@ -412,7 +412,7 @@ namespace NifOsg { const Nif::NiStencilProperty* stencilprop = static_cast(property.getPtr()); - if (stencilprop->data.enabled != 0) + if (stencilprop->mEnabled) { hasStencilProperty = true; break; @@ -959,7 +959,7 @@ namespace NifOsg } } - void handleMaterialControllers(const Nif::Property* materialProperty, + void handleMaterialControllers(const Nif::NiProperty* materialProperty, SceneUtil::CompositeStateSetUpdater* composite, int animflags, const osg::Material* baseMaterial) { for (Nif::NiTimeControllerPtr ctrl = materialProperty->mController; !ctrl.empty(); ctrl = ctrl->mNext) @@ -1008,8 +1008,9 @@ namespace NifOsg } } - void handleTextureControllers(const Nif::Property* texProperty, SceneUtil::CompositeStateSetUpdater* composite, - Resource::ImageManager* imageManager, osg::StateSet* stateset, int animflags) + void handleTextureControllers(const Nif::NiProperty* texProperty, + SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, + osg::StateSet* stateset, int animflags) { for (Nif::NiTimeControllerPtr ctrl = texProperty->mController; !ctrl.empty(); ctrl = ctrl->mNext) { @@ -1316,7 +1317,7 @@ namespace NifOsg // localToWorldMatrix for transforming to particle space handleParticlePrograms(partctrl->mModifier, partctrl->mCollider, parentNode, partsys.get(), rf); - std::vector drawableProps; + std::vector drawableProps; collectDrawableProperties(nifNode, parent, drawableProps); applyDrawableProperties(parentNode, drawableProps, composite, true, animflags); @@ -1462,7 +1463,7 @@ namespace NifOsg // - if there are no vertex colors, we need to disable colorMode. // - there are 3 "overlapping" nif properties that all affect the osg::Material, handling them // above the actual renderable would be tedious. - std::vector drawableProps; + std::vector drawableProps; collectDrawableProperties(nifNode, parent, drawableProps); applyDrawableProperties(parentNode, drawableProps, composite, !niGeometryData->mColors.empty(), animflags); } @@ -1616,50 +1617,54 @@ namespace NifOsg } } - osg::Stencil::Function getStencilFunction(int func) + osg::Stencil::Function getStencilFunction(Nif::NiStencilProperty::TestFunc func) { + using TestFunc = Nif::NiStencilProperty::TestFunc; switch (func) { - case 0: + case TestFunc::Never: return osg::Stencil::NEVER; - case 1: + case TestFunc::Less: return osg::Stencil::LESS; - case 2: + case TestFunc::Equal: return osg::Stencil::EQUAL; - case 3: + case TestFunc::LessEqual: return osg::Stencil::LEQUAL; - case 4: + case TestFunc::Greater: return osg::Stencil::GREATER; - case 5: + case TestFunc::NotEqual: return osg::Stencil::NOTEQUAL; - case 6: + case TestFunc::GreaterEqual: return osg::Stencil::GEQUAL; - case 7: + case TestFunc::Always: return osg::Stencil::ALWAYS; default: - Log(Debug::Info) << "Unexpected stencil function: " << func << " in " << mFilename; + Log(Debug::Info) << "Unexpected stencil function: " << static_cast(func) << " in " + << mFilename; return osg::Stencil::NEVER; } } - osg::Stencil::Operation getStencilOperation(int op) + osg::Stencil::Operation getStencilOperation(Nif::NiStencilProperty::Action op) { + using Action = Nif::NiStencilProperty::Action; switch (op) { - case 0: + case Action::Keep: return osg::Stencil::KEEP; - case 1: + case Action::Zero: return osg::Stencil::ZERO; - case 2: + case Action::Replace: return osg::Stencil::REPLACE; - case 3: + case Action::Increment: return osg::Stencil::INCR; - case 4: + case Action::Decrement: return osg::Stencil::DECR; - case 5: + case Action::Invert: return osg::Stencil::INVERT; default: - Log(Debug::Info) << "Unexpected stencil operation: " << op << " in " << mFilename; + Log(Debug::Info) << "Unexpected stencil operation: " << static_cast(op) << " in " + << mFilename; return osg::Stencil::KEEP; } } @@ -1827,9 +1832,9 @@ namespace NifOsg // If this loop is changed such that the base texture isn't guaranteed to end up in texture unit 0, the // shadow casting shader will need to be updated accordingly. - for (size_t i = 0; i < texprop->textures.size(); ++i) + for (size_t i = 0; i < texprop->mTextures.size(); ++i) { - if (texprop->textures[i].inUse + if (texprop->mTextures[i].mEnabled || (i == Nif::NiTexturingProperty::BaseTexture && !texprop->mController.empty())) { switch (i) @@ -1854,10 +1859,10 @@ namespace NifOsg unsigned int uvSet = 0; // create a new texture, will later attempt to share using the SharedStateManager osg::ref_ptr texture2d; - if (texprop->textures[i].inUse) + if (texprop->mTextures[i].mEnabled) { - const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i]; - if (tex.texture.empty() && texprop->mController.empty()) + const Nif::NiTexturingProperty::Texture& tex = texprop->mTextures[i]; + if (tex.mSourceTexture.empty() && texprop->mController.empty()) { if (i == 0) Log(Debug::Warning) << "Base texture is in use but empty on shape \"" << nodeName @@ -1865,9 +1870,9 @@ namespace NifOsg continue; } - if (!tex.texture.empty()) + if (!tex.mSourceTexture.empty()) { - const Nif::NiSourceTexture* st = tex.texture.getPtr(); + const Nif::NiSourceTexture* st = tex.mSourceTexture.getPtr(); osg::ref_ptr image = handleSourceTexture(st, imageManager); texture2d = new osg::Texture2D(image); if (image) @@ -1878,7 +1883,7 @@ namespace NifOsg handleTextureWrapping(texture2d, tex.wrapS(), tex.wrapT()); - uvSet = tex.uvSet; + uvSet = tex.mUVSet; } else { @@ -1926,10 +1931,10 @@ namespace NifOsg // Bump maps offset the environment map. // Set this texture to Off by default since we can't render it with the fixed-function pipeline stateset->setTextureMode(texUnit, GL_TEXTURE_2D, osg::StateAttribute::OFF); - osg::Matrix2 bumpMapMatrix(texprop->bumpMapMatrix.x(), texprop->bumpMapMatrix.y(), - texprop->bumpMapMatrix.z(), texprop->bumpMapMatrix.w()); + osg::Matrix2 bumpMapMatrix(texprop->mBumpMapMatrix.x(), texprop->mBumpMapMatrix.y(), + texprop->mBumpMapMatrix.z(), texprop->mBumpMapMatrix.w()); stateset->addUniform(new osg::Uniform("bumpMapMatrix", bumpMapMatrix)); - stateset->addUniform(new osg::Uniform("envMapLumaBias", texprop->envMapLumaBias)); + stateset->addUniform(new osg::Uniform("envMapLumaBias", texprop->mEnvMapLumaBias)); } else if (i == Nif::NiTexturingProperty::GlossTexture) { @@ -2098,6 +2103,7 @@ namespace NifOsg case Nif::BSLightingShaderType::ShaderType_LODNoise: case Nif::BSLightingShaderType::ShaderType_MultitexLandLODBlend: case Nif::BSLightingShaderType::ShaderType_Dismemberment: + case Nif::BSLightingShaderType::ShaderType_Terrain: Log(Debug::Warning) << "Unhandled BSLightingShaderType " << type << " in " << mFilename; return "bs/default"; } @@ -2105,7 +2111,7 @@ namespace NifOsg return "bs/default"; } - void handleProperty(const Nif::Property* property, osg::Node* node, + void handleProperty(const Nif::NiProperty* property, osg::Node* node, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector& boundTextures, int animflags, bool hasStencilProperty) { @@ -2114,14 +2120,17 @@ namespace NifOsg case Nif::RC_NiStencilProperty: { const Nif::NiStencilProperty* stencilprop = static_cast(property); + osg::ref_ptr frontFace = new osg::FrontFace; - switch (stencilprop->data.drawMode) + using DrawMode = Nif::NiStencilProperty::DrawMode; + switch (stencilprop->mDrawMode) { - case 2: + case DrawMode::Clockwise: frontFace->setMode(osg::FrontFace::CLOCKWISE); break; - case 0: - case 1: + case DrawMode::Default: + case DrawMode::CounterClockwise: + case DrawMode::Both: default: frontFace->setMode(osg::FrontFace::COUNTER_CLOCKWISE); break; @@ -2130,20 +2139,20 @@ namespace NifOsg osg::StateSet* stateset = node->getOrCreateStateSet(); stateset->setAttribute(frontFace, osg::StateAttribute::ON); - stateset->setMode(GL_CULL_FACE, - stencilprop->data.drawMode == 3 ? osg::StateAttribute::OFF : osg::StateAttribute::ON); + if (stencilprop->mDrawMode == DrawMode::Both) + stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + else + stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON); - if (stencilprop->data.enabled != 0) + if (stencilprop->mEnabled) { mHasStencilProperty = true; osg::ref_ptr stencil = new osg::Stencil; - stencil->setFunction(getStencilFunction(stencilprop->data.compareFunc), - stencilprop->data.stencilRef, stencilprop->data.stencilMask); - stencil->setStencilFailOperation(getStencilOperation(stencilprop->data.failAction)); - stencil->setStencilPassAndDepthFailOperation( - getStencilOperation(stencilprop->data.zFailAction)); - stencil->setStencilPassAndDepthPassOperation( - getStencilOperation(stencilprop->data.zPassAction)); + stencil->setFunction(getStencilFunction(stencilprop->mTestFunction), stencilprop->mStencilRef, + stencilprop->mStencilMask); + stencil->setStencilFailOperation(getStencilOperation(stencilprop->mFailAction)); + stencil->setStencilPassAndDepthFailOperation(getStencilOperation(stencilprop->mZFailAction)); + stencil->setStencilPassAndDepthPassOperation(getStencilOperation(stencilprop->mPassAction)); stencil = shareAttribute(stencil); stateset->setAttributeAndModes(stencil, osg::StateAttribute::ON); @@ -2155,7 +2164,7 @@ namespace NifOsg const Nif::NiWireframeProperty* wireprop = static_cast(property); osg::ref_ptr mode = new osg::PolygonMode; mode->setMode(osg::PolygonMode::FRONT_AND_BACK, - wireprop->isEnabled() ? osg::PolygonMode::LINE : osg::PolygonMode::FILL); + wireprop->mEnable ? osg::PolygonMode::LINE : osg::PolygonMode::FILL); mode = shareAttribute(mode); node->getOrCreateStateSet()->setAttributeAndModes(mode, osg::StateAttribute::ON); break; @@ -2202,18 +2211,16 @@ namespace NifOsg { auto texprop = static_cast(property); bool shaderRequired = true; - node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->type))); + node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->mType))); node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); - if (!texprop->textureSet.empty()) + if (!texprop->mTextureSet.empty()) { - auto textureSet = texprop->textureSet.getPtr(); + auto textureSet = texprop->mTextureSet.getPtr(); handleTextureSet( - textureSet, texprop->clamp, node->getName(), stateset, imageManager, boundTextures); + textureSet, texprop->mClamp, node->getName(), stateset, imageManager, boundTextures); } handleTextureControllers(texprop, composite, imageManager, stateset, animflags); - if (texprop->doubleSided()) - stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); break; } case Nif::RC_BSShaderNoLightingProperty: @@ -2221,10 +2228,10 @@ namespace NifOsg auto texprop = static_cast(property); bool shaderRequired = true; bool useFalloff = false; - node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->type))); + node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->mType))); node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); - if (!texprop->filename.empty()) + if (!texprop->mFilename.empty()) { if (!boundTextures.empty()) { @@ -2233,7 +2240,7 @@ namespace NifOsg boundTextures.clear(); } std::string filename - = Misc::ResourceHelpers::correctTexturePath(texprop->filename, imageManager->getVFS()); + = Misc::ResourceHelpers::correctTexturePath(texprop->mFilename, imageManager->getVFS()); osg::ref_ptr image = imageManager->getImage(filename); osg::ref_ptr texture2d = new osg::Texture2D(image); texture2d->setName("diffuseMap"); @@ -2247,20 +2254,18 @@ namespace NifOsg if (mBethVersion >= 27) { useFalloff = true; - stateset->addUniform(new osg::Uniform("falloffParams", texprop->falloffParams)); + stateset->addUniform(new osg::Uniform("falloffParams", texprop->mFalloffParams)); } } stateset->addUniform(new osg::Uniform("useFalloff", useFalloff)); handleTextureControllers(texprop, composite, imageManager, stateset, animflags); - if (texprop->doubleSided()) - stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); break; } case Nif::RC_BSLightingShaderProperty: { auto texprop = static_cast(property); bool shaderRequired = true; - node->setUserValue("shaderPrefix", std::string(getBSLightingShaderPrefix(texprop->type))); + node->setUserValue("shaderPrefix", std::string(getBSLightingShaderPrefix(texprop->mType))); node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); if (!texprop->mTextureSet.empty()) @@ -2365,7 +2370,7 @@ namespace NifOsg return *found; } - void applyDrawableProperties(osg::Node* node, const std::vector& properties, + void applyDrawableProperties(osg::Node* node, const std::vector& properties, SceneUtil::CompositeStateSetUpdater* composite, bool hasVertexColors, int animflags) { // Specular lighting is enabled by default, but there's a quirk... @@ -2390,7 +2395,7 @@ namespace NifOsg float emissiveMult = 1.f; float specStrength = 1.f; - for (const Nif::Property* property : properties) + for (const Nif::NiProperty* property : properties) { switch (property->recType) { @@ -2399,21 +2404,20 @@ namespace NifOsg // Specular property can turn specular lighting off. // FIXME: NiMaterialColorController doesn't care about this. auto specprop = static_cast(property); - specEnabled = specprop->isEnabled(); + specEnabled = specprop->mEnable; break; } case Nif::RC_NiMaterialProperty: { const Nif::NiMaterialProperty* matprop = static_cast(property); - mat->setDiffuse( - osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.diffuse, matprop->data.alpha)); - mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.ambient, 1.f)); - mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.emissive, 1.f)); - emissiveMult = matprop->data.emissiveMult; + mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->mDiffuse, matprop->mAlpha)); + mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->mAmbient, 1.f)); + mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->mEmissive, 1.f)); + emissiveMult = matprop->mEmissiveMult; - mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.specular, 1.f)); - mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness); + mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->mSpecular, 1.f)); + mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->mGlossiness); if (!matprop->mController.empty()) { @@ -2428,29 +2432,31 @@ namespace NifOsg const Nif::NiVertexColorProperty* vertprop = static_cast(property); + using VertexMode = Nif::NiVertexColorProperty::VertexMode; switch (vertprop->mVertexMode) { - case Nif::NiVertexColorProperty::VertexMode::VertMode_SrcIgnore: + case VertexMode::VertMode_SrcIgnore: { mat->setColorMode(osg::Material::OFF); break; } - case Nif::NiVertexColorProperty::VertexMode::VertMode_SrcEmissive: + case VertexMode::VertMode_SrcEmissive: { mat->setColorMode(osg::Material::EMISSION); break; } - case Nif::NiVertexColorProperty::VertexMode::VertMode_SrcAmbDif: + case VertexMode::VertMode_SrcAmbDif: { lightmode = vertprop->mLightingMode; + using LightMode = Nif::NiVertexColorProperty::LightMode; switch (lightmode) { - case Nif::NiVertexColorProperty::LightMode::LightMode_Emissive: + case LightMode::LightMode_Emissive: { mat->setColorMode(osg::Material::OFF); break; } - case Nif::NiVertexColorProperty::LightMode::LightMode_EmiAmbDif: + case LightMode::LightMode_EmiAmbDif: default: { mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); @@ -2503,7 +2509,7 @@ namespace NifOsg if (alphaprop->useAlphaTesting()) { osg::ref_ptr alphaFunc(new osg::AlphaFunc( - getTestMode(alphaprop->alphaTestMode()), alphaprop->data.threshold / 255.f)); + getTestMode(alphaprop->alphaTestMode()), alphaprop->mThreshold / 255.f)); alphaFunc = shareAttribute(alphaFunc); node->getOrCreateStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); }