Skip to content

How to change material of model in system plugin? #842

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

Closed
gezp opened this issue May 31, 2021 · 7 comments
Closed

How to change material of model in system plugin? #842

gezp opened this issue May 31, 2021 · 7 comments
Labels
help wanted We accept pull requests! rendering Involves Ignition Rendering

Comments

@gezp
Copy link

gezp commented May 31, 2021

I want to change color of model dynamically by using plugin. Then I modify component Material in component Visual by using SetComponentData() in PreUpdate() , but there is no effect.

SDF::Material material ;
ignition::math::Color color(1,0,0,1);
m.SetAmbient(color);
m.SetDiffuse(color);
m.SetSpecular(color);
_ecm.SetComponentData<components::Material>(visualEntity,m);

So, Does anyone know how to change material of model in system plugin? Thank you!

@chapulina
Copy link
Contributor

That functionality hasn't been implemented yet. The implementation would be similar to how we update lights, see #482 as a reference.

@chapulina chapulina added help wanted We accept pull requests! rendering Involves Ignition Rendering labels Jun 15, 2021
@mhl787156
Copy link

mhl787156 commented Dec 31, 2024

Hi, Sorry to reopen - @chapulina do you know if there has been any progress on this since 2021? I'm on gazebo fortress (ignition/gazebo6 i think).

Kind of assumed this was working and made a plugin for students to work with in 2 weeks time then saw this issue when it wasnt...

To be more specific, you can manually change the material properties in the gazebo GUI, and the changes are reflected in the live simulation. However it seems that changing the material properties in a plugin as described above does not make any differences.

@azeey
Copy link
Contributor

azeey commented Jan 6, 2025

This was implemented as part of #1123. You can change material attributes using a VisualCmd component. For reference, see

bool VisualCommand::Execute()
{
auto visualMsg = dynamic_cast<const msgs::Visual *>(this->msg);
auto materialColorMsg = dynamic_cast<const msgs::MaterialColor *>(this->msg);
if (visualMsg != nullptr)
{
Entity visualEntity = kNullEntity;
if (visualMsg->id() != kNullEntity)
{
visualEntity = visualMsg->id();
}
else if (!visualMsg->name().empty() && !visualMsg->parent_name().empty())
{
Entity parentEntity =
this->iface->ecm->EntityByComponents(
components::Name(visualMsg->parent_name()));
auto entities =
this->iface->ecm->ChildrenByComponents(parentEntity,
components::Name(visualMsg->name()));
// When size > 1, we don't know which entity to modify
if (entities.size() == 1)
{
visualEntity = entities[0];
}
}
if (visualEntity == kNullEntity)
{
gzerr << "Failed to find visual entity" << std::endl;
return false;
}
auto visualCmdComp =
this->iface->ecm->Component<components::VisualCmd>(visualEntity);
if (!visualCmdComp)
{
this->iface->ecm->CreateComponent(
visualEntity, components::VisualCmd(*visualMsg));
}
else
{
auto state = visualCmdComp->SetData(*visualMsg, this->visualEql) ?
ComponentState::OneTimeChange : ComponentState::NoChange;
this->iface->ecm->SetChanged(
visualEntity, components::VisualCmd::typeId, state);
}

@azeey azeey closed this as completed Jan 6, 2025
@mhl787156
Copy link

mhl787156 commented Mar 5, 2025

Hi @azeey I finally got back to working on this - apologies a couple of questions as I'm still not used to the component model

As far as I understand, VisualCommand is updated via an ign topic exposed by the UserCommands component. How exactly do I enable the UserCommands component so that the VisualCommands topic is created?

  1. Should it be enabled in the plugin I am writing (for changing colours of a specific object) (in which case what should I attach it to), or
  2. Should it be enabled at the world.sdf level? (In which case what is the xml to enable it?)

Observing my empty.sdf we have the following which does enable the UserCommands plugin, but when running ign topics -l I do not see a /visual_config topic as would be created per what that pull request calls.

<?xml version="1.0"?>

<sdf version="1.6">
    <world name="empty_delivery">
        <physics name="4ms" type="ignored">
            <max_step_size>0.004</max_step_size>
            <real_time_factor>1.0</real_time_factor>
        </physics>
        <plugin
            filename="ignition-gazebo-physics-system"
            name="ignition::gazebo::systems::Physics">
        </plugin>
        <plugin
            filename="ignition-gazebo-scene-broadcaster-system"
            name="ignition::gazebo::systems::SceneBroadcaster">
        </plugin>
        <plugin
            filename="ignition-gazebo-user-commands-system"
            name="ignition::gazebo::systems::UserCommands">
        </plugin>
        <plugin
            filename="ignition-gazebo-sensors-system"
            name="ignition::gazebo::systems::Sensors">
            <render_engine>ogre2</render_engine>
        </plugin>
        <!-- Uncomment this if you want to use SuctionGripper model -->
        <!-- <plugin
      filename="ignition-gazebo-contact-system"
      name="ignition::gazebo::systems::Contact">
    </plugin> -->

        <light type="directional"
            name="sun">
            <cast_shadows>true</cast_shadows>
            <pose>0 0 10 0 0 0</pose>
            <diffuse>0.8 0.8 0.8 1</diffuse>
            <specular>0.2 0.2 0.2 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> 
        
        {% if use_origin -%}

        <spherical_coordinates>
            <surface_model>EARTH_WGS84</surface_model>
            <latitude_deg>{{ origin.latitude }}</latitude_deg>
            <longitude_deg>{{ origin.longitude }}</longitude_deg>
            <elevation>{{ origin.altitude }}</elevation>
            <heading_deg>0.0</heading_deg><!-- Temporary fix for issue
            https://bitbucket.org/osrf/gazebo/issues/2022/default-sphericalcoordinates-frame-should -->
        </spherical_coordinates> 

        {% endif -%}

        <include>
            <uri>model://ground_plane</uri>
        </include>
    </world>
</sdf>

Sorry to reopen and thanks in advance!

@mhl787156
Copy link

mhl787156 commented Mar 5, 2025

So just to add on, I have created a publisher to visual_config within my plugin and can see the echos when running

> ign topic -i -t /world/empty_visual/visual_config -e
id: 38
transparency: 0.5
material {
  ambient {
    r: 0.8
    g: 0.8
    b: 0.8
    a: 1
  }
  diffuse {
    r: 0.3
    g: 0.4
    b: 0.5
    a: 1
  }
  specular {
    r: 0.8
    g: 0.8
    b: 0.8
    a: 1
  }
  emissive {
    r: 0.3
    g: 0.4
    b: 0.5
    a: 1
  }
}
type: VISUAL

id: 39
transparency: 0.5
material {
  ambient {
    r: 0.8
    g: 0.8
    b: 0.8
    a: 1
  }
  diffuse {
    r: 0.8
    g: 0.8
    b: 0.8
    a: 1
  }
  specular {
    r: 0.8
    g: 0.8
    b: 0.8
    a: 1
  }
  emissive {
    r: 0.8
    g: 0.8
    b: 0.8
    a: 1
  }
}

I have double checked that the entity ids match what the Visuals I'm trying to change in the gazebo Tree.

A weirdness is that there doesn't seem to be any subscribers on this topic which is concerning. I would have expected there to be at least one subscriber from UserCommands if I have understood the code corectly:

Image

For more information, here is the entity I am creating:

// Create a visual
sdf::Visual sdfVisual;
sdfVisual.SetName("led" + std::to_string(i));
sdfVisual.SetPoseRelativeTo("base_frame");

// Calculate x, y position
float x = this->ring_radius * std::cos(angle_delta * i);
float y = this->ring_radius * std::sin(angle_delta * i);
sdfVisual.SetRawPose(gz::math::Pose3d(x, y, this->ring_z_offset, 0.0, 0.0, 0.0));

// Set cylinder geometry
sdf::Cylinder cyl;
cyl.SetRadius(0.015);
cyl.SetLength(0.015);
sdf::Geometry sdfGeom;
sdfGeom.SetType(sdf::GeometryType::CYLINDER);
sdfGeom.SetCylinderShape(cyl);
sdfVisual.SetGeom(sdfGeom);

float r = this->led_colours[i * 3 + 0];
float b = this->led_colours[i * 3 + 1];
float g = this->led_colours[i * 3 + 2];

// Set material
sdf::Material sdfMaterial;
// Set the diffuse color to match the LED's body color
sdfMaterial.SetDiffuse(gz::math::Color(r,g,b, 1.0));
// Set the emissive color to simulate light emission
sdfMaterial.SetEmissive(gz::math::Color(r,g,b, 1.0));  // Bright red
// Set the specular color to simulate glossy reflection
sdfMaterial.SetSpecular(gz::math::Color(0.8, 0.8, 0.8, 1.0));  // Near-white for strong highlights
// Optionally add transparency for a translucent effect
sdfVisual.SetTransparency(0.1);  // Slightly translucent
sdfVisual.SetMaterial(sdfMaterial);

// Add the visual to the link
sdfLink.AddVisual(sdfVisual);

// Assuming `entityCreator` is already being used to create the entity
auto visualEntity = entityCreator.CreateEntities(&sdfVisual);
if (visualEntity == gz::sim::kNullEntity) {
	ignerr << "Failed to create visual entity" << std::endl;
	continue;
} 
// Ensure it's marked as a Visual in Gazebo
_ecm.CreateComponent(visualEntity, gz::sim::components::Visual());
_ecm.CreateComponent(visualEntity, gz::sim::components::Material());
this->led_id_entity_map[i] = visualEntity;
entityCreator.SetParent(visualEntity, linkEntity);

And here is how I'm calling the topic:

gz::msgs::Visual msg;
gz::sim::Entity visual_entity = this->led_id_entity_map[i];
msg.set_id(visual_entity);
// msg.set_name("drone0::leds_link::led0");
// Modify the material color (RGBA format)
gz::msgs::Material *mat = msg.mutable_material();
gz::msgs::Set(mat->mutable_diffuse(), gz::math::Color(r, g, b, 1.0));  // Red color
gz::msgs::Set(mat->mutable_emissive(), gz::math::Color(r, g, b, 1.0));  // Red color
gz::msgs::Set(mat->mutable_specular(), gz::math::Color(0.8, 0.8, 0.8, 1.0));  // Red color
gz::msgs::Set(mat->mutable_ambient(), gz::math::Color(0.8, 0.8, 0.8, 1.0));  // Red color
msg.set_transparency(0.5);
msg.set_type(gz::msgs::Visual::VISUAL);
this->visual_config_pub.Publish(msg);
ignerr << "Published Diffusive and Emmisive to " << visual_entity << std::endl;

For greater context here is gazebo visuals. I am trying to create a programmable ring of lights for this crazyflie drone model to match the real light rings. Unfortunately despite sending the above messages to gazebo, the "lights" remain grey. They still change if I manually change the materials within the gazebo gui (though I cannot see any messages from the gui changes in the visuals_config topic)

Image

p.s. I have verified that UserCommand is loading as we get the printouts X service on [...] matching whats in the source code

Image

@mhl787156
Copy link

mhl787156 commented Mar 6, 2025

Hi all, I finally figured out the sticking point which is that visual_config is a service! Hence it needs a service call!

gz::msgs::Visual msg;
gz::sim::Entity visual_entity = this->led_id_entity_map[i];
msg.set_id(visual_entity);

// Modify the material color (RGBA format)
gz::msgs::Material *mat = msg.mutable_material();
gz::msgs::Set(mat->mutable_diffuse(), gz::math::Color(r, g, b, 1.0));  // Red color
gz::msgs::Set(mat->mutable_emissive(), gz::math::Color(r, g, b, 1.0));  // Red color

std::function<void(const ignition::msgs::Boolean &, const bool)> cb =
	[](const ignition::msgs::Boolean &/*_rep*/, const bool _result)
{
	if (!_result)
	ignerr << "Error setting material color configuration"
			<< " on visual" << std::endl;
};

this->node.Request(this->visual_config_service_name, msg, cb);

ignerr << "Sent Service Call Diffusive and Emmisive to " << visual_entity << std::endl;
this->visual_config_pub.Publish(msg);

Where I set the visual_config_service_name with the following:

// Set World here as ECM is not fully initialised
this->world = gz::sim::World(gz::sim::worldEntity(_ecm));

// Create Subscriber to the visual_config with world
std::string world_name = *this->world.Name(_ecm);
this->visual_config_service_name = "/world/"+world_name+"/visual_config";
ignerr << "Materials Service Call Sent To " << this->visual_config_service_name << std::endl;

With that fixed, the whole stack works amazing. Hope this thread helps others! :)

Image

@iche033
Copy link
Contributor

iche033 commented Mar 14, 2025

ah didn't get a chance to respond earlier, but glad you're able to figure it out. Thanks for posting back the code snippet!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted We accept pull requests! rendering Involves Ignition Rendering
Projects
None yet
Development

No branches or pull requests

5 participants