Basic water rendering

c++11
scrawl 10 years ago
parent de8e5f0db1
commit 01944c33f5

@ -22,8 +22,8 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap bulletdebugdraw globalmap characterpreview camera localmap water
# occlusionquery water shadows ripplesimulation refraction terrainstorage # occlusionquery shadows ripplesimulation refraction terrainstorage
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput

@ -157,7 +157,7 @@ osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setRenderOrder(osg::Camera::PRE_RENDER);
camera->setCullMask(MWRender::Mask_Scene); camera->setCullMask(MWRender::Mask_Scene|MWRender::Mask_Water);
camera->setNodeMask(Mask_RenderToTexture); camera->setNodeMask(Mask_RenderToTexture);
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet; osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;

@ -1,110 +0,0 @@
#include "refraction.hpp"
#include <OgreCamera.h>
#include <OgreTextureManager.h>
#include <OgreSceneManager.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreRenderTarget.h>
#include <OgreViewport.h>
#include <OgreRoot.h>
#include <OgreRenderTexture.h>
#include <OgreSceneNode.h>
#include <extern/shiny/Main/Factory.hpp>
#include "renderconst.hpp"
namespace MWRender
{
Refraction::Refraction(Ogre::Camera *parentCamera)
: mParentCamera(parentCamera)
, mRenderActive(false)
, mIsUnderwater(false)
{
mCamera = mParentCamera->getSceneManager()->createCamera("RefractionCamera");
mParentCamera->getSceneManager()->addRenderQueueListener(this);
Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual("WaterRefraction",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, 512, 512, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
mRenderTarget = texture->getBuffer()->getRenderTarget();
Ogre::Viewport* vp = mRenderTarget->addViewport(mCamera);
vp->setOverlaysEnabled(false);
vp->setShadowsEnabled(false);
vp->setVisibilityMask(RV_Refraction);
vp->setMaterialScheme("water_refraction");
vp->setBackgroundColour (Ogre::ColourValue(0.090195f, 0.115685f, 0.12745f));
mRenderTarget->setAutoUpdated(true);
mRenderTarget->addListener(this);
}
Refraction::~Refraction()
{
mRenderTarget->removeListener(this);
Ogre::TextureManager::getSingleton().remove("WaterRefraction");
mParentCamera->getSceneManager()->destroyCamera(mCamera);
mParentCamera->getSceneManager()->removeRenderQueueListener(this);
}
void Refraction::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
{
if (mParentCamera->isAttached())
mParentCamera->getParentSceneNode ()->needUpdate ();
mCamera->setOrientation(mParentCamera->getDerivedOrientation());
mCamera->setPosition(mParentCamera->getDerivedPosition());
mCamera->setNearClipDistance(mParentCamera->getNearClipDistance());
mCamera->setFarClipDistance(mParentCamera->getFarClipDistance());
mCamera->setAspectRatio(mParentCamera->getAspectRatio());
mCamera->setFOVy(mParentCamera->getFOVy());
// for depth calculation, we want the original viewproj matrix _without_ the custom near clip plane.
// since all we are interested in is depth, we only need the third row of the matrix.
Ogre::Matrix4 projMatrix = mCamera->getProjectionMatrixWithRSDepth () * mCamera->getViewMatrix ();
sh::Vector4* row3 = new sh::Vector4(projMatrix[2][0], projMatrix[2][1], projMatrix[2][2], projMatrix[2][3]);
sh::Factory::getInstance ().setSharedParameter ("vpRow2Fix", sh::makeProperty<sh::Vector4> (row3));
// enable clip plane here to take advantage of CPU culling for overwater or underwater objects
mCamera->enableCustomNearClipPlane(mIsUnderwater ? mNearClipPlaneUnderwater : mNearClipPlane);
mRenderActive = true;
}
void Refraction::postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
{
mCamera->disableCustomNearClipPlane ();
mRenderActive = false;
}
void Refraction::setHeight(float height)
{
mNearClipPlane = Ogre::Plane( -Ogre::Vector3(0,0,1), -(height + 5));
mNearClipPlaneUnderwater = Ogre::Plane( Ogre::Vector3(0,0,1), height - 5);
}
void Refraction::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation)
{
// We don't want the sky to get clipped by custom near clip plane (the water plane)
if (queueGroupId < 20 && mRenderActive)
{
mCamera->disableCustomNearClipPlane();
Ogre::Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS());
}
}
void Refraction::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation)
{
if (queueGroupId < 20 && mRenderActive)
{
mCamera->enableCustomNearClipPlane(mIsUnderwater ? mNearClipPlaneUnderwater : mNearClipPlane);
Ogre::Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS());
}
}
void Refraction::setActive(bool active)
{
mRenderTarget->setActive(active);
}
}

@ -1,45 +0,0 @@
#ifndef MWRENDER_REFRACTION_H
#define MWRENDER_REFRACTION_H
#include <OgrePlane.h>
#include <OgreRenderTargetListener.h>
#include <OgreRenderQueueListener.h>
namespace Ogre
{
class Camera;
class RenderTarget;
}
namespace MWRender
{
class Refraction : public Ogre::RenderTargetListener, public Ogre::RenderQueueListener
{
public:
Refraction(Ogre::Camera* parentCamera);
~Refraction();
void setHeight (float height);
void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
void setUnderwater(bool underwater) {mIsUnderwater = underwater;}
void setActive (bool active);
void renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation);
void renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation);
private:
Ogre::Camera* mParentCamera;
Ogre::Camera* mCamera;
Ogre::RenderTarget* mRenderTarget;
Ogre::Plane mNearClipPlane;
Ogre::Plane mNearClipPlaneUnderwater;
bool mRenderActive;
bool mIsUnderwater;
};
}
#endif

@ -34,6 +34,7 @@
#include "vismask.hpp" #include "vismask.hpp"
#include "pathgrid.hpp" #include "pathgrid.hpp"
#include "camera.hpp" #include "camera.hpp"
#include "water.hpp"
namespace MWRender namespace MWRender
{ {
@ -129,7 +130,9 @@ namespace MWRender
mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation()); mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation());
mEffectManager.reset(new EffectManager(mRootNode, mResourceSystem)); mEffectManager.reset(new EffectManager(lightRoot, mResourceSystem));
mWater.reset(new Water(lightRoot, mResourceSystem));
mCamera.reset(new Camera(mViewer->getCamera())); mCamera.reset(new Camera(mViewer->getCamera()));
@ -177,6 +180,7 @@ namespace MWRender
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
mFieldOfView = Settings::Manager::getFloat("field of view", "General"); mFieldOfView = Settings::Manager::getFloat("field of view", "General");
updateProjectionMatrix(); updateProjectionMatrix();
mStateUpdater->setFogEnd(mViewDistance);
} }
RenderingManager::~RenderingManager() RenderingManager::~RenderingManager()
@ -230,6 +234,8 @@ namespace MWRender
void RenderingManager::addCell(const MWWorld::CellStore *store) void RenderingManager::addCell(const MWWorld::CellStore *store)
{ {
mPathgrid->addCell(store); mPathgrid->addCell(store);
mWater->changeCell(store);
} }
void RenderingManager::removeCell(const MWWorld::CellStore *store) void RenderingManager::removeCell(const MWWorld::CellStore *store)
@ -253,6 +259,22 @@ namespace MWRender
mStateUpdater->setWireframe(wireframe); mStateUpdater->setWireframe(wireframe);
return wireframe; return wireframe;
} }
else if (mode == Render_Water)
{
return mWater->toggle();
}
else if (mode == Render_Scene)
{
int mask = mViewer->getCamera()->getCullMask();
bool enabled = mask&Mask_Scene;
enabled = !enabled;
if (enabled)
mask |= Mask_Scene;
else
mask &= ~Mask_Scene;
mViewer->getCamera()->setCullMask(mask);
return enabled;
}
/* /*
else //if (mode == Render_BoundingBoxes) else //if (mode == Render_BoundingBoxes)
{ {
@ -271,12 +293,9 @@ namespace MWRender
configureFog (cell->mAmbi.mFogDensity, color); configureFog (cell->mAmbi.mFogDensity, color);
} }
void RenderingManager::configureFog(float /* fogDepth */, const osg::Vec4f &colour) void RenderingManager::configureFog(float /* fogDepth */, const osg::Vec4f &color)
{ {
mViewer->getCamera()->setClearColor(colour); mFogColor = color;
mStateUpdater->setFogColor(colour);
mStateUpdater->setFogEnd(mViewDistance);
} }
SkyManager* RenderingManager::getSkyManager() SkyManager* RenderingManager::getSkyManager()
@ -289,6 +308,19 @@ namespace MWRender
mEffectManager->update(dt); mEffectManager->update(dt);
mSky->update(dt); mSky->update(dt);
mCamera->update(dt, paused); mCamera->update(dt, paused);
osg::Vec3f focal, cameraPos;
mCamera->getPosition(focal, cameraPos);
if (mWater->isUnderwater(cameraPos))
{
setFogColor(osg::Vec4f(0.090195f, 0.115685f, 0.12745f, 1.f));
mStateUpdater->setFogEnd(1000);
}
else
{
setFogColor(mFogColor);
mStateUpdater->setFogEnd(mViewDistance);
}
} }
void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr) void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
@ -325,6 +357,16 @@ namespace MWRender
mObjects->removeObject(ptr); mObjects->removeObject(ptr);
} }
void RenderingManager::setWaterEnabled(bool enabled)
{
mWater->setEnabled(enabled);
}
void RenderingManager::setWaterHeight(float height)
{
mWater->setHeight(height);
}
void RenderingManager::getCameraToViewportRay(float nX, float nY, osg::Vec3f &origin, osg::Vec3f &dest) void RenderingManager::getCameraToViewportRay(float nX, float nY, osg::Vec3f &origin, osg::Vec3f &dest)
{ {
osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix(); osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix();
@ -390,7 +432,7 @@ namespace MWRender
osgUtil::IntersectionVisitor intersectionVisitor(intersector); osgUtil::IntersectionVisitor intersectionVisitor(intersector);
int mask = intersectionVisitor.getTraversalMask(); int mask = intersectionVisitor.getTraversalMask();
mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect); mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water);
if (ignorePlayer) if (ignorePlayer)
mask &= ~(Mask_Player); mask &= ~(Mask_Player);
@ -521,6 +563,13 @@ namespace MWRender
mViewer->startThreading(); mViewer->startThreading();
} }
void RenderingManager::setFogColor(const osg::Vec4f &color)
{
mViewer->getCamera()->setClearColor(color);
mStateUpdater->setFogColor(color);
}
void RenderingManager::processChangedSettings(const Settings::CategorySettingVector &changed) void RenderingManager::processChangedSettings(const Settings::CategorySettingVector &changed)
{ {
for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it)

@ -42,6 +42,7 @@ namespace MWRender
class NpcAnimation; class NpcAnimation;
class Pathgrid; class Pathgrid;
class Camera; class Camera;
class Water;
class RenderingManager : public MWRender::RenderingInterface class RenderingManager : public MWRender::RenderingInterface
{ {
@ -73,6 +74,9 @@ namespace MWRender
void removeObject(const MWWorld::Ptr& ptr); void removeObject(const MWWorld::Ptr& ptr);
void setWaterEnabled(bool enabled);
void setWaterHeight(float level);
/// Return the object under the mouse cursor / crosshair position, given by nX and nY normalized screen coordinates, /// Return the object under the mouse cursor / crosshair position, given by nX and nY normalized screen coordinates,
/// where (0,0) is the top left corner. /// where (0,0) is the top left corner.
MWWorld::Ptr getFacedObject(const float nX, const float nY, float maxDistance, bool ignorePlayer); MWWorld::Ptr getFacedObject(const float nX, const float nY, float maxDistance, bool ignorePlayer);
@ -131,6 +135,7 @@ namespace MWRender
private: private:
void updateProjectionMatrix(); void updateProjectionMatrix();
void updateTextureFiltering(); void updateTextureFiltering();
void setFogColor(const osg::Vec4f& color);
osg::ref_ptr<osgViewer::Viewer> mViewer; osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osg::Group> mRootNode; osg::ref_ptr<osg::Group> mRootNode;
@ -141,6 +146,7 @@ namespace MWRender
std::auto_ptr<Pathgrid> mPathgrid; std::auto_ptr<Pathgrid> mPathgrid;
std::auto_ptr<Objects> mObjects; std::auto_ptr<Objects> mObjects;
std::auto_ptr<Water> mWater;
std::auto_ptr<SkyManager> mSky; std::auto_ptr<SkyManager> mSky;
std::auto_ptr<EffectManager> mEffectManager; std::auto_ptr<EffectManager> mEffectManager;
std::auto_ptr<NpcAnimation> mPlayerAnimation; std::auto_ptr<NpcAnimation> mPlayerAnimation;
@ -149,6 +155,8 @@ namespace MWRender
osg::ref_ptr<StateUpdater> mStateUpdater; osg::ref_ptr<StateUpdater> mStateUpdater;
osg::Vec4f mFogColor;
float mNearClip; float mNearClip;
float mViewDistance; float mViewDistance;
float mFieldOfView; float mFieldOfView;

@ -9,7 +9,9 @@ namespace MWRender
Render_CollisionDebug, Render_CollisionDebug,
Render_Wireframe, Render_Wireframe,
Render_Pathgrid, Render_Pathgrid,
Render_BoundingBoxes Render_BoundingBoxes,
Render_Water,
Render_Scene
}; };
} }

@ -15,13 +15,14 @@ namespace MWRender
Mask_Actor = (1<<3), Mask_Actor = (1<<3),
Mask_Player = (1<<4), Mask_Player = (1<<4),
Mask_Sky = (1<<5), Mask_Sky = (1<<5),
Mask_Water = (1<<6),
// top level masks // top level masks
Mask_Scene = (1<<6), Mask_Scene = (1<<7),
Mask_GUI = (1<<7), Mask_GUI = (1<<8),
// Set on cameras within the main scene graph // Set on cameras within the main scene graph
Mask_RenderToTexture = (1<<8) Mask_RenderToTexture = (1<<9)
// reserved: (1<<16) for SceneUtil::Mask_Lit // reserved: (1<<16) for SceneUtil::Mask_Lit
}; };

@ -1,491 +1,184 @@
#include "water.hpp" #include "water.hpp"
#include <OgreRenderTexture.h> #include <iomanip>
#include <OgreEntity.h>
#include <OgreMeshManager.h>
#include <OgreMaterialManager.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreRoot.h>
#include <OgreCamera.h>
#include <OgreTextureManager.h>
#include <OgreViewport.h>
#include <OgreSceneNode.h>
#include <OgreTechnique.h>
#include "sky.hpp"
#include "renderingmanager.hpp"
#include "ripplesimulation.hpp"
#include "refraction.hpp"
#include <extern/shiny/Main/Factory.hpp>
#include <extern/shiny/Platforms/Ogre/OgreMaterial.hpp>
using namespace Ogre;
namespace MWRender #include <osg/Group>
{ #include <osg/Geode>
#include <osg/Geometry>
CubeReflection::CubeReflection(Ogre::SceneManager* sceneManager) #include <osg/Material>
: Reflection(sceneManager) #include <osg/PositionAttitudeTransform>
{ #include <osg/Depth>
Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton ().createManual("CubeReflection",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_CUBE_MAP,
512,512, 0, PF_R8G8B8, TU_RENDERTARGET);
mCamera = mSceneMgr->createCamera ("CubeCamera");
mCamera->setNearClipDistance (5);
mCamera->setFarClipDistance (1000);
for (int face = 0; face < 6; ++face)
{
mRenderTargets[face] = texture->getBuffer (face)->getRenderTarget();
mRenderTargets[face]->removeAllViewports ();
Viewport* vp = mRenderTargets[face]->addViewport (mCamera);
vp->setOverlaysEnabled(false);
vp->setShadowsEnabled(false);
vp->setMaterialScheme ("water_reflection");
mRenderTargets[face]->setAutoUpdated(false);
/*
Vector3 lookAt(0,0,0), up(0,0,0), right(0,0,0);
switch(face)
{
case 0: lookAt.x =-1; up.y = 1; right.z = 1; break; // +X
case 1: lookAt.x = 1; up.y = 1; right.z =-1; break; // -X
case 2: lookAt.y =-1; up.z = 1; right.x = 1; break; // +Y
case 3: lookAt.y = 1; up.z =-1; right.x = 1; break; // -Y
case 4: lookAt.z = 1; up.y = 1; right.x =-1; break; // +Z
case 5: lookAt.z =-1; up.y = 1; right.x =-1; break; // -Z
}
Quaternion orient(right, up, lookAt);
mCamera->setOrientation(orient);
*/
}
}
CubeReflection::~CubeReflection () #include <components/resource/resourcesystem.hpp>
{ #include <components/resource/texturemanager.hpp>
Ogre::TextureManager::getSingleton ().remove("CubeReflection");
mSceneMgr->destroyCamera (mCamera);
}
void CubeReflection::update () #include <components/nifosg/controller.hpp>
{ #include <components/sceneutil/controller.hpp>
if (mParentCamera->isAttached())
mParentCamera->getParentSceneNode ()->needUpdate ();
mCamera->setPosition(mParentCamera->getDerivedPosition());
}
// -------------------------------------------------------------------------------------------------------------------------------- #include "vismask.hpp"
PlaneReflection::PlaneReflection(Ogre::SceneManager* sceneManager, SkyManager* sky) namespace
: Reflection(sceneManager)
, mSky(sky)
, mRenderActive(false)
{ {
mCamera = mSceneMgr->createCamera ("PlaneReflectionCamera");
mSceneMgr->addRenderQueueListener(this);
mTexture = TextureManager::getSingleton().createManual("WaterReflection",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, 512, 512, 0, PF_R8G8B8, TU_RENDERTARGET);
mRenderTarget = mTexture->getBuffer()->getRenderTarget();
Viewport* vp = mRenderTarget->addViewport(mCamera);
vp->setOverlaysEnabled(false);
vp->setBackgroundColour(ColourValue(0.8f, 0.9f, 1.0f));
vp->setShadowsEnabled(false);
vp->setMaterialScheme("water_reflection");
mRenderTarget->addListener(this);
mRenderTarget->setActive(true);
mRenderTarget->setAutoUpdated(true);
sh::Factory::getInstance ().setTextureAlias ("WaterReflection", mTexture->getName());
}
PlaneReflection::~PlaneReflection () osg::ref_ptr<osg::Geometry> createWaterGeometry(float size, int segments, float textureRepeats)
{
mRenderTarget->removeListener (this);
mSceneMgr->destroyCamera (mCamera);
mSceneMgr->removeRenderQueueListener(this);
TextureManager::getSingleton ().remove("WaterReflection");
}
void PlaneReflection::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation)
{
// We don't want the sky to get clipped by custom near clip plane (the water plane)
if (queueGroupId < 20 && mRenderActive)
{ {
mCamera->disableCustomNearClipPlane(); osg::ref_ptr<osg::Vec3Array> verts (new osg::Vec3Array);
Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); osg::ref_ptr<osg::Vec2Array> texcoords (new osg::Vec2Array);
}
} // some drivers don't like huge triangles, so we do some subdivisons
// a paged solution would be even better
void PlaneReflection::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation) const float step = size/segments;
{ const float texCoordStep = textureRepeats / segments;
if (queueGroupId < 20 && mRenderActive) for (int x=0; x<segments; ++x)
{ {
mCamera->enableCustomNearClipPlane(mIsUnderwater ? mErrorPlaneUnderwater : mErrorPlane); for (int y=0; y<segments; ++y)
Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); {
} float x1 = -size/2.f + x*step;
} float y1 = -size/2.f + y*step;
float x2 = x1 + step;
void PlaneReflection::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) float y2 = y1 + step;
{
if (mParentCamera->isAttached()) verts->push_back(osg::Vec3f(x1, y2, 0.f));
mParentCamera->getParentSceneNode ()->needUpdate (); verts->push_back(osg::Vec3f(x1, y1, 0.f));
mCamera->setOrientation(mParentCamera->getDerivedOrientation()); verts->push_back(osg::Vec3f(x2, y1, 0.f));
mCamera->setPosition(mParentCamera->getDerivedPosition()); verts->push_back(osg::Vec3f(x2, y2, 0.f));
mCamera->setNearClipDistance(mParentCamera->getNearClipDistance());
mCamera->setFarClipDistance(mParentCamera->getFarClipDistance()); float u1 = x*texCoordStep;
mCamera->setAspectRatio(mParentCamera->getAspectRatio()); float v1 = y*texCoordStep;
mCamera->setFOVy(mParentCamera->getFOVy()); float u2 = u1 + texCoordStep;
mRenderActive = true; float v2 = v1 + texCoordStep;
mCamera->enableReflection(mWaterPlane); texcoords->push_back(osg::Vec2f(u1, v2));
texcoords->push_back(osg::Vec2f(u1, v1));
// for depth calculation, we want the original viewproj matrix _without_ the custom near clip plane. texcoords->push_back(osg::Vec2f(u2, v1));
// since all we are interested in is depth, we only need the third row of the matrix. texcoords->push_back(osg::Vec2f(u2, v2));
Ogre::Matrix4 projMatrix = mCamera->getProjectionMatrixWithRSDepth () * mCamera->getViewMatrix (); }
sh::Vector4* row3 = new sh::Vector4(projMatrix[2][0], projMatrix[2][1], projMatrix[2][2], projMatrix[2][3]); }
sh::Factory::getInstance ().setSharedParameter ("vpRow2Fix", sh::makeProperty<sh::Vector4> (row3));
// enable clip plane here to take advantage of CPU culling for overwater or underwater objects
mCamera->enableCustomNearClipPlane(mIsUnderwater ? mErrorPlaneUnderwater : mErrorPlane);
}
void PlaneReflection::postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
{
mCamera->disableReflection();
mCamera->disableCustomNearClipPlane();
mRenderActive = false;
}
void PlaneReflection::setHeight (float height)
{
mWaterPlane = Plane(Ogre::Vector3(0,0,1), height);
mErrorPlane = Plane(Ogre::Vector3(0,0,1), height - 5);
mErrorPlaneUnderwater = Plane(Ogre::Vector3(0,0,-1), -height - 5);
}
void PlaneReflection::setActive (bool active)
{
mRenderTarget->setActive(active);
}
void PlaneReflection::setViewportBackground(Ogre::ColourValue colour)
{
mRenderTarget->getViewport (0)->setBackgroundColour (colour);
}
void PlaneReflection::setVisibilityMask (int flags)
{
mRenderTarget->getViewport (0)->setVisibilityMask (flags);
}
// --------------------------------------------------------------------------------------------------------------------------------
Water::Water (Ogre::Camera *camera, RenderingManager* rend, const MWWorld::Fallback* fallback) :
mCamera (camera), mSceneMgr (camera->getSceneManager()),
mIsUnderwater(false), mActive(1),
mToggled(1), mWaterTimer(0.f),
mRendering(rend),
mVisibilityFlags(0),
mReflection(NULL),
mRefraction(NULL),
mSimulation(NULL),
mPlayer(0,0)
{
mSimulation = new RippleSimulation(mSceneMgr, fallback);
mSky = rend->getSkyManager();
mMaterial = MaterialManager::getSingleton().getByName("Water");
mTop = 0;
mIsUnderwater = false;
mWaterPlane = Plane(Vector3::UNIT_Z, 0);
int waterScale = 30;
MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane,
static_cast<Ogre::Real>(CELL_SIZE*5*waterScale), static_cast<Ogre::Real>(CELL_SIZE*5*waterScale),
40, 40, true, 1, static_cast<Ogre::Real>(3 * waterScale), static_cast<Ogre::Real>(3 * waterScale), Vector3::UNIT_Y);
mWater = mSceneMgr->createEntity("water");
mWater->setVisibilityFlags(RV_Water);
mWater->setCastShadows(false);
mWater->setRenderQueueGroup(RQG_Alpha);
mWaterNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
mWaterNode->attachObject(mWater);
applyRTT();
applyVisibilityMask();
mWater->setMaterial(mMaterial);
setHeight(mTop); osg::ref_ptr<osg::Geometry> waterGeom (new osg::Geometry);
waterGeom->setVertexArray(verts);
waterGeom->setTexCoordArray(0, texcoords);
sh::MaterialInstance* m = sh::Factory::getInstance ().getMaterialInstance ("Water"); waterGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,verts->size()));
m->setListener (this); return waterGeom;
}
// ---------------------------------------------------------------------------------------------- void createWaterStateSet(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Node> node)
// ---------------------------------- reflection debug overlay ----------------------------------
// ----------------------------------------------------------------------------------------------
/*
if (Settings::Manager::getBool("shader", "Water"))
{ {
OverlayManager& mgr = OverlayManager::getSingleton(); osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);
Overlay* overlay;
// destroy if already exists
if ((overlay = mgr.getByName("ReflectionDebugOverlay")))
mgr.destroy(overlay);
overlay = mgr.create("ReflectionDebugOverlay"); osg::ref_ptr<osg::Material> material (new osg::Material);
material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f));
material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.7f));
material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f));
material->setColorMode(osg::Material::OFF);
stateset->setAttributeAndModes(material, osg::StateAttribute::ON);
if (MaterialManager::getSingleton().resourceExists("Ogre/ReflectionDebugTexture")) stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
MaterialManager::getSingleton().remove("Ogre/ReflectionDebugTexture"); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
MaterialPtr debugMat = MaterialManager::getSingleton().create(
"Ogre/ReflectionDebugTexture",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false); osg::ref_ptr<osg::Depth> depth (new osg::Depth);
debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(mReflectionTexture->getName()); depth->setWriteMask(false);
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
OverlayContainer* debugPanel; stateset->setRenderBinDetails(9, "RenderBin");
// destroy container if exists std::vector<osg::ref_ptr<osg::Texture2D> > textures;
try for (int i=0; i<32; ++i)
{ {
if ((debugPanel = std::ostringstream texname;
static_cast<OverlayContainer*>( texname << "textures/water/water" << std::setw(2) << std::setfill('0') << i << ".dds";
mgr.getOverlayElement("Ogre/ReflectionDebugTexPanel" textures.push_back(resourceSystem->getTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT));
))))
mgr.destroyOverlayElement(debugPanel);
} }
catch (Ogre::Exception&) {}
debugPanel = (OverlayContainer*)
(OverlayManager::getSingleton().createOverlayElement("Panel", "Ogre/ReflectionDebugTexPanel"));
debugPanel->_setPosition(0, 0.55);
debugPanel->_setDimensions(0.3, 0.3);
debugPanel->setMaterialName(debugMat->getName());
debugPanel->show();
overlay->add2D(debugPanel);
overlay->show();
}
*/
}
void Water::setActive(bool active) osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 2/32.f, textures));
{ controller->setSource(boost::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));
mActive = active; node->addUpdateCallback(controller);
updateVisible(); node->setStateSet(stateset);
stateset->setTextureAttributeAndModes(0, textures[0], osg::StateAttribute::ON);
}
sh::Factory::getInstance ().setSharedParameter ("waterEnabled", sh::makeProperty<sh::FloatValue> (new sh::FloatValue(active ? 1.0f : 0.0f)));
} }
Water::~Water() namespace MWRender
{ {
MeshManager::getSingleton().remove("water");
mWaterNode->detachObject(mWater);
mSceneMgr->destroyEntity(mWater);
mSceneMgr->destroySceneNode(mWaterNode);
delete mReflection; // --------------------------------------------------------------------------------------------------------------------------------
delete mRefraction;
delete mSimulation;
}
void Water::changeCell(const ESM::Cell* cell) Water::Water(osg::Group *parent, Resource::ResourceSystem *resourceSystem)
: mParent(parent)
, mResourceSystem(resourceSystem)
, mEnabled(true)
, mToggled(true)
, mTop(0)
{ {
if(cell->isExterior()) osg::ref_ptr<osg::Geometry> waterGeom = createWaterGeometry(CELL_SIZE*150, 40, 900);
mWaterNode->setPosition(getSceneNodeCoordinates(cell->mData.mX, cell->mData.mY));
}
void Water::setHeight(const float height) osg::ref_ptr<osg::Geode> geode (new osg::Geode);
{ geode->addDrawable(waterGeom);
mTop = height; geode->setNodeMask(Mask_Water);
mSimulation->setWaterHeight(height); createWaterStateSet(mResourceSystem, geode);
mWaterPlane = Plane(Vector3::UNIT_Z, -height); mWaterNode = new osg::PositionAttitudeTransform;
mWaterNode->addChild(geode);
if (mReflection) mParent->addChild(mWaterNode);
mReflection->setHeight(height);
if (mRefraction)
mRefraction->setHeight(height);
mWaterNode->setPosition(0, 0, height); setHeight(mTop);
sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty<sh::FloatValue>(new sh::FloatValue(height)));
} }
bool Water::toggle() Water::~Water()
{ {
mToggled = !mToggled; mParent->removeChild(mWaterNode);
updateVisible();
return mToggled;
} }
void void Water::setEnabled(bool enabled)
Water::updateUnderwater(bool underwater)
{ {
if (!mActive) { mEnabled = enabled;
return;
}
mIsUnderwater =
underwater &&
mWater->isVisible() &&
mCamera->getPolygonMode() == Ogre::PM_SOLID;
if (mReflection)
mReflection->setUnderwater (mIsUnderwater);
if (mRefraction)
mRefraction->setUnderwater (mIsUnderwater);
updateVisible(); updateVisible();
} }
Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY) void Water::changeCell(const MWWorld::CellStore* store)
{ {
return Vector3(static_cast<Ogre::Real>(gridX * CELL_SIZE + (CELL_SIZE / 2)), static_cast<Ogre::Real>(gridY * CELL_SIZE + (CELL_SIZE / 2)), mTop); if (store->getCell()->isExterior())
mWaterNode->setPosition(getSceneNodeCoordinates(store->getCell()->mData.mX, store->getCell()->mData.mY));
else
mWaterNode->setPosition(osg::Vec3f(0,0,mTop));
} }
void Water::setViewportBackground(const ColourValue& bg) void Water::setHeight(const float height)
{ {
if (mReflection) mTop = height;
mReflection->setViewportBackground(bg);
}
void Water::updateVisible() osg::Vec3f pos = mWaterNode->getPosition();
{ pos.z() = height;
mWater->setVisible(mToggled && mActive); mWaterNode->setPosition(pos);
if (mReflection)
mReflection->setActive(mToggled && mActive);
if (mRefraction)
mRefraction->setActive(mToggled && mActive);
} }
void Water::update(float dt, Ogre::Vector3 player) void Water::updateVisible()
{ {
mWaterTimer += dt; mWaterNode->setNodeMask(mEnabled && mToggled ? ~0 : 0);
sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty<sh::FloatValue>(new sh::FloatValue(mWaterTimer)));
mRendering->getSkyManager ()->setGlareEnabled (!mIsUnderwater);
mPlayer = Ogre::Vector2(player.x, player.y);
} }
void Water::frameStarted(float dt) bool Water::toggle()
{
if (!mActive)
return;
mSimulation->update(dt, mPlayer);
if (mReflection)
{
mReflection->update();
}
}
void Water::applyRTT()
{ {
delete mReflection; mToggled = !mToggled;
mReflection = NULL;
delete mRefraction;
mRefraction = NULL;
// Create rendertarget for reflection
//int rttsize = Settings::Manager::getInt("rtt size", "Water");
if (Settings::Manager::getBool("shader", "Water"))
{
mReflection = new PlaneReflection(mSceneMgr, mSky);
mReflection->setParentCamera (mCamera);
mReflection->setHeight(mTop);
if (Settings::Manager::getBool("refraction", "Water"))
{
mRefraction = new Refraction(mCamera);
mRefraction->setHeight(mTop);
}
}
updateVisible(); updateVisible();
return mToggled;
} }
void Water::applyVisibilityMask() bool Water::isUnderwater(const osg::Vec3f &pos) const
{
mVisibilityFlags = RV_Terrain * Settings::Manager::getBool("reflect terrain", "Water")
+ (RV_Statics + RV_StaticsSmall + RV_Misc) * Settings::Manager::getBool("reflect statics", "Water")
+ RV_Actors * Settings::Manager::getBool("reflect actors", "Water")
+ RV_Effects
+ RV_Sky;
if (mReflection)
mReflection->setVisibilityMask(mVisibilityFlags);
}
void Water::processChangedSettings(const Settings::CategorySettingVector& settings)
{
bool applyRT = false;
bool applyVisMask = false;
for (Settings::CategorySettingVector::const_iterator it=settings.begin();
it != settings.end(); ++it)
{
if ( it->first == "Water" && (
it->second == "shader"
|| it->second == "refraction"
|| it->second == "rtt size"))
applyRT = true;
if ( it->first == "Water" && (
it->second == "reflect actors"
|| it->second == "reflect terrain"
|| it->second == "reflect statics"))
applyVisMask = true;
}
if(applyRT)
{
applyRTT();
applyVisibilityMask();
mWater->setMaterial(mMaterial);
}
if (applyVisMask)
applyVisibilityMask();
}
void Water::requestedConfiguration (sh::MaterialInstance* m, const std::string& configuration)
{ {
return pos.z() < mTop && mToggled && mEnabled;
} }
void Water::createdConfiguration (sh::MaterialInstance* m, const std::string& configuration) osg::Vec3f Water::getSceneNodeCoordinates(int gridX, int gridY)
{ {
if (configuration == "local_map" || !Settings::Manager::getBool("shader", "Water")) return osg::Vec3f(static_cast<float>(gridX * CELL_SIZE + (CELL_SIZE / 2)), static_cast<float>(gridY * CELL_SIZE + (CELL_SIZE / 2)), mTop);
{
// for simple water, set animated texture names
// these have to be set in code
std::string textureNames[32];
for (int i=0; i<32; ++i)
{
textureNames[i] = "textures\\water\\water" + StringConverter::toString(i, 2, '0') + ".dds";
}
Ogre::Technique* t = static_cast<sh::OgreMaterial*>(m->getMaterial())->getOgreTechniqueForConfiguration(configuration);
if (t->getPass(0)->getNumTextureUnitStates () == 0)
return;
t->getPass(0)->getTextureUnitState(0)->setAnimatedTextureName(textureNames, 32, 2);
t->getPass(0)->setDepthWriteEnabled (false);
t->getPass(0)->setSceneBlending (Ogre::SBT_TRANSPARENT_ALPHA);
}
} }
/*
void Water::addEmitter (const MWWorld::Ptr& ptr, float scale, float force) void Water::addEmitter (const MWWorld::Ptr& ptr, float scale, float force)
{ {
mSimulation->addEmitter (ptr, scale, force); mSimulation->addEmitter (ptr, scale, force);
@ -500,10 +193,6 @@ void Water::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr)
{ {
mSimulation->updateEmitterPtr(old, ptr); mSimulation->updateEmitterPtr(old, ptr);
} }
*/
void Water::clearRipples()
{
mSimulation->clear();
} }
} // namespace

@ -1,183 +1,60 @@
#ifndef GAME_MWRENDER_WATER_H #ifndef OPENMW_MWRENDER_WATER_H
#define GAME_MWRENDER_WATER_H #define OPENMW_MWRENDER_WATER_H
#include <OgrePlane.h> #include <osg/ref_ptr>
#include <OgreRenderQueue.h>
#include <OgreRenderQueueListener.h>
#include <OgreRenderTargetListener.h>
#include <OgreMaterial.h>
#include <OgreTexture.h>
#include <OgreVector2.h>
#include <components/esm/loadcell.hpp> #include "../mwworld/cellstore.hpp"
#include <components/settings/settings.hpp>
#include <extern/shiny/Main/MaterialInstance.hpp> namespace osg
#include "renderconst.hpp"
#include "../mwworld/ptr.hpp"
namespace Ogre
{ {
class Camera; class Group;
class SceneManager; class PositionAttitudeTransform;
class SceneNode;
class Entity;
class Vector3;
class Rectangle2D;
struct RenderTargetEvent;
} }
namespace MWWorld namespace Resource
{ {
class Fallback; class ResourceSystem;
} }
namespace MWRender { namespace MWRender
{
class SkyManager;
class RenderingManager;
class RippleSimulation;
class Refraction;
class Reflection
{
public:
Reflection(Ogre::SceneManager* sceneManager)
: mCamera(NULL)
, mParentCamera(NULL)
, mSceneMgr(sceneManager)
, mIsUnderwater(false)
{}
virtual ~Reflection() {}
virtual void setHeight (float height) {}
virtual void setParentCamera (Ogre::Camera* parent) { mParentCamera = parent; }
void setUnderwater(bool underwater) { mIsUnderwater = underwater; }
virtual void setActive (bool active) {}
virtual void setViewportBackground(Ogre::ColourValue colour) {}
virtual void update() {}
virtual void setVisibilityMask (int flags) {}
protected:
Ogre::Camera* mCamera;
Ogre::Camera* mParentCamera;
Ogre::TexturePtr mTexture;
Ogre::SceneManager* mSceneMgr;
bool mIsUnderwater;
};
class CubeReflection : public Reflection
{
public:
CubeReflection(Ogre::SceneManager* sceneManager);
virtual ~CubeReflection();
virtual void update();
protected:
Ogre::RenderTarget* mRenderTargets[6];
};
class PlaneReflection : public Reflection, public Ogre::RenderQueueListener, public Ogre::RenderTargetListener
{
public:
PlaneReflection(Ogre::SceneManager* sceneManager, SkyManager* sky);
virtual ~PlaneReflection();
virtual void setHeight (float height);
virtual void setActive (bool active);
virtual void setVisibilityMask (int flags);
void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
void renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation);
void renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation);
virtual void setViewportBackground(Ogre::ColourValue colour);
protected:
Ogre::RenderTarget* mRenderTarget;
SkyManager* mSky;
Ogre::Plane mWaterPlane;
Ogre::Plane mErrorPlane;
Ogre::Plane mErrorPlaneUnderwater;
bool mRenderActive;
};
/// Water rendering /// Water rendering
class Water : public sh::MaterialInstanceListener class Water
{ {
static const int CELL_SIZE = 8192; static const int CELL_SIZE = 8192;
Ogre::Camera *mCamera;
Ogre::SceneManager *mSceneMgr;
Ogre::Plane mWaterPlane;
Ogre::SceneNode *mWaterNode; osg::ref_ptr<osg::Group> mParent;
Ogre::Entity *mWater; osg::ref_ptr<osg::PositionAttitudeTransform> mWaterNode;
Resource::ResourceSystem* mResourceSystem;
bool mIsUnderwater; bool mEnabled;
bool mActive;
bool mToggled; bool mToggled;
float mTop; float mTop;
float mWaterTimer; osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY);
Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY);
protected:
void applyRTT();
void applyVisibilityMask();
void updateVisible(); void updateVisible();
RenderingManager* mRendering;
SkyManager* mSky;
Ogre::MaterialPtr mMaterial;
bool mUnderwaterEffect;
int mVisibilityFlags;
Reflection* mReflection;
Refraction* mRefraction;
RippleSimulation* mSimulation;
Ogre::Vector2 mPlayer;
public: public:
Water (Ogre::Camera *camera, RenderingManager* rend, const MWWorld::Fallback* fallback); Water(osg::Group* parent, Resource::ResourceSystem* resourceSystem);
~Water(); ~Water();
void clearRipples(); void setEnabled(bool enabled);
void setActive(bool active);
bool toggle(); bool toggle();
void update(float dt, Ogre::Vector3 player);
void frameStarted(float dt);
bool isUnderwater(const osg::Vec3f& pos) const;
/*
/// adds an emitter, position will be tracked automatically using its scene node /// adds an emitter, position will be tracked automatically using its scene node
void addEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f); void addEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f);
void removeEmitter (const MWWorld::Ptr& ptr); void removeEmitter (const MWWorld::Ptr& ptr);
void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr); void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr);
*/
void setViewportBackground(const Ogre::ColourValue& bg); void changeCell(const MWWorld::CellStore* store);
void processChangedSettings(const Settings::CategorySettingVector& settings);
/// Updates underwater state accordingly
void updateUnderwater(bool underwater);
void changeCell(const ESM::Cell* cell);
void setHeight(const float height); void setHeight(const float height);
virtual void requestedConfiguration (sh::MaterialInstance* m, const std::string& configuration);
virtual void createdConfiguration (sh::MaterialInstance* m, const std::string& configuration);
}; };
} }

@ -265,11 +265,11 @@ namespace MWWorld
mRendering.addCell(cell); mRendering.addCell(cell);
bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior(); bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior();
float waterLevel = cell->isExterior() ? -1.f : cell->getWaterLevel(); float waterLevel = cell->isExterior() ? -1.f : cell->getWaterLevel();
//mRendering.setWaterEnabled(waterEnabled); mRendering.setWaterEnabled(waterEnabled);
if (waterEnabled) if (waterEnabled)
{ {
mPhysics->enableWater(waterLevel); mPhysics->enableWater(waterLevel);
//mRendering.setWaterHeight(waterLevel); mRendering.setWaterHeight(waterLevel);
} }
else else
mPhysics->disableWater(); mPhysics->disableWater();

@ -1763,17 +1763,17 @@ namespace MWWorld
void World::setWaterHeight(const float height) void World::setWaterHeight(const float height)
{ {
mPhysics->setWaterHeight(height); mPhysics->setWaterHeight(height);
//mRendering->setWaterHeight(height); mRendering->setWaterHeight(height);
} }
bool World::toggleWater() bool World::toggleWater()
{ {
return 0;//mRendering->toggleWater(); return mRendering->toggleRenderMode(MWRender::Render_Water);
} }
bool World::toggleWorld() bool World::toggleWorld()
{ {
return 0;//mRendering->toggleWorld(); return mRendering->toggleRenderMode(MWRender::Render_Scene);
} }
void World::PCDropped (const Ptr& item) void World::PCDropped (const Ptr& item)

@ -399,6 +399,13 @@ FlipController::FlipController(const Nif::NiFlipController *ctrl, std::vector<os
{ {
} }
FlipController::FlipController(int texSlot, float delta, std::vector<osg::ref_ptr<osg::Texture2D> > textures)
: mTexSlot(texSlot)
, mDelta(delta)
, mTextures(textures)
{
}
FlipController::FlipController() FlipController::FlipController()
{ {
} }

@ -218,6 +218,7 @@ namespace NifOsg
public: public:
FlipController(const Nif::NiFlipController* ctrl, std::vector<osg::ref_ptr<osg::Texture2D> > textures); FlipController(const Nif::NiFlipController* ctrl, std::vector<osg::ref_ptr<osg::Texture2D> > textures);
FlipController(int texSlot, float delta, std::vector<osg::ref_ptr<osg::Texture2D> > textures);
FlipController(); FlipController();
FlipController(const FlipController& copy, const osg::CopyOp& copyop); FlipController(const FlipController& copy, const osg::CopyOp& copyop);

@ -20,7 +20,10 @@ namespace SceneUtil
float Controller::getInputValue(osg::NodeVisitor* nv) float Controller::getInputValue(osg::NodeVisitor* nv)
{ {
return mFunction->calculate(mSource->getValue(nv)); if (mFunction)
return mFunction->calculate(mSource->getValue(nv));
else
return mSource->getValue(nv);
} }
void Controller::setSource(boost::shared_ptr<ControllerSource> source) void Controller::setSource(boost::shared_ptr<ControllerSource> source)

Loading…
Cancel
Save