556 lines
15 KiB
C++
556 lines
15 KiB
C++
/***************************************************************************
|
||
kdeglwidget.cpp - description
|
||
-------------------
|
||
begin : Tue Jul 16 2002
|
||
copyright : (C) 2002 by Christophe Teyssier
|
||
email : chris@teyssier.org
|
||
***************************************************************************/
|
||
|
||
/***************************************************************************
|
||
* *
|
||
* 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. *
|
||
* *
|
||
***************************************************************************/
|
||
|
||
#include <config.h>
|
||
|
||
#include <cassert>
|
||
#include <celengine/starbrowser.h>
|
||
|
||
#include <QCursor>
|
||
#include <QPaintDevice>
|
||
#include <QMouseEvent>
|
||
#include <QSettings>
|
||
#include <QMessageBox>
|
||
|
||
#ifndef DEBUG
|
||
# define G_DISABLE_ASSERT
|
||
#endif
|
||
|
||
#include "celengine/astro.h"
|
||
#include "celutil/filetype.h"
|
||
#include "celutil/gettext.h"
|
||
#include "celestia/celestiacore.h"
|
||
#include "celengine/simulation.h"
|
||
#ifdef USE_GLCONTEXT
|
||
#include "celengine/glcontext.h"
|
||
#endif
|
||
|
||
#include "qtglwidget.h"
|
||
|
||
#include <cmath>
|
||
#include <vector>
|
||
|
||
using namespace Qt;
|
||
|
||
|
||
const int DEFAULT_ORBIT_MASK = Body::Planet | Body::Moon | Body::Stellar;
|
||
|
||
const int DEFAULT_LABEL_MODE = 2176;
|
||
|
||
const float DEFAULT_AMBIENT_LIGHT_LEVEL = 0.1f;
|
||
|
||
const int DEFAULT_STARS_COLOR = ColorTable_Blackbody_D65;
|
||
|
||
const float DEFAULT_VISUAL_MAGNITUDE = 8.0f;
|
||
|
||
const Renderer::StarStyle DEFAULT_STAR_STYLE = Renderer::FuzzyPointStars;
|
||
|
||
const unsigned int DEFAULT_TEXTURE_RESOLUTION = medres;
|
||
|
||
|
||
CelestiaGlWidget::CelestiaGlWidget(QWidget* parent, const char* /* name */, CelestiaCore* core) :
|
||
QGLWidget(parent)
|
||
{
|
||
setFocusPolicy(Qt::ClickFocus);
|
||
|
||
appCore = core;
|
||
appRenderer = appCore->getRenderer();
|
||
appSim = appCore->getSimulation();
|
||
|
||
setCursor(QCursor(Qt::CrossCursor));
|
||
currentCursor = CelestiaCore::CrossCursor;
|
||
setMouseTracking(true);
|
||
|
||
cursorVisible = true;
|
||
}
|
||
|
||
|
||
/*!
|
||
Paint the box. The actual openGL commands for drawing the box are
|
||
performed here.
|
||
*/
|
||
|
||
void CelestiaGlWidget::paintGL()
|
||
{
|
||
appCore->draw();
|
||
}
|
||
|
||
|
||
#ifdef USE_GLCONTEXT
|
||
static GLContext::GLRenderPath getBestAvailableRenderPath(const GLContext& /*glc*/)
|
||
{
|
||
#if 0
|
||
const GLContext::GLRenderPath renderPaths[] = {
|
||
GLContext::GLPath_GLSL,
|
||
};
|
||
|
||
for (auto renderPath : renderPaths)
|
||
{
|
||
if (glc.renderPathSupported(renderPath))
|
||
return renderPath;
|
||
}
|
||
#endif
|
||
|
||
return GLContext::GLPath_GLSL;
|
||
}
|
||
#endif
|
||
|
||
|
||
/*!
|
||
Set up the OpenGL rendering state, and define display list
|
||
*/
|
||
|
||
void CelestiaGlWidget::initializeGL()
|
||
{
|
||
using namespace celestia;
|
||
if (!gl::init(appCore->getConfig()->ignoreGLExtensions) || !gl::checkVersion(gl::GL_2_1))
|
||
{
|
||
QMessageBox::critical(0, "Celestia", _("Celestia was unable to initialize OpenGLÂ 2.1."));
|
||
exit(1);
|
||
}
|
||
|
||
appCore->setScreenDpi(logicalDpiY() * devicePixelRatioF());
|
||
|
||
if (!appCore->initRenderer())
|
||
{
|
||
// cerr << "Failed to initialize renderer.\n";
|
||
exit(1);
|
||
}
|
||
|
||
appCore->tick();
|
||
|
||
// Read saved settings
|
||
QSettings settings;
|
||
appRenderer->setRenderFlags(settings.value("RenderFlags", static_cast<quint64>(Renderer::DefaultRenderFlags)).toULongLong());
|
||
appRenderer->setOrbitMask(settings.value("OrbitMask", DEFAULT_ORBIT_MASK).toInt());
|
||
appRenderer->setLabelMode(settings.value("LabelMode", DEFAULT_LABEL_MODE).toInt());
|
||
appRenderer->setAmbientLightLevel((float) settings.value("AmbientLightLevel", DEFAULT_AMBIENT_LIGHT_LEVEL).toDouble());
|
||
appRenderer->setStarStyle((Renderer::StarStyle) settings.value("StarStyle", DEFAULT_STAR_STYLE).toInt());
|
||
appRenderer->setResolution(settings.value("TextureResolution", DEFAULT_TEXTURE_RESOLUTION).toUInt());
|
||
|
||
if (settings.value("StarsColor", DEFAULT_STARS_COLOR).toInt() == 0)
|
||
appRenderer->setStarColorTable(GetStarColorTable(ColorTable_Enhanced));
|
||
else
|
||
appRenderer->setStarColorTable(GetStarColorTable(ColorTable_Blackbody_D65));
|
||
|
||
appCore->getSimulation()->setFaintestVisible((float) settings.value("Preferences/VisualMagnitude", DEFAULT_VISUAL_MAGNITUDE).toDouble());
|
||
|
||
// Read the saved render path
|
||
#ifdef USE_GLCONTEXT
|
||
GLContext::GLRenderPath bestPath = getBestAvailableRenderPath(*appRenderer->getGLContext());
|
||
GLContext::GLRenderPath savedPath = (GLContext::GLRenderPath) settings.value("RenderPath", bestPath).toInt();
|
||
|
||
// Use the saved path only if it's supported (otherwise a graphics card
|
||
// downgrade could cause Celestia to not function.)
|
||
GLContext::GLRenderPath usePath;
|
||
if (appRenderer->getGLContext()->renderPathSupported(savedPath))
|
||
usePath = savedPath;
|
||
else
|
||
usePath = bestPath;
|
||
|
||
appRenderer->getGLContext()->setRenderPath(usePath);
|
||
#endif
|
||
|
||
appRenderer->setSolarSystemMaxDistance(appCore->getConfig()->SolarSystemMaxDistance);
|
||
appRenderer->setShadowMapSize(appCore->getConfig()->ShadowMapSize);
|
||
}
|
||
|
||
|
||
void CelestiaGlWidget::resizeGL(int w, int h)
|
||
{
|
||
appCore->resize(w, h);
|
||
}
|
||
|
||
|
||
void CelestiaGlWidget::mouseMoveEvent(QMouseEvent* m)
|
||
{
|
||
float scale = devicePixelRatioF();
|
||
int x = (int)(m->x() * scale);
|
||
int y = (int)(m->y() * scale);
|
||
|
||
int buttons = 0;
|
||
if (m->buttons() & LeftButton)
|
||
buttons |= CelestiaCore::LeftButton;
|
||
if (m->buttons() & MiddleButton)
|
||
buttons |= CelestiaCore::MiddleButton;
|
||
if (m->buttons() & RightButton)
|
||
buttons |= CelestiaCore::RightButton;
|
||
if (m->modifiers() & ShiftModifier)
|
||
buttons |= CelestiaCore::ShiftKey;
|
||
if (m->modifiers() & ControlModifier)
|
||
buttons |= CelestiaCore::ControlKey;
|
||
|
||
#ifdef __APPLE__
|
||
// On the Mac, right dragging is be simulated with Option+left drag.
|
||
// We may want to enable this on other platforms, though it's mostly only helpful
|
||
// for users with single button mice.
|
||
if (m->modifiers() & AltModifier)
|
||
{
|
||
buttons &= ~CelestiaCore::LeftButton;
|
||
buttons |= CelestiaCore::RightButton;
|
||
}
|
||
#endif
|
||
|
||
if ((m->buttons() & (LeftButton | RightButton)) != 0)
|
||
{
|
||
if (cursorVisible)
|
||
{
|
||
// Hide the cursor.
|
||
setCursor(QCursor(Qt::BlankCursor));
|
||
cursorVisible = false;
|
||
|
||
// Save the cursor position
|
||
QPoint pt;
|
||
pt.setX(m->x());
|
||
pt.setY(m->y());
|
||
|
||
// Store a local and global location
|
||
saveLocalCursorPos = pt;
|
||
pt = mapToGlobal(pt);
|
||
saveGlobalCursorPos = pt;
|
||
}
|
||
|
||
// Calculate mouse delta from local coordinate then move it back to the saved location
|
||
appCore->mouseMove((m->x() - saveLocalCursorPos.rx()) * scale, (m->y() - saveLocalCursorPos.ry()) * scale, buttons);
|
||
QCursor::setPos(saveGlobalCursorPos);
|
||
}
|
||
else
|
||
{
|
||
appCore->mouseMove(x, y);
|
||
}
|
||
}
|
||
|
||
|
||
void CelestiaGlWidget::mousePressEvent( QMouseEvent* m )
|
||
{
|
||
float scale = devicePixelRatioF();
|
||
int x = (int)(m->x() * scale);
|
||
int y = (int)(m->y() * scale);
|
||
|
||
if (m->button() == LeftButton)
|
||
appCore->mouseButtonDown(x, y, CelestiaCore::LeftButton);
|
||
else if (m->button() == MiddleButton)
|
||
appCore->mouseButtonDown(x, y, CelestiaCore::MiddleButton);
|
||
else if (m->button() == RightButton)
|
||
appCore->mouseButtonDown(x, y, CelestiaCore::RightButton);
|
||
}
|
||
|
||
|
||
void CelestiaGlWidget::mouseReleaseEvent( QMouseEvent* m )
|
||
{
|
||
float scale = devicePixelRatioF();
|
||
int x = (int)(m->x() * scale);
|
||
int y = (int)(m->y() * scale);
|
||
|
||
if (m->button() == LeftButton)
|
||
{
|
||
if (!cursorVisible)
|
||
{
|
||
// Restore the cursor position and make it visible again.
|
||
setCursor(QCursor(Qt::CrossCursor));
|
||
cursorVisible = true;
|
||
QCursor::setPos(saveGlobalCursorPos);
|
||
}
|
||
appCore->mouseButtonUp(x, y, CelestiaCore::LeftButton);
|
||
}
|
||
else if (m->button() == MiddleButton)
|
||
{
|
||
appCore->mouseButtonUp(x, y, CelestiaCore::MiddleButton);
|
||
}
|
||
else if (m->button() == RightButton)
|
||
{
|
||
if (!cursorVisible)
|
||
{
|
||
// Restore the cursor position and make it visible again.
|
||
setCursor(QCursor(Qt::CrossCursor));
|
||
cursorVisible = true;
|
||
QCursor::setPos(saveGlobalCursorPos);
|
||
}
|
||
appCore->mouseButtonUp(x, y, CelestiaCore::RightButton);
|
||
}
|
||
}
|
||
|
||
|
||
void CelestiaGlWidget::wheelEvent( QWheelEvent* w )
|
||
{
|
||
QPoint numDegrees = w->angleDelta();
|
||
if (numDegrees.isNull() || numDegrees.y() == 0)
|
||
return;
|
||
|
||
if (numDegrees.y() > 0 )
|
||
{
|
||
appCore->mouseWheel(-1.0f, 0);
|
||
}
|
||
else
|
||
{
|
||
appCore->mouseWheel(1.0f, 0);
|
||
}
|
||
}
|
||
|
||
|
||
bool CelestiaGlWidget::handleSpecialKey(QKeyEvent* e, bool down)
|
||
{
|
||
int k = -1;
|
||
switch (e->key())
|
||
{
|
||
case Key_Up:
|
||
k = CelestiaCore::Key_Up;
|
||
break;
|
||
case Key_Down:
|
||
k = CelestiaCore::Key_Down;
|
||
break;
|
||
case Key_Left:
|
||
k = CelestiaCore::Key_Left;
|
||
break;
|
||
case Key_Right:
|
||
k = CelestiaCore::Key_Right;
|
||
break;
|
||
case Key_Home:
|
||
k = CelestiaCore::Key_Home;
|
||
break;
|
||
case Key_End:
|
||
k = CelestiaCore::Key_End;
|
||
break;
|
||
case Key_F1:
|
||
k = CelestiaCore::Key_F1;
|
||
break;
|
||
case Key_F2:
|
||
k = CelestiaCore::Key_F2;
|
||
break;
|
||
case Key_F3:
|
||
k = CelestiaCore::Key_F3;
|
||
break;
|
||
case Key_F4:
|
||
k = CelestiaCore::Key_F4;
|
||
break;
|
||
case Key_F5:
|
||
k = CelestiaCore::Key_F5;
|
||
break;
|
||
case Key_F6:
|
||
k = CelestiaCore::Key_F6;
|
||
break;
|
||
case Key_F7:
|
||
k = CelestiaCore::Key_F7;
|
||
break;
|
||
case Key_F11:
|
||
k = CelestiaCore::Key_F11;
|
||
break;
|
||
case Key_F12:
|
||
k = CelestiaCore::Key_F12;
|
||
break;
|
||
case Key_PageDown:
|
||
k = CelestiaCore::Key_PageDown;
|
||
break;
|
||
case Key_PageUp:
|
||
k = CelestiaCore::Key_PageUp;
|
||
break;
|
||
/* case Key_F10:
|
||
if (e->modifiers()& ShiftModifier)
|
||
k = CelestiaCore::Key_F10;
|
||
break;*/
|
||
case Key_0:
|
||
if (e->modifiers() & Qt::KeypadModifier)
|
||
k = CelestiaCore::Key_NumPad0;
|
||
break;
|
||
case Key_1:
|
||
if (e->modifiers() & Qt::KeypadModifier)
|
||
k = CelestiaCore::Key_NumPad1;
|
||
break;
|
||
case Key_2:
|
||
if (e->modifiers() & Qt::KeypadModifier)
|
||
k = CelestiaCore::Key_NumPad2;
|
||
break;
|
||
case Key_3:
|
||
if (e->modifiers() & Qt::KeypadModifier)
|
||
k = CelestiaCore::Key_NumPad3;
|
||
break;
|
||
case Key_4:
|
||
if (e->modifiers() & Qt::KeypadModifier)
|
||
k = CelestiaCore::Key_NumPad4;
|
||
break;
|
||
case Key_5:
|
||
if (e->modifiers() & Qt::KeypadModifier)
|
||
k = CelestiaCore::Key_NumPad5;
|
||
break;
|
||
case Key_6:
|
||
if (e->modifiers() & Qt::KeypadModifier)
|
||
k = CelestiaCore::Key_NumPad6;
|
||
break;
|
||
case Key_7:
|
||
if (e->modifiers() & Qt::KeypadModifier)
|
||
k = CelestiaCore::Key_NumPad7;
|
||
break;
|
||
case Key_8:
|
||
if (e->modifiers() & Qt::KeypadModifier)
|
||
k = CelestiaCore::Key_NumPad8;
|
||
break;
|
||
case Key_9:
|
||
if (e->modifiers() & Qt::KeypadModifier)
|
||
k = CelestiaCore::Key_NumPad9;
|
||
break;
|
||
case Qt::Key_A:
|
||
if (e->modifiers() == NoModifier)
|
||
k = 'A';
|
||
break;
|
||
case Qt::Key_Z:
|
||
if (e->modifiers() == NoModifier)
|
||
k = 'Z';
|
||
break;
|
||
}
|
||
|
||
if (k >= 0)
|
||
{
|
||
int buttons = 0;
|
||
if (e->modifiers() & ShiftModifier)
|
||
buttons |= CelestiaCore::ShiftKey;
|
||
|
||
if (down)
|
||
appCore->keyDown(k, buttons);
|
||
else
|
||
appCore->keyUp(k);
|
||
return (k < 'A' || k > 'Z');
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
|
||
void CelestiaGlWidget::keyPressEvent( QKeyEvent* e )
|
||
{
|
||
int modifiers = 0;
|
||
if (e->modifiers() & ShiftModifier)
|
||
modifiers |= CelestiaCore::ShiftKey;
|
||
if (e->modifiers() & ControlModifier)
|
||
modifiers |= CelestiaCore::ControlKey;
|
||
|
||
switch (e->key())
|
||
{
|
||
case Key_Escape:
|
||
appCore->charEntered('\033');
|
||
break;
|
||
case Key_Backtab:
|
||
appCore->charEntered(CelestiaCore::Key_BackTab);
|
||
break;
|
||
default:
|
||
if (!handleSpecialKey(e, true))
|
||
{
|
||
if (!e->text().isEmpty())
|
||
{
|
||
QString input = e->text();
|
||
#ifdef __APPLE__
|
||
// Taken from the macOS project
|
||
if (input.length() == 1)
|
||
{
|
||
uint16_t c = input.at(0).unicode();
|
||
if (c == 0x7f /* NSDeleteCharacter */)
|
||
input.replace(0, 1, QChar((uint16_t)0x08) /* NSBackspaceCharacter */); // delete = backspace
|
||
else if (c == 0x19 /* NSBackTabCharacter */)
|
||
input.replace(0, 1, QChar((uint16_t)0x7f) /* NSDeleteCharacter */);
|
||
}
|
||
#endif
|
||
appCore->charEntered(input.toUtf8().data(), modifiers);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void CelestiaGlWidget::keyReleaseEvent( QKeyEvent* e )
|
||
{
|
||
handleSpecialKey(e, false);
|
||
}
|
||
|
||
|
||
void CelestiaGlWidget::setCursorShape(CelestiaCore::CursorShape shape)
|
||
{
|
||
Qt::CursorShape cursor;
|
||
if (currentCursor != shape)
|
||
{
|
||
switch(shape)
|
||
{
|
||
case CelestiaCore::ArrowCursor:
|
||
cursor = Qt::ArrowCursor;
|
||
break;
|
||
case CelestiaCore::UpArrowCursor:
|
||
cursor = Qt::UpArrowCursor;
|
||
break;
|
||
case CelestiaCore::CrossCursor:
|
||
cursor = Qt::CrossCursor;
|
||
break;
|
||
case CelestiaCore::InvertedCrossCursor:
|
||
cursor = Qt::CrossCursor;
|
||
break;
|
||
case CelestiaCore::WaitCursor:
|
||
cursor = Qt::WaitCursor;
|
||
break;
|
||
case CelestiaCore::BusyCursor:
|
||
cursor = Qt::WaitCursor;
|
||
break;
|
||
case CelestiaCore::IbeamCursor:
|
||
cursor = Qt::IBeamCursor;
|
||
break;
|
||
case CelestiaCore::SizeVerCursor:
|
||
cursor = Qt::SizeVerCursor;
|
||
break;
|
||
case CelestiaCore::SizeHorCursor:
|
||
cursor = Qt::SizeHorCursor;
|
||
break;
|
||
case CelestiaCore::SizeBDiagCursor:
|
||
cursor = Qt::SizeBDiagCursor;
|
||
break;
|
||
case CelestiaCore::SizeFDiagCursor:
|
||
cursor = Qt::SizeFDiagCursor;
|
||
break;
|
||
case CelestiaCore::SizeAllCursor:
|
||
cursor = Qt::SizeAllCursor;
|
||
break;
|
||
case CelestiaCore::SplitVCursor:
|
||
cursor = Qt::SplitVCursor;
|
||
break;
|
||
case CelestiaCore::SplitHCursor:
|
||
cursor = Qt::SplitHCursor;
|
||
break;
|
||
case CelestiaCore::PointingHandCursor:
|
||
cursor = Qt::PointingHandCursor;
|
||
break;
|
||
case CelestiaCore::ForbiddenCursor:
|
||
cursor = Qt::ForbiddenCursor;
|
||
break;
|
||
case CelestiaCore::WhatsThisCursor:
|
||
cursor = Qt::WhatsThisCursor;
|
||
break;
|
||
default:
|
||
cursor = Qt::CrossCursor;
|
||
break;
|
||
}
|
||
setCursor(QCursor(cursor));
|
||
currentCursor = shape;
|
||
}
|
||
}
|
||
|
||
|
||
CelestiaCore::CursorShape CelestiaGlWidget::getCursorShape() const
|
||
{
|
||
return currentCursor;
|
||
}
|
||
|
||
|
||
QSize CelestiaGlWidget::sizeHint() const
|
||
{
|
||
return QSize(640, 480);
|
||
}
|