pxmlw6n2f/Gazebo_Distributed_TCP/gazebo/rendering/OrbitViewController.cc

440 lines
13 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.
*
*/
#include "gazebo/rendering/ogre_gazebo.h"
#include "gazebo/common/MouseEvent.hh"
#include "gazebo/rendering/Conversions.hh"
#include "gazebo/rendering/Scene.hh"
#include "gazebo/rendering/Visual.hh"
#include "gazebo/rendering/UserCamera.hh"
#include "gazebo/rendering/OrbitViewController.hh"
#define TYPE_STRING "orbit"
using namespace gazebo;
using namespace rendering;
static const float PITCH_LIMIT_LOW = -M_PI*0.5 + 0.001;
static const float PITCH_LIMIT_HIGH = M_PI*0.5 - 0.001;
//////////////////////////////////////////////////
OrbitViewController::OrbitViewController(UserCameraPtr _camera,
const std::string &_name)
: ViewController(_camera), distance(5.0f)
{
this->typeString = TYPE_STRING;
this->init = false;
// Create a visual that is used a reference point.
this->refVisual.reset(new Visual(_name, this->camera->GetScene()));
this->refVisual->Load();
this->refVisual->AttachMesh("unit_sphere");
this->refVisual->SetScale(math::Vector3(0.2, 0.2, 0.1));
this->refVisual->SetCastShadows(false);
this->refVisual->SetMaterial("Gazebo/YellowTransparent");
this->refVisual->SetVisible(false);
this->refVisual->SetVisibilityFlags(GZ_VISIBILITY_GUI);
}
//////////////////////////////////////////////////
OrbitViewController::~OrbitViewController()
{
this->refVisual.reset();
}
//////////////////////////////////////////////////
void OrbitViewController::Init(const math::Vector3 &_focalPoint,
const double _yaw, const double _pitch)
{
this->yaw = _yaw;
this->pitch = _pitch;
this->focalPoint = _focalPoint.Ign();
this->distance = this->camera->WorldPosition().Distance(
this->focalPoint);
this->init = true;
}
//////////////////////////////////////////////////
void OrbitViewController::Init()
{
double dist = -1;
ignition::math::Vector3d fp;
// Try to get a point on a plane to use as the reference point
int width = this->camera->ViewportWidth();
int height = this->camera->ViewportHeight();
if (this->camera->WorldPointOnPlane(width/2.0, height/2.0,
ignition::math::Planed(ignition::math::Vector3d(0, 0, 1)), fp))
{
dist = this->camera->WorldPosition().Distance(fp);
}
// If the plane is too far away.
if (dist < 0 || dist > 20 || math::isnan(dist))
{
// First, see if the camera is looking at the origin.
ignition::math::Vector3d dir = this->camera->Direction();
dir.Normalize();
ignition::math::Vector3d origin(0, 0, 0);
ignition::math::Vector3d cameraPos = this->camera->WorldPose().Pos();
double distOrigin = cameraPos.Distance(origin);
dist = origin.DistToLine(cameraPos, cameraPos + dir * distOrigin);
if (ignition::math::equal(dist, 0.0, 1e-3))
dist = distOrigin;
else
{
// If camera is not looking at the origin, see if the camera's
// direction projected on the ground plane interescts the origin.
// Otherwise, choose a default distance of 10m for the focal point
cameraPos.Z(0);
distOrigin = cameraPos.Distance(origin);
dist = origin.DistToLine(cameraPos, cameraPos + dir * distOrigin);
if (ignition::math::equal(dist, 0.0, 1e-3))
dist = distOrigin;
else
dist = 10;
cameraPos = this->camera->WorldPose().Pos();
}
if (dist > 10)
dist = 10.0;
fp = cameraPos + dir * dist;
}
fp.Correct();
this->Init(fp);
}
//////////////////////////////////////////////////
void OrbitViewController::Update()
{
}
//////////////////////////////////////////////////
void OrbitViewController::HandleKeyPressEvent(const std::string &_key)
{
if (_key == "x" || _key == "y" || _key == "z")
this->key = _key;
}
//////////////////////////////////////////////////
void OrbitViewController::HandleKeyReleaseEvent(const std::string &_key)
{
if (_key == "x" || _key == "y" || _key == "z")
this->key.clear();
}
//////////////////////////////////////////////////
void OrbitViewController::HandleMouseEvent(const common::MouseEvent &_event)
{
if (!this->enabled)
return;
ignition::math::Vector2i drag = _event.Pos() - _event.PrevPos();
ignition::math::Vector3d directionVec(0, 0, 0);
int width = this->camera->ViewportWidth();
int height = this->camera->ViewportHeight();
// If the event is the initial press of a mouse button, then update
// the focal point and distance.
if (_event.PressPos() == _event.Pos())
{
if (!this->camera->GetScene()->FirstContact(
this->camera, _event.PressPos(), this->focalPoint))
{
ignition::math::Vector3d origin, dir;
this->camera->CameraToViewportRay(
_event.PressPos().X(), _event.PressPos().Y(), origin, dir);
this->focalPoint = origin + dir * 10.0;
}
this->distance = this->camera->WorldPose().Pos().Distance(
this->focalPoint);
this->yaw = this->camera->WorldRotation().Euler().Z();
this->pitch = this->camera->WorldRotation().Euler().Y();
}
// Turn on the reference visual.
this->refVisual->SetVisible(true);
// Middle mouse button or Shift + Left button is used to Orbit.
if (_event.Dragging() &&
(_event.Buttons() & common::MouseEvent::MIDDLE ||
(_event.Buttons() & common::MouseEvent::LEFT && _event.Shift())))
{
// Compute the delta yaw and pitch.
double dy = this->NormalizeYaw(drag.X() * _event.MoveScale() * -0.4);
double dp = this->NormalizePitch(drag.Y() * _event.MoveScale() * 0.4);
// Limit rotation to pitch only if the "y" key is pressed.
if (!this->key.empty() && this->key == "y")
dy = 0.0;
// Limit rotation to yaw if the "z" key is pressed.
else if (!this->key.empty() && this->key == "z")
dp = 0.0;
this->Orbit(dy, dp);
}
// The left mouse button is used to translate the camera.
else if ((_event.Buttons() & common::MouseEvent::LEFT) && _event.Dragging())
{
this->distance =
this->camera->WorldPose().Pos().Distance(this->focalPoint);
double fovY = this->camera->VFOV().Radian();
double fovX = 2.0f * atan(tan(fovY / 2.0f) *
this->camera->AspectRatio());
ignition::math::Vector3d translation;
double factor = 2.0;
// The control key increases zoom speed by a factor of two.
if (_event.Control())
factor *= 2.0;
// If the "x", "y", or "z" key is pressed, then lock translation to the
// indicated axis.
if (!this->key.empty())
{
if (this->key == "x")
translation.Set((drag.Y() / static_cast<float>(height)) *
this->distance * tan(fovY / 2.0) * factor, 0.0, 0.0);
else if (this->key == "y")
translation.Set(0.0, (drag.X() / static_cast<float>(width)) *
this->distance * tan(fovX / 2.0) * factor, 0.0);
else if (this->key == "z")
translation.Set(0.0, 0.0, (drag.Y() / static_cast<float>(height)) *
this->distance * tan(fovY / 2.0) * factor);
else
gzerr << "Unable to handle key [" << this->key << "] in orbit view "
<< "controller.\n";
// Translate in the global coordinate frame
this->TranslateGlobal(translation);
}
else
{
// Translate in the "y" "z" plane.
translation.Set(0.0,
(drag.X() / static_cast<float>(width)) *
this->distance * tan(fovX / 2.0) * factor,
(drag.Y() / static_cast<float>(height)) *
this->distance * tan(fovY / 2.0) * factor);
// Translate in the local coordinate frame
this->TranslateLocal(translation);
}
}
// The right mouse button is used to zoom the camera.
else if ((_event.Buttons() & common::MouseEvent::RIGHT) && _event.Dragging())
{
double fovY = this->camera->VFOV().Radian();
this->Zoom((-drag.Y() / static_cast<float>(height)) *
this->distance * tan(fovY / 2.0) * 6.0);
}
// The scroll wheel controls zoom.
else if (_event.Type() == common::MouseEvent::SCROLL)
{
if (!this->camera->GetScene()->FirstContact(
this->camera, _event.Pos(), this->focalPoint))
{
ignition::math::Vector3d origin, dir;
this->camera->CameraToViewportRay(
_event.Pos().X(), _event.Pos().Y(), origin, dir);
this->focalPoint = origin + dir * 10.0;
}
this->distance = this->camera->WorldPose().Pos().Distance(
this->focalPoint);
int factor = 80;
// The control key increases zoom speed by a factor of two.
if (_event.Control())
factor *= 2;
// This assumes that _event.scroll.y is -1 or +1
this->Zoom(-(_event.Scroll().Y() * factor) * _event.MoveScale() *
(this->distance / 5.0));
}
else
this->refVisual->SetVisible(false);
}
//////////////////////////////////////////////////
void OrbitViewController::TranslateLocal(const math::Vector3 &_vec)
{
this->camera->SetWorldPosition(
this->camera->WorldPose().Pos() +
this->camera->WorldPose().Rot() * _vec.Ign());
this->UpdateRefVisual();
}
//////////////////////////////////////////////////
void OrbitViewController::TranslateGlobal(const math::Vector3 &_vec)
{
this->camera->SetWorldPosition(
this->camera->WorldPose().Pos() + _vec.Ign());
this->UpdateRefVisual();
}
//////////////////////////////////////////////////
void OrbitViewController::SetDistance(float _d)
{
this->distance = _d;
}
//////////////////////////////////////////////////
void OrbitViewController::SetFocalPoint(const math::Vector3 &_fp)
{
this->focalPoint = _fp.Ign();
this->refVisual->SetPosition(this->focalPoint);
}
//////////////////////////////////////////////////
math::Vector3 OrbitViewController::GetFocalPoint() const
{
return this->focalPoint;
}
//////////////////////////////////////////////////
double OrbitViewController::NormalizeYaw(double _v)
{
_v = fmod(_v, M_PI*2);
if (_v < 0.0f)
{
_v = M_PI * 2 + _v;
}
return _v;
}
//////////////////////////////////////////////////
double OrbitViewController::NormalizePitch(double _v)
{
if (_v < PITCH_LIMIT_LOW)
_v = PITCH_LIMIT_LOW;
else if (_v > PITCH_LIMIT_HIGH)
_v = PITCH_LIMIT_HIGH;
return _v;
}
//////////////////////////////////////////////////
void OrbitViewController::Zoom(float _amount)
{
this->distance -= _amount;
ignition::math::Vector3d delta = this->camera->WorldPosition() -
this->focalPoint;
delta.Normalize();
delta *= this->distance;
this->camera->SetWorldPosition(this->focalPoint + delta);
this->UpdateRefVisual();
}
//////////////////////////////////////////////////
std::string OrbitViewController::GetTypeString()
{
return TYPE_STRING;
}
//////////////////////////////////////////////////
void OrbitViewController::UpdateRefVisual()
{
// Update the pose of the reference visual
this->refVisual->SetPosition(this->focalPoint);
// Update the size of the referenve visual based on the distance to the
// focal point.
double scale = this->distance * atan(GZ_DTOR(1.0));
this->refVisual->SetScale(math::Vector3(scale, scale, scale * 0.5));
}
/////////////////////////////////////////////////
void OrbitViewController::Orbit(double _dy, double _dp)
{
Ogre::SceneNode *cameraNode = this->camera->SceneNode();
Ogre::Node *parentNode = cameraNode->getParent();
Ogre::Vector3 pos = cameraNode->_getDerivedPosition();
// First detach the camera from it's parent. We need to do this in order
// to attach the camera to the reference visual
if (parentNode)
parentNode->removeChild(cameraNode);
// Add the camera node to to the reference visual, and update the
// reference visual's position.
this->refVisual->GetSceneNode()->addChild(this->camera->SceneNode());
this->refVisual->SetPosition(this->focalPoint);
// Move the camera to it's starting location. Now we can rotate the
// reference visual, which in turns rotates the camera.
cameraNode->_setDerivedPosition(pos);
cameraNode->setOrientation(Ogre::Quaternion());
// Rotate and update the reference visual.
this->yaw = this->NormalizeYaw(this->yaw + _dy);
this->pitch = this->NormalizePitch(this->pitch + _dp);
this->refVisual->SetRotation(math::Quaternion(0, this->pitch, this->yaw));
// Get the final position of the camera. Special case when the orbit view
// camera has just been initialized.
if (!this->init)
pos = cameraNode->_getDerivedPosition();
// Store the new location of the camera
Ogre::Quaternion rot = cameraNode->_getDerivedOrientation();
// Detach the camera from the reference visual.
this->refVisual->GetSceneNode()->removeChild(cameraNode);
// Reattach the camera to the reference visual.
if (parentNode)
{
parentNode->addChild(cameraNode);
cameraNode->_setDerivedPosition(pos);
this->camera->SetWorldRotation(Conversions::ConvertIgn(rot));
}
this->init = false;
this->UpdateRefVisual();
}
/////////////////////////////////////////////////
double OrbitViewController::Yaw() const
{
return this->yaw;
}
/////////////////////////////////////////////////
double OrbitViewController::Pitch() const
{
return this->pitch;
}