#include "sky.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwworld/weather.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "vismask.hpp" #include "renderbin.hpp" #include "util.hpp" #include "skyutil.hpp" namespace { class WrapAroundOperator : public osgParticle::Operator { public: WrapAroundOperator(osg::Camera *camera, const osg::Vec3 &wrapRange) : osgParticle::Operator() , mCamera(camera) , mWrapRange(wrapRange) , mHalfWrapRange(mWrapRange / 2.0) { mPreviousCameraPosition = getCameraPosition(); } osg::Object *cloneType() const override { return nullptr; } osg::Object *clone(const osg::CopyOp &op) const override { return nullptr; } void operate(osgParticle::Particle *P, double dt) override { } void operateParticles(osgParticle::ParticleSystem *ps, double dt) override { osg::Vec3 position = getCameraPosition(); osg::Vec3 positionDifference = position - mPreviousCameraPosition; osg::Matrix toWorld, toLocal; std::vector worldMatrices = ps->getWorldMatrices(); if (!worldMatrices.empty()) { toWorld = worldMatrices[0]; toLocal.invert(toWorld); } for (int i = 0; i < ps->numParticles(); ++i) { osgParticle::Particle *p = ps->getParticle(i); p->setPosition(toWorld.preMult(p->getPosition())); p->setPosition(p->getPosition() - positionDifference); for (int j = 0; j < 3; ++j) // wrap-around in all 3 dimensions { osg::Vec3 pos = p->getPosition(); if (pos[j] < -mHalfWrapRange[j]) pos[j] = mHalfWrapRange[j] + fmod(pos[j] - mHalfWrapRange[j],mWrapRange[j]); else if (pos[j] > mHalfWrapRange[j]) pos[j] = fmod(pos[j] + mHalfWrapRange[j],mWrapRange[j]) - mHalfWrapRange[j]; p->setPosition(pos); } p->setPosition(toLocal.preMult(p->getPosition())); } mPreviousCameraPosition = position; } protected: osg::Camera *mCamera; osg::Vec3 mPreviousCameraPosition; osg::Vec3 mWrapRange; osg::Vec3 mHalfWrapRange; osg::Vec3 getCameraPosition() { return mCamera->getInverseViewMatrix().getTrans(); } }; class WeatherAlphaOperator : public osgParticle::Operator { public: WeatherAlphaOperator(float& alpha, bool rain) : mAlpha(alpha) , mIsRain(rain) { } osg::Object *cloneType() const override { return nullptr; } osg::Object *clone(const osg::CopyOp &op) const override { return nullptr; } void operate(osgParticle::Particle *particle, double dt) override { constexpr float rainThreshold = 0.6f; // Rain_Threshold? float alpha = mIsRain ? mAlpha * rainThreshold : mAlpha; particle->setAlphaRange(osgParticle::rangef(alpha, alpha)); } private: float &mAlpha; bool mIsRain; }; // Updater for alpha value on a node's StateSet. Assumes the node has an existing Material StateAttribute. class AlphaFader : public SceneUtil::StateSetUpdater { public: /// @param alpha the variable alpha value is recovered from AlphaFader(const float& alpha) : mAlpha(alpha) { } void setDefaults(osg::StateSet* stateset) override { // need to create a deep copy of StateAttributes we will modify osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); stateset->setAttribute(osg::clone(mat, osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); } void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override { osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, mAlpha)); } protected: const float &mAlpha; }; // Helper for adding AlphaFaders to a subgraph class SetupVisitor : public osg::NodeVisitor { public: SetupVisitor(const float &alpha) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mAlpha(alpha) { } void apply(osg::Node &node) override { if (osg::StateSet* stateset = node.getStateSet()) { if (stateset->getAttribute(osg::StateAttribute::MATERIAL)) { SceneUtil::CompositeStateSetUpdater* composite = nullptr; osg::Callback* callback = node.getUpdateCallback(); while (callback) { composite = dynamic_cast(callback); if (composite) break; callback = callback->getNestedCallback(); } osg::ref_ptr alphaFader = new AlphaFader(mAlpha); if (composite) composite->addController(alphaFader); else node.addUpdateCallback(alphaFader); } } traverse(node); } private: const float &mAlpha; }; class SkyRTT : public SceneUtil::RTTNode { public: SkyRTT(osg::Vec2f size, osg::Group* earlyRenderBinRoot) : RTTNode(static_cast(size.x()), static_cast(size.y()), 0, false, 1, StereoAwareness::Aware), mEarlyRenderBinRoot(earlyRenderBinRoot) { setDepthBufferInternalFormat(GL_DEPTH24_STENCIL8); } void setDefaults(osg::Camera* camera) override { camera->setReferenceFrame(osg::Camera::RELATIVE_RF); camera->setName("SkyCamera"); camera->setNodeMask(MWRender::Mask_RenderToTexture); camera->setCullMask(MWRender::Mask_Sky); camera->addChild(mEarlyRenderBinRoot); SceneUtil::ShadowManager::disableShadowsForStateSet(camera->getOrCreateStateSet()); } private: osg::ref_ptr mEarlyRenderBinRoot; }; } namespace MWRender { SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneManager, bool enableSkyRTT) : mSceneManager(sceneManager) , mCamera(nullptr) , mAtmosphereNightRoll(0.f) , mCreated(false) , mIsStorm(false) , mDay(0) , mMonth(0) , mCloudAnimationTimer(0.f) , mRainTimer(0.f) , mStormParticleDirection(MWWorld::Weather::defaultDirection()) , mStormDirection(MWWorld::Weather::defaultDirection()) , mClouds() , mNextClouds() , mCloudBlendFactor(0.f) , mCloudSpeed(0.f) , mStarsOpacity(0.f) , mRemainingTransitionTime(0.f) , mRainEnabled(false) , mRainSpeed(0.f) , mRainDiameter(0.f) , mRainMinHeight(0.f) , mRainMaxHeight(0.f) , mRainEntranceSpeed(1.f) , mRainMaxRaindrops(0) , mWindSpeed(0.f) , mBaseWindSpeed(0.f) , mEnabled(true) , mSunEnabled(true) , mSunglareEnabled(true) , mPrecipitationAlpha(0.f) , mDirtyParticlesEffect(false) { osg::ref_ptr skyroot = new CameraRelativeTransform; skyroot->setName("Sky Root"); // Assign empty program to specify we don't want shaders when we are rendering in FFP pipeline if (!mSceneManager->getForceShaders()) skyroot->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::PROTECTED|osg::StateAttribute::ON); SceneUtil::ShadowManager::disableShadowsForStateSet(skyroot->getOrCreateStateSet()); parentNode->addChild(skyroot); mEarlyRenderBinRoot = new osg::Group; // render before the world is rendered mEarlyRenderBinRoot->getOrCreateStateSet()->setRenderBinDetails(RenderBin_Sky, "RenderBin"); // Prevent unwanted clipping by water reflection camera's clipping plane mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_CLIP_PLANE0, osg::StateAttribute::OFF); if (enableSkyRTT) { mSkyRTT = new SkyRTT(Settings::Manager::getVector2("sky rtt resolution", "Fog"), mEarlyRenderBinRoot); skyroot->addChild(mSkyRTT); mRootNode = new osg::Group; skyroot->addChild(mRootNode); } else mRootNode = skyroot; mRootNode->setNodeMask(Mask_Sky); mRootNode->addChild(mEarlyRenderBinRoot); mUnderwaterSwitch = new UnderwaterSwitchCallback(skyroot); } void SkyManager::create() { assert(!mCreated); bool forceShaders = mSceneManager->getForceShaders(); mAtmosphereDay = mSceneManager->getInstance(Settings::Manager::getString("skyatmosphere", "Models"), mEarlyRenderBinRoot); ModVertexAlphaVisitor modAtmosphere(ModVertexAlphaVisitor::Atmosphere); mAtmosphereDay->accept(modAtmosphere); mAtmosphereUpdater = new AtmosphereUpdater; mAtmosphereDay->addUpdateCallback(mAtmosphereUpdater); mAtmosphereNightNode = new osg::PositionAttitudeTransform; mAtmosphereNightNode->setNodeMask(0); mEarlyRenderBinRoot->addChild(mAtmosphereNightNode); osg::ref_ptr atmosphereNight; if (mSceneManager->getVFS()->exists(Settings::Manager::getString("skynight02", "Models"))) atmosphereNight = mSceneManager->getInstance(Settings::Manager::getString("skynight02", "Models"), mAtmosphereNightNode); else atmosphereNight = mSceneManager->getInstance(Settings::Manager::getString("skynight01", "Models"), mAtmosphereNightNode); atmosphereNight->getOrCreateStateSet()->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); ModVertexAlphaVisitor modStars(ModVertexAlphaVisitor::Stars); atmosphereNight->accept(modStars); mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getImageManager(), forceShaders); atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater); mSun = std::make_unique(mEarlyRenderBinRoot, *mSceneManager); mSun->setSunglare(mSunglareEnabled); mMasser = std::make_unique(mEarlyRenderBinRoot, *mSceneManager, Fallback::Map::getFloat("Moons_Masser_Size")/125, Moon::Type_Masser); mSecunda = std::make_unique(mEarlyRenderBinRoot, *mSceneManager, Fallback::Map::getFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda); mCloudNode = new osg::Group; mEarlyRenderBinRoot->addChild(mCloudNode); mCloudMesh = new osg::PositionAttitudeTransform; osg::ref_ptr cloudMeshChild = mSceneManager->getInstance(Settings::Manager::getString("skyclouds", "Models"), mCloudMesh); mCloudUpdater = new CloudUpdater(forceShaders); mCloudUpdater->setOpacity(1.f); cloudMeshChild->addUpdateCallback(mCloudUpdater); mCloudMesh->addChild(cloudMeshChild); mNextCloudMesh = new osg::PositionAttitudeTransform; osg::ref_ptr nextCloudMeshChild = mSceneManager->getInstance(Settings::Manager::getString("skyclouds", "Models"), mNextCloudMesh); mNextCloudUpdater = new CloudUpdater(forceShaders); mNextCloudUpdater->setOpacity(0.f); nextCloudMeshChild->addUpdateCallback(mNextCloudUpdater); mNextCloudMesh->setNodeMask(0); mNextCloudMesh->addChild(nextCloudMeshChild); mCloudNode->addChild(mCloudMesh); mCloudNode->addChild(mNextCloudMesh); ModVertexAlphaVisitor modClouds(ModVertexAlphaVisitor::Clouds); mCloudMesh->accept(modClouds); mNextCloudMesh->accept(modClouds); if (mSceneManager->getForceShaders()) { Shader::ShaderManager::DefineMap defines = {}; Stereo::Manager::instance().shaderStereoDefines(defines); auto vertex = mSceneManager->getShaderManager().getShader("sky_vertex.glsl", defines, osg::Shader::VERTEX); auto fragment = mSceneManager->getShaderManager().getShader("sky_fragment.glsl", defines, osg::Shader::FRAGMENT); auto program = mSceneManager->getShaderManager().getProgram(vertex, fragment); mEarlyRenderBinRoot->getOrCreateStateSet()->addUniform(new osg::Uniform("pass", -1)); mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(program, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); } osg::ref_ptr depth = new SceneUtil::AutoDepth; depth->setWriteMask(false); mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(depth); mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_FOG, osg::StateAttribute::OFF); mMoonScriptColor = Fallback::Map::getColour("Moons_Script_Color"); mCreated = true; } void SkyManager::setCamera(osg::Camera *camera) { mCamera = camera; } void SkyManager::createRain() { if (mRainNode) return; mRainNode = new osg::Group; mRainParticleSystem = new NifOsg::ParticleSystem; osg::Vec3 rainRange = osg::Vec3(mRainDiameter, mRainDiameter, (mRainMinHeight+mRainMaxHeight)/2.f); mRainParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED); mRainParticleSystem->setAlignVectorX(osg::Vec3f(0.1,0,0)); mRainParticleSystem->setAlignVectorY(osg::Vec3f(0,0,1)); osg::ref_ptr stateset = mRainParticleSystem->getOrCreateStateSet(); osg::ref_ptr raindropTex = new osg::Texture2D(mSceneManager->getImageManager()->getImage("textures/tx_raindrop_01.dds")); raindropTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); raindropTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); stateset->setTextureAttributeAndModes(0, raindropTex); stateset->setNestRenderBins(false); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); stateset->setMode(GL_BLEND, osg::StateAttribute::ON); osg::ref_ptr mat = new osg::Material; mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); stateset->setAttributeAndModes(mat); osgParticle::Particle& particleTemplate = mRainParticleSystem->getDefaultParticleTemplate(); particleTemplate.setSizeRange(osgParticle::rangef(5.f, 15.f)); particleTemplate.setAlphaRange(osgParticle::rangef(1.f, 1.f)); particleTemplate.setLifeTime(1); osg::ref_ptr emitter = new osgParticle::ModularEmitter; emitter->setParticleSystem(mRainParticleSystem); osg::ref_ptr placer = new osgParticle::BoxPlacer; placer->setXRange(-rainRange.x() / 2, rainRange.x() / 2); placer->setYRange(-rainRange.y() / 2, rainRange.y() / 2); placer->setZRange(-rainRange.z() / 2, rainRange.z() / 2); emitter->setPlacer(placer); mPlacer = placer; // FIXME: vanilla engine does not use a particle system to handle rain, it uses a NIF-file with 20 raindrops in it. // It spawns the (maxRaindrops-getParticleSystem()->numParticles())*dt/rainEntranceSpeed batches every frame (near 1-2). // Since the rain is a regular geometry, it produces water ripples, also in theory it can be removed if collides with something. osg::ref_ptr counter = new RainCounter; counter->setNumberOfParticlesPerSecondToCreate(mRainMaxRaindrops/mRainEntranceSpeed*20); emitter->setCounter(counter); mCounter = counter; osg::ref_ptr shooter = new RainShooter; mRainShooter = shooter; emitter->setShooter(shooter); osg::ref_ptr updater = new osgParticle::ParticleSystemUpdater; updater->addParticleSystem(mRainParticleSystem); osg::ref_ptr program = new osgParticle::ModularProgram; program->addOperator(new WrapAroundOperator(mCamera,rainRange)); program->addOperator(new WeatherAlphaOperator(mPrecipitationAlpha, true)); program->setParticleSystem(mRainParticleSystem); mRainNode->addChild(program); mRainNode->addChild(emitter); mRainNode->addChild(mRainParticleSystem); mRainNode->addChild(updater); // Note: if we ever switch to regular geometry rain, it'll need to use an AlphaFader. mRainNode->addCullCallback(mUnderwaterSwitch); mRainNode->setNodeMask(Mask_WeatherParticles); mRainParticleSystem->setUserValue("simpleLighting", true); mSceneManager->recreateShaders(mRainNode); mRootNode->addChild(mRainNode); } void SkyManager::destroyRain() { if (!mRainNode) return; mRootNode->removeChild(mRainNode); mRainNode = nullptr; mPlacer = nullptr; mCounter = nullptr; mRainParticleSystem = nullptr; mRainShooter = nullptr; } SkyManager::~SkyManager() { if (mRootNode) { mRootNode->getParent(0)->removeChild(mRootNode); mRootNode = nullptr; } } int SkyManager::getMasserPhase() const { if (!mCreated) return 0; return mMasser->getPhaseInt(); } int SkyManager::getSecundaPhase() const { if (!mCreated) return 0; return mSecunda->getPhaseInt(); } bool SkyManager::isEnabled() { return mEnabled; } bool SkyManager::hasRain() const { return mRainNode != nullptr; } float SkyManager::getPrecipitationAlpha() const { if (mEnabled && !mIsStorm && (hasRain() || mParticleNode)) return mPrecipitationAlpha; return 0.f; } void SkyManager::update(float duration) { if (!mEnabled) return; switchUnderwaterRain(); if (mIsStorm && mParticleNode) { osg::Quat quat; quat.makeRotate(MWWorld::Weather::defaultDirection(), mStormParticleDirection); // Morrowind deliberately rotates the blizzard mesh, so so should we. if (mCurrentParticleEffect == Settings::Manager::getString("weatherblizzard", "Models")) quat.makeRotate(osg::Vec3f(-1,0,0), mStormParticleDirection); mParticleNode->setAttitude(quat); } // UV Scroll the clouds mCloudAnimationTimer += duration * mCloudSpeed * 0.003; mNextCloudUpdater->setTextureCoord(mCloudAnimationTimer); mCloudUpdater->setTextureCoord(mCloudAnimationTimer); // morrowind rotates each cloud mesh independently osg::Quat rotation; rotation.makeRotate(MWWorld::Weather::defaultDirection(), mStormDirection); mCloudMesh->setAttitude(rotation); if (mNextCloudMesh->getNodeMask()) { rotation.makeRotate(MWWorld::Weather::defaultDirection(), mNextStormDirection); mNextCloudMesh->setAttitude(rotation); } // rotate the stars by 360 degrees every 4 days mAtmosphereNightRoll += MWBase::Environment::get().getWorld()->getTimeScaleFactor()*duration*osg::DegreesToRadians(360.f) / (3600*96.f); if (mAtmosphereNightNode->getNodeMask() != 0) mAtmosphereNightNode->setAttitude(osg::Quat(mAtmosphereNightRoll, osg::Vec3f(0,0,1))); } void SkyManager::setEnabled(bool enabled) { if (enabled && !mCreated) create(); const osg::Node::NodeMask mask = enabled ? Mask_Sky : 0u; mEarlyRenderBinRoot->setNodeMask(mask); mRootNode->setNodeMask(mask); if (!enabled && mParticleNode && mParticleEffect) { mCurrentParticleEffect.clear(); mDirtyParticlesEffect = true; } mEnabled = enabled; } void SkyManager::setMoonColour (bool red) { if (!mCreated) return; mSecunda->setColor(red ? mMoonScriptColor : osg::Vec4f(1,1,1,1)); } void SkyManager::updateRainParameters() { if (mRainShooter) { float angle = -std::atan(mWindSpeed/50.f); mRainShooter->setVelocity(osg::Vec3f(0, mRainSpeed*std::sin(angle), -mRainSpeed/std::cos(angle))); mRainShooter->setAngle(angle); osg::Vec3 rainRange = osg::Vec3(mRainDiameter, mRainDiameter, (mRainMinHeight+mRainMaxHeight)/2.f); mPlacer->setXRange(-rainRange.x() / 2, rainRange.x() / 2); mPlacer->setYRange(-rainRange.y() / 2, rainRange.y() / 2); mPlacer->setZRange(-rainRange.z() / 2, rainRange.z() / 2); mCounter->setNumberOfParticlesPerSecondToCreate(mRainMaxRaindrops/mRainEntranceSpeed*20); } } void SkyManager::switchUnderwaterRain() { if (!mRainParticleSystem) return; bool freeze = mUnderwaterSwitch->isUnderwater(); mRainParticleSystem->setFrozen(freeze); } void SkyManager::setWeather(const WeatherResult& weather) { if (!mCreated) return; mRainEntranceSpeed = weather.mRainEntranceSpeed; mRainMaxRaindrops = weather.mRainMaxRaindrops; mRainDiameter = weather.mRainDiameter; mRainMinHeight = weather.mRainMinHeight; mRainMaxHeight = weather.mRainMaxHeight; mRainSpeed = weather.mRainSpeed; mWindSpeed = weather.mWindSpeed; mBaseWindSpeed = weather.mBaseWindSpeed; if (mRainEffect != weather.mRainEffect) { mRainEffect = weather.mRainEffect; if (!mRainEffect.empty()) { createRain(); } else { destroyRain(); } } updateRainParameters(); mIsStorm = weather.mIsStorm; if (mIsStorm) mStormDirection = weather.mStormDirection; if (mDirtyParticlesEffect || (mCurrentParticleEffect != weather.mParticleEffect)) { mDirtyParticlesEffect = false; mCurrentParticleEffect = weather.mParticleEffect; // cleanup old particles if (mParticleEffect) { mParticleNode->removeChild(mParticleEffect); mParticleEffect = nullptr; } if (mCurrentParticleEffect.empty()) { if (mParticleNode) { mRootNode->removeChild(mParticleNode); mParticleNode = nullptr; } } else { if (!mParticleNode) { mParticleNode = new osg::PositionAttitudeTransform; mParticleNode->addCullCallback(mUnderwaterSwitch); mParticleNode->setNodeMask(Mask_WeatherParticles); mRootNode->addChild(mParticleNode); } mParticleEffect = mSceneManager->getInstance(mCurrentParticleEffect, mParticleNode); SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::make_shared()); mParticleEffect->accept(assignVisitor); SetupVisitor alphaFaderSetupVisitor(mPrecipitationAlpha); mParticleEffect->accept(alphaFaderSetupVisitor); SceneUtil::FindByClassVisitor findPSVisitor(std::string("ParticleSystem")); mParticleEffect->accept(findPSVisitor); for (unsigned int i = 0; i < findPSVisitor.mFoundNodes.size(); ++i) { osgParticle::ParticleSystem *ps = static_cast(findPSVisitor.mFoundNodes[i]); osg::ref_ptr program = new osgParticle::ModularProgram; if (!mIsStorm) program->addOperator(new WrapAroundOperator(mCamera,osg::Vec3(1024,1024,800))); program->addOperator(new WeatherAlphaOperator(mPrecipitationAlpha, false)); program->setParticleSystem(ps); mParticleNode->addChild(program); for (int particleIndex = 0; particleIndex < ps->numParticles(); ++particleIndex) { ps->getParticle(particleIndex)->setAlphaRange(osgParticle::rangef(mPrecipitationAlpha, mPrecipitationAlpha)); ps->getParticle(particleIndex)->update(0, true); } ps->setUserValue("simpleLighting", true); } mSceneManager->recreateShaders(mParticleNode); } } if (mClouds != weather.mCloudTexture) { mClouds = weather.mCloudTexture; std::string texture = Misc::ResourceHelpers::correctTexturePath(mClouds, mSceneManager->getVFS()); osg::ref_ptr cloudTex = new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)); cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); mCloudUpdater->setTexture(cloudTex); } if (mStormDirection != weather.mStormDirection) mStormDirection = weather.mStormDirection; if (mNextStormDirection != weather.mNextStormDirection) mNextStormDirection = weather.mNextStormDirection; if (mNextClouds != weather.mNextCloudTexture) { mNextClouds = weather.mNextCloudTexture; if (!mNextClouds.empty()) { std::string texture = Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS()); osg::ref_ptr cloudTex = new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)); cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); mNextCloudUpdater->setTexture(cloudTex); mNextStormDirection = weather.mStormDirection; } } if (mCloudBlendFactor != weather.mCloudBlendFactor) { mCloudBlendFactor = std::clamp(weather.mCloudBlendFactor, 0.f, 1.f); mCloudUpdater->setOpacity(1.f - mCloudBlendFactor); mNextCloudUpdater->setOpacity(mCloudBlendFactor); mNextCloudMesh->setNodeMask(mCloudBlendFactor > 0.f ? ~0u : 0); } if (mCloudColour != weather.mFogColor) { osg::Vec4f clr (weather.mFogColor); clr += osg::Vec4f(0.13f, 0.13f, 0.13f, 0.f); mCloudUpdater->setEmissionColor(clr); mNextCloudUpdater->setEmissionColor(clr); mCloudColour = weather.mFogColor; } if (mSkyColour != weather.mSkyColor) { mSkyColour = weather.mSkyColor; mAtmosphereUpdater->setEmissionColor(mSkyColour); mMasser->setAtmosphereColor(mSkyColour); mSecunda->setAtmosphereColor(mSkyColour); } if (mFogColour != weather.mFogColor) { mFogColour = weather.mFogColor; } mCloudSpeed = weather.mCloudSpeed; mMasser->adjustTransparency(weather.mGlareView); mSecunda->adjustTransparency(weather.mGlareView); mSun->setColor(weather.mSunDiscColor); mSun->adjustTransparency(weather.mGlareView * weather.mSunDiscColor.a()); float nextStarsOpacity = weather.mNightFade * weather.mGlareView; if (weather.mNight && mStarsOpacity != nextStarsOpacity) { mStarsOpacity = nextStarsOpacity; mAtmosphereNightUpdater->setFade(mStarsOpacity); } mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0u : 0); mPrecipitationAlpha = weather.mPrecipitationAlpha; } float SkyManager::getBaseWindSpeed() const { if (!mCreated) return 0.f; return mBaseWindSpeed; } void SkyManager::setSunglare(bool enabled) { mSunglareEnabled = enabled; if (mSun) mSun->setSunglare(mSunglareEnabled); } void SkyManager::sunEnable() { if (!mCreated) return; mSun->setVisible(true); } void SkyManager::sunDisable() { if (!mCreated) return; mSun->setVisible(false); } void SkyManager::setStormParticleDirection(const osg::Vec3f &direction) { mStormParticleDirection = direction; } void SkyManager::setSunDirection(const osg::Vec3f& direction) { if (!mCreated) return; mSun->setDirection(direction); } void SkyManager::setMasserState(const MoonState& state) { if(!mCreated) return; mMasser->setState(state); } void SkyManager::setSecundaState(const MoonState& state) { if(!mCreated) return; mSecunda->setState(state); } void SkyManager::setDate(int day, int month) { mDay = day; mMonth = month; } void SkyManager::setGlareTimeOfDayFade(float val) { mSun->setGlareTimeOfDayFade(val); } void SkyManager::setWaterHeight(float height) { mUnderwaterSwitch->setWaterLevel(height); } void SkyManager::listAssetsToPreload(std::vector& models, std::vector& textures) { models.emplace_back(Settings::Manager::getString("skyatmosphere", "Models")); if (mSceneManager->getVFS()->exists(Settings::Manager::getString("skynight02", "Models"))) models.emplace_back(Settings::Manager::getString("skynight02", "Models")); models.emplace_back(Settings::Manager::getString("skynight01", "Models")); models.emplace_back(Settings::Manager::getString("skyclouds", "Models")); models.emplace_back(Settings::Manager::getString("weatherashcloud", "Models")); models.emplace_back(Settings::Manager::getString("weatherblightcloud", "Models")); models.emplace_back(Settings::Manager::getString("weathersnow", "Models")); models.emplace_back(Settings::Manager::getString("weatherblizzard", "Models")); textures.emplace_back("textures/tx_mooncircle_full_s.dds"); textures.emplace_back("textures/tx_mooncircle_full_m.dds"); textures.emplace_back("textures/tx_masser_new.dds"); textures.emplace_back("textures/tx_masser_one_wax.dds"); textures.emplace_back("textures/tx_masser_half_wax.dds"); textures.emplace_back("textures/tx_masser_three_wax.dds"); textures.emplace_back("textures/tx_masser_one_wan.dds"); textures.emplace_back("textures/tx_masser_half_wan.dds"); textures.emplace_back("textures/tx_masser_three_wan.dds"); textures.emplace_back("textures/tx_masser_full.dds"); textures.emplace_back("textures/tx_secunda_new.dds"); textures.emplace_back("textures/tx_secunda_one_wax.dds"); textures.emplace_back("textures/tx_secunda_half_wax.dds"); textures.emplace_back("textures/tx_secunda_three_wax.dds"); textures.emplace_back("textures/tx_secunda_one_wan.dds"); textures.emplace_back("textures/tx_secunda_half_wan.dds"); textures.emplace_back("textures/tx_secunda_three_wan.dds"); textures.emplace_back("textures/tx_secunda_full.dds"); textures.emplace_back("textures/tx_sun_05.dds"); textures.emplace_back("textures/tx_sun_flash_grey_05.dds"); textures.emplace_back("textures/tx_raindrop_01.dds"); } void SkyManager::setWaterEnabled(bool enabled) { mUnderwaterSwitch->setEnabled(enabled); } }