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 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(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(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(); - 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(); + 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 mStateset; bool mPaused = true; + osg::ref_ptr mStateset; }; + void setupFragmentPipeline(); + + void setupComputePipeline(); + + inline void updateState(const osg::FrameStamp& frameStamp, State& state); + + Resource::ResourceSystem* mResourceSystem; + size_t mPositionCount = 0; std::array mPositions; std::array 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);