1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-21 18:40:01 +00:00

Merge branch 'sky_shaders' into 'master'

Sky shaders

See merge request OpenMW/openmw!1057
This commit is contained in:
psi29a 2021-10-27 20:25:40 +00:00
commit b3f84df43c
13 changed files with 3440 additions and 2950 deletions

View File

@ -12,6 +12,8 @@
Bug #4602: Robert's Bodies: crash inside createInstance()
Bug #4700: Editor: Incorrect command implementation
Bug #4744: Invisible particles must still be processed
Bug #4752: UpdateCellCommand doesn't undo properly
Bug #5088: Sky abruptly changes direction during certain weather transitions
Bug #5100: Persuasion doesn't always clamp the resulting disposition
Bug #5120: Scripted object spawning updates physics system
Bug #5207: Loose summons can be present in scene
@ -42,6 +44,7 @@
Bug #6133: Cannot reliably sneak or steal in the sight of the NPCs siding with player
Bug #6143: Capturing a screenshot makes engine to be a temporary unresponsive
Bug #6165: Paralyzed player character can pickup items when the inventory is open
Bug #6168: Weather particles flicker for a frame at start of storms
Bug #6172: Some creatures can't open doors
Bug #6174: Spellmaking and Enchanting sliders differences from vanilla
Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
@ -76,6 +79,7 @@
Feature #6017: Separate persistent and temporary cell references when saving
Feature #6032: Reverse-z depth buffer
Feature #6078: First person should not clear depth buffer
Feature #6161: Refactor Sky to use shaders and GLES/GL3 friendly
Feature #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly
Feature #6199: Support FBO Rendering
Feature #6249: Alpha testing support for Collada

View File

@ -19,7 +19,7 @@ set(GAME_HEADER
source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
actors objects renderingmanager animation rotatecontroller sky skyutil npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation screenshotmanager
bulletdebugdraw globalmap characterpreview camera viewovershoulder localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover postprocessor

File diff suppressed because it is too large Load Diff

View File

@ -8,10 +8,7 @@
#include <osg/ref_ptr>
#include <osg/Vec4f>
namespace osg
{
class Camera;
}
#include "skyutil.hpp"
namespace osg
{
@ -19,6 +16,7 @@ namespace osg
class Node;
class Material;
class PositionAttitudeTransform;
class Camera;
}
namespace osgParticle
@ -45,80 +43,6 @@ namespace MWRender
class AlphaFader;
class UnderwaterSwitchCallback;
struct WeatherResult
{
std::string mCloudTexture;
std::string mNextCloudTexture;
float mCloudBlendFactor;
osg::Vec4f mFogColor;
osg::Vec4f mAmbientColor;
osg::Vec4f mSkyColor;
// sun light color
osg::Vec4f mSunColor;
// alpha is the sun transparency
osg::Vec4f mSunDiscColor;
float mFogDepth;
float mDLFogFactor;
float mDLFogOffset;
float mWindSpeed;
float mBaseWindSpeed;
float mCurrentWindSpeed;
float mNextWindSpeed;
float mCloudSpeed;
float mGlareView;
bool mNight; // use night skybox
float mNightFade; // fading factor for night skybox
bool mIsStorm;
std::string mAmbientLoopSoundID;
float mAmbientSoundVolume;
std::string mParticleEffect;
std::string mRainEffect;
float mPrecipitationAlpha;
float mRainDiameter;
float mRainMinHeight;
float mRainMaxHeight;
float mRainSpeed;
float mRainEntranceSpeed;
int mRainMaxRaindrops;
};
struct MoonState
{
enum class Phase
{
Full = 0,
WaningGibbous,
ThirdQuarter,
WaningCrescent,
New,
WaxingCrescent,
FirstQuarter,
WaxingGibbous,
Unspecified
};
float mRotationFromHorizon;
float mRotationFromNorth;
Phase mPhase;
float mShadowBlend;
float mMoonAlpha;
};
///@brief The SkyManager handles rendering of the sky domes, celestial bodies as well as other objects that need to be rendered
/// relative to the camera (e.g. weather particle effects)
class SkyManager
@ -162,7 +86,7 @@ namespace MWRender
void setRainSpeed(float speed);
void setStormDirection(const osg::Vec3f& direction);
void setStormParticleDirection(const osg::Vec3f& direction);
void setSunDirection(const osg::Vec3f& direction);
@ -203,12 +127,12 @@ namespace MWRender
osg::ref_ptr<osg::Node> mParticleEffect;
osg::ref_ptr<UnderwaterSwitchCallback> mUnderwaterSwitch;
osg::ref_ptr<osg::PositionAttitudeTransform> mCloudNode;
osg::ref_ptr<osg::Group> mCloudNode;
osg::ref_ptr<CloudUpdater> mCloudUpdater;
osg::ref_ptr<CloudUpdater> mCloudUpdater2;
osg::ref_ptr<osg::Node> mCloudMesh;
osg::ref_ptr<osg::Node> mCloudMesh2;
osg::ref_ptr<CloudUpdater> mNextCloudUpdater;
osg::ref_ptr<osg::PositionAttitudeTransform> mCloudMesh;
osg::ref_ptr<osg::PositionAttitudeTransform> mNextCloudMesh;
osg::ref_ptr<osg::Node> mAtmosphereDay;
@ -239,7 +163,10 @@ namespace MWRender
float mRainTimer;
// particle system rotation is independent of cloud rotation internally
osg::Vec3f mStormParticleDirection;
osg::Vec3f mStormDirection;
osg::Vec3f mNextStormDirection;
// remember some settings so we don't have to apply them again if they didn't change
std::string mClouds;
@ -275,4 +202,4 @@ namespace MWRender
};
}
#endif // GAME_RENDER_SKY_H
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,343 @@
#ifndef OPENMW_MWRENDER_SKYUTIL_H
#define OPENMW_MWRENDER_SKYUTIL_H
#include <osg/Vec4f>
#include <osg/Matrixf>
#include <osg/Texture2D>
#include <osg/Transform>
#include <osg/Material>
#include <osgParticle/Shooter>
#include <osgParticle/ConstantRateCounter>
#include <components/sceneutil/statesetupdater.hpp>
#include <components/sceneutil/nodecallback.hpp>
namespace Resource
{
class ImageManager;
class SceneManager;
}
namespace MWRender
{
struct MoonUpdater;
class SunUpdater;
class SunFlashCallback;
class SunGlareCallback;
struct WeatherResult
{
std::string mCloudTexture;
std::string mNextCloudTexture;
float mCloudBlendFactor;
osg::Vec4f mFogColor;
osg::Vec4f mAmbientColor;
osg::Vec4f mSkyColor;
// sun light color
osg::Vec4f mSunColor;
// alpha is the sun transparency
osg::Vec4f mSunDiscColor;
float mFogDepth;
float mDLFogFactor;
float mDLFogOffset;
float mWindSpeed;
float mBaseWindSpeed;
float mCurrentWindSpeed;
float mNextWindSpeed;
float mCloudSpeed;
float mGlareView;
bool mNight; // use night skybox
float mNightFade; // fading factor for night skybox
bool mIsStorm;
std::string mAmbientLoopSoundID;
float mAmbientSoundVolume;
std::string mParticleEffect;
std::string mRainEffect;
float mPrecipitationAlpha;
float mRainDiameter;
float mRainMinHeight;
float mRainMaxHeight;
float mRainSpeed;
float mRainEntranceSpeed;
int mRainMaxRaindrops;
osg::Vec3f mStormDirection;
osg::Vec3f mNextStormDirection;
};
struct MoonState
{
enum class Phase
{
Full,
WaningGibbous,
ThirdQuarter,
WaningCrescent,
New,
WaxingCrescent,
FirstQuarter,
WaxingGibbous,
Unspecified
};
float mRotationFromHorizon;
float mRotationFromNorth;
Phase mPhase;
float mShadowBlend;
float mMoonAlpha;
};
osg::ref_ptr<osg::Material> createAlphaTrackingUnlitMaterial();
osg::ref_ptr<osg::Material> createUnlitMaterial();
class OcclusionCallback
{
public:
OcclusionCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal);
protected:
float getVisibleRatio (osg::Camera* camera);
private:
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryVisiblePixels;
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryTotalPixels;
std::map<osg::observer_ptr<osg::Camera>, float> mLastRatio;
};
class AtmosphereUpdater : public SceneUtil::StateSetUpdater
{
public:
void setEmissionColor(const osg::Vec4f& emissionColor);
protected:
void setDefaults(osg::StateSet* stateset) override;
void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) override;
private:
osg::Vec4f mEmissionColor;
};
class AtmosphereNightUpdater : public SceneUtil::StateSetUpdater
{
public:
AtmosphereNightUpdater(Resource::ImageManager* imageManager, bool forceShaders);
void setFade(float fade);
protected:
void setDefaults(osg::StateSet* stateset) override;
void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) override;
private:
osg::Vec4f mColor;
osg::ref_ptr<osg::Texture2D> mTexture;
bool mForceShaders;
};
class CloudUpdater : public SceneUtil::StateSetUpdater
{
public:
CloudUpdater(bool forceShaders);
void setTexture(osg::ref_ptr<osg::Texture2D> texture);
void setEmissionColor(const osg::Vec4f& emissionColor);
void setOpacity(float opacity);
void setTextureCoord(float timer);
protected:
void setDefaults(osg::StateSet *stateset) override;
void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) override;
private:
osg::ref_ptr<osg::Texture2D> mTexture;
osg::Vec4f mEmissionColor;
float mOpacity;
bool mForceShaders;
osg::Matrixf mTexMat;
};
/// Transform that removes the eyepoint of the modelview matrix,
/// i.e. its children are positioned relative to the camera.
class CameraRelativeTransform : public osg::Transform
{
public:
CameraRelativeTransform();
CameraRelativeTransform(const CameraRelativeTransform& copy, const osg::CopyOp& copyop);
META_Node(MWRender, CameraRelativeTransform)
const osg::Vec3f& getLastViewPoint() const;
bool computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv) const override;
osg::BoundingSphere computeBound() const override;
private:
// viewPoint for the current frame
mutable osg::Vec3f mViewPoint;
};
/// @brief Hides the node subgraph if the eye point is below water.
/// @note Must be added as cull callback.
/// @note Meant to be used on a node that is child of a CameraRelativeTransform.
/// The current view point must be retrieved by the CameraRelativeTransform since we can't get it anymore once we are in camera-relative space.
class UnderwaterSwitchCallback : public SceneUtil::NodeCallback<UnderwaterSwitchCallback>
{
public:
UnderwaterSwitchCallback(CameraRelativeTransform* cameraRelativeTransform);
bool isUnderwater();
void operator()(osg::Node* node, osg::NodeVisitor* nv);
void setEnabled(bool enabled);
void setWaterLevel(float waterLevel);
private:
osg::ref_ptr<CameraRelativeTransform> mCameraRelativeTransform;
bool mEnabled;
float mWaterLevel;
};
/// A base class for the sun and moons.
class CelestialBody
{
public:
CelestialBody(osg::Group* parentNode, float scaleFactor, int numUvSets, unsigned int visibleMask=~0u);
virtual ~CelestialBody() = default;
virtual void adjustTransparency(const float ratio) = 0;
void setVisible(bool visible);
protected:
unsigned int mVisibleMask;
static const float mDistance;
osg::ref_ptr<osg::PositionAttitudeTransform> mTransform;
osg::ref_ptr<osg::Geometry> mGeom;
};
class Sun : public CelestialBody
{
public:
Sun(osg::Group* parentNode, Resource::ImageManager& imageManager);
~Sun();
void setColor(const osg::Vec4f& color);
void adjustTransparency(const float ratio) override;
void setDirection(const osg::Vec3f& direction);
void setGlareTimeOfDayFade(float val);
private:
/// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels.
osg::ref_ptr<osg::OcclusionQueryNode> createOcclusionQueryNode(osg::Group* parent, bool queryVisible);
void createSunFlash(Resource::ImageManager& imageManager);
void destroySunFlash();
void createSunGlare();
void destroySunGlare();
osg::ref_ptr<SunUpdater> mUpdater;
osg::ref_ptr<osg::Node> mSunFlashNode;
osg::ref_ptr<osg::Node> mSunGlareNode;
osg::ref_ptr<SunFlashCallback> mSunFlashCallback;
osg::ref_ptr<SunGlareCallback> mSunGlareCallback;
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryVisiblePixels;
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryTotalPixels;
};
class Moon : public CelestialBody
{
public:
enum Type
{
Type_Masser = 0,
Type_Secunda
};
Moon(osg::Group* parentNode, Resource::SceneManager& sceneManager, float scaleFactor, Type type);
~Moon();
void adjustTransparency(const float ratio) override;
void setState(const MoonState state);
void setAtmosphereColor(const osg::Vec4f& color);
void setColor(const osg::Vec4f& color);
unsigned int getPhaseInt() const;
private:
Type mType;
MoonState::Phase mPhase;
osg::ref_ptr<MoonUpdater> mUpdater;
void setPhase(const MoonState::Phase& phase);
};
class RainCounter : public osgParticle::ConstantRateCounter
{
public:
int numParticlesToCreate(double dt) const override;
};
class RainShooter : public osgParticle::Shooter
{
public:
RainShooter();
osg::Object* cloneType() const override;
osg::Object* clone(const osg::CopyOp &) const override;
void shoot(osgParticle::Particle* particle) const override;
void setVelocity(const osg::Vec3f& velocity);
void setAngle(float angle);
private:
osg::Vec3f mVelocity;
float mAngle;
};
class ModVertexAlphaVisitor : public osg::NodeVisitor
{
public:
enum MeshType
{
Atmosphere,
Stars,
Clouds
};
ModVertexAlphaVisitor(MeshType type);
void apply(osg::Geometry& geometry) override;
private:
MeshType mType;
};
}
#endif

View File

@ -22,8 +22,6 @@
#include <cmath>
using namespace MWWorld;
namespace
{
static const int invalidWeatherID = -1;
@ -38,11 +36,27 @@ namespace
{
return x * (1-factor) + y * factor;
}
osg::Vec3f calculateStormDirection(const std::string& particleEffect)
{
osg::Vec3f stormDirection = MWWorld::Weather::defaultDirection();
if (particleEffect == "meshes\\ashcloud.nif" || particleEffect == "meshes\\blightcloud.nif")
{
osg::Vec3f playerPos = MWMechanics::getPlayer().getRefData().getPosition().asVec3();
playerPos.z() = 0;
osg::Vec3f redMountainPos = osg::Vec3f(25000.f, 70000.f, 0.f);
stormDirection = (playerPos - redMountainPos);
stormDirection.normalize();
}
return stormDirection;
}
}
template <typename T>
T TimeOfDayInterpolator<T>::getValue(const float gameHour, const TimeOfDaySettings& timeSettings, const std::string& prefix) const
namespace MWWorld
{
template <typename T>
T TimeOfDayInterpolator<T>::getValue(const float gameHour, const TimeOfDaySettings& timeSettings, const std::string& prefix) const
{
WeatherSetting setting = timeSettings.getSetting(prefix);
float preSunriseTime = setting.mPreSunriseTime;
float postSunriseTime = setting.mPostSunriseTime;
@ -107,14 +121,18 @@ T TimeOfDayInterpolator<T>::getValue(const float gameHour, const TimeOfDaySettin
}
// shut up compiler
return T();
}
}
template class MWWorld::TimeOfDayInterpolator<float>;
template class MWWorld::TimeOfDayInterpolator<osg::Vec4f>;
osg::Vec3f Weather::defaultDirection()
{
static const osg::Vec3f direction = osg::Vec3f(0.f, 1.f, 0.f);
return direction;
}
template class MWWorld::TimeOfDayInterpolator<float>;
template class MWWorld::TimeOfDayInterpolator<osg::Vec4f>;
Weather::Weather(const std::string& name,
Weather::Weather(const std::string& name,
float stormWindSpeed,
float rainSpeed,
float dlFactor,
@ -155,6 +173,7 @@ Weather::Weather(const std::string& name,
, mRainMaxHeight(Fallback::Map::getFloat("Weather_" + name + "_Rain_Height_Max"))
, mParticleEffect(particleEffect)
, mRainEffect(Fallback::Map::getBool("Weather_" + name + "_Using_Precip") ? "meshes\\raindrop.nif" : "")
, mStormDirection(Weather::defaultDirection())
, mTransitionDelta(Fallback::Map::getFloat("Weather_" + name + "_Transition_Delta"))
, mCloudsMaximumPercent(Fallback::Map::getFloat("Weather_" + name + "_Clouds_Maximum_Percent"))
, mThunderFrequency(Fallback::Map::getFloat("Weather_" + name + "_Thunder_Frequency"))
@ -162,7 +181,7 @@ Weather::Weather(const std::string& name,
, mThunderSoundID()
, mFlashDecrement(Fallback::Map::getFloat("Weather_" + name + "_Flash_Decrement"))
, mFlashBrightness(0.0f)
{
{
mDL.FogFactor = dlFactor;
mDL.FogOffset = dlOffset;
mThunderSoundID[0] = Fallback::Map::getString("Weather_" + name + "_Thunder_Sound_ID_0");
@ -183,23 +202,23 @@ Weather::Weather(const std::string& name,
if (Misc::StringUtils::ciEqual(mAmbientLoopSoundID, "None"))
mAmbientLoopSoundID.clear();
}
}
float Weather::transitionDelta() const
{
float Weather::transitionDelta() const
{
// Transition Delta describes how quickly transitioning to the weather in question will take, in Hz. Note that the
// measurement is in real time, not in-game time.
return mTransitionDelta;
}
}
float Weather::cloudBlendFactor(const float transitionRatio) const
{
float Weather::cloudBlendFactor(const float transitionRatio) const
{
// Clouds Maximum Percent affects how quickly the sky transitions from one sky texture to the next.
return transitionRatio / mCloudsMaximumPercent;
}
}
float Weather::calculateThunder(const float transitionRatio, const float elapsedSeconds, const bool isPaused)
{
float Weather::calculateThunder(const float transitionRatio, const float elapsedSeconds, const bool isPaused)
{
// When paused, the flash brightness remains the same and no new strikes can occur.
if(!isPaused)
{
@ -220,28 +239,28 @@ float Weather::calculateThunder(const float transitionRatio, const float elapsed
}
return mFlashBrightness;
}
}
inline void Weather::flashDecrement(const float elapsedSeconds)
{
inline void Weather::flashDecrement(const float elapsedSeconds)
{
// The Flash Decrement is measured in whole units per second. This means that if the flash brightness was
// currently 1.0, then it should take approximately 0.25 seconds to decay to 0.0 (the minimum).
float decrement = mFlashDecrement * elapsedSeconds;
mFlashBrightness = decrement > mFlashBrightness ? 0.0f : mFlashBrightness - decrement;
}
}
inline float Weather::thunderChance(const float transitionRatio, const float elapsedSeconds) const
{
inline float Weather::thunderChance(const float transitionRatio, const float elapsedSeconds) const
{
// This formula is reversed from the observation that with Thunder Frequency set to 1, there are roughly 10 strikes
// per minute. It doesn't appear to be tied to in game time as Timescale doesn't affect it. Various values of
// Thunder Frequency seem to change the average number of strikes in a linear fashion.. During a transition, it appears to
// scaled based on how far past it is past the Thunder Threshold.
float scaleFactor = (transitionRatio - mThunderThreshold) / (1.0f - mThunderThreshold);
return ((mThunderFrequency * 10.0f) / 60.0f) * elapsedSeconds * scaleFactor;
}
}
inline void Weather::lightningAndThunder(void)
{
inline void Weather::lightningAndThunder(void)
{
// Morrowind seems to vary the intensity of the brightness based on which of the four sound IDs it selects.
// They appear to go from 0 (brightest, closest) to 3 (faintest, farthest). The value of 0.25 per distance
// was derived by setting the Flash Decrement to 0.1 and measuring how long each value took to decay to 0.
@ -250,12 +269,12 @@ inline void Weather::lightningAndThunder(void)
// Flash brightness appears additive, since if multiple strikes occur, it takes longer for it to decay to 0.
mFlashBrightness += 1 - (distance * 0.25f);
MWBase::Environment::get().getSoundManager()->playSound(mThunderSoundID[distance], 1.0, 1.0);
}
}
RegionWeather::RegionWeather(const ESM::Region& region)
RegionWeather::RegionWeather(const ESM::Region& region)
: mWeather(invalidWeatherID)
, mChances()
{
{
mChances.reserve(10);
mChances.push_back(region.mData.mClear);
mChances.push_back(region.mData.mCloudy);
@ -267,16 +286,16 @@ RegionWeather::RegionWeather(const ESM::Region& region)
mChances.push_back(region.mData.mBlight);
mChances.push_back(region.mData.mA);
mChances.push_back(region.mData.mB);
}
}
RegionWeather::RegionWeather(const ESM::RegionWeatherState& state)
RegionWeather::RegionWeather(const ESM::RegionWeatherState& state)
: mWeather(state.mWeather)
, mChances(state.mChances)
{
}
{
}
RegionWeather::operator ESM::RegionWeatherState() const
{
RegionWeather::operator ESM::RegionWeatherState() const
{
ESM::RegionWeatherState state =
{
mWeather,
@ -284,10 +303,10 @@ RegionWeather::operator ESM::RegionWeatherState() const
};
return state;
}
}
void RegionWeather::setChances(const std::vector<char>& chances)
{
void RegionWeather::setChances(const std::vector<char>& chances)
{
if(mChances.size() < chances.size())
{
mChances.reserve(chances.size());
@ -305,15 +324,15 @@ void RegionWeather::setChances(const std::vector<char>& chances)
{
chooseNewWeather();
}
}
}
void RegionWeather::setWeather(int weatherID)
{
void RegionWeather::setWeather(int weatherID)
{
mWeather = weatherID;
}
}
int RegionWeather::getWeather()
{
int RegionWeather::getWeather()
{
// If the region weather was already set (by ChangeWeather, or by a previous call) then just return that value.
// Note that the region weather will be expired periodically when the weather update timer expires.
if(mWeather == invalidWeatherID)
@ -322,10 +341,10 @@ int RegionWeather::getWeather()
}
return mWeather;
}
}
void RegionWeather::chooseNewWeather()
{
void RegionWeather::chooseNewWeather()
{
// All probabilities must add to 100 (responsibility of the user).
// If chances A and B has values 30 and 70 then by generating 100 numbers 1..100, 30% will be lesser or equal 30
// and 70% will be greater than 30 (in theory).
@ -344,9 +363,9 @@ void RegionWeather::chooseNewWeather()
// if we hit this path then the chances don't add to 100, choose a default weather instead
mWeather = 0;
}
}
MoonModel::MoonModel(const std::string& name)
MoonModel::MoonModel(const std::string& name)
: mFadeInStart(Fallback::Map::getFloat("Moons_" + name + "_Fade_In_Start"))
, mFadeInFinish(Fallback::Map::getFloat("Moons_" + name + "_Fade_In_Finish"))
, mFadeOutStart(Fallback::Map::getFloat("Moons_" + name + "_Fade_Out_Start"))
@ -357,14 +376,14 @@ MoonModel::MoonModel(const std::string& name)
, mFadeStartAngle(Fallback::Map::getFloat("Moons_" + name + "_Fade_Start_Angle"))
, mFadeEndAngle(Fallback::Map::getFloat("Moons_" + name + "_Fade_End_Angle"))
, mMoonShadowEarlyFadeAngle(Fallback::Map::getFloat("Moons_" + name + "_Moon_Shadow_Early_Fade_Angle"))
{
{
// Morrowind appears to have a minimum speed in order to avoid situations where the moon couldn't conceivably
// complete a rotation in a single 24 hour period. The value of 180/23 was deduced from reverse engineering.
mSpeed = std::min(mSpeed, 180.0f / 23.0f);
}
}
MWRender::MoonState MoonModel::calculateState(const TimeStamp& gameTime) const
{
MWRender::MoonState MoonModel::calculateState(const TimeStamp& gameTime) const
{
float rotationFromHorizon = angle(gameTime);
MWRender::MoonState state =
{
@ -376,10 +395,10 @@ MWRender::MoonState MoonModel::calculateState(const TimeStamp& gameTime) const
};
return state;
}
}
inline float MoonModel::angle(const TimeStamp& gameTime) const
{
inline float MoonModel::angle(const TimeStamp& gameTime) const
{
// Morrowind's moons start travel on one side of the horizon (let's call it H-rise) and travel 180 degrees to the
// opposite horizon (let's call it H-set). Upon reaching H-set, they reset to H-rise until the next moon rise.
@ -415,10 +434,10 @@ inline float MoonModel::angle(const TimeStamp& gameTime) const
}
return moonRiseAngleToday;
}
}
inline float MoonModel::moonRiseHour(unsigned int daysPassed) const
{
inline float MoonModel::moonRiseHour(unsigned int daysPassed) const
{
// This arises from the start date of 16 Last Seed, 427
// TODO: Find an alternate formula that doesn't rely on this day being fixed.
static const unsigned int startDay = 16;
@ -429,18 +448,18 @@ inline float MoonModel::moonRiseHour(unsigned int daysPassed) const
// know if doing so would cause the moon rise to be postponed until the next day (which happens when
// the moon rise hour is >= 24 in Morrowind).
return mDailyIncrement + std::fmod((daysPassed - 1 + startDay) * mDailyIncrement, 24.0f);
}
}
inline float MoonModel::rotation(float hours) const
{
inline float MoonModel::rotation(float hours) const
{
// 15 degrees per hour was reverse engineered from the rotation matrices of the Morrowind scene graph.
// Note that this correlates to 360 / 24, which is a full rotation every 24 hours, so speed is a measure
// of whole rotations that could be completed in a day.
return 15.0f * mSpeed * hours;
}
}
MWRender::MoonState::Phase MoonModel::phase(const TimeStamp& gameTime) const
{
MWRender::MoonState::Phase MoonModel::phase(const TimeStamp& gameTime) const
{
// Morrowind starts with a full moon on 16 Last Seed and then begins to wane 17 Last Seed, working on 3 day phase cycle.
// If the moon didn't rise yet today, use yesterday's moon phase.
@ -448,10 +467,10 @@ MWRender::MoonState::Phase MoonModel::phase(const TimeStamp& gameTime) const
return static_cast<MWRender::MoonState::Phase>((gameTime.getDay() / 3) % 8);
else
return static_cast<MWRender::MoonState::Phase>(((gameTime.getDay() + 1) / 3) % 8);
}
}
inline float MoonModel::shadowBlend(float angle) const
{
inline float MoonModel::shadowBlend(float angle) const
{
// The Fade End Angle and Fade Start Angle describe a region where the moon transitions from a solid disk
// that is roughly the color of the sky, to a textured surface.
// Depending on the current angle, the following values describe the ratio between the textured moon
@ -471,10 +490,10 @@ inline float MoonModel::shadowBlend(float angle) const
return (fadeEndAngle2 - angle) / fadeAngle;
else
return 0.0f;
}
}
inline float MoonModel::hourlyAlpha(float gameHour) const
{
inline float MoonModel::hourlyAlpha(float gameHour) const
{
// The Fade Out Start / Finish and Fade In Start / Finish describe the hours at which the moon
// appears and disappears.
// Depending on the current hour, the following values describe how transparent the moon is.
@ -490,10 +509,10 @@ inline float MoonModel::hourlyAlpha(float gameHour) const
return (gameHour - mFadeInStart) / (mFadeInFinish - mFadeInStart);
else
return 1.0f;
}
}
inline float MoonModel::earlyMoonShadowAlpha(float angle) const
{
inline float MoonModel::earlyMoonShadowAlpha(float angle) const
{
// The Moon Shadow Early Fade Angle describes an arc relative to Fade End Angle.
// Depending on the current angle, the following values describe how transparent the moon is.
// 1. From Moon Shadow Early Fade Angle 1 to Fade End Angle 1 (during moon rise): 0..1
@ -511,9 +530,9 @@ inline float MoonModel::earlyMoonShadowAlpha(float angle) const
return (moonShadowEarlyFadeAngle2 - angle) / mMoonShadowEarlyFadeAngle;
else
return 0.0f;
}
}
WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, MWWorld::ESMStore& store)
WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, MWWorld::ESMStore& store)
: mStore(store)
, mRendering(rendering)
, mSunriseTime(Fallback::Map::getFloat("Weather_Sunrise_Time"))
@ -536,7 +555,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, MWWorld::E
, mNextWindSpeed(0.f)
, mIsStorm(false)
, mPrecipitation(false)
, mStormDirection(0,1,0)
, mStormDirection(Weather::defaultDirection())
, mCurrentRegion()
, mTimePassed(0)
, mFastForward(false)
@ -550,7 +569,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, MWWorld::E
, mResult()
, mAmbientSound(nullptr)
, mPlayingSoundID()
{
{
mTimeSettings.mNightStart = mSunsetTime + mSunsetDuration;
mTimeSettings.mNightEnd = mSunriseTime;
mTimeSettings.mDayStart = mSunriseTime + mSunriseDuration;
@ -597,15 +616,15 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, MWWorld::E
}
forceWeather(0);
}
}
WeatherManager::~WeatherManager()
{
WeatherManager::~WeatherManager()
{
stopSounds();
}
}
void WeatherManager::changeWeather(const std::string& regionID, const unsigned int weatherID)
{
void WeatherManager::changeWeather(const std::string& regionID, const unsigned int weatherID)
{
// In Morrowind, this seems to have the following behavior, when applied to the current region:
// - When there is no transition in progress, start transitioning to the new weather.
// - If there is a transition in progress, queue up the transition and process it when the current one completes.
@ -624,10 +643,10 @@ void WeatherManager::changeWeather(const std::string& regionID, const unsigned i
regionalWeatherChanged(it->first, it->second);
}
}
}
}
void WeatherManager::modRegion(const std::string& regionID, const std::vector<char>& chances)
{
void WeatherManager::modRegion(const std::string& regionID, const std::vector<char>& chances)
{
// Sets the region's probability for various weather patterns. Note that this appears to be saved permanently.
// In Morrowind, this seems to have the following behavior when applied to the current region:
// - If the region supports the current weather, no change in current weather occurs.
@ -643,10 +662,10 @@ void WeatherManager::modRegion(const std::string& regionID, const std::vector<ch
it->second.setChances(chances);
regionalWeatherChanged(it->first, it->second);
}
}
}
void WeatherManager::playerTeleported(const std::string& playerRegion, bool isExterior)
{
void WeatherManager::playerTeleported(const std::string& playerRegion, bool isExterior)
{
// If the player teleports to an outdoors cell in a new region (for instance, by travelling), the weather needs to
// be changed immediately, and any transitions for the previous region discarded.
{
@ -657,10 +676,10 @@ void WeatherManager::playerTeleported(const std::string& playerRegion, bool isEx
forceWeather(it->second.getWeather());
}
}
}
}
float WeatherManager::calculateWindSpeed(int weatherId, float currentSpeed)
{
float WeatherManager::calculateWindSpeed(int weatherId, float currentSpeed)
{
float targetSpeed = std::min(8.0f * mWeatherSettings[weatherId].mWindSpeed, 70.f);
if (currentSpeed == 0.f)
currentSpeed = targetSpeed;
@ -672,10 +691,10 @@ float WeatherManager::calculateWindSpeed(int weatherId, float currentSpeed)
currentSpeed = updatedSpeed;
return currentSpeed;
}
}
void WeatherManager::update(float duration, bool paused, const TimeStamp& time, bool isExterior)
{
void WeatherManager::update(float duration, bool paused, const TimeStamp& time, bool isExterior)
{
MWWorld::ConstPtr player = MWMechanics::getPlayer();
if(!paused || mFastForward)
@ -727,20 +746,8 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time,
mPrecipitation = !(mResult.mParticleEffect.empty() && mResult.mRainEffect.empty())
&& mResult.mParticleEffect != "meshes\\ashcloud.nif";
if (mIsStorm)
{
osg::Vec3f stormDirection(0, 1, 0);
if (mResult.mParticleEffect == "meshes\\ashcloud.nif" || mResult.mParticleEffect == "meshes\\blightcloud.nif")
{
osg::Vec3f playerPos (MWMechanics::getPlayer().getRefData().getPosition().asVec3());
playerPos.z() = 0;
osg::Vec3f redMountainPos (25000, 70000, 0);
stormDirection = (playerPos - redMountainPos);
stormDirection.normalize();
}
mStormDirection = stormDirection;
mRendering.getSkyManager()->setStormDirection(mStormDirection);
}
mStormDirection = calculateStormDirection(mResult.mParticleEffect);
mRendering.getSkyManager()->setStormParticleDirection(mStormDirection);
// disable sun during night
if (time.getHour() >= mTimeSettings.mNightStart || time.getHour() <= mSunriseTime)
@ -817,58 +824,58 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time,
}
else if (mAmbientSound)
mAmbientSound->setVolume(mResult.mAmbientSoundVolume);
}
}
void WeatherManager::stopSounds()
{
void WeatherManager::stopSounds()
{
if (mAmbientSound)
MWBase::Environment::get().getSoundManager()->stopSound(mAmbientSound);
mAmbientSound = nullptr;
mPlayingSoundID.clear();
}
}
float WeatherManager::getWindSpeed() const
{
float WeatherManager::getWindSpeed() const
{
return mWindSpeed;
}
}
bool WeatherManager::isInStorm() const
{
bool WeatherManager::isInStorm() const
{
return mIsStorm;
}
}
osg::Vec3f WeatherManager::getStormDirection() const
{
osg::Vec3f WeatherManager::getStormDirection() const
{
return mStormDirection;
}
}
void WeatherManager::advanceTime(double hours, bool incremental)
{
void WeatherManager::advanceTime(double hours, bool incremental)
{
// In Morrowind, when the player sleeps/waits, serves jail time, travels, or trains, all weather transitions are
// immediately applied, regardless of whatever transition time might have been remaining.
mTimePassed += hours;
mFastForward = !incremental ? true : mFastForward;
}
}
unsigned int WeatherManager::getWeatherID() const
{
unsigned int WeatherManager::getWeatherID() const
{
return mCurrentWeather;
}
}
NightDayMode WeatherManager::getNightDayMode() const
{
NightDayMode WeatherManager::getNightDayMode() const
{
return mNightDayMode;
}
}
bool WeatherManager::useTorches(float hour) const
{
bool WeatherManager::useTorches(float hour) const
{
bool isDark = hour < mSunriseTime || hour > mTimeSettings.mNightStart;
return isDark && !mPrecipitation;
}
}
void WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress)
{
void WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress)
{
ESM::WeatherState state;
state.mCurrentRegion = mCurrentRegion;
state.mTimePassed = mTimePassed;
@ -888,10 +895,10 @@ void WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress)
writer.startRecord(ESM::REC_WTHR);
state.save(writer);
writer.endRecord(ESM::REC_WTHR);
}
}
bool WeatherManager::readRecord(ESM::ESMReader& reader, uint32_t type)
{
bool WeatherManager::readRecord(ESM::ESMReader& reader, uint32_t type)
{
if(ESM::REC_WTHR == type)
{
static const int oldestCompatibleSaveFormat = 2;
@ -932,10 +939,10 @@ bool WeatherManager::readRecord(ESM::ESMReader& reader, uint32_t type)
}
return false;
}
}
void WeatherManager::clear()
{
void WeatherManager::clear()
{
stopSounds();
mCurrentRegion = "";
@ -944,30 +951,30 @@ void WeatherManager::clear()
forceWeather(0);
mRegions.clear();
importRegions();
}
}
inline void WeatherManager::addWeather(const std::string& name,
inline void WeatherManager::addWeather(const std::string& name,
float dlFactor, float dlOffset,
const std::string& particleEffect)
{
{
static const float fStromWindSpeed = mStore.get<ESM::GameSetting>().find("fStromWindSpeed")->mValue.getFloat();
Weather weather(name, fStromWindSpeed, mRainSpeed, dlFactor, dlOffset, particleEffect);
mWeatherSettings.push_back(weather);
}
}
inline void WeatherManager::importRegions()
{
inline void WeatherManager::importRegions()
{
for(const ESM::Region& region : mStore.get<ESM::Region>())
{
std::string regionID = Misc::StringUtils::lowerCase(region.mId);
mRegions.insert(std::make_pair(regionID, RegionWeather(region)));
}
}
}
inline void WeatherManager::regionalWeatherChanged(const std::string& regionID, RegionWeather& region)
{
inline void WeatherManager::regionalWeatherChanged(const std::string& regionID, RegionWeather& region)
{
// If the region is current, then add a weather transition for it.
MWWorld::ConstPtr player = MWMechanics::getPlayer();
if(player.isInCell())
@ -977,10 +984,10 @@ inline void WeatherManager::regionalWeatherChanged(const std::string& regionID,
addWeatherTransition(region.getWeather());
}
}
}
}
inline bool WeatherManager::updateWeatherTime()
{
inline bool WeatherManager::updateWeatherTime()
{
mWeatherUpdateTime -= mTimePassed;
mTimePassed = 0.0f;
if(mWeatherUpdateTime <= 0.0f)
@ -998,10 +1005,10 @@ inline bool WeatherManager::updateWeatherTime()
}
return false;
}
}
inline bool WeatherManager::updateWeatherRegion(const std::string& playerRegion)
{
inline bool WeatherManager::updateWeatherRegion(const std::string& playerRegion)
{
if(!playerRegion.empty() && playerRegion != mCurrentRegion)
{
mCurrentRegion = playerRegion;
@ -1010,10 +1017,10 @@ inline bool WeatherManager::updateWeatherRegion(const std::string& playerRegion)
}
return false;
}
}
inline void WeatherManager::updateWeatherTransitions(const float elapsedRealSeconds)
{
inline void WeatherManager::updateWeatherTransitions(const float elapsedRealSeconds)
{
// When a player chooses to train, wait, or serves jail time, any transitions will be fast forwarded to the last
// weather type set, regardless of the remaining transition time.
if(!mFastForward && inTransition())
@ -1054,23 +1061,23 @@ inline void WeatherManager::updateWeatherTransitions(const float elapsedRealSeco
mQueuedWeather = invalidWeatherID;
mFastForward = false;
}
}
}
inline void WeatherManager::forceWeather(const int weatherID)
{
inline void WeatherManager::forceWeather(const int weatherID)
{
mTransitionFactor = 0.0f;
mCurrentWeather = weatherID;
mNextWeather = invalidWeatherID;
mQueuedWeather = invalidWeatherID;
}
}
inline bool WeatherManager::inTransition()
{
inline bool WeatherManager::inTransition()
{
return mNextWeather != invalidWeatherID;
}
}
inline void WeatherManager::addWeatherTransition(const int weatherID)
{
inline void WeatherManager::addWeatherTransition(const int weatherID)
{
// In order to work like ChangeWeather expects, this method begins transitioning to the new weather immediately if
// no transition is in progress, otherwise it queues it to be transitioned.
@ -1085,12 +1092,12 @@ inline void WeatherManager::addWeatherTransition(const int weatherID)
{
mQueuedWeather = weatherID;
}
}
}
inline void WeatherManager::calculateWeatherResult(const float gameHour,
inline void WeatherManager::calculateWeatherResult(const float gameHour,
const float elapsedSeconds,
const bool isPaused)
{
{
float flash = 0.0f;
if(!inTransition())
{
@ -1113,10 +1120,10 @@ inline void WeatherManager::calculateWeatherResult(const float gameHour,
mResult.mFogColor += flashColor;
mResult.mAmbientColor += flashColor;
mResult.mSunColor += flashColor;
}
}
inline void WeatherManager::calculateResult(const int weatherID, const float gameHour)
{
inline void WeatherManager::calculateResult(const int weatherID, const float gameHour)
{
const Weather& current = mWeatherSettings[weatherID];
mResult.mCloudTexture = current.mCloudTexture;
@ -1190,15 +1197,19 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam
else
mResult.mSunDiscColor.a() = 1;
}
mResult.mStormDirection = calculateStormDirection(mResult.mParticleEffect);
}
inline void WeatherManager::calculateTransitionResult(const float factor, const float gameHour)
{
inline void WeatherManager::calculateTransitionResult(const float factor, const float gameHour)
{
calculateResult(mCurrentWeather, gameHour);
const MWRender::WeatherResult current = mResult;
calculateResult(mNextWeather, gameHour);
const MWRender::WeatherResult other = mResult;
mResult.mStormDirection = current.mStormDirection;
mResult.mNextStormDirection = other.mStormDirection;
mResult.mCloudTexture = current.mCloudTexture;
mResult.mNextCloudTexture = other.mCloudTexture;
mResult.mCloudBlendFactor = mWeatherSettings[mNextWeather].cloudBlendFactor(factor);
@ -1225,7 +1236,7 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
mResult.mNight = current.mNight;
float threshold = mWeatherSettings[mNextWeather].mRainThreshold;
if (threshold <= 0)
if (threshold <= 0.f)
threshold = 0.5f;
if(factor < threshold)
@ -1235,7 +1246,7 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
mResult.mRainEffect = current.mRainEffect;
mResult.mRainSpeed = current.mRainSpeed;
mResult.mRainEntranceSpeed = current.mRainEntranceSpeed;
mResult.mAmbientSoundVolume = 1 - factor / threshold;
mResult.mAmbientSoundVolume = 1.f - factor / threshold;
mResult.mPrecipitationAlpha = mResult.mAmbientSoundVolume;
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
mResult.mRainDiameter = current.mRainDiameter;
@ -1259,5 +1270,6 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
mResult.mRainMaxHeight = other.mRainMaxHeight;
mResult.mRainMaxRaindrops = other.mRainMaxRaindrops;
}
}
}

View File

@ -115,6 +115,8 @@ namespace MWWorld
class Weather
{
public:
static osg::Vec3f defaultDirection();
Weather(const std::string& name,
float stormWindSpeed,
float rainSpeed,
@ -189,6 +191,8 @@ namespace MWWorld
std::string mRainEffect;
osg::Vec3f mStormDirection;
// Note: For Weather Blight, there is a "Disease Chance" (=0.1) setting. But according to MWSFD this feature
// is broken in the vanilla game and was disabled.

View File

@ -516,6 +516,14 @@ namespace Shader
// We could fall back to a texture size uniform if EXT_gpu_shader4 is missing
}
bool simpleLighting = false;
node.getUserValue("simpleLighting", simpleLighting);
if (simpleLighting)
{
defineMap["forcePPL"] = "1";
defineMap["endLight"] = "0";
}
if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT && !previousAddedState->hasMode(GL_ALPHA_TEST))
removedState->setMode(GL_ALPHA_TEST, writableStateSet->getMode(GL_ALPHA_TEST));
// This disables the deprecated fixed-function alpha test

View File

@ -36,6 +36,9 @@ set(SHADER_FILES
gui_fragment.glsl
debug_vertex.glsl
debug_fragment.glsl
sky_vertex.glsl
sky_fragment.glsl
skypasses.glsl
)
copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}")

View File

@ -0,0 +1,87 @@
#version 120
#include "skypasses.glsl"
uniform int pass;
uniform sampler2D diffuseMap;
uniform sampler2D maskMap; // PASS_MOON
uniform float opacity; // PASS_CLOUDS, PASS_ATMOSPHERE_NIGHT
uniform vec4 moonBlend; // PASS_MOON
uniform vec4 atmosphereFade; // PASS_MOON
varying vec2 diffuseMapUV;
varying vec4 passColor;
void paintAtmosphere(inout vec4 color)
{
color = gl_FrontMaterial.emission;
color.a *= passColor.a;
}
void paintAtmosphereNight(inout vec4 color)
{
color = texture2D(diffuseMap, diffuseMapUV);
color.a *= passColor.a * opacity;
}
void paintClouds(inout vec4 color)
{
color = texture2D(diffuseMap, diffuseMapUV);
color.a *= passColor.a * opacity;
color.xyz = clamp(color.xyz * gl_FrontMaterial.emission.xyz, 0.0, 1.0);
// ease transition between clear color and atmosphere/clouds
color = mix(vec4(gl_Fog.color.xyz, color.a), color, passColor.a);
}
void paintMoon(inout vec4 color)
{
vec4 phase = texture2D(diffuseMap, diffuseMapUV);
vec4 mask = texture2D(maskMap, diffuseMapUV);
vec4 blendedLayer = phase * moonBlend;
color = vec4(blendedLayer.xyz + atmosphereFade.xyz, atmosphereFade.a * mask.a);
}
void paintSun(inout vec4 color)
{
color = texture2D(diffuseMap, diffuseMapUV);
color.a *= gl_FrontMaterial.diffuse.a;
}
void paintSunflashQuery(inout vec4 color)
{
const float threshold = 0.8;
color = texture2D(diffuseMap, diffuseMapUV);
if (color.a <= threshold)
discard;
}
void paintSunglare(inout vec4 color)
{
color = gl_FrontMaterial.emission;
color.a = gl_FrontMaterial.diffuse.a;
}
void main()
{
vec4 color = vec4(0.0);
if (pass == PASS_ATMOSPHERE)
paintAtmosphere(color);
else if (pass == PASS_ATMOSPHERE_NIGHT)
paintAtmosphereNight(color);
else if (pass == PASS_CLOUDS)
paintClouds(color);
else if (pass == PASS_MOON)
paintMoon(color);
else if (pass == PASS_SUN)
paintSun(color);
else if (pass == PASS_SUNFLASH_QUERY)
paintSunflashQuery(color);
else if (pass == PASS_SUNGLARE)
paintSunglare(color);
gl_FragData[0] = color;
}

View File

@ -0,0 +1,20 @@
#version 120
#include "skypasses.glsl"
uniform mat4 projectionMatrix;
uniform int pass;
varying vec4 passColor;
varying vec2 diffuseMapUV;
void main()
{
gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex);
passColor = gl_Color;
if (pass == PASS_CLOUDS)
diffuseMapUV = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy;
else
diffuseMapUV = gl_MultiTexCoord0.xy;
}

View File

@ -0,0 +1,7 @@
#define PASS_ATMOSPHERE 0
#define PASS_ATMOSPHERE_NIGHT 1
#define PASS_CLOUDS 2
#define PASS_MOON 3
#define PASS_SUN 4
#define PASS_SUNFLASH_QUERY 5
#define PASS_SUNGLARE 6