504 lines
16 KiB
C++
504 lines
16 KiB
C++
/*
|
|
* Copyright (C) 2014 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 <boost/bind.hpp>
|
|
|
|
#include "gazebo/transport/transport.hh"
|
|
|
|
#include "gazebo/rendering/RenderTypes.hh"
|
|
#include "gazebo/rendering/RenderEvents.hh"
|
|
#include "gazebo/rendering/RenderingIface.hh"
|
|
#include "gazebo/rendering/DynamicLines.hh"
|
|
#include "gazebo/rendering/Visual.hh"
|
|
#include "gazebo/rendering/Scene.hh"
|
|
#include "gazebo/rendering/UserCamera.hh"
|
|
#include "gazebo/rendering/Conversions.hh"
|
|
#include "gazebo/rendering/RayQuery.hh"
|
|
|
|
#include "gazebo/gui/qt.h"
|
|
#include "gazebo/gui/MouseEventHandler.hh"
|
|
#include "gazebo/gui/GuiIface.hh"
|
|
#include "gazebo/gui/GuiEvents.hh"
|
|
|
|
#include "gazebo/gui/ModelSnapPrivate.hh"
|
|
#include "gazebo/gui/ModelSnap.hh"
|
|
|
|
using namespace gazebo;
|
|
using namespace gui;
|
|
|
|
/////////////////////////////////////////////////
|
|
ModelSnap::ModelSnap()
|
|
: dataPtr(new ModelSnapPrivate)
|
|
{
|
|
this->dataPtr->initialized = false;
|
|
this->dataPtr->selectedTriangleDirty = false;
|
|
this->dataPtr->hoverTriangleDirty = false;
|
|
this->dataPtr->snapLines = nullptr;
|
|
this->dataPtr->snapHighlight = nullptr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
ModelSnap::~ModelSnap()
|
|
{
|
|
this->Clear();
|
|
delete this->dataPtr;
|
|
this->dataPtr = NULL;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void ModelSnap::Fini()
|
|
{
|
|
this->Clear();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void ModelSnap::Clear()
|
|
{
|
|
this->dataPtr->selectedTriangleDirty = false;
|
|
this->dataPtr->hoverTriangleDirty = false;
|
|
this->dataPtr->selectedTriangle.clear();
|
|
this->dataPtr->hoverTriangle.clear();
|
|
this->dataPtr->selectedVis.reset();
|
|
this->dataPtr->hoverVis.reset();
|
|
|
|
this->dataPtr->node.reset();
|
|
this->dataPtr->userCmdPub.reset();
|
|
|
|
if (this->dataPtr->renderConnection)
|
|
{
|
|
event::Events::DisconnectRender(this->dataPtr->renderConnection);
|
|
this->dataPtr->renderConnection.reset();
|
|
}
|
|
|
|
if (this->dataPtr->snapVisual)
|
|
{
|
|
this->dataPtr->snapVisual->DeleteDynamicLine(this->dataPtr->snapLines);
|
|
this->dataPtr->snapLines = nullptr;
|
|
this->dataPtr->snapVisual->Fini();
|
|
this->dataPtr->snapVisual.reset();
|
|
}
|
|
if (this->dataPtr->highlightVisual)
|
|
{
|
|
this->dataPtr->highlightVisual->DeleteDynamicLine(
|
|
this->dataPtr->snapHighlight);
|
|
this->dataPtr->snapHighlight = nullptr;
|
|
this->dataPtr->highlightVisual->Fini();
|
|
this->dataPtr->highlightVisual.reset();
|
|
}
|
|
|
|
delete this->dataPtr->updateMutex;
|
|
this->dataPtr->updateMutex = NULL;
|
|
|
|
this->dataPtr->scene.reset();
|
|
this->dataPtr->userCamera.reset();
|
|
this->dataPtr->rayQuery.reset();
|
|
|
|
this->dataPtr->initialized = false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void ModelSnap::Init()
|
|
{
|
|
if (this->dataPtr->initialized)
|
|
return;
|
|
|
|
rendering::UserCameraPtr cam = gui::get_active_camera();
|
|
if (!cam)
|
|
return;
|
|
|
|
if (!cam->GetScene())
|
|
return;
|
|
|
|
this->dataPtr->userCamera = cam;
|
|
this->dataPtr->scene = cam->GetScene();
|
|
|
|
this->dataPtr->updateMutex = new boost::recursive_mutex();
|
|
|
|
this->dataPtr->node = transport::NodePtr(new transport::Node());
|
|
this->dataPtr->node->TryInit(common::Time::Maximum());
|
|
this->dataPtr->userCmdPub =
|
|
this->dataPtr->node->Advertise<msgs::UserCmd>("~/user_cmd");
|
|
|
|
this->dataPtr->rayQuery.reset(
|
|
new rendering::RayQuery(this->dataPtr->userCamera));
|
|
|
|
this->dataPtr->initialized = true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void ModelSnap::Reset()
|
|
{
|
|
boost::recursive_mutex::scoped_lock lock(*this->dataPtr->updateMutex);
|
|
this->dataPtr->selectedVis.reset();
|
|
this->dataPtr->selectedTriangle.clear();
|
|
|
|
this->dataPtr->hoverVis.reset();
|
|
this->dataPtr->hoverTriangle.clear();
|
|
|
|
this->dataPtr->hoverTriangleDirty = false;
|
|
this->dataPtr->selectedTriangleDirty = false;
|
|
|
|
if (this->dataPtr->snapVisual)
|
|
{
|
|
if (this->dataPtr->snapVisual->GetVisible())
|
|
this->dataPtr->snapVisual->SetVisible(false);
|
|
}
|
|
|
|
if (this->dataPtr->highlightVisual)
|
|
{
|
|
if (this->dataPtr->highlightVisual->GetVisible())
|
|
this->dataPtr->highlightVisual->SetVisible(false);
|
|
}
|
|
|
|
if (this->dataPtr->renderConnection)
|
|
{
|
|
event::Events::DisconnectRender(this->dataPtr->renderConnection);
|
|
this->dataPtr->renderConnection.reset();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void ModelSnap::OnMousePressEvent(const common::MouseEvent &_event)
|
|
{
|
|
this->dataPtr->mouseEvent = _event;
|
|
this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void ModelSnap::OnMouseMoveEvent(const common::MouseEvent &_event)
|
|
{
|
|
this->dataPtr->mouseEvent = _event;
|
|
|
|
rendering::VisualPtr vis = this->dataPtr->userCamera->GetVisual(
|
|
this->dataPtr->mouseEvent.Pos());
|
|
if (vis && !vis->IsPlane())
|
|
{
|
|
// get the triangle being hovered so that it can be highlighted
|
|
math::Vector3 intersect;
|
|
std::vector<math::Vector3> hoverTriangle;
|
|
this->dataPtr->rayQuery->SelectMeshTriangle(_event.Pos().X(),
|
|
_event.Pos().Y(), vis->GetRootVisual(), intersect, hoverTriangle);
|
|
|
|
if (!hoverTriangle.empty())
|
|
{
|
|
boost::recursive_mutex::scoped_lock lock(*this->dataPtr->updateMutex);
|
|
this->dataPtr->hoverVis = vis;
|
|
this->dataPtr->hoverTriangle = hoverTriangle;
|
|
this->dataPtr->hoverTriangleDirty = true;
|
|
|
|
if (!this->dataPtr->renderConnection)
|
|
{
|
|
this->dataPtr->renderConnection = event::Events::ConnectRender(
|
|
boost::bind(&ModelSnap::Update, this));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
boost::recursive_mutex::scoped_lock lock(*this->dataPtr->updateMutex);
|
|
this->dataPtr->hoverVis.reset();
|
|
this->dataPtr->hoverTriangle.clear();
|
|
this->dataPtr->hoverTriangleDirty = true;
|
|
}
|
|
|
|
this->dataPtr->mouseEvent = _event;
|
|
this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void ModelSnap::OnMouseReleaseEvent(const common::MouseEvent &_event)
|
|
{
|
|
this->dataPtr->mouseEvent = _event;
|
|
|
|
rendering::VisualPtr vis = this->dataPtr->userCamera->GetVisual(
|
|
this->dataPtr->mouseEvent.Pos());
|
|
|
|
if (vis && !vis->IsPlane() &&
|
|
this->dataPtr->mouseEvent.Button() == common::MouseEvent::LEFT)
|
|
{
|
|
// Parent model or parent link
|
|
rendering::VisualPtr currentParent = vis->GetRootVisual();
|
|
rendering::VisualPtr previousParent;
|
|
rendering::VisualPtr topLevelVis = vis->GetNthAncestor(2);
|
|
|
|
if (gui::get_entity_id(currentParent->GetName()))
|
|
{
|
|
if (this->dataPtr->selectedVis)
|
|
previousParent = this->dataPtr->selectedVis->GetRootVisual();
|
|
}
|
|
else
|
|
{
|
|
currentParent = topLevelVis;
|
|
if (this->dataPtr->selectedVis)
|
|
{
|
|
previousParent = this->dataPtr->selectedVis->GetNthAncestor(2);
|
|
}
|
|
}
|
|
|
|
// Select first triangle on any mesh
|
|
// Update triangle if the new triangle is on the same model/link
|
|
if (!this->dataPtr->selectedVis || (currentParent == previousParent))
|
|
{
|
|
math::Vector3 intersect;
|
|
this->dataPtr->rayQuery->SelectMeshTriangle(_event.Pos().X(),
|
|
_event.Pos().Y(), currentParent, intersect,
|
|
this->dataPtr->selectedTriangle);
|
|
|
|
if (!this->dataPtr->selectedTriangle.empty())
|
|
{
|
|
this->dataPtr->selectedVis = vis;
|
|
this->dataPtr->selectedTriangleDirty = true;
|
|
}
|
|
if (!this->dataPtr->renderConnection)
|
|
{
|
|
this->dataPtr->renderConnection = event::Events::ConnectRender(
|
|
boost::bind(&ModelSnap::Update, this));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// select triangle on the target
|
|
math::Vector3 intersect;
|
|
std::vector<math::Vector3> vertices;
|
|
this->dataPtr->rayQuery->SelectMeshTriangle(_event.Pos().X(),
|
|
_event.Pos().Y(), currentParent, intersect, vertices);
|
|
|
|
if (!vertices.empty())
|
|
{
|
|
this->Snap(this->dataPtr->selectedTriangle, vertices, previousParent);
|
|
|
|
this->Reset();
|
|
gui::Events::manipMode("select");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void ModelSnap::Snap(const std::vector<math::Vector3> &_triangleSrc,
|
|
const std::vector<math::Vector3> &_triangleDest,
|
|
rendering::VisualPtr _visualSrc)
|
|
{
|
|
math::Vector3 translation;
|
|
math::Quaternion rotation;
|
|
|
|
this->GetSnapTransform(_triangleSrc, _triangleDest,
|
|
_visualSrc->GetWorldPose(), translation, rotation);
|
|
|
|
_visualSrc->SetWorldPose(
|
|
math::Pose(_visualSrc->GetWorldPose().pos + translation,
|
|
rotation * _visualSrc->GetWorldPose().rot));
|
|
|
|
this->PublishVisualPose(_visualSrc);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void ModelSnap::GetSnapTransform(const std::vector<math::Vector3> &_triangleSrc,
|
|
const std::vector<math::Vector3> &_triangleDest,
|
|
const math::Pose &_poseSrc, math::Vector3 &_trans,
|
|
math::Quaternion &_rot)
|
|
{
|
|
// snap the centroid of one triangle to another
|
|
math::Vector3 centroidSrc = (_triangleSrc[0] + _triangleSrc[1] +
|
|
_triangleSrc[2]) / 3.0;
|
|
|
|
math::Vector3 centroidDest =
|
|
(_triangleDest[0] + _triangleDest[1] + _triangleDest[2]) / 3.0;
|
|
|
|
math::Vector3 normalSrc = math::Vector3::GetNormal(
|
|
_triangleSrc[0], _triangleSrc[1], _triangleSrc[2]);
|
|
|
|
math::Vector3 normalDest = math::Vector3::GetNormal(
|
|
_triangleDest[0], _triangleDest[1], _triangleDest[2]);
|
|
|
|
math::Vector3 u = normalDest.Normalize() * -1;
|
|
math::Vector3 v = normalSrc.Normalize();
|
|
double cosTheta = v.Dot(u);
|
|
double angle = acos(cosTheta);
|
|
// check the parallel case
|
|
if (math::equal(angle, M_PI))
|
|
_rot.SetFromAxis(u.GetPerpendicular(), angle);
|
|
else
|
|
_rot.SetFromAxis((v.Cross(u)).Normalize(), angle);
|
|
|
|
// Get translation needed for alignment
|
|
// taking into account the rotated position of the mesh
|
|
_trans = centroidDest - (_rot * (centroidSrc - _poseSrc.pos) + _poseSrc.pos);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void ModelSnap::PublishVisualPose(rendering::VisualPtr _vis)
|
|
{
|
|
if (!_vis)
|
|
return;
|
|
|
|
// Only publish for models
|
|
if (_vis->GetType() == gazebo::rendering::Visual::VT_MODEL)
|
|
{
|
|
// Register user command on server
|
|
msgs::UserCmd userCmdMsg;
|
|
userCmdMsg.set_description("Snap [" + _vis->GetName() + "]");
|
|
userCmdMsg.set_type(msgs::UserCmd::MOVING);
|
|
|
|
msgs::Model msg;
|
|
|
|
auto id = gui::get_entity_id(_vis->GetName());
|
|
if (id)
|
|
msg.set_id(id);
|
|
|
|
msg.set_name(_vis->GetName());
|
|
msgs::Set(msg.mutable_pose(), _vis->GetWorldPose().Ign());
|
|
|
|
auto modelMsg = userCmdMsg.add_model();
|
|
modelMsg->CopyFrom(msg);
|
|
|
|
this->dataPtr->userCmdPub->Publish(userCmdMsg);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void ModelSnap::Update()
|
|
{
|
|
boost::recursive_mutex::scoped_lock lock(*this->dataPtr->updateMutex);
|
|
if (this->dataPtr->hoverTriangleDirty)
|
|
{
|
|
if (!this->dataPtr->hoverTriangle.empty())
|
|
{
|
|
// convert triangle to local coordinates relative to parent visual
|
|
std::vector<math::Vector3> hoverTriangle;
|
|
for (unsigned int i = 0; i < this->dataPtr->hoverTriangle.size(); ++i)
|
|
{
|
|
hoverTriangle.push_back(
|
|
this->dataPtr->hoverVis->GetWorldPose().rot.GetInverse() *
|
|
(this->dataPtr->hoverTriangle[i] -
|
|
this->dataPtr->hoverVis->GetWorldPose().pos));
|
|
}
|
|
|
|
if (!this->dataPtr->highlightVisual)
|
|
{
|
|
// create the highlight
|
|
std::string highlightVisName = "_SNAP_HIGHLIGHT_";
|
|
this->dataPtr->highlightVisual.reset(new rendering::Visual(
|
|
highlightVisName, this->dataPtr->hoverVis, false));
|
|
this->dataPtr->highlightVisual->Load();
|
|
this->dataPtr->snapHighlight =
|
|
this->dataPtr->highlightVisual->CreateDynamicLine(
|
|
rendering::RENDERING_TRIANGLE_FAN);
|
|
this->dataPtr->snapHighlight->setMaterial("Gazebo/RedTransparent");
|
|
this->dataPtr->snapHighlight->AddPoint(hoverTriangle[0].Ign());
|
|
this->dataPtr->snapHighlight->AddPoint(hoverTriangle[1].Ign());
|
|
this->dataPtr->snapHighlight->AddPoint(hoverTriangle[2].Ign());
|
|
this->dataPtr->snapHighlight->AddPoint(hoverTriangle[0].Ign());
|
|
this->dataPtr->highlightVisual->SetVisible(true);
|
|
this->dataPtr->highlightVisual->GetSceneNode()->setInheritScale(false);
|
|
this->dataPtr->highlightVisual->SetVisibilityFlags(
|
|
GZ_VISIBILITY_GUI & ~GZ_VISIBILITY_SELECTABLE);
|
|
}
|
|
else
|
|
{
|
|
// set new highlight position
|
|
if (!this->dataPtr->highlightVisual->GetVisible())
|
|
this->dataPtr->highlightVisual->SetVisible(true);
|
|
if (this->dataPtr->hoverVis !=
|
|
this->dataPtr->highlightVisual->GetParent())
|
|
{
|
|
if (this->dataPtr->highlightVisual->GetParent())
|
|
{
|
|
this->dataPtr->highlightVisual->GetParent()->DetachVisual(
|
|
this->dataPtr->highlightVisual);
|
|
}
|
|
this->dataPtr->hoverVis->AttachVisual(this->dataPtr->highlightVisual);
|
|
}
|
|
this->dataPtr->snapHighlight->SetPoint(0, hoverTriangle[0].Ign());
|
|
this->dataPtr->snapHighlight->SetPoint(1, hoverTriangle[1].Ign());
|
|
this->dataPtr->snapHighlight->SetPoint(2, hoverTriangle[2].Ign());
|
|
this->dataPtr->snapHighlight->SetPoint(3, hoverTriangle[0].Ign());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// turn of visualization if no mesh triangles are hovered
|
|
this->dataPtr->highlightVisual->SetVisible(false);
|
|
}
|
|
this->dataPtr->hoverTriangleDirty = false;
|
|
}
|
|
|
|
if (this->dataPtr->selectedTriangleDirty &&
|
|
!this->dataPtr->selectedTriangle.empty())
|
|
{
|
|
// convert triangle to local coordinates relative to parent visual
|
|
std::vector<math::Vector3> triangle;
|
|
for (unsigned int i = 0; i < this->dataPtr->selectedTriangle.size(); ++i)
|
|
{
|
|
triangle.push_back(
|
|
this->dataPtr->selectedVis->GetWorldPose().rot.GetInverse() *
|
|
(this->dataPtr->selectedTriangle[i] -
|
|
this->dataPtr->selectedVis->GetWorldPose().pos));
|
|
}
|
|
|
|
if (!this->dataPtr->snapVisual)
|
|
{
|
|
// draw a border around selected triangle
|
|
rendering::UserCameraPtr camera = gui::get_active_camera();
|
|
|
|
std::string snapVisName = "_SNAP_";
|
|
this->dataPtr->snapVisual.reset(new rendering::Visual(
|
|
snapVisName, this->dataPtr->selectedVis, false));
|
|
this->dataPtr->snapVisual->Load();
|
|
this->dataPtr->snapLines =
|
|
this->dataPtr->snapVisual->CreateDynamicLine(
|
|
rendering::RENDERING_LINE_STRIP);
|
|
this->dataPtr->snapLines->setMaterial("Gazebo/RedGlow");
|
|
this->dataPtr->snapLines->AddPoint(triangle[0].Ign());
|
|
this->dataPtr->snapLines->AddPoint(triangle[1].Ign());
|
|
this->dataPtr->snapLines->AddPoint(triangle[2].Ign());
|
|
this->dataPtr->snapLines->AddPoint(triangle[0].Ign());
|
|
this->dataPtr->snapVisual->SetVisible(true);
|
|
this->dataPtr->snapVisual->GetSceneNode()->setInheritScale(false);
|
|
this->dataPtr->snapVisual->SetVisibilityFlags(
|
|
GZ_VISIBILITY_GUI & ~GZ_VISIBILITY_SELECTABLE);
|
|
}
|
|
else
|
|
{
|
|
// update border if the selected triangle changes
|
|
if (!this->dataPtr->snapVisual->GetVisible())
|
|
this->dataPtr->snapVisual->SetVisible(true);
|
|
if (this->dataPtr->selectedVis != this->dataPtr->snapVisual->GetParent())
|
|
{
|
|
if (this->dataPtr->snapVisual->GetParent())
|
|
{
|
|
this->dataPtr->snapVisual->GetParent()->DetachVisual(
|
|
this->dataPtr->snapVisual);
|
|
}
|
|
this->dataPtr->selectedVis->AttachVisual(this->dataPtr->snapVisual);
|
|
}
|
|
this->dataPtr->snapLines->SetPoint(0, triangle[0].Ign());
|
|
this->dataPtr->snapLines->SetPoint(1, triangle[1].Ign());
|
|
this->dataPtr->snapLines->SetPoint(2, triangle[2].Ign());
|
|
this->dataPtr->snapLines->SetPoint(3, triangle[0].Ign());
|
|
}
|
|
this->dataPtr->selectedTriangleDirty = false;
|
|
}
|
|
}
|