You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/apps/opencs/view/render/scenewidget.cpp

400 lines
13 KiB
C++

#include "scenewidget.hpp"
#include <QEvent>
#include <QResizeEvent>
#include <QTimer>
#include <QLayout>
#include <extern/osgQt/GraphicsWindowQt>
#include <osg/GraphicsContext>
#include <osgViewer/CompositeViewer>
#include <osgViewer/ViewerEventHandlers>
#include <osg/LightModel>
#include <osg/Material>
#include <osg/Version>
#include <components/resource/scenemanager.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include "../widget/scenetoolmode.hpp"
#include "../../model/prefs/state.hpp"
#include "../../model/prefs/shortcut.hpp"
#include "../../model/prefs/shortcuteventhandler.hpp"
#include "lighting.hpp"
#include "mask.hpp"
#include "cameracontroller.hpp"
namespace CSVRender
{
RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f)
, mRootNode(0)
{
osgViewer::CompositeViewer& viewer = CompositeViewer::get();
osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
//ds->setNumMultiSamples(8);
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
traits->windowName = "";
traits->windowDecoration = true;
traits->x = 0;
traits->y = 0;
traits->width = width();
traits->height = height();
traits->doubleBuffer = true;
traits->alpha = ds->getMinimumNumAlphaBits();
traits->stencil = ds->getMinimumNumStencilBits();
traits->sampleBuffers = ds->getMultiSamples();
traits->samples = ds->getNumMultiSamples();
// Doesn't make much sense as we're running on demand updates, and there seems to be a bug with the refresh rate when running multiple QGLWidgets
traits->vsync = false;
mView = new osgViewer::View;
osg::ref_ptr<osgQt::GraphicsWindowQt> window = new osgQt::GraphicsWindowQt(traits.get());
QLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(window->getGLWidget());
setLayout(layout);
mView->getCamera()->setGraphicsContext(window);
mView->getCamera()->setClearColor( osg::Vec4(0.2, 0.2, 0.6, 1.0) );
mView->getCamera()->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) );
mView->getCamera()->setProjectionMatrixAsPerspective(30.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 1.0f, 10000.0f );
SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager;
lightMgr->setStartLight(1);
lightMgr->setLightingMask(Mask_Lighting);
mRootNode = lightMgr;
mView->getCamera()->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
mView->getCamera()->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
osg::ref_ptr<osg::Material> defaultMat (new osg::Material);
defaultMat->setColorMode(osg::Material::OFF);
defaultMat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
mView->getCamera()->getOrCreateStateSet()->setAttribute(defaultMat);
mView->setSceneData(mRootNode);
// Add ability to signal osg to show its statistics for debugging purposes
mView->addEventHandler(new osgViewer::StatsHandler);
mView->getCamera()->setCullMask(~(Mask_UpdateVisitor));
viewer.addView(mView);
viewer.setDone(false);
viewer.realize();
}
RenderWidget::~RenderWidget()
{
CompositeViewer::get().removeView(mView);
}
void RenderWidget::flagAsModified()
{
mView->requestRedraw();
}
void RenderWidget::setVisibilityMask(int mask)
{
mView->getCamera()->setCullMask(mask | Mask_ParticleSystem | Mask_Lighting);
}
osg::Camera *RenderWidget::getCamera()
{
return mView->getCamera();
}
void RenderWidget::toggleRenderStats()
{
osgViewer::GraphicsWindow* window =
static_cast<osgViewer::GraphicsWindow*>(mView->getCamera()->getGraphicsContext());
window->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_S);
window->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_S);
}
// --------------------------------------------------
CompositeViewer::CompositeViewer()
: mSimulationTime(0.0)
{
#if QT_VERSION >= 0x050000
// Qt5 is currently crashing and reporting "Cannot make QOpenGLContext current in a different thread" when the viewer is run multi-threaded, this is regression from Qt4
osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::SingleThreaded;
#else
osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::DrawThreadPerContext;
#endif
setThreadingModel(threadingModel);
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,5)
setUseConfigureAffinity(false);
#endif
// disable the default setting of viewer.done() by pressing Escape.
setKeyEventSetsDone(0);
// Only render when the camera position changed, or content flagged dirty
//setRunFrameScheme(osgViewer::ViewerBase::ON_DEMAND);
setRunFrameScheme(osgViewer::ViewerBase::CONTINUOUS);
connect( &mTimer, SIGNAL(timeout()), this, SLOT(update()) );
mTimer.start( 10 );
}
CompositeViewer &CompositeViewer::get()
{
static CompositeViewer sThis;
return sThis;
}
void CompositeViewer::update()
{
double dt = mFrameTimer.time_s();
mFrameTimer.setStartTick();
emit simulationUpdated(dt);
mSimulationTime += dt;
frame(mSimulationTime);
}
// ---------------------------------------------------
SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSystem, QWidget *parent, Qt::WindowFlags f,
bool retrieveInput)
: RenderWidget(parent, f)
, mResourceSystem(resourceSystem)
, mLighting(NULL)
, mHasDefaultAmbient(false)
, mPrevMouseX(0)
, mPrevMouseY(0)
, mCamPositionSet(false)
{
mFreeCamControl = new FreeCameraController(this);
mOrbitCamControl = new OrbitCameraController(this);
mCurrentCamControl = mFreeCamControl;
mOrbitCamControl->setPickingMask(Mask_Reference | Mask_Terrain);
// we handle lighting manually
mView->setLightingMode(osgViewer::View::NO_LIGHT);
setLighting(&mLightingDay);
mResourceSystem->getSceneManager()->setParticleSystemMask(Mask_ParticleSystem);
// Recieve mouse move event even if mouse button is not pressed
setMouseTracking(true);
setFocusPolicy(Qt::ClickFocus);
connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),
this, SLOT (settingChanged (const CSMPrefs::Setting *)));
// TODO update this outside of the constructor where virtual methods can be used
if (retrieveInput)
{
CSMPrefs::get()["3D Scene Input"].update();
CSMPrefs::get()["Tooltips"].update();
}
connect (&CompositeViewer::get(), SIGNAL (simulationUpdated(double)), this, SLOT (update(double)));
// Shortcuts
CSMPrefs::Shortcut* focusToolbarShortcut = new CSMPrefs::Shortcut("scene-focus-toolbar", this);
connect(focusToolbarShortcut, SIGNAL(activated()), this, SIGNAL(focusToolbarRequest()));
CSMPrefs::Shortcut* renderStatsShortcut = new CSMPrefs::Shortcut("scene-render-stats", this);
connect(renderStatsShortcut, SIGNAL(activated()), this, SLOT(toggleRenderStats()));
}
SceneWidget::~SceneWidget()
{
// Since we're holding on to the scene templates past the existence of this graphics context, we'll need to manually release the created objects
mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
}
void SceneWidget::setLighting(Lighting *lighting)
{
if (mLighting)
mLighting->deactivate();
mLighting = lighting;
mLighting->activate (mRootNode);
osg::Vec4f ambient = mLighting->getAmbientColour(mHasDefaultAmbient ? &mDefaultAmbient : 0);
setAmbient(ambient);
flagAsModified();
}
void SceneWidget::setAmbient(const osg::Vec4f& ambient)
{
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;
lightmodel->setAmbientIntensity(ambient);
stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
stateset->setMode(GL_LIGHT0, osg::StateAttribute::ON);
stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON);
mRootNode->setStateSet(stateset);
}
void SceneWidget::selectLightingMode (const std::string& mode)
{
if (mode=="day")
setLighting (&mLightingDay);
else if (mode=="night")
setLighting (&mLightingNight);
else if (mode=="bright")
setLighting (&mLightingBright);
}
CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent)
{
CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Lighting Mode");
/// \todo replace icons
tool->addButton (":scenetoolbar/day", "day",
"Day"
"<ul><li>Cell specific ambient in interiors</li>"
"<li>Low ambient in exteriors</li>"
"<li>Strong directional light source</li>"
"<li>This mode closely resembles day time in-game</li></ul>");
tool->addButton (":scenetoolbar/night", "night",
"Night"
"<ul><li>Cell specific ambient in interiors</li>"
"<li>Low ambient in exteriors</li>"
"<li>Weak directional light source</li>"
"<li>This mode closely resembles night time in-game</li></ul>");
tool->addButton (":scenetoolbar/bright", "bright",
"Bright"
"<ul><li>Maximum ambient</li>"
"<li>Strong directional light source</li></ul>");
connect (tool, SIGNAL (modeChanged (const std::string&)),
this, SLOT (selectLightingMode (const std::string&)));
return tool;
}
void SceneWidget::setDefaultAmbient (const osg::Vec4f& colour)
{
mDefaultAmbient = colour;
mHasDefaultAmbient = true;
setAmbient(mLighting->getAmbientColour(&mDefaultAmbient));
}
void SceneWidget::mouseMoveEvent (QMouseEvent *event)
{
mCurrentCamControl->handleMouseMoveEvent(event->x() - mPrevMouseX, event->y() - mPrevMouseY);
mPrevMouseX = event->x();
mPrevMouseY = event->y();
}
void SceneWidget::wheelEvent(QWheelEvent *event)
{
mCurrentCamControl->handleMouseScrollEvent(event->delta());
}
void SceneWidget::update(double dt)
{
if (mCamPositionSet)
{
mCurrentCamControl->update(dt);
}
else
{
mCurrentCamControl->setup(mRootNode, Mask_Reference | Mask_Terrain, CameraController::WorldUp);
mCamPositionSet = true;
}
}
void SceneWidget::settingChanged (const CSMPrefs::Setting *setting)
{
if (*setting=="3D Scene Input/p-navi-free-sensitivity")
{
mFreeCamControl->setCameraSensitivity(setting->toDouble());
}
else if (*setting=="3D Scene Input/p-navi-orbit-sensitivity")
{
mOrbitCamControl->setCameraSensitivity(setting->toDouble());
}
else if (*setting=="3D Scene Input/p-navi-free-invert")
{
mFreeCamControl->setInverted(setting->isTrue());
}
else if (*setting=="3D Scene Input/p-navi-orbit-invert")
{
mOrbitCamControl->setInverted(setting->isTrue());
}
else if (*setting=="3D Scene Input/s-navi-sensitivity")
{
mFreeCamControl->setSecondaryMovementMultiplier(setting->toDouble());
mOrbitCamControl->setSecondaryMovementMultiplier(setting->toDouble());
}
else if (*setting=="3D Scene Input/navi-wheel-factor")
{
mFreeCamControl->setWheelMovementMultiplier(setting->toDouble());
mOrbitCamControl->setWheelMovementMultiplier(setting->toDouble());
}
else if (*setting=="3D Scene Input/navi-free-lin-speed")
{
mFreeCamControl->setLinearSpeed(setting->toDouble());
}
else if (*setting=="3D Scene Input/navi-free-rot-speed")
{
mFreeCamControl->setRotationalSpeed(setting->toDouble());
}
else if (*setting=="3D Scene Input/navi-free-speed-mult")
{
mFreeCamControl->setSpeedMultiplier(setting->toDouble());
}
else if (*setting=="3D Scene Input/navi-orbit-rot-speed")
{
mOrbitCamControl->setOrbitSpeed(setting->toDouble());
}
else if (*setting=="3D Scene Input/navi-orbit-speed-mult")
{
mOrbitCamControl->setOrbitSpeedMultiplier(setting->toDouble());
}
}
void SceneWidget::selectNavigationMode (const std::string& mode)
{
if (mode=="1st")
{
mCurrentCamControl->setCamera(NULL);
mCurrentCamControl = mFreeCamControl;
mFreeCamControl->setCamera(getCamera());
mFreeCamControl->fixUpAxis(CameraController::WorldUp);
}
else if (mode=="free")
{
mCurrentCamControl->setCamera(NULL);
mCurrentCamControl = mFreeCamControl;
mFreeCamControl->setCamera(getCamera());
mFreeCamControl->unfixUpAxis();
}
else if (mode=="orbit")
{
mCurrentCamControl->setCamera(NULL);
mCurrentCamControl = mOrbitCamControl;
mOrbitCamControl->setCamera(getCamera());
}
}
}