• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Stellarium / stellarium / 4853788370

pending completion
4853788370

push

github

Alexander V. Wolf
Special patch for John Simple

3 of 3 new or added lines in 3 files covered. (100.0%)

14729 of 125046 relevant lines covered (11.78%)

20166.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/plugins/Scenery3d/src/S3DRenderer.cpp
1
/*
2
 * Stellarium Scenery3d Plug-in
3
 *
4
 * Copyright (C) 2011-2015 Simon Parzer, Peter Neubauer, Georg Zotti, Andrei Borza, Florian Schaukowitsch
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
19
 */
20

21
#include <QtGlobal>
22

23
#include "GLFuncs.hpp"
24
#include "S3DRenderer.hpp"
25
#include "S3DScene.hpp"
26

27
#include "StelApp.hpp"
28
#include "StelCore.hpp"
29
#include "StelPainter.hpp"
30
#include "StelModuleMgr.hpp"
31
#include "StelTranslator.hpp"
32
#include "StelUtils.hpp"
33
#include "LandscapeMgr.hpp"
34
#include "SolarSystem.hpp"
35
#include "StelOBJ.hpp"
36

37
#include <QKeyEvent>
38
#include <QSettings>
39
#include <stdexcept>
40
#include <cmath>
41
#include <QOpenGLShaderProgram>
42
#include <QLoggingCategory>
43

44
Q_LOGGING_CATEGORY(s3drenderer, "stel.plugin.scenery3d.renderer")
×
45

46
// (cast return value to void to silence Coverity)
47
#define GET_GLERROR() (void)StelOpenGL::checkGLErrors(__FILE__,__LINE__);
48

49
//macro for easier uniform setting
50
#define SET_UNIFORM(shd,uni,val) shd->setUniformValue(shaderManager.uniformLocation(shd,uni),val)
51

52
static const float LUNAR_BRIGHTNESS_FACTOR=0.2f;
53
static const float VENUS_BRIGHTNESS_FACTOR=0.005f;
54

55
#if !QT_CONFIG(opengles2)
56
//this is the place where this is initialized
57
GLExtFuncs* glExtFuncs;
58
#endif
59

60
#ifdef _MSC_VER
61
//disable a stupid warning about array value-initialization
62
#pragma warning(disable : 4351)
63
#endif
64

65
S3DRenderer::S3DRenderer(QObject *parent)
×
66
    :
67
      QObject(parent),
68
      sun(Q_NULLPTR), moon(Q_NULLPTR), venus(Q_NULLPTR),
×
69
      currentScene(Q_NULLPTR),
×
70
      supportsGSCubemapping(false), supportsShadows(false), supportsShadowFiltering(false), isANGLE(false), maximumFramebufferSize(0),
×
71
      defaultFBO(0),
×
72
      torchBrightness(0.5f), torchRange(5.0f), textEnabled(false), debugEnabled(false), fixShadowData(false),
×
73
      simpleShadows(false), fullCubemapShadows(false), cubemappingMode(S3DEnum::CM_TEXTURES), //set it to 6 textures as a safe default (Cubemap should work on ANGLE, but does not...)
×
74
      reinitCubemapping(true), reinitShadowmapping(true),
×
75
      cubemapSize(1024),shadowmapSize(1024),wasMovedInLastDrawCall(false),
×
76
      core(Q_NULLPTR), landscapeMgr(Q_NULLPTR),
×
77
      backfaceCullState(true), blendEnabled(false), lastMaterial(Q_NULLPTR), curShader(Q_NULLPTR),
×
78
      drawnTriangles(0), drawnModels(0), materialSwitches(0), shaderSwitches(0),
×
79
      requiresCubemap(false), cubemappingUsedLastFrame(false),
×
80
      lazyDrawing(false), updateOnlyDominantOnMoving(true), updateSecondDominantOnMoving(true), needsMovementEndUpdate(false),
×
81
      needsCubemapUpdate(true), needsMovementUpdate(false), lazyInterval(2.0), lastCubemapUpdate(0.0), lastCubemapUpdateRealTime(0), lastMovementEndRealTime(0),
×
82
      cubeMapCubeTex(0), cubeMapCubeDepth(0), cubeMapTex(), cubeRB(0), dominantFace(0), secondDominantFace(1), cubeFBO(0), cubeSideFBO(), cubeMappingCreated(false),
×
83
      cubeVertexBuffer(QOpenGLBuffer::VertexBuffer), transformedCubeVertexBuffer(QOpenGLBuffer::VertexBuffer), cubeIndexBuffer(QOpenGLBuffer::IndexBuffer), cubeIndexCount(0),
×
84
      lightOrthoNear(0.1f), lightOrthoFar(1000.0f), parallaxScale(0.015f)
×
85
{
86
        #ifndef NDEBUG
87
        qCDebug(s3drenderer)<<"Scenery3d constructor...";
×
88
        #endif
89
        //the arrays should all contain only zeroes
90
        Q_ASSERT(cubeMapTex[0]==0);
×
91
        Q_ASSERT(cubeSideFBO[0]==0);
×
92

93
        shaderParameters.openglES = false;
×
94
        shaderParameters.shadowTransform = false;
×
95
        shaderParameters.pixelLighting = false;
×
96
        shaderParameters.bump = false;
×
97
        shaderParameters.shadows = false;
×
98
        shaderParameters.shadowFilterQuality = S3DEnum::SFQ_LOW;
×
99
        shaderParameters.pcss = false;
×
100
        shaderParameters.geometryShader = false;
×
101
        shaderParameters.torchLight = false;
×
102
        shaderParameters.frustumSplits = 0;
×
103
        shaderParameters.hwShadowSamplers = false;
×
104

105
        renderShaderParameters=shaderParameters;
×
106

107
        debugTextFont.setFamily("Courier");
×
108
        debugTextFont.setPixelSize(16);
×
109

110
        #ifndef NDEBUG
111
        qCDebug(s3drenderer)<<"Scenery3d constructor...done";
×
112
        #endif
113
}
×
114

115
S3DRenderer::~S3DRenderer()
×
116
{
117
        cubeVertexBuffer.destroy();
×
118
        cubeIndexBuffer.destroy();
×
119

120
        deleteShadowmapping();
×
121
        deleteCubemapping();
×
122

123
#if !QT_CONFIG(opengles2)
124
        //delete extension functions
125
        delete glExtFuncs;
×
126
#endif
127
}
×
128

129
void S3DRenderer::saveFrusts()
×
130
{
131
        fixShadowData = !fixShadowData;
×
132

133
        camFrustShadow.saveDrawingCorners();
×
134

135
        for(int i=0; i<shaderParameters.frustumSplits; i++)
×
136
        {
137
                if(fixShadowData) frustumArray[i].saveDrawingCorners();
×
138
                else frustumArray[i].resetCorners();
×
139
        }
140
}
×
141

142
void S3DRenderer::setupPassUniforms(QOpenGLShaderProgram *shader)
×
143
{
144
        //send projection matrix
145
        SET_UNIFORM(shader,ShaderMgr::UNIFORM_MAT_PROJECTION, projectionMatrix);
×
146

147
        //set alpha test threshold (this is scene-global for now)
148
        SET_UNIFORM(shader,ShaderMgr::UNIFORM_FLOAT_ALPHA_THRESH,currentScene->getSceneInfo().transparencyThreshold);
×
149

150
        //torch attenuation factor
151
        SET_UNIFORM(shader, ShaderMgr::UNIFORM_TORCH_ATTENUATION, lightInfo.torchAttenuation);
×
152

153
        //-- Shadowing setup -- this was previously in generateCubeMap_drawSceneWithShadows
154
        //first check if shader supports shadows
155
        GLint loc = shaderManager.uniformLocation(shader,ShaderMgr::UNIFORM_VEC_SPLITDATA);
×
156

157
        //ALWAYS update the shader matrices, even if "no" shadow is cast
158
        //this fixes weird time-dependent crashes (this was fun to debug)
159
        if(shaderParameters.shadows && loc >= 0)
×
160
        {
161
                //Holds the frustum splits necessary for the lookup in the shader
162
                Vec4f splitData(0.f);
×
163
                for(int i=0; i<shaderParameters.frustumSplits; i++)
×
164
                {
165
                        float zVal;
166
                        if(i<shaderParameters.frustumSplits-1)
×
167
                        {
168
                                //the frusta have a slight overlap
169
                                //use the center of this overlap for more robust filtering
170
                                zVal = (frustumArray.at(i).zFar + frustumArray.at(i+1).zNear) / 2.0f;
×
171
                        }
172
                        else
173
                                zVal = frustumArray.at(i).zFar;
×
174

175
                        //see Nvidia CSM example for this calculation
176
                        //http://developer.download.nvidia.com/SDK/10/opengl/screenshots/samples/cascaded_shadow_maps.html
177
                        //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!)
178
                        splitData.v[i] = 0.5f*(-zVal * projectionMatrix.constData()[10] + projectionMatrix.constData()[14])/zVal + 0.5f;
×
179

180
                        //Bind current depth map texture
181
                        glActiveTexture(GL_TEXTURE4+static_cast<uint>(i));
×
182
                        glBindTexture(GL_TEXTURE_2D, shadowMapsArray.at(i));
×
183

184
                        SET_UNIFORM(shader,static_cast<ShaderMgr::UNIFORM>(ShaderMgr::UNIFORM_TEX_SHADOW0+i), 4+i);
×
185
                        SET_UNIFORM(shader,static_cast<ShaderMgr::UNIFORM>(ShaderMgr::UNIFORM_MAT_SHADOW0+i), shadowCPM.at(i));
×
186
                }
187

188
                //Send squared splits to the shader
189
                shader->setUniformValue(loc, splitData.v[0], splitData.v[1], splitData.v[2], splitData.v[3]);
×
190

191
                if(shaderParameters.shadowFilterQuality>S3DEnum::SFQ_HARDWARE)
×
192
                {
193
                        //send size of light ortho for each frustum
194
                        loc = shaderManager.uniformLocation(shader,ShaderMgr::UNIFORM_VEC_LIGHTORTHOSCALE);
×
195
                        shader->setUniformValueArray(loc,shadowFrustumSize.constData(),shaderParameters.frustumSplits);
×
196
                }
197
        }
198

199
        loc = shaderManager.uniformLocation(shader,ShaderMgr::UNIFORM_MAT_CUBEMVP);
×
200
        if(loc>=0)
×
201
        {
202
                //upload cube mvp matrices
203
                shader->setUniformValueArray(loc,cubeMVP,6);
×
204
        }
205
}
×
206

207
void S3DRenderer::setupFrameUniforms(QOpenGLShaderProgram *shader)
×
208
{
209
        //-- Transform setup --
210
        //check if shader wants a MVP or separate matrices
211
        GLint loc = shaderManager.uniformLocation(shader,ShaderMgr::UNIFORM_MAT_MVP);
×
212
        if(loc>=0)
×
213
        {
214
                shader->setUniformValue(loc,projectionMatrix * modelViewMatrix);
×
215
        }
216

217
        //this macro saves a bit of writing
218
        SET_UNIFORM(shader,ShaderMgr::UNIFORM_MAT_MODELVIEW, modelViewMatrix);
×
219

220
        //-- Lighting setup --
221
        //check if we require a normal matrix, this is assumed to be required for all "shading" shaders
222
        loc = shaderManager.uniformLocation(shader,ShaderMgr::UNIFORM_MAT_NORMAL);
×
223
        if(loc>=0)
×
224
        {
225
                QMatrix3x3 normalMatrix = modelViewMatrix.normalMatrix();
×
226
                shader->setUniformValue(loc,normalMatrix);
×
227

228
                //assume light direction is only required when normal matrix is also used (would not make much sense alone)
229
                //check if the shader wants view space info
230
                loc = shaderManager.uniformLocation(shader,ShaderMgr::UNIFORM_LIGHT_DIRECTION_VIEW);
×
231
                if(loc>=0)
×
232
                        shader->setUniformValue(loc,(normalMatrix * lightInfo.lightDirectionWorld));
×
233
        }
234
}
×
235

236
void S3DRenderer::setupMaterialUniforms(QOpenGLShaderProgram* shader, const S3DScene::Material &mat)
×
237
{
238
        //ambient is calculated depending on illum model
239
        SET_UNIFORM(shader,ShaderMgr::UNIFORM_MIX_AMBIENT,mat.Ka * lightInfo.ambient);
×
240

241
        SET_UNIFORM(shader,ShaderMgr::UNIFORM_MIX_DIFFUSE, mat.Kd * lightInfo.directional);
×
242
        SET_UNIFORM(shader,ShaderMgr::UNIFORM_MIX_TORCHDIFFUSE, mat.Kd * lightInfo.torchDiffuse);
×
243
        SET_UNIFORM(shader,ShaderMgr::UNIFORM_MIX_EMISSIVE,mat.Ke * lightInfo.emissive);
×
244
        SET_UNIFORM(shader,ShaderMgr::UNIFORM_MIX_SPECULAR,mat.Ks * lightInfo.specular);
×
245

246
        SET_UNIFORM(shader,ShaderMgr::UNIFORM_MTL_SHININESS,mat.Ns);
×
247
        //force alpha to 1 here for non-translucent mats (fixes incorrect blending in cubemap)
248
        float baseAlpha = mat.traits.hasTransparency ? mat.d : 1.0f;
×
249
        SET_UNIFORM(shader,ShaderMgr::UNIFORM_MTL_ALPHA,mat.traits.isFading ? baseAlpha * mat.vis_fadeValue : baseAlpha);
×
250

251
        if(mat.traits.hasDiffuseTexture)
×
252
        {
253
                mat.tex_Kd->bind(0); //this already sets glActiveTexture(0)
×
254
                SET_UNIFORM(shader,ShaderMgr::UNIFORM_TEX_DIFFUSE,0);
×
255
        }
256
        if(mat.traits.hasEmissiveTexture)
×
257
        {
258
                mat.tex_Ke->bind(1);
×
259
                SET_UNIFORM(shader,ShaderMgr::UNIFORM_TEX_EMISSIVE,1);
×
260
        }
261
        if(shaderParameters.bump && mat.traits.hasBumpTexture)
×
262
        {
263
                mat.tex_bump->bind(2);
×
264
                SET_UNIFORM(shader,ShaderMgr::UNIFORM_TEX_BUMP,2);
×
265
        }
266
        if(shaderParameters.bump && mat.traits.hasHeightTexture)
×
267
        {
268
                mat.tex_height->bind(3);
×
269
                SET_UNIFORM(shader,ShaderMgr::UNIFORM_TEX_HEIGHT,3);
×
270
        }
271
}
×
272

273
//depth sort helper
274
static Vec3f zSortValue;
275
bool zSortFunction(const StelOBJ::MaterialGroup* const & mLeft, const StelOBJ::MaterialGroup* const & mRight)
×
276
{
277
        //we can avoid taking the sqrt here
278
        float dist1 = (mLeft->centroid - zSortValue).normSquared();
×
279
        float dist2 = (mRight->centroid - zSortValue).normSquared();
×
280
        return dist1>dist2;
×
281
}
282

283
bool S3DRenderer::drawArrays(bool shading, bool blendAlphaAdditive)
×
284
{
285
        //override some shader Params
286
        renderShaderParameters = shaderParameters;
×
287
        switch(lightInfo.shadowCaster)
×
288
        {
289
                case LightParameters::SC_Venus:
×
290
                        //turn shadow filter off to get sharper shadows
291
                        renderShaderParameters.shadowFilterQuality = S3DEnum::SFQ_OFF;
×
292
                        break;
×
293
                case LightParameters::SC_None:
×
294
                        //disable shadow rendering to speed things up
295
                        renderShaderParameters.shadows = false;
×
296
                        break;
×
297
                default:
×
298
                        break;
×
299
        }
300

301
        //bind VAO
302
        currentScene->glBind();
×
303

304
        //assume backfaceculling is on, and blending turned off
305
        backfaceCullState = true;
×
306
        blendEnabled = false;
×
307
        lastMaterial = Q_NULLPTR;
×
308
        curShader = Q_NULLPTR;
×
309
        initializedShaders.clear();
×
310
        transparentGroups.clear();
×
311
        bool success = true;
×
312

313
        //TODO optimize: clump models with same material together when first loading to minimize state changes
314

315
        const S3DScene::ObjectList& objectList = currentScene->getObjects();
×
316
        for(int i=0; i<objectList.size(); ++i)
×
317
        {
318
                const StelOBJ::Object& obj = objectList.at(i);
×
319
                const StelOBJ::MaterialGroupList& matGroups = obj.groups;
×
320

321
                for(int j = 0; j < matGroups.size();++j)
×
322
                {
323
                        const StelOBJ::MaterialGroup& matGroup = matGroups.at(j);
×
324
                        const S3DScene::Material* pMaterial = &currentScene->getMaterial(matGroup.materialIndex);
×
325
                        Q_ASSERT(pMaterial);
×
326

327
                        if(pMaterial->traits.isFullyTransparent)
×
328
                                continue; //dont render fully invisible objects
×
329

330
                        if(shading)
×
331
                        {
332
                                if(pMaterial->traits.hasTransparency || pMaterial->traits.isFading)
×
333
                                {
334
                                        //process transparent objects later, with Z sorting
335
                                        transparentGroups.append(&matGroup);
×
336
                                        continue;
×
337
                                }
338
                        }
339
                        else
340
                        {
341
                                //objects start casting shadows with at least 0.2 opacity
342
                                if(pMaterial->d * pMaterial->vis_fadeValue < 0.2f)
×
343
                                        continue;
×
344
                        }
345

346
                        success = drawMaterialGroup(matGroup,shading,blendAlphaAdditive);
×
347
                        if(!success)
×
348
                                break;
×
349
                }
350
        }
351

352
        //sort and render transparent objects
353
        if(transparentGroups.size()>0)
×
354
        {
355
                zSortValue = currentScene->getEyePosition().toVec3f();
×
356
                std::sort(transparentGroups.begin(),transparentGroups.end(),zSortFunction);
×
357

358
                for(int i = 0; i<transparentGroups.size();++i)
×
359
                {
360
                        success = drawMaterialGroup(*transparentGroups[i],shading,blendAlphaAdditive);
×
361
                        if(!success)
×
362
                                break;
×
363
                }
364
        }
365

366
        //release last used shader and VAO
367
        if(curShader)
×
368
                curShader->release();
×
369
        currentScene->glRelease();
×
370

371
        //reset to default states
372
        if(!backfaceCullState)
×
373
                glEnable(GL_CULL_FACE);
×
374

375
        if(blendEnabled)
×
376
                glDisable(GL_BLEND);
×
377

378

379
        return success;
×
380
}
381

382
bool S3DRenderer::drawMaterialGroup(const StelOBJ::MaterialGroup &matGroup, bool shading, bool blendAlphaAdditive)
×
383
{
384
        const S3DScene::Material* pMaterial = &currentScene->getMaterial(matGroup.materialIndex);
×
385

386
        if(lastMaterial!=pMaterial)
×
387
        {
388
                ++materialSwitches;
×
389
                lastMaterial = pMaterial;
×
390

391
                //get a shader from shadermgr that fits the current state + material combo
392
                QOpenGLShaderProgram* newShader = shaderManager.getShader(renderShaderParameters,pMaterial);
×
393
                if(!newShader)
×
394
                {
395
                        //shader invalid, can't draw
396
                        rendererMessage(q_("Scenery3d shader error, can't draw. Check debug output for details."));
×
397
                        return false;
×
398
                }
399
                if(newShader!=curShader)
×
400
                {
401
                        curShader = newShader;
×
402
                        curShader->bind();
×
403
                        if(!initializedShaders.contains(curShader))
×
404
                        {
405
                                ++shaderSwitches;
×
406

407
                                //needs first-time initialization for this pass
408
                                if(shading)
×
409
                                {
410
                                        setupPassUniforms(curShader);
×
411
                                        setupFrameUniforms(curShader);
×
412
                                }
413
                                else
414
                                {
415
                                        //really only mvp+alpha thresh required, so only set this
416
                                        SET_UNIFORM(curShader,ShaderMgr::UNIFORM_MAT_MVP,projectionMatrix * modelViewMatrix);
×
417
                                        SET_UNIFORM(curShader,ShaderMgr::UNIFORM_FLOAT_ALPHA_THRESH,currentScene->getSceneInfo().transparencyThreshold);
×
418
                                }
419

420
                                //we remember if we have initialized this shader already, so we can skip "global" initialization later if we encounter it again
421
                                initializedShaders.insert(curShader);
×
422
                        }
423
                }
424
                if(shading)
×
425
                {
426
                        //perform full material setup
427
                        setupMaterialUniforms(curShader,*pMaterial);
×
428
                }
429
                else
430
                {
431
                        //set diffuse tex if possible for alpha testing
432
                        if( ! pMaterial->tex_Kd.isNull())
×
433
                        {
434
                                pMaterial->tex_Kd->bind(0);
×
435
                                SET_UNIFORM(curShader,ShaderMgr::UNIFORM_TEX_DIFFUSE,0);
×
436
                        }
437
                }
438

439
                if(shading && (pMaterial->traits.hasTransparency || pMaterial->traits.isFading))
×
440
                {
441
                        if(!blendEnabled)
×
442
                        {
443
                                glEnable(GL_BLEND);
×
444
                                if(blendAlphaAdditive)
×
445
                                        glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
×
446
                                else //traditional direct blending
447
                                        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
×
448
                                blendEnabled = true;
×
449
                        }
450
                }
451
                else if(blendEnabled)
×
452
                {
453
                        glDisable(GL_BLEND);
×
454
                        blendEnabled=false;
×
455
                }
456

457
                if(backfaceCullState && pMaterial->bBackface)
×
458
                {
459
                        glDisable(GL_CULL_FACE);
×
460
                        backfaceCullState = false;
×
461
                }
462
                else if(!backfaceCullState && !pMaterial->bBackface)
×
463
                {
464
                        glEnable(GL_CULL_FACE);
×
465
                        backfaceCullState = true;
×
466
                }
467
        }
468

469

470
        currentScene->glDraw(matGroup.startIndex,matGroup.indexCount);
×
471
        ++drawnModels;
×
472
        drawnTriangles+=matGroup.indexCount/3;
×
473
        return true;
×
474
}
475

476
void S3DRenderer::computeFrustumSplits(const Vec3d& viewPos, const Vec3d& viewDir, const Vec3d& viewUp)
×
477
{
478
        //the frustum arrays all already contain the same adjusted frustum from adjustFrustum
479
        float zNear = frustumArray[0].zNear;
×
480
        float zFar = frustumArray[0].zFar;
×
481
        float zRatio = zFar / zNear;
×
482
        float zRange = zFar - zNear;
×
483

484
        const SceneInfo& info = currentScene->getSceneInfo();
×
485

486
        //Compute the z-planes for the subfrusta
487
        for(int i=1; i<shaderParameters.frustumSplits; i++)
×
488
        {
489
                float s_i = i/static_cast<float>(shaderParameters.frustumSplits);
×
490

491
                frustumArray[i].zNear = info.shadowSplitWeight*(zNear*powf(zRatio, s_i)) + (1.0f-info.shadowSplitWeight)*(zNear + (zRange)*s_i);
×
492
                //Set the previous zFar to the newly computed zNear
493
                //use a small overlap for robustness
494
                frustumArray[i-1].zFar = frustumArray[i].zNear * 1.005f;
×
495

496
                frustumArray[i-1].calcFrustum(viewPos,viewDir,viewUp);
×
497
        }
498

499
        //last zFar is already the zFar of the adjusted frustum
500
        frustumArray[shaderParameters.frustumSplits-1].calcFrustum(viewPos,viewDir,viewUp);
×
501
}
×
502

503
void S3DRenderer::computePolyhedron(Polyhedron& body,const Frustum& frustum,const Vec3f& shadowDir)
×
504
{
505
        //Building a convex body for directional lights according to Wimmer et al. 2006
506

507
        const AABBox& sceneAABB = currentScene->getSceneAABB();
×
508
        //Add the Frustum to begin with
509
        body.add(frustum);
×
510
        //Intersect with the scene AABB
511
        body.intersect(sceneAABB);
×
512
        //Extrude towards light direction
513
        body.extrude(shadowDir, sceneAABB);
×
514
}
×
515

516
void S3DRenderer::computeOrthoProjVals(const Vec3f &shadowDir, float& orthoExtent, float& orthoNear, float& orthoFar)
×
517
{
518
        //Focus the light first on the entire scene
519
        float maxZ = -std::numeric_limits<float>::max();
×
520
        float minZ = std::numeric_limits<float>::max();
×
521
        orthoExtent = 0.0f;
×
522

523
        Vec3f eye = shadowDir;
×
524
        Vec3f vDir = -eye;
×
525
        vDir.normalize();
×
526
        Vec3f up = Vec3f(0.0f, 0.0f, 1.0f);
×
527
        Vec3f down = -up;
×
528
        Vec3f left = vDir^up;
×
529
        left.normalize();
×
530
        Vec3f right = -left;
×
531

532
        const AABBox& bbox = currentScene->getSceneAABB();
×
533

534
        for(unsigned int i=0; i<AABBox::CORNERCOUNT; i++)
×
535
        {
536
                Vec3f v = bbox.getCorner(static_cast<AABBox::Corner>(i));
×
537
                Vec3f toCam = v - eye;
×
538

539
                float dist = toCam.dot(vDir);
×
540
                maxZ = std::max(dist, maxZ);
×
541
                minZ = std::min(dist, minZ);
×
542

543
                orthoExtent = std::max(std::abs(toCam.dot(left)), orthoExtent);
×
544
                orthoExtent = std::max(std::abs(toCam.dot(right)), orthoExtent);
×
545
                orthoExtent = std::max(std::abs(toCam.dot(up)), orthoExtent);
×
546
                orthoExtent = std::max(std::abs(toCam.dot(down)), orthoExtent);
×
547
        }
548

549
        //Make sure planes aren't too small
550
        orthoNear = minZ;
×
551
        orthoFar = maxZ;
×
552
        //orthoNear = std::max(minZ, 0.01f);
553
        //orthoFar = std::max(maxZ, orthoNear + 1.0f);
554
}
×
555

556
void S3DRenderer::computeCropMatrix(QMatrix4x4& cropMatrix, QVector4D& orthoScale, Polyhedron& focusBody,const QMatrix4x4& lightProj, const QMatrix4x4& lightMVP)
×
557
{
558
        float maxX = -std::numeric_limits<float>::max();
×
559
        float maxY = maxX;
×
560
        float maxZ = maxX;
×
561
        float minX = std::numeric_limits<float>::max();
×
562
        float minY = minX;
×
563
        float minZ = minX;
×
564

565
        //Project the frustum into light space and find the boundaries
566
        for(int i=0; i<focusBody.getVertCount(); i++)
×
567
        {
568
                const Vec3f tmp = focusBody.getVerts().at(i);
×
569
                QVector4D transf4 = lightMVP*QVector4D(tmp.v[0], tmp.v[1], tmp.v[2], 1.0f);
×
570
                QVector3D transf = transf4.toVector3DAffine();
×
571

572
                if(transf.x() > maxX) maxX = transf.x();
×
573
                if(transf.x() < minX) minX = transf.x();
×
574
                if(transf.y() > maxY) maxY = transf.y();
×
575
                if(transf.y() < minY) minY = transf.y();
×
576
                if(transf.z() > maxZ) maxZ = transf.z();
×
577
                if(transf.z() < minZ) minZ = transf.z();
×
578
        }
579

580
        //To avoid artifacts caused by far plane clipping, extend far plane by 5%
581
        //or if cubemapping is used, set it to 1
582
        if(!requiresCubemap  || fullCubemapShadows)
×
583
        {
584
                float zRange = maxZ-minZ;
×
585
                maxZ = std::min(maxZ + zRange*0.05f, 1.0f);
×
586
        }
×
587
        else
588
        {
589
                maxZ = 1.0f;
×
590
        }
591

592

593
        //minZ = std::max(minZ - zRange*0.05f, 0.0f);
594

595
#ifdef QT_DEBUG
596
        //AABBox deb(Vec3f(minX,minY,minZ),Vec3f(maxX,maxY,maxZ));
597
        //focusBody.debugBox = deb.toBox();
598
        //focusBody.debugBox.transform(lightMVP.inverted());
599
#endif
600

601
        //Build the crop matrix and apply it to the light projection matrix
602
        float scaleX = 2.0f/(maxX - minX);
×
603
        float scaleY = 2.0f/(maxY - minY);
×
604
        float scaleZ = 1.0f/(maxZ - minZ); //could also be 1, but this rescales the Z range to fit better
×
605
        //float scaleZ = 1.0f;
606

607
        float offsetZ = -minZ * scaleZ;
×
608
        //float offsetZ = 0.0f;
609

610
        //Reducing swimming as specified in Practical cascaded shadow maps by Zhang et al.
611
        const float quantizer = 64.0f;
×
612
        scaleX = 1.0f/std::ceil(1.0f/scaleX*quantizer) * quantizer;
×
613
        scaleY = 1.0f/std::ceil(1.0f/scaleY*quantizer) * quantizer;
×
614

615
        orthoScale = QVector4D(scaleX,scaleY,minZ,maxZ);
×
616

617
        float offsetX = -0.5f*(maxX + minX)*scaleX;
×
618
        float offsetY = -0.5f*(maxY + minY)*scaleY;
×
619

620
        float halfTex = 0.5f*shadowmapSize;
×
621
        offsetX = std::ceil(offsetX*halfTex)/halfTex;
×
622
        offsetY = std::ceil(offsetY*halfTex)/halfTex;
×
623

624
        //Making the crop matrix
625
        QMatrix4x4 crop(scaleX, 0.0f,   0.0f, offsetX,
626
                        0.0f,   scaleY, 0.0f, offsetY,
627
                        0.0f,   0.0f,   scaleZ, offsetZ,
628
                        0.0f,   0.0f,   0.0f, 1.0f);
×
629

630
        //Crop the light projection matrix
631
        projectionMatrix = crop * lightProj;
×
632

633
        //Calculate texture matrix for projection
634
        //This matrix takes us from eye space to the light's clip space
635
        //It is postmultiplied by the inverse of the current view matrix when specifying texgen
636
        static const QMatrix4x4 biasMatrix(0.5f, 0.0f, 0.0f, 0.5f,
637
                                           0.0f, 0.5f, 0.0f, 0.5f,
638
                                           0.0f, 0.0f, 0.5f, 0.5f,
639
                                           0.0f, 0.0f, 0.0f, 1.0f);        //bias from [-1, 1] to [0, 1]
×
640

641
        //calc final matrix
642
        cropMatrix = biasMatrix * projectionMatrix * modelViewMatrix;
×
643
}
×
644

645
void S3DRenderer::adjustShadowFrustum(const Vec3d& viewPos, const Vec3d& viewDir, const Vec3d& viewUp, const float fov, const float aspect)
×
646
{
647
        if(fixShadowData)
×
648
                return;
×
649
        //calc cam frustum for shadowing range
650
        //note that this is only correct in the perspective projection case, cubemapping WILL introduce shadow artifacts in most cases
651

652
        //TODO make shadows in cubemapping mode better by projecting the frusta, more closely estimating the required shadow extents
653
        const SceneInfo& info = currentScene->getSceneInfo();
×
654
        camFrustShadow.setCamInternals(fov,aspect,info.camNearZ,info.shadowFarZ);
×
655
        camFrustShadow.calcFrustum(viewPos, viewDir, viewUp);
×
656

657
        //Compute H = V intersect S according to Zhang et al.
658
        Polyhedron p;
×
659
        p.add(camFrustShadow);
×
660
        p.intersect(currentScene->getSceneAABB());
×
661
        p.makeUniqueVerts();
×
662

663
        //Find the boundaries
664
        float maxZ = -std::numeric_limits<float>::max();
×
665
        float minZ = std::numeric_limits<float>::max();
×
666

667
        Vec3f eye = viewPos.toVec3f();
×
668

669
        Vec3f vDir = viewDir.toVec3f();
×
670
        vDir.normalize();
×
671

672
        const QVector<Vec3f> &verts = p.getVerts();
×
673
        for(int i=0; i<p.getVertCount(); i++)
×
674
        {
675
                //Find the distance to the camera
676
                Vec3f v = verts[i];
×
677
                Vec3f toCam = v - eye;
×
678
                float dist = toCam.dot(vDir);
×
679

680
                maxZ = std::max(dist, maxZ);
×
681
                minZ = std::min(dist, minZ);
×
682
        }
683

684
        //Setup the newly found near and far planes but make sure they're not too small
685
        //minZ = std::max(minZ, 0.01f);
686
        //maxZ = std::max(maxZ, minZ+1.0f);
687

688
        //save adjusted values and recalc combined frustum for debugging
689
        camFrustShadow.setCamInternals(fov,aspect,minZ,maxZ);
×
690
        camFrustShadow.calcFrustum(viewPos,viewDir,viewUp);
×
691

692
        //Re-set the subfrusta
693
        for(int i=0; i<shaderParameters.frustumSplits; i++)
×
694
        {
695
                frustumArray[i].setCamInternals(fov, aspect, minZ, maxZ);
×
696
        }
697

698
        //Compute and set z-distances for each split
699
        computeFrustumSplits(viewPos,viewDir,viewUp);
×
700
}
×
701

702
void S3DRenderer::calculateShadowCaster()
×
703
{
704
        //shadow source and direction has been calculated in calculateLightSource
705
        static const QVector3D vZero = QVector3D();
706
        static const QVector3D vZeroZeroOne = QVector3D(0,0,1);
707

708
        //calculate lights modelview matrix
709
        lightInfo.shadowModelView.setToIdentity();
×
710
        lightInfo.shadowModelView.lookAt(lightInfo.lightDirectionWorld,vZero,vZeroZeroOne);
×
711
}
×
712

713
bool S3DRenderer::renderShadowMaps()
×
714
{
715
        if(fixShadowData)
×
716
                return true;
×
717

718
        shaderParameters.shadowTransform = true;
×
719

720
        //projection matrix gets updated below in updateCropMatrix
721
        modelViewMatrix = lightInfo.shadowModelView;
×
722

723
        //Fix selfshadowing
724
        glEnable(GL_POLYGON_OFFSET_FILL);
×
725
        glPolygonOffset(0.5f,2.0f);
×
726

727
        //GL state
728
        //enable depth + front face culling
729
        glEnable(GL_DEPTH_TEST);
×
730
        //glDepthFunc(GL_LESS);
731
        glDepthMask(GL_TRUE);
×
732
        glEnable(GL_CULL_FACE);
×
733
        //frontface culling for ESM!
734
        glCullFace(GL_FRONT);
×
735

736
        //Set viewport to shadowmap
737
        glViewport(0, 0, shadowmapSize, shadowmapSize);
×
738

739
        //Compute an orthographic projection that encompasses the whole scene
740
        //a crop matrix is used to restrict this projection to the subfrusta
741
        float orthoExtent;
742
        computeOrthoProjVals(lightInfo.lightDirectionV3f,orthoExtent,lightOrthoNear,lightOrthoFar);
×
743

744
        QMatrix4x4 lightProj;
×
745
        lightProj.ortho(-orthoExtent,orthoExtent,-orthoExtent,orthoExtent,lightOrthoNear,lightOrthoFar);
×
746

747
        //multiply with lights modelView matrix
748
        QMatrix4x4 lightMVP = lightProj*modelViewMatrix;
×
749

750
        bool success = true;
×
751

752
        //For each split
753
        for(int i=0; i<shaderParameters.frustumSplits; i++)
×
754
        {
755
                //Find the convex body that encompasses all shadow receivers and casters for this split
756
                focusBodies[i].clear();
×
757
                computePolyhedron(focusBodies[i],frustumArray[i],lightInfo.lightDirectionV3f);
×
758

759
                //qDebug() << i << ".split vert count:" << focusBodies[i]->getVertCount();
760

761
                glBindFramebuffer(GL_FRAMEBUFFER,shadowFBOs.at(i));
×
762
                //Clear everything, also if focusbody is empty
763
                glClear(GL_DEPTH_BUFFER_BIT);
×
764

765
                if(lightInfo.shadowCaster != LightParameters::SC_None && focusBodies[i].getVertCount())
×
766
                {
767
                        //Calculate the crop matrix so that the light's frustum is tightly fit to the current split's PSR+PSC polyhedron
768
                        //This alters the ProjectionMatrix of the light
769
                        //the final light matrix used for lookups is stored in shadowCPM
770
                        computeCropMatrix(shadowCPM[i], shadowFrustumSize[i], focusBodies[i],lightProj,lightMVP);
×
771

772
                        //the shadow frustum size is only the scaling, multiply it with the extents of the original matrix
773
                        shadowFrustumSize[i] = QVector4D(shadowFrustumSize[i][0] / orthoExtent, shadowFrustumSize[i][1] / orthoExtent,
×
774
                                        //shadowFrustumSize[i][2], shadowFrustumSize[i][3]);
775
                                        (.5f * shadowFrustumSize[i][2] + .5f) *(lightOrthoFar - lightOrthoNear) + lightOrthoNear,
×
776
                                        (.5f * shadowFrustumSize[i][3] + .5f) *(lightOrthoFar - lightOrthoNear) + lightOrthoNear );
×
777

778
                        //Draw the scene
779
                        if(!drawArrays(false))
×
780
                        {
781
                                success = false;
×
782
                                break;
×
783
                        }
784
                }
785
        }
786

787

788
        //Unbind
789
        glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(defaultFBO));
×
790

791
        //reset viewport (see StelPainter::setProjector)
792
        const Vec4i& vp = altAzProjector->getViewport();
×
793
        glViewport(vp[0], vp[1], vp[2], vp[3]);
×
794

795
        //Move polygons back to normal position
796
        glDisable(GL_POLYGON_OFFSET_FILL);
×
797
        glPolygonOffset(0.0f,0.0f);
×
798

799
        //Reset
800
        glDepthMask(GL_FALSE);
×
801
        glDisable(GL_DEPTH_TEST);
×
802
        glCullFace(GL_BACK);
×
803
        glDisable(GL_CULL_FACE);
×
804

805
        shaderParameters.shadowTransform = false;
×
806

807
        return success;
×
808
}
809

810
void S3DRenderer::calculateLighting()
×
811
{
812
        //calculate which light source we need + intensity
813
        float ambientBrightness=0.0f, directionalBrightness=0.0f, emissiveFactor;
×
814
        float eclipseFactor=static_cast<float>(GETSTELMODULE(SolarSystem)->getSolarEclipseFactor(StelApp::getInstance().getCore()).first);
×
815

816
        Vec3d sunPosition = sun->getAltAzPosAuto(core);
×
817
        sunPosition.normalize();
×
818
        Vec3d moonPosition = moon->getAltAzPosAuto(core);
×
819
        //const float moonPhaseAngle = moon->getPhase(core->getObserverHeliocentricEclipticPos());
820
        const float moonBrightnessFromMag=moon->getVMagnitudeWithExtinction(core)/-12.73f; // This can handle eclipse situations better than just phase angle. Max. mag is around -13 (full moon surge).
×
821
        moonPosition.normalize();
×
822
        Vec3d venusPosition = venus->getAltAzPosAuto(core);
×
823
        const float venusPhaseAngle = venus->getPhase(core->getObserverHeliocentricEclipticPos());
×
824
        venusPosition.normalize();
×
825

826
        // The light model here: ambient light consists of solar twilight and day ambient,
827
        // plus lunar ambient, plus a base constant AMBIENT_BRIGHTNESS_FACTOR[0.1?],
828
        // plus an artificial "torch" that can be toggled via Ctrl-L[ight].
829
        // We define the ambient solar brightness zero when the sun is 18 degrees below the horizon, and lift the sun by 18 deg.
830
        // ambient brightness component of the sun is then  MIN(0.3, sin(sun)+0.3)
831
        // With the sun above the horizon, we raise only the directional component.
832
        // ambient brightness component of the moon is sqrt(sin(alt_moon)*(cos(moon.phase_angle)+1)/2)*LUNAR_BRIGHTNESS_FACTOR[0.2?]
833
        // 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.
834
        //                                sqrt(sin(alt_moon)*(cos(moon.phase_angle)+1)/2)*LUNAR_BRIGHTNESS_FACTOR if sin(alt_moon)>0
835
        //                                sqrt(sin(alt_venus)*(cos(venus.phase_angle)+1)/2)*VENUS_BRIGHTNESS_FACTOR[0.15?]
836
        // Note the sqrt(sin(alt))-terms: they are to increase brightness sooner than with the Lambert law.
837
        //float sinSunAngleRad = sin(qMin(M_PI_2, asin(sunPosition[2])+8.*M_PI/180.));
838
        //float sinMoonAngleRad = moonPosition[2];
839

840
        const float sinSunAngle  =  static_cast<float>(sunPosition[2]);
×
841
        const float sinMoonAngle =  static_cast<float>(moonPosition[2]);
×
842
        const float sinVenusAngle = static_cast<float>(venusPosition[2]);
×
843

844
        //set the minimum ambient brightness
845
        //this uses the LandscapeMgr values
846
        Landscape* l = landscapeMgr->getCurrentLandscape();
×
847
        if (landscapeMgr->getFlagLandscapeUseMinimalBrightness())
×
848
        {
849
                // Setting from landscape.ini has priority if enabled
850
                if (landscapeMgr->getFlagLandscapeSetsMinimalBrightness() && l && l->getLandscapeMinimalBrightness()>=0)
×
851
                        ambientBrightness = static_cast<float>(l->getLandscapeMinimalBrightness());
×
852
                else
853
                        ambientBrightness = static_cast<float>(landscapeMgr->getDefaultMinimalBrightness());
×
854
        }
855

856
        lightInfo.shadowCaster = LightParameters::SC_None;
×
857
        lightInfo.backgroundAmbient = ambientBrightness;
×
858

859
        //assume light=sun for a start.
860
        Vec3d lightPosition = sunPosition;
×
861
        lightInfo.directionalSource = LightParameters::DS_Sun_Horiz;
×
862

863
        //calculate emissive factor
864
        if(l!=Q_NULLPTR)
×
865
        {
866
                if(requiresCubemap && lazyDrawing)
×
867
                {
868
                        emissiveFactor = static_cast<float>(l->getTargetLightscapeBrightness());
×
869
                }
870
                else
871
                {
872
                        //use an interpolated value for smooth fade in/out
873
                        emissiveFactor = static_cast<float>(l->getEffectiveLightscapeBrightness());
×
874
                }
875
        }
876
        else
877
        {
878
                // I don't know if this can ever happen, but in this case,
879
                // directly use the same model as LandscapeMgr::update uses for the lightscapeBrightness
880
                emissiveFactor = 0.0f;
×
881
                if (sinSunAngle<-0.14f) emissiveFactor=1.0f;
×
882
                else if (sinSunAngle<-0.05f) emissiveFactor = 1.0f-(sinSunAngle+0.14f)/(-0.05f+0.14f);
×
883
        }
884

885
        // calculate ambient light
886
        if(sinSunAngle > -0.3f) // sun above -18 deg?
×
887
        {
888
                lightInfo.sunAmbient = qMin(0.3f, sinSunAngle+0.3f)*eclipseFactor;
×
889
                ambientBrightness += lightInfo.sunAmbient;
×
890
        }
891
        else
892
                lightInfo.sunAmbient = 0.0f;
×
893

894
        if ((sinMoonAngle>0.0f) && (sinSunAngle<0.0f))
×
895
        {
896
                //lightInfo.moonAmbient = sqrtf(sinMoonAngle * ((cosf(moonPhaseAngle)+1.0f)*0.5f)) * LUNAR_BRIGHTNESS_FACTOR;
897
                lightInfo.moonAmbient = sqrtf(sinMoonAngle * moonBrightnessFromMag) * LUNAR_BRIGHTNESS_FACTOR;
×
898
                ambientBrightness += lightInfo.moonAmbient;
×
899
        }
900
        else
901
                lightInfo.moonAmbient = 0.0f;
×
902

903
        // Now find shadow caster + directional light, if any:
904
        if (sinSunAngle>-0.1f)
×
905
        {
906
                directionalBrightness=qMin(0.7f, sqrtf(sinSunAngle+0.1f))*eclipseFactor; // limit to 0.7 in order to keep total below 1.
×
907
                //redundant
908
                //lightPosition = sunPosition;
909
                if (shaderParameters.shadows) lightInfo.shadowCaster = LightParameters::SC_Sun;
×
910
                lightInfo.directionalSource = LightParameters::DS_Sun;
×
911
        }
912
        // "else" is required now, else we have lunar shadow with sun above horizon...
913
        // and "else" implies sinSunAngle<-0.1 or sun <-6 degrees.
914
        else if (sinMoonAngle>0.0f)
×
915
        {
916
                float moonBrightness = sqrtf(sinMoonAngle) * moonBrightnessFromMag * LUNAR_BRIGHTNESS_FACTOR;
×
917
                moonBrightness -= (ambientBrightness-0.05f)*0.5f;
×
918
                moonBrightness = qMax(0.0f,moonBrightness);
×
919
                if (moonBrightness > 0.0f)
×
920
                {
921
                        directionalBrightness = moonBrightness;
×
922
                        lightPosition = moonPosition;
×
923
                        if (shaderParameters.shadows) lightInfo.shadowCaster = LightParameters::SC_Moon;
×
924
                }
925
                lightInfo.directionalSource = LightParameters::DS_Moon;
×
926
                //Alternately, construct a term around lunar brightness, like
927
                // directionalBrightness=(mag/-10)
928
        }
929
        else if (sinVenusAngle>0.0f)
×
930
        {
931
                float venusBrightness = sqrtf(sinVenusAngle)*((cosf(venusPhaseAngle)+1)*0.5f) * VENUS_BRIGHTNESS_FACTOR;
×
932
                venusBrightness -= (ambientBrightness-0.05f)/2.0f;
×
933
                venusBrightness = qMax(0.0f, venusBrightness);
×
934
                if (venusBrightness > 0.0f)
×
935
                {
936
                        directionalBrightness = venusBrightness;
×
937
                        lightPosition = venusPosition;
×
938
                        if (shaderParameters.shadows) lightInfo.shadowCaster = LightParameters::SC_Venus;
×
939
                        lightInfo.directionalSource = LightParameters::DS_Venus;
×
940
                } else lightInfo.directionalSource = LightParameters::DS_Venus_Ambient;
×
941
                //Alternately, construct a term around Venus brightness, like
942
                // directionalBrightness=(mag/-100)
943
        }
944

945
        //convert to float
946
        lightInfo.lightDirectionV3f = lightPosition.toVec3f();
×
947
        lightInfo.lightDirectionWorld = lightPosition.toQVector();
×
948

949
        lightInfo.landscapeOpacity = 0.0f;
×
950

951
        //check landscape occlusion, modify directional if needed
952
        if(directionalBrightness>0)
×
953
        {
954
                if(l)
×
955
                {
956
                        //TODO the changes are currently rather harsh, find a better method (like angular distance of light source to horizon, or bitmap interpolation for the alpha values)
957
                        lightInfo.landscapeOpacity = l->getOpacity(lightPosition);
×
958

959
                        //lerp between the determined opacity and 1.0, depending on landscape fade (visibility)
960
                        float fadeValue = 1.0f + l->getEffectiveLandFadeValue() * (-lightInfo.landscapeOpacity);
×
961
                        directionalBrightness *= fadeValue;
×
962
                }
963
        }
964

965
        //specular factor is calculated from other values for now
966
        float specular = std::min(ambientBrightness*directionalBrightness*5.0f,1.0f);
×
967

968
        //if the night vision mode is on, use red-tinted lighting
969
        bool red=StelApp::getInstance().getVisionModeNight();
×
970

971
        float torchDiff = shaderParameters.torchLight ? torchBrightness : 0.0f;
×
972
        lightInfo.torchAttenuation = 1.0f / (torchRange * torchRange);
×
973

974
        if(red)
×
975
        {
976
                lightInfo.ambient = QVector3D(ambientBrightness,0, 0);
×
977
                lightInfo.directional = QVector3D(directionalBrightness,0,0);
×
978
                lightInfo.emissive = QVector3D(emissiveFactor,0,0);
×
979
                lightInfo.specular = QVector3D(specular,0,0);
×
980
                lightInfo.torchDiffuse = QVector3D(torchDiff,0,0);
×
981
        }
982
        else
983
        {
984
                //for now, lighting is only white
985
                lightInfo.ambient = QVector3D(ambientBrightness,ambientBrightness, ambientBrightness);
×
986
                lightInfo.directional = QVector3D(directionalBrightness,directionalBrightness,directionalBrightness);
×
987
                lightInfo.emissive = QVector3D(emissiveFactor,emissiveFactor,emissiveFactor);
×
988
                lightInfo.specular = QVector3D(specular,specular,specular);
×
989
                lightInfo.torchDiffuse = QVector3D(torchDiff,torchDiff,torchDiff);
×
990
        }
991
}
×
992

993
void S3DRenderer::calcCubeMVP(const Vec3d translation)
×
994
{
995
        QMatrix4x4 tmp;
×
996
        for(int i = 0;i<6;++i)
×
997
        {
998
                tmp = cubeRotation[i];
×
999
                tmp.translate(static_cast<float>(translation.v[0]), static_cast<float>(translation.v[1]), static_cast<float>(translation.v[2]));
×
1000
                cubeMVP[i] = projectionMatrix * tmp;
×
1001
        }
1002
}
×
1003

1004
void S3DRenderer::renderIntoCubemapGeometryShader()
×
1005
{
1006
        //single FBO
1007
        glBindFramebuffer(GL_FRAMEBUFFER,cubeFBO);
×
1008

1009
        //Hack: because the modelviewmatrix is used for lighting in shader, but we dont want to perform MV transformations 6 times,
1010
        // we just set the position because that currently is all that is needed for correct lighting
1011
        modelViewMatrix.setToIdentity();
×
1012
        Vec3d negEyePos = -currentScene->getEyePosition();
×
1013
        modelViewMatrix.translate(static_cast<float>(negEyePos.v[0]), static_cast<float>(negEyePos.v[1]), static_cast<float>(negEyePos.v[2]));
×
1014
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
×
1015

1016
        //render all 6 faces at once
1017
        shaderParameters.geometryShader = true;
×
1018
        //calculate the final required matrices for each face
1019
        calcCubeMVP(negEyePos);
×
1020
        drawArrays(true,true);
×
1021
        shaderParameters.geometryShader = false;
×
1022
}
×
1023

1024
void S3DRenderer::renderShadowMapsForFace(int face)
×
1025
{
1026
        //extract view dir from the MV matrix
1027
        QVector4D viewDir = -cubeRotation[face].row(2);
×
1028

1029
        //somewhere, there are problems when the view direction points exactly up or down, causing missing shadows
1030
        //this is NOT fixed by choosing a different up vector here as could be expected
1031
        //the problem seems to occur during final rendering because shadowmap textures look alright and the scaling values seem valid
1032
        //for now, fix this by adding a tiny value to X in these cases
1033
        adjustShadowFrustum(currentScene->getEyePosition(),
×
1034
                            Vec3d(static_cast<double>(face>3?viewDir[0]+0.000001f:viewDir[0]),static_cast<double>(viewDir[1]),static_cast<double>(viewDir[2])),
×
1035
                        Vec3d(0,0,1),90.0f,1.0f);
×
1036
        //render shadowmap
1037
        if(!renderShadowMaps())
×
1038
                return;
×
1039

1040
        //gl state + viewport must be reset
1041
        glEnable(GL_DEPTH_TEST);
×
1042
        glDepthMask(GL_TRUE);
×
1043
        glEnable(GL_CULL_FACE);
×
1044
        glViewport(0, 0, cubemapSize, cubemapSize);
×
1045
}
1046

1047
void S3DRenderer::renderIntoCubemapSixPasses()
×
1048
{
1049
        //store current projection (= 90° cube projection)
1050
        QMatrix4x4 squareProjection = projectionMatrix;
×
1051

1052
        const Vec3f& eyePos = currentScene->getEyePosition().toVec3f();
×
1053

1054
        if(needsMovementUpdate && updateOnlyDominantOnMoving)
×
1055
        {
1056
                if(shaderParameters.shadows && fullCubemapShadows)
×
1057
                {
1058
                        //in the BASIC and FULL modes, the shadow frustum needs to be adapted to the cube side
1059
                        renderShadowMapsForFace(dominantFace);
×
1060
                        //projection needs to be reset
1061
                        projectionMatrix = squareProjection;
×
1062
                }
1063

1064
                //update only the dominant face
1065
                glBindFramebuffer(GL_FRAMEBUFFER, cubeSideFBO[dominantFace]);
×
1066
                modelViewMatrix = cubeRotation[dominantFace];
×
1067
                modelViewMatrix.translate(-eyePos.v[0], -eyePos.v[1], -eyePos.v[2]);
×
1068
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
×
1069

1070
                drawArrays(true,true);
×
1071

1072
                if(updateSecondDominantOnMoving)
×
1073
                {
1074
                        if(shaderParameters.shadows && fullCubemapShadows)
×
1075
                        {
1076
                                //in the BASIC and FULL modes, the shadow frustum needs to be adapted to the cube side
1077
                                renderShadowMapsForFace(secondDominantFace);
×
1078
                                //projection needs to be reset
1079
                                projectionMatrix = squareProjection;
×
1080
                        }
1081

1082
                        //update also the second-most dominant face
1083
                        glBindFramebuffer(GL_FRAMEBUFFER, cubeSideFBO[secondDominantFace]);
×
1084

1085
                        modelViewMatrix = cubeRotation[secondDominantFace];
×
1086
                        modelViewMatrix.translate(-eyePos.v[0], -eyePos.v[1], -eyePos.v[2]);
×
1087
                        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
×
1088

1089
                        drawArrays(true,true);
×
1090
                }
1091
        }
1092
        else
1093
        {
1094
                //traditional 6-pass version
1095
                for(int i=0;i<6;++i)
×
1096
                {
1097
                        if(shaderParameters.shadows && fullCubemapShadows)
×
1098
                        {
1099
                                //in the BASIC and FULL modes, the shadow frustum needs to be adapted to the cube side
1100
                                renderShadowMapsForFace(i);
×
1101
                                //projection needs to be reset
1102
                                projectionMatrix = squareProjection;
×
1103
                        }
1104

1105
                        //bind a single side of the cube
1106
                        glBindFramebuffer(GL_FRAMEBUFFER, cubeSideFBO[i]);
×
1107

1108
                        modelViewMatrix = cubeRotation[i];
×
1109
                        modelViewMatrix.translate(-eyePos.v[0], -eyePos.v[1], -eyePos.v[2]);
×
1110
                        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
×
1111

1112
                        drawArrays(true,true);
×
1113
                }
1114
        }
1115
}
×
1116

1117
void S3DRenderer::generateCubeMap()
×
1118
{
1119
        //recalculate lighting info
1120
        calculateLighting();
×
1121

1122
        //do shadow pass
1123
        //only calculate shadows if enabled
1124
        if(shaderParameters.shadows)
×
1125
        {
1126
                //shadow caster info only needs to be calculated once
1127
                calculateShadowCaster();
×
1128

1129
                //GS mode only supports the perspective shadows
1130
                if(!fullCubemapShadows || cubemappingMode == S3DEnum::CM_CUBEMAP_GSACCEL)
×
1131
                {
1132
                        //in this mode, shadow frusta are calculated the same as in perspective mode
1133
                        float fov = altAzProjector->getFov();
×
1134
                        float aspect = static_cast<float>(altAzProjector->getViewportWidth()) / static_cast<float>(altAzProjector->getViewportHeight());
×
1135

1136
                        adjustShadowFrustum(currentScene->getEyePosition(),currentScene->getViewDirection(),Vec3d(0.,0.,1.),fov,aspect);
×
1137
                        if(!renderShadowMaps())
×
1138
                                return; //shadow map rendering failed, do an early abort
×
1139
                }
1140
        }
1141

1142
        const SceneInfo& info = currentScene->getSceneInfo();
×
1143

1144
        //setup projection matrix - this is a 90-degree perspective with aspect 1.0
1145
        projectionMatrix.setToIdentity();
×
1146
        projectionMatrix.perspective(90.0f,1.0f,info.camNearZ,info.camFarZ);
×
1147

1148
        //set opengl viewport to the size of cubemap
1149
        glViewport(0, 0, cubemapSize, cubemapSize);
×
1150

1151
        //set GL state - we want depth test + culling
1152
        glEnable(GL_DEPTH_TEST);
×
1153
        //glDepthFunc(GL_LEQUAL);
1154
        glDepthMask(GL_TRUE);
×
1155
        glEnable(GL_CULL_FACE);
×
1156

1157
        if(cubemappingMode == S3DEnum::CM_CUBEMAP_GSACCEL)
×
1158
        {
1159
                //In this mode, only the "perspective" shadow mode can be used (otherwise it would need up to 6*4 shadowmaps at once)
1160
                renderIntoCubemapGeometryShader();
×
1161
        }
1162
        else
1163
        {
1164
                renderIntoCubemapSixPasses();
×
1165
        }
1166

1167
        //cubemap fbo must be released
1168
        glBindFramebuffer(GL_FRAMEBUFFER,defaultFBO);
×
1169

1170
        //reset GL state
1171
        glDepthMask(GL_FALSE);
×
1172
        glDisable(GL_DEPTH_TEST);
×
1173
        glDisable(GL_CULL_FACE);
×
1174

1175
        //reset viewport (see StelPainter::setProjector)
1176
        const Vec4i& vp = altAzProjector->getViewport();
×
1177
        glViewport(vp[0], vp[1], vp[2], vp[3]);
×
1178

1179
        if(needsCubemapUpdate)
×
1180
        {
1181
                lastCubemapUpdate = core->getJD();
×
1182
                lastCubemapUpdateRealTime = QDateTime::currentMSecsSinceEpoch();
×
1183
        }
1184
}
1185

1186
void S3DRenderer::drawFromCubeMap()
×
1187
{
1188
        QOpenGLShaderProgram* cubeShader;
1189

1190
        if(cubemappingMode>=S3DEnum::CM_CUBEMAP)
×
1191
                cubeShader = shaderManager.getCubeShader();
×
1192
        else
1193
                cubeShader = shaderManager.getTextureShader();
×
1194

1195
        cubeShader->bind();
×
1196

1197
        //We simulate the generate behavior of drawStelVertexArray ourselves
1198
        //check if discontinuties exist
1199
        //if(altAzProjector->hasDiscontinuity())
1200
        //{
1201
        //TODO fix similar to StelVertexArray::removeDiscontinuousTriangles
1202
        //this may only happen for some projections, and even then it may be preferable to simply ignore them (as done now) to retain performance
1203
        //}
1204

1205
        //transform vertices on CPU side - maybe we could do this multithreaded, kicked off at the beginning of the frame?
1206
        altAzProjector->project(cubeVertices.count(),cubeVertices.constData(),transformedCubeVertices.data());
×
1207

1208
        vao.bind();
×
1209
        //setup shader params
1210
        projectionMatrix = altAzProjector->getProjectionMatrix().toQMatrix();
×
1211
        cubeShader->setUniformValue(shaderManager.uniformLocation(cubeShader,ShaderMgr::UNIFORM_MAT_PROJECTION), projectionMatrix);
×
1212
        cubeShader->setUniformValue(shaderManager.uniformLocation(cubeShader,ShaderMgr::UNIFORM_TEX_DIFFUSE),0);
×
1213
        cubeVertexBuffer.bind();
×
1214
        if(cubemappingMode>=S3DEnum::CM_CUBEMAP)
×
1215
                cubeShader->setAttributeBuffer(StelOpenGLArray::ATTLOC_TEXCOORD,GL_FLOAT,0,3);
×
1216
        else // 2D tex coords are stored in the same buffer, but with an offset
1217
                cubeShader->setAttributeBuffer(StelOpenGLArray::ATTLOC_TEXCOORD,GL_FLOAT,cubeVertices.size() * static_cast<int>(sizeof(Vec3f)),2);
×
1218
        cubeShader->enableAttributeArray(StelOpenGLArray::ATTLOC_TEXCOORD);
×
1219
        cubeVertexBuffer.release();
×
1220

1221
        //upload transformed vertex data
1222
        transformedCubeVertexBuffer.bind();
×
1223
        transformedCubeVertexBuffer.allocate(transformedCubeVertices.constData(), transformedCubeVertices.size() * static_cast<int>(sizeof(Vec3f)));
×
1224
        cubeShader->setAttributeBuffer(StelOpenGLArray::ATTLOC_VERTEX, GL_FLOAT, 0,3);
×
1225
        cubeShader->enableAttributeArray(StelOpenGLArray::ATTLOC_VERTEX);
×
1226
        transformedCubeVertexBuffer.release();
×
1227

1228
        glEnable(GL_BLEND);
×
1229
        //note that GL_ONE is required here for correct blending (see drawArrays)
1230
        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
×
1231
        //depth test and culling is necessary for correct display,
1232
        //because the cube faces can be projected in quite "weird" ways
1233
        glEnable(GL_DEPTH_TEST);
×
1234
        //glDepthFunc(GL_LESS);
1235
        glDepthMask(GL_TRUE);
×
1236
        glEnable(GL_CULL_FACE);
×
1237

1238
        glClear(GL_DEPTH_BUFFER_BIT);
×
1239

1240
        cubeIndexBuffer.bind();
×
1241
        glActiveTexture(GL_TEXTURE0);
×
1242
        if(cubemappingMode>=S3DEnum::CM_CUBEMAP)
×
1243
        {
1244
                //can render in a single draw call
1245
                glBindTexture(GL_TEXTURE_CUBE_MAP,cubeMapCubeTex);
×
1246
                glDrawElements(GL_TRIANGLES,cubeIndexCount,GL_UNSIGNED_SHORT, Q_NULLPTR);
×
1247
        }
1248
        else
1249
        {
1250
                //use 6 drawcalls
1251
                int faceIndexCount = cubeIndexCount / 6;
×
1252
                for(int i =0;i<6;++i)
×
1253
                {
1254
                        glBindTexture(GL_TEXTURE_2D, cubeMapTex[i]);
×
1255
                        glDrawElements(GL_TRIANGLES,faceIndexCount, GL_UNSIGNED_SHORT, reinterpret_cast<const GLvoid*>(static_cast<size_t>(i * faceIndexCount) * sizeof(short)));
×
1256
                }
1257
        }
1258
        cubeIndexBuffer.release();
×
1259

1260
        cubeShader->disableAttributeArray(StelOpenGLArray::ATTLOC_TEXCOORD);
×
1261
        cubeShader->disableAttributeArray(StelOpenGLArray::ATTLOC_VERTEX);
×
1262

1263
        vao.release();
×
1264

1265
        glDisable(GL_CULL_FACE);
×
1266
        glDisable(GL_DEPTH_TEST);
×
1267
        glDisable(GL_BLEND);
×
1268

1269
        cubeShader->release();
×
1270
}
×
1271

1272
void S3DRenderer::drawDirect() // for Perspective Projection only!
×
1273
{
1274
    //calculate standard perspective projection matrix, use QMatrix4x4 for that
1275
    float fov = altAzProjector->getFov();
×
1276
    float aspect = static_cast<float>(altAzProjector->getViewportWidth()) / static_cast<float>(altAzProjector->getViewportHeight());
×
1277

1278
    //calc modelview transform
1279
    QMatrix4x4 mvMatrix = altAzProjector->getModelViewTransform()->getApproximateLinearTransfo().toQMatrix();
×
1280
    mvMatrix.optimize(); //may make inversion faster?
×
1281

1282
    //recalculate lighting info
1283
    calculateLighting();
×
1284

1285
    const Vec3d& eyePos = currentScene->getEyePosition();
×
1286
    const SceneInfo& info = currentScene->getSceneInfo();
×
1287

1288
    //do shadow pass
1289
    //only calculate shadows if enabled
1290
    if(shaderParameters.shadows)
×
1291
    {
1292
            calculateShadowCaster();
×
1293

1294
            //no need to extract view information, use the direction from stellarium
1295
            adjustShadowFrustum(eyePos,currentScene->getViewDirection(),Vec3d(0.0,0.0,1.0),fov,aspect);
×
1296

1297
            //this call modifies projection + mv matrices, so we have to set them afterwards
1298
            if(!renderShadowMaps())
×
1299
                    return; //shadow map rendering failed, do an early abort
×
1300
    }
1301

1302
    mvMatrix.translate(static_cast<float>(-eyePos.v[0]),static_cast<float>(-eyePos.v[1]),static_cast<float>(-eyePos.v[2]));
×
1303

1304
    //set final rendering matrices
1305
    modelViewMatrix = mvMatrix;
×
1306
    projectionMatrix.setToIdentity();
×
1307

1308
    //without viewport offset, you could simply call this:
1309
    //projectionMatrix.perspective(fov,aspect,currentScene.camNearZ,currentScene.camFarZ);
1310
    //these 2 lines replicate gluPerspective with glFrustum
1311
    float fH = std::tan( fov / 360.0f * M_PIf ) * info.camNearZ;
×
1312
    float fW = fH * aspect;
×
1313

1314
    //apply offset values
1315
    Vector2<qreal> vp = altAzProjector->getViewportCenterOffset();
×
1316
    float horizOffset = 2.0f * fW * static_cast<float>(vp[0]);
×
1317
    float vertOffset = - 2.0f * fH * static_cast<float>(vp[1]);
×
1318

1319
    //final projection matrix
1320
    projectionMatrix.frustum(-fW + horizOffset, fW + horizOffset,
×
1321
                             -fH + vertOffset, fH + vertOffset,
1322
                             info.camNearZ, info.camFarZ);
×
1323

1324
    //depth test needs enabling, clear depth buffer, color buffer already contains background so it stays
1325
    glEnable(GL_DEPTH_TEST);
×
1326
    //glDepthFunc(GL_LEQUAL);
1327
    glDepthMask(GL_TRUE);
×
1328
    glClear(GL_DEPTH_BUFFER_BIT);
×
1329

1330
    //enable backface culling for increased performance
1331
    glEnable(GL_CULL_FACE);
×
1332

1333
    //only 1 call needed here
1334
    drawArrays(true);
×
1335

1336
    glDepthMask(GL_FALSE);
×
1337
    glDisable(GL_DEPTH_TEST);
×
1338
    glDisable(GL_CULL_FACE);
×
1339
}
1340

1341
void S3DRenderer::drawWithCubeMap()
×
1342
{
1343
        if(needsCubemapUpdate || needsMovementUpdate)
×
1344
        {
1345
                //lazy redrawing: update cubemap in slower intervals
1346
                generateCubeMap();
×
1347
        }
1348
        drawFromCubeMap();
×
1349
}
×
1350

1351
void S3DRenderer::drawCoordinatesText()
×
1352
{
1353
        StelPainter painter(altAzProjector);
×
1354
        painter.setFont(debugTextFont);
×
1355
        painter.setColor(1.0f,0.5f,1.0f);
×
1356

1357
        // Attempt at better font scaling for Macs and HiDPI.
1358
        int fontSize=debugTextFont.pixelSize();
×
1359

1360
        float devicePixelscaling = static_cast<float>(altAzProjector->getDevicePixelsPerPixel())*StelApp::getInstance().getGlobalScalingRatio();
×
1361
        float screen_x = altAzProjector->getViewportWidth()  - 240.0f*devicePixelscaling;
×
1362
        float screen_y = altAzProjector->getViewportHeight() -  60.0f*devicePixelscaling;
×
1363
        QString str;
×
1364

1365
        Vec3d gridPos = currentScene->getGridPosition();
×
1366

1367
        const SceneInfo& info = currentScene->getSceneInfo();
×
1368

1369
        // problem: long grid names!
1370
        painter.drawText(altAzProjector->getViewportWidth()-10.f-devicePixelscaling*qMax(240, painter.getFontMetrics().boundingRect(info.gridName).width()),
×
1371
                         screen_y, info.gridName);
×
1372
        screen_y -= (fontSize+1)*devicePixelscaling;
×
1373
        str = QString("East:   %1m").arg(gridPos[0], 10, 'f', 2);
×
1374
        painter.drawText(screen_x, screen_y, str);
×
1375
        screen_y -= (fontSize-1)*devicePixelscaling;
×
1376
        str = QString("North:  %1m").arg(gridPos[1], 10, 'f', 2);
×
1377
        painter.drawText(screen_x, screen_y, str);
×
1378
        screen_y -= (fontSize-1)*devicePixelscaling;
×
1379
        str = QString("Height: %1m").arg(gridPos[2], 10, 'f', 2);
×
1380
        painter.drawText(screen_x, screen_y, str);
×
1381
        screen_y -= (fontSize-1)*devicePixelscaling;
×
1382
        str = QString("Eye:    %1m").arg(currentScene->getEyeHeight(), 10, 'f', 2);
×
1383
        painter.drawText(screen_x, screen_y, str);
×
1384

1385
//        DEBUG AIDS:
1386
//        screen_y -= 15.0f;
1387
//        str = QString("model_X:%1m").arg(model_pos[0], 10, 'f', 2);
1388
//        painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
1389
//        str = QString("model_Y:%1m").arg(model_pos[1], 10, 'f', 2);
1390
//        painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
1391
//        str = QString("model_Z:%1m").arg(model_pos[2], 10, 'f', 2);
1392
//        painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
1393
//        str = QString("abs_X:  %1m").arg(absolutePosition.v[0], 10, 'f', 2);
1394
//        painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
1395
//        str = QString("abs_Y:  %1m").arg(absolutePosition.v[1], 10, 'f', 2);
1396
//        painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
1397
//        str = QString("abs_Z:  %1m").arg(absolutePosition.v[2], 10, 'f', 2);
1398
//        painter.drawText(screen_x, screen_y, str);screen_y -= 15.0f;
1399
//        str = QString("groundNullHeight: %1m").arg(groundNullHeight, 7, 'f', 2);
1400
//        painter.drawText(screen_x, screen_y, str);
1401
}
×
1402

1403
void S3DRenderer::drawDebug()
×
1404
{
1405
        //frustum/box debug rendering only on desktop GL
1406
#if !QT_CONFIG(opengles2)
1407
        if(!shaderParameters.openglES)
×
1408
        {
1409
                QOpenGLShaderProgram* debugShader = shaderManager.getDebugShader();
×
1410
                if(debugShader)
×
1411
                {
1412
                        debugShader->bind();
×
1413

1414
                        //ensure that opengl matrix stack is empty
1415
                        glExtFuncs->glMatrixMode(GL_MODELVIEW);
×
1416
                        glExtFuncs->glLoadIdentity();
×
1417
                        glExtFuncs->glMatrixMode(GL_PROJECTION);
×
1418
                        glExtFuncs->glLoadIdentity();
×
1419

1420
                        //set mvp
1421
                        SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_MAT_MVP,projectionMatrix * modelViewMatrix);
×
1422

1423
                        //SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR,QVector4D(1.0f,1.0f,1.0f,1.0f));
1424
                        //sceneBoundingBox.render();
1425

1426
                        //SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR,QVector4D(0.4f,0.4f,0.4f,1.0f));
1427
                        //objModel->renderAABBs();
1428

1429
                        SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR,QVector4D(1.0f,1.0f,1.0f,1.0f));
×
1430

1431
                        if(fixShadowData)
×
1432
                        {
1433
                                camFrustShadow.drawFrustum();
×
1434
                                /*
1435
                        SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR,QVector4D(1.0f,0.0f,1.0f,1.0f));
1436
                        frustumArray.at(0).drawFrustum();
1437
                        SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR,QVector4D(0.0f,1.0f,0.0f,1.0f));
1438
                        focusBodies.at(0).render();
1439
                        SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR,QVector4D(0.0f,1.0f,1.0f,1.0f));
1440
                        focusBodies.at(0).debugBox.render();
1441
                        SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR,QVector4D(1.0f,0.0f,0.0f,1.0f));
1442
                        focusBodies.at(1).render();
1443
                        SET_UNIFORM(debugShader,ShaderMgr::UNIFORM_VEC_COLOR,QVector4D(1.0f,0.0f,1.0f,1.0f));
1444
                        focusBodies.at(1).debugBox.render();
1445
                        */
1446
                        }
1447

1448
                        debugShader->release();
×
1449
                }
1450
                else
1451
                {
1452
                        qCWarning(s3drenderer)<<"Cannot use debug shader, probably on OpenGL ES context";
×
1453
                }
1454
        }
1455
#endif
1456

1457
        const SceneInfo& info = currentScene->getSceneInfo();
×
1458

1459
        StelPainter painter(altAzProjector);
×
1460

1461
        const QStringList shadowCasterNames = {"None", "Sun", "Moon", "Venus"};
×
1462
        QString shadowCasterName="Error!!!";
×
1463
        Q_ASSERT(lightInfo.shadowCaster<=LightParameters::SC_Venus);
×
1464
        shadowCasterName = shadowCasterNames.at(lightInfo.shadowCaster);
×
1465

1466
        const QStringList directionalSourceStrings = { "(Sun, below horiz.)", "Sun", "Moon", "Venus", "(Venus, flooded by ambient)"};
×
1467
        QString directionalSourceString="Error!!!";
×
1468
        Q_ASSERT(lightInfo.directionalSource<=LightParameters::DS_Venus_Ambient);
×
1469
        directionalSourceString = directionalSourceStrings.at(lightInfo.directionalSource);
×
1470

1471
        const QString lightMessage=QString("Ambient: %1 Directional: %2. Shadows cast by: %3 from %4/%5/%6")
×
1472
                        .arg(lightInfo.ambient[0], 6, 'f', 4).arg(lightInfo.directional[0], 6, 'f', 4)
×
1473
                        .arg(shadowCasterName).arg(lightInfo.lightDirectionV3f.v[0], 6, 'f', 4)
×
1474
                        .arg(lightInfo.lightDirectionV3f.v[1], 6, 'f', 4).arg(lightInfo.lightDirectionV3f.v[2], 6, 'f', 4);
×
1475
        const QString lightMessage2=QString("Contributions: Ambient     Sun: %1, Moon: %2, Background+^L: %3")
×
1476
                        .arg(lightInfo.sunAmbient, 6, 'f', 4).arg(lightInfo.moonAmbient, 6, 'f', 4).arg(lightInfo.backgroundAmbient, 6, 'f', 4);
×
1477
        const QString lightMessage3=QString("               Directional %1 by: %2, emissive factor: %3, landscape opacity: %4")
×
1478
                        .arg(lightInfo.directional[0], 6, 'f', 4).arg(directionalSourceString).arg(lightInfo.emissive[0]).arg(lightInfo.landscapeOpacity);
×
1479

1480

1481
        painter.setFont(debugTextFont);
×
1482
        painter.setColor(1.f,0.f,1.f,1.f);
×
1483
        // For now, these messages print light mixture values.
1484
        painter.drawText(20, 160, lightMessage);
×
1485
        painter.drawText(20, 145, lightMessage2);
×
1486
        painter.drawText(20, 130, lightMessage3);
×
1487
        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]));
×
1488

1489
        const AABBox& bbox = currentScene->getSceneAABB();
×
1490
        QString str = QString("BB: %1/%2/%3 %4/%5/%6").arg(bbox.min.v[0], 7, 'f', 2).arg(bbox.min.v[1], 7, 'f', 2).arg(bbox.min.v[2], 7, 'f', 2)
×
1491
                        .arg(bbox.max.v[0], 7, 'f', 2).arg(bbox.max.v[1], 7, 'f', 2).arg(bbox.max.v[2], 7, 'f', 2);
×
1492
        painter.drawText(10, 100, str);
×
1493
        // PRINT OTHER MESSAGES HERE:
1494

1495
        float screen_x = altAzProjector->getViewportWidth()  - 500.0f;
×
1496
        float screen_y = altAzProjector->getViewportHeight() - 300.0f;
×
1497

1498
        //Show some debug aids
1499
        if(debugEnabled)
×
1500
        {
1501
                float debugTextureSize = 128.0f;
×
1502
                float screen_x = altAzProjector->getViewportWidth() - debugTextureSize - 30;
×
1503
                float screen_y = altAzProjector->getViewportHeight() - debugTextureSize - 30;
×
1504

1505
                if(shaderParameters.shadows)
×
1506
                {
1507
                        QString cap("SM %1");
×
1508

1509
                        for(int i=0; i<shaderParameters.frustumSplits; i++)
×
1510
                        {
1511
                                painter.drawText(screen_x+70, screen_y+130, cap.arg(i));
×
1512

1513
                                glBindTexture(GL_TEXTURE_2D, shadowMapsArray[i]);
×
1514
                                painter.drawSprite2dMode(screen_x, screen_y, debugTextureSize);
×
1515

1516
                                const int tmp = qRound(screen_y - debugTextureSize)-30;
×
1517
                                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));
×
1518
                                painter.drawText(screen_x-125, tmp-15.0f, QString("uv scale: %1/%2").arg(shadowFrustumSize[i].x(), 7, 'f', 2).arg(shadowFrustumSize[i].y(),7,'f',2));
×
1519
                                painter.drawText(screen_x-125, tmp-30.0f, QString("ortho n/f: %1/%2").arg(shadowFrustumSize[i].z(), 7, 'f', 2).arg(shadowFrustumSize[i].w(),7,'f',2));
×
1520

1521
                                screen_x -= 290;
×
1522
                        }
1523
                        painter.drawText(screen_x+165.0f, screen_y-215.0f, QString("Splitweight: %1").arg(info.shadowSplitWeight, 3, 'f', 2));
×
1524
                        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));
×
1525
                }
×
1526
        }
1527

1528
        screen_y -= 100.f;
×
1529
        str = QString("Last frame stats:");
×
1530
        painter.drawText(screen_x, screen_y, str);
×
1531
        screen_y -= 15.0f;
×
1532
        str = QString("%1 tris, %2 mdls").arg(drawnTriangles).arg(drawnModels);
×
1533
        painter.drawText(screen_x, screen_y, str);
×
1534
        screen_y -= 15.0f;
×
1535
        str = QString("%1 mats, %2 shaders").arg(materialSwitches).arg(shaderSwitches);
×
1536
        painter.drawText(screen_x, screen_y, str);
×
1537
        screen_y -= 15.0f;
×
1538
        str = "View Pos";
×
1539
        painter.drawText(screen_x, screen_y, str);
×
1540
        screen_y -= 15.0f;
×
1541
        const Vec3d& viewPos = currentScene->getEyePosition();
×
1542
        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);
×
1543
        painter.drawText(screen_x, screen_y, str);
×
1544
        screen_y -= 15.0f;
×
1545
        str = "View Dir, dominant faces";
×
1546
        painter.drawText(screen_x, screen_y, str);
×
1547
        screen_y -= 15.0f;
×
1548
        Vec3d mainViewDir = currentScene->getViewDirection();
×
1549
        str = QString("%1 %2 %3, %4/%5").arg(mainViewDir.v[0], 7, 'f', 2).arg(mainViewDir.v[1], 7, 'f', 2).arg(mainViewDir.v[2], 7, 'f', 2).arg(dominantFace).arg(secondDominantFace);
×
1550
        painter.drawText(screen_x, screen_y, str);
×
1551
        screen_y -= 15.0f;
×
1552
        str = "View Up";
×
1553
        painter.drawText(screen_x, screen_y, str);
×
1554
        screen_y -= 15.0f;
×
1555
        if(requiresCubemap)
×
1556
        {
1557
                screen_y -= 15.0f;
×
1558
                str = QString("Last cubemap update: %1ms ago").arg(QDateTime::currentMSecsSinceEpoch() - lastCubemapUpdateRealTime);
×
1559
                painter.drawText(screen_x, screen_y, str);
×
1560
                screen_y -= 15.0f;
×
1561
                str = QString("Last cubemap update JDAY: %1").arg(qAbs(core->getJD()-lastCubemapUpdate) * StelCore::ONE_OVER_JD_SECOND);
×
1562
                painter.drawText(screen_x, screen_y, str);
×
1563
        }
1564

1565
        screen_y -= 30.0f;
×
1566
        str = QString("Venus: %1").arg(lightInfo.shadowCaster == LightParameters::SC_Venus);
×
1567
        painter.drawText(screen_x, screen_y, str);
×
1568
}
×
1569

1570
void S3DRenderer::determineFeatureSupport()
×
1571
{
1572
        QOpenGLContext* ctx = QOpenGLContext::currentContext();
×
1573

1574
        // graphics hardware without FrameBufferObj extension cannot use the cubemap rendering and shadow mapping.
1575
        // In this case, set cubemapSize to 0 to signal auto-switch to perspective projection.
1576
        // OpenGL ES2 has framebuffers in the Spec
1577
        if ( !ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers) )
×
1578
        {
1579
                //TODO FS: it seems like the current stellarium requires a working framebuffer extension anyway, so skip this check?
1580
                qCWarning(s3drenderer) << "Your hardware does not support EXT_framebuffer_object.";
×
1581
                qCWarning(s3drenderer) << "Shadow mapping disabled, and display limited to perspective projection.";
×
1582

1583
                setCubemapSize(0);
×
1584
                setShadowmapSize(0);
×
1585
        }
1586
        else
1587
        {
1588
                //determine maximum framebuffer size as minimum of texture, viewport and renderbuffer size
1589
                GLint texSize,viewportSize[2],rbSize;
1590
                glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
×
1591
                glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewportSize);
×
1592
                glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &rbSize);
×
1593

1594
                qCDebug(s3drenderer)<<"Maximum texture size:"<<texSize;
×
1595
                qCDebug(s3drenderer)<<"Maximum viewport dims:"<<viewportSize[0]<<viewportSize[1];
×
1596
                qCDebug(s3drenderer)<<"Maximum renderbuffer size:"<<rbSize;
×
1597

1598
                maximumFramebufferSize = qMin(texSize,qMin(rbSize,qMin(viewportSize[0],viewportSize[1])));
×
1599
                qCDebug(s3drenderer)<<"Maximum framebuffer size:"<<maximumFramebufferSize;
×
1600
        }
1601

1602
        const QString renderer(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
×
1603
        isANGLE = renderer.contains("ANGLE");
×
1604

1605
        //check if GS cubemapping is possible
1606
        if(QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Geometry,ctx)) //this checks if version >= 3.2
×
1607
        {
1608
                this->supportsGSCubemapping = true;
×
1609
                qCDebug(s3drenderer)<<"Geometry shader supported";
×
1610
        }
1611
        else
1612
                qCWarning(s3drenderer)<<"Geometry shader not supported on this hardware";
×
1613

1614
        //Query how many texture units we have at disposal in a fragment shader
1615
        //we currently need 8 in the worst case: diffuse, emissive, bump, height + 4x shadowmap
1616
        GLint texUnits,combUnits;
1617
        glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &texUnits);
×
1618
        qCDebug(s3drenderer) << "GL_MAX_TEXTURE_IMAGE_UNITS:" << texUnits;
×
1619
        glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &combUnits);
×
1620
        qCDebug(s3drenderer) << "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:" << combUnits;
×
1621
        if(texUnits < 8 || combUnits < 8)
×
1622
        {
1623
                qCWarning(s3drenderer)<<"Insufficient texture units available for all effects, should have at least 8!";
×
1624
        }
1625

1626
        if(shaderParameters.openglES)
×
1627
        {
1628
                //shadows in our implementation require depth textures
1629
                if(ctx->hasExtension("GL_OES_depth_texture") ||
×
1630
                        ctx->hasExtension("GL_ANGLE_depth_texture"))
×
1631
                {
1632
                        supportsShadows = true;
×
1633
                        qCDebug(s3drenderer)<<"Shadows are supported";
×
1634
                }
1635
                else
1636
                {
1637
                        supportsShadows = false;
×
1638
                        qCDebug(s3drenderer)<<"Shadows are not supported on this hardware";
×
1639
                }
1640
                //shadow filtering is completely disabled for now on ES
1641
                supportsShadowFiltering = false;
×
1642
        }
1643
        else
1644
        {
1645
                //assume everything is available on Desktop GL for now (should be ok on GL>2.0 as Stellarium base requires)
1646
                supportsShadows = true;
×
1647
                supportsShadowFiltering = true;
×
1648
        }
1649
}
×
1650

1651
void S3DRenderer::init()
×
1652
{
1653
        initializeOpenGLFunctions();        
×
1654
        QOpenGLContext* ctx = QOpenGLContext::currentContext();
×
1655
        Q_ASSERT(ctx);
×
1656

1657
#if !QT_CONFIG(opengles2)
1658
        //initialize additional functions needed and not provided through StelOpenGL
1659
        glExtFuncs = new GLExtFuncs();
×
1660
        glExtFuncs->init(ctx);
×
1661
#endif
1662

1663
        vao.create();
×
1664

1665
        //save opengl ES state
1666
        shaderParameters.openglES = ctx->isOpenGLES();
×
1667

1668
        //find out what features we can enable
1669
        determineFeatureSupport();
×
1670

1671
        cubeVertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
×
1672
        cubeVertexBuffer.create();
×
1673
        transformedCubeVertexBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
×
1674
        transformedCubeVertexBuffer.create();
×
1675
        cubeIndexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
×
1676
        cubeIndexBuffer.create();
×
1677

1678
        //enable seamless cubemapping if HW supports it
1679
        if(ctx->hasExtension("GL_ARB_seamless_cube_map"))
×
1680
        {
1681
#ifdef GL_TEXTURE_CUBE_MAP_SEAMLESS
1682
                glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
×
1683
                qCDebug(s3drenderer)<<"Seamless cubemap filtering enabled";
×
1684
#endif
1685
        }
1686

1687

1688
        //shadow map init happens on first usage of shadows
1689

1690
        //finally, set core to enable update().
1691
        this->core=StelApp::getInstance().getCore();
×
1692
        //init planets
1693
        SolarSystem* ssystem = GETSTELMODULE(SolarSystem);
×
1694
        sun = ssystem->getSun();
×
1695
        moon = ssystem->getMoon();
×
1696
        venus = ssystem->searchByEnglishName("Venus");
×
1697
        landscapeMgr = GETSTELMODULE(LandscapeMgr);
×
1698
        Q_ASSERT(landscapeMgr);
×
1699
}
×
1700

1701
void S3DRenderer::deleteCubemapping()
×
1702
{
1703
        if(cubeMappingCreated)
×
1704
        {
1705
                //delete cube map - we have to check each possible variable because we dont know which ones are active
1706
                //delete in reverse, FBOs first - but it should not matter
1707
                if(cubeFBO)
×
1708
                {
1709
                        glDeleteFramebuffers(1,&cubeFBO);
×
1710
                        cubeFBO = 0;
×
1711
                }
1712

1713
                if(cubeSideFBO[0])
×
1714
                {
1715
                        //we assume if one is created, all have been created
1716
                        glDeleteFramebuffers(6,cubeSideFBO);
×
1717
                        std::fill(cubeSideFBO,cubeSideFBO + 6,0);
×
1718
                }
1719

1720
                //delete depth
1721
                if(cubeRB)
×
1722
                {
1723
                        glDeleteRenderbuffers(1,&cubeRB);
×
1724
                        cubeRB = 0;
×
1725
                }
1726

1727
                if(cubeMapCubeDepth)
×
1728
                {
1729
                        glDeleteTextures(1,&cubeMapCubeDepth);
×
1730
                        cubeMapCubeDepth = 0;
×
1731
                }
1732

1733
                //delete colors
1734
                if(cubeMapTex[0])
×
1735
                {
1736
                        glDeleteTextures(6,cubeMapTex);
×
1737
                        std::fill(cubeMapTex, cubeMapTex + 6,0);
×
1738
                }
1739

1740
                if(cubeMapCubeTex)
×
1741
                {
1742
                        glDeleteTextures(1,&cubeMapCubeTex);
×
1743
                        cubeMapCubeTex = 0;
×
1744
                }
1745

1746
                cubeMappingCreated = false;
×
1747
        }
1748
}
×
1749

1750
bool S3DRenderer::initCubemapping()
×
1751
{
1752
        GET_GLERROR()
×
1753

1754
        bool ret = false;
×
1755
        qCDebug(s3drenderer)<<"Initializing cubemap...";
×
1756

1757
        //remove old cubemap objects if they exist
1758
        deleteCubemapping();
×
1759

1760
        GET_GLERROR()
×
1761

1762
        if(cubemapSize<=0)
×
1763
        {
1764
                qCWarning(s3drenderer)<<"Cubemapping not supported or disabled";
×
1765
                rendererMessage(q_("Your hardware does not support cubemapping, please switch to 'Perspective' projection!"));
×
1766
                return false;
×
1767
        }
1768

1769
        cubeMappingCreated = true;
×
1770

1771
        //last compatibility check before possible crash
1772
        if( !isGeometryShaderCubemapSupported() && cubemappingMode == S3DEnum::CM_CUBEMAP_GSACCEL)
×
1773
        {
1774
                rendererMessage(q_("Geometry shader is not supported. Falling back to '6 Textures' mode."));
×
1775
                qCWarning(s3drenderer)<<"GS not supported, fallback to '6 Textures'";
×
1776
                cubemappingMode = S3DEnum::CM_TEXTURES;
×
1777
        }
1778

1779
        //TODO the ANGLE version included with Qt 5.4 includes a bug that prevents Cubemapping to be used
1780
        //Remove this if this is ever fixed
1781
        if(isANGLEContext() && cubemappingMode >= S3DEnum::CM_CUBEMAP)
×
1782
        {
1783
                //Fall back to "6 Textures" mode
1784
                rendererMessage(q_("Falling back to '6 Textures' because of ANGLE bug"));
×
1785
                qCWarning(s3drenderer)<<"On ANGLE, fallback to '6 Textures'";
×
1786
                cubemappingMode = S3DEnum::CM_TEXTURES;
×
1787
        }
1788

1789

1790
#if !QT_CONFIG(opengles2)
1791
        //if we are on an ES context, it may not be possible to specify texture bitdepth
1792
        bool isEs = QOpenGLContext::currentContext()->isOpenGLES();
×
1793
        GLint colorFormat = isEs ? GL_RGBA : GL_RGBA8;
×
1794
        GLint depthFormat = isEs ? GL_DEPTH_COMPONENT : GL_DEPTH_COMPONENT24;
×
1795
        GLenum rbDepth = isEs ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT24;
×
1796
#else
1797
        GLint colorFormat = GL_RGBA;
1798
        GLint depthFormat = GL_DEPTH_COMPONENT;
1799
        GLenum rbDepth = GL_DEPTH_COMPONENT16;
1800
#endif
1801

1802
        glActiveTexture(GL_TEXTURE0);
×
1803

1804
        if(cubemappingMode >= S3DEnum::CM_CUBEMAP) //CUBEMAP or CUBEMAP_GSACCEL
×
1805
        {
1806
                //gen cube tex
1807
                glGenTextures(1,&cubeMapCubeTex);
×
1808
                glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapCubeTex);
×
1809

1810
                GET_GLERROR()
×
1811

1812
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
×
1813
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
×
1814
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
×
1815
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
×
1816

1817
                GET_GLERROR()
×
1818

1819
                //create faces
1820
                for (uint i=0;i<6;++i)
×
1821
                {
1822
                        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,0,colorFormat,
×
1823
                                     cubemapSize,cubemapSize,0,GL_RGBA,GL_UNSIGNED_BYTE,Q_NULLPTR);
1824
                        GET_GLERROR()
×
1825
                }
1826
                glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
×
1827
        }
1828
        else //TEXTURES mode
1829
        {
1830
                //create 6 textures
1831
                glGenTextures(6,cubeMapTex);
×
1832

1833
                GET_GLERROR()
×
1834

1835
                for(int i = 0;i<6;++i)
×
1836
                {
1837
                        glBindTexture(GL_TEXTURE_2D, cubeMapTex[i]);
×
1838
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
×
1839
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
×
1840
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
×
1841
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
×
1842

1843
                        GET_GLERROR()
×
1844

1845
                        glTexImage2D(GL_TEXTURE_2D,0,colorFormat,
×
1846
                                     cubemapSize,cubemapSize,0,GL_RGBA,GL_UNSIGNED_BYTE,Q_NULLPTR);
1847

1848
                        GET_GLERROR()
×
1849
                }
1850
                glBindTexture(GL_TEXTURE_2D, 0);
×
1851
        }
1852

1853
        //create depth texture/RB
1854
        if(cubemappingMode == S3DEnum::CM_CUBEMAP_GSACCEL)
×
1855
        {
1856
                //a single cubemap depth texture
1857
                glGenTextures(1,&cubeMapCubeDepth);
×
1858
                glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapCubeDepth);
×
1859
                //this all has probably not much effect on depth processing because we don't intend to sample
1860
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
×
1861
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
×
1862
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
×
1863
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
×
1864

1865
                GET_GLERROR()
×
1866

1867
                //create faces
1868
                for (uint i=0;i<6;++i)
×
1869
                {
1870
                        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,0,depthFormat,
×
1871
                                     cubemapSize,cubemapSize,0,GL_DEPTH_COMPONENT,GL_UNSIGNED_BYTE,Q_NULLPTR);
1872

1873
                        GET_GLERROR()
×
1874
                }
1875

1876
                glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
×
1877
        }
1878
        else
1879
        {
1880
                //gen renderbuffer for single-face depth, reused for all faces to save some memory
1881
                int val = 0;
×
1882
                glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE,&val);
×
1883
                qCDebug(s3drenderer)<<"Max Renderbuffer size"<<val;
×
1884

1885
                glGenRenderbuffers(1,&cubeRB);
×
1886
                glBindRenderbuffer(GL_RENDERBUFFER,cubeRB);
×
1887
                glRenderbufferStorage(GL_RENDERBUFFER, rbDepth, cubemapSize,cubemapSize);
×
1888
                GLenum err=glGetError();
×
1889

1890
                switch(err){
×
1891
                        case GL_NO_ERROR:
×
1892
                                        break;
×
1893
                        case GL_INVALID_ENUM:
×
1894
                                        qCWarning(s3drenderer)<<"RB: invalid depth format?";
×
1895
                                        break;
×
1896
                        case GL_INVALID_VALUE:
×
1897
                                        qCWarning(s3drenderer)<<"RB: invalid renderbuffer size";
×
1898
                                        break;
×
1899
                        case GL_OUT_OF_MEMORY:
×
1900
                                        qCWarning(s3drenderer)<<"RB: out of memory. Cannot create renderbuffer.";
×
1901
                                        break;
×
1902
                                default:
×
1903
                                qCWarning(s3drenderer)<<"RB: unexpected OpenGL error:" << err;
×
1904
                }
1905

1906
                glBindRenderbuffer(GL_RENDERBUFFER, 0);
×
1907
        }
1908

1909
        //generate FBO/FBOs
1910
        if(cubemappingMode == S3DEnum::CM_CUBEMAP_GSACCEL)
×
1911
        {
1912
                //only 1 FBO used
1913
                //create fbo
1914
                glGenFramebuffers(1,&cubeFBO);
×
1915
                glBindFramebuffer(GL_FRAMEBUFFER,cubeFBO);
×
1916

1917
                GET_GLERROR()
×
1918

1919
#if !QT_CONFIG(opengles2)
1920
                //attach cube tex + cube depth
1921
                //note that this function will be a NULL pointer if GS is not supported, so it is important to check support before using
1922
                glExtFuncs->glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,cubeMapCubeTex,0);
×
1923
                glExtFuncs->glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cubeMapCubeDepth, 0);
×
1924
#endif
1925

1926
                GET_GLERROR()
×
1927

1928
                //check validity
1929
                if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
×
1930
                {
1931
                        qCWarning(s3drenderer) << "glCheckFramebufferStatus failed, probably can't use cube map";
×
1932
                }
1933
                else
1934
                        ret = true;
×
1935
        }
1936
        else
1937
        {
1938
                //6 FBOs used
1939
                glGenFramebuffers(6,cubeSideFBO);
×
1940

1941
                GET_GLERROR()
×
1942

1943
                for(uint i=0;i<6;++i)
×
1944
                {
1945
                        glBindFramebuffer(GL_FRAMEBUFFER, cubeSideFBO[i]);
×
1946

1947
                        //attach color - 1 side of cubemap or single texture
1948
                        if(cubemappingMode == S3DEnum::CM_CUBEMAP)
×
1949
                                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,cubeMapCubeTex,0);
×
1950
                        else
1951
                                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, cubeMapTex[i],0);
×
1952

1953
                        GET_GLERROR()
×
1954

1955
                        //attach shared depth buffer
1956
                        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER, cubeRB);
×
1957

1958
                        GET_GLERROR()
×
1959

1960
                        if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
×
1961
                        {
1962
                                qCWarning(s3drenderer) << "glCheckFramebufferStatus failed, probably can't use cube map";
×
1963
                                ret = false;
×
1964
                                break;
×
1965
                        }
1966
                        else
1967
                                ret = true;
×
1968
                }
1969
        }
1970

1971
        //unbind last framebuffer
1972
        glBindFramebuffer(GL_FRAMEBUFFER,defaultFBO);
×
1973

1974
        //initialize cube rotations... found by trial and error :)
1975
        QMatrix4x4 stackBase;
×
1976

1977
        //all angles were found using some experimenting :)
1978
        //this is the EAST face (y=1)
1979
        stackBase.rotate(90.0f,-1.0f,0.0f,0.0f);
×
1980

1981
        if(cubemappingMode >= S3DEnum::CM_CUBEMAP)
×
1982
        {
1983
                //cubemap mode needs other rotations than texture mode
1984

1985
                //south (x=1) ok
1986
                cubeRotation[0] = stackBase;
×
1987
                cubeRotation[0].rotate(-90.0f,0.0f,1.0f,0.0f);
×
1988
                cubeRotation[0].rotate(90.0f,0.0f,0.0f,1.0f);
×
1989
                //NORTH (x=-1) ok
1990
                cubeRotation[1] = stackBase;
×
1991
                cubeRotation[1].rotate(90.0f,0.0f,1.0f,0.0f);
×
1992
                cubeRotation[1].rotate(-90.0f,0.0f,0.0f,1.0f);
×
1993
                //EAST (y=1) ok
1994
                cubeRotation[2] = stackBase;
×
1995
                //west (y=-1) ok
1996
                cubeRotation[3] = stackBase;
×
1997
                cubeRotation[3].rotate(180.0f,-1.0f,0.0f,0.0f);
×
1998
                //top (z=1) ok
1999
                cubeRotation[4] = stackBase;
×
2000
                cubeRotation[4].rotate(-90.0f,1.0f,0.0f,0.0f);
×
2001
                //bottom (z=-1)
2002
                cubeRotation[5] = stackBase;
×
2003
                cubeRotation[5].rotate(90.0f,1.0f,0.0f,0.0f);
×
2004
                cubeRotation[5].rotate(180.0f,0.0f,0.0f,1.0f);
×
2005
        }
2006
        else
2007
        {
2008
                cubeRotation[0] = stackBase;
×
2009
                cubeRotation[0].rotate(90.0f,0.0f,0.0f,1.0f);
×
2010

2011
                cubeRotation[1] = stackBase;
×
2012
                cubeRotation[1].rotate(90.0f,0.0f,0.0f,-1.0f);
×
2013

2014
                cubeRotation[2] = stackBase;
×
2015

2016
                cubeRotation[3] = stackBase;
×
2017
                cubeRotation[3].rotate(180.0f,0.0f,0.0f,1.0f);
×
2018

2019
                cubeRotation[4] = stackBase;
×
2020
                cubeRotation[4].rotate(90.0f,-1.0f,0.0f,0.0f);
×
2021

2022
                cubeRotation[5] = stackBase;
×
2023
                cubeRotation[5].rotate(90.0f,1.0f,0.0f,0.0f);
×
2024
        }
2025

2026

2027
        //create a 20x20 cube subdivision to give a good approximation of non-linear projections
2028
        const int sub = 20;
×
2029
        const int vtxCount = (sub+1) * (sub+1);
×
2030
        const float d_sub_v = 2.0f / sub;
×
2031
        const float d_sub_tex = 1.0f / sub;
×
2032

2033
        //create the front cubemap face vertices
2034
        QVector<Vec3f> cubePlaneFront;
×
2035
        QVector<Vec2f> cubePlaneFrontTex;
×
2036
        QVector<unsigned short> frontIndices;
×
2037
        cubePlaneFront.reserve(vtxCount);
×
2038
        cubePlaneFrontTex.reserve(vtxCount);
×
2039

2040
        //store the indices of the vertices
2041
        //this could easily be recalculated as needed but this makes it a bit more readable
2042
        unsigned short vertexIdx[sub+1][sub+1];
2043

2044
        //first, create the actual vertex positions, (20+1)^2 vertices
2045
        for (int y = 0; y <= sub; y++) {
×
2046
                for (int x = 0; x <= sub; x++) {
×
2047
                        float xp = -1.0f + x * d_sub_v;
×
2048
                        float yp = -1.0f + y * d_sub_v;
×
2049

2050
                        float tx = x * d_sub_tex;
×
2051
                        float ty = y * d_sub_tex;
×
2052

2053
                        cubePlaneFront<< Vec3f(xp, 1.0f, yp);
×
2054
                        cubePlaneFrontTex<<Vec2f(tx,ty);
×
2055

2056
                        vertexIdx[y][x] = static_cast<unsigned short>(y*(sub+1)+x);
×
2057
                }
2058
        }
2059

2060
        Q_ASSERT(cubePlaneFrontTex.size() == vtxCount);
×
2061
        Q_ASSERT(cubePlaneFront.size() == vtxCount);
×
2062

2063
        //generate indices for each of the 20x20 subfaces
2064
        //TODO optimize for TRIANGLE_STRIP?
2065
        for ( int y = 0; y < sub; y++)
×
2066
        {
2067
                for( int x = 0; x<sub; x++)
×
2068
                {
2069
                        //first tri (top one)
2070
                        frontIndices<<vertexIdx[y+1][x];
×
2071
                        frontIndices<<vertexIdx[y][x];
×
2072
                        frontIndices<<vertexIdx[y+1][x+1];
×
2073

2074
                        //second tri
2075
                        frontIndices<<vertexIdx[y+1][x+1];
×
2076
                        frontIndices<<vertexIdx[y][x];
×
2077
                        frontIndices<<vertexIdx[y][x+1];
×
2078
                }
2079
        }
2080

2081
        int idxCount = frontIndices.size();
×
2082

2083
        //create the other faces
2084
        //note that edge vertices of the faces are duplicated
2085

2086
        cubeVertices.clear();
×
2087
        cubeVertices.reserve(vtxCount * 6);
×
2088
        cubeTexcoords.clear();
×
2089
        cubeTexcoords.reserve(vtxCount * 6);
×
2090
        QVector<unsigned short> cubeIndices; //index data is not needed afterwards on CPU side, so use a local vector
×
2091
        cubeIndices.reserve(idxCount * 6);
×
2092
        //init with copies of front face
2093
        for(int i = 0;i<6;++i)
×
2094
        {
2095
                //order of geometry should be as follows:
2096
                //basically "reversed" cubemap order
2097
                //S face x=1
2098
                //N face x=-1
2099
                //E face y=1
2100
                //W face y=-1
2101
                //up face z=1
2102
                //down face z=-1
2103
                cubeVertices<<cubePlaneFront;
×
2104
                cubeTexcoords<<cubePlaneFrontTex;
×
2105
                cubeIndices<<frontIndices;
×
2106
        }
2107

2108
        Q_ASSERT(cubeVertices.size() == cubeTexcoords.size());
×
2109

2110
        transformedCubeVertices.resize(cubeVertices.size());
×
2111
        cubeIndexCount = cubeIndices.size();
×
2112

2113
        qCDebug(s3drenderer)<<"Using cube with"<<cubeVertices.size()<<"vertices and" <<cubeIndexCount<<"indices";
×
2114

2115
        //create the other cube faces by rotating the front face
2116
#define PLANE(_PLANEID_, _MAT_) for(int i=_PLANEID_ * vtxCount;i < (_PLANEID_ + 1)*vtxCount;i++){ _MAT_.transfo(cubeVertices[i]); }\
2117
        for(int i =_PLANEID_ * idxCount; i < (_PLANEID_+1)*idxCount;++i) { cubeIndices[i] = cubeIndices[i] + _PLANEID_ * vtxCount; }
2118

2119
        PLANE(0, Mat4f::zrotation(-M_PI_2f)) //S
×
2120
        PLANE(1, Mat4f::zrotation(M_PI_2f))  //N
×
2121
        PLANE(2, Mat4f::identity())  //E
×
2122
        PLANE(3, Mat4f::zrotation(M_PIf)) //W
×
2123
        PLANE(4, Mat4f::xrotation(M_PI_2f)) //U
×
2124
        PLANE(5, Mat4f::xrotation(-M_PI_2f)) //D
×
2125
#undef PLANE
2126

2127
        //upload original cube vertices + indices to GL
2128
        cubeVertexBuffer.bind();
×
2129
        //store original vertex pos (=3D vertex coords) + 2D tex coords in same buffer
2130
        cubeVertexBuffer.allocate(cubeVertices.size() * static_cast<int>((sizeof(Vec3f) + sizeof(Vec2f))) );
×
2131
        cubeVertexBuffer.write(0, cubeVertices.constData(), cubeVertices.size() * static_cast<int>(sizeof(Vec3f)));
×
2132
        cubeVertexBuffer.write(cubeVertices.size() * static_cast<int>(sizeof(Vec3f)), cubeTexcoords.constData(), cubeTexcoords.size() * static_cast<int>(sizeof(Vec2f)));
×
2133
        cubeVertexBuffer.release();
×
2134

2135
        cubeIndexBuffer.bind();
×
2136
        cubeIndexBuffer.allocate(cubeIndices.constData(),cubeIndices.size() * static_cast<int>(sizeof(unsigned short)));
×
2137
        cubeIndexBuffer.release();
×
2138

2139
        //reset cubemap timer to make sure it is rerendered immediately after re-init
2140
        invalidateCubemap();
×
2141

2142
        qCDebug(s3drenderer)<<"Initializing cubemap...done!";
×
2143

2144
        if(!ret)
×
2145
        {
2146
                rendererMessage("Cannot use cubemapping with current settings");
×
2147
                deleteCubemapping();
×
2148
        }
2149
        return ret;
×
2150
}
2151

2152
void S3DRenderer::deleteShadowmapping()
×
2153
{
2154
        if(shadowFBOs.size()>0) //kinda hack that finds out if shadowmap related objects have been created
×
2155
        {
2156
                //we can delete them all at once then
2157
                glDeleteFramebuffers(shadowFBOs.size(),shadowFBOs.constData());
×
2158
                glDeleteTextures(shadowMapsArray.size(),shadowMapsArray.constData());
×
2159

2160
                shadowFBOs.clear();
×
2161
                shadowMapsArray.clear();
×
2162
                shadowCPM.clear();
×
2163
                shadowFrustumSize.clear();
×
2164
                frustumArray.clear();
×
2165
                focusBodies.clear();
×
2166

2167
                qCDebug(s3drenderer)<<"Shadowmapping objects cleaned up";
×
2168
        }
2169
}
×
2170

2171
bool S3DRenderer::initShadowmapping()
×
2172
{
2173
        deleteShadowmapping();
×
2174

2175
        bool valid = false;
×
2176

2177
        if(simpleShadows)
×
2178
        {
2179
                shaderParameters.frustumSplits = 1;
×
2180
        }
2181
        else
2182
        {
2183
                //TODO support changing this option by the user and/or the scene?
2184
                shaderParameters.frustumSplits = 4;
×
2185
        }
2186

2187
        if(!areShadowsSupported())
×
2188
        {
2189
                qCWarning(s3drenderer)<<"Tried to initialize shadows without shadow support!";
×
2190
                return false;
×
2191
        }
2192

2193
        if(shadowmapSize>0)
×
2194
        {
2195
                //Define shadow maps array - holds MAXSPLITS textures
2196
                shadowFBOs.resize(shaderParameters.frustumSplits);
×
2197
                shadowMapsArray.resize(shaderParameters.frustumSplits);
×
2198
                shadowCPM.resize(shaderParameters.frustumSplits);
×
2199
                shadowFrustumSize.resize(shaderParameters.frustumSplits);
×
2200
                frustumArray.resize(shaderParameters.frustumSplits);
×
2201
                focusBodies.resize(shaderParameters.frustumSplits);
×
2202

2203
                //For shadowmapping, we use create 1 SM FBO for each frustum split - this seems to be the optimal solution on modern GPUs,
2204
                //see http://www.reddit.com/r/opengl/comments/1rsnhy/most_efficient_fbo_usage_in_multipass_pipeline/
2205
                //The point seems to be that switching attachments may cause re-validation of the FB.
2206

2207
                //Generate the FBO ourselves. We do this because Qt does not support depth-only FBOs to save some memory.
2208
                glGenFramebuffers(shaderParameters.frustumSplits,shadowFBOs.data());
×
2209
                glGenTextures(shaderParameters.frustumSplits,shadowMapsArray.data());
×
2210

2211
                for(int i=0; i<shaderParameters.frustumSplits; i++)
×
2212
                {
2213
                        //Bind the FBO
2214
                        glBindFramebuffer(GL_FRAMEBUFFER, shadowFBOs.at(i));
×
2215

2216
                        //Activate the texture unit - we want sahdows + textures so this is crucial with the current Stellarium pipeline - we start at unit 4
2217
                        glActiveTexture(GL_TEXTURE4+static_cast<uint>(i));
×
2218

2219
                        //Bind the depth map and setup parameters
2220
                        glBindTexture(GL_TEXTURE_2D, shadowMapsArray.at(i));
×
2221

2222
#if !QT_CONFIG(opengles2)
2223
                        bool isEs = QOpenGLContext::currentContext()->isOpenGLES();
×
2224
                        GLenum depthPcss = isEs ? GL_DEPTH_COMPONENT : GL_DEPTH_COMPONENT32F;
×
2225
                        GLenum depthNormal = isEs ? GL_DEPTH_COMPONENT : GL_DEPTH_COMPONENT16;
×
2226
#else
2227
                        GLenum depthPcss = GL_DEPTH_COMPONENT;
2228
                        GLenum depthNormal = GL_DEPTH_COMPONENT;
2229
#endif
2230
                        //pcss is only enabled if filtering is also enabled
2231
                        bool pcssEnabled = shaderParameters.pcss && (shaderParameters.shadowFilterQuality == S3DEnum::SFQ_LOW || shaderParameters.shadowFilterQuality == S3DEnum::SFQ_HIGH);
×
2232

2233
                        //for OpenGL ES2, type has to be UNSIGNED_SHORT or UNSIGNED_INT for depth textures, desktop does probably not care
2234
                        glTexImage2D(GL_TEXTURE_2D, 0, static_cast<GLint>(pcssEnabled ? depthPcss : depthNormal), shadowmapSize, shadowmapSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, Q_NULLPTR);
×
2235

2236
                        //we use hardware-accelerated depth compare mode, unless pcss is used
2237
                        shaderParameters.hwShadowSamplers = false;
×
2238
                        //NOTE: can't use depth compare mode on ES2
2239
                        if(!pcssEnabled)
×
2240
                        {
2241
#if !QT_CONFIG(opengles2)
2242
                                if(!isEs)
×
2243
                                {
2244
                                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
×
2245
                                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
×
2246
                                        shaderParameters.hwShadowSamplers = true;
×
2247
                                }
2248
#endif
2249
                        }
2250

2251
                        //IF we support hw shadow sampling, then we may enable linear filtering, otherwise filtering depth values directly would not make much sense
2252
                        GLint filter = shaderParameters.hwShadowSamplers && (shaderParameters.shadowFilterQuality == S3DEnum::SFQ_HARDWARE
×
2253
                                        || shaderParameters.shadowFilterQuality == S3DEnum::SFQ_LOW_HARDWARE
×
2254
                                        || shaderParameters.shadowFilterQuality == S3DEnum::SFQ_HIGH_HARDWARE) ? GL_LINEAR : GL_NEAREST;
×
2255
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
×
2256
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
×
2257
#if !QT_CONFIG(opengles2)
2258
                        if(!isEs)
×
2259
                        {
2260
                                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL,0);
×
2261
                                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL,0);
×
2262
                                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
×
2263
                                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
×
2264
                                const float ones[] = {1.0f, 1.0f, 1.0f, 1.0f};
×
2265
                                glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, ones);
×
2266
                        }
2267
#endif
2268

2269
                        //Attach the depthmap to the Buffer
2270
                        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowMapsArray[i], 0);
×
2271

2272
                        //NOTE: disabling the drawbuffer should be required
2273
                        //but the respective functions are not available on GLES2?
2274
                        //On ANGLE, it seems to work without this settings (framebuffer is complete, etc.)
2275
                        //but I don't know if it will work on other ES platforms?
2276
#if !QT_CONFIG(opengles2)
2277
                        if(!isEs)
×
2278
                        {
2279
                                glExtFuncs->glDrawBuffer(GL_NONE); // essential for depth-only FBOs!!!
×
2280
                                glExtFuncs->glReadBuffer(GL_NONE);
×
2281
                        }
2282
#endif
2283

2284
                        if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
×
2285
                        {
2286
                                qCWarning(s3drenderer) << "glCheckFramebufferStatus failed, can't use FBO";
×
2287
                                break;
×
2288
                        }
2289
                        else if (i==shaderParameters.frustumSplits-1)
×
2290
                        {
2291
                                valid = true;
×
2292
                        }
2293
                }
2294

2295
                //Done. Unbind and switch to normal texture unit 0
2296
                glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
×
2297
                glActiveTexture(GL_TEXTURE0);
×
2298

2299
                qCDebug(s3drenderer)<<"shadowmapping initialized";
×
2300
        }
2301
        else
2302
        {
2303
                qCWarning(s3drenderer)<<"shadowmapping not supported or disabled";
×
2304
        }
2305

2306
        if(!valid)
×
2307
        {
2308
                deleteShadowmapping();
×
2309
                rendererMessage(q_("Shadow mapping can not be used on your hardware, check logs for details"));
×
2310
        }
2311
        return valid;
×
2312
}
2313

2314
void S3DRenderer::draw(StelCore* core, S3DScene &scene)
×
2315
{
2316
        if(!scene.isGLReady())
×
2317
        {
2318
                scene.glLoad();
×
2319
                invalidateCubemap();
×
2320
        }
2321

2322
        //find out the default FBO
2323
        defaultFBO = StelApp::getInstance().getDefaultFBO();
×
2324
        currentScene = &scene;
×
2325

2326
        //reset render statistic
2327
        drawnTriangles = drawnModels = materialSwitches = shaderSwitches = 0;
×
2328

2329
        requiresCubemap = core->getCurrentProjectionType() != StelCore::ProjectionPerspective;
×
2330
        //update projector from core
2331
        altAzProjector = core->getProjection(StelCore::FrameAltAz, StelCore::RefractionOff);
×
2332

2333
        if(requiresCubemap)
×
2334
        {
2335
                if(!cubeMappingCreated || reinitCubemapping)
×
2336
                {
2337
                        //init cubemaps
2338
                        if(!initCubemapping())
×
2339
                                return;
×
2340
                        reinitCubemapping = false;
×
2341
                }
2342

2343
                if(lazyDrawing)
×
2344
                {
2345
                        //if the viewer was moved after the last draw call
2346
                        bool wasMoved = lastDrawnPosition != currentScene->getEyePosition();
×
2347

2348
                        //get current time
2349
                        double curTime = core->getJD();
×
2350
                        qint64 curMS = QDateTime::currentMSecsSinceEpoch();
×
2351

2352
                        needsMovementUpdate = false;
×
2353

2354
                        //check if cubemap requires redraw
2355
                        if(qAbs(curTime-lastCubemapUpdate) > lazyInterval * StelCore::JD_SECOND || reinitCubemapping)
×
2356
                        {
2357
                                needsCubemapUpdate = true;
×
2358
                                needsMovementEndUpdate = false;
×
2359
                        }
2360
                        else if (wasMoved) //we have been moved currently
×
2361
                        {
2362
                                if(updateOnlyDominantOnMoving)
×
2363
                                {
2364
                                        needsMovementUpdate = true;
×
2365
                                        needsMovementEndUpdate = true;
×
2366
                                        needsCubemapUpdate = false;
×
2367
                                }
2368
                                else
2369
                                {
2370
                                        needsCubemapUpdate = true;
×
2371
                                        needsMovementEndUpdate = false;
×
2372
                                }
2373
                        }
2374
                        else
2375
                        {
2376
                                if(wasMovedInLastDrawCall) // we have been moved in the last draw call, but no longer
×
2377
                                        lastMovementEndRealTime = curMS;
×
2378

2379
                                if(needsMovementEndUpdate && (curMS - lastMovementEndRealTime)  > 700)
×
2380
                                {
2381
                                        //if the last movement was some time ago, update the whole cubemap
2382
                                        needsCubemapUpdate = true;
×
2383
                                        needsMovementEndUpdate = false;
×
2384
                                }
2385
                                else
2386
                                        needsCubemapUpdate = false;
×
2387
                        }
2388

2389
                        wasMovedInLastDrawCall = wasMoved;
×
2390
                }
2391
                else
2392
                {
2393
                        needsCubemapUpdate = true;
×
2394
                }
2395

2396

2397
                const Vec3d& mainViewDir = currentScene->getViewDirection();
×
2398
                //find cubemap face this vector points at
2399
                //only consider horizontal plane (XY)
2400
                dominantFace = qAbs(mainViewDir.v[0])<qAbs(mainViewDir.v[1]);
×
2401
                secondDominantFace = !dominantFace;
×
2402

2403
                //uncomment this to also consider up/down faces
2404
                /*
2405
                double max = qAbs(viewDir.v[dominantFace]);
2406
                if(qAbs(viewDir.v[2])>max)
2407
                {
2408
                        secondDominantFace = dominantFace;
2409
                        dominantFace = 2;
2410
                }
2411
                else if (qAbs(viewDir.v[2])>qAbs(viewDir.v[secondDominantFace]))
2412
                {
2413
                        secondDominantFace = 2;
2414
                }
2415
                */
2416

2417
                //check sign
2418
                dominantFace = dominantFace*2 + (mainViewDir.v[dominantFace]<0.0);
×
2419
                secondDominantFace = secondDominantFace*2 + (mainViewDir.v[secondDominantFace]<0.0);
×
2420
        }
2421
        else
2422
        {
2423
                //remove cubemapping objects when switching to perspective proj to save GPU memory
2424
                deleteCubemapping();
×
2425
        }
2426

2427
        //turn off blending, because it seems to be enabled somewhere we do not have access
2428
        glDisable(GL_BLEND);
×
2429

2430
        if (shaderParameters.shadows)
×
2431
        {
2432
                //test if shadow mapping has been initialized,
2433
                //or needs to be re-initialized because of setting changes
2434
                if(reinitShadowmapping || shadowFBOs.size()==0 || (cubemappingUsedLastFrame != requiresCubemap))
×
2435
                {
2436
                        reinitShadowmapping = false;
×
2437
                        if(!initShadowmapping())
×
2438
                                return; //can't use shadowmaps
×
2439
                }
2440
        }
2441
        else
2442
        {
2443
                //remove the shadow mapping stuff if not in use, this is only done once
2444
                deleteShadowmapping();
×
2445
        }
2446

2447
        if (!requiresCubemap)
×
2448
        {
2449
                //when Stellarium uses perspective projection we can use the fast direct method
2450
                drawDirect();
×
2451
        }
2452
        else
2453
        {
2454
                //we have to use a workaround using cubemapping
2455
                drawWithCubeMap();
×
2456
        }
2457
        if (textEnabled) drawCoordinatesText();
×
2458
        if (debugEnabled)
×
2459
        {
2460
                drawDebug();
×
2461
        }
2462

2463
        lastDrawnPosition = currentScene->getEyePosition();
×
2464
        cubemappingUsedLastFrame = requiresCubemap;
×
2465
        currentScene = Q_NULLPTR;
×
2466
}
2467

2468
void S3DRenderer::rendererMessage(const QString &msg) const
×
2469
{
2470
        emit message(msg);
×
2471
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc