#include "water.hpp" #include #include #include #include #include #include #include #include #include #include #include #include "sky.hpp" #include "renderingmanager.hpp" #include "ripplesimulation.hpp" #include "refraction.hpp" #include #include 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 () { if (mParentCamera->isAttached()) 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) { 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()); 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 (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), mVisibilityFlags(0), mActive(1), mToggled(1), mRendering(rend), mWaterTimer(0.f), 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(CELL_SIZE*5*waterScale), static_cast(CELL_SIZE*5*waterScale), 40, 40, true, 1, static_cast(3 * waterScale), static_cast(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( 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 (new sh::FloatValue(active ? 1.0f : 0.0f))); } 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) { if(cell->isExterior()) mWaterNode->setPosition(getSceneNodeCoordinates(cell->mData.mX, cell->mData.mY)); } void Water::setHeight(const float height) { mTop = height; mSimulation->setWaterHeight(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(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(static_cast(gridX * CELL_SIZE + (CELL_SIZE / 2)), static_cast(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(new sh::FloatValue(mWaterTimer))); mRendering->getSkyManager ()->setGlareEnabled (!mIsUnderwater); mPlayer = Ogre::Vector2(player.x, player.y); } void Water::frameStarted(float dt) { if (!mActive) return; 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_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) { } 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(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); } void Water::clearRipples() { mSimulation->clear(); } } // namespace