Water ripples

c++11
scrawl 10 years ago
parent 3663511cdb
commit 3da8f6e62e

@ -22,7 +22,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
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
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
)
add_openmw_dir (mwinput

@ -114,7 +114,7 @@ namespace MWRender
bool mWireframe;
};
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, Resource::ResourceSystem* resourceSystem)
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, Resource::ResourceSystem* resourceSystem, const MWWorld::Fallback* fallback)
: mViewer(viewer)
, mRootNode(rootNode)
, mResourceSystem(resourceSystem)
@ -136,7 +136,7 @@ namespace MWRender
mEffectManager.reset(new EffectManager(lightRoot, mResourceSystem));
mWater.reset(new Water(lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation()));
mWater.reset(new Water(lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback));
mTerrain.reset(new Terrain::TerrainGrid(lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(),
new TerrainStorage(mResourceSystem->getVFS(), false), Mask_Terrain));
@ -265,6 +265,8 @@ namespace MWRender
if (store->getCell()->isExterior())
mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
mWater->removeCell(store);
}
void RenderingManager::setSkyEnabled(bool enabled)
@ -330,6 +332,7 @@ namespace MWRender
{
mEffectManager->update(dt);
mSky->update(dt);
mWater->update(dt);
mCamera->update(dt, paused);
osg::Vec3f focal, cameraPos;
@ -354,6 +357,11 @@ namespace MWRender
mCamera->attachTo(ptr);
}
void RenderingManager::removePlayer(const MWWorld::Ptr &player)
{
mWater->removeEmitter(player);
}
void RenderingManager::rotateObject(const MWWorld::Ptr &ptr, const osg::Quat& rot)
{
if(ptr == mCamera->getTrackingPtr() &&
@ -378,6 +386,7 @@ namespace MWRender
void RenderingManager::removeObject(const MWWorld::Ptr &ptr)
{
mObjects->removeObject(ptr);
mWater->removeEmitter(ptr);
}
void RenderingManager::setWaterEnabled(bool enabled)
@ -579,7 +588,7 @@ namespace MWRender
void RenderingManager::notifyWorldSpaceChanged()
{
mEffectManager->clear();
//mWater->clearRipples();
mWater->clearRipples();
}
void RenderingManager::clear()
@ -613,6 +622,8 @@ namespace MWRender
mPlayerNode->getUserDataContainer()->addUserObject(new PtrHolder(player));
player.getRefData().setBaseNode(mPlayerNode);
mWater->addEmitter(player);
}
void RenderingManager::renderPlayer(const MWWorld::Ptr &player)
@ -621,8 +632,6 @@ namespace MWRender
mCamera->setAnimation(mPlayerAnimation.get());
mCamera->attachTo(player);
//mWater->removeEmitter(ptr);
//mWater->addEmitter(ptr);
}
void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr)
@ -643,6 +652,16 @@ namespace MWRender
}
}
void RenderingManager::addWaterRippleEmitter(const MWWorld::Ptr &ptr)
{
mWater->addEmitter(ptr);
}
void RenderingManager::removeWaterRippleEmitter(const MWWorld::Ptr &ptr)
{
mWater->removeEmitter(ptr);
}
void RenderingManager::updateProjectionMatrix()
{
double aspect = mViewer->getCamera()->getViewport()->aspectRatio();

@ -37,6 +37,11 @@ namespace Terrain
class World;
}
namespace MWWorld
{
class Fallback;
}
namespace MWRender
{
@ -52,7 +57,7 @@ namespace MWRender
class RenderingManager : public MWRender::RenderingInterface
{
public:
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, Resource::ResourceSystem* resourceSystem);
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, Resource::ResourceSystem* resourceSystem, const MWWorld::Fallback* fallback);
~RenderingManager();
MWRender::Objects& getObjects();
@ -125,8 +130,12 @@ namespace MWRender
Animation* getAnimation(const MWWorld::Ptr& ptr);
Animation* getPlayerAnimation();
void addWaterRippleEmitter(const MWWorld::Ptr& ptr);
void removeWaterRippleEmitter(const MWWorld::Ptr& ptr);
void updatePlayerPtr(const MWWorld::Ptr &ptr);
void removePlayer(const MWWorld::Ptr& player);
void setupPlayer(const MWWorld::Ptr& player);
void renderPlayer(const MWWorld::Ptr& player);

@ -0,0 +1,202 @@
#include "ripplesimulation.hpp"
#include <iomanip>
#include <osg/Geode>
#include <osg/Texture2D>
#include <osg/Material>
#include <osg/Depth>
#include <osg/PositionAttitudeTransform>
#include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleSystemUpdater>
#include <components/misc/rng.hpp>
#include <components/nifosg/controller.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/resource/resourcesystem.hpp>
#include "vismask.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/fallback.hpp"
namespace
{
void createWaterRippleStateSet(Resource::ResourceSystem* resourceSystem, const MWWorld::Fallback* fallback, osg::Node* node)
{
int rippleFrameCount = fallback->getFallbackInt("Water_RippleFrameCount");
if (rippleFrameCount <= 0)
return;
std::string tex = fallback->getFallbackString("Water_RippleTexture");
std::vector<osg::ref_ptr<osg::Texture2D> > textures;
for (int i=0; i<rippleFrameCount; ++i)
{
std::ostringstream texname;
texname << "textures/water/" << tex << std::setw(2) << std::setfill('0') << i << ".dds";
textures.push_back(resourceSystem->getTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT));
}
osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 0.3f/rippleFrameCount, textures));
controller->setSource(boost::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));
node->addUpdateCallback(controller);
osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
stateset->setTextureAttributeAndModes(0, textures[0], osg::StateAttribute::ON);
osg::ref_ptr<osg::Depth> depth (new osg::Depth);
depth->setWriteMask(false);
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
osg::ref_ptr<osg::Material> mat (new osg::Material);
mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f));
mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f));
mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f));
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
mat->setColorMode(osg::Material::DIFFUSE);
stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);
node->setStateSet(stateset);
}
}
namespace MWRender
{
RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem* resourceSystem, const MWWorld::Fallback* fallback)
: mParent(parent)
{
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
mParticleSystem = new osgParticle::ParticleSystem;
geode->addDrawable(mParticleSystem);
mParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED);
mParticleSystem->setAlignVectorX(osg::Vec3f(1,0,0));
mParticleSystem->setAlignVectorY(osg::Vec3f(0,1,0));
osgParticle::Particle& particleTemplate = mParticleSystem->getDefaultParticleTemplate();
particleTemplate.setSizeRange(osgParticle::rangef(15, 180));
particleTemplate.setColorRange(osgParticle::rangev4(osg::Vec4f(1,1,1,0.7), osg::Vec4f(1,1,1,0.7)));
particleTemplate.setAlphaRange(osgParticle::rangef(1.f, 0.f));
particleTemplate.setAngularVelocity(osg::Vec3f(0,0,fallback->getFallbackFloat("Water_RippleRotSpeed")));
particleTemplate.setLifeTime(fallback->getFallbackFloat("Water_RippleLifetime"));
osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater (new osgParticle::ParticleSystemUpdater);
updater->addParticleSystem(mParticleSystem);
mParticleNode = new osg::PositionAttitudeTransform;
mParticleNode->addChild(updater);
mParticleNode->addChild(geode);
mParticleNode->setNodeMask(Mask_Effect);
createWaterRippleStateSet(resourceSystem, fallback, mParticleNode);
mParent->addChild(mParticleNode);
}
RippleSimulation::~RippleSimulation()
{
mParent->removeChild(mParticleNode);
}
void RippleSimulation::update(float dt)
{
for (std::vector<Emitter>::iterator it=mEmitters.begin(); it !=mEmitters.end(); ++it)
{
if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayerPtr())
{
// fetch a new ptr (to handle cell change etc)
// for non-player actors this is done in updateObjectCell
it->mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr();
}
osg::Vec3f currentPos (it->mPtr.getRefData().getPosition().asVec3());
currentPos.z() = 0; // Z is set by the Scene Node
if ( (currentPos - it->mLastEmitPosition).length() > 10
// Only emit when close to the water surface, not above it and not too deep in the water
&& MWBase::Environment::get().getWorld ()->isUnderwater (it->mPtr.getCell(), it->mPtr.getRefData().getPosition().asVec3())
&& !MWBase::Environment::get().getWorld()->isSubmerged(it->mPtr))
{
it->mLastEmitPosition = currentPos;
if (mParticleSystem->numParticles()-mParticleSystem->numDeadParticles() > 500)
continue; // TODO: remove the oldest particle to make room?
osgParticle::Particle* p = mParticleSystem->createParticle(NULL);
p->setPosition(currentPos);
p->setAngle(osg::Vec3f(0,0, Misc::Rng::rollProbability() * osg::PI * 2 - osg::PI));
}
}
}
void RippleSimulation::addEmitter(const MWWorld::Ptr& ptr, float scale, float force)
{
Emitter newEmitter;
newEmitter.mPtr = ptr;
newEmitter.mScale = scale;
newEmitter.mForce = force;
newEmitter.mLastEmitPosition = osg::Vec3f(0,0,0);
mEmitters.push_back (newEmitter);
}
void RippleSimulation::removeEmitter (const MWWorld::Ptr& ptr)
{
for (std::vector<Emitter>::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it)
{
if (it->mPtr == ptr)
{
mEmitters.erase(it);
return;
}
}
}
void RippleSimulation::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr)
{
for (std::vector<Emitter>::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it)
{
if (it->mPtr == old)
{
it->mPtr = ptr;
return;
}
}
}
void RippleSimulation::removeCell(const MWWorld::CellStore *store)
{
for (std::vector<Emitter>::iterator it = mEmitters.begin(); it != mEmitters.end();)
{
if (it->mPtr.getCell() == store && it->mPtr != MWBase::Environment::get().getWorld()->getPlayerPtr())
{
it = mEmitters.erase(it);
}
else
++it;
}
}
void RippleSimulation::setWaterHeight(float height)
{
mParticleNode->setPosition(osg::Vec3f(0,0,height));
}
void RippleSimulation::clear()
{
for (int i=0; i<mParticleSystem->numParticles(); ++i)
mParticleSystem->destroyParticle(i);
}
}

@ -0,0 +1,76 @@
#ifndef OPENMW_MWRENDER_RIPPLESIMULATION_H
#define OPENMW_MWRENDER_RIPPLESIMULATION_H
#include <osg/ref_ptr>
#include "../mwworld/ptr.hpp"
namespace osg
{
class Group;
}
namespace osgParticle
{
class ParticleSystem;
}
namespace Resource
{
class ResourceSystem;
}
namespace MWWorld
{
class Fallback;
}
namespace MWRender
{
struct Emitter
{
MWWorld::Ptr mPtr;
osg::Vec3f mLastEmitPosition;
float mScale;
float mForce;
};
class RippleSimulation
{
public:
RippleSimulation(osg::Group* parent, Resource::ResourceSystem* resourceSystem, const MWWorld::Fallback* fallback);
~RippleSimulation();
/// @param dt Time since the last frame
void update(float dt);
/// adds an emitter, position will be tracked automatically
void addEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f);
void removeEmitter (const MWWorld::Ptr& ptr);
void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr);
void removeCell(const MWWorld::CellStore* store);
/// Change the height of the water surface, thus moving all ripples with it
void setWaterHeight(float height);
/// Remove all active ripples
void clear();
private:
osg::ref_ptr<osg::Group> mParent;
Resource::ResourceSystem* mResourceSystem;
osg::ref_ptr<osgParticle::ParticleSystem> mParticleSystem;
osg::ref_ptr<osg::PositionAttitudeTransform> mParticleNode;
std::vector<Emitter> mEmitters;
float mRippleLifeTime;
float mRippleRotSpeed;
};
}
#endif

@ -18,6 +18,7 @@
#include <components/sceneutil/controller.hpp>
#include "vismask.hpp"
#include "ripplesimulation.hpp"
namespace
{
@ -107,13 +108,15 @@ namespace MWRender
// --------------------------------------------------------------------------------------------------------------------------------
Water::Water(osg::Group *parent, Resource::ResourceSystem *resourceSystem, osgUtil::IncrementalCompileOperation *ico)
Water::Water(osg::Group *parent, Resource::ResourceSystem *resourceSystem, osgUtil::IncrementalCompileOperation *ico, const MWWorld::Fallback* fallback)
: mParent(parent)
, mResourceSystem(resourceSystem)
, mEnabled(true)
, mToggled(true)
, mTop(0)
{
mSimulation.reset(new RippleSimulation(parent, resourceSystem, fallback));
osg::ref_ptr<osg::Geometry> waterGeom = createWaterGeometry(CELL_SIZE*150, 40, 900);
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
@ -161,6 +164,11 @@ void Water::setHeight(const float height)
mWaterNode->setPosition(pos);
}
void Water::update(float dt)
{
mSimulation->update(dt);
}
void Water::updateVisible()
{
mWaterNode->setNodeMask(mEnabled && mToggled ? ~0 : 0);
@ -183,7 +191,6 @@ osg::Vec3f Water::getSceneNodeCoordinates(int gridX, int gridY)
return osg::Vec3f(static_cast<float>(gridX * CELL_SIZE + (CELL_SIZE / 2)), static_cast<float>(gridY * CELL_SIZE + (CELL_SIZE / 2)), mTop);
}
/*
void Water::addEmitter (const MWWorld::Ptr& ptr, float scale, float force)
{
mSimulation->addEmitter (ptr, scale, force);
@ -198,6 +205,15 @@ void Water::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr)
{
mSimulation->updateEmitterPtr(old, ptr);
}
*/
void Water::removeCell(const MWWorld::CellStore *store)
{
mSimulation->removeCell(store);
}
void Water::clearRipples()
{
mSimulation->clear();
}
}

@ -21,9 +21,16 @@ namespace Resource
class ResourceSystem;
}
namespace MWWorld
{
class Fallback;
}
namespace MWRender
{
class RippleSimulation;
/// Water rendering
class Water
{
@ -34,6 +41,8 @@ namespace MWRender
Resource::ResourceSystem* mResourceSystem;
osg::ref_ptr<osgUtil::IncrementalCompileOperation> mIncrementalCompileOperation;
std::auto_ptr<RippleSimulation> mSimulation;
bool mEnabled;
bool mToggled;
float mTop;
@ -42,7 +51,7 @@ namespace MWRender
void updateVisible();
public:
Water(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico);
Water(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, const MWWorld::Fallback* fallback);
~Water();
void setEnabled(bool enabled);
@ -51,16 +60,19 @@ namespace MWRender
bool isUnderwater(const osg::Vec3f& pos) const;
/*
/// adds an emitter, position will be tracked automatically using its scene node
void addEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f);
void removeEmitter (const MWWorld::Ptr& ptr);
void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr);
*/
void removeCell(const MWWorld::CellStore* store); ///< remove all emitters in this cell
void clearRipples();
void changeCell(const MWWorld::CellStore* store);
void setHeight(const float height);
void update(float dt);
};
}

@ -58,4 +58,5 @@ namespace MWWorld
return osg::Vec4f(boost::lexical_cast<int>(ret[0])/255.f,boost::lexical_cast<int>(ret[1])/255.f,boost::lexical_cast<int>(ret[2])/255.f, 1.f);
}
}
}

@ -39,6 +39,9 @@ namespace
model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player
ptr.getClass().insertObjectRendering(ptr, model, rendering);
ptr.getClass().insertObject (ptr, model, physics);
if (ptr.getClass().isActor())
rendering.addWaterRippleEmitter(ptr);
}
void updateObjectLocalRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,
@ -570,6 +573,8 @@ namespace MWWorld
MWBase::Environment::get().getSoundManager()->stopSound3D (ptr);
mPhysics->remove(ptr);
mRendering.removeObject (ptr);
if (ptr.getClass().isActor())
mRendering.removeWaterRippleEmitter(ptr);
}
bool Scene::isCellActive(const CellStore &cell)

@ -161,7 +161,7 @@ namespace MWWorld
{
mPhysics = new MWPhysics::PhysicsSystem(resourceSystem, rootNode);
mProjectileManager.reset(new ProjectileManager(rootNode, resourceSystem, mPhysics));
mRendering = new MWRender::RenderingManager(viewer, rootNode, resourceSystem);
mRendering = new MWRender::RenderingManager(viewer, rootNode, resourceSystem, &mFallback);
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback);
@ -2064,6 +2064,7 @@ namespace MWWorld
// Remove the old CharacterController
MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr());
mPhysics->remove(getPlayerPtr());
mRendering->removePlayer(getPlayerPtr());
mPlayer->set(player);
}

Loading…
Cancel
Save