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