summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpsi29a <psi29a@gmail.com>2024-02-26 11:21:13 +0000
committerpsi29a <psi29a@gmail.com>2024-02-26 11:21:13 +0000
commit051f3dcd56c072e3b7579eac6b3c8a8ba2c1bc3f (patch)
tree2d5868bcdd1544cecd167a092c5ef75f6cdab41c
parent357b1905714f71a0ed23f18fb3008f9f1fd0e9ad (diff)
parentc9b4c8632a4bcddb260324bdc53b747c23d7c881 (diff)
Merge branch 'fps_independent_water_ripples' into 'master'
Make water ripples simulation FPS independent (#7687) Closes #7687 See merge request OpenMW/openmw!3885
-rw-r--r--apps/openmw/mwrender/ripples.cpp148
-rw-r--r--apps/openmw/mwrender/ripples.hpp25
-rw-r--r--apps/openmw/mwrender/water.cpp4
3 files changed, 98 insertions, 79 deletions
diff --git a/apps/openmw/mwrender/ripples.cpp b/apps/openmw/mwrender/ripples.cpp
index dea372666e..94135eeec5 100644
--- a/apps/openmw/mwrender/ripples.cpp
+++ b/apps/openmw/mwrender/ripples.cpp
@@ -63,7 +63,7 @@ namespace MWRender
stateset->addUniform(new osg::Uniform("offset", osg::Vec2f()));
stateset->addUniform(new osg::Uniform("positionCount", 0));
stateset->addUniform(new osg::Uniform(osg::Uniform::Type::FLOAT_VEC3, "positions", 100));
- stateset->setAttributeAndModes(new osg::Viewport(0, 0, RipplesSurface::mRTTSize, RipplesSurface::mRTTSize));
+ stateset->setAttributeAndModes(new osg::Viewport(0, 0, RipplesSurface::sRTTSize, RipplesSurface::sRTTSize));
mState[i].mStateset = stateset;
}
@@ -78,7 +78,7 @@ namespace MWRender
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER);
texture->setBorderColor(osg::Vec4(0, 0, 0, 0));
- texture->setTextureSize(mRTTSize, mRTTSize);
+ texture->setTextureSize(sRTTSize, sRTTSize);
mTextures[i] = texture;
@@ -99,7 +99,7 @@ namespace MWRender
{
auto& shaderManager = mResourceSystem->getSceneManager()->getShaderManager();
- Shader::ShaderManager::DefineMap defineMap = { { "ripple_map_size", std::to_string(mRTTSize) + ".0" } };
+ Shader::ShaderManager::DefineMap defineMap = { { "ripple_map_size", std::to_string(sRTTSize) + ".0" } };
osg::ref_ptr<osg::Shader> vertex = shaderManager.getShader("fullscreen_tri.vert", {}, osg::Shader::VERTEX);
@@ -119,59 +119,83 @@ namespace MWRender
nullptr, shaderManager.getShader("core/ripples_simulate.comp", {}, osg::Shader::COMPUTE));
}
- void RipplesSurface::traverse(osg::NodeVisitor& nv)
+ void RipplesSurface::updateState(const osg::FrameStamp& frameStamp, State& state)
{
- if (!nv.getFrameStamp())
+ state.mPaused = mPaused;
+
+ if (mPaused)
return;
- if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
+ constexpr double updateFrequency = 60.0;
+ constexpr double updatePeriod = 1.0 / updateFrequency;
+
+ const double simulationTime = frameStamp.getSimulationTime();
+ const double frameDuration = simulationTime - mLastSimulationTime;
+ mLastSimulationTime = simulationTime;
+
+ mRemainingWaveTime += frameDuration;
+ const double ticks = std::floor(mRemainingWaveTime * updateFrequency);
+ mRemainingWaveTime -= ticks * updatePeriod;
+
+ if (ticks == 0)
{
- size_t frameId = nv.getFrameStamp()->getFrameNumber() % 2;
+ state.mPaused = true;
+ return;
+ }
- const auto& player = MWMechanics::getPlayer();
- const ESM::Position& playerPos = player.getRefData().getPosition();
+ const MWWorld::Ptr player = MWMechanics::getPlayer();
+ const ESM::Position& playerPos = player.getRefData().getPosition();
- mCurrentPlayerPos = osg::Vec2f(
- std::floor(playerPos.pos[0] / mWorldScaleFactor), std::floor(playerPos.pos[1] / mWorldScaleFactor));
- osg::Vec2f offset = mCurrentPlayerPos - mLastPlayerPos;
- mLastPlayerPos = mCurrentPlayerPos;
- mState[frameId].mPaused = mPaused;
- mState[frameId].mOffset = offset;
- mState[frameId].mStateset->getUniform("positionCount")->set(static_cast<int>(mPositionCount));
- mState[frameId].mStateset->getUniform("offset")->set(offset);
+ mCurrentPlayerPos = osg::Vec2f(
+ std::floor(playerPos.pos[0] / sWorldScaleFactor), std::floor(playerPos.pos[1] / sWorldScaleFactor));
+ const osg::Vec2f offset = mCurrentPlayerPos - mLastPlayerPos;
+ mLastPlayerPos = mCurrentPlayerPos;
- auto* positions = mState[frameId].mStateset->getUniform("positions");
+ state.mStateset->getUniform("positionCount")->set(static_cast<int>(mPositionCount));
+ state.mStateset->getUniform("offset")->set(offset);
- for (size_t i = 0; i < mPositionCount; ++i)
- {
- osg::Vec3f pos = mPositions[i]
- - osg::Vec3f(
- mCurrentPlayerPos.x() * mWorldScaleFactor, mCurrentPlayerPos.y() * mWorldScaleFactor, 0.0)
- + osg::Vec3f(mRTTSize * mWorldScaleFactor / 2, mRTTSize * mWorldScaleFactor / 2, 0.0);
- pos /= mWorldScaleFactor;
- positions->setElement(i, pos);
- }
- positions->dirty();
+ osg::Uniform* const positions = state.mStateset->getUniform("positions");
- mPositionCount = 0;
+ for (std::size_t i = 0; i < mPositionCount; ++i)
+ {
+ osg::Vec3f pos = mPositions[i]
+ - osg::Vec3f(mCurrentPlayerPos.x() * sWorldScaleFactor, mCurrentPlayerPos.y() * sWorldScaleFactor, 0.0)
+ + osg::Vec3f(sRTTSize * sWorldScaleFactor / 2, sRTTSize * sWorldScaleFactor / 2, 0.0);
+ pos /= sWorldScaleFactor;
+ positions->setElement(i, pos);
}
+ positions->dirty();
+
+ mPositionCount = 0;
+ }
+
+ void RipplesSurface::traverse(osg::NodeVisitor& nv)
+ {
+ const osg::FrameStamp* const frameStamp = nv.getFrameStamp();
+
+ if (frameStamp == nullptr)
+ return;
+
+ if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
+ updateState(*frameStamp, mState[frameStamp->getFrameNumber() % 2]);
+
osg::Geometry::traverse(nv);
}
void RipplesSurface::drawImplementation(osg::RenderInfo& renderInfo) const
{
osg::State& state = *renderInfo.getState();
- osg::GLExtensions& ext = *state.get<osg::GLExtensions>();
- size_t contextID = state.getContextID();
-
- size_t currentFrame = state.getFrameStamp()->getFrameNumber() % 2;
+ const std::size_t currentFrame = state.getFrameStamp()->getFrameNumber() % 2;
const State& frameState = mState[currentFrame];
if (frameState.mPaused)
{
return;
}
- auto bindImage = [contextID, &state, &ext](osg::Texture2D* texture, GLuint index, GLenum access) {
+ osg::GLExtensions& ext = *state.get<osg::GLExtensions>();
+ const std::size_t contextID = state.getContextID();
+
+ const auto bindImage = [&](osg::Texture2D* texture, GLuint index, GLenum access) {
osg::Texture::TextureObject* to = texture->getTextureObject(contextID);
if (!to || texture->isDirty(contextID))
{
@@ -181,52 +205,42 @@ namespace MWRender
ext.glBindImageTexture(index, to->id(), 0, GL_FALSE, 0, access, GL_RGBA16F);
};
- // Run simulation at a fixed rate independent on current FPS
- // FIXME: when we skip frames we need to preserve positions. this doesn't work now
- size_t ticks = 1;
-
// PASS: Blot in all ripple spawners
mProgramBlobber->apply(state);
state.apply(frameState.mStateset);
- for (size_t i = 0; i < ticks; i++)
+ if (mUseCompute)
{
- if (mUseCompute)
- {
- bindImage(mTextures[1], 0, GL_WRITE_ONLY_ARB);
- bindImage(mTextures[0], 1, GL_READ_ONLY_ARB);
+ bindImage(mTextures[1], 0, GL_WRITE_ONLY_ARB);
+ bindImage(mTextures[0], 1, GL_READ_ONLY_ARB);
- ext.glDispatchCompute(mRTTSize / 16, mRTTSize / 16, 1);
- ext.glMemoryBarrier(GL_ALL_BARRIER_BITS);
- }
- else
- {
- mFBOs[1]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
- state.applyTextureAttribute(0, mTextures[0]);
- osg::Geometry::drawImplementation(renderInfo);
- }
+ ext.glDispatchCompute(sRTTSize / 16, sRTTSize / 16, 1);
+ ext.glMemoryBarrier(GL_ALL_BARRIER_BITS);
+ }
+ else
+ {
+ mFBOs[1]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
+ state.applyTextureAttribute(0, mTextures[0]);
+ osg::Geometry::drawImplementation(renderInfo);
}
// PASS: Wave simulation
mProgramSimulation->apply(state);
state.apply(frameState.mStateset);
- for (size_t i = 0; i < ticks; i++)
+ if (mUseCompute)
{
- if (mUseCompute)
- {
- bindImage(mTextures[0], 0, GL_WRITE_ONLY_ARB);
- bindImage(mTextures[1], 1, GL_READ_ONLY_ARB);
+ bindImage(mTextures[0], 0, GL_WRITE_ONLY_ARB);
+ bindImage(mTextures[1], 1, GL_READ_ONLY_ARB);
- ext.glDispatchCompute(mRTTSize / 16, mRTTSize / 16, 1);
- ext.glMemoryBarrier(GL_ALL_BARRIER_BITS);
- }
- else
- {
- mFBOs[0]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
- state.applyTextureAttribute(0, mTextures[1]);
- osg::Geometry::drawImplementation(renderInfo);
- }
+ ext.glDispatchCompute(sRTTSize / 16, sRTTSize / 16, 1);
+ ext.glMemoryBarrier(GL_ALL_BARRIER_BITS);
+ }
+ else
+ {
+ mFBOs[0]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
+ state.applyTextureAttribute(0, mTextures[1]);
+ osg::Geometry::drawImplementation(renderInfo);
}
}
@@ -271,7 +285,7 @@ namespace MWRender
setReferenceFrame(osg::Camera::ABSOLUTE_RF);
setNodeMask(Mask_RenderToTexture);
setClearMask(GL_NONE);
- setViewport(0, 0, RipplesSurface::mRTTSize, RipplesSurface::mRTTSize);
+ setViewport(0, 0, RipplesSurface::sRTTSize, RipplesSurface::sRTTSize);
addChild(mRipples);
setCullingActive(false);
setImplicitBufferAttachmentMask(0, 0);
diff --git a/apps/openmw/mwrender/ripples.hpp b/apps/openmw/mwrender/ripples.hpp
index 0d5b055eb5..e355b16ecd 100644
--- a/apps/openmw/mwrender/ripples.hpp
+++ b/apps/openmw/mwrender/ripples.hpp
@@ -46,28 +46,30 @@ namespace MWRender
void releaseGLObjects(osg::State* state) const override;
- static constexpr size_t mRTTSize = 1024;
+ static constexpr size_t sRTTSize = 1024;
// e.g. texel to cell unit ratio
- static constexpr float mWorldScaleFactor = 2.5;
-
- Resource::ResourceSystem* mResourceSystem;
+ static constexpr float sWorldScaleFactor = 2.5;
+ private:
struct State
{
- osg::Vec2f mOffset;
- osg::ref_ptr<osg::StateSet> mStateset;
bool mPaused = true;
+ osg::ref_ptr<osg::StateSet> mStateset;
};
+ void setupFragmentPipeline();
+
+ void setupComputePipeline();
+
+ inline void updateState(const osg::FrameStamp& frameStamp, State& state);
+
+ Resource::ResourceSystem* mResourceSystem;
+
size_t mPositionCount = 0;
std::array<osg::Vec3f, 100> mPositions;
std::array<State, 2> mState;
- private:
- void setupFragmentPipeline();
- void setupComputePipeline();
-
osg::Vec2f mCurrentPlayerPos;
osg::Vec2f mLastPlayerPos;
@@ -79,6 +81,9 @@ namespace MWRender
bool mPaused = false;
bool mUseCompute = false;
+
+ double mLastSimulationTime = 0;
+ double mRemainingWaveTime = 0;
};
class Ripples : public osg::Camera
diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp
index 35c10b81f4..2afaa06ad0 100644
--- a/apps/openmw/mwrender/water.cpp
+++ b/apps/openmw/mwrender/water.cpp
@@ -703,8 +703,8 @@ namespace MWRender
defineMap["refraction_enabled"] = std::string(mRefraction ? "1" : "0");
const int rippleDetail = Settings::water().mRainRippleDetail;
defineMap["rain_ripple_detail"] = std::to_string(rippleDetail);
- defineMap["ripple_map_world_scale"] = std::to_string(RipplesSurface::mWorldScaleFactor);
- defineMap["ripple_map_size"] = std::to_string(RipplesSurface::mRTTSize) + ".0";
+ defineMap["ripple_map_world_scale"] = std::to_string(RipplesSurface::sWorldScaleFactor);
+ defineMap["ripple_map_size"] = std::to_string(RipplesSurface::sRTTSize) + ".0";
Stereo::shaderStereoDefines(defineMap);