diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b36eb2525..e94bf28c32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -104,6 +104,7 @@ Bug #7675: Successful lock spell doesn't produce a sound Bug #7679: Scene luminance value flashes when toggling shaders Feature #3537: Shader-based water ripples + Feature #5173: Support for NiFogProperty Feature #5492: Let rain and snow collide with statics Feature #6149: Dehardcode Lua API_REVISION Feature #6152: Playing music via lua scripts diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 7e3c7aea23..f61a5bd0b2 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -130,7 +130,7 @@ add_component_dir (nif ) add_component_dir (nifosg - nifloader controller particle matrixtransform + nifloader controller particle matrixtransform fog ) add_component_dir (nifbullet diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 2506633867..7d420f1650 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -461,11 +461,22 @@ namespace Nif struct NiFogProperty : NiProperty { + enum Flags : uint16_t + { + Enabled = 0x02, + Radial = 0x08, + VertexAlpha = 0x10, + }; + uint16_t mFlags; float mFogDepth; osg::Vec3f mColour; void read(NIFStream* nif) override; + + bool enabled() const { return mFlags & Flags::Enabled; } + bool radial() const { return mFlags & Flags::Radial; } + bool vertexAlpha() const { return mFlags & Flags::VertexAlpha; } }; struct NiMaterialProperty : NiProperty diff --git a/components/nifosg/fog.cpp b/components/nifosg/fog.cpp new file mode 100644 index 0000000000..497193ec42 --- /dev/null +++ b/components/nifosg/fog.cpp @@ -0,0 +1,31 @@ +#include "fog.hpp" + +#include +#include + +namespace NifOsg +{ + + Fog::Fog() + : osg::Fog() + { + } + + Fog::Fog(const Fog& copy, const osg::CopyOp& copyop) + : osg::Fog(copy, copyop) + , mDepth(copy.mDepth) + { + } + + void Fog::apply(osg::State& state) const + { + osg::Fog::apply(state); +#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE + float fov, aspect, near, far; + state.getProjectionMatrix().getPerspective(fov, aspect, near, far); + glFogf(GL_FOG_START, near * mDepth + far * (1.f - mDepth)); + glFogf(GL_FOG_END, far); +#endif + } + +} diff --git a/components/nifosg/fog.hpp b/components/nifosg/fog.hpp new file mode 100644 index 0000000000..5c49392a24 --- /dev/null +++ b/components/nifosg/fog.hpp @@ -0,0 +1,29 @@ +#ifndef OPENMW_COMPONENTS_NIFOSG_FOG_H +#define OPENMW_COMPONENTS_NIFOSG_FOG_H + +#include + +namespace NifOsg +{ + + // osg::Fog-based wrapper for NiFogProperty that autocalculates the fog start and end distance. + class Fog : public osg::Fog + { + public: + Fog(); + Fog(const Fog& copy, const osg::CopyOp& copyop); + + META_StateAttribute(NifOsg, Fog, FOG) + + void setDepth(float depth) { mDepth = depth; } + float getDepth() const { return mDepth; } + + void apply(osg::State& state) const override; + + private: + float mDepth{ 1.f }; + }; + +} + +#endif diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 436f2e1d34..c55d580e36 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -56,6 +56,7 @@ #include #include +#include "fog.hpp" #include "matrixtransform.hpp" #include "particle.hpp" @@ -2495,10 +2496,35 @@ namespace NifOsg handleDepthFlags(stateset, texprop->depthTest(), texprop->depthWrite()); break; } + case Nif::RC_NiFogProperty: + { + const Nif::NiFogProperty* fogprop = static_cast(property); + osg::StateSet* stateset = node->getOrCreateStateSet(); + // Vertex alpha mode appears to be broken + if (!fogprop->vertexAlpha() && fogprop->enabled()) + { + osg::ref_ptr fog = new NifOsg::Fog; + fog->setMode(osg::Fog::LINEAR); + fog->setColor(osg::Vec4f(fogprop->mColour, 1.f)); + fog->setDepth(fogprop->mFogDepth); + stateset->setAttributeAndModes(fog, osg::StateAttribute::ON); + // Intentionally ignoring radial fog flag + // We don't really want to override the global setting + } + else + { + osg::ref_ptr fog = new osg::Fog; + // Shaders don't respect glDisable(GL_FOG) + fog->setMode(osg::Fog::LINEAR); + fog->setStart(10000000); + fog->setEnd(10000000); + stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); + } + break; + } // unused by mw case Nif::RC_NiShadeProperty: case Nif::RC_NiDitherProperty: - case Nif::RC_NiFogProperty: { break; } diff --git a/components/sceneutil/serialize.cpp b/components/sceneutil/serialize.cpp index 784dafafa5..8d8acacae4 100644 --- a/components/sceneutil/serialize.cpp +++ b/components/sceneutil/serialize.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -123,6 +124,19 @@ namespace SceneUtil } }; + class FogSerializer : public osgDB::ObjectWrapper + { + public: + FogSerializer() + : osgDB::ObjectWrapper( + createInstanceFunc, "NifOsg::Fog", "osg::Object osg::StateAttribute osg::Fog NifOsg::Fog") + { + addSerializer(new osgDB::PropByValSerializer( + "Depth", 1.f, &NifOsg::Fog::getDepth, &NifOsg::Fog::setDepth), + osgDB::BaseSerializer::RW_FLOAT); + } + }; + osgDB::ObjectWrapper* makeDummySerializer(const std::string& classname) { return new osgDB::ObjectWrapper(createInstanceFunc, classname, "osg::Object"); @@ -153,6 +167,7 @@ namespace SceneUtil mgr->addWrapper(new LightManagerSerializer); mgr->addWrapper(new CameraRelativeTransformSerializer); mgr->addWrapper(new MatrixTransformSerializer); + mgr->addWrapper(new FogSerializer); // Don't serialize Geometry data as we are more interested in the overall structure rather than tons of // vertex data that would make the file large and hard to read.