mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-06 00:40:04 +00:00
Merge branch 'fps_independent_water_ripples' into 'master'
Make water ripples simulation FPS independent (#7687) Closes #7687 See merge request OpenMW/openmw!3885
This commit is contained in:
commit
051f3dcd56
@ -63,7 +63,7 @@ namespace MWRender
|
|||||||
stateset->addUniform(new osg::Uniform("offset", osg::Vec2f()));
|
stateset->addUniform(new osg::Uniform("offset", osg::Vec2f()));
|
||||||
stateset->addUniform(new osg::Uniform("positionCount", 0));
|
stateset->addUniform(new osg::Uniform("positionCount", 0));
|
||||||
stateset->addUniform(new osg::Uniform(osg::Uniform::Type::FLOAT_VEC3, "positions", 100));
|
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;
|
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_S, osg::Texture::CLAMP_TO_BORDER);
|
||||||
texture->setWrap(osg::Texture::WRAP_T, 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->setBorderColor(osg::Vec4(0, 0, 0, 0));
|
||||||
texture->setTextureSize(mRTTSize, mRTTSize);
|
texture->setTextureSize(sRTTSize, sRTTSize);
|
||||||
|
|
||||||
mTextures[i] = texture;
|
mTextures[i] = texture;
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ namespace MWRender
|
|||||||
{
|
{
|
||||||
auto& shaderManager = mResourceSystem->getSceneManager()->getShaderManager();
|
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);
|
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));
|
nullptr, shaderManager.getShader("core/ripples_simulate.comp", {}, osg::Shader::COMPUTE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RipplesSurface::updateState(const osg::FrameStamp& frameStamp, State& state)
|
||||||
|
{
|
||||||
|
state.mPaused = mPaused;
|
||||||
|
|
||||||
|
if (mPaused)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
state.mPaused = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||||
|
const ESM::Position& playerPos = player.getRefData().getPosition();
|
||||||
|
|
||||||
|
mCurrentPlayerPos = osg::Vec2f(
|
||||||
|
std::floor(playerPos.pos[0] / sWorldScaleFactor), std::floor(playerPos.pos[1] / sWorldScaleFactor));
|
||||||
|
const osg::Vec2f offset = mCurrentPlayerPos - mLastPlayerPos;
|
||||||
|
mLastPlayerPos = mCurrentPlayerPos;
|
||||||
|
|
||||||
|
state.mStateset->getUniform("positionCount")->set(static_cast<int>(mPositionCount));
|
||||||
|
state.mStateset->getUniform("offset")->set(offset);
|
||||||
|
|
||||||
|
osg::Uniform* const positions = state.mStateset->getUniform("positions");
|
||||||
|
|
||||||
|
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)
|
void RipplesSurface::traverse(osg::NodeVisitor& nv)
|
||||||
{
|
{
|
||||||
if (!nv.getFrameStamp())
|
const osg::FrameStamp* const frameStamp = nv.getFrameStamp();
|
||||||
|
|
||||||
|
if (frameStamp == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
|
if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
|
||||||
{
|
updateState(*frameStamp, mState[frameStamp->getFrameNumber() % 2]);
|
||||||
size_t frameId = nv.getFrameStamp()->getFrameNumber() % 2;
|
|
||||||
|
|
||||||
const auto& 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);
|
|
||||||
|
|
||||||
auto* positions = mState[frameId].mStateset->getUniform("positions");
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
mPositionCount = 0;
|
|
||||||
}
|
|
||||||
osg::Geometry::traverse(nv);
|
osg::Geometry::traverse(nv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RipplesSurface::drawImplementation(osg::RenderInfo& renderInfo) const
|
void RipplesSurface::drawImplementation(osg::RenderInfo& renderInfo) const
|
||||||
{
|
{
|
||||||
osg::State& state = *renderInfo.getState();
|
osg::State& state = *renderInfo.getState();
|
||||||
osg::GLExtensions& ext = *state.get<osg::GLExtensions>();
|
const std::size_t currentFrame = state.getFrameStamp()->getFrameNumber() % 2;
|
||||||
size_t contextID = state.getContextID();
|
|
||||||
|
|
||||||
size_t currentFrame = state.getFrameStamp()->getFrameNumber() % 2;
|
|
||||||
const State& frameState = mState[currentFrame];
|
const State& frameState = mState[currentFrame];
|
||||||
if (frameState.mPaused)
|
if (frameState.mPaused)
|
||||||
{
|
{
|
||||||
return;
|
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);
|
osg::Texture::TextureObject* to = texture->getTextureObject(contextID);
|
||||||
if (!to || texture->isDirty(contextID))
|
if (!to || texture->isDirty(contextID))
|
||||||
{
|
{
|
||||||
@ -181,52 +205,42 @@ namespace MWRender
|
|||||||
ext.glBindImageTexture(index, to->id(), 0, GL_FALSE, 0, access, GL_RGBA16F);
|
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
|
// PASS: Blot in all ripple spawners
|
||||||
mProgramBlobber->apply(state);
|
mProgramBlobber->apply(state);
|
||||||
state.apply(frameState.mStateset);
|
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.glDispatchCompute(sRTTSize / 16, sRTTSize / 16, 1);
|
||||||
ext.glMemoryBarrier(GL_ALL_BARRIER_BITS);
|
ext.glMemoryBarrier(GL_ALL_BARRIER_BITS);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mFBOs[1]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
mFBOs[1]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
state.applyTextureAttribute(0, mTextures[0]);
|
state.applyTextureAttribute(0, mTextures[0]);
|
||||||
osg::Geometry::drawImplementation(renderInfo);
|
osg::Geometry::drawImplementation(renderInfo);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PASS: Wave simulation
|
// PASS: Wave simulation
|
||||||
mProgramSimulation->apply(state);
|
mProgramSimulation->apply(state);
|
||||||
state.apply(frameState.mStateset);
|
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.glDispatchCompute(sRTTSize / 16, sRTTSize / 16, 1);
|
||||||
ext.glMemoryBarrier(GL_ALL_BARRIER_BITS);
|
ext.glMemoryBarrier(GL_ALL_BARRIER_BITS);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mFBOs[0]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
mFBOs[0]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||||
state.applyTextureAttribute(0, mTextures[1]);
|
state.applyTextureAttribute(0, mTextures[1]);
|
||||||
osg::Geometry::drawImplementation(renderInfo);
|
osg::Geometry::drawImplementation(renderInfo);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +285,7 @@ namespace MWRender
|
|||||||
setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||||
setNodeMask(Mask_RenderToTexture);
|
setNodeMask(Mask_RenderToTexture);
|
||||||
setClearMask(GL_NONE);
|
setClearMask(GL_NONE);
|
||||||
setViewport(0, 0, RipplesSurface::mRTTSize, RipplesSurface::mRTTSize);
|
setViewport(0, 0, RipplesSurface::sRTTSize, RipplesSurface::sRTTSize);
|
||||||
addChild(mRipples);
|
addChild(mRipples);
|
||||||
setCullingActive(false);
|
setCullingActive(false);
|
||||||
setImplicitBufferAttachmentMask(0, 0);
|
setImplicitBufferAttachmentMask(0, 0);
|
||||||
|
@ -46,28 +46,30 @@ namespace MWRender
|
|||||||
|
|
||||||
void releaseGLObjects(osg::State* state) const override;
|
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
|
// e.g. texel to cell unit ratio
|
||||||
static constexpr float mWorldScaleFactor = 2.5;
|
static constexpr float sWorldScaleFactor = 2.5;
|
||||||
|
|
||||||
Resource::ResourceSystem* mResourceSystem;
|
|
||||||
|
|
||||||
|
private:
|
||||||
struct State
|
struct State
|
||||||
{
|
{
|
||||||
osg::Vec2f mOffset;
|
|
||||||
osg::ref_ptr<osg::StateSet> mStateset;
|
|
||||||
bool mPaused = true;
|
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;
|
size_t mPositionCount = 0;
|
||||||
std::array<osg::Vec3f, 100> mPositions;
|
std::array<osg::Vec3f, 100> mPositions;
|
||||||
|
|
||||||
std::array<State, 2> mState;
|
std::array<State, 2> mState;
|
||||||
|
|
||||||
private:
|
|
||||||
void setupFragmentPipeline();
|
|
||||||
void setupComputePipeline();
|
|
||||||
|
|
||||||
osg::Vec2f mCurrentPlayerPos;
|
osg::Vec2f mCurrentPlayerPos;
|
||||||
osg::Vec2f mLastPlayerPos;
|
osg::Vec2f mLastPlayerPos;
|
||||||
|
|
||||||
@ -79,6 +81,9 @@ namespace MWRender
|
|||||||
|
|
||||||
bool mPaused = false;
|
bool mPaused = false;
|
||||||
bool mUseCompute = false;
|
bool mUseCompute = false;
|
||||||
|
|
||||||
|
double mLastSimulationTime = 0;
|
||||||
|
double mRemainingWaveTime = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Ripples : public osg::Camera
|
class Ripples : public osg::Camera
|
||||||
|
@ -703,8 +703,8 @@ namespace MWRender
|
|||||||
defineMap["refraction_enabled"] = std::string(mRefraction ? "1" : "0");
|
defineMap["refraction_enabled"] = std::string(mRefraction ? "1" : "0");
|
||||||
const int rippleDetail = Settings::water().mRainRippleDetail;
|
const int rippleDetail = Settings::water().mRainRippleDetail;
|
||||||
defineMap["rain_ripple_detail"] = std::to_string(rippleDetail);
|
defineMap["rain_ripple_detail"] = std::to_string(rippleDetail);
|
||||||
defineMap["ripple_map_world_scale"] = std::to_string(RipplesSurface::mWorldScaleFactor);
|
defineMap["ripple_map_world_scale"] = std::to_string(RipplesSurface::sWorldScaleFactor);
|
||||||
defineMap["ripple_map_size"] = std::to_string(RipplesSurface::mRTTSize) + ".0";
|
defineMap["ripple_map_size"] = std::to_string(RipplesSurface::sRTTSize) + ".0";
|
||||||
|
|
||||||
Stereo::shaderStereoDefines(defineMap);
|
Stereo::shaderStereoDefines(defineMap);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user