/* * Copyright (C) 2018 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include #include #include #include #include #include #include "AmbientOcclusionVisualPlugin.hh" namespace gazebo { /// \brief Helper class to assign the GBuffer material to compositors that /// need them class SsaoGBufferSchemeHandler : public Ogre::MaterialManager::Listener { /// \brief Constructor public: SsaoGBufferSchemeHandler() { this->gBufRefMat = Ogre::MaterialManager::getSingleton().getByName("SSAO/GBuffer"); if (this->gBufRefMat.isNull()) { gzerr << "Unable to find 'SSAO/GBuffer' material, SSAO will not work" << std::endl; } } /// \brief Destructor public: ~SsaoGBufferSchemeHandler() { this->gBufRefMat.setNull(); } /// \brief Ogre callback for assigning the GBuffer material to compositors /// \param[in] _schemeIndex Index of scheme requested /// \param[in] _schemeName Name of scheme requested /// \param[in] _originalMaterial Orignal material that does not contain /// the requested scheme /// \param[in] _lodIndex The material level-of-detail /// \param[in] _rend Pointer to the Ogre::Renderable object requesting /// the use of the techinique /// \return The Ogre material technique to use when scheme is not found. public: virtual Ogre::Technique *handleSchemeNotFound( uint16_t /*_schemeIndex*/, const Ogre::String& _schemeName, Ogre::Material *_originalMaterial, uint16_t /*_lodIndex*/, const Ogre::Renderable */*_rend*/) { // ignore transparent / semi-tranparent materials with alpha rejection set Ogre::Technique *origTech = _originalMaterial->getTechnique(0); if (origTech) { for (unsigned int i = 0; i < origTech->getNumPasses(); ++i) { Ogre::Pass *origPass = origTech->getPass(i); if (origPass && origPass->getAlphaRejectFunction() != Ogre::CMPF_ALWAYS_PASS) return nullptr; } } // set to use gbuffer Ogre::Technique *gBufferTech = _originalMaterial->createTechnique(); gBufferTech->setSchemeName(_schemeName); Ogre::Pass *gbufPass = gBufferTech->createPass(); if (!this->gBufRefMat.isNull()) *gbufPass = *this->gBufRefMat->getTechnique(0)->getPass(0); return gBufferTech; } /// \brief GBuffer material private: Ogre::MaterialPtr gBufRefMat; }; /// \brief Private data for the AmbientOcclusionVisualPlugin class. class AmbientOcclusionVisualPluginPrivate { /// \brief Destructor public: ~AmbientOcclusionVisualPluginPrivate(); /// \brief Ambient occlusion compositor name public: std::string compositorName; /// \brief Post filter compositor name public: std::string postFilterName; /// \brief GBuffer material scheme handler public: SsaoGBufferSchemeHandler *gBufSchemeHandler = nullptr; /// \brief Apply ambient occlusion to the viewport of the input camera /// \param[in] _cam Pointer to a camera public: void AddSsao(rendering::CameraPtr _cam); }; } using namespace gazebo; GZ_REGISTER_VISUAL_PLUGIN(AmbientOcclusionVisualPlugin) ///////////////////////////////////////////////// AmbientOcclusionVisualPluginPrivate::~AmbientOcclusionVisualPluginPrivate() { Ogre::MaterialManager::getSingleton().removeListener( this->gBufSchemeHandler, "GBuffer"); delete this->gBufSchemeHandler; this->gBufSchemeHandler = nullptr; } ///////////////////////////////////////////////// AmbientOcclusionVisualPlugin::AmbientOcclusionVisualPlugin() : dataPtr(new AmbientOcclusionVisualPluginPrivate) { } ///////////////////////////////////////////////// AmbientOcclusionVisualPlugin::~AmbientOcclusionVisualPlugin() { } ///////////////////////////////////////////////// void AmbientOcclusionVisualPlugin::Load(rendering::VisualPtr _visual, sdf::ElementPtr _sdf) { if (!_visual || !_sdf) { gzerr << "No visual or SDF element specified. Plugin won't load." << std::endl; return; } // Use Crease Shading. There are others but this one is fast // and gives reasonably nice looking results this->dataPtr->compositorName = "SSAO/CreaseShading"; this->dataPtr->postFilterName = "SSAO/Post/NoFilter"; rendering::ScenePtr scene = _visual->GetScene(); if (!scene) { gzerr << "Scene is null. Ambient Occlusion will not be enabled" << std::endl; return; } // apply to all cameras for (unsigned int i = 0; i < scene->CameraCount(); ++i) { this->dataPtr->AddSsao(scene->GetCamera(i)); } for (unsigned int i = 0; i < scene->UserCameraCount(); ++i) { rendering::CameraPtr cam = boost::dynamic_pointer_cast( scene->GetUserCamera(i)); this->dataPtr->AddSsao(cam); } this->dataPtr->gBufSchemeHandler = new SsaoGBufferSchemeHandler(); Ogre::MaterialManager::getSingleton().addListener( this->dataPtr->gBufSchemeHandler, "GBuffer"); } ///////////////////////////////////////////////// void AmbientOcclusionVisualPluginPrivate::AddSsao(rendering::CameraPtr _cam) { Ogre::Viewport *ogreViewport = _cam->OgreCamera()->getViewport(); if (!ogreViewport) { gzerr << "Viewport is null. Ambient Occlusion will not be enabled" << std::endl; return; } // GBuffer Ogre::CompositorInstance *gBufCompInstance = Ogre::CompositorManager::getSingleton().addCompositor( ogreViewport, "SSAO/GBuffer"); if (gBufCompInstance) { gBufCompInstance->setEnabled(true); // the GBuffer compositor should have 2 passes, // see gazebo/media/materials/scripts/CreaseShading.compositor if (gBufCompInstance->getTechnique()->getNumTargetPasses() > 1) { // set a visibility mask so that the ssao effect does not apply to // gui visuals gBufCompInstance->getTechnique()->getTargetPass(1)->setVisibilityMask( GZ_VISIBILITY_ALL & ~(GZ_VISIBILITY_GUI | GZ_VISIBILITY_SELECTABLE)); } } else { gzerr << "Failed to add GBuffer compositor" << std::endl; } // SSAO - Crease Shading if (Ogre::CompositorManager::getSingleton().addCompositor( ogreViewport, this->compositorName)) { Ogre::CompositorManager::getSingleton().setCompositorEnabled( ogreViewport, compositorName, true); } else { gzerr << "Failed to add compositor: " << this->compositorName << std::endl; } // SSAO post filter if (Ogre::CompositorManager::getSingleton().addCompositor( ogreViewport, this->postFilterName)) { Ogre::CompositorManager::getSingleton().setCompositorEnabled( ogreViewport, this->postFilterName, true); } else { gzerr << "Failed to add " << this->postFilterName << " compositor" << std::endl; } // modulate with existing scene std::string postModulateFilterName = "SSAO/Post/Modulate"; if (Ogre::CompositorManager::getSingleton().addCompositor( ogreViewport, postModulateFilterName)) { Ogre::CompositorManager::getSingleton().setCompositorEnabled( ogreViewport, postModulateFilterName, true); } else { gzerr << "Failed to add " << postModulateFilterName << " compositor" << std::endl; } }