Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Particle emitter based on SDF #730

Merged
merged 10 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/worlds/particle_emitter.sdf
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<?xml version="1.0" ?>

<!-- NOTE: This world uses a particle emitter that is implemented as
a plugin. We recommend using particle emitters based on the SDF
specification. See the test/worlds/particle_emitter2.sdf example.
-->

<!--
Launch this example with:

Expand Down
119 changes: 119 additions & 0 deletions examples/worlds/particle_emitter2.sdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?xml version="1.0" ?>

<!--
Launch this example with:

ign gazebo -r particle_emitter.sdf

Try modifying some parameters of the emitter:

To disable the particle emitter:

ign topic -t /model/fog_generator/link/fog_link/particle_emitter/emitter/cmd -m ignition.msgs.ParticleEmitter -p 'emitting: {data: false}'

Enable back the particle emitter:

ign topic -t /model/fog_generator/link/fog_link/particle_emitter/emitter/cmd -m ignition.msgs.ParticleEmitter -p 'emitting: {data: true}'

Then, change the particle rate:

ign topic -t /model/fog_generator/link/fog_link/particle_emitter/emitter/cmd -m ignition.msgs.ParticleEmitter -p 'rate: {data: 100}'
-->

<sdf version="1.6">
<world name="particle_emitters">

<physics name="1ms" type="ode">
<max_step_size>0.001</max_step_size>
<real_time_factor>1.0</real_time_factor>
</physics>
<plugin
filename="ignition-gazebo-physics-system"
name="ignition::gazebo::systems::Physics">
<!-- Use TPE in order to use nested models -->
<engine><filename>libignition-physics-tpe-plugin.so</filename></engine>
</plugin>
<plugin
filename="ignition-gazebo-user-commands-system"
name="ignition::gazebo::systems::UserCommands">
</plugin>
<plugin
filename="ignition-gazebo-scene-broadcaster-system"
name="ignition::gazebo::systems::SceneBroadcaster">
</plugin>
<plugin
filename="ignition-gazebo-particle-emitter2-system"
name="ignition::gazebo::systems::ParticleEmitter2">
</plugin>

<light type="directional" name="sun">
<cast_shadows>true</cast_shadows>
<pose>0 0 10 0 0 0</pose>
<diffuse>1 1 1 1</diffuse>
<specular>0.5 0.5 0.5 1</specular>
<attenuation>
<range>1000</range>
<constant>0.9</constant>
<linear>0.01</linear>
<quadratic>0.001</quadratic>
</attenuation>
<direction>-0.5 0.1 -0.9</direction>
</light>

<model name="ground_plane">
<static>true</static>
<link name="link">
<collision name="collision">
<geometry>
<plane>
<normal>0 0 1</normal>
</plane>
</geometry>
</collision>
<visual name="visual">
<geometry>
<plane>
<normal>0 0 1</normal>
<size>100 100</size>
</plane>
</geometry>
<material>
<ambient>0.8 0.8 0.8 1</ambient>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.8 0.8 0.8 1</specular>
</material>
</visual>
</link>
</model>

<include>
<uri>https://fuel.ignitionrobotics.org/1.0/openrobotics/models/fog generator</uri>
</include>

<!-- Nested model example, where the particles are bright red
billboards -->
<model name="nested">
<static>true</static>
<pose>20 20 0 0 0 0</pose>
<model name="fog_generator">
<link name="fog_link_nested">
<particle_emitter name="emitter" type="box">
<emitting>true</emitting>
<size>10 10 0</size>
<particle_size>1 1 1</particle_size>
<lifetime>25</lifetime>
<min_velocity>0.1</min_velocity>
<max_velocity>0.2</max_velocity>
<scale_rate>0.5</scale_rate>
<rate>5</rate>
<material>
<diffuse>1.0 0.0 0.0</diffuse>
<specular>1.0 0.0 0.0</specular>
</material>
</particle_emitter>
</link>
</model>
</model>

</world>
</sdf>
38 changes: 38 additions & 0 deletions include/ignition/gazebo/Conversions.hh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <ignition/msgs/inertial.pb.h>
#include <ignition/msgs/light.pb.h>
#include <ignition/msgs/material.pb.h>
#include <ignition/msgs/particle_emitter.pb.h>
#include <ignition/msgs/physics.pb.h>
#include <ignition/msgs/scene.pb.h>
#include <ignition/msgs/sensor.pb.h>
Expand All @@ -47,6 +48,7 @@
#include <sdf/Light.hh>
#include <sdf/Material.hh>
#include <sdf/Noise.hh>
#include <sdf/ParticleEmitter.hh>
#include <sdf/Physics.hh>
#include <sdf/Scene.hh>
#include <sdf/Sensor.hh>
Expand Down Expand Up @@ -624,6 +626,42 @@ namespace ignition
/// \return Axis aligned box object.
template<>
math::AxisAlignedBox convert(const msgs::AxisAlignedBox &_in);

/// \brief Generic conversion from a particle emitter SDF object to another
/// type.
/// \param[in] _in Particle emitter SDF object.
/// \return Conversion result.
/// \tparam Out Output type.
template<class Out>
Out convert(const sdf::ParticleEmitter &/*_in*/)
{
Out::ConversionNotImplemented;
}

/// \brief Specialized conversion from a particle emitter SDF object to
/// a particle emitter message object.
/// \param[in] _in Particle emitter SDF object.
/// \return Particle emitter message.
template<>
msgs::ParticleEmitter convert(const sdf::ParticleEmitter &_in);

/// \brief Generic conversion from a particle emitter SDF object to another
/// type.
/// \param[in] _in Particle emitter SDF object.
/// \return Conversion result.
/// \tparam Out Output type.
template<class Out>
Out convert(const msgs::ParticleEmitter &/*_in*/)
{
Out::ConversionNotImplemented;
}

/// \brief Specialized conversion from a particle emitter SDF object to
/// a particle emitter message object.
/// \param[in] _in Particle emitter SDF object.
/// \return Particle emitter message.
template<>
sdf::ParticleEmitter convert(const msgs::ParticleEmitter &_in);
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions include/ignition/gazebo/SdfEntityCreator.hh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <sdf/Light.hh>
#include <sdf/Link.hh>
#include <sdf/Model.hh>
#include <sdf/ParticleEmitter.hh>
#include <sdf/Physics.hh>
#include <sdf/Sensor.hh>
#include <sdf/Visual.hh>
Expand Down Expand Up @@ -140,6 +141,13 @@ namespace ignition
/// \sa CreateEntities(const sdf::Model *)
public: Entity CreateEntities(const sdf::Sensor *_sensor);

/// \brief Create all entities that exist in the
/// sdf::ParticleEmitter object.
/// \param[in] _emitter SDF ParticleEmitter object.
/// \return ParticleEmitter entity.
/// \sa CreateEntities(const sdf::Link *)
public: Entity CreateEntities(const sdf::ParticleEmitter *_emitter);

/// \brief Request an entity deletion. This will insert the request
/// into a queue. The queue is processed toward the end of a simulation
/// update step.
Expand Down
20 changes: 20 additions & 0 deletions include/ignition/gazebo/Util.hh
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,25 @@ namespace ignition
std::string IGNITION_GAZEBO_VISIBLE validTopic(
const std::vector<std::string> &_topics);

/// \brief Helper function that returns a valid Ignition Transport topic
/// consisting of the scoped name for the provided entity.
///
/// For example, if the provided entity has a scoped name of
/// `my_model::my_link::my_sensor` then the resulting topic name will
/// be `/model/my_model/link/my_link/sensor/my_sensor`. If _excludeWorld
/// is false, then the topic name will be prefixed by `/world/WORLD_NAME/`,
/// where `WORLD_NAME` is the name of the world.
///
/// \param[in] _entity The entity to generate the topic name for.
/// \param[in] _ecm The entity component manager.
/// \param[in] _excludeWorld True to exclude the world name from the topic.
/// \return An Ignition Transport topic name based on the scoped name of
/// the provided entity, or empty string if a topic name could not be
/// generated.
std::string topicFromScopedName(const Entity &_entity,
const EntityComponentManager &_ecm,
bool _excludeWorld = true);

/// \brief Environment variable holding resource paths.
const std::string kResourcePathEnv{"IGN_GAZEBO_RESOURCE_PATH"};

Expand All @@ -168,6 +187,7 @@ namespace ignition
/// \brief Environment variable holding paths to custom rendering engine
/// plugins.
const std::string kRenderPluginPathEnv{"IGN_GAZEBO_RENDER_ENGINE_PATH"};

}
}
}
Expand Down
127 changes: 127 additions & 0 deletions src/Conversions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1326,3 +1326,130 @@ math::AxisAlignedBox ignition::gazebo::convert(const msgs::AxisAlignedBox &_in)
out.Max() = msgs::Convert(_in.max_corner());
return out;
}

//////////////////////////////////////////////////
template<>
IGNITION_GAZEBO_VISIBLE
msgs::ParticleEmitter ignition::gazebo::convert(const sdf::ParticleEmitter &_in)
{
msgs::ParticleEmitter out;
out.set_name(_in.Name());
switch(_in.Type())
{
default:
case sdf::ParticleEmitterType::POINT:
out.set_type(msgs::ParticleEmitter::POINT);
break;
case sdf::ParticleEmitterType::BOX:
out.set_type(msgs::ParticleEmitter::BOX);
break;
case sdf::ParticleEmitterType::CYLINDER:
out.set_type(msgs::ParticleEmitter::CYLINDER);
break;
case sdf::ParticleEmitterType::ELLIPSOID:
out.set_type(msgs::ParticleEmitter::ELLIPSOID);
break;
}

msgs::Set(out.mutable_pose(), _in.RawPose());
msgs::Set(out.mutable_size(), _in.Size());
msgs::Set(out.mutable_particle_size(), _in.ParticleSize());
out.mutable_rate()->set_data(_in.Rate());
out.mutable_duration()->set_data(_in.Duration());
out.mutable_emitting()->set_data(_in.Emitting());
out.mutable_lifetime()->set_data(_in.Lifetime());
if (_in.Material())
{
out.mutable_material()->CopyFrom(convert<msgs::Material>(*_in.Material()));
}
out.mutable_min_velocity()->set_data(_in.MinVelocity());
out.mutable_max_velocity()->set_data(_in.MaxVelocity());
msgs::Set(out.mutable_color_start(), _in.ColorStart());
msgs::Set(out.mutable_color_end(), _in.ColorEnd());
out.mutable_scale_rate()->set_data(_in.ScaleRate());
out.mutable_color_range_image()->set_data(_in.ColorRangeImage());

if (!_in.ColorRangeImage().empty())
nkoenig marked this conversation as resolved.
Show resolved Hide resolved
{
std::string path = asFullPath(_in.ColorRangeImage(), _in.FilePath());

common::SystemPaths systemPaths;
systemPaths.SetFilePathEnv(kResourcePathEnv);
std::string absolutePath = systemPaths.FindFile(path);

if (!absolutePath.empty())
out.mutable_color_range_image()->set_data(absolutePath);
}

/// \todo(nkoenig) Modify the particle_emitter.proto file to
/// have a topic field.
if (!_in.Topic().empty())
{
auto header = out.mutable_header()->add_data();
header->set_key("topic");
header->add_value(_in.Topic());
}

return out;
}

//////////////////////////////////////////////////
template<>
IGNITION_GAZEBO_VISIBLE
sdf::ParticleEmitter ignition::gazebo::convert(const msgs::ParticleEmitter &_in)
{
sdf::ParticleEmitter out;
out.SetName(_in.name());
switch(_in.type())
{
default:
case msgs::ParticleEmitter::POINT:
out.SetType(sdf::ParticleEmitterType::POINT);
break;
case msgs::ParticleEmitter::BOX:
out.SetType(sdf::ParticleEmitterType::BOX);
break;
case msgs::ParticleEmitter::CYLINDER:
out.SetType(sdf::ParticleEmitterType::CYLINDER);
break;
case msgs::ParticleEmitter::ELLIPSOID:
out.SetType(sdf::ParticleEmitterType::ELLIPSOID);
break;
}
out.SetRawPose(msgs::Convert(_in.pose()));
out.SetSize(msgs::Convert(_in.size()));
out.SetParticleSize(msgs::Convert(_in.particle_size()));
out.SetMinVelocity(msgs::Convert(_in.min_velocity()));
out.SetMaxVelocity(msgs::Convert(_in.max_velocity()));
out.SetColorStart(msgs::Convert(_in.color_start()));
out.SetColorEnd(msgs::Convert(_in.color_end()));

if (_in.has_material())
{
out.SetMaterial(convert<sdf::Material>(_in.material()));
}

if (_in.has_rate())
out.SetRate(_in.rate().data());
if (_in.has_duration())
out.SetDuration(_in.duration().data());
if (_in.has_emitting())
out.SetEmitting(_in.emitting().data());
if (_in.has_lifetime())
out.SetLifetime(_in.lifetime().data());
if (_in.has_scale_rate())
out.SetScaleRate(_in.scale_rate().data());
if (_in.has_color_range_image())
out.SetColorRangeImage(_in.color_range_image().data());

for (int i = 0; i < _in.header().data_size(); ++i)
{
auto data = _in.header().data(i);
if (data.key() == "topic" && data.value_size() > 0)
{
out.SetTopic(data.value(0));
}
}

return out;
}
Loading