Skip to content

Commit

Permalink
Add Custom Render Plugin Example (#154)
Browse files Browse the repository at this point in the history
Signed-off-by: John Shepherd <[email protected]>

Co-authored-by: Ian Chen <[email protected]>
  • Loading branch information
John Shepherd and iche033 authored Oct 13, 2020
1 parent 0608cb3 commit 8876d6a
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 13 deletions.
16 changes: 16 additions & 0 deletions examples/hello_world_plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)

set(IGN_PLUGIN_VER 1)
set(IGN_COMMON_VER 3)

find_package(ignition-rendering3 REQUIRED)
find_package(ignition-plugin1 REQUIRED COMPONENTS all)

add_library(HelloWorldPlugin SHARED HelloWorldPlugin.cc)
target_link_libraries(HelloWorldPlugin
PUBLIC
${IGNITION-RENDERING_LIBRARIES}
${ignition-common${IGN_COMMON_VER}_LIBRARIES}
PRIVATE
ignition-plugin${IGN_PLUGIN_VER}::register
)
104 changes: 104 additions & 0 deletions examples/hello_world_plugin/HelloWorldPlugin.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright (C) 2020 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 <ignition/common/SingletonT.hh>

#include <ignition/plugin/Register.hh>

#include "ignition/rendering/RenderEnginePlugin.hh"
#include "ignition/rendering/base/BaseRenderEngine.hh"
#include "ignition/rendering/base/BaseRenderTypes.hh"

namespace mock
{
/// \brief The render engine class which implements a render engine.
class HelloWorldRenderEngine :
public virtual ignition::rendering::BaseRenderEngine,
public ignition::common::SingletonT<HelloWorldRenderEngine>
{
// Documentation Inherited.
public: virtual bool IsEnabled() const override
{
return true;
}

// Documentation Inherited.
public: virtual std::string Name() const override
{
return "HelloWorldPlugin";
}

// Documentation Inherited.
protected: virtual bool LoadImpl(const std::map<std::string,
std::string> &_params) override
{
return true;
}

/// \brief Initialize the render engine.
/// \return True if the operation is successful
protected: virtual bool InitImpl() override
{
return true;
}

/// \brief Get a pointer to the list of scenes managed by the render
/// engine.
/// \return list of scenes
protected: virtual ignition::rendering::SceneStorePtr Scenes()
const override
{
return nullptr;
}

/// \brief Create a scene.
/// \param[in] _id Unique scene Id
/// \parampin] _name Name of scene
protected: virtual ignition::rendering::ScenePtr
CreateSceneImpl(unsigned int _id,
const std::string &_name) override
{
return nullptr;
}

/// \brief Singelton setup.
private: friend class ignition::common::SingletonT<HelloWorldRenderEngine>;
};

/// \brief Plugin for loading the HelloWorld render engine.
class HelloWorldPlugin :
public ignition::rendering::RenderEnginePlugin
{
/// \brief Get the name of the render engine loaded by this plugin.
/// \return Name of render engine
public: std::string Name() const override
{
return HelloWorldRenderEngine::Instance()->Name();
}

/// \brief Get a pointer to the render engine loaded by this plugin.
/// \return Render engine instance
public: ignition::rendering::RenderEngine *Engine() const override
{
return HelloWorldRenderEngine::Instance();
}
};
}

// Register this plugin
IGNITION_ADD_PLUGIN(mock::HelloWorldPlugin,
ignition::rendering::RenderEnginePlugin)
43 changes: 43 additions & 0 deletions examples/hello_world_plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Engine plugin

This example shows how to create a plugin that integrates a rendering engine with
Ignition Rendering and how to load it with Ignition Gazebo.

## Build

From the root of the `ign-rendering` repository, do the following to build the example:

~~~
cd examples/hello_world_plugin
mkdir build
cd build
cmake ..
make
~~~

This will generate the `HelloWorldPlugin` library under `build`.
The exact name of the library file depends on the operating system
such as `libHelloWorldPlugin.so` on Linux, `libHelloWorldPlugin.dylib` on macOS,
and `HelloWorldPlugin.dll` on Windows.

## Run

Be sure to have the `IGN_GAZEBO_RENDER_ENGINE_PATH` environment variable set to the path
where your plugin is located. From within the `build` directory of this example, you can run

~~~
export IGN_GAZEBO_RENDER_ENGINE_PATH=$PWD
~~~

to set the environment variable accordingly.


Now you can run `ign gazebo` with the name of the resultant library file (without the `lib` prefix
or the file extension, i.e., libHelloWorldPlugin.so -> HelloWorldPlugin):

~~~
ign gazebo --render-engine HelloWorldPlugin
~~~

You should see a blank screen within the Ignition GUI, as this mocked plugin provides no implementation
for the scene.
2 changes: 1 addition & 1 deletion include/ignition/rendering/RenderingIface.hh
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ namespace ignition
void unregisterEngine(const unsigned int _index);

/// \brief Set the plugin paths from which render engines can be loaded.
/// \param[in] _path The list of the plugin paths
/// \param[in] _paths The list of the plugin paths
IGNITION_RENDERING_VISIBLE
void setPluginPaths(const std::list<std::string> &_paths);
}
Expand Down
60 changes: 48 additions & 12 deletions src/base/BaseRenderEngine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,73 +94,109 @@ bool BaseRenderEngine::IsEnabled() const
//////////////////////////////////////////////////
unsigned int BaseRenderEngine::SceneCount() const
{
return this->Scenes()->Size();
auto scenes = this->Scenes();
if (scenes)
return scenes->Size();
return 0u;
}

//////////////////////////////////////////////////
bool BaseRenderEngine::HasScene(ConstScenePtr _scene) const
{
return this->Scenes()->Contains(_scene);
auto scenes = this->Scenes();
if (scenes)
return scenes->Contains(_scene);
return false;
}

//////////////////////////////////////////////////
bool BaseRenderEngine::HasSceneId(unsigned int _id) const
{
return this->Scenes()->ContainsId(_id);
auto scenes = this->Scenes();
if (scenes)
return scenes->ContainsId(_id);
return false;
}

//////////////////////////////////////////////////
bool BaseRenderEngine::HasSceneName(const std::string &_name) const
{
return this->Scenes()->ContainsName(_name);
auto scenes = this->Scenes();
if (scenes)
return scenes->ContainsName(_name);
return false;
}

//////////////////////////////////////////////////
ScenePtr BaseRenderEngine::SceneById(unsigned int _id) const
{
return this->Scenes()->GetById(_id);
auto scenes = this->Scenes();
if (scenes)
return scenes->GetById(_id);
return ScenePtr();
}

//////////////////////////////////////////////////
ScenePtr BaseRenderEngine::SceneByName(const std::string &_name) const
{
return this->Scenes()->GetByName(_name);
auto scenes = this->Scenes();
if (scenes)
return scenes->GetByName(_name);
return ScenePtr();
}

//////////////////////////////////////////////////
ScenePtr BaseRenderEngine::SceneByIndex(unsigned int _index) const
{
return this->Scenes()->GetByIndex(_index);
auto scenes = this->Scenes();
if (scenes)
return scenes->GetByIndex(_index);
return ScenePtr();
}

//////////////////////////////////////////////////
void BaseRenderEngine::DestroyScene(ScenePtr _scene)
{
this->Scenes()->Destroy(_scene);
auto scenes = this->Scenes();
if (!scenes)
return;
scenes->Destroy(_scene);
}

//////////////////////////////////////////////////
void BaseRenderEngine::DestroySceneById(unsigned int _id)
{
this->Scenes()->DestroyById(_id);
auto scenes = this->Scenes();
if (!scenes)
return;
scenes->DestroyById(_id);
}

//////////////////////////////////////////////////
void BaseRenderEngine::DestroySceneByName(const std::string &_name)
{
this->Scenes()->DestroyByName(_name);
auto scenes = this->Scenes();
if (!scenes)
return;
scenes->DestroyByName(_name);
}

//////////////////////////////////////////////////
void BaseRenderEngine::DestroySceneByIndex(unsigned int _index)
{
this->Scenes()->DestroyByIndex(_index);
auto scenes = this->Scenes();
if (!scenes)
return;
scenes->DestroyByIndex(_index);
}

//////////////////////////////////////////////////
void BaseRenderEngine::DestroyScenes()
{
this->Scenes()->DestroyAll();
auto scenes = this->Scenes();
if (!scenes)
return;
scenes->DestroyAll();
}

//////////////////////////////////////////////////
Expand Down
1 change: 1 addition & 0 deletions tutorials.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Ignition @IGN_DESIGNATION_CAP@ library and how to use the library effectively.

1. \subpage introduction "Introduction"
2. \subpage installation "Installation"
3. \subpage renderingplugin "Understanding the Rendering Plugin"

## License

Expand Down
39 changes: 39 additions & 0 deletions tutorials/03_rendering_plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
\page renderingplugin Understanding the Rendering Plugin

This is an introduction to different rendering engines and how they are integrated into the Ignition Physics library.

## Ignition Rendering

The \ref ignition::rendering "Ignition Rendering" library integrates external rendering engines into the Ignition Simulation eco-system.
It allows users to select from multiple supported rendering engines based on their simulation needs.
Its plugin interface loads rendering engines at runtime.
It is also possible to integrate your own selected rendering engine by writing a compatible plugin interface.

#### How to Write Your Own Rendering Plugin

A mocked example of a custom rendering engine plugin can be found [here](https://github.com/ignitionrobotics/ign-rendering/tree/ign-rendering3/examples/hello_world_plugin). In order
to make your own custom rendering engine, this example is a good starting point. There are a few key things which will need to be done in order for a custom rendering engine to function:

* A singleton Render Engine class which implements the pure virtual functions in [`ignition::rendering::BaseRenderEngine`](https://github.com/ignitionrobotics/ign-rendering/blob/main/include/ignition/rendering/base/BaseRenderEngine.hh).
* A plugin class which implements the pure virtual functions in [`ignition::rendering::RenderEnginePlugin`](https://github.com/ignitionrobotics/ign-rendering/blob/main/include/ignition/rendering/RenderEnginePlugin.hh)
* Registering the plugin, this line can be seen at the bottom of the [`HelloWorldPlugin`](https://github.com/ignitionrobotics/ign-rendering/tree/ign-rendering3/examples/hello_world_plugin/HelloWorldPlugin.cc) example

Finally, for your custom rendering engine to actually have any functionality, you will need to implement your own `Scene` class, which inherits from and implements the pure virtual functions of [`ignition::rendering::Scene`](https://github.com/ignitionrobotics/ign-rendering/blob/ign-rendering3/include/ignition/rendering/Scene.hh). The mocked example simply returns `nullptr` for its `CreateSceneImpl(...)` function, so it may be useful to look at the current `Scene` implementations for the other rendering engines within `ignition::rendering` such as [`ogre`](https://github.com/ignitionrobotics/ign-rendering/blob/ign-rendering3/ogre/src/OgreScene.cc) or [`optix`](https://github.com/ignitionrobotics/ign-rendering/blob/ign-rendering3/optix/src/OptixScene.cc).

#### Building and Running Your Rendering Plugin

Once you have your own rendering plugin written, you can build it similarly to how the example is built. It may be helpful to look at the [`CMakeLists.txt`](https://github.com/ignitionrobotics/ign-rendering/tree/ign-rendering3/examples/hello_world_plugin) from the example as it contains the boilerplate code needed to get a custom rendering engine plugin built.

After you have your plugin built, you will need to set the `IGN_GAZEBO_RENDER_ENGINE_PATH` environment variable to the path in which your built shared library resides. Note that you will need to do this for every command line instance unless you add the line to your startup shell script (`.bashrc`, `.zshrc`, etc.).

From here, you should be able to reference your rendering plugin within Ignition Gazebo by the name of the generated shared library file (without the `lib` prefix or the file extension, i.e., libHelloWorldPlugin.so -> HelloWorldPlugin).

\note You can use the `--render-engine`, `--render-engine-server`, and `--render-engine-gui` flags to specify any supported or custom rendering engine at runtime, this flag will override any other pre-existing rendering engine specifications (such as in an `.sdf` file). The command line call would look something like:

~~~
ign gazebo --render-engine HelloWorldPlugin shapes.sdf
~~~

#### Feature Comparison

Do we want to fill this out for ogre, ogre2, and optix?

0 comments on commit 8876d6a

Please sign in to comment.