Files
flightgear/3rdparty/osgXR/src/Mirror.cpp
2022-10-20 20:29:11 +08:00

142 lines
4.3 KiB
C++

// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2021 James Hogan <james@albanarts.com>
#include <osgXR/Manager>
#include <osgXR/Mirror>
#include "XRState.h"
#include <osg/PolygonMode>
using namespace osgXR;
Mirror::Mirror(Manager *manager, osg::Camera *camera) :
_manager(manager),
_camera(camera),
_mirrorSettings(manager->_getSettings()->getMirrorSettings())
{
}
Mirror::~Mirror()
{
}
void Mirror::_init()
{
_camera->setAllowEventFocus(false);
_camera->setViewMatrix(osg::Matrix::identity());
_camera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1));
// Find the mirror settings
MirrorSettings *mirrorSettings = &_mirrorSettings;
// but fall back to the manager's mirror settings
if (mirrorSettings->getMirrorMode() == MirrorSettings::MIRROR_AUTOMATIC)
mirrorSettings = &_manager->_getSettings()->getMirrorSettings();
switch (mirrorSettings->getMirrorMode())
{
case MirrorSettings::MIRROR_NONE:
// Draw nothing, but still clear the viewport
_camera->setClearMask(GL_COLOR_BUFFER_BIT);
break;
case MirrorSettings::MIRROR_AUTOMATIC:
// Fall-through: Default to MIRROR_SINGLE
case MirrorSettings::MIRROR_SINGLE:
{
int viewIndex = mirrorSettings->getMirrorViewIndex();
if (viewIndex < 0)
viewIndex = 0;
setupQuad(viewIndex, 0.0f, 1.0f);
}
break;
case MirrorSettings::MIRROR_LEFT_RIGHT:
for (unsigned int viewIndex = 0; viewIndex < 2; ++viewIndex)
setupQuad(viewIndex, 0.5f * viewIndex, 0.5f);
break;
}
}
namespace {
class MirrorPreDrawCallback : public osg::Camera::DrawCallback
{
public:
MirrorPreDrawCallback(osg::ref_ptr<XRState> xrState,
osg::ref_ptr<osg::StateSet> stateSet,
unsigned int viewIndex) :
_xrState(xrState),
_stateSet(stateSet),
_viewIndex(viewIndex)
{
}
void operator()(osg::RenderInfo& renderInfo) const override
{
const osg::FrameStamp *stamp = renderInfo.getState()->getFrameStamp();
_stateSet->setTextureAttributeAndModes(0,
_xrState->getViewTexture(_viewIndex, stamp));
}
protected:
osg::observer_ptr<XRState> _xrState;
osg::ref_ptr<osg::StateSet> _stateSet;
unsigned int _viewIndex;
};
class MirrorPostDrawCallback : public osg::Camera::DrawCallback
{
public:
MirrorPostDrawCallback(osg::ref_ptr<osg::StateSet> stateSet) :
_stateSet(stateSet)
{
}
void operator()(osg::RenderInfo& renderInfo) const override
{
_stateSet->removeTextureAttribute(0, osg::StateAttribute::Type::TEXTURE);
}
protected:
osg::ref_ptr<osg::StateSet> _stateSet;
};
}
void Mirror::setupQuad(unsigned int viewIndex,
float x, float w)
{
XRState *xrState = _manager->_getXrState();
if (viewIndex >= xrState->getViewCount())
return;
// Build an always-visible quad to draw the view texture on
osg::ref_ptr<osg::Geode> quad = new osg::Geode;
quad->setCullingActive(false);
XRState::TextureRect rect = xrState->getViewTextureRect(viewIndex);
quad->addDrawable(osg::createTexturedQuadGeometry(
osg::Vec3(x, 0.0f, 0.0f),
osg::Vec3(w, 0.0f, 0.0f),
osg::Vec3(0.0f, 1.0f, 0.0f),
rect.x, rect.y,
rect.x + rect.width, rect.y + rect.height));
osg::ref_ptr<osg::StateSet> state = quad->getOrCreateStateSet();
int forceOff = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED;
int forceOn = osg::StateAttribute::ON | osg::StateAttribute::PROTECTED;
state->setMode(GL_LIGHTING, forceOff);
state->setMode(GL_DEPTH_TEST, forceOff);
state->setMode(GL_FRAMEBUFFER_SRGB, forceOn);
_camera->addChild(quad);
// Set a callback so we can switch the texture to the active swapchain image
_camera->addPreDrawCallback(new MirrorPreDrawCallback(_manager->_getXrState(),
state, viewIndex));
_camera->addPostDrawCallback(new MirrorPostDrawCallback(state));
}