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 "StelLocaleMgr.hpp"
24 #include "StelProjector.hpp"
25 #include "StelProjectorClasses.hpp"
26 #include "StelUtils.hpp"
27 #include "Dithering.hpp"
28 #include "SaturationShader.hpp"
29
30 #include <QDebug>
31 #include <QString>
32 #include <QSettings>
33 #include <QLinkedList>
34 #include <QPainter>
35 #include <QMutex>
36 #include <QVarLengthArray>
37 #include <QPaintEngine>
38 #include <QCache>
39 #include <QOpenGLPaintDevice>
40 #include <QOpenGLShader>
41 #include <QOpenGLTexture>
42 #include <QApplication>
43
44 static const int TEX_CACHE_LIMIT = 7000000;
45
46 #ifndef NDEBUG
47 QMutex* StelPainter::globalMutex = new QMutex();
48 #endif
49
50 QCache<QByteArray, StringTexture> StelPainter::texCache(TEX_CACHE_LIMIT);
51 QOpenGLShaderProgram* StelPainter::texturesShaderProgram=Q_NULLPTR;
52 QOpenGLShaderProgram* StelPainter::basicShaderProgram=Q_NULLPTR;
53 QOpenGLShaderProgram* StelPainter::colorShaderProgram=Q_NULLPTR;
54 QOpenGLShaderProgram* StelPainter::texturesColorShaderProgram=Q_NULLPTR;
55 StelPainter::BasicShaderVars StelPainter::basicShaderVars;
56 StelPainter::TexturesShaderVars StelPainter::texturesShaderVars;
57 StelPainter::BasicShaderVars StelPainter::colorShaderVars;
58 StelPainter::TexturesColorShaderVars StelPainter::texturesColorShaderVars;
59
GLState(QOpenGLFunctions * gl)60 StelPainter::GLState::GLState(QOpenGLFunctions* gl)
61 : blend(false),
62 blendSrc(GL_SRC_ALPHA), blendDst(GL_ONE_MINUS_SRC_ALPHA),
63 depthTest(false),
64 depthMask(false),
65 cullFace(false),
66 lineSmooth(false),
67 lineWidth(1.0f),
68 gl(gl)
69 {
70 }
71
apply()72 void StelPainter::GLState::apply()
73 {
74 if(blend)
75 gl->glEnable(GL_BLEND);
76 else
77 gl->glDisable(GL_BLEND);
78 gl->glBlendFunc(blendSrc,blendDst);
79 if(depthTest)
80 gl->glEnable(GL_DEPTH_TEST);
81 else
82 gl->glDisable(GL_DEPTH_TEST);
83 gl->glDepthMask(depthMask);
84 if(cullFace)
85 gl->glEnable(GL_CULL_FACE);
86 else
87 gl->glDisable(GL_CULL_FACE);
88 #ifdef GL_LINE_SMOOTH
89 if(!QOpenGLContext::currentContext()->isOpenGLES())
90 {
91 if (lineSmooth)
92 gl->glEnable(GL_LINE_SMOOTH);
93 else
94 gl->glDisable(GL_LINE_SMOOTH);
95 }
96 #endif
97 }
98
reset()99 void StelPainter::GLState::reset()
100 {
101 *this = GLState(gl);
102 apply();
103 }
104
linkProg(QOpenGLShaderProgram * prog,const QString & name)105 bool StelPainter::linkProg(QOpenGLShaderProgram* prog, const QString& name)
106 {
107 bool ret = prog->link();
108 QString log = prog->log();
109 if (!ret || (!log.isEmpty() && !log.contains("Link was successful") && !(log=="No errors."))) //"No errors." returned on some Intel drivers
110 qWarning() << QString("StelPainter: Warnings while linking %1 shader program:\n%2").arg(name, prog->log());
111 return ret;
112 }
113
parseDitheringMode(QString const & str)114 StelPainter::DitheringMode StelPainter::parseDitheringMode(QString const& str)
115 {
116 const auto s=str.trimmed().toLower();
117 if(s=="disabled" ) return DitheringMode::Disabled;
118 if(s=="color565" ) return DitheringMode::Color565;
119 if(s=="color666" ) return DitheringMode::Color666;
120 if(s=="color888" ) return DitheringMode::Color888;
121 if(s=="color101010") return DitheringMode::Color101010;
122 return DitheringMode::Disabled;
123 }
124
StelPainter(const StelProjectorP & proj)125 StelPainter::StelPainter(const StelProjectorP& proj) : QOpenGLFunctions(QOpenGLContext::currentContext()), glState(this)
126 {
127 Q_ASSERT(proj);
128
129 #ifndef NDEBUG
130 Q_ASSERT(globalMutex);
131
132 GLenum er = glGetError();
133 if (er!=GL_NO_ERROR)
134 {
135 if (er==GL_INVALID_OPERATION)
136 qFatal("Invalid openGL operation. It is likely that you used openGL calls without having a valid instance of StelPainter");
137 }
138
139 // Lock the global mutex ensuring that no other instances of StelPainter are currently being used
140 if (globalMutex->tryLock()==false)
141 {
142 qFatal("There can be only 1 instance of StelPainter at a given time");
143 }
144 #endif
145
146 //TODO: is this still required, and is there some Qt way to fix it? 0x11111111 is a bit peculiar, how was it chosen?
147 // Fix some problem when using Qt OpenGL2 engine
148 glStencilMask(0x11111111);
149 glState.apply(); //apply default OpenGL state
150 setProjector(proj);
151
152 QSettings*const conf = StelApp::getInstance().getSettings();
153 ditheringMode = parseDitheringMode(conf->value("video/dithering_mode").toString());
154 }
155
setProjector(const StelProjectorP & p)156 void StelPainter::setProjector(const StelProjectorP& p)
157 {
158 prj=p;
159 // Init GL viewport to current projector values
160 glViewport(prj->viewportXywh[0], prj->viewportXywh[1], prj->viewportXywh[2], prj->viewportXywh[3]);
161 glFrontFace(prj->needGlFrontFaceCW()?GL_CW:GL_CCW);
162 }
163
~StelPainter()164 StelPainter::~StelPainter()
165 {
166 if(bayerPatternTex)
167 glDeleteTextures(1, &bayerPatternTex);
168 //reset opengl state
169 glState.reset();
170
171 #ifndef NDEBUG
172 GLenum er = glGetError();
173 if (er!=GL_NO_ERROR)
174 {
175 if (er==GL_INVALID_OPERATION)
176 qFatal("Invalid openGL operation detected in ~StelPainter()");
177 }
178
179 // We are done with this StelPainter
180 globalMutex->unlock();
181 #endif
182 }
183
184
setFont(const QFont & font)185 void StelPainter::setFont(const QFont& font)
186 {
187 currentFont = font;
188 }
189
setColor(float r,float g,float b,float a)190 void StelPainter::setColor(float r, float g, float b, float a)
191 {
192 currentColor.set(r,g,b,a);
193 }
194
setColor(Vec3f rgb,float a)195 void StelPainter::setColor(Vec3f rgb, float a)
196 {
197 currentColor.set(rgb[0],rgb[1],rgb[2],a);
198 }
199
setColor(Vec4f rgba)200 void StelPainter::setColor(Vec4f rgba)
201 {
202 currentColor=rgba;
203 }
204
getColor() const205 Vec4f StelPainter::getColor() const
206 {
207 return currentColor;
208 }
209
getFontMetrics() const210 QFontMetrics StelPainter::getFontMetrics() const
211 {
212 return QFontMetrics(currentFont);
213 }
214
setBlending(bool enableBlending,GLenum blendSrc,GLenum blendDst)215 void StelPainter::setBlending(bool enableBlending, GLenum blendSrc, GLenum blendDst)
216 {
217 if(enableBlending != glState.blend)
218 {
219 glState.blend = enableBlending;
220 if(enableBlending)
221 glEnable(GL_BLEND);
222 else
223 glDisable(GL_BLEND);
224 }
225 if(enableBlending)
226 {
227 if(blendSrc!=glState.blendSrc||blendDst!=glState.blendDst)
228 {
229 glState.blendSrc = blendSrc;
230 glState.blendDst = blendDst;
231 glBlendFunc(blendSrc,blendDst);
232 }
233 }
234 }
235
setDepthTest(bool enable)236 void StelPainter::setDepthTest(bool enable)
237 {
238 if(glState.depthTest != enable)
239 {
240 glState.depthTest = enable;
241 if(enable)
242 glEnable(GL_DEPTH_TEST);
243 else
244 glDisable(GL_DEPTH_TEST);
245 }
246 }
247
setDepthMask(bool enable)248 void StelPainter::setDepthMask(bool enable)
249 {
250 if(glState.depthMask != enable)
251 {
252 glState.depthMask = enable;
253 if(enable)
254 glDepthMask(GL_TRUE);
255 else
256 glDepthMask(GL_FALSE);
257 }
258 }
259
setCullFace(bool enable)260 void StelPainter::setCullFace(bool enable)
261 {
262 if(glState.cullFace!=enable)
263 {
264 glState.cullFace = enable;
265 if(enable)
266 glEnable(GL_CULL_FACE);
267 else
268 glDisable(GL_CULL_FACE);
269 }
270 }
271
setLineSmooth(bool enable)272 void StelPainter::setLineSmooth(bool enable)
273 {
274 #ifdef GL_LINE_SMOOTH
275 if (!QOpenGLContext::currentContext()->isOpenGLES() && enable!=glState.lineSmooth)
276 {
277 glState.lineSmooth = enable;
278 if(enable)
279 glEnable(GL_LINE_SMOOTH);
280 else
281 glDisable(GL_LINE_SMOOTH);
282 }
283 #else
284 Q_UNUSED(enable); //noop
285 #endif
286 }
287
setLineWidth(float width)288 void StelPainter::setLineWidth(float width)
289 {
290 if(fabs(glState.lineWidth - width) > 1.e-10f)
291 {
292 glState.lineWidth = width;
293 glLineWidth(width);
294 }
295 }
296
297 ///////////////////////////////////////////////////////////////////////////
298 // Standard methods for drawing primitives
299
300 // Fill with black around the circle
drawViewportShape(void)301 void StelPainter::drawViewportShape(void)
302 {
303 if (prj->maskType != StelProjector::MaskDisk)
304 return;
305
306 bool oldBlendState = glState.blend;
307 glDisable(GL_BLEND);
308 setColor(0.f,0.f,0.f);
309
310 GLfloat innerRadius = 0.5f*static_cast<float>(prj->viewportFovDiameter);
311 GLfloat outerRadius = prj->getViewportWidth()+prj->getViewportHeight();
312 GLint slices = 239;
313
314 GLfloat sinCache[240];
315 GLfloat cosCache[240];
316 GLfloat vertices[(240+1)*2][3];
317 GLfloat deltaRadius;
318 GLfloat radiusHigh;
319
320 if (outerRadius<=0.0f || innerRadius<0.0f ||innerRadius > outerRadius)
321 {
322 Q_ASSERT(0);
323 return;
324 }
325
326 /* Compute length (needed for normal calculations) */
327 deltaRadius=outerRadius-innerRadius;
328
329 /* Cache is the vertex locations cache */
330 for (int i=0; i<=slices; i++)
331 {
332 GLfloat angle=(M_PIf*2.0f)*i/slices;
333 sinCache[i]=static_cast<GLfloat>(sin(angle));
334 cosCache[i]=static_cast<GLfloat>(cos(angle));
335 }
336
337 sinCache[slices]=sinCache[0];
338 cosCache[slices]=cosCache[0];
339
340 /* Enable arrays */
341 enableClientStates(true);
342 setVertexPointer(3, GL_FLOAT, vertices);
343
344 radiusHigh=outerRadius-deltaRadius;
345 for (int i=0; i<=slices; i++)
346 {
347 vertices[i*2][0]= static_cast<float>(prj->viewportCenter[0]) + outerRadius*sinCache[i];
348 vertices[i*2][1]= static_cast<float>(prj->viewportCenter[1]) + outerRadius*cosCache[i];
349 vertices[i*2][2] = 0.0f;
350 vertices[i*2+1][0]= static_cast<float>(prj->viewportCenter[0]) + radiusHigh*sinCache[i];
351 vertices[i*2+1][1]= static_cast<float>(prj->viewportCenter[1]) + radiusHigh*cosCache[i];
352 vertices[i*2+1][2] = 0.0f;
353 }
354 drawFromArray(TriangleStrip, (slices+1)*2, 0, false);
355 enableClientStates(false);
356 if(oldBlendState)
357 glEnable(GL_BLEND);
358 }
359
computeFanDisk(float radius,uint innerFanSlices,uint level,QVector<Vec3d> & vertexArr,QVector<Vec2f> & texCoordArr)360 void StelPainter::computeFanDisk(float radius, uint innerFanSlices, uint level, QVector<Vec3d>& vertexArr, QVector<Vec2f>& texCoordArr)
361 {
362 Q_ASSERT(level<32);
363 float rad[64];
364 uint i,j;
365 rad[level] = radius;
366 #pragma warning(suppress: 4146)
367 for (i=level-1u;i!=-1u;--i)
368 {
369 rad[i] = rad[i+1]*(1.f-M_PIf/(innerFanSlices<<(i+1)))*2.f/3.f;
370 }
371 uint slices = innerFanSlices<<level;
372
373 float* cos_sin_theta = StelUtils::ComputeCosSinTheta(static_cast<uint>(slices));
374 float* cos_sin_theta_p;
375 uint slices_step = 2;
376 float x,y,xa,ya;
377 radius*=2.f;
378 vertexArr.resize(0);
379 texCoordArr.resize(0);
380 for (i=level;i>0;--i,slices_step<<=1)
381 {
382 for (j=0,cos_sin_theta_p=cos_sin_theta; j<slices-1; j+=slices_step,cos_sin_theta_p+=2*slices_step)
383 {
384 xa = rad[i]*cos_sin_theta_p[slices_step];
385 ya = rad[i]*cos_sin_theta_p[slices_step+1];
386 texCoordArr << Vec2f(0.5f+xa/radius, 0.5f+ya/radius);
387 vertexArr << Vec3d(static_cast<double>(xa), static_cast<double>(ya), 0);
388
389 x = rad[i]*cos_sin_theta_p[2*slices_step];
390 y = rad[i]*cos_sin_theta_p[2*slices_step+1];
391 texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
392 vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
393
394 x = rad[i-1]*cos_sin_theta_p[2*slices_step];
395 y = rad[i-1]*cos_sin_theta_p[2*slices_step+1];
396 texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
397 vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
398
399 texCoordArr << Vec2f(0.5f+xa/radius, 0.5f+ya/radius);
400 vertexArr << Vec3d(static_cast<double>(xa), static_cast<double>(ya), 0);
401 texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
402 vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
403
404 x = rad[i-1]*cos_sin_theta_p[0];
405 y = rad[i-1]*cos_sin_theta_p[1];
406 texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
407 vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
408
409 texCoordArr << Vec2f(0.5f+xa/radius, 0.5f+ya/radius);
410 vertexArr << Vec3d(static_cast<double>(xa), static_cast<double>(ya), 0);
411 texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
412 vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
413
414 x = rad[i]*cos_sin_theta_p[0];
415 y = rad[i]*cos_sin_theta_p[1];
416 texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
417 vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
418 }
419 }
420 // draw the inner polygon
421 slices_step>>=1;
422 cos_sin_theta_p=cos_sin_theta;
423
424 if (slices==1)
425 {
426 x = rad[0]*cos_sin_theta_p[0];
427 y = rad[0]*cos_sin_theta_p[1];
428 texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
429 vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
430 cos_sin_theta_p+=2*slices_step;
431 x = rad[0]*cos_sin_theta_p[0];
432 y = rad[0]*cos_sin_theta_p[1];
433 texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
434 vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
435 cos_sin_theta_p+=2*slices_step;
436 x = rad[0]*cos_sin_theta_p[0];
437 y = rad[0]*cos_sin_theta_p[1];
438 texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
439 vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
440 }
441 else
442 {
443 j=0;
444 while (j<slices)
445 {
446 texCoordArr << Vec2f(0.5f, 0.5f);
447 vertexArr << Vec3d(0, 0, 0);
448 x = rad[0]*cos_sin_theta_p[0];
449 y = rad[0]*cos_sin_theta_p[1];
450 texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
451 vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
452 j+=slices_step;
453 cos_sin_theta_p+=2*slices_step;
454 x = rad[0]*cos_sin_theta_p[0];
455 y = rad[0]*cos_sin_theta_p[1];
456 texCoordArr << Vec2f(0.5f+x/radius, 0.5f+y/radius);
457 vertexArr << Vec3d(static_cast<double>(x), static_cast<double>(y), 0);
458 }
459 }
460 }
461
sSphereMapTexCoordFast(float rho_div_fov,const float costheta,const float sintheta,QVector<float> & out)462 static void sSphereMapTexCoordFast(float rho_div_fov, const float costheta, const float sintheta, QVector<float>& out)
463 {
464 if (rho_div_fov>0.5f)
465 rho_div_fov=0.5f;
466 out << 0.5f + rho_div_fov * costheta << 0.5f + rho_div_fov * sintheta;
467 }
468
sSphereMap(double radius,unsigned int slices,unsigned int stacks,float textureFov,int orientInside)469 void StelPainter::sSphereMap(double radius, unsigned int slices, unsigned int stacks, float textureFov, int orientInside)
470 {
471 float rho;
472 double x,y,z;
473 unsigned int i, j;
474 const float* cos_sin_rho = StelUtils::ComputeCosSinRho(stacks);
475 const float* cos_sin_rho_p;
476
477 const float* cos_sin_theta = StelUtils::ComputeCosSinTheta(slices);
478 const float* cos_sin_theta_p;
479
480 float drho = M_PIf / stacks;
481 drho/=textureFov;
482
483 // texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis
484 // t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes)
485 // cannot use triangle fan on texturing (s coord. at top/bottom tip varies)
486
487 const unsigned int imax = stacks;
488
489 static QVector<double> vertexArr;
490 static QVector<float> texCoordArr;
491
492 // draw intermediate stacks as quad strips
493 // LGTM comments: the floats are always <=1. We still prefer float multiplication (with insignificant accuracy loss) for speed.
494 if (!orientInside) // nsign==1
495 {
496 for (i = 0,cos_sin_rho_p=cos_sin_rho,rho=0.f; i < imax; ++i,cos_sin_rho_p+=2,rho+=drho)
497 {
498 vertexArr.resize(0);
499 texCoordArr.resize(0);
500 for (j=0,cos_sin_theta_p=cos_sin_theta;j<=slices;++j,cos_sin_theta_p+=2)
501 {
502 x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[1]);
503 y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[1]);
504 z = static_cast<double>(cos_sin_rho_p[0]);
505 sSphereMapTexCoordFast(rho, cos_sin_theta_p[0], cos_sin_theta_p[1], texCoordArr);
506 vertexArr << x*radius << y*radius << z*radius;
507
508 x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[3]);
509 y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[3]);
510 z = static_cast<double>(cos_sin_rho_p[2]);
511 sSphereMapTexCoordFast(rho + drho, cos_sin_theta_p[0], cos_sin_theta_p[1], texCoordArr);
512 vertexArr << x*radius << y*radius << z*radius;
513 }
514 setArrays(reinterpret_cast<const Vec3d*>(vertexArr.constData()), reinterpret_cast<const Vec2f*>(texCoordArr.constData()));
515 drawFromArray(TriangleStrip, vertexArr.size()/3);
516 }
517 }
518 else
519 {
520 for (i = 0,cos_sin_rho_p=cos_sin_rho,rho=0.f; i < imax; ++i,cos_sin_rho_p+=2,rho+=drho)
521 {
522 vertexArr.resize(0);
523 texCoordArr.resize(0);
524 for (j=0,cos_sin_theta_p=cos_sin_theta;j<=slices;++j,cos_sin_theta_p+=2)
525 {
526 x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[3]);
527 y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[3]);
528 z = static_cast<double>(cos_sin_rho_p[2]);
529 sSphereMapTexCoordFast(rho + drho, cos_sin_theta_p[0], -cos_sin_theta_p[1], texCoordArr);
530 vertexArr << x*radius << y*radius << z*radius;
531
532 x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[1]);
533 y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[1]);
534 z = static_cast<double>(cos_sin_rho_p[0]);
535 sSphereMapTexCoordFast(rho, cos_sin_theta_p[0], -cos_sin_theta_p[1], texCoordArr);
536 vertexArr << x*radius << y*radius << z*radius;
537 }
538 setArrays(reinterpret_cast<const Vec3d*>(vertexArr.constData()), reinterpret_cast<const Vec2f*>(texCoordArr.constData()));
539 drawFromArray(TriangleStrip, vertexArr.size()/3);
540 }
541 }
542 }
543
drawTextGravity180(float x,float y,const QString & ws,float xshift,float yshift)544 void StelPainter::drawTextGravity180(float x, float y, const QString& ws, float xshift, float yshift)
545 {
546 float dx, dy, d, theta, theta_o, psi, width;
547 dx = x - static_cast<float>(prj->viewportCenter[0]);
548 dy = y - static_cast<float>(prj->viewportCenter[1]);
549 d = std::sqrt(dx*dx + dy*dy);
550 float limit = 120.;
551
552 // If the text is too far away to be visible in the screen return
553 if (d>qMax(prj->viewportXywh[3], prj->viewportXywh[2])*2 || ws.isEmpty())
554 return;
555
556 float cWidth = static_cast<float>(getFontMetrics().boundingRect(ws).width())/ws.length();
557 float stdWidth = static_cast<float>(getFontMetrics().boundingRect("a").width());
558 theta = std::atan2(dy - 1, dx);
559 theta_o = M_PIf + std::atan2(dx, dy - 1);
560 psi = std::atan2(cWidth, d + 1) * M_180_PIf;
561 if (psi>5)
562 psi = 5;
563
564 float xVc = static_cast<float>(prj->viewportCenter[0]) + xshift;
565 float yVc = static_cast<float>(prj->viewportCenter[1]) + yshift;
566 const float cosr = std::cos(-theta_o * M_PI_180f);
567 const float sinr = std::sin(-theta_o * M_PI_180f);
568 float xom = x + xshift*cosr - yshift*sinr;
569 float yom = y + yshift*sinr + yshift*cosr;
570
571 if (!StelApp::getInstance().getLocaleMgr().isAppRTL())
572 {
573 for (int i=0; i<ws.length(); ++i)
574 {
575 if (d<limit)
576 {
577 drawText(xom, yom, ws[i], -theta_o*M_180_PIf+psi*i, 0., 0.);
578 xom += cWidth*std::cos(-theta_o+psi*i * M_PI_180f);
579 yom += cWidth*std::sin(-theta_o+psi*i * M_PI_180f);
580 }
581 else
582 {
583 x = d * std::cos(theta) + xVc ;
584 y = d * std::sin(theta) + yVc ;
585 drawText(x, y, ws[i], 90.f + theta*M_180_PIf, 0., 0.);
586 // Compute how much the character contributes to the angle
587 if (ws[i].isSpace())
588 width = stdWidth;
589 else
590 width = static_cast<float>(getFontMetrics().boundingRect(ws[i]).width());
591 theta += psi * M_PI_180f * (1 + (width - cWidth)/ cWidth);
592 }
593 }
594 }
595 else
596 {
597 int slen = ws.length();
598 for (int i=0;i<slen;i++)
599 {
600 if (d<limit)
601 {
602 drawText(xom, yom, ws[slen-1-i], -theta_o*M_180_PIf+psi*i, 0., 0.);
603 xom += cWidth*std::cos(-theta_o+psi*i * M_PI_180f);
604 yom += cWidth*std::sin(-theta_o+psi*i * M_PI_180f);
605 }
606 else
607 {
608 x = d * std::cos(theta) + xVc;
609 y = d * std::sin(theta) + yVc;
610 drawText(x, y, ws[slen-1-i], 90.f + theta*M_180_PIf, 0., 0.);
611 if (ws[slen-1-i].isSpace())
612 width = stdWidth;
613 else
614 width = static_cast<float>(getFontMetrics().boundingRect(ws[slen-1-i]).width());
615 theta += psi * M_PI_180f * (1 + (width - cWidth)/ cWidth);
616 }
617 }
618 }
619 }
620
drawText(const Vec3d & v,const QString & str,float angleDeg,float xshift,float yshift,bool noGravity)621 void StelPainter::drawText(const Vec3d& v, const QString& str, float angleDeg, float xshift, float yshift, bool noGravity)
622 {
623 Vec3d win;
624 if (prj->project(v, win))
625 drawText(static_cast<float>(win[0]), static_cast<float>(win[1]), str, angleDeg, xshift, yshift, noGravity);
626 }
627
628 /*************************************************************************
629 Draw the string at the given position and angle with the given font
630 *************************************************************************/
631
632 // Methods taken from text-use-opengl-buffer
633 // Container for one cached string texture
634 struct StringTexture
635 {
636 QOpenGLTexture* texture;
637 QSize size;
getTexSizeStringTexture638 QSizeF getTexSize() const {
639 return QSizeF(static_cast<qreal>(size.width()) / static_cast<qreal>(texture->width()),
640 static_cast<qreal>(size.height()) / static_cast<qreal>(texture->height()));
641 }
642
StringTextureStringTexture643 StringTexture(QOpenGLTexture* tex, const QSize& size) :
644 texture(tex), size(size) {}
~StringTextureStringTexture645 ~StringTexture() {delete texture;}
646 };
647
getTexTexture(const QString & str,int pixelSize) const648 StringTexture* StelPainter::getTexTexture(const QString& str, int pixelSize) const
649 {
650 // Render first the text into a QPixmap, then create a QOpenGLTexture
651 // from it. We could optimize by directly using a QImage, but for some
652 // reason the result is not exactly the same than with a QPixmap.
653 QByteArray hash = str.toUtf8() + QByteArray::number(pixelSize);
654 StringTexture* cachedTex = texCache.object(hash);
655 if (cachedTex)
656 return cachedTex;
657 QFont tmpFont = currentFont;
658 tmpFont.setPixelSize(currentFont.pixelSize()*static_cast<int>(static_cast<float>(prj->getDevicePixelsPerPixel())*StelApp::getInstance().getGlobalScalingRatio()));
659 QRect strRect = QFontMetrics(tmpFont).boundingRect(str);
660 int w = strRect.width()+1+static_cast<int>(0.02f*strRect.width());
661 int h = strRect.height();
662
663 QPixmap strImage = QPixmap(StelUtils::getBiggerPowerOfTwo(w), StelUtils::getBiggerPowerOfTwo(h));
664 strImage.fill(Qt::transparent);
665 QPainter painter(&strImage);
666 tmpFont.setStyleStrategy(QFont::NoAntialias);
667 painter.setFont(tmpFont);
668 //painter.setRenderHints(QPainter::TextAntialiasing);
669 painter.setPen(Qt::white);
670 painter.drawText(-strRect.x(), -strRect.y(), str);
671 StringTexture* newTex = new StringTexture(new QOpenGLTexture(strImage.toImage()), QSize(w, h));
672 texCache.insert(hash, newTex, 3*w*h);
673 // simply returning newTex is dangerous as the object is owned by the cache now. (Coverity Scan barks.)
674 return texCache.object(hash);
675 }
676
drawText(float x,float y,const QString & str,float angleDeg,float xshift,float yshift,bool noGravity)677 void StelPainter::drawText(float x, float y, const QString& str, float angleDeg, float xshift, float yshift, bool noGravity)
678 {
679 if (prj->gravityLabels && !noGravity)
680 {
681 drawTextGravity180(x, y, str, xshift, yshift);
682 }
683 else if (qApp->property("text_texture")==true) // CLI option -t given?
684 {
685 //qDebug() << "Text texture" << str;
686 // This is taken from branch text-use-opengl-buffer. This is essential on devices like Raspberry Pi (2016-03).
687 StringTexture* tex = getTexTexture(str, currentFont.pixelSize());
688 Q_ASSERT(tex);
689 if (!noGravity)
690 angleDeg += prj->defaultAngleForGravityText;
691 tex->texture->bind();
692
693 static float vertexData[8];
694 // compute the vertex coordinates applying the translation and the rotation
695 static const float vertexBase[] = {0., 0., 1., 0., 0., 1., 1., 1.};
696 if (std::fabs(angleDeg)>1.f*static_cast<float>(M_PI)/180.f)
697 {
698 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
699 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
700 const float cosr = std::cos(angleDeg * static_cast<float>(M_PI/180.));
701 const float sinr = std::sin(angleDeg * static_cast<float>(M_PI/180.));
702 for (int i = 0; i < 8; i+=2)
703 {
704 vertexData[i] = int(x + (tex->size.width()*vertexBase[i]+xshift) * cosr - (tex->size.height()*vertexBase[i+1]+yshift) * sinr);
705 vertexData[i+1] = int(y + (tex->size.width()*vertexBase[i]+xshift) * sinr + (tex->size.height()*vertexBase[i+1]+yshift) * cosr);
706 }
707 }
708 else
709 {
710 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
711 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
712 for (int i = 0; i < 8; i+=2)
713 {
714 vertexData[i] = int(x + tex->size.width()*vertexBase[i]+xshift);
715 vertexData[i+1] = int(y + tex->size.height()*vertexBase[i+1]+yshift);
716 }
717 }
718
719 float* texCoords = new float[8];
720 for (int i=0;i<4;i++)
721 {
722 texCoords[i*2+0] = static_cast<float>(tex->getTexSize().width()) * (i % 2);
723 texCoords[i*2+1] = static_cast<float>(tex->getTexSize().height()) * (1 - static_cast<float>(i) / 2);
724 }
725 setTexCoordPointer(2, GL_FLOAT, texCoords);
726
727 //text drawing requires blending, but we reset GL state afterwards if necessary
728 bool oldBlending = glState.blend;
729 GLenum oldSrc = glState.blendSrc, oldDst = glState.blendDst;
730 setBlending(true);
731 enableClientStates(true, true);
732 setVertexPointer(2, GL_FLOAT, vertexData);
733 drawFromArray(TriangleStrip, 4, 0, false);
734 setBlending(oldBlending, oldSrc, oldDst);
735 enableClientStates(false, false);
736 tex->texture->release();
737 delete[] texCoords;
738 }
739 else
740 {
741 QOpenGLPaintDevice device;
742 device.setSize(QSize(prj->getViewportWidth(), prj->getViewportHeight()));
743 // This doesn't seem to work correctly, so implement the hack below instead.
744 // Maybe check again later, or check on mac with retina..
745 // device.setDevicePixelRatio(prj->getDevicePixelsPerPixel());
746 // painter.setFont(currentFont);
747
748 QPainter painter(&device);
749
750 QFont tmpFont = currentFont;
751 tmpFont.setPixelSize(currentFont.pixelSize()*static_cast<int>(static_cast<float>(prj->getDevicePixelsPerPixel())*StelApp::getInstance().getGlobalScalingRatio()));
752 painter.setFont(tmpFont);
753 painter.setPen(currentColor.toQColor());
754
755 float scaleRatio = StelApp::getInstance().getGlobalScalingRatio();
756 xshift*=scaleRatio;
757 yshift*=scaleRatio;
758
759 y = prj->getViewportHeight()-y;
760 yshift = -yshift;
761
762 // Translate/rotate
763 if (!noGravity)
764 angleDeg += prj->defaultAngleForGravityText;
765
766 if (std::fabs(angleDeg)>1.f)
767 {
768 QTransform m;
769 m.translate(static_cast<qreal>(x), static_cast<qreal>(y));
770 m.rotate(static_cast<qreal>(-angleDeg));
771 painter.setTransform(m);
772 painter.drawText(qRound(xshift), qRound(yshift), str);
773 }
774 else
775 {
776 painter.drawText(qRound(x+xshift), qRound(y+yshift), str);
777 }
778
779 //important to call this before GL state restore
780 painter.end();
781
782 //fix for bug 1628072 caused by QTBUG-56798
783 #ifndef QT_NO_DEBUG
784 StelOpenGL::clearGLErrors();
785 #endif
786
787 //QPainter messes up some GL state, begin/endNativePainting or save/restore does not help
788 glState.apply();
789 }
790 }
791
792 // Recursive method cutting a small circle in small segments
fIter(const StelProjectorP & prj,const Vec3d & p1,const Vec3d & p2,Vec3d & win1,Vec3d & win2,QLinkedList<Vec3d> & vertexList,const QLinkedList<Vec3d>::iterator & iter,double radius,const Vec3d & center,int nbI=0,bool checkCrossDiscontinuity=true)793 inline void fIter(const StelProjectorP& prj, const Vec3d& p1, const Vec3d& p2, Vec3d& win1, Vec3d& win2, QLinkedList<Vec3d>& vertexList, const QLinkedList<Vec3d>::iterator& iter, double radius, const Vec3d& center, int nbI=0, bool checkCrossDiscontinuity=true)
794 {
795 const bool crossDiscontinuity = checkCrossDiscontinuity && prj->intersectViewportDiscontinuity(p1+center, p2+center);
796 if (crossDiscontinuity && nbI>=10)
797 {
798 win1[2]=-2.;
799 win2[2]=-2.;
800 vertexList.insert(iter, win1);
801 vertexList.insert(iter, win2);
802 return;
803 }
804
805 Vec3d newVertex(p1); newVertex+=p2;
806 newVertex.normalize();
807 newVertex*=radius;
808 Vec3d win3(newVertex[0]+center[0], newVertex[1]+center[1], newVertex[2]+center[2]);
809 const bool isValidVertex = prj->projectInPlace(win3);
810
811 const float v10=static_cast<float>(win1[0]-win3[0]);
812 const float v11=static_cast<float>(win1[1]-win3[1]);
813 const float v20=static_cast<float>(win2[0]-win3[0]);
814 const float v21=static_cast<float>(win2[1]-win3[1]);
815
816 const float dist = std::sqrt((v10*v10+v11*v11)*(v20*v20+v21*v21));
817 const float cosAngle = (v10*v20+v11*v21)/dist;
818 if ((cosAngle>-0.999f || dist>50*50 || crossDiscontinuity) && nbI<10)
819 {
820 // Use the 3rd component of the vector to store whether the vertex is valid
821 win3[2]= isValidVertex ? 1.0 : -1.;
822 fIter(prj, p1, newVertex, win1, win3, vertexList, vertexList.insert(iter, win3), radius, center, nbI+1, crossDiscontinuity || dist>50*50);
823 fIter(prj, newVertex, p2, win3, win2, vertexList, iter, radius, center, nbI+1, crossDiscontinuity || dist>50*50 );
824 }
825 }
826
827 // Used by the method below
828 QVector<Vec3f> StelPainter::smallCircleVertexArray;
829 QVector<Vec4f> StelPainter::smallCircleColorArray;
830
drawSmallCircleVertexArray()831 void StelPainter::drawSmallCircleVertexArray()
832 {
833 if (smallCircleVertexArray.size() == 1)
834 {
835 smallCircleVertexArray.resize(0);
836 smallCircleColorArray.resize(0);
837 return;
838 }
839 if (smallCircleVertexArray.isEmpty())
840 return;
841
842 enableClientStates(true, false, !smallCircleColorArray.isEmpty());
843 setVertexPointer(3, GL_FLOAT, smallCircleVertexArray.constData());
844 if (!smallCircleColorArray.isEmpty())
845 setColorPointer(4, GL_FLOAT, smallCircleColorArray.constData());
846 drawFromArray(LineStrip, smallCircleVertexArray.size(), 0, false);
847 enableClientStates(false);
848 smallCircleVertexArray.resize(0);
849 smallCircleColorArray.resize(0);
850 }
851
852 static Vec3d pt1, pt2;
drawGreatCircleArc(const Vec3d & start,const Vec3d & stop,const SphericalCap * clippingCap,void (* viewportEdgeIntersectCallback)(const Vec3d & screenPos,const Vec3d & direction,void * userData),void * userData)853 void StelPainter::drawGreatCircleArc(const Vec3d& start, const Vec3d& stop, const SphericalCap* clippingCap,
854 void (*viewportEdgeIntersectCallback)(const Vec3d& screenPos, const Vec3d& direction, void* userData), void* userData)
855 {
856 if (clippingCap)
857 {
858 pt1=start;
859 pt2=stop;
860 if (clippingCap->clipGreatCircle(pt1, pt2))
861 {
862 drawSmallCircleArc(pt1, pt2, Vec3d(0.), viewportEdgeIntersectCallback, userData);
863 }
864 return;
865 }
866 drawSmallCircleArc(start, stop, Vec3d(0.), viewportEdgeIntersectCallback, userData);
867 }
868
869 /*************************************************************************
870 Draw a small circle arc in the current frame
871 *************************************************************************/
drawSmallCircleArc(const Vec3d & start,const Vec3d & stop,const Vec3d & rotCenter,void (* viewportEdgeIntersectCallback)(const Vec3d & screenPos,const Vec3d & direction,void * userData),void * userData)872 void StelPainter::drawSmallCircleArc(const Vec3d& start, const Vec3d& stop, const Vec3d& rotCenter, void (*viewportEdgeIntersectCallback)(const Vec3d& screenPos, const Vec3d& direction, void* userData), void* userData)
873 {
874 Q_ASSERT(smallCircleVertexArray.empty());
875
876 QLinkedList<Vec3d> tessArc; // Contains the list of projected points from the tesselated arc
877 Vec3d win1, win2;
878 win1[2] = prj->project(start, win1) ? 1.0 : -1.;
879 win2[2] = prj->project(stop, win2) ? 1.0 : -1.;
880 tessArc.append(win1);
881
882
883 if (rotCenter.lengthSquared()<1e-11)
884 {
885 // Great circle
886 // Perform the tesselation of the arc in small segments in a way so that the lines look smooth
887 fIter(prj, start, stop, win1, win2, tessArc, tessArc.insert(tessArc.end(), win2), 1, rotCenter);
888 }
889 else
890 {
891 Vec3d tmp = (rotCenter^start)/rotCenter.length();
892 const double radius = fabs(tmp.length());
893 // Perform the tesselation of the arc in small segments in a way so that the lines look smooth
894 fIter(prj, start-rotCenter, stop-rotCenter, win1, win2, tessArc, tessArc.insert(tessArc.end(), win2), radius, rotCenter);
895 }
896
897 // And draw.
898 QLinkedList<Vec3d>::ConstIterator i = tessArc.constBegin();
899 while (i+1 != tessArc.constEnd())
900 {
901 const Vec3d& p1 = *i;
902 const Vec3d& p2 = *(++i);
903 const bool p1InViewport = prj->checkInViewport(p1);
904 const bool p2InViewport = prj->checkInViewport(p2);
905 if ((p1[2]>0 && p1InViewport) || (p2[2]>0 && p2InViewport))
906 {
907 smallCircleVertexArray.append(Vec3f(static_cast<float>(p1[0]), static_cast<float>(p1[1]), static_cast<float>(p1[2])));
908 if (i+1==tessArc.constEnd())
909 {
910 smallCircleVertexArray.append(Vec3f(static_cast<float>(p2[0]), static_cast<float>(p2[1]), static_cast<float>(p2[2])));
911 drawSmallCircleVertexArray();
912 }
913 if (viewportEdgeIntersectCallback && p1InViewport!=p2InViewport)
914 {
915 // We crossed the edge of the view port
916 if (p1InViewport)
917 viewportEdgeIntersectCallback(prj->viewPortIntersect(p1, p2), p2-p1, userData);
918 else
919 viewportEdgeIntersectCallback(prj->viewPortIntersect(p2, p1), p1-p2, userData);
920 }
921 }
922 else
923 {
924 // Break the line, draw the stored vertex and flush the list
925 if (!smallCircleVertexArray.isEmpty())
926 smallCircleVertexArray.append(Vec3f(static_cast<float>(p1[0]), static_cast<float>(p1[1]), static_cast<float>(p1[2])));
927 drawSmallCircleVertexArray();
928 }
929 }
930 Q_ASSERT(smallCircleVertexArray.isEmpty());
931 }
932
drawPath(const QVector<Vec3d> & points,const QVector<Vec4f> & colors)933 void StelPainter::drawPath(const QVector<Vec3d> &points, const QVector<Vec4f> &colors)
934 {
935 // Because the path may intersect a viewport discontinuity, we cannot render
936 // it in one OpenGL drawing call.
937 Q_ASSERT(smallCircleVertexArray.isEmpty());
938 Q_ASSERT(smallCircleColorArray.isEmpty());
939 Q_ASSERT(points.size() == colors.size());
940 Vec3d win;
941
942 // In general we should add all the points, even if they are hidden, since otherwise we don't
943 // have proper clipping on the sides. We make an exception for the orthographic projection because
944 // its clipping doesn't work well. A better solution would be to use a culling test I think.
945 bool skipHiddenPoints = dynamic_cast<StelProjectorOrthographic*>(prj.data());
946
947 for (int i = 0; i+1 != points.size(); i++)
948 {
949 const Vec3d p1 = points[i];
950 const Vec3d p2 = points[i + 1];
951 if (!prj->intersectViewportDiscontinuity(p1, p2))
952 {
953 bool visible = prj->project(p1, win);
954
955 if (!visible && skipHiddenPoints)
956 {
957 drawSmallCircleVertexArray();
958 continue;
959 }
960
961 smallCircleVertexArray.append(Vec3f(static_cast<float>(win[0]), static_cast<float>(win[1]), static_cast<float>(win[2])));
962 smallCircleColorArray.append(colors[i]);
963 if (i+2==points.size())
964 {
965 prj->project(p2, win);
966 smallCircleVertexArray.append(Vec3f(static_cast<float>(win[0]), static_cast<float>(win[1]), static_cast<float>(win[2])));
967 smallCircleColorArray.append(colors[i + 1]);
968 drawSmallCircleVertexArray();
969 }
970 }
971 else
972 {
973 // Break the line, draw the stored vertex and flush the list
974 if (!smallCircleVertexArray.isEmpty())
975 {
976 prj->project(p1, win);
977 smallCircleVertexArray.append(Vec3f(static_cast<float>(win[0]), static_cast<float>(win[1]), static_cast<float>(win[2])));
978 smallCircleColorArray.append(colors[i]);
979 }
980 drawSmallCircleVertexArray();
981 }
982 }
983 Q_ASSERT(smallCircleVertexArray.isEmpty());
984 Q_ASSERT(smallCircleColorArray.isEmpty());
985 }
986
987 // Project the passed triangle on the screen ensuring that it will look smooth, even for non linear distortion
988 // by splitting it into subtriangles.
projectSphericalTriangle(const SphericalCap * clippingCap,const Vec3d * vertices,QVarLengthArray<Vec3f,4096> * outVertices,const Vec2f * texturePos,QVarLengthArray<Vec2f,4096> * outTexturePos,const Vec3f * colors,QVarLengthArray<Vec3f,4096> * outColors,double maxSqDistortion,int nbI,bool checkDisc1,bool checkDisc2,bool checkDisc3) const989 void StelPainter::projectSphericalTriangle(const SphericalCap* clippingCap, const Vec3d* vertices, QVarLengthArray<Vec3f, 4096>* outVertices,
990 const Vec2f* texturePos, QVarLengthArray<Vec2f, 4096>* outTexturePos, const Vec3f *colors, QVarLengthArray<Vec3f, 4096> *outColors,
991 double maxSqDistortion, int nbI, bool checkDisc1, bool checkDisc2, bool checkDisc3) const
992 {
993 Q_ASSERT(fabs(vertices[0].length()-1.)<0.00001);
994 Q_ASSERT(fabs(vertices[1].length()-1.)<0.00001);
995 Q_ASSERT(fabs(vertices[2].length()-1.)<0.00001);
996 if (clippingCap && clippingCap->containsTriangle(vertices))
997 clippingCap = Q_NULLPTR;
998 if (clippingCap && !clippingCap->intersectsTriangle(vertices))
999 return;
1000 bool cDiscontinuity1 = checkDisc1 && prj->intersectViewportDiscontinuity(vertices[0], vertices[1]);
1001 bool cDiscontinuity2 = checkDisc2 && prj->intersectViewportDiscontinuity(vertices[1], vertices[2]);
1002 bool cDiscontinuity3 = checkDisc3 && prj->intersectViewportDiscontinuity(vertices[0], vertices[2]);
1003 const bool cd1=cDiscontinuity1;
1004 const bool cd2=cDiscontinuity2;
1005 const bool cd3=cDiscontinuity3;
1006
1007 Vec3d e0=vertices[0];
1008 Vec3d e1=vertices[1];
1009 Vec3d e2=vertices[2];
1010 bool valid = prj->projectInPlace(e0);
1011 valid = prj->projectInPlace(e1) || valid;
1012 valid = prj->projectInPlace(e2) || valid;
1013 // Clip polygons behind the viewer
1014 if (!valid)
1015 return;
1016
1017 if (checkDisc1 && cDiscontinuity1==false)
1018 {
1019 // If the distortion at segment e0,e1 is too big, flags it for subdivision
1020 Vec3d win3 = vertices[0]; win3+=vertices[1];
1021 prj->projectInPlace(win3);
1022 win3[0]-=(e0[0]+e1[0])*0.5; win3[1]-=(e0[1]+e1[1])*0.5;
1023 cDiscontinuity1 = (win3[0]*win3[0]+win3[1]*win3[1])>maxSqDistortion;
1024 }
1025 if (checkDisc2 && cDiscontinuity2==false)
1026 {
1027 // If the distortion at segment e1,e2 is too big, flags it for subdivision
1028 Vec3d win3 = vertices[1]; win3+=vertices[2];
1029 prj->projectInPlace(win3);
1030 win3[0]-=(e2[0]+e1[0])*0.5; win3[1]-=(e2[1]+e1[1])*0.5;
1031 cDiscontinuity2 = (win3[0]*win3[0]+win3[1]*win3[1])>maxSqDistortion;
1032 }
1033 if (checkDisc3 && cDiscontinuity3==false)
1034 {
1035 // If the distortion at segment e2,e0 is too big, flags it for subdivision
1036 Vec3d win3 = vertices[2]; win3+=vertices[0];
1037 prj->projectInPlace(win3);
1038 win3[0] -= (e0[0]+e2[0])*0.5;
1039 win3[1] -= (e0[1]+e2[1])*0.5;
1040 cDiscontinuity3 = (win3[0]*win3[0]+win3[1]*win3[1])>maxSqDistortion;
1041 }
1042
1043 if (!cDiscontinuity1 && !cDiscontinuity2 && !cDiscontinuity3)
1044 {
1045 // The triangle is clean, appends it
1046 outVertices->append(e0.toVec3f()); outVertices->append(e1.toVec3f()); outVertices->append(e2.toVec3f());
1047 if (outTexturePos)
1048 outTexturePos->append(texturePos,3);
1049 if (outColors)
1050 outColors->append(colors,3);
1051 return;
1052 }
1053
1054 if (nbI > 4)
1055 {
1056 // If we reached the limit number of iterations and still have a discontinuity,
1057 // discards the triangle.
1058 if (cd1 || cd2 || cd3)
1059 return;
1060
1061 // Else display it, it will be suboptimal though.
1062 outVertices->append(e0.toVec3f()); outVertices->append(e1.toVec3f()); outVertices->append(e2.toVec3f());
1063 if (outTexturePos)
1064 outTexturePos->append(texturePos,3);
1065 if (outColors)
1066 outColors->append(colors,3);
1067 return;
1068 }
1069
1070 // Recursively splits the triangle into sub triangles.
1071 // Depending on which combination of sides of the triangle has to be split a different strategy is used.
1072 Vec3d va[3];
1073 Vec2f ta[3];
1074 Vec3f ca[3];
1075 // Only 1 side has to be split: split the triangle in 2
1076 if (cDiscontinuity1 && !cDiscontinuity2 && !cDiscontinuity3)
1077 {
1078 va[0]=vertices[0];
1079 va[1]=vertices[0];va[1]+=vertices[1];
1080 va[1].normalize();
1081 va[2]=vertices[2];
1082 if (outTexturePos)
1083 {
1084 ta[0]=texturePos[0];
1085 ta[1]=(texturePos[0]+texturePos[1])*0.5;
1086 ta[2]=texturePos[2];
1087 }
1088 if (outColors)
1089 {
1090 ca[0]=colors[0];
1091 ca[1]=(colors[0]+colors[1])*0.5;
1092 ca[2]=colors[2];
1093 }
1094 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, true, false);
1095
1096 //va[0]=vertices[0]+vertices[1];
1097 //va[0].normalize();
1098 va[0]=va[1];
1099 va[1]=vertices[1];
1100 va[2]=vertices[2];
1101 if (outTexturePos)
1102 {
1103 ta[0]=(texturePos[0]+texturePos[1])*0.5;
1104 ta[1]=texturePos[1];
1105 ta[2]=texturePos[2];
1106 }
1107 if (outColors)
1108 {
1109 ca[0]=(colors[0]+colors[1])*0.5;
1110 ca[1]=colors[1];
1111 ca[2]=colors[2];
1112 }
1113 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, false, true);
1114 return;
1115 }
1116
1117 if (!cDiscontinuity1 && cDiscontinuity2 && !cDiscontinuity3)
1118 {
1119 va[0]=vertices[0];
1120 va[1]=vertices[1];
1121 va[2]=vertices[1];va[2]+=vertices[2];
1122 va[2].normalize();
1123 if (outTexturePos)
1124 {
1125 ta[0]=texturePos[0];
1126 ta[1]=texturePos[1];
1127 ta[2]=(texturePos[1]+texturePos[2])*0.5;
1128 }
1129 if (outColors)
1130 {
1131 ca[0]=colors[0];
1132 ca[1]=colors[1];
1133 ca[2]=(colors[1]+colors[2])*0.5;
1134 }
1135 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, false, true, true);
1136
1137 va[0]=vertices[0];
1138 //va[1]=vertices[1]+vertices[2];
1139 //va[1].normalize();
1140 va[1]=va[2];
1141 va[2]=vertices[2];
1142 if (outTexturePos)
1143 {
1144 ta[0]=texturePos[0];
1145 ta[1]=(texturePos[1]+texturePos[2])*0.5;
1146 ta[2]=texturePos[2];
1147 }
1148 if (outColors)
1149 {
1150 ca[0]=colors[0];
1151 ca[1]=(colors[1]+colors[2])*0.5;
1152 ca[2]=colors[2];
1153 }
1154 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, true, false);
1155 return;
1156 }
1157
1158 if (!cDiscontinuity1 && !cDiscontinuity2 && cDiscontinuity3)
1159 {
1160 va[0]=vertices[0];
1161 va[1]=vertices[1];
1162 va[2]=vertices[0];va[2]+=vertices[2];
1163 va[2].normalize();
1164 if (outTexturePos)
1165 {
1166 ta[0]=texturePos[0];
1167 ta[1]=texturePos[1];
1168 ta[2]=(texturePos[0]+texturePos[2])*0.5;
1169 }
1170 if (outColors)
1171 {
1172 ca[0]=colors[0];
1173 ca[1]=colors[1];
1174 ca[2]=(colors[0]+colors[2])*0.5;
1175 }
1176 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, false, true, true);
1177
1178 //va[0]=vertices[0]+vertices[2];
1179 //va[0].normalize();
1180 va[0]=va[2];
1181 va[1]=vertices[1];
1182 va[2]=vertices[2];
1183 if (outTexturePos)
1184 {
1185 ta[0]=(texturePos[0]+texturePos[2])*0.5;
1186 ta[1]=texturePos[1];
1187 ta[2]=texturePos[2];
1188 }
1189 if (outColors)
1190 {
1191 ca[0]=(colors[0]+colors[2])*0.5;
1192 ca[1]=colors[1];
1193 ca[2]=colors[2];
1194 }
1195 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, false, true);
1196 return;
1197 }
1198
1199 // 2 sides have to be split: split the triangle in 3
1200 if (cDiscontinuity1 && cDiscontinuity2 && !cDiscontinuity3)
1201 {
1202 va[0]=vertices[0];
1203 va[1]=vertices[0];va[1]+=vertices[1];
1204 va[1].normalize();
1205 va[2]=vertices[1];va[2]+=vertices[2];
1206 va[2].normalize();
1207 if (outTexturePos)
1208 {
1209 ta[0]=texturePos[0];
1210 ta[1]=(texturePos[0]+texturePos[1])*0.5;
1211 ta[2]=(texturePos[1]+texturePos[2])*0.5;
1212 }
1213 if (outColors)
1214 {
1215 ca[0]=colors[0];
1216 ca[1]=(colors[0]+colors[1])*0.5;
1217 ca[2]=(colors[1]+colors[2])*0.5;
1218 }
1219 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
1220
1221 //va[0]=vertices[0]+vertices[1];
1222 //va[0].normalize();
1223 va[0]=va[1];
1224 va[1]=vertices[1];
1225 //va[2]=vertices[1]+vertices[2];
1226 //va[2].normalize();
1227 if (outTexturePos)
1228 {
1229 ta[0]=(texturePos[0]+texturePos[1])*0.5;
1230 ta[1]=texturePos[1];
1231 ta[2]=(texturePos[1]+texturePos[2])*0.5;
1232 }
1233 if (outColors)
1234 {
1235 ca[0]=(colors[0]+colors[1])*0.5;
1236 ca[1]=colors[1];
1237 ca[2]=(colors[1]+colors[2])*0.5;
1238 }
1239 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
1240
1241 va[0]=vertices[0];
1242 //va[1]=vertices[1]+vertices[2];
1243 //va[1].normalize();
1244 va[1]=va[2];
1245 va[2]=vertices[2];
1246 if (outTexturePos)
1247 {
1248 ta[0]=texturePos[0];
1249 ta[1]=(texturePos[1]+texturePos[2])*0.5;
1250 ta[2]=texturePos[2];
1251 }
1252 if (outColors)
1253 {
1254 ca[0]=colors[0];
1255 ca[1]=(colors[1]+colors[2])*0.5;
1256 ca[2]=colors[2];
1257 }
1258 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, true, false);
1259 return;
1260 }
1261 if (cDiscontinuity1 && !cDiscontinuity2 && cDiscontinuity3)
1262 {
1263 va[0]=vertices[0];
1264 va[1]=vertices[0];va[1]+=vertices[1];
1265 va[1].normalize();
1266 va[2]=vertices[0];va[2]+=vertices[2];
1267 va[2].normalize();
1268 if (outTexturePos)
1269 {
1270 ta[0]=texturePos[0];
1271 ta[1]=(texturePos[0]+texturePos[1])*0.5;
1272 ta[2]=(texturePos[0]+texturePos[2])*0.5;
1273 }
1274 if (outColors)
1275 {
1276 ca[0]=colors[0];
1277 ca[1]=(colors[0]+colors[1])*0.5;
1278 ca[2]=(colors[0]+colors[2])*0.5;
1279 }
1280 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
1281
1282 //va[0]=vertices[0]+vertices[1];
1283 //va[0].normalize();
1284 va[0]=va[1];
1285 va[1]=vertices[2];
1286 //va[2]=vertices[0]+vertices[2];
1287 //va[2].normalize();
1288 if (outTexturePos)
1289 {
1290 ta[0]=(texturePos[0]+texturePos[1])*0.5;
1291 ta[1]=texturePos[2];
1292 ta[2]=(texturePos[0]+texturePos[2])*0.5;
1293 }
1294 if (outColors)
1295 {
1296 ca[0]=(colors[0]+colors[1])*0.5;
1297 ca[1]=colors[2];
1298 ca[2]=(colors[0]+colors[2])*0.5;
1299 }
1300 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
1301
1302
1303 //va[0]=vertices[0]+vertices[1];
1304 //va[0].normalize();
1305 va[1]=vertices[1];
1306 va[2]=vertices[2];
1307 if (outTexturePos)
1308 {
1309 ta[0]=(texturePos[0]+texturePos[1])*0.5;
1310 ta[1]=texturePos[1];
1311 ta[2]=texturePos[2];
1312 }
1313 if (outColors)
1314 {
1315 ca[0]=(colors[0]+colors[1])*0.5;
1316 ca[1]=colors[1];
1317 ca[2]=colors[2];
1318 }
1319 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, true, false, true);
1320
1321 return;
1322 }
1323 if (!cDiscontinuity1 && cDiscontinuity2 && cDiscontinuity3)
1324 {
1325 va[0]=vertices[0];
1326 va[1]=vertices[1];
1327 va[2]=vertices[1];va[2]+=vertices[2];
1328 va[2].normalize();
1329 if (outTexturePos)
1330 {
1331 ta[0]=texturePos[0];
1332 ta[1]=texturePos[1];
1333 ta[2]=(texturePos[1]+texturePos[2])*0.5;
1334 }
1335 if (outColors)
1336 {
1337 ca[0]=colors[0];
1338 ca[1]=colors[1];
1339 ca[2]=(colors[1]+colors[2])*0.5;
1340 }
1341 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1, false, true, true);
1342
1343 //va[0]=vertices[1]+vertices[2];
1344 //va[0].normalize();
1345 va[0]=va[2];
1346 va[1]=vertices[2];
1347 va[2]=vertices[0];va[2]+=vertices[2];
1348 va[2].normalize();
1349 if (outTexturePos)
1350 {
1351 ta[0]=(texturePos[1]+texturePos[2])*0.5;
1352 ta[1]=texturePos[2];
1353 ta[2]=(texturePos[0]+texturePos[2])*0.5;
1354 }
1355 if (outColors)
1356 {
1357 ca[0]=(colors[1]+colors[2])*0.5;
1358 ca[1]=colors[2];
1359 ca[2]=(colors[0]+colors[2])*0.5;
1360 }
1361 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
1362
1363 va[1]=va[0];
1364 va[0]=vertices[0];
1365 //va[1]=vertices[1]+vertices[2];
1366 //va[1].normalize();
1367 //va[2]=vertices[0]+vertices[2];
1368 //va[2].normalize();
1369 if (outTexturePos)
1370 {
1371 ta[0]=texturePos[0];
1372 ta[1]=(texturePos[1]+texturePos[2])*0.5;
1373 ta[2]=(texturePos[0]+texturePos[2])*0.5;
1374 }
1375 if (outColors)
1376 {
1377 ca[0]=colors[0];
1378 ca[1]=(colors[1]+colors[2])*0.5;
1379 ca[2]=(colors[0]+colors[2])*0.5;
1380 }
1381 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
1382 return;
1383 }
1384
1385 // Last case: the 3 sides have to be split: cut in 4 triangles a' la HTM
1386 va[0]=vertices[0];va[0]+=vertices[1];
1387 va[0].normalize();
1388 va[1]=vertices[1];va[1]+=vertices[2];
1389 va[1].normalize();
1390 va[2]=vertices[0];va[2]+=vertices[2];
1391 va[2].normalize();
1392 if (outTexturePos)
1393 {
1394 ta[0]=(texturePos[0]+texturePos[1])*0.5;
1395 ta[1]=(texturePos[1]+texturePos[2])*0.5;
1396 ta[2]=(texturePos[0]+texturePos[2])*0.5;
1397 }
1398 if (outColors)
1399 {
1400 ca[0]=(colors[0]+colors[1])*0.5;
1401 ca[1]=(colors[1]+colors[2])*0.5;
1402 ca[2]=(colors[0]+colors[2])*0.5;
1403 }
1404 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
1405
1406 va[1]=va[0];
1407 va[0]=vertices[0];
1408 //va[1]=vertices[0]+vertices[1];
1409 //va[1].normalize();
1410 //va[2]=vertices[0]+vertices[2];
1411 //va[2].normalize();
1412 if (outTexturePos)
1413 {
1414 ta[0]=texturePos[0];
1415 ta[1]=(texturePos[0]+texturePos[1])*0.5;
1416 ta[2]=(texturePos[0]+texturePos[2])*0.5;
1417 }
1418 if (outColors)
1419 {
1420 ca[0]=colors[0];
1421 ca[1]=(colors[0]+colors[1])*0.5;
1422 ca[2]=(colors[0]+colors[2])*0.5;
1423 }
1424 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
1425
1426 //va[0]=vertices[0]+vertices[1];
1427 //va[0].normalize();
1428 va[0]=va[1];
1429 va[1]=vertices[1];
1430 va[2]=vertices[1];va[2]+=vertices[2];
1431 va[2].normalize();
1432 if (outTexturePos)
1433 {
1434 ta[0]=(texturePos[0]+texturePos[1])*0.5;
1435 ta[1]=texturePos[1];
1436 ta[2]=(texturePos[1]+texturePos[2])*0.5;
1437 }
1438 if (outColors)
1439 {
1440 ca[0]=(colors[0]+colors[1])*0.5;
1441 ca[1]=colors[1];
1442 ca[2]=(colors[1]+colors[2])*0.5;
1443 }
1444 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
1445
1446 va[0]=vertices[0];va[0]+=vertices[2];
1447 va[0].normalize();
1448 //va[1]=vertices[1]+vertices[2];
1449 //va[1].normalize();
1450 va[1]=va[2];
1451 va[2]=vertices[2];
1452 if (outTexturePos)
1453 {
1454 ta[0]=(texturePos[0]+texturePos[2])*0.5;
1455 ta[1]=(texturePos[1]+texturePos[2])*0.5;
1456 ta[2]=texturePos[2];
1457 }
1458 if (outColors)
1459 {
1460 ca[0]=(colors[0]+colors[2])*0.5;
1461 ca[1]=(colors[1]+colors[2])*0.5;
1462 ca[2]=colors[2];
1463 }
1464 projectSphericalTriangle(clippingCap, va, outVertices, ta, outTexturePos, ca, outColors, maxSqDistortion, nbI+1);
1465
1466 return;
1467 }
1468
1469 static QVarLengthArray<Vec3f, 4096> polygonVertexArray;
1470 static QVarLengthArray<Vec2f, 4096> polygonTextureCoordArray;
1471 static QVarLengthArray<Vec3f, 4096> polygonColorArray;
1472 static QVarLengthArray<unsigned int, 4096> indexArray;
1473
drawGreatCircleArcs(const StelVertexArray & va,const SphericalCap * clippingCap)1474 void StelPainter::drawGreatCircleArcs(const StelVertexArray& va, const SphericalCap* clippingCap)
1475 {
1476 Q_ASSERT(va.vertex.size()!=1);
1477 Q_ASSERT(!va.isIndexed()); // Indexed unsupported yet
1478 switch (va.primitiveType)
1479 {
1480 case StelVertexArray::Lines:
1481 Q_ASSERT(va.vertex.size()%2==0);
1482 for (int i=0;i<va.vertex.size();i+=2)
1483 drawGreatCircleArc(va.vertex.at(i), va.vertex.at(i+1), clippingCap);
1484 return;
1485 case StelVertexArray::LineStrip:
1486 for (int i=0;i<va.vertex.size()-1;++i)
1487 drawGreatCircleArc(va.vertex.at(i), va.vertex.at(i+1), clippingCap);
1488 return;
1489 case StelVertexArray::LineLoop:
1490 for (int i=0;i<va.vertex.size()-1;++i)
1491 drawGreatCircleArc(va.vertex.at(i), va.vertex.at(i+1), clippingCap);
1492 drawGreatCircleArc(va.vertex.last(), va.vertex.first(), clippingCap);
1493 return;
1494 default:
1495 Q_ASSERT(0); // Unsupported primitive yype
1496 }
1497 }
1498
1499 // The function object that we use as an interface between VertexArray::foreachTriangle and
1500 // StelPainter::projectSphericalTriangle.
1501 //
1502 // This is used by drawSphericalTriangles to project all the triangles coordinates in a StelVertexArray into our global
1503 // vertex array buffer.
1504 class VertexArrayProjector
1505 {
1506 public:
VertexArrayProjector(const StelVertexArray & ar,StelPainter * apainter,const SphericalCap * aclippingCap,QVarLengthArray<Vec3f,4096> * aoutVertices,QVarLengthArray<Vec2f,4096> * aoutTexturePos=Q_NULLPTR,QVarLengthArray<Vec3f,4096> * aoutColors=Q_NULLPTR,double amaxSqDistortion=5.)1507 VertexArrayProjector(const StelVertexArray& ar, StelPainter* apainter, const SphericalCap* aclippingCap,
1508 QVarLengthArray<Vec3f, 4096>* aoutVertices, QVarLengthArray<Vec2f, 4096>* aoutTexturePos=Q_NULLPTR, QVarLengthArray<Vec3f, 4096>* aoutColors=Q_NULLPTR, double amaxSqDistortion=5.)
1509 : //vertexArray(ar),
1510 painter(apainter), clippingCap(aclippingCap), outVertices(aoutVertices),
1511 outColors(aoutColors), outTexturePos(aoutTexturePos), maxSqDistortion(amaxSqDistortion)
1512 {
1513 Q_UNUSED(ar)
1514 }
1515
1516 // Project a single triangle and add it into the output arrays
operator ()(const Vec3d * v0,const Vec3d * v1,const Vec3d * v2,const Vec2f * t0,const Vec2f * t1,const Vec2f * t2,const Vec3f * c0,const Vec3f * c1,const Vec3f * c2,unsigned int,unsigned int,unsigned) const1517 inline void operator()(const Vec3d* v0, const Vec3d* v1, const Vec3d* v2,
1518 const Vec2f* t0, const Vec2f* t1, const Vec2f* t2,
1519 const Vec3f* c0, const Vec3f* c1, const Vec3f* c2,
1520 unsigned int, unsigned int, unsigned) const
1521 {
1522 // XXX: we may optimize more by putting the declaration and the test outside of this method.
1523 Vec3d tmpVertex[3] = {*v0, *v1, *v2};
1524 // required, else assertion at begin of projectSphericalTriangle() fails!
1525 tmpVertex[0].normalize();
1526 tmpVertex[1].normalize();
1527 tmpVertex[2].normalize();
1528 if ( (outTexturePos) && (outColors))
1529 {
1530 const Vec2f tmpTexture[3] = {*t0, *t1, *t2};
1531 const Vec3f tmpColor[3] = {*c0, *c1, *c2};
1532 painter->projectSphericalTriangle(clippingCap, tmpVertex, outVertices, tmpTexture, outTexturePos, tmpColor, outColors, maxSqDistortion);
1533 }
1534 else if (outTexturePos)
1535 {
1536 const Vec2f tmpTexture[3] = {*t0, *t1, *t2};
1537 painter->projectSphericalTriangle(clippingCap, tmpVertex, outVertices, tmpTexture, outTexturePos, Q_NULLPTR, Q_NULLPTR, maxSqDistortion);
1538 }
1539 else if (outColors)
1540 {
1541 const Vec3f tmpColor[3] = {*c0, *c1, *c2};
1542 painter->projectSphericalTriangle(clippingCap, tmpVertex, outVertices, Q_NULLPTR, Q_NULLPTR, tmpColor, outColors, maxSqDistortion);
1543 }
1544 else
1545 painter->projectSphericalTriangle(clippingCap, tmpVertex, outVertices, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR, maxSqDistortion);
1546 }
1547
1548 // Draw the resulting arrays
drawResult()1549 void drawResult()
1550 {
1551 painter->setVertexPointer(3, GL_FLOAT, outVertices->constData());
1552 if (outTexturePos)
1553 painter->setTexCoordPointer(2, GL_FLOAT, outTexturePos->constData());
1554 if (outColors)
1555 painter->setColorPointer(3, GL_FLOAT, outColors->constData());
1556
1557 painter->enableClientStates(true, outTexturePos != Q_NULLPTR, outColors != Q_NULLPTR);
1558 painter->drawFromArray(StelPainter::Triangles, outVertices->size(), 0, false);
1559 painter->enableClientStates(false);
1560 }
1561
1562 private:
1563 StelPainter* painter;
1564 const SphericalCap* clippingCap;
1565 QVarLengthArray<Vec3f, 4096>* outVertices;
1566 QVarLengthArray<Vec3f, 4096>* outColors;
1567 QVarLengthArray<Vec2f, 4096>* outTexturePos;
1568 double maxSqDistortion;
1569 };
1570
drawStelVertexArray(const StelVertexArray & arr,bool checkDiscontinuity,Vec3d aberration)1571 void StelPainter::drawStelVertexArray(const StelVertexArray& arr, bool checkDiscontinuity, Vec3d aberration)
1572 {
1573 if (checkDiscontinuity && prj->hasDiscontinuity())
1574 {
1575 // The projection has discontinuities, so we need to make sure that no triangle is crossing them.
1576 drawStelVertexArray(arr.removeDiscontinuousTriangles(this->getProjector().data()), false, aberration);
1577 return;
1578 }
1579
1580 if (aberration==Vec3d(0.))
1581 {
1582 setVertexPointer(3, GL_DOUBLE, arr.vertex.constData());
1583 }
1584 else
1585 {
1586 QVector<Vec3d> aberredVertex(arr.vertex.size());
1587 for (int i=0; i<arr.vertex.size(); i++)
1588 {
1589 Q_ASSERT(qFuzzyCompare(arr.vertex.at(i).lengthSquared(), 1.0));
1590 Vec3d vec=arr.vertex.at(i)+aberration;
1591 vec.normalize();
1592 aberredVertex[i]=vec;
1593 }
1594 setVertexPointer(3, GL_DOUBLE, aberredVertex.constData());
1595 }
1596 if (arr.isTextured())
1597 {
1598 setTexCoordPointer(2, GL_FLOAT, arr.texCoords.constData());
1599 if (arr.isColored())
1600 {
1601 setColorPointer(3, GL_FLOAT, arr.colors.constData());
1602 enableClientStates(true, true, true);
1603 }
1604 else
1605 enableClientStates(true, true, false);
1606 }
1607 else
1608 {
1609 if (arr.isColored())
1610 {
1611 setColorPointer(3, GL_FLOAT, arr.colors.constData());
1612 enableClientStates(true, false, true);
1613 }
1614 else
1615 enableClientStates(true, false, false);
1616 }
1617 if (arr.isIndexed())
1618 drawFromArray(static_cast<StelPainter::DrawingMode>(arr.primitiveType), arr.indices.size(), 0, true, arr.indices.constData());
1619 else
1620 drawFromArray(static_cast<StelPainter::DrawingMode>(arr.primitiveType), arr.vertex.size());
1621
1622 enableClientStates(false);
1623 }
1624
drawSphericalTriangles(const StelVertexArray & va,bool textured,bool colored,const SphericalCap * clippingCap,bool doSubDivide,double maxSqDistortion)1625 void StelPainter::drawSphericalTriangles(const StelVertexArray& va, bool textured, bool colored, const SphericalCap* clippingCap, bool doSubDivide, double maxSqDistortion)
1626 {
1627 if (va.vertex.isEmpty())
1628 return;
1629
1630 Q_ASSERT(va.vertex.size()>2);
1631 polygonVertexArray.clear();
1632 polygonTextureCoordArray.clear();
1633
1634 indexArray.clear();
1635
1636 if (!doSubDivide)
1637 {
1638 // The simplest case, we don't need to iterate through the triangles at all.
1639 drawStelVertexArray(va);
1640 return;
1641 }
1642
1643 // the last case. It is the slowest, it process the triangles one by one.
1644 {
1645 // Project all the triangles of the VertexArray into our buffer arrays.
1646 VertexArrayProjector result = va.foreachTriangle(VertexArrayProjector(va, this, clippingCap, &polygonVertexArray, textured ? &polygonTextureCoordArray : Q_NULLPTR, colored ? &polygonColorArray : Q_NULLPTR, maxSqDistortion));
1647 result.drawResult();
1648 return;
1649 }
1650 }
1651
1652 // Draw the given SphericalPolygon.
drawSphericalRegion(SphericalRegion * poly,SphericalPolygonDrawMode drawMode,const SphericalCap * clippingCap,const bool doSubDivise,const double maxSqDistortion,const Vec3d & observerVelocity)1653 void StelPainter::drawSphericalRegion(SphericalRegion* poly, SphericalPolygonDrawMode drawMode, const SphericalCap* clippingCap, const bool doSubDivise, const double maxSqDistortion, const Vec3d &observerVelocity)
1654 {
1655 if (!prj->getBoundingCap().intersects(poly->getBoundingCap()))
1656 return;
1657
1658 bool oldCullFace = glState.cullFace;
1659
1660 switch (drawMode)
1661 {
1662 case SphericalPolygonDrawModeBoundary:
1663 if (doSubDivise || prj->intersectViewportDiscontinuity(poly->getBoundingCap()))
1664 drawGreatCircleArcs(poly->getOutlineVertexArray(), clippingCap);
1665 else
1666 drawStelVertexArray(poly->getOutlineVertexArray(), false);
1667 break;
1668 case SphericalPolygonDrawModeFill:
1669 case SphericalPolygonDrawModeTextureFill:
1670 case SphericalPolygonDrawModeTextureFillColormodulated:
1671 setCullFace(true);
1672 // The polygon is already tessellated as triangles
1673 if (doSubDivise || prj->intersectViewportDiscontinuity(poly->getBoundingCap()))
1674 // flag for color-modulated textured mode (e.g. for Milky Way/extincted)
1675 drawSphericalTriangles(poly->getFillVertexArray(observerVelocity), drawMode>=SphericalPolygonDrawModeTextureFill, drawMode==SphericalPolygonDrawModeTextureFillColormodulated, clippingCap, doSubDivise, maxSqDistortion);
1676 else
1677 drawStelVertexArray(poly->getFillVertexArray(observerVelocity), false);
1678
1679 setCullFace(oldCullFace);
1680 break;
1681 default:
1682 Q_ASSERT(0);
1683 }
1684 }
1685
1686
1687 /*************************************************************************
1688 draw a simple circle, 2d viewport coordinates in pixel
1689 *************************************************************************/
drawCircle(float x,float y,float r)1690 void StelPainter::drawCircle(float x, float y, float r)
1691 {
1692 if (r <= 1.0f)
1693 return;
1694 const Vec2f center(x,y);
1695 const Vec2f v_center(0.5f*prj->viewportXywh[2],0.5f*prj->viewportXywh[3]);
1696 const float R = v_center.length();
1697 const float d = (v_center-center).length();
1698 if (d > r+R || d < r-R)
1699 return;
1700 const int segments = 180;
1701 const float phi = 2.0f*M_PIf/segments;
1702 const float cp = std::cos(phi);
1703 const float sp = std::sin(phi);
1704 float dx = r;
1705 float dy = 0;
1706 static QVarLengthArray<Vec3f, 180> circleVertexArray(180);
1707
1708 for (int i=0;i<segments;i++)
1709 {
1710 circleVertexArray[i].set(x+dx,y+dy,0);
1711 r = dx*cp-dy*sp;
1712 dy = dx*sp+dy*cp;
1713 dx = r;
1714 }
1715 enableClientStates(true);
1716 setVertexPointer(3, GL_FLOAT, circleVertexArray.data());
1717 drawFromArray(LineLoop, 180, 0, false);
1718 enableClientStates(false);
1719 }
1720
drawSprite2dMode(float x,float y,float radius)1721 void StelPainter::drawSprite2dMode(float x, float y, float radius)
1722 {
1723 static float vertexData[] = {-10.,-10.,10.,-10., 10.,10., -10.,10.};
1724 static const float texCoordData[] = {0.,0., 1.,0., 0.,1., 1.,1.};
1725
1726 // Takes into account device pixel density and global scale ratio, as we are drawing 2D stuff.
1727 radius *= static_cast<float>(prj->getDevicePixelsPerPixel())*StelApp::getInstance().getGlobalScalingRatio();
1728
1729 vertexData[0]=x-radius; vertexData[1]=y-radius;
1730 vertexData[2]=x+radius; vertexData[3]=y-radius;
1731 vertexData[4]=x-radius; vertexData[5]=y+radius;
1732 vertexData[6]=x+radius; vertexData[7]=y+radius;
1733 enableClientStates(true, true);
1734 setVertexPointer(2, GL_FLOAT, vertexData);
1735 setTexCoordPointer(2, GL_FLOAT, texCoordData);
1736 drawFromArray(TriangleStrip, 4, 0, false);
1737 enableClientStates(false);
1738 }
1739
drawSprite2dModeNoDeviceScale(float x,float y,float radius)1740 void StelPainter::drawSprite2dModeNoDeviceScale(float x, float y, float radius)
1741 {
1742 drawSprite2dMode(x, y, radius/(static_cast<float>(prj->getDevicePixelsPerPixel())*StelApp::getInstance().getGlobalScalingRatio()));
1743 }
1744
drawSprite2dMode(const Vec3d & v,float radius)1745 void StelPainter::drawSprite2dMode(const Vec3d& v, float radius)
1746 {
1747 Vec3d win;
1748 if (prj->project(v, win))
1749 drawSprite2dMode(static_cast<float>(win[0]), static_cast<float>(win[1]), radius);
1750 }
1751
drawSprite2dMode(float x,float y,float radius,float rotation)1752 void StelPainter::drawSprite2dMode(float x, float y, float radius, float rotation)
1753 {
1754 static float vertexData[8];
1755 static const float texCoordData[] = {0.f,0.f, 1.f,0.f, 0.f,1.f, 1.f,1.f};
1756
1757 // compute the vertex coordinates applying the translation and the rotation
1758 static const float vertexBase[] = {-1., -1., 1., -1., -1., 1., 1., 1.};
1759 const float cosr = std::cos(rotation * M_PI_180f);
1760 const float sinr = std::sin(rotation * M_PI_180f);
1761
1762 // Takes into account device pixel density and global scale ratio, as we are drawing 2D stuff.
1763 radius *= static_cast<float>(prj->getDevicePixelsPerPixel())*StelApp::getInstance().getGlobalScalingRatio();
1764
1765 for (int i = 0; i < 8; i+=2)
1766 {
1767 vertexData[i] = x + radius * vertexBase[i] * cosr - radius * vertexBase[i+1] * sinr;
1768 vertexData[i+1] = y + radius * vertexBase[i] * sinr + radius * vertexBase[i+1] * cosr;
1769 }
1770
1771 enableClientStates(true, true);
1772 setVertexPointer(2, GL_FLOAT, vertexData);
1773 setTexCoordPointer(2, GL_FLOAT, texCoordData);
1774 drawFromArray(TriangleStrip, 4, 0, false);
1775 enableClientStates(false);
1776 }
1777
drawRect2d(float x,float y,float width,float height,bool textured)1778 void StelPainter::drawRect2d(float x, float y, float width, float height, bool textured)
1779 {
1780 static float vertexData[] = {-10.,-10.,10.,-10., 10.,10., -10.,10.};
1781 static const float texCoordData[] = {0.,0., 1.,0., 0.,1., 1.,1.};
1782 vertexData[0]=x; vertexData[1]=y;
1783 vertexData[2]=x+width; vertexData[3]=y;
1784 vertexData[4]=x; vertexData[5]=y+height;
1785 vertexData[6]=x+width; vertexData[7]=y+height;
1786 if (textured)
1787 {
1788 enableClientStates(true, true);
1789 setVertexPointer(2, GL_FLOAT, vertexData);
1790 setTexCoordPointer(2, GL_FLOAT, texCoordData);
1791 }
1792 else
1793 {
1794 enableClientStates(true);
1795 setVertexPointer(2, GL_FLOAT, vertexData);
1796 }
1797 drawFromArray(TriangleStrip, 4, 0, false);
1798 enableClientStates(false);
1799 }
1800
1801 /*************************************************************************
1802 Draw a GL_POINT at the given position
1803 *************************************************************************/
drawPoint2d(float x,float y)1804 void StelPainter::drawPoint2d(float x, float y)
1805 {
1806 static float vertexData[] = {0.,0.};
1807 vertexData[0]=x;
1808 vertexData[1]=y;
1809
1810 enableClientStates(true);
1811 setVertexPointer(2, GL_FLOAT, vertexData);
1812 drawFromArray(Points, 1, 0, false);
1813 enableClientStates(false);
1814 }
1815
1816
1817 /*************************************************************************
1818 Draw a line between the 2 points.
1819 *************************************************************************/
drawLine2d(const float x1,const float y1,const float x2,const float y2)1820 void StelPainter::drawLine2d(const float x1, const float y1, const float x2, const float y2)
1821 {
1822 static float vertexData[] = {0.,0.,0.,0.};
1823 vertexData[0]=x1;
1824 vertexData[1]=y1;
1825 vertexData[2]=x2;
1826 vertexData[3]=y2;
1827
1828 enableClientStates(true);
1829 setVertexPointer(2, GL_FLOAT, vertexData);
1830 drawFromArray(Lines, 2, 0, false);
1831 enableClientStates(false);
1832 }
1833
1834 ///////////////////////////////////////////////////////////////////////////
1835 // Drawing methods for general (non-linear) mode.
1836 // This used to draw a full sphere. Since 0.13 it's possible to have a spherical zone only.
sSphere(const double radius,const double oneMinusOblateness,const unsigned int slices,const unsigned int stacks,const bool orientInside,const bool flipTexture,const float topAngle,const float bottomAngle)1837 void StelPainter::sSphere(const double radius, const double oneMinusOblateness,
1838 const unsigned int slices, const unsigned int stacks,
1839 const bool orientInside, const bool flipTexture, const float topAngle, const float bottomAngle)
1840 {
1841 double x, y, z;
1842 GLfloat s=0.f, t=0.f;
1843 GLfloat nsign;
1844
1845 if (orientInside)
1846 {
1847 nsign = -1.f;
1848 t=0.f; // from inside texture is reversed
1849 }
1850 else
1851 {
1852 nsign = 1.f;
1853 t=1.f;
1854 }
1855
1856 const float* cos_sin_rho = Q_NULLPTR;
1857 Q_ASSERT(topAngle<bottomAngle); // don't forget: These are opening angles counted from top.
1858 if ((bottomAngle>3.1415f) && (topAngle<0.0001f)) // safety margin.
1859 cos_sin_rho = StelUtils::ComputeCosSinRho(stacks);
1860 else
1861 {
1862 const float drho = (bottomAngle-topAngle) / stacks; // deltaRho: originally just 180degrees/stacks, now the range clamped.
1863 cos_sin_rho = StelUtils::ComputeCosSinRhoZone(drho, stacks, M_PIf-bottomAngle);
1864 }
1865 // Allow parameters so that pole regions may remain free.
1866 const float* cos_sin_rho_p;
1867
1868 const float* cos_sin_theta = StelUtils::ComputeCosSinTheta(slices);
1869 const float *cos_sin_theta_p;
1870
1871 // texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis
1872 // t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes)
1873 // cannot use triangle fan on texturing (s coord. at top/bottom tip varies)
1874 // If the texture is flipped, we iterate the coordinates backward.
1875 const GLfloat ds = (flipTexture ? -1.f : 1.f) / slices;
1876 const GLfloat dt = nsign / stacks; // from inside texture is reversed
1877
1878 // draw intermediate as quad strips
1879 static QVector<double> vertexArr;
1880 static QVector<float> texCoordArr;
1881 static QVector<float> colorArr;
1882 static QVector<unsigned short> indiceArr;
1883
1884 texCoordArr.resize(0);
1885 vertexArr.resize(0);
1886 colorArr.resize(0);
1887 indiceArr.resize(0);
1888
1889 // LGTM comments: the floats are always <=1. We still prefer float multiplication (with insignificant accuracy loss) for speed.
1890 uint i, j;
1891 for (i = 0,cos_sin_rho_p = cos_sin_rho; i < stacks; ++i,cos_sin_rho_p+=2)
1892 {
1893 s = flipTexture ? 1.f : 0.f;
1894 for (j = 0,cos_sin_theta_p = cos_sin_theta; j<=slices;++j,cos_sin_theta_p+=2)
1895 {
1896 x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[1]);
1897 y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[1]);
1898 z = static_cast<double>(nsign * cos_sin_rho_p[0]);
1899 texCoordArr << s << t;
1900 vertexArr << x * radius << y * radius << z * oneMinusOblateness * radius;
1901 x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[3]);
1902 y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[3]);
1903 z = static_cast<double>(nsign * cos_sin_rho_p[2]);
1904 texCoordArr << s << t - dt;
1905 vertexArr << x * radius << y * radius << z * oneMinusOblateness * radius;
1906 s += ds;
1907 }
1908 unsigned int offset = i*(slices+1)*2;
1909 for (j = 2;j<slices*2+2;j+=2)
1910 {
1911 indiceArr << static_cast<unsigned short>(offset+j-2) << static_cast<unsigned short>(offset+j-1) << static_cast<unsigned short>(offset+j);
1912 indiceArr << static_cast<unsigned short>(offset+j) << static_cast<unsigned short>(offset+j-1) << static_cast<unsigned short>(offset+j+1);
1913 }
1914 t -= dt;
1915 }
1916
1917 // Draw the array now
1918 setArrays(reinterpret_cast<const Vec3d*>(vertexArr.constData()), reinterpret_cast<const Vec2f*>(texCoordArr.constData()));
1919 drawFromArray(Triangles, indiceArr.size(), 0, true, indiceArr.constData());
1920 }
1921
computeSphereNoLight(double radius,double oneMinusOblateness,unsigned int slices,unsigned int stacks,int orientInside,bool flipTexture,float topAngle,float bottomAngle)1922 StelVertexArray StelPainter::computeSphereNoLight(double radius, double oneMinusOblateness, unsigned int slices, unsigned int stacks,
1923 int orientInside, bool flipTexture, float topAngle, float bottomAngle)
1924 {
1925 StelVertexArray result(StelVertexArray::Triangles);
1926 double x, y, z;
1927 GLfloat s=0.f, t=0.f;
1928 GLuint i, j;
1929 GLfloat nsign;
1930 if (orientInside)
1931 {
1932 nsign = -1.f;
1933 t=0.f; // from inside texture is reversed
1934 }
1935 else
1936 {
1937 nsign = 1.f;
1938 t=1.f;
1939 }
1940
1941 const float* cos_sin_rho = Q_NULLPTR; //StelUtils::ComputeCosSinRho(stacks);
1942 Q_ASSERT(topAngle<bottomAngle); // don't forget: These are opening angles counted from top.
1943 if ((bottomAngle>3.1415f) && (topAngle<0.0001f)) // safety margin.
1944 cos_sin_rho = StelUtils::ComputeCosSinRho(stacks);
1945 else
1946 {
1947 const float drho = (bottomAngle-topAngle) / stacks; // deltaRho: originally just 180degrees/stacks, now the range clamped.
1948 cos_sin_rho = StelUtils::ComputeCosSinRhoZone(drho, stacks, M_PIf-bottomAngle);
1949 }
1950 // Allow parameters so that pole regions may remain free.
1951 const float* cos_sin_rho_p;
1952
1953 const float* cos_sin_theta = StelUtils::ComputeCosSinTheta(slices);
1954 const float *cos_sin_theta_p;
1955
1956 // texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis
1957 // t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes)
1958 // cannot use triangle fan on texturing (s coord. at top/bottom tip varies)
1959 // If the texture is flipped, we iterate the coordinates backward.
1960 const GLfloat ds = (flipTexture ? -1.f : 1.f) / slices;
1961 const GLfloat dt = nsign / stacks; // from inside texture is reversed
1962
1963 // draw intermediate as quad strips
1964 // LGTM comments: the floats are always <=1. We still prefer float multiplication (with insignificant accuracy loss) for speed.
1965 for (i = 0,cos_sin_rho_p = cos_sin_rho; i < stacks; ++i,cos_sin_rho_p+=2)
1966 {
1967 s = !flipTexture ? 0.f : 1.f;
1968 for (j = 0,cos_sin_theta_p = cos_sin_theta; j<=slices;++j,cos_sin_theta_p+=2)
1969 {
1970 x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[1]);
1971 y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[1]);
1972 z = static_cast<double>(nsign * cos_sin_rho_p[0]);
1973 result.texCoords << Vec2f(s,t);
1974 result.vertex << Vec3d(x*radius, y*radius, z*oneMinusOblateness*radius);
1975 x = static_cast<double>(-cos_sin_theta_p[1] * cos_sin_rho_p[3]);
1976 y = static_cast<double>(cos_sin_theta_p[0] * cos_sin_rho_p[3]);
1977 z = static_cast<double>(nsign * cos_sin_rho_p[2]);
1978 result.texCoords << Vec2f(s, t-dt);
1979 result.vertex << Vec3d(x*radius, y*radius, z*oneMinusOblateness*radius);
1980 s += ds;
1981 }
1982 unsigned int offset = i*(slices+1)*2;
1983 for (j = 2;j<slices*2+2;j+=2)
1984 {
1985 result.indices << static_cast<unsigned short>(offset+j-2) << static_cast<unsigned short>(offset+j-1) << static_cast<unsigned short>(offset+j);
1986 result.indices << static_cast<unsigned short>(offset+j) << static_cast<unsigned short>(offset+j-1) << static_cast<unsigned short>(offset+j+1);
1987 }
1988 t -= dt;
1989 }
1990 return result;
1991 }
1992
1993 // Reimplementation of gluCylinder : glu is overrided for non standard projection
sCylinder(double radius,double height,int slices,int orientInside)1994 void StelPainter::sCylinder(double radius, double height, int slices, int orientInside)
1995 {
1996 if (orientInside)
1997 glCullFace(GL_FRONT);
1998
1999 static QVarLengthArray<Vec2f, 512> texCoordArray;
2000 static QVarLengthArray<Vec3d, 512> vertexArray;
2001 texCoordArray.clear();
2002 vertexArray.clear();
2003 float s = 0.f;
2004 double x, y;
2005 const float ds = 1.f / slices;
2006 const double da = 2. * M_PI / slices;
2007 for (int i = 0; i <= slices; ++i)
2008 {
2009 x = std::sin(da*i);
2010 y = std::cos(da*i);
2011 texCoordArray.append(Vec2f(s, 0.f));
2012 vertexArray.append(Vec3d(x*radius, y*radius, 0.));
2013 texCoordArray.append(Vec2f(s, 1.f));
2014 vertexArray.append(Vec3d(x*radius, y*radius, height));
2015 s += ds;
2016 }
2017 setArrays(vertexArray.constData(), texCoordArray.constData());
2018 drawFromArray(TriangleStrip, vertexArray.size());
2019
2020 if (orientInside)
2021 glCullFace(GL_BACK);
2022 }
2023
initGLShaders()2024 void StelPainter::initGLShaders()
2025 {
2026 qDebug() << "Initializing basic GL shaders... ";
2027 // Basic shader: just vertex filled with plain color
2028 QOpenGLShader vshader3(QOpenGLShader::Vertex);
2029 const char *vsrc3 =
2030 "attribute mediump vec3 vertex;\n"
2031 "uniform mediump mat4 projectionMatrix;\n"
2032 "void main(void)\n"
2033 "{\n"
2034 " gl_Position = projectionMatrix*vec4(vertex, 1.);\n"
2035 "}\n";
2036 vshader3.compileSourceCode(vsrc3);
2037 if (!vshader3.log().isEmpty()) { qWarning() << "StelPainter: Warnings while compiling vshader3: " << vshader3.log(); }
2038 QOpenGLShader fshader3(QOpenGLShader::Fragment);
2039 const char *fsrc3 =
2040 "uniform mediump vec4 color;\n"
2041 "void main(void)\n"
2042 "{\n"
2043 " gl_FragColor = color;\n"
2044 "}\n";
2045 fshader3.compileSourceCode(fsrc3);
2046 if (!fshader3.log().isEmpty()) { qWarning() << "StelPainter: Warnings while compiling fshader3: " << fshader3.log(); }
2047 basicShaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
2048 basicShaderProgram->addShader(&vshader3);
2049 basicShaderProgram->addShader(&fshader3);
2050 linkProg(basicShaderProgram, "basicShaderProgram");
2051 basicShaderVars.projectionMatrix = basicShaderProgram->uniformLocation("projectionMatrix");
2052 basicShaderVars.color = basicShaderProgram->uniformLocation("color");
2053 basicShaderVars.vertex = basicShaderProgram->attributeLocation("vertex");
2054
2055
2056 // Basic shader: vertex filled with interpolated color
2057 QOpenGLShader vshaderInterpolatedColor(QOpenGLShader::Vertex);
2058 const char *vshaderInterpolatedColorSrc =
2059 "attribute mediump vec3 vertex;\n"
2060 "attribute mediump vec4 color;\n"
2061 "uniform mediump mat4 projectionMatrix;\n"
2062 "varying mediump vec4 fragcolor;\n"
2063 "void main(void)\n"
2064 "{\n"
2065 " gl_Position = projectionMatrix*vec4(vertex, 1.);\n"
2066 " fragcolor = color;\n"
2067 "}\n";
2068 vshaderInterpolatedColor.compileSourceCode(vshaderInterpolatedColorSrc);
2069 if (!vshaderInterpolatedColor.log().isEmpty()) {
2070 qWarning() << "StelPainter: Warnings while compiling vshaderInterpolatedColor: " << vshaderInterpolatedColor.log();
2071 }
2072 QOpenGLShader fshaderInterpolatedColor(QOpenGLShader::Fragment);
2073 const char *fshaderInterpolatedColorSrc =
2074 "varying mediump vec4 fragcolor;\n"
2075 "void main(void)\n"
2076 "{\n"
2077 " gl_FragColor = fragcolor;\n"
2078 "}\n";
2079 fshaderInterpolatedColor.compileSourceCode(fshaderInterpolatedColorSrc);
2080 if (!fshaderInterpolatedColor.log().isEmpty()) {
2081 qWarning() << "StelPainter: Warnings while compiling fshaderInterpolatedColor: " << fshaderInterpolatedColor.log();
2082 }
2083 colorShaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
2084 colorShaderProgram->addShader(&vshaderInterpolatedColor);
2085 colorShaderProgram->addShader(&fshaderInterpolatedColor);
2086 linkProg(colorShaderProgram, "colorShaderProgram");
2087 colorShaderVars.projectionMatrix = colorShaderProgram->uniformLocation("projectionMatrix");
2088 colorShaderVars.color = colorShaderProgram->attributeLocation("color");
2089 colorShaderVars.vertex = colorShaderProgram->attributeLocation("vertex");
2090
2091 // Basic texture shader program
2092 QOpenGLShader vshader2(QOpenGLShader::Vertex);
2093 const char *vsrc2 =
2094 "attribute highp vec3 vertex;\n"
2095 "attribute mediump vec2 texCoord;\n"
2096 "uniform mediump mat4 projectionMatrix;\n"
2097 "varying mediump vec2 texc;\n"
2098 "void main(void)\n"
2099 "{\n"
2100 " gl_Position = projectionMatrix * vec4(vertex, 1.);\n"
2101 " texc = texCoord;\n"
2102 "}\n";
2103 vshader2.compileSourceCode(vsrc2);
2104 if (!vshader2.log().isEmpty()) { qWarning() << "StelPainter: Warnings while compiling vshader2: " << vshader2.log(); }
2105
2106 QOpenGLShader fshader2(QOpenGLShader::Fragment);
2107 const auto fsrc2 =
2108 makeDitheringShader()+
2109 "varying mediump vec2 texc;\n"
2110 "uniform sampler2D tex;\n"
2111 "uniform mediump vec4 texColor;\n"
2112 "void main(void)\n"
2113 "{\n"
2114 " gl_FragColor = dither(texture2D(tex, texc)*texColor);\n"
2115 "}\n";
2116 fshader2.compileSourceCode(fsrc2);
2117 if (!fshader2.log().isEmpty()) { qWarning() << "StelPainter: Warnings while compiling fshader2: " << fshader2.log(); }
2118
2119 texturesShaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
2120 texturesShaderProgram->addShader(&vshader2);
2121 texturesShaderProgram->addShader(&fshader2);
2122 linkProg(texturesShaderProgram, "texturesShaderProgram");
2123 texturesShaderVars.projectionMatrix = texturesShaderProgram->uniformLocation("projectionMatrix");
2124 texturesShaderVars.texCoord = texturesShaderProgram->attributeLocation("texCoord");
2125 texturesShaderVars.vertex = texturesShaderProgram->attributeLocation("vertex");
2126 texturesShaderVars.texColor = texturesShaderProgram->uniformLocation("texColor");
2127 texturesShaderVars.texture = texturesShaderProgram->uniformLocation("tex");
2128 texturesShaderVars.bayerPattern = texturesShaderProgram->uniformLocation("bayerPattern");
2129 texturesShaderVars.rgbMaxValue = texturesShaderProgram->uniformLocation("rgbMaxValue");
2130
2131 // Texture shader program + interpolated color per vertex
2132 QOpenGLShader vshader4(QOpenGLShader::Vertex);
2133 const char *vsrc4 =
2134 "attribute highp vec3 vertex;\n"
2135 "attribute mediump vec2 texCoord;\n"
2136 "attribute mediump vec4 color;\n"
2137 "uniform mediump mat4 projectionMatrix;\n"
2138 "varying mediump vec2 texc;\n"
2139 "varying mediump vec4 outColor;\n"
2140 "void main(void)\n"
2141 "{\n"
2142 " gl_Position = projectionMatrix * vec4(vertex, 1.);\n"
2143 " texc = texCoord;\n"
2144 " outColor = color;\n"
2145 "}\n";
2146 vshader4.compileSourceCode(vsrc4);
2147 if (!vshader4.log().isEmpty()) { qWarning() << "StelPainter: Warnings while compiling vshader4: " << vshader4.log(); }
2148
2149 QOpenGLShader fshader4(QOpenGLShader::Fragment);
2150 const auto fsrc4 =
2151 makeDitheringShader()+
2152 makeSaturationShader()+
2153 "varying mediump vec2 texc;\n"
2154 "varying mediump vec4 outColor;\n"
2155 "uniform sampler2D tex;\n"
2156 "uniform lowp float saturation;\n"
2157 "void main(void)\n"
2158 "{\n"
2159 " gl_FragColor = dither(texture2D(tex, texc)*outColor);\n"
2160 " if (saturation != 1.0)\n"
2161 " gl_FragColor.rgb = saturate(gl_FragColor.rgb, saturation);\n"
2162 "}\n";
2163 fshader4.compileSourceCode(fsrc4);
2164 if (!fshader4.log().isEmpty()) { qWarning() << "StelPainter: Warnings while compiling fshader4: " << fshader4.log(); }
2165
2166 texturesColorShaderProgram = new QOpenGLShaderProgram(QOpenGLContext::currentContext());
2167 texturesColorShaderProgram->addShader(&vshader4);
2168 texturesColorShaderProgram->addShader(&fshader4);
2169 linkProg(texturesColorShaderProgram, "texturesColorShaderProgram");
2170 texturesColorShaderVars.projectionMatrix = texturesColorShaderProgram->uniformLocation("projectionMatrix");
2171 texturesColorShaderVars.texCoord = texturesColorShaderProgram->attributeLocation("texCoord");
2172 texturesColorShaderVars.vertex = texturesColorShaderProgram->attributeLocation("vertex");
2173 texturesColorShaderVars.color = texturesColorShaderProgram->attributeLocation("color");
2174 texturesColorShaderVars.texture = texturesColorShaderProgram->uniformLocation("tex");
2175 texturesColorShaderVars.bayerPattern = texturesColorShaderProgram->uniformLocation("bayerPattern");
2176 texturesColorShaderVars.rgbMaxValue = texturesColorShaderProgram->uniformLocation("rgbMaxValue");
2177 texturesColorShaderVars.saturation = texturesColorShaderProgram->uniformLocation("saturation");
2178 }
2179
2180
deinitGLShaders()2181 void StelPainter::deinitGLShaders()
2182 {
2183 delete basicShaderProgram;
2184 basicShaderProgram = Q_NULLPTR;
2185 delete colorShaderProgram;
2186 colorShaderProgram = Q_NULLPTR;
2187 delete texturesShaderProgram;
2188 texturesShaderProgram = Q_NULLPTR;
2189 delete texturesColorShaderProgram;
2190 texturesColorShaderProgram = Q_NULLPTR;
2191 texCache.clear();
2192 }
2193
2194
setArrays(const Vec3d * vertices,const Vec2f * texCoords,const Vec3f * colorArray,const Vec3f * normalArray)2195 void StelPainter::setArrays(const Vec3d* vertices, const Vec2f* texCoords, const Vec3f* colorArray, const Vec3f* normalArray)
2196 {
2197 enableClientStates(vertices, texCoords, colorArray, normalArray);
2198 setVertexPointer(3, GL_DOUBLE, vertices);
2199 setTexCoordPointer(2, GL_FLOAT, texCoords);
2200 setColorPointer(3, GL_FLOAT, colorArray);
2201 setNormalPointer(GL_FLOAT, normalArray);
2202 }
2203
setArrays(const Vec3f * vertices,const Vec2f * texCoords,const Vec3f * colorArray,const Vec3f * normalArray)2204 void StelPainter::setArrays(const Vec3f* vertices, const Vec2f* texCoords, const Vec3f* colorArray, const Vec3f* normalArray)
2205 {
2206 enableClientStates(vertices, texCoords, colorArray, normalArray);
2207 setVertexPointer(3, GL_FLOAT, vertices);
2208 setTexCoordPointer(2, GL_FLOAT, texCoords);
2209 setColorPointer(3, GL_FLOAT, colorArray);
2210 setNormalPointer(GL_FLOAT, normalArray);
2211 }
2212
enableClientStates(bool vertex,bool texture,bool color,bool normal)2213 void StelPainter::enableClientStates(bool vertex, bool texture, bool color, bool normal)
2214 {
2215 vertexArray.enabled = vertex;
2216 texCoordArray.enabled = texture;
2217 colorArray.enabled = color;
2218 normalArray.enabled = normal;
2219 }
2220
drawFromArray(DrawingMode mode,int count,int offset,bool doProj,const unsigned short * indices)2221 void StelPainter::drawFromArray(DrawingMode mode, int count, int offset, bool doProj, const unsigned short* indices)
2222 {
2223 ArrayDesc projectedVertexArray = vertexArray;
2224 if (doProj)
2225 {
2226 // Project the vertex array using current projection
2227 if (indices)
2228 projectedVertexArray = projectArray(vertexArray, 0, count, indices + offset);
2229 else
2230 projectedVertexArray = projectArray(vertexArray, offset, count, Q_NULLPTR);
2231 }
2232
2233 QOpenGLShaderProgram* pr=Q_NULLPTR;
2234
2235 const Mat4f& m = getProjector()->getProjectionMatrix();
2236 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]);
2237
2238 const auto rgbMaxValue=calcRGBMaxValue(ditheringMode);
2239 if (!texCoordArray.enabled && !colorArray.enabled && !normalArray.enabled)
2240 {
2241 pr = basicShaderProgram;
2242 pr->bind();
2243 pr->setAttributeArray(basicShaderVars.vertex, projectedVertexArray.type, projectedVertexArray.pointer, projectedVertexArray.size);
2244 pr->enableAttributeArray(basicShaderVars.vertex);
2245 pr->setUniformValue(basicShaderVars.projectionMatrix, qMat);
2246 pr->setUniformValue(basicShaderVars.color, currentColor[0], currentColor[1], currentColor[2], currentColor[3]);
2247 }
2248 else if (texCoordArray.enabled && !colorArray.enabled && !normalArray.enabled)
2249 {
2250 pr = texturesShaderProgram;
2251 pr->bind();
2252 pr->setAttributeArray(texturesShaderVars.vertex, projectedVertexArray.type, projectedVertexArray.pointer, projectedVertexArray.size);
2253 pr->enableAttributeArray(texturesShaderVars.vertex);
2254 pr->setUniformValue(texturesShaderVars.projectionMatrix, qMat);
2255 pr->setUniformValue(texturesShaderVars.texColor, currentColor[0], currentColor[1], currentColor[2], currentColor[3]);
2256 pr->setAttributeArray(texturesShaderVars.texCoord, texCoordArray.type, texCoordArray.pointer, texCoordArray.size);
2257 pr->enableAttributeArray(texturesShaderVars.texCoord);
2258 //pr->setUniformValue(texturesShaderVars.texture, 0); // use texture unit 0
2259 glActiveTexture(GL_TEXTURE1);
2260 if(!bayerPatternTex)
2261 bayerPatternTex=makeBayerPatternTexture(*this);
2262 glBindTexture(GL_TEXTURE_2D, bayerPatternTex);
2263 pr->setUniformValue(texturesShaderVars.bayerPattern, 1);
2264 pr->setUniformValue(texturesShaderVars.rgbMaxValue, rgbMaxValue[0], rgbMaxValue[1], rgbMaxValue[2]);
2265 }
2266 else if (texCoordArray.enabled && colorArray.enabled && !normalArray.enabled)
2267 {
2268 pr = texturesColorShaderProgram;
2269 pr->bind();
2270 pr->setAttributeArray(texturesColorShaderVars.vertex, projectedVertexArray.type, projectedVertexArray.pointer, projectedVertexArray.size);
2271 pr->enableAttributeArray(texturesColorShaderVars.vertex);
2272 pr->setUniformValue(texturesColorShaderVars.projectionMatrix, qMat);
2273 pr->setAttributeArray(texturesColorShaderVars.texCoord, texCoordArray.type, texCoordArray.pointer, texCoordArray.size);
2274 pr->enableAttributeArray(texturesColorShaderVars.texCoord);
2275 pr->setAttributeArray(texturesColorShaderVars.color, colorArray.type, colorArray.pointer, colorArray.size);
2276 pr->enableAttributeArray(texturesColorShaderVars.color);
2277 //pr->setUniformValue(texturesShaderVars.texture, 0); // use texture unit 0
2278 glActiveTexture(GL_TEXTURE1);
2279 if(!bayerPatternTex)
2280 bayerPatternTex=makeBayerPatternTexture(*this);
2281 glBindTexture(GL_TEXTURE_2D, bayerPatternTex);
2282 pr->setUniformValue(texturesColorShaderVars.bayerPattern, 1);
2283 pr->setUniformValue(texturesColorShaderVars.rgbMaxValue, rgbMaxValue[0], rgbMaxValue[1], rgbMaxValue[2]);
2284 pr->setUniformValue(texturesColorShaderVars.saturation, saturation);
2285 }
2286 else if (!texCoordArray.enabled && colorArray.enabled && !normalArray.enabled)
2287 {
2288 pr = colorShaderProgram;
2289 pr->bind();
2290 pr->setAttributeArray(colorShaderVars.vertex, projectedVertexArray.type, projectedVertexArray.pointer, projectedVertexArray.size);
2291 pr->enableAttributeArray(colorShaderVars.vertex);
2292 pr->setUniformValue(colorShaderVars.projectionMatrix, qMat);
2293 pr->setAttributeArray(colorShaderVars.color, colorArray.type, colorArray.pointer, colorArray.size);
2294 pr->enableAttributeArray(colorShaderVars.color);
2295 }
2296 else
2297 {
2298 qDebug() << "Unhandled parameters." << texCoordArray.enabled << colorArray.enabled << normalArray.enabled;
2299 Q_ASSERT(0);
2300 return;
2301 }
2302
2303 if (indices)
2304 glDrawElements(mode, count, GL_UNSIGNED_SHORT, indices + offset);
2305 else
2306 glDrawArrays(mode, offset, count);
2307
2308 if (pr==texturesColorShaderProgram)
2309 {
2310 pr->disableAttributeArray(texturesColorShaderVars.texCoord);
2311 pr->disableAttributeArray(texturesColorShaderVars.vertex);
2312 pr->disableAttributeArray(texturesColorShaderVars.color);
2313 }
2314 else if (pr==texturesShaderProgram)
2315 {
2316 pr->disableAttributeArray(texturesShaderVars.texCoord);
2317 pr->disableAttributeArray(texturesShaderVars.vertex);
2318 }
2319 else if (pr == basicShaderProgram)
2320 {
2321 pr->disableAttributeArray(basicShaderVars.vertex);
2322 }
2323 else if (pr == colorShaderProgram)
2324 {
2325 pr->disableAttributeArray(colorShaderVars.vertex);
2326 pr->disableAttributeArray(colorShaderVars.color);
2327 }
2328 if (pr)
2329 pr->release();
2330 }
2331
2332
projectArray(const StelPainter::ArrayDesc & array,int offset,int count,const unsigned short * indices)2333 StelPainter::ArrayDesc StelPainter::projectArray(const StelPainter::ArrayDesc& array, int offset, int count, const unsigned short* indices)
2334 {
2335 // XXX: we should use a more generic way to test whether or not to do the projection.
2336 if (dynamic_cast<StelProjector2d*>(prj.data()))
2337 {
2338 return array;
2339 }
2340
2341 Q_ASSERT(array.size == 3);
2342 Q_ASSERT(array.type == GL_DOUBLE);
2343 const Vec3d* vecArray = reinterpret_cast<const Vec3d *>(const_cast<void*>(array.pointer));
2344
2345 // We have two different cases :
2346 // 1) We are not using an indice array. In that case the size of the array is known
2347 // 2) We are using an indice array. In that case we have to find the max value by iterating through the indices.
2348 if (!indices)
2349 {
2350 polygonVertexArray.resize(offset + count);
2351 prj->project(count, vecArray + offset, polygonVertexArray.data() + offset);
2352 } else
2353 {
2354 // we need to find the max value of the indices !
2355 unsigned short max = 0;
2356 for (int i = offset; i < offset + count; ++i)
2357 {
2358 max = std::max(max, indices[i]);
2359 }
2360 polygonVertexArray.resize(max+1);
2361 prj->project(max + 1, vecArray + offset, polygonVertexArray.data() + offset);
2362 }
2363
2364 ArrayDesc ret;
2365 ret.size = 3;
2366 ret.type = GL_FLOAT;
2367 ret.pointer = polygonVertexArray.constData();
2368 ret.enabled = array.enabled;
2369 return ret;
2370 }
2371
2372