559 lines
16 KiB
C++
559 lines
16 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.
|
|
*
|
|
*/
|
|
/*
|
|
* Desc: Projector
|
|
* Author: Jared Duke, John Hsu, Nate Koenig
|
|
*/
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/bind.hpp>
|
|
|
|
#include "gazebo/rendering/RTShaderSystem.hh"
|
|
|
|
#include "gazebo/rendering/Conversions.hh"
|
|
#include "gazebo/rendering/Visual.hh"
|
|
#include "gazebo/rendering/Scene.hh"
|
|
#include "gazebo/rendering/Projector.hh"
|
|
|
|
using namespace gazebo;
|
|
using namespace rendering;
|
|
|
|
typedef std::map<std::string, Ogre::Pass*> OgrePassMap;
|
|
typedef OgrePassMap::iterator OgrePassMapIterator;
|
|
|
|
/////////////////////////////////////////////////
|
|
Projector::Projector(rendering::VisualPtr _parent)
|
|
{
|
|
this->visual = _parent;
|
|
|
|
this->node = transport::NodePtr(new transport::Node());
|
|
this->node->Init(this->visual->GetScene()->Name());
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
Projector::~Projector()
|
|
{
|
|
this->SetEnabled(false);
|
|
// Ogre cleanup
|
|
Ogre::Root::getSingletonPtr()->removeFrameListener(&this->projector);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::Load(const std::string &_name,
|
|
const math::Pose &_pose,
|
|
const std::string &_textureName,
|
|
double _nearClip,
|
|
double _farClip,
|
|
double _fov)
|
|
{
|
|
std::string topicName = std::string("~/") + _name;
|
|
|
|
boost::replace_all(topicName, "::", "/");
|
|
this->controlSub = this->node->Subscribe(topicName, &Projector::OnMsg, this);
|
|
|
|
if (!this->visual)
|
|
return;
|
|
|
|
int retryCount = 0;
|
|
|
|
// Initialize the projector
|
|
while (!this->projector.initialized && retryCount < 10)
|
|
{
|
|
// init
|
|
this->projector.Init(this->visual, _textureName, _nearClip, _farClip, _fov);
|
|
|
|
// set the projector pose relative to body
|
|
this->projector.SetPose(_pose);
|
|
|
|
if (!this->projector.initialized)
|
|
{
|
|
gzwarn << "starting projector failed, retrying in 1 sec.\n";
|
|
common::Time::MSleep(1000);
|
|
++retryCount;
|
|
}
|
|
}
|
|
|
|
// Add the projector as an Ogre frame listener
|
|
Ogre::Root::getSingletonPtr()->addFrameListener(&this->projector);
|
|
|
|
this->projector.SetEnabled(true);
|
|
|
|
// Start projector
|
|
/*this->add_model_event_ = gazebo::event::Events::ConnectWorldUpdateStart(
|
|
boost::bind(&Projector::ToggleProjector, this, true));
|
|
*/
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::Load(sdf::ElementPtr _sdf)
|
|
{
|
|
math::Pose pose;
|
|
std::string textureName;
|
|
double nearClip = 0.1;
|
|
double farClip = 10.0;
|
|
double fov = M_PI * 0.25;
|
|
|
|
if (_sdf->HasElement("pose"))
|
|
pose = _sdf->Get<math::Pose>("pose");
|
|
|
|
if (_sdf->HasElement("texture_name"))
|
|
textureName = _sdf->Get<std::string>("texture_name");
|
|
|
|
if (_sdf->HasElement("near_clip"))
|
|
nearClip = _sdf->Get<double>("near_clip");
|
|
|
|
if (_sdf->HasElement("far_clip"))
|
|
farClip = _sdf->Get<double>("far_clip");
|
|
|
|
if (_sdf->HasElement("fov"))
|
|
fov = _sdf->Get<double>("fov");
|
|
|
|
this->Load(_sdf->Get<std::string>("name"), pose, textureName,
|
|
nearClip, farClip, fov);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::Load(const msgs::Projector &_msg)
|
|
{
|
|
ignition::math::Pose3d pose;
|
|
std::string textureName;
|
|
double nearClip = 0.1;
|
|
double farClip = 10.0;
|
|
double fov = M_PI * 0.25;
|
|
|
|
if (_msg.has_pose())
|
|
pose = msgs::ConvertIgn(_msg.pose());
|
|
|
|
if (_msg.has_texture())
|
|
textureName = _msg.texture();
|
|
|
|
if (_msg.has_near_clip())
|
|
nearClip = _msg.near_clip();
|
|
|
|
if (_msg.has_far_clip())
|
|
farClip = _msg.far_clip();
|
|
|
|
if (_msg.has_fov())
|
|
fov = _msg.fov();
|
|
|
|
this->Load(_msg.name(), pose, textureName, nearClip, farClip, fov);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::SetEnabled(bool _enabled)
|
|
{
|
|
this->projector.SetEnabled(_enabled);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::SetTexture(const std::string &_textureName)
|
|
{
|
|
this->projector.SetTexture(_textureName);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::Toggle()
|
|
{
|
|
// if not headless
|
|
/*if (this->projector.initialized)
|
|
{
|
|
this->projector.SetEnabled(!this->projector.enabled);
|
|
}
|
|
else
|
|
gzwarn << "could not start projector, toggle failed\n";
|
|
*/
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
VisualPtr Projector::GetParent()
|
|
{
|
|
return this->visual;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
Projector::ProjectorFrameListener::ProjectorFrameListener()
|
|
{
|
|
this->enabled = false;
|
|
this->initialized = false;
|
|
this->usingShaders = false;
|
|
|
|
this->node = NULL;
|
|
this->filterNode = NULL;
|
|
this->projectorQuery = NULL;
|
|
this->frustum = NULL;
|
|
this->filterFrustum = NULL;
|
|
|
|
this->nodeName = "Projector";
|
|
this->filterNodeName = "ProjectorFilter";
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
Projector::ProjectorFrameListener::~ProjectorFrameListener()
|
|
{
|
|
this->RemovePassFromMaterials();
|
|
|
|
if (this->filterNode)
|
|
{
|
|
this->filterNode->detachObject(this->filterFrustum);
|
|
this->node->removeAndDestroyChild(this->filterNodeName);
|
|
this->filterNode = NULL;
|
|
}
|
|
|
|
if (this->node)
|
|
{
|
|
this->node->detachObject(this->frustum);
|
|
this->visual->GetSceneNode()->removeAndDestroyChild(this->nodeName);
|
|
this->node = NULL;
|
|
}
|
|
|
|
delete this->frustum;
|
|
delete this->filterFrustum;
|
|
this->frustum = NULL;
|
|
this->filterFrustum = NULL;
|
|
|
|
if (this->projectorQuery)
|
|
this->sceneMgr->destroyQuery(this->projectorQuery);
|
|
|
|
this->visual.reset();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::Init(VisualPtr _visual,
|
|
const std::string &_textureName,
|
|
double _near,
|
|
double _far,
|
|
double _fov)
|
|
{
|
|
if (this->initialized)
|
|
return;
|
|
|
|
if (_textureName.empty())
|
|
{
|
|
gzerr << "Projector is missing a texture\n";
|
|
return;
|
|
}
|
|
|
|
this->visual = _visual;
|
|
|
|
this->nodeName = this->visual->GetName() + "_Projector";
|
|
this->filterNodeName = this->visual->GetName() + "_ProjectorFilter";
|
|
|
|
this->frustum = new Ogre::Frustum();
|
|
this->filterFrustum = new Ogre::Frustum();
|
|
this->filterFrustum->setProjectionType(Ogre::PT_ORTHOGRAPHIC);
|
|
|
|
this->sceneMgr = this->visual->GetScene()->OgreSceneManager();
|
|
this->projectorQuery = this->sceneMgr->createPlaneBoundedVolumeQuery(
|
|
Ogre::PlaneBoundedVolumeList());
|
|
|
|
this->SetSceneNode();
|
|
this->SetTexture(_textureName);
|
|
this->SetFrustumClipDistance(_near, _far);
|
|
this->SetFrustumFOV(_fov);
|
|
|
|
this->initialized = true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
bool Projector::ProjectorFrameListener::frameStarted(
|
|
const Ogre::FrameEvent &/*_evt*/)
|
|
{
|
|
if (!this->initialized || !this->enabled || this->textureName.empty())
|
|
return true;
|
|
|
|
this->AddPassToVisibleMaterials();
|
|
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::SetEnabled(bool _enabled)
|
|
{
|
|
this->enabled = _enabled;
|
|
if (!this->enabled)
|
|
this->RemovePassFromMaterials();
|
|
rendering::RTShaderSystem::Instance()->UpdateShaders();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::SetUsingShaders(bool _usingShaders)
|
|
{
|
|
this->usingShaders = _usingShaders;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::SetSceneNode()
|
|
{
|
|
if (this->filterNode)
|
|
{
|
|
this->filterNode->detachObject(this->filterFrustum);
|
|
this->node->removeAndDestroyChild(this->filterNodeName);
|
|
this->filterNode = NULL;
|
|
}
|
|
|
|
if (this->node)
|
|
{
|
|
this->node->detachObject(this->frustum);
|
|
this->visual->GetSceneNode()->removeAndDestroyChild(this->nodeName);
|
|
this->node = NULL;
|
|
}
|
|
|
|
this->node = this->visual->GetSceneNode()->createChildSceneNode(
|
|
this->nodeName);
|
|
|
|
this->filterNode = this->node->createChildSceneNode(
|
|
this->filterNodeName);
|
|
|
|
if (this->node)
|
|
this->node->attachObject(this->frustum);
|
|
|
|
if (this->filterNode)
|
|
{
|
|
this->filterNode->attachObject(this->filterFrustum);
|
|
this->filterNode->setOrientation(
|
|
Ogre::Quaternion(Ogre::Degree(90), Ogre::Vector3::UNIT_Y));
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::SetPose(const math::Pose &_pose)
|
|
{
|
|
Ogre::Quaternion ogreQuaternion = Conversions::Convert(_pose.rot);
|
|
Ogre::Vector3 ogreVec = Conversions::Convert(_pose.pos);
|
|
Ogre::Quaternion offsetQuaternion;
|
|
|
|
this->node->setPosition(ogreVec);
|
|
this->node->setOrientation(ogreQuaternion);
|
|
this->filterNode->setPosition(ogreVec);
|
|
|
|
offsetQuaternion = Ogre::Quaternion(Ogre::Degree(90), Ogre::Vector3::UNIT_Y);
|
|
this->filterNode->setOrientation(offsetQuaternion);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::SetTexture(
|
|
const std::string &_textureName)
|
|
{
|
|
this->textureName = _textureName;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::SetFrustumClipDistance(double _near,
|
|
double _far)
|
|
{
|
|
this->frustum->setNearClipDistance(_near);
|
|
this->filterFrustum->setNearClipDistance(_near);
|
|
this->frustum->setFarClipDistance(_far);
|
|
this->filterFrustum->setFarClipDistance(_far);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::SetFrustumFOV(double _fov)
|
|
{
|
|
this->frustum->setFOVy(Ogre::Radian(_fov));
|
|
this->filterFrustum->setFOVy(Ogre::Radian(_fov));
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::AddPassToAllMaterials()
|
|
{
|
|
std::list<std::string> allMaterials;
|
|
|
|
Ogre::SceneManager::MovableObjectIterator it =
|
|
this->sceneMgr->getMovableObjectIterator("Entity");
|
|
|
|
while (it.hasMoreElements())
|
|
{
|
|
Ogre::Entity* entity = dynamic_cast<Ogre::Entity*>(it.getNext());
|
|
if (entity && entity->getName().find("visual") != std::string::npos)
|
|
|
|
for (unsigned int i = 0; i < entity->getNumSubEntities(); i++)
|
|
{
|
|
allMaterials.push_back(entity->getSubEntity(i)->getMaterialName());
|
|
}
|
|
}
|
|
|
|
this->AddPassToMaterials(allMaterials);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::AddPassToVisibleMaterials()
|
|
{
|
|
std::list<std::string> newVisibleMaterials;
|
|
Ogre::PlaneBoundedVolumeList volumeList;
|
|
|
|
volumeList.push_back(this->frustum->getPlaneBoundedVolume());
|
|
|
|
this->projectorQuery->setVolumes(volumeList);
|
|
Ogre::SceneQueryResult result = this->projectorQuery->execute();
|
|
|
|
// Find all visible materials
|
|
Ogre::SceneQueryResultMovableList::iterator it;
|
|
for (it = result.movables.begin(); it != result.movables.end(); ++it)
|
|
{
|
|
Ogre::Entity *entity = dynamic_cast<Ogre::Entity*>(*it);
|
|
if (entity && entity->getName().find("visual") != std::string::npos)
|
|
{
|
|
for (unsigned int i = 0; i < entity->getNumSubEntities(); i++)
|
|
{
|
|
newVisibleMaterials.push_back(
|
|
entity->getSubEntity(i)->getMaterialName());
|
|
}
|
|
}
|
|
}
|
|
|
|
this->AddPassToMaterials(newVisibleMaterials);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::AddPassToMaterials(
|
|
std::list<std::string> &_matList)
|
|
{
|
|
_matList.remove("");
|
|
_matList.unique();
|
|
|
|
std::string invisibleMaterial;
|
|
std::list<std::string>::iterator visibleMaterial;
|
|
|
|
// Loop through all existing passes, removing those for materials
|
|
// not in the newlist and skipping pass creation for those in the
|
|
// newlist that have already been created
|
|
OgrePassMapIterator used = projectorTargets.begin();
|
|
while (used != projectorTargets.end())
|
|
{
|
|
visibleMaterial = std::find(_matList.begin(), _matList.end(), used->first);
|
|
|
|
// Remove the pass if it applies to a material not in the new list
|
|
if (visibleMaterial == _matList.end())
|
|
{
|
|
invisibleMaterial = used->first;
|
|
++used;
|
|
this->RemovePassFromMaterial(invisibleMaterial);
|
|
}
|
|
// Otherwise remove it from the list of passes to be added
|
|
else
|
|
{
|
|
_matList.remove(used->first);
|
|
++used;
|
|
}
|
|
}
|
|
|
|
if (!_matList.empty())
|
|
{
|
|
// Add pass for new materials
|
|
while (!_matList.empty())
|
|
{
|
|
this->AddPassToMaterial(_matList.front());
|
|
_matList.erase(_matList.begin());
|
|
}
|
|
|
|
RTShaderSystem::Instance()->UpdateShaders();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::AddPassToMaterial(
|
|
const std::string &_matName)
|
|
{
|
|
if (this->projectorTargets.find(_matName) != this->projectorTargets.end())
|
|
{
|
|
return;
|
|
}
|
|
|
|
Ogre::MaterialPtr mat = static_cast<Ogre::MaterialPtr>(
|
|
Ogre::MaterialManager::getSingleton().getByName(_matName));
|
|
Ogre::Pass *pass = mat->getTechnique(0)->createPass();
|
|
|
|
if (this->usingShaders)
|
|
{
|
|
Ogre::Matrix4 viewProj = this->frustum->getProjectionMatrix() *
|
|
this->frustum->getViewMatrix();
|
|
|
|
pass->setVertexProgram("Gazebo/TextureProjectionVP");
|
|
|
|
// pass->setFragmentProgram("GazeboWorlds/TexProjectionFP");
|
|
Ogre::GpuProgramParametersSharedPtr vsParams =
|
|
pass->getVertexProgramParameters();
|
|
|
|
Ogre::GpuProgramParametersSharedPtr psParams =
|
|
pass->getFragmentProgramParameters();
|
|
|
|
// vsParams->setNamedAutoConstant(
|
|
// "worldViewProjMatrix",
|
|
// GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
|
|
// vsParams->setNamedAutoConstant(
|
|
// "worldMatrix",GpuProgramParameters::ACT_WORLD_MATRIX);
|
|
// vsParams->setNamedConstant("texViewProjMatrix", viewProj);
|
|
|
|
vsParams->setNamedAutoConstant(
|
|
"worldMatrix", Ogre::GpuProgramParameters::ACT_WORLD_MATRIX);
|
|
|
|
vsParams->setNamedConstant("texProjMatrix", viewProj);
|
|
|
|
// psParams->setNamedConstant("projMap", viewProj);
|
|
|
|
pass->setVertexProgramParameters(vsParams);
|
|
|
|
// pass->setFragmentProgramParameters(psParams);
|
|
}
|
|
|
|
pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
|
|
pass->setDepthBias(1);
|
|
pass->setLightingEnabled(false);
|
|
|
|
Ogre::TextureUnitState *texState =
|
|
pass->createTextureUnitState(this->textureName);
|
|
texState->setProjectiveTexturing(true, this->frustum);
|
|
texState->setTextureAddressingMode(Ogre::TextureUnitState::TAM_BORDER);
|
|
texState->setTextureFiltering(Ogre::TFO_ANISOTROPIC);
|
|
texState->setTextureBorderColour(Ogre::ColourValue(0.0, 0.0, 0.0, 0.0));
|
|
texState->setColourOperation(Ogre::LBO_ALPHA_BLEND);
|
|
|
|
texState = pass->createTextureUnitState("projection_filter.png");
|
|
texState->setProjectiveTexturing(true, this->filterFrustum);
|
|
texState->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP);
|
|
texState->setTextureFiltering(Ogre::TFO_NONE);
|
|
|
|
this->projectorTargets[_matName] = pass;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::RemovePassFromMaterials()
|
|
{
|
|
for (OgrePassMap::const_iterator it = this->projectorTargets.begin();
|
|
it != this->projectorTargets.end(); ++it)
|
|
{
|
|
it->second->getParent()->removePass(it->second->getIndex());
|
|
}
|
|
this->projectorTargets.clear();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::ProjectorFrameListener::RemovePassFromMaterial(
|
|
const std::string &_matName)
|
|
{
|
|
this->projectorTargets[_matName]->getParent()->removePass(
|
|
this->projectorTargets[_matName]->getIndex());
|
|
this->projectorTargets.erase(this->projectorTargets.find(_matName));
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Projector::OnMsg(ConstProjectorPtr &_msg)
|
|
{
|
|
if (_msg->has_enabled())
|
|
this->projector.SetEnabled(_msg->enabled());
|
|
}
|