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

Stellarium / stellarium / 15291801018

28 May 2025 04:52AM UTC coverage: 11.931% (-0.02%) from 11.951%
15291801018

push

github

alex-w
Added new set of navigational stars (XIX century)

0 of 6 new or added lines in 2 files covered. (0.0%)

14124 existing lines in 74 files now uncovered.

14635 of 122664 relevant lines covered (11.93%)

18291.42 hits per line

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

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

20
#include "StelPainter.hpp"
21

22
#include "StelApp.hpp"
23
#include "StelMainView.hpp"
24
#include "StelLocaleMgr.hpp"
25
#include "StelProjector.hpp"
26
#include "StelProjectorClasses.hpp"
27
#include "StelUtils.hpp"
28
#include "SaturationShader.hpp"
29

30
#include <QDebug>
31
#include <QString>
32
#include <QSettings>
33
#include <QPainter>
34
#include <QMutex>
35
#include <QVarLengthArray>
36
#include <QPaintEngine>
37
#include <QCache>
38
#include <QOpenGLVertexArrayObject>
39
#include <QOpenGLPaintDevice>
40
#include <QOpenGLBuffer>
41
#include <QOpenGLShader>
42
#include <QOpenGLTexture>
43
#include <QApplication>
44

45
namespace
46
{
47
static const int TEX_CACHE_LIMIT = 7000000;
48

49
// These raw pointers are never deleted, because the only time their pointees
50
// need to be destroyed is when the whole program is exiting, and at this point
51
// we may not even have an OpenGL context anymore, so we'll never need to
52
// delete these objects manually.
53
QOpenGLVertexArrayObject* vao;
54
QOpenGLBuffer* verticesVBO;
55
QOpenGLBuffer* indicesVBO;
56
}
57

58
#ifndef NDEBUG
59
QMutex* StelPainter::globalMutex = new QMutex();
60
#endif
61

62
QCache<QByteArray, StringTexture> StelPainter::texCache(TEX_CACHE_LIMIT);
63
QOpenGLShaderProgram* StelPainter::texturesShaderProgram=Q_NULLPTR;
64
QOpenGLShaderProgram* StelPainter::textShaderProgram=Q_NULLPTR;
65
QOpenGLShaderProgram* StelPainter::basicShaderProgram=Q_NULLPTR;
66
QOpenGLShaderProgram* StelPainter::colorShaderProgram=Q_NULLPTR;
67
QOpenGLShaderProgram* StelPainter::texturesColorShaderProgram=Q_NULLPTR;
68
QOpenGLShaderProgram* StelPainter::wideLineShaderProgram=Q_NULLPTR;
69
QOpenGLShaderProgram* StelPainter::colorfulWideLineShaderProgram=Q_NULLPTR;
70
StelPainter::BasicShaderVars StelPainter::basicShaderVars;
71
StelPainter::TexturesShaderVars StelPainter::texturesShaderVars;
72
StelPainter::TextShaderVars StelPainter::textShaderVars;
73
StelPainter::BasicShaderVars StelPainter::colorShaderVars;
74
StelPainter::TexturesColorShaderVars StelPainter::texturesColorShaderVars;
75
StelPainter::WideLineShaderVars StelPainter::wideLineShaderVars;
76
StelPainter::ColorfulWideLineShaderVars StelPainter::colorfulWideLineShaderVars;
77
bool StelPainter::multisamplingEnabled=false;
78

UNCOV
79
StelPainter::GLState::GLState(QOpenGLFunctions* gl)
×
80
        : blend(false),
×
81
          blendSrc(GL_SRC_ALPHA), blendDst(GL_ONE_MINUS_SRC_ALPHA),
×
82
          depthTest(false),
×
83
          depthMask(false),
×
84
          cullFace(false),
×
85
          lineSmooth(false),
×
86
          lineWidth(1.0f),
×
87
          gl(gl)
×
88
{
UNCOV
89
}
×
90

UNCOV
91
void StelPainter::GLState::apply()
×
92
{
UNCOV
93
        if(blend)
×
94
                gl->glEnable(GL_BLEND);
×
95
        else
UNCOV
96
                gl->glDisable(GL_BLEND);
×
97
        gl->glBlendFunc(blendSrc,blendDst);
×
98
        if(depthTest)
×
99
                gl->glEnable(GL_DEPTH_TEST);
×
100
        else
UNCOV
101
                gl->glDisable(GL_DEPTH_TEST);
×
102
        gl->glDepthMask(depthMask);
×
103
        if(cullFace)
×
104
                gl->glEnable(GL_CULL_FACE);
×
105
        else
UNCOV
106
                gl->glDisable(GL_CULL_FACE);
×
107
        gl->glLineWidth(lineWidth);
×
108
#ifdef GL_LINE_SMOOTH
UNCOV
109
        if(!QOpenGLContext::currentContext()->isOpenGLES())
×
110
        {
UNCOV
111
                if (lineSmooth)
×
112
                        gl->glEnable(GL_LINE_SMOOTH);
×
113
                else
UNCOV
114
                        gl->glDisable(GL_LINE_SMOOTH);
×
115
        }
116
#endif
UNCOV
117
}
×
118

UNCOV
119
void StelPainter::GLState::reset()
×
120
{
UNCOV
121
        *this = GLState(gl);
×
122
        apply();
×
123
}
×
124

UNCOV
125
bool StelPainter::linkProg(QOpenGLShaderProgram* prog, const QString& name)
×
126
{
UNCOV
127
        bool ret = prog->link();
×
128
        QString log = prog->log();
×
129
        if (!ret || (!log.isEmpty() && !log.contains("Link was successful") && !(log=="No errors."))) //"No errors." returned on some Intel drivers
×
130
                qWarning().noquote() << QString("StelPainter: Warnings while linking %1 shader program:\n%2").arg(name, prog->log());
×
131
        return ret;
×
132
}
×
133

UNCOV
134
StelPainter::StelPainter(const StelProjectorP& proj)
×
135
        : QOpenGLFunctions(QOpenGLContext::currentContext())
UNCOV
136
        , glState(this)
×
137
{
UNCOV
138
        Q_ASSERT(proj);
×
139

140
#ifndef NDEBUG
UNCOV
141
        Q_ASSERT(globalMutex);
×
142
        
UNCOV
143
        GLenum er = glGetError();
×
144
        if (er!=GL_NO_ERROR)
×
145
        {
UNCOV
146
                if (er==GL_INVALID_OPERATION)
×
147
                        qFatal("Invalid openGL operation. It is likely that you used openGL calls without having a valid instance of StelPainter");
×
148
        }
149

150
        // Lock the global mutex ensuring that no other instances of StelPainter are currently being used
UNCOV
151
        if (globalMutex->tryLock()==false)
×
152
        {
UNCOV
153
                qFatal("There can be only 1 instance of StelPainter at a given time");
×
154
        }
155
#endif
156

157
        //TODO: is this still required, and is there some Qt way to fix it? 0x11111111 is a bit peculiar, how was it chosen?
158
        // Fix some problem when using Qt OpenGL2 engine
UNCOV
159
        glStencilMask(0x11111111);
×
160
        glState.apply(); //apply default OpenGL state
×
161
        setProjector(proj);
×
162

UNCOV
163
        if(!vao)
×
164
        {
165
                // First ever creation of StelPainter, initialize the objects
UNCOV
166
                vao = new QOpenGLVertexArrayObject;
×
167
                verticesVBO = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
×
168
                indicesVBO = new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
×
169

UNCOV
170
                vao->create();
×
171
                indicesVBO->create();
×
172
                indicesVBO->setUsagePattern(QOpenGLBuffer::StreamDraw);
×
173
                verticesVBO->create();
×
174
                verticesVBO->setUsagePattern(QOpenGLBuffer::StreamDraw);
×
175
        }
UNCOV
176
}
×
177

UNCOV
178
void StelPainter::setProjector(const StelProjectorP& p)
×
179
{
UNCOV
180
        prj=p;
×
181
        // Init GL viewport to current projector values
UNCOV
182
        glViewport(prj->viewportXywh[0], prj->viewportXywh[1], prj->viewportXywh[2], prj->viewportXywh[3]);
×
183
        glFrontFace(prj->needGlFrontFaceCW()?GL_CW:GL_CCW);
×
184
}
×
185

UNCOV
186
StelPainter::~StelPainter()
×
187
{
188
        //reset opengl state
UNCOV
189
        glState.reset();
×
190

191
#ifndef NDEBUG
UNCOV
192
        GLenum er = glGetError();
×
193
        if (er!=GL_NO_ERROR)
×
194
        {
UNCOV
195
                if (er==GL_INVALID_OPERATION)
×
196
                        qFatal("Invalid openGL operation detected in ~StelPainter()");
×
197
        }
198

199
        // We are done with this StelPainter
UNCOV
200
        globalMutex->unlock();
×
201
#endif
UNCOV
202
}
×
203

204

UNCOV
205
void StelPainter::setFont(const QFont& font)
×
206
{
UNCOV
207
        currentFont = font;
×
208
}
×
209

UNCOV
210
void StelPainter::setColor(float r, float g, float b, float a)
×
211
{
UNCOV
212
        currentColor.set(r,g,b,a);
×
213
}
×
214

UNCOV
215
void StelPainter::setColor(Vec3f rgb, float a)
×
216
{
UNCOV
217
        currentColor.set(rgb[0],rgb[1],rgb[2],a);
×
218
}
×
219

UNCOV
220
void StelPainter::setColor(Vec4f rgba)
×
221
{
UNCOV
222
        currentColor=rgba;
×
223
}
×
224

UNCOV
225
Vec4f StelPainter::getColor() const
×
226
{
UNCOV
227
        return currentColor;
×
228
}
229

UNCOV
230
QFontMetrics StelPainter::getFontMetrics() const
×
231
{
UNCOV
232
        return QFontMetrics(currentFont);
×
233
}
234

UNCOV
235
void StelPainter::setBlending(bool enableBlending, GLenum blendSrc, GLenum blendDst)
×
236
{
UNCOV
237
        if(enableBlending != glState.blend)
×
238
        {
UNCOV
239
                glState.blend = enableBlending;
×
240
                if(enableBlending)
×
241
                        glEnable(GL_BLEND);
×
242
                else
UNCOV
243
                        glDisable(GL_BLEND);
×
244
        }
UNCOV
245
        if(enableBlending)
×
246
        {
UNCOV
247
                if(blendSrc!=glState.blendSrc||blendDst!=glState.blendDst)
×
248
                {
UNCOV
249
                        glState.blendSrc = blendSrc;
×
250
                        glState.blendDst = blendDst;
×
251
                        glBlendFunc(blendSrc,blendDst);
×
252
                }
253
        }
UNCOV
254
}
×
255

UNCOV
256
bool StelPainter::getBlending(GLenum *src, GLenum *dst) const
×
257
{
UNCOV
258
        if (dst!=Q_NULLPTR)
×
259
                *dst=glState.blendDst;
×
260
        if (src!=Q_NULLPTR)
×
261
                *src=glState.blendSrc;
×
262
        return glState.blend;
×
263
}
264

UNCOV
265
void StelPainter::setDepthTest(bool enable)
×
266
{
UNCOV
267
        if(glState.depthTest != enable)
×
268
        {
UNCOV
269
                glState.depthTest = enable;
×
270
                if(enable)
×
271
                        glEnable(GL_DEPTH_TEST);
×
272
                else
UNCOV
273
                        glDisable(GL_DEPTH_TEST);
×
274
        }
UNCOV
275
}
×
276

UNCOV
277
void StelPainter::setDepthMask(bool enable)
×
278
{
UNCOV
279
        if(glState.depthMask != enable)
×
280
        {
UNCOV
281
                glState.depthMask = enable;
×
282
                if(enable)
×
283
                        glDepthMask(GL_TRUE);
×
284
                else
UNCOV
285
                        glDepthMask(GL_FALSE);
×
286
        }
UNCOV
287
}
×
288

UNCOV
289
void StelPainter::setCullFace(bool enable)
×
290
{
UNCOV
291
        if(glState.cullFace!=enable)
×
292
        {
UNCOV
293
                glState.cullFace = enable;
×
294
                if(enable)
×
295
                        glEnable(GL_CULL_FACE);
×
296
                else
UNCOV
297
                        glDisable(GL_CULL_FACE);
×
298
        }
UNCOV
299
}
×
300

UNCOV
301
void StelPainter::setLineSmooth(bool enable)
×
302
{
303
#ifdef GL_LINE_SMOOTH
UNCOV
304
        if (!QOpenGLContext::currentContext()->isOpenGLES() && enable!=glState.lineSmooth)
×
305
        {
UNCOV
306
                glState.lineSmooth = enable;
×
307
                if(enable)
×
308
                        glEnable(GL_LINE_SMOOTH);
×
309
                else
UNCOV
310
                        glDisable(GL_LINE_SMOOTH);
×
311
        }
312
#else
313
        Q_UNUSED(enable); //noop
314
#endif
UNCOV
315
}
×
316

UNCOV
317
void StelPainter::setLineWidth(float width)
×
318
{
UNCOV
319
        if(fabs(glState.lineWidth - width) < 1.e-10f)
×
320
                return;
×
321

UNCOV
322
        glState.lineWidth = width;
×
323

UNCOV
324
        if(width > 1 && StelMainView::getInstance().getGLInformation().isCoreProfile)
×
325
                return;
×
326

UNCOV
327
        glLineWidth(width);
×
328
}
329

330
///////////////////////////////////////////////////////////////////////////
331
// Standard methods for drawing primitives
332

333
// Fill with black around the circle
UNCOV
334
void StelPainter::drawViewportShape(void)
×
335
{
UNCOV
336
        if (prj->maskType != StelProjector::MaskDisk)
×
337
                return;
×
338

UNCOV
339
        bool oldBlendState = glState.blend;
×
340
        glDisable(GL_BLEND);
×
341
        setColor(0.f,0.f,0.f);
×
342

UNCOV
343
        GLfloat innerRadius = 0.5f*static_cast<float>(prj->viewportFovDiameter);
×
344
        GLfloat outerRadius = static_cast<float>(prj->getViewportWidth()+prj->getViewportHeight());
×
345
        GLint slices = 239;
×
346

347
        GLfloat sinCache[240];
348
        GLfloat cosCache[240];
349
        GLfloat vertices[(240+1)*2][3];
350
        GLfloat deltaRadius;
351
        GLfloat radiusHigh;
352

UNCOV
353
        if (outerRadius<=0.0f || innerRadius<0.0f ||innerRadius > outerRadius)
×
354
        {
UNCOV
355
                Q_ASSERT(0);
×
356
                return;
357
        }
358

359
        /* Compute length (needed for normal calculations) */
UNCOV
360
        deltaRadius=outerRadius-innerRadius;
×
361

362
        /* Cache is the vertex locations cache */
UNCOV
363
        for (int i=0; i<=slices; i++)
×
364
        {
UNCOV
365
                GLfloat angle=(M_PIf*2.0f)*static_cast<float>(i)/static_cast<float>(slices);
×
366
                sinCache[i]=static_cast<GLfloat>(sin(angle));
×
367
                cosCache[i]=static_cast<GLfloat>(cos(angle));
×
368
        }
369

UNCOV
370
        sinCache[slices]=sinCache[0];
×
371
        cosCache[slices]=cosCache[0];
×
372

373
        /* Enable arrays */
UNCOV
374
        enableClientStates(true);
×
375
        setVertexPointer(3, GL_FLOAT, vertices);
×
376

UNCOV
377
        radiusHigh=outerRadius-deltaRadius;
×
378
        for (int i=0; i<=slices; i++)
×
379
        {
UNCOV
380
                vertices[i*2][0]= static_cast<float>(prj->viewportCenter[0]) + outerRadius*sinCache[i];
×
381
                vertices[i*2][1]= static_cast<float>(prj->viewportCenter[1]) + outerRadius*cosCache[i];
×
382
                vertices[i*2][2] = 0.0f;
×
383
                vertices[i*2+1][0]= static_cast<float>(prj->viewportCenter[0]) + radiusHigh*sinCache[i];
×
384
                vertices[i*2+1][1]= static_cast<float>(prj->viewportCenter[1]) + radiusHigh*cosCache[i];
×
385
                vertices[i*2+1][2] = 0.0f;
×
386
        }
UNCOV
387
        drawFromArray(TriangleStrip, (slices+1)*2, 0, false);
×
388
        enableClientStates(false);
×
389
        if(oldBlendState)
×
390
                glEnable(GL_BLEND);
×
391
}
392

UNCOV
393
void StelPainter::computeFanDisk(float radius, uint innerFanSlices, uint level, QVector<Vec3d>& vertexArr, QVector<Vec2f>& texCoordArr)
×
394
{
UNCOV
395
        Q_ASSERT(level<32);
×
396
        float rad[64];
397
        uint i,j;
UNCOV
398
        rad[level] = radius;
×
399
#pragma warning(suppress: 4146)
UNCOV
400
        for (i=level-1u;i!=-1u;--i)
×
401
        {
UNCOV
402
                rad[i] = rad[i+1]*(1.f-M_PIf/(innerFanSlices<<(i+1)))*2.f/3.f;
×
403
        }
UNCOV
404
        uint slices = innerFanSlices<<level;
×
405

UNCOV
406
        float* cos_sin_theta = StelUtils::ComputeCosSinTheta(static_cast<uint>(slices));
×
407
        float* cos_sin_theta_p;
UNCOV
408
        uint slices_step = 2;
×
409
        float x,y,xa,ya;
UNCOV
410
        radius*=2.f;
×
411
        vertexArr.resize(0);
×
412
        texCoordArr.resize(0);
×
413
        for (i=level;i>0;--i,slices_step<<=1)
×
414
        {
UNCOV
415
                for (j=0,cos_sin_theta_p=cos_sin_theta; j<slices-1; j+=slices_step,cos_sin_theta_p+=2*slices_step)
×
416
                {
UNCOV
417
                        xa = rad[i]*cos_sin_theta_p[slices_step];
×
418
                        ya = rad[i]*cos_sin_theta_p[slices_step+1];
×
419
                        texCoordArr << Vec2f(0.5f+xa/radius, 0.5f+ya/radius);
×
420
                        vertexArr << Vec3d(static_cast<double>(xa), static_cast<double>(ya), 0);
×
421

UNCOV
422
                        x = rad[i]*cos_sin_theta_p[2*slices_step];
×
423
                        y = rad[i]*cos_sin_theta_p[2*slices_step+1];
×
424
                        texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
×
425
                        vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
×
426

UNCOV
427
                        x = rad[i-1]*cos_sin_theta_p[2*slices_step];
×
428
                        y = rad[i-1]*cos_sin_theta_p[2*slices_step+1];
×
429
                        texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
×
430
                        vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
×
431

UNCOV
432
                        texCoordArr << Vec2f(0.5f+xa/radius, 0.5f+ya/radius);
×
433
                        vertexArr << Vec3d(static_cast<double>(xa), static_cast<double>(ya), 0);
×
434
                        texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
×
435
                        vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
×
436

UNCOV
437
                        x = rad[i-1]*cos_sin_theta_p[0];
×
438
                        y = rad[i-1]*cos_sin_theta_p[1];
×
439
                        texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
×
440
                        vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
×
441

UNCOV
442
                        texCoordArr << Vec2f(0.5f+xa/radius, 0.5f+ya/radius);
×
443
                        vertexArr << Vec3d(static_cast<double>(xa), static_cast<double>(ya), 0);
×
444
                        texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
×
445
                        vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
×
446

UNCOV
447
                        x = rad[i]*cos_sin_theta_p[0];
×
448
                        y = rad[i]*cos_sin_theta_p[1];
×
449
                        texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
×
450
                        vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
×
451
                }
452
        }
453
        // draw the inner polygon
UNCOV
454
        slices_step>>=1;
×
455
        cos_sin_theta_p=cos_sin_theta;
×
456

UNCOV
457
        if (slices==1)
×
458
        {
UNCOV
459
                x = rad[0]*cos_sin_theta_p[0];
×
460
                y = rad[0]*cos_sin_theta_p[1];
×
461
                texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
×
462
                vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
×
463
                cos_sin_theta_p+=2*slices_step;
×
464
                x = rad[0]*cos_sin_theta_p[0];
×
465
                y = rad[0]*cos_sin_theta_p[1];
×
466
                texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
×
467
                vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
×
468
                cos_sin_theta_p+=2*slices_step;
×
469
                x = rad[0]*cos_sin_theta_p[0];
×
470
                y = rad[0]*cos_sin_theta_p[1];
×
471
                texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
×
472
                vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
×
473
        }
474
        else
475
        {
UNCOV
476
                j=0;
×
477
                while (j<slices)
×
478
                {
UNCOV
479
                        texCoordArr << Vec2f(0.5f, 0.5f);
×
480
                        vertexArr << Vec3d(0, 0, 0);
×
481
                        x = rad[0]*cos_sin_theta_p[0];
×
482
                        y = rad[0]*cos_sin_theta_p[1];
×
483
                        texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
×
484
                        vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
×
485
                        j+=slices_step;
×
486
                        cos_sin_theta_p+=2*slices_step;
×
487
                        x = rad[0]*cos_sin_theta_p[0];
×
488
                        y = rad[0]*cos_sin_theta_p[1];
×
489
                        texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
×
490
                        vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
×
491
                }
492
        }
UNCOV
493
}
×
494

UNCOV
495
static void sSphereMapTexCoordFast(float rho_div_fov, const float costheta, const float sintheta, QVector<float>& out)
×
496
{
UNCOV
497
        if (rho_div_fov>0.5f)
×
498
                rho_div_fov=0.5f;
×
499
        out << 0.5f + rho_div_fov * costheta << 0.5f + rho_div_fov * sintheta;
×
500
}
×
501

UNCOV
502
void StelPainter::sSphereMap(double radius, unsigned int slices, unsigned int stacks, float textureFov, int orientInside)
×
503
{
504
        float rho;
505
        double x,y,z;
506
        unsigned int i, j;
UNCOV
507
        const float* cos_sin_rho = StelUtils::ComputeCosSinRho(stacks);
×
508
        const float* cos_sin_rho_p;
509

UNCOV
510
        const float* cos_sin_theta = StelUtils::ComputeCosSinTheta(slices);
×
511
        const float* cos_sin_theta_p;
512

UNCOV
513
        float drho = M_PIf / static_cast<float>(stacks);
×
514
        drho/=textureFov;
×
515

516
        // texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis
517
        // t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes)
518
        // cannot use triangle fan on texturing (s coord. at top/bottom tip varies)
519

UNCOV
520
        const unsigned int imax = stacks;
×
521

UNCOV
522
        static QVector<double> vertexArr;
×
523
        static QVector<float> texCoordArr;
×
524

525
        // draw intermediate stacks as quad strips
526
        // LGTM comments: the floats are always <=1. We still prefer float multiplication (with insignificant accuracy loss) for speed.
UNCOV
527
        if (!orientInside) // nsign==1
×
528
        {
UNCOV
529
                for (i = 0,cos_sin_rho_p=cos_sin_rho,rho=0.f; i < imax; ++i,cos_sin_rho_p+=2,rho+=drho)
×
530
                {
UNCOV
531
                        vertexArr.resize(0);
×
532
                        texCoordArr.resize(0);
×
533
                        for (j=0,cos_sin_theta_p=cos_sin_theta;j<=slices;++j,cos_sin_theta_p+=2)
×
534
                        {
UNCOV
535
                                x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[1]);
×
536
                                y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[1]);
×
537
                                z = static_cast<double>(cos_sin_rho_p[0]);
×
538
                                sSphereMapTexCoordFast(rho, cos_sin_theta_p[0], cos_sin_theta_p[1], texCoordArr);
×
539
                                vertexArr << x*radius << y*radius << z*radius;
×
540

UNCOV
541
                                x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[3]);
×
542
                                y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[3]);
×
543
                                z = static_cast<double>(cos_sin_rho_p[2]);
×
544
                                sSphereMapTexCoordFast(rho + drho, cos_sin_theta_p[0], cos_sin_theta_p[1], texCoordArr);
×
545
                                vertexArr << x*radius << y*radius << z*radius;
×
546
                        }
UNCOV
547
                        setArrays(reinterpret_cast<const Vec3d*>(vertexArr.constData()), reinterpret_cast<const Vec2f*>(texCoordArr.constData()));
×
548
                        drawFromArray(TriangleStrip, vertexArr.size()/3);
×
549
                }
550
        }
551
        else
552
        {
UNCOV
553
                for (i = 0,cos_sin_rho_p=cos_sin_rho,rho=0.f; i < imax; ++i,cos_sin_rho_p+=2,rho+=drho)
×
554
                {
UNCOV
555
                        vertexArr.resize(0);
×
556
                        texCoordArr.resize(0);
×
557
                        for (j=0,cos_sin_theta_p=cos_sin_theta;j<=slices;++j,cos_sin_theta_p+=2)
×
558
                        {
UNCOV
559
                                x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[3]);
×
560
                                y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[3]);
×
561
                                z = static_cast<double>(cos_sin_rho_p[2]);
×
562
                                sSphereMapTexCoordFast(rho + drho, cos_sin_theta_p[0], -cos_sin_theta_p[1], texCoordArr);
×
563
                                vertexArr << x*radius << y*radius << z*radius;
×
564

UNCOV
565
                                x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[1]);
×
566
                                y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[1]);
×
567
                                z = static_cast<double>(cos_sin_rho_p[0]);
×
568
                                sSphereMapTexCoordFast(rho, cos_sin_theta_p[0], -cos_sin_theta_p[1], texCoordArr);
×
569
                                vertexArr << x*radius << y*radius << z*radius;
×
570
                        }
UNCOV
571
                        setArrays(reinterpret_cast<const Vec3d*>(vertexArr.constData()), reinterpret_cast<const Vec2f*>(texCoordArr.constData()));
×
572
                        drawFromArray(TriangleStrip, vertexArr.size()/3);
×
573
                }
574
        }
UNCOV
575
}
×
576

UNCOV
577
void StelPainter::drawTextGravity180(const float x, const float y, const QString& ws, const float xshift, const float yshift)
×
578
{
UNCOV
579
        const float dx = x - static_cast<float>(prj->viewportCenter[0]);
×
580
        const float dy = y - static_cast<float>(prj->viewportCenter[1]);
×
581
        float d = std::sqrt(dx*dx + dy*dy);
×
582

583
        // If the text is too far away to be visible in the screen return
UNCOV
584
        if (d>qMax(prj->viewportXywh[3], prj->viewportXywh[2])*2 || ws.isEmpty())
×
585
                return;
×
586

UNCOV
587
        const auto fm = getFontMetrics();
×
588
        const bool rtl = StelApp::getInstance().getLocaleMgr().isSkyRTL();
×
589

UNCOV
590
        const float ppx = static_cast<float>(prj->getDevicePixelsPerPixel());
×
591
        float anglePerUnitWidth = ppx / d;
×
592
        float theta = std::atan2(dy - 1, dx);
×
593

UNCOV
594
        float xVc = static_cast<float>(prj->viewportCenter[0]) + xshift;
×
595
        float yVc = static_cast<float>(prj->viewportCenter[1]) + yshift;
×
596

UNCOV
597
        const float charWidth = fm.averageCharWidth();
×
598
        constexpr float maxAnglePerChar = 10 * M_PI_180f;
×
599
        if (charWidth * anglePerUnitWidth > maxAnglePerChar)
×
600
        {
601
                // Too curvy text, limit its curvature by moving the center of curvature away
UNCOV
602
                const float x0 = d * std::cos(theta) + xVc;
×
603
                const float y0 = d * std::sin(theta) + yVc;
×
604

UNCOV
605
                anglePerUnitWidth = maxAnglePerChar / charWidth;
×
606
                d = ppx / anglePerUnitWidth;
×
607

UNCOV
608
                xVc = x0 - d * std::cos(theta);
×
609
                yVc = y0 - d * std::sin(theta);
×
610
        }
611

UNCOV
612
        const int slen = ws.length();
×
613
        const int startI = rtl ? slen - 1 : 0;
×
614
        const int endI = rtl ? -1 : slen;
×
615
        const int inc = rtl ? -1 : 1;
×
616
        for (int i = startI; i != endI; i += inc)
×
617
        {
UNCOV
618
                const QChar c = ws[i];
×
619
                const float x = d * std::cos(theta) + xVc;
×
620
                const float y = d * std::sin(theta) + yVc;
×
621
                drawText(x, y, c, 90.f + theta*M_180_PIf, 0., 0.);
×
622
                theta += fm.horizontalAdvance(c) * anglePerUnitWidth;
×
623
        }
UNCOV
624
}
×
625

UNCOV
626
void StelPainter::drawText(const Vec3d& v, const QString& str, float angleDeg, float xshift, float yshift, bool noGravity)
×
627
{
UNCOV
628
        Vec3d win;
×
629
        if (prj->project(v, win))
×
630
                drawText(static_cast<float>(win[0]), static_cast<float>(win[1]), str, angleDeg, xshift, yshift, noGravity);
×
631
}
×
632

633
/*************************************************************************
634
 Draw the string at the given position and angle with the given font
635
*************************************************************************/
636

637
// Methods taken from text-use-opengl-buffer
638
// Container for one cached string texture
639
struct StringTexture
640
{
641
        QOpenGLTexture* texture;
642
        QSize size;
643
        QPoint baselineShift;
UNCOV
644
        QSizeF getTexSize() const {
×
645
                return QSizeF(static_cast<qreal>(size.width())  / static_cast<qreal>(texture->width()),
×
646
                              static_cast<qreal>(size.height()) / static_cast<qreal>(texture->height()));
×
647
        }
648

UNCOV
649
        StringTexture(QOpenGLTexture* tex, const QSize& size, const QPoint& baselineShift) :
×
650
             texture(tex), size(size), baselineShift(baselineShift) {}
×
651
        ~StringTexture() {delete texture;}
×
652
};
653

UNCOV
654
StringTexture* StelPainter::getTextTexture(const QString& str, int pixelSize) const
×
655
{
UNCOV
656
        QByteArray hash = str.toUtf8() + QByteArray::number(pixelSize);
×
657
        StringTexture* cachedTex = texCache.object(hash);
×
658
        if (cachedTex)
×
659
                return cachedTex;
×
660
        QFont tmpFont = currentFont;
×
661
        tmpFont.setPixelSize(currentFont.pixelSize()*prj->getDevicePixelsPerPixel());
×
662
        tmpFont.setStyleStrategy(QFont::NoSubpixelAntialias); // The text may rotate, which would break subpixel AA
×
663
        QRect strRect = QFontMetrics(tmpFont).boundingRect(str);
×
664
        int w = strRect.width()+1+static_cast<int>(0.02f*strRect.width());
×
665
        int h = strRect.height();
×
666

667
        QImage strImage(StelUtils::getBiggerPowerOfTwo(w), StelUtils::getBiggerPowerOfTwo(h),
UNCOV
668
                        QImage::Format_RGBX8888);
×
669
        strImage.fill(Qt::black);
×
670
        QPainter painter(&strImage);
×
671
        painter.setRenderHints(QPainter::TextAntialiasing);
×
672
        painter.setFont(tmpFont);
×
673
        painter.setPen(Qt::white);
×
674
        painter.drawText(-strRect.x(), -strRect.y(), str);
×
675
        StringTexture* newTex = new StringTexture(new QOpenGLTexture(strImage), QSize(w, h), QPoint(strRect.x(), -(strRect.y()+h)));
×
676
        newTex->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
×
677
        texCache.insert(hash, newTex, static_cast<long>(w)*h*3);
×
678
        // simply returning newTex is dangerous as the object is owned by the cache now. (Coverity Scan barks.)
UNCOV
679
        return texCache.object(hash);
×
680
}
×
681

UNCOV
682
void StelPainter::drawText(float x, float y, const QString& str, float angleDeg, float xshift, float yshift, bool noGravity)
×
683
{
UNCOV
684
        if (prj->gravityLabels && !noGravity)
×
685
        {
UNCOV
686
                drawTextGravity180(x, y, str, xshift, yshift);
×
687
        }
688
        else
689
        {
UNCOV
690
                StringTexture* tex = getTextTexture(str, currentFont.pixelSize());
×
691
                Q_ASSERT(tex);
×
692
                if (!noGravity)
×
693
                        angleDeg += prj->defaultAngleForGravityText;
×
694
                GLint oldTex = 0;
×
695
                glActiveTexture(GL_TEXTURE0);
×
696
                glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex);
×
697
                tex->texture->bind(0);
×
698
                xshift += tex->baselineShift.x();
×
699
                yshift += tex->baselineShift.y();
×
700

701
                static float vertexData[8];
702
                // compute the vertex coordinates applying the translation and the rotation
703
                static const float vertexBase[] = {0., 0., 1., 0., 0., 1., 1., 1.};
UNCOV
704
                if (std::fabs(angleDeg)>1.f*M_PI_180f)
×
705
                {
UNCOV
706
                        const float cosr = std::cos(angleDeg * M_PI_180f);
×
707
                        const float sinr = std::sin(angleDeg * M_PI_180f);
×
708
                        for (int i = 0; i < 8; i+=2)
×
709
                        {
UNCOV
710
                                vertexData[i]   = x + (tex->size.width()*vertexBase[i]+xshift) * cosr - (tex->size.height()*vertexBase[i+1]+yshift) * sinr;
×
711
                                vertexData[i+1] = y + (tex->size.width()*vertexBase[i]+xshift) * sinr + (tex->size.height()*vertexBase[i+1]+yshift) * cosr;
×
712
                        }
713
                }
714
                else
715
                {
UNCOV
716
                        for (int i = 0; i < 8; i+=2)
×
717
                        {
UNCOV
718
                                vertexData[i]   = int(x + tex->size.width()*vertexBase[i]+xshift);
×
719
                                vertexData[i+1] = int(y + tex->size.height()*vertexBase[i+1]+yshift);
×
720
                        }
721
                }
722

723
                float texCoords[8];
UNCOV
724
                for (int i=0;i<4;i++)
×
725
                {
UNCOV
726
                        texCoords[i*2+0] = static_cast<float>(tex->getTexSize().width()) * (i % 2);
×
727
                        texCoords[i*2+1] = static_cast<float>(tex->getTexSize().height()) * (1 - i / 2);
×
728
                }
UNCOV
729
                setTexCoordPointer(2, GL_FLOAT, texCoords);
×
730

731
                //text drawing requires blending, but we reset GL state afterwards if necessary
UNCOV
732
                bool oldBlending = glState.blend;
×
733
                GLenum oldSrc = glState.blendSrc, oldDst = glState.blendDst;
×
734
                setBlending(true);
×
735
                enableClientStates(true, true);
×
736
                setVertexPointer(2, GL_FLOAT, vertexData);
×
737

UNCOV
738
                const Mat4f& m = getProjector()->getProjectionMatrix();
×
739
                const QMatrix4x4 qMat(m[0], m[4], m[8], m[12],
740
                                      m[1], m[5], m[9], m[13],
741
                                      m[2], m[6], m[10], m[14],
UNCOV
742
                                      m[3], m[7], m[11], m[15]);
×
743

UNCOV
744
                vao->bind();
×
745
                verticesVBO->bind();
×
746
                const GLsizeiptr vertexCount = 4;
×
747

UNCOV
748
                auto& pr = *textShaderProgram;
×
749
                pr.bind();
×
750

UNCOV
751
                const auto bufferSize = vertexArray.vertexSizeInBytes()*vertexCount +
×
752
                                        texCoordArray.vertexSizeInBytes()*vertexCount;
×
753
                verticesVBO->allocate(bufferSize);
×
754
                const auto vertexDataSize = vertexArray.vertexSizeInBytes()*vertexCount;
×
755
                verticesVBO->write(0, vertexArray.pointer, vertexDataSize);
×
756
                const auto texCoordDataOffset = vertexDataSize;
×
757
                const auto texCoordDataSize = texCoordArray.vertexSizeInBytes()*vertexCount;
×
758
                verticesVBO->write(texCoordDataOffset, texCoordArray.pointer, texCoordDataSize);
×
759

UNCOV
760
                pr.setAttributeBuffer(textShaderVars.vertex, vertexArray.type, 0, vertexArray.size);
×
761
                pr.enableAttributeArray(textShaderVars.vertex);
×
762
                pr.setUniformValue(textShaderVars.projectionMatrix, qMat);
×
763
                pr.setUniformValue(textShaderVars.textColor, currentColor.toQVector());
×
764
                pr.setAttributeBuffer(textShaderVars.texCoord, texCoordArray.type,
×
765
                                      texCoordDataOffset, texCoordArray.size);
UNCOV
766
                pr.enableAttributeArray(textShaderVars.texCoord);
×
767

UNCOV
768
                glDrawArrays(TriangleStrip, 0, vertexCount);
×
769

UNCOV
770
                verticesVBO->release();
×
771
                vao->release();
×
772

UNCOV
773
                pr.release();
×
774

UNCOV
775
                setBlending(oldBlending, oldSrc, oldDst);
×
776
                enableClientStates(false, false);
×
777
                glBindTexture(GL_TEXTURE_2D, oldTex);
×
778
        }
UNCOV
779
}
×
780

781
// Recursive method cutting a small circle in small segments
UNCOV
782
inline void fIter(const StelProjectorP& prj, const Vec3d& p1, const Vec3d& p2, Vec3d& win1, Vec3d& win2, std::list<Vec3d>& vertexList, const std::list<Vec3d>::const_iterator& iter, double radius, const Vec3d& center, int nbI=0, bool checkCrossDiscontinuity=true)
×
783
{
UNCOV
784
        const bool crossDiscontinuity = checkCrossDiscontinuity && prj->intersectViewportDiscontinuity(p1+center, p2+center);
×
785
        if (crossDiscontinuity && nbI>=10)
×
786
        {
UNCOV
787
                win1[2]=-2.;
×
788
                win2[2]=-2.;
×
789
                vertexList.insert(iter, win1);
×
790
                vertexList.insert(iter, win2);
×
791
                return;
×
792
        }
793

UNCOV
794
        Vec3d newVertex(p1); newVertex+=p2;
×
795
        newVertex.normalize();
×
796
        newVertex*=radius;
×
797
        Vec3d win3(newVertex[0]+center[0], newVertex[1]+center[1], newVertex[2]+center[2]);
×
798
        const bool isValidVertex = prj->projectInPlace(win3);
×
799

UNCOV
800
        const float v10=static_cast<float>(win1[0]-win3[0]);
×
801
        const float v11=static_cast<float>(win1[1]-win3[1]);
×
802
        const float v20=static_cast<float>(win2[0]-win3[0]);
×
803
        const float v21=static_cast<float>(win2[1]-win3[1]);
×
804

UNCOV
805
        const float dist = std::sqrt((v10*v10+v11*v11)*(v20*v20+v21*v21));
×
806
        const float cosAngle = (v10*v20+v11*v21)/dist;
×
807
        if ((cosAngle>-0.999f || dist>50*50 || crossDiscontinuity) && nbI<10)
×
808
        {
809
                // Use the 3rd component of the vector to store whether the vertex is valid
UNCOV
810
                win3[2]= isValidVertex ? 1.0 : -1.;
×
811
                fIter(prj, p1, newVertex, win1, win3, vertexList, vertexList.insert(iter, win3), radius, center, nbI+1, crossDiscontinuity || dist>50*50);
×
812
                fIter(prj, newVertex, p2, win3, win2, vertexList, iter, radius, center, nbI+1, crossDiscontinuity || dist>50*50 );
×
813
        }
814
}
815

816
// Used by the method below. A test before 24.3 showed no more than 64 vertices ever used.
817
// This uses much faster stack allocation if specified max length is not exceeded, else falls back to heap allocation.
818
QVarLengthArray<Vec3f, 128> StelPainter::smallCircleVertexArray;
819
QVarLengthArray<Vec4f, 128> StelPainter::smallCircleColorArray;
820

UNCOV
821
void StelPainter::drawSmallCircleVertexArray()
×
822
{
UNCOV
823
        if (smallCircleVertexArray.size() == 1)
×
824
        {
UNCOV
825
                smallCircleVertexArray.resize(0);
×
826
                smallCircleColorArray.resize(0);
×
827
                return;
×
828
        }
UNCOV
829
        if (smallCircleVertexArray.isEmpty())
×
830
                return;
×
831
        //if (smallCircleVertexArray.length()>64)
832
        //        qDebug() << "Size: " << smallCircleVertexArray.length();
833

UNCOV
834
        enableClientStates(true, false, !smallCircleColorArray.isEmpty());
×
835
        setVertexPointer(3, GL_FLOAT, smallCircleVertexArray.constData());
×
836
        if (!smallCircleColorArray.isEmpty())
×
837
                setColorPointer(4, GL_FLOAT, smallCircleColorArray.constData());
×
838
        drawFromArray(LineStrip, smallCircleVertexArray.size(), 0, false);
×
839
        enableClientStates(false);
×
840
        smallCircleVertexArray.resize(0);
×
841
        smallCircleColorArray.resize(0);
×
842
}
843

UNCOV
844
void StelPainter::drawGreatCircleArc(const Vec3d& start, const Vec3d& stop, const SphericalCap* clippingCap,
×
845
        void (*viewportEdgeIntersectCallback)(const Vec3d& screenPos, const Vec3d& direction, void* userData), void* userData)
846
 {
UNCOV
847
         if (clippingCap)
×
848
         {
UNCOV
849
                 Vec3d pt1=start;
×
850
                 Vec3d pt2=stop;
×
851
                 if (clippingCap->clipGreatCircle(pt1, pt2))
×
852
                 {
UNCOV
853
                        drawSmallCircleArc(pt1, pt2, Vec3d(0.), viewportEdgeIntersectCallback, userData);
×
854
                 }
UNCOV
855
                 return;
×
856
        }
UNCOV
857
        drawSmallCircleArc(start, stop, Vec3d(0.), viewportEdgeIntersectCallback, userData);
×
858
 }
859

860
/*************************************************************************
861
 Draw a small circle arc in the current frame
862
*************************************************************************/
UNCOV
863
void StelPainter::drawSmallCircleArc(const Vec3d& start, const Vec3d& stop, const Vec3d& rotCenter, void (*viewportEdgeIntersectCallback)(const Vec3d& screenPos, const Vec3d& direction, void* userData), void* userData)
×
864
{
UNCOV
865
        Q_ASSERT(smallCircleVertexArray.empty());
×
866

UNCOV
867
        std::list<Vec3d> tessArc;        // Contains the list of projected points from the tesselated arc. (QLinkedList no longer available in Qt6.)
×
868
        Vec3d win1, win2;
×
869
        win1[2] = prj->project(start, win1) ? 1.0 : -1.;
×
870
        win2[2] = prj->project(stop, win2) ? 1.0 : -1.;
×
871
        tessArc.push_back(win1);
×
872

873

UNCOV
874
        if (rotCenter.normSquared()<1e-11)
×
875
        {
876
                // Great circle
877
                // Perform the tesselation of the arc in small segments in a way so that the lines look smooth
UNCOV
878
                fIter(prj, start, stop, win1, win2, tessArc, tessArc.insert(tessArc.end(), win2), 1, rotCenter);
×
879
        }
880
        else
881
        {
UNCOV
882
                Vec3d tmp = (rotCenter^start)/rotCenter.norm();
×
883
                const double radius = fabs(tmp.norm());
×
884
                // Perform the tesselation of the arc in small segments in a way so that the lines look smooth
UNCOV
885
                fIter(prj, start-rotCenter, stop-rotCenter, win1, win2, tessArc, tessArc.insert(tessArc.end(), win2), radius, rotCenter);
×
886
        }
887

888
        // And draw.
UNCOV
889
        std::list<Vec3d>::const_iterator i = tessArc.cbegin();
×
890
        //while (i<tessArc.cend())
UNCOV
891
        while (std::next(i, 1) != tessArc.cend())
×
892
        {
UNCOV
893
                const Vec3d& p1 = *i;
×
894
                const Vec3d& p2 = *(++i);
×
895
                const bool p1InViewport = prj->checkInViewport(p1);
×
896
                const bool p2InViewport = prj->checkInViewport(p2);
×
897
                if ((p1[2]>0 && p1InViewport) || (p2[2]>0 && p2InViewport))
×
898
                {
UNCOV
899
                        smallCircleVertexArray.append(p1.toVec3f()); //Vec3f(static_cast<float>(p1[0]), static_cast<float>(p1[1]), static_cast<float>(p1[2])));
×
900
                        if (std::next(i,1)==tessArc.cend())
×
901
                        {
UNCOV
902
                                smallCircleVertexArray.append(p2.toVec3f()); //Vec3f(static_cast<float>(p2[0]), static_cast<float>(p2[1]), static_cast<float>(p2[2])));
×
903
                                drawSmallCircleVertexArray();
×
904
                        }
UNCOV
905
                        if (viewportEdgeIntersectCallback && p1InViewport!=p2InViewport)
×
906
                        {
907
                                // We crossed the edge of the view port
UNCOV
908
                                if (p1InViewport)
×
909
                                        viewportEdgeIntersectCallback(prj->viewPortIntersect(p1, p2), p2-p1, userData);
×
910
                                else
UNCOV
911
                                        viewportEdgeIntersectCallback(prj->viewPortIntersect(p2, p1), p1-p2, userData);
×
912
                        }
913
                }
914
                else
915
                {
916
                        // Break the line, draw the stored vertex and flush the list
UNCOV
917
                        if (!smallCircleVertexArray.isEmpty())
×
918
                                smallCircleVertexArray.append(Vec3f(static_cast<float>(p1[0]), static_cast<float>(p1[1]), static_cast<float>(p1[2])));
×
919
                        drawSmallCircleVertexArray();
×
920
                }
921
        }
UNCOV
922
        Q_ASSERT(smallCircleVertexArray.isEmpty());
×
923
}
×
924

UNCOV
925
void StelPainter::drawPath(const QVector<Vec3d> &points, const QVector<Vec4f> &colors)
×
926
{
927
        // Because the path may intersect a viewport discontinuity, we cannot render
928
        // it in one OpenGL drawing call.
UNCOV
929
        Q_ASSERT(smallCircleVertexArray.isEmpty());
×
930
        Q_ASSERT(smallCircleColorArray.isEmpty());
×
931
        Q_ASSERT(points.size() == colors.size());
×
932
        Vec3d win;
×
933

934
        // In general we should add all the points, even if they are hidden, since otherwise we don't
935
        // have proper clipping on the sides.  We make an exception for the orthographic projection because
936
        // its clipping doesn't work well.  A better solution would be to use a culling test I think.
UNCOV
937
        bool skipHiddenPoints = dynamic_cast<StelProjectorOrthographic*>(prj.data());
×
938

UNCOV
939
        for (int i = 0; i+1 != points.size(); i++)
×
940
        {
UNCOV
941
                const Vec3d p1 = points[i];
×
942
                const Vec3d p2 = points[i + 1];
×
943
                if (!prj->intersectViewportDiscontinuity(p1, p2))
×
944
                {
UNCOV
945
                        bool visible = prj->project(p1, win);
×
946

UNCOV
947
                        if (!visible && skipHiddenPoints)
×
948
                        {
UNCOV
949
                                drawSmallCircleVertexArray();
×
950
                                continue;
×
951
                        }
952

UNCOV
953
                        smallCircleVertexArray.append(Vec3f(static_cast<float>(win[0]), static_cast<float>(win[1]), static_cast<float>(win[2])));
×
954
                        smallCircleColorArray.append(colors[i]);
×
955
                        if (i+2==points.size())
×
956
                        {
UNCOV
957
                                prj->project(p2, win);
×
958
                                smallCircleVertexArray.append(Vec3f(static_cast<float>(win[0]), static_cast<float>(win[1]), static_cast<float>(win[2])));
×
959
                                smallCircleColorArray.append(colors[i + 1]);
×
960
                                drawSmallCircleVertexArray();
×
961
                        }
962
                }
963
                else
964
                {
965
                        // Break the line, draw the stored vertex and flush the list
UNCOV
966
                        if (!smallCircleVertexArray.isEmpty())
×
967
                        {
UNCOV
968
                                prj->project(p1, win);
×
969
                                smallCircleVertexArray.append(Vec3f(static_cast<float>(win[0]), static_cast<float>(win[1]), static_cast<float>(win[2])));
×
970
                                smallCircleColorArray.append(colors[i]);
×
971
                        }
UNCOV
972
                        drawSmallCircleVertexArray();
×
973
                }
974
        }
UNCOV
975
        Q_ASSERT(smallCircleVertexArray.isEmpty());
×
976
        Q_ASSERT(smallCircleColorArray.isEmpty());
×
977
}
×
978

979
// Project the passed triangle on the screen ensuring that it will look smooth, even for non linear distortion
980
// by splitting it into subtriangles.
UNCOV
981
void StelPainter::projectSphericalTriangle(const SphericalCap* clippingCap, const Vec3d* vertices, QVarLengthArray<Vec3f, 4096>* outVertices,
×
982
        const Vec2f* texturePos, QVarLengthArray<Vec2f, 4096>* outTexturePos, const Vec3f *colors, QVarLengthArray<Vec3f, 4096> *outColors,
983
        double maxSqDistortion, int nbI, bool checkDisc1, bool checkDisc2, bool checkDisc3) const
984
{
UNCOV
985
        Q_ASSERT(fabs(vertices[0].norm()-1.)<0.00001);
×
986
        Q_ASSERT(fabs(vertices[1].norm()-1.)<0.00001);
×
987
        Q_ASSERT(fabs(vertices[2].norm()-1.)<0.00001);
×
988
        if (clippingCap && clippingCap->containsTriangle(vertices))
×
989
                clippingCap = Q_NULLPTR;
×
990
        if (clippingCap && !clippingCap->intersectsTriangle(vertices))
×
991
                return;
×
992
        bool cDiscontinuity1 = checkDisc1 && prj->intersectViewportDiscontinuity(vertices[0], vertices[1]);
×
993
        bool cDiscontinuity2 = checkDisc2 && prj->intersectViewportDiscontinuity(vertices[1], vertices[2]);
×
994
        bool cDiscontinuity3 = checkDisc3 && prj->intersectViewportDiscontinuity(vertices[0], vertices[2]);
×
995
        const bool cd1=cDiscontinuity1;
×
996
        const bool cd2=cDiscontinuity2;
×
997
        const bool cd3=cDiscontinuity3;
×
998

UNCOV
999
        Vec3d e0=vertices[0];
×
1000
        Vec3d e1=vertices[1];
×
1001
        Vec3d e2=vertices[2];
×
1002
        bool valid = prj->projectInPlace(e0);
×
1003
        valid = prj->projectInPlace(e1) || valid;
×
1004
        valid = prj->projectInPlace(e2) || valid;
×
1005
        // Clip polygons behind the viewer
UNCOV
1006
        if (!valid)
×
1007
                return;
×
1008

UNCOV
1009
        if (checkDisc1 && cDiscontinuity1==false)
×
1010
        {
1011
                // If the distortion at segment e0,e1 is too big, flags it for subdivision
UNCOV
1012
                Vec3d win3 = vertices[0]; win3+=vertices[1];
×
1013
                prj->projectInPlace(win3);
×
1014
                win3[0]-=(e0[0]+e1[0])*0.5; win3[1]-=(e0[1]+e1[1])*0.5;
×
1015
                cDiscontinuity1 = (win3[0]*win3[0]+win3[1]*win3[1])>maxSqDistortion;
×
1016
        }
UNCOV
1017
        if (checkDisc2 && cDiscontinuity2==false)
×
1018
        {
1019
                // If the distortion at segment e1,e2 is too big, flags it for subdivision
UNCOV
1020
                Vec3d win3 = vertices[1]; win3+=vertices[2];
×
1021
                prj->projectInPlace(win3);
×
1022
                win3[0]-=(e2[0]+e1[0])*0.5; win3[1]-=(e2[1]+e1[1])*0.5;
×
1023
                cDiscontinuity2 = (win3[0]*win3[0]+win3[1]*win3[1])>maxSqDistortion;
×
1024
        }
UNCOV
1025
        if (checkDisc3 && cDiscontinuity3==false)
×
1026
        {
1027
                // If the distortion at segment e2,e0 is too big, flags it for subdivision
UNCOV
1028
                Vec3d win3 = vertices[2]; win3+=vertices[0];
×
1029
                prj->projectInPlace(win3);
×
1030
                win3[0] -= (e0[0]+e2[0])*0.5;
×
1031
                win3[1] -= (e0[1]+e2[1])*0.5;
×
1032
                cDiscontinuity3 = (win3[0]*win3[0]+win3[1]*win3[1])>maxSqDistortion;
×
1033
        }
1034

UNCOV
1035
        if (!cDiscontinuity1 && !cDiscontinuity2 && !cDiscontinuity3)
×
1036
        {
1037
                // The triangle is clean, appends it
UNCOV
1038
                outVertices->append(e0.toVec3f()); outVertices->append(e1.toVec3f()); outVertices->append(e2.toVec3f());
×
1039
                if (outTexturePos)
×
1040
                        outTexturePos->append(texturePos,3);
×
1041
                if (outColors)
×
1042
                        outColors->append(colors,3);
×
1043
                return;
×
1044
        }
1045

UNCOV
1046
        if (nbI > 4)
×
1047
        {
1048
                // If we reached the limit number of iterations and still have a discontinuity,
1049
                // discards the triangle.
UNCOV
1050
                if (cd1 || cd2 || cd3)
×
1051
                        return;
×
1052

1053
                // Else display it, it will be suboptimal though.
UNCOV
1054
                outVertices->append(e0.toVec3f()); outVertices->append(e1.toVec3f()); outVertices->append(e2.toVec3f());
×
1055
                if (outTexturePos)
×
1056
                        outTexturePos->append(texturePos,3);
×
1057
                if (outColors)
×
1058
                        outColors->append(colors,3);
×
1059
                return;
×
1060
        }
1061

1062
        // Recursively splits the triangle into sub triangles.
1063
        // Depending on which combination of sides of the triangle has to be split a different strategy is used.
UNCOV
1064
        Vec3d va[3];
×
1065
        Vec2f ta[3];
×
1066
        Vec3f ca[3];
×
1067
        // Only 1 side has to be split: split the triangle in 2
UNCOV
1068
        if (cDiscontinuity1 && !cDiscontinuity2 && !cDiscontinuity3)
×
1069
        {
UNCOV
1070
                va[0]=vertices[0];
×
1071
                va[1]=vertices[0];va[1]+=vertices[1];
×
1072
                va[1].normalize();
×
1073
                va[2]=vertices[2];
×
1074
                if (outTexturePos)
×
1075
                {
UNCOV
1076
                        ta[0]=texturePos[0];
×
1077
                        ta[1]=(texturePos[0]+texturePos[1])*0.5;
×
1078
                        ta[2]=texturePos[2];
×
1079
                }
UNCOV
1080
                if (outColors)
×
1081
                {
UNCOV
1082
                        ca[0]=colors[0];
×
1083
                        ca[1]=(colors[0]+colors[1])*0.5;
×
1084
                        ca[2]=colors[2];
×
1085
                }
UNCOV
1086
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, true, false);
×
1087

1088
                //va[0]=vertices[0]+vertices[1];
1089
                //va[0].normalize();
UNCOV
1090
                va[0]=va[1];
×
1091
                va[1]=vertices[1];
×
1092
                va[2]=vertices[2];
×
1093
                if (outTexturePos)
×
1094
                {
UNCOV
1095
                        ta[0]=(texturePos[0]+texturePos[1])*0.5;
×
1096
                        ta[1]=texturePos[1];
×
1097
                        ta[2]=texturePos[2];
×
1098
                }
UNCOV
1099
                if (outColors)
×
1100
                {
UNCOV
1101
                        ca[0]=(colors[0]+colors[1])*0.5;
×
1102
                        ca[1]=colors[1];
×
1103
                        ca[2]=colors[2];
×
1104
                }
UNCOV
1105
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, false, true);
×
1106
                return;
×
1107
        }
1108

UNCOV
1109
        if (!cDiscontinuity1 && cDiscontinuity2 && !cDiscontinuity3)
×
1110
        {
UNCOV
1111
                va[0]=vertices[0];
×
1112
                va[1]=vertices[1];
×
1113
                va[2]=vertices[1];va[2]+=vertices[2];
×
1114
                va[2].normalize();
×
1115
                if (outTexturePos)
×
1116
                {
UNCOV
1117
                        ta[0]=texturePos[0];
×
1118
                        ta[1]=texturePos[1];
×
1119
                        ta[2]=(texturePos[1]+texturePos[2])*0.5;
×
1120
                }
UNCOV
1121
                if (outColors)
×
1122
                {
UNCOV
1123
                        ca[0]=colors[0];
×
1124
                        ca[1]=colors[1];
×
1125
                        ca[2]=(colors[1]+colors[2])*0.5;
×
1126
                }
UNCOV
1127
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, false, true, true);
×
1128

UNCOV
1129
                va[0]=vertices[0];
×
1130
                //va[1]=vertices[1]+vertices[2];
1131
                //va[1].normalize();
UNCOV
1132
                va[1]=va[2];
×
1133
                va[2]=vertices[2];
×
1134
                if (outTexturePos)
×
1135
                {
UNCOV
1136
                        ta[0]=texturePos[0];
×
1137
                        ta[1]=(texturePos[1]+texturePos[2])*0.5;
×
1138
                        ta[2]=texturePos[2];
×
1139
                }
UNCOV
1140
                if (outColors)
×
1141
                {
UNCOV
1142
                        ca[0]=colors[0];
×
1143
                        ca[1]=(colors[1]+colors[2])*0.5;
×
1144
                        ca[2]=colors[2];
×
1145
                }
UNCOV
1146
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, true, false);
×
1147
                return;
×
1148
        }
1149

UNCOV
1150
        if (!cDiscontinuity1 && !cDiscontinuity2 && cDiscontinuity3)
×
1151
        {
UNCOV
1152
                va[0]=vertices[0];
×
1153
                va[1]=vertices[1];
×
1154
                va[2]=vertices[0];va[2]+=vertices[2];
×
1155
                va[2].normalize();
×
1156
                if (outTexturePos)
×
1157
                {
UNCOV
1158
                        ta[0]=texturePos[0];
×
1159
                        ta[1]=texturePos[1];
×
1160
                        ta[2]=(texturePos[0]+texturePos[2])*0.5;
×
1161
                }
UNCOV
1162
                if (outColors)
×
1163
                {
UNCOV
1164
                        ca[0]=colors[0];
×
1165
                        ca[1]=colors[1];
×
1166
                        ca[2]=(colors[0]+colors[2])*0.5;
×
1167
                }
UNCOV
1168
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, false, true, true);
×
1169

1170
                //va[0]=vertices[0]+vertices[2];
1171
                //va[0].normalize();
UNCOV
1172
                va[0]=va[2];
×
1173
                va[1]=vertices[1];
×
1174
                va[2]=vertices[2];
×
1175
                if (outTexturePos)
×
1176
                {
UNCOV
1177
                        ta[0]=(texturePos[0]+texturePos[2])*0.5;
×
1178
                        ta[1]=texturePos[1];
×
1179
                        ta[2]=texturePos[2];
×
1180
                }
UNCOV
1181
                if (outColors)
×
1182
                {
UNCOV
1183
                        ca[0]=(colors[0]+colors[2])*0.5;
×
1184
                        ca[1]=colors[1];
×
1185
                        ca[2]=colors[2];
×
1186
                }
UNCOV
1187
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, false, true);
×
1188
                return;
×
1189
        }
1190

1191
        // 2 sides have to be split: split the triangle in 3
UNCOV
1192
        if (cDiscontinuity1 && cDiscontinuity2 && !cDiscontinuity3)
×
1193
        {
UNCOV
1194
                va[0]=vertices[0];
×
1195
                va[1]=vertices[0];va[1]+=vertices[1];
×
1196
                va[1].normalize();
×
1197
                va[2]=vertices[1];va[2]+=vertices[2];
×
1198
                va[2].normalize();
×
1199
                if (outTexturePos)
×
1200
                {
UNCOV
1201
                        ta[0]=texturePos[0];
×
1202
                        ta[1]=(texturePos[0]+texturePos[1])*0.5;
×
1203
                        ta[2]=(texturePos[1]+texturePos[2])*0.5;
×
1204
                }
UNCOV
1205
                if (outColors)
×
1206
                {
UNCOV
1207
                        ca[0]=colors[0];
×
1208
                        ca[1]=(colors[0]+colors[1])*0.5;
×
1209
                        ca[2]=(colors[1]+colors[2])*0.5;
×
1210
                }
UNCOV
1211
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
×
1212

1213
                //va[0]=vertices[0]+vertices[1];
1214
                //va[0].normalize();
UNCOV
1215
                va[0]=va[1];
×
1216
                va[1]=vertices[1];
×
1217
                //va[2]=vertices[1]+vertices[2];
1218
                //va[2].normalize();
UNCOV
1219
                if (outTexturePos)
×
1220
                {
UNCOV
1221
                        ta[0]=(texturePos[0]+texturePos[1])*0.5;
×
1222
                        ta[1]=texturePos[1];
×
1223
                        ta[2]=(texturePos[1]+texturePos[2])*0.5;
×
1224
                }
UNCOV
1225
                if (outColors)
×
1226
                {
UNCOV
1227
                        ca[0]=(colors[0]+colors[1])*0.5;
×
1228
                        ca[1]=colors[1];
×
1229
                        ca[2]=(colors[1]+colors[2])*0.5;
×
1230
                }
UNCOV
1231
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
×
1232

UNCOV
1233
                va[0]=vertices[0];
×
1234
                //va[1]=vertices[1]+vertices[2];
1235
                //va[1].normalize();
UNCOV
1236
                va[1]=va[2];
×
1237
                va[2]=vertices[2];
×
1238
                if (outTexturePos)
×
1239
                {
UNCOV
1240
                        ta[0]=texturePos[0];
×
1241
                        ta[1]=(texturePos[1]+texturePos[2])*0.5;
×
1242
                        ta[2]=texturePos[2];
×
1243
                }
UNCOV
1244
                if (outColors)
×
1245
                {
UNCOV
1246
                        ca[0]=colors[0];
×
1247
                        ca[1]=(colors[1]+colors[2])*0.5;
×
1248
                        ca[2]=colors[2];
×
1249
                }
UNCOV
1250
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, true, false);
×
1251
                return;
×
1252
        }
UNCOV
1253
        if (cDiscontinuity1 && !cDiscontinuity2 && cDiscontinuity3)
×
1254
        {
UNCOV
1255
                va[0]=vertices[0];
×
1256
                va[1]=vertices[0];va[1]+=vertices[1];
×
1257
                va[1].normalize();
×
1258
                va[2]=vertices[0];va[2]+=vertices[2];
×
1259
                va[2].normalize();
×
1260
                if (outTexturePos)
×
1261
                {
UNCOV
1262
                        ta[0]=texturePos[0];
×
1263
                        ta[1]=(texturePos[0]+texturePos[1])*0.5;
×
1264
                        ta[2]=(texturePos[0]+texturePos[2])*0.5;
×
1265
                }
UNCOV
1266
                if (outColors)
×
1267
                {
UNCOV
1268
                        ca[0]=colors[0];
×
1269
                        ca[1]=(colors[0]+colors[1])*0.5;
×
1270
                        ca[2]=(colors[0]+colors[2])*0.5;
×
1271
                }
UNCOV
1272
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
×
1273

1274
                //va[0]=vertices[0]+vertices[1];
1275
                //va[0].normalize();
UNCOV
1276
                va[0]=va[1];
×
1277
                va[1]=vertices[2];
×
1278
                //va[2]=vertices[0]+vertices[2];
1279
                //va[2].normalize();
UNCOV
1280
                if (outTexturePos)
×
1281
                {
UNCOV
1282
                        ta[0]=(texturePos[0]+texturePos[1])*0.5;
×
1283
                        ta[1]=texturePos[2];
×
1284
                        ta[2]=(texturePos[0]+texturePos[2])*0.5;
×
1285
                }
UNCOV
1286
                if (outColors)
×
1287
                {
UNCOV
1288
                        ca[0]=(colors[0]+colors[1])*0.5;
×
1289
                        ca[1]=colors[2];
×
1290
                        ca[2]=(colors[0]+colors[2])*0.5;
×
1291
                }
UNCOV
1292
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
×
1293

1294

1295
                //va[0]=vertices[0]+vertices[1];
1296
                //va[0].normalize();
UNCOV
1297
                va[1]=vertices[1];
×
1298
                va[2]=vertices[2];
×
1299
                if (outTexturePos)
×
1300
                {
UNCOV
1301
                        ta[0]=(texturePos[0]+texturePos[1])*0.5;
×
1302
                        ta[1]=texturePos[1];
×
1303
                        ta[2]=texturePos[2];
×
1304
                }
UNCOV
1305
                if (outColors)
×
1306
                {
UNCOV
1307
                        ca[0]=(colors[0]+colors[1])*0.5;
×
1308
                        ca[1]=colors[1];
×
1309
                        ca[2]=colors[2];
×
1310
                }
UNCOV
1311
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, false, true);
×
1312

UNCOV
1313
                return;
×
1314
        }
UNCOV
1315
        if (!cDiscontinuity1 && cDiscontinuity2 && cDiscontinuity3)
×
1316
        {
UNCOV
1317
                va[0]=vertices[0];
×
1318
                va[1]=vertices[1];
×
1319
                va[2]=vertices[1];va[2]+=vertices[2];
×
1320
                va[2].normalize();
×
1321
                if (outTexturePos)
×
1322
                {
UNCOV
1323
                        ta[0]=texturePos[0];
×
1324
                        ta[1]=texturePos[1];
×
1325
                        ta[2]=(texturePos[1]+texturePos[2])*0.5;
×
1326
                }
UNCOV
1327
                if (outColors)
×
1328
                {
UNCOV
1329
                        ca[0]=colors[0];
×
1330
                        ca[1]=colors[1];
×
1331
                        ca[2]=(colors[1]+colors[2])*0.5;
×
1332
                }
UNCOV
1333
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, false, true, true);
×
1334

1335
                //va[0]=vertices[1]+vertices[2];
1336
                //va[0].normalize();
UNCOV
1337
                va[0]=va[2];
×
1338
                va[1]=vertices[2];
×
1339
                va[2]=vertices[0];va[2]+=vertices[2];
×
1340
                va[2].normalize();
×
1341
                if (outTexturePos)
×
1342
                {
UNCOV
1343
                        ta[0]=(texturePos[1]+texturePos[2])*0.5;
×
1344
                        ta[1]=texturePos[2];
×
1345
                        ta[2]=(texturePos[0]+texturePos[2])*0.5;
×
1346
                }
UNCOV
1347
                if (outColors)
×
1348
                {
UNCOV
1349
                        ca[0]=(colors[1]+colors[2])*0.5;
×
1350
                        ca[1]=colors[2];
×
1351
                        ca[2]=(colors[0]+colors[2])*0.5;
×
1352
                }
UNCOV
1353
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
×
1354

UNCOV
1355
                va[1]=va[0];
×
1356
                va[0]=vertices[0];
×
1357
                //va[1]=vertices[1]+vertices[2];
1358
                //va[1].normalize();
1359
                //va[2]=vertices[0]+vertices[2];
1360
                //va[2].normalize();
UNCOV
1361
                if (outTexturePos)
×
1362
                {
UNCOV
1363
                        ta[0]=texturePos[0];
×
1364
                        ta[1]=(texturePos[1]+texturePos[2])*0.5;
×
1365
                        ta[2]=(texturePos[0]+texturePos[2])*0.5;
×
1366
                }
UNCOV
1367
                if (outColors)
×
1368
                {
UNCOV
1369
                        ca[0]=colors[0];
×
1370
                        ca[1]=(colors[1]+colors[2])*0.5;
×
1371
                        ca[2]=(colors[0]+colors[2])*0.5;
×
1372
                }
UNCOV
1373
                projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
×
1374
                return;
×
1375
        }
1376

1377
        // Last case: the 3 sides have to be split: cut in 4 triangles a' la HTM
UNCOV
1378
        va[0]=vertices[0];va[0]+=vertices[1];
×
1379
        va[0].normalize();
×
1380
        va[1]=vertices[1];va[1]+=vertices[2];
×
1381
        va[1].normalize();
×
1382
        va[2]=vertices[0];va[2]+=vertices[2];
×
1383
        va[2].normalize();
×
1384
        if (outTexturePos)
×
1385
        {
UNCOV
1386
                ta[0]=(texturePos[0]+texturePos[1])*0.5;
×
1387
                ta[1]=(texturePos[1]+texturePos[2])*0.5;
×
1388
                ta[2]=(texturePos[0]+texturePos[2])*0.5;
×
1389
        }
UNCOV
1390
        if (outColors)
×
1391
        {
UNCOV
1392
                ca[0]=(colors[0]+colors[1])*0.5;
×
1393
                ca[1]=(colors[1]+colors[2])*0.5;
×
1394
                ca[2]=(colors[0]+colors[2])*0.5;
×
1395
        }
UNCOV
1396
        projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
×
1397

UNCOV
1398
        va[1]=va[0];
×
1399
        va[0]=vertices[0];
×
1400
        //va[1]=vertices[0]+vertices[1];
1401
        //va[1].normalize();
1402
        //va[2]=vertices[0]+vertices[2];
1403
        //va[2].normalize();
UNCOV
1404
        if (outTexturePos)
×
1405
        {
UNCOV
1406
                ta[0]=texturePos[0];
×
1407
                ta[1]=(texturePos[0]+texturePos[1])*0.5;
×
1408
                ta[2]=(texturePos[0]+texturePos[2])*0.5;
×
1409
        }
UNCOV
1410
        if (outColors)
×
1411
        {
UNCOV
1412
                ca[0]=colors[0];
×
1413
                ca[1]=(colors[0]+colors[1])*0.5;
×
1414
                ca[2]=(colors[0]+colors[2])*0.5;
×
1415
        }
UNCOV
1416
        projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
×
1417

1418
        //va[0]=vertices[0]+vertices[1];
1419
        //va[0].normalize();
UNCOV
1420
        va[0]=va[1];
×
1421
        va[1]=vertices[1];
×
1422
        va[2]=vertices[1];va[2]+=vertices[2];
×
1423
        va[2].normalize();
×
1424
        if (outTexturePos)
×
1425
        {
UNCOV
1426
                ta[0]=(texturePos[0]+texturePos[1])*0.5;
×
1427
                ta[1]=texturePos[1];
×
1428
                ta[2]=(texturePos[1]+texturePos[2])*0.5;
×
1429
        }
UNCOV
1430
        if (outColors)
×
1431
        {
UNCOV
1432
                ca[0]=(colors[0]+colors[1])*0.5;
×
1433
                ca[1]=colors[1];
×
1434
                ca[2]=(colors[1]+colors[2])*0.5;
×
1435
        }
UNCOV
1436
        projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
×
1437

UNCOV
1438
        va[0]=vertices[0];va[0]+=vertices[2];
×
1439
        va[0].normalize();
×
1440
        //va[1]=vertices[1]+vertices[2];
1441
        //va[1].normalize();
UNCOV
1442
        va[1]=va[2];
×
1443
        va[2]=vertices[2];
×
1444
        if (outTexturePos)
×
1445
        {
UNCOV
1446
                ta[0]=(texturePos[0]+texturePos[2])*0.5;
×
1447
                ta[1]=(texturePos[1]+texturePos[2])*0.5;
×
1448
                ta[2]=texturePos[2];
×
1449
        }
UNCOV
1450
        if (outColors)
×
1451
        {
UNCOV
1452
                ca[0]=(colors[0]+colors[2])*0.5;
×
1453
                ca[1]=(colors[1]+colors[2])*0.5;
×
1454
                ca[2]=colors[2];
×
1455
        }
UNCOV
1456
        projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
×
1457

UNCOV
1458
        return;
×
1459
}
1460

1461
QVarLengthArray<Vec3f, 4096> StelPainter::polygonVertexArray;
1462
QVarLengthArray<Vec2f, 4096> StelPainter::polygonTextureCoordArray;
1463
QVarLengthArray<Vec3f, 4096> StelPainter::polygonColorArray;
1464
QVarLengthArray<unsigned int, 4096> StelPainter::indexArray;
1465

UNCOV
1466
void StelPainter::drawGreatCircleArcs(const StelVertexArray& va, const SphericalCap* clippingCap)
×
1467
{
UNCOV
1468
        Q_ASSERT(va.vertex.size()!=1);
×
1469
        Q_ASSERT(!va.isIndexed());        // Indexed unsupported yet
×
1470
        switch (va.primitiveType)
×
1471
        {
UNCOV
1472
                case StelVertexArray::Lines:
×
1473
                        Q_ASSERT(va.vertex.size()%2==0);
×
1474
                        for (int i=0;i<va.vertex.size();i+=2)
×
1475
                                drawGreatCircleArc(va.vertex.at(i), va.vertex.at(i+1), clippingCap);
×
1476
                        return;
×
1477
                case StelVertexArray::LineStrip:
×
1478
                        for (int i=0;i<va.vertex.size()-1;++i)
×
1479
                                drawGreatCircleArc(va.vertex.at(i), va.vertex.at(i+1), clippingCap);
×
1480
                        return;
×
1481
                case StelVertexArray::LineLoop:
×
1482
                        for (int i=0;i<va.vertex.size()-1;++i)
×
1483
                                drawGreatCircleArc(va.vertex.at(i), va.vertex.at(i+1), clippingCap);
×
1484
                        drawGreatCircleArc(va.vertex.last(), va.vertex.first(), clippingCap);
×
1485
                        return;
×
1486
                default:
×
1487
                        Q_ASSERT(0); // Unsupported primitive yype
×
1488
        }
1489
}
1490

1491
// The function object that we use as an interface between VertexArray::foreachTriangle and
1492
// StelPainter::projectSphericalTriangle.
1493
//
1494
// This is used by drawSphericalTriangles to project all the triangles coordinates in a StelVertexArray into our global
1495
// vertex array buffer.
1496
class VertexArrayProjector
1497
{
1498
public:
UNCOV
1499
        VertexArrayProjector(const StelVertexArray& ar, StelPainter* apainter, const SphericalCap* aclippingCap,
×
1500
                                                 QVarLengthArray<Vec3f, 4096>* aoutVertices, QVarLengthArray<Vec2f, 4096>* aoutTexturePos=Q_NULLPTR, QVarLengthArray<Vec3f, 4096>* aoutColors=Q_NULLPTR, double amaxSqDistortion=5.)
UNCOV
1501
                   : //vertexArray(ar),
×
1502
                     painter(apainter), clippingCap(aclippingCap), outVertices(aoutVertices),
×
1503
                         outColors(aoutColors), outTexturePos(aoutTexturePos), maxSqDistortion(amaxSqDistortion)
×
1504
        {
1505
                Q_UNUSED(ar)
UNCOV
1506
        }
×
1507

1508
        // Project a single triangle and add it into the output arrays
UNCOV
1509
        inline void operator()(const Vec3d* v0, const Vec3d* v1, const Vec3d* v2,
×
1510
                                                   const Vec2f* t0, const Vec2f* t1, const Vec2f* t2,
1511
                                                   const Vec3f* c0, const Vec3f* c1, const Vec3f* c2,
1512
                                                   unsigned int, unsigned int, unsigned) const
1513
        {
1514
                // XXX: we may optimize more by putting the declaration and the test outside of this method.
UNCOV
1515
                Vec3d tmpVertex[3] = {*v0, *v1, *v2};
×
1516
                // required, else assertion at begin of projectSphericalTriangle() fails!
UNCOV
1517
                tmpVertex[0].normalize();
×
1518
                tmpVertex[1].normalize();
×
1519
                tmpVertex[2].normalize();
×
1520
                if ( (outTexturePos) && (outColors))
×
1521
                {
UNCOV
1522
                        const Vec2f tmpTexture[3] = {*t0, *t1, *t2};
×
1523
                        const Vec3f tmpColor[3] = {*c0, *c1, *c2};
×
1524
                        painter->projectSphericalTriangle(clippingCap, tmpVertex, outVertices, tmpTexture, outTexturePos, tmpColor, outColors, maxSqDistortion);
×
1525
                }
×
1526
                else if (outTexturePos)
×
1527
                {
UNCOV
1528
                        const Vec2f tmpTexture[3] = {*t0, *t1, *t2};
×
1529
                        painter->projectSphericalTriangle(clippingCap, tmpVertex, outVertices, tmpTexture, outTexturePos, Q_NULLPTR, Q_NULLPTR, maxSqDistortion);
×
1530
                }
UNCOV
1531
                else if (outColors)
×
1532
                {
UNCOV
1533
                        const Vec3f tmpColor[3] = {*c0, *c1, *c2};
×
1534
                        painter->projectSphericalTriangle(clippingCap, tmpVertex, outVertices, Q_NULLPTR, Q_NULLPTR, tmpColor, outColors, maxSqDistortion);
×
1535
                }
1536
                else
UNCOV
1537
                        painter->projectSphericalTriangle(clippingCap, tmpVertex, outVertices, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR, maxSqDistortion);
×
1538
        }
×
1539

1540
        // Draw the resulting arrays
UNCOV
1541
        void drawResult()
×
1542
        {
UNCOV
1543
                painter->setVertexPointer(3, GL_FLOAT, outVertices->constData());
×
1544
                if (outTexturePos)
×
1545
                        painter->setTexCoordPointer(2, GL_FLOAT, outTexturePos->constData());
×
1546
                if (outColors)
×
1547
                        painter->setColorPointer(3, GL_FLOAT, outColors->constData());
×
1548

UNCOV
1549
                painter->enableClientStates(true, outTexturePos != Q_NULLPTR, outColors != Q_NULLPTR);
×
1550
                painter->drawFromArray(StelPainter::Triangles, outVertices->size(), 0, false);
×
1551
                painter->enableClientStates(false);
×
1552
        }
×
1553

1554
private:
1555
        StelPainter* painter;
1556
        const SphericalCap* clippingCap;
1557
        QVarLengthArray<Vec3f, 4096>* outVertices;
1558
        QVarLengthArray<Vec3f, 4096>* outColors;
1559
        QVarLengthArray<Vec2f, 4096>* outTexturePos;
1560
        double maxSqDistortion;
1561
};
1562

UNCOV
1563
void StelPainter::drawStelVertexArray(const StelVertexArray& arr, bool checkDiscontinuity, Vec3d aberration)
×
1564
{
UNCOV
1565
        if (checkDiscontinuity && prj->hasDiscontinuity())
×
1566
        {
1567
                // The projection has discontinuities, so we need to make sure that no triangle is crossing them.
UNCOV
1568
                drawStelVertexArray(arr.removeDiscontinuousTriangles(this->getProjector().data()), false, aberration);
×
1569
                return;
×
1570
        }
1571

UNCOV
1572
        if (aberration==Vec3d(0.))
×
1573
        {
UNCOV
1574
                setVertexPointer(3, GL_DOUBLE, arr.vertex.constData());
×
1575
        }
1576
        else
1577
        {
UNCOV
1578
                QVector<Vec3d> aberredVertex(arr.vertex.size());
×
1579
                for (int i=0; i<arr.vertex.size(); i++)
×
1580
                {
UNCOV
1581
                        Q_ASSERT(qFuzzyCompare(arr.vertex.at(i).normSquared(), 1.0));
×
1582
                        Vec3d vec=arr.vertex.at(i)+aberration;
×
1583
                        vec.normalize();
×
1584
                        aberredVertex[i]=vec;
×
1585
                }
UNCOV
1586
                setVertexPointer(3, GL_DOUBLE, aberredVertex.constData());
×
1587
        }
×
1588
        if (arr.isTextured())
×
1589
        {
UNCOV
1590
                setTexCoordPointer(2, GL_FLOAT, arr.texCoords.constData());
×
1591
                if (arr.isColored())
×
1592
                {
UNCOV
1593
                        setColorPointer(3, GL_FLOAT, arr.colors.constData());
×
1594
                        enableClientStates(true, true, true);
×
1595
                }
1596
                else
UNCOV
1597
                        enableClientStates(true, true, false);
×
1598
        }
1599
        else
1600
        {
UNCOV
1601
                if (arr.isColored())
×
1602
                {
UNCOV
1603
                        setColorPointer(3, GL_FLOAT, arr.colors.constData());
×
1604
                        enableClientStates(true, false, true);
×
1605
                }
1606
                else
UNCOV
1607
                        enableClientStates(true, false, false);
×
1608
        }
UNCOV
1609
        if (arr.isIndexed())
×
1610
                drawFromArray(static_cast<StelPainter::DrawingMode>(arr.primitiveType), arr.indices.size(), 0, true, arr.indices.constData());
×
1611
        else
UNCOV
1612
                drawFromArray(static_cast<StelPainter::DrawingMode>(arr.primitiveType), arr.vertex.size());
×
1613

UNCOV
1614
        enableClientStates(false);
×
1615
}
1616

UNCOV
1617
void StelPainter::drawSphericalTriangles(const StelVertexArray& va, bool textured, bool colored, const SphericalCap* clippingCap, bool doSubDivide, double maxSqDistortion)
×
1618
{
UNCOV
1619
        if (va.vertex.isEmpty())
×
1620
                return;
×
1621

UNCOV
1622
        Q_ASSERT(va.vertex.size()>2);
×
1623
        polygonVertexArray.clear();
×
1624
        polygonTextureCoordArray.clear();
×
1625

UNCOV
1626
        indexArray.clear();
×
1627

UNCOV
1628
        if (!doSubDivide)
×
1629
        {
1630
                // The simplest case, we don't need to iterate through the triangles at all.
UNCOV
1631
                drawStelVertexArray(va);
×
1632
                return;
×
1633
        }
1634

1635
        // the last case.  It is the slowest, it process the triangles one by one.
1636
        {
1637
                // Project all the triangles of the VertexArray into our buffer arrays.
UNCOV
1638
                VertexArrayProjector result = va.foreachTriangle(VertexArrayProjector(va, this, clippingCap, &polygonVertexArray, textured ? &polygonTextureCoordArray : Q_NULLPTR, colored ? &polygonColorArray : Q_NULLPTR, maxSqDistortion));
×
1639
                result.drawResult();
×
1640
                return;
×
1641
        }
1642
}
1643

1644
// Draw the given SphericalPolygon.
UNCOV
1645
void StelPainter::drawSphericalRegion(SphericalRegion* poly, SphericalPolygonDrawMode drawMode, const SphericalCap* clippingCap, const bool doSubDivise, const double maxSqDistortion, const Vec3d &observerVelocity)
×
1646
{
UNCOV
1647
        if (!prj->getBoundingCap().intersects(poly->getBoundingCap()))
×
1648
                return;
×
1649

UNCOV
1650
        bool oldCullFace = glState.cullFace;
×
1651

UNCOV
1652
        switch (drawMode)
×
1653
        {
UNCOV
1654
                case SphericalPolygonDrawModeBoundary:
×
1655
                        if (doSubDivise || prj->intersectViewportDiscontinuity(poly->getBoundingCap()))
×
1656
                                drawGreatCircleArcs(poly->getOutlineVertexArray(), clippingCap);
×
1657
                        else
UNCOV
1658
                                drawStelVertexArray(poly->getOutlineVertexArray(), false);
×
1659
                        break;
×
1660
                case SphericalPolygonDrawModeFill:
×
1661
                case SphericalPolygonDrawModeTextureFill:
1662
                case SphericalPolygonDrawModeTextureFillColormodulated:
UNCOV
1663
                        setCullFace(true);
×
1664
                        // The polygon is already tessellated as triangles
UNCOV
1665
                        if (doSubDivise || prj->intersectViewportDiscontinuity(poly->getBoundingCap()))
×
1666
                                // flag for color-modulated textured mode (e.g. for Milky Way/extincted)
UNCOV
1667
                                drawSphericalTriangles(poly->getFillVertexArray(observerVelocity), drawMode>=SphericalPolygonDrawModeTextureFill, drawMode==SphericalPolygonDrawModeTextureFillColormodulated, clippingCap, doSubDivise, maxSqDistortion);
×
1668
                        else
UNCOV
1669
                                drawStelVertexArray(poly->getFillVertexArray(observerVelocity), false);
×
1670

UNCOV
1671
                        setCullFace(oldCullFace);
×
1672
                        break;
×
1673
                default:
×
1674
                        Q_ASSERT(0);
×
1675
        }
1676
}
1677

1678

1679
/*************************************************************************
1680
 draw a simple circle, 2d viewport coordinates in pixel
1681
*************************************************************************/
UNCOV
1682
void StelPainter::drawCircle(float x, float y, float r)
×
1683
{
UNCOV
1684
        if (r <= 1.0f)
×
1685
                return;
×
1686
        const Vec2f center(x,y);
×
1687
        const Vec2f v_center(0.5f*prj->viewportXywh[2],0.5f*prj->viewportXywh[3]);
×
1688
        const float R = v_center.norm();
×
1689
        const float d = (v_center-center).norm();
×
1690
        if (d > r+R || d < r-R)
×
1691
                return;
×
1692
        const int segments = 180;
×
1693
        const float phi = 2.0f*M_PIf/segments;
×
1694
        const float cp = std::cos(phi);
×
1695
        const float sp = std::sin(phi);
×
1696
        float dx = r;
×
1697
        float dy = 0;
×
1698
        static QVarLengthArray<Vec3f, 180> circleVertexArray(180);
×
1699

UNCOV
1700
        for (int i=0;i<segments;i++)
×
1701
        {
UNCOV
1702
                circleVertexArray[i].set(x+dx,y+dy,0);
×
1703
                r = dx*cp-dy*sp;
×
1704
                dy = dx*sp+dy*cp;
×
1705
                dx = r;
×
1706
        }
UNCOV
1707
        enableClientStates(true);
×
1708
        setVertexPointer(3, GL_FLOAT, circleVertexArray.data());
×
1709
        drawFromArray(LineLoop, 180, 0, false);
×
1710
        enableClientStates(false);
×
1711
}
1712

1713
// rx: radius in x axis
1714
// ry: radius in y axis
1715
// angle rotation (counterclockwise), radians [0..2pi]
UNCOV
1716
void StelPainter::drawEllipse(double x, double y, double rX, double rY, double angle)
×
1717
{
UNCOV
1718
        if (rX <= 1.0 || rY <= 1.0)
×
1719
                return;
×
1720

1721
        //const float radiusY = 0.35 * size;
1722
        //const float radiusX = aspectRatio * radiusY;
UNCOV
1723
        const int numPoints = std::lround(std::clamp(qMax(rX, rY)/3, 32., 1024.));
×
1724
        std::vector<float> vertexData;
×
1725
        vertexData.reserve(numPoints*2);
×
1726
        const float*const cossin = StelUtils::ComputeCosSinTheta(numPoints);
×
1727
        const auto cosa = std::cos(angle);
×
1728
        const auto sina = std::sin(angle);
×
1729
        for(int n = 0; n < numPoints; ++n)
×
1730
        {
UNCOV
1731
                const auto cosb = cossin[2*n], sinb = cossin[2*n+1];
×
1732
                const auto pointX = rX*sinb;
×
1733
                const auto pointY = rY*cosb;
×
1734
                vertexData.push_back(x + pointX*cosa - pointY*sina);
×
1735
                vertexData.push_back(y + pointY*cosa + pointX*sina);
×
1736
        }
UNCOV
1737
        const auto vertCount = vertexData.size() / 2;
×
1738
        setLineSmooth(true);
×
1739
        setLineWidth(std::clamp(qMax(rX, rY)/40, 1., 2.));
×
1740
        enableClientStates(true);
×
1741
        setVertexPointer(2, GL_FLOAT, vertexData.data());
×
1742
        drawFromArray(StelPainter::LineLoop, vertCount, 0, false);
×
1743
        enableClientStates(false);
×
1744
}
×
1745

UNCOV
1746
void StelPainter::drawSprite2dMode(float x, float y, float radius)
×
1747
{
1748
        static float vertexData[] = {-10.,-10.,10.,-10., 10.,10., -10.,10.};
1749
        static const float texCoordData[] = {0.,0., 1.,0., 0.,1., 1.,1.};
1750
        
1751
        // Takes into account device pixel density and global scale ratio, as we are drawing 2D stuff.
UNCOV
1752
        radius *= static_cast<float>(prj->getDevicePixelsPerPixel());
×
1753
        
UNCOV
1754
        vertexData[0]=x-radius; vertexData[1]=y-radius;
×
1755
        vertexData[2]=x+radius; vertexData[3]=y-radius;
×
1756
        vertexData[4]=x-radius; vertexData[5]=y+radius;
×
1757
        vertexData[6]=x+radius; vertexData[7]=y+radius;
×
1758
        enableClientStates(true, true);
×
1759
        setVertexPointer(2, GL_FLOAT, vertexData);
×
1760
        setTexCoordPointer(2, GL_FLOAT, texCoordData);
×
1761
        drawFromArray(TriangleStrip, 4, 0, false);
×
1762
        enableClientStates(false);
×
1763
}
×
1764

UNCOV
1765
void StelPainter::drawSprite2dMode(const std::vector<Vec2f>& points, float radius)
×
1766
{
UNCOV
1767
        std::vector<Vec2f> texCoordData;
×
1768
        // Each sprite has 2 triangles, each with separate 3 vertices
UNCOV
1769
        texCoordData.reserve(points.size() * (2*3));
×
1770
        for (size_t i = 0; i < points.size(); ++i)
×
1771
        {
UNCOV
1772
                texCoordData.emplace_back(0.f, 0.f);
×
1773
                texCoordData.emplace_back(1.f, 0.f);
×
1774
                texCoordData.emplace_back(0.f, 1.f);
×
1775

UNCOV
1776
                texCoordData.emplace_back(1.f, 0.f);
×
1777
                texCoordData.emplace_back(1.f, 1.f);
×
1778
                texCoordData.emplace_back(0.f, 1.f);
×
1779
        }
1780

1781
        // Takes into account device pixel density and global scale ratio, as we are drawing 2D stuff.
UNCOV
1782
        radius *= static_cast<float>(prj->getDevicePixelsPerPixel());
×
1783

UNCOV
1784
        std::vector<Vec2f> vertexData;
×
1785
        // Each sprite has 2 triangles, each with separate 3 vertices
UNCOV
1786
        vertexData.reserve(points.size() * (2*3));
×
1787
        for (size_t i = 0; i < points.size(); ++i)
×
1788
        {
UNCOV
1789
                vertexData.emplace_back(points[i][0] - radius, points[i][1] - radius);
×
1790
                vertexData.emplace_back(points[i][0] + radius, points[i][1] - radius);
×
1791
                vertexData.emplace_back(points[i][0] - radius, points[i][1] + radius);
×
1792

UNCOV
1793
                vertexData.emplace_back(points[i][0] + radius, points[i][1] - radius);
×
1794
                vertexData.emplace_back(points[i][0] + radius, points[i][1] + radius);
×
1795
                vertexData.emplace_back(points[i][0] - radius, points[i][1] + radius);
×
1796
        }
1797

UNCOV
1798
        enableClientStates(true, true);
×
1799
        setVertexPointer(2, GL_FLOAT, vertexData.data());
×
1800
        setTexCoordPointer(2, GL_FLOAT, texCoordData.data());
×
1801
        drawFromArray(Triangles, points.size() * 6, 0, false);
×
1802
        enableClientStates(false);
×
1803
}
×
1804

UNCOV
1805
void StelPainter::drawSprite2dModeNoDeviceScale(float x, float y, float radius)
×
1806
{
UNCOV
1807
        drawSprite2dMode(x, y, radius/(static_cast<float>(prj->getDevicePixelsPerPixel())));
×
1808
}
×
1809

UNCOV
1810
void StelPainter::drawSprite2dModeNoDeviceScale(const std::vector<Vec2f>& points, float radius)
×
1811
{
UNCOV
1812
        drawSprite2dMode(points, radius/(static_cast<float>(prj->getDevicePixelsPerPixel())));
×
1813
}
×
1814

UNCOV
1815
void StelPainter::drawSprite2dMode(const Vec3d& v, float radius)
×
1816
{
UNCOV
1817
        Vec3d win;
×
1818
        if (prj->project(v, win))
×
1819
                drawSprite2dMode(static_cast<float>(win[0]), static_cast<float>(win[1]), radius);
×
1820
}
×
1821

UNCOV
1822
void StelPainter::drawSprite2dMode(float x, float y, float radius, float rotation)
×
1823
{
1824
        static float vertexData[8];
1825
        static const float texCoordData[] = {0.f,0.f, 1.f,0.f, 0.f,1.f, 1.f,1.f};
1826

1827
        // compute the vertex coordinates applying the translation and the rotation
1828
        static const float vertexBase[] = {-1., -1., 1., -1., -1., 1., 1., 1.};
UNCOV
1829
        const float cosr = std::cos(rotation * M_PI_180f);
×
1830
        const float sinr = std::sin(rotation * M_PI_180f);
×
1831
        
1832
        // Takes into account device pixel density and global scale ratio, as we are drawing 2D stuff.
UNCOV
1833
        radius *= static_cast<float>(prj->getDevicePixelsPerPixel());
×
1834
        
UNCOV
1835
        for (int i = 0; i < 8; i+=2)
×
1836
        {
UNCOV
1837
                vertexData[i] = x + radius * vertexBase[i] * cosr - radius * vertexBase[i+1] * sinr;
×
1838
                vertexData[i+1] = y + radius * vertexBase[i] * sinr + radius * vertexBase[i+1] * cosr;
×
1839
        }
1840

UNCOV
1841
        enableClientStates(true, true);
×
1842
        setVertexPointer(2, GL_FLOAT, vertexData);
×
1843
        setTexCoordPointer(2, GL_FLOAT, texCoordData);
×
1844
        drawFromArray(TriangleStrip, 4, 0, false);
×
1845
        enableClientStates(false);
×
1846
}
×
1847

UNCOV
1848
void StelPainter::drawRect2d(float x, float y, float width, float height, bool textured)
×
1849
{
1850
        static float vertexData[] = {-10.,-10.,10.,-10., 10.,10., -10.,10.};
1851
        static const float texCoordData[] = {0.,0., 1.,0., 0.,1., 1.,1.};
UNCOV
1852
        vertexData[0]=x; vertexData[1]=y;
×
1853
        vertexData[2]=x+width; vertexData[3]=y;
×
1854
        vertexData[4]=x; vertexData[5]=y+height;
×
1855
        vertexData[6]=x+width; vertexData[7]=y+height;
×
1856
        if (textured)
×
1857
        {
UNCOV
1858
                enableClientStates(true, true);
×
1859
                setVertexPointer(2, GL_FLOAT, vertexData);
×
1860
                setTexCoordPointer(2, GL_FLOAT, texCoordData);
×
1861
        }
1862
        else
1863
        {
UNCOV
1864
                enableClientStates(true);
×
1865
                setVertexPointer(2, GL_FLOAT, vertexData);
×
1866
        }
UNCOV
1867
        drawFromArray(TriangleStrip, 4, 0, false);
×
1868
        enableClientStates(false);
×
1869
}
×
1870

1871
/*************************************************************************
1872
 Draw a GL_POINT at the given position
1873
*************************************************************************/
UNCOV
1874
void StelPainter::drawPoint2d(float x, float y)
×
1875
{
1876
        static float vertexData[] = {0.,0.};
UNCOV
1877
        vertexData[0]=x;
×
1878
        vertexData[1]=y;
×
1879

UNCOV
1880
        enableClientStates(true);
×
1881
        setVertexPointer(2, GL_FLOAT, vertexData);
×
1882
        drawFromArray(Points, 1, 0, false);
×
1883
        enableClientStates(false);
×
1884
}
×
1885

1886

1887
/*************************************************************************
1888
 Draw a line between the 2 points.
1889
*************************************************************************/
UNCOV
1890
void StelPainter::drawLine2d(const float x1, const float y1, const float x2, const float y2)
×
1891
{
1892
        static float vertexData[] = {0.,0.,0.,0.};
UNCOV
1893
        vertexData[0]=x1;
×
1894
        vertexData[1]=y1;
×
1895
        vertexData[2]=x2;
×
1896
        vertexData[3]=y2;
×
1897

UNCOV
1898
        enableClientStates(true);
×
1899
        setVertexPointer(2, GL_FLOAT, vertexData);
×
1900
        drawFromArray(Lines, 2, 0, false);
×
1901
        enableClientStates(false);
×
1902
}
×
1903

1904
///////////////////////////////////////////////////////////////////////////
1905
// Drawing methods for general (non-linear) mode.
1906
// This used to draw a full sphere. Since 0.13 it's possible to have a spherical zone only.
UNCOV
1907
void StelPainter::sSphere(const double radius, const double oneMinusOblateness,
×
1908
                          const unsigned int slices, const unsigned int stacks,
1909
                          const bool orientInside, const bool flipTexture, const float topAngle, const float bottomAngle)
1910
{
1911
        double x, y, z;
UNCOV
1912
        GLfloat s=0.f, t=0.f;
×
1913
        GLfloat nsign;
1914

UNCOV
1915
        if (orientInside)
×
1916
        {
UNCOV
1917
                nsign = -1.f;
×
1918
                t=0.f; // from inside texture is reversed
×
1919
        }
1920
        else
1921
        {
UNCOV
1922
                nsign = 1.f;
×
1923
                t=1.f;
×
1924
        }
1925

UNCOV
1926
        const float* cos_sin_rho = Q_NULLPTR;
×
1927
        Q_ASSERT(topAngle<bottomAngle); // don't forget: These are opening angles counted from top.
×
1928
        if ((bottomAngle>3.1415f) && (topAngle<0.0001f)) // safety margin.
×
1929
                cos_sin_rho = StelUtils::ComputeCosSinRho(stacks);
×
1930
        else
1931
        {
UNCOV
1932
                const float drho = (bottomAngle-topAngle) / static_cast<float>(stacks); // deltaRho:  originally just 180degrees/stacks, now the range clamped.
×
1933
                cos_sin_rho = StelUtils::ComputeCosSinRhoZone(drho, stacks, M_PIf-bottomAngle);
×
1934
        }
1935
        // Allow parameters so that pole regions may remain free.
1936
        const float* cos_sin_rho_p;
1937

UNCOV
1938
        const float* cos_sin_theta = StelUtils::ComputeCosSinTheta(slices);
×
1939
        const float *cos_sin_theta_p;
1940

1941
        // texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis
1942
        // t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes)
1943
        // cannot use triangle fan on texturing (s coord. at top/bottom tip varies)
1944
        // If the texture is flipped, we iterate the coordinates backward.
UNCOV
1945
        const GLfloat ds = (flipTexture ? -1.f : 1.f) / static_cast<float>(slices);
×
1946
        const GLfloat dt = nsign / static_cast<float>(stacks); // from inside texture is reversed
×
1947

1948
        // draw intermediate as quad strips
UNCOV
1949
        static QVector<double> vertexArr;
×
1950
        static QVector<float> texCoordArr;
×
1951
        static QVector<float> colorArr;
×
1952
        static QVector<unsigned short> indiceArr;
×
1953

UNCOV
1954
        texCoordArr.resize(0);
×
1955
        vertexArr.resize(0);
×
1956
        colorArr.resize(0);
×
1957
        indiceArr.resize(0);
×
1958

1959
        // LGTM comments: the floats are always <=1. We still prefer float multiplication (with insignificant accuracy loss) for speed.
1960
        uint i, j;
UNCOV
1961
        for (i = 0,cos_sin_rho_p = cos_sin_rho; i < stacks; ++i,cos_sin_rho_p+=2)
×
1962
        {
UNCOV
1963
                s = flipTexture ? 1.f : 0.f;
×
1964
                for (j = 0,cos_sin_theta_p = cos_sin_theta; j<=slices;++j,cos_sin_theta_p+=2)
×
1965
                {
UNCOV
1966
                        x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[1]);
×
1967
                        y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[1]);
×
1968
                        z = static_cast<double>(nsign * cos_sin_rho_p[0]);
×
1969
                        texCoordArr << s << t;
×
1970
                        vertexArr << x * radius << y * radius << z * oneMinusOblateness * radius;
×
1971
                        x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[3]);
×
1972
                        y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[3]);
×
1973
                        z = static_cast<double>(nsign * cos_sin_rho_p[2]);
×
1974
                        texCoordArr << s << t - dt;
×
1975
                        vertexArr << x * radius << y * radius << z * oneMinusOblateness * radius;
×
1976
                        s += ds;
×
1977
                }
UNCOV
1978
                unsigned int offset = i*(slices+1)*2;
×
1979
                for (j = 2;j<slices*2+2;j+=2)
×
1980
                {
UNCOV
1981
                        indiceArr << static_cast<unsigned short>(offset+j-2) << static_cast<unsigned short>(offset+j-1) << static_cast<unsigned short>(offset+j);
×
1982
                        indiceArr << static_cast<unsigned short>(offset+j) << static_cast<unsigned short>(offset+j-1) << static_cast<unsigned short>(offset+j+1);
×
1983
                }
UNCOV
1984
                t -= dt;
×
1985
        }
1986

1987
        // Draw the array now
UNCOV
1988
        setArrays(reinterpret_cast<const Vec3d*>(vertexArr.constData()), reinterpret_cast<const Vec2f*>(texCoordArr.constData()));
×
1989
        drawFromArray(Triangles, indiceArr.size(), 0, true, indiceArr.constData());
×
1990
}
×
1991

UNCOV
1992
StelVertexArray StelPainter::computeSphereNoLight(double radius, double oneMinusOblateness, unsigned int slices, unsigned int stacks,
×
1993
                          int orientInside, bool flipTexture, float topAngle, float bottomAngle)
1994
{
UNCOV
1995
        StelVertexArray result(StelVertexArray::Triangles);
×
1996
        double x, y, z;
UNCOV
1997
        GLfloat s=0.f, t=0.f;
×
1998
        GLuint i, j;
1999
        GLfloat nsign;
UNCOV
2000
        if (orientInside)
×
2001
        {
UNCOV
2002
                nsign = -1.f;
×
2003
                t=0.f; // from inside texture is reversed
×
2004
        }
2005
        else
2006
        {
UNCOV
2007
                nsign = 1.f;
×
2008
                t=1.f;
×
2009
        }
2010

UNCOV
2011
        const float* cos_sin_rho = Q_NULLPTR; //StelUtils::ComputeCosSinRho(stacks);
×
2012
        Q_ASSERT(topAngle<bottomAngle); // don't forget: These are opening angles counted from top.
×
2013
        if ((bottomAngle>3.1415f) && (topAngle<0.0001f)) // safety margin.
×
2014
                cos_sin_rho = StelUtils::ComputeCosSinRho(stacks);
×
2015
        else
2016
        {
UNCOV
2017
                const float drho = (bottomAngle-topAngle) / static_cast<float>(stacks); // deltaRho:  originally just 180degrees/stacks, now the range clamped.
×
2018
                cos_sin_rho = StelUtils::ComputeCosSinRhoZone(drho, stacks, M_PIf-bottomAngle);
×
2019
        }
2020
        // Allow parameters so that pole regions may remain free.
2021
        const float* cos_sin_rho_p;
2022

UNCOV
2023
        const float* cos_sin_theta = StelUtils::ComputeCosSinTheta(slices);
×
2024
        const float *cos_sin_theta_p;
2025

2026
        // texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis
2027
        // t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes)
2028
        // cannot use triangle fan on texturing (s coord. at top/bottom tip varies)
2029
        // If the texture is flipped, we iterate the coordinates backward.
UNCOV
2030
        const GLfloat ds = (flipTexture ? -1.f : 1.f) / static_cast<float>(slices);
×
2031
        const GLfloat dt = nsign / static_cast<float>(stacks); // from inside texture is reversed
×
2032

2033
        // draw intermediate as quad strips
2034
        // LGTM comments: the floats are always <=1. We still prefer float multiplication (with insignificant accuracy loss) for speed.
UNCOV
2035
        for (i = 0,cos_sin_rho_p = cos_sin_rho; i < stacks; ++i,cos_sin_rho_p+=2)
×
2036
        {
UNCOV
2037
                s = !flipTexture ? 0.f : 1.f;
×
2038
                for (j = 0,cos_sin_theta_p = cos_sin_theta; j<=slices;++j,cos_sin_theta_p+=2)
×
2039
                {
UNCOV
2040
                        x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[1]);
×
2041
                        y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[1]);
×
2042
                        z = static_cast<double>(nsign * cos_sin_rho_p[0]);
×
2043
                        result.texCoords << Vec2f(s,t);
×
2044
                        result.vertex << Vec3d(x*radius, y*radius, z*oneMinusOblateness*radius);
×
2045
                        x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[3]);
×
2046
                        y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[3]);
×
2047
                        z = static_cast<double>(nsign * cos_sin_rho_p[2]);
×
2048
                        result.texCoords << Vec2f(s, t-dt);
×
2049
                        result.vertex << Vec3d(x*radius, y*radius, z*oneMinusOblateness*radius);
×
2050
                        s += ds;
×
2051
                }
UNCOV
2052
                unsigned int offset = i*(slices+1)*2;
×
2053
                for (j = 2;j<slices*2+2;j+=2)
×
2054
                {
UNCOV
2055
                        result.indices << static_cast<unsigned short>(offset+j-2) << static_cast<unsigned short>(offset+j-1) << static_cast<unsigned short>(offset+j);
×
2056
                        result.indices << static_cast<unsigned short>(offset+j) << static_cast<unsigned short>(offset+j-1) << static_cast<unsigned short>(offset+j+1);
×
2057
                }
UNCOV
2058
                t -= dt;
×
2059
        }
UNCOV
2060
        return result;
×
2061
}
×
2062

2063
// Reimplementation of gluCylinder : glu is overridden for non standard projection
UNCOV
2064
void StelPainter::sCylinder(double radius, double height, int slices, int orientInside)
×
2065
{
UNCOV
2066
        if (orientInside)
×
2067
                glCullFace(GL_FRONT);
×
2068

UNCOV
2069
        static QVarLengthArray<Vec2f, 512> texCoordArray;
×
2070
        static QVarLengthArray<Vec3d, 512> vertexArray;
×
2071
        texCoordArray.clear();
×
2072
        vertexArray.clear();
×
2073
        float s = 0.f;
×
2074
        const float ds = 1.f / static_cast<float>(slices);
×
UNCOV
2075
        const double da = 2. * M_PI / slices;
×
2076
        for (int i = 0; i <= slices; ++i)
×
2077
        {
2078
                double x = std::sin(da*i);
×
UNCOV
2079
                double y = std::cos(da*i);
×
2080
                texCoordArray.append(Vec2f(s, 0.f));
×
2081
                vertexArray.append(Vec3d(x*radius, y*radius, 0.));
×
2082
                texCoordArray.append(Vec2f(s, 1.f));
×
2083
                vertexArray.append(Vec3d(x*radius, y*radius, height));
×
2084
                s += ds;
×
2085
        }
2086
        setArrays(vertexArray.constData(), texCoordArray.constData());
×
UNCOV
2087
        drawFromArray(TriangleStrip, vertexArray.size());
×
2088

2089
        if (orientInside)
×
UNCOV
2090
                glCullFace(GL_BACK);
×
2091
}
×
2092

2093
void StelPainter::initGLShaders()
×
2094
{
2095
        qInfo() << "Initializing basic GL shaders... ";
×
2096
        // Basic shader: just vertex filled with plain color
2097
        QOpenGLShader vshader3(QOpenGLShader::Vertex);
×
2098
        const auto vsrc3 =
2099
                StelOpenGL::globalShaderPrefix(StelOpenGL::VERTEX_SHADER) +
×
2100
                "ATTRIBUTE mediump vec3 vertex;\n"
2101
                "uniform mediump mat4 projectionMatrix;\n"
2102
                "void main(void)\n"
2103
                "{\n"
2104
                "    gl_Position = projectionMatrix*vec4(vertex, 1.);\n"
UNCOV
2105
                "}\n";
×
UNCOV
2106
        vshader3.compileSourceCode(vsrc3);
×
2107
        if (!vshader3.log().isEmpty())
×
2108
                qWarning().noquote() << "StelPainter: Warnings while compiling vshader3: " << vshader3.log();
×
2109
        QOpenGLShader fshader3(QOpenGLShader::Fragment);
×
2110
        const auto fsrc3 =
2111
                StelOpenGL::globalShaderPrefix(StelOpenGL::FRAGMENT_SHADER) +
×
2112
                "uniform mediump vec4 color;\n"
2113
                "void main(void)\n"
2114
                "{\n"
2115
                "    FRAG_COLOR = color;\n"
UNCOV
2116
                "}\n";
×
UNCOV
2117
        fshader3.compileSourceCode(fsrc3);
×
2118
        if (!fshader3.log().isEmpty())
×
2119
                qWarning().noquote() << "StelPainter: Warnings while compiling fshader3: " << fshader3.log();
×
2120
        basicShaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
×
2121
        basicShaderProgram->addShader(&vshader3);
×
2122
        basicShaderProgram->addShader(&fshader3);
×
2123
        linkProg(basicShaderProgram, "basicShaderProgram");
×
2124
        basicShaderVars.projectionMatrix = basicShaderProgram->uniformLocation("projectionMatrix");
×
2125
        basicShaderVars.color = basicShaderProgram->uniformLocation("color");
×
2126
        basicShaderVars.vertex = basicShaderProgram->attributeLocation("vertex");
×
2127
        
2128

2129
        // Basic shader: vertex filled with interpolated color
UNCOV
2130
        QOpenGLShader vshaderInterpolatedColor(QOpenGLShader::Vertex);
×
2131
        const auto vshaderInterpolatedColorSrc =
2132
                StelOpenGL::globalShaderPrefix(StelOpenGL::VERTEX_SHADER) +
×
2133
                "ATTRIBUTE mediump vec3 vertex;\n"
2134
                "ATTRIBUTE mediump vec4 color;\n"
2135
                "uniform mediump mat4 projectionMatrix;\n"
2136
                "VARYING mediump vec4 fragcolor;\n"
2137
                "void main(void)\n"
2138
                "{\n"
2139
                "    gl_Position = projectionMatrix*vec4(vertex, 1.);\n"
2140
                "    fragcolor = color;\n"
UNCOV
2141
                "}\n";
×
UNCOV
2142
        vshaderInterpolatedColor.compileSourceCode(vshaderInterpolatedColorSrc);
×
2143
        if (!vshaderInterpolatedColor.log().isEmpty()) {
×
2144
          qWarning().noquote() << "StelPainter: Warnings while compiling vshaderInterpolatedColor: " << vshaderInterpolatedColor.log();
×
2145
        }
2146
        QOpenGLShader fshaderInterpolatedColor(QOpenGLShader::Fragment);
×
2147
        const auto fshaderInterpolatedColorSrc =
2148
                StelOpenGL::globalShaderPrefix(StelOpenGL::FRAGMENT_SHADER) +
×
2149
                "VARYING mediump vec4 fragcolor;\n"
2150
                "void main(void)\n"
2151
                "{\n"
2152
                "    FRAG_COLOR = fragcolor;\n"
UNCOV
2153
                "}\n";
×
UNCOV
2154
        fshaderInterpolatedColor.compileSourceCode(fshaderInterpolatedColorSrc);
×
2155
        if (!fshaderInterpolatedColor.log().isEmpty()) {
×
2156
          qWarning().noquote() << "StelPainter: Warnings while compiling fshaderInterpolatedColor: " << fshaderInterpolatedColor.log();
×
2157
        }
2158
        colorShaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
×
UNCOV
2159
        colorShaderProgram->addShader(&vshaderInterpolatedColor);
×
2160
        colorShaderProgram->addShader(&fshaderInterpolatedColor);
×
2161
        linkProg(colorShaderProgram, "colorShaderProgram");
×
2162
        colorShaderVars.projectionMatrix = colorShaderProgram->uniformLocation("projectionMatrix");
×
2163
        colorShaderVars.color = colorShaderProgram->attributeLocation("color");
×
2164
        colorShaderVars.vertex = colorShaderProgram->attributeLocation("vertex");
×
2165
        
2166
        // Text shader program
UNCOV
2167
        QOpenGLShader textVShader(QOpenGLShader::Vertex);
×
2168
        const auto textVSrc =
2169
                StelOpenGL::globalShaderPrefix(StelOpenGL::VERTEX_SHADER) + R"(
×
2170
ATTRIBUTE highp vec3 vertex;
2171
ATTRIBUTE mediump vec2 texCoord;
2172
uniform mediump mat4 projectionMatrix;
2173
VARYING mediump vec2 texc;
2174
void main()
2175
{
2176
        gl_Position = projectionMatrix * vec4(vertex, 1.);
2177
        texc = texCoord;
2178
})";
UNCOV
2179
        textVShader.compileSourceCode(textVSrc);
×
UNCOV
2180
        if (!textVShader.log().isEmpty())
×
2181
                qWarning().noquote() << "StelPainter: Warnings while compiling text vertex shader: " << textVShader.log();
×
2182

2183
        QOpenGLShader textFShader(QOpenGLShader::Fragment);
×
2184
        const auto textFSrc =
2185
                StelOpenGL::globalShaderPrefix(StelOpenGL::FRAGMENT_SHADER) + R"(
×
2186
VARYING mediump vec2 texc;
2187
uniform sampler2D tex;
2188
uniform mediump vec4 textColor;
2189
void main()
2190
{
2191
        float mask = texture2D(tex, texc).r;
2192
        FRAG_COLOR = vec4(textColor.rgb, textColor.a*mask);
2193
})";
UNCOV
2194
        textFShader.compileSourceCode(textFSrc);
×
UNCOV
2195
        if (!textFShader.log().isEmpty())
×
2196
                qWarning().noquote() << "StelPainter: Warnings while compiling text fragment shader: " << textFShader.log();
×
2197

2198
        textShaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
×
UNCOV
2199
        textShaderProgram->addShader(&textVShader);
×
2200
        textShaderProgram->addShader(&textFShader);
×
2201
        linkProg(textShaderProgram, "textShaderProgram");
×
2202
        textShaderVars.projectionMatrix = textShaderProgram->uniformLocation("projectionMatrix");
×
2203
        textShaderVars.texCoord = textShaderProgram->attributeLocation("texCoord");
×
2204
        textShaderVars.vertex = textShaderProgram->attributeLocation("vertex");
×
2205
        textShaderVars.textColor = textShaderProgram->uniformLocation("textColor");
×
2206
        textShaderVars.texture = textShaderProgram->uniformLocation("tex");
×
2207

2208
        // Basic texture shader program
UNCOV
2209
        QOpenGLShader vshader2(QOpenGLShader::Vertex);
×
2210
        const auto vsrc2 =
2211
                StelOpenGL::globalShaderPrefix(StelOpenGL::VERTEX_SHADER) +
×
2212
                "ATTRIBUTE highp vec3 vertex;\n"
2213
                "ATTRIBUTE mediump vec2 texCoord;\n"
2214
                "uniform mediump mat4 projectionMatrix;\n"
2215
                "VARYING mediump vec2 texc;\n"
2216
                "void main(void)\n"
2217
                "{\n"
2218
                "    gl_Position = projectionMatrix * vec4(vertex, 1.);\n"
2219
                "    texc = texCoord;\n"
UNCOV
2220
                "}\n";
×
UNCOV
2221
        vshader2.compileSourceCode(vsrc2);
×
2222
        if (!vshader2.log().isEmpty())
×
2223
                qWarning().noquote() << "StelPainter: Warnings while compiling vshader2: " << vshader2.log();
×
2224

2225
        QOpenGLShader fshader2(QOpenGLShader::Fragment);
×
2226
        const auto fsrc2 =
2227
                StelOpenGL::globalShaderPrefix(StelOpenGL::FRAGMENT_SHADER) +
×
2228
                "VARYING mediump vec2 texc;\n"
2229
                "uniform sampler2D tex;\n"
2230
                "uniform mediump vec4 texColor;\n"
2231
                "void main(void)\n"
2232
                "{\n"
2233
                "    FRAG_COLOR = texture2D(tex, texc)*texColor;\n"
UNCOV
2234
                "}\n";
×
UNCOV
2235
        fshader2.compileSourceCode(fsrc2);
×
2236
        if (!fshader2.log().isEmpty())
×
2237
                qWarning().noquote() << "StelPainter: Warnings while compiling fshader2: " << fshader2.log();
×
2238

2239
        texturesShaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
×
UNCOV
2240
        texturesShaderProgram->addShader(&vshader2);
×
2241
        texturesShaderProgram->addShader(&fshader2);
×
2242
        linkProg(texturesShaderProgram, "texturesShaderProgram");
×
2243
        texturesShaderVars.projectionMatrix = texturesShaderProgram->uniformLocation("projectionMatrix");
×
2244
        texturesShaderVars.texCoord = texturesShaderProgram->attributeLocation("texCoord");
×
2245
        texturesShaderVars.vertex = texturesShaderProgram->attributeLocation("vertex");
×
2246
        texturesShaderVars.texColor = texturesShaderProgram->uniformLocation("texColor");
×
2247
        texturesShaderVars.texture = texturesShaderProgram->uniformLocation("tex");
×
2248

2249
        // Texture shader program + interpolated color per vertex
UNCOV
2250
        QOpenGLShader vshader4(QOpenGLShader::Vertex);
×
2251
        const auto vsrc4 =
2252
                StelOpenGL::globalShaderPrefix(StelOpenGL::VERTEX_SHADER) +
×
2253
                "ATTRIBUTE highp vec3 vertex;\n"
2254
                "ATTRIBUTE mediump vec2 texCoord;\n"
2255
                "ATTRIBUTE mediump vec4 color;\n"
2256
                "uniform mediump mat4 projectionMatrix;\n"
2257
                "VARYING mediump vec2 texc;\n"
2258
                "VARYING mediump vec4 outColor;\n"
2259
                "void main(void)\n"
2260
                "{\n"
2261
                "    gl_Position = projectionMatrix * vec4(vertex, 1.);\n"
2262
                "    texc = texCoord;\n"
2263
                "    outColor = color;\n"
UNCOV
2264
                "}\n";
×
UNCOV
2265
        vshader4.compileSourceCode(vsrc4);
×
2266
        if (!vshader4.log().isEmpty())
×
2267
                qWarning().noquote() << "StelPainter: Warnings while compiling vshader4: " << vshader4.log();
×
2268

2269
        QOpenGLShader fshader4(QOpenGLShader::Fragment);
×
2270
        const auto fsrc4 =
2271
                StelOpenGL::globalShaderPrefix(StelOpenGL::FRAGMENT_SHADER) +
×
UNCOV
2272
                makeSaturationShader()+
×
2273
                "VARYING mediump vec2 texc;\n"
2274
                "VARYING mediump vec4 outColor;\n"
2275
                "uniform sampler2D tex;\n"
2276
                "uniform lowp float saturation;\n"
2277
                "void main(void)\n"
2278
                "{\n"
2279
                "    FRAG_COLOR = texture2D(tex, texc)*outColor;\n"
2280
                "    if (saturation != 1.0)\n"
2281
                "        FRAG_COLOR.rgb = saturate(FRAG_COLOR.rgb, saturation);\n"
UNCOV
2282
                "}\n";
×
UNCOV
2283
        fshader4.compileSourceCode(fsrc4);
×
2284
        if (!fshader4.log().isEmpty())
×
2285
                qWarning().noquote() << "StelPainter: Warnings while compiling fshader4: " << fshader4.log();
×
2286

2287
        texturesColorShaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
×
UNCOV
2288
        texturesColorShaderProgram->addShader(&vshader4);
×
2289
        texturesColorShaderProgram->addShader(&fshader4);
×
2290
        linkProg(texturesColorShaderProgram, "texturesColorShaderProgram");
×
2291
        texturesColorShaderVars.projectionMatrix = texturesColorShaderProgram->uniformLocation("projectionMatrix");
×
2292
        texturesColorShaderVars.texCoord = texturesColorShaderProgram->attributeLocation("texCoord");
×
2293
        texturesColorShaderVars.vertex = texturesColorShaderProgram->attributeLocation("vertex");
×
2294
        texturesColorShaderVars.color = texturesColorShaderProgram->attributeLocation("color");
×
2295
        texturesColorShaderVars.texture = texturesColorShaderProgram->uniformLocation("tex");
×
2296
        texturesColorShaderVars.saturation = texturesColorShaderProgram->uniformLocation("saturation");
×
2297

2298
        if(StelMainView::getInstance().getGLInformation().isCoreProfile)
×
2299
        {
2300
                // In Core profile wide lines (width>1px) are not supported, so this shader is used to render them with triangles.
2301

2302
                // Common template for the geometry shader
UNCOV
2303
                const QByteArray geomSrc = 1+R"(
×
2304
#version 330
2305

2306
layout(lines) in;
2307
layout(triangle_strip, max_vertices=4) out;
2308
uniform vec2 viewportSize;
2309
uniform float lineWidth;
2310
#if @VARYING_COLOR@
2311
in vec4 varyingColor[2];
2312
out vec4 geomColor;
2313
#endif
2314

2315
void main()
2316
{
2317
        vec4 clip0 = gl_in[0].gl_Position;
2318
        vec4 clip1 = gl_in[1].gl_Position;
2319

2320
        vec3 ndc0 = clip0.xyz / clip0.w;
2321
        vec3 ndc1 = clip1.xyz / clip1.w;
2322

2323
        vec2 lineDir2d = normalize((ndc1.xy - ndc0.xy) * viewportSize);
2324
        vec2 perpendicularDir2d = vec2(-lineDir2d.y, lineDir2d.x);
2325

2326
        vec2 offset2d = (lineWidth / viewportSize) * perpendicularDir2d;
2327

2328
        gl_Position = vec4(clip0.xy + offset2d*clip0.w, clip0.zw);
2329
#if @VARYING_COLOR@
2330
        geomColor = varyingColor[0];
2331
#endif
2332
        EmitVertex();
2333

2334
        gl_Position = vec4(clip0.xy - offset2d*clip0.w, clip0.zw);
2335
#if @VARYING_COLOR@
2336
        geomColor = varyingColor[0];
2337
#endif
2338
        EmitVertex();
2339

2340
        gl_Position = vec4(clip1.xy + offset2d*clip1.w, clip1.zw);
2341
#if @VARYING_COLOR@
2342
        geomColor = varyingColor[1];
2343
#endif
2344
        EmitVertex();
2345

2346
        gl_Position = vec4(clip1.xy - offset2d*clip1.w, clip1.zw);
2347
#if @VARYING_COLOR@
2348
        geomColor = varyingColor[1];
2349
#endif
2350
        EmitVertex();
2351

2352
        EndPrimitive();
2353
}
2354
)";
2355

2356
                // First a basic shader using constant line color
UNCOV
2357
                QOpenGLShader wideLineVertShader(QOpenGLShader::Vertex);
×
UNCOV
2358
                wideLineVertShader.compileSourceCode(1+R"(
×
2359
#version 330
2360
in vec3 vertex;
2361
uniform mat4 projectionMatrix;
2362
void main()
2363
{
2364
    gl_Position = projectionMatrix*vec4(vertex, 1.);
2365
}
2366
)");
UNCOV
2367
                if (!wideLineVertShader.log().isEmpty())
×
UNCOV
2368
                        qWarning().noquote() << "StelPainter: Warnings while compiling wide line vertex shader: " << wideLineVertShader.log();
×
2369

2370
                QOpenGLShader wideLineGeomShader(QOpenGLShader::Geometry);
×
UNCOV
2371
                wideLineGeomShader.compileSourceCode(QByteArray(geomSrc).replace("@VARYING_COLOR@","0"));
×
2372
                if (!wideLineGeomShader.log().isEmpty())
×
2373
                        qWarning().noquote() << "StelPainter: Warnings while compiling wide line geometry shader: " << wideLineGeomShader.log();
×
2374

2375
                QOpenGLShader wideLineFragShader(QOpenGLShader::Fragment);
×
UNCOV
2376
                wideLineFragShader.compileSourceCode(1+R"(
×
2377
#version 330
2378
uniform vec4 color;
2379
out vec4 outputColor;
2380
void main()
2381
{
2382
    outputColor = color;
2383
}
2384
)");
UNCOV
2385
                if (!wideLineFragShader.log().isEmpty())
×
UNCOV
2386
                        qWarning().noquote() << "StelPainter: Warnings while compiling wide line fragment shader: " << wideLineFragShader.log();
×
2387
                wideLineShaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
×
2388
                wideLineShaderProgram->addShader(&wideLineVertShader);
×
2389
                wideLineShaderProgram->addShader(&wideLineGeomShader);
×
2390
                wideLineShaderProgram->addShader(&wideLineFragShader);
×
2391
                linkProg(wideLineShaderProgram, "wideLineShaderProgram");
×
2392
                wideLineShaderVars.projectionMatrix = wideLineShaderProgram->uniformLocation("projectionMatrix");
×
2393
                wideLineShaderVars.viewportSize     = wideLineShaderProgram->uniformLocation("viewportSize");
×
2394
                wideLineShaderVars.lineWidth        = wideLineShaderProgram->uniformLocation("lineWidth");
×
2395
                wideLineShaderVars.color            = wideLineShaderProgram->uniformLocation("color");
×
2396
                wideLineShaderVars.vertex = wideLineShaderProgram->attributeLocation("vertex");
×
2397

2398
                // Now a version of the shader that supports color interpolated along the line
UNCOV
2399
                QOpenGLShader colorfulWideLineVertShader(QOpenGLShader::Vertex);
×
UNCOV
2400
                colorfulWideLineVertShader.compileSourceCode(1+R"(
×
2401
#version 330
2402
in vec3 vertex;
2403
in vec4 color;
2404
out vec4 varyingColor;
2405
uniform mat4 projectionMatrix;
2406
void main()
2407
{
2408
    gl_Position = projectionMatrix*vec4(vertex, 1.);
2409
        varyingColor = color;
2410
}
2411
)");
UNCOV
2412
                if (!colorfulWideLineVertShader.log().isEmpty())
×
UNCOV
2413
                        qWarning().noquote() << "StelPainter: Warnings while compiling colorful wide line vertex shader: " << colorfulWideLineVertShader.log();
×
2414

2415
                QOpenGLShader colorfulWideLineGeomShader(QOpenGLShader::Geometry);
×
UNCOV
2416
                colorfulWideLineGeomShader.compileSourceCode(QByteArray(geomSrc).replace("@VARYING_COLOR@","1"));
×
2417
                if (!colorfulWideLineGeomShader.log().isEmpty())
×
2418
                        qWarning().noquote() << "StelPainter: Warnings while compiling colorful wide line geometry shader: " << colorfulWideLineGeomShader.log();
×
2419

2420
                QOpenGLShader colorfulWideLineFragShader(QOpenGLShader::Fragment);
×
UNCOV
2421
                colorfulWideLineFragShader.compileSourceCode(1+R"(
×
2422
#version 330
2423
in vec4 geomColor;
2424
out vec4 outputColor;
2425
void main()
2426
{
2427
    outputColor = geomColor;
2428
}
2429
)");
UNCOV
2430
                if (!colorfulWideLineFragShader.log().isEmpty())
×
UNCOV
2431
                        qWarning().noquote() << "StelPainter: Warnings while compiling colorful wide line fragment shader: " << colorfulWideLineFragShader.log();
×
2432
                colorfulWideLineShaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
×
2433
                colorfulWideLineShaderProgram->addShader(&colorfulWideLineVertShader);
×
2434
                colorfulWideLineShaderProgram->addShader(&colorfulWideLineGeomShader);
×
2435
                colorfulWideLineShaderProgram->addShader(&colorfulWideLineFragShader);
×
2436
                linkProg(colorfulWideLineShaderProgram, "colorfulWideLineShaderProgram");
×
2437
                colorfulWideLineShaderVars.projectionMatrix = colorfulWideLineShaderProgram->uniformLocation("projectionMatrix");
×
2438
                colorfulWideLineShaderVars.viewportSize     = colorfulWideLineShaderProgram->uniformLocation("viewportSize");
×
2439
                colorfulWideLineShaderVars.lineWidth        = colorfulWideLineShaderProgram->uniformLocation("lineWidth");
×
2440
                colorfulWideLineShaderVars.color  = colorfulWideLineShaderProgram->attributeLocation("color");
×
2441
                colorfulWideLineShaderVars.vertex = colorfulWideLineShaderProgram->attributeLocation("vertex");
×
2442
        }
×
2443

2444
        multisamplingEnabled = StelApp::getInstance().getSettings()->value("video/multisampling", 0).toUInt() != 0;
×
UNCOV
2445
}
×
2446

2447

UNCOV
2448
void StelPainter::deinitGLShaders()
×
2449
{
2450
        delete basicShaderProgram;
×
UNCOV
2451
        basicShaderProgram = Q_NULLPTR;
×
2452
        delete colorShaderProgram;
×
2453
        colorShaderProgram = Q_NULLPTR;
×
2454
        delete texturesShaderProgram;
×
2455
        texturesShaderProgram = Q_NULLPTR;
×
2456
        delete texturesColorShaderProgram;
×
2457
        texturesColorShaderProgram = Q_NULLPTR;
×
2458
        delete wideLineShaderProgram;
×
2459
        wideLineShaderProgram = Q_NULLPTR;
×
2460
        delete colorfulWideLineShaderProgram;
×
2461
        colorfulWideLineShaderProgram = Q_NULLPTR;
×
2462
        texCache.clear();
×
2463
}
×
2464

2465

UNCOV
2466
void StelPainter::setArrays(const Vec3d* vertices, const Vec2f* texCoords, const Vec3f* colorArray, const Vec3f* normalArray)
×
2467
{
2468
        enableClientStates(vertices, texCoords, colorArray, normalArray);
×
UNCOV
2469
        setVertexPointer(3, GL_DOUBLE, vertices);
×
2470
        setTexCoordPointer(2, GL_FLOAT, texCoords);
×
2471
        setColorPointer(3, GL_FLOAT, colorArray);
×
2472
        setNormalPointer(GL_FLOAT, normalArray);
×
2473
}
×
2474

2475
void StelPainter::setArrays(const Vec3f* vertices, const Vec2f* texCoords, const Vec3f* colorArray, const Vec3f* normalArray)
×
2476
{
2477
        enableClientStates(vertices, texCoords, colorArray, normalArray);
×
UNCOV
2478
        setVertexPointer(3, GL_FLOAT, vertices);
×
2479
        setTexCoordPointer(2, GL_FLOAT, texCoords);
×
2480
        setColorPointer(3, GL_FLOAT, colorArray);
×
2481
        setNormalPointer(GL_FLOAT, normalArray);
×
2482
}
×
2483

2484
void StelPainter::enableClientStates(bool vertex, bool texture, bool color, bool normal)
×
2485
{
2486
        vertexArray.enabled = vertex;
×
UNCOV
2487
        texCoordArray.enabled = texture;
×
2488
        colorArray.enabled = color;
×
2489
        normalArray.enabled = normal;
×
2490
}
×
2491

2492
void StelPainter::drawFixedColorWideLinesAsQuads(const ArrayDesc& vertexArray, int count, int offset,
×
2493
                                                                                                         const Mat4f& projMat, const DrawingMode mode)
2494
{
UNCOV
2495
        if(count < 2) return;
×
2496

2497
        GLint viewport[4] = {};
×
UNCOV
2498
        glGetIntegerv(GL_VIEWPORT, viewport);
×
2499
        const auto viewportSize = Vec2f(viewport[2], viewport[3]);
×
2500

2501
        std::vector<Vec4f> newVertices;
×
UNCOV
2502
        if(mode == LineStrip)
×
2503
                newVertices.reserve((count-1)*6);
×
2504
        else
2505
                newVertices.reserve(count*6);
×
UNCOV
2506
        newVertices.resize((count-1)*6);
×
2507
        Q_ASSERT(vertexArray.type == GL_FLOAT);
×
2508
        const auto lineVertData = static_cast<const float*>(vertexArray.pointer) + vertexArray.size*offset;
×
2509
        const int step = mode==Lines ? 2 : 1;
×
2510
        const bool connected = mode!=Lines;
×
2511
        Vec2f prevV1aNDC, prevV1bNDC, prevLineDirNDC;
×
2512
        for(int n = 0; n < count-1; n += step)
×
2513
        {
2514
                Vec4f in0;
×
UNCOV
2515
                Vec4f in1;
×
2516
                switch(vertexArray.size)
×
2517
                {
2518
                case 4:
×
UNCOV
2519
                        in0 = Vec4f(lineVertData+4*(n+0));
×
2520
                        in1 = Vec4f(lineVertData+4*(n+1));
×
2521
                        break;
×
2522
                case 3:
×
2523
                        in0 = Vec4f(lineVertData[3*(n+0)], lineVertData[3*(n+0)+1], lineVertData[3*(n+0)+2], 1);
×
2524
                        in1 = Vec4f(lineVertData[3*(n+1)], lineVertData[3*(n+1)+1], lineVertData[3*(n+1)+2], 1);
×
2525
                        break;
×
2526
                case 2:
×
2527
                        in0 = Vec4f(lineVertData[2*(n+0)], lineVertData[2*(n+0)+1], 0, 1);
×
2528
                        in1 = Vec4f(lineVertData[2*(n+1)], lineVertData[2*(n+1)+1], 0, 1);
×
2529
                        break;
×
2530
                case 1:
×
2531
                        in0 = Vec4f(lineVertData[n+0], 0,0,1);
×
2532
                        in1 = Vec4f(lineVertData[n+1], 0,0,1);
×
2533
                        break;
×
2534
                default:
×
2535
                        in0 = Vec4f(0.f);
×
2536
                        in1 = Vec4f(0.f);
×
2537
                        qCritical("Bad number of elements in vertex array"); // or qFatal()?
×
2538
                }
2539

UNCOV
2540
                const Vec4f clip0 = projMat * Vec4f(in0);
×
UNCOV
2541
                const Vec4f clip1 = projMat * Vec4f(in1);
×
2542

2543
                const Vec3f ndc0 = Vec3f(clip0.v) / clip0[3];
×
UNCOV
2544
                const Vec3f ndc1 = Vec3f(clip1.v) / clip1[3];
×
2545

2546
                const Vec2f lineDirNDC = normalize(Vec2f(ndc1.v) - Vec2f(ndc0.v));
×
UNCOV
2547
                const Vec2f lineDir2d = normalize(lineDirNDC * viewportSize);
×
2548
                const Vec2f perpendicularDir2d = Vec2f(-lineDir2d[1], lineDir2d[0]);
×
2549

2550
                const Vec2f offset2d = (Vec2f(glState.lineWidth) / viewportSize) * perpendicularDir2d;
×
2551

2552
                // 2D NDC of the new pairs (a,b) of vertices for each input vertex (0,1)
UNCOV
2553
                      Vec2f v0aNDC = Vec2f(ndc0.v) + offset2d;
×
UNCOV
2554
                      Vec2f v0bNDC = Vec2f(ndc0.v) - offset2d;
×
2555
                const Vec2f v1aNDC = Vec2f(ndc1.v) + offset2d;
×
2556
                const Vec2f v1bNDC = Vec2f(ndc1.v) - offset2d;
×
2557

2558
                if(connected && n > 0) // at n==0 we initialize prevV1{a,b}XY
×
2559
                {
2560
                        const auto processLineSide = [lineDirNDC,prevLineDirNDC](const Vec2f& prevV1, Vec2f& currV0) -> bool
×
2561
                        {
2562
                                // Find prevT and currT for which prevV1+prevLineDirNDC*prevT==currV0+lineDirNDC*currT.
2563
                                // This will define the intersection point of the line side.
UNCOV
2564
                                const float prevT = (lineDirNDC[1]*(prevV1[0]-currV0[0]) + lineDirNDC[0]*(currV0[1]-prevV1[1]))
×
2565
                                                                                               /
2566
                                                               (lineDirNDC[0]*prevLineDirNDC[1] - lineDirNDC[1]*prevLineDirNDC[0]);
×
UNCOV
2567
                                const float currT = (prevLineDirNDC[1]*(prevV1[0]-currV0[0]) + prevLineDirNDC[0]*(currV0[1]-prevV1[1]))
×
2568
                                                                                               /
2569
                                                               (lineDirNDC[0]*prevLineDirNDC[1] - lineDirNDC[1]*prevLineDirNDC[0]);
×
UNCOV
2570
                                if(prevT > 0 && currT < 0)
×
2571
                                {
2572
                                        // This is the outer corner of a joint. Extrapolate beginning
2573
                                        // of the current line back to the intersection.
UNCOV
2574
                                        currV0 += lineDirNDC*currT;
×
UNCOV
2575
                                        return true;
×
2576
                                }
2577
                                return false;
×
UNCOV
2578
                        };
×
2579

2580
                        // Process one side of the line (we call it A)
UNCOV
2581
                        if(processLineSide(prevV1aNDC, v0aNDC))
×
2582
                        {
2583
                                // Extrapolate the previous line end forward to the intersection
UNCOV
2584
                                newVertices[6*(n-1)+2][0] = v0aNDC[0]*clip0[3];
×
UNCOV
2585
                                newVertices[6*(n-1)+2][1] = v0aNDC[1]*clip0[3];
×
2586
                                newVertices[6*(n-1)+3][0] = v0aNDC[0]*clip0[3];
×
2587
                                newVertices[6*(n-1)+3][1] = v0aNDC[1]*clip0[3];
×
2588
                        }
2589

2590
                        // Process the second side of the line (we call it B)
UNCOV
2591
                        if(processLineSide(prevV1bNDC, v0bNDC))
×
2592
                        {
2593
                                // Extrapolate the previous line end forward to the intersection
UNCOV
2594
                                newVertices[6*(n-1)+5][0] = v0bNDC[0]*clip0[3];
×
UNCOV
2595
                                newVertices[6*(n-1)+5][1] = v0bNDC[1]*clip0[3];
×
2596
                        }
2597
                }
UNCOV
2598
                prevV1aNDC = v1aNDC;
×
UNCOV
2599
                prevV1bNDC = v1bNDC;
×
2600
                prevLineDirNDC = lineDirNDC;
×
2601

2602
                // 2D clip space coordinates of the new pairs (a,b) of vertices for each input vertex (0,1)
UNCOV
2603
                const Vec2f v0a_xy = v0aNDC*clip0[3];
×
UNCOV
2604
                const Vec2f v0b_xy = v0bNDC*clip0[3];
×
2605
                const Vec2f v1a_xy = v1aNDC*clip1[3];
×
2606
                const Vec2f v1b_xy = v1bNDC*clip1[3];
×
2607

2608
                // Final 4D coordinates of the new vertices
UNCOV
2609
                const auto v0a = Vec4f(v0a_xy[0], v0a_xy[1], clip0[2], clip0[3]);
×
UNCOV
2610
                const auto v0b = Vec4f(v0b_xy[0], v0b_xy[1], clip0[2], clip0[3]);
×
2611
                const auto v1a = Vec4f(v1a_xy[0], v1a_xy[1], clip1[2], clip1[3]);
×
2612
                const auto v1b = Vec4f(v1b_xy[0], v1b_xy[1], clip1[2], clip1[3]);
×
2613

2614
                newVertices[6*n+0] = v0a;
×
UNCOV
2615
                newVertices[6*n+1] = v0b;
×
2616
                newVertices[6*n+2] = v1a;
×
2617

2618
                newVertices[6*n+3] = v1a;
×
UNCOV
2619
                newVertices[6*n+4] = v0b;
×
2620
                newVertices[6*n+5] = v1b;
×
2621
        }
2622
        if(mode == LineLoop)
×
2623
        {
2624
                // Connect the ends
UNCOV
2625
                const auto lastN = newVertices.size()-1;
×
UNCOV
2626
                Q_ASSERT(lastN >= 2);
×
2627
                newVertices.push_back(newVertices[lastN-2]);
×
2628
                newVertices.push_back(newVertices[lastN]);
×
2629
                newVertices.push_back(newVertices[0]);
×
2630

2631
                newVertices.push_back(newVertices[0]);
×
UNCOV
2632
                newVertices.push_back(newVertices[lastN]);
×
2633
                newVertices.push_back(newVertices[1]);
×
2634
        }
2635
        vao->bind();
×
UNCOV
2636
        verticesVBO->bind();
×
2637

2638
        verticesVBO->allocate(newVertices.data(), newVertices.size() * sizeof newVertices[0]);
×
2639

2640
        const auto& pr = basicShaderProgram;
×
UNCOV
2641
        pr->bind();
×
2642
        pr->setAttributeBuffer(basicShaderVars.vertex, vertexArray.type, 0, 4);
×
2643
        pr->enableAttributeArray(basicShaderVars.vertex);
×
2644
        pr->setUniformValue(basicShaderVars.projectionMatrix, QMatrix4x4{});
×
2645
        pr->setUniformValue(basicShaderVars.color, currentColor.toQVector());
×
2646

2647
#ifdef GL_MULTISAMPLE
UNCOV
2648
        const bool multisampleWasOn = multisamplingEnabled && glIsEnabled(GL_MULTISAMPLE);
×
UNCOV
2649
        if(multisamplingEnabled && glState.lineSmooth)
×
2650
                glEnable(GL_MULTISAMPLE);
×
2651
#endif
2652

2653
        // TODO: convert this to a triangle strip, but mind the overlaps and gaps of non-parallel lines' corners
UNCOV
2654
        glDrawArrays(GL_TRIANGLES, 0, newVertices.size());
×
2655

2656
        verticesVBO->release();
×
UNCOV
2657
        vao->release();
×
2658

2659
        if (pr) pr->release();
×
2660

2661
#ifdef GL_MULTISAMPLE
UNCOV
2662
        if(multisamplingEnabled && !multisampleWasOn && glState.lineSmooth)
×
UNCOV
2663
                glDisable(GL_MULTISAMPLE);
×
2664
#endif
2665
}
×
2666

2667
void StelPainter::drawFromArray(DrawingMode mode, int count, int offset, bool doProj, const unsigned short* indices)
×
2668
{
2669
        if(!count) return;
×
2670

2671
        ArrayDesc projectedVertexArray = vertexArray;
×
UNCOV
2672
        if (doProj)
×
2673
        {
2674
                // Project the vertex array using current projection
UNCOV
2675
                if (indices)
×
UNCOV
2676
                        projectedVertexArray = projectArray(vertexArray, 0, count, indices + offset);
×
2677
                else
2678
                        projectedVertexArray = projectArray(vertexArray, offset, count, Q_NULLPTR);
×
2679
        }
2680

UNCOV
2681
        QOpenGLShaderProgram* pr=Q_NULLPTR;
×
2682

2683
        const Mat4f& m = getProjector()->getProjectionMatrix();
×
UNCOV
2684
        const QMatrix4x4 qMat(m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]);
×
2685

2686
        const bool lineMode = mode==LineStrip || mode==LineLoop || mode==Lines;
×
UNCOV
2687
        const bool isCoreProfile = StelMainView::getInstance().getGLInformation().isCoreProfile;
×
2688
        const bool isGLES = StelMainView::getInstance().getGLInformation().isGLES;
×
2689
        const bool wideLineMode = lineMode && glState.lineWidth>1;
×
2690
        if(wideLineMode && (isCoreProfile || isGLES) && projectedVertexArray.type == GL_FLOAT)
×
2691
        {
2692
                const bool fixedColor = !texCoordArray.enabled && !colorArray.enabled && !normalArray.enabled;
×
UNCOV
2693
                if(fixedColor && !indices)
×
2694
                {
2695
                        // Optimized version that doesn't use geometry shader, which appears to be slow on some GPUs.
2696
                        // Ideally, we'd like to get rid of the geometry shader completely,
2697
                        // but this will result in more code for small performance gain.
UNCOV
2698
                        drawFixedColorWideLinesAsQuads(projectedVertexArray, count, offset, m, mode);
×
UNCOV
2699
                        return;
×
2700
                }
2701
        }
UNCOV
2702
        const bool coreProfileWideLineMode = wideLineMode && isCoreProfile;
×
2703

2704
        vao->bind();
×
UNCOV
2705
        verticesVBO->bind();
×
2706
        GLsizeiptr numberOfVerticesToCopy = count + offset;
×
2707
        if(indices)
×
2708
        {
2709
                indicesVBO->bind();
×
UNCOV
2710
                indicesVBO->allocate(indices+offset, count * sizeof indices[0]);
×
2711
                numberOfVerticesToCopy = 1 + *std::max_element(indices + offset, indices + offset + count);
×
2712
        }
2713

2714
#ifdef GL_MULTISAMPLE
UNCOV
2715
        const bool multisampleWasOn = multisamplingEnabled && glIsEnabled(GL_MULTISAMPLE);
×
2716
#endif
2717

UNCOV
2718
        if (!texCoordArray.enabled && !colorArray.enabled && !normalArray.enabled)
×
2719
        {
2720
                pr = coreProfileWideLineMode ? wideLineShaderProgram : basicShaderProgram;
×
UNCOV
2721
                pr->bind();
×
2722

2723
                verticesVBO->allocate(projectedVertexArray.pointer, projectedVertexArray.vertexSizeInBytes()*numberOfVerticesToCopy);
×
2724

2725
#ifdef GL_MULTISAMPLE
UNCOV
2726
                if(multisamplingEnabled && glState.lineSmooth)
×
UNCOV
2727
                        glEnable(GL_MULTISAMPLE);
×
2728
#endif
2729
                if(coreProfileWideLineMode)
×
2730
                {
2731
                        pr->setAttributeBuffer(wideLineShaderVars.vertex, projectedVertexArray.type, 0, projectedVertexArray.size);
×
UNCOV
2732
                        pr->enableAttributeArray(wideLineShaderVars.vertex);
×
2733
                        pr->setUniformValue(wideLineShaderVars.projectionMatrix, qMat);
×
2734
                        pr->setUniformValue(wideLineShaderVars.color, currentColor.toQVector());
×
2735
                        pr->setUniformValue(wideLineShaderVars.lineWidth, glState.lineWidth);
×
2736
                        GLint viewport[4] = {};
×
2737
                        glGetIntegerv(GL_VIEWPORT, viewport);
×
2738
                        pr->setUniformValue(wideLineShaderVars.viewportSize, QVector2D(viewport[2], viewport[3]));
×
2739
                }
2740
                else
2741
                {
UNCOV
2742
                        pr->setAttributeBuffer(basicShaderVars.vertex, projectedVertexArray.type, 0, projectedVertexArray.size);
×
UNCOV
2743
                        pr->enableAttributeArray(basicShaderVars.vertex);
×
2744
                        pr->setUniformValue(basicShaderVars.projectionMatrix, qMat);
×
2745
                        pr->setUniformValue(basicShaderVars.color, currentColor.toQVector());
×
2746
                }
2747
        }
×
UNCOV
2748
        else if (texCoordArray.enabled && !colorArray.enabled && !normalArray.enabled && !wideLineMode)
×
2749
        {
2750
                pr = texturesShaderProgram;
×
UNCOV
2751
                pr->bind();
×
2752

2753
                const auto bufferSize = projectedVertexArray.vertexSizeInBytes()*numberOfVerticesToCopy +
×
UNCOV
2754
                                                                texCoordArray.vertexSizeInBytes()*numberOfVerticesToCopy;
×
2755
                verticesVBO->allocate(bufferSize);
×
2756
                const auto vertexDataSize = projectedVertexArray.vertexSizeInBytes()*numberOfVerticesToCopy;
×
2757
                verticesVBO->write(0, projectedVertexArray.pointer, vertexDataSize);
×
2758
                const auto texCoordDataOffset = vertexDataSize;
×
2759
                const auto texCoordDataSize = texCoordArray.vertexSizeInBytes()*numberOfVerticesToCopy;
×
2760
                verticesVBO->write(texCoordDataOffset, texCoordArray.pointer, texCoordDataSize);
×
2761

2762
                pr->setAttributeBuffer(texturesShaderVars.vertex, projectedVertexArray.type, 0, projectedVertexArray.size);
×
UNCOV
2763
                pr->enableAttributeArray(texturesShaderVars.vertex);
×
2764
                pr->setUniformValue(texturesShaderVars.projectionMatrix, qMat);
×
2765
                pr->setUniformValue(texturesShaderVars.texColor, currentColor.toQVector());
×
2766
                pr->setAttributeBuffer(texturesShaderVars.texCoord, texCoordArray.type, texCoordDataOffset, texCoordArray.size);
×
2767
                pr->enableAttributeArray(texturesShaderVars.texCoord);
×
2768
                //pr->setUniformValue(texturesShaderVars.texture, 0);    // use texture unit 0
2769
        }
×
UNCOV
2770
        else if (texCoordArray.enabled && colorArray.enabled && !normalArray.enabled && !wideLineMode)
×
2771
        {
2772
                pr = texturesColorShaderProgram;
×
UNCOV
2773
                pr->bind();
×
2774

2775
                const auto bufferSize = projectedVertexArray.vertexSizeInBytes()*numberOfVerticesToCopy +
×
UNCOV
2776
                                                                texCoordArray.vertexSizeInBytes()*numberOfVerticesToCopy +
×
2777
                                                                colorArray.vertexSizeInBytes()*numberOfVerticesToCopy;
×
2778
                verticesVBO->allocate(bufferSize);
×
2779
                const auto vertexDataSize = projectedVertexArray.vertexSizeInBytes()*numberOfVerticesToCopy;
×
2780
                verticesVBO->write(0, projectedVertexArray.pointer, vertexDataSize);
×
2781
                const auto texCoordDataOffset = vertexDataSize;
×
2782
                const auto texCoordDataSize = texCoordArray.vertexSizeInBytes()*numberOfVerticesToCopy;
×
2783
                verticesVBO->write(texCoordDataOffset, texCoordArray.pointer, texCoordDataSize);
×
2784
                const auto colorDataOffset = vertexDataSize + texCoordDataSize;
×
2785
                const auto colorDataSize = colorArray.vertexSizeInBytes()*numberOfVerticesToCopy;
×
2786
                verticesVBO->write(colorDataOffset, colorArray.pointer, colorDataSize);
×
2787

2788
                pr->setAttributeBuffer(texturesColorShaderVars.vertex, projectedVertexArray.type, 0, projectedVertexArray.size);
×
UNCOV
2789
                pr->enableAttributeArray(texturesColorShaderVars.vertex);
×
2790
                pr->setUniformValue(texturesColorShaderVars.projectionMatrix, qMat);
×
2791
                pr->setAttributeBuffer(texturesColorShaderVars.texCoord, texCoordArray.type, texCoordDataOffset, texCoordArray.size);
×
2792
                pr->enableAttributeArray(texturesColorShaderVars.texCoord);
×
2793
                pr->setAttributeBuffer(texturesColorShaderVars.color, colorArray.type, colorDataOffset, colorArray.size);
×
2794
                pr->enableAttributeArray(texturesColorShaderVars.color);
×
2795
                //pr->setUniformValue(texturesShaderVars.texture, 0);    // use texture unit 0
2796
                pr->setUniformValue(texturesColorShaderVars.saturation, saturation);
×
UNCOV
2797
        }
×
2798
        else if (!texCoordArray.enabled && colorArray.enabled && !normalArray.enabled)
×
2799
        {
2800
                pr = coreProfileWideLineMode ? colorfulWideLineShaderProgram : colorShaderProgram;
×
UNCOV
2801
                pr->bind();
×
2802

2803
                const auto bufferSize = projectedVertexArray.vertexSizeInBytes()*numberOfVerticesToCopy +
×
UNCOV
2804
                                                                colorArray.vertexSizeInBytes()*numberOfVerticesToCopy;
×
2805
                verticesVBO->allocate(bufferSize);
×
2806
                const auto vertexDataSize = projectedVertexArray.vertexSizeInBytes()*numberOfVerticesToCopy;
×
2807
                verticesVBO->write(0, projectedVertexArray.pointer, vertexDataSize);
×
2808
                const auto colorDataOffset = vertexDataSize;
×
2809
                const auto colorDataSize = colorArray.vertexSizeInBytes()*numberOfVerticesToCopy;
×
2810
                verticesVBO->write(colorDataOffset, colorArray.pointer, colorDataSize);
×
2811

2812
                if(coreProfileWideLineMode)
×
2813
                {
2814
                        pr->setAttributeBuffer(colorfulWideLineShaderVars.vertex, projectedVertexArray.type, 0, projectedVertexArray.size);
×
UNCOV
2815
                        pr->enableAttributeArray(colorfulWideLineShaderVars.vertex);
×
2816
                        pr->setAttributeBuffer(colorfulWideLineShaderVars.color, colorArray.type, colorDataOffset, colorArray.size);
×
2817
                        pr->enableAttributeArray(colorfulWideLineShaderVars.color);
×
2818
                        pr->setUniformValue(colorfulWideLineShaderVars.projectionMatrix, qMat);
×
2819
                        pr->setUniformValue(colorfulWideLineShaderVars.lineWidth, glState.lineWidth);
×
2820
                        GLint viewport[4] = {};
×
2821
                        glGetIntegerv(GL_VIEWPORT, viewport);
×
2822
                        pr->setUniformValue(colorfulWideLineShaderVars.viewportSize, QVector2D(viewport[2], viewport[3]));
×
2823
                }
2824
                else
2825
                {
UNCOV
2826
                        pr->setAttributeBuffer(colorShaderVars.vertex, projectedVertexArray.type, 0, projectedVertexArray.size);
×
UNCOV
2827
                        pr->enableAttributeArray(colorShaderVars.vertex);
×
2828
                        pr->setUniformValue(colorShaderVars.projectionMatrix, qMat);
×
2829
                        pr->setAttributeBuffer(colorShaderVars.color, colorArray.type, colorDataOffset, colorArray.size);
×
2830
                        pr->enableAttributeArray(colorShaderVars.color);
×
2831
                }
2832
#ifdef GL_MULTISAMPLE
UNCOV
2833
                if(multisamplingEnabled && glState.lineSmooth)
×
UNCOV
2834
                        glEnable(GL_MULTISAMPLE);
×
2835
#endif
2836
        }
×
2837
        else
2838
        {
UNCOV
2839
                qDebug() << "Unhandled parameters." << texCoordArray.enabled << colorArray.enabled << normalArray.enabled << wideLineMode;
×
UNCOV
2840
                Q_ASSERT(0);
×
2841
                return;
2842
        }
2843
        
UNCOV
2844
        if (indices)
×
UNCOV
2845
                glDrawElements(mode, count, GL_UNSIGNED_SHORT, 0);
×
2846
        else
2847
                glDrawArrays(mode, offset, count);
×
2848

2849
        verticesVBO->release();
×
UNCOV
2850
        indicesVBO->release();
×
2851
        vao->release();
×
2852

2853
        if (pr)
×
UNCOV
2854
                pr->release();
×
2855

2856
#ifdef GL_MULTISAMPLE
UNCOV
2857
        if(multisamplingEnabled && !multisampleWasOn && wideLineMode && glState.lineSmooth)
×
UNCOV
2858
                glDisable(GL_MULTISAMPLE);
×
2859
#endif
2860
}
2861

UNCOV
2862
size_t StelPainter::ArrayDesc::vertexSizeInBytes() const
×
2863
{
2864
        size_t elemSize = 1;
×
UNCOV
2865
        switch(type)
×
2866
        {
2867
        case GL_SHORT:
×
UNCOV
2868
                elemSize = sizeof(GLshort);
×
2869
                break;
×
2870
        case GL_INT:
×
2871
                elemSize = sizeof(GLint);
×
2872
                break;
×
2873
        case GL_FLOAT:
×
2874
                elemSize = sizeof(GLfloat);
×
2875
                break;
×
2876
#if GL_DOUBLE != GL_FLOAT // see StelOpenGL.hpp
2877
        case GL_DOUBLE:
×
UNCOV
2878
                elemSize = sizeof(GLdouble);
×
2879
                break;
×
2880
#endif
2881
        default:
×
UNCOV
2882
                Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected element type");
×
2883
                break;
2884
        }
UNCOV
2885
        return elemSize * size;
×
2886
}
2887

UNCOV
2888
StelPainter::ArrayDesc StelPainter::projectArray(const StelPainter::ArrayDesc& array, int offset, int count, const unsigned short* indices)
×
2889
{
2890
        // XXX: we should use a more generic way to test whether or not to do the projection.
UNCOV
2891
        if (dynamic_cast<StelProjector2d*>(prj.data()))
×
2892
        {
2893
                return array;
×
2894
        }
2895

UNCOV
2896
        Q_ASSERT(array.size == 3);
×
UNCOV
2897
        Q_ASSERT(array.type == GL_DOUBLE);
×
2898
        const Vec3d* vecArray = reinterpret_cast<const Vec3d *>(const_cast<void*>(array.pointer));
×
2899

2900
        // We have two different cases :
2901
        // 1) We are not using an indice array.  In that case the size of the array is known
2902
        // 2) We are using an indice array.  In that case we have to find the max value by iterating through the indices.
UNCOV
2903
        if (!indices)
×
2904
        {
2905
                polygonVertexArray.resize(offset + count);
×
UNCOV
2906
                prj->project(count, vecArray + offset, polygonVertexArray.data() + offset);
×
2907
        } else
2908
        {
2909
                // we need to find the max value of the indices !
UNCOV
2910
                unsigned short max = 0;
×
UNCOV
2911
                for (int i = offset; i < offset + count; ++i)
×
2912
                {
2913
                        max = std::max(max, indices[i]);
×
2914
                }
2915
                polygonVertexArray.resize(max+1);
×
UNCOV
2916
                prj->project(max + 1, vecArray + offset, polygonVertexArray.data() + offset);
×
2917
        }
2918

UNCOV
2919
        ArrayDesc ret;
×
UNCOV
2920
        ret.size = 3;
×
2921
        ret.type = GL_FLOAT;
×
2922
        ret.pointer = polygonVertexArray.constData();
×
2923
        ret.enabled = array.enabled;
×
2924
        return ret;
×
2925
}
2926

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