mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 20:26:43 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into sceneinput
This commit is contained in:
		
						commit
						62047b1868
					
				
					 16 changed files with 561 additions and 102 deletions
				
			
		|  | @ -39,8 +39,6 @@ char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const | |||
| std::pair<char, bool> CSMWorld::ScriptContext::getMemberType (const std::string& name, | ||||
|     const std::string& id) const | ||||
| { | ||||
|     /// \todo invalidate locals cache on change to scripts
 | ||||
| 
 | ||||
|     std::string id2 = Misc::StringUtils::lowerCase (id); | ||||
| 
 | ||||
|     int index = mData.getScripts().searchId (id2); | ||||
|  | @ -120,3 +118,18 @@ void CSMWorld::ScriptContext::clear() | |||
|     mIdsUpdated = false; | ||||
|     mLocals.clear(); | ||||
| } | ||||
| 
 | ||||
| bool CSMWorld::ScriptContext::clearLocals (const std::string& script) | ||||
| { | ||||
|     std::map<std::string, Compiler::Locals>::iterator iter = | ||||
|         mLocals.find (Misc::StringUtils::lowerCase (script)); | ||||
| 
 | ||||
|     if (iter!=mLocals.end()) | ||||
|     { | ||||
|         mLocals.erase (iter); | ||||
|         mIdsUpdated = false; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
|  |  | |||
|  | @ -46,6 +46,9 @@ namespace CSMWorld | |||
| 
 | ||||
|             void clear(); | ||||
|             ///< Remove all cached data.
 | ||||
| 
 | ||||
|             /// \return Were there any locals that needed clearing?
 | ||||
|             bool clearLocals (const std::string& script); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -170,7 +170,10 @@ void CSVRender::PagedWorldspaceWidget::referenceAdded (const QModelIndex& parent | |||
| 
 | ||||
| std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction() | ||||
| { | ||||
|     osg::Vec3d position = mView->getCamera()->getViewMatrix().getTrans(); | ||||
|     osg::Vec3d eye, center, up; | ||||
|     mView->getCamera()->getViewMatrixAsLookAt(eye, center, up); | ||||
|     osg::Vec3d position = eye; | ||||
| 
 | ||||
|     std::ostringstream stream; | ||||
| 
 | ||||
|     stream | ||||
|  |  | |||
|  | @ -166,7 +166,9 @@ void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons ( | |||
| 
 | ||||
| std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction() | ||||
| { | ||||
|     osg::Vec3d position = mView->getCamera()->getViewMatrix().getTrans(); | ||||
|     osg::Vec3d eye, center, up; | ||||
|     mView->getCamera()->getViewMatrixAsLookAt(eye, center, up); | ||||
|     osg::Vec3d position = eye; | ||||
| 
 | ||||
|     std::ostringstream stream; | ||||
| 
 | ||||
|  |  | |||
|  | @ -131,6 +131,11 @@ void CSVWorld::ScriptErrorTable::clear() | |||
|     setRowCount (0); | ||||
| } | ||||
| 
 | ||||
| bool CSVWorld::ScriptErrorTable::clearLocals (const std::string& script) | ||||
| { | ||||
|     return mContext.clearLocals (script); | ||||
| } | ||||
| 
 | ||||
| void CSVWorld::ScriptErrorTable::cellClicked (int row, int column) | ||||
| { | ||||
|     if (item (row, 1)) | ||||
|  |  | |||
|  | @ -44,6 +44,11 @@ namespace CSVWorld | |||
| 
 | ||||
|             void clear(); | ||||
| 
 | ||||
|             /// Clear local variable cache for \a script.
 | ||||
|             ///
 | ||||
|             /// \return Were there any locals that needed clearing?
 | ||||
|             bool clearLocals (const std::string& script); | ||||
| 
 | ||||
|         private slots: | ||||
| 
 | ||||
|             void cellClicked (int row, int column); | ||||
|  |  | |||
|  | @ -89,6 +89,7 @@ CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc: | |||
|         *document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts)); | ||||
| 
 | ||||
|     mColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_ScriptText); | ||||
|     mIdColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); | ||||
|     mStateColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); | ||||
| 
 | ||||
|     QString source = mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString(); | ||||
|  | @ -241,6 +242,15 @@ void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QMo | |||
| 
 | ||||
|     ScriptEdit::ChangeLock lock (*mEditor); | ||||
| 
 | ||||
|     bool updateRequired = false; | ||||
| 
 | ||||
|     for (int i=topLeft.row(); i<=bottomRight.row(); ++i) | ||||
|     { | ||||
|         std::string id = mModel->data (mModel->index (i, mIdColumn)).toString().toUtf8().constData(); | ||||
|         if (mErrors->clearLocals (id)) | ||||
|             updateRequired = true; | ||||
|     } | ||||
| 
 | ||||
|     QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); | ||||
| 
 | ||||
|     if (index.row()>=topLeft.row() && index.row()<=bottomRight.row()) | ||||
|  | @ -256,13 +266,28 @@ void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QMo | |||
|             mEditor->setPlainText (source); | ||||
|             mEditor->setTextCursor (cursor); | ||||
| 
 | ||||
|             recompile(); | ||||
|             updateRequired = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (updateRequired) | ||||
|         recompile(); | ||||
| } | ||||
| 
 | ||||
| void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end) | ||||
| { | ||||
|     bool updateRequired = false; | ||||
| 
 | ||||
|     for (int i=start; i<=end; ++i) | ||||
|     { | ||||
|         std::string id = mModel->data (mModel->index (i, mIdColumn)).toString().toUtf8().constData(); | ||||
|         if (mErrors->clearLocals (id)) | ||||
|             updateRequired = true; | ||||
|     } | ||||
| 
 | ||||
|     if (updateRequired) | ||||
|         recompile(); | ||||
| 
 | ||||
|     QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); | ||||
| 
 | ||||
|     if (!parent.isValid() && index.row()>=start && index.row()<=end) | ||||
|  |  | |||
|  | @ -38,6 +38,7 @@ namespace CSVWorld | |||
|             CSMDoc::Document& mDocument; | ||||
|             CSMWorld::IdTable *mModel; | ||||
|             int mColumn; | ||||
|             int mIdColumn; | ||||
|             int mStateColumn; | ||||
|             TableBottomBox *mBottom; | ||||
|             RecordButtonBar *mButtons; | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ add_openmw_dir (mwrender | |||
|     actors objects renderingmanager animation rotatecontroller sky npcanimation vismask | ||||
|     creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation | ||||
|     bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation | ||||
|     renderbin | ||||
|     ) | ||||
| 
 | ||||
| add_openmw_dir (mwinput | ||||
|  |  | |||
							
								
								
									
										20
									
								
								apps/openmw/mwrender/renderbin.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								apps/openmw/mwrender/renderbin.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| #ifndef OPENMW_MWRENDER_RENDERBIN_H | ||||
| #define OPENMW_MWRENDER_RENDERBIN_H | ||||
| 
 | ||||
| namespace MWRender | ||||
| { | ||||
| 
 | ||||
|     /// Defines the render bin numbers used in the OpenMW scene graph. The bin with the lowest number is rendered first.
 | ||||
|     /// Beware of RenderBin nesting, in most cases you will want to use setNestRenderBins(false).
 | ||||
|     enum RenderBins | ||||
|     { | ||||
|         RenderBin_Sky = -1, | ||||
|         RenderBin_Default = 0, | ||||
|         RenderBin_Water = 9, | ||||
|         RenderBin_OcclusionQuery = 10, | ||||
|         RenderBin_SunGlare = 11 | ||||
|     }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -11,6 +11,11 @@ | |||
| #include <osg/TexEnvCombine> | ||||
| #include <osg/TexMat> | ||||
| #include <osg/Version> | ||||
| #include <osg/OcclusionQueryNode> | ||||
| #include <osg/ColorMask> | ||||
| #include <osg/MatrixTransform> | ||||
| #include <osg/BlendFunc> | ||||
| #include <osg/AlphaFunc> | ||||
| 
 | ||||
| #include <osgParticle/ParticleSystem> | ||||
| #include <osgParticle/ParticleSystemUpdater> | ||||
|  | @ -38,6 +43,7 @@ | |||
| #include "../mwworld/fallback.hpp" | ||||
| 
 | ||||
| #include "vismask.hpp" | ||||
| #include "renderbin.hpp" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|  | @ -359,19 +365,57 @@ class Sun : public CelestialBody | |||
| public: | ||||
|     Sun(osg::Group* parentNode, Resource::TextureManager& textureManager) | ||||
|         : CelestialBody(parentNode, 1.0f, 1) | ||||
|         , mUpdater(new Updater(textureManager)) | ||||
|         , mUpdater(new Updater) | ||||
|     { | ||||
|         mGeode->addUpdateCallback(mUpdater); | ||||
|         mTransform->addUpdateCallback(mUpdater); | ||||
| 
 | ||||
|         osg::ref_ptr<osg::Texture2D> sunTex = textureManager.getTexture2D("textures/tx_sun_05.dds", | ||||
|                                                                         osg::Texture::CLAMP, | ||||
|                                                                         osg::Texture::CLAMP); | ||||
| 
 | ||||
|         mGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON); | ||||
| 
 | ||||
|         osg::ref_ptr<osg::Group> queryNode (new osg::Group); | ||||
|         // Need to render after the world geometry so we can correctly test for occlusions
 | ||||
|         queryNode->getOrCreateStateSet()->setRenderBinDetails(RenderBin_OcclusionQuery, "RenderBin"); | ||||
|         queryNode->getOrCreateStateSet()->setNestRenderBins(false); | ||||
|         // Set up alpha testing on the occlusion testing subgraph, that way we can get the occlusion tested fragments to match the circular shape of the sun
 | ||||
|         osg::ref_ptr<osg::AlphaFunc> alphaFunc (new osg::AlphaFunc); | ||||
|         alphaFunc->setFunction(osg::AlphaFunc::GREATER, 0.8); | ||||
|         queryNode->getOrCreateStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); | ||||
|         queryNode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON); | ||||
|         queryNode->getOrCreateStateSet()->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON); | ||||
| 
 | ||||
|         mTransform->addChild(queryNode); | ||||
| 
 | ||||
|         mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true); | ||||
|         mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false); | ||||
| 
 | ||||
|         createSunFlash(textureManager); | ||||
|         createSunGlare(); | ||||
|     } | ||||
| 
 | ||||
|     ~Sun() | ||||
|     { | ||||
|         mGeode->removeUpdateCallback(mUpdater); | ||||
|         mTransform->removeUpdateCallback(mUpdater); | ||||
|         destroySunFlash(); | ||||
|         destroySunGlare(); | ||||
|     } | ||||
| 
 | ||||
|     void setColor(const osg::Vec4f& color) | ||||
|     { | ||||
|         mUpdater->mColor.r() = color.r(); | ||||
|         mUpdater->mColor.g() = color.g(); | ||||
|         mUpdater->mColor.b() = color.b(); | ||||
|     } | ||||
| 
 | ||||
|     virtual void adjustTransparency(const float ratio) | ||||
|     { | ||||
|         mUpdater->mColor.a() = ratio; | ||||
|         if (mSunGlareCallback) | ||||
|             mSunGlareCallback->setGlareView(ratio); | ||||
|         if (mSunFlashCallback) | ||||
|             mSunFlashCallback->setGlareView(ratio); | ||||
|     } | ||||
| 
 | ||||
|     void setDirection(const osg::Vec3f& direction) | ||||
|  | @ -384,37 +428,390 @@ public: | |||
|         mTransform->setAttitude(quat); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     struct Updater : public SceneUtil::StateSetUpdater | ||||
|     void setGlareTimeOfDayFade(float val) | ||||
|     { | ||||
|         Resource::TextureManager& mTextureManager; | ||||
|         if (mSunGlareCallback) | ||||
|             mSunGlareCallback->setTimeOfDayFade(val); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     /// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels.
 | ||||
|     osg::ref_ptr<osg::OcclusionQueryNode> createOcclusionQueryNode(osg::Group* parent, bool queryVisible) | ||||
|     { | ||||
|         osg::ref_ptr<osg::OcclusionQueryNode> oqn = new osg::OcclusionQueryNode; | ||||
|         oqn->setQueriesEnabled(true); | ||||
| 
 | ||||
|         // Make it fast! A DYNAMIC query geometry means we can't break frame until the flare is rendered (which is rendered after all the other geometry,
 | ||||
|         // so that would be pretty bad). STATIC should be safe, since our node's local bounds are static, thus computeBounds() which modifies the queryGeometry
 | ||||
|         // is only called once.
 | ||||
|         // Note the debug geometry setDebugDisplay(true) is always DYNAMIC and that can't be changed, not a big deal.
 | ||||
|         oqn->getQueryGeometry()->setDataVariance(osg::Object::STATIC); | ||||
| 
 | ||||
|         osg::ref_ptr<osg::Geode> queryGeode = osg::clone(mGeode.get(), osg::CopyOp::DEEP_COPY_ALL); | ||||
|         // Disable writing to the color buffer. We are using this geode for visibility tests only.
 | ||||
|         osg::ref_ptr<osg::ColorMask> colormask (new osg::ColorMask(0, 0, 0, 0)); | ||||
|         queryGeode->getOrCreateStateSet()->setAttributeAndModes(colormask, osg::StateAttribute::ON); | ||||
| 
 | ||||
|         oqn->addChild(queryGeode); | ||||
| 
 | ||||
|         // Remove the default OFF|PROTECTED setting for texturing. We *want* to enable texturing for alpha testing purposes
 | ||||
|         oqn->getQueryStateSet()->removeTextureMode(0, GL_TEXTURE_2D); | ||||
| 
 | ||||
|         // Need to add texture coordinates so that texturing works. A bit ugly, relies on the vertex ordering
 | ||||
|         // used within OcclusionQueryNode.
 | ||||
|         osg::ref_ptr<osg::Vec2Array> texCoordArray (new osg::Vec2Array); | ||||
|         for (int i=0; i<8; ++i) | ||||
|         { | ||||
|             texCoordArray->push_back(osg::Vec2(0,0)); | ||||
|             texCoordArray->push_back(osg::Vec2(1,0)); | ||||
|             texCoordArray->push_back(osg::Vec2(0,0)); | ||||
|             texCoordArray->push_back(osg::Vec2(1,0)); | ||||
|             texCoordArray->push_back(osg::Vec2(1,1)); | ||||
|             texCoordArray->push_back(osg::Vec2(0,1)); | ||||
|             texCoordArray->push_back(osg::Vec2(0,1)); | ||||
|             texCoordArray->push_back(osg::Vec2(1,1)); | ||||
|         } | ||||
| 
 | ||||
|         oqn->getQueryGeometry()->setTexCoordArray(0, texCoordArray, osg::Array::BIND_PER_VERTEX); | ||||
| 
 | ||||
|         if (queryVisible) | ||||
|         { | ||||
|             osg::ref_ptr<osg::Depth> depth (new osg::Depth); | ||||
|             depth->setFunction(osg::Depth::LESS); | ||||
|             // This is a trick to make fragments written by the query always use the maximum depth value,
 | ||||
|             // without having to retrieve the current far clipping distance.
 | ||||
|             // We want the sun glare to be "infinitely" far away.
 | ||||
|             depth->setZNear(1.0); | ||||
|             depth->setZFar(1.0); | ||||
|             oqn->getQueryStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             oqn->getQueryStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); | ||||
|         } | ||||
| 
 | ||||
|         parent->addChild(oqn); | ||||
| 
 | ||||
|         return oqn; | ||||
|     } | ||||
| 
 | ||||
|     void createSunFlash(Resource::TextureManager& textureManager) | ||||
|     { | ||||
|         osg::ref_ptr<osg::Texture2D> tex = textureManager.getTexture2D("textures/tx_sun_flash_grey_05.dds", | ||||
|                                                                         osg::Texture::CLAMP, | ||||
|                                                                         osg::Texture::CLAMP); | ||||
| 
 | ||||
|         osg::ref_ptr<osg::PositionAttitudeTransform> transform (new osg::PositionAttitudeTransform); | ||||
|         const float scale = 2.6f; | ||||
|         transform->setScale(osg::Vec3f(scale,scale,scale)); | ||||
| 
 | ||||
|         mTransform->addChild(transform); | ||||
| 
 | ||||
|         osg::ref_ptr<osg::Geode> geode (new osg::Geode); | ||||
|         transform->addChild(geode); | ||||
| 
 | ||||
|         geode->addDrawable(createTexturedQuad()); | ||||
| 
 | ||||
|         osg::StateSet* stateset = geode->getOrCreateStateSet(); | ||||
| 
 | ||||
|         stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON); | ||||
|         stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); | ||||
|         stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin"); | ||||
|         stateset->setNestRenderBins(false); | ||||
| 
 | ||||
|         mSunFlashNode = transform; | ||||
| 
 | ||||
|         mSunFlashCallback = new SunFlashCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels); | ||||
|         mSunFlashNode->addCullCallback(mSunFlashCallback); | ||||
|     } | ||||
|     void destroySunFlash() | ||||
|     { | ||||
|         if (mSunFlashNode) | ||||
|         { | ||||
|             mSunFlashNode->removeCullCallback(mSunFlashCallback); | ||||
|             mSunFlashCallback = NULL; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void createSunGlare() | ||||
|     { | ||||
|         osg::ref_ptr<osg::Camera> camera (new osg::Camera); | ||||
|         camera->setProjectionMatrix(osg::Matrix::identity()); | ||||
|         camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); // add to skyRoot instead?
 | ||||
|         camera->setViewMatrix(osg::Matrix::identity()); | ||||
|         camera->setClearMask(0); | ||||
|         camera->setRenderOrder(osg::Camera::NESTED_RENDER); | ||||
|         camera->setAllowEventFocus(false); | ||||
| 
 | ||||
|         osg::ref_ptr<osg::Geode> geode (new osg::Geode); | ||||
|         osg::ref_ptr<osg::Geometry> geom = osg::createTexturedQuadGeometry(osg::Vec3f(-1,-1,0), osg::Vec3f(2,0,0), osg::Vec3f(0,2,0)); | ||||
|         geode->addDrawable(geom); | ||||
| 
 | ||||
|         camera->addChild(geode); | ||||
| 
 | ||||
|         osg::StateSet* stateset = geom->getOrCreateStateSet(); | ||||
| 
 | ||||
|         stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin"); | ||||
|         stateset->setNestRenderBins(false); | ||||
|         stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); | ||||
| 
 | ||||
|         // set up additive blending
 | ||||
|         osg::ref_ptr<osg::BlendFunc> blendFunc (new osg::BlendFunc); | ||||
|         blendFunc->setSource(osg::BlendFunc::SRC_ALPHA); | ||||
|         blendFunc->setDestination(osg::BlendFunc::ONE); | ||||
|         stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); | ||||
| 
 | ||||
|         mSunGlareCallback = new SunGlareCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels, mTransform); | ||||
|         mSunGlareNode = camera; | ||||
| 
 | ||||
|         mSunGlareNode->addCullCallback(mSunGlareCallback); | ||||
| 
 | ||||
|         mTransform->addChild(camera); | ||||
|     } | ||||
|     void destroySunGlare() | ||||
|     { | ||||
|         if (mSunGlareNode) | ||||
|         { | ||||
|             mSunGlareNode->removeCullCallback(mSunGlareCallback); | ||||
|             mSunGlareCallback = NULL; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     class Updater : public SceneUtil::StateSetUpdater | ||||
|     { | ||||
|     public: | ||||
|         osg::Vec4f mColor; | ||||
| 
 | ||||
|         Updater(Resource::TextureManager& textureManager) | ||||
|             : mTextureManager(textureManager) | ||||
|             , mColor(0.0f, 0.0f, 0.0f, 1.0f) | ||||
|         Updater() | ||||
|             : mColor(1.f, 1.f, 1.f, 1.f) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         virtual void setDefaults(osg::StateSet* stateset) | ||||
|         { | ||||
|             osg::ref_ptr<osg::Texture2D> tex = mTextureManager.getTexture2D("textures/tx_sun_05.dds", | ||||
|                                                                             osg::Texture::CLAMP, | ||||
|                                                                             osg::Texture::CLAMP); | ||||
| 
 | ||||
|             stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON); | ||||
|             stateset->setAttributeAndModes(createUnlitMaterial(), | ||||
|                                            osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); | ||||
|             stateset->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON); | ||||
|         } | ||||
| 
 | ||||
|         virtual void apply(osg::StateSet* stateset, osg::NodeVisitor*) | ||||
|         { | ||||
|             osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL)); | ||||
|             mat->setDiffuse(osg::Material::FRONT_AND_BACK, mColor); | ||||
|             mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,mColor.a())); | ||||
|             mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(mColor.r(), mColor.g(), mColor.b(), 1)); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class OcclusionCallback : public osg::NodeCallback | ||||
|     { | ||||
|     public: | ||||
|         OcclusionCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal) | ||||
|             : mOcclusionQueryVisiblePixels(oqnVisible) | ||||
|             , mOcclusionQueryTotalPixels(oqnTotal) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|     protected: | ||||
|         float getVisibleRatio (osg::Camera* camera) | ||||
|         { | ||||
|             int visible = mOcclusionQueryVisiblePixels->getQueryGeometry()->getNumPixels(camera); | ||||
|             int total = mOcclusionQueryTotalPixels->getQueryGeometry()->getNumPixels(camera); | ||||
| 
 | ||||
|             float visibleRatio = 0.f; | ||||
|             if (total > 0) | ||||
|                 visibleRatio = static_cast<float>(visible) / static_cast<float>(total); | ||||
| 
 | ||||
|             float dt = MWBase::Environment::get().getFrameDuration(); | ||||
| 
 | ||||
|             float lastRatio = mLastRatio[osg::observer_ptr<osg::Camera>(camera)]; | ||||
| 
 | ||||
|             float change = dt*10; | ||||
| 
 | ||||
|             if (visibleRatio > lastRatio) | ||||
|                 visibleRatio = std::min(visibleRatio, lastRatio + change); | ||||
|             else | ||||
|                 visibleRatio = std::max(visibleRatio, lastRatio - change); | ||||
| 
 | ||||
|             mLastRatio[osg::observer_ptr<osg::Camera>(camera)] = visibleRatio; | ||||
| 
 | ||||
|             return visibleRatio; | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryVisiblePixels; | ||||
|         osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryTotalPixels; | ||||
| 
 | ||||
|         std::map<osg::observer_ptr<osg::Camera>, float> mLastRatio; | ||||
|     }; | ||||
| 
 | ||||
|     /// SunFlashCallback handles fading/scaling of a node depending on occlusion query result. Must be attached as a cull callback.
 | ||||
|     class SunFlashCallback : public OcclusionCallback | ||||
|     { | ||||
|     public: | ||||
|         SunFlashCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal) | ||||
|             : OcclusionCallback(oqnVisible, oqnTotal) | ||||
|             , mGlareView(1.f) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) | ||||
|         { | ||||
|             osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv); | ||||
| 
 | ||||
|             float visibleRatio = getVisibleRatio(cv->getCurrentCamera()); | ||||
| 
 | ||||
|             osg::ref_ptr<osg::StateSet> stateset; | ||||
| 
 | ||||
|             if (visibleRatio > 0.f) | ||||
|             { | ||||
|                 const float fadeThreshold = 0.1; | ||||
|                 if (visibleRatio < fadeThreshold) | ||||
|                 { | ||||
|                     float fade = 1.f - (fadeThreshold - visibleRatio) / fadeThreshold; | ||||
|                     osg::ref_ptr<osg::Material> mat (createUnlitMaterial()); | ||||
|                     mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade*mGlareView)); | ||||
|                     stateset = new osg::StateSet; | ||||
|                     stateset->setAttributeAndModes(mat, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); | ||||
|                 } | ||||
| 
 | ||||
|                 const float threshold = 0.6; | ||||
|                 visibleRatio = visibleRatio * (1.f - threshold) + threshold; | ||||
|             } | ||||
| 
 | ||||
|             float scale = visibleRatio; | ||||
| 
 | ||||
|             if (scale == 0.f) | ||||
|             { | ||||
|                 // no traverse
 | ||||
|                 return; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 osg::Matrix modelView = *cv->getModelViewMatrix(); | ||||
| 
 | ||||
|                 modelView.preMultScale(osg::Vec3f(visibleRatio, visibleRatio, visibleRatio)); | ||||
| 
 | ||||
|                 if (stateset) | ||||
|                     cv->pushStateSet(stateset); | ||||
| 
 | ||||
|                 cv->pushModelViewMatrix(new osg::RefMatrix(modelView), osg::Transform::RELATIVE_RF); | ||||
| 
 | ||||
|                 traverse(node, nv); | ||||
| 
 | ||||
|                 cv->popModelViewMatrix(); | ||||
| 
 | ||||
|                 if (stateset) | ||||
|                     cv->popStateSet(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         void setGlareView(float value) | ||||
|         { | ||||
|             mGlareView = value; | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         float mGlareView; | ||||
|     }; | ||||
| 
 | ||||
| 
 | ||||
|     /// SunGlareCallback controls a full-screen glare effect depending on occlusion query result and the angle between sun and camera.
 | ||||
|     /// Must be attached as a cull callback to the node above the glare node.
 | ||||
|     class SunGlareCallback : public OcclusionCallback | ||||
|     { | ||||
|     public: | ||||
|         SunGlareCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal, | ||||
|                          osg::ref_ptr<osg::PositionAttitudeTransform> sunTransform) | ||||
|             : OcclusionCallback(oqnVisible, oqnTotal) | ||||
|             , mSunTransform(sunTransform) | ||||
|             , mTimeOfDayFade(1.f) | ||||
|             , mGlareView(1.f) | ||||
|         { | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         virtual void operator ()(osg::Node* node, osg::NodeVisitor* nv) | ||||
|         { | ||||
|             osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv); | ||||
| 
 | ||||
|             float angleRadians = getAngleToSunInRadians(cv->getCurrentCamera()); | ||||
|             float visibleRatio = getVisibleRatio(cv->getCurrentCamera()); | ||||
| 
 | ||||
|             const float angleMaxRadians = osg::DegreesToRadians(30.f); // Sun Glare Fader Angle Max
 | ||||
| 
 | ||||
|             float value = 1.f - std::min(1.f, angleRadians / angleMaxRadians); | ||||
| 
 | ||||
|             const float sunGlareFaderMax = 0.5f; | ||||
|             float fade = value * sunGlareFaderMax; | ||||
| 
 | ||||
|             fade *= mTimeOfDayFade * mGlareView * visibleRatio; | ||||
| 
 | ||||
|             if (fade == 0.f) | ||||
|             { | ||||
|                 // no traverse
 | ||||
|                 return; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet); | ||||
| 
 | ||||
|                 osg::ref_ptr<osg::Material> mat (createUnlitMaterial()); | ||||
| 
 | ||||
|                 osg::Vec4f sunGlareFaderColor (222/255.f, 95/255.f, 39/255.f, 1); | ||||
| 
 | ||||
|                 // Replicating a design flaw in MW. The color was being set on both ambient and emissive properties, which multiplies the result by two,
 | ||||
|                 // then finally gets clamped by the fixed function pipeline. With the default INI settings, only the red component gets clamped,
 | ||||
|                 // so the resulting color looks more orange than red.
 | ||||
|                 sunGlareFaderColor *= 2; | ||||
|                 for (int i=0; i<3; ++i) | ||||
|                     sunGlareFaderColor[i] = std::min(1.f, sunGlareFaderColor[i]); | ||||
| 
 | ||||
|                 mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade)); | ||||
|                 mat->setEmission(osg::Material::FRONT_AND_BACK, sunGlareFaderColor); | ||||
| 
 | ||||
|                 stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); | ||||
| 
 | ||||
|                 cv->pushStateSet(stateset); | ||||
|                 traverse(node, nv); | ||||
|                 cv->popStateSet(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         void setTimeOfDayFade(float val) | ||||
|         { | ||||
|             mTimeOfDayFade = val; | ||||
|         } | ||||
| 
 | ||||
|         void setGlareView(float glareView) | ||||
|         { | ||||
|             mGlareView = glareView; | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         float getAngleToSunInRadians(osg::Camera* camera) const | ||||
|         { | ||||
|             osg::Vec3d eye, center, up; | ||||
|             camera->getViewMatrixAsLookAt(eye, center, up); | ||||
| 
 | ||||
|             osg::Vec3d forward = center - eye; | ||||
|             osg::Vec3d sun = mSunTransform->getPosition(); | ||||
| 
 | ||||
|             forward.normalize(); | ||||
|             sun.normalize(); | ||||
|             float angleRadians = std::acos(forward * sun); | ||||
|             return angleRadians; | ||||
|         } | ||||
| 
 | ||||
|         osg::ref_ptr<osg::PositionAttitudeTransform> mSunTransform; | ||||
|         float mTimeOfDayFade; | ||||
|         float mGlareView; | ||||
|     }; | ||||
| 
 | ||||
|     osg::ref_ptr<Updater> mUpdater; | ||||
|     osg::ref_ptr<SunFlashCallback> mSunFlashCallback; | ||||
|     osg::ref_ptr<osg::Node> mSunFlashNode; | ||||
|     osg::ref_ptr<SunGlareCallback> mSunGlareCallback; | ||||
|     osg::ref_ptr<osg::Node> mSunGlareNode; | ||||
|     osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryVisiblePixels; | ||||
|     osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryTotalPixels; | ||||
| }; | ||||
| 
 | ||||
| class Moon : public CelestialBody | ||||
|  | @ -608,8 +1005,6 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana | |||
|     , mCloudSpeed(0.0f) | ||||
|     , mStarsOpacity(0.0f) | ||||
|     , mRemainingTransitionTime(0.0f) | ||||
|     , mGlare(0.0f) | ||||
|     , mGlareFade(0.0f) | ||||
|     , mRainEnabled(false) | ||||
|     , mRainSpeed(0) | ||||
|     , mRainFrequency(1) | ||||
|  | @ -624,7 +1019,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana | |||
|     mRootNode = skyroot; | ||||
| 
 | ||||
|     // By default render before the world is rendered
 | ||||
|     mRootNode->getOrCreateStateSet()->setRenderBinDetails(-1, "RenderBin"); | ||||
|     mRootNode->getOrCreateStateSet()->setRenderBinDetails(RenderBin_Sky, "RenderBin"); | ||||
| } | ||||
| 
 | ||||
| void SkyManager::create() | ||||
|  | @ -926,32 +1321,6 @@ void SkyManager::update(float duration) | |||
|     mCloudUpdater->setAnimationTimer(mCloudAnimationTimer); | ||||
|     mCloudUpdater2->setAnimationTimer(mCloudAnimationTimer); | ||||
| 
 | ||||
|     if (mSunEnabled) | ||||
|     { | ||||
|         // take 1/10 sec for fading the glare effect from invisible to full
 | ||||
|         if (mGlareFade > mGlare) | ||||
|         { | ||||
|             mGlareFade -= duration*10; | ||||
|             if (mGlareFade < mGlare) mGlareFade = mGlare; | ||||
|         } | ||||
|         else if (mGlareFade < mGlare) | ||||
|         { | ||||
|             mGlareFade += duration*10; | ||||
|             if (mGlareFade > mGlare) mGlareFade = mGlare; | ||||
|         } | ||||
| 
 | ||||
|         // increase the strength of the sun glare effect depending
 | ||||
|         // on how directly the player is looking at the sun
 | ||||
|         /*
 | ||||
|         Vector3 sun = mSunGlare->getPosition(); | ||||
|         Vector3 cam = mCamera->getRealDirection(); | ||||
|         const Degree angle = sun.angleBetween( cam ); | ||||
|         float val = 1- (angle.valueDegrees() / 180.f); | ||||
|         val = (val*val*val*val)*6; | ||||
|         mSunGlare->setSize(val * mGlareFade); | ||||
|         */ | ||||
|     } | ||||
| 
 | ||||
|     // rotate the stars by 360 degrees every 4 days
 | ||||
|     mAtmosphereNightRoll += MWBase::Environment::get().getWorld()->getTimeScaleFactor()*duration*osg::DegreesToRadians(360.f) / (3600*96.f); | ||||
|     if (mAtmosphereNightNode->getNodeMask() != 0) | ||||
|  | @ -1102,7 +1471,9 @@ void SkyManager::setWeather(const WeatherResult& weather) | |||
| 
 | ||||
|     mMasser->adjustTransparency(weather.mGlareView); | ||||
|     mSecunda->adjustTransparency(weather.mGlareView); | ||||
|     mSun->adjustTransparency(weather.mGlareView); | ||||
| 
 | ||||
|     mSun->setColor(weather.mSunDiscColor); | ||||
|     mSun->adjustTransparency(weather.mGlareView * weather.mSunDiscColor.a()); | ||||
| 
 | ||||
|     float nextStarsOpacity = weather.mNightFade * weather.mGlareView; | ||||
|     if(weather.mNight && mStarsOpacity != nextStarsOpacity) | ||||
|  | @ -1114,31 +1485,12 @@ void SkyManager::setWeather(const WeatherResult& weather) | |||
| 
 | ||||
|     mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0 : 0); | ||||
| 
 | ||||
| 
 | ||||
|     /*
 | ||||
|     float strength; | ||||
|     float timeofday_angle = std::abs(mSunGlare->getPosition().z/mSunGlare->getPosition().length()); | ||||
|     if (timeofday_angle <= 0.44) | ||||
|         strength = timeofday_angle/0.44f; | ||||
|     else | ||||
|         strength = 1.f; | ||||
| 
 | ||||
|     mSunGlare->setVisibility(weather.mGlareView * mGlareFade * strength); | ||||
| 
 | ||||
|     mSun->setVisibility(weather.mGlareView * strength); | ||||
|     */ | ||||
| 
 | ||||
|     if (mRainFader) | ||||
|         mRainFader->setAlpha(weather.mEffectFade * 0.6); // * Rain_Threshold?
 | ||||
|     if (mParticleFader) | ||||
|         mParticleFader->setAlpha(weather.mEffectFade); | ||||
| } | ||||
| 
 | ||||
| void SkyManager::setGlare(const float glare) | ||||
| { | ||||
|     mGlare = glare; | ||||
| } | ||||
| 
 | ||||
| void SkyManager::sunEnable() | ||||
| { | ||||
|     if (!mCreated) return; | ||||
|  | @ -1163,8 +1515,6 @@ void SkyManager::setSunDirection(const osg::Vec3f& direction) | |||
|     if (!mCreated) return; | ||||
| 
 | ||||
|     mSun->setDirection(direction); | ||||
| 
 | ||||
|     //mSunGlare->setPosition(direction);
 | ||||
| } | ||||
| 
 | ||||
| void SkyManager::setMasserState(const MoonState& state) | ||||
|  | @ -1187,11 +1537,9 @@ void SkyManager::setDate(int day, int month) | |||
|     mMonth = month; | ||||
| } | ||||
| 
 | ||||
| void SkyManager::setGlareEnabled (bool enabled) | ||||
| void SkyManager::setGlareTimeOfDayFade(float val) | ||||
| { | ||||
|     if (!mCreated || !mEnabled) | ||||
|         return; | ||||
|     //mSunGlare->setVisible (mSunEnabled && enabled);
 | ||||
|     mSun->setGlareTimeOfDayFade(val); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -48,8 +48,10 @@ namespace MWRender | |||
| 
 | ||||
|         osg::Vec4f mSkyColor; | ||||
| 
 | ||||
|         // sun light color
 | ||||
|         osg::Vec4f mSunColor; | ||||
| 
 | ||||
|         // alpha is the sun transparency
 | ||||
|         osg::Vec4f mSunDiscColor; | ||||
| 
 | ||||
|         float mFogDepth; | ||||
|  | @ -140,8 +142,7 @@ namespace MWRender | |||
|         void setMasserState(const MoonState& state); | ||||
|         void setSecundaState(const MoonState& state); | ||||
| 
 | ||||
|         void setGlare(const float glare); | ||||
|         void setGlareEnabled(bool enabled); | ||||
|         void setGlareTimeOfDayFade(float val); | ||||
| 
 | ||||
|     private: | ||||
|         void create(); | ||||
|  | @ -210,9 +211,6 @@ namespace MWRender | |||
| 
 | ||||
|         float mRemainingTransitionTime; | ||||
| 
 | ||||
|         float mGlare; // target
 | ||||
|         float mGlareFade; // actual
 | ||||
| 
 | ||||
|         bool mRainEnabled; | ||||
|         std::string mRainEffect; | ||||
|         float mRainSpeed; | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
| 
 | ||||
| #include "vismask.hpp" | ||||
| #include "ripplesimulation.hpp" | ||||
| #include "renderbin.hpp" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|  | @ -86,7 +87,7 @@ namespace | |||
|         depth->setWriteMask(false); | ||||
|         stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); | ||||
| 
 | ||||
|         stateset->setRenderBinDetails(9, "RenderBin"); | ||||
|         stateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin"); | ||||
| 
 | ||||
|         std::vector<osg::ref_ptr<osg::Texture2D> > textures; | ||||
|         for (int i=0; i<32; ++i) | ||||
|  |  | |||
|  | @ -432,6 +432,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const MWWo | |||
|     , mSunsetTime(fallback.getFallbackFloat("Weather_Sunset_Time")) | ||||
|     , mSunriseDuration(fallback.getFallbackFloat("Weather_Sunrise_Duration")) | ||||
|     , mSunsetDuration(fallback.getFallbackFloat("Weather_Sunset_Duration")) | ||||
|     , mSunPreSunsetTime(fallback.getFallbackFloat("Weather_Sun_Pre-Sunset_Time")) | ||||
|     , mNightStart(mSunsetTime + mSunsetDuration) | ||||
|     , mNightEnd(mSunriseTime - 0.5f) | ||||
|     , mDayStart(mSunriseTime + mSunriseDuration) | ||||
|  | @ -458,16 +459,16 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const MWWo | |||
|     , mPlayingSoundID() | ||||
| { | ||||
|     mWeatherSettings.reserve(10); | ||||
|     addWeather("Clear", fallback); | ||||
|     addWeather("Cloudy", fallback); | ||||
|     addWeather("Foggy", fallback); | ||||
|     addWeather("Overcast", fallback); | ||||
|     addWeather("Rain", fallback, "rain"); | ||||
|     addWeather("Thunderstorm", fallback, "rain heavy"); | ||||
|     addWeather("Ashstorm", fallback, "ashstorm", "meshes\\ashcloud.nif"); | ||||
|     addWeather("Blight", fallback, "blight", "meshes\\blightcloud.nif"); | ||||
|     addWeather("Snow", fallback, "", "meshes\\snow.nif"); | ||||
|     addWeather("Blizzard", fallback, "BM Blizzard", "meshes\\blizzard.nif"); | ||||
|     addWeather("Clear", fallback); // 0
 | ||||
|     addWeather("Cloudy", fallback); // 1
 | ||||
|     addWeather("Foggy", fallback); // 2
 | ||||
|     addWeather("Overcast", fallback); // 3
 | ||||
|     addWeather("Rain", fallback, "rain"); // 4
 | ||||
|     addWeather("Thunderstorm", fallback, "rain heavy"); // 5
 | ||||
|     addWeather("Ashstorm", fallback, "ashstorm", "meshes\\ashcloud.nif"); // 6
 | ||||
|     addWeather("Blight", fallback, "blight", "meshes\\blightcloud.nif"); // 7
 | ||||
|     addWeather("Snow", fallback, "", "meshes\\snow.nif"); // 8
 | ||||
|     addWeather("Blizzard", fallback, "BM Blizzard", "meshes\\blizzard.nif"); // 9
 | ||||
| 
 | ||||
|     Store<ESM::Region>::iterator it = store.get<ESM::Region>().begin(); | ||||
|     for(; it != store.get<ESM::Region>().end(); ++it) | ||||
|  | @ -624,6 +625,14 @@ void WeatherManager::update(float duration, bool paused) | |||
|         mRendering.setSunDirection( final * -1 ); | ||||
|     } | ||||
| 
 | ||||
|     float peakHour = mSunriseTime + (mSunsetTime - mSunriseTime) / 2; | ||||
|     if (time.getHour() < mSunriseTime || time.getHour() > mSunsetTime) | ||||
|         mRendering.getSkyManager()->setGlareTimeOfDayFade(0); | ||||
|     else if (time.getHour() < peakHour) | ||||
|         mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (peakHour - time.getHour()) / (peakHour - mSunriseTime)); | ||||
|     else | ||||
|         mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (time.getHour() - peakHour) / (mSunsetTime - peakHour)); | ||||
| 
 | ||||
|     mRendering.getSkyManager()->setMasserState(mMasser.calculateState(time)); | ||||
|     mRendering.getSkyManager()->setSecundaState(mSecunda.calculateState(time)); | ||||
| 
 | ||||
|  | @ -958,7 +967,6 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam | |||
|     mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; | ||||
|     mResult.mAmbientSoundVolume = 1.f; | ||||
|     mResult.mEffectFade = 1.f; | ||||
|     mResult.mSunColor = current.mSunDiscSunsetColor; | ||||
| 
 | ||||
|     mResult.mIsStorm = current.mIsStorm; | ||||
| 
 | ||||
|  | @ -972,6 +980,7 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam | |||
| 
 | ||||
|     mResult.mFogDepth = mResult.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth; | ||||
| 
 | ||||
|     // TODO: use pre/post sunset/sunrise time values in [Weather] section
 | ||||
|     // night
 | ||||
|     if (gameHour <= mNightEnd || gameHour >= mNightStart + 1) | ||||
|     { | ||||
|  | @ -1042,6 +1051,36 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam | |||
|             mResult.mNightFade = factor; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (gameHour >= mSunsetTime - mSunPreSunsetTime) | ||||
|     { | ||||
|         float factor = (gameHour - (mSunsetTime - mSunPreSunsetTime)) / mSunPreSunsetTime; | ||||
|         factor = std::min(1.f, factor); | ||||
|         mResult.mSunDiscColor = lerp(osg::Vec4f(1,1,1,1), current.mSunDiscSunsetColor, factor); | ||||
|         // The SunDiscSunsetColor in the INI isn't exactly the resulting color on screen, most likely because
 | ||||
|         // MW applied the color to the ambient term as well. After the ambient and emissive terms are added together, the fixed pipeline
 | ||||
|         // would then clamp the total lighting to (1,1,1). A noticable change in color tone can be observed when only one of the color components gets clamped.
 | ||||
|         // Unfortunately that means we can't use the INI color as is, have to replicate the above nonsense.
 | ||||
|         mResult.mSunDiscColor = mResult.mSunDiscColor + osg::componentMultiply(mResult.mSunDiscColor, mResult.mAmbientColor); | ||||
|         for (int i=0; i<3; ++i) | ||||
|             mResult.mSunDiscColor[i] = std::min(1.f, mResult.mSunDiscColor[i]); | ||||
|     } | ||||
|     else | ||||
|         mResult.mSunDiscColor = osg::Vec4f(1,1,1,1); | ||||
| 
 | ||||
|     if (gameHour >= mSunsetTime) | ||||
|     { | ||||
|         float fade = std::min(1.f, (gameHour - mSunsetTime) / 2.f); | ||||
|         fade = fade*fade; | ||||
|         mResult.mSunDiscColor.a() = 1.f - fade; | ||||
|     } | ||||
|     else if (gameHour >= mSunriseTime && gameHour <= mSunriseTime + 1) | ||||
|     { | ||||
|         mResult.mSunDiscColor.a() = gameHour - mSunriseTime; | ||||
|     } | ||||
|     else | ||||
|         mResult.mSunDiscColor.a() = 1; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| inline void WeatherManager::calculateTransitionResult(const float factor, const float gameHour) | ||||
|  |  | |||
|  | @ -75,7 +75,7 @@ namespace MWWorld | |||
|         float mLandFogDayDepth; | ||||
|         float mLandFogNightDepth; | ||||
| 
 | ||||
|         // Color modulation for the sun itself during sunset (not completely sure)
 | ||||
|         // Color modulation for the sun itself during sunset
 | ||||
|         osg::Vec4f mSunDiscSunsetColor; | ||||
| 
 | ||||
|         // Used by scripts to animate signs, etc based on the wind (GetWindSpeed)
 | ||||
|  | @ -242,12 +242,7 @@ namespace MWWorld | |||
|         float mSunsetTime; | ||||
|         float mSunriseDuration; | ||||
|         float mSunsetDuration; | ||||
|         // Some useful values
 | ||||
|         /* TODO: Use pre-sunrise_time, pre-sunset_time,
 | ||||
|          * post-sunrise_time, and post-sunset_time to better | ||||
|          * describe sunrise/sunset time. | ||||
|          * These values are fallbacks attached to weather. | ||||
|          */ | ||||
|         float mSunPreSunsetTime; | ||||
|         float mNightStart; | ||||
|         float mNightEnd; | ||||
|         float mDayStart; | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| namespace SceneUtil | ||||
| { | ||||
| 
 | ||||
|     /// @brief Implements efficient pre-frame updating of StateSets.
 | ||||
|     /// @brief Implements efficient per-frame updating of StateSets.
 | ||||
|     /// @par With a naive update there would be race conditions when the OSG draw thread of the last frame
 | ||||
|     ///     queues up a StateSet that we want to modify for the next frame. To solve this we could set the StateSet to
 | ||||
|     ///     DYNAMIC data variance but that would undo all the benefits of the threading model - having the cull and draw
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue