1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-05 15:40:10 +00:00
OpenMW/apps/openmw/mwrender/renderingmanager.cpp
glassmancody.info 16856d45c5 Lighting Patch
Fixes build errors with older OSG builds and some issues with 'shared' layout.
Bring back ambient in inventory through lightmodel instead of sun ambient, mirrors scene ambient/sunlight relationship.
Forces shaders when certain lighting methods are enabled and finalize settings.
Correctly override sun for localmap.
2021-04-16 11:55:40 -07:00

1353 lines
52 KiB
C++

#include "renderingmanager.hpp"
#include <limits>
#include <cstdlib>
#include <osg/Light>
#include <osg/LightModel>
#include <osg/Fog>
#include <osg/Material>
#include <osg/PolygonMode>
#include <osg/Group>
#include <osg/UserDataContainer>
#include <osg/ComputeBoundsVisitor>
#include <osgUtil/LineSegmentIntersector>
#include <osgViewer/Viewer>
#include <components/nifosg/nifloader.hpp>
#include <components/debug/debuglog.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/resource/keyframemanager.hpp>
#include <components/shader/removedalphafunc.hpp>
#include <components/shader/shadermanager.hpp>
#include <components/settings/settings.hpp>
#include <components/sceneutil/util.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/statesetupdater.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/workqueue.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include <components/sceneutil/writescene.hpp>
#include <components/sceneutil/shadow.hpp>
#include <components/terrain/terraingrid.hpp>
#include <components/terrain/quadtreeworld.hpp>
#include <components/esm/loadcell.hpp>
#include <components/fallback/fallback.hpp>
#include <components/detournavigator/navigator.hpp>
#include "../mwworld/cellstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwgui/loadingscreen.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "sky.hpp"
#include "effectmanager.hpp"
#include "npcanimation.hpp"
#include "vismask.hpp"
#include "pathgrid.hpp"
#include "camera.hpp"
#include "viewovershoulder.hpp"
#include "water.hpp"
#include "terrainstorage.hpp"
#include "util.hpp"
#include "navmesh.hpp"
#include "actorspaths.hpp"
#include "recastmesh.hpp"
#include "fogmanager.hpp"
#include "objectpaging.hpp"
#include "screenshotmanager.hpp"
#include "groundcover.hpp"
namespace MWRender
{
class StateUpdater : public SceneUtil::StateSetUpdater
{
public:
StateUpdater()
: mFogStart(0.f)
, mFogEnd(0.f)
, mWireframe(false)
{
}
void setDefaults(osg::StateSet *stateset) override
{
osg::LightModel* lightModel = new osg::LightModel;
stateset->setAttribute(lightModel, osg::StateAttribute::ON);
osg::Fog* fog = new osg::Fog;
fog->setMode(osg::Fog::LINEAR);
stateset->setAttributeAndModes(fog, osg::StateAttribute::ON);
if (mWireframe)
{
osg::PolygonMode* polygonmode = new osg::PolygonMode;
polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
stateset->setAttributeAndModes(polygonmode, osg::StateAttribute::ON);
}
else
stateset->removeAttribute(osg::StateAttribute::POLYGONMODE);
}
void apply(osg::StateSet* stateset, osg::NodeVisitor*) override
{
osg::LightModel* lightModel = static_cast<osg::LightModel*>(stateset->getAttribute(osg::StateAttribute::LIGHTMODEL));
lightModel->setAmbientIntensity(mAmbientColor);
osg::Fog* fog = static_cast<osg::Fog*>(stateset->getAttribute(osg::StateAttribute::FOG));
fog->setColor(mFogColor);
fog->setStart(mFogStart);
fog->setEnd(mFogEnd);
}
void setAmbientColor(const osg::Vec4f& col)
{
mAmbientColor = col;
}
void setFogColor(const osg::Vec4f& col)
{
mFogColor = col;
}
void setFogStart(float start)
{
mFogStart = start;
}
void setFogEnd(float end)
{
mFogEnd = end;
}
void setWireframe(bool wireframe)
{
if (mWireframe != wireframe)
{
mWireframe = wireframe;
reset();
}
}
bool getWireframe() const
{
return mWireframe;
}
private:
osg::Vec4f mAmbientColor;
osg::Vec4f mFogColor;
float mFogStart;
float mFogEnd;
bool mWireframe;
};
class PreloadCommonAssetsWorkItem : public SceneUtil::WorkItem
{
public:
PreloadCommonAssetsWorkItem(Resource::ResourceSystem* resourceSystem)
: mResourceSystem(resourceSystem)
{
}
void doWork() override
{
try
{
for (std::vector<std::string>::const_iterator it = mModels.begin(); it != mModels.end(); ++it)
mResourceSystem->getSceneManager()->cacheInstance(*it);
for (std::vector<std::string>::const_iterator it = mTextures.begin(); it != mTextures.end(); ++it)
mResourceSystem->getImageManager()->getImage(*it);
for (std::vector<std::string>::const_iterator it = mKeyframes.begin(); it != mKeyframes.end(); ++it)
mResourceSystem->getKeyframeManager()->get(*it);
}
catch (std::exception&)
{
// ignore error (will be shown when these are needed proper)
}
}
std::vector<std::string> mModels;
std::vector<std::string> mTextures;
std::vector<std::string> mKeyframes;
private:
Resource::ResourceSystem* mResourceSystem;
};
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::string& resourcePath, DetourNavigator::Navigator& navigator)
: mViewer(viewer)
, mRootNode(rootNode)
, mResourceSystem(resourceSystem)
, mWorkQueue(workQueue)
, mUnrefQueue(new SceneUtil::UnrefQueue)
, mNavigator(navigator)
, mMinimumAmbientLuminance(0.f)
, mNightEyeFactor(0.f)
, mFieldOfViewOverridden(false)
, mFieldOfViewOverride(0.f)
{
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
// Shadows and radial fog have problems with fixed-function mode
bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders")
|| Settings::Manager::getBool("force shaders", "Shaders")
|| Settings::Manager::getBool("enable shadows", "Shadows")
|| lightingMethod != SceneUtil::LightingMethod::FFP;
resourceSystem->getSceneManager()->setForceShaders(forceShaders);
// FIXME: calling dummy method because terrain needs to know whether lighting is clamped
resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders"));
resourceSystem->getSceneManager()->setAutoUseNormalMaps(Settings::Manager::getBool("auto use object normal maps", "Shaders"));
resourceSystem->getSceneManager()->setNormalMapPattern(Settings::Manager::getString("normal map pattern", "Shaders"));
resourceSystem->getSceneManager()->setNormalHeightMapPattern(Settings::Manager::getString("normal height map pattern", "Shaders"));
resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders"));
resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders"));
resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders"));
resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1);
// Let LightManager choose which backend to use based on our hint. For methods besides legacy lighting, this depends on support for various OpenGL extensions.
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(lightingMethod == SceneUtil::LightingMethod::FFP);
resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod());
resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod());
resourceSystem->getSceneManager()->setSupportedLightingMethods(sceneRoot->getSupportedLightingMethods());
mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f);
sceneRoot->setLightingMask(Mask_Lighting);
mSceneRoot = sceneRoot;
sceneRoot->setStartLight(1);
sceneRoot->setNodeMask(Mask_Scene);
sceneRoot->setName("Scene Root");
int shadowCastingTraversalMask = Mask_Scene;
if (Settings::Manager::getBool("actor shadows", "Shadows"))
shadowCastingTraversalMask |= Mask_Actor;
if (Settings::Manager::getBool("player shadows", "Shadows"))
shadowCastingTraversalMask |= Mask_Player;
if (Settings::Manager::getBool("terrain shadows", "Shadows"))
shadowCastingTraversalMask |= Mask_Terrain;
int indoorShadowCastingTraversalMask = shadowCastingTraversalMask;
if (Settings::Manager::getBool("object shadows", "Shadows"))
shadowCastingTraversalMask |= (Mask_Object|Mask_Static);
mShadowManager.reset(new SceneUtil::ShadowManager(sceneRoot, mRootNode, shadowCastingTraversalMask, indoorShadowCastingTraversalMask, mResourceSystem->getSceneManager()->getShaderManager()));
Shader::ShaderManager::DefineMap shadowDefines = mShadowManager->getShadowDefines();
Shader::ShaderManager::DefineMap lightDefines = sceneRoot->getLightDefines();
Shader::ShaderManager::DefineMap globalDefines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
for (auto itr = shadowDefines.begin(); itr != shadowDefines.end(); itr++)
globalDefines[itr->first] = itr->second;
globalDefines["forcePPL"] = Settings::Manager::getBool("force per pixel lighting", "Shaders") ? "1" : "0";
globalDefines["clamp"] = Settings::Manager::getBool("clamp lighting", "Shaders") ? "1" : "0";
globalDefines["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0";
globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0";
globalDefines["useGPUShader4"] = "0";
for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++)
globalDefines[itr->first] = itr->second;
float groundcoverDistance = (Constants::CellSizeInUnits * std::max(1, Settings::Manager::getInt("distance", "Groundcover")) - 1024) * 0.93;
globalDefines["groundcoverFadeStart"] = std::to_string(groundcoverDistance * 0.9f);
globalDefines["groundcoverFadeEnd"] = std::to_string(groundcoverDistance);
// It is unnecessary to stop/start the viewer as no frames are being rendered yet.
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines);
mNavMesh.reset(new NavMesh(mRootNode, Settings::Manager::getBool("enable nav mesh render", "Navigator")));
mActorsPaths.reset(new ActorsPaths(mRootNode, Settings::Manager::getBool("enable agents paths render", "Navigator")));
mRecastMesh.reset(new RecastMesh(mRootNode, Settings::Manager::getBool("enable recast mesh render", "Navigator")));
mPathgrid.reset(new Pathgrid(mRootNode));
mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get()));
if (getenv("OPENMW_DONT_PRECOMPILE") == nullptr)
{
mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation);
mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells"));
}
mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation());
mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem));
const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders");
const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders");
const std::string specularMapPattern = Settings::Manager::getString("terrain specular map pattern", "Shaders");
const bool useTerrainNormalMaps = Settings::Manager::getBool("auto use terrain normal maps", "Shaders");
const bool useTerrainSpecularMaps = Settings::Manager::getBool("auto use terrain specular maps", "Shaders");
mTerrainStorage.reset(new TerrainStorage(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps));
const float lodFactor = Settings::Manager::getFloat("lod factor", "Terrain");
if (Settings::Manager::getBool("distant terrain", "Terrain"))
{
const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain");
int compMapPower = Settings::Manager::getInt("composite map level", "Terrain");
compMapPower = std::max(-3, compMapPower);
float compMapLevel = pow(2, compMapPower);
const int vertexLodMod = Settings::Manager::getInt("vertex lod mod", "Terrain");
float maxCompGeometrySize = Settings::Manager::getFloat("max composite geometry size", "Terrain");
maxCompGeometrySize = std::max(maxCompGeometrySize, 1.f);
mTerrain.reset(new Terrain::QuadTreeWorld(
sceneRoot, mRootNode, mResourceSystem, mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug,
compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize));
if (Settings::Manager::getBool("object paging", "Terrain"))
{
mObjectPaging.reset(new ObjectPaging(mResourceSystem->getSceneManager()));
static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mObjectPaging.get());
mResourceSystem->addResourceManager(mObjectPaging.get());
}
}
else
mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug));
mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells"));
mTerrain->setWorkQueue(mWorkQueue.get());
if (Settings::Manager::getBool("enabled", "Groundcover"))
{
osg::ref_ptr<osg::Group> groundcoverRoot = new osg::Group;
groundcoverRoot->setNodeMask(Mask_Groundcover);
groundcoverRoot->setName("Groundcover Root");
sceneRoot->addChild(groundcoverRoot);
mGroundcoverUpdater = new GroundcoverUpdater;
groundcoverRoot->addUpdateCallback(mGroundcoverUpdater);
float chunkSize = Settings::Manager::getFloat("min chunk size", "Groundcover");
if (chunkSize >= 1.0f)
chunkSize = 1.0f;
else if (chunkSize >= 0.5f)
chunkSize = 0.5f;
else if (chunkSize >= 0.25f)
chunkSize = 0.25f;
else if (chunkSize != 0.125f)
chunkSize = 0.125f;
float density = Settings::Manager::getFloat("density", "Groundcover");
density = std::clamp(density, 0.f, 1.f);
mGroundcoverWorld.reset(new Terrain::QuadTreeWorld(groundcoverRoot, mTerrainStorage.get(), Mask_Groundcover, lodFactor, chunkSize));
mGroundcover.reset(new Groundcover(mResourceSystem->getSceneManager(), density));
static_cast<Terrain::QuadTreeWorld*>(mGroundcoverWorld.get())->addChunkManager(mGroundcover.get());
mResourceSystem->addResourceManager(mGroundcover.get());
}
// water goes after terrain for correct waterculling order
mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
mCamera.reset(new Camera(mViewer->getCamera()));
if (Settings::Manager::getBool("view over shoulder", "Camera"))
mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get()));
mScreenshotManager.reset(new ScreenshotManager(viewer, mRootNode, sceneRoot, mResourceSystem, mWater.get()));
mViewer->setLightingMode(osgViewer::View::NO_LIGHT);
osg::ref_ptr<osg::LightSource> source = new osg::LightSource;
source->setNodeMask(Mask_Lighting);
mSunLight = new osg::Light;
source->setLight(mSunLight);
mSunLight->setDiffuse(osg::Vec4f(0,0,0,1));
mSunLight->setAmbient(osg::Vec4f(0,0,0,1));
mSunLight->setSpecular(osg::Vec4f(0,0,0,0));
mSunLight->setConstantAttenuation(1.f);
sceneRoot->setSunlight(mSunLight);
sceneRoot->addChild(source);
sceneRoot->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
sceneRoot->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON);
sceneRoot->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
osg::ref_ptr<osg::Material> defaultMat (new osg::Material);
defaultMat->setColorMode(osg::Material::OFF);
defaultMat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat);
mFog.reset(new FogManager());
mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager()));
mSky->setCamera(mViewer->getCamera());
source->setStateSetModes(*mRootNode->getOrCreateStateSet(), osg::StateAttribute::ON);
mStateUpdater = new StateUpdater;
sceneRoot->addUpdateCallback(mStateUpdater);
osg::Camera::CullingMode cullingMode = osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING;
if (!Settings::Manager::getBool("small feature culling", "Camera"))
cullingMode &= ~(osg::CullStack::SMALL_FEATURE_CULLING);
else
{
mViewer->getCamera()->setSmallFeatureCullingPixelSize(Settings::Manager::getFloat("small feature culling pixel size", "Camera"));
cullingMode |= osg::CullStack::SMALL_FEATURE_CULLING;
}
mViewer->getCamera()->setCullingMode( cullingMode );
mViewer->getCamera()->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
mViewer->getCamera()->setCullingMode(cullingMode);
mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater));
NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor);
NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect);
Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models"));
mNearClip = Settings::Manager::getFloat("near clip", "Camera");
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
float fov = Settings::Manager::getFloat("field of view", "Camera");
mFieldOfView = std::min(std::max(1.f, fov), 179.f);
float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera");
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
mStateUpdater->setFogEnd(mViewDistance);
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("simpleWater", false));
// Hopefully, anything genuinely requiring the default alpha func of GL_ALWAYS explicitly sets it
mRootNode->getOrCreateStateSet()->setAttribute(Shader::RemovedAlphaFunc::getInstance(GL_ALWAYS));
// The transparent renderbin sets alpha testing on because that was faster on old GPUs. It's now slower and breaks things.
mRootNode->getOrCreateStateSet()->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near");
mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far");
updateProjectionMatrix();
}
RenderingManager::~RenderingManager()
{
// let background loading thread finish before we delete anything else
mWorkQueue = nullptr;
}
osgUtil::IncrementalCompileOperation* RenderingManager::getIncrementalCompileOperation()
{
return mViewer->getIncrementalCompileOperation();
}
MWRender::Objects& RenderingManager::getObjects()
{
return *mObjects.get();
}
Resource::ResourceSystem* RenderingManager::getResourceSystem()
{
return mResourceSystem;
}
SceneUtil::WorkQueue* RenderingManager::getWorkQueue()
{
return mWorkQueue.get();
}
SceneUtil::UnrefQueue* RenderingManager::getUnrefQueue()
{
return mUnrefQueue.get();
}
Terrain::World* RenderingManager::getTerrain()
{
return mTerrain.get();
}
void RenderingManager::preloadCommonAssets()
{
osg::ref_ptr<PreloadCommonAssetsWorkItem> workItem (new PreloadCommonAssetsWorkItem(mResourceSystem));
mSky->listAssetsToPreload(workItem->mModels, workItem->mTextures);
mWater->listAssetsToPreload(workItem->mTextures);
workItem->mModels.push_back(Settings::Manager::getString("xbaseanim", "Models"));
workItem->mModels.push_back(Settings::Manager::getString("xbaseanim1st", "Models"));
workItem->mModels.push_back(Settings::Manager::getString("xbaseanimfemale", "Models"));
workItem->mModels.push_back(Settings::Manager::getString("xargonianswimkna", "Models"));
workItem->mKeyframes.push_back(Settings::Manager::getString("xbaseanimkf", "Models"));
workItem->mKeyframes.push_back(Settings::Manager::getString("xbaseanim1stkf", "Models"));
workItem->mKeyframes.push_back(Settings::Manager::getString("xbaseanimfemalekf", "Models"));
workItem->mKeyframes.push_back(Settings::Manager::getString("xargonianswimknakf", "Models"));
workItem->mTextures.emplace_back("textures/_land_default.dds");
mWorkQueue->addWorkItem(workItem);
}
double RenderingManager::getReferenceTime() const
{
return mViewer->getFrameStamp()->getReferenceTime();
}
osg::Group* RenderingManager::getLightRoot()
{
return mSceneRoot.get();
}
void RenderingManager::setNightEyeFactor(float factor)
{
if (factor != mNightEyeFactor)
{
mNightEyeFactor = factor;
updateAmbient();
}
}
void RenderingManager::setAmbientColour(const osg::Vec4f &colour)
{
mAmbientColor = colour;
updateAmbient();
}
void RenderingManager::skySetDate(int day, int month)
{
mSky->setDate(day, month);
}
int RenderingManager::skyGetMasserPhase() const
{
return mSky->getMasserPhase();
}
int RenderingManager::skyGetSecundaPhase() const
{
return mSky->getSecundaPhase();
}
void RenderingManager::skySetMoonColour(bool red)
{
mSky->setMoonColour(red);
}
void RenderingManager::configureAmbient(const ESM::Cell *cell)
{
bool needsAdjusting = false;
if (mResourceSystem->getSceneManager()->getLightingMethod() != SceneUtil::LightingMethod::FFP)
needsAdjusting = !cell->isExterior() && !(cell->mData.mFlags & ESM::Cell::QuasiEx);
auto ambient = SceneUtil::colourFromRGB(cell->mAmbi.mAmbient);
if (needsAdjusting)
{
constexpr float pR = 0.2126;
constexpr float pG = 0.7152;
constexpr float pB = 0.0722;
// we already work in linear RGB so no conversions are needed for the luminosity function
float relativeLuminance = pR*ambient.r() + pG*ambient.g() + pB*ambient.b();
if (relativeLuminance < mMinimumAmbientLuminance)
{
// brighten ambient so it reaches the minimum threshold but no more, we want to mess with content data as least we can
float targetBrightnessIncreaseFactor = mMinimumAmbientLuminance / relativeLuminance;
if (ambient.r() == 0.f && ambient.g() == 0.f && ambient.b() == 0.f)
ambient = osg::Vec4(mMinimumAmbientLuminance, mMinimumAmbientLuminance, mMinimumAmbientLuminance, ambient.a());
else
ambient *= targetBrightnessIncreaseFactor;
}
}
setAmbientColour(ambient);
osg::Vec4f diffuse = SceneUtil::colourFromRGB(cell->mAmbi.mSunlight);
mSunLight->setDiffuse(diffuse);
mSunLight->setSpecular(diffuse);
mSunLight->setPosition(osg::Vec4f(-0.15f, 0.15f, 1.f, 0.f));
}
void RenderingManager::setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular)
{
// need to wrap this in a StateUpdater?
mSunLight->setDiffuse(diffuse);
mSunLight->setSpecular(specular);
}
void RenderingManager::setSunDirection(const osg::Vec3f &direction)
{
osg::Vec3 position = direction * -1;
// need to wrap this in a StateUpdater?
mSunLight->setPosition(osg::Vec4(position.x(), position.y(), position.z(), 0));
mSky->setSunDirection(position);
}
void RenderingManager::addCell(const MWWorld::CellStore *store)
{
mPathgrid->addCell(store);
mWater->changeCell(store);
if (store->getCell()->isExterior())
{
mTerrain->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
if (mGroundcoverWorld)
mGroundcoverWorld->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
}
}
void RenderingManager::removeCell(const MWWorld::CellStore *store)
{
mPathgrid->removeCell(store);
mActorsPaths->removeCell(store);
mObjects->removeCell(store);
if (store->getCell()->isExterior())
{
mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
if (mGroundcoverWorld)
mGroundcoverWorld->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
}
mWater->removeCell(store);
}
void RenderingManager::enableTerrain(bool enable)
{
if (!enable)
mWater->setCullCallback(nullptr);
mTerrain->enable(enable);
if (mGroundcoverWorld)
mGroundcoverWorld->enable(enable);
}
void RenderingManager::setSkyEnabled(bool enabled)
{
mSky->setEnabled(enabled);
if (enabled)
mShadowManager->enableOutdoorMode();
else
mShadowManager->enableIndoorMode();
}
bool RenderingManager::toggleBorders()
{
bool borders = !mTerrain->getBordersVisible();
mTerrain->setBordersVisible(borders);
return borders;
}
bool RenderingManager::toggleRenderMode(RenderMode mode)
{
if (mode == Render_CollisionDebug || mode == Render_Pathgrid)
return mPathgrid->toggleRenderMode(mode);
else if (mode == Render_Wireframe)
{
bool wireframe = !mStateUpdater->getWireframe();
mStateUpdater->setWireframe(wireframe);
return wireframe;
}
else if (mode == Render_Water)
{
return mWater->toggle();
}
else if (mode == Render_Scene)
{
int mask = mViewer->getCamera()->getCullMask();
bool enabled = mask&Mask_Scene;
enabled = !enabled;
if (enabled)
mask |= Mask_Scene;
else
mask &= ~Mask_Scene;
mViewer->getCamera()->setCullMask(mask);
return enabled;
}
else if (mode == Render_NavMesh)
{
return mNavMesh->toggle();
}
else if (mode == Render_ActorsPaths)
{
return mActorsPaths->toggle();
}
else if (mode == Render_RecastMesh)
{
return mRecastMesh->toggle();
}
return false;
}
void RenderingManager::configureFog(const ESM::Cell *cell)
{
mFog->configure(mViewDistance, cell);
}
void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color)
{
mFog->configure(mViewDistance, fogDepth, underwaterFog, dlFactor, dlOffset, color);
}
SkyManager* RenderingManager::getSkyManager()
{
return mSky.get();
}
void RenderingManager::update(float dt, bool paused)
{
reportStats();
mUnrefQueue->flush(mWorkQueue.get());
float rainIntensity = mSky->getPrecipitationAlpha();
mWater->setRainIntensity(rainIntensity);
if (!paused)
{
mEffectManager->update(dt);
mSky->update(dt);
mWater->update(dt);
if (mGroundcoverUpdater)
{
const MWWorld::Ptr& player = mPlayerAnimation->getPtr();
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
float windSpeed = mSky->getBaseWindSpeed();
mGroundcoverUpdater->setWindSpeed(windSpeed);
mGroundcoverUpdater->setPlayerPos(playerPos);
}
}
updateNavMesh();
updateRecastMesh();
if (mViewOverShoulderController)
mViewOverShoulderController->update();
mCamera->update(dt, paused);
osg::Vec3d focal, cameraPos;
mCamera->getPosition(focal, cameraPos);
mCurrentCameraPos = cameraPos;
bool isUnderwater = mWater->isUnderwater(cameraPos);
mStateUpdater->setFogStart(mFog->getFogStart(isUnderwater));
mStateUpdater->setFogEnd(mFog->getFogEnd(isUnderwater));
setFogColor(mFog->getFogColor(isUnderwater));
}
void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
{
if(mPlayerAnimation.get())
{
setupPlayer(ptr);
mPlayerAnimation->updatePtr(ptr);
}
mCamera->attachTo(ptr);
}
void RenderingManager::removePlayer(const MWWorld::Ptr &player)
{
mWater->removeEmitter(player);
}
void RenderingManager::rotateObject(const MWWorld::Ptr &ptr, const osg::Quat& rot)
{
if(ptr == mCamera->getTrackingPtr() &&
!mCamera->isVanityOrPreviewModeEnabled())
{
mCamera->rotateCameraToTrackingPtr();
}
ptr.getRefData().getBaseNode()->setAttitude(rot);
}
void RenderingManager::moveObject(const MWWorld::Ptr &ptr, const osg::Vec3f &pos)
{
ptr.getRefData().getBaseNode()->setPosition(pos);
}
void RenderingManager::scaleObject(const MWWorld::Ptr &ptr, const osg::Vec3f &scale)
{
ptr.getRefData().getBaseNode()->setScale(scale);
if (ptr == mCamera->getTrackingPtr()) // update height of camera
mCamera->processViewChange();
}
void RenderingManager::removeObject(const MWWorld::Ptr &ptr)
{
mActorsPaths->remove(ptr);
mObjects->removeObject(ptr);
mWater->removeEmitter(ptr);
}
void RenderingManager::setWaterEnabled(bool enabled)
{
mWater->setEnabled(enabled);
mSky->setWaterEnabled(enabled);
}
void RenderingManager::setWaterHeight(float height)
{
mWater->setCullCallback(mTerrain->getHeightCullCallback(height, Mask_Water));
mWater->setHeight(height);
mSky->setWaterHeight(height);
}
void RenderingManager::screenshot(osg::Image* image, int w, int h)
{
mScreenshotManager->screenshot(image, w, h);
}
bool RenderingManager::screenshot360(osg::Image* image)
{
if (mCamera->isVanityOrPreviewModeEnabled())
{
Log(Debug::Warning) << "Spherical screenshots are not allowed in preview mode.";
return false;
}
int maskBackup = mPlayerAnimation->getObjectRoot()->getNodeMask();
if (mCamera->isFirstPerson())
mPlayerAnimation->getObjectRoot()->setNodeMask(0);
mScreenshotManager->screenshot360(image);
mPlayerAnimation->getObjectRoot()->setNodeMask(maskBackup);
return true;
}
osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox &worldbb)
{
if (!worldbb.valid()) return osg::Vec4f();
osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix();
float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f;
for (int i=0; i<8; ++i)
{
osg::Vec3f corner = worldbb.corner(i);
corner = corner * viewProj;
float x = (corner.x() + 1.f) * 0.5f;
float y = (corner.y() - 1.f) * (-0.5f);
if (x < min_x)
min_x = x;
if (x > max_x)
max_x = x;
if (y < min_y)
min_y = y;
if (y > max_y)
max_y = y;
}
return osg::Vec4f(min_x, min_y, max_x, max_y);
}
RenderingManager::RayResult getIntersectionResult (osgUtil::LineSegmentIntersector* intersector)
{
RenderingManager::RayResult result;
result.mHit = false;
result.mHitRefnum.mContentFile = -1;
result.mHitRefnum.mIndex = -1;
result.mRatio = 0;
if (intersector->containsIntersections())
{
result.mHit = true;
osgUtil::LineSegmentIntersector::Intersection intersection = intersector->getFirstIntersection();
result.mHitPointWorld = intersection.getWorldIntersectPoint();
result.mHitNormalWorld = intersection.getWorldIntersectNormal();
result.mRatio = intersection.ratio;
PtrHolder* ptrHolder = nullptr;
std::vector<RefnumMarker*> refnumMarkers;
for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it)
{
osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer();
if (!userDataContainer)
continue;
for (unsigned int i=0; i<userDataContainer->getNumUserObjects(); ++i)
{
if (PtrHolder* p = dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i)))
ptrHolder = p;
if (RefnumMarker* r = dynamic_cast<RefnumMarker*>(userDataContainer->getUserObject(i)))
refnumMarkers.push_back(r);
}
}
if (ptrHolder)
result.mHitObject = ptrHolder->mPtr;
unsigned int vertexCounter = 0;
for (unsigned int i=0; i<refnumMarkers.size(); ++i)
{
unsigned int intersectionIndex = intersection.indexList.empty() ? 0 : intersection.indexList[0];
if (!refnumMarkers[i]->mNumVertices || (intersectionIndex >= vertexCounter && intersectionIndex < vertexCounter + refnumMarkers[i]->mNumVertices))
{
result.mHitRefnum = refnumMarkers[i]->mRefnum;
break;
}
vertexCounter += refnumMarkers[i]->mNumVertices;
}
}
return result;
}
osg::ref_ptr<osgUtil::IntersectionVisitor> RenderingManager::getIntersectionVisitor(osgUtil::Intersector *intersector, bool ignorePlayer, bool ignoreActors)
{
if (!mIntersectionVisitor)
mIntersectionVisitor = new osgUtil::IntersectionVisitor;
mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber());
mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp());
mIntersectionVisitor->setIntersector(intersector);
int mask = ~0;
mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water|Mask_SimpleWater|Mask_Groundcover);
if (ignorePlayer)
mask &= ~(Mask_Player);
if (ignoreActors)
mask &= ~(Mask_Actor|Mask_Player);
mIntersectionVisitor->setTraversalMask(mask);
return mIntersectionVisitor;
}
RenderingManager::RayResult RenderingManager::castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors)
{
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::MODEL,
origin, dest));
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST);
mRootNode->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors));
return getIntersectionResult(intersector);
}
RenderingManager::RayResult RenderingManager::castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors)
{
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::PROJECTION,
nX * 2.f - 1.f, nY * (-2.f) + 1.f));
osg::Vec3d dist (0.f, 0.f, -maxDistance);
dist = dist * mViewer->getCamera()->getProjectionMatrix();
osg::Vec3d end = intersector->getEnd();
end.z() = dist.z();
intersector->setEnd(end);
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST);
mViewer->getCamera()->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors));
return getIntersectionResult(intersector);
}
void RenderingManager::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated)
{
mObjects->updatePtr(old, updated);
mActorsPaths->updatePtr(old, updated);
}
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
{
mEffectManager->addEffect(model, texture, worldPosition, scale, isMagicVFX);
}
void RenderingManager::notifyWorldSpaceChanged()
{
mEffectManager->clear();
mWater->clearRipples();
}
void RenderingManager::clear()
{
mSky->setMoonColour(false);
notifyWorldSpaceChanged();
if (mObjectPaging)
mObjectPaging->clear();
}
MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
{
if (mPlayerAnimation.get() && ptr == mPlayerAnimation->getPtr())
return mPlayerAnimation.get();
return mObjects->getAnimation(ptr);
}
const MWRender::Animation* RenderingManager::getAnimation(const MWWorld::ConstPtr &ptr) const
{
if (mPlayerAnimation.get() && ptr == mPlayerAnimation->getPtr())
return mPlayerAnimation.get();
return mObjects->getAnimation(ptr);
}
void RenderingManager::setupPlayer(const MWWorld::Ptr &player)
{
if (!mPlayerNode)
{
mPlayerNode = new SceneUtil::PositionAttitudeTransform;
mPlayerNode->setNodeMask(Mask_Player);
mPlayerNode->setName("Player Root");
mSceneRoot->addChild(mPlayerNode);
}
mPlayerNode->setUserDataContainer(new osg::DefaultUserDataContainer);
mPlayerNode->getUserDataContainer()->addUserObject(new PtrHolder(player));
player.getRefData().setBaseNode(mPlayerNode);
mWater->removeEmitter(player);
mWater->addEmitter(player);
}
void RenderingManager::renderPlayer(const MWWorld::Ptr &player)
{
mPlayerAnimation = new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0, NpcAnimation::VM_Normal,
mFirstPersonFieldOfView);
mCamera->setAnimation(mPlayerAnimation.get());
mCamera->attachTo(player);
}
void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr)
{
NpcAnimation *anim = nullptr;
if(ptr == mPlayerAnimation->getPtr())
anim = mPlayerAnimation.get();
else
anim = dynamic_cast<NpcAnimation*>(mObjects->getAnimation(ptr));
if(anim)
{
anim->rebuild();
if(mCamera->getTrackingPtr() == ptr)
{
mCamera->attachTo(ptr);
mCamera->setAnimation(anim);
}
}
}
void RenderingManager::addWaterRippleEmitter(const MWWorld::Ptr &ptr)
{
mWater->addEmitter(ptr);
}
void RenderingManager::removeWaterRippleEmitter(const MWWorld::Ptr &ptr)
{
mWater->removeEmitter(ptr);
}
void RenderingManager::emitWaterRipple(const osg::Vec3f &pos)
{
mWater->emitRipple(pos);
}
void RenderingManager::updateProjectionMatrix()
{
double aspect = mViewer->getCamera()->getViewport()->aspectRatio();
float fov = mFieldOfView;
if (mFieldOfViewOverridden)
fov = mFieldOfViewOverride;
mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance);
mUniformNear->set(mNearClip);
mUniformFar->set(mViewDistance);
// Since our fog is not radial yet, we should take FOV in account, otherwise terrain near viewing distance may disappear.
// Limit FOV here just for sure, otherwise viewing distance can be too high.
fov = std::min(mFieldOfView, 140.f);
float distanceMult = std::cos(osg::DegreesToRadians(fov)/2.f);
mTerrain->setViewDistance(mViewDistance * (distanceMult ? 1.f/distanceMult : 1.f));
if (mGroundcoverWorld)
{
int groundcoverDistance = Constants::CellSizeInUnits * std::max(1, Settings::Manager::getInt("distance", "Groundcover"));
mGroundcoverWorld->setViewDistance(groundcoverDistance * (distanceMult ? 1.f/distanceMult : 1.f));
}
}
void RenderingManager::updateTextureFiltering()
{
mViewer->stopThreading();
mResourceSystem->getSceneManager()->setFilterSettings(
Settings::Manager::getString("texture mag filter", "General"),
Settings::Manager::getString("texture min filter", "General"),
Settings::Manager::getString("texture mipmap", "General"),
Settings::Manager::getInt("anisotropy", "General")
);
mTerrain->updateTextureFiltering();
mViewer->startThreading();
}
void RenderingManager::updateAmbient()
{
osg::Vec4f color = mAmbientColor;
if (mNightEyeFactor > 0.f)
color += osg::Vec4f(0.7, 0.7, 0.7, 0.0) * mNightEyeFactor;
mStateUpdater->setAmbientColor(color);
}
void RenderingManager::setFogColor(const osg::Vec4f &color)
{
mViewer->getCamera()->setClearColor(color);
mStateUpdater->setFogColor(color);
}
void RenderingManager::reportStats() const
{
osg::Stats* stats = mViewer->getViewerStats();
unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
if (stats->collectStats("resource"))
{
stats->setAttribute(frameNumber, "UnrefQueue", mUnrefQueue->getNumItems());
mTerrain->reportStats(frameNumber, stats);
}
}
void RenderingManager::processChangedSettings(const Settings::CategorySettingVector &changed)
{
for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it)
{
if (it->first == "Camera" && it->second == "field of view")
{
mFieldOfView = Settings::Manager::getFloat("field of view", "Camera");
updateProjectionMatrix();
}
else if (it->first == "Camera" && it->second == "viewing distance")
{
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
if(!Settings::Manager::getBool("use distant fog", "Fog"))
mStateUpdater->setFogEnd(mViewDistance);
updateProjectionMatrix();
}
else if (it->first == "General" && (it->second == "texture filter" ||
it->second == "texture mipmap" ||
it->second == "anisotropy"))
{
updateTextureFiltering();
}
else if (it->first == "Water")
{
mWater->processChangedSettings(changed);
}
else if (it->first == "Shaders" && it->second == "minimum interior brightness")
{
mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f);
if (MWMechanics::getPlayer().isInCell())
configureAmbient(MWMechanics::getPlayer().getCell()->getCell());
}
else if (it->first == "Shaders" && (it->second == "light bounds multiplier" ||
it->second == "maximum light distance" ||
it->second == "light fade start" ||
it->second == "max lights"))
{
auto* lightManager = static_cast<SceneUtil::LightManager*>(getLightRoot());
lightManager->processChangedSettings(changed);
if (it->second == "max lights" && !lightManager->usingFFP())
{
mViewer->stopThreading();
lightManager->updateMaxLights();
auto defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
for (const auto& [name, key] : lightManager->getLightDefines())
defines[name] = key;
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
mSceneRoot->removeUpdateCallback(mStateUpdater);
mStateUpdater = new StateUpdater;
mSceneRoot->addUpdateCallback(mStateUpdater);
mStateUpdater->setFogEnd(mViewDistance);
updateAmbient();
mViewer->startThreading();
}
}
}
}
float RenderingManager::getNearClipDistance() const
{
return mNearClip;
}
float RenderingManager::getTerrainHeightAt(const osg::Vec3f &pos)
{
return mTerrain->getHeightAt(pos);
}
void RenderingManager::overrideFieldOfView(float val)
{
if (mFieldOfViewOverridden != true || mFieldOfViewOverride != val)
{
mFieldOfViewOverridden = true;
mFieldOfViewOverride = val;
updateProjectionMatrix();
}
}
osg::Vec3f RenderingManager::getHalfExtents(const MWWorld::ConstPtr& object) const
{
osg::Vec3f halfExtents(0, 0, 0);
std::string modelName = object.getClass().getModel(object);
if (modelName.empty())
return halfExtents;
osg::ref_ptr<const osg::Node> node = mResourceSystem->getSceneManager()->getTemplate(modelName);
osg::ComputeBoundsVisitor computeBoundsVisitor;
computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect));
const_cast<osg::Node*>(node.get())->accept(computeBoundsVisitor);
osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox();
if (bounds.valid())
{
halfExtents[0] = std::abs(bounds.xMax() - bounds.xMin()) / 2.f;
halfExtents[1] = std::abs(bounds.yMax() - bounds.yMin()) / 2.f;
halfExtents[2] = std::abs(bounds.zMax() - bounds.zMin()) / 2.f;
}
return halfExtents;
}
void RenderingManager::resetFieldOfView()
{
if (mFieldOfViewOverridden == true)
{
mFieldOfViewOverridden = false;
updateProjectionMatrix();
}
}
void RenderingManager::exportSceneGraph(const MWWorld::Ptr &ptr, const std::string &filename, const std::string &format)
{
osg::Node* node = mViewer->getSceneData();
if (!ptr.isEmpty())
node = ptr.getRefData().getBaseNode();
SceneUtil::writeScene(node, filename, format);
}
LandManager *RenderingManager::getLandManager() const
{
return mTerrainStorage->getLandManager();
}
void RenderingManager::updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,
const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const
{
mActorsPaths->update(actor, path, halfExtents, start, end, mNavigator.getSettings());
}
void RenderingManager::removeActorPath(const MWWorld::ConstPtr& actor) const
{
mActorsPaths->remove(actor);
}
void RenderingManager::setNavMeshNumber(const std::size_t value)
{
mNavMeshNumber = value;
}
void RenderingManager::updateNavMesh()
{
if (!mNavMesh->isEnabled())
return;
const auto navMeshes = mNavigator.getNavMeshes();
auto it = navMeshes.begin();
for (std::size_t i = 0; it != navMeshes.end() && i < mNavMeshNumber; ++i)
++it;
if (it == navMeshes.end())
{
mNavMesh->reset();
}
else
{
try
{
const auto locked = it->second->lockConst();
mNavMesh->update(locked->getImpl(), mNavMeshNumber, locked->getGeneration(),
locked->getNavMeshRevision(), mNavigator.getSettings());
}
catch (const std::exception& e)
{
Log(Debug::Error) << "NavMesh render update exception: " << e.what();
}
}
}
void RenderingManager::updateRecastMesh()
{
if (!mRecastMesh->isEnabled())
return;
mRecastMesh->update(mNavigator.getRecastMeshTiles(), mNavigator.getSettings());
}
void RenderingManager::setActiveGrid(const osg::Vec4i &grid)
{
mTerrain->setActiveGrid(grid);
if (mGroundcoverWorld)
mGroundcoverWorld->setActiveGrid(grid);
}
bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled)
{
if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging)
return false;
if (mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getCellRef().getPosition().asVec3(), osg::Vec2i(ptr.getCell()->getCell()->getGridX(), ptr.getCell()->getCell()->getGridY()), enabled))
{
mTerrain->rebuildViews();
return true;
}
return false;
}
void RenderingManager::pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr)
{
if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging)
return;
const ESM::RefNum & refnum = ptr.getCellRef().getRefNum();
if (!refnum.hasContentFile()) return;
if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3(), osg::Vec2i(ptr.getCell()->getCell()->getGridX(), ptr.getCell()->getCell()->getGridY())))
mTerrain->rebuildViews();
}
bool RenderingManager::pagingUnlockCache()
{
if (mObjectPaging && mObjectPaging->unlockCache())
{
mTerrain->rebuildViews();
return true;
}
return false;
}
void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out)
{
if (mObjectPaging)
mObjectPaging->getPagedRefnums(activeGrid, out);
}
}