pxmlw6n2f/Gazebo_Distributed_TCP/gazebo/gui/GLWidget.cc

1397 lines
41 KiB
C++

/*
* Copyright (C) 2012 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 <functional>
#include <boost/algorithm/string.hpp>
#include <math.h>
#include "gazebo/common/Assert.hh"
#include "gazebo/common/Exception.hh"
#include "gazebo/math/gzmath.hh"
#include "gazebo/rendering/Conversions.hh"
#include "gazebo/rendering/FPSViewController.hh"
#include "gazebo/rendering/Heightmap.hh"
#include "gazebo/rendering/OrbitViewController.hh"
#include "gazebo/rendering/RenderEngine.hh"
#include "gazebo/rendering/RenderEvents.hh"
#include "gazebo/rendering/RenderingIface.hh"
#include "gazebo/rendering/Scene.hh"
#include "gazebo/rendering/UserCamera.hh"
#include "gazebo/rendering/Visual.hh"
#include "gazebo/rendering/WindowManager.hh"
#include "gazebo/gui/Actions.hh"
#include "gazebo/gui/GLWidget.hh"
#include "gazebo/gui/GLWidgetPrivate.hh"
#include "gazebo/gui/GuiEvents.hh"
#include "gazebo/gui/GuiIface.hh"
#include "gazebo/gui/KeyEventHandler.hh"
#include "gazebo/gui/ModelAlign.hh"
#include "gazebo/gui/ModelManipulator.hh"
#include "gazebo/gui/ModelRightMenu.hh"
#include "gazebo/gui/ModelSnap.hh"
#include "gazebo/gui/MouseEventHandler.hh"
#include "gazebo/transport/transport.hh"
using namespace gazebo;
using namespace gui;
extern bool g_fullscreen;
extern ModelRightMenu *g_modelRightMenu;
/////////////////////////////////////////////////
GLWidget::GLWidget(QWidget *_parent)
: QWidget(_parent),
dataPtr(new GLWidgetPrivate())
{
this->setObjectName("GLWidget");
this->dataPtr->state = "select";
this->dataPtr->copyEntityName = "";
this->dataPtr->modelEditorEnabled = false;
this->dataPtr->updateTimer = new QTimer(this);
connect(this->dataPtr->updateTimer, SIGNAL(timeout()),
this, SLOT(update()));
this->setFocusPolicy(Qt::StrongFocus);
this->dataPtr->windowId = -1;
this->setAttribute(Qt::WA_OpaquePaintEvent, true);
this->setAttribute(Qt::WA_PaintOnScreen, true);
this->dataPtr->renderFrame = new QFrame;
this->dataPtr->renderFrame->setFrameShape(QFrame::NoFrame);
this->dataPtr->renderFrame->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
this->dataPtr->renderFrame->setContentsMargins(0, 0, 0, 0);
this->dataPtr->renderFrame->show();
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(this->dataPtr->renderFrame);
mainLayout->setContentsMargins(0, 0, 0, 0);
this->setLayout(mainLayout);
this->dataPtr->connections.push_back(
rendering::Events::ConnectRemoveScene(
std::bind(&GLWidget::OnRemoveScene, this, std::placeholders::_1)));
this->dataPtr->connections.push_back(
gui::Events::ConnectMoveMode(
std::bind(&GLWidget::OnMoveMode, this, std::placeholders::_1)));
this->dataPtr->connections.push_back(
gui::Events::ConnectCreateEntity(
std::bind(&GLWidget::OnCreateEntity, this, std::placeholders::_1,
std::placeholders::_2)));
this->dataPtr->connections.push_back(
gui::Events::ConnectFPS(
std::bind(&GLWidget::OnFPS, this)));
this->dataPtr->connections.push_back(
gui::Events::ConnectOrbit(
std::bind(&GLWidget::OnOrbit, this)));
this->dataPtr->connections.push_back(
gui::Events::ConnectManipMode(
std::bind(&GLWidget::OnManipMode, this, std::placeholders::_1)));
this->dataPtr->connections.push_back(
event::Events::ConnectSetSelectedEntity(
std::bind(&GLWidget::OnSetSelectedEntity, this, std::placeholders::_1,
std::placeholders::_2)));
this->dataPtr->connections.push_back(
gui::Events::ConnectAlignMode(
std::bind(&GLWidget::OnAlignMode, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5)));
this->dataPtr->renderFrame->setMouseTracking(true);
this->setMouseTracking(true);
this->dataPtr->entityMaker = NULL;
this->dataPtr->node = transport::NodePtr(new transport::Node());
this->dataPtr->node->TryInit(common::Time::Maximum());
// Publishes information about user selections.
this->dataPtr->selectionPub =
this->dataPtr->node->Advertise<msgs::Selection>("~/selection");
this->dataPtr->requestSub = this->dataPtr->node->Subscribe("~/request",
&GLWidget::OnRequest, this);
this->installEventFilter(this);
this->dataPtr->keyModifiers = 0;
MouseEventHandler::Instance()->AddPressFilter("glwidget",
std::bind(&GLWidget::OnMousePress, this, std::placeholders::_1));
MouseEventHandler::Instance()->AddReleaseFilter("glwidget",
std::bind(&GLWidget::OnMouseRelease, this, std::placeholders::_1));
MouseEventHandler::Instance()->AddMoveFilter("glwidget",
std::bind(&GLWidget::OnMouseMove, this, std::placeholders::_1));
MouseEventHandler::Instance()->AddDoubleClickFilter("glwidget",
std::bind(&GLWidget::OnMouseDoubleClick, this, std::placeholders::_1));
connect(g_copyAct, SIGNAL(triggered()), this, SLOT(OnCopy()));
connect(g_pasteAct, SIGNAL(triggered()), this, SLOT(OnPaste()));
connect(g_editModelAct, SIGNAL(toggled(bool)), this,
SLOT(OnModelEditor(bool)));
// Connect the ortho action
connect(g_cameraOrthoAct, SIGNAL(triggered()), this,
SLOT(OnOrtho()));
// Connect the perspective action
connect(g_cameraPerspectiveAct, SIGNAL(triggered()), this,
SLOT(OnPerspective()));
// Create the scene. This must be done in the constructor so that
// we can then create a user camera.
this->dataPtr->scene = rendering::create_scene(gui::get_world(), true);
if (!this->dataPtr->scene)
{
gzerr << "GLWidget could not create a scene. This will likely result "
<< "in a blank screen.\n";
}
else
{
// This will ultimately create a user camera. We need to create a user
// camera in the constructor so that communications (such as via the
// ~/gui topic) can work properly (see MainWindow::OnGUI).
//
// All of this means that we must have a GL Context by this point. So,
// we have to create a dummy 1x1 window in RenderEngine::Load.
this->OnCreateScene(this->dataPtr->scene->Name());
}
}
/////////////////////////////////////////////////
GLWidget::~GLWidget()
{
MouseEventHandler::Instance()->RemovePressFilter("glwidget");
MouseEventHandler::Instance()->RemoveReleaseFilter("glwidget");
MouseEventHandler::Instance()->RemoveMoveFilter("glwidget");
MouseEventHandler::Instance()->RemoveDoubleClickFilter("glwidget");
this->dataPtr->connections.clear();
this->dataPtr->node.reset();
this->dataPtr->selectionPub.reset();
ModelManipulator::Instance()->Clear();
ModelSnap::Instance()->Clear();
ModelAlign::Instance()->Clear();
if (this->dataPtr->userCamera)
this->dataPtr->userCamera->Fini();
this->dataPtr->userCamera.reset();
this->dataPtr->scene.reset();
}
/////////////////////////////////////////////////
bool GLWidget::eventFilter(QObject * /*_obj*/, QEvent *_event)
{
if (_event->type() == QEvent::Enter)
{
this->setFocus(Qt::OtherFocusReason);
return true;
}
return false;
}
/////////////////////////////////////////////////
void GLWidget::showEvent(QShowEvent *_event)
{
// These two functions are most applicable for Linux.
QApplication::flush();
QApplication::syncX();
if (this->dataPtr->windowId <=0)
{
// Get the window handle in a form that OGRE can use.
std::string winHandle = this->OgreHandle();
// Create the OGRE render window
this->dataPtr->windowId =
rendering::RenderEngine::Instance()->GetWindowManager()->
CreateWindow(winHandle, this->width(), this->height());
// Attach the user camera to the window
rendering::RenderEngine::Instance()->GetWindowManager()->SetCamera(
this->dataPtr->windowId, this->dataPtr->userCamera);
}
// Let QT continue processing the show event.
QWidget::showEvent(_event);
// Grab focus.
this->setFocus();
}
/////////////////////////////////////////////////
void GLWidget::enterEvent(QEvent * /*_event*/)
{
}
/////////////////////////////////////////////////
void GLWidget::moveEvent(QMoveEvent *_e)
{
QWidget::moveEvent(_e);
if (_e->isAccepted() && this->dataPtr->windowId >= 0)
{
rendering::RenderEngine::Instance()->GetWindowManager()->Moved(
this->dataPtr->windowId);
}
}
/////////////////////////////////////////////////
void GLWidget::paintEvent(QPaintEvent *_e)
{
rendering::UserCameraPtr cam = gui::get_active_camera();
if (cam && cam->Initialized())
{
event::Events::preRender();
// Tell all the cameras to render
event::Events::render();
event::Events::postRender();
}
else
{
event::Events::preRender();
}
_e->accept();
}
/////////////////////////////////////////////////
void GLWidget::resizeEvent(QResizeEvent *_e)
{
if (this->dataPtr->windowId >= 0)
{
rendering::RenderEngine::Instance()->GetWindowManager()->Resize(
this->dataPtr->windowId, _e->size().width(), _e->size().height());
if (this->dataPtr->userCamera)
{
this->dataPtr->userCamera->Resize(
_e->size().width(), _e->size().height());
}
}
}
/////////////////////////////////////////////////
void GLWidget::keyPressEvent(QKeyEvent *_event)
{
if (!this->dataPtr->scene)
return;
if (_event->isAutoRepeat() && !KeyEventHandler::Instance()->AutoRepeat())
return;
this->dataPtr->keyText = _event->text().toStdString();
this->dataPtr->keyModifiers = _event->modifiers();
this->dataPtr->keyEvent.key = _event->key();
this->dataPtr->keyEvent.text = this->dataPtr->keyText;
// Toggle full screen
if (_event->key() == Qt::Key_F11)
{
g_fullscreen = !g_fullscreen;
gui::Events::fullScreen(g_fullscreen);
}
// Trigger a model delete if the Delete key was pressed, and a model
// is currently selected.
if (_event->key() == Qt::Key_Delete &&
this->dataPtr->selectionLevel == SelectionLevels::MODEL)
{
std::lock_guard<std::mutex> lock(this->dataPtr->selectedVisMutex);
while (!this->dataPtr->selectedVisuals.empty())
{
std::string name = this->dataPtr->selectedVisuals.back()->GetName();
int id = this->dataPtr->selectedVisuals.back()->GetId();
this->dataPtr->selectedVisuals.pop_back();
// Publish message about visual deselection
msgs::Selection msg;
msg.set_id(id);
msg.set_name(name);
msg.set_selected(false);
this->dataPtr->selectionPub->Publish(msg);
g_deleteAct->Signal(name);
}
}
if (_event->key() == Qt::Key_Escape)
{
event::Events::setSelectedEntity("", "normal");
if (this->dataPtr->state == "make_entity")
{
if (this->dataPtr->entityMaker)
this->dataPtr->entityMaker->Stop();
}
}
/// Switch between RTS modes
if (this->dataPtr->keyModifiers == Qt::NoModifier &&
this->dataPtr->state != "make_entity")
{
if (_event->key() == Qt::Key_R && g_rotateAct->isEnabled())
g_rotateAct->trigger();
else if (_event->key() == Qt::Key_T && g_translateAct->isEnabled())
g_translateAct->trigger();
else if (_event->key() == Qt::Key_S && g_scaleAct->isEnabled())
g_scaleAct->trigger();
else if (_event->key() == Qt::Key_N && g_snapAct->isEnabled())
g_snapAct->trigger();
else if (_event->key() == Qt::Key_Escape && g_arrowAct->isEnabled())
g_arrowAct->trigger();
}
this->dataPtr->keyEvent.control =
this->dataPtr->keyModifiers & Qt::ControlModifier ? true : false;
this->dataPtr->keyEvent.shift =
this->dataPtr->keyModifiers & Qt::ShiftModifier ? true : false;
this->dataPtr->keyEvent.alt =
this->dataPtr->keyModifiers & Qt::AltModifier ? true : false;
this->dataPtr->mouseEvent.SetControl(this->dataPtr->keyEvent.control);
this->dataPtr->mouseEvent.SetShift(this->dataPtr->keyEvent.shift);
this->dataPtr->mouseEvent.SetAlt(this->dataPtr->keyEvent.alt);
if (this->dataPtr->mouseEvent.Control())
{
if (_event->key() == Qt::Key_C && !this->dataPtr->selectedVisuals.empty()
&& !this->dataPtr->modelEditorEnabled && g_copyAct->isEnabled())
{
g_copyAct->trigger();
}
else if (_event->key() == Qt::Key_V &&
!this->dataPtr->copyEntityName.empty() &&
!this->dataPtr->modelEditorEnabled && g_pasteAct->isEnabled())
{
g_pasteAct->trigger();
}
}
// Process Key Events
if (!KeyEventHandler::Instance()->HandlePress(this->dataPtr->keyEvent))
{
// model editor exit pop-up message is modal so can block event propagation.
// So using hotkeys to exit will leave the control variable in a bad state.
// Manually override and reset the control value.
if (this->dataPtr->modelEditorEnabled &&
this->dataPtr->mouseEvent.Control())
{
this->dataPtr->mouseEvent.SetControl(false);
}
ModelManipulator::Instance()->OnKeyPressEvent(this->dataPtr->keyEvent);
this->dataPtr->userCamera->HandleKeyPressEvent(this->dataPtr->keyText);
}
}
/////////////////////////////////////////////////
void GLWidget::keyReleaseEvent(QKeyEvent *_event)
{
if (!this->dataPtr->scene)
return;
// this shouldn't happen, but in case it does...
if (_event->isAutoRepeat() && !KeyEventHandler::Instance()->AutoRepeat())
return;
this->dataPtr->keyModifiers = _event->modifiers();
this->dataPtr->keyEvent.control =
this->dataPtr->keyModifiers & Qt::ControlModifier ? true : false;
this->dataPtr->keyEvent.shift =
this->dataPtr->keyModifiers & Qt::ShiftModifier ? true : false;
this->dataPtr->keyEvent.alt =
this->dataPtr->keyModifiers & Qt::AltModifier ? true : false;
this->dataPtr->mouseEvent.SetControl(this->dataPtr->keyEvent.control);
this->dataPtr->mouseEvent.SetShift(this->dataPtr->keyEvent.shift);
this->dataPtr->mouseEvent.SetAlt(this->dataPtr->keyEvent.alt);
ModelManipulator::Instance()->OnKeyReleaseEvent(this->dataPtr->keyEvent);
this->dataPtr->keyText = "";
this->dataPtr->userCamera->HandleKeyReleaseEvent(
_event->text().toStdString());
// Process Key Events
KeyEventHandler::Instance()->HandleRelease(this->dataPtr->keyEvent);
}
/////////////////////////////////////////////////
void GLWidget::mouseDoubleClickEvent(QMouseEvent *_event)
{
if (!this->dataPtr->scene)
return;
this->dataPtr->mouseEvent.SetPressPos(_event->pos().x(), _event->pos().y());
this->dataPtr->mouseEvent.SetPrevPos(this->dataPtr->mouseEvent.PressPos());
/// Set the button which cause the press event
this->SetMouseEventButton(_event->button());
this->dataPtr->mouseEvent.SetButtons(common::MouseEvent::NO_BUTTON);
this->dataPtr->mouseEvent.SetType(common::MouseEvent::PRESS);
this->SetMouseEventButtons(_event->buttons());
this->dataPtr->mouseEvent.SetDragging(false);
// Process Mouse Events
MouseEventHandler::Instance()->HandleDoubleClick(this->dataPtr->mouseEvent);
}
/////////////////////////////////////////////////
void GLWidget::mousePressEvent(QMouseEvent *_event)
{
if (!this->dataPtr->scene)
return;
this->dataPtr->mouseEvent.SetPressPos(_event->pos().x(), _event->pos().y());
this->dataPtr->mouseEvent.SetPrevPos(this->dataPtr->mouseEvent.PressPos());
/// Set the button which cause the press event
this->SetMouseEventButton(_event->button());
this->dataPtr->mouseEvent.SetButtons(common::MouseEvent::NO_BUTTON);
this->dataPtr->mouseEvent.SetType(common::MouseEvent::PRESS);
this->SetMouseEventButtons(_event->buttons());
this->dataPtr->mouseEvent.SetDragging(false);
// Process Mouse Events
MouseEventHandler::Instance()->HandlePress(this->dataPtr->mouseEvent);
}
/////////////////////////////////////////////////
bool GLWidget::OnMousePress(const common::MouseEvent & /*_event*/)
{
if (this->dataPtr->state == "make_entity")
this->OnMousePressMakeEntity();
else if (this->dataPtr->state == "select")
this->OnMousePressNormal();
else if (this->dataPtr->state == "translate" ||
this->dataPtr->state == "rotate" ||
this->dataPtr->state == "scale")
{
ModelManipulator::Instance()->OnMousePressEvent(this->dataPtr->mouseEvent);
}
else if (this->dataPtr->state == "snap")
ModelSnap::Instance()->OnMousePressEvent(this->dataPtr->mouseEvent);
return true;
}
/////////////////////////////////////////////////
bool GLWidget::OnMouseRelease(const common::MouseEvent & /*_event*/)
{
if (this->dataPtr->state == "make_entity")
this->OnMouseReleaseMakeEntity();
else if (this->dataPtr->state == "select")
this->OnMouseReleaseNormal();
else if (this->dataPtr->state == "translate" ||
this->dataPtr->state == "rotate" ||
this->dataPtr->state == "scale")
{
ModelManipulator::Instance()->OnMouseReleaseEvent(
this->dataPtr->mouseEvent);
}
else if (this->dataPtr->state == "snap")
ModelSnap::Instance()->OnMouseReleaseEvent(this->dataPtr->mouseEvent);
return true;
}
/////////////////////////////////////////////////
bool GLWidget::OnMouseMove(const common::MouseEvent & /*_event*/)
{
// Update the view depending on the current GUI state
if (this->dataPtr->state == "make_entity")
this->OnMouseMoveMakeEntity();
else if (this->dataPtr->state == "select")
this->OnMouseMoveNormal();
else if (this->dataPtr->state == "translate" ||
this->dataPtr->state == "rotate" ||
this->dataPtr->state == "scale")
{
ModelManipulator::Instance()->OnMouseMoveEvent(this->dataPtr->mouseEvent);
}
else if (this->dataPtr->state == "snap")
ModelSnap::Instance()->OnMouseMoveEvent(this->dataPtr->mouseEvent);
return true;
}
/////////////////////////////////////////////////
bool GLWidget::OnMouseDoubleClick(const common::MouseEvent & /*_event*/)
{
rendering::VisualPtr vis =
this->dataPtr->userCamera->GetVisual(this->dataPtr->mouseEvent.Pos());
if (vis && gui::get_entity_id(vis->GetRootVisual()->GetName()))
{
if (vis->IsPlane())
{
ignition::math::Pose3d pose;
ignition::math::Pose3d camPose;
camPose = this->dataPtr->userCamera->WorldPose();
if (this->dataPtr->scene->FirstContact(this->dataPtr->userCamera,
this->dataPtr->mouseEvent.Pos(), pose.Pos()))
{
this->dataPtr->userCamera->SetFocalPoint(pose.Pos());
ignition::math::Vector3d dir = pose.Pos() - camPose.Pos();
pose.Pos() = camPose.Pos() + (dir * 0.8);
pose.Rot() = this->dataPtr->userCamera->WorldRotation();
this->dataPtr->userCamera->MoveToPosition(pose, 0.5);
}
}
else
{
this->dataPtr->userCamera->MoveToVisual(vis);
}
}
else
return false;
return true;
}
/////////////////////////////////////////////////
void GLWidget::OnMousePressNormal()
{
if (!this->dataPtr->userCamera)
return;
rendering::VisualPtr vis = this->dataPtr->userCamera->GetVisual(
this->dataPtr->mouseEvent.Pos());
this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent);
}
/////////////////////////////////////////////////
void GLWidget::OnMousePressMakeEntity()
{
if (!this->dataPtr->userCamera)
return;
// Allow camera orbiting while making an entity
this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent);
}
/////////////////////////////////////////////////
void GLWidget::wheelEvent(QWheelEvent *_event)
{
if (!this->dataPtr->scene)
return;
if (_event->delta() > 0)
{
this->dataPtr->mouseEvent.SetScroll(
this->dataPtr->mouseEvent.Scroll().X(), -1);
}
else
{
this->dataPtr->mouseEvent.SetScroll(
this->dataPtr->mouseEvent.Scroll().X(), 1);
}
this->dataPtr->mouseEvent.SetType(common::MouseEvent::SCROLL);
this->SetMouseEventButtons(_event->buttons());
this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent);
}
/////////////////////////////////////////////////
void GLWidget::mouseMoveEvent(QMouseEvent *_event)
{
if (!this->dataPtr->scene)
return;
this->setFocus(Qt::MouseFocusReason);
this->dataPtr->mouseEvent.SetPos(_event->pos().x(), _event->pos().y());
this->dataPtr->mouseEvent.SetType(common::MouseEvent::MOVE);
this->SetMouseEventButtons(_event->buttons());
if (_event->buttons())
this->dataPtr->mouseEvent.SetDragging(true);
else
this->dataPtr->mouseEvent.SetDragging(false);
// Process Mouse Events
MouseEventHandler::Instance()->HandleMove(this->dataPtr->mouseEvent);
this->dataPtr->mouseEvent.SetPrevPos(this->dataPtr->mouseEvent.Pos());
}
/////////////////////////////////////////////////
void GLWidget::OnMouseMoveMakeEntity()
{
if (!this->dataPtr->userCamera)
return;
if (this->dataPtr->entityMaker)
{
// Allow camera orbiting while inserting a new model
if (this->dataPtr->mouseEvent.Dragging())
this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent);
else
this->dataPtr->entityMaker->OnMouseMove(this->dataPtr->mouseEvent);
}
}
/////////////////////////////////////////////////
void GLWidget::OnMouseMoveNormal()
{
if (!this->dataPtr->userCamera)
return;
rendering::VisualPtr vis = this->dataPtr->userCamera->GetVisual(
this->dataPtr->mouseEvent.Pos());
if (vis && !vis->IsPlane())
QApplication::setOverrideCursor(Qt::PointingHandCursor);
else
QApplication::setOverrideCursor(Qt::ArrowCursor);
this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent);
}
/////////////////////////////////////////////////
void GLWidget::mouseReleaseEvent(QMouseEvent *_event)
{
if (!this->dataPtr->scene)
return;
this->dataPtr->mouseEvent.SetPos(_event->pos().x(), _event->pos().y());
this->dataPtr->mouseEvent.SetPrevPos(this->dataPtr->mouseEvent.Pos());
this->SetMouseEventButton(_event->button());
this->dataPtr->mouseEvent.SetButtons(common::MouseEvent::NO_BUTTON);
this->dataPtr->mouseEvent.SetType(common::MouseEvent::RELEASE);
this->SetMouseEventButtons(_event->buttons());
// Process Mouse Events
MouseEventHandler::Instance()->HandleRelease(this->dataPtr->mouseEvent);
emit clicked();
}
//////////////////////////////////////////////////
void GLWidget::OnMouseReleaseMakeEntity()
{
if (this->dataPtr->entityMaker)
this->dataPtr->entityMaker->OnMouseRelease(this->dataPtr->mouseEvent);
}
//////////////////////////////////////////////////
void GLWidget::OnMouseReleaseNormal()
{
if (!this->dataPtr->userCamera)
return;
if (!this->dataPtr->mouseEvent.Dragging())
{
rendering::VisualPtr vis =
this->dataPtr->userCamera->GetVisual(this->dataPtr->mouseEvent.Pos());
if (vis)
{
rendering::VisualPtr selectVis;
rendering::VisualPtr linkVis = vis->GetParent();
if (!linkVis)
{
gzerr << "Link visual not found, this should not happen." << std::endl;
return;
}
rendering::VisualPtr modelVis = vis->GetRootVisual();
if (!modelVis)
{
gzerr << "Model visual not found, this should not happen." << std::endl;
return;
}
// Flags to check if we should select a link or a model
bool rightButton = (this->dataPtr->mouseEvent.Button() ==
common::MouseEvent::RIGHT);
bool modelHighlighted = modelVis->GetHighlighted();
int linkCount = 0;
bool linkHighlighted = false;
for (unsigned int i = 0; i < modelVis->GetChildCount(); ++i)
{
// Find out if there's only one link in the model
uint32_t flags = modelVis->GetChild(i)->GetVisibilityFlags();
if ((flags != GZ_VISIBILITY_ALL) && (flags & GZ_VISIBILITY_GUI))
{
continue;
}
linkCount++;
// A link from the same model is currently selected
if (modelVis->GetChild(i)->GetHighlighted())
{
linkHighlighted = true;
}
}
// Select link
if (linkCount > 1 && !this->dataPtr->mouseEvent.Control() &&
((modelHighlighted && !rightButton) || linkHighlighted))
{
selectVis = linkVis;
this->dataPtr->selectionLevel = SelectionLevels::LINK;
}
// Select model
else
{
// Can't select a link and a model at the same time
if (this->dataPtr->selectionLevel == SelectionLevels::LINK)
this->DeselectAllVisuals();
selectVis = modelVis;
this->dataPtr->selectionLevel = SelectionLevels::MODEL;
}
this->SetSelectedVisual(selectVis);
event::Events::setSelectedEntity(selectVis->GetName(), "normal");
// Open context menu
if (rightButton)
{
if (selectVis == modelVis)
{
g_modelRightMenu->Run(selectVis->GetName(), QCursor::pos(),
ModelRightMenu::EntityTypes::MODEL);
}
else if (selectVis == linkVis)
{
g_modelRightMenu->Run(selectVis->GetName(), QCursor::pos(),
ModelRightMenu::EntityTypes::LINK);
}
}
}
else
this->SetSelectedVisual(rendering::VisualPtr());
}
this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent);
}
//////////////////////////////////////////////////
void GLWidget::ViewScene(rendering::ScenePtr _scene)
{
// The user camera name.
std::string cameraBaseName = "gzclient_camera";
std::string cameraName = cameraBaseName;
transport::ConnectionPtr connection = transport::connectToMaster();
if (connection)
{
std::string topicData;
msgs::Packet packet;
msgs::Request request;
msgs::GzString_V topics;
request.set_id(0);
request.set_request("get_topics");
connection->EnqueueMsg(msgs::Package("request", request), true);
connection->Read(topicData);
packet.ParseFromString(topicData);
topics.ParseFromString(packet.serialized_data());
std::string searchable;
for (int i = 0; i < topics.data_size(); ++i)
searchable += topics.data(i);
int i = 0;
while (searchable.find(cameraName) != std::string::npos)
{
cameraName = cameraBaseName + boost::lexical_cast<std::string>(++i);
}
}
else
gzerr << "Unable to connect to a running Gazebo master.\n";
if (_scene->UserCameraCount() == 0)
{
this->dataPtr->userCamera = _scene->CreateUserCamera(cameraName,
gazebo::gui::getINIProperty<int>("rendering.stereo", 0));
}
else
{
this->dataPtr->userCamera = _scene->GetUserCamera(0);
}
gui::set_active_camera(this->dataPtr->userCamera);
this->dataPtr->scene = _scene;
math::Vector3 camPos(5, -5, 2);
math::Vector3 lookAt(0, 0, 0);
math::Vector3 delta = lookAt - camPos;
double yaw = atan2(delta.y, delta.x);
double pitch = atan2(-delta.z, sqrt(delta.x*delta.x + delta.y*delta.y));
this->dataPtr->userCamera->SetDefaultPose(math::Pose(camPos,
math::Vector3(0, pitch, yaw)));
// client side heightmap configuration
_scene->SetHeightmapLOD(gazebo::gui::getINIProperty<int>("heightmap.lod", 0));
// Update at the camera's update rate
this->dataPtr->updateTimer->start(
static_cast<int>(
std::round(1000.0 / (4*this->dataPtr->userCamera->RenderRate()))));
}
/////////////////////////////////////////////////
rendering::ScenePtr GLWidget::GetScene() const
{
return this->Scene();
}
/////////////////////////////////////////////////
rendering::ScenePtr GLWidget::Scene() const
{
return this->dataPtr->scene;
}
/////////////////////////////////////////////////
void GLWidget::Clear()
{
gui::clear_active_camera();
this->dataPtr->userCamera.reset();
this->dataPtr->scene.reset();
this->SetSelectedVisual(rendering::VisualPtr());
this->dataPtr->keyModifiers = 0;
}
//////////////////////////////////////////////////
rendering::UserCameraPtr GLWidget::GetCamera() const
{
return this->Camera();
}
//////////////////////////////////////////////////
rendering::UserCameraPtr GLWidget::Camera() const
{
return this->dataPtr->userCamera;
}
//////////////////////////////////////////////////
std::string GLWidget::OgreHandle() const
{
std::string ogreHandle;
#if defined(__APPLE__)
ogreHandle = std::to_string(this->winId());
#elif defined(WIN32)
ogreHandle = std::to_string(
reinterpret_cast<uint32_t>(this->renderFrame->winId()));
#else
QX11Info info = x11Info();
QWidget *q_parent = dynamic_cast<QWidget*>(this->dataPtr->renderFrame);
GZ_ASSERT(q_parent, "q_parent is null");
ogreHandle =
std::to_string(reinterpret_cast<uint64_t>(info.display())) + ":" +
std::to_string(static_cast<uint32_t>(info.screen())) + ":" +
std::to_string(static_cast<uint64_t>(q_parent->winId()));
#endif
return ogreHandle;
}
/////////////////////////////////////////////////
void GLWidget::OnRemoveScene(const std::string &_name)
{
if (this->dataPtr->scene && this->dataPtr->scene->Name() == _name)
{
this->Clear();
}
}
/////////////////////////////////////////////////
void GLWidget::OnCreateScene(const std::string &_name)
{
this->SetSelectedVisual(rendering::VisualPtr());
this->ViewScene(rendering::get_scene(_name));
ModelManipulator::Instance()->Init();
ModelSnap::Instance()->Init();
ModelAlign::Instance()->Init();
}
/////////////////////////////////////////////////
void GLWidget::OnMoveMode(bool _mode)
{
if (_mode)
{
this->dataPtr->entityMaker = NULL;
this->dataPtr->state = "select";
}
}
/////////////////////////////////////////////////
void GLWidget::OnCreateEntity(const std::string &_type,
const std::string &_data)
{
if (this->dataPtr->modelEditorEnabled)
return;
this->ClearSelection();
if (this->dataPtr->entityMaker)
this->dataPtr->entityMaker->Stop();
this->dataPtr->entityMaker = nullptr;
if (_type == "box")
{
if (this->dataPtr->modelMaker.InitSimpleShape(
ModelMaker::SimpleShapes::BOX))
{
this->dataPtr->entityMaker = &this->dataPtr->modelMaker;
}
}
else if (_type == "sphere")
{
if (this->dataPtr->modelMaker.InitSimpleShape(
ModelMaker::SimpleShapes::SPHERE))
{
this->dataPtr->entityMaker = &this->dataPtr->modelMaker;
}
}
else if (_type == "cylinder")
{
if (this->dataPtr->modelMaker.InitSimpleShape(
ModelMaker::SimpleShapes::CYLINDER))
{
this->dataPtr->entityMaker = &this->dataPtr->modelMaker;
}
}
else if (_type == "model" && !_data.empty())
{
if (this->dataPtr->modelMaker.InitFromFile(_data))
this->dataPtr->entityMaker = &this->dataPtr->modelMaker;
}
else if (_type == "pointlight")
this->dataPtr->entityMaker = &this->dataPtr->pointLightMaker;
else if (_type == "spotlight")
this->dataPtr->entityMaker = &this->dataPtr->spotLightMaker;
else if (_type == "directionallight")
this->dataPtr->entityMaker = &this->dataPtr->directionalLightMaker;
if (this->dataPtr->entityMaker)
{
gui::Events::manipMode("make_entity");
// TODO: change the cursor to a cross
this->dataPtr->entityMaker->Start();
}
else
{
this->dataPtr->state = "select";
// TODO: make sure cursor state stays at the default
}
}
/////////////////////////////////////////////////
void GLWidget::OnFPS()
{
this->dataPtr->userCamera->SetViewController(
rendering::FPSViewController::GetTypeString());
}
/////////////////////////////////////////////////
void GLWidget::OnOrbit()
{
this->dataPtr->userCamera->SetViewController(
rendering::OrbitViewController::GetTypeString());
}
/////////////////////////////////////////////////
std::vector<rendering::VisualPtr> GLWidget::SelectedVisuals() const
{
return this->dataPtr->selectedVisuals;
}
/////////////////////////////////////////////////
void GLWidget::SetSelectedVisual(rendering::VisualPtr _vis)
{
// deselect all if not in multi-selection mode.
if (!this->dataPtr->mouseEvent.Control())
{
this->DeselectAllVisuals();
}
std::lock_guard<std::mutex> lock(this->dataPtr->selectedVisMutex);
msgs::Selection msg;
if (_vis && !_vis->IsPlane())
{
if (_vis == _vis->GetRootVisual())
this->dataPtr->selectionLevel = SelectionLevels::MODEL;
else
this->dataPtr->selectionLevel = SelectionLevels::LINK;
_vis->SetHighlighted(true);
// enable multi-selection if control is pressed
if (this->dataPtr->selectedVisuals.empty() ||
this->dataPtr->mouseEvent.Control())
{
std::vector<rendering::VisualPtr>::iterator it =
std::find(this->dataPtr->selectedVisuals.begin(),
this->dataPtr->selectedVisuals.end(), _vis);
if (it == this->dataPtr->selectedVisuals.end())
this->dataPtr->selectedVisuals.push_back(_vis);
else
{
// if element already exists, move to the back of vector
rendering::VisualPtr vis = (*it);
this->dataPtr->selectedVisuals.erase(it);
this->dataPtr->selectedVisuals.push_back(vis);
}
}
g_copyAct->setEnabled(true);
msg.set_id(_vis->GetId());
msg.set_name(_vis->GetName());
msg.set_selected(true);
this->dataPtr->selectionPub->Publish(msg);
}
else if (g_copyAct)
{
g_copyAct->setEnabled(false);
}
if (g_alignAct)
g_alignAct->setEnabled(this->dataPtr->selectedVisuals.size() > 1);
}
/////////////////////////////////////////////////
void GLWidget::DeselectAllVisuals()
{
std::lock_guard<std::mutex> lock(this->dataPtr->selectedVisMutex);
msgs::Selection msg;
for (unsigned int i = 0; i < this->dataPtr->selectedVisuals.size(); ++i)
{
this->dataPtr->selectedVisuals[i]->SetHighlighted(false);
msg.set_id(this->dataPtr->selectedVisuals[i]->GetId());
msg.set_name(this->dataPtr->selectedVisuals[i]->GetName());
msg.set_selected(false);
this->dataPtr->selectionPub->Publish(msg);
}
this->dataPtr->selectedVisuals.clear();
}
/////////////////////////////////////////////////
void GLWidget::OnManipMode(const std::string &_mode)
{
this->dataPtr->state = _mode;
if (!this->dataPtr->selectedVisuals.empty())
{
std::lock_guard<std::mutex> lock(this->dataPtr->selectedVisMutex);
ModelManipulator::Instance()->SetAttachedVisual(
this->dataPtr->selectedVisuals.back());
if (_mode == "select")
{
this->dataPtr->scene->SelectVisual("", "select");
}
else
{
// Make sure model is not updated by server during manipulation
this->dataPtr->scene->SelectVisual(
this->dataPtr->selectedVisuals.back()->GetName(), "move");
}
}
ModelManipulator::Instance()->SetManipulationMode(_mode);
ModelSnap::Instance()->Reset();
if (this->dataPtr->state != "select")
{
std::lock_guard<std::mutex> lock(this->dataPtr->selectedVisMutex);
// only support multi-model selection in select mode for now.
// deselect 0 to n-1 models.
if (this->dataPtr->selectedVisuals.size() > 1)
{
for (std::vector<rendering::VisualPtr>::iterator it
= this->dataPtr->selectedVisuals.begin();
it != --this->dataPtr->selectedVisuals.end();)
{
(*it)->SetHighlighted(false);
it = this->dataPtr->selectedVisuals.erase(it);
}
}
}
}
/////////////////////////////////////////////////
void GLWidget::OnCopy()
{
std::lock_guard<std::mutex> lock(this->dataPtr->selectedVisMutex);
if (!this->dataPtr->selectedVisuals.empty() &&
!this->dataPtr->modelEditorEnabled)
{
this->Copy(this->dataPtr->selectedVisuals.back()->GetName());
}
}
/////////////////////////////////////////////////
void GLWidget::OnPaste()
{
if (!this->dataPtr->modelEditorEnabled)
this->Paste(this->dataPtr->copyEntityName);
}
/////////////////////////////////////////////////
void GLWidget::Copy(const std::string &_name)
{
this->dataPtr->copyEntityName = _name;
g_pasteAct->setEnabled(true);
}
/////////////////////////////////////////////////
void GLWidget::Paste(const std::string &_name)
{
if (!_name.empty())
{
bool isModel = false;
bool isLight = false;
if (this->dataPtr->scene->GetLight(_name))
isLight = true;
else if (this->dataPtr->scene->GetVisual(_name))
isModel = true;
if (isLight || isModel)
{
this->ClearSelection();
if (this->dataPtr->entityMaker)
this->dataPtr->entityMaker->Stop();
if (isLight && this->dataPtr->lightMaker.InitFromLight(_name))
{
this->dataPtr->entityMaker = &this->dataPtr->lightMaker;
this->dataPtr->entityMaker->Start();
// this makes the entity appear at the mouse cursor
this->dataPtr->entityMaker->OnMouseMove(this->dataPtr->mouseEvent);
gui::Events::manipMode("make_entity");
}
else if (isModel && this->dataPtr->modelMaker.InitFromModel(_name))
{
this->dataPtr->entityMaker = &this->dataPtr->modelMaker;
this->dataPtr->entityMaker->Start();
// this makes the entity appear at the mouse cursor
this->dataPtr->entityMaker->OnMouseMove(this->dataPtr->mouseEvent);
gui::Events::manipMode("make_entity");
}
}
}
}
/////////////////////////////////////////////////
void GLWidget::ClearSelection()
{
this->SetSelectedVisual(rendering::VisualPtr());
this->dataPtr->scene->SelectVisual("", "normal");
}
/////////////////////////////////////////////////
void GLWidget::OnSetSelectedEntity(const std::string &_name,
const std::string &_mode)
{
if (!_name.empty())
{
std::string name = _name;
boost::replace_first(name, gui::get_world()+"::", "");
rendering::VisualPtr selection = this->dataPtr->scene->GetVisual(name);
std::vector<rendering::VisualPtr>::iterator it =
std::find(this->dataPtr->selectedVisuals.begin(),
this->dataPtr->selectedVisuals.end(), selection);
// Shortcircuit the case when GLWidget already selected the visual.
if (it == this->dataPtr->selectedVisuals.end() || _name != (*it)->GetName())
{
this->SetSelectedVisual(selection);
this->dataPtr->scene->SelectVisual(name, _mode);
}
}
else if (!this->dataPtr->selectedVisuals.empty())
{
this->SetSelectedVisual(rendering::VisualPtr());
this->dataPtr->scene->SelectVisual("", _mode);
}
}
/////////////////////////////////////////////////
void GLWidget::OnRequest(ConstRequestPtr &_msg)
{
if (_msg->request() == "entity_delete")
{
std::lock_guard<std::mutex> lock(this->dataPtr->selectedVisMutex);
if (!this->dataPtr->selectedVisuals.empty())
{
for (std::vector<rendering::VisualPtr>::iterator it =
this->dataPtr->selectedVisuals.begin();
it != this->dataPtr->selectedVisuals.end();
++it)
{
if ((*it)->GetName() == _msg->data())
{
ModelManipulator::Instance()->Detach();
this->dataPtr->selectedVisuals.erase(it);
break;
}
}
}
if (this->dataPtr->copyEntityName == _msg->data())
{
this->dataPtr->copyEntityName = "";
g_pasteAct->setEnabled(false);
}
}
}
/////////////////////////////////////////////////
void GLWidget::OnAlignMode(const std::string &_axis, const std::string &_config,
const std::string &_target, const bool _preview, const bool _inverted)
{
ModelAlign::Instance()->AlignVisuals(this->dataPtr->selectedVisuals, _axis,
_config, _target, !_preview, _inverted);
}
/////////////////////////////////////////////////
void GLWidget::OnModelEditor(bool _checked)
{
this->dataPtr->modelEditorEnabled = _checked;
g_arrowAct->trigger();
event::Events::setSelectedEntity("", "normal");
// Manually deselect, in case the editor was opened with Ctrl
this->DeselectAllVisuals();
}
/////////////////////////////////////////////////
void GLWidget::OnOrtho()
{
// Disable view control options when in ortho projection
g_fpsAct->setEnabled(false);
g_orbitAct->setEnabled(false);
this->dataPtr->userCamera->SetProjectionType("orthographic");
}
/////////////////////////////////////////////////
void GLWidget::OnPerspective()
{
// Enable view control options when in perspective projection
g_fpsAct->setEnabled(true);
g_orbitAct->setEnabled(true);
this->dataPtr->userCamera->SetProjectionType("perspective");
}
/////////////////////////////////////////////////
QPaintEngine *GLWidget::paintEngine() const
{
return NULL;
}
/////////////////////////////////////////////////
void GLWidget::SetMouseEventButtons(const Qt::MouseButtons &_buttons)
{
if (_buttons & Qt::LeftButton)
{
this->dataPtr->mouseEvent.SetButtons(
this->dataPtr->mouseEvent.Buttons() | common::MouseEvent::LEFT);
}
else
{
this->dataPtr->mouseEvent.SetButtons(
this->dataPtr->mouseEvent.Buttons() | 0x0);
}
if (_buttons & Qt::RightButton)
{
this->dataPtr->mouseEvent.SetButtons(
this->dataPtr->mouseEvent.Buttons() | common::MouseEvent::RIGHT);
}
else
{
this->dataPtr->mouseEvent.SetButtons(
this->dataPtr->mouseEvent.Buttons() | 0x0);
}
if (_buttons & Qt::MidButton)
{
this->dataPtr->mouseEvent.SetButtons(
this->dataPtr->mouseEvent.Buttons() | common::MouseEvent::MIDDLE);
}
else
{
this->dataPtr->mouseEvent.SetButtons(
this->dataPtr->mouseEvent.Buttons() | 0x0);
}
}
/////////////////////////////////////////////////
void GLWidget::SetMouseEventButton(const Qt::MouseButton &_button)
{
if (_button == Qt::LeftButton)
this->dataPtr->mouseEvent.SetButton(common::MouseEvent::LEFT);
else if (_button == Qt::RightButton)
this->dataPtr->mouseEvent.SetButton(common::MouseEvent::RIGHT);
else if (_button == Qt::MidButton)
this->dataPtr->mouseEvent.SetButton(common::MouseEvent::MIDDLE);
}