pxmlw6n2f/Gazebo_Distributed_TCP/gazebo/rendering/RenderEngine.cc

865 lines
24 KiB
C++

/*
* Copyright (C) 2012 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.
*
*/
#ifdef _WIN32
// Ensure that Winsock2.h is included before Windows.h, which can get
// pulled in by anybody (e.g., Boost).
#include <Winsock2.h>
#endif
#include <string>
#include <iostream>
#include <functional>
#include <boost/filesystem.hpp>
#include <sys/types.h>
#ifdef __APPLE__
# include <QtCore/qglobal.h>
#endif
// Not Apple or Windows
#if not defined( Q_OS_MAC) && not defined(_WIN32)
# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include <GL/glx.h>
#endif
#ifndef _WIN32
#include <dirent.h>
#else
#include "gazebo/common/win_dirent.h"
#endif
#include "gazebo/gazebo_config.h"
#include "gazebo/common/CommonIface.hh"
#include "gazebo/common/Events.hh"
#include "gazebo/common/Exception.hh"
#include "gazebo/common/Console.hh"
#include "gazebo/common/SystemPaths.hh"
#include "gazebo/rendering/ogre_gazebo.h"
#include "gazebo/rendering/Material.hh"
#include "gazebo/rendering/RenderEvents.hh"
#include "gazebo/rendering/RTShaderSystem.hh"
#include "gazebo/rendering/WindowManager.hh"
#include "gazebo/rendering/Scene.hh"
#include "gazebo/rendering/RenderTypes.hh"
#include "gazebo/rendering/RenderEngine.hh"
#include "gazebo/rendering/RenderEnginePrivate.hh"
using namespace gazebo;
using namespace rendering;
//////////////////////////////////////////////////
RenderEngine::RenderEngine()
: dataPtr(new RenderEnginePrivate)
{
this->dataPtr->logManager = NULL;
this->dataPtr->root = NULL;
#if (OGRE_VERSION >= ((1 << 16) | (9 << 8) | 0))
this->dataPtr->overlaySystem = NULL;
#endif
this->dummyDisplay = NULL;
this->dataPtr->initialized = false;
this->dataPtr->renderPathType = NONE;
this->dataPtr->windowManager.reset(new WindowManager);
}
//////////////////////////////////////////////////
RenderEngine::~RenderEngine()
{
// this->Fini();
}
//////////////////////////////////////////////////
void RenderEngine::Load()
{
if (!this->CreateContext())
{
gzwarn << "Unable to create X window. Rendering will be disabled\n";
return;
}
if (!this->dataPtr->root)
{
this->dataPtr->connections.push_back(event::Events::ConnectPreRender(
std::bind(&RenderEngine::PreRender, this)));
this->dataPtr->connections.push_back(event::Events::ConnectRender(
std::bind(&RenderEngine::Render, this)));
this->dataPtr->connections.push_back(event::Events::ConnectPostRender(
std::bind(&RenderEngine::PostRender, this)));
// Create a new log manager and prevent output from going to stdout
this->dataPtr->logManager = new Ogre::LogManager();
std::string logPath = common::SystemPaths::Instance()->GetLogPath();
logPath += "/ogre.log";
this->dataPtr->logManager->createLog(logPath, true, false, false);
// Make the root
try
{
this->dataPtr->root = new Ogre::Root("", "");
}
catch(Ogre::Exception &e)
{
gzthrow("Unable to create an Ogre rendering environment, no Root ");
}
#if (OGRE_VERSION >= ((1 << 16) | (9 << 8) | 0))
// OgreOverlay is a component on its own in ogre 1.9 so must manually
// initialize it. Must be created after this->dataPtr->root,
// but before this->dataPtr->root is initialized.
if (!this->dataPtr->overlaySystem)
this->dataPtr->overlaySystem = new Ogre::OverlaySystem();
#endif
// Load all the plugins
this->LoadPlugins();
// Setup the rendering system, and create the context
this->SetupRenderSystem();
// Initialize the root node, and don't create a window
this->dataPtr->root->initialise(false);
// Setup the available resources
this->SetupResources();
}
// Create a 1x1 render window so that we can grab a GL context. Based on
// testing, this is a hard requirement by Apple. We also need it to
// properly initialize GLWidget and UserCameras. See the GLWidget
// constructor.
this->dataPtr->windowManager->CreateWindow(
std::to_string(this->dummyWindowId), 1, 1);
this->CheckSystemCapabilities();
}
//////////////////////////////////////////////////
ScenePtr RenderEngine::CreateScene(const std::string &_name,
bool _enableVisualizations,
bool _isServer)
{
if (this->dataPtr->renderPathType == NONE)
return ScenePtr();
if (!this->dataPtr->initialized)
{
gzerr << "RenderEngine is not initialized\n";
return ScenePtr();
}
ScenePtr scene;
try
{
scene.reset(new Scene(_name, _enableVisualizations, _isServer));
}
catch(...)
{
gzerr << "Failed to instantiate a scene\n";
scene.reset();
return scene;
}
try
{
scene->Load();
}
catch(...)
{
gzerr << "Failed to load scene\n";
scene.reset();
return scene;
}
try
{
scene->Init();
}
catch(...)
{
gzerr << "Failed to initialize scene\n";
scene.reset();
return scene;
}
this->dataPtr->scenes.push_back(scene);
rendering::Events::createScene(_name);
return scene;
}
//////////////////////////////////////////////////
void RenderEngine::RemoveScene(const std::string &_name)
{
if (this->dataPtr->renderPathType == NONE)
return;
for (auto iter = this->dataPtr->scenes.begin();
iter != this->dataPtr->scenes.end(); ++iter)
{
if ((*iter)->Name() == _name)
{
rendering::Events::removeScene(_name);
(*iter)->Clear();
(*iter).reset();
this->dataPtr->scenes.erase(iter);
return;
}
}
}
//////////////////////////////////////////////////
ScenePtr RenderEngine::GetScene(const std::string &_name)
{
if (this->dataPtr->renderPathType == NONE)
return ScenePtr();
for (const auto &scene : this->dataPtr->scenes)
{
if (_name.empty() || scene->Name() == _name)
return scene;
}
return ScenePtr();
}
//////////////////////////////////////////////////
ScenePtr RenderEngine::GetScene(unsigned int index)
{
if (index < this->dataPtr->scenes.size())
return this->dataPtr->scenes[index];
else
{
gzerr << "Invalid Scene Index[" << index << "]\n";
return ScenePtr();
}
}
//////////////////////////////////////////////////
unsigned int RenderEngine::GetSceneCount() const
{
return this->SceneCount();
}
//////////////////////////////////////////////////
unsigned int RenderEngine::SceneCount() const
{
return this->dataPtr->scenes.size();
}
//////////////////////////////////////////////////
void RenderEngine::PreRender()
{
this->dataPtr->root->_fireFrameStarted();
}
//////////////////////////////////////////////////
void RenderEngine::Render()
{
}
//////////////////////////////////////////////////
void RenderEngine::PostRender()
{
// _fireFrameRenderingQueued was here for CEGUI to work. Leaving because
// it shouldn't harm anything, and we don't want to introduce
// a regression.
this->dataPtr->root->_fireFrameRenderingQueued();
this->dataPtr->root->_fireFrameEnded();
}
//////////////////////////////////////////////////
void RenderEngine::Init()
{
if (this->dataPtr->renderPathType == NONE)
{
gzwarn << "Cannot initialize render engine since "
<< "render path type is NONE. Ignore this warning if"
<< "rendering has been turned off on purpose.\n";
return;
}
this->dataPtr->initialized = false;
Ogre::ColourValue ambient;
/// Create a dummy rendering context.
/// This will allow gazebo to run headless. And it also allows OGRE to
/// initialize properly
// Set default mipmap level (NB some APIs ignore this)
Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
// init the resources
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(
Ogre::TFO_ANISOTROPIC);
RTShaderSystem::Instance()->Init();
rendering::Material::CreateMaterials();
for (unsigned int i = 0; i < this->dataPtr->scenes.size(); i++)
this->dataPtr->scenes[i]->Init();
this->dataPtr->initialized = true;
}
//////////////////////////////////////////////////
void RenderEngine::Fini()
{
// TODO: this was causing a segfault on shutdown
// Windows are created on load so clear them even
// if render engine is not initialized
// Close all the windows first;
this->dataPtr->windowManager->Fini();
if (!this->dataPtr->initialized)
return;
this->dataPtr->connections.clear();
RTShaderSystem::Instance()->Fini();
// Deallocate memory for every scene
while (!this->dataPtr->scenes.empty())
{
this->RemoveScene(this->dataPtr->scenes.front()->Name());
}
#if (OGRE_VERSION >= ((1 << 16) | (9 << 8) | 0))
delete this->dataPtr->overlaySystem;
this->dataPtr->overlaySystem = NULL;
#endif
// TODO: this was causing a segfault. Need to debug, and put back in
if (this->dataPtr->root)
{
/*const Ogre::Root::PluginInstanceList ll =
this->dataPtr->root->getInstalledPlugins();
for (Ogre::Root::PluginInstanceList::const_iterator iter = ll.begin();
iter != ll.end(); iter++)
{
this->dataPtr->root->unloadPlugin((*iter)->getName());
this->dataPtr->root->uninstallPlugin(*iter);
}
*/
try
{
delete this->dataPtr->root;
}
catch(...)
{
}
}
this->dataPtr->root = NULL;
delete this->dataPtr->logManager;
this->dataPtr->logManager = NULL;
for (unsigned int i = 0; i < this->dataPtr->scenes.size(); ++i)
this->dataPtr->scenes[i].reset();
this->dataPtr->scenes.clear();
// Not Apple or Windows
# if not defined( Q_OS_MAC) && not defined(_WIN32)
if (this->dummyDisplay)
{
glXDestroyContext(static_cast<Display*>(this->dummyDisplay),
static_cast<GLXContext>(this->dummyContext));
XDestroyWindow(static_cast<Display*>(this->dummyDisplay),
this->dummyWindowId);
XCloseDisplay(static_cast<Display*>(this->dummyDisplay));
this->dummyDisplay = NULL;
}
# endif
this->dataPtr->initialized = false;
}
//////////////////////////////////////////////////
void RenderEngine::LoadPlugins()
{
std::list<std::string>::iterator iter;
std::list<std::string> ogrePaths =
common::SystemPaths::Instance()->GetOgrePaths();
for (iter = ogrePaths.begin();
iter != ogrePaths.end(); ++iter)
{
std::string path(*iter);
DIR *dir = opendir(path.c_str());
if (dir == NULL)
{
continue;
}
closedir(dir);
std::vector<std::string> plugins;
std::vector<std::string>::iterator piter;
#ifdef __APPLE__
std::string prefix = "lib";
std::string extension = ".dylib";
#else
std::string prefix = "";
std::string extension = ".so";
#endif
plugins.push_back(path+"/"+prefix+"RenderSystem_GL");
plugins.push_back(path+"/"+prefix+"Plugin_ParticleFX");
plugins.push_back(path+"/"+prefix+"Plugin_BSPSceneManager");
plugins.push_back(path+"/"+prefix+"Plugin_OctreeSceneManager");
#ifdef HAVE_OCULUS
plugins.push_back(path+"/Plugin_CgProgramManager");
#endif
for (piter = plugins.begin(); piter != plugins.end(); ++piter)
{
try
{
// Load the plugin into OGRE
this->dataPtr->root->loadPlugin(*piter+extension);
}
catch(Ogre::Exception &e)
{
try
{
// Load the debug plugin into OGRE
this->dataPtr->root->loadPlugin(*piter+"_d"+extension);
}
catch(Ogre::Exception &ed)
{
if ((*piter).find("RenderSystem") != std::string::npos)
{
gzerr << "Unable to load Ogre Plugin[" << *piter
<< "]. Rendering will not be possible."
<< "Make sure you have installed OGRE and Gazebo properly.\n";
}
}
}
}
}
}
/////////////////////////////////////////////////
void RenderEngine::AddResourcePath(const std::string &_uri)
{
if (_uri == "__default__" || _uri.empty())
return;
std::string path = common::find_file_path(_uri);
if (path.empty())
{
gzerr << "URI doesn't exist[" << _uri << "]\n";
return;
}
try
{
if (!Ogre::ResourceGroupManager::getSingleton().resourceLocationExists(
path, "General"))
{
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
path, "FileSystem", "General", true);
Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup(
"General");
// Parse all material files in the path if any exist
boost::filesystem::path dir(path);
if (boost::filesystem::exists(dir) &&
boost::filesystem::is_directory(dir))
{
std::vector<boost::filesystem::path> paths;
std::copy(boost::filesystem::directory_iterator(dir),
boost::filesystem::directory_iterator(),
std::back_inserter(paths));
std::sort(paths.begin(), paths.end());
// Iterate over all the models in the current gazebo path
for (std::vector<boost::filesystem::path>::iterator dIter =
paths.begin(); dIter != paths.end(); ++dIter)
{
if (dIter->filename().extension() == ".material")
{
boost::filesystem::path fullPath = path / dIter->filename();
Ogre::DataStreamPtr stream =
Ogre::ResourceGroupManager::getSingleton().openResource(
fullPath.string(), "General");
// There is a material file under there somewhere, read the thing in
try
{
Ogre::MaterialManager::getSingleton().parseScript(
stream, "General");
Ogre::MaterialPtr matPtr =
Ogre::MaterialManager::getSingleton().getByName(
fullPath.string());
if (!matPtr.isNull())
{
// is this necessary to do here? Someday try it without
matPtr->compile();
matPtr->load();
}
}
catch(Ogre::Exception& e)
{
gzerr << "Unable to parse material file[" << fullPath << "]\n";
}
stream->close();
}
}
}
}
}
catch(Ogre::Exception &/*_e*/)
{
gzthrow("Unable to load Ogre Resources.\nMake sure the"
"resources path in the world file is set correctly.");
}
}
//////////////////////////////////////////////////
RenderEngine::RenderPathType RenderEngine::GetRenderPathType() const
{
return this->dataPtr->renderPathType;
}
//////////////////////////////////////////////////
void RenderEngine::SetupResources()
{
std::vector< std::pair<std::string, std::string> > archNames;
std::vector< std::pair<std::string, std::string> >::iterator aiter;
std::list<std::string>::const_iterator iter;
std::list<std::string> paths =
common::SystemPaths::Instance()->GetGazeboPaths();
std::list<std::string> mediaDirs;
mediaDirs.push_back("media");
for (iter = paths.begin(); iter != paths.end(); ++iter)
{
DIR *dir;
if ((dir = opendir((*iter).c_str())) == NULL)
{
continue;
}
closedir(dir);
archNames.push_back(
std::make_pair((*iter)+"/", "General"));
for (std::list<std::string>::iterator mediaIter = mediaDirs.begin();
mediaIter != mediaDirs.end(); ++mediaIter)
{
std::string prefix = (*iter) + "/" + (*mediaIter);
archNames.push_back(
std::make_pair(prefix, "General"));
archNames.push_back(
std::make_pair(prefix + "/skyx", "SkyX"));
#if (OGRE_VERSION >= ((1 << 16) | (9 << 8) | 0) && !defined(__APPLE__))
archNames.push_back(
std::make_pair(prefix + "/rtshaderlib150", "General"));
#endif
archNames.push_back(
std::make_pair(prefix + "/rtshaderlib", "General"));
archNames.push_back(
std::make_pair(prefix + "/materials/programs", "General"));
archNames.push_back(
std::make_pair(prefix + "/materials/scripts", "General"));
archNames.push_back(
std::make_pair(prefix + "/materials/textures", "General"));
archNames.push_back(
std::make_pair(prefix + "/media/models", "General"));
archNames.push_back(
std::make_pair(prefix + "/fonts", "Fonts"));
archNames.push_back(
std::make_pair(prefix + "/gui/looknfeel", "LookNFeel"));
archNames.push_back(
std::make_pair(prefix + "/gui/schemes", "Schemes"));
archNames.push_back(
std::make_pair(prefix + "/gui/imagesets", "Imagesets"));
archNames.push_back(
std::make_pair(prefix + "/gui/fonts", "Fonts"));
archNames.push_back(
std::make_pair(prefix + "/gui/layouts", "Layouts"));
archNames.push_back(
std::make_pair(prefix + "/gui/animations", "Animations"));
}
for (aiter = archNames.begin(); aiter != archNames.end(); ++aiter)
{
try
{
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
aiter->first, "FileSystem", aiter->second);
}
catch(Ogre::Exception &/*_e*/)
{
gzthrow("Unable to load Ogre Resources. Make sure the resources path "
"in the world file is set correctly.");
}
}
}
}
//////////////////////////////////////////////////
void RenderEngine::SetupRenderSystem()
{
Ogre::RenderSystem *renderSys;
const Ogre::RenderSystemList *rsList;
// Set parameters of render system (window size, etc.)
#if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR == 6
rsList = this->dataPtr->root->getAvailableRenderers();
#else
rsList = &(this->dataPtr->root->getAvailableRenderers());
#endif
int c = 0;
renderSys = NULL;
do
{
if (c == static_cast<int>(rsList->size()))
break;
renderSys = rsList->at(c);
c++;
}
while (renderSys &&
renderSys->getName().compare("OpenGL Rendering Subsystem") != 0);
if (renderSys == NULL)
{
gzthrow("unable to find OpenGL rendering system. OGRE is probably "
"installed incorrectly. Double check the OGRE cmake output, "
"and make sure OpenGL is enabled.");
}
// We operate in windowed mode
renderSys->setConfigOption("Full Screen", "No");
/// We used to allow the user to set the RTT mode to PBuffer, FBO, or Copy.
/// Copy is slow, and there doesn't seem to be a good reason to use it
/// PBuffer limits the size of the renderable area of the RTT to the
/// size of the first window created.
/// FBO seem to be the only good option
renderSys->setConfigOption("RTT Preferred Mode", "FBO");
// get all supported fsaa values
Ogre::ConfigOptionMap configMap = renderSys->getConfigOptions();
auto fsaaOoption = configMap.find("FSAA");
if (fsaaOoption != configMap.end())
{
auto values = (*fsaaOoption).second.possibleValues;
for (auto const &str : values)
{
int value = 0;
try
{
value = std::stoi(str);
}
catch(...)
{
continue;
}
this->dataPtr->fsaaLevels.push_back(value);
}
}
std::sort(this->dataPtr->fsaaLevels.begin(), this->dataPtr->fsaaLevels.end());
// check if target fsaa is supported
unsigned int fsaa = 0;
unsigned int targetFSAA = 4;
auto const it = std::find(this->dataPtr->fsaaLevels.begin(),
this->dataPtr->fsaaLevels.end(), targetFSAA);
if (it != this->dataPtr->fsaaLevels.end())
fsaa = targetFSAA;
renderSys->setConfigOption("FSAA", std::to_string(fsaa));
this->dataPtr->root->setRenderSystem(renderSys);
}
/////////////////////////////////////////////////
bool RenderEngine::CreateContext()
{
bool result = true;
#if defined Q_OS_MAC || _WIN32
this->dummyDisplay = 0;
#else
try
{
this->dummyDisplay = XOpenDisplay(0);
if (!this->dummyDisplay)
{
gzerr << "Can't open display: " << XDisplayName(0) << "\n";
return false;
}
int screen = DefaultScreen(this->dummyDisplay);
int attribList[] = {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 16,
GLX_STENCIL_SIZE, 8, None };
XVisualInfo *dummyVisual = glXChooseVisual(
static_cast<Display*>(this->dummyDisplay),
screen, static_cast<int *>(attribList));
if (!dummyVisual)
{
gzerr << "Unable to create glx visual\n";
return false;
}
this->dummyWindowId = XCreateSimpleWindow(
static_cast<Display*>(this->dummyDisplay),
RootWindow(static_cast<Display*>(this->dummyDisplay), screen),
0, 0, 1, 1, 0, 0, 0);
this->dummyContext = glXCreateContext(
static_cast<Display*>(this->dummyDisplay),
dummyVisual, NULL, 1);
if (!this->dummyContext)
{
gzerr << "Unable to create glx context\n";
return false;
}
glXMakeCurrent(static_cast<Display*>(this->dummyDisplay),
this->dummyWindowId, static_cast<GLXContext>(this->dummyContext));
}
catch(...)
{
result = false;
}
#endif
return result;
}
/////////////////////////////////////////////////
void RenderEngine::CheckSystemCapabilities()
{
const Ogre::RenderSystemCapabilities *capabilities;
Ogre::RenderSystemCapabilities::ShaderProfiles profiles;
Ogre::RenderSystemCapabilities::ShaderProfiles::const_iterator iter;
capabilities = this->dataPtr->root->getRenderSystem()->getCapabilities();
profiles = capabilities->getSupportedShaderProfiles();
bool hasFragmentPrograms =
capabilities->hasCapability(Ogre::RSC_FRAGMENT_PROGRAM);
bool hasVertexPrograms =
capabilities->hasCapability(Ogre::RSC_VERTEX_PROGRAM);
// bool hasGeometryPrograms =
// capabilities->hasCapability(Ogre::RSC_GEOMETRY_PROGRAM);
// bool hasRenderToVertexBuffer =
// capabilities->hasCapability(Ogre::RSC_HWRENDER_TO_VERTEX_BUFFER);
// int multiRenderTargetCount = capabilities->getNumMultiRenderTargets();
bool hasFBO =
capabilities->hasCapability(Ogre::RSC_FBO);
bool hasGLSL =
std::find(profiles.begin(), profiles.end(), "glsl") != profiles.end();
if (!hasFragmentPrograms || !hasVertexPrograms)
gzwarn << "Vertex and fragment shaders are missing. "
<< "Fixed function rendering will be used.\n";
if (!hasGLSL)
gzwarn << "GLSL is missing."
<< "Fixed function rendering will be used.\n";
if (!hasFBO)
gzwarn << "Frame Buffer Objects (FBO) is missing. "
<< "Rendering will be disabled.\n";
this->dataPtr->renderPathType = RenderEngine::NONE;
if (hasFBO && hasGLSL && hasVertexPrograms && hasFragmentPrograms)
this->dataPtr->renderPathType = RenderEngine::FORWARD;
else if (hasFBO)
this->dataPtr->renderPathType = RenderEngine::VERTEX;
// Disable deferred rendering for now. Needs more work.
// if (hasRenderToVertexBuffer && multiRenderTargetCount >= 8)
// this->dataPtr->renderPathType = RenderEngine::DEFERRED;
}
/////////////////////////////////////////////////
WindowManagerPtr RenderEngine::GetWindowManager() const
{
return this->dataPtr->windowManager;
}
/////////////////////////////////////////////////
Ogre::Root *RenderEngine::Root() const
{
return this->dataPtr->root;
}
/////////////////////////////////////////////////
std::vector<unsigned int> RenderEngine::FSAALevels() const
{
return this->dataPtr->fsaaLevels;
}
#if (OGRE_VERSION >= ((1 << 16) | (9 << 8) | 0))
/////////////////////////////////////////////////
Ogre::OverlaySystem *RenderEngine::GetOverlaySystem() const
{
return this->OverlaySystem();
}
/////////////////////////////////////////////////
Ogre::OverlaySystem *RenderEngine::OverlaySystem() const
{
return this->dataPtr->overlaySystem;
}
#endif