Merge remote-tracking branch 'origin/master' into sceneinput

sceneinput
Marc Zinnschlag 9 years ago
commit 62047b1868

@ -39,8 +39,6 @@ char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const
std::pair<char, bool> CSMWorld::ScriptContext::getMemberType (const std::string& name, std::pair<char, bool> CSMWorld::ScriptContext::getMemberType (const std::string& name,
const std::string& id) const const std::string& id) const
{ {
/// \todo invalidate locals cache on change to scripts
std::string id2 = Misc::StringUtils::lowerCase (id); std::string id2 = Misc::StringUtils::lowerCase (id);
int index = mData.getScripts().searchId (id2); int index = mData.getScripts().searchId (id2);
@ -120,3 +118,18 @@ void CSMWorld::ScriptContext::clear()
mIdsUpdated = false; mIdsUpdated = false;
mLocals.clear(); 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(); void clear();
///< Remove all cached data. ///< 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() 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; std::ostringstream stream;
stream stream

@ -166,7 +166,9 @@ void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons (
std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction() 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; std::ostringstream stream;

@ -131,6 +131,11 @@ void CSVWorld::ScriptErrorTable::clear()
setRowCount (0); setRowCount (0);
} }
bool CSVWorld::ScriptErrorTable::clearLocals (const std::string& script)
{
return mContext.clearLocals (script);
}
void CSVWorld::ScriptErrorTable::cellClicked (int row, int column) void CSVWorld::ScriptErrorTable::cellClicked (int row, int column)
{ {
if (item (row, 1)) if (item (row, 1))

@ -44,6 +44,11 @@ namespace CSVWorld
void clear(); 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: private slots:
void cellClicked (int row, int column); 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)); *document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts));
mColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_ScriptText); mColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_ScriptText);
mIdColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
mStateColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); mStateColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification);
QString source = mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString(); 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); 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); QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);
if (index.row()>=topLeft.row() && index.row()<=bottomRight.row()) 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->setPlainText (source);
mEditor->setTextCursor (cursor); mEditor->setTextCursor (cursor);
recompile(); updateRequired = true;
} }
} }
if (updateRequired)
recompile();
} }
void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end) 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); QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);
if (!parent.isValid() && index.row()>=start && index.row()<=end) if (!parent.isValid() && index.row()>=start && index.row()<=end)

@ -38,6 +38,7 @@ namespace CSVWorld
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
CSMWorld::IdTable *mModel; CSMWorld::IdTable *mModel;
int mColumn; int mColumn;
int mIdColumn;
int mStateColumn; int mStateColumn;
TableBottomBox *mBottom; TableBottomBox *mBottom;
RecordButtonBar *mButtons; RecordButtonBar *mButtons;

@ -23,6 +23,7 @@ add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
renderbin
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput

@ -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/TexEnvCombine>
#include <osg/TexMat> #include <osg/TexMat>
#include <osg/Version> #include <osg/Version>
#include <osg/OcclusionQueryNode>
#include <osg/ColorMask>
#include <osg/MatrixTransform>
#include <osg/BlendFunc>
#include <osg/AlphaFunc>
#include <osgParticle/ParticleSystem> #include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleSystemUpdater> #include <osgParticle/ParticleSystemUpdater>
@ -38,6 +43,7 @@
#include "../mwworld/fallback.hpp" #include "../mwworld/fallback.hpp"
#include "vismask.hpp" #include "vismask.hpp"
#include "renderbin.hpp"
namespace namespace
{ {
@ -359,19 +365,57 @@ class Sun : public CelestialBody
public: public:
Sun(osg::Group* parentNode, Resource::TextureManager& textureManager) Sun(osg::Group* parentNode, Resource::TextureManager& textureManager)
: CelestialBody(parentNode, 1.0f, 1) : 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() ~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) virtual void adjustTransparency(const float ratio)
{ {
mUpdater->mColor.a() = ratio; mUpdater->mColor.a() = ratio;
if (mSunGlareCallback)
mSunGlareCallback->setGlareView(ratio);
if (mSunFlashCallback)
mSunFlashCallback->setGlareView(ratio);
} }
void setDirection(const osg::Vec3f& direction) void setDirection(const osg::Vec3f& direction)
@ -384,37 +428,390 @@ public:
mTransform->setAttitude(quat); mTransform->setAttitude(quat);
} }
void setGlareTimeOfDayFade(float val)
{
if (mSunGlareCallback)
mSunGlareCallback->setTimeOfDayFade(val);
}
private: private:
struct Updater : public SceneUtil::StateSetUpdater /// @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)
{ {
Resource::TextureManager& mTextureManager; osg::ref_ptr<osg::OcclusionQueryNode> oqn = new osg::OcclusionQueryNode;
osg::Vec4f mColor; oqn->setQueriesEnabled(true);
Updater(Resource::TextureManager& textureManager) // 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,
: mTextureManager(textureManager) // so that would be pretty bad). STATIC should be safe, since our node's local bounds are static, thus computeBounds() which modifies the queryGeometry
, mColor(0.0f, 0.0f, 0.0f, 1.0f) // 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));
} }
virtual void setDefaults(osg::StateSet* stateset) 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 = mTextureManager.getTexture2D("textures/tx_sun_05.dds", osg::ref_ptr<osg::Texture2D> tex = textureManager.getTexture2D("textures/tx_sun_flash_grey_05.dds",
osg::Texture::CLAMP, osg::Texture::CLAMP,
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->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON);
stateset->setAttributeAndModes(createUnlitMaterial(), stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); 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()
: mColor(1.f, 1.f, 1.f, 1.f)
{
}
virtual void setDefaults(osg::StateSet* stateset)
{
stateset->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON);
} }
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor*) virtual void apply(osg::StateSet* stateset, osg::NodeVisitor*)
{ {
osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL)); 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<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 class Moon : public CelestialBody
@ -608,8 +1005,6 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
, mCloudSpeed(0.0f) , mCloudSpeed(0.0f)
, mStarsOpacity(0.0f) , mStarsOpacity(0.0f)
, mRemainingTransitionTime(0.0f) , mRemainingTransitionTime(0.0f)
, mGlare(0.0f)
, mGlareFade(0.0f)
, mRainEnabled(false) , mRainEnabled(false)
, mRainSpeed(0) , mRainSpeed(0)
, mRainFrequency(1) , mRainFrequency(1)
@ -624,7 +1019,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
mRootNode = skyroot; mRootNode = skyroot;
// By default render before the world is rendered // By default render before the world is rendered
mRootNode->getOrCreateStateSet()->setRenderBinDetails(-1, "RenderBin"); mRootNode->getOrCreateStateSet()->setRenderBinDetails(RenderBin_Sky, "RenderBin");
} }
void SkyManager::create() void SkyManager::create()
@ -926,32 +1321,6 @@ void SkyManager::update(float duration)
mCloudUpdater->setAnimationTimer(mCloudAnimationTimer); mCloudUpdater->setAnimationTimer(mCloudAnimationTimer);
mCloudUpdater2->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 // rotate the stars by 360 degrees every 4 days
mAtmosphereNightRoll += MWBase::Environment::get().getWorld()->getTimeScaleFactor()*duration*osg::DegreesToRadians(360.f) / (3600*96.f); mAtmosphereNightRoll += MWBase::Environment::get().getWorld()->getTimeScaleFactor()*duration*osg::DegreesToRadians(360.f) / (3600*96.f);
if (mAtmosphereNightNode->getNodeMask() != 0) if (mAtmosphereNightNode->getNodeMask() != 0)
@ -1102,7 +1471,9 @@ void SkyManager::setWeather(const WeatherResult& weather)
mMasser->adjustTransparency(weather.mGlareView); mMasser->adjustTransparency(weather.mGlareView);
mSecunda->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; float nextStarsOpacity = weather.mNightFade * weather.mGlareView;
if(weather.mNight && mStarsOpacity != nextStarsOpacity) if(weather.mNight && mStarsOpacity != nextStarsOpacity)
@ -1114,31 +1485,12 @@ void SkyManager::setWeather(const WeatherResult& weather)
mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0 : 0); 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) if (mRainFader)
mRainFader->setAlpha(weather.mEffectFade * 0.6); // * Rain_Threshold? mRainFader->setAlpha(weather.mEffectFade * 0.6); // * Rain_Threshold?
if (mParticleFader) if (mParticleFader)
mParticleFader->setAlpha(weather.mEffectFade); mParticleFader->setAlpha(weather.mEffectFade);
} }
void SkyManager::setGlare(const float glare)
{
mGlare = glare;
}
void SkyManager::sunEnable() void SkyManager::sunEnable()
{ {
if (!mCreated) return; if (!mCreated) return;
@ -1163,8 +1515,6 @@ void SkyManager::setSunDirection(const osg::Vec3f& direction)
if (!mCreated) return; if (!mCreated) return;
mSun->setDirection(direction); mSun->setDirection(direction);
//mSunGlare->setPosition(direction);
} }
void SkyManager::setMasserState(const MoonState& state) void SkyManager::setMasserState(const MoonState& state)
@ -1187,11 +1537,9 @@ void SkyManager::setDate(int day, int month)
mMonth = month; mMonth = month;
} }
void SkyManager::setGlareEnabled (bool enabled) void SkyManager::setGlareTimeOfDayFade(float val)
{ {
if (!mCreated || !mEnabled) mSun->setGlareTimeOfDayFade(val);
return;
//mSunGlare->setVisible (mSunEnabled && enabled);
} }
} }

@ -48,8 +48,10 @@ namespace MWRender
osg::Vec4f mSkyColor; osg::Vec4f mSkyColor;
// sun light color
osg::Vec4f mSunColor; osg::Vec4f mSunColor;
// alpha is the sun transparency
osg::Vec4f mSunDiscColor; osg::Vec4f mSunDiscColor;
float mFogDepth; float mFogDepth;
@ -140,8 +142,7 @@ namespace MWRender
void setMasserState(const MoonState& state); void setMasserState(const MoonState& state);
void setSecundaState(const MoonState& state); void setSecundaState(const MoonState& state);
void setGlare(const float glare); void setGlareTimeOfDayFade(float val);
void setGlareEnabled(bool enabled);
private: private:
void create(); void create();
@ -210,9 +211,6 @@ namespace MWRender
float mRemainingTransitionTime; float mRemainingTransitionTime;
float mGlare; // target
float mGlareFade; // actual
bool mRainEnabled; bool mRainEnabled;
std::string mRainEffect; std::string mRainEffect;
float mRainSpeed; float mRainSpeed;

@ -21,6 +21,7 @@
#include "vismask.hpp" #include "vismask.hpp"
#include "ripplesimulation.hpp" #include "ripplesimulation.hpp"
#include "renderbin.hpp"
namespace namespace
{ {
@ -86,7 +87,7 @@ namespace
depth->setWriteMask(false); depth->setWriteMask(false);
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
stateset->setRenderBinDetails(9, "RenderBin"); stateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin");
std::vector<osg::ref_ptr<osg::Texture2D> > textures; std::vector<osg::ref_ptr<osg::Texture2D> > textures;
for (int i=0; i<32; ++i) for (int i=0; i<32; ++i)

@ -432,6 +432,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const MWWo
, mSunsetTime(fallback.getFallbackFloat("Weather_Sunset_Time")) , mSunsetTime(fallback.getFallbackFloat("Weather_Sunset_Time"))
, mSunriseDuration(fallback.getFallbackFloat("Weather_Sunrise_Duration")) , mSunriseDuration(fallback.getFallbackFloat("Weather_Sunrise_Duration"))
, mSunsetDuration(fallback.getFallbackFloat("Weather_Sunset_Duration")) , mSunsetDuration(fallback.getFallbackFloat("Weather_Sunset_Duration"))
, mSunPreSunsetTime(fallback.getFallbackFloat("Weather_Sun_Pre-Sunset_Time"))
, mNightStart(mSunsetTime + mSunsetDuration) , mNightStart(mSunsetTime + mSunsetDuration)
, mNightEnd(mSunriseTime - 0.5f) , mNightEnd(mSunriseTime - 0.5f)
, mDayStart(mSunriseTime + mSunriseDuration) , mDayStart(mSunriseTime + mSunriseDuration)
@ -458,16 +459,16 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const MWWo
, mPlayingSoundID() , mPlayingSoundID()
{ {
mWeatherSettings.reserve(10); mWeatherSettings.reserve(10);
addWeather("Clear", fallback); addWeather("Clear", fallback); // 0
addWeather("Cloudy", fallback); addWeather("Cloudy", fallback); // 1
addWeather("Foggy", fallback); addWeather("Foggy", fallback); // 2
addWeather("Overcast", fallback); addWeather("Overcast", fallback); // 3
addWeather("Rain", fallback, "rain"); addWeather("Rain", fallback, "rain"); // 4
addWeather("Thunderstorm", fallback, "rain heavy"); addWeather("Thunderstorm", fallback, "rain heavy"); // 5
addWeather("Ashstorm", fallback, "ashstorm", "meshes\\ashcloud.nif"); addWeather("Ashstorm", fallback, "ashstorm", "meshes\\ashcloud.nif"); // 6
addWeather("Blight", fallback, "blight", "meshes\\blightcloud.nif"); addWeather("Blight", fallback, "blight", "meshes\\blightcloud.nif"); // 7
addWeather("Snow", fallback, "", "meshes\\snow.nif"); addWeather("Snow", fallback, "", "meshes\\snow.nif"); // 8
addWeather("Blizzard", fallback, "BM Blizzard", "meshes\\blizzard.nif"); addWeather("Blizzard", fallback, "BM Blizzard", "meshes\\blizzard.nif"); // 9
Store<ESM::Region>::iterator it = store.get<ESM::Region>().begin(); Store<ESM::Region>::iterator it = store.get<ESM::Region>().begin();
for(; it != store.get<ESM::Region>().end(); ++it) for(; it != store.get<ESM::Region>().end(); ++it)
@ -624,6 +625,14 @@ void WeatherManager::update(float duration, bool paused)
mRendering.setSunDirection( final * -1 ); 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()->setMasserState(mMasser.calculateState(time));
mRendering.getSkyManager()->setSecundaState(mSecunda.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.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
mResult.mAmbientSoundVolume = 1.f; mResult.mAmbientSoundVolume = 1.f;
mResult.mEffectFade = 1.f; mResult.mEffectFade = 1.f;
mResult.mSunColor = current.mSunDiscSunsetColor;
mResult.mIsStorm = current.mIsStorm; 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; mResult.mFogDepth = mResult.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth;
// TODO: use pre/post sunset/sunrise time values in [Weather] section
// night // night
if (gameHour <= mNightEnd || gameHour >= mNightStart + 1) if (gameHour <= mNightEnd || gameHour >= mNightStart + 1)
{ {
@ -1042,6 +1051,36 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam
mResult.mNightFade = factor; 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) inline void WeatherManager::calculateTransitionResult(const float factor, const float gameHour)

@ -75,7 +75,7 @@ namespace MWWorld
float mLandFogDayDepth; float mLandFogDayDepth;
float mLandFogNightDepth; float mLandFogNightDepth;
// Color modulation for the sun itself during sunset (not completely sure) // Color modulation for the sun itself during sunset
osg::Vec4f mSunDiscSunsetColor; osg::Vec4f mSunDiscSunsetColor;
// Used by scripts to animate signs, etc based on the wind (GetWindSpeed) // Used by scripts to animate signs, etc based on the wind (GetWindSpeed)
@ -242,12 +242,7 @@ namespace MWWorld
float mSunsetTime; float mSunsetTime;
float mSunriseDuration; float mSunriseDuration;
float mSunsetDuration; float mSunsetDuration;
// Some useful values float mSunPreSunsetTime;
/* 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 mNightStart; float mNightStart;
float mNightEnd; float mNightEnd;
float mDayStart; float mDayStart;

@ -6,7 +6,7 @@
namespace SceneUtil 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 /// @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 /// 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 /// DYNAMIC data variance but that would undo all the benefits of the threading model - having the cull and draw

Loading…
Cancel
Save