@ -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"
namespace MWRender
{
# include "../mwworld/fallback.hpp"
# include "renderconst.hpp"
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 )
namespace MWRender
{
Ogre : : AxisAlignedBox aabInf ;
aabInf . setInfinite ( ) ;
mSceneMgr = Ogre : : Root : : getSingleton ( ) . createSceneManager ( Ogre : : ST_GENERIC ) ;
mCamera = mSceneMgr - > createCamera ( " RippleCamera " ) ;
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 ) ;
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 ) ;
//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 ) ;
Ogre : : RenderTexture * rt = texture - > getBuffer ( ) - > getRenderTarget ( ) ;
rt - > removeAllViewports ( ) ;
rt - > addViewport ( mCamera ) ;
rt - > setAutoUpdated ( false ) ;
rt - > getViewport ( 0 ) - > setClearEveryFrame ( false ) ;
RippleSimulation : : RippleSimulation ( Ogre : : SceneManager * mainSceneManager , const MWWorld : : Fallback * fallback )
: mSceneMgr ( mainSceneManager )
, mParticleSystem ( NULL )
, mSceneNode ( NULL )
{
mRippleLifeTime = fallback - > getFallbackFloat ( " Water_RippleLifetime " ) ;
mRippleRotSpeed = fallback - > getFallbackFloat ( " Water_RippleRotSpeed " ) ;
// 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 ) ;
// Unknown:
// fallback=Water_RippleScale,0.15, 6.5
// fallback=Water_RippleAlphas,0.7, 0.1, 0.01
Ogre : : MaterialPtr debugMaterial = Ogre : : MaterialManager : : getSingleton ( ) . create ( " RippleDebug " + Ogre : : StringConverter : : toString ( i ) ,
Ogre : : ResourceGroupManager : : DEFAULT_RESOURCE_GROUP_NAME ) ;
// Instantiate from ripples.particle file
mParticleSystem = mSceneMgr - > createParticleSystem ( " openmw/Ripples " , " openmw/Ripples " ) ;
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 ) ;
mParticleSystem - > setRenderQueueGroup ( RQG_Ripples ) ;
mParticleSystem - > setVisibilityFlags ( RV_Effects ) ;
debugOverlay - > setMaterial ( " RippleDebug " + Ogre : : StringConverter : : toString ( i ) ) ;
*/
int rippleFrameCount = fallback - > getFallbackInt ( " Water_RippleFrameCount " ) ;
std : : string tex = fallback - > getFallbackString ( " Water_RippleTexture " ) ;
mRenderTargets [ i ] = rt ;
mTextures [ i ] = texture ;
}
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 ) ) ) ) ;
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 ) ) ) ;
// seems to be required to allocate mFreeParticles. TODO: patch Ogre to handle this better
mParticleSystem - > _update ( 0.f ) ;
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 ( ) ;
}
}