1 #include "renderingmanager.hpp"
2 
3 #include <limits>
4 #include <cstdlib>
5 
6 #include <osg/Light>
7 #include <osg/LightModel>
8 #include <osg/Fog>
9 #include <osg/Material>
10 #include <osg/PolygonMode>
11 #include <osg/Group>
12 #include <osg/UserDataContainer>
13 #include <osg/ComputeBoundsVisitor>
14 
15 #include <osgUtil/LineSegmentIntersector>
16 
17 #include <osgViewer/Viewer>
18 
19 #include <components/nifosg/nifloader.hpp>
20 
21 #include <components/debug/debuglog.hpp>
22 
23 #include <components/resource/resourcesystem.hpp>
24 #include <components/resource/imagemanager.hpp>
25 #include <components/resource/scenemanager.hpp>
26 #include <components/resource/keyframemanager.hpp>
27 
28 #include <components/shader/removedalphafunc.hpp>
29 #include <components/shader/shadermanager.hpp>
30 
31 #include <components/settings/settings.hpp>
32 
33 #include <components/sceneutil/util.hpp>
34 #include <components/sceneutil/lightmanager.hpp>
35 #include <components/sceneutil/statesetupdater.hpp>
36 #include <components/sceneutil/positionattitudetransform.hpp>
37 #include <components/sceneutil/workqueue.hpp>
38 #include <components/sceneutil/unrefqueue.hpp>
39 #include <components/sceneutil/writescene.hpp>
40 #include <components/sceneutil/shadow.hpp>
41 
42 #include <components/terrain/terraingrid.hpp>
43 #include <components/terrain/quadtreeworld.hpp>
44 
45 #include <components/esm/loadcell.hpp>
46 
47 #include <components/detournavigator/navigator.hpp>
48 
49 #include "../mwworld/cellstore.hpp"
50 #include "../mwworld/class.hpp"
51 #include "../mwgui/loadingscreen.hpp"
52 #include "../mwbase/windowmanager.hpp"
53 #include "../mwmechanics/actorutil.hpp"
54 
55 #include "sky.hpp"
56 #include "effectmanager.hpp"
57 #include "npcanimation.hpp"
58 #include "vismask.hpp"
59 #include "pathgrid.hpp"
60 #include "camera.hpp"
61 #include "viewovershoulder.hpp"
62 #include "water.hpp"
63 #include "terrainstorage.hpp"
64 #include "navmesh.hpp"
65 #include "actorspaths.hpp"
66 #include "recastmesh.hpp"
67 #include "fogmanager.hpp"
68 #include "objectpaging.hpp"
69 #include "screenshotmanager.hpp"
70 #include "groundcover.hpp"
71 
72 namespace MWRender
73 {
74 
75     class StateUpdater : public SceneUtil::StateSetUpdater
76     {
77     public:
StateUpdater()78         StateUpdater()
79             : mFogStart(0.f)
80             , mFogEnd(0.f)
81             , mWireframe(false)
82         {
83         }
84 
setDefaults(osg::StateSet * stateset)85         void setDefaults(osg::StateSet *stateset) override
86         {
87             osg::LightModel* lightModel = new osg::LightModel;
88             stateset->setAttribute(lightModel, osg::StateAttribute::ON);
89             osg::Fog* fog = new osg::Fog;
90             fog->setMode(osg::Fog::LINEAR);
91             stateset->setAttributeAndModes(fog, osg::StateAttribute::ON);
92             if (mWireframe)
93             {
94                 osg::PolygonMode* polygonmode = new osg::PolygonMode;
95                 polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
96                 stateset->setAttributeAndModes(polygonmode, osg::StateAttribute::ON);
97             }
98             else
99                 stateset->removeAttribute(osg::StateAttribute::POLYGONMODE);
100         }
101 
apply(osg::StateSet * stateset,osg::NodeVisitor *)102         void apply(osg::StateSet* stateset, osg::NodeVisitor*) override
103         {
104             osg::LightModel* lightModel = static_cast<osg::LightModel*>(stateset->getAttribute(osg::StateAttribute::LIGHTMODEL));
105             lightModel->setAmbientIntensity(mAmbientColor);
106             osg::Fog* fog = static_cast<osg::Fog*>(stateset->getAttribute(osg::StateAttribute::FOG));
107             fog->setColor(mFogColor);
108             fog->setStart(mFogStart);
109             fog->setEnd(mFogEnd);
110         }
111 
setAmbientColor(const osg::Vec4f & col)112         void setAmbientColor(const osg::Vec4f& col)
113         {
114             mAmbientColor = col;
115         }
116 
setFogColor(const osg::Vec4f & col)117         void setFogColor(const osg::Vec4f& col)
118         {
119             mFogColor = col;
120         }
121 
setFogStart(float start)122         void setFogStart(float start)
123         {
124             mFogStart = start;
125         }
126 
setFogEnd(float end)127         void setFogEnd(float end)
128         {
129             mFogEnd = end;
130         }
131 
setWireframe(bool wireframe)132         void setWireframe(bool wireframe)
133         {
134             if (mWireframe != wireframe)
135             {
136                 mWireframe = wireframe;
137                 reset();
138             }
139         }
140 
getWireframe() const141         bool getWireframe() const
142         {
143             return mWireframe;
144         }
145 
146     private:
147         osg::Vec4f mAmbientColor;
148         osg::Vec4f mFogColor;
149         float mFogStart;
150         float mFogEnd;
151         bool mWireframe;
152     };
153 
154     class PreloadCommonAssetsWorkItem : public SceneUtil::WorkItem
155     {
156     public:
PreloadCommonAssetsWorkItem(Resource::ResourceSystem * resourceSystem)157         PreloadCommonAssetsWorkItem(Resource::ResourceSystem* resourceSystem)
158             : mResourceSystem(resourceSystem)
159         {
160         }
161 
doWork()162         void doWork() override
163         {
164             try
165             {
166                 for (std::vector<std::string>::const_iterator it = mModels.begin(); it != mModels.end(); ++it)
167                     mResourceSystem->getSceneManager()->cacheInstance(*it);
168                 for (std::vector<std::string>::const_iterator it = mTextures.begin(); it != mTextures.end(); ++it)
169                     mResourceSystem->getImageManager()->getImage(*it);
170                 for (std::vector<std::string>::const_iterator it = mKeyframes.begin(); it != mKeyframes.end(); ++it)
171                     mResourceSystem->getKeyframeManager()->get(*it);
172             }
173             catch (std::exception&)
174             {
175                 // ignore error (will be shown when these are needed proper)
176             }
177         }
178 
179         std::vector<std::string> mModels;
180         std::vector<std::string> mTextures;
181         std::vector<std::string> mKeyframes;
182 
183     private:
184         Resource::ResourceSystem* mResourceSystem;
185     };
186 
RenderingManager(osgViewer::Viewer * viewer,osg::ref_ptr<osg::Group> rootNode,Resource::ResourceSystem * resourceSystem,SceneUtil::WorkQueue * workQueue,const std::string & resourcePath,DetourNavigator::Navigator & navigator)187     RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
188                                        Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
189                                        const std::string& resourcePath, DetourNavigator::Navigator& navigator)
190         : mViewer(viewer)
191         , mRootNode(rootNode)
192         , mResourceSystem(resourceSystem)
193         , mWorkQueue(workQueue)
194         , mUnrefQueue(new SceneUtil::UnrefQueue)
195         , mNavigator(navigator)
196         , mMinimumAmbientLuminance(0.f)
197         , mNightEyeFactor(0.f)
198         , mFieldOfViewOverridden(false)
199         , mFieldOfViewOverride(0.f)
200     {
201         auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
202 
203         resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
204         resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
205         // Shadows and radial fog have problems with fixed-function mode
206         bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders")
207                             || Settings::Manager::getBool("force shaders", "Shaders")
208                             || Settings::Manager::getBool("enable shadows", "Shadows")
209                             || lightingMethod != SceneUtil::LightingMethod::FFP;
210         resourceSystem->getSceneManager()->setForceShaders(forceShaders);
211         // FIXME: calling dummy method because terrain needs to know whether lighting is clamped
212         resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders"));
213         resourceSystem->getSceneManager()->setAutoUseNormalMaps(Settings::Manager::getBool("auto use object normal maps", "Shaders"));
214         resourceSystem->getSceneManager()->setNormalMapPattern(Settings::Manager::getString("normal map pattern", "Shaders"));
215         resourceSystem->getSceneManager()->setNormalHeightMapPattern(Settings::Manager::getString("normal height map pattern", "Shaders"));
216         resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders"));
217         resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders"));
218         resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders"));
219         resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1);
220 
221         // Let LightManager choose which backend to use based on our hint. For methods besides legacy lighting, this depends on support for various OpenGL extensions.
222         osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(lightingMethod == SceneUtil::LightingMethod::FFP);
223         resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod());
224         resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod());
225         resourceSystem->getSceneManager()->setSupportedLightingMethods(sceneRoot->getSupportedLightingMethods());
226         mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f);
227 
228         sceneRoot->setLightingMask(Mask_Lighting);
229         mSceneRoot = sceneRoot;
230         sceneRoot->setStartLight(1);
231         sceneRoot->setNodeMask(Mask_Scene);
232         sceneRoot->setName("Scene Root");
233 
234         int shadowCastingTraversalMask = Mask_Scene;
235         if (Settings::Manager::getBool("actor shadows", "Shadows"))
236             shadowCastingTraversalMask |= Mask_Actor;
237         if (Settings::Manager::getBool("player shadows", "Shadows"))
238             shadowCastingTraversalMask |= Mask_Player;
239         if (Settings::Manager::getBool("terrain shadows", "Shadows"))
240             shadowCastingTraversalMask |= Mask_Terrain;
241 
242         int indoorShadowCastingTraversalMask = shadowCastingTraversalMask;
243         if (Settings::Manager::getBool("object shadows", "Shadows"))
244             shadowCastingTraversalMask |= (Mask_Object|Mask_Static);
245 
246         mShadowManager.reset(new SceneUtil::ShadowManager(sceneRoot, mRootNode, shadowCastingTraversalMask, indoorShadowCastingTraversalMask, mResourceSystem->getSceneManager()->getShaderManager()));
247 
248         Shader::ShaderManager::DefineMap shadowDefines = mShadowManager->getShadowDefines();
249         Shader::ShaderManager::DefineMap lightDefines = sceneRoot->getLightDefines();
250         Shader::ShaderManager::DefineMap globalDefines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
251 
252         for (auto itr = shadowDefines.begin(); itr != shadowDefines.end(); itr++)
253             globalDefines[itr->first] = itr->second;
254 
255         globalDefines["forcePPL"] = Settings::Manager::getBool("force per pixel lighting", "Shaders") ? "1" : "0";
256         globalDefines["clamp"] = Settings::Manager::getBool("clamp lighting", "Shaders") ? "1" : "0";
257         globalDefines["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0";
258         globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0";
259         globalDefines["useGPUShader4"] = "0";
260 
261         for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++)
262             globalDefines[itr->first] = itr->second;
263 
264         // Refactor this at some point - most shaders don't care about these defines
265         float groundcoverDistance = std::max(0.f, Settings::Manager::getFloat("rendering distance", "Groundcover"));
266         globalDefines["groundcoverFadeStart"] = std::to_string(groundcoverDistance * 0.9f);
267         globalDefines["groundcoverFadeEnd"] = std::to_string(groundcoverDistance);
268         globalDefines["groundcoverStompMode"] = std::to_string(std::clamp(Settings::Manager::getInt("stomp mode", "Groundcover"), 0, 2));
269         globalDefines["groundcoverStompIntensity"] = std::to_string(std::clamp(Settings::Manager::getInt("stomp intensity", "Groundcover"), 0, 2));
270 
271         // It is unnecessary to stop/start the viewer as no frames are being rendered yet.
272         mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines);
273 
274         mNavMesh.reset(new NavMesh(mRootNode, Settings::Manager::getBool("enable nav mesh render", "Navigator")));
275         mActorsPaths.reset(new ActorsPaths(mRootNode, Settings::Manager::getBool("enable agents paths render", "Navigator")));
276         mRecastMesh.reset(new RecastMesh(mRootNode, Settings::Manager::getBool("enable recast mesh render", "Navigator")));
277         mPathgrid.reset(new Pathgrid(mRootNode));
278 
279         mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get()));
280 
281         if (getenv("OPENMW_DONT_PRECOMPILE") == nullptr)
282         {
283             mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation);
284             mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells"));
285         }
286 
287         mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation());
288 
289         mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem));
290 
291         const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders");
292         const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders");
293         const std::string specularMapPattern = Settings::Manager::getString("terrain specular map pattern", "Shaders");
294         const bool useTerrainNormalMaps = Settings::Manager::getBool("auto use terrain normal maps", "Shaders");
295         const bool useTerrainSpecularMaps = Settings::Manager::getBool("auto use terrain specular maps", "Shaders");
296 
297         mTerrainStorage.reset(new TerrainStorage(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps));
298         const float lodFactor = Settings::Manager::getFloat("lod factor", "Terrain");
299 
300         if (Settings::Manager::getBool("distant terrain", "Terrain"))
301         {
302             const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain");
303             int compMapPower = Settings::Manager::getInt("composite map level", "Terrain");
304             compMapPower = std::max(-3, compMapPower);
305             float compMapLevel = pow(2, compMapPower);
306             const int vertexLodMod = Settings::Manager::getInt("vertex lod mod", "Terrain");
307             float maxCompGeometrySize = Settings::Manager::getFloat("max composite geometry size", "Terrain");
308             maxCompGeometrySize = std::max(maxCompGeometrySize, 1.f);
309             mTerrain.reset(new Terrain::QuadTreeWorld(
310                 sceneRoot, mRootNode, mResourceSystem, mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug,
311                 compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize));
312             if (Settings::Manager::getBool("object paging", "Terrain"))
313             {
314                 mObjectPaging.reset(new ObjectPaging(mResourceSystem->getSceneManager()));
315                 static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mObjectPaging.get());
316                 mResourceSystem->addResourceManager(mObjectPaging.get());
317             }
318         }
319         else
320             mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug));
321 
322         mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells"));
323         mTerrain->setWorkQueue(mWorkQueue.get());
324 
325         if (Settings::Manager::getBool("enabled", "Groundcover"))
326         {
327             osg::ref_ptr<osg::Group> groundcoverRoot = new osg::Group;
328             groundcoverRoot->setNodeMask(Mask_Groundcover);
329             groundcoverRoot->setName("Groundcover Root");
330             sceneRoot->addChild(groundcoverRoot);
331 
332             mGroundcoverUpdater = new GroundcoverUpdater;
333             groundcoverRoot->addUpdateCallback(mGroundcoverUpdater);
334 
335             float chunkSize = Settings::Manager::getFloat("min chunk size", "Groundcover");
336             if (chunkSize >= 1.0f)
337                 chunkSize = 1.0f;
338             else if (chunkSize >= 0.5f)
339                 chunkSize = 0.5f;
340             else if (chunkSize >= 0.25f)
341                 chunkSize = 0.25f;
342             else if (chunkSize != 0.125f)
343                 chunkSize = 0.125f;
344 
345             float density = Settings::Manager::getFloat("density", "Groundcover");
346             density = std::clamp(density, 0.f, 1.f);
347 
348             mGroundcoverWorld.reset(new Terrain::QuadTreeWorld(groundcoverRoot, mTerrainStorage.get(), Mask_Groundcover, lodFactor, chunkSize));
349             mGroundcover.reset(new Groundcover(mResourceSystem->getSceneManager(), density));
350             static_cast<Terrain::QuadTreeWorld*>(mGroundcoverWorld.get())->addChunkManager(mGroundcover.get());
351             mResourceSystem->addResourceManager(mGroundcover.get());
352 
353             // Groundcover it is handled in the same way indifferently from if it is from active grid or from distant cell.
354             // Use a stub grid to avoid splitting between chunks for active grid and chunks for distant cells.
355             mGroundcoverWorld->setActiveGrid(osg::Vec4i(0, 0, 0, 0));
356         }
357         // water goes after terrain for correct waterculling order
358         mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
359 
360         mCamera.reset(new Camera(mViewer->getCamera()));
361         if (Settings::Manager::getBool("view over shoulder", "Camera"))
362             mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get()));
363 
364         mScreenshotManager.reset(new ScreenshotManager(viewer, mRootNode, sceneRoot, mResourceSystem, mWater.get()));
365 
366         mViewer->setLightingMode(osgViewer::View::NO_LIGHT);
367 
368         osg::ref_ptr<osg::LightSource> source = new osg::LightSource;
369         source->setNodeMask(Mask_Lighting);
370         mSunLight = new osg::Light;
371         source->setLight(mSunLight);
372         mSunLight->setDiffuse(osg::Vec4f(0,0,0,1));
373         mSunLight->setAmbient(osg::Vec4f(0,0,0,1));
374         mSunLight->setSpecular(osg::Vec4f(0,0,0,0));
375         mSunLight->setConstantAttenuation(1.f);
376         sceneRoot->setSunlight(mSunLight);
377         sceneRoot->addChild(source);
378 
379         sceneRoot->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
380         sceneRoot->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON);
381         sceneRoot->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
382         osg::ref_ptr<osg::Material> defaultMat (new osg::Material);
383         defaultMat->setColorMode(osg::Material::OFF);
384         defaultMat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
385         defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
386         defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
387         sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat);
388 
389         mFog.reset(new FogManager());
390 
391         mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager()));
392         mSky->setCamera(mViewer->getCamera());
393 
394         source->setStateSetModes(*mRootNode->getOrCreateStateSet(), osg::StateAttribute::ON);
395 
396         mStateUpdater = new StateUpdater;
397         sceneRoot->addUpdateCallback(mStateUpdater);
398 
399         osg::Camera::CullingMode cullingMode = osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING;
400 
401         if (!Settings::Manager::getBool("small feature culling", "Camera"))
402             cullingMode &= ~(osg::CullStack::SMALL_FEATURE_CULLING);
403         else
404         {
405             mViewer->getCamera()->setSmallFeatureCullingPixelSize(Settings::Manager::getFloat("small feature culling pixel size", "Camera"));
406             cullingMode |= osg::CullStack::SMALL_FEATURE_CULLING;
407         }
408 
409         mViewer->getCamera()->setCullingMode( cullingMode );
410 
411         mViewer->getCamera()->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
412         mViewer->getCamera()->setCullingMode(cullingMode);
413 
414         mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater));
415         NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor);
416         NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect);
417         Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models"));
418 
419         mNearClip = Settings::Manager::getFloat("near clip", "Camera");
420         mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
421         float fov = Settings::Manager::getFloat("field of view", "Camera");
422         mFieldOfView = std::min(std::max(1.f, fov), 179.f);
423         float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera");
424         mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
425         mStateUpdater->setFogEnd(mViewDistance);
426 
427         mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
428         mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
429         mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("simpleWater", false));
430 
431         // Hopefully, anything genuinely requiring the default alpha func of GL_ALWAYS explicitly sets it
432         mRootNode->getOrCreateStateSet()->setAttribute(Shader::RemovedAlphaFunc::getInstance(GL_ALWAYS));
433         // The transparent renderbin sets alpha testing on because that was faster on old GPUs. It's now slower and breaks things.
434         mRootNode->getOrCreateStateSet()->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
435 
436         mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near");
437         mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far");
438         updateProjectionMatrix();
439     }
440 
~RenderingManager()441     RenderingManager::~RenderingManager()
442     {
443         // let background loading thread finish before we delete anything else
444         mWorkQueue = nullptr;
445     }
446 
getIncrementalCompileOperation()447     osgUtil::IncrementalCompileOperation* RenderingManager::getIncrementalCompileOperation()
448     {
449         return mViewer->getIncrementalCompileOperation();
450     }
451 
getObjects()452     MWRender::Objects& RenderingManager::getObjects()
453     {
454         return *mObjects.get();
455     }
456 
getResourceSystem()457     Resource::ResourceSystem* RenderingManager::getResourceSystem()
458     {
459         return mResourceSystem;
460     }
461 
getWorkQueue()462     SceneUtil::WorkQueue* RenderingManager::getWorkQueue()
463     {
464         return mWorkQueue.get();
465     }
466 
getUnrefQueue()467     SceneUtil::UnrefQueue* RenderingManager::getUnrefQueue()
468     {
469         return mUnrefQueue.get();
470     }
471 
getTerrain()472     Terrain::World* RenderingManager::getTerrain()
473     {
474         return mTerrain.get();
475     }
476 
preloadCommonAssets()477     void RenderingManager::preloadCommonAssets()
478     {
479         osg::ref_ptr<PreloadCommonAssetsWorkItem> workItem (new PreloadCommonAssetsWorkItem(mResourceSystem));
480         mSky->listAssetsToPreload(workItem->mModels, workItem->mTextures);
481         mWater->listAssetsToPreload(workItem->mTextures);
482 
483         workItem->mModels.push_back(Settings::Manager::getString("xbaseanim", "Models"));
484         workItem->mModels.push_back(Settings::Manager::getString("xbaseanim1st", "Models"));
485         workItem->mModels.push_back(Settings::Manager::getString("xbaseanimfemale", "Models"));
486         workItem->mModels.push_back(Settings::Manager::getString("xargonianswimkna", "Models"));
487 
488         workItem->mKeyframes.push_back(Settings::Manager::getString("xbaseanimkf", "Models"));
489         workItem->mKeyframes.push_back(Settings::Manager::getString("xbaseanim1stkf", "Models"));
490         workItem->mKeyframes.push_back(Settings::Manager::getString("xbaseanimfemalekf", "Models"));
491         workItem->mKeyframes.push_back(Settings::Manager::getString("xargonianswimknakf", "Models"));
492 
493         workItem->mTextures.emplace_back("textures/_land_default.dds");
494 
495         mWorkQueue->addWorkItem(workItem);
496     }
497 
getReferenceTime() const498     double RenderingManager::getReferenceTime() const
499     {
500         return mViewer->getFrameStamp()->getReferenceTime();
501     }
502 
getLightRoot()503     osg::Group* RenderingManager::getLightRoot()
504     {
505         return mSceneRoot.get();
506     }
507 
setNightEyeFactor(float factor)508     void RenderingManager::setNightEyeFactor(float factor)
509     {
510         if (factor != mNightEyeFactor)
511         {
512             mNightEyeFactor = factor;
513             updateAmbient();
514         }
515     }
516 
setAmbientColour(const osg::Vec4f & colour)517     void RenderingManager::setAmbientColour(const osg::Vec4f &colour)
518     {
519         mAmbientColor = colour;
520         updateAmbient();
521     }
522 
skySetDate(int day,int month)523     void RenderingManager::skySetDate(int day, int month)
524     {
525         mSky->setDate(day, month);
526     }
527 
skyGetMasserPhase() const528     int RenderingManager::skyGetMasserPhase() const
529     {
530         return mSky->getMasserPhase();
531     }
532 
skyGetSecundaPhase() const533     int RenderingManager::skyGetSecundaPhase() const
534     {
535         return mSky->getSecundaPhase();
536     }
537 
skySetMoonColour(bool red)538     void RenderingManager::skySetMoonColour(bool red)
539     {
540         mSky->setMoonColour(red);
541     }
542 
configureAmbient(const ESM::Cell * cell)543     void RenderingManager::configureAmbient(const ESM::Cell *cell)
544     {
545         bool needsAdjusting = false;
546         if (mResourceSystem->getSceneManager()->getLightingMethod() != SceneUtil::LightingMethod::FFP)
547             needsAdjusting = !cell->isExterior() && !(cell->mData.mFlags & ESM::Cell::QuasiEx);
548 
549         auto ambient = SceneUtil::colourFromRGB(cell->mAmbi.mAmbient);
550 
551         if (needsAdjusting)
552         {
553             constexpr float pR = 0.2126;
554             constexpr float pG = 0.7152;
555             constexpr float pB = 0.0722;
556 
557             // we already work in linear RGB so no conversions are needed for the luminosity function
558             float relativeLuminance = pR*ambient.r() + pG*ambient.g() + pB*ambient.b();
559             if (relativeLuminance < mMinimumAmbientLuminance)
560             {
561                 // brighten ambient so it reaches the minimum threshold but no more, we want to mess with content data as least we can
562                 float targetBrightnessIncreaseFactor = mMinimumAmbientLuminance / relativeLuminance;
563                 if (ambient.r() == 0.f && ambient.g() == 0.f && ambient.b() == 0.f)
564                     ambient = osg::Vec4(mMinimumAmbientLuminance, mMinimumAmbientLuminance, mMinimumAmbientLuminance, ambient.a());
565                 else
566                     ambient *= targetBrightnessIncreaseFactor;
567             }
568         }
569 
570         setAmbientColour(ambient);
571 
572         osg::Vec4f diffuse = SceneUtil::colourFromRGB(cell->mAmbi.mSunlight);
573         mSunLight->setDiffuse(diffuse);
574         mSunLight->setSpecular(diffuse);
575         mSunLight->setPosition(osg::Vec4f(-0.15f, 0.15f, 1.f, 0.f));
576     }
577 
setSunColour(const osg::Vec4f & diffuse,const osg::Vec4f & specular)578     void RenderingManager::setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular)
579     {
580         // need to wrap this in a StateUpdater?
581         mSunLight->setDiffuse(diffuse);
582         mSunLight->setSpecular(specular);
583     }
584 
setSunDirection(const osg::Vec3f & direction)585     void RenderingManager::setSunDirection(const osg::Vec3f &direction)
586     {
587         osg::Vec3 position = direction * -1;
588         // need to wrap this in a StateUpdater?
589         mSunLight->setPosition(osg::Vec4(position.x(), position.y(), position.z(), 0));
590 
591         mSky->setSunDirection(position);
592     }
593 
addCell(const MWWorld::CellStore * store)594     void RenderingManager::addCell(const MWWorld::CellStore *store)
595     {
596         mPathgrid->addCell(store);
597 
598         mWater->changeCell(store);
599 
600         if (store->getCell()->isExterior())
601         {
602             mTerrain->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
603             if (mGroundcoverWorld)
604                 mGroundcoverWorld->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
605         }
606     }
removeCell(const MWWorld::CellStore * store)607     void RenderingManager::removeCell(const MWWorld::CellStore *store)
608     {
609         mPathgrid->removeCell(store);
610         mActorsPaths->removeCell(store);
611         mObjects->removeCell(store);
612 
613         if (store->getCell()->isExterior())
614         {
615             mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
616             if (mGroundcoverWorld)
617                 mGroundcoverWorld->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
618         }
619 
620         mWater->removeCell(store);
621     }
622 
enableTerrain(bool enable)623     void RenderingManager::enableTerrain(bool enable)
624     {
625         if (!enable)
626             mWater->setCullCallback(nullptr);
627         mTerrain->enable(enable);
628         if (mGroundcoverWorld)
629             mGroundcoverWorld->enable(enable);
630     }
631 
setSkyEnabled(bool enabled)632     void RenderingManager::setSkyEnabled(bool enabled)
633     {
634         mSky->setEnabled(enabled);
635         if (enabled)
636             mShadowManager->enableOutdoorMode();
637         else
638             mShadowManager->enableIndoorMode();
639     }
640 
toggleBorders()641     bool RenderingManager::toggleBorders()
642     {
643         bool borders = !mTerrain->getBordersVisible();
644         mTerrain->setBordersVisible(borders);
645         return borders;
646     }
647 
toggleRenderMode(RenderMode mode)648     bool RenderingManager::toggleRenderMode(RenderMode mode)
649     {
650         if (mode == Render_CollisionDebug || mode == Render_Pathgrid)
651             return mPathgrid->toggleRenderMode(mode);
652         else if (mode == Render_Wireframe)
653         {
654             bool wireframe = !mStateUpdater->getWireframe();
655             mStateUpdater->setWireframe(wireframe);
656             return wireframe;
657         }
658         else if (mode == Render_Water)
659         {
660             return mWater->toggle();
661         }
662         else if (mode == Render_Scene)
663         {
664             unsigned int mask = mViewer->getCamera()->getCullMask();
665             bool enabled = mask&Mask_Scene;
666             enabled = !enabled;
667             if (enabled)
668                 mask |= Mask_Scene;
669             else
670                 mask &= ~Mask_Scene;
671             mViewer->getCamera()->setCullMask(mask);
672             return enabled;
673         }
674         else if (mode == Render_NavMesh)
675         {
676             return mNavMesh->toggle();
677         }
678         else if (mode == Render_ActorsPaths)
679         {
680             return mActorsPaths->toggle();
681         }
682         else if (mode == Render_RecastMesh)
683         {
684             return mRecastMesh->toggle();
685         }
686         return false;
687     }
688 
configureFog(const ESM::Cell * cell)689     void RenderingManager::configureFog(const ESM::Cell *cell)
690     {
691         mFog->configure(mViewDistance, cell);
692     }
693 
configureFog(float fogDepth,float underwaterFog,float dlFactor,float dlOffset,const osg::Vec4f & color)694     void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color)
695     {
696         mFog->configure(mViewDistance, fogDepth, underwaterFog, dlFactor, dlOffset, color);
697     }
698 
getSkyManager()699     SkyManager* RenderingManager::getSkyManager()
700     {
701         return mSky.get();
702     }
703 
update(float dt,bool paused)704     void RenderingManager::update(float dt, bool paused)
705     {
706         reportStats();
707 
708         mUnrefQueue->flush(mWorkQueue.get());
709 
710         float rainIntensity = mSky->getPrecipitationAlpha();
711         mWater->setRainIntensity(rainIntensity);
712 
713         if (!paused)
714         {
715             mEffectManager->update(dt);
716             mSky->update(dt);
717             mWater->update(dt);
718 
719             if (mGroundcoverUpdater)
720             {
721                 const MWWorld::Ptr& player = mPlayerAnimation->getPtr();
722                 osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
723 
724                 float windSpeed = mSky->getBaseWindSpeed();
725                 mGroundcoverUpdater->setWindSpeed(windSpeed);
726                 mGroundcoverUpdater->setPlayerPos(playerPos);
727             }
728         }
729 
730         updateNavMesh();
731         updateRecastMesh();
732 
733         if (mViewOverShoulderController)
734             mViewOverShoulderController->update();
735         mCamera->update(dt, paused);
736 
737         osg::Vec3d focal, cameraPos;
738         mCamera->getPosition(focal, cameraPos);
739         mCurrentCameraPos = cameraPos;
740 
741         bool isUnderwater = mWater->isUnderwater(cameraPos);
742         mStateUpdater->setFogStart(mFog->getFogStart(isUnderwater));
743         mStateUpdater->setFogEnd(mFog->getFogEnd(isUnderwater));
744         setFogColor(mFog->getFogColor(isUnderwater));
745     }
746 
updatePlayerPtr(const MWWorld::Ptr & ptr)747     void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
748     {
749         if(mPlayerAnimation.get())
750         {
751             setupPlayer(ptr);
752             mPlayerAnimation->updatePtr(ptr);
753         }
754         mCamera->attachTo(ptr);
755     }
756 
removePlayer(const MWWorld::Ptr & player)757     void RenderingManager::removePlayer(const MWWorld::Ptr &player)
758     {
759         mWater->removeEmitter(player);
760     }
761 
rotateObject(const MWWorld::Ptr & ptr,const osg::Quat & rot)762     void RenderingManager::rotateObject(const MWWorld::Ptr &ptr, const osg::Quat& rot)
763     {
764         if(ptr == mCamera->getTrackingPtr() &&
765            !mCamera->isVanityOrPreviewModeEnabled())
766         {
767             mCamera->rotateCameraToTrackingPtr();
768         }
769 
770         ptr.getRefData().getBaseNode()->setAttitude(rot);
771     }
772 
moveObject(const MWWorld::Ptr & ptr,const osg::Vec3f & pos)773     void RenderingManager::moveObject(const MWWorld::Ptr &ptr, const osg::Vec3f &pos)
774     {
775         ptr.getRefData().getBaseNode()->setPosition(pos);
776     }
777 
scaleObject(const MWWorld::Ptr & ptr,const osg::Vec3f & scale)778     void RenderingManager::scaleObject(const MWWorld::Ptr &ptr, const osg::Vec3f &scale)
779     {
780         ptr.getRefData().getBaseNode()->setScale(scale);
781 
782         if (ptr == mCamera->getTrackingPtr()) // update height of camera
783             mCamera->processViewChange();
784     }
785 
removeObject(const MWWorld::Ptr & ptr)786     void RenderingManager::removeObject(const MWWorld::Ptr &ptr)
787     {
788         mActorsPaths->remove(ptr);
789         mObjects->removeObject(ptr);
790         mWater->removeEmitter(ptr);
791     }
792 
setWaterEnabled(bool enabled)793     void RenderingManager::setWaterEnabled(bool enabled)
794     {
795         mWater->setEnabled(enabled);
796         mSky->setWaterEnabled(enabled);
797     }
798 
setWaterHeight(float height)799     void RenderingManager::setWaterHeight(float height)
800     {
801         mWater->setCullCallback(mTerrain->getHeightCullCallback(height, Mask_Water));
802         mWater->setHeight(height);
803         mSky->setWaterHeight(height);
804     }
805 
screenshot(osg::Image * image,int w,int h)806     void RenderingManager::screenshot(osg::Image* image, int w, int h)
807     {
808         mScreenshotManager->screenshot(image, w, h);
809     }
810 
screenshot360(osg::Image * image)811     bool RenderingManager::screenshot360(osg::Image* image)
812     {
813         if (mCamera->isVanityOrPreviewModeEnabled())
814         {
815             Log(Debug::Warning) << "Spherical screenshots are not allowed in preview mode.";
816             return false;
817         }
818 
819         unsigned int maskBackup = mPlayerAnimation->getObjectRoot()->getNodeMask();
820 
821         if (mCamera->isFirstPerson())
822             mPlayerAnimation->getObjectRoot()->setNodeMask(0);
823 
824         mScreenshotManager->screenshot360(image);
825 
826         mPlayerAnimation->getObjectRoot()->setNodeMask(maskBackup);
827 
828         return true;
829     }
830 
getScreenBounds(const osg::BoundingBox & worldbb)831     osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox &worldbb)
832     {
833         if (!worldbb.valid()) return osg::Vec4f();
834         osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix();
835         float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f;
836         for (int i=0; i<8; ++i)
837         {
838             osg::Vec3f corner = worldbb.corner(i);
839             corner = corner * viewProj;
840 
841             float x = (corner.x() + 1.f) * 0.5f;
842             float y = (corner.y() - 1.f) * (-0.5f);
843 
844             if (x < min_x)
845             min_x = x;
846 
847             if (x > max_x)
848             max_x = x;
849 
850             if (y < min_y)
851             min_y = y;
852 
853             if (y > max_y)
854             max_y = y;
855         }
856 
857         return osg::Vec4f(min_x, min_y, max_x, max_y);
858     }
859 
getIntersectionResult(osgUtil::LineSegmentIntersector * intersector)860     RenderingManager::RayResult getIntersectionResult (osgUtil::LineSegmentIntersector* intersector)
861     {
862         RenderingManager::RayResult result;
863         result.mHit = false;
864         result.mHitRefnum.unset();
865         result.mRatio = 0;
866         if (intersector->containsIntersections())
867         {
868             result.mHit = true;
869             osgUtil::LineSegmentIntersector::Intersection intersection = intersector->getFirstIntersection();
870 
871             result.mHitPointWorld = intersection.getWorldIntersectPoint();
872             result.mHitNormalWorld = intersection.getWorldIntersectNormal();
873             result.mRatio = intersection.ratio;
874 
875             PtrHolder* ptrHolder = nullptr;
876             std::vector<RefnumMarker*> refnumMarkers;
877             for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it)
878             {
879                 osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer();
880                 if (!userDataContainer)
881                     continue;
882                 for (unsigned int i=0; i<userDataContainer->getNumUserObjects(); ++i)
883                 {
884                     if (PtrHolder* p = dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i)))
885                         ptrHolder = p;
886                     if (RefnumMarker* r = dynamic_cast<RefnumMarker*>(userDataContainer->getUserObject(i)))
887                         refnumMarkers.push_back(r);
888                 }
889             }
890 
891             if (ptrHolder)
892                 result.mHitObject = ptrHolder->mPtr;
893 
894             unsigned int vertexCounter = 0;
895             for (unsigned int i=0; i<refnumMarkers.size(); ++i)
896             {
897                 unsigned int intersectionIndex = intersection.indexList.empty() ? 0 : intersection.indexList[0];
898                 if (!refnumMarkers[i]->mNumVertices || (intersectionIndex >= vertexCounter && intersectionIndex < vertexCounter + refnumMarkers[i]->mNumVertices))
899                 {
900                     result.mHitRefnum = refnumMarkers[i]->mRefnum;
901                     break;
902                 }
903                 vertexCounter += refnumMarkers[i]->mNumVertices;
904             }
905         }
906 
907         return result;
908 
909     }
910 
getIntersectionVisitor(osgUtil::Intersector * intersector,bool ignorePlayer,bool ignoreActors)911     osg::ref_ptr<osgUtil::IntersectionVisitor> RenderingManager::getIntersectionVisitor(osgUtil::Intersector *intersector, bool ignorePlayer, bool ignoreActors)
912     {
913         if (!mIntersectionVisitor)
914             mIntersectionVisitor = new osgUtil::IntersectionVisitor;
915 
916         mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber());
917         mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp());
918         mIntersectionVisitor->setIntersector(intersector);
919 
920         unsigned int mask = ~0u;
921         mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water|Mask_SimpleWater|Mask_Groundcover);
922         if (ignorePlayer)
923             mask &= ~(Mask_Player);
924         if (ignoreActors)
925             mask &= ~(Mask_Actor|Mask_Player);
926 
927         mIntersectionVisitor->setTraversalMask(mask);
928         return mIntersectionVisitor;
929     }
930 
castRay(const osg::Vec3f & origin,const osg::Vec3f & dest,bool ignorePlayer,bool ignoreActors)931     RenderingManager::RayResult RenderingManager::castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors)
932     {
933         osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::MODEL,
934             origin, dest));
935         intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST);
936 
937         mRootNode->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors));
938 
939         return getIntersectionResult(intersector);
940     }
941 
castCameraToViewportRay(const float nX,const float nY,float maxDistance,bool ignorePlayer,bool ignoreActors)942     RenderingManager::RayResult RenderingManager::castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors)
943     {
944         osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::PROJECTION,
945                                                                                                        nX * 2.f - 1.f, nY * (-2.f) + 1.f));
946 
947         osg::Vec3d dist (0.f, 0.f, -maxDistance);
948 
949         dist = dist * mViewer->getCamera()->getProjectionMatrix();
950 
951         osg::Vec3d end = intersector->getEnd();
952         end.z() = dist.z();
953         intersector->setEnd(end);
954         intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST);
955 
956         mViewer->getCamera()->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors));
957 
958         return getIntersectionResult(intersector);
959     }
960 
updatePtr(const MWWorld::Ptr & old,const MWWorld::Ptr & updated)961     void RenderingManager::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated)
962     {
963         mObjects->updatePtr(old, updated);
964         mActorsPaths->updatePtr(old, updated);
965     }
966 
spawnEffect(const std::string & model,const std::string & texture,const osg::Vec3f & worldPosition,float scale,bool isMagicVFX)967     void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
968     {
969         mEffectManager->addEffect(model, texture, worldPosition, scale, isMagicVFX);
970     }
971 
notifyWorldSpaceChanged()972     void RenderingManager::notifyWorldSpaceChanged()
973     {
974         mEffectManager->clear();
975         mWater->clearRipples();
976     }
977 
clear()978     void RenderingManager::clear()
979     {
980         mSky->setMoonColour(false);
981 
982         notifyWorldSpaceChanged();
983         if (mObjectPaging)
984             mObjectPaging->clear();
985     }
986 
getAnimation(const MWWorld::Ptr & ptr)987     MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
988     {
989         if (mPlayerAnimation.get() && ptr == mPlayerAnimation->getPtr())
990             return mPlayerAnimation.get();
991 
992         return mObjects->getAnimation(ptr);
993     }
994 
getAnimation(const MWWorld::ConstPtr & ptr) const995     const MWRender::Animation* RenderingManager::getAnimation(const MWWorld::ConstPtr &ptr) const
996     {
997         if (mPlayerAnimation.get() && ptr == mPlayerAnimation->getPtr())
998             return mPlayerAnimation.get();
999 
1000         return mObjects->getAnimation(ptr);
1001     }
1002 
setupPlayer(const MWWorld::Ptr & player)1003     void RenderingManager::setupPlayer(const MWWorld::Ptr &player)
1004     {
1005         if (!mPlayerNode)
1006         {
1007             mPlayerNode = new SceneUtil::PositionAttitudeTransform;
1008             mPlayerNode->setNodeMask(Mask_Player);
1009             mPlayerNode->setName("Player Root");
1010             mSceneRoot->addChild(mPlayerNode);
1011         }
1012 
1013         mPlayerNode->setUserDataContainer(new osg::DefaultUserDataContainer);
1014         mPlayerNode->getUserDataContainer()->addUserObject(new PtrHolder(player));
1015 
1016         player.getRefData().setBaseNode(mPlayerNode);
1017 
1018         mWater->removeEmitter(player);
1019         mWater->addEmitter(player);
1020     }
1021 
renderPlayer(const MWWorld::Ptr & player)1022     void RenderingManager::renderPlayer(const MWWorld::Ptr &player)
1023     {
1024         mPlayerAnimation = new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0, NpcAnimation::VM_Normal,
1025                                                 mFirstPersonFieldOfView);
1026 
1027         mCamera->setAnimation(mPlayerAnimation.get());
1028         mCamera->attachTo(player);
1029     }
1030 
rebuildPtr(const MWWorld::Ptr & ptr)1031     void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr)
1032     {
1033         NpcAnimation *anim = nullptr;
1034         if(ptr == mPlayerAnimation->getPtr())
1035             anim = mPlayerAnimation.get();
1036         else
1037             anim = dynamic_cast<NpcAnimation*>(mObjects->getAnimation(ptr));
1038         if(anim)
1039         {
1040             anim->rebuild();
1041             if(mCamera->getTrackingPtr() == ptr)
1042             {
1043                 mCamera->attachTo(ptr);
1044                 mCamera->setAnimation(anim);
1045             }
1046         }
1047     }
1048 
addWaterRippleEmitter(const MWWorld::Ptr & ptr)1049     void RenderingManager::addWaterRippleEmitter(const MWWorld::Ptr &ptr)
1050     {
1051         mWater->addEmitter(ptr);
1052     }
1053 
removeWaterRippleEmitter(const MWWorld::Ptr & ptr)1054     void RenderingManager::removeWaterRippleEmitter(const MWWorld::Ptr &ptr)
1055     {
1056         mWater->removeEmitter(ptr);
1057     }
1058 
emitWaterRipple(const osg::Vec3f & pos)1059     void RenderingManager::emitWaterRipple(const osg::Vec3f &pos)
1060     {
1061         mWater->emitRipple(pos);
1062     }
1063 
updateProjectionMatrix()1064     void RenderingManager::updateProjectionMatrix()
1065     {
1066         double aspect = mViewer->getCamera()->getViewport()->aspectRatio();
1067         float fov = mFieldOfView;
1068         if (mFieldOfViewOverridden)
1069             fov = mFieldOfViewOverride;
1070         mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance);
1071 
1072         mUniformNear->set(mNearClip);
1073         mUniformFar->set(mViewDistance);
1074 
1075         // Since our fog is not radial yet, we should take FOV in account, otherwise terrain near viewing distance may disappear.
1076         // Limit FOV here just for sure, otherwise viewing distance can be too high.
1077         fov = std::min(mFieldOfView, 140.f);
1078         float distanceMult = std::cos(osg::DegreesToRadians(fov)/2.f);
1079         mTerrain->setViewDistance(mViewDistance * (distanceMult ? 1.f/distanceMult : 1.f));
1080 
1081         if (mGroundcoverWorld)
1082         {
1083             float groundcoverDistance = std::max(0.f, Settings::Manager::getFloat("rendering distance", "Groundcover"));
1084             mGroundcoverWorld->setViewDistance(groundcoverDistance * (distanceMult ? 1.f/distanceMult : 1.f));
1085         }
1086     }
1087 
updateTextureFiltering()1088     void RenderingManager::updateTextureFiltering()
1089     {
1090         mViewer->stopThreading();
1091 
1092         mResourceSystem->getSceneManager()->setFilterSettings(
1093             Settings::Manager::getString("texture mag filter", "General"),
1094             Settings::Manager::getString("texture min filter", "General"),
1095             Settings::Manager::getString("texture mipmap", "General"),
1096             Settings::Manager::getInt("anisotropy", "General")
1097         );
1098 
1099         mTerrain->updateTextureFiltering();
1100 
1101         mViewer->startThreading();
1102     }
1103 
updateAmbient()1104     void RenderingManager::updateAmbient()
1105     {
1106         osg::Vec4f color = mAmbientColor;
1107 
1108         if (mNightEyeFactor > 0.f)
1109             color += osg::Vec4f(0.7, 0.7, 0.7, 0.0) * mNightEyeFactor;
1110 
1111         mStateUpdater->setAmbientColor(color);
1112     }
1113 
setFogColor(const osg::Vec4f & color)1114     void RenderingManager::setFogColor(const osg::Vec4f &color)
1115     {
1116         mViewer->getCamera()->setClearColor(color);
1117 
1118         mStateUpdater->setFogColor(color);
1119     }
1120 
reportStats() const1121     void RenderingManager::reportStats() const
1122     {
1123         osg::Stats* stats = mViewer->getViewerStats();
1124         unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
1125         if (stats->collectStats("resource"))
1126         {
1127             stats->setAttribute(frameNumber, "UnrefQueue", mUnrefQueue->getNumItems());
1128 
1129             mTerrain->reportStats(frameNumber, stats);
1130         }
1131     }
1132 
processChangedSettings(const Settings::CategorySettingVector & changed)1133     void RenderingManager::processChangedSettings(const Settings::CategorySettingVector &changed)
1134     {
1135         for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it)
1136         {
1137             if (it->first == "Camera" && it->second == "field of view")
1138             {
1139                 mFieldOfView = Settings::Manager::getFloat("field of view", "Camera");
1140                 updateProjectionMatrix();
1141             }
1142             else if (it->first == "Camera" && it->second == "viewing distance")
1143             {
1144                 mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
1145                 if(!Settings::Manager::getBool("use distant fog", "Fog"))
1146                     mStateUpdater->setFogEnd(mViewDistance);
1147                 updateProjectionMatrix();
1148             }
1149             else if (it->first == "General" && (it->second == "texture filter" ||
1150                                                 it->second == "texture mipmap" ||
1151                                                 it->second == "anisotropy"))
1152             {
1153                 updateTextureFiltering();
1154             }
1155             else if (it->first == "Water")
1156             {
1157                 mWater->processChangedSettings(changed);
1158             }
1159             else if (it->first == "Shaders" && it->second == "minimum interior brightness")
1160             {
1161                 mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f);
1162                 if (MWMechanics::getPlayer().isInCell())
1163                     configureAmbient(MWMechanics::getPlayer().getCell()->getCell());
1164             }
1165             else if (it->first == "Shaders" && (it->second == "light bounds multiplier" ||
1166                                                 it->second == "maximum light distance" ||
1167                                                 it->second == "light fade start" ||
1168                                                 it->second == "max lights"))
1169             {
1170                 auto* lightManager = static_cast<SceneUtil::LightManager*>(getLightRoot());
1171                 lightManager->processChangedSettings(changed);
1172 
1173                 if (it->second == "max lights" && !lightManager->usingFFP())
1174                 {
1175                     mViewer->stopThreading();
1176 
1177                     lightManager->updateMaxLights();
1178 
1179                     auto defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
1180                     for (const auto& [name, key] : lightManager->getLightDefines())
1181                         defines[name] = key;
1182                     mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
1183 
1184                     mSceneRoot->removeUpdateCallback(mStateUpdater);
1185                     mStateUpdater = new StateUpdater;
1186                     mSceneRoot->addUpdateCallback(mStateUpdater);
1187                     mStateUpdater->setFogEnd(mViewDistance);
1188                     updateAmbient();
1189 
1190                     mViewer->startThreading();
1191                 }
1192             }
1193         }
1194     }
1195 
getNearClipDistance() const1196     float RenderingManager::getNearClipDistance() const
1197     {
1198         return mNearClip;
1199     }
1200 
getTerrainHeightAt(const osg::Vec3f & pos)1201     float RenderingManager::getTerrainHeightAt(const osg::Vec3f &pos)
1202     {
1203         return mTerrain->getHeightAt(pos);
1204     }
1205 
overrideFieldOfView(float val)1206     void RenderingManager::overrideFieldOfView(float val)
1207     {
1208         if (mFieldOfViewOverridden != true || mFieldOfViewOverride != val)
1209         {
1210             mFieldOfViewOverridden = true;
1211             mFieldOfViewOverride = val;
1212             updateProjectionMatrix();
1213         }
1214     }
1215 
getHalfExtents(const MWWorld::ConstPtr & object) const1216     osg::Vec3f RenderingManager::getHalfExtents(const MWWorld::ConstPtr& object) const
1217     {
1218         osg::Vec3f halfExtents(0, 0, 0);
1219         std::string modelName = object.getClass().getModel(object);
1220         if (modelName.empty())
1221             return halfExtents;
1222 
1223         osg::ref_ptr<const osg::Node> node = mResourceSystem->getSceneManager()->getTemplate(modelName);
1224         osg::ComputeBoundsVisitor computeBoundsVisitor;
1225         computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect));
1226         const_cast<osg::Node*>(node.get())->accept(computeBoundsVisitor);
1227         osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox();
1228 
1229         if (bounds.valid())
1230         {
1231             halfExtents[0] = std::abs(bounds.xMax() - bounds.xMin()) / 2.f;
1232             halfExtents[1] = std::abs(bounds.yMax() - bounds.yMin()) / 2.f;
1233             halfExtents[2] = std::abs(bounds.zMax() - bounds.zMin()) / 2.f;
1234         }
1235 
1236         return halfExtents;
1237     }
1238 
resetFieldOfView()1239     void RenderingManager::resetFieldOfView()
1240     {
1241         if (mFieldOfViewOverridden == true)
1242         {
1243             mFieldOfViewOverridden = false;
1244 
1245             updateProjectionMatrix();
1246         }
1247     }
exportSceneGraph(const MWWorld::Ptr & ptr,const std::string & filename,const std::string & format)1248     void RenderingManager::exportSceneGraph(const MWWorld::Ptr &ptr, const std::string &filename, const std::string &format)
1249     {
1250         osg::Node* node = mViewer->getSceneData();
1251         if (!ptr.isEmpty())
1252             node = ptr.getRefData().getBaseNode();
1253 
1254         SceneUtil::writeScene(node, filename, format);
1255     }
1256 
getLandManager() const1257     LandManager *RenderingManager::getLandManager() const
1258     {
1259         return mTerrainStorage->getLandManager();
1260     }
1261 
updateActorPath(const MWWorld::ConstPtr & actor,const std::deque<osg::Vec3f> & path,const osg::Vec3f & halfExtents,const osg::Vec3f & start,const osg::Vec3f & end) const1262     void RenderingManager::updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,
1263             const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const
1264     {
1265         mActorsPaths->update(actor, path, halfExtents, start, end, mNavigator.getSettings());
1266     }
1267 
removeActorPath(const MWWorld::ConstPtr & actor) const1268     void RenderingManager::removeActorPath(const MWWorld::ConstPtr& actor) const
1269     {
1270         mActorsPaths->remove(actor);
1271     }
1272 
setNavMeshNumber(const std::size_t value)1273     void RenderingManager::setNavMeshNumber(const std::size_t value)
1274     {
1275         mNavMeshNumber = value;
1276     }
1277 
updateNavMesh()1278     void RenderingManager::updateNavMesh()
1279     {
1280         if (!mNavMesh->isEnabled())
1281             return;
1282 
1283         const auto navMeshes = mNavigator.getNavMeshes();
1284 
1285         auto it = navMeshes.begin();
1286         for (std::size_t i = 0; it != navMeshes.end() && i < mNavMeshNumber; ++i)
1287             ++it;
1288         if (it == navMeshes.end())
1289         {
1290             mNavMesh->reset();
1291         }
1292         else
1293         {
1294             try
1295             {
1296                 const auto locked = it->second->lockConst();
1297                 mNavMesh->update(locked->getImpl(), mNavMeshNumber, locked->getGeneration(),
1298                                  locked->getNavMeshRevision(), mNavigator.getSettings());
1299             }
1300             catch (const std::exception& e)
1301             {
1302                 Log(Debug::Error) << "NavMesh render update exception: " << e.what();
1303             }
1304         }
1305     }
1306 
updateRecastMesh()1307     void RenderingManager::updateRecastMesh()
1308     {
1309         if (!mRecastMesh->isEnabled())
1310             return;
1311 
1312         mRecastMesh->update(mNavigator.getRecastMeshTiles(), mNavigator.getSettings());
1313     }
1314 
setActiveGrid(const osg::Vec4i & grid)1315     void RenderingManager::setActiveGrid(const osg::Vec4i &grid)
1316     {
1317         mTerrain->setActiveGrid(grid);
1318     }
pagingEnableObject(int type,const MWWorld::ConstPtr & ptr,bool enabled)1319     bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled)
1320     {
1321         if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging)
1322             return false;
1323         if (mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getCellRef().getPosition().asVec3(), osg::Vec2i(ptr.getCell()->getCell()->getGridX(), ptr.getCell()->getCell()->getGridY()), enabled))
1324         {
1325             mTerrain->rebuildViews();
1326             return true;
1327         }
1328         return false;
1329     }
pagingBlacklistObject(int type,const MWWorld::ConstPtr & ptr)1330     void RenderingManager::pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr)
1331     {
1332         if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging)
1333             return;
1334         const ESM::RefNum & refnum = ptr.getCellRef().getRefNum();
1335         if (!refnum.hasContentFile()) return;
1336         if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3(), osg::Vec2i(ptr.getCell()->getCell()->getGridX(), ptr.getCell()->getCell()->getGridY())))
1337             mTerrain->rebuildViews();
1338     }
pagingUnlockCache()1339     bool RenderingManager::pagingUnlockCache()
1340     {
1341         if (mObjectPaging && mObjectPaging->unlockCache())
1342         {
1343             mTerrain->rebuildViews();
1344             return true;
1345         }
1346         return false;
1347     }
getPagedRefnums(const osg::Vec4i & activeGrid,std::set<ESM::RefNum> & out)1348     void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out)
1349     {
1350         if (mObjectPaging)
1351             mObjectPaging->getPagedRefnums(activeGrid, out);
1352     }
1353 }
1354