/* * 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 #endif #include "gazebo/transport/transport.hh" #include "gazebo/rendering/RenderEvents.hh" #include "gazebo/rendering/RenderingIface.hh" #include "gazebo/rendering/Visual.hh" #include "gazebo/rendering/RenderEngine.hh" #include "gazebo/rendering/Scene.hh" #include "gazebo/rendering/UserCamera.hh" #include "gazebo/rendering/SelectionObj.hh" #include "gazebo/gui/qt.h" #include "gazebo/gui/GuiEvents.hh" #include "gazebo/gui/MouseEventHandler.hh" #include "gazebo/gui/GuiIface.hh" #include "gazebo/gui/ModelManipulatorPrivate.hh" #include "gazebo/gui/ModelManipulator.hh" using namespace gazebo; using namespace gui; ///////////////////////////////////////////////// ModelManipulator::ModelManipulator() : dataPtr(new ModelManipulatorPrivate) { this->dataPtr->manipMode = ""; this->dataPtr->globalManip = false; this->dataPtr->initialized = false; } ///////////////////////////////////////////////// ModelManipulator::~ModelManipulator() { this->Clear(); delete this->dataPtr; this->dataPtr = NULL; } ///////////////////////////////////////////////// void ModelManipulator::Clear() { this->dataPtr->userCmdPub.reset(); this->dataPtr->selectionObj.reset(); this->dataPtr->userCamera.reset(); this->dataPtr->scene.reset(); this->dataPtr->node.reset(); this->dataPtr->mouseMoveVis.reset(); this->dataPtr->mouseChildVisualScale.clear(); this->dataPtr->manipMode = ""; this->dataPtr->globalManip = false; this->dataPtr->initialized = false; } ///////////////////////////////////////////////// void ModelManipulator::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->node = transport::NodePtr(new transport::Node()); this->dataPtr->node->TryInit(common::Time::Maximum()); this->dataPtr->userCmdPub = this->dataPtr->node->Advertise("~/user_cmd"); this->dataPtr->selectionObj.reset(new rendering::SelectionObj("__GL_MANIP__", this->dataPtr->scene->WorldVisual())); this->dataPtr->selectionObj->Load(); this->dataPtr->initialized = true; } ///////////////////////////////////////////////// void ModelManipulator::Detach() { this->dataPtr->selectionObj->SetMode( rendering::SelectionObj::SELECTION_NONE); this->dataPtr->selectionObj->Detach(); } ///////////////////////////////////////////////// void ModelManipulator::RotateEntity(rendering::VisualPtr &_vis, const math::Vector3 &_axis, bool _local) { ignition::math::Vector3d normal; if (_local) { if (_axis.x > 0) normal = this->dataPtr->mouseMoveVisStartPose.rot.GetXAxis().Ign(); else if (_axis.y > 0) normal = this->dataPtr->mouseMoveVisStartPose.rot.GetYAxis().Ign(); else if (_axis.z > 0) normal = this->dataPtr->mouseMoveVisStartPose.rot.GetZAxis().Ign(); } else normal = _axis.Ign(); double offset = this->dataPtr->mouseMoveVisStartPose.pos.Dot(normal); ignition::math::Vector3d pressPoint; this->dataPtr->userCamera->WorldPointOnPlane( this->dataPtr->mouseEvent.PressPos().X(), this->dataPtr->mouseEvent.PressPos().Y(), ignition::math::Planed(normal, offset), pressPoint); ignition::math::Vector3d newPoint; this->dataPtr->userCamera->WorldPointOnPlane( this->dataPtr->mouseEvent.Pos().X(), this->dataPtr->mouseEvent.Pos().Y(), ignition::math::Planed(normal, offset), newPoint); ignition::math::Vector3d v1 = pressPoint - this->dataPtr->mouseMoveVisStartPose.pos.Ign(); ignition::math::Vector3d v2 = newPoint - this->dataPtr->mouseMoveVisStartPose.pos.Ign(); v1 = v1.Normalize(); v2 = v2.Normalize(); double signTest = v1.Cross(v2).Dot(normal); double angle = atan2((v1.Cross(v2)).Length(), v1.Dot(v2)); if (signTest < 0 ) angle *= -1; // Using Qt control modifier instead of Gazebo's for now. // See GLWidget::keyPressEvent if (QApplication::keyboardModifiers() & Qt::ControlModifier) angle = rint(angle / (M_PI * 0.25)) * (M_PI * 0.25); math::Quaternion rot(_axis, angle); if (_local) rot = this->dataPtr->mouseMoveVisStartPose.rot * rot; else rot = rot * this->dataPtr->mouseMoveVisStartPose.rot; _vis->SetWorldRotation(rot); } ///////////////////////////////////////////////// math::Vector3 ModelManipulator::GetMousePositionOnPlane( rendering::CameraPtr _camera, const common::MouseEvent &_event) { ignition::math::Vector3d origin1, dir1, p1; // Cast ray from the camera into the world _camera->CameraToViewportRay(_event.Pos().X(), _event.Pos().Y(), origin1, dir1); // Compute the distance from the camera to plane of translation ignition::math::Planed plane(ignition::math::Vector3d(0, 0, 1), 0); double dist1 = plane.Distance(origin1, dir1); p1 = origin1 + dir1 * dist1; return p1; } ///////////////////////////////////////////////// math::Vector3 ModelManipulator::SnapPoint(const math::Vector3 &_point, double _interval, double _sensitivity) { if (_interval < 0) { gzerr << "Interval distance must be greater than or equal to 0" << std::endl; return math::Vector3::Zero; } if (_sensitivity < 0 || _sensitivity > 1.0) { gzerr << "Sensitivity must be between 0 and 1" << std::endl; return math::Vector3::Zero; } math::Vector3 point = _point; double snap = _interval * _sensitivity; double remainder = fmod(point.x, _interval); int sign = remainder >= 0 ? 1 : -1; if (fabs(remainder) < snap) point.x -= remainder; else if (fabs(remainder) > (_interval - snap)) point.x = point.x - remainder + _interval * sign; remainder = fmod(point.y, _interval); sign = remainder >= 0 ? 1 : -1; if (fabs(remainder) < snap) point.y -= remainder; else if (fabs(remainder) > (_interval - snap)) point.y = point.y - remainder + _interval * sign; remainder = fmod(point.z, _interval); sign = remainder >= 0 ? 1 : -1; if (fabs(remainder) < snap) point.z -= remainder; else if (fabs(remainder) > (_interval - snap)) point.z = point.z - remainder + _interval * sign; return point; } ///////////////////////////////////////////////// math::Vector3 ModelManipulator::GetMouseMoveDistance( rendering::CameraPtr _camera, const math::Vector2i &_start, const math::Vector2i &_end, const math::Pose &_pose, const math::Vector3 &_axis, bool _local) { ignition::math::Pose3d pose = _pose.Ign(); ignition::math::Vector3d origin1, dir1, p1; ignition::math::Vector3d origin2, dir2, p2; // Cast two rays from the camera into the world _camera->CameraToViewportRay(_end.x, _end.y, origin1, dir1); _camera->CameraToViewportRay(_start.x, _start.y, origin2, dir2); ignition::math::Vector3d planeNorm(0, 0, 0); ignition::math::Vector3d projNorm(0, 0, 0); ignition::math::Vector3d planeNormOther(0, 0, 0); if (_axis.x > 0 && _axis.y > 0) { planeNorm.Z(1); projNorm.Z(1); } else if (_axis.z > 0) { planeNorm.Y(1); projNorm.X(1); planeNormOther.X(1); } else if (_axis.x > 0) { planeNorm.Z(1); projNorm.Y(1); planeNormOther.Y(1); } else if (_axis.y > 0) { planeNorm.Z(1); projNorm.X(1); planeNormOther.X(1); } if (_local) { planeNorm = pose.Rot().RotateVector(planeNorm); projNorm = pose.Rot().RotateVector(projNorm); } // Fine tune ray casting: cast a second ray and compare the two rays' angle // to plane. Use the one that is less parallel to plane for better results. double angle = dir1.Dot(planeNorm); if (_local) planeNormOther = pose.Rot().RotateVector(planeNormOther); double angleOther = dir1.Dot(planeNormOther); if (fabs(angleOther) > fabs(angle)) { projNorm = planeNorm; planeNorm = planeNormOther; } // Compute the distance from the camera to plane double d = pose.Pos().Dot(planeNorm); ignition::math::Planed plane(planeNorm, d); double dist1 = plane.Distance(origin1, dir1); double dist2 = plane.Distance(origin2, dir2); // Compute two points on the plane. The first point is the current // mouse position, the second is the previous mouse position p1 = origin1 + dir1 * dist1; p2 = origin2 + dir2 * dist2; if (_local) p1 = p1 - (p1-p2).Dot(projNorm) * projNorm; ignition::math::Vector3d distance = p1 - p2; if (!_local) distance *= _axis.Ign(); return distance; } ///////////////////////////////////////////////// math::Vector3 ModelManipulator::GetMouseMoveDistance(const math::Pose &_pose, const math::Vector3 &_axis, bool _local) const { return GetMouseMoveDistance(this->dataPtr->userCamera, this->dataPtr->mouseStart, math::Vector2i(this->dataPtr->mouseEvent.Pos().X(), this->dataPtr->mouseEvent.Pos().Y()), _pose, _axis, _local); } ///////////////////////////////////////////////// void ModelManipulator::ScaleEntity(rendering::VisualPtr &_vis, const math::Vector3 &_axis, bool _local) { math::Box bbox = this->dataPtr->mouseVisualBbox; math::Pose pose = _vis->GetWorldPose(); math::Vector3 distance = this->GetMouseMoveDistance(pose, _axis, _local); math::Vector3 bboxSize = bbox.GetSize(); math::Vector3 scale = (bboxSize + pose.rot.RotateVectorReverse(distance)) / bboxSize; // extended scaling to work in model editor mode by checking geometry // type of first visual child. std::string geomType; if (_vis == _vis->GetRootVisual()) { // link-level visuals for (unsigned int i = 0; i < _vis->GetChildCount(); ++i) { rendering::VisualPtr childVis = _vis->GetChild(i); if (childVis->GetPose().pos != math::Vector3::Zero) { gzwarn << "Scaling is currently limited to simple shapes with their " << "origin in the centroid." << std::endl; return; } // visual/collision level visuals for (unsigned int j = 0; j < childVis->GetChildCount(); ++j) { rendering::VisualPtr grandChildVis = childVis->GetChild(j); std::string thisGeomType = grandChildVis->GetGeometryType(); if (grandChildVis->GetPose().pos != math::Vector3::Zero) { gzwarn << "Scaling is currently limited to simple shapes with their " << "origin in the centroid." << std::endl; return; } if (thisGeomType == "") continue; if (geomType == "") { geomType = thisGeomType; } else if (thisGeomType != geomType) { gzwarn << "Scaling is currently limited to models consisting of a " << "single simple geometry type." << std::endl; return; } } } if (QApplication::keyboardModifiers() & Qt::ShiftModifier || geomType == "sphere") { scale = this->UpdateScale(_axis, scale, "sphere"); } else if (geomType == "cylinder") { scale = this->UpdateScale(_axis, scale, "cylinder"); } else if (geomType == "box") { // keep new scale as it is } else { // TODO scaling for complex models are not yet functional. // Limit scaling to simple shapes for now. gzwarn << " Scaling is currently limited to simple shapes." << std::endl; return; } math::Vector3 newScale = this->dataPtr->mouseVisualScale * scale.GetAbs(); if (QApplication::keyboardModifiers() & Qt::ControlModifier) { newScale = SnapPoint(newScale); // prevent setting zero scale newScale.x = std::max(1e-4, newScale.x); newScale.y = std::max(1e-4, newScale.y); newScale.z = std::max(1e-4, newScale.z); } _vis->SetScale(newScale); Events::scaleEntity(_vis->GetName(), newScale); } else { // model editor mode -> apply scaling to individual visuals if (this->dataPtr->mouseChildVisualScale.size() != _vis->GetChildCount()) { gzerr << "Incorrect number of child visuals to be scaled. " << "This should not happen" << std::endl; return; } for (unsigned int i = 0; i < _vis->GetChildCount(); ++i) { rendering::VisualPtr childVis = _vis->GetChild(i); geomType = childVis->GetGeometryType(); if (childVis != this->dataPtr->selectionObj && geomType != "" && geomType != "mesh") { math::Vector3 geomScale; if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { geomScale = this->UpdateScale(_axis, scale, "sphere"); } else { geomScale = this->UpdateScale(_axis, scale, geomType); } math::Vector3 newScale = this->dataPtr->mouseChildVisualScale[i] * geomScale.GetAbs(); if (QApplication::keyboardModifiers() & Qt::ControlModifier) { newScale = SnapPoint(newScale); // prevent setting zero scale newScale.x = std::max(1e-4, newScale.x); newScale.y = std::max(1e-4, newScale.y); newScale.z = std::max(1e-4, newScale.z); } childVis->SetScale(newScale); Events::scaleEntity(childVis->GetName(), newScale); } } } } ///////////////////////////////////////////////// math::Vector3 ModelManipulator::UpdateScale(const math::Vector3 &_axis, const math::Vector3 &_scale, const std::string &_geom) { math::Vector3 scale = _scale; if (_geom == "sphere") { if (_axis.x > 0) { scale.y = scale.x; scale.z = scale.x; } else if (_axis.y > 0) { scale.x = scale.y; scale.z = scale.y; } else if (_axis.z > 0) { scale.x = scale.z; scale.y = scale.z; } } else if (_geom == "cylinder") { if (_axis.x > 0) { scale.y = scale.x; } else if (_axis.y > 0) { scale.x = scale.y; } } return scale; } ///////////////////////////////////////////////// void ModelManipulator::TranslateEntity(rendering::VisualPtr &_vis, const math::Vector3 &_axis, bool _local) { math::Pose pose = _vis->GetWorldPose(); math::Vector3 distance = this->GetMouseMoveDistance(pose, _axis, _local); pose.pos = this->dataPtr->mouseMoveVisStartPose.pos + distance; if (QApplication::keyboardModifiers() & Qt::ControlModifier) { pose.pos = SnapPoint(pose.pos); } if (!(_axis.z > 0) && !_local) pose.pos.z = _vis->GetWorldPose().pos.z; _vis->SetWorldPose(pose); } ///////////////////////////////////////////////// void ModelManipulator::PublishVisualPose(rendering::VisualPtr _vis) { if (!_vis) return; // Register user command on server std::string description; if (this->dataPtr->manipMode == "translate") { description = "Translate ["; } else if (this->dataPtr->manipMode == "rotate") { description = "Rotate ["; } else { gzerr << "Unknown mode [" << this->dataPtr->manipMode << "]. " << "Not sending user command." << std::endl; return; } msgs::UserCmd userCmdMsg; userCmdMsg.set_description(description + _vis->GetName() + "]"); userCmdMsg.set_type(msgs::UserCmd::MOVING); // Only publish for models if (_vis->GetType() == gazebo::rendering::Visual::VT_MODEL) { 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); } // Otherwise, check to see if the visual is a light else if (this->dataPtr->scene->GetLight(_vis->GetName())) { msgs::Light msg; msg.set_name(_vis->GetName()); msgs::Set(msg.mutable_pose(), _vis->GetWorldPose().Ign()); auto lightMsg = userCmdMsg.add_light(); lightMsg->CopyFrom(msg); } else { // We get here in the model editor for example return; } this->dataPtr->userCmdPub->Publish(userCmdMsg); } ///////////////////////////////////////////////// void ModelManipulator::PublishVisualScale(rendering::VisualPtr _vis) { if (!_vis || this->dataPtr->manipMode != "scale" || _vis->GetType() != gazebo::rendering::Visual::VT_MODEL) { return; } // Register user command on server msgs::UserCmd userCmdMsg; userCmdMsg.set_description("Scale [" + _vis->GetName() + "]"); userCmdMsg.set_type(msgs::UserCmd::SCALING); 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_scale(), _vis->GetScale().Ign()); auto modelMsg = userCmdMsg.add_model(); modelMsg->CopyFrom(msg); this->dataPtr->userCmdPub->Publish(userCmdMsg); _vis->SetScale(this->dataPtr->mouseVisualScale); } ///////////////////////////////////////////////// void ModelManipulator::OnMousePressEvent(const common::MouseEvent &_event) { this->dataPtr->mouseEvent = _event; this->dataPtr->mouseStart = _event.PressPos(); this->SetMouseMoveVisual(rendering::VisualPtr()); rendering::VisualPtr vis; std::string manipState; rendering::VisualPtr mouseVis = this->dataPtr->userCamera->GetVisual(this->dataPtr->mouseStart, manipState); this->dataPtr->selectionObj->SetState(manipState); // See issue #1510 bool keyIsPressed = (QApplication::keyboardModifiers() != Qt::NoModifier); // set the new mouse vis only if there are no modifier keys pressed and the // entity was different from the previously selected one. if (!keyIsPressed && (this->dataPtr->selectionObj->GetMode() == rendering::SelectionObj::SELECTION_NONE || (mouseVis && mouseVis != this->dataPtr->selectionObj->GetParent()))) { vis = mouseVis; } else { vis = this->dataPtr->selectionObj->GetParent(); } if (vis && !vis->IsPlane() && this->dataPtr->mouseEvent.Button() == common::MouseEvent::LEFT) { // Root visual rendering::VisualPtr rootVis = vis->GetRootVisual(); // If the root visual's ID can be found, it is a model in simulation mode, // so we select it instead of a child link/visual/collision. // If the visual's depth is less than 2, it is a root visual already. This // is the case of lights in simulation mode for example. // TODO gui::get_entity_id always return 0 in QTestFixture due to nullptr // g_main_win if (gui::get_entity_id(rootVis->GetName()) || vis->GetDepth() < 2) { // select model vis = rootVis; } // Otherwise, we assume we're in the model editor and get the first child // visual after the root, which may be a link or a nested model. else { vis = vis->GetNthAncestor(2); } this->dataPtr->mouseMoveVisStartPose = vis->GetWorldPose(); this->SetMouseMoveVisual(vis); event::Events::setSelectedEntity( this->dataPtr->mouseMoveVis->GetName(), "move"); QApplication::setOverrideCursor(Qt::ClosedHandCursor); if (this->dataPtr->mouseMoveVis && !this->dataPtr->mouseMoveVis->IsPlane()) { this->dataPtr->selectionObj->Attach(this->dataPtr->mouseMoveVis); this->dataPtr->selectionObj->SetMode(this->dataPtr->manipMode); } else { this->dataPtr->selectionObj->SetMode( rendering::SelectionObj::SELECTION_NONE); this->dataPtr->selectionObj->Detach(); } } else this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent); } ///////////////////////////////////////////////// void ModelManipulator::OnMouseMoveEvent(const common::MouseEvent &_event) { this->dataPtr->mouseEvent = _event; if (this->dataPtr->mouseEvent.Dragging()) { if (this->dataPtr->mouseMoveVis && this->dataPtr->mouseEvent.Button() == common::MouseEvent::LEFT) { math::Vector3 axis = math::Vector3::Zero; if (this->dataPtr->keyEvent.key == Qt::Key_X) axis.x = 1; else if (this->dataPtr->keyEvent.key == Qt::Key_Y) axis.y = 1; else if (this->dataPtr->keyEvent.key == Qt::Key_Z) axis.z = 1; if (this->dataPtr->selectionObj->GetMode() == rendering::SelectionObj::TRANS) { if (axis != math::Vector3::Zero) { this->TranslateEntity(this->dataPtr->mouseMoveVis, axis, false); } else if (this->dataPtr->selectionObj->GetState() == rendering::SelectionObj::TRANS_X) { this->TranslateEntity(this->dataPtr->mouseMoveVis, math::Vector3::UnitX, !this->dataPtr->globalManip); } else if (this->dataPtr->selectionObj->GetState() == rendering::SelectionObj::TRANS_Y) { this->TranslateEntity(this->dataPtr->mouseMoveVis, math::Vector3::UnitY, !this->dataPtr->globalManip); } else if (this->dataPtr->selectionObj->GetState() == rendering::SelectionObj::TRANS_Z) { this->TranslateEntity(this->dataPtr->mouseMoveVis, math::Vector3::UnitZ, !this->dataPtr->globalManip); } else { this->TranslateEntity( this->dataPtr->mouseMoveVis, math::Vector3(1, 1, 0)); } } else if (this->dataPtr->selectionObj->GetMode() == rendering::SelectionObj::ROT) { if (axis != math::Vector3::Zero) { this->RotateEntity(this->dataPtr->mouseMoveVis, axis, false); } else if (this->dataPtr->selectionObj->GetState() == rendering::SelectionObj::ROT_X || this->dataPtr->keyEvent.key == Qt::Key_X) { this->RotateEntity(this->dataPtr->mouseMoveVis, math::Vector3::UnitX, !this->dataPtr->globalManip); } else if (this->dataPtr->selectionObj->GetState() == rendering::SelectionObj::ROT_Y || this->dataPtr->keyEvent.key == Qt::Key_Y) { this->RotateEntity(this->dataPtr->mouseMoveVis, math::Vector3::UnitY, !this->dataPtr->globalManip); } else if (this->dataPtr->selectionObj->GetState() == rendering::SelectionObj::ROT_Z || this->dataPtr->keyEvent.key == Qt::Key_Z) { this->RotateEntity(this->dataPtr->mouseMoveVis, math::Vector3::UnitZ, !this->dataPtr->globalManip); } } else if (this->dataPtr->selectionObj->GetMode() == rendering::SelectionObj::SCALE) { if (axis != math::Vector3::Zero) { this->ScaleEntity(this->dataPtr->mouseMoveVis, axis, false); } else if (this->dataPtr->selectionObj->GetState() == rendering::SelectionObj::SCALE_X || this->dataPtr->keyEvent.key == Qt::Key_X) { this->ScaleEntity(this->dataPtr->mouseMoveVis, math::Vector3::UnitX, true); } else if (this->dataPtr->selectionObj->GetState() == rendering::SelectionObj::SCALE_Y || this->dataPtr->keyEvent.key == Qt::Key_Y) { this->ScaleEntity(this->dataPtr->mouseMoveVis, math::Vector3::UnitY, true); } else if (this->dataPtr->selectionObj->GetState() == rendering::SelectionObj::SCALE_Z || this->dataPtr->keyEvent.key == Qt::Key_Z) { this->ScaleEntity(this->dataPtr->mouseMoveVis, math::Vector3::UnitZ, true); } } } else this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent); } else { std::string manipState; this->dataPtr->userCamera->GetVisual(this->dataPtr->mouseEvent.Pos(), manipState); this->dataPtr->selectionObj->SetState(manipState); if (!manipState.empty()) QApplication::setOverrideCursor(Qt::OpenHandCursor); else { rendering::VisualPtr vis = this->dataPtr->userCamera->GetVisual( this->dataPtr->mouseEvent.Pos()); if (vis && !vis->IsPlane()) QApplication::setOverrideCursor(Qt::OpenHandCursor); else QApplication::setOverrideCursor(Qt::ArrowCursor); this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent); } } } ////////////////////////////////////////////////// void ModelManipulator::OnMouseReleaseEvent(const common::MouseEvent &_event) { this->dataPtr->mouseEvent = _event; if (this->dataPtr->mouseEvent.Dragging()) { // If we were dragging a visual around, then publish its new pose to the // server if (this->dataPtr->mouseMoveVis) { if (this->dataPtr->manipMode == "scale") { this->dataPtr->selectionObj->UpdateSize(); this->PublishVisualScale(this->dataPtr->mouseMoveVis); } else this->PublishVisualPose(this->dataPtr->mouseMoveVis); this->SetMouseMoveVisual(rendering::VisualPtr()); QApplication::setOverrideCursor(Qt::OpenHandCursor); } event::Events::setSelectedEntity("", "normal"); } else { if (this->dataPtr->mouseEvent.Button() == common::MouseEvent::LEFT) { rendering::VisualPtr vis = this->dataPtr->userCamera->GetVisual(this->dataPtr->mouseEvent.Pos()); if (vis && vis->IsPlane()) { this->dataPtr->selectionObj->SetMode( rendering::SelectionObj::SELECTION_NONE); this->dataPtr->selectionObj->Detach(); } } } this->dataPtr->userCamera->HandleMouseEvent(this->dataPtr->mouseEvent); } ////////////////////////////////////////////////// void ModelManipulator::SetManipulationMode(const std::string &_mode) { this->dataPtr->manipMode = _mode; if (this->dataPtr->selectionObj->GetMode() != rendering::SelectionObj::SELECTION_NONE || this->dataPtr->mouseMoveVis) { this->dataPtr->selectionObj->SetMode(this->dataPtr->manipMode); if (this->dataPtr->manipMode != "translate" && this->dataPtr->manipMode != "rotate" && this->dataPtr->manipMode != "scale") this->SetMouseMoveVisual(rendering::VisualPtr()); } } ///////////////////////////////////////////////// void ModelManipulator::SetAttachedVisual(rendering::VisualPtr _vis) { rendering::VisualPtr vis = _vis; if (gui::get_entity_id(vis->GetRootVisual()->GetName())) vis = vis->GetRootVisual(); this->dataPtr->mouseMoveVisStartPose = vis->GetWorldPose(); this->SetMouseMoveVisual(vis); if (this->dataPtr->mouseMoveVis && !this->dataPtr->mouseMoveVis->IsPlane()) this->dataPtr->selectionObj->Attach(this->dataPtr->mouseMoveVis); } ///////////////////////////////////////////////// void ModelManipulator::SetMouseMoveVisual(rendering::VisualPtr _vis) { this->dataPtr->mouseMoveVis = _vis; if (_vis) { this->dataPtr->mouseVisualScale = _vis->GetScale(); this->dataPtr->mouseChildVisualScale.clear(); // keep track of all child visual scale for scaling to work in // model editor mode. for (unsigned int i = 0; i < _vis->GetChildCount(); ++i) { rendering::VisualPtr childVis = _vis->GetChild(i); this->dataPtr->mouseChildVisualScale.push_back(childVis->GetScale()); } this->dataPtr->mouseVisualBbox = _vis->GetBoundingBox(); } else this->dataPtr->mouseVisualScale = math::Vector3::One; } ////////////////////////////////////////////////// void ModelManipulator::OnKeyPressEvent(const common::KeyEvent &_event) { this->dataPtr->keyEvent = _event; // reset mouseMoveVisStartPose if in manipulation mode. if (this->dataPtr->manipMode == "translate" || this->dataPtr->manipMode == "rotate" || this->dataPtr->manipMode == "scale") { if (_event.key == Qt::Key_X || _event.key == Qt::Key_Y || _event.key == Qt::Key_Z) { this->dataPtr->mouseStart = this->dataPtr->mouseEvent.Pos(); if (this->dataPtr->mouseMoveVis) { this->dataPtr->mouseMoveVisStartPose = this->dataPtr->mouseMoveVis->GetWorldPose(); } } else if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { this->dataPtr->globalManip = true; this->dataPtr->selectionObj->SetGlobal(this->dataPtr->globalManip); } } } ////////////////////////////////////////////////// void ModelManipulator::OnKeyReleaseEvent(const common::KeyEvent &_event) { this->dataPtr->keyEvent = _event; // reset mouseMoveVisStartPose if in manipulation mode. if (this->dataPtr->manipMode == "translate" || this->dataPtr->manipMode == "rotate" || this->dataPtr->manipMode == "scale") { if (_event.key == Qt::Key_X || _event.key == Qt::Key_Y || _event.key == Qt::Key_Z) { this->dataPtr->mouseStart = this->dataPtr->mouseEvent.Pos(); if (this->dataPtr->mouseMoveVis) { this->dataPtr->mouseMoveVisStartPose = this->dataPtr->mouseMoveVis->GetWorldPose(); } } else if (this->dataPtr->keyEvent.key == Qt::Key_Shift) { this->dataPtr->globalManip = false; this->dataPtr->selectionObj->SetGlobal(this->dataPtr->globalManip); } } this->dataPtr->keyEvent.key = 0; } // Function migrated here from GLWidget.cc and commented out since it doesn't // seem like it's currently used. Kept here for future references ///////////////////////////////////////////////// /*void GLWidget::SmartMoveVisual(rendering::VisualPtr _vis) { if (!this->dataPtr->mouseEvent.dragging) return; // Get the point on the plane which correspoinds to the mouse math::Vector3 pp; // Rotate the visual using the middle mouse button if (this->dataPtr->mouseEvent.buttons == common::MouseEvent::MIDDLE) { math::Vector3 rpy = this->dataPtr->mouseMoveVisStartPose.rot.GetAsEuler(); math::Vector2i delta = this->dataPtr->mouseEvent.pos - this->dataPtr->mouseEvent.pressPos; double yaw = (delta.x * 0.01) + rpy.z; if (!this->dataPtr->mouseEvent.shift) { double snap = rint(yaw / (M_PI * .25)) * (M_PI * 0.25); if (fabs(yaw - snap) < GZ_DTOR(10)) yaw = snap; } _vis->SetWorldRotation(math::Quaternion(rpy.x, rpy.y, yaw)); } else if (this->dataPtr->mouseEvent.buttons == common::MouseEvent::RIGHT) { math::Vector3 rpy = this->dataPtr->mouseMoveVisStartPose.rot.GetAsEuler(); math::Vector2i delta = this->dataPtr->mouseEvent.pos - this->dataPtr->mouseEvent.pressPos; double pitch = (delta.y * 0.01) + rpy.y; if (!this->dataPtr->mouseEvent.shift) { double snap = rint(pitch / (M_PI * .25)) * (M_PI * 0.25); if (fabs(pitch - snap) < GZ_DTOR(10)) pitch = snap; } _vis->SetWorldRotation(math::Quaternion(rpy.x, pitch, rpy.z)); } else if (this->dataPtr->mouseEvent.buttons & common::MouseEvent::LEFT && this->dataPtr->mouseEvent.buttons & common::MouseEvent::RIGHT) { math::Vector3 rpy = this->dataPtr->mouseMoveVisStartPose.rot.GetAsEuler(); math::Vector2i delta = this->dataPtr->mouseEvent.pos - this->dataPtr->mouseEvent.pressPos; double roll = (delta.x * 0.01) + rpy.x; if (!this->dataPtr->mouseEvent.shift) { double snap = rint(roll / (M_PI * .25)) * (M_PI * 0.25); if (fabs(roll - snap) < GZ_DTOR(10)) roll = snap; } _vis->SetWorldRotation(math::Quaternion(roll, rpy.y, rpy.z)); } else { this->TranslateEntity(_vis); } }*/