diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 4debe760bc..48f843b595 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -169,6 +169,7 @@ namespace Nif { "bhkRigidBody", &construct }, { "bhkRigidBodyT", &construct }, { "BSLightingShaderProperty", &construct }, + { "BSEffectShaderProperty", &construct }, { "NiSortAdjustNode", &construct }, { "NiClusterAccumulator", &construct }, { "NiAlphaAccumulator", &construct }, diff --git a/components/nif/property.cpp b/components/nif/property.cpp index a67b5d6121..3c1c424de9 100644 --- a/components/nif/property.cpp +++ b/components/nif/property.cpp @@ -204,6 +204,25 @@ namespace Nif mTextureSet.post(nif); } + void BSEffectShaderProperty::read(NIFStream* nif) + { + BSShaderProperty::read(nif); + flags1 = nif->getUInt(); + flags2 = nif->getUInt(); + mUVOffset = nif->getVector2(); + mUVScale = nif->getVector2(); + mSourceTexture = nif->getSizedString(); + unsigned int miscParams = nif->getUInt(); + mClamp = miscParams & 0xFF; + mLightingInfluence = (miscParams >> 8) & 0xFF; + mEnvMapMinLOD = (miscParams >> 16) & 0xFF; + mFalloffParams = nif->getVector4(); + mBaseColor = nif->getVector4(); + mBaseColorScale = nif->getFloat(); + mFalloffDepth = nif->getFloat(); + mGreyscaleTexture = nif->getSizedString(); + } + void NiFogProperty::read(NIFStream* nif) { Property::read(nif); diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 70a822d7eb..108fb62a2b 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -215,6 +215,22 @@ namespace Nif void post(Reader& nif) override; }; + struct BSEffectShaderProperty : public BSShaderProperty + { + 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; + }; + struct NiDitherProperty : public Property { unsigned short flags; diff --git a/components/nif/record.hpp b/components/nif/record.hpp index bc2fa41409..7315d0bdc7 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -145,6 +145,7 @@ namespace Nif RC_bhkRigidBody, RC_bhkRigidBodyT, RC_BSLightingShaderProperty, + RC_BSEffectShaderProperty, RC_NiClusterAccumulator, RC_NiAlphaAccumulator, RC_NiSortAdjustNode, diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 0c0bd4295c..984dc9370d 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include // resource @@ -502,6 +503,12 @@ namespace NifOsg return image; } + void handleTextureWrapping(osg::Texture2D* texture, bool wrapS, bool wrapT) + { + texture->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + texture->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + } + bool handleEffect(const Nif::Node* nifNode, osg::StateSet* stateset, Resource::ImageManager* imageManager) { if (nifNode->recType != Nif::RC_NiTextureEffect) @@ -547,10 +554,7 @@ namespace NifOsg if (image) texture2d->setTextureSize(image->s(), image->t()); texture2d->setName("envMap"); - texture2d->setWrap( - osg::Texture::WRAP_S, textureEffect->wrapS() ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); - texture2d->setWrap( - osg::Texture::WRAP_T, textureEffect->wrapT() ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + handleTextureWrapping(texture2d, textureEffect->wrapS(), textureEffect->wrapT()); int texUnit = 3; // FIXME @@ -1809,10 +1813,7 @@ namespace NifOsg else texture2d = new osg::Texture2D; - texture2d->setWrap( - osg::Texture::WRAP_S, tex.wrapS() ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); - texture2d->setWrap( - osg::Texture::WRAP_T, tex.wrapT() ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + handleTextureWrapping(texture2d, tex.wrapS(), tex.wrapT()); uvSet = tex.uvSet; } @@ -1820,8 +1821,7 @@ namespace NifOsg { // Texture only comes from NiFlipController, so tex is ignored, set defaults texture2d = new osg::Texture2D; - texture2d->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); - texture2d->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + handleTextureWrapping(texture2d, true, true); uvSet = 0; } @@ -1966,10 +1966,7 @@ namespace NifOsg osg::ref_ptr texture2d = new osg::Texture2D(image); if (image) texture2d->setTextureSize(image->s(), image->t()); - bool wrapT = clamp & 0x1; - bool wrapS = (clamp >> 1) & 0x1; - texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); - texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + handleTextureWrapping(texture2d, (clamp >> 1) & 0x1, clamp & 0x1); unsigned int texUnit = boundTextures.size(); stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); // BSShaderTextureSet presence means there's no need for FFP support for the affected node @@ -2174,10 +2171,7 @@ namespace NifOsg texture2d->setName("diffuseMap"); if (image) texture2d->setTextureSize(image->s(), image->t()); - texture2d->setWrap(osg::Texture::WRAP_S, - texprop->wrapS() ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); - texture2d->setWrap(osg::Texture::WRAP_T, - texprop->wrapT() ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + handleTextureWrapping(texture2d, texprop->wrapS(), texprop->wrapT()); const unsigned int texUnit = 0; const unsigned int uvSet = 0; stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); @@ -2208,6 +2202,56 @@ namespace NifOsg handleTextureControllers(texprop, composite, imageManager, stateset, animflags); break; } + case Nif::RC_BSEffectShaderProperty: + { + auto texprop = static_cast(property); + bool shaderRequired = true; + node->setUserValue("shaderPrefix", std::string("nv_nolighting")); + node->setUserValue("shaderRequired", shaderRequired); + osg::StateSet* stateset = node->getOrCreateStateSet(); + if (!texprop->mSourceTexture.empty()) + { + if (!boundTextures.empty()) + { + for (unsigned int i = 0; i < boundTextures.size(); ++i) + stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF); + boundTextures.clear(); + } + std::string filename = Misc::ResourceHelpers::correctTexturePath( + texprop->mSourceTexture, imageManager->getVFS()); + osg::ref_ptr image = imageManager->getImage(filename); + osg::ref_ptr texture2d = new osg::Texture2D(image); + texture2d->setName("diffuseMap"); + if (image) + texture2d->setTextureSize(image->s(), image->t()); + handleTextureWrapping(texture2d, (texprop->mClamp >> 1) & 0x1, texprop->mClamp & 0x1); + const unsigned int texUnit = 0; + const unsigned int uvSet = 0; + stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); + boundTextures.push_back(uvSet); + + { + osg::ref_ptr texMat(new osg::TexMat); + // This handles 20.2.0.7 UV settings like 4.0.0.2 UV settings (see NifOsg::UVController) + // TODO: verify + osg::Vec3f uvOrigin(0.5f, 0.5f, 0.f); + osg::Vec3f uvScale(texprop->mUVScale.x(), texprop->mUVScale.y(), 1.f); + osg::Vec3f uvTrans(-texprop->mUVOffset.x(), -texprop->mUVOffset.y(), 0.f); + + osg::Matrixf mat = osg::Matrixf::translate(uvOrigin); + mat.preMultScale(uvScale); + mat.preMultTranslate(-uvOrigin); + mat.setTrans(mat.getTrans() + uvTrans); + + texMat->setMatrix(mat); + stateset->setTextureAttributeAndModes(texUnit, texMat, osg::StateAttribute::ON); + } + } + stateset->addUniform(new osg::Uniform("useFalloff", true)); // Should use the shader flag + stateset->addUniform(new osg::Uniform("falloffParams", texprop->mFalloffParams)); + handleTextureControllers(texprop, composite, imageManager, stateset, animflags); + break; + } // unused by mw case Nif::RC_NiShadeProperty: case Nif::RC_NiDitherProperty: @@ -2393,6 +2437,11 @@ namespace NifOsg } break; } + case Nif::RC_BSShaderNoLightingProperty: + { + mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); + break; + } case Nif::RC_BSLightingShaderProperty: { auto shaderprop = static_cast(property); @@ -2404,6 +2453,13 @@ namespace NifOsg specStrength = shaderprop->mSpecStrength; break; } + case Nif::RC_BSEffectShaderProperty: + { + auto shaderprop = static_cast(property); + mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(shaderprop->mBaseColor)); + emissiveMult = shaderprop->mBaseColorScale; + break; + } default: break; } diff --git a/files/shaders/nv_nolighting_fragment.glsl b/files/shaders/nv_nolighting_fragment.glsl index 59dfda9ee8..09c00eaeae 100644 --- a/files/shaders/nv_nolighting_fragment.glsl +++ b/files/shaders/nv_nolighting_fragment.glsl @@ -15,6 +15,7 @@ varying float linearDepth; uniform bool useFalloff; uniform vec2 screenRes; +uniform float emissiveMult; varying float passFalloff; @@ -36,6 +37,10 @@ void main() if (useFalloff) gl_FragData[0].a *= passFalloff; + vec4 emissionColor = getEmissionColor(); + gl_FragData[0].rgb *= emissionColor.rgb * emissiveMult; + gl_FragData[0].a *= emissionColor.a * emissionColor.a; // sic + alphaTest(); #if defined(FORCE_OPAQUE) && FORCE_OPAQUE