forked from mirror/openmw-tes3mp
Fix ripples for D3D - added simpler effect (Fixes #1649)
This commit is contained in:
parent
9dbd9af31c
commit
dbd4abd6fe
19 changed files with 196 additions and 475 deletions
|
@ -349,6 +349,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
addResourcesDirectory(mResDir / "mygui");
|
||||
addResourcesDirectory(mResDir / "water");
|
||||
addResourcesDirectory(mResDir / "shadows");
|
||||
addResourcesDirectory(mResDir / "materials");
|
||||
|
||||
OEngine::Render::WindowSettings windowSettings;
|
||||
windowSettings.fullscreen = settings.getBool("fullscreen", "Video");
|
||||
|
|
|
@ -21,6 +21,7 @@ enum RenderQueueGroups
|
|||
RQG_UnderWater = Ogre::RENDER_QUEUE_4,
|
||||
|
||||
RQG_Water = RQG_Alpha,
|
||||
RQG_Ripples = RQG_Water+1,
|
||||
|
||||
// Sky late (sun & sun flare)
|
||||
RQG_SkiesLate = Ogre::RENDER_QUEUE_SKIES_LATE
|
||||
|
|
|
@ -175,7 +175,7 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
|
|||
mDebugging = new Debugging(mRootNode, engine);
|
||||
mLocalMap = new MWRender::LocalMap(&mRendering, this);
|
||||
|
||||
mWater = new MWRender::Water(mRendering.getCamera(), this);
|
||||
mWater = new MWRender::Water(mRendering.getCamera(), this, mFallback);
|
||||
|
||||
setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI"));
|
||||
}
|
||||
|
@ -693,6 +693,7 @@ void RenderingManager::enableLights(bool sun)
|
|||
void RenderingManager::notifyWorldSpaceChanged()
|
||||
{
|
||||
mEffectManager->clear();
|
||||
mWater->clearRipples();
|
||||
}
|
||||
|
||||
Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds)
|
||||
|
|
|
@ -1,162 +1,72 @@
|
|||
#include "ripplesimulation.hpp"
|
||||
|
||||
#include <OgreTextureManager.h>
|
||||
#include <OgreStringConverter.h>
|
||||
#include <OgreHardwarePixelBuffer.h>
|
||||
#include <OgreRoot.h>
|
||||
#include <OgreRectangle2D.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreRenderTexture.h>
|
||||
#include <OgreViewport.h>
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreParticleSystem.h>
|
||||
#include <OgreParticle.h>
|
||||
|
||||
#include <extern/shiny/Main/Factory.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/fallback.hpp"
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
|
||||
RippleSimulation::RippleSimulation(Ogre::SceneManager* mainSceneManager)
|
||||
: mMainSceneMgr(mainSceneManager),
|
||||
mTime(0),
|
||||
mCurrentFrameOffset(0,0),
|
||||
mPreviousFrameOffset(0,0),
|
||||
mRippleCenter(0,0),
|
||||
mTextureSize(512),
|
||||
mRippleAreaLength(1000),
|
||||
mImpulseSize(20),
|
||||
mTexelOffset(0,0),
|
||||
mFirstUpdate(true),
|
||||
mRectangle(NULL),
|
||||
mImpulse(NULL)
|
||||
RippleSimulation::RippleSimulation(Ogre::SceneManager* mainSceneManager, const MWWorld::Fallback* fallback)
|
||||
: mSceneMgr(mainSceneManager)
|
||||
, mParticleSystem(NULL)
|
||||
, mSceneNode(NULL)
|
||||
{
|
||||
Ogre::AxisAlignedBox aabInf;
|
||||
aabInf.setInfinite();
|
||||
mRippleLifeTime = fallback->getFallbackFloat("Water_RippleLifetime");
|
||||
mRippleRotSpeed = fallback->getFallbackFloat("Water_RippleRotSpeed");
|
||||
|
||||
mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
|
||||
// Unknown:
|
||||
// fallback=Water_RippleScale,0.15, 6.5
|
||||
// fallback=Water_RippleAlphas,0.7, 0.1, 0.01
|
||||
|
||||
mCamera = mSceneMgr->createCamera("RippleCamera");
|
||||
// Instantiate from ripples.particle file
|
||||
mParticleSystem = mSceneMgr->createParticleSystem("openmw/Ripples", "openmw/Ripples");
|
||||
|
||||
mRectangle = new Ogre::Rectangle2D(true);
|
||||
mRectangle->setBoundingBox(aabInf);
|
||||
mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0, false);
|
||||
Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
node->attachObject(mRectangle);
|
||||
mParticleSystem->setRenderQueueGroup(RQG_Ripples);
|
||||
mParticleSystem->setVisibilityFlags(RV_Effects);
|
||||
|
||||
mImpulse = new Ogre::Rectangle2D(true);
|
||||
mImpulse->setCorners(-0.1, 0.1, 0.1, -0.1, false);
|
||||
mImpulse->setBoundingBox(aabInf);
|
||||
mImpulse->setMaterial("AddImpulse");
|
||||
Ogre::SceneNode* impulseNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
impulseNode->attachObject(mImpulse);
|
||||
int rippleFrameCount = fallback->getFallbackInt("Water_RippleFrameCount");
|
||||
std::string tex = fallback->getFallbackString("Water_RippleTexture");
|
||||
|
||||
//float w=0.05;
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
Ogre::TexturePtr texture;
|
||||
if (i != 3)
|
||||
texture = Ogre::TextureManager::getSingleton().createManual("RippleHeight" + Ogre::StringConverter::toString(i),
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mTextureSize, mTextureSize, 1, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
|
||||
else
|
||||
texture = Ogre::TextureManager::getSingleton().createManual("RippleNormal",
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mTextureSize, mTextureSize, 1, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
|
||||
sh::MaterialInstance* mat = sh::Factory::getInstance().getMaterialInstance("openmw/Ripple");
|
||||
mat->setProperty("anim_texture2", sh::makeProperty(new sh::StringValue(std::string("textures\\water\\") + tex + ".dds "
|
||||
+ Ogre::StringConverter::toString(rippleFrameCount)
|
||||
+ " "
|
||||
+ Ogre::StringConverter::toString(0.3))));
|
||||
|
||||
// seems to be required to allocate mFreeParticles. TODO: patch Ogre to handle this better
|
||||
mParticleSystem->_update(0.f);
|
||||
|
||||
Ogre::RenderTexture* rt = texture->getBuffer()->getRenderTarget();
|
||||
rt->removeAllViewports();
|
||||
rt->addViewport(mCamera);
|
||||
rt->setAutoUpdated(false);
|
||||
rt->getViewport(0)->setClearEveryFrame(false);
|
||||
|
||||
// debug overlay
|
||||
/*
|
||||
Ogre::Rectangle2D* debugOverlay = new Ogre::Rectangle2D(true);
|
||||
debugOverlay->setCorners(w*2-1, 0.9, (w+0.18)*2-1, 0.4, false);
|
||||
w += 0.2;
|
||||
debugOverlay->setBoundingBox(aabInf);
|
||||
Ogre::SceneNode* debugNode = mMainSceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
debugNode->attachObject(debugOverlay);
|
||||
|
||||
Ogre::MaterialPtr debugMaterial = Ogre::MaterialManager::getSingleton().create("RippleDebug" + Ogre::StringConverter::toString(i),
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
|
||||
if (i != 3)
|
||||
debugMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("RippleHeight" + Ogre::StringConverter::toString(i));
|
||||
else
|
||||
debugMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("RippleNormal");
|
||||
debugMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);
|
||||
|
||||
debugOverlay->setMaterial("RippleDebug" + Ogre::StringConverter::toString(i));
|
||||
*/
|
||||
|
||||
mRenderTargets[i] = rt;
|
||||
mTextures[i] = texture;
|
||||
}
|
||||
|
||||
sh::Factory::getInstance().setSharedParameter("rippleTextureSize", sh::makeProperty<sh::Vector4>(
|
||||
new sh::Vector4(1.0/512, 1.0/512, 512, 512)));
|
||||
sh::Factory::getInstance().setSharedParameter("rippleCenter", sh::makeProperty<sh::Vector3>(
|
||||
new sh::Vector3(0, 0, 0)));
|
||||
sh::Factory::getInstance().setSharedParameter("rippleAreaLength", sh::makeProperty<sh::FloatValue>(
|
||||
new sh::FloatValue(mRippleAreaLength)));
|
||||
|
||||
mSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
mSceneNode->attachObject(mParticleSystem);
|
||||
}
|
||||
|
||||
RippleSimulation::~RippleSimulation()
|
||||
{
|
||||
delete mRectangle;
|
||||
delete mImpulse;
|
||||
if (mParticleSystem)
|
||||
mSceneMgr->destroyParticleSystem(mParticleSystem);
|
||||
mParticleSystem = NULL;
|
||||
|
||||
Ogre::Root::getSingleton().destroySceneManager(mSceneMgr);
|
||||
if (mSceneNode)
|
||||
mSceneMgr->destroySceneNode(mSceneNode);
|
||||
mSceneNode = NULL;
|
||||
}
|
||||
|
||||
void RippleSimulation::update(float dt, Ogre::Vector2 position)
|
||||
{
|
||||
// try to keep 20 fps
|
||||
mTime += dt;
|
||||
|
||||
while (mTime >= 1/20.0 || mFirstUpdate)
|
||||
{
|
||||
mPreviousFrameOffset = mCurrentFrameOffset;
|
||||
|
||||
mCurrentFrameOffset = position - mRippleCenter;
|
||||
// add texel offsets from previous frame.
|
||||
mCurrentFrameOffset += mTexelOffset;
|
||||
|
||||
mTexelOffset = Ogre::Vector2(std::fmod(mCurrentFrameOffset.x, 1.0f/mTextureSize),
|
||||
std::fmod(mCurrentFrameOffset.y, 1.0f/mTextureSize));
|
||||
|
||||
// now subtract new offset in order to snap to texels
|
||||
mCurrentFrameOffset -= mTexelOffset;
|
||||
|
||||
// texture coordinate space
|
||||
mCurrentFrameOffset /= mRippleAreaLength;
|
||||
|
||||
mRippleCenter = position;
|
||||
|
||||
addImpulses();
|
||||
waterSimulation();
|
||||
heightMapToNormalMap();
|
||||
|
||||
swapHeightMaps();
|
||||
if (!mFirstUpdate)
|
||||
mTime -= 1/20.0;
|
||||
else
|
||||
mFirstUpdate = false;
|
||||
}
|
||||
|
||||
sh::Factory::getInstance().setSharedParameter("rippleCenter", sh::makeProperty<sh::Vector3>(
|
||||
new sh::Vector3(mRippleCenter.x + mTexelOffset.x, mRippleCenter.y + mTexelOffset.y, 0)));
|
||||
}
|
||||
|
||||
void RippleSimulation::addImpulses()
|
||||
{
|
||||
mRectangle->setVisible(false);
|
||||
mImpulse->setVisible(true);
|
||||
|
||||
/// \todo it should be more efficient to render all emitters at once
|
||||
bool newParticle = false;
|
||||
for (std::vector<Emitter>::iterator it=mEmitters.begin(); it !=mEmitters.end(); ++it)
|
||||
{
|
||||
if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayerPtr())
|
||||
|
@ -165,69 +75,50 @@ void RippleSimulation::addImpulses()
|
|||
// for non-player actors this is done in updateObjectCell
|
||||
it->mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr();
|
||||
}
|
||||
const float* _currentPos = it->mPtr.getRefData().getPosition().pos;
|
||||
Ogre::Vector3 currentPos (_currentPos[0], _currentPos[1], _currentPos[2]);
|
||||
|
||||
if ( (currentPos - it->mLastEmitPosition).length() > 2
|
||||
&& MWBase::Environment::get().getWorld ()->isUnderwater (it->mPtr.getCell(), currentPos))
|
||||
Ogre::Vector3 currentPos (it->mPtr.getRefData().getPosition().pos);
|
||||
currentPos.z = 0;
|
||||
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(),
|
||||
Ogre::Vector3(it->mPtr.getRefData().getPosition().pos))
|
||||
&& !MWBase::Environment::get().getWorld()->isSubmerged(it->mPtr))
|
||||
{
|
||||
it->mLastEmitPosition = currentPos;
|
||||
|
||||
Ogre::Vector2 pos (currentPos.x, currentPos.y);
|
||||
pos -= mRippleCenter;
|
||||
pos /= mRippleAreaLength;
|
||||
float size = mImpulseSize / mRippleAreaLength;
|
||||
mImpulse->setCorners(pos.x-size, pos.y+size, pos.x+size, pos.y-size, false);
|
||||
|
||||
// don't render if we are offscreen
|
||||
if (pos.x - size >= 1.0 || pos.y+size <= -1.0 || pos.x+size <= -1.0 || pos.y-size >= 1.0)
|
||||
continue;
|
||||
mRenderTargets[1]->update();
|
||||
newParticle = true;
|
||||
Ogre::Particle* created = mParticleSystem->createParticle();
|
||||
if (!created)
|
||||
break; // TODO: cleanup the oldest particle to make room
|
||||
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
|
||||
Ogre::Vector3& position = created->mPosition;
|
||||
Ogre::Vector3& direction = created->mDirection;
|
||||
Ogre::ColourValue& colour = created->mColour;
|
||||
float& totalTimeToLive = created->mTotalTimeToLive;
|
||||
float& timeToLive = created->mTimeToLive;
|
||||
Ogre::Radian& rotSpeed = created->mRotationSpeed;
|
||||
Ogre::Radian& rotation = created->mRotation;
|
||||
#else
|
||||
Ogre::Vector3& position = created->position;
|
||||
Ogre::Vector3& direction = created->direction;
|
||||
Ogre::ColourValue& colour = created->colour;
|
||||
float& totalTimeToLive = created->totalTimeToLive;
|
||||
float& timeToLive = created->timeToLive;
|
||||
Ogre::Radian& rotSpeed = created->rotationSpeed;
|
||||
Ogre::Radian& rotation = created->rotation;
|
||||
#endif
|
||||
timeToLive = totalTimeToLive = mRippleLifeTime;
|
||||
colour = Ogre::ColourValue(0.f, 0.f, 0.f, 0.7); // Water_RippleAlphas.x?
|
||||
direction = Ogre::Vector3(0,0,0);
|
||||
position = currentPos;
|
||||
position.z = 0; // Z is set by the Scene Node
|
||||
rotSpeed = mRippleRotSpeed;
|
||||
rotation = Ogre::Radian(Ogre::Math::RangeRandom(-Ogre::Math::PI, Ogre::Math::PI));
|
||||
created->setDimensions(50,50);
|
||||
}
|
||||
}
|
||||
|
||||
mImpulse->setVisible(false);
|
||||
mRectangle->setVisible(true);
|
||||
}
|
||||
|
||||
void RippleSimulation::waterSimulation()
|
||||
{
|
||||
mRectangle->setMaterial("HeightmapSimulation");
|
||||
|
||||
sh::Factory::getInstance().setTextureAlias("Heightmap0", mTextures[0]->getName());
|
||||
sh::Factory::getInstance().setTextureAlias("Heightmap1", mTextures[1]->getName());
|
||||
|
||||
sh::Factory::getInstance().setSharedParameter("currentFrameOffset", sh::makeProperty<sh::Vector3>(
|
||||
new sh::Vector3(mCurrentFrameOffset.x, mCurrentFrameOffset.y, 0)));
|
||||
sh::Factory::getInstance().setSharedParameter("previousFrameOffset", sh::makeProperty<sh::Vector3>(
|
||||
new sh::Vector3(mPreviousFrameOffset.x, mPreviousFrameOffset.y, 0)));
|
||||
|
||||
mRenderTargets[2]->update();
|
||||
}
|
||||
|
||||
void RippleSimulation::heightMapToNormalMap()
|
||||
{
|
||||
mRectangle->setMaterial("HeightToNormalMap");
|
||||
|
||||
sh::Factory::getInstance().setTextureAlias("Heightmap2", mTextures[2]->getName());
|
||||
|
||||
mRenderTargets[TEX_NORMAL]->update();
|
||||
}
|
||||
|
||||
void RippleSimulation::swapHeightMaps()
|
||||
{
|
||||
// 0 -> 1 -> 2 to 2 -> 0 ->1
|
||||
Ogre::RenderTexture* tmp = mRenderTargets[0];
|
||||
Ogre::TexturePtr tmp2 = mTextures[0];
|
||||
|
||||
mRenderTargets[0] = mRenderTargets[1];
|
||||
mTextures[0] = mTextures[1];
|
||||
|
||||
mRenderTargets[1] = mRenderTargets[2];
|
||||
mTextures[1] = mTextures[2];
|
||||
|
||||
mRenderTargets[2] = tmp;
|
||||
mTextures[2] = tmp2;
|
||||
if (newParticle) // now apparently needs another update, otherwise it won't render in the first frame after a particle is created. TODO: patch Ogre to handle this better
|
||||
mParticleSystem->_update(0.f);
|
||||
}
|
||||
|
||||
void RippleSimulation::addEmitter(const MWWorld::Ptr& ptr, float scale, float force)
|
||||
|
@ -264,5 +155,15 @@ void RippleSimulation::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld:
|
|||
}
|
||||
}
|
||||
|
||||
void RippleSimulation::setWaterHeight(float height)
|
||||
{
|
||||
mSceneNode->setPosition(0,0,height);
|
||||
}
|
||||
|
||||
void RippleSimulation::clear()
|
||||
{
|
||||
mParticleSystem->clear();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
#ifndef RIPPLE_SIMULATION_H
|
||||
#define RIPPLE_SIMULATION_H
|
||||
|
||||
#include <OgreTexture.h>
|
||||
#include <OgreMaterial.h>
|
||||
#include <OgreVector2.h>
|
||||
#include <OgreVector3.h>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class RenderTexture;
|
||||
class Camera;
|
||||
class SceneManager;
|
||||
class Rectangle2D;
|
||||
class ParticleSystem;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Fallback;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
|
@ -30,9 +30,11 @@ struct Emitter
|
|||
class RippleSimulation
|
||||
{
|
||||
public:
|
||||
RippleSimulation(Ogre::SceneManager* mainSceneManager);
|
||||
RippleSimulation(Ogre::SceneManager* mainSceneManager, const MWWorld::Fallback* fallback);
|
||||
~RippleSimulation();
|
||||
|
||||
/// @param dt Time since the last frame
|
||||
/// @param position Position of the player
|
||||
void update(float dt, Ogre::Vector2 position);
|
||||
|
||||
/// adds an emitter, position will be tracked automatically
|
||||
|
@ -40,47 +42,21 @@ public:
|
|||
void removeEmitter (const MWWorld::Ptr& ptr);
|
||||
void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr);
|
||||
|
||||
/// Change the height of the water surface, thus moving all ripples with it
|
||||
void setWaterHeight(float height);
|
||||
|
||||
/// Remove all active ripples
|
||||
void clear();
|
||||
|
||||
private:
|
||||
RippleSimulation(const RippleSimulation&);
|
||||
RippleSimulation& operator=(const RippleSimulation&);
|
||||
Ogre::SceneManager* mSceneMgr;
|
||||
Ogre::ParticleSystem* mParticleSystem;
|
||||
Ogre::SceneNode* mSceneNode;
|
||||
|
||||
std::vector<Emitter> mEmitters;
|
||||
|
||||
Ogre::RenderTexture* mRenderTargets[4];
|
||||
Ogre::TexturePtr mTextures[4];
|
||||
|
||||
int mTextureSize;
|
||||
float mRippleAreaLength;
|
||||
float mImpulseSize;
|
||||
|
||||
bool mFirstUpdate;
|
||||
|
||||
Ogre::Camera* mCamera;
|
||||
|
||||
// own scenemanager to render our simulation
|
||||
Ogre::SceneManager* mSceneMgr;
|
||||
Ogre::Rectangle2D* mRectangle;
|
||||
|
||||
// scenemanager to create the debug overlays on
|
||||
Ogre::SceneManager* mMainSceneMgr;
|
||||
|
||||
static const int TEX_NORMAL = 3;
|
||||
|
||||
Ogre::Rectangle2D* mImpulse;
|
||||
|
||||
void addImpulses();
|
||||
void heightMapToNormalMap();
|
||||
void waterSimulation();
|
||||
void swapHeightMaps();
|
||||
|
||||
float mTime;
|
||||
|
||||
Ogre::Vector2 mRippleCenter;
|
||||
|
||||
Ogre::Vector2 mTexelOffset;
|
||||
|
||||
Ogre::Vector2 mCurrentFrameOffset;
|
||||
Ogre::Vector2 mPreviousFrameOffset;
|
||||
float mRippleLifeTime;
|
||||
float mRippleRotSpeed;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -20,9 +20,6 @@
|
|||
#include <extern/shiny/Main/Factory.hpp>
|
||||
#include <extern/shiny/Platforms/Ogre/OgreMaterial.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
using namespace Ogre;
|
||||
|
||||
namespace MWRender
|
||||
|
@ -187,7 +184,7 @@ void PlaneReflection::setVisibilityMask (int flags)
|
|||
|
||||
// --------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Water::Water (Ogre::Camera *camera, RenderingManager* rend) :
|
||||
Water::Water (Ogre::Camera *camera, RenderingManager* rend, const MWWorld::Fallback* fallback) :
|
||||
mCamera (camera), mSceneMgr (camera->getSceneManager()),
|
||||
mIsUnderwater(false), mVisibilityFlags(0),
|
||||
mActive(1), mToggled(1),
|
||||
|
@ -198,7 +195,7 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend) :
|
|||
mSimulation(NULL),
|
||||
mPlayer(0,0)
|
||||
{
|
||||
mSimulation = new RippleSimulation(mSceneMgr);
|
||||
mSimulation = new RippleSimulation(mSceneMgr, fallback);
|
||||
|
||||
mSky = rend->getSkyManager();
|
||||
|
||||
|
@ -313,6 +310,8 @@ void Water::setHeight(const float height)
|
|||
{
|
||||
mTop = height;
|
||||
|
||||
mSimulation->setWaterHeight(height);
|
||||
|
||||
mWaterPlane = Plane(Vector3::UNIT_Z, -height);
|
||||
|
||||
if (mReflection)
|
||||
|
@ -382,9 +381,13 @@ void Water::update(float dt, Ogre::Vector3 player)
|
|||
|
||||
void Water::frameStarted(float dt)
|
||||
{
|
||||
if (!mActive)
|
||||
return;
|
||||
|
||||
mSimulation->update(dt, mPlayer);
|
||||
|
||||
if (mReflection)
|
||||
{
|
||||
mSimulation->update(dt, mPlayer);
|
||||
mReflection->update();
|
||||
}
|
||||
}
|
||||
|
@ -497,4 +500,9 @@ void Water::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr)
|
|||
mSimulation->updateEmitterPtr(old, ptr);
|
||||
}
|
||||
|
||||
void Water::clearRipples()
|
||||
{
|
||||
mSimulation->clear();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -30,6 +30,11 @@ namespace Ogre
|
|||
struct RenderTargetEvent;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Fallback;
|
||||
}
|
||||
|
||||
namespace MWRender {
|
||||
|
||||
class SkyManager;
|
||||
|
@ -145,9 +150,11 @@ namespace MWRender {
|
|||
Ogre::Vector2 mPlayer;
|
||||
|
||||
public:
|
||||
Water (Ogre::Camera *camera, RenderingManager* rend);
|
||||
Water (Ogre::Camera *camera, RenderingManager* rend, const MWWorld::Fallback* fallback);
|
||||
~Water();
|
||||
|
||||
void clearRipples();
|
||||
|
||||
void setActive(bool active);
|
||||
|
||||
bool toggle();
|
||||
|
|
|
@ -22,6 +22,15 @@ namespace MWWorld
|
|||
else
|
||||
return boost::lexical_cast<float>(fallback);
|
||||
}
|
||||
int Fallback::getFallbackInt(const std::string& fall) const
|
||||
{
|
||||
std::string fallback=getFallbackString(fall);
|
||||
if(fallback.empty())
|
||||
return 0;
|
||||
else
|
||||
return boost::lexical_cast<int>(fallback);
|
||||
}
|
||||
|
||||
bool Fallback::getFallbackBool(const std::string& fall) const
|
||||
{
|
||||
std::string fallback=getFallbackString(fall);
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace MWWorld
|
|||
Fallback(const std::map<std::string,std::string>& fallback);
|
||||
std::string getFallbackString(const std::string& fall) const;
|
||||
float getFallbackFloat(const std::string& fall) const;
|
||||
int getFallbackInt(const std::string& fall) const;
|
||||
bool getFallbackBool(const std::string& fall) const;
|
||||
Ogre::ColourValue getFallbackColour(const std::string& fall) const;
|
||||
};
|
||||
|
|
|
@ -38,15 +38,10 @@ set(MATERIAL_FILES
|
|||
selection.mat
|
||||
selection.shader
|
||||
selection.shaderset
|
||||
watersim_heightmap.shader
|
||||
watersim_addimpulse.shader
|
||||
watersim_heighttonormal.shader
|
||||
watersim_common.h
|
||||
watersim.mat
|
||||
watersim.shaderset
|
||||
mygui.mat
|
||||
mygui.shader
|
||||
mygui.shaderset
|
||||
ripples.particle
|
||||
)
|
||||
|
||||
copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/water "${OpenMW_BINARY_DIR}/resources/water/" "${WATER_FILES}")
|
||||
|
|
26
files/materials/ripples.particle
Normal file
26
files/materials/ripples.particle
Normal file
|
@ -0,0 +1,26 @@
|
|||
particle_system openmw/Ripples
|
||||
{
|
||||
material openmw/Ripple
|
||||
particle_width 50
|
||||
particle_height 50
|
||||
// To make the particles move with the scene node when the waterlevel changes
|
||||
local_space true
|
||||
quota 300
|
||||
billboard_type perpendicular_common
|
||||
common_up_vector 0 1 0
|
||||
common_direction 0 0 1
|
||||
|
||||
affector ColourFader
|
||||
{
|
||||
alpha -0.33
|
||||
}
|
||||
|
||||
affector Scaler
|
||||
{
|
||||
rate 100
|
||||
}
|
||||
|
||||
affector Rotator
|
||||
{
|
||||
}
|
||||
}
|
|
@ -75,3 +75,27 @@ material Water
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
material openmw/Ripple
|
||||
{
|
||||
// this will be overridden by Water_RippleFrameCount fallback setting
|
||||
anim_texture2 textures\water\ripple.dds 4 0.25
|
||||
pass
|
||||
{
|
||||
scene_blend alpha_blend
|
||||
depth_write off
|
||||
cull_hardware none
|
||||
diffuse vertexcolour
|
||||
emissive 1 1 1
|
||||
ambient 0 0 0
|
||||
texture_unit diffuseMap
|
||||
{
|
||||
create_in_ffp true
|
||||
anim_texture2 $anim_texture2
|
||||
|
||||
// to make sure rotating doesn't cause the texture to repeat
|
||||
tex_address_mode border
|
||||
tex_border_colour 0 0 0 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,6 @@
|
|||
#include "shadows.h"
|
||||
#endif
|
||||
|
||||
#define RIPPLES 1
|
||||
#define REFRACTION @shGlobalSettingBool(refraction)
|
||||
|
||||
#ifdef SH_VERTEX_SHADER
|
||||
|
@ -187,11 +186,6 @@
|
|||
shInput(float3, screenCoordsPassthrough)
|
||||
shInput(float4, position)
|
||||
shInput(float, depthPassthrough)
|
||||
|
||||
#if RIPPLES
|
||||
shUniform(float3, rippleCenter) @shSharedParameter(rippleCenter, rippleCenter)
|
||||
shUniform(float, rippleAreaLength) @shSharedParameter(rippleAreaLength, rippleAreaLength)
|
||||
#endif
|
||||
|
||||
shUniform(float, far) @shAutoConstant(far, far_clip_distance)
|
||||
|
||||
|
@ -203,10 +197,6 @@
|
|||
shSampler2D(normalMap)
|
||||
|
||||
shUniform(float4x4, wMat) @shAutoConstant(wMat, world_matrix)
|
||||
|
||||
#if RIPPLES
|
||||
shSampler2D(rippleNormalMap)
|
||||
#endif
|
||||
|
||||
shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed)
|
||||
#define WIND_SPEED windDir_windSpeed.z
|
||||
|
@ -296,12 +286,7 @@
|
|||
normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y +
|
||||
normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y);
|
||||
|
||||
float4 worldPosition = shMatrixMult(wMat, float4(position.xyz, 1));
|
||||
float2 relPos = (worldPosition.xy - rippleCenter.xy) / rippleAreaLength + 0.5;
|
||||
float3 normal_ripple = normalize(shSample(rippleNormalMap, relPos.xy).xyz * 2.0 - 1.0);
|
||||
|
||||
//normal = normalize(normal + normal_ripple);
|
||||
normal = normalize(float3(normal.x * BUMP + normal_ripple.x, normal.y * BUMP + normal_ripple.y, normal.z));
|
||||
normal = normalize(float3(normal.x * BUMP, normal.y * BUMP, normal.z));
|
||||
normal = float3(normal.x, normal.y, -normal.z);
|
||||
|
||||
// normal for sunlight scattering
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
material HeightmapSimulation
|
||||
{
|
||||
allow_fixed_function false
|
||||
pass
|
||||
{
|
||||
depth_check off
|
||||
depth_write off
|
||||
vertex_program transform_vertex
|
||||
fragment_program watersim_fragment
|
||||
|
||||
texture_unit heightPrevSampler
|
||||
{
|
||||
tex_address_mode border
|
||||
tex_border_colour 0 0 0
|
||||
texture_alias Heightmap0
|
||||
}
|
||||
texture_unit heightCurrentSampler
|
||||
{
|
||||
tex_address_mode border
|
||||
tex_border_colour 0 0 0
|
||||
texture_alias Heightmap1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
material HeightToNormalMap
|
||||
{
|
||||
allow_fixed_function false
|
||||
pass
|
||||
{
|
||||
depth_check off
|
||||
depth_write off
|
||||
vertex_program transform_vertex
|
||||
fragment_program height_to_normal_fragment
|
||||
|
||||
texture_unit heightCurrentSampler
|
||||
{
|
||||
texture_alias Heightmap2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
material AddImpulse
|
||||
{
|
||||
allow_fixed_function false
|
||||
pass
|
||||
{
|
||||
depth_check off
|
||||
depth_write off
|
||||
scene_blend alpha_blend
|
||||
vertex_program transform_vertex
|
||||
fragment_program add_impulse_fragment
|
||||
|
||||
texture_unit alphaMap
|
||||
{
|
||||
texture circle.png
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
shader_set transform_vertex
|
||||
{
|
||||
source quad.shader
|
||||
type vertex
|
||||
profiles_cg vs_2_0 vp40 arbvp1
|
||||
profiles_hlsl vs_2_0
|
||||
}
|
||||
|
||||
shader_set watersim_fragment
|
||||
{
|
||||
source watersim_heightmap.shader
|
||||
type fragment
|
||||
profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1
|
||||
profiles_hlsl ps_3_0 ps_2_0
|
||||
}
|
||||
|
||||
shader_set height_to_normal_fragment
|
||||
{
|
||||
source watersim_heighttonormal.shader
|
||||
type fragment
|
||||
profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1
|
||||
profiles_hlsl ps_3_0 ps_2_0
|
||||
}
|
||||
|
||||
shader_set add_impulse_fragment
|
||||
{
|
||||
source watersim_addimpulse.shader
|
||||
type fragment
|
||||
profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1
|
||||
profiles_hlsl ps_3_0 ps_2_0
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#include "core.h"
|
||||
#include "watersim_common.h"
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shInput(float2, UV)
|
||||
shSampler2D(alphaMap)
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
shOutputColour(0) = EncodeHeightmap(1.0);
|
||||
shOutputColour(0).a = shSample (alphaMap, UV.xy).a;
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
float DecodeHeightmap(float4 heightmap)
|
||||
{
|
||||
float4 table = float4(1.0, -1.0, 0.0, 0.0);
|
||||
return dot(heightmap, table);
|
||||
}
|
||||
|
||||
float DecodeHeightmap(shTexture2D HeightmapSampler, float2 texcoord)
|
||||
{
|
||||
float4 heightmap = shSample(HeightmapSampler, texcoord);
|
||||
return DecodeHeightmap(heightmap);
|
||||
}
|
||||
|
||||
float4 EncodeHeightmap(float fHeight)
|
||||
{
|
||||
float h = fHeight;
|
||||
float positive = fHeight > 0.0 ? fHeight : 0.0;
|
||||
float negative = fHeight < 0.0 ? -fHeight : 0.0;
|
||||
|
||||
float4 color = float4(0,0,0,0);
|
||||
|
||||
color.r = positive;
|
||||
color.g = negative;
|
||||
|
||||
return color;
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
#include "core.h"
|
||||
|
||||
#define DAMPING 0.95
|
||||
|
||||
#include "watersim_common.h"
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shInput(float2, UV)
|
||||
shSampler2D(heightPrevSampler)
|
||||
shSampler2D(heightCurrentSampler)
|
||||
shUniform(float3, previousFrameOffset) @shSharedParameter(previousFrameOffset, previousFrameOffset)
|
||||
shUniform(float3, currentFrameOffset) @shSharedParameter(currentFrameOffset, currentFrameOffset)
|
||||
shUniform(float4, rippleTextureSize) @shSharedParameter(rippleTextureSize, rippleTextureSize)
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
#if !SH_HLSL
|
||||
const float3 offset[4] = float3[4](
|
||||
float3(-1.0, 0.0, 0.25),
|
||||
float3( 1.0, 0.0, 0.25),
|
||||
float3( 0.0,-1.0, 0.25),
|
||||
float3( 0.0, 1.0, 0.25)
|
||||
);
|
||||
#else
|
||||
const float3 offset[4] = {
|
||||
float3(-1.0, 0.0, 0.25),
|
||||
float3( 1.0, 0.0, 0.25),
|
||||
float3( 0.0,-1.0, 0.25),
|
||||
float3( 0.0, 1.0, 0.25)
|
||||
};
|
||||
#endif
|
||||
|
||||
float fHeightPrev = DecodeHeightmap(heightPrevSampler, UV.xy + previousFrameOffset.xy + currentFrameOffset.xy);
|
||||
|
||||
float fNeighCurrent = 0;
|
||||
for ( int i=0; i<4; i++ )
|
||||
{
|
||||
float2 vTexcoord = UV + currentFrameOffset.xy + offset[i].xy * rippleTextureSize.xy;
|
||||
fNeighCurrent += (DecodeHeightmap(heightCurrentSampler, vTexcoord) * offset[i].z);
|
||||
}
|
||||
|
||||
float fHeight = fNeighCurrent * 2.0 - fHeightPrev;
|
||||
|
||||
fHeight *= DAMPING;
|
||||
|
||||
shOutputColour(0) = EncodeHeightmap(fHeight);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#include "core.h"
|
||||
#include "watersim_common.h"
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shInput(float2, UV)
|
||||
shSampler2D(heightCurrentSampler)
|
||||
shUniform(float4, rippleTextureSize) @shSharedParameter(rippleTextureSize, rippleTextureSize)
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
#if !SH_HLSL
|
||||
float2 offset[4] = float2[4] (
|
||||
float2(-1.0, 0.0),
|
||||
float2( 1.0, 0.0),
|
||||
float2( 0.0,-1.0),
|
||||
float2( 0.0, 1.0)
|
||||
);
|
||||
#else
|
||||
float2 offset[4] = {
|
||||
float2(-1.0, 0.0),
|
||||
float2( 1.0, 0.0),
|
||||
float2( 0.0,-1.0),
|
||||
float2( 0.0, 1.0)
|
||||
};
|
||||
#endif
|
||||
|
||||
float fHeightL = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[0]*rippleTextureSize.xy);
|
||||
float fHeightR = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[1]*rippleTextureSize.xy);
|
||||
float fHeightT = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[2]*rippleTextureSize.xy);
|
||||
float fHeightB = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[3]*rippleTextureSize.xy);
|
||||
|
||||
float3 n = float3(fHeightB - fHeightT, fHeightR - fHeightL, 1.0);
|
||||
float3 normal = (n + 1.0) * 0.5;
|
||||
|
||||
shOutputColour(0) = float4(normal.rgb, 1.0);
|
||||
}
|
Loading…
Reference in a new issue