2012-01-14 23:34:14 +00:00
# include "water.hpp"
2012-05-29 04:45:44 +00:00
2015-06-02 14:35:35 +00:00
# include <iomanip>
2012-01-14 23:34:14 +00:00
2015-12-06 14:56:30 +00:00
# include <osg/Fog>
2015-10-26 20:36:19 +00:00
# include <osg/Depth>
2015-06-02 14:35:35 +00:00
# include <osg/Group>
# include <osg/Geometry>
# include <osg/Material>
# include <osg/PositionAttitudeTransform>
2015-10-26 20:36:19 +00:00
# include <osg/ClipNode>
# include <osg/FrontFace>
2015-10-28 18:57:20 +00:00
# include <osgDB/ReadFile>
2013-01-16 08:13:36 +00:00
2015-10-28 18:25:46 +00:00
# include <boost/filesystem/path.hpp>
# include <boost/filesystem/fstream.hpp>
2015-06-02 23:18:36 +00:00
# include <osgUtil/IncrementalCompileOperation>
2015-10-26 20:36:19 +00:00
# include <osgUtil/CullVisitor>
2015-06-02 23:18:36 +00:00
2018-08-14 19:05:43 +00:00
# include <components/debug/debuglog.hpp>
2015-06-02 14:35:35 +00:00
# include <components/resource/resourcesystem.hpp>
2016-02-05 22:03:53 +00:00
# include <components/resource/imagemanager.hpp>
2016-09-28 19:10:44 +00:00
# include <components/resource/scenemanager.hpp>
2013-01-16 08:13:36 +00:00
2018-10-30 22:34:53 +00:00
# include <components/sceneutil/shadow.hpp>
2016-08-05 17:25:05 +00:00
# include <components/sceneutil/waterutil.hpp>
2018-09-17 10:52:43 +00:00
# include <components/misc/constants.hpp>
2015-06-02 14:35:35 +00:00
# include <components/nifosg/controller.hpp>
2013-01-16 08:13:36 +00:00
2017-01-15 13:17:22 +00:00
# include <components/shader/shadermanager.hpp>
2015-07-25 02:14:22 +00:00
# include <components/esm/loadcell.hpp>
2016-01-06 11:46:06 +00:00
# include <components/fallback/fallback.hpp>
2015-11-03 01:17:42 +00:00
# include "../mwworld/cellstore.hpp"
2015-06-02 14:35:35 +00:00
# include "vismask.hpp"
2015-06-16 18:36:48 +00:00
# include "ripplesimulation.hpp"
2015-09-19 16:19:36 +00:00
# include "renderbin.hpp"
2016-01-29 16:00:18 +00:00
# include "util.hpp"
2013-01-16 08:13:36 +00:00
2015-06-02 14:35:35 +00:00
namespace MWRender
2012-03-29 16:33:08 +00:00
{
2013-02-03 14:46:23 +00:00
2015-06-02 14:35:35 +00:00
// --------------------------------------------------------------------------------------------------------------------------------
2012-01-20 22:59:56 +00:00
2015-10-26 20:36:19 +00:00
/// @brief Allows to cull and clip meshes that are below a plane. Useful for reflection & refraction camera effects.
/// Also handles flipping of the plane when the eye point goes below it.
/// To use, simply create the scene as subgraph of this node, then do setPlane(const osg::Plane& plane);
class ClipCullNode : public osg : : Group
{
class PlaneCullCallback : public osg : : NodeCallback
{
public :
/// @param cullPlane The culling plane (in world space).
PlaneCullCallback ( const osg : : Plane * cullPlane )
: osg : : NodeCallback ( )
, mCullPlane ( cullPlane )
{
}
virtual void operator ( ) ( osg : : Node * node , osg : : NodeVisitor * nv )
{
osgUtil : : CullVisitor * cv = static_cast < osgUtil : : CullVisitor * > ( nv ) ;
osg : : Polytope : : PlaneList origPlaneList = cv - > getProjectionCullingStack ( ) . back ( ) . getFrustum ( ) . getPlaneList ( ) ;
osg : : Plane plane = * mCullPlane ;
plane . transform ( * cv - > getCurrentRenderStage ( ) - > getInitialViewMatrix ( ) ) ;
osg : : Vec3d eyePoint = cv - > getEyePoint ( ) ;
if ( mCullPlane - > intersect ( osg : : BoundingSphere ( osg : : Vec3d ( 0 , 0 , eyePoint . z ( ) ) , 0 ) ) > 0 )
plane . flip ( ) ;
cv - > getProjectionCullingStack ( ) . back ( ) . getFrustum ( ) . add ( plane ) ;
traverse ( node , nv ) ;
// undo
cv - > getProjectionCullingStack ( ) . back ( ) . getFrustum ( ) . set ( origPlaneList ) ;
}
private :
const osg : : Plane * mCullPlane ;
} ;
class FlipCallback : public osg : : NodeCallback
{
public :
FlipCallback ( const osg : : Plane * cullPlane )
: mCullPlane ( cullPlane )
{
}
virtual void operator ( ) ( osg : : Node * node , osg : : NodeVisitor * nv )
{
osgUtil : : CullVisitor * cv = static_cast < osgUtil : : CullVisitor * > ( nv ) ;
osg : : Vec3d eyePoint = cv - > getEyePoint ( ) ;
2015-10-28 17:54:49 +00:00
osg : : RefMatrix * modelViewMatrix = new osg : : RefMatrix ( * cv - > getModelViewMatrix ( ) ) ;
2015-11-03 22:10:52 +00:00
// apply the height of the plane
2015-11-02 00:23:21 +00:00
// we can't apply this height in the addClipPlane() since the "flip the below graph" function would otherwise flip the height as well
2015-11-03 22:10:52 +00:00
modelViewMatrix - > preMultTranslate ( mCullPlane - > getNormal ( ) * ( ( * mCullPlane ) [ 3 ] * - 1 ) ) ;
2015-11-02 00:23:21 +00:00
2015-10-26 20:36:19 +00:00
// flip the below graph if the eye point is above the plane
if ( mCullPlane - > intersect ( osg : : BoundingSphere ( osg : : Vec3d ( 0 , 0 , eyePoint . z ( ) ) , 0 ) ) > 0 )
{
modelViewMatrix - > preMultScale ( osg : : Vec3 ( 1 , 1 , - 1 ) ) ;
}
2015-10-28 17:54:49 +00:00
2015-11-03 22:10:52 +00:00
// move the plane back along its normal a little bit to prevent bleeding at the water shore
2017-09-26 12:14:28 +00:00
const float clipFudge = - 5 ;
2015-11-03 22:10:52 +00:00
modelViewMatrix - > preMultTranslate ( mCullPlane - > getNormal ( ) * clipFudge ) ;
2015-10-28 17:54:49 +00:00
cv - > pushModelViewMatrix ( modelViewMatrix , osg : : Transform : : RELATIVE_RF ) ;
traverse ( node , nv ) ;
cv - > popModelViewMatrix ( ) ;
2015-10-26 20:36:19 +00:00
}
private :
const osg : : Plane * mCullPlane ;
} ;
public :
ClipCullNode ( )
{
addCullCallback ( new PlaneCullCallback ( & mPlane ) ) ;
2015-11-02 00:23:21 +00:00
mClipNodeTransform = new osg : : Group ;
2015-10-26 20:36:19 +00:00
mClipNodeTransform - > addCullCallback ( new FlipCallback ( & mPlane ) ) ;
addChild ( mClipNodeTransform ) ;
mClipNode = new osg : : ClipNode ;
mClipNodeTransform - > addChild ( mClipNode ) ;
}
void setPlane ( const osg : : Plane & plane )
{
if ( plane = = mPlane )
return ;
mPlane = plane ;
mClipNode - > getClipPlaneList ( ) . clear ( ) ;
2015-11-02 00:23:21 +00:00
mClipNode - > addClipPlane ( new osg : : ClipPlane ( 0 , osg : : Plane ( mPlane . getNormal ( ) , 0 ) ) ) ; // mPlane.d() applied in FlipCallback
2015-10-26 20:36:19 +00:00
mClipNode - > setStateSetModes ( * getOrCreateStateSet ( ) , osg : : StateAttribute : : ON ) ;
2015-11-20 01:22:37 +00:00
mClipNode - > setCullingActive ( false ) ;
2015-10-26 20:36:19 +00:00
}
private :
2015-11-02 00:23:21 +00:00
osg : : ref_ptr < osg : : Group > mClipNodeTransform ;
2015-10-26 20:36:19 +00:00
osg : : ref_ptr < osg : : ClipNode > mClipNode ;
osg : : Plane mPlane ;
} ;
2019-02-20 13:37:00 +00:00
/// This callback on the Camera has the effect of a RELATIVE_RF_INHERIT_VIEWPOINT transform mode (which does not exist in OSG).
/// We want to keep the View Point of the parent camera so we will not have to recreate LODs.
class InheritViewPointCallback : public osg : : NodeCallback
{
public :
InheritViewPointCallback ( ) { }
virtual void operator ( ) ( osg : : Node * node , osg : : NodeVisitor * nv )
{
osgUtil : : CullVisitor * cv = static_cast < osgUtil : : CullVisitor * > ( nv ) ;
osg : : ref_ptr < osg : : RefMatrix > modelViewMatrix = new osg : : RefMatrix ( * cv - > getModelViewMatrix ( ) ) ;
cv - > popModelViewMatrix ( ) ;
cv - > pushModelViewMatrix ( modelViewMatrix , osg : : Transform : : ABSOLUTE_RF_INHERIT_VIEWPOINT ) ;
traverse ( node , nv ) ;
}
} ;
2015-11-02 00:23:21 +00:00
/// Moves water mesh away from the camera slightly if the camera gets too close on the Z axis.
2016-04-07 09:55:49 +00:00
/// The offset works around graphics artifacts that occurred with the GL_DEPTH_CLAMP when the camera gets extremely close to the mesh (seen on NVIDIA at least).
2015-11-01 23:30:18 +00:00
/// Must be added as a Cull callback.
class FudgeCallback : public osg : : NodeCallback
{
public :
virtual void operator ( ) ( osg : : Node * node , osg : : NodeVisitor * nv )
{
osgUtil : : CullVisitor * cv = static_cast < osgUtil : : CullVisitor * > ( nv ) ;
2015-11-02 00:23:21 +00:00
const float fudge = 0.2 ;
2015-11-01 23:30:18 +00:00
if ( std : : abs ( cv - > getEyeLocal ( ) . z ( ) ) < fudge )
{
float diff = fudge - cv - > getEyeLocal ( ) . z ( ) ;
osg : : RefMatrix * modelViewMatrix = new osg : : RefMatrix ( * cv - > getModelViewMatrix ( ) ) ;
if ( cv - > getEyeLocal ( ) . z ( ) > 0 )
modelViewMatrix - > preMultTranslate ( osg : : Vec3f ( 0 , 0 , - diff ) ) ;
else
modelViewMatrix - > preMultTranslate ( osg : : Vec3f ( 0 , 0 , diff ) ) ;
cv - > pushModelViewMatrix ( modelViewMatrix , osg : : Transform : : RELATIVE_RF ) ;
traverse ( node , nv ) ;
cv - > popModelViewMatrix ( ) ;
}
else
traverse ( node , nv ) ;
}
} ;
2015-10-28 18:25:46 +00:00
osg : : ref_ptr < osg : : Image > readPngImage ( const std : : string & file )
{
// use boost in favor of osgDB::readImage, to handle utf-8 path issues on Windows
boost : : filesystem : : ifstream inStream ;
inStream . open ( file , std : : ios_base : : in | std : : ios_base : : binary ) ;
if ( inStream . fail ( ) )
2018-08-14 19:05:43 +00:00
Log ( Debug : : Error ) < < " Error: Failed to open " < < file ;
2015-10-28 18:25:46 +00:00
osgDB : : ReaderWriter * reader = osgDB : : Registry : : instance ( ) - > getReaderWriterForExtension ( " png " ) ;
if ( ! reader )
{
2018-08-14 19:05:43 +00:00
Log ( Debug : : Error ) < < " Error: Failed to read " < < file < < " , no png readerwriter found " ;
2015-10-28 18:25:46 +00:00
return osg : : ref_ptr < osg : : Image > ( ) ;
}
osgDB : : ReaderWriter : : ReadResult result = reader - > readImage ( inStream ) ;
if ( ! result . success ( ) )
2018-08-14 19:05:43 +00:00
Log ( Debug : : Error ) < < " Error: Failed to read " < < file < < " : " < < result . message ( ) < < " code " < < result . status ( ) ;
2015-10-28 18:25:46 +00:00
return result . getImage ( ) ;
}
2015-10-28 18:57:58 +00:00
class Refraction : public osg : : Camera
{
public :
Refraction ( )
{
unsigned int rttSize = Settings : : Manager : : getInt ( " rtt size " , " Water " ) ;
setRenderOrder ( osg : : Camera : : PRE_RENDER ) ;
setClearMask ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
setRenderTargetImplementation ( osg : : Camera : : FRAME_BUFFER_OBJECT ) ;
setReferenceFrame ( osg : : Camera : : RELATIVE_RF ) ;
2017-02-01 04:55:33 +00:00
setSmallFeatureCullingPixelSize ( Settings : : Manager : : getInt ( " small feature culling pixel size " , " Water " ) ) ;
2017-02-02 20:46:25 +00:00
setName ( " RefractionCamera " ) ;
2019-02-20 13:37:00 +00:00
setCullCallback ( new InheritViewPointCallback ) ;
2015-10-28 18:57:58 +00:00
2019-02-21 16:14:18 +00:00
setCullMask ( Mask_Effect | Mask_Scene | Mask_Object | Mask_Static | Mask_Terrain | Mask_Actor | Mask_ParticleSystem | Mask_Sky | Mask_Sun | Mask_Player | Mask_Lighting ) ;
2015-10-28 18:57:58 +00:00
setNodeMask ( Mask_RenderToTexture ) ;
setViewport ( 0 , 0 , rttSize , rttSize ) ;
// No need for Update traversal since the scene is already updated as part of the main scene graph
// A double update would mess with the light collection (in addition to being plain redundant)
setUpdateCallback ( new NoTraverseCallback ) ;
// No need for fog here, we are already applying fog on the water surface itself as well as underwater fog
2016-02-17 00:56:41 +00:00
// assign large value to effectively turn off fog
// shaders don't respect glDisable(GL_FOG)
osg : : ref_ptr < osg : : Fog > fog ( new osg : : Fog ) ;
fog - > setStart ( 10000000 ) ;
fog - > setEnd ( 10000000 ) ;
getOrCreateStateSet ( ) - > setAttributeAndModes ( fog , osg : : StateAttribute : : OFF | osg : : StateAttribute : : OVERRIDE ) ;
2015-10-28 18:57:58 +00:00
mClipCullNode = new ClipCullNode ;
addChild ( mClipCullNode ) ;
mRefractionTexture = new osg : : Texture2D ;
mRefractionTexture - > setTextureSize ( rttSize , rttSize ) ;
mRefractionTexture - > setWrap ( osg : : Texture : : WRAP_S , osg : : Texture : : CLAMP_TO_EDGE ) ;
mRefractionTexture - > setWrap ( osg : : Texture : : WRAP_T , osg : : Texture : : CLAMP_TO_EDGE ) ;
mRefractionTexture - > setInternalFormat ( GL_RGB ) ;
mRefractionTexture - > setFilter ( osg : : Texture : : MIN_FILTER , osg : : Texture : : LINEAR ) ;
mRefractionTexture - > setFilter ( osg : : Texture : : MAG_FILTER , osg : : Texture : : LINEAR ) ;
attach ( osg : : Camera : : COLOR_BUFFER , mRefractionTexture ) ;
mRefractionDepthTexture = new osg : : Texture2D ;
mRefractionDepthTexture - > setSourceFormat ( GL_DEPTH_COMPONENT ) ;
2015-10-29 12:52:48 +00:00
mRefractionDepthTexture - > setInternalFormat ( GL_DEPTH_COMPONENT24 ) ;
2015-10-28 18:57:58 +00:00
mRefractionDepthTexture - > setWrap ( osg : : Texture : : WRAP_S , osg : : Texture : : CLAMP_TO_EDGE ) ;
mRefractionDepthTexture - > setWrap ( osg : : Texture : : WRAP_T , osg : : Texture : : CLAMP_TO_EDGE ) ;
mRefractionDepthTexture - > setSourceType ( GL_UNSIGNED_INT ) ;
mRefractionDepthTexture - > setFilter ( osg : : Texture : : MIN_FILTER , osg : : Texture : : LINEAR ) ;
mRefractionDepthTexture - > setFilter ( osg : : Texture : : MAG_FILTER , osg : : Texture : : LINEAR ) ;
attach ( osg : : Camera : : DEPTH_BUFFER , mRefractionDepthTexture ) ;
2018-10-30 22:34:53 +00:00
SceneUtil : : ShadowManager : : disableShadowsForStateSet ( getOrCreateStateSet ( ) ) ;
2015-10-28 18:57:58 +00:00
}
void setScene ( osg : : Node * scene )
{
if ( mScene )
mClipCullNode - > removeChild ( mScene ) ;
mScene = scene ;
mClipCullNode - > addChild ( scene ) ;
}
void setWaterLevel ( float waterLevel )
{
2018-01-01 12:28:14 +00:00
const float refractionScale = std : : min ( 1.0f , std : : max ( 0.0f ,
2017-12-06 19:39:09 +00:00
Settings : : Manager : : getFloat ( " refraction scale " , " Water " ) ) ) ;
setViewMatrix ( osg : : Matrix : : scale ( 1 , 1 , refractionScale ) *
osg : : Matrix : : translate ( 0 , 0 , ( 1.0 - refractionScale ) * waterLevel ) ) ;
2015-10-28 18:57:58 +00:00
mClipCullNode - > setPlane ( osg : : Plane ( osg : : Vec3d ( 0 , 0 , - 1 ) , osg : : Vec3d ( 0 , 0 , waterLevel ) ) ) ;
}
osg : : Texture2D * getRefractionTexture ( ) const
{
return mRefractionTexture . get ( ) ;
}
osg : : Texture2D * getRefractionDepthTexture ( ) const
{
return mRefractionDepthTexture . get ( ) ;
}
private :
osg : : ref_ptr < ClipCullNode > mClipCullNode ;
osg : : ref_ptr < osg : : Texture2D > mRefractionTexture ;
osg : : ref_ptr < osg : : Texture2D > mRefractionDepthTexture ;
osg : : ref_ptr < osg : : Node > mScene ;
} ;
class Reflection : public osg : : Camera
{
2015-10-28 19:09:44 +00:00
public :
2019-02-21 16:14:18 +00:00
Reflection ( bool isInterior )
2015-10-28 19:09:44 +00:00
{
setRenderOrder ( osg : : Camera : : PRE_RENDER ) ;
setClearMask ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
setRenderTargetImplementation ( osg : : Camera : : FRAME_BUFFER_OBJECT ) ;
setReferenceFrame ( osg : : Camera : : RELATIVE_RF ) ;
2017-02-01 04:55:33 +00:00
setSmallFeatureCullingPixelSize ( Settings : : Manager : : getInt ( " small feature culling pixel size " , " Water " ) ) ;
2017-02-02 20:46:25 +00:00
setName ( " ReflectionCamera " ) ;
2019-02-20 13:37:00 +00:00
setCullCallback ( new InheritViewPointCallback ) ;
2015-10-28 19:09:44 +00:00
2019-02-24 16:54:01 +00:00
int reflectionDetail = Settings : : Manager : : getInt ( " reflection detail " , " Water " ) ;
reflectionDetail = std : : min ( 4 , std : : max ( isInterior ? 2 : 0 , reflectionDetail ) ) ;
2019-02-21 16:14:18 +00:00
unsigned int extraMask = 0 ;
2019-02-24 16:54:01 +00:00
if ( reflectionDetail > = 1 ) extraMask | = Mask_Terrain ;
if ( reflectionDetail > = 2 ) extraMask | = Mask_Static ;
if ( reflectionDetail > = 3 ) extraMask | = Mask_Effect | Mask_ParticleSystem | Mask_Object ;
if ( reflectionDetail > = 4 ) extraMask | = Mask_Player | Mask_Actor ;
setCullMask ( Mask_Scene | Mask_Sky | Mask_Lighting | extraMask ) ;
2015-10-28 19:09:44 +00:00
setNodeMask ( Mask_RenderToTexture ) ;
unsigned int rttSize = Settings : : Manager : : getInt ( " rtt size " , " Water " ) ;
setViewport ( 0 , 0 , rttSize , rttSize ) ;
// No need for Update traversal since the mSceneRoot is already updated as part of the main scene graph
// A double update would mess with the light collection (in addition to being plain redundant)
setUpdateCallback ( new NoTraverseCallback ) ;
mReflectionTexture = new osg : : Texture2D ;
mReflectionTexture - > setInternalFormat ( GL_RGB ) ;
mReflectionTexture - > setFilter ( osg : : Texture : : MIN_FILTER , osg : : Texture : : LINEAR ) ;
mReflectionTexture - > setFilter ( osg : : Texture : : MAG_FILTER , osg : : Texture : : LINEAR ) ;
mReflectionTexture - > setWrap ( osg : : Texture : : WRAP_S , osg : : Texture : : CLAMP_TO_EDGE ) ;
mReflectionTexture - > setWrap ( osg : : Texture : : WRAP_T , osg : : Texture : : CLAMP_TO_EDGE ) ;
attach ( osg : : Camera : : COLOR_BUFFER , mReflectionTexture ) ;
// XXX: should really flip the FrontFace on each renderable instead of forcing clockwise.
osg : : ref_ptr < osg : : FrontFace > frontFace ( new osg : : FrontFace ) ;
frontFace - > setMode ( osg : : FrontFace : : CLOCKWISE ) ;
getOrCreateStateSet ( ) - > setAttributeAndModes ( frontFace , osg : : StateAttribute : : ON ) ;
mClipCullNode = new ClipCullNode ;
addChild ( mClipCullNode ) ;
2018-10-30 22:34:53 +00:00
SceneUtil : : ShadowManager : : disableShadowsForStateSet ( getOrCreateStateSet ( ) ) ;
2015-10-28 19:09:44 +00:00
}
void setWaterLevel ( float waterLevel )
{
2017-09-20 14:34:27 +00:00
setViewMatrix ( osg : : Matrix : : scale ( 1 , 1 , - 1 ) * osg : : Matrix : : translate ( 0 , 0 , 2 * waterLevel ) ) ;
2015-10-28 19:09:44 +00:00
mClipCullNode - > setPlane ( osg : : Plane ( osg : : Vec3d ( 0 , 0 , 1 ) , osg : : Vec3d ( 0 , 0 , waterLevel ) ) ) ;
}
2015-10-28 18:57:58 +00:00
2015-10-28 19:09:44 +00:00
void setScene ( osg : : Node * scene )
{
if ( mScene )
mClipCullNode - > removeChild ( mScene ) ;
mScene = scene ;
mClipCullNode - > addChild ( scene ) ;
}
osg : : Texture2D * getReflectionTexture ( ) const
{
return mReflectionTexture . get ( ) ;
}
private :
osg : : ref_ptr < osg : : Texture2D > mReflectionTexture ;
osg : : ref_ptr < ClipCullNode > mClipCullNode ;
osg : : ref_ptr < osg : : Node > mScene ;
2015-10-28 18:57:58 +00:00
} ;
2015-11-01 23:29:09 +00:00
/// DepthClampCallback enables GL_DEPTH_CLAMP for the current draw, if supported.
class DepthClampCallback : public osg : : Drawable : : DrawCallback
{
public :
virtual void drawImplementation ( osg : : RenderInfo & renderInfo , const osg : : Drawable * drawable ) const
{
2015-11-01 23:52:20 +00:00
static bool supported = osg : : isGLExtensionOrVersionSupported ( renderInfo . getState ( ) - > getContextID ( ) , " GL_ARB_depth_clamp " , 3.3 ) ;
if ( ! supported )
{
2015-11-01 23:29:09 +00:00
drawable - > drawImplementation ( renderInfo ) ;
2015-11-01 23:52:20 +00:00
return ;
}
2015-11-01 23:29:09 +00:00
glEnable ( GL_DEPTH_CLAMP ) ;
drawable - > drawImplementation ( renderInfo ) ;
// restore default
glDisable ( GL_DEPTH_CLAMP ) ;
}
} ;
2015-10-26 20:36:19 +00:00
Water : : Water ( osg : : Group * parent , osg : : Group * sceneRoot , Resource : : ResourceSystem * resourceSystem , osgUtil : : IncrementalCompileOperation * ico ,
2016-01-06 11:46:06 +00:00
const Fallback : : Map * fallback , const std : : string & resourcePath )
2015-06-02 14:35:35 +00:00
: mParent ( parent )
2015-10-26 20:36:19 +00:00
, mSceneRoot ( sceneRoot )
2015-06-02 14:35:35 +00:00
, mResourceSystem ( resourceSystem )
2015-11-03 01:24:50 +00:00
, mFallback ( fallback )
2015-10-28 19:24:52 +00:00
, mResourcePath ( resourcePath )
2015-06-02 14:35:35 +00:00
, mEnabled ( true )
, mToggled ( true )
, mTop ( 0 )
2019-02-21 16:14:18 +00:00
, mInterior ( false )
2012-03-29 16:33:08 +00:00
{
2015-06-16 18:36:48 +00:00
mSimulation . reset ( new RippleSimulation ( parent , resourceSystem , fallback ) ) ;
2018-09-17 10:52:43 +00:00
mWaterGeom = SceneUtil : : createWaterGeometry ( Constants : : CellSizeInUnits * 150 , 40 , 900 ) ;
2016-03-10 12:17:01 +00:00
mWaterGeom - > setDrawCallback ( new DepthClampCallback ) ;
mWaterGeom - > setNodeMask ( Mask_Water ) ;
2012-07-19 20:23:07 +00:00
2015-06-02 23:18:36 +00:00
if ( ico )
2016-03-10 12:17:01 +00:00
ico - > add ( mWaterGeom ) ;
2015-06-02 23:18:36 +00:00
2015-06-02 14:35:35 +00:00
mWaterNode = new osg : : PositionAttitudeTransform ;
2017-02-02 20:46:25 +00:00
mWaterNode - > setName ( " Water Root " ) ;
2016-03-10 12:17:01 +00:00
mWaterNode - > addChild ( mWaterGeom ) ;
2015-11-01 23:30:18 +00:00
mWaterNode - > addCullCallback ( new FudgeCallback ) ;
2012-07-19 20:23:07 +00:00
2015-10-28 18:39:22 +00:00
// simple water fallback for the local map
2016-03-10 12:17:01 +00:00
osg : : ref_ptr < osg : : Geometry > geom2 ( osg : : clone ( mWaterGeom . get ( ) , osg : : CopyOp : : DEEP_COPY_NODES ) ) ;
createSimpleWaterStateSet ( geom2 , mFallback - > getFallbackFloat ( " Water_Map_Alpha " ) ) ;
geom2 - > setNodeMask ( Mask_SimpleWater ) ;
mWaterNode - > addChild ( geom2 ) ;
2017-10-16 17:47:08 +00:00
2015-10-26 20:36:19 +00:00
mSceneRoot - > addChild ( mWaterNode ) ;
2012-07-19 20:23:07 +00:00
2015-06-02 14:35:35 +00:00
setHeight ( mTop ) ;
2015-10-26 20:36:19 +00:00
2017-10-16 22:13:55 +00:00
mRainIntensityUniform = new osg : : Uniform ( " rainIntensity " , ( float ) 0.0 ) ;
2017-10-24 12:12:41 +00:00
updateWaterMaterial ( ) ;
2015-10-28 20:22:14 +00:00
}
2017-10-16 17:47:08 +00:00
osg : : Uniform * Water : : getRainIntensityUniform ( )
{
2017-10-16 22:13:55 +00:00
return mRainIntensityUniform . get ( ) ;
2017-10-16 17:47:08 +00:00
}
2015-10-28 20:22:14 +00:00
void Water : : updateWaterMaterial ( )
{
if ( mReflection )
{
2016-08-15 16:11:36 +00:00
mReflection - > removeChildren ( 0 , mReflection - > getNumChildren ( ) ) ;
2015-10-28 20:22:14 +00:00
mParent - > removeChild ( mReflection ) ;
2018-10-09 06:21:12 +00:00
mReflection = nullptr ;
2015-10-28 20:22:14 +00:00
}
if ( mRefraction )
{
2016-08-15 16:11:36 +00:00
mRefraction - > removeChildren ( 0 , mRefraction - > getNumChildren ( ) ) ;
2015-10-28 20:22:14 +00:00
mParent - > removeChild ( mRefraction ) ;
2018-10-09 06:21:12 +00:00
mRefraction = nullptr ;
2015-10-28 20:22:14 +00:00
}
if ( Settings : : Manager : : getBool ( " shader " , " Water " ) )
{
2019-02-21 16:14:18 +00:00
mReflection = new Reflection ( mInterior ) ;
2015-10-28 20:22:14 +00:00
mReflection - > setWaterLevel ( mTop ) ;
mReflection - > setScene ( mSceneRoot ) ;
mParent - > addChild ( mReflection ) ;
if ( Settings : : Manager : : getBool ( " refraction " , " Water " ) )
{
mRefraction = new Refraction ;
mRefraction - > setWaterLevel ( mTop ) ;
mRefraction - > setScene ( mSceneRoot ) ;
mParent - > addChild ( mRefraction ) ;
}
2016-03-10 12:17:01 +00:00
createShaderWaterStateSet ( mWaterGeom , mReflection , mRefraction ) ;
2015-10-28 20:22:14 +00:00
}
else
2016-03-10 12:17:01 +00:00
createSimpleWaterStateSet ( mWaterGeom , mFallback - > getFallbackFloat ( " Water_World_Alpha " ) ) ;
2015-10-28 20:22:14 +00:00
updateVisible ( ) ;
2015-10-28 19:24:52 +00:00
}
2015-10-26 20:36:19 +00:00
2017-11-10 18:18:16 +00:00
osg : : Camera * Water : : getReflectionCamera ( )
{
return mReflection ;
}
osg : : Camera * Water : : getRefractionCamera ( )
{
return mRefraction ;
}
2015-11-03 01:24:50 +00:00
void Water : : createSimpleWaterStateSet ( osg : : Node * node , float alpha )
2015-10-28 19:24:52 +00:00
{
2016-08-05 17:25:05 +00:00
osg : : ref_ptr < osg : : StateSet > stateset = SceneUtil : : createSimpleWaterStateSet ( alpha , MWRender : : RenderBin_Water ) ;
2015-10-28 19:24:52 +00:00
2015-11-29 23:41:26 +00:00
node - > setStateSet ( stateset ) ;
2016-08-05 17:25:05 +00:00
// Add animated textures
2015-10-28 19:24:52 +00:00
std : : vector < osg : : ref_ptr < osg : : Texture2D > > textures ;
2015-11-03 01:24:50 +00:00
int frameCount = mFallback - > getFallbackInt ( " Water_SurfaceFrameCount " ) ;
std : : string texture = mFallback - > getFallbackString ( " Water_SurfaceTexture " ) ;
for ( int i = 0 ; i < frameCount ; + + i )
2015-10-28 19:24:52 +00:00
{
std : : ostringstream texname ;
2015-11-03 01:24:50 +00:00
texname < < " textures/water/ " < < texture < < std : : setw ( 2 ) < < std : : setfill ( ' 0 ' ) < < i < < " .dds " ;
2016-02-05 22:10:27 +00:00
osg : : ref_ptr < osg : : Texture2D > tex ( new osg : : Texture2D ( mResourceSystem - > getImageManager ( ) - > getImage ( texname . str ( ) ) ) ) ;
2016-02-05 21:58:02 +00:00
tex - > setWrap ( osg : : Texture : : WRAP_S , osg : : Texture : : REPEAT ) ;
tex - > setWrap ( osg : : Texture : : WRAP_T , osg : : Texture : : REPEAT ) ;
textures . push_back ( tex ) ;
2015-10-28 19:24:52 +00:00
}
2016-02-22 18:06:12 +00:00
if ( textures . empty ( ) )
2015-11-29 23:41:26 +00:00
return ;
2015-11-03 01:24:50 +00:00
float fps = mFallback - > getFallbackFloat ( " Water_SurfaceFPS " ) ;
osg : : ref_ptr < NifOsg : : FlipController > controller ( new NifOsg : : FlipController ( 0 , 1.f / fps , textures ) ) ;
2017-05-05 17:26:09 +00:00
controller - > setSource ( std : : shared_ptr < SceneUtil : : ControllerSource > ( new SceneUtil : : FrameTimeSource ) ) ;
2015-10-28 20:22:14 +00:00
node - > setUpdateCallback ( controller ) ;
2015-11-29 23:41:26 +00:00
2015-10-28 19:24:52 +00:00
stateset - > setTextureAttributeAndModes ( 0 , textures [ 0 ] , osg : : StateAttribute : : ON ) ;
2016-09-28 19:10:44 +00:00
// use a shader to render the simple water, ensuring that fog is applied per pixel as required.
// this could be removed if a more detailed water mesh, using some sort of paging solution, is implemented.
Resource : : SceneManager * sceneManager = mResourceSystem - > getSceneManager ( ) ;
bool oldValue = sceneManager - > getForceShaders ( ) ;
sceneManager - > setForceShaders ( true ) ;
sceneManager - > recreateShaders ( node ) ;
sceneManager - > setForceShaders ( oldValue ) ;
2015-10-28 19:24:52 +00:00
}
void Water : : createShaderWaterStateSet ( osg : : Node * node , Reflection * reflection , Refraction * refraction )
{
2015-10-28 20:22:14 +00:00
// use a define map to conditionally compile the shader
std : : map < std : : string , std : : string > defineMap ;
2017-01-15 13:17:22 +00:00
defineMap . insert ( std : : make_pair ( std : : string ( " refraction_enabled " ) , std : : string ( refraction ? " 1 " : " 0 " ) ) ) ;
2015-10-28 19:24:52 +00:00
2017-01-15 13:17:22 +00:00
Shader : : ShaderManager & shaderMgr = mResourceSystem - > getSceneManager ( ) - > getShaderManager ( ) ;
osg : : ref_ptr < osg : : Shader > vertexShader ( shaderMgr . getShader ( " water_vertex.glsl " , defineMap , osg : : Shader : : VERTEX ) ) ;
osg : : ref_ptr < osg : : Shader > fragmentShader ( shaderMgr . getShader ( " water_fragment.glsl " , defineMap , osg : : Shader : : FRAGMENT ) ) ;
2015-10-26 20:36:19 +00:00
2015-10-28 19:24:52 +00:00
osg : : ref_ptr < osg : : Texture2D > normalMap ( new osg : : Texture2D ( readPngImage ( mResourcePath + " /shaders/water_nm.png " ) ) ) ;
2017-10-24 12:12:41 +00:00
2016-02-03 14:24:28 +00:00
if ( normalMap - > getImage ( ) )
normalMap - > getImage ( ) - > flipVertical ( ) ;
2015-10-26 20:36:19 +00:00
normalMap - > setWrap ( osg : : Texture : : WRAP_S , osg : : Texture : : REPEAT ) ;
normalMap - > setWrap ( osg : : Texture : : WRAP_T , osg : : Texture : : REPEAT ) ;
normalMap - > setMaxAnisotropy ( 16 ) ;
normalMap - > setFilter ( osg : : Texture : : MIN_FILTER , osg : : Texture : : LINEAR_MIPMAP_LINEAR ) ;
normalMap - > setFilter ( osg : : Texture : : MAG_FILTER , osg : : Texture : : LINEAR ) ;
osg : : ref_ptr < osg : : StateSet > shaderStateset = new osg : : StateSet ;
2015-10-28 19:24:52 +00:00
shaderStateset - > addUniform ( new osg : : Uniform ( " normalMap " , 0 ) ) ;
shaderStateset - > addUniform ( new osg : : Uniform ( " reflectionMap " , 1 ) ) ;
shaderStateset - > setTextureAttributeAndModes ( 0 , normalMap , osg : : StateAttribute : : ON ) ;
shaderStateset - > setTextureAttributeAndModes ( 1 , reflection - > getReflectionTexture ( ) , osg : : StateAttribute : : ON ) ;
2017-10-24 12:12:41 +00:00
2015-10-28 19:24:52 +00:00
if ( refraction )
{
shaderStateset - > setTextureAttributeAndModes ( 2 , refraction - > getRefractionTexture ( ) , osg : : StateAttribute : : ON ) ;
shaderStateset - > setTextureAttributeAndModes ( 3 , refraction - > getRefractionDepthTexture ( ) , osg : : StateAttribute : : ON ) ;
2015-10-28 20:22:14 +00:00
shaderStateset - > addUniform ( new osg : : Uniform ( " refractionMap " , 2 ) ) ;
shaderStateset - > addUniform ( new osg : : Uniform ( " refractionDepthMap " , 3 ) ) ;
2015-10-28 19:24:52 +00:00
shaderStateset - > setRenderBinDetails ( MWRender : : RenderBin_Default , " RenderBin " ) ;
}
else
{
shaderStateset - > setMode ( GL_BLEND , osg : : StateAttribute : : ON ) ;
2015-10-26 20:36:19 +00:00
2015-10-28 19:24:52 +00:00
shaderStateset - > setRenderBinDetails ( MWRender : : RenderBin_Water , " RenderBin " ) ;
2015-10-26 20:36:19 +00:00
2015-10-28 19:24:52 +00:00
osg : : ref_ptr < osg : : Depth > depth ( new osg : : Depth ) ;
depth - > setWriteMask ( false ) ;
shaderStateset - > setAttributeAndModes ( depth , osg : : StateAttribute : : ON ) ;
}
shaderStateset - > setMode ( GL_CULL_FACE , osg : : StateAttribute : : OFF ) ;
2015-10-26 20:36:19 +00:00
2017-10-24 12:12:41 +00:00
shaderStateset - > addUniform ( mRainIntensityUniform . get ( ) ) ;
2015-10-28 20:22:14 +00:00
osg : : ref_ptr < osg : : Program > program ( new osg : : Program ) ;
program - > addShader ( vertexShader ) ;
program - > addShader ( fragmentShader ) ;
shaderStateset - > setAttributeAndModes ( program , osg : : StateAttribute : : ON ) ;
2015-10-28 19:24:52 +00:00
node - > setStateSet ( shaderStateset ) ;
2018-10-09 06:21:12 +00:00
node - > setUpdateCallback ( nullptr ) ;
2015-10-28 20:22:14 +00:00
}
void Water : : processChangedSettings ( const Settings : : CategorySettingVector & settings )
{
updateWaterMaterial ( ) ;
2012-03-29 16:33:08 +00:00
}
2012-01-14 23:34:14 +00:00
2015-06-02 14:35:35 +00:00
Water : : ~ Water ( )
2012-03-29 16:33:08 +00:00
{
2015-06-02 14:35:35 +00:00
mParent - > removeChild ( mWaterNode ) ;
2015-11-01 23:57:59 +00:00
if ( mReflection )
{
2016-08-15 16:11:36 +00:00
mReflection - > removeChildren ( 0 , mReflection - > getNumChildren ( ) ) ;
2015-11-01 23:57:59 +00:00
mParent - > removeChild ( mReflection ) ;
2018-10-09 06:21:12 +00:00
mReflection = nullptr ;
2015-11-01 23:57:59 +00:00
}
if ( mRefraction )
{
2016-08-15 16:11:36 +00:00
mRefraction - > removeChildren ( 0 , mRefraction - > getNumChildren ( ) ) ;
2015-11-01 23:57:59 +00:00
mParent - > removeChild ( mRefraction ) ;
2018-10-09 06:21:12 +00:00
mRefraction = nullptr ;
2015-11-01 23:57:59 +00:00
}
2012-03-29 16:33:08 +00:00
}
2012-01-14 23:34:14 +00:00
2016-02-09 00:17:02 +00:00
void Water : : listAssetsToPreload ( std : : vector < std : : string > & textures )
{
int frameCount = mFallback - > getFallbackInt ( " Water_SurfaceFrameCount " ) ;
std : : string texture = mFallback - > getFallbackString ( " Water_SurfaceTexture " ) ;
for ( int i = 0 ; i < frameCount ; + + i )
{
std : : ostringstream texname ;
texname < < " textures/water/ " < < texture < < std : : setw ( 2 ) < < std : : setfill ( ' 0 ' ) < < i < < " .dds " ;
textures . push_back ( texname . str ( ) ) ;
}
}
2015-06-02 14:35:35 +00:00
void Water : : setEnabled ( bool enabled )
2012-03-29 16:33:08 +00:00
{
2015-06-02 14:35:35 +00:00
mEnabled = enabled ;
2012-04-12 14:46:56 +00:00
updateVisible ( ) ;
2012-01-14 23:34:14 +00:00
}
2012-03-29 16:33:08 +00:00
2015-06-02 14:35:35 +00:00
void Water : : changeCell ( const MWWorld : : CellStore * store )
2012-04-03 12:23:23 +00:00
{
2019-02-21 16:14:18 +00:00
bool isInterior = ! store - > getCell ( ) - > isExterior ( ) ;
bool wasInterior = mInterior ;
if ( ! isInterior )
{
2015-06-02 14:35:35 +00:00
mWaterNode - > setPosition ( getSceneNodeCoordinates ( store - > getCell ( ) - > mData . mX , store - > getCell ( ) - > mData . mY ) ) ;
2019-02-21 16:14:18 +00:00
mInterior = false ;
}
2015-06-02 14:35:35 +00:00
else
2019-02-21 16:14:18 +00:00
{
2015-06-02 14:35:35 +00:00
mWaterNode - > setPosition ( osg : : Vec3f ( 0 , 0 , mTop ) ) ;
2019-02-21 16:14:18 +00:00
mInterior = true ;
}
if ( mInterior ! = wasInterior )
updateWaterMaterial ( ) ;
2015-10-28 18:11:32 +00:00
// create a new StateSet to prevent threading issues
osg : : ref_ptr < osg : : StateSet > nodeStateSet ( new osg : : StateSet ) ;
nodeStateSet - > addUniform ( new osg : : Uniform ( " nodePosition " , osg : : Vec3f ( mWaterNode - > getPosition ( ) ) ) ) ;
mWaterNode - > setStateSet ( nodeStateSet ) ;
2012-04-03 12:23:23 +00:00
}
2015-06-02 14:35:35 +00:00
void Water : : setHeight ( const float height )
2012-04-05 13:30:55 +00:00
{
2015-06-02 14:35:35 +00:00
mTop = height ;
2012-04-05 13:30:55 +00:00
2015-06-17 20:49:20 +00:00
mSimulation - > setWaterHeight ( height ) ;
2015-06-02 14:35:35 +00:00
osg : : Vec3f pos = mWaterNode - > getPosition ( ) ;
pos . z ( ) = height ;
mWaterNode - > setPosition ( pos ) ;
2015-10-28 22:31:59 +00:00
if ( mReflection )
mReflection - > setWaterLevel ( mTop ) ;
if ( mRefraction )
mRefraction - > setWaterLevel ( mTop ) ;
2012-04-18 23:08:26 +00:00
}
2015-06-16 18:36:48 +00:00
void Water : : update ( float dt )
{
mSimulation - > update ( dt ) ;
}
2015-06-02 14:35:35 +00:00
void Water : : updateVisible ( )
2012-04-18 23:08:26 +00:00
{
2015-10-31 02:14:05 +00:00
bool visible = mEnabled & & mToggled ;
mWaterNode - > setNodeMask ( visible ? ~ 0 : 0 ) ;
2015-10-28 19:24:52 +00:00
if ( mRefraction )
2015-10-31 02:14:05 +00:00
mRefraction - > setNodeMask ( visible ? Mask_RenderToTexture : 0 ) ;
2015-10-28 19:24:52 +00:00
if ( mReflection )
2015-10-31 02:14:05 +00:00
mReflection - > setNodeMask ( visible ? Mask_RenderToTexture : 0 ) ;
2013-03-05 13:24:29 +00:00
}
2015-06-02 14:35:35 +00:00
bool Water : : toggle ( )
2012-05-29 04:45:44 +00:00
{
2015-06-02 14:35:35 +00:00
mToggled = ! mToggled ;
2013-03-03 14:11:45 +00:00
updateVisible ( ) ;
2015-06-02 14:35:35 +00:00
return mToggled ;
2012-05-29 04:45:44 +00:00
}
2015-06-02 14:35:35 +00:00
bool Water : : isUnderwater ( const osg : : Vec3f & pos ) const
2012-07-20 12:45:42 +00:00
{
2015-06-02 14:35:35 +00:00
return pos . z ( ) < mTop & & mToggled & & mEnabled ;
2012-07-20 12:45:42 +00:00
}
2015-06-02 14:35:35 +00:00
osg : : Vec3f Water : : getSceneNodeCoordinates ( int gridX , int gridY )
2012-07-20 12:45:42 +00:00
{
2018-09-17 10:52:43 +00:00
return osg : : Vec3f ( static_cast < float > ( gridX * Constants : : CellSizeInUnits + ( Constants : : CellSizeInUnits / 2 ) ) ,
static_cast < float > ( gridY * Constants : : CellSizeInUnits + ( Constants : : CellSizeInUnits / 2 ) ) , mTop ) ;
2012-07-20 12:45:42 +00:00
}
2013-02-27 08:20:42 +00:00
void Water : : addEmitter ( const MWWorld : : Ptr & ptr , float scale , float force )
{
mSimulation - > addEmitter ( ptr , scale , force ) ;
}
void Water : : removeEmitter ( const MWWorld : : Ptr & ptr )
{
mSimulation - > removeEmitter ( ptr ) ;
}
void Water : : updateEmitterPtr ( const MWWorld : : Ptr & old , const MWWorld : : Ptr & ptr )
2013-02-23 04:53:20 +00:00
{
2013-02-27 08:20:42 +00:00
mSimulation - > updateEmitterPtr ( old , ptr ) ;
2013-02-23 04:53:20 +00:00
}
2015-06-16 18:36:48 +00:00
2015-12-04 22:28:11 +00:00
void Water : : emitRipple ( const osg : : Vec3f & pos )
{
mSimulation - > emitRipple ( pos ) ;
}
2015-06-16 18:36:48 +00:00
void Water : : removeCell ( const MWWorld : : CellStore * store )
{
mSimulation - > removeCell ( store ) ;
}
void Water : : clearRipples ( )
{
mSimulation - > clear ( ) ;
}
2013-02-23 04:53:20 +00:00
2015-02-09 18:28:29 +00:00
}