mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 15:56:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			500 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			500 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "water.hpp"
 | |
| 
 | |
| #include <OgreRenderTexture.h>
 | |
| #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>
 | |
| 
 | |
| #include "../mwbase/environment.hpp"
 | |
| #include "../mwbase/world.hpp"
 | |
| 
 | |
| using namespace Ogre;
 | |
| 
 | |
| namespace MWRender
 | |
| {
 | |
| 
 | |
| CubeReflection::CubeReflection(Ogre::SceneManager* sceneManager)
 | |
|     : Reflection(sceneManager)
 | |
| {
 | |
|     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 ()
 | |
| {
 | |
|     Ogre::TextureManager::getSingleton ().remove("CubeReflection");
 | |
|     mSceneMgr->destroyCamera (mCamera);
 | |
| }
 | |
| 
 | |
| void CubeReflection::update ()
 | |
| {
 | |
|     mParentCamera->getParentSceneNode ()->needUpdate ();
 | |
|     mCamera->setPosition(mParentCamera->getDerivedPosition());
 | |
| }
 | |
| 
 | |
| // --------------------------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
| PlaneReflection::PlaneReflection(Ogre::SceneManager* sceneManager, SkyManager* sky)
 | |
|     : 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 ()
 | |
| {
 | |
|     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();
 | |
|         Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS());
 | |
|     }
 | |
| }
 | |
| 
 | |
| void PlaneReflection::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation)
 | |
| {
 | |
|     if (queueGroupId < 20 && mRenderActive)
 | |
|     {
 | |
|         mCamera->enableCustomNearClipPlane(mIsUnderwater ? mErrorPlaneUnderwater : mErrorPlane);
 | |
|         Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS());
 | |
|     }
 | |
| }
 | |
| 
 | |
| void PlaneReflection::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
 | |
| {
 | |
|     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());
 | |
|     mRenderActive = true;
 | |
| 
 | |
|     mCamera->enableReflection(mWaterPlane);
 | |
| 
 | |
|     // 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 ? 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) :
 | |
|     mCamera (camera), mSceneMgr (camera->getSceneManager()),
 | |
|     mIsUnderwater(false), mVisibilityFlags(0),
 | |
|     mActive(1), mToggled(1),
 | |
|     mRendering(rend),
 | |
|     mWaterTimer(0.f),
 | |
|     mReflection(NULL),
 | |
|     mRefraction(NULL),
 | |
|     mSimulation(NULL),
 | |
|     mPlayer(0,0)
 | |
| {
 | |
|     mSimulation = new RippleSimulation(mSceneMgr);
 | |
| 
 | |
|     mSky = rend->getSkyManager();
 | |
| 
 | |
|     mMaterial = MaterialManager::getSingleton().getByName("Water");
 | |
| 
 | |
|     mTop = 0;
 | |
| 
 | |
|     mIsUnderwater = false;
 | |
| 
 | |
|     mWaterPlane = Plane(Vector3::UNIT_Z, 0);
 | |
| 
 | |
|     int waterScale = 300;
 | |
| 
 | |
|     MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane,
 | |
|         CELL_SIZE*5*waterScale, CELL_SIZE*5*waterScale, 10, 10, true, 1, 3*waterScale,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);
 | |
| 
 | |
|     sh::MaterialInstance* m = sh::Factory::getInstance ().getMaterialInstance ("Water");
 | |
|     m->setListener (this);
 | |
| 
 | |
|     // ----------------------------------------------------------------------------------------------
 | |
|     // ---------------------------------- reflection debug overlay ----------------------------------
 | |
|     // ----------------------------------------------------------------------------------------------
 | |
| /*
 | |
|     if (Settings::Manager::getBool("shader", "Water"))
 | |
|     {
 | |
|         OverlayManager& mgr = OverlayManager::getSingleton();
 | |
|         Overlay* overlay;
 | |
|         // destroy if already exists
 | |
|         if ((overlay = mgr.getByName("ReflectionDebugOverlay")))
 | |
|             mgr.destroy(overlay);
 | |
| 
 | |
|         overlay = mgr.create("ReflectionDebugOverlay");
 | |
| 
 | |
|         if (MaterialManager::getSingleton().resourceExists("Ogre/ReflectionDebugTexture"))
 | |
|             MaterialManager::getSingleton().remove("Ogre/ReflectionDebugTexture");
 | |
|         MaterialPtr debugMat = MaterialManager::getSingleton().create(
 | |
|             "Ogre/ReflectionDebugTexture",
 | |
|             ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
 | |
| 
 | |
|         debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false);
 | |
|         debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(mReflectionTexture->getName());
 | |
| 
 | |
|         OverlayContainer* debugPanel;
 | |
| 
 | |
|         // destroy container if exists
 | |
|         try
 | |
|         {
 | |
|             if ((debugPanel =
 | |
|                 static_cast<OverlayContainer*>(
 | |
|                     mgr.getOverlayElement("Ogre/ReflectionDebugTexPanel"
 | |
|                 ))))
 | |
|                 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)
 | |
| {
 | |
|     mActive = active;
 | |
|     updateVisible();
 | |
| 
 | |
|     sh::Factory::getInstance ().setSharedParameter ("waterEnabled", sh::makeProperty<sh::FloatValue> (new sh::FloatValue(active ? 1.0 : 0.0)));
 | |
| }
 | |
| 
 | |
| Water::~Water()
 | |
| {
 | |
|     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)
 | |
| {
 | |
|     mTop = cell->mWater;
 | |
| 
 | |
|     setHeight(mTop);
 | |
| 
 | |
|     if(!(cell->mData.mFlags & cell->Interior))
 | |
|         mWaterNode->setPosition(getSceneNodeCoordinates(cell->mData.mX, cell->mData.mY));
 | |
| }
 | |
| 
 | |
| void Water::setHeight(const float height)
 | |
| {
 | |
|     mTop = height;
 | |
| 
 | |
|     mWaterPlane = Plane(Vector3::UNIT_Z, -height);
 | |
| 
 | |
|     if (mReflection)
 | |
|         mReflection->setHeight(height);
 | |
|     if (mRefraction)
 | |
|         mRefraction->setHeight(height);
 | |
| 
 | |
|     mWaterNode->setPosition(0, 0, height);
 | |
|     sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty<sh::FloatValue>(new sh::FloatValue(height)));
 | |
| }
 | |
| 
 | |
| bool Water::toggle()
 | |
| {
 | |
|     mToggled = !mToggled;
 | |
|     updateVisible();
 | |
|     return mToggled;
 | |
| }
 | |
| 
 | |
| void
 | |
| Water::updateUnderwater(bool underwater)
 | |
| {
 | |
|     if (!mActive) {
 | |
|         return;
 | |
|     }
 | |
|     mIsUnderwater =
 | |
|         underwater &&
 | |
|         mWater->isVisible() &&
 | |
|         mCamera->getPolygonMode() == Ogre::PM_SOLID;
 | |
| 
 | |
|     if (mReflection)
 | |
|         mReflection->setUnderwater (mIsUnderwater);
 | |
|     if (mRefraction)
 | |
|         mRefraction->setUnderwater (mIsUnderwater);
 | |
| 
 | |
|     updateVisible();
 | |
| }
 | |
| 
 | |
| Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY)
 | |
| {
 | |
|     return Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), gridY * CELL_SIZE + (CELL_SIZE / 2), mTop);
 | |
| }
 | |
| 
 | |
| void Water::setViewportBackground(const ColourValue& bg)
 | |
| {
 | |
|     if (mReflection)
 | |
|         mReflection->setViewportBackground(bg);
 | |
| }
 | |
| 
 | |
| void Water::updateVisible()
 | |
| {
 | |
|     mWater->setVisible(mToggled && mActive);
 | |
|     if (mReflection)
 | |
|         mReflection->setActive(mToggled && mActive);
 | |
|     if (mRefraction)
 | |
|         mRefraction->setActive(mToggled && mActive);
 | |
| }
 | |
| 
 | |
| void Water::update(float dt, Ogre::Vector3 player)
 | |
| {
 | |
|     mWaterTimer += dt;
 | |
|     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)
 | |
| {
 | |
|     mSimulation->update(dt, mPlayer);
 | |
| 
 | |
|     if (mReflection)
 | |
|         mReflection->update();
 | |
| }
 | |
| 
 | |
| void Water::applyRTT()
 | |
| {
 | |
|     delete mReflection;
 | |
|     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();
 | |
| }
 | |
| 
 | |
| void Water::applyVisibilityMask()
 | |
| {
 | |
|     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_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)
 | |
| {
 | |
| }
 | |
| 
 | |
| void Water::createdConfiguration (sh::MaterialInstance* m, const std::string& configuration)
 | |
| {
 | |
|     if (configuration == "local_map" || !Settings::Manager::getBool("shader", "Water"))
 | |
|     {
 | |
|         // 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)
 | |
| {
 | |
|     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);
 | |
| }
 | |
| 
 | |
| } // namespace
 |