Scenery3d.cpp   Scenery3d.cpp 
/* /*
* Stellarium Scenery3d Plug-in * Stellarium Scenery3d Plug-in
* *
* Copyright (C) 2011-2015 Simon Parzer, Peter Neubauer, Georg Zotti, Andre i Borza, Florian Schaukowitsch * Copyright (C) 2011 Simon Parzer, Peter Neubauer, Georg Zotti, Andrei Bor za
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
*/ */
#include <QtGlobal> #include <QDebug>
#include <QSettings>
#include <QString>
#include <QDir>
#include <QFile>
#include <QKeyEvent>
#include <QTimer>
#include <QOpenGLShaderProgram>
#include <QtConcurrent>
#include "GLFuncs.hpp" #include <stdexcept>
#include "Scenery3d.hpp"
#include "Scenery3d.hpp"
#include "Scenery3dRemoteControlService.hpp"
#include "S3DRenderer.hpp"
#include "S3DScene.hpp"
#include "Scenery3dDialog.hpp"
#include "StoredViewDialog.hpp"
#include "StelApp.hpp" #include "StelApp.hpp"
#include "StelCore.hpp" #include "StelCore.hpp"
#include "StelActionMgr.hpp"
#include "StelMovementMgr.hpp"
#include "StelGui.hpp"
#include "StelGuiItems.hpp"
#include "StelFileMgr.hpp" #include "StelFileMgr.hpp"
#include "StelPainter.hpp" #include "StelPainter.hpp"
#include "StelModuleMgr.hpp" #include "StelModuleMgr.hpp"
#include "StelMovementMgr.hpp"
#include "StelTranslator.hpp" #include "StelTranslator.hpp"
#include "StelUtils.hpp"
#include "LandscapeMgr.hpp" #include "LandscapeMgr.hpp"
#include "SolarSystem.hpp" #include "StelMainView.hpp"
#include "Scenery3dMgr.hpp"
#include "AABB.hpp"
#include <QKeyEvent>
#include <QSettings>
#include <stdexcept>
#include <cmath>
#include <QOpenGLShaderProgram>
#define GET_GLERROR() StelOpenGL::checkGLErrors(__FILE__,__LINE__);
//macro for easier uniform setting Q_LOGGING_CATEGORY(scenery3d,"stel.plugin.scenery3d")
#define SET_UNIFORM(shd,uni,val) shd->setUniformValue(shaderManager.uniform
Location(shd,uni),val)
static const float LUNAR_BRIGHTNESS_FACTOR=0.2f; #define S3D_CONFIG_PREFIX QString("Scenery3d")
static const float VENUS_BRIGHTNESS_FACTOR=0.005f;
#ifndef QT_OPENGL_ES_2 Scenery3d::Scenery3d() :
//this is the place where this is initialized renderer(Q_NULLPTR),
GLExtFuncs* glExtFuncs; flagEnabled(false),
#endif cleanedUp(false),
movementKeyInput(0.0,0.0,0.0),
#ifdef _MSC_VER oldProjectionType(StelCore::ProjectionPerspective),
//disable a stupid warning about array value-initialization loadCancel(false),
#pragma warning(disable : 4351) progressBar(Q_NULLPTR),
#endif currentLoadScene(),
currentScene(Q_NULLPTR),
Scenery3d::Scenery3d(Scenery3dMgr* parent) currentLoadFuture(this)
: parent(parent), currentScene(), loadingScene(), {
//sun(NULL), moon(NULL), venus(NULL), setObjectName("Scenery3d");
supportsGSCubemapping(false), supportsShadows(false), supportsShadowF scenery3dDialog = new Scenery3dDialog();
iltering(false), isANGLE(false), maximumFramebufferSize(0), storedViewDialog = new StoredViewDialog();
torchBrightness(0.5f), torchRange(5.0f), textEnabled(false), debugEna
bled(false), fixShadowData(false), font.setPixelSize(16);
simpleShadows(false), fullCubemapShadows(false), cubemappingMode(S3DE messageFader.setDuration(500);
num::CM_TEXTURES), //set it to 6 textures as a safe default (Cubemap should messageTimer = new QTimer(this);
work on ANGLE, but does not...) messageTimer->setInterval(2000);
reinitCubemapping(true), reinitShadowmapping(true), messageTimer->setSingleShot(true);
loadCancel(false), connect(messageTimer, &QTimer::timeout, this, &Scenery3d::clearMessa
cubemapSize(1024),shadowmapSize(1024), ge);
absolutePosition(0.0, 0.0, 0.0), moveVector(0.0, 0.0, 0.0), movement( connect(&currentLoadFuture,&QFutureWatcherBase::finished, this, &Sce
0.0f,0.0f,0.0f), eye_height(0.0f), nery3d::loadSceneCompleted);
core(NULL), landscapeMgr(NULL), heightmap(NULL), heightmapLoad(NULL)
, connect(this, &Scenery3d::progressReport, this, &Scenery3d::progress
mainViewUp(0.0, 0.0, 1.0), mainViewDir(1.0, 0.0, 0.0), viewPos(0.0, 0 Receive, Qt::QueuedConnection);
.0, 0.0),
drawnTriangles(0), drawnModels(0), materialSwitches(0), shaderSwitche //get the global configuration object
s(0), conf = StelApp::getInstance().getSettings();
requiresCubemap(false), cubemappingUsedLastFrame(false),
lazyDrawing(false), updateOnlyDominantOnMoving(true), updateSecondDom core = StelApp::getInstance().getCore();
inantOnMoving(true), needsMovementEndUpdate(false), mvMgr = GETSTELMODULE(StelMovementMgr);
needsCubemapUpdate(true), needsMovementUpdate(false), lazyInterval(2.
0), lastCubemapUpdate(0.0), lastCubemapUpdateRealTime(0), lastMovementEndRe //create scenery3d object
alTime(0), renderer = new S3DRenderer();
cubeMapCubeTex(0), cubeMapCubeDepth(0), cubeMapTex(), cubeRB(0), domi connect(renderer, SIGNAL(message(QString)), this, SLOT(showMessage(Q
nantFace(0), secondDominantFace(1), cubeFBO(0), cubeSideFBO(), cubeMappingC String)));
reated(false),
cubeVertexBuffer(QOpenGLBuffer::VertexBuffer), transformedCubeVertexB
uffer(QOpenGLBuffer::VertexBuffer), cubeIndexBuffer(QOpenGLBuffer::IndexBuf
fer), cubeIndexCount(0),
lightOrthoNear(0.1f), lightOrthoFar(1000.0f), parallaxScale(0.015f)
{
#ifndef NDEBUG
qDebug()<<"Scenery3d constructor...";
#endif
//the arrays should all contain only zeroes
Q_ASSERT(cubeMapTex[0]==0);
Q_ASSERT(cubeSideFBO[0]==0);
shaderParameters.openglES = false;
shaderParameters.shadowTransform = false;
shaderParameters.pixelLighting = false;
shaderParameters.bump = false;
shaderParameters.shadows = false;
shaderParameters.shadowFilterQuality = S3DEnum::SFQ_LOW;
shaderParameters.pcss = false;
shaderParameters.geometryShader = false;
shaderParameters.torchLight = false;
shaderParameters.frustumSplits = 0;
shaderParameters.hwShadowSamplers = false;
sceneBoundingBox = AABB(Vec3f(0.0f), Vec3f(0.0f));
debugTextFont.setFamily("Courier");
debugTextFont.setPixelSize(16);
#ifndef NDEBUG
qDebug()<<"Scenery3d constructor...done";
#endif
} }
Scenery3d::~Scenery3d() Scenery3d::~Scenery3d()
{ {
if (heightmap) { if(!cleanedUp)
delete heightmap; deinit();
heightmap = NULL;
}
if(heightmapLoad)
{
delete heightmapLoad;
heightmapLoad = NULL;
}
cubeVertexBuffer.destroy();
cubeIndexBuffer.destroy();
deleteShadowmapping();
deleteCubemapping();
#ifndef QT_OPENGL_ES_2
//delete extension functions
delete glExtFuncs;
#endif
}
bool Scenery3d::loadScene(const SceneInfo &scene)
{
loadingScene = scene;
if(loadCancel)
return false;
//setup some state
QMatrix4x4 zRot2Grid = (loadingScene.zRotateMatrix*loadingScene.obj2
gridMatrix).convertToQMatrix();
OBJ::vertexOrder objVertexOrder=OBJ::XYZ;
if (loadingScene.vertexOrder.compare("XZY") == 0) objVertexOrder=OBJ
::XZY;
else if (loadingScene.vertexOrder.compare("YXZ") == 0) objVertexOrde
r=OBJ::YXZ;
else if (loadingScene.vertexOrder.compare("YZX") == 0) objVertexOrde
r=OBJ::YZX;
else if (loadingScene.vertexOrder.compare("ZXY") == 0) objVertexOrde
r=OBJ::ZXY;
else if (loadingScene.vertexOrder.compare("ZYX") == 0) objVertexOrde
r=OBJ::ZYX;
parent->updateProgress(q_("Loading model..."),1,0,6);
//load model
objModelLoad.reset(new OBJ());
QString modelFile = StelFileMgr::findFile( loadingScene.fullPath+ "/
" + loadingScene.modelScenery);
qDebug()<<"[Scenery3d] Loading scene from "<<modelFile;
if(!objModelLoad->load(modelFile, objVertexOrder, loadingScene.scene
ryGenerateNormals))
{
qCritical()<<"[Scenery3d] Failed to load OBJ file.";
return false;
}
if(loadCancel)
return false;
parent->updateProgress(q_("Transforming model..."),2,0,6);
//transform the vertices of the model to match the grid
objModelLoad->transform( zRot2Grid );
if(loadCancel)
return false;
if(loadingScene.modelGround.isEmpty())
groundModelLoad = objModelLoad;
else if (loadingScene.modelGround != "NULL")
{
parent->updateProgress(q_("Loading ground..."),3,0,6);
groundModelLoad.reset(new OBJ());
modelFile = StelFileMgr::findFile(loadingScene.fullPath + "/
" + loadingScene.modelGround);
qDebug()<<"[Scenery3d] Loading ground from"<<modelFile;
if(!groundModelLoad->load(modelFile, objVertexOrder, loading
Scene.groundGenerateNormals))
{
qCritical()<<"[Scenery3d] Failed to load OBJ file.";
return false;
}
parent->updateProgress(q_("Transforming ground..."),4,0,6);
if(loadCancel)
return false;
groundModelLoad->transform( zRot2Grid );
}
if(loadCancel)
return false;
if(loadingScene.hasLocation())
{
if(loadingScene.altitudeFromModel)
{
loadingScene.location->altitude=static_cast<int>(0.5
*(objModelLoad->getBoundingBox().min[2]+objModelLoad->getBoundingBox().max[
2])+loadingScene.modelWorldOffset[2]);
}
}
if(scene.groundNullHeightFromModel)
{
loadingScene.groundNullHeight = ((!groundModelLoad.isNull()
&& groundModelLoad->isLoaded()) ? groundModelLoad->getBoundingBox().min[2]
: objModelLoad->getBoundingBox().min[2]);
qDebug() << "[Scenery3d] Ground outside model is " << loadin
gScene.groundNullHeight << "m high (in model coordinates)";
}
else qDebug() << "[Scenery3d] Ground outside model stays " << loadin
gScene.groundNullHeight << "m high (in model coordinates)";
//calculate heightmap
if(loadCancel)
return false;
parent->updateProgress(q_("Calculating collision map..."),5,0,6);
if(heightmapLoad)
{
delete heightmapLoad;
}
if( !groundModelLoad.isNull() && groundModelLoad->isLoaded())
{
heightmapLoad = new Heightmap(groundModelLoad.data());
heightmapLoad->setNullHeight(loadingScene.groundNullHeight);
}
else
heightmapLoad = NULL;
parent->updateProgress(q_("Finalizing load..."),6,0,6); delete storedViewDialog;
delete scenery3dDialog;
return true;
} }
void Scenery3d::finalizeLoad() double Scenery3d::getCallOrder(StelModuleActionName actionName) const
{ {
//must ensure the correct GL context is active! if (actionName == StelModule::ActionDraw)
//this is not guaranteed with the new QOpenGLWidget outside of init( return StelApp::getInstance().getModuleMgr().getModule("Land
) and draw()! scapeMgr")->getCallOrder(actionName) + 5; // between Landscape and compass
StelApp::getInstance().ensureGLContextCurrent(); marks!
if (actionName == StelModule::ActionUpdate)
currentScene = loadingScene; return StelApp::getInstance().getModuleMgr().getModule("Land
scapeMgr")->getCallOrder(actionName) + 10;
//move load data to current one if (actionName == StelModule::ActionHandleKeys)
objModel = objModelLoad; return 3; // GZ: low number means high precedence!
objModelLoad.clear(); return 0;
groundModel = groundModelLoad;
groundModelLoad.clear();
//upload GL
objModel->uploadBuffersGL();
objModel->uploadTexturesGL();
//call this after texture load
objModel->finalizeForRendering();
//the ground model needs no opengl uploads, so we skip them
//delete old heightmap
if(heightmap)
{
delete heightmap;
}
heightmap = heightmapLoad;
heightmapLoad = NULL;
if(currentScene.startPositionFromModel)
{
absolutePosition.v[0] = -(objModel->getBoundingBox().max[0]+
objModel->getBoundingBox().min[0])/2.0;
qDebug() << "Setting Easting to BBX center: " << objModel->
getBoundingBox().min[0] << ".." << objModel->getBoundingBox().max[0] << ":
" << absolutePosition.v[0];
absolutePosition.v[1] = -(objModel->getBoundingBox().max[1]+
objModel->getBoundingBox().min[1])/2.0;
qDebug() << "Setting Northing to BBX center: " << objModel->
getBoundingBox().min[1] << ".." << objModel->getBoundingBox().max[1] << ":
" << -absolutePosition.v[1];
}
else
{
absolutePosition[0] = currentScene.relativeStartPosition[0];
absolutePosition[1] = currentScene.relativeStartPosition[1];
}
eye_height = currentScene.eyeLevel;
//TODO: maybe introduce a switch in scenery3d.ini that allows the "g
round" bounding box to be used for shadow calculations
//this would allow some scenes to have better shadows
OBJ* cur = objModel.data();
//Set the scene's AABB
setSceneAABB(cur->getBoundingBox());
//Find a good splitweight based on the scene's size
float maxSize = -std::numeric_limits<float>::max();
maxSize = std::max(sceneBoundingBox.max.v[0], maxSize);
maxSize = std::max(sceneBoundingBox.max.v[1], maxSize);
if(currentScene.shadowSplitWeight<0)
{
//qDebug() << "MAXSIZE:" << maxSize;
if(maxSize < 100.0f)
currentScene.shadowSplitWeight = 0.5f;
else if(maxSize < 200.0f)
currentScene.shadowSplitWeight = 0.60f;
else if(maxSize < 400.0f)
currentScene.shadowSplitWeight = 0.70f;
else
currentScene.shadowSplitWeight = 0.99f;
}
//reset the cubemap time so that is ensured it is immediately rerend
ered
invalidateCubemap();
} }
void Scenery3d::handleKeys(QKeyEvent* e) void Scenery3d::handleKeys(QKeyEvent* e)
{ {
//TODO FS maybe move this to Mgr, so that input is separate from ren if (!flagEnabled)
dering and scene management? return;
static const Qt::KeyboardModifier S3D_SPEEDBASE_MODIFIER = Qt::Shift Modifier; static const Qt::KeyboardModifier S3D_SPEEDBASE_MODIFIER = Qt::Shift Modifier;
//on OSX, there is a still-unfixed bug which prevents the command ke y (=Qt's Control key) to be used here //on OSX, there is a still-unfixed bug which prevents the command ke y (=Qt's Control key) to be used here
//see https://bugreports.qt.io/browse/QTBUG-36839 //see https://bugreports.qt.io/browse/QTBUG-36839
//we have to use the option/ALT key instead to activate walking arou nd, and CMD is used as multiplier. //we have to use the option/ALT key instead to activate walking arou nd, and CMD is used as multiplier.
#ifdef Q_OS_OSX #ifdef Q_OS_OSX
static const Qt::KeyboardModifier S3D_CTRL_MODIFIER = Qt::AltModifie r; static const Qt::KeyboardModifier S3D_CTRL_MODIFIER = Qt::AltModifie r;
static const Qt::KeyboardModifier S3D_SPEEDMUL_MODIFIER = Qt::Contro lModifier; static const Qt::KeyboardModifier S3D_SPEEDMUL_MODIFIER = Qt::Contro lModifier;
#else #else
skipping to change at line 340 skipping to change at line 139
#endif #endif
if ((e->type() == QKeyEvent::KeyPress) && (e->modifiers() & S3D_CTRL _MODIFIER)) if ((e->type() == QKeyEvent::KeyPress) && (e->modifiers() & S3D_CTRL _MODIFIER))
{ {
// Pressing CTRL+ALT: 5x, CTRL+SHIFT: 10x speedup; CTRL+SHIF T+ALT: 50x! // Pressing CTRL+ALT: 5x, CTRL+SHIFT: 10x speedup; CTRL+SHIF T+ALT: 50x!
float speedup=((e->modifiers() & S3D_SPEEDBASE_MODIFIER)? 10 .0f : 1.0f); float speedup=((e->modifiers() & S3D_SPEEDBASE_MODIFIER)? 10 .0f : 1.0f);
speedup *= ((e->modifiers() & S3D_SPEEDMUL_MODIFIER)? 5.0f : 1.0f); speedup *= ((e->modifiers() & S3D_SPEEDMUL_MODIFIER)? 5.0f : 1.0f);
switch (e->key()) switch (e->key())
{ {
case Qt::Key_PageUp: movement[2] = -1.0f * speedu case Qt::Key_PageUp: movementKeyInput[2] = 1.0f
p; e->accept(); break; * speedup; e->accept(); break;
case Qt::Key_PageDown: movement[2] = 1.0f * speedu case Qt::Key_PageDown: movementKeyInput[2] = -1.0f
p; e->accept(); break; * speedup; e->accept(); break;
case Qt::Key_Up: movement[1] = -1.0f * speedu case Qt::Key_Up: movementKeyInput[1] = 1.0f
p; e->accept(); break; * speedup; e->accept(); break;
case Qt::Key_Down: movement[1] = 1.0f * speedu case Qt::Key_Down: movementKeyInput[1] = -1.0f
p; e->accept(); break; * speedup; e->accept(); break;
case Qt::Key_Right: movement[0] = 1.0f * speedu case Qt::Key_Right: movementKeyInput[0] = 1.0f
p; e->accept(); break; * speedup; e->accept(); break;
case Qt::Key_Left: movement[0] = -1.0f * speedu case Qt::Key_Left: movementKeyInput[0] = -1.0f
p; e->accept(); break; * speedup; e->accept(); break;
#ifdef QT_DEBUG #ifdef QT_DEBUG
//leave this out on non-debug builds to reduce confl //leave this out on non-debug builds to redu
ict chance ce conflict chance
case Qt::Key_P: saveFrusts(); e->accept(); b case Qt::Key_P: renderer->saveFrusts(); e->a
reak; ccept(); break;
#endif #endif
} }
} }
// FS: No modifier here!? GZ: I want the lock feature. If this does not work for MacOS, it is not there, but only on that platform... // FS: No modifier here!? GZ: I want the lock feature. If this does not work for MacOS, it is not there, but only on that platform...
#ifdef Q_OS_OSX #ifdef Q_OS_OSX
else if ((e->type() == QKeyEvent::KeyRelease) ) else if ((e->type() == QKeyEvent::KeyRelease) )
#else #else
else if ((e->type() == QKeyEvent::KeyRelease) && (e->modifiers() & S 3D_CTRL_MODIFIER)) else if ((e->type() == QKeyEvent::KeyRelease) && (e->modifiers() & S 3D_CTRL_MODIFIER))
#endif #endif
{ {
//if a movement key is released, stop moving in that directi on //if a movement key is released, stop moving in that directi on
//we do not accept the event on MacOS to allow further handl ing the event in other modules. (Else the regular view motion stop does not work!) //we do not accept the event on MacOS to allow further handl ing the event in other modules. (Else the regular view motion stop does not work!)
switch (e->key()) switch (e->key())
{ {
case Qt::Key_PageUp: case Qt::Key_PageUp:
case Qt::Key_PageDown: case Qt::Key_PageDown:
movement[2] = 0.0f; movementKeyInput[2] = 0.0f;
#ifndef Q_OS_OSX #ifndef Q_OS_OSX
e->accept(); e->accept();
#endif #endif
break; break;
case Qt::Key_Up: case Qt::Key_Up:
case Qt::Key_Down: case Qt::Key_Down:
movement[1] = 0.0f; movementKeyInput[1] = 0.0f;
#ifndef Q_OS_OSX #ifndef Q_OS_OSX
e->accept(); e->accept();
#endif #endif
break; break;
case Qt::Key_Right: case Qt::Key_Right:
case Qt::Key_Left: case Qt::Key_Left:
movement[0] = 0.0f; movementKeyInput[0] = 0.0f;
#ifndef Q_OS_OSX #ifndef Q_OS_OSX
e->accept(); e->accept();
#endif #endif
break; break;
} }
} }
} }
void Scenery3d::saveFrusts() void Scenery3d::update(double deltaTime)
{ {
fixShadowData = !fixShadowData; if (flagEnabled && currentScene)
camFrustShadow.saveDrawingCorners();
for(int i=0; i<shaderParameters.frustumSplits; i++)
{ {
if(fixShadowData) frustumArray[i].saveDrawingCorners(); //update view direction
else frustumArray[i].resetCorners(); Vec3d mainViewDir = mvMgr->getViewDirectionJ2000();
mainViewDir = core->j2000ToAltAz(mainViewDir, StelCore::Refr
actionOff);
currentScene->setViewDirection(mainViewDir);
//perform movement
//when zoomed in more than 5°, we slow down movement
if(movementKeyInput.lengthSquared() > 0.00001)
currentScene->moveViewer(movementKeyInput * deltaTim
e * 0.01 * std::max(5.0, mvMgr->getCurrentFov()));
//update material fade info, if necessary
double curTime = core->getJD();
S3DScene::MaterialList& matList = currentScene->getMaterialL
ist();
for(int i = 0; i<matList.size();++i)
{
S3DScene::Material& mat = matList[i];
if(mat.traits.hasTimeFade)
mat.updateFadeInfo(curTime);
}
} }
}
void Scenery3d::setSceneAABB(const AABB& bbox) messageFader.update((int)(deltaTime*1000));
{
sceneBoundingBox = bbox;
} }
void Scenery3d::update(double deltaTime) void Scenery3d::draw(StelCore* core)
{ {
if (core != NULL) if (flagEnabled && currentScene)
{ {
StelMovementMgr *stelMovementMgr = GETSTELMODULE(StelMovemen renderer->draw(core,*currentScene);
tMgr); }
Vec3d viewDirection = core->getMovementMgr()->getViewDirecti
onJ2000();
Vec3d viewDirectionAltAz=core->j2000ToAltAz(viewDirection);
double alt, az;
StelUtils::rectToSphe(&az, &alt, viewDirectionAltAz);
//if we were moving in the last update
bool wasMoving = moveVector.lengthSquared()>0.0;
//moveVector = Vec3d(( movement[0] * std::cos(az) + movement
[1] * std::sin(az)),
// ( movement[0] * std::sin(az) - movement[1] *
std::cos(az)),
// movement[2]);
//Bring move into world-grid space
//currentScene.zRotateMatrix.transfo(moveVector);
// GZ DON'T!: Rotating by zRotateMatrix will make a case of
convergence_angle=180 (i.e. misconfigured model) very silly (inverted!). --
>Just swap x/y.
// moveVector.set(-moveVector.v[1], moveVector.v[0], moveVec
tor.v[2]);
// Better yet: immediately make it right.
moveVector.set( movement[1] * std::cos(az) - movement[0] * s
td::sin(az),
movement[0] * std::cos(az) + movement[1] * s
td::sin(az),
movement[2]);
//get current time
double curTime = core->getJD();
if(lazyDrawing)
{
needsMovementUpdate = false;
//check if cubemap requires redraw //the message is always drawn
if(qAbs(curTime-lastCubemapUpdate) > lazyInterval * if (messageFader.getInterstate() > 0.000001f)
StelCore::JD_SECOND || reinitCubemapping) {
{
needsCubemapUpdate = true;
needsMovementEndUpdate = false;
}
else if (moveVector.lengthSquared() > 0.0 )
{
if(updateOnlyDominantOnMoving)
{
needsMovementUpdate = true;
needsMovementEndUpdate = true;
needsCubemapUpdate = false;
}
else
{
needsCubemapUpdate = true;
needsMovementEndUpdate = false;
}
}
else
{
if(wasMoving)
lastMovementEndRealTime = QDateTime:
:currentMSecsSinceEpoch();
if(needsMovementEndUpdate && (QDateTime::cur const StelProjectorP prj = core->getProjection(StelCore::Fra
rentMSecsSinceEpoch() - lastMovementEndRealTime) > 700) meEquinoxEqu);
{ StelPainter painter(prj);
//if the last movement was some time painter.setFont(font);
ago, update the whole cubemap painter.setColor(textColor[0], textColor[1], textColor[2], m
needsCubemapUpdate = true; essageFader.getInterstate());
needsMovementEndUpdate = false; painter.drawText(83, 120, currentMessage);
} }
else
needsCubemapUpdate = false;
}
}
else
{
needsCubemapUpdate = true;
}
moveVector *= deltaTime * 0.01 * qMax(5.0, stelMovementMgr-> getCurrentFov()); }
absolutePosition.v[0] += moveVector.v[0]; void Scenery3d::init()
absolutePosition.v[1] += moveVector.v[1]; {
eye_height -= moveVector.v[2]; qCDebug(scenery3d) << "Scenery3d plugin - press KGA button to toggle
absolutePosition.v[2] = -groundHeight()-eye_height; 3D scenery, KGA tool button for settings";
//View Up in our case always pointing positive up //Initialize the renderer - this also finds out what features are su
mainViewUp.v[0] = 0; pported
mainViewUp.v[1] = 0; qCDebug(scenery3d) << "Initializing Scenery3d renderer...";
mainViewUp.v[2] = 1; renderer->init();
qCDebug(scenery3d) << "Initializing Scenery3d renderer...done";
viewPos = -absolutePosition; //make sure shadows are off if unsupported
if(! renderer->areShadowsSupported())
setEnableShadows(false);
if(! renderer->isShadowFilteringSupported())
{
setShadowFilterQuality(S3DEnum::SFQ_OFF);
setEnablePCSS(false);
}
//View Direction //load config and create interface actions
mainViewDir = core->getMovementMgr()->getViewDirectionJ2000( loadConfig();
); createActions();
mainViewDir = core->j2000ToAltAz(mainViewDir); createToolbarButtons();
//find cubemap face this vector points at connect(&StelMainView::getInstance(), SIGNAL(reloadShadersRequested(
//only consider horizontal plane (XY) )), this, SLOT(reloadShaders()));
dominantFace = qAbs(mainViewDir.v[0])<qAbs(mainViewDir.v[1])
;
secondDominantFace = !dominantFace;
//uncomment this to also consider up/down faces //finally, hook up the lightscape toggle event (external to this plu
/* gin) to cubemap redraw
double max = qAbs(viewDir.v[dominantFace]); StelAction* action = StelApp::getInstance().getStelActionManager()->
if(qAbs(viewDir.v[2])>max) findAction("actionShow_LandscapeIllumination");
{ Q_ASSERT(action);
secondDominantFace = dominantFace; connect(action, &StelAction::toggled, this, &Scenery3d::forceCubemap
dominantFace = 2; Redraw);
}
else if (qAbs(viewDir.v[2])>qAbs(viewDir.v[secondDominantFace]))
{
secondDominantFace = 2;
}
*/
//check sign #ifndef NDEBUG
dominantFace = dominantFace*2 + (mainViewDir.v[dominantFace] showMessage(q_("Scenery3d plugin loaded!"));
<0.0); #endif
secondDominantFace = secondDominantFace*2 + (mainViewDir.v[s
econdDominantFace]<0.0);
}
} }
float Scenery3d::groundHeight() void Scenery3d::deinit()
{ {
if (heightmap == NULL) { //wait until loading is finished. (If test added after hint from Cov
return currentScene.groundNullHeight; erity)
} else { if(renderer)
return heightmap->getHeight(-absolutePosition.v[0],-absolute {
Position.v[1]); loadCancel = true;
currentLoadFuture.waitForFinished();
} }
//this is correct the place to delete all OpenGL related stuff, not
the destructor
delete renderer;
renderer = Q_NULLPTR;
delete currentScene;
currentScene = Q_NULLPTR;
cleanedUp = true;
} }
void Scenery3d::setupPassUniforms(QOpenGLShaderProgram *shader) void Scenery3d::loadConfig()
{ {
//send projection matrix conf->beginGroup(S3D_CONFIG_PREFIX);
SET_UNIFORM(shader,ShaderMgr::UNIFORM_MAT_PROJECTION, projectionMatr
ix);
//set alpha test threshold (this is scene-global for now) textColor = StelUtils::strToVec3f(conf->value("text_color", "0.5,0.5
SET_UNIFORM(shader,ShaderMgr::UNIFORM_FLOAT_ALPHA_THRESH,currentScen ,1").toString());
e.transparencyThreshold); renderer->setCubemappingMode( static_cast<S3DEnum::CubemappingMode>(
conf->value("cubemap_mode",0).toInt()) );
renderer->setCubemapSize(conf->value("cubemap_size",2048).toInt());
renderer->setShadowmapSize(conf->value("shadowmap_size", 1024).toInt
());
renderer->setShadowFilterQuality( static_cast<S3DEnum::ShadowFilterQ
uality>(conf->value("shadow_filter_quality", 1).toInt()) );
renderer->setPCSS(conf->value("flag_pcss").toBool());
renderer->setTorchEnabled(conf->value("torch_enabled", false).toBool
());
renderer->setTorchBrightness(conf->value("torch_brightness", 0.5f).t
oFloat());
renderer->setTorchRange(conf->value("torch_range",5.0f).toFloat());
renderer->setBumpsEnabled(conf->value("flag_bumpmap", false).toBool(
));
renderer->setShadowsEnabled(conf->value("flag_shadow", false).toBool
());
renderer->setUseSimpleShadows(conf->value("flag_shadow_simple", fals
e).toBool());
renderer->setUseFullCubemapShadows(conf->value("flag_cubemap_fullsha
dows", false).toBool());
renderer->setLazyCubemapEnabled(conf->value("flag_lazy_cubemap", tru
e).toBool());
renderer->setLazyCubemapInterval(conf->value("cubemap_lazy_interval"
,1.0).toDouble());
renderer->setPixelLightingEnabled(conf->value("flag_pixel_lighting",
false).toBool());
renderer->setLocationInfoEnabled(conf->value("flag_location_info", f
alse).toBool());
//torch attenuation factor bool v1 = conf->value("flag_lazy_dominantface",false).toBool();
SET_UNIFORM(shader, ShaderMgr::UNIFORM_TORCH_ATTENUATION, lightInfo. bool v2 = conf->value("flag_lazy_seconddominantface",true).toBool();
torchAttenuation); renderer->setLazyCubemapUpdateOnlyDominantFaceOnMoving(v1,v2);
//-- Shadowing setup -- this was previously in generateCubeMap_drawS defaultScenery3dID = conf->value("default_location_id","").toString(
ceneWithShadows );
//first check if shader supports shadows
GLint loc = shaderManager.uniformLocation(shader,ShaderMgr::UNIFORM_
VEC_SPLITDATA);
//ALWAYS update the shader matrices, even if "no" shadow is cast conf->endGroup();
//this fixes weird time-dependent crashes (this was fun to debug) }
if(shaderParameters.shadows && loc >= 0)
{
//Holds the frustum splits necessary for the lookup in the s
hader
Vec4f splitData;
for(int i=0; i<shaderParameters.frustumSplits; i++)
{
float zVal;
if(i<shaderParameters.frustumSplits-1)
{
//the frusta have a slight overlap
//use the center of this overlap for more ro
bust filtering
zVal = (frustumArray.at(i).zFar + frustumArr
ay.at(i+1).zNear) / 2.0f;
}
else
zVal = frustumArray.at(i).zFar;
//see Nvidia CSM example for this calculation void Scenery3d::createActions()
//http://developer.download.nvidia.com/SDK/10/opengl {
/screenshots/samples/cascaded_shadow_maps.html QString groupName = N_("Scenery3d: 3D landscapes");
//the distance needs to be in the final clip space,
not in eye space (or it would be a clipping sphere instead of a plane!)
splitData.v[i] = 0.5f*(-zVal * projectionMatrix.cons
tData()[10] + projectionMatrix.constData()[14])/zVal + 0.5f;
//Bind current depth map texture
glActiveTexture(GL_TEXTURE4+i);
glBindTexture(GL_TEXTURE_2D, shadowMapsArray.at(i));
SET_UNIFORM(shader,static_cast<ShaderMgr::UNIFORM>(S //enable action will be set checkable if a scene was loaded
haderMgr::UNIFORM_TEX_SHADOW0+i), 4+i); addAction("actionShow_Scenery3d", groupName, N_("To
SET_UNIFORM(shader,static_cast<ShaderMgr::UNIFORM>(S ggle 3D landscape"), this, "enableScene", "Ctrl+W");
haderMgr::UNIFORM_MAT_SHADOW0+i), shadowCPM.at(i)); addAction("actionShow_Scenery3d_dialog", groupName, N_("Sh
} ow settings dialog"), scenery3dDialog, "visible", "Ctrl+Shift+W
");
addAction("actionShow_Scenery3d_storedViewDialog", groupName, N_("Sh
ow viewpoint dialog"), storedViewDialog, "visible", "Ctrl+Alt+W")
;
addAction("actionShow_Scenery3d_shadows", groupName, N_("To
ggle shadows"), this, "enableShadows", "Ctrl+R, S");
addAction("actionShow_Scenery3d_debuginfo", groupName, N_("To
ggle debug information"), this, "enableDebugInfo", "Ctrl+R, D");
addAction("actionShow_Scenery3d_locationinfo", groupName, N_("To
ggle location text"), this, "enableLocationInfo","Ctrl+R, T");
addAction("actionShow_Scenery3d_torchlight", groupName, N_("To
ggle torchlight"), this, "enableTorchLight", "Ctrl+R, L");
}
//Send squared splits to the shader void Scenery3d::createToolbarButtons() const
shader->setUniformValue(loc, splitData.v[0], splitData.v[1], {
splitData.v[2], splitData.v[3]); // Add 3 toolbar buttons (copy/paste widely from AngleMeasure): acti
vate, settings, and viewpoints.
try
{
StelGui* gui = dynamic_cast<StelGui*>(StelApp::getInstance()
.getGui());
if(shaderParameters.shadowFilterQuality>S3DEnum::SFQ_HARDWAR E) if (gui!=Q_NULLPTR)
{ {
//send size of light ortho for each frustum StelButton* toolbarEnableButton = new StelButt
loc = shaderManager.uniformLocation(shader,ShaderMgr on(Q_NULLPTR,
::UNIFORM_VEC_LIGHTORTHOSCALE);
shader->setUniformValueArray(loc,shadowFrustumSize.c QPixmap(":/Scenery3d/bt_scenery3d_on.png"),
onstData(),shaderParameters.frustumSplits);
QPixmap(":/Scenery3d/bt_scenery3d_off.png"),
QPixmap(":/graphicGui/glow32x32.png"),
"actionShow_Scenery3d");
StelButton* toolbarSettingsButton = new StelButt
on(Q_NULLPTR,
QPixmap(":/Scenery3d/bt_scenery3d_settings_on.png"),
QPixmap(":/Scenery3d/bt_scenery3d_settings_off.png"),
QPixmap(":/graphicGui/glow32x32.png"),
"actionShow_Scenery3d_dialog");
StelButton* toolbarStoredViewButton = new StelButt
on(Q_NULLPTR,
QPixmap(":/Scenery3d/bt_scenery3d_eyepoint_on.png"),
QPixmap(":/Scenery3d/bt_scenery3d_eyepoint_off.png"),
QPixmap(":/graphicGui/glow32x32.png"),
"actionShow_Scenery3d_storedViewDialog");
gui->getButtonBar()->addButton(toolbarEnableButton,
"065-pluginsGroup");
gui->getButtonBar()->addButton(toolbarSettingsButton
, "065-pluginsGroup");
gui->getButtonBar()->addButton(toolbarStoredViewButt
on, "065-pluginsGroup");
} }
} }
catch (std::runtime_error& e)
loc = shaderManager.uniformLocation(shader,ShaderMgr::UNIFORM_MAT_CU
BEMVP);
if(loc>=0)
{ {
//upload cube mvp matrices qCWarning(scenery3d) << "WARNING: unable to create toolbar b
shader->setUniformValueArray(loc,cubeMVP,6); uttons for Scenery3d plugin: " << e.what();
} }
} }
void Scenery3d::setupFrameUniforms(QOpenGLShaderProgram *shader) void Scenery3d::relativeMove(const Vec3d &move)
{ {
//-- Transform setup -- if(currentScene)
//check if shader wants a MVP or separate matrices
GLint loc = shaderManager.uniformLocation(shader,ShaderMgr::UNIFORM_
MAT_MVP);
if(loc>=0)
{
shader->setUniformValue(loc,projectionMatrix * modelViewMatr
ix);
}
//this macro saves a bit of writing
SET_UNIFORM(shader,ShaderMgr::UNIFORM_MAT_MODELVIEW, modelViewMatrix
);
//-- Lighting setup --
//check if we require a normal matrix, this is assumed to be require
d for all "shading" shaders
loc = shaderManager.uniformLocation(shader,ShaderMgr::UNIFORM_MAT_NO
RMAL);
if(loc>=0)
{ {
QMatrix3x3 normalMatrix = modelViewMatrix.normalMatrix(); currentScene->moveViewer(move);
shader->setUniformValue(loc,normalMatrix);
//assume light direction is only required when normal matrix
is also used (would not make much sense alone)
//check if the shader wants view space info
loc = shaderManager.uniformLocation(shader,ShaderMgr::UNIFOR
M_LIGHT_DIRECTION_VIEW);
if(loc>=0)
shader->setUniformValue(loc,(normalMatrix * lightInf
o.lightDirectionWorld));
} }
} }
void Scenery3d::setupMaterialUniforms(QOpenGLShaderProgram* shader, const O BJ::Material &mat) void Scenery3d::reloadShaders()
{ {
//ambient is calculated depending on illum model showMessage(q_("Scenery3d shaders reloaded"));
SET_UNIFORM(shader,ShaderMgr::UNIFORM_MIX_AMBIENT,mat.ambient * ligh qCDebug(scenery3d)<<"Reloading Scenery3d shaders";
tInfo.ambient);
SET_UNIFORM(shader,ShaderMgr::UNIFORM_MIX_DIFFUSE, mat.diffuse * lig renderer->getShaderManager().clearCache();
htInfo.directional); }
SET_UNIFORM(shader,ShaderMgr::UNIFORM_MIX_TORCHDIFFUSE, mat.diffuse
* lightInfo.torchDiffuse);
SET_UNIFORM(shader,ShaderMgr::UNIFORM_MIX_EMISSIVE,mat.emission * li
ghtInfo.emissive);
SET_UNIFORM(shader,ShaderMgr::UNIFORM_MIX_SPECULAR,mat.specular * li
ghtInfo.specular);
SET_UNIFORM(shader,ShaderMgr::UNIFORM_MTL_SHININESS,mat.shininess); bool Scenery3d::configureGui(bool show)
//force alpha to 1 here for non-translucent mats (fixes incorrect bl {
ending in cubemap) if (show)
SET_UNIFORM(shader,ShaderMgr::UNIFORM_MTL_ALPHA,mat.hasTransparency scenery3dDialog->setVisible(show);
? mat.alpha : 1.0f); return true;
}
if(mat.texture) void Scenery3d::showStoredViewDialog()
{ {
mat.texture->bind(0); //this already sets glActiveTexture(0) storedViewDialog->setVisible(true);
SET_UNIFORM(shader,ShaderMgr::UNIFORM_TEX_DIFFUSE,0);
}
if(mat.emissive_texture)
{
mat.emissive_texture->bind(1);
SET_UNIFORM(shader,ShaderMgr::UNIFORM_TEX_EMISSIVE,1);
}
if(shaderParameters.bump && mat.bump_texture)
{
mat.bump_texture->bind(2);
SET_UNIFORM(shader,ShaderMgr::UNIFORM_TEX_BUMP,2);
}
if(shaderParameters.bump && mat.height_texture)
{
mat.height_texture->bind(3);
SET_UNIFORM(shader,ShaderMgr::UNIFORM_TEX_HEIGHT,3);
}
} }
bool Scenery3d::drawArrays(bool shading, bool blendAlphaAdditive) void Scenery3d::updateProgress(const QString &str, int val, int min, int ma x) const
{ {
QOpenGLShaderProgram* curShader = NULL; emit progressReport(str,val,min,max);
QSet<QOpenGLShaderProgram*> initialized; }
GLenum indexDataType = OBJ::getIndexBufferType();
size_t indexDataTypeSize = OBJ::getIndexBufferTypeSize();
//override some shader Params void Scenery3d::progressReceive(const QString &str, int val, int min, int m
GlobalShaderParameters pm = shaderParameters; ax)
switch(lightInfo.lightSource) {
//update progress bar
if(progressBar)
{ {
case Venus: progressBar->setFormat(str);
//turn shadow filter off to get sharper shadows progressBar->setRange(min,max);
pm.shadowFilterQuality = S3DEnum::SFQ_OFF; progressBar->setValue(val);
break;
case None:
//disable shadow rendering to speed things up
pm.shadows = false;
break;
default:
break;
} }
}
//bind VAO void Scenery3d::loadScene(const SceneInfo& scene)
objModel->bindGL(); {
loadCancel = true;
//assume backfaceculling is on //If currently loading, we have to wait until it is finished
bool backfaceCullState = true; //This currently blocks the GUI thread until the loading can be canc
bool success = true; eled
// (which is for now rather rough-grained and so may take a while)
currentLoadFuture.waitForFinished();
loadCancel = false;
//TODO optimize: clump models with same material together when first if(progressBar)
loading to minimize state changes
const OBJ::Material* lastMaterial = NULL;
bool blendEnabled = false;
for(int i=0; i<objModel->getNumberOfStelModels(); i++)
{ {
//kinda hack: it can be that this here is executed before lo
adSceneBackground is called
//so we push the call back into the queue to ensure correct
execution order
QMetaObject::invokeMethod(this,"loadScene",Qt::QueuedConnect
ion,Q_ARG(SceneInfo, scene));
return;
}
const OBJ::StelModel* pStelModel = &objModel->getStelModel(i // Loading may take a while...
); showMessage(QString(q_("Loading scene. Please be patient!")));
const OBJ::Material* pMaterial = pStelModel->pMaterial; progressBar = StelApp::getInstance().addProgressBar();
Q_ASSERT(pMaterial); progressBar->setFormat(QString(q_("Loading scene '%1'")).arg(scene.n
ame));
progressBar->setValue(0);
++drawnModels; currentLoadScene = scene;
emit loadingSceneIDChanged(currentLoadScene.id);
if(lastMaterial!=pMaterial) QFuture<S3DScene*> future = QtConcurrent::run(this,&Scenery3d::loadS
{ ceneBackground,scene);
++materialSwitches; currentLoadFuture.setFuture(future);
lastMaterial = pMaterial; }
//get a shader from shadermgr that fits the current S3DScene* Scenery3d::loadSceneBackground(const SceneInfo& scene) const
state + material combo {
QOpenGLShaderProgram* newShader = shaderManager.getS //the scoped pointer ensures this scene is deleted when errors occur
hader(pm,pMaterial); QScopedPointer<S3DScene> newScene(new S3DScene(scene));
if(!newShader)
{
//shader invalid, can't draw
parent->showMessage(q_("Scenery3d shader err
or, can't draw. Check debug output for details."));
success = false;
break;
}
if(newShader!=curShader)
{
curShader = newShader;
curShader->bind();
if(!initialized.contains(curShader))
{
++shaderSwitches;
//needs first-time initialization fo
r this pass
if(shading)
{
setupPassUniforms(curShader)
;
setupFrameUniforms(curShader
);
}
else
{
//really only mvp+alpha thre
sh required, so only set this
SET_UNIFORM(curShader,Shader
Mgr::UNIFORM_MAT_MVP,projectionMatrix * modelViewMatrix);
SET_UNIFORM(curShader,Shader
Mgr::UNIFORM_FLOAT_ALPHA_THRESH,currentScene.transparencyThreshold);
}
//we remember if we have initialized
this shader already, so we can skip "global" initialization later if we en
counter it again
initialized.insert(curShader);
}
}
if(shading)
{
//perform full material setup
setupMaterialUniforms(curShader,*pMaterial);
}
else
{
//set diffuse tex if possible for alpha test
ing
if( ! pMaterial->texture.isNull())
{
pMaterial->texture->bind(0);
SET_UNIFORM(curShader,ShaderMgr::UNI
FORM_TEX_DIFFUSE,0);
}
}
if(pMaterial->hasTransparency ) if(loadCancel)
{ return Q_NULLPTR;
//TODO provide Z-sorting for transparent obj
ects (center of bounding box should be fine)
if(!blendEnabled)
{
glEnable(GL_BLEND);
if(blendAlphaAdditive)
glBlendFuncSeparate(GL_SRC_A
LPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
else //traditional direct blending
glBlendFunc(GL_SRC_ALPHA, GL
_ONE_MINUS_SRC_ALPHA);
blendEnabled = true;
}
}
else
{
if(blendEnabled)
{
glDisable(GL_BLEND);
blendEnabled=false;
}
}
if(backfaceCullState && !pMaterial->backfacecull) updateProgress(q_("Loading model..."),1,0,6);
{
glDisable(GL_CULL_FACE);
backfaceCullState = false;
}
else if(!backfaceCullState && pMaterial->backfacecul
l)
{
glEnable(GL_CULL_FACE);
backfaceCullState = true;
}
}
GET_GLERROR() //load model
glDrawElements(GL_TRIANGLES, pStelModel->triangleCount * 3, StelOBJ modelOBJ;
indexDataType, reinterpret_cast<const void*>(pStelModel->startIndex * index QString modelFile = StelFileMgr::findFile( scene.fullPath+ "/" + sce
DataTypeSize)); ne.modelScenery);
drawnTriangles+=pStelModel->triangleCount; qCDebug(scenery3d)<<"Loading scene from "<<modelFile;
if(!modelOBJ.load(modelFile, scene.vertexOrderEnum))
{
qCCritical(scenery3d)<<"Failed to load OBJ file"<<modelFile;
return Q_NULLPTR;
} }
if(!backfaceCullState) if(loadCancel)
glEnable(GL_CULL_FACE); return Q_NULLPTR;
if(curShader)
curShader->release();
if(blendEnabled)
glDisable(GL_BLEND);
//release VAO
objModel->unbindGL();
return success; updateProgress(q_("Transforming model..."),2,0,6);
} newScene->setModel(modelOBJ);
void Scenery3d::computeFrustumSplits(const Vec3d viewPos, const Vec3d viewD if(loadCancel)
ir, const Vec3d viewUp) return Q_NULLPTR;
{
//the frustum arrays all already contain the same adjusted frustum f
rom adjustFrustum
float zNear = frustumArray[0].zNear;
float zFar = frustumArray[0].zFar;
float zRatio = zFar / zNear;
float zRange = zFar - zNear;
//Compute the z-planes for the subfrusta if(scene.modelGround.isEmpty())
for(int i=1; i<shaderParameters.frustumSplits; i++) {
updateProgress(q_("Calculating collision map..."),5,0,6);
newScene->setGround(modelOBJ);
}
else if (scene.modelGround != "NULL")
{ {
float s_i = i/static_cast<float>(shaderParameters.frustumSpl updateProgress(q_("Loading ground..."),3,0,6);
its);
StelOBJ groundOBJ;
modelFile = StelFileMgr::findFile(scene.fullPath + "/" + sce
ne.modelGround);
qCDebug(scenery3d)<<"Loading ground from"<<modelFile;
if(!groundOBJ.load(modelFile, scene.vertexOrderEnum))
{
qCCritical(scenery3d)<<"Failed to load ground model"
<<modelFile;
return Q_NULLPTR;
}
frustumArray[i].zNear = currentScene.shadowSplitWeight*(zNea updateProgress(q_("Transforming ground..."),4,0,6);
r*powf(zRatio, s_i)) + (1.0f-currentScene.shadowSplitWeight)*(zNear + (zRan if(loadCancel)
ge)*s_i); return Q_NULLPTR;
//Set the previous zFar to the newly computed zNear
//use a small overlap for robustness
frustumArray[i-1].zFar = frustumArray[i].zNear * 1.005f;
frustumArray[i-1].calcFrustum(viewPos,viewDir,viewUp); updateProgress(q_("Calculating collision map..."),5,0,6);
newScene->setGround(groundOBJ);
} }
//last zFar is already the zFar of the adjusted frustum if(loadCancel)
frustumArray[shaderParameters.frustumSplits-1].calcFrustum(viewPos,v return Q_NULLPTR;
iewDir,viewUp);
}
void Scenery3d::computePolyhedron(Polyhedron& body,const Frustum& frustum,c updateProgress(q_("Finalizing load..."),6,0,6);
onst Vec3f& shadowDir)
{
//Building a convex body for directional lights according to Wimmer
et al. 2006
//Add the Frustum to begin with return newScene.take();
body.add(frustum);
//Intersect with the scene AABB
body.intersect(sceneBoundingBox);
//Extrude towards light direction
body.extrude(shadowDir, sceneBoundingBox);
} }
void Scenery3d::computeOrthoProjVals(const Vec3f shadowDir,float& orthoExte nt,float& orthoNear,float& orthoFar) void Scenery3d::loadSceneCompleted()
{ {
//Focus the light first on the entire scene S3DScene* result = currentLoadFuture.result();
float maxZ = -std::numeric_limits<float>::max();
float minZ = std::numeric_limits<float>::max();
orthoExtent = 0.0f;
Vec3f eye = shadowDir; progressBar->setValue(100);
Vec3f vDir = -eye; StelApp::getInstance().removeProgressBar(progressBar);
vDir.normalize(); progressBar=Q_NULLPTR;
Vec3f up = Vec3f(0.0f, 0.0f, 1.0f);
Vec3f down = -up;
Vec3f left = vDir^up;
left.normalize();
Vec3f right = -left;
for(unsigned int i=0; i<AABB::CORNERCOUNT; i++) if(!result)
{ {
Vec3f v = sceneBoundingBox.getCorner(static_cast<AABB::Corne showMessage(q_("Could not load scene, please check log for e
r>(i)); rror messages!"));
Vec3f toCam = v - eye; return;
float dist = toCam.dot(vDir);
maxZ = std::max(dist, maxZ);
minZ = std::min(dist, minZ);
orthoExtent = std::max(std::abs(toCam.dot(left)), orthoExten
t);
orthoExtent = std::max(std::abs(toCam.dot(right)), orthoExte
nt);
orthoExtent = std::max(std::abs(toCam.dot(up)), orthoExtent)
;
orthoExtent = std::max(std::abs(toCam.dot(down)), orthoExten
t);
} }
else
showMessage(q_("Scene successfully loaded."));
//Make sure planes arent too small //do stuff that requires the main thread
orthoNear = minZ; const SceneInfo& info = result->getSceneInfo();
orthoFar = maxZ;
//orthoNear = std::max(minZ, 0.01f);
//orthoFar = std::max(maxZ, orthoNear + 1.0f);
}
void Scenery3d::computeCropMatrix(QMatrix4x4& cropMatrix, QVector4D& orthoS //move to the location specified by the scene
cale, Polyhedron& focusBody,const QMatrix4x4& lightProj, const QMatrix4x4& LandscapeMgr* lmgr = GETSTELMODULE(LandscapeMgr);
lightMVP) bool landscapeSetsLocation=lmgr->getFlagLandscapeSetsLocation();
{ lmgr->setFlagLandscapeSetsLocation(true);
float maxX = -std::numeric_limits<float>::max(); lmgr->setCurrentLandscapeName(info.landscapeName, 0.); // took a sec
float maxY = maxX; ond, implicitly.
float maxZ = maxX; // Switched to immediate landscape loading: Else,
float minX = std::numeric_limits<float>::max(); // Landscape and Navigator at this time have old coordinates! But it
float minY = minX; should be possible to
float minZ = minX; // delay rot_z computation up to this point and live without an own
location section even
// with meridian_convergence=from_grid.
lmgr->setFlagLandscapeSetsLocation(landscapeSetsLocation); // restor
e
//Project the frustum into light space and find the boundaries if (info.hasLocation())
for(int i=0; i<focusBody.getVertCount(); i++)
{ {
const Vec3f tmp = focusBody.getVerts().at(i); qCDebug(scenery3d) << "Setting location to given coordinates
QVector4D transf4 = lightMVP*QVector4D(tmp.v[0], tmp.v[1], t ";
mp.v[2], 1.0f); StelApp::getInstance().getCore()->moveObserverTo(*(info.loca
QVector3D transf = transf4.toVector3DAffine(); tion.data()), 0., 0.);
if(transf.x() > maxX) maxX = transf.x();
if(transf.x() < minX) minX = transf.x();
if(transf.y() > maxY) maxY = transf.y();
if(transf.y() < minY) minY = transf.y();
if(transf.z() > maxZ) maxZ = transf.z();
if(transf.z() < minZ) minZ = transf.z();
} }
else qCDebug(scenery3d) << "No coordinates given in scenery3d.ini";
//To avoid artifacts caused by far plane clipping, extend far plane if (info.hasLookAtFOV())
by 5%
//or if cubemapping is used, set it to 1
if(!requiresCubemap || fullCubemapShadows)
{
float zRange = maxZ-minZ;
maxZ = std::min(maxZ + zRange*0.05f, 1.0f);
}
else
{ {
maxZ = 1.0f; qCDebug(scenery3d) << "Setting orientation";
} Vec3f lookat=currentLoadScene.lookAt_fov;
// This vector is (az_deg, alt_deg, fov_deg)
//minZ = std::max(minZ - zRange*0.05f, 0.0f); Vec3d v;
StelUtils::spheToRect(lookat[0]*M_PI/180.0, lookat[1]*M_PI/1
#ifdef QT_DEBUG 80.0, v);
AABB deb(Vec3f(minX,minY,minZ),Vec3f(maxX,maxY,maxZ)); mvMgr->setViewDirectionJ2000(StelApp::getInstance().getCore(
focusBody.debugBox = deb.toBox(); )->altAzToJ2000(v, StelCore::RefractionOff));
focusBody.debugBox.transform(lightMVP.inverted()); mvMgr->zoomTo(lookat[2]);
#endif } else qCDebug(scenery3d) << "No orientation given in scenery3d.ini"
;
//Build the crop matrix and apply it to the light projection matrix
float scaleX = 2.0f/(maxX - minX);
float scaleY = 2.0f/(maxY - minY);
float scaleZ = 1.0f/(maxZ - minZ); //could also be 1, but this resca
les the Z range to fit better
//float scaleZ = 1.0f;
float offsetZ = -minZ * scaleZ; //clear loading scene
//float offsetZ = 0.0f; currentLoadScene = SceneInfo();
emit loadingSceneIDChanged(QString());
//Reducing swimming as specified in Practical cascaded shadow maps b //switch scenes
y Zhang et al. delete currentScene;
const float quantizer = 64.0f; currentScene = result;
scaleX = 1.0f/std::ceil(1.0f/scaleX*quantizer) * quantizer;
scaleY = 1.0f/std::ceil(1.0f/scaleY*quantizer) * quantizer;
orthoScale = QVector4D(scaleX,scaleY,minZ,maxZ); //show the scene
setEnableScene(true);
float offsetX = -0.5f*(maxX + minX)*scaleX; emit currentSceneChanged(info);
float offsetY = -0.5f*(maxY + minY)*scaleY; emit currentSceneIDChanged(info.id);
float halfTex = 0.5f*shadowmapSize;
offsetX = std::ceil(offsetX*halfTex)/halfTex;
offsetY = std::ceil(offsetY*halfTex)/halfTex;
//Making the crop matrix
QMatrix4x4 crop(scaleX, 0.0f, 0.0f, offsetX,
0.0f, scaleY, 0.0f, offsetY,
0.0f, 0.0f, scaleZ, offsetZ,
0.0f, 0.0f, 0.0f, 1.0f);
//Crop the light projection matrix
projectionMatrix = crop * lightProj;
//Calculate texture matrix for projection
//This matrix takes us from eye space to the light's clip space
//It is postmultiplied by the inverse of the current view matrix whe
n specifying texgen
static const QMatrix4x4 biasMatrix(0.5f, 0.0f, 0.0f, 0.5f,
0.0f, 0.5f, 0.0f, 0.5f,
0.0f, 0.0f, 0.5f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f); //bi
as from [-1, 1] to [0, 1]
//calc final matrix
cropMatrix = biasMatrix * projectionMatrix * modelViewMatrix;
} }
void Scenery3d::adjustShadowFrustum(const Vec3d viewPos, const Vec3d viewDi r, const Vec3d viewUp, const float fov, const float aspect) SceneInfo Scenery3d::loadScenery3dByID(const QString& id)
{ {
if(fixShadowData) if (id.isEmpty())
return; return SceneInfo();
//calc cam frustum for shadowing range
//note that this is only correct in the perspective projection case,
cubemapping WILL introduce shadow artifacts in most cases
//TODO make shadows in cubemapping mode better by projecting the fru
sta, more closely estimating the required shadow extents
camFrustShadow.setCamInternals(fov,aspect,currentScene.camNearZ,curr
entScene.shadowFarZ);
camFrustShadow.calcFrustum(viewPos, viewDir, viewUp);
//Compute H = V intersect S according to Zhang et al.
Polyhedron p;
p.add(camFrustShadow);
p.intersect(sceneBoundingBox);
p.makeUniqueVerts();
//Find the boundaries SceneInfo scene;
float maxZ = -std::numeric_limits<float>::max(); try
float minZ = std::numeric_limits<float>::max();
Vec3f eye = viewPos.toVec3f();
Vec3f vDir = viewDir.toVec3f();
vDir.normalize();
const QVector<Vec3f> &verts = p.getVerts();
for(int i=0; i<p.getVertCount(); i++)
{ {
//Find the distance to the camera if(!SceneInfo::loadByID(id,scene))
Vec3f v = verts[i]; {
Vec3f toCam = v - eye; showMessage(q_("Could not load scene info, please ch
float dist = toCam.dot(vDir); eck log for error messages!"));
return SceneInfo();
maxZ = std::max(dist, maxZ); }
minZ = std::min(dist, minZ); }
catch (std::runtime_error& e)
{
//TODO do away with the exceptions if possible
qCCritical(scenery3d) << "ERROR while loading 3D scenery wit
h id " << id << ", (" << e.what() << ")";
return SceneInfo();
} }
//Setup the newly found near and far planes but make sure they're no loadScene(scene);
t too small return scene;
//minZ = std::max(minZ, 0.01f); }
//maxZ = std::max(maxZ, minZ+1.0f);
SceneInfo Scenery3d::loadScenery3dByName(const QString& name)
{
if (name.isEmpty())
return SceneInfo();
//save adjusted values and recalc combined frustum for debugging QString id = SceneInfo::getIDFromName(name);
camFrustShadow.setCamInternals(fov,aspect,minZ,maxZ);
camFrustShadow.calcFrustum(viewPos,viewDir,viewUp);
//Re-set the subfrusta if(id.isEmpty())
for(int i=0; i<shaderParameters.frustumSplits; i++)
{ {
frustumArray[i].setCamInternals(fov, aspect, minZ, maxZ); showMessage(QString(q_("Could not find scene ID for %1")).ar
g(name));
return SceneInfo();
} }
return loadScenery3dByID(id);
}
//Compute and set z-distances for each split SceneInfo Scenery3d::getCurrentScene() const
computeFrustumSplits(viewPos,viewDir,viewUp); {
if(currentScene)
return currentScene->getSceneInfo();
return SceneInfo();
} }
void Scenery3d::calculateShadowCaster() QString Scenery3d::getCurrentSceneID() const
{ {
//shadow source and direction has been calculated in calculateLightS return currentScene ? currentScene->getSceneInfo().id : QString();
ource }
static const QVector3D vZero = QVector3D();
static const QVector3D vZeroZeroOne = QVector3D(0,0,1);
//calculate lights modelview matrix QString Scenery3d::getLoadingSceneID() const
lightInfo.shadowModelView.setToIdentity(); {
lightInfo.shadowModelView.lookAt(lightInfo.lightDirectionWorld,vZero return currentLoadScene.id;
,vZeroZeroOne);
} }
bool Scenery3d::renderShadowMaps() void Scenery3d::setDefaultScenery3dID(const QString& id)
{ {
if(fixShadowData) defaultScenery3dID = id;
return true;
shaderParameters.shadowTransform = true; conf->setValue(S3D_CONFIG_PREFIX + "/default_location_id", id);
}
//projection matrix gets updated below in updateCropMatrix void Scenery3d::setEnableScene(const bool enable)
modelViewMatrix = lightInfo.shadowModelView;
//Fix selfshadowing
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.5f,2.0f);
//GL state
//enable depth + front face culling
glEnable(GL_DEPTH_TEST);
//glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
//frontface culling for ESM!
glCullFace(GL_FRONT);
//Set viewport to shadowmap
glViewport(0, 0, shadowmapSize, shadowmapSize);
//Compute an orthographic projection that encompasses the whole scen
e
//a crop matrix is used to restrict this projection to the subfrusta
float orthoExtent;
computeOrthoProjVals(lightInfo.lightDirectionV3f,orthoExtent,lightOr
thoNear,lightOrthoFar);
QMatrix4x4 lightProj;
lightProj.ortho(-orthoExtent,orthoExtent,-orthoExtent,orthoExtent,li
ghtOrthoNear,lightOrthoFar);
//multiply with lights modelView matrix
QMatrix4x4 lightMVP = lightProj*modelViewMatrix;
bool success = true;
//For each split
for(int i=0; i<shaderParameters.frustumSplits; i++)
{
//Find the convex body that encompasses all shadow receivers
and casters for this split
focusBodies[i].clear();
computePolyhedron(focusBodies[i],frustumArray[i],lightInfo.l
ightDirectionV3f);
//qDebug() << i << ".split vert count:" << focusBodies[i]->g
etVertCount();
glBindFramebuffer(GL_FRAMEBUFFER,shadowFBOs.at(i));
//Clear everything, also if focusbody is empty
glClear(GL_DEPTH_BUFFER_BIT);
if(lightInfo.lightSource != None && focusBodies[i].getVertCo
unt())
{
//Calculate the crop matrix so that the light's frus
tum is tightly fit to the current split's PSR+PSC polyhedron
//This alters the ProjectionMatrix of the light
//the final light matrix used for lookups is stored
in shadowCPM
computeCropMatrix(shadowCPM[i], shadowFrustumSize[i]
, focusBodies[i],lightProj,lightMVP);
//the shadow frustum size is only the scaling, multi
ply it with the extents of the original matrix
shadowFrustumSize[i] = QVector4D(shadowFrustumSize[i
][0] / orthoExtent, shadowFrustumSize[i][1] / orthoExtent,
//shadowFrustumSize[i][2], shadowFru
stumSize[i][3]);
(.5f * shadowFrustumSize[i][2] + .5f
) *(lightOrthoFar - lightOrthoNear) + lightOrthoNear,
(.5f * shadowFrustumSize[i][3] + .5f
) *(lightOrthoFar - lightOrthoNear) + lightOrthoNear );
//Draw the scene
if(!drawArrays(false))
{
success = false;
break;
}
}
}
//Unbind
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
//reset viewport (see StelPainter::setProjector)
const Vec4i& vp = altAzProjector->getViewport();
glViewport(vp[0], vp[1], vp[2], vp[3]);
//Move polygons back to normal position
glDisable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0f,0.0f);
//Reset
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
glCullFace(GL_BACK);
glDisable(GL_CULL_FACE);
shaderParameters.shadowTransform = false;
return success;
}
void Scenery3d::calculateLighting()
{ {
//calculate which light source we need + intensity if(enable && ! getCurrentScene().isValid)
float ambientBrightness, directionalBrightness,emissiveFactor;
lightInfo.lightSource = calculateLightSource(ambientBrightness, dire
ctionalBrightness, lightInfo.lightDirectionV3f, emissiveFactor);
lightInfo.lightDirectionWorld = QVector3D(lightInfo.lightDirectionV3
f.v[0],lightInfo.lightDirectionV3f.v[1],lightInfo.lightDirectionV3f.v[2]);
//specular factor is calculated from other values for now
float specular = std::min(ambientBrightness*directionalBrightness*5.
0f,1.0f);
//if the night vision mode is on, use red-tinted lighting
bool red=StelApp::getInstance().getVisionModeNight();
float torchDiff = shaderParameters.torchLight ? torchBrightness : 0.
0f;
lightInfo.torchAttenuation = 1.0f / (torchRange * torchRange);
if(red)
{
lightInfo.ambient = QVector3D(ambientBrightness,0, 0);
lightInfo.directional = QVector3D(directionalBrightness,0,0)
;
lightInfo.emissive = QVector3D(emissiveFactor,0,0);
lightInfo.specular = QVector3D(specular,0,0);
lightInfo.torchDiffuse = QVector3D(torchDiff,0,0);
}
else
{
//for now, lighting is only white
lightInfo.ambient = QVector3D(ambientBrightness,ambientBrigh
tness, ambientBrightness);
lightInfo.directional = QVector3D(directionalBrightness,dire
ctionalBrightness,directionalBrightness);
lightInfo.emissive = QVector3D(emissiveFactor,emissiveFactor
,emissiveFactor);
lightInfo.specular = QVector3D(specular,specular,specular);
lightInfo.torchDiffuse = QVector3D(torchDiff,torchDiff,torch
Diff);
}
}
Scenery3d::ShadowCaster Scenery3d::calculateLightSource(float &ambientBrig
htness, float &directionalBrightness, Vec3f &lightsourcePosition, float &em
issiveFactor)
{
Vec3d sunPosition = sun->getAltAzPosAuto(core);
sunPosition.normalize();
Vec3d moonPosition = moon->getAltAzPosAuto(core);
float moonPhaseAngle = moon->getPhase(core->getObserverHeliocentricE
clipticPos());
moonPosition.normalize();
Vec3d venusPosition = venus->getAltAzPosAuto(core);
float venusPhaseAngle = venus->getPhase(core->getObserverHeliocentri
cEclipticPos());
venusPosition.normalize();
// The light model here: ambient light consists of solar twilight an
d day ambient,
// plus lunar ambient, plus a base constant AMBIENT_BRIGHTNESS_FACTO
R[0.1?],
// plus an artificial "torch" that can be toggled via Ctrl-L[ight].
// We define the ambient solar brightness zero when the sun is 18 de
grees below the horizon, and lift the sun by 18 deg.
// ambient brightness component of the sun is then MIN(0.3, sin(sun
)+0.3)
// With the sun above the horizon, we raise only the directional com
ponent.
// ambient brightness component of the moon is sqrt(sin(alt_moon)*(c
os(moon.phase_angle)+1)/2)*LUNAR_BRIGHTNESS_FACTOR[0.2?]
// Directional brightness factor: sqrt(sin(alt_sun)) if sin(alt_sun)
>0 --> NO: MIN(0.7, sin(sun)+0.1), i.e. sun 6 degrees higher.
// sqrt(sin(alt_moon)*(cos(moon.phase
_angle)+1)/2)*LUNAR_BRIGHTNESS_FACTOR if sin(alt_moon)>0
// sqrt(sin(alt_venus)*(cos(venus.pha
se_angle)+1)/2)*VENUS_BRIGHTNESS_FACTOR[0.15?]
// Note the sqrt(sin(alt))-terms: they are to increase brightness so
oner than with the Lambert law.
//float sinSunAngleRad = sin(qMin(M_PI_2, asin(sunPosition[2])+8.*M_
PI/180.));
//float sinMoonAngleRad = moonPosition[2];
float sinSunAngle = sunPosition[2];
float sinMoonAngle = moonPosition[2];
float sinVenusAngle = venusPosition[2];
//set the minimum ambient brightness
//this uses the LandscapeMgr values
Landscape* l = landscapeMgr->getCurrentLandscape();
if (landscapeMgr->getFlagLandscapeUseMinimalBrightness())
{
// Setting from landscape.ini has priority if enabled
if (landscapeMgr->getFlagLandscapeSetsMinimalBrightness() &&
l && l->getLandscapeMinimalBrightness()>=0)
ambientBrightness = l->getLandscapeMinimalBrightness
();
else
ambientBrightness = landscapeMgr->getDefaultMinimalB
rightness();
}
else
{ {
ambientBrightness = 0.0f; //check if a default scene is set and load that
} QString id = getDefaultScenery3dID();
if(!id.isEmpty())
directionalBrightness=0.0f;
ShadowCaster shadowcaster = None;
// DEBUG AIDS: Helper strings to be displayed
//TODO move these string manipulations to drawDebug, it is a bit dum
b to do this every frame, even if not needed
QString sunAmbientString;
QString moonAmbientString;
QString backgroundAmbientString=QString("%1").arg(ambientBrightness,
6, 'f', 4);
QString directionalSourceString;
//assume light=sun for a start.
Vec3d lightPosition = sunPosition;
directionalSourceString="(Sun, below horiz.)";
//calculate emissive factor
if(l!=NULL)
{
if(requiresCubemap && lazyDrawing)
{ {
emissiveFactor = l->getTargetLightscapeBrightness(); if(!currentLoadScene.isValid)
loadScenery3dByID(id);
return;
} }
else else
{ {
//use an interpolated value for smooth fade in/out flagEnabled = false;
emissiveFactor = l->getEffectiveLightscapeBrightness showMessage(q_("Please load a scene first!"));
(); emit enableSceneChanged(false);
} }
} }
else if(enable!=flagEnabled)
{ {
// I don't know if this can ever happen, but in this case, flagEnabled=enable;
// directly use the same model as LandscapeMgr::update uses if (renderer->getCubemapSize()==0)
for the lightscapeBrightness
emissiveFactor = 0.0f;
if (sunPosition[2]<-0.14f) emissiveFactor=1.0f;
else if (sunPosition[2]<-0.05f) emissiveFactor = 1.0f-(sunPo
sition[2]+0.14)/(-0.05+0.14);
}
// calculate ambient light
if(sinSunAngle > -0.3f) // sun above -18 deg?
{
ambientBrightness += qMin(0.3f, sinSunAngle+0.3f);
sunAmbientString=QString("%1").arg(qMin(0.3f, sinSunAngle+0.
3f), 6, 'f', 4);
}
else
sunAmbientString=QString("0.0");
if ((sinMoonAngle>0.0f) && (sinSunAngle<0.0f))
{
ambientBrightness += sqrt(sinMoonAngle * ((std::cos(moonPhas
eAngle)+1)/2)) * LUNAR_BRIGHTNESS_FACTOR;
moonAmbientString=QString("%1").arg(sqrt(sinMoonAngle * ((st
d::cos(moonPhaseAngle)+1)/2)) * LUNAR_BRIGHTNESS_FACTOR);
}
else
moonAmbientString=QString("0.0");
// Now find shadow caster + directional light, if any:
if (sinSunAngle>-0.1f)
{
directionalBrightness=qMin(0.7f, std::sqrt(sinSunAngle+0.1f)
); // limit to 0.7 in order to keep total below 1.
//redundant
//lightPosition = sunPosition;
if (shaderParameters.shadows) shadowcaster = Sun;
directionalSourceString="Sun";
}
/* else if (sinSunAngle> -0.3f) // sun above -18: create shadowles
s directional pseudo-light from solar azimuth
{
directionalBrightness=qMin(0.7, sinSunAngle+0.3); // limit to 0.7 in
order to keep total below 1.
lightsourcePosition.set(sunPosition.v[0], sunPosition.v[1], sinSunAn
gle+0.3);
directionalSourceString="(Sun, below hor.)";
}*/
// "else" is required now, else we have lunar shadow with sun above
horizon...
else if (sinMoonAngle>0.0f)
{
float moonBrightness = std::sqrt(sinMoonAngle) * ((std::cos(
moonPhaseAngle)+1.0f)/2.0f) * LUNAR_BRIGHTNESS_FACTOR;
moonBrightness -= (ambientBrightness-0.05f)/2.0f;
moonBrightness = qMax(0.0f,moonBrightness);
if(sinSunAngle<0.0f && sinSunAngle >-0.1f)
{
//interpolate directional brightness between sun and
moon
float t = sinSunAngle/-0.1f;
directionalBrightness = (1.0f-t) * directionalBright
ness + t*moonBrightness;
/*
//uncomment to also move the light direction linearly to
avoid possible jarring transitions
//but that does not seem to have much of an effect
if(moonBrightness>0)
{
lightPosition = (1.0-t) * sunPosition + (double)
t * moonPosition;
lightPosition.normalize();
}
*/
}
else if (moonBrightness >0)
{
directionalBrightness = moonBrightness;
lightPosition = moonPosition;
if (shaderParameters.shadows) shadowcaster = Moon;
directionalSourceString="Moon";
} else directionalSourceString="Moon";
//Alternately, construct a term around lunar brightness, lik
e
// directionalBrightness=(mag/-10)
}
else if (sinVenusAngle>0.0f)
{
float venusBrightness = std::sqrt(sinVenusAngle)*((std::cos(
venusPhaseAngle)+1)/2) * VENUS_BRIGHTNESS_FACTOR;
venusBrightness -= (ambientBrightness-0.05)/2.0f;
venusBrightness = qMax(0.0f, venusBrightness);
if(sinSunAngle<0.0f && sinSunAngle >-0.1f)
{ {
//interpolate directional brightness between sun and //TODO FS: remove this?
venus if (flagEnabled)
float t = sinSunAngle/-0.1f; {
directionalBrightness = (1.0f-t) * directionalBright oldProjectionType= StelApp::getInstance().ge
ness + t*venusBrightness; tCore()->getCurrentProjectionType();
/* StelApp::getInstance().getCore()->setCurrent
//uncomment to also move the light direction linearly to ProjectionType(StelCore::ProjectionPerspective);
avoid possible jarring transitions }
//but that does not seem to have much of an effect else
if(venusBrightness>0) StelApp::getInstance().getCore()->setCurrent
{ ProjectionType(oldProjectionType);
lightPosition = (1.0-t) * sunPosition + (double)
t * venusPosition;
lightPosition.normalize();
}
*/
}
else if (venusBrightness > 0.0f)
{
directionalBrightness = venusBrightness;
lightPosition = venusPosition;
if (shaderParameters.shadows) shadowcaster = Venus;
directionalSourceString="Venus";
} else directionalSourceString="(Venus, flooded by ambient)"
;
//Alternately, construct a term around Venus brightness, lik
e
// directionalBrightness=(mag/-100)
}
else if(sinSunAngle<0.0f && sinSunAngle >-0.1f)
{
//let sunlight fall off to zero
float t = sinSunAngle/-0.1f;
directionalBrightness = (1.0f - t) * directionalBrightness;
}
//convert to float
lightsourcePosition.set(lightPosition.v[0], lightPosition.v[1], ligh
tPosition.v[2]);
float landscapeOpacity = 0.0f;
//check landscape occlusion, modify directional if needed
if(directionalBrightness>0)
{
if(l)
{
//TODO the changes are currently rather harsh, find
a better method (like angular distance of light source to horizon, or bitma
p interpolation for the alpha values)
landscapeOpacity = l->getOpacity(lightPosition);
//lerp between the determined opacity and 1.0, depen
ding on landscape fade (visibility)
float fadeValue = 1.0f + l->getEffectiveLandFadeValu
e() * (-landscapeOpacity);
directionalBrightness *= fadeValue;
} }
}
//TODO remove the string stuff from this method... emit enableSceneChanged(flagEnabled);
// DEBUG: Prepare output message
QString shadowCasterName;
switch (shadowcaster) {
case None: shadowCasterName="None"; break;
case Sun: shadowCasterName="Sun"; break;
case Moon: shadowCasterName="Moon"; break;
case Venus: shadowCasterName="Venus"; break;
default: shadowCasterName="Error!!!";
} }
lightMessage=QString("Ambient: %1 Directional: %2. Shadows cast by:
%3 from %4/%5/%6")
.arg(ambientBrightness, 6, 'f', 4).arg(directionalBr
ightness, 6, 'f', 4)
.arg(shadowCasterName).arg(lightsourcePosition.v[0],
6, 'f', 4)
.arg(lightsourcePosition.v[1], 6, 'f', 4).arg(lights
ourcePosition.v[2], 6, 'f', 4);
lightMessage2=QString("Contributions: Ambient Sun: %1, Moon: %2,
Background+^L: %3").arg(sunAmbientString).arg(moonAmbientString).arg(backg
roundAmbientString);
lightMessage3=QString(" Directional %1 by: %2, emissiv
e factor: %3, landscape opacity: %4").arg(directionalBrightness, 6, 'f', 4)
.arg(directionalSourceString).arg(emissiveFactor).arg(landscapeOpacity);
return shadowcaster;
} }
void Scenery3d::calcCubeMVP() bool Scenery3d::getEnablePixelLighting() const
{ {
QMatrix4x4 tmp; return renderer->getPixelLightingEnabled();
for(int i = 0;i<6;++i)
{
tmp = cubeRotation[i];
tmp.translate(absolutePosition.v[0], absolutePosition.v[1],
absolutePosition.v[2]);
cubeMVP[i] = projectionMatrix * tmp;
}
} }
void Scenery3d::renderIntoCubemapGeometryShader() void Scenery3d::setEnablePixelLighting(const bool val)
{ {
//single FBO if(val != getEnablePixelLighting())
glBindFramebuffer(GL_FRAMEBUFFER,cubeFBO); {
if(!val)
//Hack: because the modelviewmatrix is used for lighting in shader, {
but we dont want to perform MV transformations 6 times, setEnableBumps(false);
// we just set the position because that currently is all that is ne setEnableShadows(false);
edeed for correct lighting }
modelViewMatrix.setToIdentity(); showMessage(QString(q_("Per-Pixel shading %1.")).arg(val? qc
modelViewMatrix.translate(absolutePosition.v[0], absolutePosition.v[ _("on","enable") : qc_("off","disable")));
1], absolutePosition.v[2]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//render all 6 faces at once renderer->setPixelLightingEnabled(val);
shaderParameters.geometryShader = true; conf->setValue(S3D_CONFIG_PREFIX + "/flag_pixel_lighting", v
//calculate the final required matrices for each face al);
calcCubeMVP(); emit enablePixelLightingChanged(val);
drawArrays(true,true); }
shaderParameters.geometryShader = false;
} }
void Scenery3d::renderShadowMapsForFace(int face) bool Scenery3d::getEnableShadows() const
{ {
//extract view dir from the MV matrix return renderer->getShadowsEnabled();
QVector4D viewDir = -cubeRotation[face].row(2);
//somewhere, there are problems when the view direction points exact
ly up or down, causing missing shadows
//this is NOT fixed by choosing a different up vector here as could
be expected
//the problem seems to occur during final rendering because shadowma
p textures look alright and the scaling values seem valid
//for now, fix this by adding a tiny value to X in these cases
adjustShadowFrustum(viewPos,Vec3d(face>3?viewDir[0]+0.000001:viewDir
[0],viewDir[1],viewDir[2]),Vec3d(0,0,1),90.0f,1.0f);
//render shadowmap
if(!renderShadowMaps())
return;
//gl state + viewport must be reset
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glViewport(0, 0, cubemapSize, cubemapSize);
} }
void Scenery3d::renderIntoCubemapSixPasses() void Scenery3d::setEnableShadows(const bool enableShadows)
{ {
//store current projection (= 90° cube projection) if(enableShadows != getEnableShadows())
QMatrix4x4 squareProjection = projectionMatrix;
if(needsMovementUpdate && updateOnlyDominantOnMoving)
{ {
if(shaderParameters.shadows && fullCubemapShadows) if (renderer->getShadowmapSize() && getEnablePixelLighting() )
{ {
//in the BASIC and FULL modes, the shadow frustum ne showMessage(QString(q_("Shadows %1.")).arg(enableSha
eds to be adapted to the cube side dows? qc_("on","enable") : qc_("off","disable")));
renderShadowMapsForFace(dominantFace); renderer->setShadowsEnabled(enableShadows);
//projection needs to be reset emit enableShadowsChanged(enableShadows);
projectionMatrix = squareProjection; } else
}
//update only the dominant face
glBindFramebuffer(GL_FRAMEBUFFER, cubeSideFBO[dominantFace])
;
modelViewMatrix = cubeRotation[dominantFace];
modelViewMatrix.translate(absolutePosition.v[0], absolutePos
ition.v[1], absolutePosition.v[2]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawArrays(true,true);
if(updateSecondDominantOnMoving)
{ {
if(shaderParameters.shadows && fullCubemapShadows) showMessage(QString(q_("Shadows deactivated or not p
{ ossible.")));
//in the BASIC and FULL modes, the shadow fr renderer->setShadowsEnabled(false);
ustum needs to be adapted to the cube side emit enableShadowsChanged(false);
renderShadowMapsForFace(secondDominantFace);
//projection needs to be reset
projectionMatrix = squareProjection;
}
//update also the second-most dominant face
glBindFramebuffer(GL_FRAMEBUFFER, cubeSideFBO[second
DominantFace]);
modelViewMatrix = cubeRotation[secondDominantFace];
modelViewMatrix.translate(absolutePosition.v[0], abs
olutePosition.v[1], absolutePosition.v[2]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawArrays(true,true);
} }
}
else
{
//traditional 6-pass version
for(int i=0;i<6;++i)
{
if(shaderParameters.shadows && fullCubemapShadows)
{
//in the BASIC and FULL modes, the shadow fr
ustum needs to be adapted to the cube side
renderShadowMapsForFace(i);
//projection needs to be reset
projectionMatrix = squareProjection;
}
//bind a single side of the cube
glBindFramebuffer(GL_FRAMEBUFFER, cubeSideFBO[i]);
modelViewMatrix = cubeRotation[i]; conf->setValue(S3D_CONFIG_PREFIX + "/flag_shadow",getEnableS
modelViewMatrix.translate(absolutePosition.v[0], abs hadows());
olutePosition.v[1], absolutePosition.v[2]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawArrays(true,true);
}
} }
} }
void Scenery3d::generateCubeMap() bool Scenery3d::getUseSimpleShadows() const
{ {
//recalculate lighting info return renderer->getUseSimpleShadows();
calculateLighting(); }
//do shadow pass void Scenery3d::setUseSimpleShadows(const bool simpleShadows)
//only calculate shadows if enabled {
if(shaderParameters.shadows) renderer->setUseSimpleShadows(simpleShadows);
{
//shadow caster info only needs to be calculated once
calculateShadowCaster();
//GS mode only supports the perspective shadows conf->setValue(S3D_CONFIG_PREFIX + "/flag_shadow_simple",simpleShado
if(!fullCubemapShadows || cubemappingMode == S3DEnum::CM_CUB ws);
EMAP_GSACCEL) emit useSimpleShadowsChanged(simpleShadows);
{ }
//in this mode, shadow frusta are calculated the sam
e as in perspective mode
float fov = altAzProjector->getFov();
float aspect = (float)altAzProjector->getViewportWid
th() / (float)altAzProjector->getViewportHeight();
adjustShadowFrustum(viewPos,mainViewDir,mainViewUp,f
ov,aspect);
if(!renderShadowMaps())
return; //shadow map rendering failed, do an
early abort
}
}
//setup projection matrix - this is a 90-degree perspective with asp bool Scenery3d::getEnableBumps() const
ect 1.0 {
projectionMatrix.setToIdentity(); return renderer->getBumpsEnabled();
projectionMatrix.perspective(90.0f,1.0f,currentScene.camNearZ,curren }
tScene.camFarZ);
//set opengl viewport to the size of cubemap
glViewport(0, 0, cubemapSize, cubemapSize);
//set GL state - we want depth test + culling
glEnable(GL_DEPTH_TEST);
//glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
if(cubemappingMode == S3DEnum::CM_CUBEMAP_GSACCEL) void Scenery3d::setEnableBumps(const bool enableBumps)
{ {
//In this mode, only the "perspective" shadow mode can be us if(enableBumps != getEnableBumps())
ed (otherwise it would need up to 6*4 shadowmaps at once)
renderIntoCubemapGeometryShader();
}
else
{ {
renderIntoCubemapSixPasses(); showMessage(QString(q_("Surface bumps %1.")).arg(enableBumps
} ? qc_("on","enable") : qc_("off","disable")));
renderer->setBumpsEnabled(enableBumps);
//cubemap fbo must be released
glBindFramebuffer(GL_FRAMEBUFFER,defaultFBO);
//reset GL state conf->setValue(S3D_CONFIG_PREFIX + "/flag_bumpmap", enableBu
glDepthMask(GL_FALSE); mps);
glDisable(GL_DEPTH_TEST); emit enableBumpsChanged(enableBumps);
glDisable(GL_CULL_FACE);
//reset viewport (see StelPainter::setProjector)
const Vec4i& vp = altAzProjector->getViewport();
glViewport(vp[0], vp[1], vp[2], vp[3]);
if(needsCubemapUpdate)
{
lastCubemapUpdate = core->getJD();
lastCubemapUpdateRealTime = QDateTime::currentMSecsSinceEpoc
h();
} }
} }
void Scenery3d::drawFromCubeMap() S3DEnum::ShadowFilterQuality Scenery3d::getShadowFilterQuality() const
{ {
QOpenGLShaderProgram* cubeShader; return renderer->getShadowFilterQuality();
if(cubemappingMode>=S3DEnum::CM_CUBEMAP)
cubeShader = shaderManager.getCubeShader();
else
cubeShader = shaderManager.getTextureShader();
cubeShader->bind();
//We simulate the generate behavoir of drawStelVertexArray ourselves
//check if discontinuties exist
//if(altAzProjector->hasDiscontinuity())
//{
//TODO fix similar to StelVertexArray::removeDiscontinuousTriangles
//this may only happen for some projections, and even then it may be
preferable to simply ignore them (as done now) to retain performance
//}
//transform vertices on CPU side - maybe we could do this multithrea
ded, kicked off at the beginning of the frame?
altAzProjector->project(cubeVertices.count(),cubeVertices.constData(
),transformedCubeVertices.data());
//setup shader params
projectionMatrix = altAzProjector->getProjectionMatrix().convertToQM
atrix();
cubeShader->setUniformValue(shaderManager.uniformLocation(cubeShader
,ShaderMgr::UNIFORM_MAT_PROJECTION), projectionMatrix);
cubeShader->setUniformValue(shaderManager.uniformLocation(cubeShader
,ShaderMgr::UNIFORM_TEX_DIFFUSE),0);
cubeVertexBuffer.bind();
if(cubemappingMode>=S3DEnum::CM_CUBEMAP)
cubeShader->setAttributeBuffer(ShaderMgr::ATTLOC_TEXCOORD,GL
_FLOAT,0,3);
else // 2D tex coords are stored in the same buffer, but with an off
set
cubeShader->setAttributeBuffer(ShaderMgr::ATTLOC_TEXCOORD,GL
_FLOAT,cubeVertices.size() * sizeof(Vec3f),2);
cubeShader->enableAttributeArray(ShaderMgr::ATTLOC_TEXCOORD);
cubeVertexBuffer.release();
//upload transformed vertex data
transformedCubeVertexBuffer.bind();
transformedCubeVertexBuffer.allocate(transformedCubeVertices.constDa
ta(), transformedCubeVertices.size() * sizeof(Vec3f));
cubeShader->setAttributeBuffer(ShaderMgr::ATTLOC_VERTEX, GL_FLOAT, 0
,3);
cubeShader->enableAttributeArray(ShaderMgr::ATTLOC_VERTEX);
transformedCubeVertexBuffer.release();
glEnable(GL_BLEND);
//note that GL_ONE is required here for correct blending (see drawAr
rays)
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
//depth test and culling is necessary for correct display,
//because the cube faces can be projected in quite "weird" ways
glEnable(GL_DEPTH_TEST);
//glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glClear(GL_DEPTH_BUFFER_BIT);
cubeIndexBuffer.bind();
glActiveTexture(GL_TEXTURE0);
if(cubemappingMode>=S3DEnum::CM_CUBEMAP)
{
//can render in a single draw call
glBindTexture(GL_TEXTURE_CUBE_MAP,cubeMapCubeTex);
glDrawElements(GL_TRIANGLES,cubeIndexCount,GL_UNSIGNED_SHORT
, NULL);
}
else
{
//use 6 drawcalls
int faceIndexCount = cubeIndexCount / 6;
for(int i =0;i<6;++i)
{
glBindTexture(GL_TEXTURE_2D, cubeMapTex[i]);
glDrawElements(GL_TRIANGLES,faceIndexCount, GL_UNSIG
NED_SHORT, (const GLvoid*)(i * faceIndexCount * sizeof(short)));
}
}
cubeIndexBuffer.release();
cubeShader->disableAttributeArray(ShaderMgr::ATTLOC_TEXCOORD);
cubeShader->disableAttributeArray(ShaderMgr::ATTLOC_VERTEX);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
cubeShader->release();
} }
void Scenery3d::drawDirect() // for Perspective Projection only! void Scenery3d::setShadowFilterQuality(const S3DEnum::ShadowFilterQuality v al)
{ {
//calculate standard perspective projection matrix, use QMatrix4x4 for S3DEnum::ShadowFilterQuality oldVal = getShadowFilterQuality();
that if(oldVal == val)
float fov = altAzProjector->getFov(); return;
float aspect = (float)altAzProjector->getViewportWidth() / (float)altAz
Projector->getViewportHeight();
//calc modelview transform
QMatrix4x4 mvMatrix = altAzProjector->getModelViewTransform()->getAppro
ximateLinearTransfo().convertToQMatrix();
mvMatrix.optimize(); //may make inversion faster?
//recalculate lighting info
calculateLighting();
//do shadow pass
//only calculate shadows if enabled
if(shaderParameters.shadows)
{
calculateShadowCaster();
//no need to extract view information, use the direction from st
ellarium
adjustShadowFrustum(viewPos,mainViewDir,mainViewUp,fov,aspect);
//this call modifies projection + mv matrices, so we have to set
them afterwards
if(!renderShadowMaps())
return; //shadow map rendering failed, do an early abort
}
mvMatrix.translate(absolutePosition.v[0],absolutePosition.v[1],absolute
Position.v[2]);
//set final rendering matrices
modelViewMatrix = mvMatrix;
projectionMatrix.setToIdentity();
//without viewport offset, you could simply call this:
//projectionMatrix.perspective(fov,aspect,currentScene.camNearZ,current
Scene.camFarZ);
//these 2 lines replicate gluPerspective with glFrustum
float fH = qTan( fov / 360.0f * M_PI ) * currentScene.camNearZ;
float fW = fH * aspect;
//apply offset values
Vec2f vp = altAzProjector->getViewportCenterOffset();
float horizOffset = 2.0 * fW * vp[0];
float vertOffset = - 2.0 * fH * vp[1];
//final projection matrix
projectionMatrix.frustum(-fW + horizOffset, fW + horizOffset,
-fH + vertOffset, fH + vertOffset,
currentScene.camNearZ, currentScene.camFarZ);
//depth test needs enabling, clear depth buffer, color buffer already c
ontains background so it stays
glEnable(GL_DEPTH_TEST);
//glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
glClear(GL_DEPTH_BUFFER_BIT);
//enable backface culling for increased performance
glEnable(GL_CULL_FACE);
//only 1 call needed here
drawArrays(true);
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
conf->setValue(S3D_CONFIG_PREFIX + "/shadow_filter_quality",val);
renderer->setShadowFilterQuality(val);
emit shadowFilterQualityChanged(val);
} }
void Scenery3d::drawWithCubeMap() bool Scenery3d::getEnablePCSS() const
{ {
if(needsCubemapUpdate || needsMovementUpdate) return renderer->getPCSS();
{
//lazy redrawing: update cubemap in slower intervals
generateCubeMap();
}
drawFromCubeMap();
} }
Vec3d Scenery3d::getCurrentGridPosition() const void Scenery3d::setEnablePCSS(const bool val)
{ {
// this is the observer position (camera eye position) in model-grid renderer->setPCSS(val);
coordinates, relative to the origin conf->setValue(S3D_CONFIG_PREFIX + "/flag_pcss",val);
Vec3d pos=currentScene.zRotateMatrix.inverse()* (-absolutePosition);
// this is the observer position (camera eye position) in grid coord
inates, e.g. Gauss-Krueger or UTM.
pos+= currentScene.modelWorldOffset;
//subtract the eye_height to get the foot position emit enablePCSSChanged(val);
pos[2]-=eye_height;
return pos;
} }
void Scenery3d::setGridPosition(Vec3d pos) S3DEnum::CubemappingMode Scenery3d::getCubemappingMode() const
{ {
//this is basically the same as getCurrentGridPosition(), but in rev return renderer->getCubemappingMode();
erse
pos[2]+=eye_height;
pos-=currentScene.modelWorldOffset;
//calc opengl position
absolutePosition = - (currentScene.zRotateMatrix * pos);
//reset cube map time
invalidateCubemap();
} }
void Scenery3d::drawCoordinatesText() void Scenery3d::setCubemappingMode(const S3DEnum::CubemappingMode val)
{ {
StelPainter painter(altAzProjector); renderer->setCubemappingMode(val);
painter.setFont(debugTextFont); S3DEnum::CubemappingMode realVal = renderer->getCubemappingMode();
painter.setColor(1.0f,0.0f,1.0f);
float screen_x = altAzProjector->getViewportWidth() - 240.0f;
float screen_y = altAzProjector->getViewportHeight() - 60.0f;
QString str;
Vec3d gridPos = getCurrentGridPosition(); if(val!=realVal)
showMessage(q_("Selected cubemap mode not supported, falling
back to '6 Textures'"));
// problem: long grid names! conf->setValue(S3D_CONFIG_PREFIX + "/cubemap_mode",realVal);
painter.drawText(altAzProjector->getViewportWidth()-10-qMax(240, painte emit cubemappingModeChanged(realVal);
r.getFontMetrics().boundingRect(currentScene.gridName).width()),
screen_y, currentScene.gridName);
screen_y -= 17.0f;
str = QString("East: %1m").arg(gridPos[0], 10, 'f', 2);
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = QString("North: %1m").arg(gridPos[1], 10, 'f', 2);
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = QString("Height: %1m").arg(gridPos[2], 10, 'f', 2);
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = QString("Eye: %1m").arg(eye_height, 10, 'f', 2);
painter.drawText(screen_x, screen_y, str);
/*// DEBUG AIDS:
screen_y -= 15.0f;
str = QString("model_X:%1m").arg(model_pos[0], 10, 'f', 2);
painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
str = QString("model_Y:%1m").arg(model_pos[1], 10, 'f', 2);
painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
str = QString("model_Z:%1m").arg(model_pos[2], 10, 'f', 2);
painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
str = QString("abs_X: %1m").arg(absolutePosition.v[0], 10, 'f', 2);
painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
str = QString("abs_Y: %1m").arg(absolutePosition.v[1], 10, 'f', 2);
painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
str = QString("abs_Z: %1m").arg(absolutePosition.v[2], 10, 'f', 2);
painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
str = QString("groundNullHeight: %1m").arg(groundNullHeight, 7, 'f', 2)
;
painter.drawText(screen_x, screen_y, str);
//*/
} }
void Scenery3d::drawDebug() bool Scenery3d::getUseFullCubemapShadows() const
{ {
//frustum/box debug rendering only on desktop GL return renderer->getUseFullCubemapShadows();
#ifndef QT_OPENGL_ES_2 }
if(!shaderParameters.openglES)
{
QOpenGLShaderProgram* debugShader = shaderManager.getDebugSh
ader();
if(debugShader)
{
debugShader->bind();
//ensure that opengl matrix stack is empty
glExtFuncs->glMatrixMode(GL_MODELVIEW);
glExtFuncs->glLoadIdentity();
glExtFuncs->glMatrixMode(GL_PROJECTION);
glExtFuncs->glLoadIdentity();
//set mvp
SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_MAT_MVP,p
rojectionMatrix * modelViewMatrix);
SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR
,QVector4D(1.0f,1.0f,1.0f,1.0f));
sceneBoundingBox.render();
SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR
,QVector4D(0.4f,0.4f,0.4f,1.0f));
//objModel->renderAABBs();
SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR
,QVector4D(1.0f,1.0f,1.0f,1.0f));
if(fixShadowData) void Scenery3d::setUseFullCubemapShadows(const bool useFullCubemapShadows)
{ {
camFrustShadow.drawFrustum(); renderer->setUseFullCubemapShadows(useFullCubemapShadows);
/*
SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR
,QVector4D(1.0f,0.0f,1.0f,1.0f));
frustumArray.at(0).drawFrustum();
SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR
,QVector4D(0.0f,1.0f,0.0f,1.0f));
focusBodies.at(0).render();
SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR
,QVector4D(0.0f,1.0f,1.0f,1.0f));
focusBodies.at(0).debugBox.render();
SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR
,QVector4D(1.0f,0.0f,0.0f,1.0f));
focusBodies.at(1).render();
SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR
,QVector4D(1.0f,0.0f,1.0f,1.0f));
focusBodies.at(1).debugBox.render();
*/
}
debugShader->release(); conf->setValue(S3D_CONFIG_PREFIX + "/flag_cubemap_fullshadows",useFu
} llCubemapShadows);
else emit useFullCubemapShadowsChanged(useFullCubemapShadows);
{ }
qWarning()<<"[Scenery3d] Cannot use debug shader, pr
obably on OpenGL ES context";
}
}
#endif
StelPainter painter(altAzProjector); bool Scenery3d::getEnableDebugInfo() const
painter.setFont(debugTextFont); {
painter.setColor(1.f,0.f,1.f,1.f); return renderer->getDebugEnabled();
// For now, these messages print light mixture values. }
painter.drawText(20, 160, lightMessage);
painter.drawText(20, 145, lightMessage2);
painter.drawText(20, 130, lightMessage3);
painter.drawText(20, 115, QString("Torch range %1, brightness %2/%3/%4"
).arg(torchRange).arg(lightInfo.torchDiffuse[0]).arg(lightInfo.torchDiffuse
[1]).arg(lightInfo.torchDiffuse[2]));
QString str = QString("BB: %1/%2/%3 %4/%5/%6").arg(sceneBoundingBox.min
.v[0], 7, 'f', 2).arg(sceneBoundingBox.min.v[1], 7, 'f', 2).arg(sceneBoundi
ngBox.min.v[2], 7, 'f', 2)
.arg(sceneBoundingBox.max.v[0], 7, 'f', 2).arg(sceneBoun
dingBox.max.v[1], 7, 'f', 2).arg(sceneBoundingBox.max.v[2], 7, 'f', 2);
painter.drawText(10, 100, str);
// PRINT OTHER MESSAGES HERE:
float screen_x = altAzProjector->getViewportWidth() - 500.0f;
float screen_y = altAzProjector->getViewportHeight() - 300.0f;
//Show some debug aids
if(debugEnabled)
{
float debugTextureSize = 128.0f;
float screen_x = altAzProjector->getViewportWidth() - debugTextureSi
ze - 30;
float screen_y = altAzProjector->getViewportHeight() - debugTextureS
ize - 30;
if(shaderParameters.shadows) void Scenery3d::setEnableDebugInfo(const bool debugEnabled)
{
if(debugEnabled != getEnableDebugInfo())
{ {
QString cap("SM %1"); renderer->setDebugEnabled(debugEnabled);
emit enableDebugInfoChanged(debugEnabled);
for(int i=0; i<shaderParameters.frustumSplits; i++)
{
painter.drawText(screen_x+70, screen_y+130, cap.arg(
i));
glBindTexture(GL_TEXTURE_2D, shadowMapsArray[i]);
painter.drawSprite2dMode(screen_x, screen_y, debugTe
xtureSize);
int tmp = screen_y - debugTextureSize-30;
painter.drawText(screen_x-125, tmp, QString("cam n/f
: %1/%2").arg(frustumArray[i].zNear, 7, 'f', 2).arg(frustumArray[i].zFar, 7
, 'f', 2));
painter.drawText(screen_x-125, tmp-15.0f, QString("u
v scale: %1/%2").arg(shadowFrustumSize[i].x(), 7, 'f', 2).arg(shadowFrustum
Size[i].y(),7,'f',2));
painter.drawText(screen_x-125, tmp-30.0f, QString("o
rtho n/f: %1/%2").arg(shadowFrustumSize[i].z(), 7, 'f', 2).arg(shadowFrustu
mSize[i].w(),7,'f',2));
screen_x -= 290;
}
painter.drawText(screen_x+165.0f, screen_y-215.0f, QString("
Splitweight: %1").arg(currentScene.shadowSplitWeight, 3, 'f', 2));
painter.drawText(screen_x+165.0f, screen_y-230.0f, QString("
Light near/far: %1/%2").arg(lightOrthoNear, 3, 'f', 2).arg(lightOrthoFar, 3
, 'f', 2));
} }
}
} bool Scenery3d::getEnableLocationInfo() const
{
screen_y -= 100.f; return renderer->getLocationInfoEnabled();
str = QString("Last frame stats:"); }
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = QString("%1 tris, %2 mdls").arg(drawnTriangles).arg(drawnModels);
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = QString("%1 mats, %2 shaders").arg(materialSwitches).arg(shaderSw
itches);
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = "View Pos";
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = QString("%1 %2 %3").arg(viewPos.v[0], 7, 'f', 2).arg(viewPos.v[1]
, 7, 'f', 2).arg(viewPos.v[2], 7, 'f', 2);
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = "View Dir, dominant faces";
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = QString("%1 %2 %3, %4/%5").arg(mainViewDir.v[0], 7, 'f', 2).arg(m
ainViewDir.v[1], 7, 'f', 2).arg(mainViewDir.v[2], 7, 'f', 2).arg(dominantFa
ce).arg(secondDominantFace);
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = "View Up";
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = QString("%1 %2 %3").arg(mainViewUp.v[0], 7, 'f', 2).arg(mainViewU
p.v[1], 7, 'f', 2).arg(mainViewUp.v[2], 7, 'f', 2);
painter.drawText(screen_x, screen_y, str);
if(requiresCubemap)
{
screen_y -= 15.0f;
str = QString("Last cubemap update: %1ms ago").arg(QDateTime::cu
rrentMSecsSinceEpoch() - lastCubemapUpdateRealTime);
painter.drawText(screen_x, screen_y, str);
screen_y -= 15.0f;
str = QString("Last cubemap update JDAY: %1").arg(qAbs(core->get
JD()-lastCubemapUpdate) * StelCore::ONE_OVER_JD_SECOND);
painter.drawText(screen_x, screen_y, str);
}
screen_y -= 30.0f;
str = QString("Venus: %1").arg(lightInfo.lightSource == Venus);
painter.drawText(screen_x, screen_y, str);
}
void Scenery3d::determineFeatureSupport()
{
QOpenGLContext* ctx = QOpenGLContext::currentContext();
// graphics hardware without FrameBufferObj extension cannot use the
cubemap rendering and shadow mapping.
// In this case, set cubemapSize to 0 to signal auto-switch to persp
ective projection.
// OpenGL ES2 has framebuffers in the Spec
if ( !ctx->hasExtension("GL_EXT_framebuffer_object") && !ctx->isOpen
GLES() ) {
//TODO FS: it seems like the current stellarium requires a w
orking framebuffer extension anyway, so skip this check?
qWarning() << "[Scenery3d] Your hardware does not support EX
T_framebuffer_object.";
qWarning() << "[Scenery3d] Shadow mapping disabled, and disp
lay limited to perspective projection.";
setCubemapSize(0); void Scenery3d::setEnableLocationInfo(const bool enableLocationInfo)
setShadowmapSize(0); {
} if(enableLocationInfo != getEnableLocationInfo())
else
{ {
//determine maximum framebuffer size as minimum of texture, renderer->setLocationInfoEnabled(enableLocationInfo);
viewport and renderbuffer size
GLint texSize,viewportSize[2],rbSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewportSize);
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &rbSize);
qDebug()<<"[Scenery3d] Maximum texture size:"<<texSize; conf->setValue(S3D_CONFIG_PREFIX + "/flag_location_info",ena
qDebug()<<"[Scenery3d] Maximum viewport dims:"<<viewportSize bleLocationInfo);
[0]<<viewportSize[1];
qDebug()<<"[Scenery3d] Maximum renderbuffer size:"<<rbSize;
maximumFramebufferSize = qMin(texSize,qMin(rbSize,qMin(viewp emit enableLocationInfoChanged(enableLocationInfo);
ortSize[0],viewportSize[1])));
qDebug()<<"[Scenery3d] Maximum framebuffer size:"<<maximumFr
amebufferSize;
} }
}
QString renderer(reinterpret_cast<const char*>(glGetString(GL_RENDER bool Scenery3d::getEnableTorchLight() const
ER))); {
isANGLE = renderer.contains("ANGLE"); return renderer->getTorchEnabled();
}
//check if GS cubemapping is possible void Scenery3d::setEnableTorchLight(const bool enableTorchLight)
if(QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Geometry,ctx)) //t {
his checks if version >= 3.2 if(enableTorchLight != getEnableTorchLight())
{ {
this->supportsGSCubemapping = true; renderer->setTorchEnabled(enableTorchLight);
qDebug()<<"[Scenery3d] Geometry shader supported";
}
else
qDebug()<<"[Scenery3d] Geometry shader not supported on this
hardware";
//Query how many texture units we have at disposal in a fragment sha conf->setValue(S3D_CONFIG_PREFIX + "/torch_enabled",enableTo
der rchLight);
//we currently need 8 in the worst case: diffuse, emissive, bump, he
ight + 4x shadowmap
GLint texUnits,combUnits;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &texUnits);
qDebug() << "[Scenery3d] GL_MAX_TEXTURE_IMAGE_UNITS:" << texUnits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &combUnits);
qDebug() << "[Scenery3d] GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:" << co
mbUnits;
if(texUnits < 8 || combUnits < 8)
{
qWarning()<<"Insufficient texture units available for all ef
fects, should have at least 8!";
}
if(shaderParameters.openglES) emit enableTorchLightChanged(enableTorchLight);
{
//shadows in our implementation require depth textures
if(ctx->hasExtension("GL_OES_depth_texture") ||
ctx->hasExtension("GL_ANGLE_depth_texture"))
{
supportsShadows = true;
qDebug()<<"[Scenery3d] Shadows are supported";
}
else
{
supportsShadows = false;
qDebug()<<"[Scenery3d] Shadows are not supported on
this hardware";
}
//shadow filtering is completely disabled for now on ES
supportsShadowFiltering = false;
}
else
{
//assume everything is available on Desktop GL for now (shou
ld be ok on GL>2.0 as Stellarium base requires)
supportsShadows = true;
supportsShadowFiltering = true;
} }
} }
void Scenery3d::init() float Scenery3d::getTorchStrength() const
{ {
initializeOpenGLFunctions(); return renderer->getTorchBrightness();
OBJ::setupGL(); }
QOpenGLContext* ctx = QOpenGLContext::currentContext();
Q_ASSERT(ctx);
#ifndef QT_OPENGL_ES_2
//initialize additional functions needed and not provided through St
elOpenGL
glExtFuncs = new GLExtFuncs();
glExtFuncs->init(ctx);
#endif
//save opengl ES state void Scenery3d::setTorchStrength(const float torchStrength)
shaderParameters.openglES = ctx->isOpenGLES(); {
renderer->setTorchBrightness(torchStrength);
//find out what features we can enable conf->setValue(S3D_CONFIG_PREFIX + "/torch_brightness",torchStrength
determineFeatureSupport(); );
cubeVertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw); emit torchStrengthChanged(torchStrength);
cubeVertexBuffer.create(); }
transformedCubeVertexBuffer.setUsagePattern(QOpenGLBuffer::StreamDra
w);
transformedCubeVertexBuffer.create();
cubeIndexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
cubeIndexBuffer.create();
//enable seamless cubemapping if HW supports it
if(ctx->hasExtension("GL_ARB_seamless_cube_map"))
{
#ifdef GL_TEXTURE_CUBE_MAP_SEAMLESS
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
qDebug()<<"[Scenery3d] Seamless cubemap filtering enabled";
#endif
}
//shadow map init happens on first usage of shadows float Scenery3d::getTorchRange() const
{
return renderer->getTorchRange();
}
//finally, set core to enable update(). void Scenery3d::setTorchRange(const float torchRange)
this->core=StelApp::getInstance().getCore(); {
//init planets renderer->setTorchRange(torchRange);
SolarSystem* ssystem = GETSTELMODULE(SolarSystem);
sun = ssystem->getSun();
moon = ssystem->getMoon();
venus = ssystem->searchByEnglishName("Venus");
landscapeMgr = GETSTELMODULE(LandscapeMgr);
Q_ASSERT(landscapeMgr);
}
void Scenery3d::deleteCubemapping()
{
if(cubeMappingCreated)
{
//delete cube map - we have to check each possible variable
because we dont know which ones are active
//delete in reverse, FBOs first - but it should not matter
if(cubeFBO)
{
glDeleteFramebuffers(1,&cubeFBO);
cubeFBO = 0;
}
if(cubeSideFBO[0]) conf->setValue(S3D_CONFIG_PREFIX+ "/torch_range",torchRange);
{
//we assume if one is created, all have been created
glDeleteFramebuffers(6,cubeSideFBO);
std::fill(cubeSideFBO,cubeSideFBO + 6,0);
}
//delete depth emit torchRangeChanged(torchRange);
if(cubeRB) }
{
glDeleteRenderbuffers(1,&cubeRB);
cubeRB = 0;
}
if(cubeMapCubeDepth) bool Scenery3d::getEnableLazyDrawing() const
{ {
glDeleteTextures(1,&cubeMapCubeDepth); return renderer->getLazyCubemapEnabled();
cubeMapCubeDepth = 0; }
}
//delete colors void Scenery3d::setEnableLazyDrawing(const bool val)
if(cubeMapTex[0]) {
{ showMessage(QString(q_("Lazy cubemapping: %1")).arg(val?q_("enabled"
glDeleteTextures(6,cubeMapTex); ):q_("disabled")));
std::fill(cubeMapTex, cubeMapTex + 6,0); renderer->setLazyCubemapEnabled(val);
}
if(cubeMapCubeTex) conf->setValue(S3D_CONFIG_PREFIX + "/flag_lazy_cubemap",val);
{ emit enableLazyDrawingChanged(val);
glDeleteTextures(1,&cubeMapCubeTex); }
cubeMapCubeTex = 0;
}
cubeMappingCreated = false; double Scenery3d::getLazyDrawingInterval() const
} {
return renderer->getLazyCubemapInterval();
} }
bool Scenery3d::initCubemapping() void Scenery3d::setLazyDrawingInterval(const double val)
{ {
GET_GLERROR() renderer->setLazyCubemapInterval(val);
bool ret = false; conf->setValue(S3D_CONFIG_PREFIX + "/cubemap_lazy_interval",val);
qDebug()<<"[Scenery3d] Initializing cubemap..."; emit lazyDrawingIntervalChanged(val);
}
//remove old cubemap objects if they exist bool Scenery3d::getOnlyDominantFaceWhenMoving() const
deleteCubemapping(); {
bool v1,v2;
renderer->getLazyCubemapUpdateOnlyDominantFaceOnMoving(v1,v2);
return v1;
}
GET_GLERROR() void Scenery3d::setOnlyDominantFaceWhenMoving(const bool val)
{
bool v1,v2;
renderer->getLazyCubemapUpdateOnlyDominantFaceOnMoving(v1,v2);
renderer->setLazyCubemapUpdateOnlyDominantFaceOnMoving(val,v2);
if(cubemapSize<=0) conf->setValue(S3D_CONFIG_PREFIX + "/flag_lazy_dominantface",val);
{ emit onlyDominantFaceWhenMovingChanged(val);
qWarning()<<"[Scenery3d] Cubemapping not supported or disabl }
ed";
parent->showMessage(q_("Your hardware does not support cubem
apping, please switch to 'Perspective' projection!"));
return false;
}
cubeMappingCreated = true; bool Scenery3d::getSecondDominantFaceWhenMoving() const
{
bool v1,v2;
renderer->getLazyCubemapUpdateOnlyDominantFaceOnMoving(v1,v2);
return v2;
}
//last compatibility check before possible crash void Scenery3d::setSecondDominantFaceWhenMoving(const bool val)
if( !isGeometryShaderCubemapSupported() && cubemappingMode == S3DEnu {
m::CM_CUBEMAP_GSACCEL) bool v1,v2;
{ renderer->getLazyCubemapUpdateOnlyDominantFaceOnMoving(v1,v2);
parent->showMessage(q_("Geometry shader is not supported. Fa renderer->setLazyCubemapUpdateOnlyDominantFaceOnMoving(v1,val);
lling back to '6 Textures' mode."));
qWarning()<<"[Scenery3d] GS not supported, fallback to '6 Te
xtures'";
cubemappingMode = S3DEnum::CM_TEXTURES;
}
//TODO the ANGLE version included with Qt 5.4 includes a bug that pr conf->setValue(S3D_CONFIG_PREFIX + "/flag_lazy_seconddominantface",v
events Cubemapping to be used al);
//Remove this if this is ever fixed emit secondDominantFaceWhenMovingChanged(val);
if(isANGLEContext() && cubemappingMode >= S3DEnum::CM_CUBEMAP) }
{
//Fall back to "6 Textures" mode
parent->showMessage(q_("Falling back to '6 Textures' because
of ANGLE bug"));
qWarning()<<"[Scenery3d] On ANGLE, fallback to '6 Textures'"
;
cubemappingMode = S3DEnum::CM_TEXTURES;
}
#ifndef QT_OPENGL_ES_2 void Scenery3d::forceCubemapRedraw()
//if we are on an ES context, it may not be possible to specify text {
ure bitdepth renderer->invalidateCubemap();
bool isEs = QOpenGLContext::currentContext()->isOpenGLES(); }
GLenum colorFormat = isEs ? GL_RGBA : GL_RGBA8;
GLenum depthFormat = isEs ? GL_DEPTH_COMPONENT : GL_DEPTH_COMPONENT2
4;
GLenum rbDepth = isEs ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT24;
#else
GLenum colorFormat = GL_RGBA;
GLenum depthFormat = GL_DEPTH_COMPONENT;
GLenum rbDepth = GL_DEPTH_COMPONENT16;
#endif
glActiveTexture(GL_TEXTURE0); uint Scenery3d::getCubemapSize() const
{
return renderer->getCubemapSize();
}
if(cubemappingMode >= S3DEnum::CM_CUBEMAP) //CUBEMAP or CUBEMAP_GSAC void Scenery3d::setCubemapSize(const uint val)
CEL {
if(val != getCubemapSize())
{ {
//gen cube tex renderer->setCubemapSize(val);
glGenTextures(1,&cubeMapCubeTex);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapCubeTex);
GET_GLERROR()
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_C //hardware may not support the value, get real value set
LAMP_TO_EDGE); uint realVal = renderer->getCubemapSize();
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_C if(realVal==val)
LAMP_TO_EDGE); showMessage(q_("Cubemap size changed"));
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, else
GL_LINEAR); showMessage(QString(q_("Cubemap size not supported,
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, set to %1")).arg(realVal));
GL_LINEAR);
GET_GLERROR()
//create faces conf->setValue(S3D_CONFIG_PREFIX + "/cubemap_size",realVal);
for (int i=0;i<6;++i) emit cubemapSizeChanged(realVal);
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,0,colo
rFormat,
cubemapSize,cubemapSize,0,GL_RGBA,GL_UN
SIGNED_BYTE,NULL);
GET_GLERROR()
}
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
} }
else //TEXTURES mode }
{
//create 6 textures
glGenTextures(6,cubeMapTex);
GET_GLERROR()
for(int i = 0;i<6;++i)
{
glBindTexture(GL_TEXTURE_2D, cubeMapTex[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL
_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL
_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER
, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER
, GL_LINEAR);
GET_GLERROR()
glTexImage2D(GL_TEXTURE_2D,0,colorFormat,
cubemapSize,cubemapSize,0,GL_RGBA,GL_UN
SIGNED_BYTE,NULL);
GET_GLERROR() uint Scenery3d::getShadowmapSize() const
} {
glBindTexture(GL_TEXTURE_2D, 0); return renderer->getShadowmapSize();
} }
//create depth texture/RB void Scenery3d::setShadowmapSize(const uint val)
if(cubemappingMode == S3DEnum::CM_CUBEMAP_GSACCEL) {
if(val != getShadowmapSize())
{ {
//a single cubemap depth texture renderer->setShadowmapSize(val);
glGenTextures(1,&cubeMapCubeDepth);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapCubeDepth);
//this all has probably not much effect on depth processing
because we don't intend to sample
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_C
LAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_C
LAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,
GL_LINEAR);
GET_GLERROR() //hardware may not support the value, get real value set
uint realVal = renderer->getShadowmapSize();
//create faces if(realVal==val)
for (int i=0;i<6;++i) showMessage(q_("Shadowmap size changed"));
{ else
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,0,dept showMessage(QString(q_("Shadowmap size not supported
hFormat, , set to %1")).arg(realVal));
cubemapSize,cubemapSize,0,GL_DEPTH_COMP
ONENT,GL_UNSIGNED_BYTE,NULL);
GET_GLERROR()
}
glBindTexture(GL_TEXTURE_CUBE_MAP, 0); conf->setValue(S3D_CONFIG_PREFIX + "/shadowmap_size",realVal
);
emit shadowmapSizeChanged(realVal);
} }
else }
{
//gen renderbuffer for single-face depth, reused for all fac
es to save some memory
int val = 0;
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE,&val);
qDebug()<<"[Scenery3d] Max Renderbuffer size"<<val;
glGenRenderbuffers(1,&cubeRB);
glBindRenderbuffer(GL_RENDERBUFFER,cubeRB);
glRenderbufferStorage(GL_RENDERBUFFER, rbDepth, cubemapSize,
cubemapSize);
GLenum err=glGetError();
switch(err){
case GL_NO_ERROR:
break;
case GL_INVALID_ENUM:
qWarning()<<"Scenery3D: RB: invalid
depth format?";
break;
case GL_INVALID_VALUE:
qWarning()<<"Scenery3D: RB: invalid
renderbuffer size";
break;
case GL_OUT_OF_MEMORY:
qWarning()<<"Scenery3D: RB: out of m
emory. Cannot create renderbuffer.";
break;
default:
qWarning()<<"Scenery3D: RB: unexpected OpenG
L error:" << err;
}
glBindRenderbuffer(GL_RENDERBUFFER, 0); bool Scenery3d::getIsGeometryShaderSupported() const
} {
return renderer->isGeometryShaderCubemapSupported();
}
//generate FBO/FBOs bool Scenery3d::getAreShadowsSupported() const
if(cubemappingMode == S3DEnum::CM_CUBEMAP_GSACCEL) {
{ return renderer->areShadowsSupported();
//only 1 FBO used }
//create fbo
glGenFramebuffers(1,&cubeFBO);
glBindFramebuffer(GL_FRAMEBUFFER,cubeFBO);
GET_GLERROR() bool Scenery3d::getIsShadowFilteringSupported() const
{
return renderer->isShadowFilteringSupported();
}
#ifndef QT_OPENGL_ES_2 bool Scenery3d::getIsANGLE() const
//attach cube tex + cube depth {
//note that this function will be a NULL pointer if GS is no return renderer->isANGLEContext();
t supported, so it is important to check support before using }
glExtFuncs->glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_AT
TACHMENT0,cubeMapCubeTex,0);
glExtFuncs->glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_AT
TACHMENT, cubeMapCubeDepth, 0);
#endif
GET_GLERROR() uint Scenery3d::getMaximumFramebufferSize() const
{
return renderer->getMaximumFramebufferSize();
}
//check validity void Scenery3d::setView(const StoredView &view, const bool setDate)
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFE {
R_COMPLETE) if(!currentScene)
{
qWarning() << "[Scenery3D] glCheckFramebufferStatus
failed, probably can't use cube map";
}
else
ret = true;
}
else
{ {
//6 FBOs used qCWarning(scenery3d)<<"Can't set current view, no scene load
glGenFramebuffers(6,cubeSideFBO); ed!";
return;
GET_GLERROR()
for(int i=0;i<6;++i)
{
glBindFramebuffer(GL_FRAMEBUFFER, cubeSideFBO[i]);
//attach color - 1 side of cubemap or single texture
if(cubemappingMode == S3DEnum::CM_CUBEMAP)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_CO
LOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,cubeMapCubeTex,0);
else
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_CO
LOR_ATTACHMENT0, GL_TEXTURE_2D, cubeMapTex[i],0);
GET_GLERROR()
//attach shared depth buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_A
TTACHMENT,GL_RENDERBUFFER, cubeRB);
GET_GLERROR()
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FR
AMEBUFFER_COMPLETE)
{
qWarning() << "[Scenery3D] glCheckFramebuffe
rStatus failed, probably can't use cube map";
ret = false;
break;
}
else
ret = true;
}
} }
//unbind last framebuffer //update position
glBindFramebuffer(GL_FRAMEBUFFER,defaultFBO); //important: set eye height first
currentScene->setEyeHeight(view.position[3]);
//initialize cube rotations... found by trial and error :) //then, set grid position
QMatrix4x4 stackBase; currentScene->setGridPosition(Vec3d(view.position[0],view.position[1
],view.position[2]));
//all angles were found using some experimenting :) //update time, if relevant and wanted.
//this is the EAST face (y=1) if (view.jdIsRelevant && setDate)
stackBase.rotate(90.0f,-1.0f,0.0f,0.0f);
if(cubemappingMode >= S3DEnum::CM_CUBEMAP)
{
//cubemap mode needs other rotations than texture mode
//south (x=1) ok
cubeRotation[0] = stackBase;
cubeRotation[0].rotate(-90.0f,0.0f,1.0f,0.0f);
cubeRotation[0].rotate(90.0f,0.0f,0.0f,1.0f);
//NORTH (x=-1) ok
cubeRotation[1] = stackBase;
cubeRotation[1].rotate(90.0f,0.0f,1.0f,0.0f);
cubeRotation[1].rotate(-90.0f,0.0f,0.0f,1.0f);
//EAST (y=1) ok
cubeRotation[2] = stackBase;
//west (y=-1) ok
cubeRotation[3] = stackBase;
cubeRotation[3].rotate(180.0f,-1.0f,0.0f,0.0f);
//top (z=1) ok
cubeRotation[4] = stackBase;
cubeRotation[4].rotate(-90.0f,1.0f,0.0f,0.0f);
//bottom (z=-1)
cubeRotation[5] = stackBase;
cubeRotation[5].rotate(90.0f,1.0f,0.0f,0.0f);
cubeRotation[5].rotate(180.0f,0.0f,0.0f,1.0f);
}
else
{ {
cubeRotation[0] = stackBase; StelCore *core=StelApp::getInstance().getCore();
cubeRotation[0].rotate(90.0f,0.0f,0.0f,1.0f); core->setJD(view.jd);
cubeRotation[1] = stackBase;
cubeRotation[1].rotate(90.0f,0.0f,0.0f,-1.0f);
cubeRotation[2] = stackBase;
cubeRotation[3] = stackBase;
cubeRotation[3].rotate(180.0f,0.0f,0.0f,1.0f);
cubeRotation[4] = stackBase;
cubeRotation[4].rotate(90.0f,-1.0f,0.0f,0.0f);
cubeRotation[5] = stackBase;
cubeRotation[5].rotate(90.0f,1.0f,0.0f,0.0f);
}
//create a 20x20 cube subdivision to give a good approximation of no
n-linear projections
const int sub = 20;
const int vtxCount = (sub+1) * (sub+1);
const double d_sub_v = 2.0 / sub;
const double d_sub_tex = 1.0 / sub;
//create the front cubemap face vertices
QVector<Vec3f> cubePlaneFront;
QVector<Vec2f> cubePlaneFrontTex;
QVector<unsigned short> frontIndices;
cubePlaneFront.reserve(vtxCount);
cubePlaneFrontTex.reserve(vtxCount);
//store the indices of the vertices
//this could easily be recalculated as needed but this makes it a bi
t more readable
unsigned short vertexIdx[sub+1][sub+1];
//first, create the actual vertex positions, (20+1)^2 vertices
for (int y = 0; y <= sub; y++) {
for (int x = 0; x <= sub; x++) {
float xp = -1.0 + x * d_sub_v;
float yp = -1.0 + y * d_sub_v;
float tx = x * d_sub_tex;
float ty = y * d_sub_tex;
cubePlaneFront<< Vec3f(xp, 1.0f, yp);
cubePlaneFrontTex<<Vec2f(tx,ty);
vertexIdx[y][x] = y*(sub+1)+x;
}
} }
Q_ASSERT(cubePlaneFrontTex.size() == vtxCount); //update view vector
Q_ASSERT(cubePlaneFront.size() == vtxCount); // This vector is (az_deg, alt_deg, fov_deg)
Vec3d v;
StelUtils::spheToRect((180.0-view.view_fov[0])*M_PI/180.0, view.view
_fov[1]*M_PI/180.0, v);
mvMgr->setViewDirectionJ2000(StelApp::getInstance().getCore()->altAz
ToJ2000(v, StelCore::RefractionOff));
mvMgr->zoomTo(view.view_fov[2]);
}
//generate indices for each of the 20x20 subfaces StoredView Scenery3d::getCurrentView()
//TODO optimize for TRIANGLE_STRIP? {
for ( int y = 0; y < sub; y++) if(!currentScene)
{ {
for( int x = 0; x<sub; x++) qCWarning(scenery3d)<<"Can't return current view, no scene l
{ oaded!";
//first tri (top one) return StoredView();
frontIndices<<vertexIdx[y+1][x];
frontIndices<<vertexIdx[y][x];
frontIndices<<vertexIdx[y+1][x+1];
//second tri
frontIndices<<vertexIdx[y+1][x+1];
frontIndices<<vertexIdx[y][x];
frontIndices<<vertexIdx[y][x+1];
}
} }
int idxCount = frontIndices.size(); StoredView view;
view.isGlobal = false;
//create the other faces StelCore* core = StelApp::getInstance().getCore();
//note that edge vertices of the faces are duplicated
cubeVertices.clear(); //get view vec
cubeVertices.reserve(vtxCount * 6); Vec3d vd = mvMgr->getViewDirectionJ2000();
cubeTexcoords.clear(); //convert to alt/az format
cubeTexcoords.reserve(vtxCount * 6); vd = core->j2000ToAltAz(vd, StelCore::RefractionOff);
QVector<unsigned short> cubeIndices; //index data is not needed afte //convert to spherical angles
rwards on CPU side, so use a local vector StelUtils::rectToSphe(&view.view_fov[0],&view.view_fov[1],vd);
cubeIndices.reserve(idxCount * 6); //convert to degrees
//init with copies of front face view.view_fov[0]*=180.0/M_PI;
for(int i = 0;i<6;++i) view.view_fov[1]*=180.0/M_PI;
{ // we must patch azimuth
//order of geometry should be as follows: view.view_fov[0]=180.0-view.view_fov[0];
//basically "reversed" cubemap order //3rd comp is fov
//S face x=1 view.view_fov[2] = mvMgr->getAimFov();
//N face x=-1
//E face y=1
//W face y=-1
//up face z=1
//down face z=-1
cubeVertices<<cubePlaneFront;
cubeTexcoords<<cubePlaneFrontTex;
cubeIndices<<frontIndices;
}
Q_ASSERT(cubeVertices.size() == cubeTexcoords.size());
transformedCubeVertices.resize(cubeVertices.size());
cubeIndexCount = cubeIndices.size();
qDebug()<<"[Scenery3d] Using cube with"<<cubeVertices.size()<<"verti
ces and" <<cubeIndexCount<<"indices";
//create the other cube faces by rotating the front face
#define PLANE(_PLANEID_, _MAT_) for(int i=_PLANEID_ * vtxCount;i < (_PLANEI
D_ + 1)*vtxCount;i++){ _MAT_.transfo(cubeVertices[i]); }\
for(int i =_PLANEID_ * idxCount; i < (_PLANEID_+1)*idxCount;++i) { c
ubeIndices[i] = cubeIndices[i] + _PLANEID_ * vtxCount; }
PLANE(0, Mat4f::zrotation(-M_PI_2)); //S
PLANE(1, Mat4f::zrotation(M_PI_2)); //N
PLANE(2, Mat4f::identity()); //E
PLANE(3, Mat4f::zrotation(M_PI)); //W
PLANE(4, Mat4f::xrotation(M_PI_2)); //U
PLANE(5, Mat4f::xrotation(-M_PI_2)); //D
#undef PLANE
//upload original cube vertices + indices to GL
cubeVertexBuffer.bind();
//store original vertex pos (=3D vertex coords) + 2D tex coords in s
ame buffer
cubeVertexBuffer.allocate(cubeVertices.size() * (sizeof(Vec3f) + siz
eof(Vec2f)) );
cubeVertexBuffer.write(0, cubeVertices.constData(), cubeVertices.siz
e() * sizeof(Vec3f));
cubeVertexBuffer.write(cubeVertices.size() * sizeof(Vec3f), cubeTexc
oords.constData(), cubeTexcoords.size() * sizeof(Vec2f));
cubeVertexBuffer.release();
cubeIndexBuffer.bind();
cubeIndexBuffer.allocate(cubeIndices.constData(),cubeIndices.size()
* sizeof(unsigned short));
cubeIndexBuffer.release();
//reset cubemap timer to make sure it is rerendered immediately afte
r re-init
invalidateCubemap();
qDebug()<<"[Scenery3d] Initializing cubemap...done!"; //get current grid pos + eye height
Vec3d pos = currentScene->getGridPosition();
view.position[0] = pos[0];
view.position[1] = pos[1];
view.position[2] = pos[2];
view.position[3] = currentScene->getEyeHeight();
if(!ret) return view;
{
parent->showMessage("Cannot use cubemapping with current set
tings");
deleteCubemapping();
}
return ret;
} }
void Scenery3d::deleteShadowmapping() void Scenery3d::showMessage(const QString& message)
{ {
if(shadowFBOs.size()>0) //kinda hack that finds out if shadowmap rel currentMessage=message;
ated objects have been created messageFader=true;
{ messageTimer->start();
//we can delete them all at once then
glDeleteFramebuffers(shadowFBOs.size(),shadowFBOs.constData(
));
glDeleteTextures(shadowMapsArray.size(),shadowMapsArray.cons
tData());
shadowFBOs.clear();
shadowMapsArray.clear();
shadowCPM.clear();
shadowFrustumSize.clear();
frustumArray.clear();
focusBodies.clear();
qDebug()<<"[Scenery3d] Shadowmapping objects cleaned up";
}
} }
bool Scenery3d::initShadowmapping() void Scenery3d::clearMessage()
{ {
deleteShadowmapping(); messageFader = false;
bool valid = false;
if(simpleShadows)
{
shaderParameters.frustumSplits = 1;
}
else
{
//TODO support changing this option by the user and/or the s
cene?
shaderParameters.frustumSplits = 4;
}
if(!areShadowsSupported())
{
qWarning()<<"[Scenery3d] Tried to initialize shadows without
shadow support!";
return false;
}
if(shadowmapSize>0)
{
//Define shadow maps array - holds MAXSPLITS textures
shadowFBOs.resize(shaderParameters.frustumSplits);
shadowMapsArray.resize(shaderParameters.frustumSplits);
shadowCPM.resize(shaderParameters.frustumSplits);
shadowFrustumSize.resize(shaderParameters.frustumSplits);
frustumArray.resize(shaderParameters.frustumSplits);
focusBodies.resize(shaderParameters.frustumSplits);
//For shadowmapping, we use create 1 SM FBO for each frustum
split - this seems to be the optimal solution on modern GPUs,
//see http://www.reddit.com/r/opengl/comments/1rsnhy/most_ef
ficient_fbo_usage_in_multipass_pipeline/
//The point seems to be that switching attachments may cause
re-validation of the FB.
//Generate the FBO ourselves. We do this because Qt does not
support depth-only FBOs to save some memory.
glGenFramebuffers(shaderParameters.frustumSplits,shadowFBOs.
data());
glGenTextures(shaderParameters.frustumSplits,shadowMapsArray
.data());
for(int i=0; i<shaderParameters.frustumSplits; i++)
{
//Bind the FBO
glBindFramebuffer(GL_FRAMEBUFFER, shadowFBOs.at(i));
//Activate the texture unit - we want sahdows + text
ures so this is crucial with the current Stellarium pipeline - we start at
unit 4
glActiveTexture(GL_TEXTURE4+i);
//Bind the depth map and setup parameters
glBindTexture(GL_TEXTURE_2D, shadowMapsArray.at(i));
#ifndef QT_OPENGL_ES_2
bool isEs = QOpenGLContext::currentContext()->isOpen
GLES();
GLenum depthPcss = isEs ? GL_DEPTH_COMPONENT : GL_DE
PTH_COMPONENT32F;
GLenum depthNormal = isEs ? GL_DEPTH_COMPONENT : GL_
DEPTH_COMPONENT16;
#else
GLenum depthPcss = GL_DEPTH_COMPONENT;
GLenum depthNormal = GL_DEPTH_COMPONENT;
#endif
//pcss is only enabled if filtering is also enabled
bool pcssEnabled = shaderParameters.pcss && (shaderP
arameters.shadowFilterQuality == S3DEnum::SFQ_LOW || shaderParameters.shado
wFilterQuality == S3DEnum::SFQ_HIGH);
//for OpenGL ES2, type has to be UNSIGNED_SHORT or U
NSIGNED_INT for depth textures, desktop does probably not care
glTexImage2D(GL_TEXTURE_2D, 0, (pcssEnabled ? depthP
css : depthNormal), shadowmapSize, shadowmapSize, 0, GL_DEPTH_COMPONENT, GL
_UNSIGNED_SHORT, NULL);
//we use hardware-accelerated depth compare mode, un
less pcss is used
shaderParameters.hwShadowSamplers = false;
//NOTE: cant use depth compare mode on ES2
if(!pcssEnabled)
{
#ifndef QT_OPENGL_ES_2
if(!isEs)
{
glTexParameteri(GL_TEXTURE_2D, GL_TE
XTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TE
XTURE_COMPARE_FUNC, GL_LEQUAL);
shaderParameters.hwShadowSamplers =
true;
}
#endif
}
//IF we support hw shadow sampling, then we may enab
le linear filtering, otherwise filtering depth values directly would not ma
ke much sense
GLint filter = shaderParameters.hwShadowSamplers &&
(shaderParameters.shadowFilterQuality == S3DEnum::SFQ_HARDWARE
|| shaderParameters.shadowFilterQual
ity == S3DEnum::SFQ_LOW_HARDWARE
|| shaderParameters.shadowFilterQual
ity == S3DEnum::SFQ_HIGH_HARDWARE) ? GL_LINEAR : GL_NEAREST;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER
, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER
, filter);
#ifndef QT_OPENGL_ES_2
if(!isEs)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BA
SE_LEVEL,0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MA
X_LEVEL,0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WR
AP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WR
AP_T, GL_CLAMP_TO_BORDER);
const float ones[] = {1.0f, 1.0f, 1.0f, 1.0f
};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_B
ORDER_COLOR, ones);
}
#endif
//Attach the depthmap to the Buffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTA
CHMENT, GL_TEXTURE_2D, shadowMapsArray[i], 0);
//NOTE: disabling the drawbuffer should be required
//but the respective functions are not available on
GLES2?
//On ANGLE, it seems to work without this settings (
framebuffer is complete, etc.)
//but I don't know if it will work on other ES platf
orms?
#ifndef QT_OPENGL_ES_2
if(!isEs)
{
glExtFuncs->glDrawBuffer(GL_NONE); // essent
ial for depth-only FBOs!!!
glExtFuncs->glReadBuffer(GL_NONE);
}
#endif
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FR
AMEBUFFER_COMPLETE)
{
qWarning() << "[Scenery3D] glCheckFramebuffe
rStatus failed, can't use FBO";
break;
}
else if (i==shaderParameters.frustumSplits-1)
{
valid = true;
}
}
//Done. Unbind and switch to normal texture unit 0
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
glActiveTexture(GL_TEXTURE0);
qDebug()<<"[Scenery3D] shadowmapping initialized";
}
else
{
qWarning()<<"[Scenery3D] shadowmapping not supported or disa
bled";
}
if(!valid)
{
deleteShadowmapping();
parent->showMessage(q_("Shadow mapping can not be used on yo
ur hardware, check logs for details"));
}
return valid;
} }
void Scenery3d::draw(StelCore* core) /////////////////////////////////////////////////////////////////////
StelModule* Scenery3dStelPluginInterface::getStelModule() const
{ {
//cant draw if no models return new Scenery3d();
if(!objModel || !objModel->hasStelModels()) }
return;
//find out the default FBO
defaultFBO = StelApp::getInstance().getDefaultFBO();
//reset render statistic
drawnTriangles = drawnModels = materialSwitches = shaderSwitches = 0
;
requiresCubemap = core->getCurrentProjectionType() != StelCore::Proj
ectionPerspective;
//update projector from core
altAzProjector = core->getProjection(StelCore::FrameAltAz, StelCore:
:RefractionOff);
//perform Z-sorting for correct transparency
//this uses the object's centroids for sorting, so the OBJ must be c
reated correctly
objModel->transparencyDepthSort(-absolutePosition.toVec3f());
if(requiresCubemap) StelPluginInfo Scenery3dStelPluginInterface::getPluginInfo() const
{ {
if(!cubeMappingCreated || reinitCubemapping) // Allow to load the resources when used as a static plugin
{ Q_INIT_RESOURCE(Scenery3d);
//init cubemaps
if(!initCubemapping())
return;
reinitCubemapping = false;
}
}
else
{
//remove cubemapping objects when switching to perspective p
roj to save GPU memory
deleteCubemapping();
}
//turn off blending, because it seems to be enabled somewhere we do StelPluginInfo info;
not have access info.id = "Scenery3d";
glDisable(GL_BLEND); info.version = SCENERY3D_PLUGIN_VERSION;
info.displayedName = N_("3D Sceneries");
info.authors = "Georg Zotti, Simon Parzer, Peter Neubauer, Andrei Bo
rza, Florian Schaukowitsch";
info.contact = "Georg.Zotti@univie.ac.at";
info.description = N_("<p>3D foreground renderer. Walk around, find
and avoid obstructions in your garden, "
"find and demonstrate possible astronomical al
ignments in temples, see shadows on sundials etc.</p>"
"<p>To move around, press Ctrl+cursor keys. To
lift eye height, use Ctrl+PgUp/PgDn. "
"Movement speed is linked to field of view (i.
e. zoom in for fine adjustments). "
"You can even keep moving by releasing Ctrl be
fore cursor key.</p>"
"<p>Development of this plugin was in parts su
pported by the Austrian Science Fund (FWF) project ASTROSIM (P 21208-G19).
More: http://astrosim.univie.ac.at/</p>");
if (shaderParameters.shadows) return info;
{ }
//test if shadow mapping has been initialized,
//or needs to be re-initialized because of setting changes
if(reinitShadowmapping || shadowFBOs.size()==0 || (cubemappi
ngUsedLastFrame != requiresCubemap))
{
reinitShadowmapping = false;
if(!initShadowmapping())
return; //can't use shadowmaps
}
}
else
{
//remove the shadow mapping stuff if not in use, this is onl
y done once
deleteShadowmapping();
}
if (!requiresCubemap)
{
//when Stellarium uses perspective projection we can use the
fast direct method
drawDirect();
}
else
{
//we have to use a workaround using cubemapping
drawWithCubeMap();
}
if (textEnabled) drawCoordinatesText();
if (debugEnabled)
{
drawDebug();
}
cubemappingUsedLastFrame = requiresCubemap; QObjectList Scenery3dStelPluginInterface::getExtensionList() const
{
QObjectList ret;
ret.append(new Scenery3dRemoteControlService());
return ret;
} }
/////////////////////////////////////////////////////////////////////
 End of changes. 253 change blocks. 
2848 lines changed or deleted 884 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/