You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Conception/drake-master/geometry/meshcat_visualizer.cc

268 lines
9.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "drake/geometry/meshcat_visualizer.h"
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <fmt/format.h>
#include "drake/common/extract_double.h"
#include "drake/geometry/proximity/volume_to_surface_mesh.h"
#include "drake/geometry/utilities.h"
namespace drake {
namespace geometry {
namespace {
// Boilerplate for std::visit.
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
} // namespace
template <typename T>
MeshcatVisualizer<T>::MeshcatVisualizer(std::shared_ptr<Meshcat> meshcat,
MeshcatVisualizerParams params)
: systems::LeafSystem<T>(systems::SystemTypeTag<MeshcatVisualizer>{}),
meshcat_(std::move(meshcat)),
params_(std::move(params)),
alpha_slider_name_(std::string(params_.prefix + " α")) {
DRAKE_DEMAND(meshcat_ != nullptr);
DRAKE_DEMAND(params_.publish_period >= 0.0);
if (params_.role == Role::kUnassigned) {
throw std::runtime_error(
"MeshcatVisualizer cannot be used for geometries with the "
"Role::kUnassigned value. Please choose kProximity, kPerception, or "
"kIllustration");
}
this->DeclarePeriodicPublishEvent(params_.publish_period, 0.0,
&MeshcatVisualizer<T>::UpdateMeshcat);
this->DeclareForcedPublishEvent(&MeshcatVisualizer<T>::UpdateMeshcat);
if (params_.delete_on_initialization_event) {
this->DeclareInitializationPublishEvent(
&MeshcatVisualizer<T>::OnInitialization);
}
query_object_input_port_ =
this->DeclareAbstractInputPort("query_object", Value<QueryObject<T>>())
.get_index();
if (params_.enable_alpha_slider) {
meshcat_->AddSlider(
alpha_slider_name_, 0.02, 1.0, 0.02, alpha_value_);
}
}
template <typename T>
template <typename U>
MeshcatVisualizer<T>::MeshcatVisualizer(const MeshcatVisualizer<U>& other)
: MeshcatVisualizer(other.meshcat_, other.params_) {}
template <typename T>
void MeshcatVisualizer<T>::Delete() const {
meshcat_->Delete(params_.prefix);
version_ = GeometryVersion();
}
template <typename T>
MeshcatAnimation* MeshcatVisualizer<T>::StartRecording(
bool set_transforms_while_recording) {
meshcat_->StartRecording(1.0 / params_.publish_period,
set_transforms_while_recording);
return &meshcat_->get_mutable_recording();
}
template <typename T>
void MeshcatVisualizer<T>::StopRecording() {
meshcat_->StopRecording();
}
template <typename T>
void MeshcatVisualizer<T>::PublishRecording() const {
meshcat_->PublishRecording();
}
template <typename T>
void MeshcatVisualizer<T>::DeleteRecording() {
meshcat_->DeleteRecording();
}
template <typename T>
MeshcatAnimation* MeshcatVisualizer<T>::get_mutable_recording() {
return &meshcat_->get_mutable_recording();
}
template <typename T>
MeshcatVisualizer<T>& MeshcatVisualizer<T>::AddToBuilder(
systems::DiagramBuilder<T>* builder, const SceneGraph<T>& scene_graph,
std::shared_ptr<Meshcat> meshcat, MeshcatVisualizerParams params) {
return AddToBuilder(builder, scene_graph.get_query_output_port(),
std::move(meshcat), std::move(params));
}
template <typename T>
MeshcatVisualizer<T>& MeshcatVisualizer<T>::AddToBuilder(
systems::DiagramBuilder<T>* builder,
const systems::OutputPort<T>& query_object_port,
std::shared_ptr<Meshcat> meshcat, MeshcatVisualizerParams params) {
const std::string aspirational_name =
fmt::format("meshcat_visualizer({})", params.prefix);
auto& visualizer = *builder->template AddSystem<MeshcatVisualizer<T>>(
std::move(meshcat), std::move(params));
if (!builder->HasSubsystemNamed(aspirational_name)) {
visualizer.set_name(aspirational_name);
}
builder->Connect(query_object_port, visualizer.query_object_input_port());
return visualizer;
}
template <typename T>
systems::EventStatus MeshcatVisualizer<T>::UpdateMeshcat(
const systems::Context<T>& context) const {
const auto& query_object =
query_object_input_port().template Eval<QueryObject<T>>(context);
const GeometryVersion& current_version =
query_object.inspector().geometry_version();
if (!version_.IsSameAs(current_version, params_.role)) {
SetObjects(query_object.inspector());
version_ = current_version;
}
SetTransforms(context, query_object);
if (params_.enable_alpha_slider) {
double new_alpha_value = meshcat_->GetSliderValue(alpha_slider_name_);
if (new_alpha_value != alpha_value_) {
alpha_value_ = new_alpha_value;
SetColorAlphas();
}
}
std::optional<double> rate = realtime_rate_calculator_.UpdateAndRecalculate(
ExtractDoubleOrThrow(context.get_time()));
if (rate) {
meshcat_->SetRealtimeRate(rate.value());
}
return systems::EventStatus::Succeeded();
}
template <typename T>
void MeshcatVisualizer<T>::SetObjects(
const SceneGraphInspector<T>& inspector) const {
colors_.clear();
// Frames registered previously that are not set again here should be deleted.
std::map <FrameId, std::string> frames_to_delete{};
dynamic_frames_.swap(frames_to_delete);
// Geometries registered previously that are not set again here should be
// deleted.
std::map <GeometryId, std::string> geometries_to_delete{};
geometries_.swap(geometries_to_delete);
// TODO(SeanCurtis-TRI): Mimic the full tree structure in SceneGraph.
// SceneGraph supports arbitrary hierarchies of frames just like Meshcat.
// This code is arbitrarily flattening it because the current SceneGraph API
// is insufficient to support walking the tree.
for (FrameId frame_id : inspector.GetAllFrameIds()) {
std::string frame_path =
frame_id == inspector.world_frame_id()
? params_.prefix
: fmt::format("{}/{}", params_.prefix, inspector.GetName(frame_id));
// MultibodyPlant declares frames with SceneGraph using "::". We replace
// those with `/` here to expose the full tree to Meshcat.
size_t pos = 0;
while ((pos = frame_path.find("::", pos)) != std::string::npos) {
frame_path.replace(pos++, 2, "/");
}
if (frame_id != inspector.world_frame_id() &&
inspector.NumGeometriesForFrameWithRole(frame_id, params_.role) > 0) {
dynamic_frames_[frame_id] = frame_path;
frames_to_delete.erase(frame_id); // Don't delete this one.
}
for (GeometryId geom_id : inspector.GetGeometries(frame_id, params_.role)) {
// Note: We use the frame_path/id instead of instance.GetName(geom_id),
// which is a garbled mess of :: and _ and a memory address by default
// when coming from MultibodyPlant.
// TODO(russt): Use the geometry names if/when they are cleaned up.
const std::string path =
fmt::format("{}/{}", frame_path, geom_id.get_value());
const Rgba rgba = inspector.GetProperties(geom_id, params_.role)
->GetPropertyOrDefault("phong", "diffuse", params_.default_color);
bool used_hydroelastic = false;
if constexpr (std::is_same_v<T, double>) {
if (params_.show_hydroelastic) {
auto maybe_mesh = inspector.maybe_get_hydroelastic_mesh(geom_id);
std::visit(
overloaded{[](std::monostate) -> void {},
[&](const TriangleSurfaceMesh<double>* mesh) -> void {
DRAKE_DEMAND(mesh != nullptr);
meshcat_->SetObject(path, *mesh, rgba);
used_hydroelastic = true;
},
[&](const VolumeMesh<double>* mesh) -> void {
DRAKE_DEMAND(mesh != nullptr);
meshcat_->SetObject(
path, ConvertVolumeToSurfaceMesh(*mesh), rgba);
used_hydroelastic = true;
}},
maybe_mesh);
}
}
if (!used_hydroelastic) {
meshcat_->SetObject(path, inspector.GetShape(geom_id), rgba);
}
meshcat_->SetTransform(path, inspector.GetPoseInFrame(geom_id));
geometries_[geom_id] = path;
colors_[geom_id] = rgba;
geometries_to_delete.erase(geom_id); // Don't delete this one.
}
}
for (const auto& [geom_id, path] : geometries_to_delete) {
unused(geom_id);
meshcat_->Delete(path);
}
for (const auto& [frame_id, path] : frames_to_delete) {
unused(frame_id);
meshcat_->Delete(path);
}
}
template <typename T>
void MeshcatVisualizer<T>::SetTransforms(
const systems::Context<T>& context,
const QueryObject<T>& query_object) const {
for (const auto& [frame_id, path] : dynamic_frames_) {
const math::RigidTransformd X_WF =
internal::convert_to_double(query_object.GetPoseInWorld(frame_id));
meshcat_->SetTransform(path, X_WF,
ExtractDoubleOrThrow(context.get_time()));
}
}
template <typename T>
void MeshcatVisualizer<T>::SetColorAlphas() const {
for (const auto& [geom_id, path] : geometries_) {
Rgba color = colors_[geom_id];
color.set(color.r(), color.g(), color.b(), alpha_value_ * color.a());
meshcat_->SetProperty(path, "color",
{color.r(), color.g(), color.b(), alpha_value_ * color.a()});
}
}
template <typename T>
systems::EventStatus MeshcatVisualizer<T>::OnInitialization(
const systems::Context<T>&) const {
Delete();
meshcat_->SetProperty(params_.prefix, "visible", params_.visible_by_default);
return systems::EventStatus::Succeeded();
}
} // namespace geometry
} // namespace drake
DRAKE_DEFINE_CLASS_TEMPLATE_INSTANTIATIONS_ON_DEFAULT_NONSYMBOLIC_SCALARS(
class ::drake::geometry::MeshcatVisualizer)