435 lines
14 KiB
C++
435 lines
14 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.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#ifndef _WIN32
|
||
|
#include <dirent.h>
|
||
|
#else
|
||
|
// Ensure that Winsock2.h is included before Windows.h, which can get
|
||
|
// pulled in by anybody (e.g., Boost).
|
||
|
#include <Winsock2.h>
|
||
|
#include "gazebo/common/win_dirent.h"
|
||
|
#endif
|
||
|
|
||
|
#include "gazebo/common/Events.hh"
|
||
|
#include "gazebo/common/Console.hh"
|
||
|
|
||
|
#include "gazebo/rendering/ogre_gazebo.h"
|
||
|
#include "gazebo/rendering/Conversions.hh"
|
||
|
#include "gazebo/rendering/Scene.hh"
|
||
|
#include "gazebo/rendering/DepthCamera.hh"
|
||
|
#include "gazebo/rendering/DepthCameraPrivate.hh"
|
||
|
|
||
|
using namespace gazebo;
|
||
|
using namespace rendering;
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
DepthCamera::DepthCamera(const std::string &_namePrefix, ScenePtr _scene,
|
||
|
bool _autoRender)
|
||
|
: Camera(_namePrefix, _scene, _autoRender),
|
||
|
dataPtr(new DepthCameraPrivate)
|
||
|
{
|
||
|
this->depthTarget = NULL;
|
||
|
this->dataPtr->depthBuffer = NULL;
|
||
|
this->dataPtr->depthMaterial = NULL;
|
||
|
this->dataPtr->pcdTarget = NULL;
|
||
|
this->dataPtr->pcdBuffer = NULL;
|
||
|
this->dataPtr->pcdMaterial = NULL;
|
||
|
this->dataPtr->outputPoints = false;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
DepthCamera::~DepthCamera()
|
||
|
{
|
||
|
if (this->dataPtr->depthBuffer)
|
||
|
delete [] this->dataPtr->depthBuffer;
|
||
|
|
||
|
if (this->dataPtr->pcdBuffer)
|
||
|
delete [] this->dataPtr->pcdBuffer;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
void DepthCamera::Load(sdf::ElementPtr _sdf)
|
||
|
{
|
||
|
Camera::Load(_sdf);
|
||
|
this->dataPtr->outputPoints =
|
||
|
(_sdf->GetElement("depth_camera")->Get<std::string>("output")
|
||
|
== "points");
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
void DepthCamera::Load()
|
||
|
{
|
||
|
Camera::Load();
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
void DepthCamera::Init()
|
||
|
{
|
||
|
Camera::Init();
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
void DepthCamera::Fini()
|
||
|
{
|
||
|
Camera::Fini();
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
void DepthCamera::CreateDepthTexture(const std::string &_textureName)
|
||
|
{
|
||
|
// Create the depth buffer
|
||
|
std::string depthMaterialName = this->Name() + "_RttMat_Camera_Depth";
|
||
|
|
||
|
this->depthTexture = Ogre::TextureManager::getSingleton().createManual(
|
||
|
_textureName,
|
||
|
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||
|
Ogre::TEX_TYPE_2D,
|
||
|
this->ImageWidth(), this->ImageHeight(), 0,
|
||
|
Ogre::PF_FLOAT32_R,
|
||
|
Ogre::TU_RENDERTARGET).getPointer();
|
||
|
|
||
|
this->depthTarget = this->depthTexture->getBuffer()->getRenderTarget();
|
||
|
this->depthTarget->setAutoUpdated(false);
|
||
|
|
||
|
this->SetDepthTarget(this->depthTarget);
|
||
|
|
||
|
this->depthViewport->setOverlaysEnabled(false);
|
||
|
this->depthViewport->setBackgroundColour(
|
||
|
Ogre::ColourValue(Ogre::ColourValue(0, 0, 0)));
|
||
|
|
||
|
// Create materials for all the render textures.
|
||
|
Ogre::MaterialPtr matPtr = Ogre::MaterialManager::getSingleton().create(
|
||
|
depthMaterialName,
|
||
|
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||
|
|
||
|
matPtr->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
|
||
|
matPtr->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
|
||
|
matPtr->getTechnique(0)->getPass(0)->setLightingEnabled(false);
|
||
|
|
||
|
matPtr->getTechnique(0)->getPass(0)->createTextureUnitState(
|
||
|
_textureName);
|
||
|
|
||
|
this->dataPtr->depthMaterial = (Ogre::Material*)(
|
||
|
Ogre::MaterialManager::getSingleton().getByName("Gazebo/DepthMap").get());
|
||
|
|
||
|
this->dataPtr->depthMaterial->load();
|
||
|
|
||
|
if (this->dataPtr->outputPoints)
|
||
|
{
|
||
|
this->dataPtr->pcdTexture =
|
||
|
Ogre::TextureManager::getSingleton().createManual(
|
||
|
_textureName + "_pcd",
|
||
|
"General",
|
||
|
Ogre::TEX_TYPE_2D,
|
||
|
this->ImageWidth(), this->ImageHeight(), 0,
|
||
|
Ogre::PF_FLOAT32_RGBA,
|
||
|
Ogre::TU_RENDERTARGET).getPointer();
|
||
|
|
||
|
this->dataPtr->pcdTarget =
|
||
|
this->dataPtr->pcdTexture->getBuffer()->getRenderTarget();
|
||
|
this->dataPtr->pcdTarget->setAutoUpdated(false);
|
||
|
|
||
|
this->dataPtr->pcdViewport =
|
||
|
this->dataPtr->pcdTarget->addViewport(this->camera);
|
||
|
this->dataPtr->pcdViewport->setClearEveryFrame(true);
|
||
|
this->dataPtr->pcdViewport->setBackgroundColour(
|
||
|
Conversions::Convert(this->scene->BackgroundColor()));
|
||
|
this->dataPtr->pcdViewport->setOverlaysEnabled(false);
|
||
|
this->dataPtr->pcdViewport->setVisibilityMask(
|
||
|
GZ_VISIBILITY_ALL & ~(GZ_VISIBILITY_GUI | GZ_VISIBILITY_SELECTABLE));
|
||
|
|
||
|
this->dataPtr->pcdMaterial = (Ogre::Material*)(
|
||
|
Ogre::MaterialManager::getSingleton().getByName("Gazebo/XYZPoints").get());
|
||
|
|
||
|
this->dataPtr->pcdMaterial->getTechnique(0)->getPass(0)->
|
||
|
createTextureUnitState(this->renderTexture->getName());
|
||
|
|
||
|
this->dataPtr->pcdMaterial->load();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
// Create a custom render queue invocation sequence for the depth
|
||
|
// render texture
|
||
|
Ogre::RenderQueueInvocationSequence* invocationSequence =
|
||
|
Ogre::Root::getSingleton().createRenderQueueInvocationSequence(_textureName
|
||
|
+ "_DepthMap");
|
||
|
|
||
|
// Add a render queue invocation to the sequence, and disable shadows for it
|
||
|
Ogre::RenderQueueInvocation* invocation =
|
||
|
invocationSequence->add(Ogre::RENDER_QUEUE_MAIN, _textureName + "_main");
|
||
|
invocation->setSuppressShadows(true);
|
||
|
|
||
|
// Set the render queue invocation sequence for the depth render texture
|
||
|
// viewport
|
||
|
this->depthViewport->setRenderQueueInvocationSequenceName(
|
||
|
_textureName + "_DepthMap");
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
void DepthCamera::PostRender()
|
||
|
{
|
||
|
this->depthTarget->swapBuffers();
|
||
|
if (this->dataPtr->outputPoints)
|
||
|
this->dataPtr->pcdTarget->swapBuffers();
|
||
|
|
||
|
if (this->newData && this->captureData)
|
||
|
{
|
||
|
unsigned int width = this->ImageWidth();
|
||
|
unsigned int height = this->ImageHeight();
|
||
|
|
||
|
if (!this->dataPtr->outputPoints)
|
||
|
{
|
||
|
Ogre::HardwarePixelBufferSharedPtr pixelBuffer;
|
||
|
|
||
|
// Get access to the buffer and make an image and write it to file
|
||
|
pixelBuffer = this->depthTexture->getBuffer();
|
||
|
|
||
|
size_t size = Ogre::PixelUtil::getMemorySize(width, height, 1,
|
||
|
Ogre::PF_FLOAT32_R);
|
||
|
|
||
|
// Blit the depth buffer if needed
|
||
|
if (!this->dataPtr->depthBuffer)
|
||
|
this->dataPtr->depthBuffer = new float[size];
|
||
|
|
||
|
Ogre::PixelBox dstBox(width, height,
|
||
|
1, Ogre::PF_FLOAT32_R, this->dataPtr->depthBuffer);
|
||
|
|
||
|
pixelBuffer->lock(Ogre::HardwarePixelBuffer::HBL_NORMAL);
|
||
|
pixelBuffer->blitToMemory(dstBox);
|
||
|
pixelBuffer->unlock(); // FIXME: do we need to lock/unlock still?
|
||
|
|
||
|
this->dataPtr->newDepthFrame(
|
||
|
this->dataPtr->depthBuffer, width, height, 1, "FLOAT32");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Ogre::HardwarePixelBufferSharedPtr pcdPixelBuffer;
|
||
|
|
||
|
// Get access to the buffer and make an image and write it to file
|
||
|
pcdPixelBuffer = this->dataPtr->pcdTexture->getBuffer();
|
||
|
|
||
|
// Blit the depth buffer if needed
|
||
|
if (!this->dataPtr->pcdBuffer)
|
||
|
this->dataPtr->pcdBuffer = new float[width * height * 4];
|
||
|
|
||
|
memset(this->dataPtr->pcdBuffer, 0, width * height * 4);
|
||
|
|
||
|
Ogre::Box pcd_src_box(0, 0, width, height);
|
||
|
Ogre::PixelBox pcd_dst_box(width, height,
|
||
|
1, Ogre::PF_FLOAT32_RGBA, this->dataPtr->pcdBuffer);
|
||
|
|
||
|
pcdPixelBuffer->lock(Ogre::HardwarePixelBuffer::HBL_NORMAL);
|
||
|
pcdPixelBuffer->blitToMemory(pcd_src_box, pcd_dst_box);
|
||
|
pcdPixelBuffer->unlock();
|
||
|
|
||
|
this->dataPtr->newRGBPointCloud(
|
||
|
this->dataPtr->pcdBuffer, width, height, 1, "RGBPOINTS");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// also new image frame for camera texture
|
||
|
Camera::PostRender();
|
||
|
|
||
|
this->newData = false;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
void DepthCamera::UpdateRenderTarget(Ogre::RenderTarget *_target,
|
||
|
Ogre::Material *_material, const std::string &_matName)
|
||
|
{
|
||
|
Ogre::RenderSystem *renderSys;
|
||
|
Ogre::Viewport *vp = NULL;
|
||
|
Ogre::SceneManager *sceneMgr = this->scene->OgreSceneManager();
|
||
|
Ogre::Pass *pass;
|
||
|
|
||
|
renderSys = this->scene->OgreSceneManager()->getDestinationRenderSystem();
|
||
|
// Get pointer to the material pass
|
||
|
pass = _material->getBestTechnique()->getPass(0);
|
||
|
|
||
|
// Render the depth texture
|
||
|
// OgreSceneManager::_render function automatically sets farClip to 0.
|
||
|
// Which normally equates to infinite distance. We don't want this. So
|
||
|
// we have to set the distance every time.
|
||
|
this->OgreCamera()->setFarClipDistance(this->FarClip());
|
||
|
|
||
|
Ogre::AutoParamDataSource autoParamDataSource;
|
||
|
|
||
|
vp = _target->getViewport(0);
|
||
|
|
||
|
// return 0 in case no renderable object is inside frustrum
|
||
|
vp->setBackgroundColour(Ogre::ColourValue(Ogre::ColourValue(0, 0, 0)));
|
||
|
|
||
|
Ogre::CompositorManager::getSingleton().setCompositorEnabled(
|
||
|
vp, _matName, true);
|
||
|
|
||
|
// Need this line to render the ground plane. No idea why it's necessary.
|
||
|
renderSys->_setViewport(vp);
|
||
|
sceneMgr->_setPass(pass, true, false);
|
||
|
autoParamDataSource.setCurrentPass(pass);
|
||
|
autoParamDataSource.setCurrentViewport(vp);
|
||
|
autoParamDataSource.setCurrentRenderTarget(_target);
|
||
|
autoParamDataSource.setCurrentSceneManager(sceneMgr);
|
||
|
autoParamDataSource.setCurrentCamera(this->OgreCamera(), true);
|
||
|
|
||
|
renderSys->setLightingEnabled(false);
|
||
|
renderSys->_setFog(Ogre::FOG_NONE);
|
||
|
|
||
|
// These two lines don't seem to do anything useful
|
||
|
renderSys->_setProjectionMatrix(
|
||
|
this->OgreCamera()->getProjectionMatrixRS());
|
||
|
renderSys->_setViewMatrix(this->OgreCamera()->getViewMatrix(true));
|
||
|
|
||
|
#if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR == 6
|
||
|
pass->_updateAutoParamsNoLights(&autoParamDataSource);
|
||
|
#else
|
||
|
pass->_updateAutoParams(&autoParamDataSource, 1);
|
||
|
#endif
|
||
|
|
||
|
// NOTE: We MUST bind parameters AFTER updating the autos
|
||
|
if (pass->hasVertexProgram())
|
||
|
{
|
||
|
renderSys->bindGpuProgram(
|
||
|
pass->getVertexProgram()->_getBindingDelegate());
|
||
|
|
||
|
#if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR == 6
|
||
|
renderSys->bindGpuProgramParameters(Ogre::GPT_VERTEX_PROGRAM,
|
||
|
pass->getVertexProgramParameters());
|
||
|
#else
|
||
|
renderSys->bindGpuProgramParameters(Ogre::GPT_VERTEX_PROGRAM,
|
||
|
pass->getVertexProgramParameters(), 1);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (pass->hasFragmentProgram())
|
||
|
{
|
||
|
renderSys->bindGpuProgram(
|
||
|
pass->getFragmentProgram()->_getBindingDelegate());
|
||
|
|
||
|
#if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR == 6
|
||
|
renderSys->bindGpuProgramParameters(Ogre::GPT_FRAGMENT_PROGRAM,
|
||
|
pass->getFragmentProgramParameters());
|
||
|
#else
|
||
|
renderSys->bindGpuProgramParameters(Ogre::GPT_FRAGMENT_PROGRAM,
|
||
|
pass->getFragmentProgramParameters(), 1);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
void DepthCamera::RenderImpl()
|
||
|
{
|
||
|
Ogre::SceneManager *sceneMgr = this->scene->OgreSceneManager();
|
||
|
|
||
|
Ogre::ShadowTechnique shadowTech = sceneMgr->getShadowTechnique();
|
||
|
|
||
|
sceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_NONE);
|
||
|
sceneMgr->_suppressRenderStateChanges(true);
|
||
|
|
||
|
this->UpdateRenderTarget(this->depthTarget,
|
||
|
this->dataPtr->depthMaterial, "Gazebo/DepthMap");
|
||
|
|
||
|
// Does actual rendering
|
||
|
this->depthTarget->update(false);
|
||
|
|
||
|
sceneMgr->_suppressRenderStateChanges(false);
|
||
|
sceneMgr->setShadowTechnique(shadowTech);
|
||
|
|
||
|
// for camera image
|
||
|
Camera::RenderImpl();
|
||
|
|
||
|
if (this->dataPtr->outputPoints)
|
||
|
{
|
||
|
sceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_NONE);
|
||
|
sceneMgr->_suppressRenderStateChanges(true);
|
||
|
|
||
|
this->UpdateRenderTarget(this->dataPtr->pcdTarget,
|
||
|
this->dataPtr->pcdMaterial, "Gazebo/XYZPoints");
|
||
|
|
||
|
this->dataPtr->pcdTarget->update(false);
|
||
|
|
||
|
sceneMgr->_suppressRenderStateChanges(false);
|
||
|
sceneMgr->setShadowTechnique(shadowTech);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
const float* DepthCamera::GetDepthData()
|
||
|
{
|
||
|
return this->DepthData();
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
const float* DepthCamera::DepthData() const
|
||
|
{
|
||
|
return this->dataPtr->depthBuffer;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
void DepthCamera::SetDepthTarget(Ogre::RenderTarget *_target)
|
||
|
{
|
||
|
this->depthTarget = _target;
|
||
|
|
||
|
if (this->depthTarget)
|
||
|
{
|
||
|
// Setup the viewport to use the texture
|
||
|
this->depthViewport = this->depthTarget->addViewport(this->camera);
|
||
|
this->depthViewport->setClearEveryFrame(true);
|
||
|
this->depthViewport->setBackgroundColour(
|
||
|
Conversions::Convert(this->scene->BackgroundColor()));
|
||
|
this->depthViewport->setVisibilityMask(
|
||
|
GZ_VISIBILITY_ALL & ~(GZ_VISIBILITY_GUI | GZ_VISIBILITY_SELECTABLE));
|
||
|
|
||
|
double ratio = static_cast<double>(this->depthViewport->getActualWidth()) /
|
||
|
static_cast<double>(this->depthViewport->getActualHeight());
|
||
|
|
||
|
double hfov = this->HFOV().Radian();
|
||
|
double vfov = 2.0 * atan(tan(hfov / 2.0) / ratio);
|
||
|
// gzerr << "debug " << hfov << " " << vfov << " " << ratio << "\n";
|
||
|
this->camera->setAspectRatio(ratio);
|
||
|
this->camera->setFOVy(Ogre::Radian(vfov));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
event::ConnectionPtr DepthCamera::ConnectNewDepthFrame(
|
||
|
std::function<void (const float *, unsigned int, unsigned int, unsigned int,
|
||
|
const std::string &)> _subscriber)
|
||
|
{
|
||
|
return this->dataPtr->newDepthFrame.Connect(_subscriber);
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
void DepthCamera::DisconnectNewDepthFrame(event::ConnectionPtr &_c)
|
||
|
{
|
||
|
this->dataPtr->newDepthFrame.Disconnect(_c);
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
event::ConnectionPtr DepthCamera::ConnectNewRGBPointCloud(
|
||
|
std::function<void (const float *, unsigned int, unsigned int, unsigned int,
|
||
|
const std::string &)> _subscriber)
|
||
|
{
|
||
|
return this->dataPtr->newRGBPointCloud.Connect(_subscriber);
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////
|
||
|
void DepthCamera::DisconnectNewRGBPointCloud(event::ConnectionPtr &_c)
|
||
|
{
|
||
|
this->dataPtr->newRGBPointCloud.Disconnect(_c);
|
||
|
}
|