first commit
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
// Viewer.hxx -- alternative flightgear viewer application
|
||||
//
|
||||
// Copyright (C) 2009 - 2012 Mathias Froehlich
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "SlaveCamera.hxx"
|
||||
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
|
||||
#include "Viewer.hxx"
|
||||
|
||||
#if FG_HAVE_HLA
|
||||
#include "HLAViewerFederate.hxx"
|
||||
#include "HLAPerspectiveViewer.hxx"
|
||||
#endif
|
||||
|
||||
namespace fgviewer {
|
||||
|
||||
class NoUpdateCallback : public osg::NodeCallback {
|
||||
public:
|
||||
virtual ~NoUpdateCallback()
|
||||
{ }
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nodeVisitor)
|
||||
{ }
|
||||
};
|
||||
|
||||
SlaveCamera::SlaveCamera(const std::string& name) :
|
||||
_name(name),
|
||||
_viewport(new osg::Viewport())
|
||||
{
|
||||
_referencePointMap["lowerLeft"] = osg::Vec2(-1, -1);
|
||||
_referencePointMap["lowerRight"] = osg::Vec2(1, -1);
|
||||
_referencePointMap["upperRight"] = osg::Vec2(1, 1);
|
||||
_referencePointMap["upperLeft"] = osg::Vec2(-1, 1);
|
||||
}
|
||||
|
||||
SlaveCamera::~SlaveCamera()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
SlaveCamera::setDrawableName(const std::string& drawableName)
|
||||
{
|
||||
if (_camera.valid())
|
||||
return false;
|
||||
_drawableName = drawableName;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SlaveCamera::setViewport(const SGVec4i& viewport)
|
||||
{
|
||||
_viewport->setViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
_frustum.setAspectRatio(getAspectRatio());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SlaveCamera::setViewOffset(const osg::Matrix& viewOffset)
|
||||
{
|
||||
_viewOffset = viewOffset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SlaveCamera::setViewOffsetDeg(double headingDeg, double pitchDeg, double rollDeg)
|
||||
{
|
||||
osg::Matrix viewOffset = osg::Matrix::identity();
|
||||
viewOffset.postMultRotate(osg::Quat(SGMiscd::deg2rad(headingDeg), osg::Vec3(0, 1, 0)));
|
||||
viewOffset.postMultRotate(osg::Quat(SGMiscd::deg2rad(pitchDeg), osg::Vec3(-1, 0, 0)));
|
||||
viewOffset.postMultRotate(osg::Quat(SGMiscd::deg2rad(rollDeg), osg::Vec3(0, 0, 1)));
|
||||
return setViewOffset(viewOffset);
|
||||
}
|
||||
|
||||
bool
|
||||
SlaveCamera::setFrustum(const Frustum& frustum)
|
||||
{
|
||||
_frustum = frustum;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SlaveCamera::setFustumByFieldOfViewDeg(double fieldOfViewDeg)
|
||||
{
|
||||
Frustum frustum(getAspectRatio());
|
||||
frustum.setFieldOfViewDeg(fieldOfViewDeg);
|
||||
setFrustum(frustum);
|
||||
}
|
||||
|
||||
bool
|
||||
SlaveCamera::setRelativeFrustum(const std::string names[2], const SlaveCamera& referenceCameraData,
|
||||
const std::string referenceNames[2])
|
||||
{
|
||||
// Track the way from one projection space to the other:
|
||||
// We want
|
||||
// P = T2*S*T*P0
|
||||
// where P0 is the projection template sensible for the given window size,
|
||||
// S a scale matrix and T is a translation matrix.
|
||||
// We need to determine T and S so that the reference points in the parents
|
||||
// projection space match the two reference points in this cameras projection space.
|
||||
|
||||
// Starting from the parents camera projection space, we get into this cameras
|
||||
// projection space by the transform matrix:
|
||||
// P*R*inv(pP*pR) = T2*S*T*P0*R*inv(pP*pR)
|
||||
// So, at first compute that matrix without T2*S*T and determine S and T* from that
|
||||
|
||||
// The initial projeciton matrix to build upon
|
||||
osg::Matrix P = Frustum(getAspectRatio()).getMatrix();
|
||||
|
||||
osg::Matrix R = getViewOffset();
|
||||
osg::Matrix pP = referenceCameraData.getFrustum().getMatrix();
|
||||
osg::Matrix pR = referenceCameraData.getViewOffset();
|
||||
|
||||
// Transform from the reference cameras projection space into this cameras eye space.
|
||||
osg::Matrix pPtoEye = osg::Matrix::inverse(pR*pP)*R;
|
||||
|
||||
osg::Vec2 pRef[2] = {
|
||||
referenceCameraData.getProjectionReferencePoint(referenceNames[0]),
|
||||
referenceCameraData.getProjectionReferencePoint(referenceNames[1])
|
||||
};
|
||||
|
||||
// The first reference point transformed to this cameras projection space
|
||||
osg::Vec3d pRefInThis0 = P.preMult(pPtoEye.preMult(osg::Vec3d(pRef[0], 1)));
|
||||
// Translate this proejction matrix so that the first reference point is at the origin
|
||||
P.postMultTranslate(-pRefInThis0);
|
||||
|
||||
// Transform the second reference point and get the scaling correct.
|
||||
osg::Vec3d pRefInThis1 = P.preMult(pPtoEye.preMult(osg::Vec3d(pRef[1], 1)));
|
||||
double s = osg::Vec2d(pRefInThis1[0], pRefInThis1[1]).length();
|
||||
if (s <= std::numeric_limits<double>::min())
|
||||
return false;
|
||||
osg::Vec2 ref[2] = {
|
||||
getProjectionReferencePoint(names[0]),
|
||||
getProjectionReferencePoint(names[1])
|
||||
};
|
||||
s = (ref[0] - ref[1]).length()/s;
|
||||
P.postMultScale(osg::Vec3d(s, s, 1));
|
||||
|
||||
// The first reference point still maps to the origin in this projection space.
|
||||
// Translate the origin to the desired first reference point.
|
||||
P.postMultTranslate(osg::Vec3d(ref[0], 1));
|
||||
|
||||
// Now osg::Matrix::inverse(pR*pP)*R*P should map pRef[i] exactly onto ref[i] for i = 0, 1.
|
||||
// Note that osg::Matrix::inverse(pR*pP)*R*P should exactly map pRef[0] at the near plane
|
||||
// to ref[0] at the near plane. The far plane is not taken care of.
|
||||
|
||||
Frustum frustum;
|
||||
if (!frustum.setMatrix(P))
|
||||
return false;
|
||||
|
||||
return setFrustum(frustum);
|
||||
}
|
||||
|
||||
void
|
||||
SlaveCamera::setProjectionReferencePoint(const std::string& name, const osg::Vec2& point)
|
||||
{
|
||||
_referencePointMap[name] = point;
|
||||
}
|
||||
|
||||
osg::Vec2
|
||||
SlaveCamera::getProjectionReferencePoint(const std::string& name) const
|
||||
{
|
||||
NameReferencePointMap::const_iterator i = _referencePointMap.find(name);
|
||||
if (i != _referencePointMap.end())
|
||||
return i->second;
|
||||
return osg::Vec2(0, 0);
|
||||
}
|
||||
|
||||
void
|
||||
SlaveCamera::setMonitorProjectionReferences(double width, double height,
|
||||
double bezelTop, double bezelBottom,
|
||||
double bezelLeft, double bezelRight)
|
||||
{
|
||||
double left = 1 + 2*bezelLeft/width;
|
||||
double right = 1 + 2*bezelRight/width;
|
||||
|
||||
double bottom = 1 + 2*bezelBottom/height;
|
||||
double top = 1 + 2*bezelTop/height;
|
||||
|
||||
setProjectionReferencePoint("lowerLeft", osg::Vec2(-left, -bottom));
|
||||
setProjectionReferencePoint("lowerRight", osg::Vec2(right, -bottom));
|
||||
setProjectionReferencePoint("upperRight", osg::Vec2(right, top));
|
||||
setProjectionReferencePoint("upperLeft", osg::Vec2(-left, top));
|
||||
}
|
||||
|
||||
osg::Vec3
|
||||
SlaveCamera::getLeftEyeOffset(const Viewer& viewer) const
|
||||
{
|
||||
#if FG_HAVE_HLA
|
||||
const HLAViewerFederate* viewerFederate = viewer.getViewerFederate();
|
||||
if (!viewerFederate)
|
||||
return osg::Vec3(0, 0, 0);
|
||||
const HLAPerspectiveViewer* perspectiveViewer = viewerFederate->getViewer();
|
||||
if (!perspectiveViewer)
|
||||
return osg::Vec3(0, 0, 0);
|
||||
return toOsg(perspectiveViewer->getLeftEyeOffset());
|
||||
#else
|
||||
return osg::Vec3(0, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
osg::Vec3
|
||||
SlaveCamera::getRightEyeOffset(const Viewer& viewer) const
|
||||
{
|
||||
#if FG_HAVE_HLA
|
||||
const HLAViewerFederate* viewerFederate = viewer.getViewerFederate();
|
||||
if (!viewerFederate)
|
||||
return osg::Vec3(0, 0, 0);
|
||||
const HLAPerspectiveViewer* perspectiveViewer = viewerFederate->getViewer();
|
||||
if (!perspectiveViewer)
|
||||
return osg::Vec3(0, 0, 0);
|
||||
return toOsg(perspectiveViewer->getRightEyeOffset());
|
||||
#else
|
||||
return osg::Vec3(0, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
double
|
||||
SlaveCamera::getZoomFactor(const Viewer& viewer) const
|
||||
{
|
||||
#if FG_HAVE_HLA
|
||||
const HLAViewerFederate* viewerFederate = viewer.getViewerFederate();
|
||||
if (!viewerFederate)
|
||||
return 1;
|
||||
const HLAPerspectiveViewer* perspectiveViewer = viewerFederate->getViewer();
|
||||
if (!perspectiveViewer)
|
||||
return 1;
|
||||
return perspectiveViewer->getZoomFactor();
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
osg::Matrix
|
||||
SlaveCamera::getEffectiveViewOffset(const Viewer& viewer) const
|
||||
{
|
||||
// The eye offset in the master cameras coordinates.
|
||||
osg::Vec3 eyeOffset = getLeftEyeOffset(viewer);
|
||||
|
||||
// Transform the eye offset into this slaves coordinates
|
||||
eyeOffset = eyeOffset*getViewOffset();
|
||||
|
||||
// The slaves view matrix is composed of the master matrix
|
||||
osg::Matrix viewOffset = viewer.getCamera()->getViewMatrix();
|
||||
// ... its view offset ...
|
||||
viewOffset.postMult(getViewOffset());
|
||||
// ... and the inverse of the eye offset that is required
|
||||
// to keep the world at the same position wrt the projection walls
|
||||
viewOffset.postMultTranslate(-eyeOffset);
|
||||
return viewOffset;
|
||||
}
|
||||
|
||||
Frustum
|
||||
SlaveCamera::getEffectiveFrustum(const Viewer& viewer) const
|
||||
{
|
||||
// The eye offset in the master cameras coordinates.
|
||||
osg::Vec3 eyeOffset = getLeftEyeOffset(viewer);
|
||||
|
||||
// Transform the eye offset into this slaves coordinates
|
||||
eyeOffset = eyeOffset*getViewOffset();
|
||||
|
||||
/// FIXME read that from external
|
||||
osg::Vec3 zoomScaleCenter(0, 0, -1);
|
||||
double zoomFactor = getZoomFactor(viewer);
|
||||
|
||||
/// Transform into the local cameras orientation.
|
||||
zoomScaleCenter = getViewOffset().preMult(zoomScaleCenter);
|
||||
|
||||
// The unmodified frustum
|
||||
Frustum frustum = getFrustum();
|
||||
|
||||
// For unresized views this is a noop
|
||||
frustum.setAspectRatio(getAspectRatio());
|
||||
|
||||
// need to correct this for the eye position within the projection system
|
||||
frustum.translate(-eyeOffset);
|
||||
|
||||
// Scale the whole geometric extent of the projection surfaces by the zoom factor
|
||||
frustum.scale(1/zoomFactor, zoomScaleCenter);
|
||||
|
||||
return frustum;
|
||||
}
|
||||
|
||||
bool
|
||||
SlaveCamera::realize(Viewer& viewer)
|
||||
{
|
||||
if (_camera.valid())
|
||||
return false;
|
||||
_camera = _realizeImplementation(viewer);
|
||||
return _camera.valid();
|
||||
}
|
||||
|
||||
bool
|
||||
SlaveCamera::update(Viewer& viewer)
|
||||
{
|
||||
return _updateImplementation(viewer);
|
||||
}
|
||||
|
||||
osg::Camera*
|
||||
SlaveCamera::_realizeImplementation(Viewer& viewer)
|
||||
{
|
||||
Drawable* drawable = viewer.getDrawable(_drawableName);
|
||||
if (!drawable)
|
||||
return 0;
|
||||
osg::GraphicsContext* graphicsContext = drawable->getGraphicsContext();
|
||||
if (!graphicsContext)
|
||||
return 0;
|
||||
|
||||
osg::Camera* camera = new osg::Camera;
|
||||
camera->setName(getName());
|
||||
camera->setGraphicsContext(graphicsContext);
|
||||
camera->setViewport(_viewport.get());
|
||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||
|
||||
// Not seriously consider someting different
|
||||
camera->setDrawBuffer(GL_BACK);
|
||||
camera->setReadBuffer(GL_BACK);
|
||||
|
||||
camera->setUpdateCallback(new NoUpdateCallback);
|
||||
|
||||
return camera;
|
||||
}
|
||||
|
||||
bool
|
||||
SlaveCamera::_updateImplementation(Viewer& viewer)
|
||||
{
|
||||
if (!_camera.valid())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fgviewer
|
||||
Reference in New Issue
Block a user