mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-15 21:19:57 +00:00
29556a1802
A Warning indicates a potential problem in the content file(s) that the user told OpenMW to load. E.g. this might cause an object to not display at all or as intended, however the rest of the game will run fine. An Error, however, is more likely to be a bug with the engine itself - it means that basic assumptions have been violated and the engine might not run correctly anymore. The above mostly applies to errors/warnings during game-play; startup issues are handled differently: when a file is completely invalid/corrupted to the point that the engine can not start, that might cause messages that are worded as Error due to the severity of the issue but are not necessarily the engine's fault. Hopefully, being a little more consistent here will alleviate confusion among users as to when a log message should be reported and to whom.
694 lines
24 KiB
C++
694 lines
24 KiB
C++
#include "water.hpp"
|
|
|
|
#include <iomanip>
|
|
|
|
#include <osg/Fog>
|
|
#include <osg/Depth>
|
|
#include <osg/Group>
|
|
#include <osg/Geometry>
|
|
#include <osg/Material>
|
|
#include <osg/PositionAttitudeTransform>
|
|
#include <osg/ClipNode>
|
|
#include <osg/FrontFace>
|
|
#include <osg/Shader>
|
|
#include <osg/GLExtensions>
|
|
|
|
#include <osgDB/ReadFile>
|
|
|
|
#include <boost/filesystem/path.hpp>
|
|
#include <boost/filesystem/fstream.hpp>
|
|
|
|
#include <osgUtil/IncrementalCompileOperation>
|
|
#include <osgUtil/CullVisitor>
|
|
|
|
#include <components/resource/resourcesystem.hpp>
|
|
#include <components/resource/imagemanager.hpp>
|
|
#include <components/resource/scenemanager.hpp>
|
|
|
|
#include <components/sceneutil/waterutil.hpp>
|
|
|
|
#include <components/nifosg/controller.hpp>
|
|
#include <components/sceneutil/controller.hpp>
|
|
|
|
#include <components/shader/shadermanager.hpp>
|
|
|
|
#include <components/settings/settings.hpp>
|
|
|
|
#include <components/esm/loadcell.hpp>
|
|
|
|
#include <components/fallback/fallback.hpp>
|
|
|
|
#include "../mwworld/cellstore.hpp"
|
|
|
|
#include "vismask.hpp"
|
|
#include "ripplesimulation.hpp"
|
|
#include "renderbin.hpp"
|
|
#include "util.hpp"
|
|
|
|
namespace MWRender
|
|
{
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
/// @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();
|
|
|
|
osg::RefMatrix* modelViewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix());
|
|
|
|
// apply the height of the plane
|
|
// we can't apply this height in the addClipPlane() since the "flip the below graph" function would otherwise flip the height as well
|
|
modelViewMatrix->preMultTranslate(mCullPlane->getNormal() * ((*mCullPlane)[3] * -1));
|
|
|
|
// 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));
|
|
}
|
|
|
|
// move the plane back along its normal a little bit to prevent bleeding at the water shore
|
|
const float clipFudge = -5;
|
|
modelViewMatrix->preMultTranslate(mCullPlane->getNormal() * clipFudge);
|
|
|
|
cv->pushModelViewMatrix(modelViewMatrix, osg::Transform::RELATIVE_RF);
|
|
traverse(node, nv);
|
|
cv->popModelViewMatrix();
|
|
}
|
|
|
|
private:
|
|
const osg::Plane* mCullPlane;
|
|
};
|
|
|
|
public:
|
|
ClipCullNode()
|
|
{
|
|
addCullCallback (new PlaneCullCallback(&mPlane));
|
|
|
|
mClipNodeTransform = new osg::Group;
|
|
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();
|
|
mClipNode->addClipPlane(new osg::ClipPlane(0, osg::Plane(mPlane.getNormal(), 0))); // mPlane.d() applied in FlipCallback
|
|
mClipNode->setStateSetModes(*getOrCreateStateSet(), osg::StateAttribute::ON);
|
|
mClipNode->setCullingActive(false);
|
|
}
|
|
|
|
private:
|
|
osg::ref_ptr<osg::Group> mClipNodeTransform;
|
|
osg::ref_ptr<osg::ClipNode> mClipNode;
|
|
|
|
osg::Plane mPlane;
|
|
};
|
|
|
|
/// Moves water mesh away from the camera slightly if the camera gets too close on the Z axis.
|
|
/// 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).
|
|
/// 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);
|
|
|
|
const float fudge = 0.2;
|
|
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);
|
|
}
|
|
};
|
|
|
|
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())
|
|
std::cerr << "Error: Failed to open " << file << std::endl;
|
|
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("png");
|
|
if (!reader)
|
|
{
|
|
std::cerr << "Error: Failed to read " << file << ", no png readerwriter found" << std::endl;
|
|
return osg::ref_ptr<osg::Image>();
|
|
}
|
|
osgDB::ReaderWriter::ReadResult result = reader->readImage(inStream);
|
|
if (!result.success())
|
|
std::cerr << "Error: Failed to read " << file << ": " << result.message() << " code " << result.status() << std::endl;
|
|
|
|
return result.getImage();
|
|
}
|
|
|
|
|
|
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);
|
|
setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
|
|
setName("RefractionCamera");
|
|
|
|
setCullMask(Mask_Effect|Mask_Scene|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting);
|
|
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
|
|
// 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);
|
|
|
|
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);
|
|
mRefractionDepthTexture->setInternalFormat(GL_DEPTH_COMPONENT24);
|
|
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);
|
|
}
|
|
|
|
void setScene(osg::Node* scene)
|
|
{
|
|
if (mScene)
|
|
mClipCullNode->removeChild(mScene);
|
|
mScene = scene;
|
|
mClipCullNode->addChild(scene);
|
|
}
|
|
|
|
void setWaterLevel(float waterLevel)
|
|
{
|
|
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
|
|
{
|
|
public:
|
|
Reflection()
|
|
{
|
|
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);
|
|
setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
|
|
setName("ReflectionCamera");
|
|
|
|
bool reflectActors = Settings::Manager::getBool("reflect actors", "Water");
|
|
|
|
setCullMask(Mask_Effect|Mask_Scene|Mask_Terrain|Mask_ParticleSystem|Mask_Sky|Mask_Player|Mask_Lighting|(reflectActors ? Mask_Actor : 0));
|
|
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);
|
|
}
|
|
|
|
void setWaterLevel(float waterLevel)
|
|
{
|
|
setViewMatrix(osg::Matrix::translate(0,0,-waterLevel) * osg::Matrix::scale(1,1,-1) * osg::Matrix::translate(0,0,waterLevel));
|
|
|
|
mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0,0,1), osg::Vec3d(0,0,waterLevel)));
|
|
}
|
|
|
|
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;
|
|
};
|
|
|
|
/// 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
|
|
{
|
|
static bool supported = osg::isGLExtensionOrVersionSupported(renderInfo.getState()->getContextID(), "GL_ARB_depth_clamp", 3.3);
|
|
if (!supported)
|
|
{
|
|
drawable->drawImplementation(renderInfo);
|
|
return;
|
|
}
|
|
|
|
glEnable(GL_DEPTH_CLAMP);
|
|
|
|
drawable->drawImplementation(renderInfo);
|
|
|
|
// restore default
|
|
glDisable(GL_DEPTH_CLAMP);
|
|
}
|
|
};
|
|
|
|
Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem *resourceSystem, osgUtil::IncrementalCompileOperation *ico,
|
|
const Fallback::Map* fallback, const std::string& resourcePath)
|
|
: mParent(parent)
|
|
, mSceneRoot(sceneRoot)
|
|
, mResourceSystem(resourceSystem)
|
|
, mFallback(fallback)
|
|
, mResourcePath(resourcePath)
|
|
, mEnabled(true)
|
|
, mToggled(true)
|
|
, mTop(0)
|
|
{
|
|
mSimulation.reset(new RippleSimulation(parent, resourceSystem, fallback));
|
|
|
|
mWaterGeom = SceneUtil::createWaterGeometry(CELL_SIZE*150, 40, 900);
|
|
mWaterGeom->setDrawCallback(new DepthClampCallback);
|
|
mWaterGeom->setNodeMask(Mask_Water);
|
|
|
|
if (ico)
|
|
ico->add(mWaterGeom);
|
|
|
|
mWaterNode = new osg::PositionAttitudeTransform;
|
|
mWaterNode->setName("Water Root");
|
|
mWaterNode->addChild(mWaterGeom);
|
|
mWaterNode->addCullCallback(new FudgeCallback);
|
|
|
|
// simple water fallback for the local map
|
|
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);
|
|
|
|
mSceneRoot->addChild(mWaterNode);
|
|
|
|
setHeight(mTop);
|
|
|
|
updateWaterMaterial();
|
|
}
|
|
|
|
void Water::updateWaterMaterial()
|
|
{
|
|
if (mReflection)
|
|
{
|
|
mReflection->removeChildren(0, mReflection->getNumChildren());
|
|
mParent->removeChild(mReflection);
|
|
mReflection = NULL;
|
|
}
|
|
if (mRefraction)
|
|
{
|
|
mRefraction->removeChildren(0, mRefraction->getNumChildren());
|
|
mParent->removeChild(mRefraction);
|
|
mRefraction = NULL;
|
|
}
|
|
|
|
if (Settings::Manager::getBool("shader", "Water"))
|
|
{
|
|
mReflection = new Reflection;
|
|
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);
|
|
}
|
|
|
|
createShaderWaterStateSet(mWaterGeom, mReflection, mRefraction);
|
|
}
|
|
else
|
|
createSimpleWaterStateSet(mWaterGeom, mFallback->getFallbackFloat("Water_World_Alpha"));
|
|
|
|
updateVisible();
|
|
}
|
|
|
|
void Water::createSimpleWaterStateSet(osg::Node* node, float alpha)
|
|
{
|
|
osg::ref_ptr<osg::StateSet> stateset = SceneUtil::createSimpleWaterStateSet(alpha, MWRender::RenderBin_Water);
|
|
|
|
node->setStateSet(stateset);
|
|
|
|
// Add animated textures
|
|
std::vector<osg::ref_ptr<osg::Texture2D> > 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";
|
|
osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(mResourceSystem->getImageManager()->getImage(texname.str())));
|
|
tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
|
tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
|
textures.push_back(tex);
|
|
}
|
|
|
|
if (textures.empty())
|
|
return;
|
|
|
|
float fps = mFallback->getFallbackFloat("Water_SurfaceFPS");
|
|
|
|
osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 1.f/fps, textures));
|
|
controller->setSource(boost::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));
|
|
node->setUpdateCallback(controller);
|
|
|
|
stateset->setTextureAttributeAndModes(0, textures[0], osg::StateAttribute::ON);
|
|
|
|
// 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.
|
|
#if !defined(OPENGL_ES) && !defined(ANDROID)
|
|
Resource::SceneManager* sceneManager = mResourceSystem->getSceneManager();
|
|
bool oldValue = sceneManager->getForceShaders();
|
|
sceneManager->setForceShaders(true);
|
|
sceneManager->recreateShaders(node);
|
|
sceneManager->setForceShaders(oldValue);
|
|
#endif
|
|
}
|
|
|
|
void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, Refraction* refraction)
|
|
{
|
|
// use a define map to conditionally compile the shader
|
|
std::map<std::string, std::string> defineMap;
|
|
defineMap.insert(std::make_pair(std::string("refraction_enabled"), std::string(refraction ? "1" : "0")));
|
|
|
|
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));
|
|
|
|
osg::ref_ptr<osg::Texture2D> normalMap (new osg::Texture2D(readPngImage(mResourcePath + "/shaders/water_nm.png")));
|
|
if (normalMap->getImage())
|
|
normalMap->getImage()->flipVertical();
|
|
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;
|
|
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);
|
|
if (refraction)
|
|
{
|
|
shaderStateset->setTextureAttributeAndModes(2, refraction->getRefractionTexture(), osg::StateAttribute::ON);
|
|
shaderStateset->setTextureAttributeAndModes(3, refraction->getRefractionDepthTexture(), osg::StateAttribute::ON);
|
|
shaderStateset->addUniform(new osg::Uniform("refractionMap", 2));
|
|
shaderStateset->addUniform(new osg::Uniform("refractionDepthMap", 3));
|
|
shaderStateset->setRenderBinDetails(MWRender::RenderBin_Default, "RenderBin");
|
|
}
|
|
else
|
|
{
|
|
shaderStateset->setMode(GL_BLEND, osg::StateAttribute::ON);
|
|
|
|
shaderStateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin");
|
|
|
|
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);
|
|
|
|
osg::ref_ptr<osg::Program> program (new osg::Program);
|
|
program->addShader(vertexShader);
|
|
program->addShader(fragmentShader);
|
|
shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);
|
|
|
|
node->setStateSet(shaderStateset);
|
|
node->setUpdateCallback(NULL);
|
|
}
|
|
|
|
void Water::processChangedSettings(const Settings::CategorySettingVector& settings)
|
|
{
|
|
updateWaterMaterial();
|
|
}
|
|
|
|
Water::~Water()
|
|
{
|
|
mParent->removeChild(mWaterNode);
|
|
|
|
if (mReflection)
|
|
{
|
|
mReflection->removeChildren(0, mReflection->getNumChildren());
|
|
mParent->removeChild(mReflection);
|
|
mReflection = NULL;
|
|
}
|
|
if (mRefraction)
|
|
{
|
|
mRefraction->removeChildren(0, mRefraction->getNumChildren());
|
|
mParent->removeChild(mRefraction);
|
|
mRefraction = NULL;
|
|
}
|
|
}
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
void Water::setEnabled(bool enabled)
|
|
{
|
|
mEnabled = enabled;
|
|
updateVisible();
|
|
}
|
|
|
|
void Water::changeCell(const MWWorld::CellStore* store)
|
|
{
|
|
if (store->getCell()->isExterior())
|
|
mWaterNode->setPosition(getSceneNodeCoordinates(store->getCell()->mData.mX, store->getCell()->mData.mY));
|
|
else
|
|
mWaterNode->setPosition(osg::Vec3f(0,0,mTop));
|
|
|
|
// 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);
|
|
}
|
|
|
|
void Water::setHeight(const float height)
|
|
{
|
|
mTop = height;
|
|
|
|
mSimulation->setWaterHeight(height);
|
|
|
|
osg::Vec3f pos = mWaterNode->getPosition();
|
|
pos.z() = height;
|
|
mWaterNode->setPosition(pos);
|
|
|
|
if (mReflection)
|
|
mReflection->setWaterLevel(mTop);
|
|
if (mRefraction)
|
|
mRefraction->setWaterLevel(mTop);
|
|
}
|
|
|
|
void Water::update(float dt)
|
|
{
|
|
mSimulation->update(dt);
|
|
}
|
|
|
|
void Water::updateVisible()
|
|
{
|
|
bool visible = mEnabled && mToggled;
|
|
mWaterNode->setNodeMask(visible ? ~0 : 0);
|
|
if (mRefraction)
|
|
mRefraction->setNodeMask(visible ? Mask_RenderToTexture : 0);
|
|
if (mReflection)
|
|
mReflection->setNodeMask(visible ? Mask_RenderToTexture : 0);
|
|
}
|
|
|
|
bool Water::toggle()
|
|
{
|
|
mToggled = !mToggled;
|
|
updateVisible();
|
|
return mToggled;
|
|
}
|
|
|
|
bool Water::isUnderwater(const osg::Vec3f &pos) const
|
|
{
|
|
return pos.z() < mTop && mToggled && mEnabled;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void Water::removeEmitter (const MWWorld::Ptr& ptr)
|
|
{
|
|
mSimulation->removeEmitter (ptr);
|
|
}
|
|
|
|
void Water::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr)
|
|
{
|
|
mSimulation->updateEmitterPtr(old, ptr);
|
|
}
|
|
|
|
void Water::emitRipple(const osg::Vec3f &pos)
|
|
{
|
|
mSimulation->emitRipple(pos);
|
|
}
|
|
|
|
void Water::removeCell(const MWWorld::CellStore *store)
|
|
{
|
|
mSimulation->removeCell(store);
|
|
}
|
|
|
|
void Water::clearRipples()
|
|
{
|
|
mSimulation->clear();
|
|
}
|
|
|
|
}
|