mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-25 15:35:23 +00:00
Merge remote-tracking branch 'origin/master' into sceneinput
This commit is contained in:
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,
|
||||
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…
x
Reference in New Issue
Block a user