1 // render.cpp
2 //
3 // Copyright (C) 2001-2008, the Celestia Development Team
4 // Original version by Chris Laurel <claurel@gmail.com>
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
10 
11 #include <algorithm>
12 #include <cstdio>
13 #include <cstring>
14 #include <cassert>
15 #include <sstream>
16 #include <iomanip>
17 
18 
19 #define DEBUG_COALESCE 0
20 
21 //#define DEBUG_HDR
22 #ifdef DEBUG_HDR
23 //#define DEBUG_HDR_FILE
24 //#define DEBUG_HDR_ADAPT
25 //#define DEBUG_HDR_TONEMAP
26 #endif
27 #ifdef DEBUG_HDR_FILE
28 #include <fstream>
29 std::ofstream hdrlog;
30 #define HDR_LOG     hdrlog
31 #else
32 #define HDR_LOG     cout
33 #endif
34 
35 #ifdef USE_HDR
36 #define BLUR_PASS_COUNT     2
37 #define BLUR_SIZE           128
38 #define DEFAULT_EXPOSURE    -23.35f
39 #define EXPOSURE_HALFLIFE   0.4f
40 //#define USE_BLOOM_LISTS
41 #endif
42 
43 #ifndef _WIN32
44 #ifndef TARGET_OS_MAC
45 #include <config.h>
46 #endif
47 #endif /* _WIN32 */
48 
49 #include <celutil/debug.h>
50 #include <celmath/frustum.h>
51 #include <celmath/distance.h>
52 #include <celmath/intersect.h>
53 #include <celutil/utf8.h>
54 #include <celutil/util.h>
55 #include <celutil/timer.h>
56 #include "gl.h"
57 #include "astro.h"
58 #include "glext.h"
59 #include "vecgl.h"
60 #include "glshader.h"
61 #include "shadermanager.h"
62 #include "spheremesh.h"
63 #include "lodspheremesh.h"
64 #include "model.h"
65 #include "regcombine.h"
66 #include "vertexprog.h"
67 #include "texmanager.h"
68 #include "meshmanager.h"
69 #include "render.h"
70 #include "renderinfo.h"
71 #include "renderglsl.h"
72 #include "axisarrow.h"
73 #include "frametree.h"
74 #include "timelinephase.h"
75 #include "skygrid.h"
76 
77 using namespace std;
78 
79 #define FOV           45.0f
80 #define NEAR_DIST      0.5f
81 #define FAR_DIST       1.0e9f
82 
83 // This should be in the GL headers, but where?
84 #ifndef GL_COLOR_SUM_EXT
85 #define GL_COLOR_SUM_EXT 0x8458
86 #endif
87 
88 static const float  STAR_DISTANCE_LIMIT  = 1.0e6f;
89 static const int REF_DISTANCE_TO_SCREEN  = 400; //[mm]
90 
91 // Contribution from planetshine beyond this distance (in units of object radius)
92 // is considered insignificant.
93 static const float PLANETSHINE_DISTANCE_LIMIT_FACTOR = 100.0f;
94 
95 // Planetshine from objects less than this pixel size is treated as insignificant
96 // and will be ignored.
97 static const float PLANETSHINE_PIXEL_SIZE_LIMIT      =   0.1f;
98 
99 // Distance from the Sun at which comet tails will start to fade out
100 static const float COMET_TAIL_ATTEN_DIST_SOL = astro::AUtoKilometers(5.0f);
101 
102 static const int StarVertexListSize = 1024;
103 
104 // Fractional pixel offset used when rendering text as texture mapped
105 // quads to ensure consistent mapping of texels to pixels.
106 static const float PixelOffset = 0.125f;
107 
108 // These two values constrain the near and far planes of the view frustum
109 // when rendering planet and object meshes.  The near plane will never be
110 // closer than MinNearPlaneDistance, and the far plane is set so that far/near
111 // will not exceed MaxFarNearRatio.
112 static const float MinNearPlaneDistance = 0.0001f; // km
113 static const float MaxFarNearRatio      = 2000000.0f;
114 
115 static const float RenderDistance       = 50.0f;
116 
117 // Star disc size in pixels
118 static const float BaseStarDiscSize      = 5.0f;
119 static const float MaxScaledDiscStarSize = 8.0f;
120 static const float GlareOpacity = 0.65f;
121 
122 static const float MinRelativeOccluderRadius = 0.005f;
123 
124 static const float CubeCornerToCenterDistance = (float) sqrt(3.0);
125 
126 
127 // The minimum apparent size of an objects orbit in pixels before we display
128 // a label for it.  This minimizes label clutter.
129 static const float MinOrbitSizeForLabel = 20.0f;
130 
131 // The minimum apparent size of a surface feature in pixels before we display
132 // a label for it.
133 static const float MinFeatureSizeForLabel = 20.0f;
134 
135 /* The maximum distance of the observer to the origin of coordinates before
136    asterism lines and labels start to linearly fade out (in uLY) */
137 static const float MaxAsterismLabelsConstDist  = 6.0e6f;
138 static const float MaxAsterismLinesConstDist   = 6.0e8f;
139 
140 /* The maximum distance of the observer to the origin of coordinates before
141    asterisms labels and lines fade out completely (in uLY) */
142 static const float MaxAsterismLabelsDist = 2.0e7f;
143 static const float MaxAsterismLinesDist  = 6.52e10f;
144 
145 // Maximum size of a solar system in light years. Features beyond this distance
146 // will not necessarily be rendered correctly. This limit is used for
147 // visibility culling of solar systems.
148 static const float MaxSolarSystemSize = 1.0f;
149 
150 
151 // Static meshes and textures used by all instances of Simulation
152 
153 static bool commonDataInitialized = false;
154 
155 static const string EMPTY_STRING("");
156 
157 
158 LODSphereMesh* g_lodSphere = NULL;
159 
160 static Texture* normalizationTex = NULL;
161 
162 static Texture* starTex = NULL;
163 static Texture* glareTex = NULL;
164 static Texture* shadowTex = NULL;
165 static Texture* gaussianDiscTex = NULL;
166 static Texture* gaussianGlareTex = NULL;
167 
168 // Shadow textures are scaled down slightly to leave some extra blank pixels
169 // near the border.  This keeps axis aligned streaks from appearing on hardware
170 // that doesn't support clamp to border color.
171 static const float ShadowTextureScale = 15.0f / 16.0f;
172 
173 static Texture* eclipseShadowTextures[4];
174 static Texture* shadowMaskTexture = NULL;
175 static Texture* penumbraFunctionTexture = NULL;
176 
177 Texture* rectToSphericalTexture = NULL;
178 
179 static const Color compassColor(0.4f, 0.4f, 1.0f);
180 
181 static const float CoronaHeight = 0.2f;
182 
183 static bool buggyVertexProgramEmulation = true;
184 
185 static const int MaxSkyRings = 32;
186 static const int MaxSkySlices = 180;
187 static const int MinSkySlices = 30;
188 
189 // Size at which the orbit cache will be flushed of old orbit paths
190 static const unsigned int OrbitCacheCullThreshold = 200;
191 // Age in frames at which unused orbit paths may be eliminated from the cache
192 static const uint32 OrbitCacheRetireAge = 16;
193 
194 Color Renderer::StarLabelColor          (0.471f, 0.356f, 0.682f);
195 Color Renderer::PlanetLabelColor        (0.407f, 0.333f, 0.964f);
196 Color Renderer::DwarfPlanetLabelColor   (0.407f, 0.333f, 0.964f);
197 Color Renderer::MoonLabelColor          (0.231f, 0.733f, 0.792f);
198 Color Renderer::MinorMoonLabelColor     (0.231f, 0.733f, 0.792f);
199 Color Renderer::AsteroidLabelColor      (0.596f, 0.305f, 0.164f);
200 Color Renderer::CometLabelColor         (0.768f, 0.607f, 0.227f);
201 Color Renderer::SpacecraftLabelColor    (0.93f,  0.93f,  0.93f);
202 Color Renderer::LocationLabelColor      (0.24f,  0.89f,  0.43f);
203 Color Renderer::GalaxyLabelColor        (0.0f,   0.45f,  0.5f);
204 Color Renderer::GlobularLabelColor      (0.8f,   0.45f,  0.5f);
205 Color Renderer::NebulaLabelColor        (0.541f, 0.764f, 0.278f);
206 Color Renderer::OpenClusterLabelColor   (0.239f, 0.572f, 0.396f);
207 Color Renderer::ConstellationLabelColor (0.225f, 0.301f, 0.36f);
208 Color Renderer::EquatorialGridLabelColor(0.64f,  0.72f,  0.88f);
209 Color Renderer::PlanetographicGridLabelColor(0.8f, 0.8f, 0.8f);
210 Color Renderer::GalacticGridLabelColor  (0.88f,  0.72f,  0.64f);
211 Color Renderer::EclipticGridLabelColor  (0.72f,  0.64f,  0.88f);
212 Color Renderer::HorizonGridLabelColor   (0.72f,  0.72f,  0.72f);
213 
214 
215 Color Renderer::StarOrbitColor          (0.5f,   0.5f,   0.8f);
216 Color Renderer::PlanetOrbitColor        (0.3f,   0.323f, 0.833f);
217 Color Renderer::DwarfPlanetOrbitColor   (0.3f,   0.323f, 0.833f);
218 Color Renderer::MoonOrbitColor          (0.08f,  0.407f, 0.392f);
219 Color Renderer::MinorMoonOrbitColor     (0.08f,  0.407f, 0.392f);
220 Color Renderer::AsteroidOrbitColor      (0.58f,  0.152f, 0.08f);
221 Color Renderer::CometOrbitColor         (0.639f, 0.487f, 0.168f);
222 Color Renderer::SpacecraftOrbitColor    (0.4f,   0.4f,   0.4f);
223 Color Renderer::SelectionOrbitColor     (1.0f,   0.0f,   0.0f);
224 
225 Color Renderer::ConstellationColor      (0.0f,   0.24f,  0.36f);
226 Color Renderer::BoundaryColor           (0.24f,  0.10f,  0.12f);
227 Color Renderer::EquatorialGridColor     (0.28f,  0.28f,  0.38f);
228 Color Renderer::PlanetographicGridColor (0.8f,   0.8f,   0.8f);
229 Color Renderer::PlanetEquatorColor      (0.5f,   1.0f,   1.0f);
230 Color Renderer::GalacticGridColor       (0.38f,  0.38f,  0.28f);
231 Color Renderer::EclipticGridColor       (0.38f,  0.28f,  0.38f);
232 Color Renderer::HorizonGridColor        (0.38f,  0.38f,  0.38f);
233 Color Renderer::EclipticColor           (0.5f,   0.1f,   0.1f);
234 
235 Color Renderer::SelectionCursorColor    (1.0f,   0.0f,   0.0f);
236 
237 
238 // Some useful unit conversions
mmToInches(float mm)239 inline float mmToInches(float mm)
240 {
241     return mm * (1.0f / 25.4f);
242 }
243 
inchesToMm(float in)244 inline float inchesToMm(float in)
245 {
246     return in * 25.4f;
247 }
248 
249 
250 // Fade function for objects that shouldn't be shown when they're too small
251 // on screen such as orbit paths and some object labels. The will fade linearly
252 // from invisible at minSize pixels to full visibility at opaqueScale*minSize.
sizeFade(float screenSize,float minScreenSize,float opaqueScale)253 inline float sizeFade(float screenSize, float minScreenSize, float opaqueScale)
254 {
255     return min(1.0f, (screenSize - minScreenSize) / (minScreenSize * (opaqueScale - 1)));
256 }
257 
258 
259 // Calculate the cosine of half the maximum field of view. We'll use this for
260 // fast testing of object visibility.  The function takes the vertical FOV (in
261 // degrees) as an argument. When computing the view cone, we want the field of
262 // view as measured on the diagonal between viewport corners.
computeCosViewConeAngle(double verticalFOV,double width,double height)263 double computeCosViewConeAngle(double verticalFOV, double width, double height)
264 {
265     double h = tan(degToRad(verticalFOV / 2));
266     double diag = sqrt(1.0 + square(h) + square(h * width / height));
267     return 1.0 / diag;
268 }
269 
270 
Renderer()271 Renderer::Renderer() :
272     context(0),
273     windowWidth(0),
274     windowHeight(0),
275     fov(FOV),
276     cosViewConeAngle(computeCosViewConeAngle(fov, 1, 1)),
277     screenDpi(96),
278     corrFac(1.12f),
279     faintestAutoMag45deg(7.0f),
280     renderMode(GL_FILL),
281     labelMode(NoLabels),
282     renderFlags(ShowStars | ShowPlanets),
283     orbitMask(Body::Planet | Body::Moon | Body::Stellar),
284     ambientLightLevel(0.1f),
285     fragmentShaderEnabled(false),
286     vertexShaderEnabled(false),
287     brightnessBias(0.0f),
288     saturationMagNight(1.0f),
289     saturationMag(1.0f),
290     starStyle(FuzzyPointStars),
291     starVertexBuffer(NULL),
292     pointStarVertexBuffer(NULL),
293     glareVertexBuffer(NULL),
294     useVertexPrograms(false),
295     useRescaleNormal(false),
296     usePointSprite(false),
297     textureResolution(medres),
298     useNewStarRendering(false),
299     frameCount(0),
300     lastOrbitCacheFlush(0),
301     minOrbitSize(MinOrbitSizeForLabel),
302     distanceLimit(1.0e6f),
303     minFeatureSize(MinFeatureSizeForLabel),
304     locationFilter(~0u),
305     colorTemp(NULL),
306 #ifdef USE_HDR
307     sceneTexture(0),
308     blurFormat(GL_RGBA),
309     useBlendSubtract(true),
310     useLuminanceAlpha(false),
311     bloomEnabled(true),
312     maxBodyMag(100.0f),
313     exposure(1.0f),
314     exposurePrev(1.0f),
315     brightPlus(0.0f),
316 #endif
317     videoSync(false),
318     settingsChanged(true),
319     objectAnnotationSetOpen(false)
320 {
321     starVertexBuffer = new StarVertexBuffer(2048);
322     pointStarVertexBuffer = new PointStarVertexBuffer(2048);
323     glareVertexBuffer = new PointStarVertexBuffer(2048);
324     skyVertices = new SkyVertex[MaxSkySlices * (MaxSkyRings + 1)];
325     skyIndices = new uint32[(MaxSkySlices + 1) * 2 * MaxSkyRings];
326     skyContour = new SkyContourPoint[MaxSkySlices + 1];
327     colorTemp = GetStarColorTable(ColorTable_Enhanced);
328 #ifdef DEBUG_HDR_FILE
329     HDR_LOG.open("hdr.log", ios_base::app);
330 #endif
331 #ifdef USE_HDR
332     blurTextures = new Texture*[BLUR_PASS_COUNT];
333     blurTempTexture = NULL;
334     for (size_t i = 0; i < BLUR_PASS_COUNT; ++i)
335     {
336         blurTextures[i] = NULL;
337     }
338 #endif
339 #ifdef USE_BLOOM_LISTS
340     for (size_t i = 0; i < (sizeof gaussianLists/sizeof(GLuint)); ++i)
341     {
342         gaussianLists[i] = 0;
343     }
344 #endif
345 
346     for (int i = 0; i < (int) FontCount; i++)
347     {
348         font[i] = NULL;
349     }
350 }
351 
352 
~Renderer()353 Renderer::~Renderer()
354 {
355     if (starVertexBuffer != NULL)
356         delete starVertexBuffer;
357     if (pointStarVertexBuffer != NULL)
358         delete pointStarVertexBuffer;
359     delete[] skyVertices;
360     delete[] skyIndices;
361     delete[] skyContour;
362 #ifdef USE_BLOOM_LISTS
363     for (size_t i = 0; i < (sizeof gaussianLists/sizeof(GLuint)); ++i)
364     {
365         if (gaussianLists[i] != 0)
366             glDeleteLists(gaussianLists[i], 1);
367     }
368 #endif
369 #ifdef USE_HDR
370     for (size_t i = 0; i < BLUR_PASS_COUNT; ++i)
371     {
372         if (blurTextures[i] != NULL)
373             delete blurTextures[i];
374     }
375     delete [] blurTextures;
376     if (blurTempTexture)
377         delete blurTempTexture;
378 
379     if (sceneTexture != 0)
380         glDeleteTextures(1, &sceneTexture);
381 #endif
382 }
383 
384 
DetailOptions()385 Renderer::DetailOptions::DetailOptions() :
386     ringSystemSections(100),
387     orbitPathSamplePoints(100),
388     shadowTextureSize(256),
389     eclipseTextureSize(128)
390 {
391 }
392 
393 
StarTextureEval(float u,float v,float,unsigned char * pixel)394 static void StarTextureEval(float u, float v, float,
395                             unsigned char *pixel)
396 {
397     float r = 1 - (float) sqrt(u * u + v * v);
398     if (r < 0)
399         r = 0;
400     else if (r < 0.5f)
401         r = 2.0f * r;
402     else
403         r = 1;
404 
405     int pixVal = (int) (r * 255.99f);
406     pixel[0] = pixVal;
407     pixel[1] = pixVal;
408     pixel[2] = pixVal;
409 }
410 
GlareTextureEval(float u,float v,float,unsigned char * pixel)411 static void GlareTextureEval(float u, float v, float,
412                              unsigned char *pixel)
413 {
414     float r = 0.9f - (float) sqrt(u * u + v * v);
415     if (r < 0)
416         r = 0;
417 
418     int pixVal = (int) (r * 255.99f);
419     pixel[0] = 65;
420     pixel[1] = 64;
421     pixel[2] = 65;
422     pixel[3] = pixVal;
423 }
424 
ShadowTextureEval(float u,float v,float,unsigned char * pixel)425 static void ShadowTextureEval(float u, float v, float,
426                               unsigned char *pixel)
427 {
428     float r = (float) sqrt(u * u + v * v);
429 
430     // Leave some white pixels around the edges to the shadow doesn't
431     // 'leak'.  We'll also set the maximum mip map level for this texture to 3
432     // so we don't have problems with the edge texels at high mip map levels.
433     int pixVal = r < 15.0f / 16.0f ? 0 : 255;
434     pixel[0] = pixVal;
435     pixel[1] = pixVal;
436     pixel[2] = pixVal;
437 }
438 
439 
440 //! Lookup function for eclipse penumbras--the input is the amount of overlap
441 //  between the occluder and sun disc, and the output is the fraction of
442 //  full brightness.
PenumbraFunctionEval(float u,float,float,unsigned char * pixel)443 static void PenumbraFunctionEval(float u, float, float,
444                                  unsigned char *pixel)
445 {
446     u = (u + 1.0f) * 0.5f;
447 
448     // Using the cube root produces a good visual result
449     unsigned char pixVal = (unsigned char) (::pow((double) u, 0.33) * 255.99);
450 
451     pixel[0] = pixVal;
452 }
453 
454 
455 // ShadowTextureFunction is a function object for creating shadow textures
456 // used for rendering eclipses.
457 class ShadowTextureFunction : public TexelFunctionObject
458 {
459 public:
ShadowTextureFunction(float _umbra)460     ShadowTextureFunction(float _umbra) : umbra(_umbra) {};
461     virtual void operator()(float u, float v, float w, unsigned char* pixel);
462     float umbra;
463 };
464 
operator ()(float u,float v,float,unsigned char * pixel)465 void ShadowTextureFunction::operator()(float u, float v, float,
466                                        unsigned char* pixel)
467 {
468     float r = (float) sqrt(u * u + v * v);
469     int pixVal = 255;
470 
471     // Leave some white pixels around the edges to the shadow doesn't
472     // 'leak'.  We'll also set the maximum mip map level for this texture to 3
473     // so we don't have problems with the edge texels at high mip map levels.
474     r = r / (15.0f / 16.0f);
475     if (r < 1)
476     {
477         // The pixel value should depend on the area of the sun which is
478         // occluded.  We just fudge it here and use the square root of the
479         // radius.
480         if (r <= umbra)
481             pixVal = 0;
482         else
483             pixVal = (int) (sqrt((r - umbra) / (1 - umbra)) * 255.99f);
484     }
485 
486     pixel[0] = pixVal;
487     pixel[1] = pixVal;
488     pixel[2] = pixVal;
489 };
490 
491 
492 class ShadowMaskTextureFunction : public TexelFunctionObject
493 {
494 public:
ShadowMaskTextureFunction()495     ShadowMaskTextureFunction() {};
496     virtual void operator()(float u, float v, float w, unsigned char* pixel);
497     float dummy;
498 };
499 
operator ()(float u,float,float,unsigned char * pixel)500 void ShadowMaskTextureFunction::operator()(float u, float, float,
501                                            unsigned char* pixel)
502 {
503     unsigned char a = u > 0.0f ? 255 : 0;
504     pixel[0] = a;
505     pixel[1] = a;
506     pixel[2] = a;
507     pixel[3] = a;
508 }
509 
510 
IllumMapEval(float x,float y,float z,unsigned char * pixel)511 static void IllumMapEval(float x, float y, float z,
512                          unsigned char* pixel)
513 {
514     Vec3f v(x, y, z);
515 
516     pixel[0] = 128 + (int) (127 * v.x);
517     pixel[1] = 128 + (int) (127 * v.y);
518     pixel[2] = 128 + (int) (127 * v.z);
519 }
520 
521 
522 #if 0
523 // Not used yet.
524 
525 // The RectToSpherical map converts XYZ coordinates to UV coordinates
526 // via a cube map lookup. However, a lot of GPUs don't support linear
527 // interpolation of textures with > 8 bits per component, which is
528 // inadequate precision for storing texture coordinates. To work around
529 // this, we'll store the u and v texture coordinates with two 8 bit
530 // coordinates each: rg for u, ba for v. The coordinates are unpacked
531 // as: u = r * 255/256 + g * 1/255
532 //     v = b * 255/256 + a * 1/255
533 // This gives an effective precision of 16 bits for each texture coordinate.
534 static void RectToSphericalMapEval(float x, float y, float z,
535                                    unsigned char* pixel)
536 {
537     // Compute spherical coodinates (r is always 1)
538     double phi = asin(y);
539     double theta = atan2(z, -x);
540 
541     // Convert to texture coordinates
542     double u = (theta / PI + 1.0) * 0.5;
543     double v = (-phi / PI + 0.5);
544 
545     // Pack texture coordinates in red/green and blue/alpha
546     // u = red + green/256
547     // v = blue* + alpha/256
548     uint16 rg = (uint16) (u * 65535.99);
549     uint16 ba = (uint16) (v * 65535.99);
550     pixel[0] = rg >> 8;
551     pixel[1] = rg & 0xff;
552     pixel[2] = ba >> 8;
553     pixel[3] = ba & 0xff;
554 }
555 #endif
556 
557 
BuildGaussianDiscMipLevel(unsigned char * mipPixels,unsigned int log2size,float fwhm,float power)558 static void BuildGaussianDiscMipLevel(unsigned char* mipPixels,
559                                       unsigned int log2size,
560                                       float fwhm,
561                                       float power)
562 {
563     unsigned int size = 1 << log2size;
564     float sigma = fwhm / 2.3548f;
565     float isig2 = 1.0f / (2.0f * sigma * sigma);
566     float s = 1.0f / (sigma * (float) sqrt(2.0 * PI));
567 
568     for (unsigned int i = 0; i < size; i++)
569     {
570         float y = (float) i - size / 2;
571         for (unsigned int j = 0; j < size; j++)
572         {
573             float x = (float) j - size / 2;
574             float r2 = x * x + y * y;
575             float f = s * (float) exp(-r2 * isig2) * power;
576 
577             mipPixels[i * size + j] = (unsigned char) (255.99f * min(f, 1.0f));
578         }
579     }
580 }
581 
582 
BuildGlareMipLevel(unsigned char * mipPixels,unsigned int log2size,float scale,float base)583 static void BuildGlareMipLevel(unsigned char* mipPixels,
584                                unsigned int log2size,
585                                float scale,
586                                float base)
587 {
588     unsigned int size = 1 << log2size;
589 
590     for (unsigned int i = 0; i < size; i++)
591     {
592         float y = (float) i - size / 2;
593         for (unsigned int j = 0; j < size; j++)
594         {
595             float x = (float) j - size / 2;
596             float r = (float) sqrt(x * x + y * y);
597             float f = (float) pow(base, r * scale);
598             mipPixels[i * size + j] = (unsigned char) (255.99f * min(f, 1.0f));
599         }
600     }
601 }
602 
603 
604 #if 0
605 // An alternate glare function, based roughly on results in Spencer, G. et al,
606 // 1995, "Physically-Based Glare Effects for Digital Images"
607 static void BuildGlareMipLevel2(unsigned char* mipPixels,
608                                 unsigned int log2size,
609                                 float scale)
610 {
611     unsigned int size = 1 << log2size;
612 
613     for (unsigned int i = 0; i < size; i++)
614     {
615         float y = (float) i - size / 2;
616         for (unsigned int j = 0; j < size; j++)
617         {
618             float x = (float) j - size / 2;
619             float r = (float) sqrt(x * x + y * y);
620             float f = 0.3f / (0.3f + r * r * scale * scale * 100);
621             /*
622             if (i == 0 || j == 0 || i == size - 1 || j == size - 1)
623                 f = 1.0f;
624             */
625             mipPixels[i * size + j] = (unsigned char) (255.99f * min(f, 1.0f));
626         }
627     }
628 }
629 #endif
630 
631 
BuildGaussianDiscTexture(unsigned int log2size)632 static Texture* BuildGaussianDiscTexture(unsigned int log2size)
633 {
634     unsigned int size = 1 << log2size;
635     Image* img = new Image(GL_LUMINANCE, size, size, log2size + 1);
636 
637     for (unsigned int mipLevel = 0; mipLevel <= log2size; mipLevel++)
638     {
639         float fwhm = (float) pow(2.0f, (float) (log2size - mipLevel)) * 0.3f;
640         BuildGaussianDiscMipLevel(img->getMipLevel(mipLevel),
641                                   log2size - mipLevel,
642                                   fwhm,
643                                   (float) pow(2.0f, (float) (log2size - mipLevel)));
644     }
645 
646     ImageTexture* texture = new ImageTexture(*img,
647                                              Texture::BorderClamp,
648                                              Texture::DefaultMipMaps);
649     texture->setBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
650 
651     delete img;
652 
653     return texture;
654 }
655 
656 
BuildGaussianGlareTexture(unsigned int log2size)657 static Texture* BuildGaussianGlareTexture(unsigned int log2size)
658 {
659     unsigned int size = 1 << log2size;
660     Image* img = new Image(GL_LUMINANCE, size, size, log2size + 1);
661 
662     for (unsigned int mipLevel = 0; mipLevel <= log2size; mipLevel++)
663     {
664         /*
665         // Optional gaussian glare
666         float fwhm = (float) pow(2.0f, (float) (log2size - mipLevel)) * 0.15f;
667         float power = (float) pow(2.0f, (float) (log2size - mipLevel)) * 0.15f;
668         BuildGaussianDiscMipLevel(img->getMipLevel(mipLevel),
669                                   log2size - mipLevel,
670                                   fwhm,
671                                   power);
672         */
673         BuildGlareMipLevel(img->getMipLevel(mipLevel),
674                            log2size - mipLevel,
675                            25.0f / (float) pow(2.0f, (float) (log2size - mipLevel)),
676                            0.66f);
677         /*
678         BuildGlareMipLevel2(img->getMipLevel(mipLevel),
679                             log2size - mipLevel,
680                             1.0f / (float) pow(2.0f, (float) (log2size - mipLevel)));
681         */
682     }
683 
684     ImageTexture* texture = new ImageTexture(*img,
685                                              Texture::BorderClamp,
686                                              Texture::DefaultMipMaps);
687     texture->setBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
688 
689     delete img;
690 
691     return texture;
692 }
693 
694 
translateLabelModeToClassMask(int labelMode)695 static int translateLabelModeToClassMask(int labelMode)
696 {
697     int classMask = 0;
698 
699     if (labelMode & Renderer::PlanetLabels)
700         classMask |= Body::Planet;
701     if (labelMode & Renderer::DwarfPlanetLabels)
702         classMask |= Body::DwarfPlanet;
703     if (labelMode & Renderer::MoonLabels)
704         classMask |= Body::Moon;
705     if (labelMode & Renderer::MinorMoonLabels)
706         classMask |= Body::MinorMoon;
707     if (labelMode & Renderer::AsteroidLabels)
708         classMask |= Body::Asteroid;
709     if (labelMode & Renderer::CometLabels)
710         classMask |= Body::Comet;
711     if (labelMode & Renderer::SpacecraftLabels)
712         classMask |= Body::Spacecraft;
713 
714     return classMask;
715 }
716 
717 
718 // Depth comparison function for render list entries
operator <(const RenderListEntry & a,const RenderListEntry & b)719 bool operator<(const RenderListEntry& a, const RenderListEntry& b)
720 {
721     // Operation is reversed because -z axis points into the screen
722     return a.centerZ - a.radius > b.centerZ - b.radius;
723 }
724 
725 
726 // Depth comparison for labels
727 // Note that it's essential to declare this operator as a member
728 // function of Renderer::Label; if it's not a class member, C++'s
729 // argument dependent lookup will not find the operator when it's
730 // used as a predicate for STL algorithms.
operator <(const Annotation & a) const731 bool Renderer::Annotation::operator<(const Annotation& a) const
732 {
733     // Operation is reversed because -z axis points into the screen
734     return position.z > a.position.z;
735 }
736 
737 // Depth comparison for orbit paths
operator <(const Renderer::OrbitPathListEntry & o) const738 bool Renderer::OrbitPathListEntry::operator<(const Renderer::OrbitPathListEntry& o) const
739 {
740     // Operation is reversed because -z axis points into the screen
741     return centerZ - radius > o.centerZ - o.radius;
742 }
743 
744 
init(GLContext * _context,int winWidth,int winHeight,DetailOptions & _detailOptions)745 bool Renderer::init(GLContext* _context,
746                     int winWidth, int winHeight,
747                     DetailOptions& _detailOptions)
748 {
749     context = _context;
750     detailOptions = _detailOptions;
751 
752     // Initialize static meshes and textures common to all instances of Renderer
753     if (!commonDataInitialized)
754     {
755         g_lodSphere = new LODSphereMesh();
756 
757         starTex = CreateProceduralTexture(64, 64, GL_RGB, StarTextureEval);
758 
759         glareTex = LoadTextureFromFile("textures/flare.jpg");
760         if (glareTex == NULL)
761             glareTex = CreateProceduralTexture(64, 64, GL_RGB, GlareTextureEval);
762 
763         // Max mipmap level doesn't work reliably on all graphics
764         // cards.  In particular, Rage 128 and TNT cards resort to software
765         // rendering when this feature is enabled.  The only workaround is to
766         // disable mipmapping completely unless texture border clamping is
767         // supported, which solves the problem much more elegantly than all
768         // the mipmap level nonsense.
769         // shadowTex->setMaxMipMapLevel(3);
770         Texture::AddressMode shadowTexAddress = Texture::EdgeClamp;
771         Texture::MipMapMode shadowTexMip = Texture::NoMipMaps;
772         useClampToBorder = context->extensionSupported("GL_ARB_texture_border_clamp");
773         if (useClampToBorder)
774         {
775             shadowTexAddress = Texture::BorderClamp;
776             shadowTexMip = Texture::DefaultMipMaps;
777         }
778 
779         shadowTex = CreateProceduralTexture(detailOptions.shadowTextureSize,
780                                             detailOptions.shadowTextureSize,
781                                             GL_RGB,
782                                             ShadowTextureEval,
783                                             shadowTexAddress, shadowTexMip);
784         shadowTex->setBorderColor(Color::White);
785 
786         if (gaussianDiscTex == NULL)
787             gaussianDiscTex = BuildGaussianDiscTexture(8);
788         if (gaussianGlareTex == NULL)
789             gaussianGlareTex = BuildGaussianGlareTexture(9);
790 
791         // Create the eclipse shadow textures
792         {
793             for (int i = 0; i < 4; i++)
794             {
795                 ShadowTextureFunction func(i * 0.25f);
796                 eclipseShadowTextures[i] =
797                     CreateProceduralTexture(detailOptions.eclipseTextureSize,
798                                             detailOptions.eclipseTextureSize,
799                                             GL_RGB, func,
800                                             shadowTexAddress, shadowTexMip);
801                 if (eclipseShadowTextures[i] != NULL)
802                 {
803                     // eclipseShadowTextures[i]->setMaxMipMapLevel(2);
804                     eclipseShadowTextures[i]->setBorderColor(Color::White);
805                 }
806             }
807         }
808 
809         // Create the shadow mask texture
810         {
811             ShadowMaskTextureFunction func;
812             shadowMaskTexture = CreateProceduralTexture(128, 2, GL_RGBA, func);
813             //shadowMaskTexture->bindName();
814         }
815 
816         // Create a function lookup table in a texture for use with
817         // fragment program eclipse shadows.
818         penumbraFunctionTexture = CreateProceduralTexture(512, 1, GL_LUMINANCE,
819                                                           PenumbraFunctionEval,
820                                                           Texture::EdgeClamp);
821 
822         if (context->extensionSupported("GL_ARB_texture_cube_map"))
823         {
824             normalizationTex = CreateProceduralCubeMap(64, GL_RGB, IllumMapEval);
825 #if ADVANCED_CLOUD_SHADOWS
826             rectToSphericalTexture = CreateProceduralCubeMap(128, GL_RGBA, RectToSphericalMapEval);
827 #endif
828         }
829 
830 #ifdef USE_HDR
831         genSceneTexture();
832         genBlurTextures();
833 #endif
834         commonDataInitialized = true;
835     }
836 
837 #if 0
838     if (context->extensionSupported("GL_ARB_multisample"))
839     {
840         int nSamples = 0;
841         int sampleBuffers = 0;
842         int enabled = (int) glIsEnabled(GL_MULTISAMPLE_ARB);
843         glGetIntegerv(GL_SAMPLE_BUFFERS_ARB, &sampleBuffers);
844         glGetIntegerv(GL_SAMPLES_ARB, &nSamples);
845         clog << "AA samples: " << nSamples
846              << ", enabled=" << (int) enabled
847              << ", sample buffers=" << (sampleBuffers)
848              << "\n";
849         glEnable(GL_MULTISAMPLE_ARB);
850     }
851 #endif
852 
853     if (context->extensionSupported("GL_EXT_rescale_normal"))
854     {
855         // We need this enabled because we use glScale, but only
856         // with uniform scale factors.
857         DPRINTF(1, "Renderer: EXT_rescale_normal supported.\n");
858         useRescaleNormal = true;
859         glEnable(GL_RESCALE_NORMAL_EXT);
860     }
861 
862     if (context->extensionSupported("GL_ARB_point_sprite"))
863     {
864         DPRINTF(1, "Renderer: point sprites supported.\n");
865         usePointSprite = true;
866     }
867 
868     if (context->extensionSupported("GL_EXT_separate_specular_color"))
869     {
870         glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT, GL_SEPARATE_SPECULAR_COLOR_EXT);
871     }
872 
873     // Ugly renderer-specific bug workarounds follow . . .
874     char* glRenderer = (char*) glGetString(GL_RENDERER);
875     if (glRenderer != NULL)
876     {
877         // Fog is broken with vertex program emulation in most versions of
878         // the GF 1 and 2 drivers; we need to detect this and disable
879         // vertex programs which output fog coordinates
880         if (strstr(glRenderer, "GeForce3") != NULL ||
881             strstr(glRenderer, "GeForce4") != NULL)
882         {
883             buggyVertexProgramEmulation = false;
884         }
885 
886         if (strstr(glRenderer, "Savage4") != NULL ||
887             strstr(glRenderer, "ProSavage") != NULL)
888         {
889             // S3 Savage4 drivers appear to rescale normals without reporting
890             // EXT_rescale_normal.  Lighting will be messed up unless
891             // we set the useRescaleNormal flag.
892             useRescaleNormal = true;
893         }
894 #ifdef TARGET_OS_MAC
895         if (strstr(glRenderer, "ATI") != NULL ||
896             strstr(glRenderer, "GMA 900") != NULL)
897         {
898             // Some drivers on the Mac appear to limit point sprite size.
899             // This causes an abrupt size transition when going from billboards
900             // to sprites. Rather than incur overhead accounting for the size limit,
901             // do not use sprites on these renderers.
902             // Affected cards: ATI (various), etc
903             // Renderer strings are not unique.
904             usePointSprite = false;
905         }
906 #endif
907     }
908 
909     // More ugly hacks; according to Matt Craighead at NVIDIA, an NVIDIA
910     // OpenGL driver that reports version 1.3.1 or greater will have working
911     // fog in emulated vertex programs.
912     char* glVersion = (char*) glGetString(GL_VERSION);
913     if (glVersion != NULL)
914     {
915         int major = 0, minor = 0, extra = 0;
916         int nScanned = sscanf(glVersion, "%d.%d.%d", &major, &minor, &extra);
917 
918         if (nScanned >= 2)
919         {
920             if (major > 1 || minor > 3 || (minor == 3 && extra >= 1))
921                 buggyVertexProgramEmulation = false;
922         }
923     }
924 
925 #ifdef USE_HDR
926     useBlendSubtract = context->extensionSupported("GL_EXT_blend_subtract");
927     Image        *testImg = new Image(GL_LUMINANCE_ALPHA, 1, 1);
928     ImageTexture *testTex = new ImageTexture(*testImg,
929                                              Texture::EdgeClamp,
930                                              Texture::NoMipMaps);
931     delete testImg;
932     GLint actualTexFormat = 0;
933     glEnable(GL_TEXTURE_2D);
934     testTex->bind();
935     glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &actualTexFormat);
936     glBindTexture(GL_TEXTURE_2D, 0);
937     glDisable(GL_TEXTURE_2D);
938     switch (actualTexFormat)
939     {
940     case 2:
941     case GL_LUMINANCE_ALPHA:
942     case GL_LUMINANCE4_ALPHA4:
943     case GL_LUMINANCE6_ALPHA2:
944     case GL_LUMINANCE8_ALPHA8:
945     case GL_LUMINANCE12_ALPHA4:
946     case GL_LUMINANCE12_ALPHA12:
947     case GL_LUMINANCE16_ALPHA16:
948         useLuminanceAlpha = true;
949         break;
950     default:
951         useLuminanceAlpha = false;
952         break;
953     }
954 
955     blurFormat = useLuminanceAlpha ? GL_LUMINANCE_ALPHA : GL_RGBA;
956     delete testTex;
957 #endif
958 
959     glLoadIdentity();
960 
961     glEnable(GL_CULL_FACE);
962     glCullFace(GL_BACK);
963 
964     glEnable(GL_COLOR_MATERIAL);
965     glEnable(GL_LIGHTING);
966     glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
967 
968     // LEQUAL rather than LESS required for multipass rendering
969     glDepthFunc(GL_LEQUAL);
970 
971     resize(winWidth, winHeight);
972 
973     return true;
974 }
975 
976 
resize(int width,int height)977 void Renderer::resize(int width, int height)
978 {
979 #ifdef USE_HDR
980     if (width == windowWidth && height == windowHeight)
981         return;
982 #endif
983     windowWidth = width;
984     windowHeight = height;
985     cosViewConeAngle = computeCosViewConeAngle(fov, windowWidth, windowHeight);
986     // glViewport(windowWidth, windowHeight);
987 
988 #ifdef USE_HDR
989     if (commonDataInitialized)
990     {
991         genSceneTexture();
992         genBlurTextures();
993     }
994 #endif
995 }
996 
calcPixelSize(float fovY,float windowHeight)997 float Renderer::calcPixelSize(float fovY, float windowHeight)
998 {
999     return 2 * (float) tan(degToRad(fovY / 2.0)) / (float) windowHeight;
1000 }
1001 
setFieldOfView(float _fov)1002 void Renderer::setFieldOfView(float _fov)
1003 {
1004     fov = _fov;
1005     corrFac = (0.12f * fov/FOV * fov/FOV + 1.0f);
1006     cosViewConeAngle = computeCosViewConeAngle(fov, windowWidth, windowHeight);
1007 }
1008 
getScreenDpi() const1009 int Renderer::getScreenDpi() const
1010 {
1011     return screenDpi;
1012 }
1013 
setScreenDpi(int _dpi)1014 void Renderer::setScreenDpi(int _dpi)
1015 {
1016     screenDpi = _dpi;
1017 }
1018 
setFaintestAM45deg(float _faintestAutoMag45deg)1019 void Renderer::setFaintestAM45deg(float _faintestAutoMag45deg)
1020 {
1021     faintestAutoMag45deg = _faintestAutoMag45deg;
1022     markSettingsChanged();
1023 }
1024 
getFaintestAM45deg() const1025 float Renderer::getFaintestAM45deg() const
1026 {
1027     return faintestAutoMag45deg;
1028 }
1029 
getResolution() const1030 unsigned int Renderer::getResolution() const
1031 {
1032     return textureResolution;
1033 }
1034 
1035 
setResolution(unsigned int resolution)1036 void Renderer::setResolution(unsigned int resolution)
1037 {
1038     if (resolution < TEXTURE_RESOLUTION)
1039         textureResolution = resolution;
1040     markSettingsChanged();
1041 }
1042 
1043 
getFont(FontStyle fs) const1044 TextureFont* Renderer::getFont(FontStyle fs) const
1045 {
1046     return font[(int) fs];
1047 }
1048 
setFont(FontStyle fs,TextureFont * txf)1049 void Renderer::setFont(FontStyle fs, TextureFont* txf)
1050 {
1051     font[(int) fs] = txf;
1052     markSettingsChanged();
1053 }
1054 
setRenderMode(int _renderMode)1055 void Renderer::setRenderMode(int _renderMode)
1056 {
1057     renderMode = _renderMode;
1058     markSettingsChanged();
1059 }
1060 
getRenderFlags() const1061 int Renderer::getRenderFlags() const
1062 {
1063     return renderFlags;
1064 }
1065 
setRenderFlags(int _renderFlags)1066 void Renderer::setRenderFlags(int _renderFlags)
1067 {
1068     renderFlags = _renderFlags;
1069     markSettingsChanged();
1070 }
1071 
getLabelMode() const1072 int Renderer::getLabelMode() const
1073 {
1074     return labelMode;
1075 }
1076 
setLabelMode(int _labelMode)1077 void Renderer::setLabelMode(int _labelMode)
1078 {
1079     labelMode = _labelMode;
1080     markSettingsChanged();
1081 }
1082 
getOrbitMask() const1083 int Renderer::getOrbitMask() const
1084 {
1085     return orbitMask;
1086 }
1087 
setOrbitMask(int mask)1088 void Renderer::setOrbitMask(int mask)
1089 {
1090     orbitMask = mask;
1091     markSettingsChanged();
1092 }
1093 
1094 
1095 const ColorTemperatureTable*
getStarColorTable() const1096 Renderer::getStarColorTable() const
1097 {
1098     return colorTemp;
1099 }
1100 
1101 
1102 void
setStarColorTable(const ColorTemperatureTable * ct)1103 Renderer::setStarColorTable(const ColorTemperatureTable* ct)
1104 {
1105     colorTemp = ct;
1106     markSettingsChanged();
1107 }
1108 
1109 
getVideoSync() const1110 bool Renderer::getVideoSync() const
1111 {
1112     return videoSync;
1113 }
1114 
setVideoSync(bool sync)1115 void Renderer::setVideoSync(bool sync)
1116 {
1117     videoSync = sync;
1118     markSettingsChanged();
1119 }
1120 
1121 
getAmbientLightLevel() const1122 float Renderer::getAmbientLightLevel() const
1123 {
1124     return ambientLightLevel;
1125 }
1126 
1127 
setAmbientLightLevel(float level)1128 void Renderer::setAmbientLightLevel(float level)
1129 {
1130     ambientLightLevel = level;
1131     markSettingsChanged();
1132 }
1133 
1134 
getMinimumFeatureSize() const1135 float Renderer::getMinimumFeatureSize() const
1136 {
1137     return minFeatureSize;
1138 }
1139 
1140 
setMinimumFeatureSize(float pixels)1141 void Renderer::setMinimumFeatureSize(float pixels)
1142 {
1143     minFeatureSize = pixels;
1144     markSettingsChanged();
1145 }
1146 
1147 
getMinimumOrbitSize() const1148 float Renderer::getMinimumOrbitSize() const
1149 {
1150     return minOrbitSize;
1151 }
1152 
1153 // Orbits and labels are only rendered when the orbit of the object
1154 // occupies some minimum number of pixels on screen.
setMinimumOrbitSize(float pixels)1155 void Renderer::setMinimumOrbitSize(float pixels)
1156 {
1157     minOrbitSize = pixels;
1158     markSettingsChanged();
1159 }
1160 
1161 
getDistanceLimit() const1162 float Renderer::getDistanceLimit() const
1163 {
1164     return distanceLimit;
1165 }
1166 
1167 
setDistanceLimit(float distanceLimit_)1168 void Renderer::setDistanceLimit(float distanceLimit_)
1169 {
1170     distanceLimit = distanceLimit_;
1171     markSettingsChanged();
1172 }
1173 
1174 
getFragmentShaderEnabled() const1175 bool Renderer::getFragmentShaderEnabled() const
1176 {
1177     return fragmentShaderEnabled;
1178 }
1179 
setFragmentShaderEnabled(bool enable)1180 void Renderer::setFragmentShaderEnabled(bool enable)
1181 {
1182     fragmentShaderEnabled = enable && fragmentShaderSupported();
1183     markSettingsChanged();
1184 }
1185 
fragmentShaderSupported() const1186 bool Renderer::fragmentShaderSupported() const
1187 {
1188     return context->bumpMappingSupported();
1189 }
1190 
getVertexShaderEnabled() const1191 bool Renderer::getVertexShaderEnabled() const
1192 {
1193     return vertexShaderEnabled;
1194 }
1195 
setVertexShaderEnabled(bool enable)1196 void Renderer::setVertexShaderEnabled(bool enable)
1197 {
1198     vertexShaderEnabled = enable && vertexShaderSupported();
1199     markSettingsChanged();
1200 }
1201 
vertexShaderSupported() const1202 bool Renderer::vertexShaderSupported() const
1203 {
1204     return useVertexPrograms;
1205 }
1206 
1207 
addAnnotation(vector<Annotation> & annotations,const MarkerRepresentation * markerRep,const string & labelText,Color color,const Point3f & pos,LabelAlignment halign,LabelVerticalAlignment valign,float size)1208 void Renderer::addAnnotation(vector<Annotation>& annotations,
1209                              const MarkerRepresentation* markerRep,
1210                              const string& labelText,
1211                              Color color,
1212                              const Point3f& pos,
1213                              LabelAlignment halign,
1214                              LabelVerticalAlignment valign,
1215                              float size)
1216 {
1217     double winX, winY, winZ;
1218     int view[4] = { 0, 0, 0, 0 };
1219     view[0] = -windowWidth / 2;
1220     view[1] = -windowHeight / 2;
1221     view[2] = windowWidth;
1222     view[3] = windowHeight;
1223     float depth = (float) (pos.x * modelMatrix[2] +
1224                            pos.y * modelMatrix[6] +
1225                            pos.z * modelMatrix[10]);
1226     if (gluProject(pos.x, pos.y, pos.z,
1227                    modelMatrix,
1228                    projMatrix,
1229                    (const GLint*) view,
1230                    &winX, &winY, &winZ) != GL_FALSE)
1231     {
1232         Annotation a;
1233 
1234         a.labelText[0] = '\0';
1235         ReplaceGreekLetterAbbr(a.labelText, MaxLabelLength, labelText.c_str(), labelText.length());
1236         a.labelText[MaxLabelLength - 1] = '\0';
1237 
1238         a.markerRep = markerRep;
1239         a.color = color;
1240         a.position = Point3f((float) winX, (float) winY, -depth);
1241         a.halign = halign;
1242         a.valign = valign;
1243         a.size = size;
1244         annotations.push_back(a);
1245     }
1246 }
1247 
1248 
addForegroundAnnotation(const MarkerRepresentation * markerRep,const string & labelText,Color color,const Point3f & pos,LabelAlignment halign,LabelVerticalAlignment valign,float size)1249 void Renderer::addForegroundAnnotation(const MarkerRepresentation* markerRep,
1250                                        const string& labelText,
1251                                        Color color,
1252                                        const Point3f& pos,
1253                                        LabelAlignment halign,
1254                                        LabelVerticalAlignment valign,
1255                                        float size)
1256 {
1257     addAnnotation(foregroundAnnotations, markerRep, labelText, color, pos, halign, valign, size);
1258 }
1259 
1260 
addBackgroundAnnotation(const MarkerRepresentation * markerRep,const string & labelText,Color color,const Point3f & pos,LabelAlignment halign,LabelVerticalAlignment valign,float size)1261 void Renderer::addBackgroundAnnotation(const MarkerRepresentation* markerRep,
1262                                        const string& labelText,
1263                                        Color color,
1264                                        const Point3f& pos,
1265                                        LabelAlignment halign,
1266                                        LabelVerticalAlignment valign,
1267                                        float size)
1268 {
1269     addAnnotation(backgroundAnnotations, markerRep, labelText, color, pos, halign, valign, size);
1270 }
1271 
1272 
addSortedAnnotation(const MarkerRepresentation * markerRep,const string & labelText,Color color,const Point3f & pos,LabelAlignment halign,LabelVerticalAlignment valign,float size)1273 void Renderer::addSortedAnnotation(const MarkerRepresentation* markerRep,
1274                                    const string& labelText,
1275                                    Color color,
1276                                    const Point3f& pos,
1277                                    LabelAlignment halign,
1278                                    LabelVerticalAlignment valign,
1279                                    float size)
1280 {
1281     double winX, winY, winZ;
1282     int view[4] = { 0, 0, 0, 0 };
1283     view[0] = -windowWidth / 2;
1284     view[1] = -windowHeight / 2;
1285     view[2] = windowWidth;
1286     view[3] = windowHeight;
1287     float depth = (float) (pos.x * modelMatrix[2] +
1288                            pos.y * modelMatrix[6] +
1289                            pos.z * modelMatrix[10]);
1290     if (gluProject(pos.x, pos.y, pos.z,
1291                    modelMatrix,
1292                    projMatrix,
1293                    (const GLint*) view,
1294                    &winX, &winY, &winZ) != GL_FALSE)
1295     {
1296         Annotation a;
1297 
1298         a.labelText[0] = '\0';
1299         if (markerRep == NULL)
1300         {
1301             //l.text = ReplaceGreekLetterAbbr(_(text.c_str()));
1302             strncpy(a.labelText, labelText.c_str(), MaxLabelLength);
1303             a.labelText[MaxLabelLength - 1] = '\0';
1304         }
1305         a.markerRep = markerRep;
1306         a.color = color;
1307         a.position = Point3f((float) winX, (float) winY, -depth);
1308         a.halign = halign;
1309         a.valign = valign;
1310         a.size = size;
1311         depthSortedAnnotations.push_back(a);
1312     }
1313 }
1314 
1315 
clearAnnotations(vector<Annotation> & annotations)1316 void Renderer::clearAnnotations(vector<Annotation>& annotations)
1317 {
1318     annotations.clear();
1319 }
1320 
1321 
clearSortedAnnotations()1322 void Renderer::clearSortedAnnotations()
1323 {
1324     depthSortedAnnotations.clear();
1325 }
1326 
1327 
1328 // Return the orientation of the camera used to render the current
1329 // frame. Available only while rendering a frame.
getCameraOrientation() const1330 Quatf Renderer::getCameraOrientation() const
1331 {
1332     return m_cameraOrientation;
1333 }
1334 
1335 
getNearPlaneDistance() const1336 float Renderer::getNearPlaneDistance() const
1337 {
1338     return depthPartitions[currentIntervalIndex].nearZ;
1339 }
1340 
1341 
beginObjectAnnotations()1342 void Renderer::beginObjectAnnotations()
1343 {
1344     // It's an error to call beginObjectAnnotations a second time
1345     // without first calling end.
1346     assert(!objectAnnotationSetOpen);
1347     assert(objectAnnotations.empty());
1348 
1349     objectAnnotations.clear();
1350     objectAnnotationSetOpen = true;
1351 }
1352 
1353 
endObjectAnnotations()1354 void Renderer::endObjectAnnotations()
1355 {
1356     objectAnnotationSetOpen = false;
1357 
1358     if (!objectAnnotations.empty())
1359     {
1360         renderAnnotations(objectAnnotations.begin(),
1361                           objectAnnotations.end(),
1362                           -depthPartitions[currentIntervalIndex].nearZ,
1363                           -depthPartitions[currentIntervalIndex].farZ,
1364                           FontNormal);
1365 
1366         objectAnnotations.clear();
1367     }
1368 }
1369 
1370 
addObjectAnnotation(const MarkerRepresentation * markerRep,const string & labelText,Color color,const Point3f & pos)1371 void Renderer::addObjectAnnotation(const MarkerRepresentation* markerRep,
1372                                    const string& labelText,
1373                                    Color color,
1374                                    const Point3f& pos)
1375 {
1376     assert(objectAnnotationSetOpen);
1377     if (objectAnnotationSetOpen)
1378     {
1379         double winX, winY, winZ;
1380         int view[4] = { 0, 0, 0, 0 };
1381         view[0] = -windowWidth / 2;
1382         view[1] = -windowHeight / 2;
1383         view[2] = windowWidth;
1384         view[3] = windowHeight;
1385         float depth = (float) (pos.x * modelMatrix[2] +
1386                                pos.y * modelMatrix[6] +
1387                                pos.z * modelMatrix[10]);
1388         if (gluProject(pos.x, pos.y, pos.z,
1389                        modelMatrix,
1390                        projMatrix,
1391                        (const GLint*) view,
1392                        &winX, &winY, &winZ) != GL_FALSE)
1393         {
1394 
1395             Annotation a;
1396 
1397             a.labelText[0] = '\0';
1398             if (!labelText.empty())
1399             {
1400                 strncpy(a.labelText, labelText.c_str(), MaxLabelLength);
1401                 a.labelText[MaxLabelLength - 1] = '\0';
1402             }
1403             a.markerRep = markerRep;
1404             a.color = color;
1405             a.position = Point3f((float) winX, (float) winY, -depth);
1406             a.size = 0.0f;
1407 
1408             objectAnnotations.push_back(a);
1409         }
1410     }
1411 }
1412 
1413 
enableSmoothLines()1414 static void enableSmoothLines()
1415 {
1416     // glEnable(GL_BLEND);
1417 #ifdef USE_HDR
1418     glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
1419 #else
1420     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1421 #endif
1422     glEnable(GL_LINE_SMOOTH);
1423     glLineWidth(1.5f);
1424 }
1425 
disableSmoothLines()1426 static void disableSmoothLines()
1427 {
1428     // glDisable(GL_BLEND);
1429     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1430     glDisable(GL_LINE_SMOOTH);
1431     glLineWidth(1.0f);
1432 }
1433 
1434 
1435 class OrbitSampler : public OrbitSampleProc
1436 {
1437 public:
1438     vector<Renderer::OrbitSample>* samples;
1439 
OrbitSampler(vector<Renderer::OrbitSample> * _samples)1440     OrbitSampler(vector<Renderer::OrbitSample>* _samples) : samples(_samples) {};
sample(double t,const Point3d & p)1441     void sample(double t, const Point3d& p)
1442     {
1443         Renderer::OrbitSample samp;
1444         samp.pos = p;
1445         samp.t = t;
1446         samples->push_back(samp);
1447     };
1448 };
1449 
1450 
renderOrbitColor(const Body * body,bool selected,float opacity)1451 void renderOrbitColor(const Body *body, bool selected, float opacity)
1452 {
1453     Color orbitColor;
1454 
1455     if (selected)
1456     {
1457         // Highlight the orbit of the selected object in red
1458         orbitColor = Renderer::SelectionOrbitColor;
1459     }
1460     else if (body != NULL && body->isOrbitColorOverridden())
1461     {
1462         orbitColor = body->getOrbitColor();
1463     }
1464     else
1465     {
1466         int classification;
1467         if (body != NULL)
1468             classification = body->getOrbitClassification();
1469         else
1470             classification = Body::Stellar;
1471 
1472         switch (classification)
1473         {
1474         case Body::Moon:
1475             orbitColor = Renderer::MoonOrbitColor;
1476             break;
1477         case Body::MinorMoon:
1478             orbitColor = Renderer::MinorMoonOrbitColor;
1479             break;
1480         case Body::Asteroid:
1481             orbitColor = Renderer::AsteroidOrbitColor;
1482             break;
1483         case Body::Comet:
1484             orbitColor = Renderer::CometOrbitColor;
1485             break;
1486         case Body::Spacecraft:
1487             orbitColor = Renderer::SpacecraftOrbitColor;
1488             break;
1489         case Body::Stellar:
1490             orbitColor = Renderer::StarOrbitColor;
1491             break;
1492         case Body::DwarfPlanet:
1493             orbitColor = Renderer::DwarfPlanetOrbitColor;
1494             break;
1495         case Body::Planet:
1496         default:
1497             orbitColor = Renderer::PlanetOrbitColor;
1498             break;
1499         }
1500     }
1501 
1502 #ifdef USE_HDR
1503     glColor(orbitColor, 1.f - opacity * orbitColor.alpha());
1504 #else
1505     glColor(orbitColor, opacity * orbitColor.alpha());
1506 #endif
1507 }
1508 
1509 
1510 // Subdivide the orbit into sections and compute a bounding volume for each section. The bounding
1511 // volumes used are capsules, the set of all points less than some constant distance from a line
1512 // segment.
computeOrbitSectionBoundingVolumes(Renderer::CachedOrbit & orbit)1513 static void computeOrbitSectionBoundingVolumes(Renderer::CachedOrbit& orbit)
1514 {
1515     const unsigned int MinOrbitSections = 6;
1516     const unsigned int MinSamplesPerSection = 32;
1517 
1518     // Determine the number of trajectory samples to include in each bounding volume; typically,
1519     // the last volume will contain any leftover samples.
1520     unsigned int nSections = max((unsigned int) orbit.trajectory.size() / MinSamplesPerSection, MinOrbitSections);
1521     unsigned int samplesPerSection = orbit.trajectory.size() / nSections;
1522     if (samplesPerSection <= 1)
1523     {
1524         if (orbit.trajectory.size() == 0)
1525             nSections = 0;
1526         else
1527             nSections = 1;
1528     }
1529 
1530     for (unsigned int i = 0; i < nSections; i++)
1531     {
1532         unsigned int nSamples;
1533         if (i != nSections - 1)
1534             nSamples = samplesPerSection;
1535         else
1536             nSamples = orbit.trajectory.size() - (nSections - 1) * samplesPerSection;
1537 
1538         Renderer::OrbitSection section;
1539         section.firstSample = samplesPerSection * i;
1540         unsigned int lastSample = min((unsigned int) orbit.trajectory.size() - 1, section.firstSample + nSamples + 1);
1541 
1542         // Set the initial axis and origin of the capsule bounding volume; they will be adjusted
1543         // to contain all points in the trajectory. The length of the axis may change, but the
1544         // direction will remain the same.
1545         Vec3d axis = orbit.trajectory[section.firstSample].pos - orbit.trajectory[lastSample].pos;
1546         Point3d orig = orbit.trajectory[section.firstSample].pos;
1547         double d = 1.0 / (axis * axis);
1548         double minT = 0.0;
1549         double maxT = 0.0;
1550         double maxDistSquared = 0.0;
1551 
1552         for (unsigned int j = section.firstSample; j <= lastSample; j++)
1553         {
1554             Point3d p = orbit.trajectory[j].pos;
1555             double t = ((p - orig) * axis) * d;
1556             Vec3d pointToAxis = p - (orig + axis * t);
1557             double distSquared = pointToAxis * pointToAxis;
1558             if (t < minT)
1559                 minT = t;
1560             if (t > maxT)
1561                 maxT = t;
1562             if (distSquared > maxDistSquared)
1563                 maxDistSquared = distSquared;
1564         }
1565 
1566         section.boundingVolume.origin = orig + axis * minT;
1567         section.boundingVolume.axis = axis * (maxT - minT);
1568 
1569         // Make the bounding volume a bit thicker to avoid roundoff problems, and
1570         // to account cases when interpolation adds points slightly outside the
1571         // volume defined by the sampled points.
1572         section.boundingVolume.radius = sqrt(maxDistSquared) * 1.1f;;
1573 
1574         orbit.sections.push_back(section);
1575     }
1576 }
1577 
1578 
cubicInterpolate(const Point3d & p0,const Vec3d & v0,const Point3d & p1,const Vec3d & v1,double t)1579 static Point3d cubicInterpolate(const Point3d& p0, const Vec3d& v0,
1580                                 const Point3d& p1, const Vec3d& v1,
1581                                 double t)
1582 {
1583     return p0 + (((2.0 * (p0 - p1) + v1 + v0) * (t * t * t)) +
1584                  ((3.0 * (p1 - p0) - 2.0 * v0 - v1) * (t * t)) +
1585                  (v0 * t));
1586 }
1587 
1588 
1589 static int splinesRendered = 0;
1590 static int orbitsRendered = 0;
1591 static int orbitsSkipped = 0;
1592 static int sectionsCulled = 0;
renderOrbitSplineSegment(const Renderer::OrbitSample & p0,const Renderer::OrbitSample & p1,const Renderer::OrbitSample & p2,const Renderer::OrbitSample & p3,double nearZ,double farZ,unsigned int subdivisions,int lastOutcode,bool drawLastSegment)1593 static Point3d renderOrbitSplineSegment(const Renderer::OrbitSample& p0,
1594                                         const Renderer::OrbitSample& p1,
1595                                         const Renderer::OrbitSample& p2,
1596                                         const Renderer::OrbitSample& p3,
1597                                         double nearZ,
1598                                         double farZ,
1599                                         unsigned int subdivisions,
1600                                         int lastOutcode,
1601                                         bool drawLastSegment)
1602 {
1603     Vec3d v0 = (p2.pos - p0.pos) * ((p2.t - p1.t) / (p2.t - p0.t));
1604     Vec3d v1 = (p3.pos - p1.pos) * ((p2.t - p1.t) / (p3.t - p1.t));
1605     double dt = 1.0 / (double) subdivisions;
1606     if (drawLastSegment)
1607         subdivisions++;
1608 
1609     splinesRendered++;
1610 
1611     Point3d lastP = p1.pos;
1612 
1613     for (unsigned int i = 0; i < subdivisions; i++)
1614     {
1615         Point3d p = cubicInterpolate(p1.pos, v0, p2.pos, v1, i * dt);
1616         int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
1617 
1618         if ((outcode | lastOutcode) == 0)
1619         {
1620             glVertex3d(p.x, p.y, p.z);
1621         }
1622         else if ((outcode & lastOutcode) == 0)
1623         {
1624             // Need to clip
1625             Point3d q0 = lastP;
1626             Point3d q1 = p;
1627 
1628             if (lastOutcode != 0)
1629             {
1630                 glBegin(GL_LINE_STRIP);
1631                 double t;
1632                 if (lastOutcode == 1)
1633                     t = (nearZ - lastP.z) / (p.z - lastP.z);
1634                 else
1635                     t = (farZ - lastP.z) / (p.z - lastP.z);
1636                 q0 = lastP + t * (p - lastP);
1637             }
1638 
1639             if (outcode != 0)
1640             {
1641                 double t;
1642                 if (outcode == 1)
1643                     t = (nearZ - lastP.z) / (p.z - lastP.z);
1644                 else
1645                     t = (farZ - lastP.z) / (p.z - lastP.z);
1646                 q1 = lastP + t * (p - lastP);
1647             }
1648 
1649             glVertex3d(q0.x, q0.y, q0.z);
1650             glVertex3d(q1.x, q1.y, q1.z);
1651 
1652             if (outcode != 0)
1653             {
1654                 glEnd();
1655             }
1656         }
1657 
1658         lastOutcode = outcode;
1659         lastP = p;
1660     }
1661 
1662     return lastP;
1663 }
1664 
1665 
1666 #if 0
1667 // Not yet used
1668 static Point3d renderOrbitSplineAdaptive(const Renderer::OrbitSample& p0,
1669                                          const Renderer::OrbitSample& p1,
1670                                          const Renderer::OrbitSample& p2,
1671                                          const Renderer::OrbitSample& p3,
1672                                          double nearZ,
1673                                          double farZ,
1674                                          unsigned int minSubdivisions,
1675                                          unsigned int maxSubdivisions,
1676                                          int lastOutcode,
1677                                          bool drawLastSegment)
1678 {
1679     Vec3d v0 = (p2.pos - p0.pos) * ((p2.t - p1.t) / (p2.t - p0.t));
1680     Vec3d v1 = (p3.pos - p1.pos) * ((p2.t - p1.t) / (p3.t - p1.t));
1681     double minDt = 1.0 / (double) maxSubdivisions;
1682     double maxDt = 1.0 / (double) minSubdivisions;
1683     double g = (p2.pos - p1.pos).length() * maxSubdivisions;
1684     double t = 0.0;
1685 
1686     splinesRendered += 10000;
1687 
1688     Point3d lastP = p1.pos;
1689 
1690     while (t < 1.0)
1691     {
1692         t += max(minDt, max(lastP.distanceFromOrigin() / g, maxDt));
1693         if (drawLastSegment && t > 1.0)
1694             t = 1.0;
1695         else
1696             break;
1697 
1698         Point3d p = cubicInterpolate(p1.pos, v0, p2.pos, v1, t);
1699         int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
1700 
1701         if ((outcode | lastOutcode) == 0)
1702         {
1703             glVertex3d(p.x, p.y, p.z);
1704         }
1705         else if ((outcode & lastOutcode) == 0)
1706         {
1707             // Need to clip
1708             Point3d q0 = lastP;
1709             Point3d q1 = p;
1710 
1711             if (lastOutcode != 0)
1712             {
1713                 glBegin(GL_LINE_STRIP);
1714                 double t;
1715                 if (lastOutcode == 1)
1716                     t = (nearZ - lastP.z) / (p.z - lastP.z);
1717                 else
1718                     t = (farZ - lastP.z) / (p.z - lastP.z);
1719                 q0 = lastP + t * (p - lastP);
1720             }
1721 
1722             if (outcode != 0)
1723             {
1724                 double t;
1725                 if (outcode == 1)
1726                     t = (nearZ - lastP.z) / (p.z - lastP.z);
1727                 else
1728                     t = (farZ - lastP.z) / (p.z - lastP.z);
1729                 q1 = lastP + t * (p - lastP);
1730             }
1731 
1732             glVertex3d(q0.x, q0.y, q0.z);
1733             glVertex3d(q1.x, q1.y, q1.z);
1734 
1735             if (outcode != 0)
1736             {
1737                 glEnd();
1738             }
1739         }
1740 
1741         lastOutcode = outcode;
1742         lastP = p;
1743     }
1744 
1745     return lastP;
1746 }
1747 #endif
1748 
1749 
renderOrbitSection(const Orbit & orbit,Renderer::CachedOrbit & cachedOrbit,unsigned int sectionNumber,const Mat4d & modelview,Point3d lastP,int lastOutcode,double nearZ,double farZ,uint32)1750 static Point3d renderOrbitSection(const Orbit& orbit,
1751                                   Renderer::CachedOrbit& cachedOrbit,
1752                                   unsigned int sectionNumber,
1753                                   const Mat4d& modelview,
1754                                   Point3d lastP,
1755                                   int lastOutcode,
1756                                   double nearZ,
1757                                   double farZ,
1758                                   uint32 /* renderFlags */)
1759 {
1760     vector<Renderer::OrbitSample>& trajectory(cachedOrbit.trajectory);
1761     unsigned int nPoints = cachedOrbit.trajectory.size();
1762 
1763     unsigned int firstPoint = cachedOrbit.sections[sectionNumber].firstSample + 1;
1764     unsigned int lastPoint;
1765     if (sectionNumber != cachedOrbit.sections.size() - 1)
1766         lastPoint = cachedOrbit.sections[sectionNumber + 1].firstSample;
1767     else
1768         lastPoint = nPoints - 1;
1769 
1770     double sectionRadius = cachedOrbit.sections[sectionNumber].boundingVolume.radius;
1771     double minSmoothZ = -sectionRadius * 8;
1772     double maxSmoothZ = nearZ + sectionRadius * 8;
1773 
1774     for (unsigned int i = firstPoint; i <= lastPoint; i++)
1775     {
1776         Point3d p = trajectory[i].pos * modelview;
1777         int outcode;
1778 
1779         // This segment of the orbit is very close to the camera and may appear
1780         // jagged. We'll add extra segments with cubic spline interpolation.
1781         // TODO: This calculation should depend on the field of view as well
1782         unsigned int splineSubdivisions = 0;
1783         if ((p.z > minSmoothZ || lastP.z > minSmoothZ) &&
1784             !(p.z > maxSmoothZ && lastP.z > maxSmoothZ))
1785         {
1786             double distFromEye = distanceToSegment(Point3d(0.0, 0.0, 0.0), lastP, p - lastP);
1787 
1788             if (distFromEye < sectionRadius)
1789                 splineSubdivisions = 64;
1790             else if (distFromEye < sectionRadius * 8)
1791                 splineSubdivisions = (unsigned int) (sectionRadius / distFromEye * 64);
1792         }
1793 
1794         if (splineSubdivisions > 1)
1795         {
1796             // Render this part of the orbit as a spline instead of a line segment
1797             Renderer::OrbitSample s0, s1, s2, s3;
1798             if (i > 1)
1799             {
1800                 s0 = trajectory[i - 2];
1801             }
1802             else if (orbit.isPeriodic())
1803             {
1804                 // Careful: use second to last sample, since first sample is duplicate of last
1805                 s0 = trajectory[nPoints - 2];
1806                 s0.t -= orbit.getPeriod();
1807             }
1808             else
1809             {
1810                 s0 = trajectory[i - 1];
1811             }
1812 
1813             if (i < trajectory.size() - 1)
1814             {
1815                 s3 = trajectory[i + 1];
1816             }
1817             else if (orbit.isPeriodic())
1818             {
1819                 // Careful: use second sample, since first sample is duplicate of last
1820                 s3 = trajectory[1];
1821                 s3.t += orbit.getPeriod();
1822             }
1823             else
1824             {
1825                 s3 = trajectory[i];
1826             }
1827 
1828             s1 = Renderer::OrbitSample(lastP, trajectory[i - 1].t);
1829             s2 = Renderer::OrbitSample(p,     trajectory[i].t);
1830 
1831             s0.pos = s0.pos * modelview;
1832             s3.pos = s3.pos * modelview;
1833 
1834             bool drawLastSegment = i == nPoints - 1;
1835 
1836             p = renderOrbitSplineSegment(s0, s1, s2, s3,
1837                                          nearZ, farZ,
1838                                          splineSubdivisions,
1839                                          lastOutcode,
1840                                          drawLastSegment);
1841             outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
1842         }
1843         else
1844         {
1845             // Just draw a line segment
1846 
1847             // Compute the outcode mask for clipping:
1848             //   00 = between near and far
1849             //   01 = greater than nearZ (nearZ and farZ are always negative)
1850             //   10 = less than farZ
1851             // Given two outcodes o1 and o2 of line segment endpoints:
1852             //   o1 | o2 == 0 means segment lies completely between near and far plans
1853             //   o2 & o2 != 0 means segment lies completely outside planes
1854             //   else we have to clip the line.
1855             outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
1856 
1857             if ((outcode | lastOutcode) == 0)
1858             {
1859                 // Segment is completely between near and far planes
1860                 glVertex3d(p.x, p.y, p.z);
1861             }
1862             else if ((outcode & lastOutcode) == 0)
1863             {
1864                 // Need to clip
1865                 Point3d q0 = lastP;
1866                 Point3d q1 = p;
1867 
1868                 // Clip against the enter plane
1869                 if (lastOutcode != 0)
1870                 {
1871                     glBegin(GL_LINE_STRIP);
1872                     double t;
1873                     if (lastOutcode == 1)
1874                         t = (nearZ - lastP.z) / (p.z - lastP.z);
1875                     else
1876                         t = (farZ - lastP.z) / (p.z - lastP.z);
1877                     q0 = lastP + t * (p - lastP);
1878                 }
1879 
1880                 // Clip against the exit plane
1881                 if (outcode != 0)
1882                 {
1883                     double t;
1884                     if (outcode == 1)
1885                         t = (nearZ - lastP.z) / (p.z - lastP.z);
1886                     else
1887                         t = (farZ - lastP.z) / (p.z - lastP.z);
1888                     q1 = lastP + t * (p - lastP);
1889                 }
1890 
1891                 glVertex3d(q0.x, q0.y, q0.z);
1892                 glVertex3d(q1.x, q1.y, q1.z);
1893 
1894                 if (outcode != 0)
1895                 {
1896                     glEnd();
1897                 }
1898             }
1899         }
1900 
1901         lastOutcode = outcode;
1902         lastP = p;
1903     }
1904 
1905     return lastP;
1906 }
1907 
1908 
renderOrbit(const OrbitPathListEntry & orbitPath,double t,const Quatf & cameraOrientationf,const Frustum & frustum,float nearDist,float farDist)1909 void Renderer::renderOrbit(const OrbitPathListEntry& orbitPath,
1910                            double t,
1911                            const Quatf& cameraOrientationf,
1912                            const Frustum& frustum,
1913                            float nearDist,
1914                            float farDist)
1915 {
1916     Body* body = orbitPath.body;
1917     Quatd cameraOrientation(cameraOrientationf.w, cameraOrientationf.x, cameraOrientationf.y, cameraOrientationf.z);
1918     double nearZ = -nearDist;  // negate, becase z is into the screen in camera space
1919     double farZ = -farDist;
1920 
1921     const Orbit* orbit = NULL;
1922     if (body != NULL)
1923         orbit = body->getOrbit(t);
1924     else
1925         orbit = orbitPath.star->getOrbit();
1926 
1927     CachedOrbit* cachedOrbit = NULL;
1928     OrbitCache::iterator cached = orbitCache.find(orbit);
1929     if (cached != orbitCache.end())
1930     {
1931         cachedOrbit = cached->second;
1932         cachedOrbit->lastUsed = frameCount;
1933     }
1934 
1935     // If it's not in the cache already
1936     if (cachedOrbit == NULL)
1937     {
1938         double startTime = t;
1939         int nSamples = detailOptions.orbitPathSamplePoints;
1940 
1941         // Adjust the number of samples used for aperiodic orbits--these aren't
1942         // true orbits, but are sampled trajectories, generally of spacecraft.
1943         // Better control is really needed--some sort of adaptive sampling would
1944         // be ideal.
1945         if (!orbit->isPeriodic())
1946         {
1947             double begin = 0.0, end = 0.0;
1948             orbit->getValidRange(begin, end);
1949 
1950             if (begin != end)
1951             {
1952                 startTime = begin;
1953                 nSamples = (int) (orbit->getPeriod() * 100.0);
1954                 nSamples = max(min(nSamples, 1000), 100);
1955             }
1956             else
1957             {
1958                 // If the orbit is aperiodic and doesn't have a
1959                 // finite duration, we don't render it. A compromise
1960                 // would be to pick some time window centered at the
1961                 // current time, but we'd have to pick some arbitrary
1962                 // duration.
1963                 nSamples = 0;
1964             }
1965         }
1966 
1967         cachedOrbit = new CachedOrbit();
1968         cachedOrbit->lastUsed = frameCount;
1969 
1970         OrbitSampler sampler(&cachedOrbit->trajectory);
1971         orbit->sample(startTime,
1972                       orbit->getPeriod(),
1973                       nSamples,
1974                       sampler);
1975 
1976         // Add an extra sample to close a periodic orbit
1977         if (orbit->isPeriodic())
1978         {
1979             if (!cachedOrbit->trajectory.empty())
1980             {
1981                 double lastSampleTime = cachedOrbit->trajectory[0].t + orbit->getPeriod();
1982                 cachedOrbit->trajectory.push_back(OrbitSample(cachedOrbit->trajectory[0].pos, lastSampleTime));
1983             }
1984         }
1985 
1986         computeOrbitSectionBoundingVolumes(*cachedOrbit);
1987 
1988         // If the orbit cache is full, first try and eliminate some old orbits
1989         if (orbitCache.size() > OrbitCacheCullThreshold)
1990         {
1991             // Check for old orbits at most once per frame
1992             if (lastOrbitCacheFlush != frameCount)
1993             {
1994                 for (OrbitCache::iterator iter = orbitCache.begin(); iter != orbitCache.end();)
1995                 {
1996                     // Tricky code to eliminate a node in the orbit cache without screwing
1997                     // up the iterator. Should work in all STL implementations.
1998                     if (frameCount - iter->second->lastUsed > OrbitCacheRetireAge)
1999                         orbitCache.erase(iter++);
2000                     else
2001                         ++iter;
2002                 }
2003                 lastOrbitCacheFlush = frameCount;
2004             }
2005         }
2006 
2007         orbitCache[orbit] = cachedOrbit;
2008     }
2009 
2010     vector<OrbitSample>* trajectory = &cachedOrbit->trajectory;
2011 
2012     // The rest of the function isn't designed for empty trajectories
2013     if (trajectory->empty())
2014         return;
2015 
2016     // We perform vertex tranformations on the CPU because double precision is necessary to
2017     // render orbits properly. Start by computing the modelview matrix, to transform orbit
2018     // vertices into camera space.
2019     Mat4d modelview;
2020     {
2021         Quatd orientation(1.0);
2022         if (body != NULL)
2023         {
2024             orientation = body->getOrbitFrame(t)->getOrientation(t);
2025         }
2026 
2027         // Equivalent to:
2028         // glRotate(cameraOrientation);
2029         // glTranslate(orbitPath.origin);
2030         // glRotate(~orientation);
2031         modelview =
2032             (orientation).toMatrix4() *
2033             Mat4d::translation(Point3d(orbitPath.origin.x, orbitPath.origin.y, orbitPath.origin.z)) *
2034             (~cameraOrientation).toMatrix4();
2035     }
2036 
2037     glPushMatrix();
2038     glLoadIdentity();
2039 
2040     bool highlight;
2041     if (body != NULL)
2042         highlight = highlightObject.body() == body;
2043     else
2044         highlight = highlightObject.star() == orbitPath.star;
2045     renderOrbitColor(body, highlight, orbitPath.opacity);
2046 
2047     if ((renderFlags & ShowPartialTrajectories) == 0 || orbit->isPeriodic())
2048     {
2049         // Show the complete trajectory
2050         Point3d p;
2051         p = (*trajectory)[0].pos * modelview;
2052         int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
2053 
2054         if (outcode == 0)
2055         {
2056             glBegin(GL_LINE_STRIP);
2057             glVertex3d(p.x, p.y, p.z);
2058         }
2059 
2060         Point3d lastP = p;
2061         int lastOutcode = outcode;
2062 
2063         // The trajectory is subdivided into sections that each contain a number of samples.
2064         // Process each section in the trajectory, using its precomputed bounding volume to
2065         // quickly check for visibility.
2066         for (unsigned int i = 0; i < cachedOrbit->sections.size(); i++)
2067         {
2068             Capsuled& bv = cachedOrbit->sections[i].boundingVolume;
2069             Point3d orig = bv.origin * modelview;
2070             Vec3d axis   = bv.axis   * modelview;
2071             Capsulef bvf(Point3f((float) orig.x, (float) orig.y, (float) orig.z),
2072                          Vec3f((float) axis.x, (float) axis.y, (float) axis.z),
2073                          (float) bv.radius);
2074 
2075             // TODO: Create a fast path for the case when the bounding volume lies completely
2076             // within the frustum and clipping can be ignored.
2077             if (frustum.testCapsule(bvf) != Frustum::Outside)
2078             {
2079                 lastP = renderOrbitSection(*orbit,
2080                                            *cachedOrbit, i,
2081                                            modelview,
2082                                            lastP, lastOutcode,
2083                                            nearZ, farZ,
2084                                            renderFlags);
2085                 lastOutcode = (lastP.z > nearZ ? 1 : 0) | (lastP.z < farZ ? 2 : 0);
2086             }
2087             else
2088             {
2089                 // The section was culled because it lies completely outside the view frustum,
2090                 // but we still need to do some work to keep the begin/end state of the line
2091                 // strip current. We just need to process the final point in the section.
2092                 unsigned int lastSample;
2093                 if (i < cachedOrbit->sections.size() - 1)
2094                     lastSample = cachedOrbit->sections[i + 1].firstSample;
2095                 else
2096                     lastSample = cachedOrbit->trajectory.size() - 1;
2097 
2098                 p = (*trajectory)[lastSample].pos * modelview;
2099                 outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
2100 
2101                 if ((outcode | lastOutcode) == 0)
2102                 {
2103                     // Segment is completely between near and far planes
2104                     glVertex3d(p.x, p.y, p.z);
2105                 }
2106                 else if ((outcode & lastOutcode) == 0)
2107                 {
2108                     // Need to clip
2109                     Point3d q0 = lastP;
2110                     Point3d q1 = p;
2111 
2112                     // Clip against the enter plane
2113                     if (lastOutcode != 0)
2114                     {
2115                         glBegin(GL_LINE_STRIP);
2116                         double t;
2117                         if (lastOutcode == 1)
2118                             t = (nearZ - lastP.z) / (p.z - lastP.z);
2119                         else
2120                             t = (farZ - lastP.z) / (p.z - lastP.z);
2121                         q0 = lastP + t * (p - lastP);
2122                     }
2123 
2124                     // Clip against the exit plane
2125                     if (outcode != 0)
2126                     {
2127                         double t;
2128                         if (outcode == 1)
2129                             t = (nearZ - lastP.z) / (p.z - lastP.z);
2130                         else
2131                             t = (farZ - lastP.z) / (p.z - lastP.z);
2132                         q1 = lastP + t * (p - lastP);
2133                     }
2134 
2135                     glVertex3d(q0.x, q0.y, q0.z);
2136                     glVertex3d(q1.x, q1.y, q1.z);
2137 
2138                     if (outcode != 0)
2139                     {
2140                         glEnd();
2141                     }
2142                 }
2143 
2144                 lastP = p;
2145                 lastOutcode = outcode;
2146 
2147                 sectionsCulled++;
2148             }
2149         }
2150 
2151         if (lastOutcode == 0)
2152         {
2153             glEnd();
2154         }
2155     }
2156     else
2157     {
2158         double endTime = t;
2159         bool endTimeReached = false;
2160 
2161         Point3d p;
2162         p = (*trajectory)[0].pos * modelview;
2163         int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
2164 
2165         if (outcode == 0)
2166         {
2167             glBegin(GL_LINE_STRIP);
2168             glVertex3d(p.x, p.y, p.z);
2169         }
2170 
2171         Point3d lastP = p;
2172         int lastOutcode = outcode;
2173 
2174         unsigned int nPoints = trajectory->size();
2175         for (unsigned int i = 1; i < nPoints && !endTimeReached; i++)
2176         {
2177             if ((*trajectory)[i].t > endTime)
2178             {
2179                 p = orbit->positionAtTime(endTime) * modelview;
2180                 endTimeReached = true;
2181             }
2182             else
2183             {
2184                 p = (*trajectory)[i].pos * modelview;
2185             }
2186 
2187             outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
2188             if ((outcode | lastOutcode) == 0)
2189             {
2190                 glVertex3d(p.x, p.y, p.z);
2191             }
2192             else if ((outcode & lastOutcode) == 0)
2193             {
2194                 // Need to clip
2195 
2196                 Point3d p0 = lastP;
2197                 Point3d p1 = p;
2198 
2199                 if (lastOutcode != 0)
2200                 {
2201                     glBegin(GL_LINE_STRIP);
2202                     double t;
2203                     if (lastOutcode == 1)
2204                         t = (nearZ - lastP.z) / (p.z - lastP.z);
2205                     else
2206                         t = (farZ - lastP.z) / (p.z - lastP.z);
2207                     p0 = lastP + t * (p - lastP);
2208                 }
2209 
2210                 if (outcode != 0)
2211                 {
2212                     double t;
2213                     if (outcode == 1)
2214                         t = (nearZ - lastP.z) / (p.z - lastP.z);
2215                     else
2216                         t = (farZ - lastP.z) / (p.z - lastP.z);
2217                     p1 = lastP + t * (p - lastP);
2218                 }
2219 
2220                 glVertex3d(p0.x, p0.y, p0.z);
2221                 glVertex3d(p1.x, p1.y, p1.z);
2222 
2223                 if (outcode != 0)
2224                 {
2225                     glEnd();
2226                 }
2227             }
2228 
2229             lastOutcode = outcode;
2230             lastP = p;
2231         }
2232 
2233         if (lastOutcode == 0)
2234         {
2235             glEnd();
2236         }
2237     }
2238 
2239     glPopMatrix();
2240 }
2241 
2242 
2243 // Convert a position in the universal coordinate system to astrocentric
2244 // coordinates, taking into account possible orbital motion of the star.
astrocentricPosition(const UniversalCoord & pos,const Star & star,double t)2245 static Point3d astrocentricPosition(const UniversalCoord& pos,
2246                                     const Star& star,
2247                                     double t)
2248 {
2249     UniversalCoord starPos = star.getPosition(t);
2250 
2251     Vec3d v = pos - starPos;
2252     return Point3d(astro::microLightYearsToKilometers(v.x),
2253                    astro::microLightYearsToKilometers(v.y),
2254                    astro::microLightYearsToKilometers(v.z));
2255 }
2256 
2257 
autoMag(float & faintestMag)2258 void Renderer::autoMag(float& faintestMag)
2259 {
2260       float fieldCorr = 2.0f * FOV/(fov + FOV);
2261       faintestMag = (float) (faintestAutoMag45deg * sqrt(fieldCorr));
2262       saturationMag = saturationMagNight * (1.0f + fieldCorr * fieldCorr);
2263 }
2264 
2265 
2266 // Set up the light sources for rendering a solar system.  The positions of
2267 // all nearby stars are converted from universal to viewer-centered
2268 // coordinates.
2269 static void
setupLightSources(const vector<const Star * > & nearStars,const UniversalCoord & observerPos,double t,vector<LightSource> & lightSources)2270 setupLightSources(const vector<const Star*>& nearStars,
2271                   const UniversalCoord& observerPos,
2272                   double t,
2273                   vector<LightSource>& lightSources)
2274 {
2275     lightSources.clear();
2276 
2277     for (vector<const Star*>::const_iterator iter = nearStars.begin();
2278          iter != nearStars.end(); iter++)
2279     {
2280         if ((*iter)->getVisibility())
2281         {
2282             Vec3d v = ((*iter)->getPosition(t) - observerPos) *
2283                 astro::microLightYearsToKilometers(1.0);
2284 
2285             LightSource ls;
2286             ls.position = v;
2287             ls.luminosity = (*iter)->getLuminosity();
2288             ls.radius = (*iter)->getRadius();
2289 
2290             // If the star is sufficiently cool, change the light color
2291             // from white.  Though our sun appears yellow, we still make
2292             // it and all hotter stars emit white light, as this is the
2293             // 'natural' light to which our eyes are accustomed.  We also
2294             // assign a slight bluish tint to light from O and B type stars,
2295             // though these will almost never have planets for their light
2296             // to shine upon.
2297             float temp = (*iter)->getTemperature();
2298             if (temp > 30000.0f)
2299                 ls.color = Color(0.8f, 0.8f, 1.0f);
2300             else if (temp > 10000.0f)
2301                 ls.color = Color(0.9f, 0.9f, 1.0f);
2302             else if (temp > 5400.0f)
2303                 ls.color = Color(1.0f, 1.0f, 1.0f);
2304             else if (temp > 3900.0f)
2305                 ls.color = Color(1.0f, 0.9f, 0.8f);
2306             else if (temp > 2000.0f)
2307                 ls.color = Color(1.0f, 0.7f, 0.7f);
2308             else
2309                 ls.color = Color(1.0f, 0.4f, 0.4f);
2310 
2311             lightSources.push_back(ls);
2312         }
2313     }
2314 }
2315 
2316 
2317 // Set up the potential secondary light sources for rendering solar system
2318 // bodies.
2319 static void
setupSecondaryLightSources(vector<SecondaryIlluminator> & secondaryIlluminators,const vector<LightSource> & primaryIlluminators)2320 setupSecondaryLightSources(vector<SecondaryIlluminator>& secondaryIlluminators,
2321                            const vector<LightSource>& primaryIlluminators)
2322 {
2323     float au2 = square(astro::kilometersToAU(1.0f));
2324 
2325     for (vector<SecondaryIlluminator>::iterator i = secondaryIlluminators.begin();
2326          i != secondaryIlluminators.end(); i++)
2327     {
2328         i->reflectedIrradiance = 0.0f;
2329 
2330         for (vector<LightSource>::const_iterator j = primaryIlluminators.begin();
2331              j != primaryIlluminators.end(); j++)
2332         {
2333             i->reflectedIrradiance += j->luminosity / ((float) (i->position_v - j->position).lengthSquared() * au2);
2334         }
2335 
2336         i->reflectedIrradiance *= i->body->getAlbedo();
2337     }
2338 }
2339 
2340 
2341 // Render an item from the render list
renderItem(const RenderListEntry & rle,const Observer & observer,const Quatf & cameraOrientation,float nearPlaneDistance,float farPlaneDistance)2342 void Renderer::renderItem(const RenderListEntry& rle,
2343                           const Observer& observer,
2344                           const Quatf& cameraOrientation,
2345                           float nearPlaneDistance,
2346                           float farPlaneDistance)
2347 {
2348     switch (rle.renderableType)
2349     {
2350     case RenderListEntry::RenderableStar:
2351         renderStar(*rle.star,
2352                    rle.position,
2353                    rle.distance,
2354                    rle.appMag,
2355                    cameraOrientation,
2356                    observer.getTime(),
2357                    nearPlaneDistance, farPlaneDistance);
2358         break;
2359 
2360     case RenderListEntry::RenderableBody:
2361         renderPlanet(*rle.body,
2362                      rle.position,
2363                      rle.distance,
2364                      rle.appMag,
2365                      observer,
2366                      cameraOrientation,
2367                      nearPlaneDistance, farPlaneDistance);
2368         break;
2369 
2370     case RenderListEntry::RenderableCometTail:
2371         renderCometTail(*rle.body,
2372                         rle.position,
2373                         observer.getTime(),
2374                         rle.discSizeInPixels);
2375         break;
2376 
2377     case RenderListEntry::RenderableReferenceMark:
2378         renderReferenceMark(*rle.refMark,
2379                             rle.position,
2380                             rle.distance,
2381                             observer.getTime(),
2382                             nearPlaneDistance);
2383         break;
2384 
2385     default:
2386         break;
2387     }
2388 }
2389 
2390 
2391 #ifdef USE_HDR
genBlurTextures()2392 void Renderer::genBlurTextures()
2393 {
2394     for (size_t i = 0; i < BLUR_PASS_COUNT; ++i)
2395     {
2396         if (blurTextures[i] != NULL)
2397         {
2398             delete blurTextures[i];
2399             blurTextures[i] = NULL;
2400         }
2401     }
2402     if (blurTempTexture)
2403     {
2404         delete blurTempTexture;
2405         blurTempTexture = NULL;
2406     }
2407 
2408     blurBaseWidth = sceneTexWidth, blurBaseHeight = sceneTexHeight;
2409 
2410     if (blurBaseWidth > blurBaseHeight)
2411     {
2412         while (blurBaseWidth > BLUR_SIZE)
2413         {
2414             blurBaseWidth  >>= 1;
2415             blurBaseHeight >>= 1;
2416         }
2417     }
2418     else
2419     {
2420         while (blurBaseHeight > BLUR_SIZE)
2421         {
2422             blurBaseWidth  >>= 1;
2423             blurBaseHeight >>= 1;
2424         }
2425     }
2426     genBlurTexture(0);
2427     genBlurTexture(1);
2428 
2429     Image *tempImg;
2430     ImageTexture *tempTexture;
2431     tempImg = new Image(GL_LUMINANCE, blurBaseWidth, blurBaseHeight);
2432     tempTexture = new ImageTexture(*tempImg, Texture::EdgeClamp, Texture::DefaultMipMaps);
2433     delete tempImg;
2434     if (tempTexture && tempTexture->getName() != 0)
2435         blurTempTexture = tempTexture;
2436 }
2437 
genBlurTexture(int blurLevel)2438 void Renderer::genBlurTexture(int blurLevel)
2439 {
2440     Image *img;
2441     ImageTexture *texture;
2442 
2443 #ifdef DEBUG_HDR
2444     HDR_LOG <<
2445         "Window width = "    << windowWidth << ", " <<
2446         "Window height = "   << windowHeight << ", " <<
2447         "Blur tex width = "  << (blurBaseWidth>>blurLevel) << ", " <<
2448         "Blur tex height = " << (blurBaseHeight>>blurLevel) << endl;
2449 #endif
2450     img = new Image(blurFormat,
2451                     blurBaseWidth>>blurLevel,
2452                     blurBaseHeight>>blurLevel);
2453     texture = new ImageTexture(*img,
2454                                Texture::EdgeClamp,
2455                                Texture::NoMipMaps);
2456     delete img;
2457 
2458     if (texture && texture->getName() != 0)
2459         blurTextures[blurLevel] = texture;
2460 }
2461 
genSceneTexture()2462 void Renderer::genSceneTexture()
2463 {
2464 	unsigned int *data;
2465     if (sceneTexture != 0)
2466         glDeleteTextures(1, &sceneTexture);
2467 
2468     sceneTexWidth  = 1;
2469     sceneTexHeight = 1;
2470     while (sceneTexWidth < windowWidth)
2471         sceneTexWidth <<= 1;
2472     while (sceneTexHeight < windowHeight)
2473         sceneTexHeight <<= 1;
2474     sceneTexWScale = (windowWidth > 0)  ? (GLfloat)sceneTexWidth  / (GLfloat)windowWidth :
2475         1.0f;
2476     sceneTexHScale = (windowHeight > 0) ? (GLfloat)sceneTexHeight / (GLfloat)windowHeight :
2477         1.0f;
2478 	data = (unsigned int* )malloc(sceneTexWidth*sceneTexHeight*4*sizeof(unsigned int));
2479     memset(data, 0, sceneTexWidth*sceneTexHeight*4*sizeof(unsigned int));
2480 
2481     glGenTextures(1, &sceneTexture);
2482 	glBindTexture(GL_TEXTURE_2D, sceneTexture);
2483     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
2484     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
2485     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
2486     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
2487 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, sceneTexWidth, sceneTexHeight, 0,
2488                  GL_RGBA, GL_UNSIGNED_BYTE, data);
2489 
2490 	free(data);
2491 #ifdef DEBUG_HDR
2492     static int genSceneTexCounter = 1;
2493     HDR_LOG <<
2494         "[" << genSceneTexCounter++ << "] " <<
2495         "Window width = "  << windowWidth << ", " <<
2496         "Window height = " << windowHeight << ", " <<
2497         "Tex width = "  << sceneTexWidth << ", " <<
2498         "Tex height = " << sceneTexHeight << endl;
2499 #endif
2500 }
2501 
renderToBlurTexture(int blurLevel)2502 void Renderer::renderToBlurTexture(int blurLevel)
2503 {
2504     if (blurTextures[blurLevel] == NULL)
2505         return;
2506     GLsizei blurTexWidth  = blurBaseWidth>>blurLevel;
2507     GLsizei blurTexHeight = blurBaseHeight>>blurLevel;
2508     GLsizei blurDrawWidth = (GLfloat)windowWidth/(GLfloat)sceneTexWidth * blurTexWidth;
2509     GLsizei blurDrawHeight = (GLfloat)windowHeight/(GLfloat)sceneTexHeight * blurTexHeight;
2510     GLfloat blurWScale = 1.f;
2511     GLfloat blurHScale = 1.f;
2512     GLfloat savedWScale = 1.f;
2513     GLfloat savedHScale = 1.f;
2514 
2515     glPushAttrib(GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
2516     glClearColor(0, 0, 0, 1.f);
2517     glViewport(0, 0, blurDrawWidth, blurDrawHeight);
2518     glBindTexture(GL_TEXTURE_2D, sceneTexture);
2519 
2520     if (useBlendSubtract)
2521     {
2522         glBegin(GL_QUADS);
2523         drawBlendedVertices(0.0f, 0.0f, 1.0f);
2524         glEnd();
2525         // Do not need to scale alpha so mask it off
2526         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
2527         glEnable(GL_BLEND);
2528         savedWScale = sceneTexWScale;
2529         savedHScale = sceneTexHScale;
2530 
2531         // Remove ldr part of image
2532         {
2533             const GLfloat bias  = -0.5f;
2534             glBlendFunc(GL_ONE, GL_ONE);
2535             glx::glBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
2536             glColor4f(-bias, -bias, -bias, 0.0f);
2537 
2538             glDisable(GL_TEXTURE_2D);
2539             glBegin(GL_QUADS);
2540             glVertex2f(0.0f,           0.0f);
2541             glVertex2f(1.f, 0.0f);
2542             glVertex2f(1.f, 1.f);
2543             glVertex2f(0.0f,           1.f);
2544             glEnd();
2545 
2546             glEnable(GL_TEXTURE_2D);
2547             blurTextures[blurLevel]->bind();
2548             glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
2549                              blurTexWidth, blurTexHeight, 0);
2550         }
2551 
2552         // Scale back up hdr part
2553         {
2554             glx::glBlendEquationEXT(GL_FUNC_ADD_EXT);
2555             glBlendFunc(GL_DST_COLOR, GL_ONE);
2556 
2557             glBegin(GL_QUADS);
2558             drawBlendedVertices(0.f, 0.f, 1.f); //x2
2559             drawBlendedVertices(0.f, 0.f, 1.f); //x2
2560             glEnd();
2561         }
2562 
2563         glDisable(GL_BLEND);
2564 
2565         if (!useLuminanceAlpha)
2566         {
2567             blurTempTexture->bind();
2568             glCopyTexImage2D(GL_TEXTURE_2D, blurLevel, GL_LUMINANCE, 0, 0,
2569                              blurTexWidth, blurTexHeight, 0);
2570             // Erase color, replace with luminance image
2571             glBegin(GL_QUADS);
2572             glColor4f(0.f, 0.f, 0.f, 1.f);
2573             glVertex2f(0.0f, 0.0f);
2574             glVertex2f(1.0f, 0.0f);
2575             glVertex2f(1.0f, 1.0f);
2576             glVertex2f(0.0f, 1.0f);
2577             glEnd();
2578             glBegin(GL_QUADS);
2579             drawBlendedVertices(0.f, 0.f, 1.f);
2580             glEnd();
2581         }
2582 
2583         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2584         blurTextures[blurLevel]->bind();
2585         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
2586                          blurTexWidth, blurTexHeight, 0);
2587     }
2588     else
2589     {
2590         // GL_EXT_blend_subtract not supported
2591         // Use compatible (but slow) glPixelTransfer instead
2592         glBegin(GL_QUADS);
2593         drawBlendedVertices(0.0f, 0.0f, 1.0f);
2594         glEnd();
2595         savedWScale = sceneTexWScale;
2596         savedHScale = sceneTexHScale;
2597         sceneTexWScale = blurWScale;
2598         sceneTexHScale = blurHScale;
2599 
2600         blurTextures[blurLevel]->bind();
2601         glPixelTransferf(GL_RED_SCALE,   8.f);
2602         glPixelTransferf(GL_GREEN_SCALE, 8.f);
2603         glPixelTransferf(GL_BLUE_SCALE,  8.f);
2604         glPixelTransferf(GL_RED_BIAS,   -0.5f);
2605         glPixelTransferf(GL_GREEN_BIAS, -0.5f);
2606         glPixelTransferf(GL_BLUE_BIAS,  -0.5f);
2607         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
2608                          blurTexWidth, blurTexHeight, 0);
2609         glPixelTransferf(GL_RED_SCALE,   1.f);
2610         glPixelTransferf(GL_GREEN_SCALE, 1.f);
2611         glPixelTransferf(GL_BLUE_SCALE,  1.f);
2612         glPixelTransferf(GL_RED_BIAS,    0.f);
2613         glPixelTransferf(GL_GREEN_BIAS,  0.f);
2614         glPixelTransferf(GL_BLUE_BIAS,   0.f);
2615     }
2616 
2617     glClear(GL_COLOR_BUFFER_BIT);
2618 
2619     glEnable(GL_BLEND);
2620     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2621 
2622     GLfloat xdelta = 1.0f / (GLfloat)blurTexWidth;
2623     GLfloat ydelta = 1.0f / (GLfloat)blurTexHeight;
2624     blurWScale = ((GLfloat)blurTexWidth / (GLfloat)blurDrawWidth);
2625     blurHScale = ((GLfloat)blurTexHeight / (GLfloat)blurDrawHeight);
2626     sceneTexWScale = blurWScale;
2627     sceneTexHScale = blurHScale;
2628 
2629     // Butterworth low pass filter to reduce flickering dots
2630     {
2631         glBegin(GL_QUADS);
2632         drawBlendedVertices(0.0f,    0.0f, .5f*1.f);
2633         drawBlendedVertices(-xdelta, 0.0f, .5f*0.333f);
2634         drawBlendedVertices( xdelta, 0.0f, .5f*0.25f);
2635         glEnd();
2636         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
2637                          blurTexWidth, blurTexHeight, 0);
2638         glBegin(GL_QUADS);
2639         drawBlendedVertices(0.0f, -ydelta, .5f*0.667f);
2640         drawBlendedVertices(0.0f,  ydelta, .5f*0.333f);
2641         glEnd();
2642         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
2643                          blurTexWidth, blurTexHeight, 0);
2644         glClear(GL_COLOR_BUFFER_BIT);
2645     }
2646 
2647     // Gaussian blur
2648     switch (blurLevel)
2649     {
2650 /*
2651     case 0:
2652         drawGaussian3x3(xdelta, ydelta, blurTexWidth, blurTexHeight, 1.f);
2653         break;
2654 */
2655 #ifdef TARGET_OS_MAC
2656     case 0:
2657         drawGaussian5x5(xdelta, ydelta, blurTexWidth, blurTexHeight, 1.f);
2658         break;
2659     case 1:
2660         drawGaussian9x9(xdelta, ydelta, blurTexWidth, blurTexHeight, .3f);
2661         break;
2662 #else
2663     // Gamma correct: windows=(mac^1.8)^(1/2.2)
2664     case 0:
2665         drawGaussian5x5(xdelta, ydelta, blurTexWidth, blurTexHeight, 1.f);
2666         break;
2667     case 1:
2668         drawGaussian9x9(xdelta, ydelta, blurTexWidth, blurTexHeight, .373f);
2669         break;
2670 #endif
2671     default:
2672         break;
2673     }
2674 
2675     blurTextures[blurLevel]->bind();
2676     glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
2677                      blurTexWidth, blurTexHeight, 0);
2678 
2679     glDisable(GL_BLEND);
2680     glClear(GL_COLOR_BUFFER_BIT);
2681     glPopAttrib();
2682     sceneTexWScale = savedWScale;
2683     sceneTexHScale = savedHScale;
2684 }
2685 
renderToTexture(const Observer & observer,const Universe & universe,float faintestMagNight,const Selection & sel)2686 void Renderer::renderToTexture(const Observer& observer,
2687                                const Universe& universe,
2688                                float faintestMagNight,
2689                                const Selection& sel)
2690 {
2691     if (sceneTexture == 0)
2692         return;
2693     glPushAttrib(GL_COLOR_BUFFER_BIT);
2694 
2695     draw(observer, universe, faintestMagNight, sel);
2696 
2697     glBindTexture(GL_TEXTURE_2D, sceneTexture);
2698     glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0,
2699                      sceneTexWidth, sceneTexHeight, 0);
2700     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
2701     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2702     glPopAttrib();
2703 }
2704 
drawSceneTexture()2705 void Renderer::drawSceneTexture()
2706 {
2707     if (sceneTexture == 0)
2708         return;
2709     glBindTexture(GL_TEXTURE_2D, sceneTexture);
2710     glBegin(GL_QUADS);
2711     drawBlendedVertices(0.0f, 0.0f, 1.0f);
2712     glEnd();
2713 }
2714 
drawBlendedVertices(float xdelta,float ydelta,float blend)2715 void Renderer::drawBlendedVertices(float xdelta, float ydelta, float blend)
2716 {
2717     glColor4f(1.0f, 1.0f, 1.0f, blend);
2718     glTexCoord2i(0, 0); glVertex2f(xdelta,                ydelta);
2719     glTexCoord2i(1, 0); glVertex2f(sceneTexWScale+xdelta, ydelta);
2720     glTexCoord2i(1, 1); glVertex2f(sceneTexWScale+xdelta, sceneTexHScale+ydelta);
2721     glTexCoord2i(0, 1); glVertex2f(xdelta,                sceneTexHScale+ydelta);
2722 }
2723 
drawGaussian3x3(float xdelta,float ydelta,GLsizei width,GLsizei height,float blend)2724 void Renderer::drawGaussian3x3(float xdelta, float ydelta, GLsizei width, GLsizei height, float blend)
2725 {
2726 #ifdef USE_BLOOM_LISTS
2727     if (gaussianLists[0] == 0)
2728     {
2729         gaussianLists[0] = glGenLists(1);
2730         glNewList(gaussianLists[0], GL_COMPILE);
2731 #endif
2732         glBegin(GL_QUADS);
2733         drawBlendedVertices(0.0f, 0.0f, blend);
2734         drawBlendedVertices(-xdelta, 0.0f, 0.25f*blend);
2735         drawBlendedVertices( xdelta, 0.0f, 0.20f*blend);
2736         glEnd();
2737 
2738         // Take result of horiz pass and apply vertical pass
2739         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
2740                          width, height, 0);
2741         glBegin(GL_QUADS);
2742         drawBlendedVertices(0.0f, -ydelta, 0.429f);
2743         drawBlendedVertices(0.0f,  ydelta, 0.300f);
2744         glEnd();
2745 #ifdef USE_BLOOM_LISTS
2746         glEndList();
2747     }
2748     glCallList(gaussianLists[0]);
2749 #endif
2750 }
2751 
drawGaussian5x5(float xdelta,float ydelta,GLsizei width,GLsizei height,float blend)2752 void Renderer::drawGaussian5x5(float xdelta, float ydelta, GLsizei width, GLsizei height, float blend)
2753 {
2754 #ifdef USE_BLOOM_LISTS
2755     if (gaussianLists[1] == 0)
2756     {
2757         gaussianLists[1] = glGenLists(1);
2758         glNewList(gaussianLists[1], GL_COMPILE);
2759 #endif
2760         glBegin(GL_QUADS);
2761         drawBlendedVertices(0.0f, 0.0f, blend);
2762         drawBlendedVertices(-xdelta,      0.0f, 0.475f*blend);
2763         drawBlendedVertices( xdelta,      0.0f, 0.475f*blend);
2764         drawBlendedVertices(-2.0f*xdelta, 0.0f, 0.075f*blend);
2765         drawBlendedVertices( 2.0f*xdelta, 0.0f, 0.075f*blend);
2766         glEnd();
2767         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
2768                          width, height, 0);
2769         glBegin(GL_QUADS);
2770         drawBlendedVertices(0.0f, -ydelta,      0.475f);
2771         drawBlendedVertices(0.0f,  ydelta,      0.475f);
2772         drawBlendedVertices(0.0f, -2.0f*ydelta, 0.075f);
2773         drawBlendedVertices(0.0f,  2.0f*ydelta, 0.075f);
2774         glEnd();
2775 #ifdef USE_BLOOM_LISTS
2776         glEndList();
2777     }
2778     glCallList(gaussianLists[1]);
2779 #endif
2780 }
2781 
drawGaussian9x9(float xdelta,float ydelta,GLsizei width,GLsizei height,float blend)2782 void Renderer::drawGaussian9x9(float xdelta, float ydelta, GLsizei width, GLsizei height, float blend)
2783 {
2784 #ifdef USE_BLOOM_LISTS
2785     if (gaussianLists[2] == 0)
2786     {
2787         gaussianLists[2] = glGenLists(1);
2788         glNewList(gaussianLists[2], GL_COMPILE);
2789 #endif
2790         glBegin(GL_QUADS);
2791         drawBlendedVertices(0.0f, 0.0f, blend);
2792         drawBlendedVertices(-xdelta,      0.0f, 0.632f*blend);
2793         drawBlendedVertices( xdelta,      0.0f, 0.632f*blend);
2794         drawBlendedVertices(-2.0f*xdelta, 0.0f, 0.159f*blend);
2795         drawBlendedVertices( 2.0f*xdelta, 0.0f, 0.159f*blend);
2796         drawBlendedVertices(-3.0f*xdelta, 0.0f, 0.016f*blend);
2797         drawBlendedVertices( 3.0f*xdelta, 0.0f, 0.016f*blend);
2798         glEnd();
2799 
2800         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
2801                          width, height, 0);
2802         glBegin(GL_QUADS);
2803         drawBlendedVertices(0.0f, -ydelta,      0.632f);
2804         drawBlendedVertices(0.0f,  ydelta,      0.632f);
2805         drawBlendedVertices(0.0f, -2.0f*ydelta, 0.159f);
2806         drawBlendedVertices(0.0f,  2.0f*ydelta, 0.159f);
2807         drawBlendedVertices(0.0f, -3.0f*ydelta, 0.016f);
2808         drawBlendedVertices(0.0f,  3.0f*ydelta, 0.016f);
2809         glEnd();
2810 #ifdef USE_BLOOM_LISTS
2811         glEndList();
2812     }
2813     glCallList(gaussianLists[2]);
2814 #endif
2815 }
2816 
drawBlur()2817 void Renderer::drawBlur()
2818 {
2819     blurTextures[0]->bind();
2820     glBegin(GL_QUADS);
2821     drawBlendedVertices(0.0f, 0.0f, 1.0f);
2822     glEnd();
2823     blurTextures[1]->bind();
2824     glBegin(GL_QUADS);
2825     drawBlendedVertices(0.0f, 0.0f, 1.0f);
2826     glEnd();
2827 }
2828 
getBloomEnabled()2829 bool Renderer::getBloomEnabled()
2830 {
2831     return bloomEnabled;
2832 }
2833 
setBloomEnabled(bool aBloomEnabled)2834 void Renderer::setBloomEnabled(bool aBloomEnabled)
2835 {
2836     bloomEnabled = aBloomEnabled;
2837 }
2838 
increaseBrightness()2839 void Renderer::increaseBrightness()
2840 {
2841     brightPlus += 1.0f;
2842 }
2843 
decreaseBrightness()2844 void Renderer::decreaseBrightness()
2845 {
2846     brightPlus -= 1.0f;
2847 }
2848 
getBrightness()2849 float Renderer::getBrightness()
2850 {
2851     return brightPlus;
2852 }
2853 #endif // USE_HDR
2854 
render(const Observer & observer,const Universe & universe,float faintestMagNight,const Selection & sel)2855 void Renderer::render(const Observer& observer,
2856                       const Universe& universe,
2857                       float faintestMagNight,
2858                       const Selection& sel)
2859 {
2860     glMatrixMode(GL_PROJECTION);
2861     glLoadIdentity();
2862 
2863 #ifdef USE_HDR
2864     renderToTexture(observer, universe, faintestMagNight, sel);
2865 
2866     //------------- Post processing from here ------------//
2867     glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
2868     glEnable(GL_TEXTURE_2D);
2869     glDisable(GL_BLEND);
2870     glDisable(GL_LIGHTING);
2871     glDisable(GL_DEPTH_TEST);
2872     glDepthMask(GL_FALSE);
2873 
2874     glMatrixMode(GL_PROJECTION);
2875     glPushMatrix();
2876     glLoadIdentity();
2877     glOrtho( 0.0, 1.0, 0.0, 1.0, -1.0, 1.0 );
2878     glMatrixMode (GL_MODELVIEW);
2879     glPushMatrix();
2880     glLoadIdentity();
2881 
2882     if (bloomEnabled)
2883     {
2884         renderToBlurTexture(0);
2885         renderToBlurTexture(1);
2886 //        renderToBlurTexture(2);
2887     }
2888 
2889     drawSceneTexture();
2890 
2891     glEnable(GL_BLEND);
2892     glBlendFunc(GL_ONE, GL_ONE);
2893 
2894 #ifdef HDR_COMPRESS
2895     // Assume luminance 1.0 mapped to 128 previously
2896     // Compositing a 2nd copy doubles 128->255
2897     drawSceneTexture();
2898 #endif
2899 
2900     if (bloomEnabled)
2901     {
2902         drawBlur();
2903     }
2904 
2905     glMatrixMode(GL_PROJECTION);
2906     glPopMatrix();
2907     glMatrixMode(GL_MODELVIEW);
2908     glPopMatrix();
2909     glPopAttrib();
2910 #else
2911     draw(observer, universe, faintestMagNight, sel);
2912 #endif
2913 }
2914 
draw(const Observer & observer,const Universe & universe,float faintestMagNight,const Selection & sel)2915 void Renderer::draw(const Observer& observer,
2916                     const Universe& universe,
2917                     float faintestMagNight,
2918                     const Selection& sel)
2919 {
2920     // Get the observer's time
2921     double now = observer.getTime();
2922     realTime = observer.getRealTime();
2923 
2924     frameCount++;
2925     settingsChanged = false;
2926 
2927     // Compute the size of a pixel
2928     setFieldOfView(radToDeg(observer.getFOV()));
2929     pixelSize = calcPixelSize(fov, (float) windowHeight);
2930 
2931     // Set up the projection we'll use for rendering stars.
2932     gluPerspective(fov,
2933                    (float) windowWidth / (float) windowHeight,
2934                    NEAR_DIST, FAR_DIST);
2935 
2936     // Set the modelview matrix
2937     glMatrixMode(GL_MODELVIEW);
2938 
2939     // Get the displayed surface texture set to use from the observer
2940     displayedSurface = observer.getDisplayedSurface();
2941 
2942     locationFilter = observer.getLocationFilter();
2943 
2944     if (usePointSprite && getGLContext()->getVertexProcessor() != NULL)
2945     {
2946         useNewStarRendering = true;
2947     }
2948     else
2949     {
2950         useNewStarRendering = false;
2951     }
2952 
2953     // Highlight the selected object
2954     highlightObject = sel;
2955 
2956     m_cameraOrientation = observer.getOrientationf();
2957 
2958     // Get the view frustum used for culling in camera space.
2959     float viewAspectRatio = (float) windowWidth / (float) windowHeight;
2960     Frustum frustum(degToRad(fov),
2961                     viewAspectRatio,
2962                     MinNearPlaneDistance);
2963 
2964     // Get the transformed frustum, used for culling in the astrocentric coordinate
2965     // system.
2966     Frustum xfrustum(degToRad(fov),
2967                      viewAspectRatio,
2968                      MinNearPlaneDistance);
2969     xfrustum.transform(conjugate(observer.getOrientationf()).toMatrix3());
2970 
2971     // Set up the camera for star rendering; the units of this phase
2972     // are light years.
2973     Point3f observerPosLY = (Point3f) observer.getPosition();
2974     observerPosLY.x *= 1e-6f;
2975     observerPosLY.y *= 1e-6f;
2976     observerPosLY.z *= 1e-6f;
2977     glPushMatrix();
2978     glRotate(m_cameraOrientation);
2979 
2980     // Get the model matrix *before* translation.  We'll use this for
2981     // positioning star and planet labels.
2982     glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
2983     glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
2984 
2985     clearSortedAnnotations();
2986 
2987     // Put all solar system bodies into the render list.  Stars close and
2988     // large enough to have discernible surface detail are also placed in
2989     // renderList.
2990     renderList.clear();
2991     orbitPathList.clear();
2992     lightSourceList.clear();
2993     secondaryIlluminators.clear();
2994 
2995     // See if we want to use AutoMag.
2996     if ((renderFlags & ShowAutoMag) != 0)
2997     {
2998         autoMag(faintestMag);
2999     }
3000     else
3001     {
3002         faintestMag = faintestMagNight;
3003         saturationMag = saturationMagNight;
3004     }
3005 
3006     faintestPlanetMag = faintestMag;
3007 #ifdef USE_HDR
3008     float maxBodyMagPrev = saturationMag;
3009     maxBodyMag = min(maxBodyMag, saturationMag);
3010     vector<RenderListEntry>::iterator closestBody;
3011     const Star *brightestStar = NULL;
3012     bool foundClosestBody   = false;
3013     bool foundBrightestStar = false;
3014 #endif
3015 
3016     if (renderFlags & ShowPlanets)
3017     {
3018         nearStars.clear();
3019         universe.getNearStars(observer.getPosition(), 1.0f, nearStars);
3020 
3021         // Set up direct light sources (i.e. just stars at the moment)
3022         setupLightSources(nearStars, observer.getPosition(), now, lightSourceList);
3023 
3024         // Traverse the frame trees of each nearby solar system and
3025         // build the list of objects to be rendered.
3026         for (vector<const Star*>::const_iterator iter = nearStars.begin();
3027              iter != nearStars.end(); iter++)
3028         {
3029             const Star* sun = *iter;
3030             SolarSystem* solarSystem = universe.getSolarSystem(sun);
3031             if (solarSystem != NULL)
3032             {
3033                 FrameTree* solarSysTree = solarSystem->getFrameTree();
3034                 if (solarSysTree != NULL)
3035                 {
3036                     if (solarSysTree->updateRequired())
3037                     {
3038                         // Tree has changed, so we must recompute bounding spheres.
3039                         solarSysTree->recomputeBoundingSphere();
3040                         solarSysTree->markUpdated();
3041                     }
3042 
3043                     // Compute the position of the observer in astrocentric coordinates
3044                     Point3d astrocentricObserverPos = astrocentricPosition(observer.getPosition(), *sun, now);
3045 
3046                     // Build render lists for bodies and orbits paths
3047                     buildRenderLists(astrocentricObserverPos,
3048                                      xfrustum,
3049                                      Vec3d(0.0, 0.0, -1.0) * observer.getOrientation().toMatrix3(),
3050                                      Vec3d(0.0, 0.0, 0.0),
3051                                      solarSysTree,
3052                                      observer,
3053                                      now);
3054                     if (renderFlags & ShowOrbits)
3055                     {
3056                         buildOrbitLists(astrocentricObserverPos,
3057                                         observer.getOrientationf(),
3058                                         xfrustum,
3059                                         solarSysTree,
3060                                         now);
3061                     }
3062                 }
3063             }
3064 
3065             addStarOrbitToRenderList(*sun, observer, now);
3066         }
3067 
3068         if ((labelMode & (BodyLabelMask)) != 0)
3069         {
3070             buildLabelLists(xfrustum, now);
3071         }
3072 
3073         starTex->bind();
3074     }
3075 
3076     setupSecondaryLightSources(secondaryIlluminators, lightSourceList);
3077 
3078 #ifdef USE_HDR
3079     Mat3f viewMat = conjugate(observer.getOrientationf()).toMatrix3();
3080     float maxSpan = (float) sqrt(square((float) windowWidth) +
3081                                  square((float) windowHeight));
3082     float nearZcoeff = (float) cos(degToRad(fov / 2)) *
3083         ((float) windowHeight / maxSpan);
3084 
3085     // Remove objects from the render list that lie completely outside the
3086     // view frustum.
3087     vector<RenderListEntry>::iterator notCulled = renderList.begin();
3088     for (vector<RenderListEntry>::iterator iter = renderList.begin();
3089          iter != renderList.end(); iter++)
3090     {
3091         Point3f center = iter->position * viewMat;
3092 
3093         bool convex = true;
3094         float radius = 1.0f;
3095         float cullRadius = 1.0f;
3096         float cloudHeight = 0.0f;
3097 
3098         switch (iter->renderableType)
3099         {
3100         case RenderListEntry::RenderableStar:
3101             continue;
3102 
3103         case RenderListEntry::RenderableCometTail:
3104             radius = iter->radius;
3105             cullRadius = radius;
3106             convex = false;
3107             break;
3108 
3109         case RenderListEntry::RenderableReferenceMark:
3110             radius = iter->radius;
3111             cullRadius = radius;
3112             convex = false;
3113             break;
3114 
3115         case RenderListEntry::RenderableBody:
3116         default:
3117             radius = iter->body->getBoundingRadius();
3118             if (iter->body->getRings() != NULL)
3119             {
3120                 radius = iter->body->getRings()->outerRadius;
3121                 convex = false;
3122             }
3123 
3124             if (!iter->body->isEllipsoid())
3125                 convex = false;
3126 
3127             cullRadius = radius;
3128             if (iter->body->getAtmosphere() != NULL)
3129             {
3130                 cullRadius += iter->body->getAtmosphere()->height;
3131                 cloudHeight = max(iter->body->getAtmosphere()->cloudHeight,
3132                                   iter->body->getAtmosphere()->mieScaleHeight * (float) -log(AtmosphereExtinctionThreshold));
3133             }
3134             break;
3135         }
3136 
3137         // Test the object's bounding sphere against the view frustum
3138         if (frustum.testSphere(center, cullRadius) != Frustum::Outside)
3139         {
3140             float nearZ = center.distanceFromOrigin() - radius;
3141             nearZ = -nearZ * nearZcoeff;
3142 
3143             if (nearZ > -MinNearPlaneDistance)
3144                 iter->nearZ = -max(MinNearPlaneDistance, radius / 2000.0f);
3145             else
3146                 iter->nearZ = nearZ;
3147 
3148             if (!convex)
3149             {
3150                 iter->farZ = center.z - radius;
3151                 if (iter->farZ / iter->nearZ > MaxFarNearRatio * 0.5f)
3152                     iter->nearZ = iter->farZ / (MaxFarNearRatio * 0.5f);
3153             }
3154             else
3155             {
3156                 // Make the far plane as close as possible
3157                 float d = center.distanceFromOrigin();
3158 
3159                 // Account for ellipsoidal objects
3160                 float eradius = radius;
3161                 if (iter->body != NULL)
3162                 {
3163                     Vec3f semiAxes = iter->body->getSemiAxes();
3164                     float minSemiAxis = min(semiAxes.x, min(semiAxes.y, semiAxes.z));
3165                     eradius *= minSemiAxis / radius;
3166                 }
3167 
3168                 if (d > eradius)
3169                 {
3170                     iter->farZ = iter->centerZ - iter->radius;
3171                 }
3172                 else
3173                 {
3174                     // We're inside the bounding sphere (and, if the planet
3175                     // is spherical, inside the planet.)
3176                     iter->farZ = iter->nearZ * 2.0f;
3177                 }
3178 
3179                 if (cloudHeight > 0.0f)
3180                 {
3181                     // If there's a cloud layer, we need to move the
3182                     // far plane out so that the clouds aren't clipped
3183                     float cloudLayerRadius = eradius + cloudHeight;
3184                     iter->farZ -= (float) sqrt(square(cloudLayerRadius) -
3185                                                square(eradius));
3186                 }
3187             }
3188 
3189             *notCulled = *iter;
3190             notCulled++;
3191 
3192             maxBodyMag = min(maxBodyMag, iter->appMag);
3193             foundClosestBody = true;
3194         }
3195     }
3196 
3197     renderList.resize(notCulled - renderList.begin());
3198     saturationMag = maxBodyMag;
3199 #endif // USE_HDR
3200 
3201     Color skyColor(0.0f, 0.0f, 0.0f);
3202 
3203     // Scan through the render list to see if we're inside a planetary
3204     // atmosphere.  If so, we need to adjust the sky color as well as the
3205     // limiting magnitude of stars (so stars aren't visible in the daytime
3206     // on planets with thick atmospheres.)
3207     if ((renderFlags & ShowAtmospheres) != 0)
3208     {
3209         for (vector<RenderListEntry>::iterator iter = renderList.begin();
3210              iter != renderList.end(); iter++)
3211         {
3212             if (iter->renderableType == RenderListEntry::RenderableBody && iter->body->getAtmosphere() != NULL)
3213             {
3214                 // Compute the density of the atmosphere, and from that
3215                 // the amount light scattering.  It's complicated by the
3216                 // possibility that the planet is oblate and a simple distance
3217                 // to sphere calculation will not suffice.
3218                 const Atmosphere* atmosphere = iter->body->getAtmosphere();
3219                 float radius = iter->body->getRadius();
3220                 Vec3f semiAxes = iter->body->getSemiAxes() * (1.0f / radius);
3221                 Vec3f recipSemiAxes(1.0f / semiAxes.x,
3222                                     1.0f / semiAxes.y,
3223                                     1.0f / semiAxes.z);
3224                 Mat3f A = Mat3f::scaling(recipSemiAxes);
3225                 Vec3f eyeVec = iter->position - Point3f(0.0f, 0.0f, 0.0f);
3226                 eyeVec *= (1.0f / radius);
3227 
3228                 // Compute the orientation of the planet before axial rotation
3229                 Quatd qd = iter->body->getEclipticToEquatorial(now);
3230                 Quatf q((float) qd.w, (float) qd.x, (float) qd.y, (float) qd.z);
3231                 eyeVec = eyeVec * conjugate(q).toMatrix3();
3232 
3233                 // ellipDist is not the true distance from the surface unless
3234                 // the planet is spherical.  The quantity that we do compute
3235                 // is the distance to the surface along a line from the eye
3236                 // position to the center of the ellipsoid.
3237                 float ellipDist = (float) sqrt((eyeVec * A) * (eyeVec * A)) - 1.0f;
3238                 if (ellipDist < atmosphere->height / radius &&
3239                     atmosphere->height > 0.0f)
3240                 {
3241                     float density = 1.0f - ellipDist /
3242                         (atmosphere->height / radius);
3243                     if (density > 1.0f)
3244                         density = 1.0f;
3245 
3246                     Vec3f sunDir = iter->sun;
3247                     Vec3f normal = Point3f(0.0f, 0.0f, 0.0f) - iter->position;
3248                     sunDir.normalize();
3249                     normal.normalize();
3250 #ifdef USE_HDR
3251                     // Ignore magnitude of planet underneath when lighting atmosphere
3252                     // Could be changed to simulate light pollution, etc
3253                     maxBodyMag = maxBodyMagPrev;
3254                     saturationMag = maxBodyMag;
3255 #endif
3256                     float illumination = Math<float>::clamp((sunDir * normal) + 0.2f);
3257 
3258                     float lightness = illumination * density;
3259                     faintestMag = faintestMag  - 15.0f * lightness;
3260                     saturationMag = saturationMag - 15.0f * lightness;
3261                 }
3262             }
3263         }
3264     }
3265 
3266     // Now we need to determine how to scale the brightness of stars.  The
3267     // brightness will be proportional to the apparent magnitude, i.e.
3268     // a logarithmic function of the stars apparent brightness.  This mimics
3269     // the response of the human eye.  We sort of fudge things here and
3270     // maintain a minimum range of six magnitudes between faintest visible
3271     // and saturation; this keeps stars from popping in or out as the sun
3272     // sets or rises.
3273 #ifdef USE_HDR
3274     brightnessScale = 1.0f / (faintestMag -  saturationMag);
3275 #else
3276     if (faintestMag - saturationMag >= 6.0f)
3277         brightnessScale = 1.0f / (faintestMag -  saturationMag);
3278     else
3279         brightnessScale = 0.1667f;
3280 #endif
3281 
3282 #ifdef USE_HDR
3283     exposurePrev = exposure;
3284     float exposureNow = 1.f / (1.f+exp((faintestMag - saturationMag + DEFAULT_EXPOSURE)/2.f));
3285     exposure = exposurePrev + (exposureNow - exposurePrev) * (1.f - exp(-1.f/(15.f * EXPOSURE_HALFLIFE)));
3286     brightnessScale /= exposure;
3287 #endif
3288 
3289 #ifdef DEBUG_HDR_TONEMAP
3290     HDR_LOG <<
3291 //        "brightnessScale = " << brightnessScale <<
3292         "faint = "    << faintestMag << ", " <<
3293         "sat = "      << saturationMag << ", " <<
3294         "exposure = " << (exposure+brightPlus) << endl;
3295 #endif
3296 
3297 #ifdef HDR_COMPRESS
3298     ambientColor = Color(ambientLightLevel*.5f, ambientLightLevel*.5f, ambientLightLevel*.5f);
3299 #else
3300     ambientColor = Color(ambientLightLevel, ambientLightLevel, ambientLightLevel);
3301 #endif
3302 
3303     // Create the ambient light source.  For realistic scenes in space, this
3304     // should be black.
3305     glAmbientLightColor(ambientColor);
3306 
3307 #ifdef USE_HDR
3308     glClearColor(skyColor.red(), skyColor.green(), skyColor.blue(), 0.0f);
3309 #else
3310     glClearColor(skyColor.red(), skyColor.green(), skyColor.blue(), 1);
3311 #endif
3312     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
3313 
3314     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
3315 
3316     glDisable(GL_LIGHTING);
3317     glDepthMask(GL_FALSE);
3318     glEnable(GL_BLEND);
3319     glEnable(GL_TEXTURE_2D);
3320 
3321     // Render sky grids first--these will always be in the background
3322     {
3323         glDisable(GL_TEXTURE_2D);
3324         if ((renderFlags & ShowSmoothLines) != 0)
3325             enableSmoothLines();
3326         renderSkyGrids(observer);
3327         if ((renderFlags & ShowSmoothLines) != 0)
3328             disableSmoothLines();
3329         glEnable(GL_BLEND);
3330         glEnable(GL_TEXTURE_2D);
3331     }
3332 
3333     // Render deep sky objects
3334     if ((renderFlags & (ShowGalaxies |
3335 						ShowGlobulars |
3336                         ShowNebulae |
3337                         ShowOpenClusters)) != 0 &&
3338         universe.getDSOCatalog() != NULL)
3339     {
3340         renderDeepSkyObjects(universe, observer, faintestMag);
3341     }
3342 
3343     // Translate the camera before rendering the stars
3344     glPushMatrix();
3345 
3346     // Render stars
3347 #ifdef USE_HDR
3348     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
3349 #endif
3350     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
3351 
3352     if ((renderFlags & ShowStars) != 0 && universe.getStarCatalog() != NULL)
3353     {
3354         // Disable multisample rendering when drawing point stars
3355         bool toggleAA = (starStyle == Renderer::PointStars && glIsEnabled(GL_MULTISAMPLE_ARB));
3356         if (toggleAA)
3357             glDisable(GL_MULTISAMPLE_ARB);
3358 
3359         if (useNewStarRendering)
3360             renderPointStars(*universe.getStarCatalog(), faintestMag, observer);
3361         else
3362             renderStars(*universe.getStarCatalog(), faintestMag, observer);
3363 
3364         if (toggleAA)
3365             glEnable(GL_MULTISAMPLE_ARB);
3366     }
3367 
3368 #ifdef USE_HDR
3369     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
3370 #endif
3371 
3372     glTranslatef(-observerPosLY.x, -observerPosLY.y, -observerPosLY.z);
3373 
3374     // Render asterisms
3375     if ((renderFlags & ShowDiagrams) != 0 && universe.getAsterisms() != NULL)
3376     {
3377         /* We'll linearly fade the lines as a function of the observer's
3378            distance to the origin of coordinates: */
3379         float opacity = 1.0f;
3380         float dist = observerPosLY.distanceFromOrigin() * 1e6f;
3381         if (dist > MaxAsterismLinesConstDist)
3382         {
3383             opacity = clamp((MaxAsterismLinesConstDist - dist) /
3384                             (MaxAsterismLinesDist - MaxAsterismLinesConstDist) + 1);
3385         }
3386 
3387         glColor(ConstellationColor, opacity);
3388         glDisable(GL_TEXTURE_2D);
3389         if ((renderFlags & ShowSmoothLines) != 0)
3390             enableSmoothLines();
3391         AsterismList* asterisms = universe.getAsterisms();
3392         for (AsterismList::const_iterator iter = asterisms->begin();
3393              iter != asterisms->end(); iter++)
3394         {
3395             Asterism* ast = *iter;
3396 
3397 			if (ast->getActive())
3398             {
3399 				if (ast->isColorOverridden())
3400 					glColor(ast->getOverrideColor(), opacity);
3401 				else
3402 					glColor(ConstellationColor, opacity);
3403 
3404 				for (int i = 0; i < ast->getChainCount(); i++)
3405 				{
3406 					const Asterism::Chain& chain = ast->getChain(i);
3407 
3408 					glBegin(GL_LINE_STRIP);
3409 					for (Asterism::Chain::const_iterator iter = chain.begin();
3410 						 iter != chain.end(); iter++)
3411 						glVertex(*iter);
3412 					glEnd();
3413 				}
3414 			}
3415         }
3416 
3417         if ((renderFlags & ShowSmoothLines) != 0)
3418             disableSmoothLines();
3419     }
3420 
3421     if ((renderFlags & ShowBoundaries) != 0)
3422     {
3423         /* We'll linearly fade the boundaries as a function of the
3424            observer's distance to the origin of coordinates: */
3425         float opacity = 1.0f;
3426         float dist = observerPosLY.distanceFromOrigin() * 1e6f;
3427         if (dist > MaxAsterismLabelsConstDist)
3428         {
3429             opacity = clamp((MaxAsterismLabelsConstDist - dist) /
3430                             (MaxAsterismLabelsDist - MaxAsterismLabelsConstDist) + 1);
3431         }
3432         glColor(BoundaryColor, opacity);
3433 
3434         glDisable(GL_TEXTURE_2D);
3435         if ((renderFlags & ShowSmoothLines) != 0)
3436             enableSmoothLines();
3437         if (universe.getBoundaries() != NULL)
3438             universe.getBoundaries()->render();
3439         if ((renderFlags & ShowSmoothLines) != 0)
3440             disableSmoothLines();
3441     }
3442 
3443     // Render star and deep sky object labels
3444     renderBackgroundAnnotations(FontNormal);
3445 
3446     // Render constellations labels
3447     if ((labelMode & ConstellationLabels) != 0 && universe.getAsterisms() != NULL)
3448     {
3449         labelConstellations(*universe.getAsterisms(), observer);
3450         renderBackgroundAnnotations(FontLarge);
3451     }
3452 
3453     // Pop observer translation
3454     glPopMatrix();
3455 
3456     if ((renderFlags & ShowMarkers) != 0)
3457     {
3458         renderMarkers(*universe.getMarkers(),
3459                       observer.getPosition(),
3460         		      observer.getOrientation(),
3461                       now);
3462 
3463         // Render background markers; rendering of other markers is deferred until
3464         // solar system objects are rendered.
3465         renderBackgroundAnnotations(FontNormal);
3466     }
3467 
3468     // Draw the selection cursor
3469     bool selectionVisible = false;
3470     if (!sel.empty() && (renderFlags & ShowMarkers))
3471     {
3472         UniversalCoord uc = sel.getPosition(now);
3473         Vec3d offset = (uc - observer.getPosition()) * astro::microLightYearsToKilometers(1.0);
3474         static MarkerRepresentation cursorRep(MarkerRepresentation::Crosshair);
3475         selectionVisible = xfrustum.testSphere(Point3d(0, 0, 0) + offset, sel.radius()) != Frustum::Outside;
3476 
3477         if (selectionVisible)
3478         {
3479             double distance = offset.length();
3480             float symbolSize = (float) (sel.radius() / distance) / pixelSize;
3481 
3482             // Modify the marker position so that it is always in front of the marked object.
3483             double boundingRadius;
3484             if (sel.body() != NULL)
3485                 boundingRadius = sel.body()->getBoundingRadius();
3486             else
3487                 boundingRadius = sel.radius();
3488             offset *= (1.0 - boundingRadius * 1.01 / distance);
3489 
3490             // The selection cursor is only partially visible when the selected object is obscured. To implement
3491             // this behavior we'll draw two markers at the same position: one that's always visible, and another one
3492             // that's depth sorted. When the selection is occluded, only the foreground marker is visible. Otherwise,
3493             // both markers are drawn and cursor appears much brighter as a result.
3494             if (distance < astro::lightYearsToKilometers(1.0))
3495             {
3496                 addSortedAnnotation(&cursorRep, EMPTY_STRING, Color(SelectionCursorColor, 1.0f),
3497                                     Point3f((float) offset.x, (float) offset.y, (float) offset.z),
3498                                     AlignLeft, VerticalAlignTop, symbolSize);
3499             }
3500             else
3501             {
3502                 addAnnotation(backgroundAnnotations, &cursorRep, EMPTY_STRING, Color(SelectionCursorColor, 1.0f),
3503                               Point3f((float) offset.x, (float) offset.y, (float) offset.z),
3504                               AlignLeft, VerticalAlignTop, symbolSize);
3505             }
3506 
3507             Color occludedCursorColor(SelectionCursorColor.red(), SelectionCursorColor.green() + 0.3f, SelectionCursorColor.blue());
3508             addAnnotation(foregroundAnnotations,
3509                           &cursorRep, EMPTY_STRING, Color(occludedCursorColor, 0.4f),
3510                           Point3f((float) offset.x, (float) offset.y, (float) offset.z),
3511                           AlignLeft, VerticalAlignTop, symbolSize);
3512         }
3513     }
3514 
3515     glPolygonMode(GL_FRONT, (GLenum) renderMode);
3516     glPolygonMode(GL_BACK, (GLenum) renderMode);
3517 
3518     {
3519         Mat3f viewMat = conjugate(observer.getOrientationf()).toMatrix3();
3520 
3521         // Remove objects from the render list that lie completely outside the
3522         // view frustum.
3523 #ifdef USE_HDR
3524         maxBodyMag = maxBodyMagPrev;
3525         float starMaxMag = maxBodyMagPrev;
3526         notCulled = renderList.begin();
3527 #else
3528         vector<RenderListEntry>::iterator notCulled = renderList.begin();
3529 #endif
3530         for (vector<RenderListEntry>::iterator iter = renderList.begin();
3531              iter != renderList.end(); iter++)
3532         {
3533 #ifdef USE_HDR
3534             switch (iter->renderableType)
3535             {
3536             case RenderListEntry::RenderableStar:
3537                 break;
3538             default:
3539                 *notCulled = *iter;
3540                 notCulled++;
3541                 continue;
3542             }
3543 #endif
3544             Point3f center = iter->position * viewMat;
3545 
3546             bool convex = true;
3547             float radius = 1.0f;
3548             float cullRadius = 1.0f;
3549             float cloudHeight = 0.0f;
3550 
3551 #ifndef USE_HDR
3552             switch (iter->renderableType)
3553             {
3554             case RenderListEntry::RenderableStar:
3555                 radius = iter->star->getRadius();
3556                 cullRadius = radius * (1.0f + CoronaHeight);
3557                 break;
3558 
3559             case RenderListEntry::RenderableCometTail:
3560                 radius = iter->radius;
3561                 cullRadius = radius;
3562                 convex = false;
3563                 break;
3564 
3565             case RenderListEntry::RenderableBody:
3566                 radius = iter->body->getBoundingRadius();
3567                 if (iter->body->getRings() != NULL)
3568                 {
3569                     radius = iter->body->getRings()->outerRadius;
3570                     convex = false;
3571                 }
3572 
3573                 if (!iter->body->isEllipsoid())
3574                     convex = false;
3575 
3576                 cullRadius = radius;
3577                 if (iter->body->getAtmosphere() != NULL)
3578                 {
3579                     cullRadius += iter->body->getAtmosphere()->height;
3580                     cloudHeight = max(iter->body->getAtmosphere()->cloudHeight,
3581                                       iter->body->getAtmosphere()->mieScaleHeight * (float) -log(AtmosphereExtinctionThreshold));
3582                 }
3583                 break;
3584 
3585             case RenderListEntry::RenderableReferenceMark:
3586                 radius = iter->radius;
3587                 cullRadius = radius;
3588                 convex = false;
3589                 break;
3590 
3591             default:
3592                 break;
3593             }
3594 #else
3595             radius = iter->star->getRadius();
3596             cullRadius = radius * (1.0f + CoronaHeight);
3597 #endif // USE_HDR
3598 
3599             // Test the object's bounding sphere against the view frustum
3600             if (frustum.testSphere(center, cullRadius) != Frustum::Outside)
3601             {
3602                 float nearZ = center.distanceFromOrigin() - radius;
3603 #ifdef USE_HDR
3604                 nearZ = -nearZ * nearZcoeff;
3605 #else
3606                 float maxSpan = (float) sqrt(square((float) windowWidth) +
3607                                              square((float) windowHeight));
3608 
3609                 nearZ = -nearZ * (float) cos(degToRad(fov / 2)) *
3610                     ((float) windowHeight / maxSpan);
3611 #endif
3612                 if (nearZ > -MinNearPlaneDistance)
3613                     iter->nearZ = -max(MinNearPlaneDistance, radius / 2000.0f);
3614                 else
3615                     iter->nearZ = nearZ;
3616 
3617                 if (!convex)
3618                 {
3619                     iter->farZ = center.z - radius;
3620                     if (iter->farZ / iter->nearZ > MaxFarNearRatio * 0.5f)
3621                         iter->nearZ = iter->farZ / (MaxFarNearRatio * 0.5f);
3622                 }
3623                 else
3624                 {
3625                     // Make the far plane as close as possible
3626                     float d = center.distanceFromOrigin();
3627 
3628                     // Account for ellipsoidal objects
3629                     float eradius = radius;
3630                     if (iter->renderableType == RenderListEntry::RenderableBody)
3631                     {
3632                         Vec3f semiAxes = iter->body->getSemiAxes();
3633                         float minSemiAxis = min(semiAxes.x, min(semiAxes.y, semiAxes.z));
3634                         eradius *= minSemiAxis / radius;
3635                     }
3636 
3637                     if (d > eradius)
3638                     {
3639                         iter->farZ = iter->centerZ - iter->radius;
3640                     }
3641                     else
3642                     {
3643                         // We're inside the bounding sphere (and, if the planet
3644                         // is spherical, inside the planet.)
3645                         iter->farZ = iter->nearZ * 2.0f;
3646                     }
3647 
3648                     if (cloudHeight > 0.0f)
3649                     {
3650                         // If there's a cloud layer, we need to move the
3651                         // far plane out so that the clouds aren't clipped
3652                         float cloudLayerRadius = eradius + cloudHeight;
3653                         iter->farZ -= (float) sqrt(square(cloudLayerRadius) -
3654                                                    square(eradius));
3655                     }
3656                 }
3657 
3658                 *notCulled = *iter;
3659                 notCulled++;
3660 #ifdef USE_HDR
3661                 if (iter->discSizeInPixels > 1.0f &&
3662                     iter->appMag < starMaxMag)
3663                 {
3664                     starMaxMag = iter->appMag;
3665                     brightestStar = iter->star;
3666                     foundBrightestStar = true;
3667                 }
3668 #endif
3669             }
3670         }
3671 
3672         renderList.resize(notCulled - renderList.begin());
3673 
3674         // The calls to buildRenderLists/renderStars filled renderList
3675         // with visible bodies.  Sort it front to back, then
3676         // render each entry in reverse order (TODO: convenient, but not
3677         // ideal for performance; should render opaque objects front to
3678         // back, then translucent objects back to front. However, the
3679         // amount of overdraw in Celestia is typically low.)
3680         sort(renderList.begin(), renderList.end());
3681 
3682         // Sort the annotations
3683         sort(depthSortedAnnotations.begin(), depthSortedAnnotations.end());
3684 
3685         // Sort the orbit paths
3686         sort(orbitPathList.begin(), orbitPathList.end());
3687 
3688         int nEntries = renderList.size();
3689 
3690 #ifdef USE_HDR
3691         // Compute 1 eclipse between eye - closest body - brightest star
3692         // This prevents an eclipsed star from increasing exposure
3693         bool eyeNotEclipsed = true;
3694         closestBody = renderList.empty() ? renderList.end() : renderList.begin();
3695         if (foundClosestBody &&
3696             closestBody != renderList.end() &&
3697             closestBody->renderableType == RenderListEntry::RenderableBody &&
3698             closestBody->body && brightestStar)
3699         {
3700             const Body *body = closestBody->body;
3701             double scale = astro::microLightYearsToKilometers(1.0);
3702             Point3d posBody = body->getAstrocentricPosition(now);
3703             Point3d posStar;
3704             Point3d posEye = astrocentricPosition(observer.getPosition(), *brightestStar, now);
3705 
3706             if (body->getSystem() &&
3707                 body->getSystem()->getStar() &&
3708                 body->getSystem()->getStar() != brightestStar)
3709             {
3710                 UniversalCoord center = body->getSystem()->getStar()->getPosition(now);
3711                 Vec3d v = brightestStar->getPosition(now) - center;
3712                 posStar = Point3d(v.x, v.y, v.z);
3713             }
3714             else
3715             {
3716                 posStar = brightestStar->getPosition(now);
3717             }
3718 
3719             posStar.x /= scale;
3720             posStar.y /= scale;
3721             posStar.z /= scale;
3722             Vec3d lightToBodyDir = posBody - posStar;
3723             Vec3d bodyToEyeDir = posEye - posBody;
3724 
3725             if (lightToBodyDir * bodyToEyeDir > 0.0)
3726             {
3727                 double dist = distance(posEye,
3728                                        Ray3d(posBody, lightToBodyDir));
3729                 if (dist < body->getRadius())
3730                     eyeNotEclipsed = false;
3731             }
3732         }
3733 
3734         if (eyeNotEclipsed)
3735         {
3736             maxBodyMag = min(maxBodyMag, starMaxMag);
3737         }
3738 #endif
3739 
3740         // Since we're rendering objects of a huge range of sizes spread over
3741         // vast distances, we can't just rely on the hardware depth buffer to
3742         // handle hidden surface removal without a little help. We'll partition
3743         // the depth buffer into spans that can be rendered without running
3744         // into terrible depth buffer precision problems. Typically, each body
3745         // with an apparent size greater than one pixel is allocated its own
3746         // depth buffer interval. However, this will not correctly handle
3747         // overlapping objects.  If two objects overlap in depth, we must
3748         // assign them to the same interval.
3749 
3750         depthPartitions.clear();
3751         int nIntervals = 0;
3752         float prevNear = -1e12f;  // ~ 1 light year
3753         if (nEntries > 0)
3754             prevNear = renderList[nEntries - 1].farZ * 1.01f;
3755 
3756         int i;
3757 
3758         // Completely partition the depth buffer. Scan from back to front
3759         // through all the renderable items that passed the culling test.
3760         for (i = nEntries - 1; i >= 0; i--)
3761         {
3762             // Only consider renderables that will occupy more than one pixel.
3763             if (renderList[i].discSizeInPixels > 1)
3764             {
3765                 if (nIntervals == 0 || renderList[i].farZ >= depthPartitions[nIntervals - 1].nearZ)
3766                 {
3767                     // This object spans a depth interval that's disjoint with
3768                     // the current interval, so create a new one for it, and
3769                     // another interval to fill the gap between the last
3770                     // interval.
3771                     DepthBufferPartition partition;
3772                     partition.index = nIntervals;
3773                     partition.nearZ = renderList[i].farZ;
3774                     partition.farZ = prevNear;
3775 
3776                     // Omit null intervals
3777                     // TODO: Is this necessary? Shouldn't the >= test prevent this?
3778                     if (partition.nearZ != partition.farZ)
3779                     {
3780                         depthPartitions.push_back(partition);
3781                         nIntervals++;
3782                     }
3783 
3784                     partition.index = nIntervals;
3785                     partition.nearZ = renderList[i].nearZ;
3786                     partition.farZ = renderList[i].farZ;
3787                     depthPartitions.push_back(partition);
3788                     nIntervals++;
3789 
3790                     prevNear = partition.nearZ;
3791                 }
3792                 else
3793                 {
3794                     // This object overlaps the current span; expand the
3795                     // interval so that it completely contains the object.
3796                     DepthBufferPartition& partition = depthPartitions[nIntervals - 1];
3797                     partition.nearZ = max(partition.nearZ, renderList[i].nearZ);
3798                     partition.farZ = min(partition.farZ, renderList[i].farZ);
3799                     prevNear = partition.nearZ;
3800                 }
3801             }
3802         }
3803 
3804         // Scan the list of orbit paths and find the closest one. We'll need
3805         // adjust the nearest interval to accommodate it.
3806         float zNearest = prevNear;
3807         for (i = 0; i < (int) orbitPathList.size(); i++)
3808         {
3809             const OrbitPathListEntry& o = orbitPathList[i];
3810             float minNearDistance = min(-o.radius * 0.0001f, o.centerZ + o.radius);
3811             if (minNearDistance > zNearest)
3812                 zNearest = minNearDistance;
3813         }
3814 
3815 
3816         // Adjust the nearest interval to include the closest marker (if it's
3817         // closer to the observer than anything else
3818         if (!depthSortedAnnotations.empty())
3819         {
3820             // Factor of 0.999 makes sure ensures that the near plane does not fall
3821             // exactly at the marker's z coordinate (in which case the marker
3822             // would be susceptible to being clipped.)
3823             if (-depthSortedAnnotations[0].position.z > zNearest)
3824                 zNearest = -depthSortedAnnotations[0].position.z * 0.999f;
3825         }
3826 
3827 
3828 #if DEBUG_COALESCE
3829         clog << "nEntries: " << nEntries << ",   zNearest: " << zNearest << ",   prevNear: " << prevNear << "\n";
3830 #endif
3831 
3832         // If the nearest distance wasn't set, nothing should appear
3833         // in the frontmost depth buffer interval (so we can set the near plane
3834         // of the front interval to whatever we want as long as it's less than
3835         // the far plane distance.
3836         if (zNearest == prevNear)
3837             zNearest = 0.0f;
3838 
3839         // Add one last interval for the span from 0 to the front of the
3840         // nearest object
3841         {
3842             // TODO: closest object may not be at entry 0, since objects are
3843             // sorted by far distance.
3844             float closest = zNearest;
3845             if (nEntries > 0)
3846             {
3847                 closest = max(closest, renderList[0].nearZ);
3848 
3849                 // Setting a the near plane distance to zero results in unreliable rendering, even
3850                 // if we don't care about the depth buffer. Compromise and set the near plane
3851                 // distance to a small fraction of distance to the nearest object.
3852                 if (closest == 0.0f)
3853                 {
3854                     closest = renderList[0].nearZ * 0.01f;
3855                 }
3856             }
3857 
3858             DepthBufferPartition partition;
3859             partition.index = nIntervals;
3860             partition.nearZ = closest;
3861             partition.farZ = prevNear;
3862             depthPartitions.push_back(partition);
3863 
3864             nIntervals++;
3865         }
3866 
3867         // If orbits are enabled, adjust the farthest partition so that it
3868         // can contain the orbit.
3869         if (!orbitPathList.empty())
3870         {
3871             depthPartitions[0].farZ = min(depthPartitions[0].farZ,
3872                                            orbitPathList[orbitPathList.size() - 1].centerZ -
3873                                            orbitPathList[orbitPathList.size() - 1].radius);
3874         }
3875 
3876         // We want to avoid overpartitioning the depth buffer. In this stage, we coalesce
3877         // partitions that have small spans in the depth buffer.
3878         // TODO: Implement this step!
3879 
3880         vector<Annotation>::iterator annotation = depthSortedAnnotations.begin();
3881 
3882         // Render everything that wasn't culled.
3883         float intervalSize = 1.0f / (float) max(1, nIntervals);
3884         i = nEntries - 1;
3885         for (int interval = 0; interval < nIntervals; interval++)
3886         {
3887             currentIntervalIndex = interval;
3888             beginObjectAnnotations();
3889 
3890             float nearPlaneDistance = -depthPartitions[interval].nearZ;
3891             float farPlaneDistance  = -depthPartitions[interval].farZ;
3892 
3893             // Set the depth range for this interval--each interval is allocated an
3894             // equal section of the depth buffer.
3895             glDepthRange(1.0f - (float) (interval + 1) * intervalSize,
3896                          1.0f - (float) interval * intervalSize);
3897 
3898             // Set up a perspective projection using the current interval's near and
3899             // far clip planes.
3900             glMatrixMode(GL_PROJECTION);
3901             glLoadIdentity();
3902             gluPerspective(fov,
3903                            (float) windowWidth / (float) windowHeight,
3904                            nearPlaneDistance,
3905                            farPlaneDistance);
3906             glMatrixMode(GL_MODELVIEW);
3907 
3908             Frustum intervalFrustum(degToRad(fov),
3909                                     (float) windowWidth / (float) windowHeight,
3910                                     -depthPartitions[interval].nearZ,
3911                                     -depthPartitions[interval].farZ);
3912 
3913 
3914 #if DEBUG_COALESCE
3915             clog << "interval: " << interval <<
3916                     ", near: " << -depthPartitions[interval].nearZ <<
3917                     ", far: " << -depthPartitions[interval].farZ <<
3918                     "\n";
3919 #endif
3920             int firstInInterval = i;
3921 
3922             // Render just the opaque objects in the first pass
3923             while (i >= 0 && renderList[i].farZ < depthPartitions[interval].nearZ)
3924             {
3925                 // This interval should completely contain the item
3926                 // Unless it's just a point?
3927                 //assert(renderList[i].nearZ <= depthPartitions[interval].near);
3928 
3929 #if DEBUG_COALESCE
3930                 switch (renderList[i].renderableType)
3931                 {
3932                 case RenderListEntry::RenderableBody:
3933                     if (renderList[i].discSizeInPixels > 1)
3934                     {
3935                         clog << renderList[i].body->getName() << "\n";
3936                     }
3937                     else
3938                     {
3939                         clog << "point: " << renderList[i].body->getName() << "\n";
3940                     }
3941                     break;
3942 
3943                 case RenderListEntry::RenderableStar:
3944                     if (renderList[i].discSizeInPixels > 1)
3945                     {
3946                         clog << "Star\n";
3947                     }
3948                     else
3949                     {
3950                         clog << "point: " << "Star" << "\n";
3951                     }
3952                     break;
3953 
3954                 default:
3955                     break;
3956                 }
3957 #endif
3958                 // Treat objects that are smaller than one pixel as transparent and render
3959                 // them in the second pass.
3960                 if (renderList[i].isOpaque && renderList[i].discSizeInPixels > 1.0f)
3961                     renderItem(renderList[i], observer, m_cameraOrientation, nearPlaneDistance, farPlaneDistance);
3962 
3963                 i--;
3964             }
3965 
3966             // Render orbit paths
3967             if (!orbitPathList.empty())
3968             {
3969                 glDisable(GL_LIGHTING);
3970                 glDisable(GL_TEXTURE_2D);
3971                 glEnable(GL_DEPTH_TEST);
3972                 glDepthMask(GL_FALSE);
3973 #ifdef USE_HDR
3974                 glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
3975 #else
3976                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3977 #endif
3978                 if ((renderFlags & ShowSmoothLines) != 0)
3979                 {
3980                     enableSmoothLines();
3981                 }
3982 
3983                 // Scan through the list of orbits and render any that overlap this interval
3984                 for (vector<OrbitPathListEntry>::const_iterator orbitIter = orbitPathList.begin();
3985                      orbitIter != orbitPathList.end(); orbitIter++)
3986                 {
3987                     // Test for overlap
3988                     float nearZ = -orbitIter->centerZ - orbitIter->radius;
3989                     float farZ = -orbitIter->centerZ + orbitIter->radius;
3990 
3991                     // Don't render orbits when they're completely outside this
3992                     // depth interval. Also, don't render an orbit in this
3993                     // interval if it is vastly larger than the interval
3994                     // range; otherwise, the GPU will have precision troubles
3995                     // when clipping, producing visual artifacts. The factor
3996                     // of 1e5 may need some tuning.
3997                     if (nearZ < farPlaneDistance && farZ > nearPlaneDistance &&
3998                         orbitIter->radius < 1.0e8f * (farPlaneDistance - nearPlaneDistance))
3999                     {
4000 #ifdef DEBUG_COALESCE
4001                         switch (interval % 6)
4002                         {
4003                             case 0: glColor4f(1.0f, 0.0f, 0.0f, 1.0f); break;
4004                             case 1: glColor4f(1.0f, 1.0f, 0.0f, 1.0f); break;
4005                             case 2: glColor4f(0.0f, 1.0f, 0.0f, 1.0f); break;
4006                             case 3: glColor4f(0.0f, 1.0f, 1.0f, 1.0f); break;
4007                             case 4: glColor4f(0.0f, 0.0f, 1.0f, 1.0f); break;
4008                             case 5: glColor4f(1.0f, 0.0f, 1.0f, 1.0f); break;
4009                             default: glColor4f(1.0f, 1.0f, 1.0f, 1.0f); break;
4010                         }
4011 #endif
4012                         orbitsRendered++;
4013                         renderOrbit(*orbitIter, now, m_cameraOrientation, intervalFrustum, nearPlaneDistance, farPlaneDistance);
4014 
4015 #if DEBUG_COALESCE
4016                         if (highlightObject.body() == orbitIter->body)
4017                         {
4018                             clog << "orbit, radius=" << orbitIter->radius << "\n";
4019                         }
4020 #endif
4021                     }
4022                     else
4023                         orbitsSkipped++;
4024                 }
4025 
4026                 if ((renderFlags & ShowSmoothLines) != 0)
4027                     disableSmoothLines();
4028                 glDepthMask(GL_FALSE);
4029             }
4030 
4031             // Render transparent objects in the second pass
4032             i = firstInInterval;
4033             while (i >= 0 && renderList[i].farZ < depthPartitions[interval].nearZ)
4034             {
4035                 if (!renderList[i].isOpaque || renderList[i].discSizeInPixels <= 1.0f)
4036                     renderItem(renderList[i], observer, m_cameraOrientation, nearPlaneDistance, farPlaneDistance);
4037 
4038                 i--;
4039             }
4040 
4041             // Render annotations in this interval
4042             if ((renderFlags & ShowSmoothLines) != 0)
4043                 enableSmoothLines();
4044             annotation = renderSortedAnnotations(annotation, -depthPartitions[interval].nearZ, -depthPartitions[interval].farZ, FontNormal);
4045             endObjectAnnotations();
4046             if ((renderFlags & ShowSmoothLines) != 0)
4047                 disableSmoothLines();
4048             glDisable(GL_DEPTH_TEST);
4049         }
4050 #if 0
4051         // TODO: Debugging output for new orbit code; remove when development is complete
4052         clog << "orbits: " << orbitsRendered
4053              << ", splines: " << splinesRendered
4054              << ", skipped: " << orbitsSkipped
4055              << ", sections culled: " << sectionsCulled
4056              << ", nIntervals: " << nIntervals << "\n";
4057 #endif
4058         splinesRendered = 0;
4059         orbitsRendered = 0;
4060         orbitsSkipped = 0;
4061         sectionsCulled = 0;
4062 
4063         // reset the depth range
4064         glDepthRange(0, 1);
4065     }
4066 
4067     renderForegroundAnnotations(FontNormal);
4068 
4069     glMatrixMode(GL_PROJECTION);
4070     glLoadIdentity();
4071     gluPerspective(fov,
4072                    (float) windowWidth / (float) windowHeight,
4073                    NEAR_DIST, FAR_DIST);
4074     glMatrixMode(GL_MODELVIEW);
4075 
4076     if (!selectionVisible && (renderFlags & ShowMarkers))
4077         renderSelectionPointer(observer, now, xfrustum, sel);
4078 
4079     // Pop camera orientation matrix
4080     glPopMatrix();
4081 
4082     glEnable(GL_TEXTURE_2D);
4083     glDisable(GL_LIGHTING);
4084     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
4085 
4086     glPolygonMode(GL_FRONT, GL_FILL);
4087     glPolygonMode(GL_BACK, GL_FILL);
4088 
4089     glDisable(GL_BLEND);
4090     glDepthMask(GL_TRUE);
4091     glEnable(GL_LIGHTING);
4092 
4093 #if 0
4094     int errCode = glGetError();
4095     if (errCode != GL_NO_ERROR)
4096     {
4097         cout << "glError: " << (char*) gluErrorString(errCode) << '\n';
4098     }
4099 #endif
4100 
4101     if (videoSync && glx::glXWaitVideoSyncSGI != NULL)
4102     {
4103         unsigned int count;
4104         glx::glXGetVideoSyncSGI(&count);
4105         glx::glXWaitVideoSyncSGI(2, (count+1) & 1, &count);
4106     }
4107 }
4108 
4109 
renderRingSystem(float innerRadius,float outerRadius,float beginAngle,float endAngle,unsigned int nSections)4110 static void renderRingSystem(float innerRadius,
4111                              float outerRadius,
4112                              float beginAngle,
4113                              float endAngle,
4114                              unsigned int nSections)
4115 {
4116     float angle = endAngle - beginAngle;
4117 
4118     glBegin(GL_QUAD_STRIP);
4119     for (unsigned int i = 0; i <= nSections; i++)
4120     {
4121         float t = (float) i / (float) nSections;
4122         float theta = beginAngle + t * angle;
4123         float s = (float) sin(theta);
4124         float c = (float) cos(theta);
4125         glTexCoord2f(0, 0.5f);
4126         glVertex3f(c * innerRadius, 0, s * innerRadius);
4127         glTexCoord2f(1, 0.5f);
4128         glVertex3f(c * outerRadius, 0, s * outerRadius);
4129     }
4130     glEnd();
4131 }
4132 
4133 
4134 // If the an object occupies a pixel or less of screen space, we don't
4135 // render its mesh at all and just display a starlike point instead.
4136 // Switching between the particle and mesh renderings of an object is
4137 // jarring, however . . . so we'll blend in the particle view of the
4138 // object to smooth things out, making it dimmer as the disc size exceeds the
4139 // max disc size.
renderObjectAsPoint_nosprite(Point3f position,float radius,float appMag,float _faintestMag,float discSizeInPixels,Color color,const Quatf & cameraOrientation,bool useHalos)4140 void Renderer::renderObjectAsPoint_nosprite(Point3f position,
4141                                             float radius,
4142                                             float appMag,
4143                                             float _faintestMag,
4144                                             float discSizeInPixels,
4145                                             Color color,
4146                                             const Quatf& cameraOrientation,
4147                                             bool useHalos)
4148 {
4149     float maxDiscSize = 1.0f;
4150     float maxBlendDiscSize = maxDiscSize + 3.0f;
4151     float discSize = 1.0f;
4152 
4153     if (discSizeInPixels < maxBlendDiscSize || useHalos)
4154     {
4155         float fade = 1.0f;
4156         if (discSizeInPixels > maxDiscSize)
4157         {
4158             fade = (maxBlendDiscSize - discSizeInPixels) /
4159                 (maxBlendDiscSize - maxDiscSize - 1.0f);
4160             if (fade > 1)
4161                 fade = 1;
4162         }
4163 
4164 #ifdef USE_HDR
4165         float fieldCorr = 2.0f * FOV/(fov + FOV);
4166         float satPoint = saturationMagNight * (1.0f + fieldCorr * fieldCorr);
4167 #else
4168         float satPoint = saturationMag;
4169 #endif
4170         float a = (_faintestMag - appMag) * brightnessScale + brightnessBias;
4171         if (starStyle == ScaledDiscStars && a > 1.0f)
4172             discSize = min(discSize * (2.0f * a - 1.0f), maxDiscSize);
4173         a = clamp(a) * fade;
4174 
4175         // We scale up the particle by a factor of 1.6 (at fov = 45deg)
4176         // so that it's more visible--the texture we use has fuzzy edges,
4177         // and if we render it in just one pixel, it's likely to disappear.
4178         Mat3f m = cameraOrientation.toMatrix3();
4179         Point3f center = position;
4180 
4181         // Offset the glare sprite so that it lies in front of the object
4182         Vec3f direction(center.x, center.y, center.z);
4183         direction.normalize();
4184 
4185         // Position the sprite on the the line between the viewer and the
4186         // object, and on a plane normal to the view direction.
4187         center = center + direction * (radius / ((Vec3f(0, 0, 1.0f) * m) * direction));
4188 
4189         float centerZ = (center * m.transpose()).z;
4190         float size = discSize * pixelSize * 1.6f * centerZ / corrFac;
4191 
4192         Vec3f v0 = Vec3f(-1, -1, 0) * m;
4193         Vec3f v1 = Vec3f( 1, -1, 0) * m;
4194         Vec3f v2 = Vec3f( 1,  1, 0) * m;
4195         Vec3f v3 = Vec3f(-1,  1, 0) * m;
4196 
4197         glEnable(GL_DEPTH_TEST);
4198 
4199         starTex->bind();
4200         glColor(color, a);
4201         glBegin(GL_QUADS);
4202         glTexCoord2f(0, 1);
4203         glVertex(center + (v0 * size));
4204         glTexCoord2f(1, 1);
4205         glVertex(center + (v1 * size));
4206         glTexCoord2f(1, 0);
4207         glVertex(center + (v2 * size));
4208         glTexCoord2f(0, 0);
4209         glVertex(center + (v3 * size));
4210         glEnd();
4211 
4212         // If the object is brighter than magnitude 1, add a halo around it to
4213         // make it appear more brilliant.  This is a hack to compensate for the
4214         // limited dynamic range of monitors.
4215         if (useHalos && appMag < satPoint)
4216         {
4217             float dist = center.distanceFromOrigin();
4218             float s    = dist * 0.001f * (3 - (appMag - satPoint)) * 2;
4219             if (s > size * 3)
4220                 size = s * 2.0f/(1.0f + FOV/fov);
4221             else
4222                 size = size * 3;
4223 
4224             float realSize = discSizeInPixels * pixelSize * dist;
4225             if (size < realSize * 6)
4226                 size = realSize * 6;
4227 
4228             a = GlareOpacity * clamp((appMag - satPoint) * -0.8f);
4229             gaussianGlareTex->bind();
4230             glColor(color, a);
4231             glBegin(GL_QUADS);
4232             glTexCoord2f(0, 1);
4233             glVertex(center + (v0 * size));
4234             glTexCoord2f(1, 1);
4235             glVertex(center + (v1 * size));
4236             glTexCoord2f(1, 0);
4237             glVertex(center + (v2 * size));
4238             glTexCoord2f(0, 0);
4239             glVertex(center + (v3 * size));
4240             glEnd();
4241         }
4242 
4243         glDisable(GL_DEPTH_TEST);
4244     }
4245 }
4246 
4247 
4248 // If the an object occupies a pixel or less of screen space, we don't
4249 // render its mesh at all and just display a starlike point instead.
4250 // Switching between the particle and mesh renderings of an object is
4251 // jarring, however . . . so we'll blend in the particle view of the
4252 // object to smooth things out, making it dimmer as the disc size exceeds the
4253 // max disc size.
renderObjectAsPoint(Point3f position,float radius,float appMag,float _faintestMag,float discSizeInPixels,Color color,const Quatf & cameraOrientation,bool useHalos,bool emissive)4254 void Renderer::renderObjectAsPoint(Point3f position,
4255                                    float radius,
4256                                    float appMag,
4257                                    float _faintestMag,
4258                                    float discSizeInPixels,
4259                                    Color color,
4260                                    const Quatf& cameraOrientation,
4261                                    bool useHalos,
4262                                    bool emissive)
4263 {
4264     float maxDiscSize = (starStyle == ScaledDiscStars) ? MaxScaledDiscStarSize : 1.0f;
4265     float maxBlendDiscSize = maxDiscSize + 3.0f;
4266 
4267     bool useScaledDiscs = starStyle == ScaledDiscStars;
4268 
4269     if (discSizeInPixels < maxBlendDiscSize || useHalos)
4270     {
4271         float alpha = 1.0f;
4272         float fade = 1.0f;
4273         float size = BaseStarDiscSize;
4274 #ifdef USE_HDR
4275         float fieldCorr = 2.0f * FOV/(fov + FOV);
4276         float satPoint = saturationMagNight * (1.0f + fieldCorr * fieldCorr);
4277         satPoint += brightPlus;
4278 #else
4279         float satPoint = _faintestMag - (1.0f - brightnessBias) / brightnessScale;
4280 #endif
4281 
4282         if (discSizeInPixels > maxDiscSize)
4283         {
4284             fade = (maxBlendDiscSize - discSizeInPixels) /
4285                 (maxBlendDiscSize - maxDiscSize);
4286             if (fade > 1)
4287                 fade = 1;
4288         }
4289 
4290         alpha = (_faintestMag - appMag) * brightnessScale * 2.0f + brightnessBias;
4291 
4292         float pointSize = size;
4293         float glareSize = 0.0f;
4294         float glareAlpha = 0.0f;
4295         if (useScaledDiscs)
4296         {
4297             if (alpha < 0.0f)
4298             {
4299                 alpha = 0.0f;
4300             }
4301             else if (alpha > 1.0f)
4302             {
4303                 float discScale = min(MaxScaledDiscStarSize, (float) pow(2.0f, 0.3f * (satPoint - appMag)));
4304                 pointSize *= max(1.0f, discScale);
4305 
4306                 glareAlpha = min(0.5f, discScale / 4.0f);
4307                 if (discSizeInPixels > MaxScaledDiscStarSize)
4308                 {
4309                     glareAlpha = min(glareAlpha,
4310                                      (MaxScaledDiscStarSize - discSizeInPixels) / MaxScaledDiscStarSize + 1.0f);
4311                 }
4312                 glareSize = pointSize * 3.0f;
4313 
4314                 alpha = 1.0f;
4315             }
4316         }
4317         else
4318         {
4319             if (alpha < 0.0f)
4320             {
4321                 alpha = 0.0f;
4322             }
4323             else if (alpha > 1.0f)
4324             {
4325                 float discScale = min(100.0f, satPoint - appMag + 2.0f);
4326                 glareAlpha = min(GlareOpacity, (discScale - 2.0f) / 4.0f);
4327                 glareSize = pointSize * discScale * 2.0f ;
4328                 if (emissive)
4329                     glareSize = max(glareSize, pointSize * discSizeInPixels * 3.0f);
4330             }
4331         }
4332 
4333         alpha *= fade;
4334         if (!emissive)
4335         {
4336             glareSize = max(glareSize, pointSize * discSizeInPixels * 3.0f);
4337             glareAlpha *= fade;
4338         }
4339 
4340         Mat3f m = cameraOrientation.toMatrix3();
4341         Point3f center = position;
4342 
4343         // Offset the glare sprite so that it lies in front of the object
4344         Vec3f direction(center.x, center.y, center.z);
4345         direction.normalize();
4346 
4347         // Position the sprite on the the line between the viewer and the
4348         // object, and on a plane normal to the view direction.
4349         center = center + direction * (radius / ((Vec3f(0, 0, 1.0f) * m) * direction));
4350 
4351         glEnable(GL_DEPTH_TEST);
4352 #if !defined(NO_MAX_POINT_SIZE)
4353         // TODO: OpenGL appears to limit the max point size unless we
4354         // actually set up a shader that writes the pointsize values. To get
4355         // around this, we'll use billboards.
4356         Vec3f v0 = Vec3f(-1, -1, 0) * m;
4357         Vec3f v1 = Vec3f( 1, -1, 0) * m;
4358         Vec3f v2 = Vec3f( 1,  1, 0) * m;
4359         Vec3f v3 = Vec3f(-1,  1, 0) * m;
4360         float distanceAdjust = pixelSize * center.distanceFromOrigin() * 0.5f;
4361 
4362         if (starStyle == PointStars)
4363         {
4364             glDisable(GL_TEXTURE_2D);
4365             glBegin(GL_POINTS);
4366             glColor(color, alpha);
4367             glVertex(center);
4368             glEnd();
4369             glEnable(GL_TEXTURE_2D);
4370         }
4371         else
4372         {
4373             gaussianDiscTex->bind();
4374 
4375             pointSize *= distanceAdjust;
4376             glBegin(GL_QUADS);
4377             glColor(color, alpha);
4378             glTexCoord2f(0, 1);
4379             glVertex(center + (v0 * pointSize));
4380             glTexCoord2f(1, 1);
4381             glVertex(center + (v1 * pointSize));
4382             glTexCoord2f(1, 0);
4383             glVertex(center + (v2 * pointSize));
4384             glTexCoord2f(0, 0);
4385             glVertex(center + (v3 * pointSize));
4386             glEnd();
4387         }
4388 
4389         // If the object is brighter than magnitude 1, add a halo around it to
4390         // make it appear more brilliant.  This is a hack to compensate for the
4391         // limited dynamic range of monitors.
4392         //
4393         // TODO: Stars look fine but planets look unrealistically bright
4394         // with halos.
4395         if (useHalos && glareAlpha > 0.0f)
4396         {
4397             gaussianGlareTex->bind();
4398 
4399             glareSize *= distanceAdjust;
4400             glBegin(GL_QUADS);
4401             glColor(color, glareAlpha);
4402             glTexCoord2f(0, 1);
4403             glVertex(center + (v0 * glareSize));
4404             glTexCoord2f(1, 1);
4405             glVertex(center + (v1 * glareSize));
4406             glTexCoord2f(1, 0);
4407             glVertex(center + (v2 * glareSize));
4408             glTexCoord2f(0, 0);
4409             glVertex(center + (v3 * glareSize));
4410             glEnd();
4411         }
4412 #else
4413         // Disabled because of point size limits
4414         glEnable(GL_POINT_SPRITE_ARB);
4415         glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
4416 
4417         gaussianDiscTex->bind();
4418         glColor(color, alpha);
4419         glPointSize(pointSize);
4420         glBegin(GL_POINTS);
4421         glVertex(center);
4422         glEnd();
4423 
4424         // If the object is brighter than magnitude 1, add a halo around it to
4425         // make it appear more brilliant.  This is a hack to compensate for the
4426         // limited dynamic range of monitors.
4427         //
4428         // TODO: Stars look fine but planets look unrealistically bright
4429         // with halos.
4430         if (useHalos && glareAlpha > 0.0f)
4431         {
4432             gaussianGlareTex->bind();
4433 
4434             glColor(color, glareAlpha);
4435             glPointSize(glareSize);
4436             glBegin(GL_POINTS);
4437             glVertex(center);
4438             glEnd();
4439         }
4440 
4441         glDisable(GL_POINT_SPRITE_ARB);
4442         glDisable(GL_DEPTH_TEST);
4443 #endif // NO_MAX_POINT_SIZE
4444     }
4445 }
4446 
4447 
renderBumpMappedMesh(const GLContext & context,Texture & baseTexture,Texture & bumpTexture,Vec3f lightDirection,Quatf orientation,Color ambientColor,const Frustum & frustum,float lod)4448 static void renderBumpMappedMesh(const GLContext& context,
4449                                  Texture& baseTexture,
4450                                  Texture& bumpTexture,
4451                                  Vec3f lightDirection,
4452                                  Quatf orientation,
4453                                  Color ambientColor,
4454                                  const Frustum& frustum,
4455                                  float lod)
4456 {
4457     // We're doing our own per-pixel lighting, so disable GL's lighting
4458     glDisable(GL_LIGHTING);
4459 
4460     // Render the base texture on the first pass . . .  The color
4461     // should have already been set up by the caller.
4462     g_lodSphere->render(context,
4463                         LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
4464                         frustum, lod,
4465                         &baseTexture);
4466 
4467     // The 'default' light vector for the bump map is (0, 0, 1).  Determine
4468     // a rotation transformation that will move the sun direction to
4469     // this vector.
4470     Quatf lightOrientation = Quatf::vecToVecRotation(Vec3f(0.0f, 0.0f, 1.0f), lightDirection);
4471 
4472     glEnable(GL_BLEND);
4473     glBlendFunc(GL_DST_COLOR, GL_ZERO);
4474 
4475     // Set up the bump map with one directional light source
4476     SetupCombinersBumpMap(bumpTexture, *normalizationTex, ambientColor);
4477 
4478     // The second set texture coordinates will contain the light
4479     // direction in tangent space.  We'll generate the texture coordinates
4480     // from the surface normals using GL_NORMAL_MAP_EXT and then
4481     // use the texture matrix to rotate them into tangent space.
4482     // This method of generating tangent space light direction vectors
4483     // isn't as general as transforming the light direction by an
4484     // orthonormal basis for each mesh vertex, but it works well enough
4485     // for spheres illuminated by directional light sources.
4486     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
4487 
4488     // Set up GL_NORMAL_MAP_EXT texture coordinate generation.  This
4489     // mode is part of the cube map extension.
4490     glEnable(GL_TEXTURE_GEN_R);
4491     glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
4492     glEnable(GL_TEXTURE_GEN_S);
4493     glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
4494     glEnable(GL_TEXTURE_GEN_T);
4495     glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
4496 
4497     // Set up the texture transformation--the light direction and the
4498     // viewer orientation both need to be considered.
4499     glMatrixMode(GL_TEXTURE);
4500     glScalef(-1.0f, 1.0f, 1.0f);
4501     glRotate(lightOrientation * ~orientation);
4502     glMatrixMode(GL_MODELVIEW);
4503     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
4504 
4505     g_lodSphere->render(context,
4506                         LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
4507                         frustum, lod,
4508                         &bumpTexture);
4509 
4510     // Reset the second texture unit
4511     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
4512     glMatrixMode(GL_TEXTURE);
4513     glLoadIdentity();
4514     glMatrixMode(GL_MODELVIEW);
4515     glDisable(GL_TEXTURE_GEN_R);
4516     glDisable(GL_TEXTURE_GEN_S);
4517     glDisable(GL_TEXTURE_GEN_T);
4518 
4519     DisableCombiners();
4520     glDisable(GL_BLEND);
4521 }
4522 
4523 
renderSmoothMesh(const GLContext & context,Texture & baseTexture,Vec3f lightDirection,Quatf orientation,Color ambientColor,float lod,const Frustum & frustum,bool invert=false)4524 static void renderSmoothMesh(const GLContext& context,
4525                              Texture& baseTexture,
4526                              Vec3f lightDirection,
4527                              Quatf orientation,
4528                              Color ambientColor,
4529                              float lod,
4530                              const Frustum& frustum,
4531                              bool invert = false)
4532 {
4533     Texture* textures[4];
4534 
4535     // We're doing our own per-pixel lighting, so disable GL's lighting
4536     glDisable(GL_LIGHTING);
4537 
4538     // The 'default' light vector for the bump map is (0, 0, 1).  Determine
4539     // a rotation transformation that will move the sun direction to
4540     // this vector.
4541     Quatf lightOrientation = Quatf::vecToVecRotation(Vec3f(0.0f, 0.0f, 1.0f), lightDirection);
4542 
4543     SetupCombinersSmooth(baseTexture, *normalizationTex, ambientColor, invert);
4544 
4545     // The second set texture coordinates will contain the light
4546     // direction in tangent space.  We'll generate the texture coordinates
4547     // from the surface normals using GL_NORMAL_MAP_EXT and then
4548     // use the texture matrix to rotate them into tangent space.
4549     // This method of generating tangent space light direction vectors
4550     // isn't as general as transforming the light direction by an
4551     // orthonormal basis for each mesh vertex, but it works well enough
4552     // for spheres illuminated by directional light sources.
4553     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
4554 
4555     // Set up GL_NORMAL_MAP_EXT texture coordinate generation.  This
4556     // mode is part of the cube map extension.
4557     glEnable(GL_TEXTURE_GEN_R);
4558     glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
4559     glEnable(GL_TEXTURE_GEN_S);
4560     glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
4561     glEnable(GL_TEXTURE_GEN_T);
4562     glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
4563 
4564     // Set up the texture transformation--the light direction and the
4565     // viewer orientation both need to be considered.
4566     glMatrixMode(GL_TEXTURE);
4567     glRotate(lightOrientation * ~orientation);
4568     glMatrixMode(GL_MODELVIEW);
4569     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
4570 
4571     textures[0] = &baseTexture;
4572     g_lodSphere->render(context,
4573                         LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
4574                         frustum, lod,
4575                         textures, 1);
4576 
4577     // Reset the second texture unit
4578     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
4579     glMatrixMode(GL_TEXTURE);
4580     glLoadIdentity();
4581     glMatrixMode(GL_MODELVIEW);
4582     glDisable(GL_TEXTURE_GEN_R);
4583     glDisable(GL_TEXTURE_GEN_S);
4584     glDisable(GL_TEXTURE_GEN_T);
4585 
4586     DisableCombiners();
4587 }
4588 
4589 
4590 // Used to sort light sources in order of decreasing irradiance
4591 struct LightIrradiancePredicate
4592 {
4593     int unused;
4594 
LightIrradiancePredicateLightIrradiancePredicate4595     LightIrradiancePredicate() {};
4596 
operator ()LightIrradiancePredicate4597     bool operator()(const DirectionalLight& l0,
4598                     const DirectionalLight& l1) const
4599     {
4600         return (l0.irradiance > l1.irradiance);
4601     }
4602 };
4603 
4604 
renderAtmosphere(const Atmosphere & atmosphere,Point3f center,float radius,const Vec3f & sunDirection,Color ambientColor,float fade,bool lit)4605 void renderAtmosphere(const Atmosphere& atmosphere,
4606                       Point3f center,
4607                       float radius,
4608                       const Vec3f& sunDirection,
4609                       Color ambientColor,
4610                       float fade,
4611                       bool lit)
4612 {
4613     if (atmosphere.height == 0.0f)
4614         return;
4615 
4616     glDepthMask(GL_FALSE);
4617 
4618     Vec3f eyeVec = center - Point3f(0.0f, 0.0f, 0.0f);
4619     double centerDist = eyeVec.length();
4620     // double surfaceDist = (double) centerDist - (double) radius;
4621 
4622     Vec3f normal = eyeVec;
4623     normal = normal / (float) centerDist;
4624 
4625     float tangentLength = (float) sqrt(square(centerDist) - square(radius));
4626     float atmRadius = tangentLength * radius / (float) centerDist;
4627     float atmOffsetFromCenter = square(radius) / (float) centerDist;
4628     Point3f atmCenter = center - atmOffsetFromCenter * normal;
4629 
4630     Vec3f uAxis, vAxis;
4631     if (abs(normal.x) < abs(normal.y) && abs(normal.x) < abs(normal.z))
4632     {
4633         uAxis = Vec3f(1, 0, 0) ^ normal;
4634         uAxis.normalize();
4635     }
4636     else if (abs(eyeVec.y) < abs(normal.z))
4637     {
4638         uAxis = Vec3f(0, 1, 0) ^ normal;
4639         uAxis.normalize();
4640     }
4641     else
4642     {
4643         uAxis = Vec3f(0, 0, 1) ^ normal;
4644         uAxis.normalize();
4645     }
4646     vAxis = uAxis ^ normal;
4647 
4648     float height = atmosphere.height / radius;
4649 
4650     glBegin(GL_QUAD_STRIP);
4651     int divisions = 180;
4652     for (int i = 0; i <= divisions; i++)
4653     {
4654         float theta = (float) i / (float) divisions * 2 * (float) PI;
4655         Vec3f v = (float) cos(theta) * uAxis + (float) sin(theta) * vAxis;
4656         Point3f base = atmCenter + v * atmRadius;
4657         Vec3f toCenter = base - center;
4658 
4659         float cosSunAngle = (toCenter * sunDirection) / radius;
4660         float brightness = 1.0f;
4661         float botColor[3];
4662         float topColor[3];
4663         botColor[0] = atmosphere.lowerColor.red();
4664         botColor[1] = atmosphere.lowerColor.green();
4665         botColor[2] = atmosphere.lowerColor.blue();
4666         topColor[0] = atmosphere.upperColor.red();
4667         topColor[1] = atmosphere.upperColor.green();
4668         topColor[2] = atmosphere.upperColor.blue();
4669 
4670         if (cosSunAngle < 0.2f && lit)
4671         {
4672             if (cosSunAngle < -0.2f)
4673             {
4674                 brightness = 0;
4675             }
4676             else
4677             {
4678                 float t = (0.2f + cosSunAngle) * 2.5f;
4679                 brightness = t;
4680                 botColor[0] = Mathf::lerp(t, 1.0f, botColor[0]);
4681                 botColor[1] = Mathf::lerp(t, 0.3f, botColor[1]);
4682                 botColor[2] = Mathf::lerp(t, 0.0f, botColor[2]);
4683                 topColor[0] = Mathf::lerp(t, 1.0f, topColor[0]);
4684                 topColor[1] = Mathf::lerp(t, 0.3f, topColor[1]);
4685                 topColor[2] = Mathf::lerp(t, 0.0f, topColor[2]);
4686             }
4687         }
4688 
4689         glColor4f(botColor[0], botColor[1], botColor[2],
4690                   0.85f * fade * brightness + ambientColor.red());
4691         glVertex(base - toCenter * height * 0.05f);
4692         glColor4f(topColor[0], topColor[1], topColor[2], 0.0f);
4693         glVertex(base + toCenter * height);
4694     }
4695     glEnd();
4696 }
4697 
4698 
ellipsoidTangent(const Vec3f & recipSemiAxes,const Vec3f & w,const Vec3f & e,const Vec3f & e_,float ee)4699 static Vec3f ellipsoidTangent(const Vec3f& recipSemiAxes,
4700                               const Vec3f& w,
4701                               const Vec3f& e,
4702                               const Vec3f& e_,
4703                               float ee)
4704 {
4705     // We want to find t such that -E(1-t) + Wt is the direction of a ray
4706     // tangent to the ellipsoid.  A tangent ray will intersect the ellipsoid
4707     // at exactly one point.  Finding the intersection between a ray and an
4708     // ellipsoid ultimately requires using the quadratic formula, which has
4709     // one solution when the discriminant (b^2 - 4ac) is zero.  The code below
4710     // computes the value of t that results in a discriminant of zero.
4711     Vec3f w_(w.x * recipSemiAxes.x, w.y * recipSemiAxes.y, w.z * recipSemiAxes.z);
4712     float ww = w_ * w_;
4713     float ew = w_ * e_;
4714 
4715     // Before elimination of terms:
4716     // float a =  4 * square(ee + ew) - 4 * (ee + 2 * ew + ww) * (ee - 1.0f);
4717     // float b = -8 * ee * (ee + ew)  - 4 * (-2 * (ee + ew) * (ee - 1.0f));
4718     // float c =  4 * ee * ee         - 4 * (ee * (ee - 1.0f));
4719 
4720     // Simplify the below expression and eliminate the ee^2 terms; this
4721     // prevents precision errors, as ee tends to be a very large value.
4722     //T a =  4 * square(ee + ew) - 4 * (ee + 2 * ew + ww) * (ee - 1);
4723     //float a =  4 * square(ee + ew) - 4 * (ee + 2 * ew + ww) * (ee - 1.0f);
4724     float a =  4 * (square(ew) - ee * ww + ee + 2 * ew + ww);
4725     float b = -8 * (ee + ew);
4726     float c =  4 * ee;
4727 
4728     float t = 0.0f;
4729     float discriminant = b * b - 4 * a * c;
4730 
4731     if (discriminant < 0.0f)
4732         t = (-b + (float) sqrt(-discriminant)) / (2 * a); // Bad!
4733     else
4734         t = (-b + (float) sqrt(discriminant)) / (2 * a);
4735 
4736     // V is the direction vector.  We now need the point of intersection,
4737     // which we obtain by solving the quadratic equation for the ray-ellipse
4738     // intersection.  Since we already know that the discriminant is zero,
4739     // the solution is just -b/2a
4740     Vec3f v = -e * (1 - t) + w * t;
4741     Vec3f v_(v.x * recipSemiAxes.x, v.y * recipSemiAxes.y, v.z * recipSemiAxes.z);
4742     float a1 = v_ * v_;
4743     float b1 = 2.0f * v_ * e_;
4744     float t1 = -b1 / (2 * a1);
4745 
4746     return e + v * t1;
4747 }
4748 
4749 
renderEllipsoidAtmosphere(const Atmosphere & atmosphere,Point3f center,const Quatf & orientation,Vec3f semiAxes,const Vec3f & sunDirection,const LightingState & ls,float pixSize,bool lit)4750 void Renderer::renderEllipsoidAtmosphere(const Atmosphere& atmosphere,
4751                                          Point3f center,
4752                                          const Quatf& orientation,
4753                                          Vec3f semiAxes,
4754                                          const Vec3f& sunDirection,
4755                                          const LightingState& ls,
4756                                          float pixSize,
4757                                          bool lit)
4758 {
4759     if (atmosphere.height == 0.0f)
4760         return;
4761 
4762     glDepthMask(GL_FALSE);
4763 
4764     // Gradually fade in the atmosphere if it's thickness on screen is just
4765     // over one pixel.
4766     float fade = clamp(pixSize - 2);
4767 
4768     Mat3f rot = orientation.toMatrix3();
4769     Mat3f irot = conjugate(orientation).toMatrix3();
4770 
4771     Point3f eyePos(0.0f, 0.0f, 0.0f);
4772     float radius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
4773     Vec3f eyeVec = center - eyePos;
4774     eyeVec = eyeVec * irot;
4775     double centerDist = eyeVec.length();
4776 
4777     float height = atmosphere.height / radius;
4778     Vec3f recipSemiAxes(1.0f / semiAxes.x, 1.0f / semiAxes.y, 1.0f / semiAxes.z);
4779 
4780     Vec3f recipAtmSemiAxes = recipSemiAxes / (1.0f + height);
4781     Mat3f A = Mat3f::scaling(recipAtmSemiAxes);
4782     Mat3f A1 = Mat3f::scaling(recipSemiAxes);
4783 
4784     // ellipDist is not the true distance from the surface unless the
4785     // planet is spherical.  Computing the true distance requires finding
4786     // the roots of a sixth degree polynomial, and isn't actually what we
4787     // want anyhow since the atmosphere region is just the planet ellipsoid
4788     // multiplied by a uniform scale factor.  The value that we do compute
4789     // is the distance to the surface along a line from the eye position to
4790     // the center of the ellipsoid.
4791     float ellipDist = (float) sqrt((eyeVec * A1) * (eyeVec * A1)) - 1.0f;
4792     bool within = ellipDist < height;
4793 
4794     // Adjust the tesselation of the sky dome/ring based on distance from the
4795     // planet surface.
4796     int nSlices = MaxSkySlices;
4797     if (ellipDist < 0.25f)
4798     {
4799         nSlices = MinSkySlices + max(0, (int) ((ellipDist / 0.25f) * (MaxSkySlices - MinSkySlices)));
4800         nSlices &= ~1;
4801     }
4802 
4803     int nRings = min(1 + (int) pixSize / 5, 6);
4804     int nHorizonRings = nRings;
4805     if (within)
4806         nRings += 12;
4807 
4808     float horizonHeight = height;
4809     if (within)
4810     {
4811         if (ellipDist <= 0.0f)
4812             horizonHeight = 0.0f;
4813         else
4814             horizonHeight *= max((float) pow(ellipDist / height, 0.33f), 0.001f);
4815     }
4816 
4817     Vec3f e = -eyeVec;
4818     Vec3f e_(e.x * recipSemiAxes.x, e.y * recipSemiAxes.y, e.z * recipSemiAxes.z);
4819     float ee = e_ * e_;
4820 
4821     // Compute the cosine of the altitude of the sun.  This is used to compute
4822     // the degree of sunset/sunrise coloration.
4823     float cosSunAltitude = 0.0f;
4824     {
4825         // Check for a sun either directly behind or in front of the viewer
4826         float cosSunAngle = (float) ((sunDirection * e) / centerDist);
4827         if (cosSunAngle < -1.0f + 1.0e-6f)
4828         {
4829             cosSunAltitude = 0.0f;
4830         }
4831         else if (cosSunAngle > 1.0f - 1.0e-6f)
4832         {
4833             cosSunAltitude = 0.0f;
4834         }
4835         else
4836         {
4837             Point3f tangentPoint = center +
4838                 ellipsoidTangent(recipSemiAxes,
4839                                  (-sunDirection * irot) * (float) centerDist,
4840                                  e, e_, ee) * rot;
4841             Vec3f tangentDir = tangentPoint - eyePos;
4842             tangentDir.normalize();
4843             cosSunAltitude = sunDirection * tangentDir;
4844         }
4845     }
4846 
4847     Vec3f normal = eyeVec;
4848     normal = normal / (float) centerDist;
4849 
4850     Vec3f uAxis, vAxis;
4851     if (abs(normal.x) < abs(normal.y) && abs(normal.x) < abs(normal.z))
4852     {
4853         uAxis = Vec3f(1, 0, 0) ^ normal;
4854         uAxis.normalize();
4855     }
4856     else if (abs(eyeVec.y) < abs(normal.z))
4857     {
4858         uAxis = Vec3f(0, 1, 0) ^ normal;
4859         uAxis.normalize();
4860     }
4861     else
4862     {
4863         uAxis = Vec3f(0, 0, 1) ^ normal;
4864         uAxis.normalize();
4865     }
4866     vAxis = uAxis ^ normal;
4867 
4868     // Compute the contour of the ellipsoid
4869     int i;
4870     for (i = 0; i <= nSlices; i++)
4871     {
4872         // We want rays with an origin at the eye point and tangent to the the
4873         // ellipsoid.
4874         float theta = (float) i / (float) nSlices * 2 * (float) PI;
4875         Vec3f w = (float) cos(theta) * uAxis + (float) sin(theta) * vAxis;
4876         w = w * (float) centerDist;
4877 
4878         Vec3f toCenter = ellipsoidTangent(recipSemiAxes, w, e, e_, ee);
4879         skyContour[i].v = toCenter * rot;
4880         skyContour[i].centerDist = skyContour[i].v.length();
4881         skyContour[i].eyeDir = skyContour[i].v + (center - eyePos);
4882         skyContour[i].eyeDist = skyContour[i].eyeDir.length();
4883         skyContour[i].eyeDir.normalize();
4884 
4885         float skyCapDist = (float) sqrt(square(skyContour[i].eyeDist) +
4886                                         square(horizonHeight * radius));
4887         skyContour[i].cosSkyCapAltitude = skyContour[i].eyeDist /
4888             skyCapDist;
4889     }
4890 
4891 
4892     Vec3f botColor(atmosphere.lowerColor.red(),
4893                    atmosphere.lowerColor.green(),
4894                    atmosphere.lowerColor.blue());
4895     Vec3f topColor(atmosphere.upperColor.red(),
4896                    atmosphere.upperColor.green(),
4897                    atmosphere.upperColor.blue());
4898     Vec3f sunsetColor(atmosphere.sunsetColor.red(),
4899                       atmosphere.sunsetColor.green(),
4900                       atmosphere.sunsetColor.blue());
4901     if (within)
4902     {
4903         Vec3f skyColor(atmosphere.skyColor.red(),
4904                        atmosphere.skyColor.green(),
4905                        atmosphere.skyColor.blue());
4906         if (ellipDist < 0.0f)
4907             topColor = skyColor;
4908         else
4909             topColor = skyColor + (topColor - skyColor) * (ellipDist / height);
4910     }
4911 
4912     if (ls.nLights == 0 && lit)
4913     {
4914         Vec3f black(0.0f, 0.0f, 0.0f);
4915         botColor = topColor = sunsetColor = black;
4916     }
4917 
4918     Vec3f zenith = (skyContour[0].v + skyContour[nSlices / 2].v);
4919     zenith.normalize();
4920     zenith *= skyContour[0].centerDist * (1.0f + horizonHeight * 2.0f);
4921 
4922     float minOpacity = within ? (1.0f - ellipDist / height) * 0.75f : 0.0f;
4923     float sunset = cosSunAltitude < 0.9f ? 0.0f : (cosSunAltitude - 0.9f) * 10.0f;
4924 
4925     // Build the list of vertices
4926     SkyVertex* vtx = skyVertices;
4927     for (i = 0; i <= nRings; i++)
4928     {
4929         float h = min(1.0f, (float) i / (float) nHorizonRings);
4930         float hh = (float) sqrt(h);
4931         float u = i <= nHorizonRings ? 0.0f :
4932             (float) (i - nHorizonRings) / (float) (nRings - nHorizonRings);
4933         float r = Mathf::lerp(h, 1.0f - (horizonHeight * 0.05f), 1.0f + horizonHeight);
4934         float atten = 1.0f - hh;
4935 
4936         for (int j = 0; j < nSlices; j++)
4937         {
4938             Vec3f v;
4939             if (i <= nHorizonRings)
4940                 v = skyContour[j].v * r;
4941             else
4942                 v = (skyContour[j].v * (1.0f - u) + zenith * u) * r;
4943             Point3f p = center + v;
4944 
4945 
4946             Vec3f viewDir(p.x, p.y, p.z);
4947             viewDir.normalize();
4948             float cosSunAngle = viewDir * sunDirection;
4949             float cosAltitude = viewDir * skyContour[j].eyeDir;
4950             float brightness = 1.0f;
4951             float coloration = 0.0f;
4952             if (lit)
4953             {
4954                 if (sunset > 0.0f && cosSunAngle > 0.7f && cosAltitude > 0.98f)
4955                 {
4956                     coloration =  (1.0f / 0.30f) * (cosSunAngle - 0.70f);
4957                     coloration *= 50.0f * (cosAltitude - 0.98f);
4958                     coloration *= sunset;
4959                 }
4960 
4961                 cosSunAngle = (skyContour[j].v * sunDirection) / skyContour[j].centerDist;
4962                 if (cosSunAngle > -0.2f)
4963                 {
4964                     if (cosSunAngle < 0.3f)
4965                         brightness = (cosSunAngle + 0.2f) * 2.0f;
4966                     else
4967                         brightness = 1.0f;
4968                 }
4969                 else
4970                 {
4971                     brightness = 0.0f;
4972                 }
4973             }
4974 
4975             vtx->x = p.x;
4976             vtx->y = p.y;
4977             vtx->z = p.z;
4978 
4979 #if 0
4980             // Better way of generating sky color gradients--based on
4981             // altitude angle.
4982             if (!within)
4983             {
4984                 hh = (1.0f - cosAltitude) / (1.0f - skyContour[j].cosSkyCapAltitude);
4985             }
4986             else
4987             {
4988                 float top = pow((ellipDist / height), 0.125f) * skyContour[j].cosSkyCapAltitude;
4989                 if (cosAltitude < top)
4990                     hh = 1.0f;
4991                 else
4992                     hh = (1.0f - cosAltitude) / (1.0f - top);
4993             }
4994             hh = sqrt(hh);
4995             //hh = (float) pow(hh, 0.25f);
4996 #endif
4997 
4998             atten = 1.0f - hh;
4999             Vec3f color = (1.0f - hh) * botColor + hh * topColor;
5000             brightness *= minOpacity + (1.0f - minOpacity) * fade * atten;
5001             if (coloration != 0.0f)
5002                 color = (1.0f - coloration) * color + coloration * sunsetColor;
5003 
5004 #ifdef HDR_COMPRESS
5005             brightness *= 0.5f;
5006 #endif
5007             Color(brightness * color.x,
5008                   brightness * color.y,
5009                   brightness * color.z,
5010                   fade * (minOpacity + (1.0f - minOpacity)) * atten).get(vtx->color);
5011             vtx++;
5012         }
5013     }
5014 
5015     // Create the index list
5016     int index = 0;
5017     for (i = 0; i < nRings; i++)
5018     {
5019         int baseVertex = i * nSlices;
5020         for (int j = 0; j < nSlices; j++)
5021         {
5022             skyIndices[index++] = baseVertex + j;
5023             skyIndices[index++] = baseVertex + nSlices + j;
5024         }
5025         skyIndices[index++] = baseVertex;
5026         skyIndices[index++] = baseVertex + nSlices;
5027     }
5028 
5029     glEnableClientState(GL_VERTEX_ARRAY);
5030     glVertexPointer(3, GL_FLOAT, sizeof(SkyVertex), &skyVertices[0].x);
5031     glEnableClientState(GL_COLOR_ARRAY);
5032     glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(SkyVertex),
5033                    static_cast<void*>(&skyVertices[0].color));
5034 
5035     for (i = 0; i < nRings; i++)
5036     {
5037         glDrawElements(GL_QUAD_STRIP,
5038                        (nSlices + 1) * 2,
5039                        GL_UNSIGNED_INT,
5040                        &skyIndices[(nSlices + 1) * 2 * i]);
5041     }
5042 
5043     glDisableClientState(GL_COLOR_ARRAY);
5044 }
5045 
5046 
renderCompass(Point3f center,const Quatf & orientation,Vec3f semiAxes,float pixelSize)5047 void renderCompass(Point3f center,
5048                    const Quatf& orientation,
5049                    Vec3f semiAxes,
5050                    float pixelSize)
5051 {
5052     Mat3f rot = orientation.toMatrix3();
5053     Mat3f irot = conjugate(orientation).toMatrix3();
5054 
5055     Point3f eyePos(0.0f, 0.0f, 0.0f);
5056     float radius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
5057     Vec3f eyeVec = center - eyePos;
5058     eyeVec = eyeVec * irot;
5059     double centerDist = eyeVec.length();
5060 
5061     float height = 1.0f / radius;
5062     Vec3f recipSemiAxes(1.0f / semiAxes.x,
5063                         1.0f / semiAxes.y,
5064                         1.0f / semiAxes.z);
5065 
5066     Vec3f recipAtmSemiAxes = recipSemiAxes / (1.0f + height);
5067     Mat3f A = Mat3f::scaling(recipAtmSemiAxes);
5068     Mat3f A1 = Mat3f::scaling(recipSemiAxes);
5069 
5070     const int nCompassPoints = 16;
5071     Vec3f compassPoints[nCompassPoints];
5072 
5073 
5074     // ellipDist is not the true distance from the surface unless the
5075     // planet is spherical.  Computing the true distance requires finding
5076     // the roots of a sixth degree polynomial, and isn't actually what we
5077     // want anyhow since the atmosphere region is just the planet ellipsoid
5078     // multiplied by a uniform scale factor.  The value that we do compute
5079     // is the distance to the surface along a line from the eye position to
5080     // the center of the ellipsoid.
5081 
5082     /*float ellipDist = (float) sqrt((eyeVec * A1) * (eyeVec * A1)) - 1.0f; Unused*/
5083 
5084     Vec3f e = -eyeVec;
5085     Vec3f e_(e.x * recipSemiAxes.x, e.y * recipSemiAxes.y, e.z * recipSemiAxes.z);
5086     float ee = e_ * e_;
5087 
5088     Vec3f normal = eyeVec;
5089     normal = normal / (float) centerDist;
5090 
5091     Vec3f uAxis, vAxis;
5092     Vec3f northPole(0.0f, 1.0f, 0.0f);
5093     vAxis = normal ^ northPole;
5094     vAxis.normalize();
5095     uAxis = vAxis ^ normal;
5096 
5097     // Compute the compass points
5098     int i;
5099     for (i = 0; i < nCompassPoints; i++)
5100     {
5101         // We want rays with an origin at the eye point and tangent to the the
5102         // ellipsoid.
5103         float theta = (float) i / (float) nCompassPoints * 2 * (float) PI;
5104         Vec3f w = (float) cos(theta) * uAxis + (float) sin(theta) * vAxis;
5105         w = w * (float) centerDist;
5106 
5107         Vec3f toCenter = ellipsoidTangent(recipSemiAxes, w, e, e_, ee);
5108         compassPoints[i] = toCenter * rot;
5109     }
5110 
5111     glColor(compassColor);
5112     glBegin(GL_LINES);
5113     glDisable(GL_LIGHTING);
5114     for (i = 0; i < nCompassPoints; i++)
5115     {
5116         float distance = (center + compassPoints[i]).distanceFromOrigin();
5117 
5118         float length = distance * pixelSize * 8.0f;
5119         if (i % 4 == 0)
5120             length *= 3.0f;
5121         else if (i % 2 == 0)
5122             length *= 2.0f;
5123 
5124         glVertex(center + compassPoints[i]);
5125         glVertex(center + compassPoints[i] * (1.0f + length));
5126     }
5127     glEnd();
5128 }
5129 
5130 
setupNightTextureCombine()5131 static void setupNightTextureCombine()
5132 {
5133     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
5134     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_EXT);
5135     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_ONE_MINUS_SRC_COLOR);
5136     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
5137     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
5138     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
5139 }
5140 
5141 
setupBumpTexenv()5142 static void setupBumpTexenv()
5143 {
5144     // Set up the texenv_combine extension to do DOT3 bump mapping.
5145     // No support for ambient light yet.
5146     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
5147 
5148     // The primary color contains the light direction in surface
5149     // space, and texture0 is a normal map.  The lighting is
5150     // calculated by computing the dot product.
5151     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_DOT3_RGB_ARB);
5152     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_EXT);
5153     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
5154     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
5155     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
5156 
5157     // In the final stage, modulate the lighting value by the
5158     // base texture color.
5159     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
5160     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
5161     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
5162     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
5163     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
5164     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
5165     glEnable(GL_TEXTURE_2D);
5166 
5167     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
5168 }
5169 
5170 
5171 #if 0
5172 static void setupBumpTexenvAmbient(Color ambientColor)
5173 {
5174     float texenvConst[4];
5175     texenvConst[0] = ambientColor.red();
5176     texenvConst[1] = ambientColor.green();
5177     texenvConst[2] = ambientColor.blue();
5178     texenvConst[3] = ambientColor.alpha();
5179 
5180     // Set up the texenv_combine extension to do DOT3 bump mapping.
5181     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
5182 
5183     // The primary color contains the light direction in surface
5184     // space, and texture0 is a normal map.  The lighting is
5185     // calculated by computing the dot product.
5186     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
5187     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_DOT3_RGB_ARB);
5188     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_EXT);
5189     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
5190     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
5191     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
5192 
5193     // Add in the ambient color
5194     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
5195     glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, texenvConst);
5196     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
5197     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
5198     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
5199     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
5200     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_CONSTANT_EXT);
5201     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
5202     glEnable(GL_TEXTURE_2D);
5203 
5204     // In the final stage, modulate the lighting value by the
5205     // base texture color.
5206     glx::glActiveTextureARB(GL_TEXTURE2_ARB);
5207     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
5208     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
5209     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
5210     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
5211     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
5212     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
5213     glEnable(GL_TEXTURE_2D);
5214 
5215     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
5216 }
5217 #endif
5218 
5219 
setupTexenvAmbient(Color ambientColor)5220 static void setupTexenvAmbient(Color ambientColor)
5221 {
5222     float texenvConst[4];
5223     texenvConst[0] = ambientColor.red();
5224     texenvConst[1] = ambientColor.green();
5225     texenvConst[2] = ambientColor.blue();
5226     texenvConst[3] = ambientColor.alpha();
5227 
5228     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
5229 
5230     // The primary color contains the light direction in surface
5231     // space, and texture0 is a normal map.  The lighting is
5232     // calculated by computing the dot product.
5233     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
5234     glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, texenvConst);
5235     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
5236     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
5237     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
5238     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
5239     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_CONSTANT_EXT);
5240     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
5241     glEnable(GL_TEXTURE_2D);
5242 }
5243 
5244 
setupTexenvGlossMapAlpha()5245 static void setupTexenvGlossMapAlpha()
5246 {
5247     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
5248     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
5249     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_EXT);
5250     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
5251     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
5252     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_ALPHA);
5253 
5254 }
5255 
5256 
setLightParameters_VP(VertexProcessor & vproc,const LightingState & ls,Color materialDiffuse,Color materialSpecular)5257 static void setLightParameters_VP(VertexProcessor& vproc,
5258                                   const LightingState& ls,
5259                                   Color materialDiffuse,
5260                                   Color materialSpecular)
5261 {
5262     Vec3f diffuseColor(materialDiffuse.red(),
5263                        materialDiffuse.green(),
5264                        materialDiffuse.blue());
5265 #ifdef HDR_COMPRESS
5266     Vec3f specularColor(materialSpecular.red()   * 0.5f,
5267                         materialSpecular.green() * 0.5f,
5268                         materialSpecular.blue()  * 0.5f);
5269 #else
5270     Vec3f specularColor(materialSpecular.red(),
5271                         materialSpecular.green(),
5272                         materialSpecular.blue());
5273 #endif
5274     for (unsigned int i = 0; i < ls.nLights; i++)
5275     {
5276         const DirectionalLight& light = ls.lights[i];
5277 
5278         Vec3f lightColor = Vec3f(light.color.red(),
5279                                  light.color.green(),
5280                                  light.color.blue()) * light.irradiance;
5281         Vec3f diffuse(diffuseColor.x * lightColor.x,
5282                       diffuseColor.y * lightColor.y,
5283                       diffuseColor.z * lightColor.z);
5284         Vec3f specular(specularColor.x * lightColor.x,
5285                        specularColor.y * lightColor.y,
5286                        specularColor.z * lightColor.z);
5287 
5288         // Just handle two light sources for now
5289         if (i == 0)
5290         {
5291             vproc.parameter(vp::LightDirection0, ls.lights[0].direction_obj);
5292             vproc.parameter(vp::DiffuseColor0, diffuse);
5293             vproc.parameter(vp::SpecularColor0, specular);
5294         }
5295         else if (i == 1)
5296         {
5297             vproc.parameter(vp::LightDirection1, ls.lights[1].direction_obj);
5298             vproc.parameter(vp::DiffuseColor1, diffuse);
5299             vproc.parameter(vp::SpecularColor1, specular);
5300         }
5301     }
5302 }
5303 
5304 
renderModelDefault(Geometry * geometry,const RenderInfo & ri,bool lit,ResourceHandle texOverride)5305 static void renderModelDefault(Geometry* geometry,
5306                                const RenderInfo& ri,
5307                                bool lit,
5308                                ResourceHandle texOverride)
5309 {
5310     FixedFunctionRenderContext rc;
5311     Mesh::Material m;
5312 
5313     rc.setLighting(lit);
5314 
5315     if (ri.baseTex == NULL)
5316     {
5317         glDisable(GL_TEXTURE_2D);
5318     }
5319     else
5320     {
5321         glEnable(GL_TEXTURE_2D);
5322         ri.baseTex->bind();
5323     }
5324 
5325     glColor(ri.color);
5326 
5327     if (ri.baseTex != NULL)
5328     {
5329         m.diffuse = ri.color;
5330         m.specular = ri.specularColor;
5331         m.specularPower = ri.specularPower;
5332         m.maps[Mesh::DiffuseMap] = texOverride;
5333         rc.setMaterial(&m);
5334         rc.lock();
5335     }
5336 
5337     geometry->render(rc);
5338     if (geometry->usesTextureType(Mesh::EmissiveMap))
5339     {
5340         glDisable(GL_LIGHTING);
5341         glEnable(GL_BLEND);
5342         glBlendFunc(GL_ONE, GL_ONE);
5343         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
5344         rc.setRenderPass(RenderContext::EmissivePass);
5345         rc.setMaterial(NULL);
5346 
5347         geometry->render(rc);
5348 
5349         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
5350     }
5351 
5352     // Reset the material
5353     float black[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
5354     float zero = 0.0f;
5355     glColor4fv(black);
5356     glMaterialfv(GL_FRONT, GL_EMISSION, black);
5357     glMaterialfv(GL_FRONT, GL_SPECULAR, black);
5358     glMaterialfv(GL_FRONT, GL_SHININESS, &zero);
5359 }
5360 
5361 
renderSphereDefault(const RenderInfo & ri,const Frustum & frustum,bool lit,const GLContext & context)5362 static void renderSphereDefault(const RenderInfo& ri,
5363                                 const Frustum& frustum,
5364                                 bool lit,
5365                                 const GLContext& context)
5366 {
5367     if (lit)
5368         glEnable(GL_LIGHTING);
5369     else
5370         glDisable(GL_LIGHTING);
5371 
5372     if (ri.baseTex == NULL)
5373     {
5374         glDisable(GL_TEXTURE_2D);
5375     }
5376     else
5377     {
5378         glEnable(GL_TEXTURE_2D);
5379         ri.baseTex->bind();
5380     }
5381 
5382     glColor(ri.color);
5383 
5384     g_lodSphere->render(context,
5385                         LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
5386                         frustum, ri.pixWidth,
5387                         ri.baseTex);
5388     if (ri.nightTex != NULL && ri.useTexEnvCombine)
5389     {
5390         ri.nightTex->bind();
5391 #ifdef USE_HDR
5392 #ifdef HDR_COMPRESS
5393         Color nightColor(ri.color.red()   * 2.f,
5394                          ri.color.green() * 2.f,
5395                          ri.color.blue()  * 2.f,
5396                          ri.nightLightScale);  // Modulate brightness using alpha
5397 #else
5398         Color nightColor(ri.color.red(),
5399                          ri.color.green(),
5400                          ri.color.blue(),
5401                          ri.nightLightScale);  // Modulate brightness using alpha
5402 #endif
5403         glColor(nightColor);
5404 #endif
5405         setupNightTextureCombine();
5406         glEnable(GL_BLEND);
5407 #ifdef USE_HDR
5408         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
5409 #else
5410         glBlendFunc(GL_ONE, GL_ONE);
5411 #endif
5412         glAmbientLightColor(Color::Black); // Disable ambient light
5413         g_lodSphere->render(context,
5414                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
5415                             frustum, ri.pixWidth,
5416                             ri.nightTex);
5417         glAmbientLightColor(ri.ambientColor);
5418 #ifdef USE_HDR
5419         glColor(ri.color);
5420 #endif
5421         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
5422     }
5423 
5424     if (ri.overlayTex != NULL)
5425     {
5426         ri.overlayTex->bind();
5427         glEnable(GL_BLEND);
5428         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5429         g_lodSphere->render(context,
5430                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
5431                             frustum, ri.pixWidth,
5432                             ri.overlayTex);
5433         glBlendFunc(GL_ONE, GL_ONE);
5434     }
5435 }
5436 
5437 
5438 // DEPRECATED -- renderSphere_Combiners_VP should be used instead; only
5439 // very old drivers don't support vertex programs.
renderSphere_Combiners(const RenderInfo & ri,const Frustum & frustum,const GLContext & context)5440 static void renderSphere_Combiners(const RenderInfo& ri,
5441                                    const Frustum& frustum,
5442                                    const GLContext& context)
5443 {
5444     glDisable(GL_LIGHTING);
5445 
5446     if (ri.baseTex == NULL)
5447     {
5448         glDisable(GL_TEXTURE_2D);
5449     }
5450     else
5451     {
5452         glEnable(GL_TEXTURE_2D);
5453         ri.baseTex->bind();
5454     }
5455 
5456     glColor(ri.color * ri.sunColor);
5457 
5458     // Don't use a normal map if it's a dxt5nm map--only the GLSL path
5459     // can handle them.
5460     if (ri.bumpTex != NULL &&
5461         (ri.bumpTex->getFormatOptions() & Texture::DXT5NormalMap) == 0)
5462     {
5463         renderBumpMappedMesh(context,
5464                              *(ri.baseTex),
5465                              *(ri.bumpTex),
5466                              ri.sunDir_eye,
5467                              ri.orientation,
5468                              ri.ambientColor,
5469                              frustum,
5470                              ri.pixWidth);
5471     }
5472     else if (ri.baseTex != NULL)
5473     {
5474         renderSmoothMesh(context,
5475                          *(ri.baseTex),
5476                          ri.sunDir_eye,
5477                          ri.orientation,
5478                          ri.ambientColor,
5479                          ri.pixWidth,
5480                          frustum);
5481     }
5482     else
5483     {
5484         glEnable(GL_LIGHTING);
5485         g_lodSphere->render(context, frustum, ri.pixWidth, NULL, 0);
5486     }
5487 
5488     if (ri.nightTex != NULL)
5489     {
5490         ri.nightTex->bind();
5491         glEnable(GL_BLEND);
5492         glBlendFunc(GL_ONE, GL_ONE);
5493         renderSmoothMesh(context,
5494                          *(ri.nightTex),
5495                          ri.sunDir_eye,
5496                          ri.orientation,
5497                          Color::Black,
5498                          ri.pixWidth,
5499                          frustum,
5500                          true);
5501     }
5502 
5503     if (ri.overlayTex != NULL)
5504     {
5505         glEnable(GL_LIGHTING);
5506         ri.overlayTex->bind();
5507         glEnable(GL_BLEND);
5508         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5509         g_lodSphere->render(context,
5510                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
5511                             frustum, ri.pixWidth,
5512                             ri.overlayTex);
5513 #if 0
5514         renderSmoothMesh(context,
5515                          *(ri.overlayTex),
5516                          ri.sunDir_eye,
5517                          ri.orientation,
5518                          ri.ambientColor,
5519                          ri.pixWidth,
5520                          frustum);
5521 #endif
5522         glBlendFunc(GL_ONE, GL_ONE);
5523     }
5524 
5525     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
5526 }
5527 
5528 
renderSphere_DOT3_VP(const RenderInfo & ri,const LightingState & ls,const Frustum & frustum,const GLContext & context)5529 static void renderSphere_DOT3_VP(const RenderInfo& ri,
5530                                  const LightingState& ls,
5531                                  const Frustum& frustum,
5532                                  const GLContext& context)
5533 {
5534     VertexProcessor* vproc = context.getVertexProcessor();
5535     assert(vproc != NULL);
5536 
5537     if (ri.baseTex == NULL)
5538     {
5539         glDisable(GL_TEXTURE_2D);
5540     }
5541     else
5542     {
5543         glEnable(GL_TEXTURE_2D);
5544         ri.baseTex->bind();
5545     }
5546 
5547     vproc->enable();
5548     vproc->parameter(vp::EyePosition, ri.eyePos_obj);
5549     setLightParameters_VP(*vproc, ls, ri.color, ri.specularColor);
5550 
5551     Color ambient(ri.ambientColor * ri.color);
5552 #ifdef USE_HDR
5553     ambient = ri.ambientColor;
5554 #endif
5555     vproc->parameter(vp::AmbientColor, ambient);
5556     vproc->parameter(vp::SpecularExponent, 0.0f, 1.0f, 0.5f, ri.specularPower);
5557 
5558     // Don't use a normal map if it's a dxt5nm map--only the GLSL path
5559     // can handle them.
5560     if (ri.bumpTex != NULL &&
5561         (ri.bumpTex->getFormatOptions() & Texture::DXT5NormalMap) == 0 &&
5562         ri.baseTex != NULL)
5563     {
5564         // We don't yet handle the case where there's a bump map but no
5565         // base texture.
5566 #ifdef HDR_COMPRESS
5567         vproc->use(vp::diffuseBumpHDR);
5568 #else
5569         vproc->use(vp::diffuseBump);
5570 #endif
5571         if (ri.ambientColor != Color::Black)
5572         {
5573             // If there's ambient light, we'll need to render in two passes:
5574             // one for the ambient light, and the second for light from the star.
5575             // We could do this in a single pass using three texture stages, but
5576             // this isn't won't work with hardware that only supported two
5577             // texture stages.
5578 
5579             // Render the base texture modulated by the ambient color
5580             setupTexenvAmbient(ambient);
5581             g_lodSphere->render(context,
5582                                 LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
5583                                 frustum, ri.pixWidth,
5584                                 ri.baseTex);
5585 
5586             // Add the light from the sun
5587             glEnable(GL_BLEND);
5588             glBlendFunc(GL_ONE, GL_ONE);
5589             setupBumpTexenv();
5590             g_lodSphere->render(context,
5591                                 LODSphereMesh::Normals | LODSphereMesh::Tangents |
5592                                 LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
5593                                 frustum, ri.pixWidth,
5594                                 ri.bumpTex, ri.baseTex);
5595             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
5596             glDisable(GL_BLEND);
5597         }
5598         else
5599         {
5600             glx::glActiveTextureARB(GL_TEXTURE1_ARB);
5601             ri.baseTex->bind();
5602             glx::glActiveTextureARB(GL_TEXTURE0_ARB);
5603             ri.bumpTex->bind();
5604             setupBumpTexenv();
5605             g_lodSphere->render(context,
5606                                 LODSphereMesh::Normals | LODSphereMesh::Tangents |
5607                                 LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
5608                                 frustum, ri.pixWidth,
5609                                 ri.bumpTex, ri.baseTex);
5610             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
5611         }
5612     }
5613     else
5614     {
5615         if (ls.nLights > 1)
5616             vproc->use(vp::diffuse_2light);
5617         else
5618             vproc->use(vp::diffuse);
5619         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
5620         g_lodSphere->render(context,
5621                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
5622                             LODSphereMesh::VertexProgParams,
5623                             frustum, ri.pixWidth,
5624                             ri.baseTex);
5625     }
5626 
5627     // Render a specular pass; can't be done in one pass because
5628     // specular needs to be modulated with a gloss map.
5629     if (ri.specularColor != Color::Black)
5630     {
5631         glEnable(GL_BLEND);
5632         glBlendFunc(GL_ONE, GL_ONE);
5633         vproc->use(vp::glossMap);
5634 
5635         if (ri.glossTex != NULL)
5636             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
5637         else
5638             setupTexenvGlossMapAlpha();
5639 
5640         g_lodSphere->render(context,
5641                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
5642                             frustum, ri.pixWidth,
5643                             ri.glossTex != NULL ? ri.glossTex : ri.baseTex);
5644 
5645         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
5646         glDisable(GL_BLEND);
5647     }
5648 
5649     if (ri.nightTex != NULL)
5650     {
5651         ri.nightTex->bind();
5652 #ifdef USE_HDR
5653         // Scale night light intensity
5654 #ifdef HDR_COMPRESS
5655         Color nightColor0(ls.lights[0].color.red()  *ri.color.red()  *2.f,
5656                           ls.lights[0].color.green()*ri.color.green()*2.f,
5657                           ls.lights[0].color.blue() *ri.color.blue() *2.f,
5658                           ri.nightLightScale); // Modulate brightness with alpha
5659 #else
5660         Color nightColor0(ls.lights[0].color.red()  *ri.color.red(),
5661                           ls.lights[0].color.green()*ri.color.green(),
5662                           ls.lights[0].color.blue() *ri.color.blue(),
5663                           ri.nightLightScale); // Modulate brightness with alpha
5664 #endif
5665         vproc->parameter(vp::DiffuseColor0, nightColor0);
5666 #endif
5667         if (ls.nLights > 1)
5668         {
5669 #ifdef USE_HDR
5670 #ifdef HDR_COMPRESS
5671             Color nightColor1(ls.lights[1].color.red()  *ri.color.red()  *2.f,
5672                               ls.lights[1].color.green()*ri.color.green()*2.f,
5673                               ls.lights[1].color.blue() *ri.color.blue() *2.f,
5674                               ri.nightLightScale);
5675 #else
5676             Color nightColor1(ls.lights[1].color.red()  *ri.color.red(),
5677                               ls.lights[1].color.green()*ri.color.green(),
5678                               ls.lights[1].color.blue() *ri.color.blue(),
5679                               ri.nightLightScale);
5680 #endif
5681             vproc->parameter(vp::DiffuseColor0, nightColor1);
5682 #endif
5683 #ifdef HDR_COMPRESS
5684             vproc->use(vp::nightLights_2lightHDR);
5685 #else
5686             vproc->use(vp::nightLights_2light);
5687 #endif
5688         }
5689         else
5690         {
5691 #ifdef HDR_COMPRESS
5692             vproc->use(vp::nightLightsHDR);
5693 #else
5694             vproc->use(vp::nightLights);
5695 #endif
5696         }
5697 #ifdef USE_HDR
5698         glEnable(GL_BLEND);
5699         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
5700 #else
5701         setupNightTextureCombine();
5702         glEnable(GL_BLEND);
5703         glBlendFunc(GL_ONE, GL_ONE);
5704 #endif
5705         g_lodSphere->render(context,
5706                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
5707                             frustum, ri.pixWidth,
5708                             ri.nightTex);
5709 #ifdef USE_HDR
5710         vproc->parameter(vp::DiffuseColor0, ls.lights[0].color * ri.color);
5711         if (ls.nLights > 1)
5712             vproc->parameter(vp::DiffuseColor1, ls.lights[1].color * ri.color);
5713 #endif
5714         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
5715     }
5716 
5717     if (ri.overlayTex != NULL)
5718     {
5719         ri.overlayTex->bind();
5720         vproc->use(vp::diffuse);
5721         glEnable(GL_BLEND);
5722         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5723         g_lodSphere->render(context,
5724                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
5725                             frustum, ri.pixWidth,
5726                             ri.overlayTex);
5727         glBlendFunc(GL_ONE, GL_ONE);
5728     }
5729 
5730     vproc->disable();
5731 }
5732 
5733 
renderSphere_Combiners_VP(const RenderInfo & ri,const LightingState & ls,const Frustum & frustum,const GLContext & context)5734 static void renderSphere_Combiners_VP(const RenderInfo& ri,
5735                                       const LightingState& ls,
5736                                       const Frustum& frustum,
5737                                       const GLContext& context)
5738 {
5739     Texture* textures[4];
5740     VertexProcessor* vproc = context.getVertexProcessor();
5741     assert(vproc != NULL);
5742 
5743     if (ri.baseTex == NULL)
5744     {
5745         glDisable(GL_TEXTURE_2D);
5746     }
5747     else
5748     {
5749         glEnable(GL_TEXTURE_2D);
5750         ri.baseTex->bind();
5751     }
5752 
5753     // Set up the fog parameters if the haze density is non-zero
5754     float hazeDensity = ri.hazeColor.alpha();
5755 #ifdef HDR_COMPRESS
5756     Color hazeColor(ri.hazeColor.red()   * 0.5f,
5757                     ri.hazeColor.green() * 0.5f,
5758                     ri.hazeColor.blue()  * 0.5f,
5759                     hazeDensity);
5760 #else
5761     Color hazeColor = ri.hazeColor;
5762 #endif
5763 
5764     if (hazeDensity > 0.0f && !buggyVertexProgramEmulation)
5765     {
5766         glEnable(GL_FOG);
5767         float fogColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
5768         fogColor[0] = hazeColor.red();
5769         fogColor[1] = hazeColor.green();
5770         fogColor[2] = hazeColor.blue();
5771         glFogfv(GL_FOG_COLOR, fogColor);
5772         glFogi(GL_FOG_MODE, GL_LINEAR);
5773         glFogf(GL_FOG_START, 0.0);
5774         glFogf(GL_FOG_END, 1.0f / hazeDensity);
5775     }
5776 
5777     vproc->enable();
5778 
5779     vproc->parameter(vp::EyePosition, ri.eyePos_obj);
5780     setLightParameters_VP(*vproc, ls, ri.color, ri.specularColor);
5781 
5782     vproc->parameter(vp::SpecularExponent, 0.0f, 1.0f, 0.5f, ri.specularPower);
5783     vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
5784     vproc->parameter(vp::HazeColor, hazeColor);
5785 
5786     // Don't use a normal map if it's a dxt5nm map--only the GLSL path
5787     // can handle them.
5788     if (ri.bumpTex != NULL &&
5789         (ri.bumpTex->getFormatOptions() & Texture::DXT5NormalMap) == 0)
5790     {
5791         if (hazeDensity > 0.0f)
5792         {
5793 #ifdef HDR_COMPRESS
5794             vproc->use(vp::diffuseBumpHazeHDR);
5795 #else
5796             vproc->use(vp::diffuseBumpHaze);
5797 #endif
5798         }
5799         else
5800         {
5801 #ifdef HDR_COMPRESS
5802             vproc->use(vp::diffuseBumpHDR);
5803 #else
5804             vproc->use(vp::diffuseBump);
5805 #endif
5806         }
5807         SetupCombinersDecalAndBumpMap(*(ri.bumpTex),
5808                                       ri.ambientColor * ri.color,
5809                                       ri.sunColor * ri.color);
5810         g_lodSphere->render(context,
5811                             LODSphereMesh::Normals | LODSphereMesh::Tangents |
5812                             LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
5813                             frustum, ri.pixWidth,
5814                             ri.baseTex, ri.bumpTex);
5815         DisableCombiners();
5816 
5817         // Render a specular pass
5818         if (ri.specularColor != Color::Black)
5819         {
5820             glEnable(GL_BLEND);
5821             glBlendFunc(GL_ONE, GL_ONE);
5822             glEnable(GL_COLOR_SUM_EXT);
5823             vproc->use(vp::specular);
5824 
5825             // Disable ambient and diffuse
5826             vproc->parameter(vp::AmbientColor, Color::Black);
5827             vproc->parameter(vp::DiffuseColor0, Color::Black);
5828             SetupCombinersGlossMap(ri.glossTex != NULL ? GL_TEXTURE0_ARB : 0);
5829 
5830             textures[0] = ri.glossTex != NULL ? ri.glossTex : ri.baseTex;
5831             g_lodSphere->render(context,
5832                                 LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
5833                                 frustum, ri.pixWidth,
5834                                 textures, 1);
5835 
5836             // re-enable diffuse
5837             vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
5838 
5839             DisableCombiners();
5840             glDisable(GL_COLOR_SUM_EXT);
5841             glDisable(GL_BLEND);
5842         }
5843     }
5844     else if (ri.specularColor != Color::Black)
5845     {
5846         glEnable(GL_COLOR_SUM_EXT);
5847         if (ls.nLights > 1)
5848             vproc->use(vp::specular_2light);
5849         else
5850             vproc->use(vp::specular);
5851         SetupCombinersGlossMapWithFog(ri.glossTex != NULL ? GL_TEXTURE1_ARB : 0);
5852         unsigned int attributes = LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
5853             LODSphereMesh::VertexProgParams;
5854         g_lodSphere->render(context,
5855                             attributes, frustum, ri.pixWidth,
5856                             ri.baseTex, ri.glossTex);
5857         DisableCombiners();
5858         glDisable(GL_COLOR_SUM_EXT);
5859     }
5860     else
5861     {
5862         if (ls.nLights > 1)
5863         {
5864             if (hazeDensity > 0.0f)
5865                 vproc->use(vp::diffuseHaze_2light);
5866             else
5867                 vproc->use(vp::diffuse_2light);
5868         }
5869         else
5870         {
5871             if (hazeDensity > 0.0f)
5872                 vproc->use(vp::diffuseHaze);
5873             else
5874                 vproc->use(vp::diffuse);
5875         }
5876 
5877         g_lodSphere->render(context,
5878                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
5879                             LODSphereMesh::VertexProgParams,
5880                             frustum, ri.pixWidth,
5881                             ri.baseTex);
5882     }
5883 
5884     if (hazeDensity > 0.0f)
5885         glDisable(GL_FOG);
5886 
5887     if (ri.nightTex != NULL)
5888     {
5889         ri.nightTex->bind();
5890 #ifdef USE_HDR
5891         // Scale night light intensity
5892 #ifdef HDR_COMPRESS
5893         Color nightColor0(ls.lights[0].color.red()  *ri.color.red()  *2.f,
5894                           ls.lights[0].color.green()*ri.color.green()*2.f,
5895                           ls.lights[0].color.blue() *ri.color.blue() *2.f,
5896                           ri.nightLightScale); // Modulate brightness with alpha
5897 #else
5898         Color nightColor0(ls.lights[0].color.red()  *ri.color.red(),
5899                           ls.lights[0].color.green()*ri.color.green(),
5900                           ls.lights[0].color.blue() *ri.color.blue(),
5901                           ri.nightLightScale); // Modulate brightness with alpha
5902 #endif
5903         vproc->parameter(vp::DiffuseColor0, nightColor0);
5904 #endif
5905         if (ls.nLights > 1)
5906         {
5907 #ifdef USE_HDR
5908 #ifdef HDR_COMPRESS
5909             Color nightColor1(ls.lights[1].color.red()  *ri.color.red()  *2.f,
5910                               ls.lights[1].color.green()*ri.color.green()*2.f,
5911                               ls.lights[1].color.blue() *ri.color.blue() *2.f,
5912                               ri.nightLightScale);
5913 #else
5914             Color nightColor1(ls.lights[1].color.red()  *ri.color.red(),
5915                               ls.lights[1].color.green()*ri.color.green(),
5916                               ls.lights[1].color.blue() *ri.color.blue(),
5917                               ri.nightLightScale);
5918 #endif
5919             vproc->parameter(vp::DiffuseColor0, nightColor1);
5920 #endif
5921 #ifdef HDR_COMPRESS
5922             vproc->use(vp::nightLights_2lightHDR);
5923 #else
5924             vproc->use(vp::nightLights_2light);
5925 #endif
5926         }
5927         else
5928         {
5929 #ifdef HDR_COMPRESS
5930             vproc->use(vp::nightLightsHDR);
5931 #else
5932             vproc->use(vp::nightLights);
5933 #endif
5934         }
5935 #ifdef USE_HDR
5936         glEnable(GL_BLEND);
5937         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
5938 #else
5939         setupNightTextureCombine();
5940         glEnable(GL_BLEND);
5941         glBlendFunc(GL_ONE, GL_ONE);
5942 #endif
5943         g_lodSphere->render(context,
5944                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
5945                             frustum, ri.pixWidth,
5946                             ri.nightTex);
5947 #ifdef USE_HDR
5948         vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
5949 #endif
5950         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
5951     }
5952 
5953     if (ri.overlayTex != NULL)
5954     {
5955         ri.overlayTex->bind();
5956         vproc->use(vp::diffuse);
5957         glEnable(GL_BLEND);
5958         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5959         g_lodSphere->render(context,
5960                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
5961                             frustum, ri.pixWidth,
5962                             ri.overlayTex);
5963         glBlendFunc(GL_ONE, GL_ONE);
5964     }
5965 
5966     vproc->disable();
5967 }
5968 
5969 
5970 // Render a planet sphere using both fragment and vertex programs
renderSphere_FP_VP(const RenderInfo & ri,const Frustum & frustum,const GLContext & context)5971 static void renderSphere_FP_VP(const RenderInfo& ri,
5972                                const Frustum& frustum,
5973                                const GLContext& context)
5974 {
5975     Texture* textures[4];
5976     VertexProcessor* vproc = context.getVertexProcessor();
5977     FragmentProcessor* fproc = context.getFragmentProcessor();
5978     assert(vproc != NULL && fproc != NULL);
5979 
5980     if (ri.baseTex == NULL)
5981     {
5982         glDisable(GL_TEXTURE_2D);
5983     }
5984     else
5985     {
5986         glEnable(GL_TEXTURE_2D);
5987         ri.baseTex->bind();
5988     }
5989 
5990     // Compute the half angle vector required for specular lighting
5991     Vec3f halfAngle_obj = ri.eyeDir_obj + ri.sunDir_obj;
5992     if (halfAngle_obj.length() != 0.0f)
5993         halfAngle_obj.normalize();
5994 
5995     // Set up the fog parameters if the haze density is non-zero
5996     float hazeDensity = ri.hazeColor.alpha();
5997 
5998     if (hazeDensity > 0.0f)
5999     {
6000         glEnable(GL_FOG);
6001         float fogColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
6002         fogColor[0] = ri.hazeColor.red();
6003         fogColor[1] = ri.hazeColor.green();
6004         fogColor[2] = ri.hazeColor.blue();
6005         glFogfv(GL_FOG_COLOR, fogColor);
6006         glFogi(GL_FOG_MODE, GL_LINEAR);
6007         glFogf(GL_FOG_START, 0.0);
6008         glFogf(GL_FOG_END, 1.0f / hazeDensity);
6009     }
6010 
6011     vproc->enable();
6012 
6013     vproc->parameter(vp::EyePosition, ri.eyePos_obj);
6014     vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
6015     vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
6016     vproc->parameter(vp::SpecularExponent, 0.0f, 1.0f, 0.5f, ri.specularPower);
6017     vproc->parameter(vp::SpecularColor0, ri.sunColor * ri.specularColor);
6018     vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
6019     vproc->parameter(vp::HazeColor, ri.hazeColor);
6020 
6021     if (ri.bumpTex != NULL)
6022     {
6023         fproc->enable();
6024 
6025         if (hazeDensity > 0.0f)
6026             vproc->use(vp::diffuseBumpHaze);
6027         else
6028             vproc->use(vp::diffuseBump);
6029         fproc->use(fp::texDiffuseBump);
6030         g_lodSphere->render(context,
6031                             LODSphereMesh::Normals | LODSphereMesh::Tangents |
6032                             LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
6033                             frustum, ri.pixWidth,
6034                             ri.baseTex, ri.bumpTex);
6035         fproc->disable();
6036 
6037         // Render a specular pass
6038         if (ri.specularColor != Color::Black)
6039         {
6040             glEnable(GL_BLEND);
6041             glBlendFunc(GL_ONE, GL_ONE);
6042             glEnable(GL_COLOR_SUM_EXT);
6043             vproc->use(vp::specular);
6044 
6045             // Disable ambient and diffuse
6046             vproc->parameter(vp::AmbientColor, Color::Black);
6047             vproc->parameter(vp::DiffuseColor0, Color::Black);
6048             SetupCombinersGlossMap(ri.glossTex != NULL ? GL_TEXTURE0_ARB : 0);
6049 
6050             textures[0] = ri.glossTex != NULL ? ri.glossTex : ri.baseTex;
6051             g_lodSphere->render(context,
6052                                 LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
6053                                 frustum, ri.pixWidth,
6054                                 textures, 1);
6055 
6056             // re-enable diffuse
6057             vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
6058 
6059             DisableCombiners();
6060             glDisable(GL_COLOR_SUM_EXT);
6061             glDisable(GL_BLEND);
6062         }
6063     }
6064     else if (ri.specularColor != Color::Black)
6065     {
6066         fproc->enable();
6067         if (ri.glossTex == NULL)
6068         {
6069             vproc->use(vp::perFragmentSpecularAlpha);
6070             fproc->use(fp::texSpecularAlpha);
6071         }
6072         else
6073         {
6074             vproc->use(vp::perFragmentSpecular);
6075             fproc->use(fp::texSpecular);
6076         }
6077         fproc->parameter(fp::DiffuseColor, ri.sunColor * ri.color);
6078         fproc->parameter(fp::SunDirection, ri.sunDir_obj);
6079         fproc->parameter(fp::SpecularColor, ri.specularColor);
6080         fproc->parameter(fp::SpecularExponent, ri.specularPower, 0.0f, 0.0f, 0.0f);
6081         fproc->parameter(fp::AmbientColor, ri.ambientColor);
6082 
6083         unsigned int attributes = LODSphereMesh::Normals |
6084                                   LODSphereMesh::TexCoords0 |
6085                                   LODSphereMesh::VertexProgParams;
6086         g_lodSphere->render(context,
6087                             attributes, frustum, ri.pixWidth,
6088                             ri.baseTex, ri.glossTex);
6089         fproc->disable();
6090     }
6091     else
6092     {
6093         fproc->enable();
6094         if (hazeDensity > 0.0f)
6095             vproc->use(vp::diffuseHaze);
6096         else
6097             vproc->use(vp::diffuse);
6098         fproc->use(fp::texDiffuse);
6099         g_lodSphere->render(context,
6100                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
6101                             LODSphereMesh::VertexProgParams,
6102                             frustum, ri.pixWidth,
6103                             ri.baseTex);
6104         fproc->disable();
6105     }
6106 
6107     if (hazeDensity > 0.0f)
6108         glDisable(GL_FOG);
6109 
6110     if (ri.nightTex != NULL)
6111     {
6112         ri.nightTex->bind();
6113         vproc->use(vp::nightLights);
6114         setupNightTextureCombine();
6115         glEnable(GL_BLEND);
6116         glBlendFunc(GL_ONE, GL_ONE);
6117         g_lodSphere->render(context,
6118                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
6119                             frustum, ri.pixWidth,
6120                             ri.nightTex);
6121         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
6122     }
6123 
6124     if (ri.overlayTex != NULL)
6125     {
6126         ri.overlayTex->bind();
6127         vproc->use(vp::diffuse);
6128         glEnable(GL_BLEND);
6129         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
6130         g_lodSphere->render(context,
6131                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
6132                             frustum, ri.pixWidth,
6133                             ri.overlayTex);
6134         glBlendFunc(GL_ONE, GL_ONE);
6135     }
6136 
6137     vproc->disable();
6138 }
6139 
6140 
texGenPlane(GLenum coord,GLenum mode,const Vec4f & plane)6141 static void texGenPlane(GLenum coord, GLenum mode, const Vec4f& plane)
6142 {
6143     float f[4];
6144     f[0] = plane.x; f[1] = plane.y; f[2] = plane.z; f[3] = plane.w;
6145     glTexGenfv(coord, mode, f);
6146 }
6147 
renderShadowedGeometryDefault(Geometry * geometry,const RenderInfo & ri,const Frustum & frustum,float * sPlane,float * tPlane,const Vec3f & lightDir,bool useShadowMask,const GLContext & context)6148 static void renderShadowedGeometryDefault(Geometry* geometry,
6149                                        const RenderInfo& ri,
6150                                        const Frustum& frustum,
6151                                        float *sPlane,
6152                                        float *tPlane,
6153                                        const Vec3f& lightDir,
6154                                        bool useShadowMask,
6155                                        const GLContext& context)
6156 {
6157     glEnable(GL_TEXTURE_GEN_S);
6158     glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
6159     glTexGenfv(GL_S, GL_OBJECT_PLANE, sPlane);
6160     //texGenPlane(GL_S, GL_OBJECT_PLANE, sPlane);
6161     glEnable(GL_TEXTURE_GEN_T);
6162     glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
6163     glTexGenfv(GL_T, GL_OBJECT_PLANE, tPlane);
6164 
6165     if (useShadowMask)
6166     {
6167         glx::glActiveTextureARB(GL_TEXTURE1_ARB);
6168         glEnable(GL_TEXTURE_GEN_S);
6169         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
6170         texGenPlane(GL_S, GL_OBJECT_PLANE,
6171                     Vec4f(lightDir.x, lightDir.y, lightDir.z, 0.5f));
6172         glx::glActiveTextureARB(GL_TEXTURE0_ARB);
6173     }
6174 
6175     glColor4f(1, 1, 1, 1);
6176     glDisable(GL_LIGHTING);
6177 
6178     if (geometry == NULL)
6179     {
6180         g_lodSphere->render(context,
6181                             LODSphereMesh::Normals | LODSphereMesh::Multipass,
6182                             frustum, ri.pixWidth, NULL);
6183     }
6184     else
6185     {
6186         FixedFunctionRenderContext rc;
6187         geometry->render(rc);
6188     }
6189     glEnable(GL_LIGHTING);
6190 
6191     if (useShadowMask)
6192     {
6193         glx::glActiveTextureARB(GL_TEXTURE1_ARB);
6194         glDisable(GL_TEXTURE_GEN_S);
6195         glx::glActiveTextureARB(GL_TEXTURE0_ARB);
6196     }
6197     glDisable(GL_TEXTURE_GEN_S);
6198     glDisable(GL_TEXTURE_GEN_T);
6199 }
6200 
6201 
renderShadowedGeometryVertexShader(const RenderInfo & ri,const Frustum & frustum,float * sPlane,float * tPlane,Vec3f & lightDir,const GLContext & context)6202 static void renderShadowedGeometryVertexShader(const RenderInfo& ri,
6203                                             const Frustum& frustum,
6204                                             float* sPlane, float* tPlane,
6205                                             Vec3f& lightDir,
6206                                             const GLContext& context)
6207 {
6208     VertexProcessor* vproc = context.getVertexProcessor();
6209     assert(vproc != NULL);
6210 
6211     vproc->enable();
6212     vproc->parameter(vp::LightDirection0, lightDir);
6213     vproc->parameter(vp::TexGen_S, sPlane);
6214     vproc->parameter(vp::TexGen_T, tPlane);
6215     vproc->use(vp::shadowTexture);
6216 
6217     g_lodSphere->render(context,
6218                         LODSphereMesh::Normals | LODSphereMesh::Multipass, frustum,
6219                         ri.pixWidth, NULL);
6220 
6221     vproc->disable();
6222 }
6223 
6224 
renderRings(RingSystem & rings,RenderInfo & ri,float planetRadius,float planetOblateness,unsigned int textureResolution,bool renderShadow,const GLContext & context,unsigned int nSections)6225 static void renderRings(RingSystem& rings,
6226                         RenderInfo& ri,
6227                         float planetRadius,
6228                         float planetOblateness,
6229                         unsigned int textureResolution,
6230                         bool renderShadow,
6231                         const GLContext& context,
6232                         unsigned int nSections)
6233 {
6234     float inner = rings.innerRadius / planetRadius;
6235     float outer = rings.outerRadius / planetRadius;
6236 
6237     // Ring Illumination:
6238     // Since a ring system is composed of millions of individual
6239     // particles, it's not at all realistic to model it as a flat
6240     // Lambertian surface.  We'll approximate the llumination
6241     // function by assuming that the ring system contains Lambertian
6242     // particles, and that the brightness at some point in the ring
6243     // system is proportional to the illuminated fraction of a
6244     // particle there.  In fact, we'll simplify things further and
6245     // set the illumination of the entire ring system to the same
6246     // value, computing the illuminated fraction of a hypothetical
6247     // particle located at the center of the planet.  This
6248     // approximation breaks down when you get close to the planet.
6249     float ringIllumination = 0.0f;
6250     {
6251         float illumFraction = (1.0f + ri.eyeDir_obj * ri.sunDir_obj) / 2.0f;
6252         // Just use the illuminated fraction for now . . .
6253         ringIllumination = illumFraction;
6254     }
6255 
6256     GLContext::VertexPath vpath = context.getVertexPath();
6257     VertexProcessor* vproc = context.getVertexProcessor();
6258     FragmentProcessor* fproc = context.getFragmentProcessor();
6259 
6260     if (vproc != NULL)
6261     {
6262         vproc->enable();
6263         vproc->use(vp::ringIllum);
6264         vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
6265         vproc->parameter(vp::DiffuseColor0, ri.sunColor * rings.color);
6266         vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
6267         vproc->parameter(vp::Constant0, Vec3f(0, 0.5, 1.0));
6268     }
6269 
6270     // If we have multi-texture support, we'll use the second texture unit
6271     // to render the shadow of the planet on the rings.  This is a bit of
6272     // a hack, and assumes that the planet is ellipsoidal in shape,
6273     // and only works for a planet illuminated by a single sun where the
6274     // distance to the sun is very large relative to its diameter.
6275     if (renderShadow)
6276     {
6277         glx::glActiveTextureARB(GL_TEXTURE1_ARB);
6278         glEnable(GL_TEXTURE_2D);
6279         shadowTex->bind();
6280 
6281         float sPlane[4] = { 0, 0, 0, 0.5f };
6282         float tPlane[4] = { 0, 0, 0, 0.5f };
6283 
6284         // Compute the projection vectors based on the sun direction.
6285         // I'm being a little careless here--if the sun direction lies
6286         // along the y-axis, this will fail.  It's unlikely that a
6287         // planet would ever orbit underneath its sun (an orbital
6288         // inclination of 90 degrees), but this should be made
6289         // more robust anyway.
6290         Vec3f axis = Vec3f(0, 1, 0) ^ ri.sunDir_obj;
6291         float cosAngle = Vec3f(0.0f, 1.0f, 0.0f) * ri.sunDir_obj;
6292         /*float angle = (float) acos(cosAngle);     Unused*/
6293         axis.normalize();
6294 
6295         float sScale = 1.0f;
6296         float tScale = 1.0f;
6297         if (fproc == NULL)
6298         {
6299             // When fragment programs aren't used, we render shadows with circular
6300             // textures.  We scale up the texture slightly to account for the
6301             // padding pixels near the texture borders.
6302             sScale *= ShadowTextureScale;
6303             tScale *= ShadowTextureScale;
6304         }
6305 
6306         if (planetOblateness != 0.0f)
6307         {
6308             // For oblate planets, the size of the shadow volume will vary based
6309             // on the light direction.
6310 
6311             // A vertical slice of the planet is an ellipse
6312             float a = 1.0f;                          // semimajor axis
6313             float b = a * (1.0f - planetOblateness); // semiminor axis
6314             float ecc2 = 1.0f - (b * b) / (a * a);   // square of eccentricity
6315 
6316             // Calculate the radius of the ellipse at the incident angle of the
6317             // light on the ring plane + 90 degrees.
6318             float r = a * (float) sqrt((1.0f - ecc2) /
6319                                        (1.0f - ecc2 * square(cosAngle)));
6320 
6321             tScale *= a / r;
6322         }
6323 
6324         // The s axis is perpendicular to the shadow axis in the plane of the
6325         // of the rings, and the t axis completes the orthonormal basis.
6326         Vec3f sAxis = axis * 0.5f;
6327         Vec3f tAxis = (axis ^ ri.sunDir_obj) * 0.5f * tScale;
6328 
6329         sPlane[0] = sAxis.x; sPlane[1] = sAxis.y; sPlane[2] = sAxis.z;
6330         tPlane[0] = tAxis.x; tPlane[1] = tAxis.y; tPlane[2] = tAxis.z;
6331 
6332         if (vproc != NULL)
6333         {
6334             vproc->parameter(vp::TexGen_S, sPlane);
6335             vproc->parameter(vp::TexGen_T, tPlane);
6336         }
6337         else
6338         {
6339             glEnable(GL_TEXTURE_GEN_S);
6340             glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
6341             glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
6342             glEnable(GL_TEXTURE_GEN_T);
6343             glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
6344             glTexGenfv(GL_T, GL_EYE_PLANE, tPlane);
6345         }
6346 
6347         glx::glActiveTextureARB(GL_TEXTURE0_ARB);
6348 
6349         if (fproc != NULL)
6350         {
6351             float r0 = 0.24f;
6352             float r1 = 0.25f;
6353             float bias = 1.0f / (1.0f - r1 / r0);
6354             float scale = -bias / r0;
6355 
6356             fproc->enable();
6357             fproc->use(fp::sphereShadowOnRings);
6358             fproc->parameter(fp::ShadowParams0, scale, bias, 0.0f, 0.0f);
6359             fproc->parameter(fp::AmbientColor, ri.ambientColor * ri.color);
6360         }
6361     }
6362 
6363     glEnable(GL_BLEND);
6364     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
6365 
6366     Texture* ringsTex = rings.texture.find(textureResolution);
6367 
6368     if (ringsTex != NULL)
6369     {
6370         glEnable(GL_TEXTURE_2D);
6371         ringsTex->bind();
6372     }
6373     else
6374     {
6375         glDisable(GL_TEXTURE_2D);
6376     }
6377 
6378     // Perform our own lighting for the rings.
6379     // TODO: Don't forget about light source color (required when we
6380     // paying attention to star color.)
6381     if (vpath == GLContext::VPath_Basic)
6382     {
6383         glDisable(GL_LIGHTING);
6384         Vec3f litColor(rings.color.red(), rings.color.green(), rings.color.blue());
6385         litColor = litColor * ringIllumination +
6386             Vec3f(ri.ambientColor.red(), ri.ambientColor.green(),
6387                   ri.ambientColor.blue());
6388         glColor4f(litColor.x, litColor.y, litColor.z, 1.0f);
6389     }
6390 
6391     // This gets tricky . . .  we render the rings in two parts.  One
6392     // part is potentially shadowed by the planet, and we need to
6393     // render that part with the projected shadow texture enabled.
6394     // The other part isn't shadowed, but will appear so if we don't
6395     // first disable the shadow texture.  The problem is that the
6396     // shadow texture will affect anything along the line between the
6397     // sun and the planet, regardless of whether it's in front or
6398     // behind the planet.
6399 
6400     // Compute the angle of the sun projected on the ring plane
6401     float sunAngle = (float) atan2(ri.sunDir_obj.z, ri.sunDir_obj.x);
6402 
6403     // If there's a fragment program, it will handle the ambient term--make
6404     // sure that we don't add it both in the fragment and vertex programs.
6405     if (vproc != NULL && fproc != NULL)
6406         glAmbientLightColor(Color::Black);
6407 
6408     renderRingSystem(inner, outer,
6409                      (float) (sunAngle + PI / 2),
6410                      (float) (sunAngle + 3 * PI / 2),
6411                      nSections / 2);
6412     renderRingSystem(inner, outer,
6413                      (float) (sunAngle +  3 * PI / 2),
6414                      (float) (sunAngle + PI / 2),
6415                      nSections / 2);
6416 
6417     if (vproc != NULL && fproc != NULL)
6418         glAmbientLightColor(ri.ambientColor * ri.color);
6419 
6420     // Disable the second texture unit if it was used
6421     if (renderShadow)
6422     {
6423         glx::glActiveTextureARB(GL_TEXTURE1_ARB);
6424         glDisable(GL_TEXTURE_2D);
6425         glDisable(GL_TEXTURE_GEN_S);
6426         glDisable(GL_TEXTURE_GEN_T);
6427         glx::glActiveTextureARB(GL_TEXTURE0_ARB);
6428 
6429         if (fproc != NULL)
6430             fproc->disable();
6431     }
6432 
6433     // Render the unshadowed side
6434     renderRingSystem(inner, outer,
6435                      (float) (sunAngle - PI / 2),
6436                      (float) (sunAngle + PI / 2),
6437                      nSections / 2);
6438     renderRingSystem(inner, outer,
6439                      (float) (sunAngle + PI / 2),
6440                      (float) (sunAngle - PI / 2),
6441                      nSections / 2);
6442     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
6443 
6444     if (vproc != NULL)
6445         vproc->disable();
6446 }
6447 
6448 
6449 static void
renderEclipseShadows(Geometry * geometry,vector<EclipseShadow> & eclipseShadows,RenderInfo & ri,float planetRadius,Mat4f & planetMat,Frustum & viewFrustum,const GLContext & context)6450 renderEclipseShadows(Geometry* geometry,
6451                      vector<EclipseShadow>& eclipseShadows,
6452                      RenderInfo& ri,
6453                      float planetRadius,
6454                      Mat4f& planetMat,
6455                      Frustum& viewFrustum,
6456                      const GLContext& context)
6457 {
6458     // Eclipse shadows on mesh objects aren't working yet.
6459     if (geometry != NULL)
6460         return;
6461 
6462     for (vector<EclipseShadow>::iterator iter = eclipseShadows.begin();
6463          iter != eclipseShadows.end(); iter++)
6464     {
6465         EclipseShadow shadow = *iter;
6466 
6467 #ifdef DEBUG_ECLIPSE_SHADOWS
6468         // Eclipse debugging: render the central axis of the eclipse
6469         // shadow volume.
6470         glDisable(GL_TEXTURE_2D);
6471         glColor4f(1, 0, 0, 1);
6472         Point3f blorp = shadow.origin * planetMat;
6473         Vec3f blah = shadow.direction * planetMat;
6474         blorp.x /= planetRadius; blorp.y /= planetRadius; blorp.z /= planetRadius;
6475         float foo = blorp.distanceFromOrigin();
6476         glBegin(GL_LINES);
6477         glVertex(blorp);
6478         glVertex(blorp + foo * blah);
6479         glEnd();
6480         glEnable(GL_TEXTURE_2D);
6481 #endif
6482 
6483         // Determine which eclipse shadow texture to use.  This is only
6484         // a very rough approximation to reality.  Since there are an
6485         // infinite number of possible eclipse volumes, what we should be
6486         // doing is generating the eclipse textures on the fly using
6487         // render-to-texture.  But for now, we'll just choose from a fixed
6488         // set of eclipse shadow textures based on the relative size of
6489         // the umbra and penumbra.
6490         Texture* eclipseTex = NULL;
6491         float umbra = shadow.umbraRadius / shadow.penumbraRadius;
6492         if (umbra < 0.1f)
6493             eclipseTex = eclipseShadowTextures[0];
6494         else if (umbra < 0.35f)
6495             eclipseTex = eclipseShadowTextures[1];
6496         else if (umbra < 0.6f)
6497             eclipseTex = eclipseShadowTextures[2];
6498         else if (umbra < 0.9f)
6499             eclipseTex = eclipseShadowTextures[3];
6500         else
6501             eclipseTex = shadowTex;
6502 
6503         // Compute the transformation to use for generating texture
6504         // coordinates from the object vertices.
6505         Point3f origin = shadow.origin * planetMat;
6506         Vec3f dir = shadow.direction * planetMat;
6507         float scale = planetRadius / shadow.penumbraRadius;
6508         Vec3f axis = Vec3f(0, 1, 0) ^ dir;
6509         float angle = (float) acos(Vec3f(0, 1, 0) * dir);
6510         axis.normalize();
6511         Mat4f mat = Mat4f::rotation(axis, -angle);
6512         Vec3f sAxis = Vec3f(0.5f * scale, 0, 0) * mat;
6513         Vec3f tAxis = Vec3f(0, 0, 0.5f * scale) * mat;
6514 
6515         float sPlane[4] = { 0, 0, 0, 0 };
6516         float tPlane[4] = { 0, 0, 0, 0 };
6517         sPlane[0] = sAxis.x; sPlane[1] = sAxis.y; sPlane[2] = sAxis.z;
6518         tPlane[0] = tAxis.x; tPlane[1] = tAxis.y; tPlane[2] = tAxis.z;
6519         sPlane[3] = (Point3f(0, 0, 0) - origin) * sAxis / planetRadius + 0.5f;
6520         tPlane[3] = (Point3f(0, 0, 0) - origin) * tAxis / planetRadius + 0.5f;
6521 
6522         // TODO: Multiple eclipse shadows should be rendered in a single
6523         // pass using multitexture.
6524         if (eclipseTex != NULL)
6525             eclipseTex->bind();
6526         // shadowMaskTexture->bind();
6527         glEnable(GL_BLEND);
6528         glBlendFunc(GL_ZERO, GL_SRC_COLOR);
6529 
6530         // If the ambient light level is greater than zero, reduce the
6531         // darkness of the shadows.
6532         if (ri.useTexEnvCombine)
6533         {
6534             float color[4] = { ri.ambientColor.red(), ri.ambientColor.green(),
6535                                ri.ambientColor.blue(), 1.0f };
6536             glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
6537             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
6538             glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_CONSTANT_EXT);
6539             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
6540             glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
6541             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
6542             glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
6543 
6544             // The second texture unit has the shadow 'mask'
6545             glx::glActiveTextureARB(GL_TEXTURE1_ARB);
6546             glEnable(GL_TEXTURE_2D);
6547             shadowMaskTexture->bind();
6548             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
6549             glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
6550             glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
6551             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
6552             glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
6553             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
6554             glx::glActiveTextureARB(GL_TEXTURE0_ARB);
6555         }
6556 
6557         // Since invariance between nVidia's vertex programs and the
6558         // standard transformation pipeline isn't guaranteed, we have to
6559         // make sure to use the same transformation engine on subsequent
6560         // rendering passes as we did on the initial one.
6561         if (context.getVertexPath() != GLContext::VPath_Basic && geometry == NULL)
6562         {
6563             renderShadowedGeometryVertexShader(ri, viewFrustum,
6564                                            sPlane, tPlane,
6565                                            dir,
6566                                            context);
6567         }
6568         else
6569         {
6570             renderShadowedGeometryDefault(geometry, ri, viewFrustum,
6571                                        sPlane, tPlane,
6572                                        dir,
6573                                        ri.useTexEnvCombine,
6574                                        context);
6575         }
6576 
6577         if (ri.useTexEnvCombine)
6578         {
6579             // Disable second texture unit
6580             glx::glActiveTextureARB(GL_TEXTURE1_ARB);
6581             glDisable(GL_TEXTURE_2D);
6582             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
6583             glx::glActiveTextureARB(GL_TEXTURE0_ARB);
6584 
6585             float color[4] = { 0, 0, 0, 0 };
6586             glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
6587             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
6588         }
6589 
6590         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
6591         glDisable(GL_BLEND);
6592     }
6593 }
6594 
6595 
6596 static void
renderEclipseShadows_Shaders(Geometry * geometry,vector<EclipseShadow> & eclipseShadows,RenderInfo & ri,float planetRadius,Mat4f & planetMat,Frustum & viewFrustum,const GLContext & context)6597 renderEclipseShadows_Shaders(Geometry* geometry,
6598                              vector<EclipseShadow>& eclipseShadows,
6599                              RenderInfo& ri,
6600                              float planetRadius,
6601                              Mat4f& planetMat,
6602                              Frustum& viewFrustum,
6603                              const GLContext& context)
6604 {
6605     // Eclipse shadows on mesh objects aren't working yet.
6606     if (geometry != NULL)
6607         return;
6608 
6609     glEnable(GL_TEXTURE_2D);
6610     penumbraFunctionTexture->bind();
6611 
6612     glEnable(GL_BLEND);
6613     glBlendFunc(GL_ZERO, GL_SRC_COLOR);
6614 
6615     float sPlanes[4][4];
6616     float tPlanes[4][4];
6617     float shadowParams[4][4];
6618 
6619     int n = 0;
6620     for (vector<EclipseShadow>::iterator iter = eclipseShadows.begin();
6621          iter != eclipseShadows.end() && n < 4; iter++, n++)
6622     {
6623         EclipseShadow shadow = *iter;
6624 
6625         float R2 = 0.25f;
6626         float umbra = shadow.umbraRadius / shadow.penumbraRadius;
6627         umbra = umbra * umbra;
6628         if (umbra < 0.0001f)
6629             umbra = 0.0001f;
6630         else if (umbra > 0.99f)
6631             umbra = 0.99f;
6632 
6633         float umbraRadius = R2 * umbra;
6634         float penumbraRadius = R2;
6635         float shadowBias = 1.0f / (1.0f - penumbraRadius / umbraRadius);
6636         float shadowScale = -shadowBias / umbraRadius;
6637 
6638         shadowParams[n][0] = shadowScale;
6639         shadowParams[n][1] = shadowBias;
6640         shadowParams[n][2] = 0.0f;
6641         shadowParams[n][3] = 0.0f;
6642 
6643         // Compute the transformation to use for generating texture
6644         // coordinates from the object vertices.
6645         Point3f origin = shadow.origin * planetMat;
6646         Vec3f dir = shadow.direction * planetMat;
6647         float scale = planetRadius / shadow.penumbraRadius;
6648         Vec3f axis = Vec3f(0, 1, 0) ^ dir;
6649         float angle = (float) acos(Vec3f(0, 1, 0) * dir);
6650         axis.normalize();
6651         Mat4f mat = Mat4f::rotation(axis, -angle);
6652         Vec3f sAxis = Vec3f(0.5f * scale, 0, 0) * mat;
6653         Vec3f tAxis = Vec3f(0, 0, 0.5f * scale) * mat;
6654 
6655         sPlanes[n][0] = sAxis.x;
6656         sPlanes[n][1] = sAxis.y;
6657         sPlanes[n][2] = sAxis.z;
6658         sPlanes[n][3] = (Point3f(0, 0, 0) - origin) * sAxis / planetRadius + 0.5f;
6659         tPlanes[n][0] = tAxis.x;
6660         tPlanes[n][1] = tAxis.y;
6661         tPlanes[n][2] = tAxis.z;
6662         tPlanes[n][3] = (Point3f(0, 0, 0) - origin) * tAxis / planetRadius + 0.5f;
6663     }
6664 
6665 
6666     VertexProcessor* vproc = context.getVertexProcessor();
6667     FragmentProcessor* fproc = context.getFragmentProcessor();
6668 
6669     vproc->enable();
6670     vproc->use(vp::multiShadow);
6671 
6672     fproc->enable();
6673     if (n == 1)
6674         fproc->use(fp::eclipseShadow1);
6675     else
6676         fproc->use(fp::eclipseShadow2);
6677 
6678     fproc->parameter(fp::ShadowParams0, shadowParams[0]);
6679     vproc->parameter(vp::TexGen_S, sPlanes[0]);
6680     vproc->parameter(vp::TexGen_T, tPlanes[0]);
6681     if (n >= 2)
6682     {
6683         fproc->parameter(fp::ShadowParams1, shadowParams[1]);
6684         vproc->parameter(vp::TexGen_S2, sPlanes[1]);
6685         vproc->parameter(vp::TexGen_T2, tPlanes[1]);
6686     }
6687     if (n >= 3)
6688     {
6689         //fproc->parameter(fp::ShadowParams2, shadowParams[2]);
6690         vproc->parameter(vp::TexGen_S3, sPlanes[2]);
6691         vproc->parameter(vp::TexGen_T3, tPlanes[2]);
6692     }
6693     if (n >= 4)
6694     {
6695         //fproc->parameter(fp::ShadowParams3, shadowParams[3]);
6696         vproc->parameter(vp::TexGen_S4, sPlanes[3]);
6697         vproc->parameter(vp::TexGen_T4, tPlanes[3]);
6698     }
6699 
6700     //vproc->parameter(vp::LightDirection0, lightDir);
6701 
6702     g_lodSphere->render(context,
6703                         LODSphereMesh::Normals | LODSphereMesh::Multipass,
6704                         viewFrustum,
6705                         ri.pixWidth, NULL);
6706 
6707     vproc->disable();
6708     fproc->disable();
6709 
6710     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
6711     glDisable(GL_BLEND);
6712 }
6713 
6714 
6715 static void
renderRingShadowsVS(Geometry *,const RingSystem & rings,const Vec3f &,RenderInfo & ri,float planetRadius,float,Mat4f &,Frustum & viewFrustum,const GLContext & context)6716 renderRingShadowsVS(Geometry* /*geometry*/,           //TODO: Remove unused parameters??
6717                     const RingSystem& rings,
6718                     const Vec3f& /*sunDir*/,
6719                     RenderInfo& ri,
6720                     float planetRadius,
6721                     float /*oblateness*/,
6722                     Mat4f& /*planetMat*/,
6723                     Frustum& viewFrustum,
6724                     const GLContext& context)
6725 {
6726     // Compute the transformation to use for generating texture
6727     // coordinates from the object vertices.
6728     float ringWidth = rings.outerRadius - rings.innerRadius;
6729     float s = ri.sunDir_obj.y;
6730     float scale = (abs(s) < 0.001f) ? 1000.0f : 1.0f / s;
6731 
6732     if (abs(s) > 1.0f - 1.0e-4f)
6733     {
6734         // Planet is illuminated almost directly from above, so
6735         // no ring shadow will be cast on the planet.  Conveniently
6736         // avoids some potential division by zero when ray-casting.
6737         return;
6738     }
6739 
6740     glEnable(GL_BLEND);
6741     glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
6742 
6743     // If the ambient light level is greater than zero, reduce the
6744     // darkness of the shadows.
6745     float color[4] = { ri.ambientColor.red(), ri.ambientColor.green(),
6746                        ri.ambientColor.blue(), 1.0f };
6747     glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
6748     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
6749     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_CONSTANT_EXT);
6750     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
6751     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
6752     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
6753     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
6754 
6755     // Tweak the texture--set clamp to border and a border color with
6756     // a zero alpha.  If a graphics card doesn't support clamp to border,
6757     // it doesn't get to play.  It's possible to get reasonable behavior
6758     // by turning off mipmaps and assuming transparent rows of pixels for
6759     // the top and bottom of the ring textures . . . maybe later.
6760     float bc[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
6761     glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bc);
6762     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB);
6763 
6764     // Ring shadows look strange if they're always completely black.  Vary
6765     // the darkness of the shadow based on the angle between the sun and the
6766     // ring plane.  There's some justification for this--the larger the angle
6767     // between the sun and the ring plane (normal), the more ring material
6768     // there is to travel through.
6769     //float alpha = (1.0f - abs(ri.sunDir_obj.y)) * 1.0f;
6770     // ...but, images from Cassini are showing very dark ring shadows, so we'll
6771     // go with that.
6772     float alpha = 1.0f;
6773 
6774     VertexProcessor* vproc = context.getVertexProcessor();
6775     assert(vproc != NULL);
6776 
6777     vproc->enable();
6778     vproc->use(vp::ringShadow);
6779     vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
6780     vproc->parameter(vp::DiffuseColor0, 1, 1, 1, alpha); // color = white
6781     vproc->parameter(vp::TexGen_S,
6782                      rings.innerRadius / planetRadius,
6783                      1.0f / (ringWidth / planetRadius),
6784                      0.0f, 0.5f);
6785     vproc->parameter(vp::TexGen_T, scale, 0, 0, 0);
6786     g_lodSphere->render(context, LODSphereMesh::Multipass,
6787                         viewFrustum, ri.pixWidth, NULL);
6788     vproc->disable();
6789 
6790     // Restore the texture combiners
6791     if (ri.useTexEnvCombine)
6792     {
6793         float color[4] = { 0, 0, 0, 0 };
6794         glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
6795         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
6796     }
6797 
6798     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
6799     glDisable(GL_BLEND);
6800 }
6801 
6802 
renderLocations(const Body & body,const Vec3d & bodyPosition,const Quatd & bodyOrientation)6803 void Renderer::renderLocations(const Body& body,
6804                                const Vec3d& bodyPosition,
6805                                const Quatd& bodyOrientation)
6806 {
6807     const vector<Location*>* locations = body.getLocations();
6808     if (locations == NULL)
6809         return;
6810 
6811     Vec3f semiAxes = body.getSemiAxes();
6812 
6813     float nearDist = getNearPlaneDistance();
6814     double boundingRadius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
6815 
6816     Vec3d bodyCenter(bodyPosition.x, bodyPosition.y, bodyPosition.z);
6817     Vec3d viewRayOrigin = -bodyCenter * (~bodyOrientation).toMatrix3();
6818     double labelOffset = 0.0001;
6819 
6820     Vec3f vn  = Vec3f(0.0f, 0.0f, -1.0f) * getCameraOrientation().toMatrix3();
6821     Vec3d viewNormal(vn.x, vn.y, vn.z);
6822 
6823     Ellipsoidd bodyEllipsoid(Vec3d(semiAxes.x, semiAxes.y, semiAxes.z));
6824 
6825     Mat3d bodyMatrix = bodyOrientation.toMatrix3();
6826 
6827     for (vector<Location*>::const_iterator iter = locations->begin();
6828          iter != locations->end(); iter++)
6829     {
6830         const Location& location = **iter;
6831 
6832         if (location.getFeatureType() & locationFilter)
6833         {
6834             // Get the position of the location with respect to the planet center
6835             Vec3f ppos = location.getPosition();
6836 
6837             // Compute the bodycentric position of the location
6838             Vec3d locPos = Vec3d(ppos.x, ppos.y, ppos.z);
6839 
6840             // Get the planetocentric position of the label.  Add a slight scale factor
6841             // to keep the point from being exactly on the surface.
6842             Vec3d pcLabelPos = locPos * (1.0 + labelOffset);
6843 
6844             // Get the camera space label position
6845             Vec3d labelPos = bodyCenter + locPos * bodyMatrix;
6846 
6847             float effSize = location.getImportance();
6848             if (effSize < 0.0f)
6849                 effSize = location.getSize();
6850 
6851             float pixSize = effSize / (float) (labelPos.length() * pixelSize);
6852 
6853             if (pixSize > minFeatureSize && labelPos * viewNormal > 0.0)
6854             {
6855                 // Labels on non-ellipsoidal bodies need special handling; the
6856                 // ellipsoid visibility test will always fail for them, since they
6857                 // will lie on the surface of the mesh, which is inside the
6858                 // the bounding ellipsoid. The following code projects location positions
6859                 // onto the bounding sphere.
6860                 if (!body.isEllipsoid())
6861                 {
6862                     double r = locPos.length();
6863                     if (r < boundingRadius)
6864                         pcLabelPos = locPos * (boundingRadius * 1.01 / r);
6865                 }
6866 
6867                 double t = 0.0;
6868 
6869                 // Test for an intersection of the eye-to-location ray with
6870                 // the planet ellipsoid.  If we hit the planet first, then
6871                 // the label is obscured by the planet.  An exact calculation
6872                 // for irregular objects would be too expensive, and the
6873                 // ellipsoid approximation works reasonably well for them.
6874                 Ray3d testRay(Point3d(viewRayOrigin.x, viewRayOrigin.y, viewRayOrigin.z),
6875                               pcLabelPos - viewRayOrigin);
6876                 bool hit = testIntersection(testRay, bodyEllipsoid, t);
6877                 Vec3d blah = labelPos - viewRayOrigin;
6878 
6879                 if (!hit || t >= 1.0)
6880                 {
6881                     // Calculate the intersection of the eye-to-label ray with the plane perpendicular to
6882                     // the view normal that touches the front of the object's bounding sphere
6883                     double planetZ = viewNormal * bodyCenter - boundingRadius;
6884                     if (planetZ < -nearDist * 1.001)
6885                         planetZ = -nearDist * 1.001;
6886                     double z = viewNormal * labelPos;
6887                     labelPos *= planetZ / z;
6888 
6889                     uint32 featureType = location.getFeatureType();
6890                     MarkerRepresentation* locationMarker = NULL;
6891                     if (featureType & Location::City)
6892                         locationMarker = &cityRep;
6893                     else if (featureType & (Location::LandingSite | Location::Observatory))
6894                         locationMarker = &observatoryRep;
6895                     else if (featureType & (Location::Crater | Location::Patera))
6896                         locationMarker = &craterRep;
6897                     else if (featureType & (Location::Mons | Location::Tholus))
6898                         locationMarker = &mountainRep;
6899                     else if (featureType & (Location::EruptiveCenter))
6900                         locationMarker = &genericLocationRep;
6901 
6902                     Color labelColor = location.isLabelColorOverridden() ? location.getLabelColor() : LocationLabelColor;
6903                     addObjectAnnotation(locationMarker,
6904                                         location.getName(true),
6905                                         labelColor,
6906                                         Point3f((float) labelPos.x, (float) labelPos.y, (float) labelPos.z));
6907                 }
6908             }
6909         }
6910     }
6911 }
6912 
6913 
6914 // Estimate the fraction of light reflected from a sphere that
6915 // reaches an object at the specified position relative to that
6916 // sphere.
6917 //
6918 // This is function is just a rough approximation to the actual
6919 // lighting integral, but it reproduces the important features
6920 // of the way that phase and distance affect reflected light:
6921 //    - Higher phase angles mean less reflected light
6922 //    - The closer an object is to the reflector, the less
6923 //      area of the reflector that is visible.
6924 //
6925 // We approximate the reflected light by taking a weighted average
6926 // of the reflected light at three points on the reflector: the
6927 // light receiver's sub-point, and the two horizon points in the
6928 // plane of the light vector and receiver-to-reflector vector.
6929 //
6930 // The reflecting object is assumed to be spherical and perfectly
6931 // Lambertian.
6932 static float
estimateReflectedLightFraction(const Vec3d & toSun,const Vec3d & toObject,float radius)6933 estimateReflectedLightFraction(const Vec3d& toSun,
6934                                const Vec3d& toObject,
6935                                float radius)
6936 {
6937     // Theta is half the arc length visible to the reflector
6938     double d = toObject.length();
6939     float cosTheta = (float) (radius / d);
6940     if (cosTheta > 0.999f)
6941         cosTheta = 0.999f;
6942 
6943     // Phi is the angle between the light vector and receiver-to-reflector vector.
6944     // cos(phi) is thus the illumination at the sub-point. The horizon points are
6945     // at phi+theta and phi-theta.
6946     float cosPhi = (float) ((toSun * toObject) / (d * toSun.length()));
6947 
6948     // Use a trigonometric identity to compute cos(phi +/- theta):
6949     //   cos(phi + theta) = cos(phi) * cos(theta) - sin(phi) * sin(theta)
6950 
6951     // s = sin(phi) * sin(theta)
6952     float s = (float) sqrt((1.0f - cosPhi * cosPhi) * (1.0f - cosTheta * cosTheta));
6953 
6954     float cosPhi1 = cosPhi * cosTheta - s;  // cos(phi + theta)
6955     float cosPhi2 = cosPhi * cosTheta + s;  // cos(phi - theta)
6956 
6957     // Calculate a weighted average of illumination at the three points
6958     return (2.0f * max(cosPhi, 0.0f) + max(cosPhi1, 0.0f) + max(cosPhi2, 0.0f)) * 0.25f;
6959 }
6960 
6961 
6962 static void
setupObjectLighting(const vector<LightSource> & suns,const vector<SecondaryIlluminator> & secondaryIlluminators,const Quatf & objOrientation,const Vec3f & objScale,const Point3f & objPosition_eye,bool isNormalized,const float faintestMag,const float saturationMag,const float appMag,LightingState & ls)6963 setupObjectLighting(const vector<LightSource>& suns,
6964                     const vector<SecondaryIlluminator>& secondaryIlluminators,
6965                     const Quatf& objOrientation,
6966                     const Vec3f& objScale,
6967                     const Point3f& objPosition_eye,
6968                     bool isNormalized,
6969 #ifdef USE_HDR
6970                     const float faintestMag,
6971                     const float saturationMag,
6972                     const float appMag,
6973 #endif
6974                     LightingState& ls)
6975 {
6976     unsigned int nLights = min(MaxLights, (unsigned int) suns.size());
6977     if (nLights == 0)
6978         return;
6979 
6980 #ifdef USE_HDR
6981     float exposureFactor = (faintestMag - appMag)/(faintestMag - saturationMag + 0.001f);
6982 #endif
6983 
6984     unsigned int i;
6985     for (i = 0; i < nLights; i++)
6986     {
6987         Vec3d dir = suns[i].position - Vec3d(objPosition_eye.x, objPosition_eye.y, objPosition_eye.z);
6988 
6989         ls.lights[i].direction_eye =
6990             Vec3f((float) dir.x, (float) dir.y, (float) dir.z);
6991         float distance = ls.lights[i].direction_eye.length();
6992         ls.lights[i].direction_eye *= 1.0f / distance;
6993         distance = astro::kilometersToAU((float) dir.length());
6994         ls.lights[i].irradiance = suns[i].luminosity / (distance * distance);
6995         ls.lights[i].color = suns[i].color;
6996 
6997         // Store the position and apparent size because we'll need them for
6998         // testing for eclipses.
6999         ls.lights[i].position = dir;
7000         ls.lights[i].apparentSize = (float) (suns[i].radius / dir.length());
7001         ls.lights[i].castsShadows = true;
7002     }
7003 
7004     // Include effects of secondary illumination (i.e. planetshine)
7005     if (!secondaryIlluminators.empty() && i < MaxLights - 1)
7006     {
7007         float maxIrr = 0.0f;
7008         unsigned int maxIrrSource = 0;
7009         Vec3d objpos(objPosition_eye.x, objPosition_eye.y, objPosition_eye.z);
7010 
7011         // Only account for light from the brightest secondary source
7012         for (vector<SecondaryIlluminator>::const_iterator iter = secondaryIlluminators.begin();
7013              iter != secondaryIlluminators.end(); iter++)
7014         {
7015             Vec3d toIllum = iter->position_v - objpos;  // reflector-to-object vector
7016             float distSquared = (float) toIllum.lengthSquared() / square(iter->radius);
7017 
7018             if (distSquared > 0.01f)
7019             {
7020                 // Irradiance falls off with distance^2
7021                 float irr = iter->reflectedIrradiance / distSquared;
7022 
7023                 // Phase effects will always leave the irradiance unaffected or reduce it;
7024                 // don't bother calculating them if we've already found a brighter secondary
7025                 // source.
7026                 if (irr > maxIrr)
7027                 {
7028                     // Account for the phase
7029                     Vec3d toSun = objpos - suns[0].position;
7030                     irr *= estimateReflectedLightFraction(toSun, toIllum, iter->radius);
7031                     if (irr > maxIrr)
7032                     {
7033                         maxIrr = irr;
7034                         maxIrrSource = iter - secondaryIlluminators.begin();
7035                     }
7036                 }
7037             }
7038         }
7039 #if DEBUG_SECONDARY_ILLUMINATION
7040         clog << "maxIrr = " << maxIrr << ", "
7041              << secondaryIlluminators[maxIrrSource].body->getName() << ", "
7042              << secondaryIlluminators[maxIrrSource].reflectedIrradiance << endl;
7043 #endif
7044 
7045         if (maxIrr > 0.0f)
7046         {
7047             Vec3d toIllum = secondaryIlluminators[maxIrrSource].position_v - objpos;
7048 
7049             ls.lights[i].direction_eye = Vec3f((float) toIllum.x, (float) toIllum.y, (float) toIllum.z);
7050             ls.lights[i].direction_eye.normalize();
7051             ls.lights[i].irradiance = maxIrr;
7052             ls.lights[i].color = secondaryIlluminators[maxIrrSource].body->getSurface().color;
7053             ls.lights[i].apparentSize = 0.0f;
7054             ls.lights[i].castsShadows = false;
7055             i++;
7056             nLights++;
7057         }
7058     }
7059 
7060     // Sort light sources by brightness.  Light zero should always be the
7061     // brightest.  Optimize common cases of one and two lights.
7062     if (nLights == 2)
7063     {
7064         if (ls.lights[0].irradiance < ls.lights[1].irradiance)
7065             swap(ls.lights[0], ls.lights[1]);
7066     }
7067     else if (nLights > 2)
7068     {
7069         sort(ls.lights, ls.lights + nLights, LightIrradiancePredicate());
7070     }
7071 
7072     // Compute the total irradiance
7073     float totalIrradiance = 0.0f;
7074     for (i = 0; i < nLights; i++)
7075         totalIrradiance += ls.lights[i].irradiance;
7076 
7077     // Compute a gamma factor to make dim light sources visible.  This is
7078     // intended to approximate what we see with our eyes--for example,
7079     // Earth-shine is visible on the night side of the Moon, even though
7080     // the amount of reflected light from the Earth is 1/10000 of what
7081     // the Moon receives directly from the Sun.
7082     //
7083     // TODO: Skip this step when high dynamic range rendering to floating point
7084     //   buffers is enabled.
7085     float minVisibleFraction = 1.0f / 10000.0f;
7086     float minDisplayableValue = 1.0f / 255.0f;
7087     float gamma = (float) (log(minDisplayableValue) / log(minVisibleFraction));
7088     float minVisibleIrradiance = minVisibleFraction * totalIrradiance;
7089 
7090     Mat3f m = (~objOrientation).toMatrix3();
7091 
7092     // Gamma scale and normalize the light sources; cull light sources that
7093     // aren't bright enough to contribute the final pixels rendered into the
7094     // frame buffer.
7095     ls.nLights = 0;
7096     for (i = 0; i < nLights && ls.lights[i].irradiance > minVisibleIrradiance; i++)
7097     {
7098 #ifdef USE_HDR
7099         ls.lights[i].irradiance *= exposureFactor / totalIrradiance;
7100 #else
7101         ls.lights[i].irradiance =
7102             (float) pow(ls.lights[i].irradiance / totalIrradiance, gamma);
7103 #endif
7104 
7105         // Compute the direction of the light in object space
7106         ls.lights[i].direction_obj = ls.lights[i].direction_eye * m;
7107 
7108         ls.nLights++;
7109     }
7110 
7111     ls.eyePos_obj = Point3f(-objPosition_eye.x / objScale.x,
7112                             -objPosition_eye.y / objScale.y,
7113                             -objPosition_eye.z / objScale.z) * m;
7114     ls.eyeDir_obj = (Point3f(0.0f, 0.0f, 0.0f) - objPosition_eye) * m;
7115     ls.eyeDir_obj.normalize();
7116 
7117     // When the camera is very far from the object, some view-dependent
7118     // calculations in the shaders can exhibit precision problems. This
7119     // occurs with atmospheres, where the scale height of the atmosphere
7120     // is very small relative to the planet radius. To address the problem,
7121     // we'll clamp the eye distance to some maximum value. The effect of the
7122     // adjustment should be impercetible, since at large distances rays from
7123     // the camera to object vertices are all nearly parallel to each other.
7124     float eyeFromCenterDistance = ls.eyePos_obj.distanceFromOrigin();
7125     if (eyeFromCenterDistance > 100.0f && isNormalized)
7126     {
7127         float s = 100.0f / eyeFromCenterDistance;
7128         ls.eyePos_obj.x *= s;
7129         ls.eyePos_obj.y *= s;
7130         ls.eyePos_obj.z *= s;
7131     }
7132 
7133     ls.ambientColor = Vec3f(0.0f, 0.0f, 0.0f);
7134 
7135 #if 0
7136     // Old code: linear scaling approach
7137 
7138     // After sorting, the first light is always the brightest
7139     float maxIrradiance = ls.lights[0].irradiance;
7140 
7141     // Normalize the brightnesses of the light sources.
7142     // TODO: Investigate logarithmic functions for scaling light brightness, to
7143     //   better simulate what the human eye would see.
7144     ls.nLights = 0;
7145     for (i = 0; i < nLights; i++)
7146     {
7147         ls.lights[i].irradiance /= maxIrradiance;
7148 
7149         // Cull light sources that don't contribute significantly (less than
7150         // the resolution of an 8-bit color channel.)
7151         if (ls.lights[i].irradiance < 1.0f / 255.0f)
7152             break;
7153 
7154         // Compute the direction of the light in object space
7155         ls.lights[i].direction_obj = ls.lights[i].direction_eye * m;
7156 
7157         ls.nLights++;
7158     }
7159 #endif
7160 }
7161 
7162 
renderObject(Point3f pos,float distance,double now,Quatf cameraOrientation,float nearPlaneDistance,float farPlaneDistance,RenderProperties & obj,const LightingState & ls)7163 void Renderer::renderObject(Point3f pos,
7164                             float distance,
7165                             double now,
7166                             Quatf cameraOrientation,
7167                             float nearPlaneDistance,
7168                             float farPlaneDistance,
7169                             RenderProperties& obj,
7170                             const LightingState& ls)
7171 {
7172     RenderInfo ri;
7173 
7174     float altitude = distance - obj.radius;
7175     float discSizeInPixels = obj.radius /
7176         (max(nearPlaneDistance, altitude) * pixelSize);
7177 
7178     ri.sunDir_eye = Vec3f(0.0f, 1.0f, 0.0f);
7179     ri.sunDir_obj = Vec3f(0.0f, 1.0f, 0.0f);
7180     ri.sunColor = Color(0.0f, 0.0f, 0.0f);
7181     if (ls.nLights > 0)
7182     {
7183         ri.sunDir_eye = ls.lights[0].direction_eye;
7184         ri.sunDir_obj = ls.lights[0].direction_obj;
7185         ri.sunColor   = ls.lights[0].color;// * ls.lights[0].intensity;
7186     }
7187 
7188     // Enable depth buffering
7189     glEnable(GL_DEPTH_TEST);
7190     glDepthMask(GL_TRUE);
7191 
7192     glDisable(GL_BLEND);
7193 
7194     // Get the object's geometry; NULL indicates that object is an
7195     // ellipsoid.
7196     Geometry* geometry = NULL;
7197     if (obj.geometry != InvalidResource)
7198     {
7199         // This is a model loaded from a file
7200         geometry = GetGeometryManager()->find(obj.geometry);
7201     }
7202 
7203     // Get the textures . . .
7204     if (obj.surface->baseTexture.tex[textureResolution] != InvalidResource)
7205         ri.baseTex = obj.surface->baseTexture.find(textureResolution);
7206     if ((obj.surface->appearanceFlags & Surface::ApplyBumpMap) != 0 &&
7207         context->bumpMappingSupported() &&
7208         obj.surface->bumpTexture.tex[textureResolution] != InvalidResource)
7209         ri.bumpTex = obj.surface->bumpTexture.find(textureResolution);
7210     if ((obj.surface->appearanceFlags & Surface::ApplyNightMap) != 0 &&
7211         (renderFlags & ShowNightMaps) != 0)
7212         ri.nightTex = obj.surface->nightTexture.find(textureResolution);
7213     if ((obj.surface->appearanceFlags & Surface::SeparateSpecularMap) != 0)
7214         ri.glossTex = obj.surface->specularTexture.find(textureResolution);
7215     if ((obj.surface->appearanceFlags & Surface::ApplyOverlay) != 0)
7216         ri.overlayTex = obj.surface->overlayTexture.find(textureResolution);
7217 
7218     // Apply the modelview transform for the object
7219     glPushMatrix();
7220     glTranslate(pos);
7221     glRotate(~obj.orientation);
7222 
7223     // Scaling will be nonuniform for nonspherical planets. As long as the
7224     // deviation from spherical isn't too large, the nonuniform scale factor
7225     // shouldn't mess up the lighting calculations enough to be noticeable
7226     // (and we turn on renormalization anyhow, which most graphics cards
7227     // support.)
7228     float radius = obj.radius;
7229     Vec3f scaleFactors;
7230     float geometryScale;
7231     if (geometry == NULL || geometry->isNormalized())
7232     {
7233         geometryScale = obj.radius;
7234         scaleFactors = obj.radius * obj.semiAxes;
7235         ri.pointScale = 2.0f * obj.radius / pixelSize;
7236     }
7237     else
7238     {
7239         geometryScale = obj.geometryScale;
7240         scaleFactors = Vec3f(geometryScale, geometryScale, geometryScale);
7241         ri.pointScale = 2.0f * geometryScale / pixelSize;
7242     }
7243     glScale(scaleFactors);
7244 
7245     Mat4f planetMat = (~obj.orientation).toMatrix4();
7246     ri.eyeDir_obj = (Point3f(0, 0, 0) - pos) * planetMat;
7247     ri.eyeDir_obj.normalize();
7248     ri.eyePos_obj = Point3f(-pos.x / scaleFactors.x,
7249                             -pos.y / scaleFactors.y,
7250                             -pos.z / scaleFactors.z) * planetMat;
7251 
7252     ri.orientation = cameraOrientation * ~obj.orientation;
7253 
7254     ri.pixWidth = discSizeInPixels;
7255 
7256     // Set up the colors
7257     if (ri.baseTex == NULL ||
7258         (obj.surface->appearanceFlags & Surface::BlendTexture) != 0)
7259     {
7260         ri.color = obj.surface->color;
7261     }
7262 
7263     ri.ambientColor = ambientColor;
7264     ri.hazeColor = obj.surface->hazeColor;
7265     ri.specularColor = obj.surface->specularColor;
7266     ri.specularPower = obj.surface->specularPower;
7267     ri.useTexEnvCombine = context->getRenderPath() != GLContext::GLPath_Basic;
7268     ri.lunarLambert = obj.surface->lunarLambert;
7269 #ifdef USE_HDR
7270     ri.nightLightScale = obj.surface->nightLightRadiance * exposure * 1.e5f * .5f;
7271 #endif
7272 
7273     // See if the surface should be lit
7274     bool lit = (obj.surface->appearanceFlags & Surface::Emissive) == 0;
7275 
7276     // Set the OpenGL light state
7277     unsigned int i;
7278     for (i = 0; i < ls.nLights; i++)
7279     {
7280         const DirectionalLight& light = ls.lights[i];
7281 
7282         glLightDirection(GL_LIGHT0 + i, ls.lights[i].direction_obj);
7283 
7284         // RANT ALERT!
7285         // This sucks, but it's necessary.  glScale is used to scale a unit
7286         // sphere up to planet size.  Since normals are transformed by the
7287         // inverse transpose of the model matrix, this means they end up
7288         // getting scaled by a factor of 1.0 / planet radius (in km).  This
7289         // has terrible effects on lighting: the planet appears almost
7290         // completely dark.  To get around this, the GL_rescale_normal
7291         // extension was introduced and eventually incorporated into into the
7292         // OpenGL 1.2 standard.  Of course, not everyone implemented this
7293         // incredibly simple and essential little extension.  Microsoft is
7294         // notorious for half-assed support of OpenGL, but 3dfx should have
7295         // known better: no Voodoo 1/2/3 drivers seem to support this
7296         // extension.  The following is an attempt to get around the problem by
7297         // scaling the light brightness by the planet radius.  According to the
7298         // OpenGL spec, this should work fine, as clamping of colors to [0, 1]
7299         // occurs *after* lighting.  It works fine on my GeForce3 when I
7300         // disable EXT_rescale_normal, but I'm not certain whether other
7301         // drivers are as well behaved as nVidia's.
7302         //
7303         // Addendum: Unsurprisingly, using color values outside [0, 1] produces
7304         // problems on Savage4 cards.
7305 
7306         Vec3f lightColor = Vec3f(light.color.red(),
7307                                  light.color.green(),
7308                                  light.color.blue()) * light.irradiance;
7309         if (useRescaleNormal)
7310         {
7311             glLightColor(GL_LIGHT0 + i, GL_DIFFUSE, lightColor);
7312             glLightColor(GL_LIGHT0 + i, GL_SPECULAR, lightColor);
7313         }
7314         else
7315         {
7316             glLightColor(GL_LIGHT0 + i, GL_DIFFUSE, lightColor * radius);
7317         }
7318         glEnable(GL_LIGHT0 + i);
7319     }
7320 
7321     // Compute the inverse model/view matrix
7322     Mat4f invMV = (cameraOrientation.toMatrix4() *
7323                    Mat4f::translation(Point3f(-pos.x, -pos.y, -pos.z)) *
7324                    planetMat *
7325                    Mat4f::scaling(1.0f / radius));
7326 
7327     // The sphere rendering code uses the view frustum to determine which
7328     // patches are visible. In order to avoid rendering patches that can't
7329     // be seen, make the far plane of the frustum as close to the viewer
7330     // as possible.
7331     float frustumFarPlane = farPlaneDistance;
7332     if (obj.geometry == InvalidResource)
7333     {
7334         // Only adjust the far plane for ellipsoidal objects
7335         float d = pos.distanceFromOrigin();
7336 
7337         // Account for non-spherical objects
7338         float eradius = min(scaleFactors.x, min(scaleFactors.y, scaleFactors.z));
7339 
7340         if (d > eradius)
7341         {
7342             // Include a fudge factor to eliminate overaggressive clipping
7343             // due to limited floating point precision
7344             frustumFarPlane = (float) sqrt(square(d) - square(eradius)) * 1.1f;
7345         }
7346         else
7347         {
7348             // We're inside the bounding sphere; leave the far plane alone
7349         }
7350 
7351         if (obj.atmosphere != NULL)
7352         {
7353             float atmosphereHeight = max(obj.atmosphere->cloudHeight,
7354                                          obj.atmosphere->mieScaleHeight * (float) -log(AtmosphereExtinctionThreshold));
7355             if (atmosphereHeight > 0.0f)
7356             {
7357                 // If there's an atmosphere, we need to move the far plane
7358                 // out so that the clouds and atmosphere shell aren't clipped.
7359                 float atmosphereRadius = eradius + atmosphereHeight;
7360                 frustumFarPlane += (float) sqrt(square(atmosphereRadius) -
7361                                                 square(eradius));
7362             }
7363         }
7364     }
7365 
7366     // Transform the frustum into object coordinates using the
7367     // inverse model/view matrix.
7368     Frustum viewFrustum(degToRad(fov),
7369                         (float) windowWidth / (float) windowHeight,
7370                         nearPlaneDistance, frustumFarPlane);
7371     viewFrustum.transform(invMV);
7372 
7373     // Get cloud layer parameters
7374     Texture* cloudTex       = NULL;
7375     Texture* cloudNormalMap = NULL;
7376     float cloudTexOffset    = 0.0f;
7377     if (obj.atmosphere != NULL)
7378     {
7379         Atmosphere* atmosphere = const_cast<Atmosphere*>(obj.atmosphere); // Ugly cast required because MultiResTexture::find() is non-const
7380         if ((renderFlags & ShowCloudMaps) != 0)
7381         {
7382             if (atmosphere->cloudTexture.tex[textureResolution] != InvalidResource)
7383                 cloudTex = atmosphere->cloudTexture.find(textureResolution);
7384             if (atmosphere->cloudNormalMap.tex[textureResolution] != InvalidResource)
7385                 cloudNormalMap = atmosphere->cloudNormalMap.find(textureResolution);
7386         }
7387         if (atmosphere->cloudSpeed != 0.0f)
7388             cloudTexOffset = (float) (-pfmod(now * atmosphere->cloudSpeed / (2 * PI), 1.0));
7389     }
7390 
7391     if (obj.geometry == InvalidResource)
7392     {
7393         // A null model indicates that this body is a sphere
7394         if (lit)
7395         {
7396             switch (context->getRenderPath())
7397             {
7398             case GLContext::GLPath_GLSL:
7399                 renderSphere_GLSL(ri, ls, obj.rings,
7400                                   const_cast<Atmosphere*>(obj.atmosphere), cloudTexOffset,
7401                                   obj.radius,
7402                                   textureResolution,
7403                                   renderFlags,
7404                                   planetMat, viewFrustum, *context);
7405                 break;
7406 
7407             case GLContext::GLPath_NV30:
7408                 renderSphere_FP_VP(ri, viewFrustum, *context);
7409                 break;
7410 
7411             case GLContext::GLPath_NvCombiner_ARBVP:
7412             case GLContext::GLPath_NvCombiner_NvVP:
7413                 renderSphere_Combiners_VP(ri, ls, viewFrustum, *context);
7414                 break;
7415 
7416             case GLContext::GLPath_NvCombiner:
7417                 renderSphere_Combiners(ri, viewFrustum, *context);
7418                 break;
7419 
7420             case GLContext::GLPath_DOT3_ARBVP:
7421                 renderSphere_DOT3_VP(ri, ls, viewFrustum, *context);
7422                 break;
7423 
7424             default:
7425                 renderSphereDefault(ri, viewFrustum, true, *context);
7426             }
7427         }
7428         else
7429         {
7430             renderSphereDefault(ri, viewFrustum, false, *context);
7431         }
7432     }
7433     else
7434     {
7435         if (geometry != NULL)
7436         {
7437             ResourceHandle texOverride = obj.surface->baseTexture.tex[textureResolution];
7438 
7439             if (context->getRenderPath() == GLContext::GLPath_GLSL)
7440             {
7441                 if (lit)
7442                 {
7443                     renderGeometry_GLSL(geometry,
7444                                         ri,
7445                                         texOverride,
7446                                         ls,
7447                                         obj.atmosphere,
7448                                         geometryScale,
7449                                         renderFlags,
7450                                         planetMat,
7451                                         astro::daysToSecs(now - astro::J2000));
7452                 }
7453                 else
7454                 {
7455                     renderGeometry_GLSL_Unlit(geometry,
7456                                               ri,
7457                                               texOverride,
7458                                               geometryScale,
7459                                               renderFlags,
7460                                               planetMat,
7461                                               astro::daysToSecs(now - astro::J2000));
7462                 }
7463 
7464                 for (unsigned int i = 1; i < 8;/*context->getMaxTextures();*/ i++)
7465                 {
7466                     glx::glActiveTextureARB(GL_TEXTURE0_ARB + i);
7467                     glDisable(GL_TEXTURE_2D);
7468                 }
7469                 glx::glActiveTextureARB(GL_TEXTURE0_ARB);
7470                 glEnable(GL_TEXTURE_2D);
7471                 glx::glUseProgramObjectARB(0);
7472             }
7473             else
7474             {
7475                 renderModelDefault(geometry, ri, lit, texOverride);
7476             }
7477         }
7478     }
7479 
7480     if (obj.rings != NULL && distance <= obj.rings->innerRadius)
7481     {
7482         if (context->getRenderPath() == GLContext::GLPath_GLSL)
7483         {
7484             renderRings_GLSL(*obj.rings, ri, ls,
7485                              radius, 1.0f - obj.semiAxes.y,
7486                              textureResolution,
7487                              (renderFlags & ShowRingShadows) != 0 && lit,
7488                              detailOptions.ringSystemSections);
7489         }
7490         else
7491         {
7492             renderRings(*obj.rings, ri, radius, 1.0f - obj.semiAxes.y,
7493                         textureResolution,
7494                         context->getMaxTextures() > 1 &&
7495                         (renderFlags & ShowRingShadows) != 0 && lit,
7496                         *context,
7497                         detailOptions.ringSystemSections);
7498         }
7499     }
7500 
7501     if (obj.atmosphere != NULL)
7502     {
7503         Atmosphere* atmosphere = const_cast<Atmosphere *>(obj.atmosphere);
7504 
7505         // Compute the apparent thickness in pixels of the atmosphere.
7506         // If it's only one pixel thick, it can look quite unsightly
7507         // due to aliasing.  To avoid popping, we gradually fade in the
7508         // atmosphere as it grows from two to three pixels thick.
7509         float fade;
7510         float thicknessInPixels = 0.0f;
7511         if (distance - radius > 0.0f)
7512         {
7513             thicknessInPixels = atmosphere->height /
7514                 ((distance - radius) * pixelSize);
7515             fade = clamp(thicknessInPixels - 2);
7516         }
7517         else
7518         {
7519             fade = 1.0f;
7520         }
7521 
7522         if (fade > 0 && (renderFlags & ShowAtmospheres) != 0)
7523         {
7524             // Only use new atmosphere code in OpenGL 2.0 path when new style parameters are defined.
7525             // TODO: convert old style atmopshere parameters
7526             if (context->getRenderPath() == GLContext::GLPath_GLSL &&
7527                 atmosphere->mieScaleHeight > 0.0f)
7528             {
7529                 float atmScale = 1.0f + atmosphere->height / radius;
7530 
7531                 renderAtmosphere_GLSL(ri, ls,
7532                                       atmosphere,
7533                                       radius * atmScale,
7534                                       planetMat,
7535                                       viewFrustum,
7536                                       *context);
7537             }
7538             else
7539             {
7540                 glPushMatrix();
7541                 glLoadIdentity();
7542                 glDisable(GL_LIGHTING);
7543                 glDisable(GL_TEXTURE_2D);
7544                 glEnable(GL_BLEND);
7545                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
7546 
7547                 glRotate(cameraOrientation);
7548 
7549                 renderEllipsoidAtmosphere(*atmosphere,
7550                                           pos,
7551                                           obj.orientation,
7552                                           scaleFactors,
7553                                           ri.sunDir_eye,
7554                                           ls,
7555                                           thicknessInPixels,
7556                                           lit);
7557                 glEnable(GL_TEXTURE_2D);
7558                 glPopMatrix();
7559             }
7560         }
7561 
7562         // If there's a cloud layer, we'll render it now.
7563         if (cloudTex != NULL)
7564         {
7565             glPushMatrix();
7566 
7567             float cloudScale = 1.0f + atmosphere->cloudHeight / radius;
7568             glScalef(cloudScale, cloudScale, cloudScale);
7569 
7570             // If we're beneath the cloud level, render the interior of
7571             // the cloud sphere.
7572             if (distance - radius < atmosphere->cloudHeight)
7573                 glFrontFace(GL_CW);
7574 
7575             if (atmosphere->cloudSpeed != 0.0f)
7576             {
7577                 // Make the clouds appear to rotate above the surface of
7578                 // the planet.  This is easier to do with the texture
7579                 // matrix than the model matrix because changing the
7580                 // texture matrix doesn't require us to compute a second
7581                 // set of model space rendering parameters.
7582                 glMatrixMode(GL_TEXTURE);
7583                 glTranslatef(cloudTexOffset, 0.0f, 0.0f);
7584                 glMatrixMode(GL_MODELVIEW);
7585             }
7586 
7587             glEnable(GL_LIGHTING);
7588             glDepthMask(GL_FALSE);
7589             cloudTex->bind();
7590             glEnable(GL_BLEND);
7591             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
7592 #ifdef HDR_COMPRESS
7593             glColor4f(0.5f, 0.5f, 0.5f, 1);
7594 #else
7595             glColor4f(1, 1, 1, 1);
7596 #endif
7597 
7598             // Cloud layers can be trouble for the depth buffer, since they tend
7599             // to be very close to the surface of a planet relative to the radius
7600             // of the planet. We'll help out by offsetting the cloud layer toward
7601             // the viewer.
7602             if (distance > radius * 1.1f)
7603             {
7604                 glEnable(GL_POLYGON_OFFSET_FILL);
7605                 glPolygonOffset(-1.0f, -1.0f);
7606             }
7607 
7608             if (lit)
7609             {
7610                 if (context->getRenderPath() == GLContext::GLPath_GLSL)
7611                 {
7612                     renderClouds_GLSL(ri, ls,
7613                                       atmosphere,
7614                                       cloudTex,
7615                                       cloudNormalMap,
7616                                       cloudTexOffset,
7617                                       obj.rings,
7618                                       radius,
7619                                       textureResolution,
7620                                       renderFlags,
7621                                       planetMat,
7622                                       viewFrustum,
7623                                       *context);
7624                 }
7625                 else
7626                 {
7627                     VertexProcessor* vproc = context->getVertexProcessor();
7628                     if (vproc != NULL)
7629                     {
7630                         vproc->enable();
7631                         vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
7632                         vproc->parameter(vp::TextureTranslation,
7633                                          cloudTexOffset, 0.0f, 0.0f, 0.0f);
7634                         if (ls.nLights > 1)
7635                             vproc->use(vp::diffuseTexOffset_2light);
7636                         else
7637                             vproc->use(vp::diffuseTexOffset);
7638                         setLightParameters_VP(*vproc, ls, ri.color, Color::Black);
7639                     }
7640 
7641                     g_lodSphere->render(*context,
7642                                         LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
7643                                         viewFrustum,
7644                                         ri.pixWidth,
7645                                         cloudTex);
7646 
7647                     if (vproc != NULL)
7648                         vproc->disable();
7649                 }
7650             }
7651             else
7652             {
7653                 glDisable(GL_LIGHTING);
7654                 g_lodSphere->render(*context,
7655                                     LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
7656                                     viewFrustum,
7657                                     ri.pixWidth,
7658                                     cloudTex);
7659                 glEnable(GL_LIGHTING);
7660             }
7661 
7662             glDisable(GL_POLYGON_OFFSET_FILL);
7663 
7664             // Reset the texture matrix
7665             glMatrixMode(GL_TEXTURE);
7666             glLoadIdentity();
7667             glMatrixMode(GL_MODELVIEW);
7668 
7669             glDepthMask(GL_TRUE);
7670             glFrontFace(GL_CCW);
7671 
7672             glPopMatrix();
7673         }
7674     }
7675 
7676     // No separate shadow rendering pass required for GLSL path
7677     if (ls.shadows[0] != NULL &&
7678         ls.shadows[0]->size() != 0 &&
7679         (obj.surface->appearanceFlags & Surface::Emissive) == 0 &&
7680         context->getRenderPath() != GLContext::GLPath_GLSL)
7681     {
7682         if (context->getVertexProcessor() != NULL &&
7683             context->getFragmentProcessor() != NULL)
7684         {
7685             renderEclipseShadows_Shaders(geometry,
7686                                          *ls.shadows[0],
7687                                          ri,
7688                                          radius, planetMat, viewFrustum,
7689                                          *context);
7690         }
7691         else
7692         {
7693             renderEclipseShadows(geometry,
7694                                  *ls.shadows[0],
7695                                  ri,
7696                                  radius, planetMat, viewFrustum,
7697                                  *context);
7698         }
7699     }
7700 
7701     if (obj.rings != NULL &&
7702         (obj.surface->appearanceFlags & Surface::Emissive) == 0 &&
7703         (renderFlags & ShowRingShadows) != 0)
7704     {
7705         Texture* ringsTex = obj.rings->texture.find(textureResolution);
7706         if (ringsTex != NULL)
7707         {
7708             Vec3f sunDir = pos - Point3f(0, 0, 0);
7709             sunDir.normalize();
7710 
7711             glEnable(GL_TEXTURE_2D);
7712             ringsTex->bind();
7713 
7714             if (useClampToBorder &&
7715                 context->getVertexPath() != GLContext::VPath_Basic &&
7716                 context->getRenderPath() != GLContext::GLPath_GLSL)
7717             {
7718                 renderRingShadowsVS(geometry,
7719                                     *obj.rings,
7720                                     sunDir,
7721                                     ri,
7722                                     radius, 1.0f - obj.semiAxes.y,
7723                                     planetMat, viewFrustum,
7724                                     *context);
7725             }
7726         }
7727     }
7728 
7729     if (obj.rings != NULL && distance > obj.rings->innerRadius)
7730     {
7731         glDepthMask(GL_FALSE);
7732         if (context->getRenderPath() == GLContext::GLPath_GLSL)
7733         {
7734             renderRings_GLSL(*obj.rings, ri, ls,
7735                              radius, 1.0f - obj.semiAxes.y,
7736                              textureResolution,
7737                              (renderFlags & ShowRingShadows) != 0 && lit,
7738                              detailOptions.ringSystemSections);
7739         }
7740         else
7741         {
7742             renderRings(*obj.rings, ri, radius, 1.0f - obj.semiAxes.y,
7743                         textureResolution,
7744                         (context->hasMultitexture() &&
7745                          (renderFlags & ShowRingShadows) != 0 && lit),
7746                         *context,
7747                         detailOptions.ringSystemSections);
7748         }
7749     }
7750 
7751     // Disable all light sources other than the first
7752     for (i = 0; i < ls.nLights; i++)
7753         glDisable(GL_LIGHT0 + i);
7754 
7755     glPopMatrix();
7756     glDisable(GL_DEPTH_TEST);
7757     glDepthMask(GL_FALSE);
7758     glDisable(GL_LIGHTING);
7759     glEnable(GL_BLEND);
7760 }
7761 
7762 
testEclipse(const Body & receiver,const Body & caster,const DirectionalLight & light,double now,vector<EclipseShadow> & shadows)7763 bool Renderer::testEclipse(const Body& receiver,
7764                            const Body& caster,
7765                            const DirectionalLight& light,
7766                            double now,
7767                            vector<EclipseShadow>& shadows)
7768 {
7769     // Ignore situations where the shadow casting body is much smaller than
7770     // the receiver, as these shadows aren't likely to be relevant.  Also,
7771     // ignore eclipses where the caster is not an ellipsoid, since we can't
7772     // generate correct shadows in this case.
7773     if (caster.getRadius() >= receiver.getRadius() * MinRelativeOccluderRadius &&
7774         caster.hasVisibleGeometry() &&
7775         caster.extant(now) &&
7776         caster.isEllipsoid())
7777     {
7778         // All of the eclipse related code assumes that both the caster
7779         // and receiver are spherical.  Irregular receivers will work more
7780         // or less correctly, but casters that are sufficiently non-spherical
7781         // will produce obviously incorrect shadows.  Another assumption we
7782         // make is that the distance between the caster and receiver is much
7783         // less than the distance between the sun and the receiver.  This
7784         // approximation works everywhere in the solar system, and is likely
7785         // valid for any orbitally stable pair of objects orbiting a star.
7786         Point3d posReceiver = receiver.getAstrocentricPosition(now);
7787         Point3d posCaster = caster.getAstrocentricPosition(now);
7788 
7789         //const Star* sun = receiver.getSystem()->getStar();
7790         //assert(sun != NULL);
7791         //double distToSun = posReceiver.distanceFromOrigin();
7792         //float appSunRadius = (float) (sun->getRadius() / distToSun);
7793         float appSunRadius = light.apparentSize;
7794 
7795         Vec3d dir = posCaster - posReceiver;
7796         double distToCaster = dir.length() - receiver.getRadius();
7797         float appOccluderRadius = (float) (caster.getRadius() / distToCaster);
7798 
7799         // The shadow radius is the radius of the occluder plus some additional
7800         // amount that depends upon the apparent radius of the sun.  For
7801         // a sun that's distant/small and effectively a point, the shadow
7802         // radius will be the same as the radius of the occluder.
7803         float shadowRadius = (1 + appSunRadius / appOccluderRadius) *
7804             caster.getRadius();
7805 
7806         // Test whether a shadow is cast on the receiver.  We want to know
7807         // if the receiver lies within the shadow volume of the caster.  Since
7808         // we're assuming that everything is a sphere and the sun is far
7809         // away relative to the caster, the shadow volume is a
7810         // cylinder capped at one end.  Testing for the intersection of a
7811         // singly capped cylinder is as simple as checking the distance
7812         // from the center of the receiver to the axis of the shadow cylinder.
7813         // If the distance is less than the sum of the caster's and receiver's
7814         // radii, then we have an eclipse. We also need to verify that the
7815         // receiver is behind the caster when seen from the light source.
7816         float R = receiver.getRadius() + shadowRadius;
7817 
7818         // The stored light position is receiver-relative; thus the caster-to-light
7819         // direction is casterPos - (receiverPos + lightPos)
7820         Point3d lightPosition = posReceiver + light.position;
7821         Vec3d lightToCasterDir = posCaster - lightPosition;
7822         Vec3d receiverToCasterDir = posReceiver - posCaster;
7823 
7824         double dist = distance(posReceiver,
7825                                Ray3d(posCaster, lightToCasterDir));
7826         if (dist < R && lightToCasterDir * receiverToCasterDir > 0.0)
7827         {
7828             Vec3d sunDir = lightToCasterDir;
7829             sunDir.normalize();
7830 
7831             EclipseShadow shadow;
7832             shadow.origin = Point3f((float) dir.x,
7833                                     (float) dir.y,
7834                                     (float) dir.z);
7835             shadow.direction = Vec3f((float) sunDir.x,
7836                                      (float) sunDir.y,
7837                                      (float) sunDir.z);
7838             shadow.penumbraRadius = shadowRadius;
7839 
7840             // The umbra radius will be positive if the apparent size of the occluder
7841             // is greater than the apparent size of the sun, zero if they're equal,
7842             // and negative when the eclipse is partial. The absolute value of the
7843             // umbra radius is the radius of the shadow region with constant depth:
7844             // for total eclipses, this area is actually the umbra, with a depth of
7845             // 1. For annular eclipses and transits, it is less than 1.
7846             shadow.umbraRadius = caster.getRadius() *
7847                 (appOccluderRadius - appSunRadius) / appOccluderRadius;
7848             shadow.maxDepth = std::min(1.0f, square(appOccluderRadius / appSunRadius));
7849 
7850             // Ignore transits that don't produce a visible shadow.
7851             if (shadow.maxDepth > 1.0f / 256.0f)
7852                 shadows.push_back(shadow);
7853 
7854             return true;
7855         }
7856     }
7857 
7858     return false;
7859 }
7860 
7861 
renderPlanet(Body & body,Point3f pos,float distance,float appMag,const Observer & observer,const Quatf & cameraOrientation,float nearPlaneDistance,float farPlaneDistance)7862 void Renderer::renderPlanet(Body& body,
7863                             Point3f pos,
7864                             float distance,
7865                             float appMag,
7866                             const Observer& observer,
7867                             const Quatf& cameraOrientation,
7868                             float nearPlaneDistance,
7869                             float farPlaneDistance)
7870 {
7871     double now = observer.getTime();
7872     float altitude = distance - body.getRadius();
7873     float discSizeInPixels = body.getRadius() /
7874         (max(nearPlaneDistance, altitude) * pixelSize);
7875 
7876     if (discSizeInPixels > 1 && body.hasVisibleGeometry())
7877     {
7878         RenderProperties rp;
7879 
7880         if (displayedSurface.empty())
7881         {
7882             rp.surface = const_cast<Surface*>(&body.getSurface());
7883         }
7884         else
7885         {
7886             rp.surface = body.getAlternateSurface(displayedSurface);
7887             if (rp.surface == NULL)
7888                 rp.surface = const_cast<Surface*>(&body.getSurface());
7889         }
7890         rp.atmosphere = body.getAtmosphere();
7891         rp.rings = body.getRings();
7892         rp.radius = body.getRadius();
7893         rp.geometry = body.getGeometry();
7894         rp.semiAxes = body.getSemiAxes() * (1.0f / rp.radius);
7895         rp.geometryScale = body.getGeometryScale();
7896 
7897         Quatd q = body.getRotationModel(now)->spin(now) *
7898             body.getEclipticToEquatorial(now);
7899 
7900         rp.orientation = body.getOrientation() *
7901             Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
7902 
7903         if (body.getLocations() != NULL && (labelMode & LocationLabels) != 0)
7904             body.computeLocations();
7905 
7906         Vec3f scaleFactors;
7907         bool isNormalized = false;
7908         Geometry* geometry = NULL;
7909         if (rp.geometry != InvalidResource)
7910             geometry = GetGeometryManager()->find(rp.geometry);
7911         if (geometry == NULL || geometry->isNormalized())
7912         {
7913             scaleFactors = rp.semiAxes * rp.radius;
7914             isNormalized = true;
7915         }
7916         else
7917         {
7918             float scale = rp.geometryScale;
7919             scaleFactors = Vec3f(scale, scale, scale);
7920         }
7921 
7922         LightingState lights;
7923         setupObjectLighting(lightSourceList,
7924                             secondaryIlluminators,
7925                             rp.orientation,
7926                             scaleFactors,
7927                             pos,
7928                             isNormalized,
7929 #ifdef USE_HDR
7930                             faintestMag,
7931                             DEFAULT_EXPOSURE + brightPlus, //exposure + brightPlus,
7932                             appMag,
7933 #endif
7934                             lights);
7935         assert(lights.nLights <= MaxLights);
7936 
7937         lights.ambientColor = Vec3f(ambientColor.red(),
7938                                     ambientColor.green(),
7939                                     ambientColor.blue());
7940 
7941         {
7942             // Clear out the list of eclipse shadows
7943             for (unsigned int li = 0; li < lights.nLights; li++)
7944             {
7945                 eclipseShadows[li].clear();
7946                 lights.shadows[li] = &eclipseShadows[li];
7947             }
7948         }
7949 
7950 
7951         // Calculate eclipse circumstances
7952         if ((renderFlags & ShowEclipseShadows) != 0 &&
7953             body.getSystem() != NULL)
7954         {
7955             PlanetarySystem* system = body.getSystem();
7956 
7957             if (system->getPrimaryBody() == NULL &&
7958                 body.getSatellites() != NULL)
7959             {
7960                 // The body is a planet.  Check for eclipse shadows
7961                 // from all of its satellites.
7962                 PlanetarySystem* satellites = body.getSatellites();
7963                 if (satellites != NULL)
7964                 {
7965                     int nSatellites = satellites->getSystemSize();
7966                     for (unsigned int li = 0; li < lights.nLights; li++)
7967                     {
7968                         if (lights.lights[li].castsShadows)
7969                         {
7970                             for (int i = 0; i < nSatellites; i++)
7971                             {
7972                                 testEclipse(body, *satellites->getBody(i),
7973                                             lights.lights[li],
7974                                             now, *lights.shadows[li]);
7975                             }
7976                         }
7977                     }
7978                 }
7979             }
7980             else if (system->getPrimaryBody() != NULL)
7981             {
7982                 for (unsigned int li = 0; li < lights.nLights; li++)
7983                 {
7984                     if (lights.lights[li].castsShadows)
7985                     {
7986                         // The body is a moon.  Check for eclipse shadows from
7987                         // the parent planet and all satellites in the system.
7988                         // Traverse up the hierarchy so that any parent objects
7989                         // of the parent are also considered (TODO: their child
7990                         // objects will not be checked for shadows.)
7991                         Body* planet = system->getPrimaryBody();
7992                         while (planet != NULL)
7993                         {
7994                             testEclipse(body, *planet, lights.lights[li],
7995                                         now, *lights.shadows[li]);
7996                             if (planet->getSystem() != NULL)
7997                                 planet = planet->getSystem()->getPrimaryBody();
7998                             else
7999                                 planet = NULL;
8000                         }
8001 
8002                         int nSatellites = system->getSystemSize();
8003                         for (int i = 0; i < nSatellites; i++)
8004                         {
8005                             if (system->getBody(i) != &body)
8006                             {
8007                                 testEclipse(body, *system->getBody(i),
8008                                             lights.lights[li],
8009                                             now, *lights.shadows[li]);
8010                             }
8011                         }
8012                     }
8013                 }
8014             }
8015         }
8016 
8017         renderObject(pos, distance, now,
8018                      cameraOrientation, nearPlaneDistance, farPlaneDistance,
8019                      rp, lights);
8020 
8021         if (body.getLocations() != NULL && (labelMode & LocationLabels) != 0)
8022         {
8023             // Set up location markers for this body
8024             mountainRep    = MarkerRepresentation(MarkerRepresentation::Triangle, 8.0f, LocationLabelColor);
8025             craterRep      = MarkerRepresentation(MarkerRepresentation::Circle,   8.0f, LocationLabelColor);
8026             observatoryRep = MarkerRepresentation(MarkerRepresentation::Plus,     8.0f, LocationLabelColor);
8027             cityRep        = MarkerRepresentation(MarkerRepresentation::X,        3.0f, LocationLabelColor);
8028             genericLocationRep = MarkerRepresentation(MarkerRepresentation::Square, 8.0f, LocationLabelColor);
8029 
8030             glEnable(GL_DEPTH_TEST);
8031             glDepthMask(GL_FALSE);
8032             glDisable(GL_BLEND);
8033 
8034             // We need a double precision body-relative position of the
8035             // observer, otherwise location labels will tend to jitter.
8036             Vec3d posd = (body.getPosition(observer.getTime()) -
8037                           observer.getPosition()) * astro::microLightYearsToKilometers(1.0);
8038             renderLocations(body, posd, q);
8039 
8040             glDisable(GL_DEPTH_TEST);
8041         }
8042     }
8043 
8044     glEnable(GL_TEXTURE_2D);
8045     glEnable(GL_BLEND);
8046     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
8047 #ifdef USE_HDR
8048     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
8049 #endif
8050 
8051     if (body.isVisibleAsPoint())
8052     {
8053         if (useNewStarRendering)
8054         {
8055             renderObjectAsPoint(pos,
8056                                 body.getRadius(),
8057                                 appMag,
8058                                 faintestMag,
8059                                 discSizeInPixels,
8060                                 body.getSurface().color,
8061                                 cameraOrientation,
8062                                 false, false);
8063         }
8064         else
8065         {
8066             renderObjectAsPoint_nosprite(pos,
8067                                  body.getRadius(),
8068                                  appMag,
8069                                  faintestMag,
8070                                  discSizeInPixels,
8071                                  body.getSurface().color,
8072                                  cameraOrientation,
8073                                  false);
8074         }
8075     }
8076 #ifdef USE_HDR
8077     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
8078 #endif
8079 }
8080 
8081 
renderStar(const Star & star,Point3f pos,float distance,float appMag,Quatf cameraOrientation,double now,float nearPlaneDistance,float farPlaneDistance)8082 void Renderer::renderStar(const Star& star,
8083                           Point3f pos,
8084                           float distance,
8085                           float appMag,
8086                           Quatf cameraOrientation,
8087                           double now,
8088                           float nearPlaneDistance,
8089                           float farPlaneDistance)
8090 {
8091     if (!star.getVisibility())
8092         return;
8093 
8094     Color color = colorTemp->lookupColor(star.getTemperature());
8095     float radius = star.getRadius();
8096     float discSizeInPixels = radius / (distance * pixelSize);
8097 
8098     if (discSizeInPixels > 1)
8099     {
8100         Surface surface;
8101         RenderProperties rp;
8102 
8103         surface.color = color;
8104 
8105         MultiResTexture mtex = star.getTexture();
8106         if (mtex.tex[textureResolution] != InvalidResource)
8107         {
8108             surface.baseTexture = mtex;
8109         }
8110         else
8111         {
8112             surface.baseTexture = InvalidResource;
8113         }
8114         surface.appearanceFlags |= Surface::ApplyBaseTexture;
8115         surface.appearanceFlags |= Surface::Emissive;
8116 
8117         rp.surface = &surface;
8118         rp.rings = NULL;
8119         rp.radius = star.getRadius();
8120         rp.semiAxes = star.getEllipsoidSemiAxes();
8121         rp.geometry = star.getGeometry();
8122 
8123 #ifndef USE_HDR
8124         Atmosphere atmosphere;
8125         Color atmColor(color.red() * 0.5f, color.green() * 0.5f, color.blue() * 0.5f);
8126         atmosphere.height = radius * CoronaHeight;
8127         atmosphere.lowerColor = atmColor;
8128         atmosphere.upperColor = atmColor;
8129         atmosphere.skyColor = atmColor;
8130 
8131         // Use atmosphere effect to give stars a fuzzy fringe
8132         if (rp.geometry == InvalidResource)
8133             rp.atmosphere = &atmosphere;
8134         else
8135 #endif
8136         rp.atmosphere = NULL;
8137 
8138         Quatd q = star.getRotationModel()->orientationAtTime(now);
8139         rp.orientation = Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
8140 
8141         renderObject(pos, distance, now,
8142                      cameraOrientation, nearPlaneDistance, farPlaneDistance,
8143                      rp, LightingState());
8144     }
8145 
8146     glEnable(GL_TEXTURE_2D);
8147     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
8148 #ifdef USE_HDR
8149     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
8150 #endif
8151 
8152 #ifndef USE_HDR
8153     if (useNewStarRendering)
8154     {
8155 #endif
8156         renderObjectAsPoint(pos,
8157                             star.getRadius(),
8158                             appMag,
8159                             faintestMag,
8160                             discSizeInPixels,
8161                             color,
8162                             cameraOrientation,
8163                             true, true);
8164 #ifndef USE_HDR
8165     }
8166     else
8167     {
8168         renderObjectAsPoint_nosprite(pos,
8169                                      star.getRadius(),
8170                                      appMag,
8171                                      faintestPlanetMag,
8172                                      discSizeInPixels,
8173                                      color,
8174                                      cameraOrientation,
8175                                      true);
8176     }
8177 #endif
8178 #ifdef USE_HDR
8179     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
8180 #endif
8181 }
8182 
8183 
8184 static const int MaxCometTailPoints = 120;
8185 static const int CometTailSlices = 48;
8186 struct CometTailVertex
8187 {
8188     Point3f point;
8189     Vec3f normal;
8190     Point3f paraboloidPoint;
8191     float brightness;
8192 };
8193 
8194 static CometTailVertex cometTailVertices[CometTailSlices * MaxCometTailPoints];
8195 
ProcessCometTailVertex(const CometTailVertex & v,const Vec3f & viewDir,float fadeDistFromSun)8196 static void ProcessCometTailVertex(const CometTailVertex& v,
8197                                    const Vec3f& viewDir,
8198                                    float fadeDistFromSun)
8199 {
8200     // If fadeDistFromSun = x/x0 >= 1.0, comet tail starts fading,
8201     // i.e. fadeFactor quickly transits from 1 to 0.
8202 
8203     float fadeFactor = 0.5f - 0.5f * (float) tanh(fadeDistFromSun - 1.0f / fadeDistFromSun);
8204     float shade = abs(viewDir * v.normal * v.brightness * fadeFactor);
8205     glColor4f(0.5f, 0.5f, 0.75f, shade);
8206     glVertex(v.point);
8207 }
8208 
8209 #if 0
8210 static void ProcessCometTailVertex(const CometTailVertex& v,
8211                                    const Point3f& cameraPos)
8212 {
8213     Vec3f viewDir = v.point - cameraPos;
8214     viewDir.normalize();
8215     float shade = abs(viewDir * v.normal * v.brightness);
8216     glColor4f(0.0f, 0.5f, 1.0f, shade);
8217     glVertex(v.point);
8218 }
8219 #endif
8220 
8221 #if 0
8222 static void ProcessCometTailVertex(const CometTailVertex& v,
8223                                    const Point3f& eyePos_obj,
8224                                    float b,
8225                                    float h)
8226 {
8227     float shade = 0.0f;
8228     Vec3f R = v.paraboloidPoint - eyePos_obj;
8229     float c0 = b * (square(eyePos_obj.x) + square(eyePos_obj.y)) + eyePos_obj.z;
8230     float c1 = 2 * b * (R.x * eyePos_obj.x + R.y * eyePos_obj.y) - R.z;
8231     float c2 = b * (square(R.x) + square(R.y));
8232 
8233     float disc = square(c1) - 4 * c0 * c2;
8234 
8235     if (disc < 0.0f)
8236     {
8237         shade = 0.0f;
8238     }
8239     else
8240     {
8241         disc = (float) sqrt(disc);
8242         float s = 1.0f / (2 * c2);
8243         float t0 = (h - eyePos_obj.z) / R.z;
8244         float t1 = (c1 - disc) * s;
8245         float t2 = (c1 + disc) * s;
8246         /*float u0 = max(t0, 0.0f);     Unused*/
8247         float u1, u2;
8248 
8249         if (R.z < 0.0f)
8250         {
8251             u1 = max(t1, t0);
8252             u2 = max(t2, t0);
8253         }
8254         else
8255         {
8256             u1 = min(t1, t0);
8257             u2 = min(t2, t0);
8258         }
8259         u1 = max(0.0f, u1);
8260         u2 = max(0.0f, u2);
8261 
8262         shade = u2 - u1;
8263     }
8264 
8265     glColor4f(0.0f, 0.5f, 1.0f, shade);
8266     glVertex(v.point);
8267 }
8268 #endif
8269 
8270 
8271 // Compute a rough estimate of the visible length of the dust tail.
8272 // TODO: This is old code that needs to be rewritten. For one thing,
8273 // the length is inversely proportional to the distance from the sun,
8274 // whereas the 1/distance^2 is probably more realistic. There should
8275 // also be another parameter that specifies how active the comet is.
cometDustTailLength(float distanceToSun,float radius)8276 static float cometDustTailLength(float distanceToSun,
8277                                  float radius)
8278 {
8279     return (1.0e8f / distanceToSun) * (radius / 5.0f) * 1.0e7f;
8280 }
8281 
8282 
8283 // TODO: Remove unused parameters??
renderCometTail(const Body & body,Point3f pos,double now,float discSizeInPixels)8284 void Renderer::renderCometTail(const Body& body,
8285                                Point3f pos,
8286                                double now,
8287                                float discSizeInPixels)
8288 {
8289     Point3f cometPoints[MaxCometTailPoints];
8290     Point3d pos0 = body.getOrbit(now)->positionAtTime(now);
8291     Point3d pos1 = body.getOrbit(now)->positionAtTime(now - 0.01);
8292     Vec3d vd = pos1 - pos0;
8293     double t = now;
8294     /*float f = 1.0e15f;    Unused*/
8295     /*int nSteps = MaxCometTailPoints;  Unused*/
8296     /*float dt = 10000000.0f / (nSteps * (float) vd.length() * 100.0f);     Unused*/
8297     float distanceFromSun, irradiance_max = 0.0f;
8298     unsigned int li_eff = 0;    // Select the first sun as default to
8299                                 // shut up compiler warnings
8300 
8301     // Adjust the amount of triangles used for the comet tail based on
8302     // the screen size of the comet.
8303     float lod = min(1.0f, max(0.2f, discSizeInPixels / 1000.0f));
8304     int nTailPoints = (int) (MaxCometTailPoints * lod);
8305     int nTailSlices = (int) (CometTailSlices * lod);
8306 
8307     // Find the sun with the largest irrradiance of light onto the comet
8308     // as function of the comet's position;
8309     // irradiance = sun's luminosity / square(distanceFromSun);
8310 
8311     for (unsigned int li = 0; li < lightSourceList.size(); li++)
8312     {
8313         distanceFromSun = (float) (Vec3d(pos.x, pos.y, pos.z) - lightSourceList[li].position).length();
8314         float irradiance = lightSourceList[li].luminosity / square(distanceFromSun);
8315         if (irradiance > irradiance_max)
8316         {
8317             li_eff = li;
8318             irradiance_max = irradiance;
8319         }
8320     }
8321     float fadeDistance = 1.0f / (float) (COMET_TAIL_ATTEN_DIST_SOL * sqrt(irradiance_max));
8322 
8323     // direction to sun with dominant light irradiance:
8324 
8325     Vec3d sd = Vec3d(pos.x, pos.y, pos.z) - lightSourceList[li_eff].position;
8326     Vec3f sunDir = Vec3f((float) sd.x, (float) sd.y, (float) sd.z);
8327     sunDir.normalize();
8328 
8329     float dustTailLength = cometDustTailLength((float) pos0.distanceFromOrigin(), body.getRadius());
8330     float dustTailRadius = dustTailLength * 0.1f;
8331 
8332     Point3f origin(0, 0, 0);
8333     origin -= sunDir * (body.getRadius() * 100);
8334 
8335     int i;
8336     for (i = 0; i < nTailPoints; i++)
8337     {
8338         float alpha = (float) i / (float) nTailPoints;
8339         alpha = alpha * alpha;
8340         cometPoints[i] = origin + sunDir * (dustTailLength * alpha);
8341     }
8342 
8343     // We need three axes to define the coordinate system for rendering the
8344     // comet.  The first axis is the velocity.  We choose the second one
8345     // based on the orientation of the comet.  And the third is just the cross
8346     // product of the first and second axes.
8347     Quatd qd = body.getEclipticToEquatorial(t);
8348     Quatf q((float) qd.w, (float) qd.x, (float) qd.y, (float) qd.z);
8349     Vec3f v = cometPoints[1] - cometPoints[0];
8350     v.normalize();
8351     Vec3f u0 = Vec3f(0, 1, 0) * q.toMatrix3();
8352     Vec3f u1 = Vec3f(1, 0, 0) * q.toMatrix3();
8353     Vec3f u;
8354     if (abs(u0 * v) < abs(u1 * v))
8355         u = v ^ u0;
8356     else
8357         u = v ^ u1;
8358     u.normalize();
8359     Vec3f w = u ^ v;
8360 
8361     glColor4f(0.0f, 1.0f, 1.0f, 0.5f);
8362     glPushMatrix();
8363     glTranslate(pos);
8364 
8365     // glx::glActiveTextureARB(GL_TEXTURE0_ARB);
8366     glDisable(GL_TEXTURE_2D);
8367     glDisable(GL_LIGHTING);
8368     glDepthMask(GL_FALSE);
8369     glEnable(GL_BLEND);
8370     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
8371 
8372     for (i = 0; i < nTailPoints; i++)
8373     {
8374         float brightness = 1.0f - (float) i / (float) (nTailPoints - 1);
8375         Vec3f v0, v1;
8376         float sectionLength;
8377         if (i != 0 && i != nTailPoints - 1)
8378         {
8379             v0 = cometPoints[i] - cometPoints[i - 1];
8380             v1 = cometPoints[i + 1] - cometPoints[i];
8381             sectionLength = v0.length();
8382             v0.normalize();
8383             v1.normalize();
8384             Vec3f axis = v1 ^ v0;
8385             float axisLength = axis.length();
8386             if (axisLength > 1e-5f)
8387             {
8388                 axis *= 1.0f / axisLength;
8389                 q.setAxisAngle(axis, (float) asin(axisLength));
8390                 Mat3f m = q.toMatrix3();
8391 
8392                 u = u * m;
8393                 v = v * m;
8394                 w = w * m;
8395             }
8396         }
8397         else if (i == 0)
8398         {
8399             v0 = cometPoints[i + 1] - cometPoints[i];
8400             sectionLength = v0.length();
8401             v0.normalize();
8402             v1 = v0;
8403         }
8404         else
8405         {
8406             v0 = v1 = cometPoints[i] - cometPoints[i - 1];
8407             sectionLength = v0.length();
8408             v0.normalize();
8409             v1 = v0;
8410         }
8411 
8412         float radius = (float) i / (float) nTailPoints *
8413             dustTailRadius;
8414         float dr = (dustTailRadius / (float) nTailPoints) /
8415             sectionLength;
8416 
8417         float w0 = (float) atan(dr);
8418         float d = std::sqrt(1.0f + w0 * w0);
8419         float w1 = 1.0f / d;
8420         w0 = w0 / d;
8421 
8422         // Special case the first vertex in the comet tail
8423         if (i == 0)
8424         {
8425             w0 = 1;
8426             w1 = 0.0f;
8427         }
8428 
8429         for (int j = 0; j < nTailSlices; j++)
8430         {
8431             float theta = (float) (2 * PI * (float) j / nTailSlices);
8432             float s = (float) sin(theta);
8433             float c = (float) cos(theta);
8434             CometTailVertex* vtx = &cometTailVertices[i * nTailSlices + j];
8435             vtx->normal = u * (s * w1) + w * (c * w1) + v * w0;
8436             s *= radius;
8437             c *= radius;
8438 
8439             vtx->point = Point3f(cometPoints[i].x + u.x * s + w.x * c,
8440                                  cometPoints[i].y + u.y * s + w.y * c,
8441                                  cometPoints[i].z + u.z * s + w.z * c);
8442             vtx->brightness = brightness;
8443             vtx->paraboloidPoint =
8444                 Point3f(c, s, square((float) i / (float) MaxCometTailPoints));
8445         }
8446     }
8447 
8448     Vec3f viewDir = pos - Point3f(0.0f, 0.0f, 0.0f);
8449     viewDir.normalize();
8450 
8451     glDisable(GL_CULL_FACE);
8452     for (i = 0; i < nTailPoints - 1; i++)
8453     {
8454         glBegin(GL_QUAD_STRIP);
8455         int n = i * nTailSlices;
8456         for (int j = 0; j < nTailSlices; j++)
8457         {
8458             ProcessCometTailVertex(cometTailVertices[n + j], viewDir, fadeDistance);
8459             ProcessCometTailVertex(cometTailVertices[n + j + nTailSlices],
8460                                    viewDir, fadeDistance);
8461         }
8462         ProcessCometTailVertex(cometTailVertices[n], viewDir, fadeDistance);
8463         ProcessCometTailVertex(cometTailVertices[n + nTailSlices],
8464                                viewDir, fadeDistance);
8465         glEnd();
8466     }
8467     glEnable(GL_CULL_FACE);
8468 
8469     glBegin(GL_LINE_STRIP);
8470     for (i = 0; i < nTailPoints; i++)
8471     {
8472         glVertex(cometPoints[i]);
8473     }
8474     glEnd();
8475 
8476     glEnable(GL_TEXTURE_2D);
8477     glEnable(GL_BLEND);
8478 
8479     glPopMatrix();
8480 }
8481 
8482 
8483 // Render a reference mark
renderReferenceMark(const ReferenceMark & refMark,Point3f pos,float distance,double now,float nearPlaneDistance)8484 void Renderer::renderReferenceMark(const ReferenceMark& refMark,
8485                                    Point3f pos,
8486                                    float distance,
8487                                    double now,
8488                                    float nearPlaneDistance)
8489 {
8490     float altitude = distance - refMark.boundingSphereRadius();
8491     float discSizeInPixels = refMark.boundingSphereRadius() /
8492         (max(nearPlaneDistance, altitude) * pixelSize);
8493 
8494     if (discSizeInPixels <= 1)
8495         return;
8496 
8497     // Apply the modelview transform for the object
8498     glPushMatrix();
8499     glTranslate(pos);
8500 
8501     refMark.render(this, pos, discSizeInPixels, now);
8502 
8503     glPopMatrix();
8504 
8505     glDisable(GL_DEPTH_TEST);
8506     glDepthMask(GL_FALSE);
8507     glEnable(GL_TEXTURE_2D);
8508     glEnable(GL_BLEND);
8509     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
8510 }
8511 
8512 
8513 // Helper function to compute the luminosity of a perfectly
8514 // reflective disc with the specified radius. This is used as an upper
8515 // bound for the apparent brightness of an object when culling
8516 // invisible objects.
luminosityAtOpposition(float sunLuminosity,float distanceFromSun,float objRadius)8517 static float luminosityAtOpposition(float sunLuminosity,
8518                                     float distanceFromSun,
8519                                     float objRadius)
8520 {
8521     // Compute the total power of the star in Watts
8522     double power = astro::SOLAR_POWER * sunLuminosity;
8523 
8524     // Compute the irradiance at the body's distance from the star
8525     double irradiance = power / sphereArea(distanceFromSun * 1000);
8526 
8527     // Compute the total energy hitting the planet; assume an albedo of 1.0, so
8528     // reflected energy = incident energy.
8529     double incidentEnergy = irradiance * circleArea(objRadius * 1000);
8530 
8531     // Compute the luminosity (i.e. power relative to solar power)
8532     return (float) (incidentEnergy / astro::SOLAR_POWER);
8533 }
8534 
8535 
addRenderListEntries(RenderListEntry & rle,Body & body,bool isLabeled)8536 void Renderer::addRenderListEntries(RenderListEntry& rle,
8537                                     Body& body,
8538                                     bool isLabeled)
8539 {
8540     bool visibleAsPoint = rle.appMag < faintestPlanetMag && body.isVisibleAsPoint();
8541 
8542     if (rle.discSizeInPixels > 1 || visibleAsPoint || isLabeled)
8543     {
8544         rle.renderableType = RenderListEntry::RenderableBody;
8545         rle.body = &body;
8546 
8547         if (body.getGeometry() != InvalidResource && rle.discSizeInPixels > 1)
8548         {
8549             Geometry* geometry = GetGeometryManager()->find(body.getGeometry());
8550             if (geometry == NULL)
8551                 rle.isOpaque = true;
8552             else
8553                 rle.isOpaque = geometry->isOpaque();
8554         }
8555         else
8556         {
8557             rle.isOpaque = true;
8558         }
8559         rle.radius = body.getRadius();
8560         renderList.push_back(rle);
8561     }
8562 
8563     if (body.getClassification() == Body::Comet && (renderFlags & ShowCometTails) != 0)
8564     {
8565         float radius = cometDustTailLength(rle.sun.length(), body.getRadius());
8566         float discSize = (radius / (float) rle.distance) / pixelSize;
8567         if (discSize > 1)
8568         {
8569             rle.renderableType = RenderListEntry::RenderableCometTail;
8570             rle.body = &body;
8571             rle.isOpaque = false;
8572             rle.radius = radius;
8573             rle.discSizeInPixels = discSize;
8574             renderList.push_back(rle);
8575         }
8576     }
8577 
8578     const list<ReferenceMark*>* refMarks = body.getReferenceMarks();
8579     if (refMarks != NULL)
8580     {
8581         for (list<ReferenceMark*>::const_iterator iter = refMarks->begin();
8582             iter != refMarks->end(); ++iter)
8583         {
8584             const ReferenceMark* rm = *iter;
8585 
8586             rle.renderableType = RenderListEntry::RenderableReferenceMark;
8587             rle.refMark = rm;
8588             rle.isOpaque = rm->isOpaque();
8589             rle.radius = rm->boundingSphereRadius();
8590             renderList.push_back(rle);
8591         }
8592     }
8593 }
8594 
8595 
buildRenderLists(const Point3d & astrocentricObserverPos,const Frustum & viewFrustum,const Vec3d & viewPlaneNormal,const Vec3d & frameCenter,const FrameTree * tree,const Observer & observer,double now)8596 void Renderer::buildRenderLists(const Point3d& astrocentricObserverPos,
8597                                 const Frustum& viewFrustum,
8598                                 const Vec3d& viewPlaneNormal,
8599                                 const Vec3d& frameCenter,
8600                                 const FrameTree* tree,
8601                                 const Observer& observer,
8602                                 double now)
8603 {
8604     int labelClassMask = translateLabelModeToClassMask(labelMode);
8605 
8606     Mat3f viewMat = observer.getOrientationf().toMatrix3();
8607     Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
8608     double invCosViewAngle = 1.0 / cosViewConeAngle;
8609     double sinViewAngle = sqrt(1.0 - square(cosViewConeAngle));
8610 
8611     unsigned int nChildren = tree != NULL ? tree->childCount() : 0;
8612     for (unsigned int i = 0; i < nChildren; i++)
8613     {
8614         const TimelinePhase* phase = tree->getChild(i);
8615 
8616         // No need to do anything if the phase isn't active now
8617         if (!phase->includes(now))
8618             continue;
8619 
8620         Body* body = phase->body();
8621 
8622         // pos_s: sun-relative position of object
8623         // pos_v: viewer-relative position of object
8624 
8625         // Get the position of the body relative to the sun.
8626         Point3d p = phase->orbit()->positionAtTime(now);
8627         ReferenceFrame* frame = phase->orbitFrame();
8628         Vec3d pos_s = frameCenter + Vec3d(p.x, p.y, p.z) * frame->getOrientation(now).toMatrix3();
8629 
8630         // We now have the positions of the observer and the planet relative
8631         // to the sun.  From these, compute the position of the body
8632         // relative to the observer.
8633         Vec3d pos_v = Point3d(pos_s.x, pos_s.y, pos_s.z) - astrocentricObserverPos;
8634 
8635         // dist_vn: distance along view normal from the viewer to the
8636         // projection of the object's center.
8637         double dist_vn = viewPlaneNormal * pos_v;
8638 
8639         // Vector from object center to its projection on the view normal.
8640         Vec3d toViewNormal = pos_v - dist_vn * viewPlaneNormal;
8641 
8642         float cullingRadius = body->getCullingRadius();
8643 
8644         // The result of the planetshine test can be reused for the view cone
8645         // test, but only when the object's light influence sphere is larger
8646         // than the geometry. This is not
8647         bool viewConeTestFailed = false;
8648         if (body->isSecondaryIlluminator())
8649         {
8650             float influenceRadius = body->getBoundingRadius() + (body->getRadius() * PLANETSHINE_DISTANCE_LIMIT_FACTOR);
8651             if (dist_vn > -influenceRadius)
8652             {
8653                 double maxPerpDist = (influenceRadius + dist_vn * sinViewAngle) * invCosViewAngle;
8654                 double perpDistSq = toViewNormal * toViewNormal;
8655                 if (perpDistSq < maxPerpDist * maxPerpDist)
8656                 {
8657                     if ((body->getRadius() / (float) pos_v.length()) / pixelSize > PLANETSHINE_PIXEL_SIZE_LIMIT)
8658                     {
8659                         // add to planetshine list if larger than 1/10 pixel
8660 #if DEBUG_SECONDARY_ILLUMINATION
8661                         clog << "Planetshine: " << body->getName()
8662                              << ", " << body->getRadius() / (float) pos_v.length() / pixelSize << endl;
8663 #endif
8664                         SecondaryIlluminator illum;
8665                         illum.body = body;
8666                         illum.position_v = pos_v;
8667                         illum.radius = body->getRadius();
8668                         secondaryIlluminators.push_back(illum);
8669                     }
8670                 }
8671                 else
8672                 {
8673                     viewConeTestFailed = influenceRadius > cullingRadius;
8674                 }
8675             }
8676             else
8677             {
8678                 viewConeTestFailed = influenceRadius > cullingRadius;
8679             }
8680         }
8681 
8682         bool insideViewCone = false;
8683         if (!viewConeTestFailed)
8684         {
8685             float radius = body->getCullingRadius();
8686             if (dist_vn > -radius)
8687             {
8688                 double maxPerpDist = (radius + dist_vn * sinViewAngle) * invCosViewAngle;
8689                 double perpDistSq = toViewNormal * toViewNormal;
8690                 insideViewCone = perpDistSq < maxPerpDist * maxPerpDist;
8691             }
8692         }
8693 
8694         if (insideViewCone)
8695         {
8696             // Calculate the distance to the viewer
8697             double dist_v = pos_v.length();
8698 
8699             // Calculate the size of the planet/moon disc in pixels
8700             float discSize = (body->getCullingRadius() / (float) dist_v) / pixelSize;
8701 
8702             // Compute the apparent magnitude; instead of summing the reflected
8703             // light from all nearby stars, we just consider the one with the
8704             // highest apparent brightness.
8705             float appMag = 100.0f;
8706             for (unsigned int li = 0; li < lightSourceList.size(); li++)
8707             {
8708                 Vec3d sunPos = pos_v - lightSourceList[li].position;
8709                 appMag = min(appMag, body->getApparentMagnitude(lightSourceList[li].luminosity, sunPos, pos_v));
8710             }
8711 
8712             bool visibleAsPoint = appMag < faintestPlanetMag && body->isVisibleAsPoint();
8713             bool isLabeled = (body->getOrbitClassification() & labelClassMask) != 0;
8714             bool visible = body->isVisible();
8715 
8716             if ((discSize > 1 || visibleAsPoint || isLabeled) && visible)
8717             {
8718                 RenderListEntry rle;
8719 
8720                 rle.position = Point3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z);
8721                 rle.distance = (float) dist_v;
8722                 rle.centerZ = Vec3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z) * viewMatZ;
8723                 rle.appMag   = appMag;
8724                 rle.discSizeInPixels = body->getRadius() / ((float) dist_v * pixelSize);
8725 
8726                 // TODO: Remove this. It's only used in two places: for calculating comet tail
8727                 // length, and for calculating sky brightness to adjust the limiting magnitude.
8728                 // In both cases, it's the wrong quantity to use (e.g. for objects with orbits
8729                 // defined relative to the SSB.)
8730                 rle.sun = Vec3f((float) -pos_s.x, (float) -pos_s.y, (float) -pos_s.z);
8731 
8732                 addRenderListEntries(rle, *body, isLabeled);
8733             }
8734         }
8735 
8736         const FrameTree* subtree = body->getFrameTree();
8737         if (subtree != NULL)
8738         {
8739             double dist_v = pos_v.length();
8740             bool traverseSubtree = false;
8741 
8742             // There are two different tests available to determine whether we can reject
8743             // the object's subtree. If the subtree contains no light reflecting objects,
8744             // then render the subtree only when:
8745             //    - the subtree bounding sphere intersects the view frustum, and
8746             //    - the subtree contains an object bright or large enough to be visible.
8747             // Otherwise, render the subtree when any of the above conditions are
8748             // true or when a subtree object could potentially illuminate something
8749             // in the view cone.
8750             float minPossibleDistance = (float) (dist_v - subtree->boundingSphereRadius());
8751             float brightestPossible = 0.0;
8752             float largestPossible = 0.0;
8753 
8754             // If the viewer is not within the subtree bounding sphere, see if we can cull it because
8755             // it contains no objects brighter than the limiting magnitude and no objects that will
8756             // be larger than one pixel in size.
8757             if (minPossibleDistance > 1.0f)
8758             {
8759                 // Figure out the magnitude of the brightest possible object in the subtree.
8760 
8761                 // Compute the luminosity from reflected light of the largest object in the subtree
8762                 float lum = 0.0f;
8763                 for (unsigned int li = 0; li < lightSourceList.size(); li++)
8764                 {
8765                     Vec3d sunPos = pos_v - lightSourceList[li].position;
8766                     lum += luminosityAtOpposition(lightSourceList[li].luminosity, (float) sunPos.length(), (float) subtree->maxChildRadius());
8767                 }
8768                 brightestPossible = astro::lumToAppMag(lum, astro::kilometersToLightYears(minPossibleDistance));
8769                 largestPossible = (float) subtree->maxChildRadius() / (float) minPossibleDistance / pixelSize;
8770             }
8771             else
8772             {
8773                 // Viewer is within the bounding sphere, so the object could be very close.
8774                 // Assume that an object in the subree could be very bright or large,
8775                 // so no culling will occur.
8776                 brightestPossible = -100.0f;
8777                 largestPossible = 100.0f;
8778             }
8779 
8780             if (brightestPossible < faintestPlanetMag || largestPossible > 1.0f)
8781             {
8782                 // See if the object or any of its children are within the view frustum
8783                 if (viewFrustum.testSphere(Point3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z), (float) subtree->boundingSphereRadius()) != Frustum::Outside)
8784                 {
8785                     traverseSubtree = true;
8786                 }
8787             }
8788 
8789             // If the subtree contains secondary illuminators, do one last check if it hasn't
8790             // already been determined if we need to traverse the subtree: see if something
8791             // in the subtree could possibly contribute significant illumination to an
8792             // object in the view cone.
8793             if (subtree->containsSecondaryIlluminators() &&
8794                 !traverseSubtree                         &&
8795                 largestPossible > PLANETSHINE_PIXEL_SIZE_LIMIT)
8796             {
8797                 float influenceRadius = (float) (subtree->boundingSphereRadius() +
8798                     (subtree->maxChildRadius() * PLANETSHINE_DISTANCE_LIMIT_FACTOR));
8799                 if (dist_vn > -influenceRadius)
8800                 {
8801                     double maxPerpDist = (influenceRadius + dist_vn * sinViewAngle) * invCosViewAngle;
8802                     double perpDistSq = toViewNormal * toViewNormal;
8803                     if (perpDistSq < maxPerpDist * maxPerpDist)
8804                         traverseSubtree = true;
8805                 }
8806             }
8807 
8808             if (traverseSubtree)
8809             {
8810                 buildRenderLists(astrocentricObserverPos,
8811                                  viewFrustum,
8812                                  viewPlaneNormal,
8813                                  pos_s,
8814                                  subtree,
8815                                  observer,
8816                                  now);
8817             }
8818         } // end subtree traverse
8819 
8820     }
8821 }
8822 
8823 
buildOrbitLists(const Point3d & astrocentricObserverPos,const Quatf & observerOrientation,const Frustum & viewFrustum,const FrameTree * tree,double now)8824 void Renderer::buildOrbitLists(const Point3d& astrocentricObserverPos,
8825                                const Quatf& observerOrientation,
8826                                const Frustum& viewFrustum,
8827                                const FrameTree* tree,
8828                                double now)
8829 {
8830     Mat3f viewMat = observerOrientation.toMatrix3();
8831     Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
8832 
8833     unsigned int nChildren = tree != NULL ? tree->childCount() : 0;
8834     for (unsigned int i = 0; i < nChildren; i++)
8835     {
8836         const TimelinePhase* phase = tree->getChild(i);
8837 
8838         // No need to do anything if the phase isn't active now
8839         if (!phase->includes(now))
8840             continue;
8841 
8842         Body* body = phase->body();
8843 
8844         // pos_s: sun-relative position of object
8845         // pos_v: viewer-relative position of object
8846 
8847         // Get the position of the body relative to the sun.
8848         Point3d pos_s = body->getAstrocentricPosition(now);
8849 
8850         // We now have the positions of the observer and the planet relative
8851         // to the sun.  From these, compute the position of the body
8852         // relative to the observer.
8853         Vec3d pos_v = pos_s - astrocentricObserverPos;
8854 
8855         // Only show orbits for major bodies or selected objects.
8856         Body::VisibilityPolicy orbitVis = body->getOrbitVisibility();
8857 
8858         if (body->isVisible() &&
8859             (body == highlightObject.body() ||
8860              orbitVis == Body::AlwaysVisible ||
8861              (orbitVis == Body::UseClassVisibility && (body->getOrbitClassification() & orbitMask) != 0)))
8862         {
8863             Point3d orbitOrigin(0.0, 0.0, 0.0);
8864             Selection centerObject = phase->orbitFrame()->getCenter();
8865             if (centerObject.body() != NULL)
8866             {
8867                 orbitOrigin = centerObject.body()->getAstrocentricPosition(now);
8868             }
8869 
8870             // Calculate the origin of the orbit relative to the observer
8871             Vec3d relOrigin = orbitOrigin - astrocentricObserverPos;
8872             Vec3f origf((float) relOrigin.x, (float) relOrigin.y, (float) relOrigin.z);
8873 
8874             // Compute the size of the orbit in pixels
8875             double originDistance = pos_v.length();
8876             double boundingRadius = body->getOrbit(now)->getBoundingRadius();
8877             float orbitRadiusInPixels = (float) (boundingRadius / (originDistance * pixelSize));
8878 
8879             if (orbitRadiusInPixels > minOrbitSize)
8880             {
8881                 // Add the orbit of this body to the list of orbits to be rendered
8882                 OrbitPathListEntry path;
8883                 path.body = body;
8884                 path.star = NULL;
8885                 path.centerZ = origf * viewMatZ;
8886                 path.radius = (float) boundingRadius;
8887                 path.origin = Point3f(origf.x, origf.y, origf.z);
8888                 path.opacity = sizeFade(orbitRadiusInPixels, minOrbitSize, 2.0f);
8889                 orbitPathList.push_back(path);
8890             }
8891         }
8892 
8893         const FrameTree* subtree = body->getFrameTree();
8894         if (subtree != NULL)
8895         {
8896             // Only try to render orbits of child objects when:
8897             //   - The apparent size of the subtree bounding sphere is large enough that
8898             //     orbit paths will be visible, and
8899             //   - The subtree bounding sphere isn't outside the view frustum
8900             double dist_v = pos_v.length();
8901             float distanceToBoundingSphere = (float) (dist_v - subtree->boundingSphereRadius());
8902             bool traverseSubtree = false;
8903             if (distanceToBoundingSphere > 0.0f)
8904             {
8905                 // We're inside the subtree's bounding sphere
8906                 traverseSubtree = true;
8907             }
8908             else
8909             {
8910                 float maxPossibleOrbitSize = (float) subtree->boundingSphereRadius() / ((float) dist_v * pixelSize);
8911                 if (maxPossibleOrbitSize > minOrbitSize)
8912                     traverseSubtree = true;
8913             }
8914 
8915             if (traverseSubtree)
8916             {
8917                 // See if the object or any of its children are within the view frustum
8918                 if (viewFrustum.testSphere(Point3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z), (float) subtree->boundingSphereRadius()) != Frustum::Outside)
8919                 {
8920                     buildOrbitLists(astrocentricObserverPos,
8921                                     observerOrientation,
8922                                     viewFrustum,
8923                                     subtree,
8924                                     now);
8925                 }
8926             }
8927         } // end subtree traverse
8928     }
8929 }
8930 
8931 
buildLabelLists(const Frustum & viewFrustum,double now)8932 void Renderer::buildLabelLists(const Frustum& viewFrustum,
8933                                double now)
8934 {
8935     int labelClassMask = translateLabelModeToClassMask(labelMode);
8936     Body* lastPrimary = NULL;
8937     Sphered primarySphere;
8938 
8939     for (vector<RenderListEntry>::const_iterator iter = renderList.begin();
8940          iter != renderList.end(); iter++)
8941     {
8942         int classification = iter->body->getOrbitClassification();
8943 
8944         if (iter->renderableType == RenderListEntry::RenderableBody &&
8945             (classification & labelClassMask)                       &&
8946             viewFrustum.testSphere(iter->position, iter->radius) != Frustum::Outside)
8947         {
8948             const Body* body = iter->body;
8949             Vec3f pos(iter->position.x, iter->position.y, iter->position.z);
8950 
8951             float boundingRadiusSize = (float) (body->getOrbit(now)->getBoundingRadius() / iter->distance) / pixelSize;
8952             if (boundingRadiusSize > minOrbitSize)
8953             {
8954                 Color labelColor;
8955                 float opacity = sizeFade(boundingRadiusSize, minOrbitSize, 2.0f);
8956 
8957                 switch (classification)
8958                 {
8959                 case Body::Planet:
8960                     labelColor = PlanetLabelColor;
8961                     break;
8962                 case Body::DwarfPlanet:
8963                     labelColor = DwarfPlanetLabelColor;
8964                     break;
8965                 case Body::Moon:
8966                     labelColor = MoonLabelColor;
8967                     break;
8968                 case Body::MinorMoon:
8969                     labelColor = MinorMoonLabelColor;
8970                     break;
8971                 case Body::Asteroid:
8972                     labelColor = AsteroidLabelColor;
8973                     break;
8974                 case Body::Comet:
8975                     labelColor = CometLabelColor;
8976                     break;
8977                 case Body::Spacecraft:
8978                     labelColor = SpacecraftLabelColor;
8979                     break;
8980                 default:
8981                     labelColor = Color::Black;
8982                     break;
8983                 }
8984 
8985                 labelColor = Color(labelColor, opacity * labelColor.alpha());
8986 
8987                 if (!body->getName().empty())
8988                 {
8989                     bool isBehindPrimary = false;
8990 
8991                     const TimelinePhase* phase = body->getTimeline()->findPhase(now);
8992                     Body* primary = phase->orbitFrame()->getCenter().body();
8993                     if (primary != NULL && (primary->getClassification() & Body::Invisible) != 0)
8994                     {
8995                         Body* parent = phase->orbitFrame()->getCenter().body();
8996                         if (parent != NULL)
8997                             primary = parent;
8998                     }
8999 
9000                     // Position the label slightly in front of the object along a line from
9001                     // object center to viewer.
9002                     pos = pos * (1.0f - body->getBoundingRadius() * 1.01f / pos.length());
9003 
9004                     // Try and position the label so that it's not partially
9005                     // occluded by other objects. We'll consider just the object
9006                     // that the labeled body is orbiting (its primary) as a
9007                     // potential occluder. If a ray from the viewer to labeled
9008                     // object center intersects the occluder first, skip
9009                     // rendering the object label. Otherwise, ensure that the
9010                     // label is completely in front of the primary by projecting
9011                     // it onto the plane tangent to the primary at the
9012                     // viewer-primary intersection point. Whew. Don't do any of
9013                     // this if the primary isn't an ellipsoid.
9014                     //
9015                     // This only handles the problem of partial label occlusion
9016                     // for low orbiting and surface positioned objects, but that
9017                     // case is *much* more common than other possibilities.
9018                     if (primary != NULL && primary->isEllipsoid())
9019                     {
9020                         // In the typical case, we're rendering labels for many
9021                         // objects that orbit the same primary. Avoid repeatedly
9022                         // calling getPosition() by caching the last primary
9023                         // position.
9024                         if (primary != lastPrimary)
9025                         {
9026                             Point3d p = phase->orbit()->positionAtTime(now) *
9027                                         phase->orbitFrame()->getOrientation(now).toMatrix3();
9028                             Vec3d v(iter->position.x - p.x, iter->position.y - p.y, iter->position.z - p.z);
9029 
9030                             primarySphere = Sphered(Point3d(v.x, v.y, v.z),
9031                                                     primary->getRadius());
9032                             lastPrimary = primary;
9033                         }
9034 
9035                         Ray3d testRay(Point3d(0.0, 0.0, 0.0), Vec3d(pos.x, pos.y, pos.z));
9036 
9037                         // Test the viewer-to-labeled object ray against
9038                         // the primary sphere (TODO: handle ellipsoids)
9039                         double t = 0.0;
9040                         if (testIntersection(testRay, primarySphere, t))
9041                         {
9042                             // Center of labeled object is behind primary
9043                             // sphere; mark it for rejection.
9044                             isBehindPrimary = t < 1.0;
9045                         }
9046 
9047                         if (!isBehindPrimary)
9048                         {
9049                             // Not rejected. Compute the plane tangent to
9050                             // the primary at the viewer-to-primary
9051                             // intersection point.
9052                             Vec3d primaryVec(primarySphere.center.x,
9053                                              primarySphere.center.y,
9054                                              primarySphere.center.z);
9055                             double distToPrimary = primaryVec.length();
9056                             Planed primaryTangentPlane(primaryVec, primaryVec * (primaryVec * (1.0 - primarySphere.radius / distToPrimary)));
9057 
9058                             // Compute the intersection of the viewer-to-labeled
9059                             // object ray with the tangent plane.
9060                             float u = (float) (primaryTangentPlane.d / (primaryTangentPlane.normal * Vec3d(pos.x, pos.y, pos.z)));
9061 
9062                             // If the intersection point is closer to the viewer
9063                             // than the label, then project the label onto the
9064                             // tangent plane.
9065                             if (u < 1.0f && u > 0.0f)
9066                             {
9067                                 pos = pos * u;
9068                             }
9069                         }
9070                     }
9071 
9072                     addSortedAnnotation(NULL, body->getName(true), labelColor,
9073                                         Point3f(pos.x, pos.y, pos.z));
9074                 }
9075             }
9076         }
9077     } // for each render list entry
9078 }
9079 
9080 
9081 // Add a star orbit to the render list
addStarOrbitToRenderList(const Star & star,const Observer & observer,double now)9082 void Renderer::addStarOrbitToRenderList(const Star& star,
9083                                         const Observer& observer,
9084                                         double now)
9085 {
9086     // If the star isn't fixed, add its orbit to the render list
9087     if ((renderFlags & ShowOrbits) != 0 &&
9088         ((orbitMask & Body::Stellar) != 0 || highlightObject.star() == &star))
9089     {
9090         Mat3f viewMat = observer.getOrientationf().toMatrix3();
9091         Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
9092 
9093         if (star.getOrbit() != NULL)
9094         {
9095             // Get orbit origin relative to the observer
9096             Vec3d orbitOrigin = star.getOrbitBarycenterPosition(now) - observer.getPosition();
9097             orbitOrigin *= astro::microLightYearsToKilometers(1.0);
9098 
9099             Vec3f origf((float) orbitOrigin.x, (float) orbitOrigin.y, (float) orbitOrigin.z);
9100 
9101             // Compute the size of the orbit in pixels
9102             double originDistance = orbitOrigin.length();
9103             double boundingRadius = star.getOrbit()->getBoundingRadius();
9104             float orbitRadiusInPixels = (float) (boundingRadius / (originDistance * pixelSize));
9105 
9106             if (orbitRadiusInPixels > minOrbitSize)
9107             {
9108                 // Add the orbit of this body to the list of orbits to be rendered
9109                 OrbitPathListEntry path;
9110                 path.star = &star;
9111                 path.body = NULL;
9112                 path.centerZ = origf * viewMatZ;
9113                 path.radius = (float) boundingRadius;
9114                 path.origin = Point3f(origf.x, origf.y, origf.z);
9115                 path.opacity = sizeFade(orbitRadiusInPixels, minOrbitSize, 2.0f);
9116                 orbitPathList.push_back(path);
9117             }
9118         }
9119     }
9120 }
9121 
9122 
9123 template <class OBJ, class PREC> class ObjectRenderer : public OctreeProcessor<OBJ, PREC>
9124 {
9125  public:
9126     ObjectRenderer(const PREC distanceLimit);
9127 
process(const OBJ &,PREC,float)9128     void process(const OBJ&, PREC, float) {};
9129 
9130  public:
9131     const Observer* observer;
9132 
9133     GLContext* context;
9134     Renderer*  renderer;
9135 
9136     Vec3f viewNormal;
9137 
9138     float fov;
9139     float size;
9140     float pixelSize;
9141     float faintestMag;
9142     float faintestMagNight;
9143     float saturationMag;
9144 #ifdef USE_HDR
9145     float exposure;
9146 #endif
9147     float brightnessScale;
9148     float brightnessBias;
9149     float distanceLimit;
9150 
9151     // Objects brighter than labelThresholdMag will be labeled
9152     float labelThresholdMag;
9153 
9154     // These are not fully used by this template's descendants
9155     // but we place them here just in case a more sophisticated
9156     // rendering scheme is implemented:
9157     int nRendered;
9158     int nClose;
9159     int nBright;
9160     int nProcessed;
9161     int nLabelled;
9162 
9163     int renderFlags;
9164     int labelMode;
9165 };
9166 
9167 
9168 template <class OBJ, class PREC>
ObjectRenderer(const PREC _distanceLimit)9169 ObjectRenderer<OBJ, PREC>::ObjectRenderer(const PREC _distanceLimit) :
9170     distanceLimit((float) _distanceLimit),
9171 #ifdef USE_HDR
9172     exposure     (0.0f),
9173 #endif
9174     nRendered    (0),
9175     nClose       (0),
9176     nBright      (0),
9177     nProcessed   (0),
9178     nLabelled    (0)
9179 {
9180 }
9181 
9182 
9183 class StarRenderer : public ObjectRenderer<Star, float>
9184 {
9185  public:
9186     StarRenderer();
9187 
9188     void process(const Star& star, float distance, float appMag);
9189 
9190  public:
9191     Point3d obsPos;
9192 
9193     vector<Renderer::Particle>*      glareParticles;
9194     vector<RenderListEntry>*         renderList;
9195     Renderer::StarVertexBuffer*      starVertexBuffer;
9196     Renderer::PointStarVertexBuffer* pointStarVertexBuffer;
9197 
9198     const StarDatabase* starDB;
9199 
9200     bool   useScaledDiscs;
9201     GLenum starPrimitive;
9202     float  maxDiscSize;
9203 
9204     float cosFOV;
9205 
9206     const ColorTemperatureTable* colorTemp;
9207 #ifdef DEBUG_HDR_ADAPT
9208     float minMag;
9209     float maxMag;
9210     float minAlpha;
9211     float maxAlpha;
9212     float maxSize;
9213     float above;
9214     unsigned long countAboveN;
9215     unsigned long total;
9216 #endif
9217 };
9218 
9219 
StarRenderer()9220 StarRenderer::StarRenderer() :
9221     ObjectRenderer<Star, float>(STAR_DISTANCE_LIMIT),
9222     starVertexBuffer     (NULL),
9223     pointStarVertexBuffer(NULL),
9224     useScaledDiscs       (false),
9225     maxDiscSize          (1.0f),
9226     cosFOV               (1.0f),
9227     colorTemp            (NULL)
9228 {
9229 }
9230 
9231 
process(const Star & star,float distance,float appMag)9232 void StarRenderer::process(const Star& star, float distance, float appMag)
9233 {
9234     nProcessed++;
9235 
9236     Point3f starPos = star.getPosition();
9237     Vec3f   relPos((float) ((double) starPos.x - obsPos.x),
9238                    (float) ((double) starPos.y - obsPos.y),
9239                    (float) ((double) starPos.z - obsPos.z));
9240     float   orbitalRadius = star.getOrbitalRadius();
9241     bool    hasOrbit = orbitalRadius > 0.0f;
9242 
9243     if (distance > distanceLimit)
9244         return;
9245 
9246     if (relPos * viewNormal > 0 || relPos.x * relPos.x < 0.1f || hasOrbit)
9247     {
9248 #ifdef HDR_COMPRESS
9249         Color starColorFull = colorTemp->lookupColor(star.getTemperature());
9250         Color starColor(starColorFull.red()   * 0.5f,
9251                         starColorFull.green() * 0.5f,
9252                         starColorFull.blue()  * 0.5f);
9253 #else
9254         Color starColor = colorTemp->lookupColor(star.getTemperature());
9255 #endif
9256         float renderDistance = distance;
9257         float s = renderDistance * size;
9258         float discSizeInPixels = 0.0f;
9259         float orbitSizeInPixels = 0.0f;
9260 
9261         if (hasOrbit)
9262             orbitSizeInPixels = orbitalRadius / (distance * pixelSize);
9263 
9264         // Special handling for stars less than one light year away . . .
9265         // We can't just go ahead and render a nearby star in the usual way
9266         // for two reasons:
9267         //   * It may be clipped by the near plane
9268         //   * It may be large enough that we should render it as a mesh
9269         //     instead of a particle
9270         // It's possible that the second condition might apply for stars
9271         // further than one light year away if the star is huge, the fov is
9272         // very small and the resolution is high.  We'll ignore this for now
9273         // and use the most inexpensive test possible . . .
9274         if (distance < 1.0f || orbitSizeInPixels > 1.0f)
9275         {
9276             // Compute the position of the observer relative to the star.
9277             // This is a much more accurate (and expensive) distance
9278             // calculation than the previous one which used the observer's
9279             // position rounded off to floats.
9280             Point3d hPos = astrocentricPosition(observer->getPosition(),
9281                                                 star,
9282                                                 observer->getTime());
9283             relPos = Vec3f((float) hPos.x, (float) hPos.y, (float) hPos.z) *
9284                 -astro::kilometersToLightYears(1.0f),
9285             distance = relPos.length();
9286 
9287             // Recompute apparent magnitude using new distance computation
9288             appMag = astro::absToAppMag(star.getAbsoluteMagnitude(), distance);
9289 
9290             float f        = RenderDistance / distance;
9291             renderDistance = RenderDistance;
9292             starPos        = obsPos + relPos * f;
9293 
9294             float radius = star.getRadius();
9295             discSizeInPixels = radius / astro::lightYearsToKilometers(distance) / pixelSize;
9296             ++nClose;
9297         }
9298 
9299         // Place labels for stars brighter than the specified label threshold brightness
9300         if ((labelMode & Renderer::StarLabels) && appMag < labelThresholdMag)
9301         {
9302             Vec3f starDir = relPos;
9303             starDir.normalize();
9304             if (dot(starDir, viewNormal) > cosFOV)
9305             {
9306                 char nameBuffer[Renderer::MaxLabelLength];
9307                 starDB->getStarName(star, nameBuffer, sizeof(nameBuffer), true);
9308                 float distr = 3.5f * (labelThresholdMag - appMag)/labelThresholdMag;
9309                 if (distr > 1.0f)
9310                     distr = 1.0f;
9311                 renderer->addBackgroundAnnotation(NULL, nameBuffer,
9312                                                   Color(Renderer::StarLabelColor, distr * Renderer::StarLabelColor.alpha()),
9313                                                   Point3f(relPos.x, relPos.y, relPos.z));
9314                 nLabelled++;
9315             }
9316         }
9317 
9318         // Stars closer than the maximum solar system size are actually
9319         // added to the render list and depth sorted, since they may occlude
9320         // planets.
9321         if (distance > MaxSolarSystemSize)
9322         {
9323 #ifdef USE_HDR
9324             float alpha = exposure*(faintestMag - appMag)/(faintestMag - saturationMag + 0.001f);
9325 #else
9326             float alpha = (faintestMag - appMag) * brightnessScale + brightnessBias;
9327 #endif
9328 #ifdef DEBUG_HDR_ADAPT
9329             minMag = max(minMag, appMag);
9330             maxMag = min(maxMag, appMag);
9331             minAlpha = min(minAlpha, alpha);
9332             maxAlpha = max(maxAlpha, alpha);
9333             ++total;
9334             if (alpha > above)
9335             {
9336                 ++countAboveN;
9337             }
9338 #endif
9339             float pointSize;
9340 
9341             if (useScaledDiscs)
9342             {
9343                 float discSize = size;
9344                 if (alpha < 0.0f)
9345                 {
9346                     alpha = 0.0f;
9347                 }
9348                 else if (alpha > 1.0f)
9349                 {
9350                     discSize = min(discSize * (2.0f * alpha - 1.0f), maxDiscSize);
9351                     alpha = 1.0f;
9352                 }
9353                 pointSize = discSize;
9354             }
9355             else
9356             {
9357                 alpha = clamp(alpha);
9358                 pointSize = size;
9359 #ifdef DEBUG_HDR_ADAPT
9360                 maxSize = max(maxSize, pointSize);
9361 #endif
9362             }
9363 
9364             if (starPrimitive == GL_POINTS)
9365             {
9366                 pointStarVertexBuffer->addStar(relPos,
9367                                                Color(starColor, alpha),
9368                                                pointSize);
9369             }
9370             else
9371             {
9372                 starVertexBuffer->addStar(relPos,
9373                                           Color(starColor, alpha),
9374                                           pointSize * renderDistance);
9375             }
9376 
9377             ++nRendered;
9378 
9379             // If the star is brighter than the saturation magnitude, add a
9380             // halo around it to make it appear more brilliant.  This is a
9381             // hack to compensate for the limited dynamic range of monitors.
9382             if (appMag < saturationMag)
9383             {
9384                 Renderer::Particle p;
9385                 p.center = Point3f(relPos.x, relPos.y, relPos.z);
9386                 p.size = size;
9387                 p.color = Color(starColor, alpha);
9388 
9389                 alpha = GlareOpacity * clamp((appMag - saturationMag) * -0.8f);
9390                 s = renderDistance * 0.001f * (3 - (appMag - saturationMag)) * 2;
9391                 if (s > p.size * 3)
9392                 {
9393                     p.size = s * 2.0f/(1.0f + FOV/fov);
9394                 }
9395                 else
9396                 {
9397                     if (s > p.size * 3)
9398                         p.size = s * 2.0f; //2.0f/(1.0f +FOV/fov);
9399                     else
9400                         p.size = p.size * 3;
9401                     p.size *= 1.6f;
9402                 }
9403 
9404                 p.color = Color(starColor, alpha);
9405                 glareParticles->insert(glareParticles->end(), p);
9406                 ++nBright;
9407             }
9408         }
9409         else
9410         {
9411             Mat3f viewMat = observer->getOrientationf().toMatrix3();
9412             Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
9413 
9414             RenderListEntry rle;
9415             rle.renderableType = RenderListEntry::RenderableStar;
9416             rle.star = &star;
9417             rle.isOpaque = true;
9418 
9419             // Objects in the render list are always rendered relative to
9420             // a viewer at the origin--this is different than for distant
9421             // stars.
9422             float scale = astro::lightYearsToKilometers(1.0f);
9423             rle.position = Point3f(relPos.x * scale, relPos.y * scale, relPos.z * scale);
9424             rle.centerZ = Vec3f(rle.position.x, rle.position.y, rle.position.z) * viewMatZ;
9425             rle.distance = rle.position.distanceFromOrigin();
9426             rle.radius = star.getRadius();
9427             rle.discSizeInPixels = discSizeInPixels;
9428             rle.appMag = appMag;
9429             renderList->insert(renderList->end(), rle);
9430         }
9431     }
9432 }
9433 
9434 
9435 class PointStarRenderer : public ObjectRenderer<Star, float>
9436 {
9437  public:
9438     PointStarRenderer();
9439 
9440     void process(const Star& star, float distance, float appMag);
9441 
9442  public:
9443     Point3d obsPos;
9444 
9445     vector<RenderListEntry>*         renderList;
9446     Renderer::PointStarVertexBuffer* starVertexBuffer;
9447     Renderer::PointStarVertexBuffer* glareVertexBuffer;
9448 
9449     const StarDatabase* starDB;
9450 
9451     bool   useScaledDiscs;
9452     GLenum starPrimitive;
9453     float  maxDiscSize;
9454 
9455     float cosFOV;
9456 
9457     const ColorTemperatureTable* colorTemp;
9458 #ifdef DEBUG_HDR_ADAPT
9459     float minMag;
9460     float maxMag;
9461     float minAlpha;
9462     float maxAlpha;
9463     float maxSize;
9464     float above;
9465     unsigned long countAboveN;
9466     unsigned long total;
9467 #endif
9468 };
9469 
9470 
PointStarRenderer()9471 PointStarRenderer::PointStarRenderer() :
9472     ObjectRenderer<Star, float>(STAR_DISTANCE_LIMIT),
9473     starVertexBuffer     (NULL),
9474     useScaledDiscs       (false),
9475     maxDiscSize          (1.0f),
9476     cosFOV               (1.0f),
9477     colorTemp            (NULL)
9478 {
9479 }
9480 
9481 
process(const Star & star,float distance,float appMag)9482 void PointStarRenderer::process(const Star& star, float distance, float appMag)
9483 {
9484     nProcessed++;
9485 
9486     Point3f starPos = star.getPosition();
9487     Vec3f   relPos((float) ((double) starPos.x - obsPos.x),
9488                    (float) ((double) starPos.y - obsPos.y),
9489                    (float) ((double) starPos.z - obsPos.z));
9490     float   orbitalRadius = star.getOrbitalRadius();
9491     bool    hasOrbit = orbitalRadius > 0.0f;
9492 
9493     if (distance > distanceLimit)
9494         return;
9495 
9496 
9497     // A very rough check to see if the star may be visible: is the star in
9498     // front of the viewer? If the star might be close (relPos.x^2 < 0.1) or
9499     // is moving in an orbit, we'll always regard it as potentially visible.
9500     // TODO: consider normalizing relPos and comparing relPos*viewNormal against
9501     // cosFOV--this will cull many more stars than relPos*viewNormal, at the
9502     // cost of a normalize per star.
9503     if (relPos * viewNormal > 0 || relPos.x * relPos.x < 0.1f || hasOrbit)
9504     {
9505 #ifdef HDR_COMPRESS
9506         Color starColorFull = colorTemp->lookupColor(star.getTemperature());
9507         Color starColor(starColorFull.red()   * 0.5f,
9508                         starColorFull.green() * 0.5f,
9509                         starColorFull.blue()  * 0.5f);
9510 #else
9511         Color starColor = colorTemp->lookupColor(star.getTemperature());
9512 #endif
9513         float renderDistance = distance;
9514         /*float s = renderDistance * size;      Unused*/
9515         float discSizeInPixels = 0.0f;
9516         float orbitSizeInPixels = 0.0f;
9517 
9518         if (hasOrbit)
9519             orbitSizeInPixels = orbitalRadius / (distance * pixelSize);
9520 
9521         // Special handling for stars less than one light year away . . .
9522         // We can't just go ahead and render a nearby star in the usual way
9523         // for two reasons:
9524         //   * It may be clipped by the near plane
9525         //   * It may be large enough that we should render it as a mesh
9526         //     instead of a particle
9527         // It's possible that the second condition might apply for stars
9528         // further than one light year away if the star is huge, the fov is
9529         // very small and the resolution is high.  We'll ignore this for now
9530         // and use the most inexpensive test possible . . .
9531         if (distance < 1.0f || orbitSizeInPixels > 1.0f)
9532         {
9533             // Compute the position of the observer relative to the star.
9534             // This is a much more accurate (and expensive) distance
9535             // calculation than the previous one which used the observer's
9536             // position rounded off to floats.
9537             Point3d hPos = astrocentricPosition(observer->getPosition(),
9538                                                 star,
9539                                                 observer->getTime());
9540             relPos = Vec3f((float) hPos.x, (float) hPos.y, (float) hPos.z) *
9541                 -astro::kilometersToLightYears(1.0f),
9542             distance = relPos.length();
9543 
9544             // Recompute apparent magnitude using new distance computation
9545             appMag = astro::absToAppMag(star.getAbsoluteMagnitude(), distance);
9546 
9547             float f        = RenderDistance / distance;
9548             renderDistance = RenderDistance;
9549             starPos        = obsPos + relPos * f;
9550 
9551             float radius = star.getRadius();
9552             discSizeInPixels = radius / astro::lightYearsToKilometers(distance) / pixelSize;
9553             ++nClose;
9554         }
9555 
9556         // Place labels for stars brighter than the specified label threshold brightness
9557         if ((labelMode & Renderer::StarLabels) && appMag < labelThresholdMag)
9558         {
9559             Vec3f starDir = relPos;
9560             starDir.normalize();
9561             if (dot(starDir, viewNormal) > cosFOV)
9562             {
9563                 char nameBuffer[Renderer::MaxLabelLength];
9564                 starDB->getStarName(star, nameBuffer, sizeof(nameBuffer), true);
9565                 float distr = 3.5f * (labelThresholdMag - appMag)/labelThresholdMag;
9566                 if (distr > 1.0f)
9567                     distr = 1.0f;
9568                 renderer->addBackgroundAnnotation(NULL, nameBuffer,
9569                                                   Color(Renderer::StarLabelColor, distr * Renderer::StarLabelColor.alpha()),
9570                                                   Point3f(relPos.x, relPos.y, relPos.z));
9571                 nLabelled++;
9572             }
9573         }
9574 
9575         // Stars closer than the maximum solar system size are actually
9576         // added to the render list and depth sorted, since they may occlude
9577         // planets.
9578         if (distance > MaxSolarSystemSize)
9579         {
9580 #ifdef USE_HDR
9581             float satPoint = saturationMag;
9582             float alpha = exposure*(faintestMag - appMag)/(faintestMag - saturationMag + 0.001f);
9583 #else
9584             float satPoint = faintestMag - (1.0f - brightnessBias) / brightnessScale; // TODO: precompute this value
9585             float alpha = (faintestMag - appMag) * brightnessScale + brightnessBias;
9586 #endif
9587 #ifdef DEBUG_HDR_ADAPT
9588             minMag = max(minMag, appMag);
9589             maxMag = min(maxMag, appMag);
9590             minAlpha = min(minAlpha, alpha);
9591             maxAlpha = max(maxAlpha, alpha);
9592             ++total;
9593             if (alpha > above)
9594             {
9595                 ++countAboveN;
9596             }
9597 #endif
9598 
9599             if (useScaledDiscs)
9600             {
9601                 float discSize = size;
9602                 if (alpha < 0.0f)
9603                 {
9604                     alpha = 0.0f;
9605                 }
9606                 else if (alpha > 1.0f)
9607                 {
9608                     float discScale = min(MaxScaledDiscStarSize, (float) pow(2.0f, 0.3f * (satPoint - appMag)));
9609                     discSize *= discScale;
9610 
9611                     float glareAlpha = min(0.5f, discScale / 4.0f);
9612                     glareVertexBuffer->addStar(relPos, Color(starColor, glareAlpha), discSize * 3.0f);
9613 
9614                     alpha = 1.0f;
9615                 }
9616                 starVertexBuffer->addStar(relPos, Color(starColor, alpha), discSize);
9617             }
9618             else
9619             {
9620                 if (alpha < 0.0f)
9621                 {
9622                     alpha = 0.0f;
9623                 }
9624                 else if (alpha > 1.0f)
9625                 {
9626                     float discScale = min(100.0f, satPoint - appMag + 2.0f);
9627                     float glareAlpha = min(GlareOpacity, (discScale - 2.0f) / 4.0f);
9628                     glareVertexBuffer->addStar(relPos, Color(starColor, glareAlpha), 2.0f * discScale * size);
9629 #ifdef DEBUG_HDR_ADAPT
9630                     maxSize = max(maxSize, 2.0f * discScale * size);
9631 #endif
9632                 }
9633                 starVertexBuffer->addStar(relPos, Color(starColor, alpha), size);
9634             }
9635 
9636             ++nRendered;
9637         }
9638         else
9639         {
9640             Mat3f viewMat = observer->getOrientationf().toMatrix3();
9641             Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
9642 
9643             RenderListEntry rle;
9644             rle.renderableType = RenderListEntry::RenderableStar;
9645             rle.star = &star;
9646 
9647             // Objects in the render list are always rendered relative to
9648             // a viewer at the origin--this is different than for distant
9649             // stars.
9650             float scale = astro::lightYearsToKilometers(1.0f);
9651             rle.position = Point3f(relPos.x * scale, relPos.y * scale, relPos.z * scale);
9652             rle.centerZ = Vec3f(rle.position.x, rle.position.y, rle.position.z) * viewMatZ;
9653             rle.distance = rle.position.distanceFromOrigin();
9654             rle.radius = star.getRadius();
9655             rle.discSizeInPixels = discSizeInPixels;
9656             rle.appMag = appMag;
9657             renderList->insert(renderList->end(), rle);
9658         }
9659     }
9660 }
9661 
9662 
microLYToLY(const Point3<T> & p)9663 template<class T> static Point3<T> microLYToLY(const Point3<T>& p)
9664 {
9665     return Point3<T>(p.x * (T) 1e-6, p.y * (T) 1e-6, p.z * (T) 1e-6);
9666 }
9667 
9668 
9669 // Calculate the maximum field of view (from top left corner to bottom right) of
9670 // a frustum with the specified aspect ratio (width/height) and vertical field of
9671 // view. We follow the convention used elsewhere and use units of degrees for
9672 // the field of view angle.
calcMaxFOV(double fovY_degrees,double aspectRatio)9673 static double calcMaxFOV(double fovY_degrees, double aspectRatio)
9674 {
9675     double l = 1.0 / tan(degToRad(fovY_degrees / 2.0));
9676     return radToDeg(atan(sqrt(aspectRatio * aspectRatio + 1.0) / l)) * 2.0;
9677 }
9678 
9679 
renderStars(const StarDatabase & starDB,float faintestMagNight,const Observer & observer)9680 void Renderer::renderStars(const StarDatabase& starDB,
9681                            float faintestMagNight,
9682                            const Observer& observer)
9683 {
9684     StarRenderer starRenderer;
9685     Point3d obsPos = microLYToLY((Point3d) observer.getPosition());
9686 
9687 
9688     starRenderer.context          = context;
9689     starRenderer.renderer         = this;
9690     starRenderer.starDB           = &starDB;
9691     starRenderer.observer         = &observer;
9692     starRenderer.obsPos           = obsPos;
9693     starRenderer.viewNormal       = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3();
9694     starRenderer.glareParticles   = &glareParticles;
9695     starRenderer.renderList       = &renderList;
9696     starRenderer.starVertexBuffer = starVertexBuffer;
9697     starRenderer.pointStarVertexBuffer = pointStarVertexBuffer;
9698     starRenderer.fov              = fov;
9699     starRenderer.cosFOV            = (float) cos(degToRad(calcMaxFOV(fov, (float) windowWidth / (float) windowHeight)) / 2.0f);
9700 
9701     // size/pixelSize =0.86 at 120deg, 1.43 at 45deg and 1.6 at 0deg.
9702     starRenderer.size             = pixelSize * 1.6f / corrFac;
9703     starRenderer.pixelSize        = pixelSize;
9704     starRenderer.brightnessScale  = brightnessScale * corrFac;
9705     starRenderer.brightnessBias   = brightnessBias;
9706     starRenderer.faintestMag      = faintestMag;
9707     starRenderer.faintestMagNight = faintestMagNight;
9708     starRenderer.saturationMag    = saturationMag;
9709 #ifdef USE_HDR
9710     starRenderer.exposure         = exposure + brightPlus;
9711 #endif
9712 #ifdef DEBUG_HDR_ADAPT
9713     starRenderer.minMag = -100.f;
9714     starRenderer.maxMag =  100.f;
9715     starRenderer.minAlpha = 1.f;
9716     starRenderer.maxAlpha = 0.f;
9717     starRenderer.maxSize  = 0.f;
9718     starRenderer.above    = 1.f;
9719     starRenderer.countAboveN = 0L;
9720     starRenderer.total       = 0L;
9721 #endif
9722     starRenderer.distanceLimit    = distanceLimit;
9723     starRenderer.labelMode        = labelMode;
9724 
9725     // = 1.0 at startup
9726     float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
9727     starRenderer.labelThresholdMag = max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
9728 
9729     if (starStyle == PointStars || useNewStarRendering)
9730     {
9731         starRenderer.starPrimitive = GL_POINTS;
9732         //starRenderer.size          = 3.2f;
9733     }
9734     else
9735     {
9736         starRenderer.starPrimitive = GL_QUADS;
9737     }
9738 
9739     if (starStyle == ScaledDiscStars)
9740     {
9741         starRenderer.useScaledDiscs = true;
9742         starRenderer.brightnessScale *= 2.0f;
9743         starRenderer.maxDiscSize = starRenderer.size * MaxScaledDiscStarSize;
9744     }
9745 
9746     starRenderer.colorTemp = colorTemp;
9747 
9748     glareParticles.clear();
9749 
9750     starVertexBuffer->setBillboardOrientation(observer.getOrientationf());
9751 
9752     glEnable(GL_TEXTURE_2D);
9753 
9754     if (useNewStarRendering)
9755         gaussianDiscTex->bind();
9756     else
9757         starTex->bind();
9758     if (starRenderer.starPrimitive == GL_POINTS)
9759     {
9760         // Point primitives (either real points or point sprites)
9761         if (starStyle == PointStars)
9762             starRenderer.pointStarVertexBuffer->startPoints(*context);
9763         else
9764             starRenderer.pointStarVertexBuffer->startSprites(*context);
9765     }
9766     else
9767     {
9768         // Use quad primitives
9769         starRenderer.starVertexBuffer->start();
9770     }
9771     starDB.findVisibleStars(starRenderer,
9772                             Point3f((float) obsPos.x, (float) obsPos.y, (float) obsPos.z),
9773                             observer.getOrientationf(),
9774                             degToRad(fov),
9775                             (float) windowWidth / (float) windowHeight,
9776                             faintestMagNight);
9777 #ifdef DEBUG_HDR_ADAPT
9778   HDR_LOG <<
9779       "* minMag = "    << starRenderer.minMag << ", " <<
9780       "maxMag = "      << starRenderer.maxMag << ", " <<
9781       "percent above " << starRenderer.above << " = " <<
9782       (100.0*(double)starRenderer.countAboveN/(double)starRenderer.total) << endl;
9783 #endif
9784 
9785     if (starRenderer.starPrimitive == GL_POINTS)
9786         starRenderer.pointStarVertexBuffer->finish();
9787     else
9788         starRenderer.starVertexBuffer->finish();
9789 
9790     gaussianGlareTex->bind();
9791     renderParticles(glareParticles, observer.getOrientationf());
9792 }
9793 
9794 
renderPointStars(const StarDatabase & starDB,float faintestMagNight,const Observer & observer)9795 void Renderer::renderPointStars(const StarDatabase& starDB,
9796                                 float faintestMagNight,
9797                                 const Observer& observer)
9798 {
9799     Point3d obsPos = microLYToLY((Point3d) observer.getPosition());
9800 
9801     PointStarRenderer starRenderer;
9802     starRenderer.context           = context;
9803     starRenderer.renderer          = this;
9804     starRenderer.starDB            = &starDB;
9805     starRenderer.observer          = &observer;
9806     starRenderer.obsPos            = obsPos;
9807     starRenderer.viewNormal        = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3();
9808     starRenderer.renderList        = &renderList;
9809     starRenderer.starVertexBuffer  = pointStarVertexBuffer;
9810     starRenderer.glareVertexBuffer = glareVertexBuffer;
9811     starRenderer.fov               = fov;
9812     starRenderer.cosFOV            = (float) cos(degToRad(calcMaxFOV(fov, (float) windowWidth / (float) windowHeight)) / 2.0f);
9813 
9814     starRenderer.pixelSize         = pixelSize;
9815     starRenderer.brightnessScale   = brightnessScale * corrFac;
9816     starRenderer.brightnessBias    = brightnessBias;
9817     starRenderer.faintestMag       = faintestMag;
9818     starRenderer.faintestMagNight  = faintestMagNight;
9819     starRenderer.saturationMag     = saturationMag;
9820 #ifdef USE_HDR
9821     starRenderer.exposure          = exposure + brightPlus;
9822 #endif
9823 #ifdef DEBUG_HDR_ADAPT
9824     starRenderer.minMag = -100.f;
9825     starRenderer.maxMag =  100.f;
9826     starRenderer.minAlpha = 1.f;
9827     starRenderer.maxAlpha = 0.f;
9828     starRenderer.maxSize  = 0.f;
9829     starRenderer.above    = 1.0f;
9830     starRenderer.countAboveN = 0L;
9831     starRenderer.total       = 0L;
9832 #endif
9833     starRenderer.distanceLimit     = distanceLimit;
9834     starRenderer.labelMode         = labelMode;
9835 
9836     // = 1.0 at startup
9837     float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
9838     starRenderer.labelThresholdMag = 1.2f * max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
9839 
9840     starRenderer.size          = BaseStarDiscSize;
9841     if (starStyle == ScaledDiscStars)
9842     {
9843         starRenderer.useScaledDiscs = true;
9844         starRenderer.brightnessScale *= 2.0f;
9845         starRenderer.maxDiscSize = starRenderer.size * MaxScaledDiscStarSize;
9846     }
9847     else if (starStyle == FuzzyPointStars)
9848     {
9849         starRenderer.brightnessScale *= 1.0f;
9850     }
9851 
9852     starRenderer.colorTemp = colorTemp;
9853 
9854     glEnable(GL_TEXTURE_2D);
9855     gaussianDiscTex->bind();
9856     starRenderer.starVertexBuffer->setTexture(gaussianDiscTex);
9857     starRenderer.glareVertexBuffer->setTexture(gaussianGlareTex);
9858 
9859     starRenderer.glareVertexBuffer->startSprites(*context);
9860     if (starStyle == PointStars)
9861         starRenderer.starVertexBuffer->startPoints(*context);
9862     else
9863         starRenderer.starVertexBuffer->startSprites(*context);
9864 
9865     starDB.findVisibleStars(starRenderer,
9866                             Point3f((float) obsPos.x, (float) obsPos.y, (float) obsPos.z),
9867                             observer.getOrientationf(),
9868                             degToRad(fov),
9869                             (float) windowWidth / (float) windowHeight,
9870                             faintestMagNight);
9871 
9872     starRenderer.starVertexBuffer->render();
9873     starRenderer.glareVertexBuffer->render();
9874     starRenderer.starVertexBuffer->finish();
9875     starRenderer.glareVertexBuffer->finish();
9876 }
9877 
9878 
9879 class DSORenderer : public ObjectRenderer<DeepSkyObject*, double>
9880 {
9881  public:
9882     DSORenderer();
9883 
9884     void process(DeepSkyObject* const &, double, float);
9885 
9886  public:
9887     Point3d      obsPos;
9888     DSODatabase* dsoDB;
9889     Frustum      frustum;
9890 
9891     Mat3f orientationMatrix;
9892 
9893     int wWidth;
9894     int wHeight;
9895 
9896     double avgAbsMag;
9897 };
9898 
9899 
DSORenderer()9900 DSORenderer::DSORenderer() :
9901     ObjectRenderer<DeepSkyObject*, double>(DSO_OCTREE_ROOT_SIZE),
9902     frustum(degToRad(45.0f), 1.0f, 1.0f)
9903 {
9904 }
9905 
9906 
process(DeepSkyObject * const & dso,double distanceToDSO,float absMag)9907 void DSORenderer::process(DeepSkyObject* const & dso,
9908                           double distanceToDSO,
9909                           float  absMag)
9910 {
9911     if (distanceToDSO > distanceLimit)
9912         return;
9913 
9914     Point3d dsoPos = dso->getPosition();
9915     Vec3f   relPos = Vec3f((float)(dsoPos.x - obsPos.x),
9916                            (float)(dsoPos.y - obsPos.y),
9917                            (float)(dsoPos.z - obsPos.z));
9918 
9919     Point3d center = Point3d(0.0f, 0.0f, 0.0f) + relPos * orientationMatrix;
9920 
9921 	double enhance = 4.0, pc10 = 32.6167;
9922 
9923 	// The parameter 'enhance' adjusts the DSO brightness as viewed from "inside"
9924 	// (e.g. MilkyWay as seen from Earth). It provides an enhanced apparent  core
9925 	// brightness appMag  ~ absMag - enhance. 'enhance' thus serves to uniformly
9926 	// enhance the too low sprite luminosity at close distance.
9927 
9928     float  appMag = (distanceToDSO >= pc10)? (float) astro::absToAppMag((double) absMag, distanceToDSO): absMag + (float) (enhance * tanh(distanceToDSO/pc10 - 1.0));
9929 
9930 	// Test the object's bounding sphere against the view frustum. If we
9931     // avoid this stage, overcrowded octree cells may hit performance badly:
9932     // each object (even if it's not visible) would be sent to the OpenGL
9933     // pipeline.
9934 
9935 
9936     if ((renderFlags & dso->getRenderMask()) && dso->isVisible())
9937     {
9938 		double dsoRadius = dso->getBoundingSphereRadius();
9939 
9940         if (frustum.testSphere(center, dsoRadius) != Frustum::Outside)
9941         {
9942             // Input: display looks satisfactory for 0.2 < brightness < O(1.0)
9943             // Ansatz: brightness = a - b * appMag(distanceToDSO), emulating eye sensitivity...
9944             // determine a,b such that
9945             // a - b * absMag = absMag / avgAbsMag ~ 1; a - b * faintestMag = 0.2.
9946             // The 2nd eq. guarantees that the faintest galaxies are still visible.
9947 
9948 			if(!strcmp(dso->getObjTypeName(),"globular"))
9949 				avgAbsMag =  -6.86;    // average over 150  globulars in globulars.dsc.
9950 			else if (!strcmp(dso->getObjTypeName(),"galaxy"))
9951 				avgAbsMag = -19.04;    // average over 10937 galaxies in galaxies.dsc.
9952 
9953 
9954             float r   = absMag / (float) avgAbsMag;
9955 			float brightness = r - (r - 0.2f) * (absMag - appMag) / (absMag - faintestMag);
9956 
9957 			// obviously, brightness(appMag = absMag) = r and
9958 			// brightness(appMag = faintestMag) = 0.2, as desired.
9959 
9960 			brightness = 2.3f * brightness * (faintestMag - 4.75f) / renderer->getFaintestAM45deg();
9961 
9962 #ifdef USE_HDR
9963             brightness *= exposure;
9964 #endif
9965             if (brightness < 0)
9966 				brightness = 0;
9967 
9968 			if (dsoRadius < 1000.0)
9969             {
9970                 // Small objects may be prone to clipping; give them special
9971                 // handling.  We don't want to always set the projection
9972                 // matrix, since that could be expensive with large galaxy
9973                 // catalogs.
9974                 float nearZ = (float) (distanceToDSO / 2);
9975                 float farZ  = (float) (distanceToDSO + dsoRadius * 2 * CubeCornerToCenterDistance);
9976                 if (nearZ < dsoRadius * 0.001)
9977                 {
9978                     nearZ = (float) (dsoRadius * 0.001);
9979                     farZ  = nearZ * 10000.0f;
9980                 }
9981 
9982                 glMatrixMode(GL_PROJECTION);
9983                 glPushMatrix();
9984                 glLoadIdentity();
9985                 gluPerspective(fov,
9986                                (float) wWidth / (float) wHeight,
9987                                nearZ,
9988                                farZ);
9989                 glMatrixMode(GL_MODELVIEW);
9990             }
9991 
9992             glPushMatrix();
9993             glTranslate(relPos);
9994 
9995             dso->render(*context,
9996                         relPos,
9997                         observer->getOrientationf(),
9998                         (float) brightness,
9999                         pixelSize);
10000             glPopMatrix();
10001 
10002 #if 1
10003             if (dsoRadius < 1000.0)
10004             {
10005                 glMatrixMode(GL_PROJECTION);
10006                 glPopMatrix();
10007                 glMatrixMode(GL_MODELVIEW);
10008             }
10009 #endif
10010         } // frustum test
10011     } // renderFlags check
10012 
10013     // Only render those labels that are in front of the camera:
10014     // Place labels for DSOs brighter than the specified label threshold brightness
10015     //
10016     unsigned int labelMask = dso->getLabelMask();
10017 
10018     if ((labelMask & labelMode) && dot(relPos, viewNormal) > 0 && dso->isVisible())
10019     {
10020         Color labelColor;
10021         float appMagEff = 6.0f;
10022         float step = 6.0f;
10023         float symbolSize = 0.0f;
10024         MarkerRepresentation* rep = NULL;
10025 
10026         // Use magnitude based fading for galaxies, and distance based
10027         // fading for nebulae and open clusters.
10028         switch (labelMask)
10029         {
10030         case Renderer::NebulaLabels:
10031             rep = &renderer->nebulaRep;
10032             labelColor = Renderer::NebulaLabelColor;
10033             appMagEff = astro::absToAppMag(-7.5f, (float) distanceToDSO);
10034             symbolSize = (float) (dso->getRadius() / distanceToDSO) / pixelSize;
10035             step = 6.0f;
10036             break;
10037         case Renderer::OpenClusterLabels:
10038             rep = &renderer->openClusterRep;
10039             labelColor = Renderer::OpenClusterLabelColor;
10040             appMagEff = astro::absToAppMag(-6.0f, (float) distanceToDSO);
10041             symbolSize = (float) (dso->getRadius() / distanceToDSO) / pixelSize;
10042             step = 4.0f;
10043             break;
10044         case Renderer::GalaxyLabels:
10045             labelColor = Renderer::GalaxyLabelColor;
10046             appMagEff = appMag;
10047             step = 6.0f;
10048             break;
10049 		case Renderer::GlobularLabels:
10050             labelColor = Renderer::GlobularLabelColor;
10051             appMagEff = appMag;
10052             step = 3.0f;
10053             break;
10054         default:
10055             // Unrecognized object class
10056             labelColor = Color::White;
10057             appMagEff = appMag;
10058             step = 6.0f;
10059             break;
10060         }
10061 
10062         if (appMagEff < labelThresholdMag)
10063         {
10064             // introduce distance dependent label transparency.
10065             float distr = step * (labelThresholdMag - appMagEff) / labelThresholdMag;
10066             if (distr > 1.0f)
10067                 distr = 1.0f;
10068 
10069             renderer->addBackgroundAnnotation(rep,
10070                                               dsoDB->getDSOName(dso, true),
10071                                               Color(labelColor, distr * labelColor.alpha()),
10072                                               Point3f(relPos.x, relPos.y, relPos.z),
10073                                               Renderer::AlignLeft, Renderer::VerticalAlignCenter, symbolSize);
10074         }
10075     }
10076 }
10077 
10078 
renderDeepSkyObjects(const Universe & universe,const Observer & observer,const float faintestMagNight)10079 void Renderer::renderDeepSkyObjects(const Universe&  universe,
10080                                     const Observer& observer,
10081                                     const float     faintestMagNight)
10082 {
10083     DSORenderer dsoRenderer;
10084 
10085     Point3d obsPos    = (Point3d) observer.getPosition();
10086     obsPos.x         *= 1e-6;
10087     obsPos.y         *= 1e-6;
10088     obsPos.z         *= 1e-6;
10089 
10090     DSODatabase* dsoDB  = universe.getDSOCatalog();
10091 
10092     dsoRenderer.context          = context;
10093     dsoRenderer.renderer         = this;
10094     dsoRenderer.dsoDB            = dsoDB;
10095     dsoRenderer.orientationMatrix = conjugate(observer.getOrientationf()).toMatrix3();
10096     dsoRenderer.observer          = &observer;
10097     dsoRenderer.obsPos            = obsPos;
10098     dsoRenderer.viewNormal        = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3();
10099     dsoRenderer.fov              = fov;
10100     // size/pixelSize =0.86 at 120deg, 1.43 at 45deg and 1.6 at 0deg.
10101     dsoRenderer.size             = pixelSize * 1.6f / corrFac;
10102     dsoRenderer.pixelSize        = pixelSize;
10103     dsoRenderer.brightnessScale  = brightnessScale * corrFac;
10104     dsoRenderer.brightnessBias   = brightnessBias;
10105     dsoRenderer.avgAbsMag        = dsoDB->getAverageAbsoluteMagnitude();
10106     dsoRenderer.faintestMag      = faintestMag;
10107     dsoRenderer.faintestMagNight = faintestMagNight;
10108     dsoRenderer.saturationMag    = saturationMag;
10109 #ifdef USE_HDR
10110     dsoRenderer.exposure         = exposure + brightPlus;
10111 #endif
10112     dsoRenderer.renderFlags      = renderFlags;
10113     dsoRenderer.labelMode        = labelMode;
10114     dsoRenderer.wWidth           = windowWidth;
10115     dsoRenderer.wHeight          = windowHeight;
10116 
10117     dsoRenderer.frustum = Frustum(degToRad(fov),
10118                         (float) windowWidth / (float) windowHeight,
10119                         MinNearPlaneDistance);
10120     // Use pixelSize * screenDpi instead of FoV, to eliminate windowHeight dependence.
10121     // = 1.0 at startup
10122     float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
10123 
10124     dsoRenderer.labelThresholdMag = 2.0f * max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
10125 
10126     galaxyRep      = MarkerRepresentation(MarkerRepresentation::Triangle, 8.0f, GalaxyLabelColor);
10127     nebulaRep      = MarkerRepresentation(MarkerRepresentation::Square,   8.0f, NebulaLabelColor);
10128     openClusterRep = MarkerRepresentation(MarkerRepresentation::Circle,   8.0f, OpenClusterLabelColor);
10129     globularRep    = MarkerRepresentation(MarkerRepresentation::Circle,   8.0f, OpenClusterLabelColor);
10130 
10131     // Render any line primitives with smooth lines
10132     // (mostly to make graticules look good.)
10133     if ((renderFlags & ShowSmoothLines) != 0)
10134         enableSmoothLines();
10135 
10136     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
10137 
10138     dsoDB->findVisibleDSOs(dsoRenderer,
10139                            obsPos,
10140                            observer.getOrientationf(),
10141                            degToRad(fov),
10142                            (float) windowWidth / (float) windowHeight,
10143                            2 * faintestMagNight);
10144 
10145     if ((renderFlags & ShowSmoothLines) != 0)
10146         disableSmoothLines();
10147 }
10148 
10149 
toStandardCoords(const Vec3d & v)10150 static Vec3d toStandardCoords(const Vec3d& v)
10151 {
10152     return Vec3d(v.x, -v.z, v.y);
10153 }
10154 
10155 
renderSkyGrids(const Observer & observer)10156 void Renderer::renderSkyGrids(const Observer& observer)
10157 {
10158     if (renderFlags & ShowCelestialSphere)
10159     {
10160         SkyGrid grid;
10161         grid.setOrientation(Quatd::xrotation(astro::J2000Obliquity));
10162         grid.setLineColor(EquatorialGridColor);
10163         grid.setLabelColor(EquatorialGridLabelColor);
10164         grid.render(*this, observer, windowWidth, windowHeight);
10165     }
10166 
10167     if (renderFlags & ShowGalacticGrid)
10168     {
10169         SkyGrid galacticGrid;
10170         galacticGrid.setOrientation(~(astro::eclipticToEquatorial() * astro::equatorialToGalactic()));
10171         galacticGrid.setLineColor(GalacticGridColor);
10172         galacticGrid.setLabelColor(GalacticGridLabelColor);
10173         galacticGrid.setLongitudeUnits(SkyGrid::LongitudeDegrees);
10174         galacticGrid.render(*this, observer, windowWidth, windowHeight);
10175     }
10176 
10177     if (renderFlags & ShowEclipticGrid)
10178     {
10179         SkyGrid grid;
10180         grid.setOrientation(Quatd(1.0));
10181         grid.setLineColor(EclipticGridColor);
10182         grid.setLabelColor(EclipticGridLabelColor);
10183         grid.setLongitudeUnits(SkyGrid::LongitudeDegrees);
10184         grid.render(*this, observer, windowWidth, windowHeight);
10185     }
10186 
10187     if (renderFlags & ShowHorizonGrid)
10188     {
10189         double tdb = observer.getTime();
10190         const ObserverFrame* frame = observer.getFrame();
10191         Body* body = frame->getRefObject().body();
10192 
10193         if (body)
10194         {
10195             SkyGrid grid;
10196             grid.setLineColor(HorizonGridColor);
10197             grid.setLabelColor(HorizonGridLabelColor);
10198             grid.setLongitudeUnits(SkyGrid::LongitudeDegrees);
10199             grid.setLongitudeDirection(SkyGrid::IncreasingClockwise);
10200 
10201             Vec3d zenithDirection = observer.getPosition() - body->getPosition(tdb);
10202             zenithDirection.normalize();
10203 
10204             Vec3d northPole = Vec3d(0.0, 1.0, 0.0) * (body->getEclipticToEquatorial(tdb)).toMatrix3();
10205             zenithDirection = toStandardCoords(zenithDirection);
10206             northPole = toStandardCoords(northPole);
10207 
10208             Vec3d v = zenithDirection ^ northPole;
10209 
10210             // Horizontal coordinate system not well defined when observer
10211             // is at a pole.
10212             double tolerance = 1.0e-10;
10213             if (v.length() > tolerance && v.length() < 1.0 - tolerance)
10214             {
10215                 v.normalize();
10216                 Vec3d u = v ^ zenithDirection;
10217 
10218                 Mat3d m = Mat3d(u, v, zenithDirection);
10219                 Quatd q = Quatd::matrixToQuaternion(m);
10220                 grid.setOrientation(q);
10221 
10222                 grid.render(*this, observer, windowWidth, windowHeight);
10223             }
10224         }
10225     }
10226 
10227     if (renderFlags & ShowEcliptic)
10228     {
10229         // Draw the J2000.0 ecliptic; trivial, since this forms the basis for
10230         // Celestia's coordinate system.
10231         const int subdivision = 200;
10232         glColor(EclipticColor);
10233         glBegin(GL_LINE_LOOP);
10234         for (int i = 0; i < subdivision; i++)
10235         {
10236             double theta = (double) i / (double) subdivision * 2 * PI;
10237             glVertex3f((float) cos(theta) * 1000.0f, 0.0f, (float) sin(theta) * 1000.0f);
10238         }
10239         glEnd();
10240     }
10241 }
10242 
10243 
10244 /*! Draw an arrow at the view border pointing to an offscreen selection. This method
10245  *  should only be called when the selection lies outside the view frustum.
10246  */
renderSelectionPointer(const Observer & observer,double now,const Frustum & viewFrustum,const Selection & sel)10247 void Renderer::renderSelectionPointer(const Observer& observer,
10248                                       double now,
10249                                       const Frustum& viewFrustum,
10250                                       const Selection& sel)
10251 {
10252     const float cursorDistance = 20.0f;
10253     if (sel.empty())
10254         return;
10255 
10256     Mat3f m = observer.getOrientationf().toMatrix3();
10257     Vec3f u = Vec3f(1, 0, 0) * m;
10258     Vec3f v = Vec3f(0, 1, 0) * m;
10259 
10260     // Get the position of the cursor relative to the eye
10261     Vec3d position = (sel.getPosition(now) - observer.getPosition()) * astro::microLightYearsToKilometers(1.0);
10262     double distance = position.length();
10263     bool isVisible = viewFrustum.testSphere(Point3d(0, 0, 0) + position, sel.radius()) != Frustum::Outside;
10264     position *= cursorDistance / distance;
10265 
10266 #ifdef USE_HDR
10267     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
10268 #endif
10269     glDisable(GL_DEPTH_TEST);
10270     glDisable(GL_TEXTURE_2D);
10271     glEnable(GL_BLEND);
10272     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
10273 
10274     if (!isVisible)
10275     {
10276         double viewAspectRatio = (double) windowWidth / (double) windowHeight;
10277         double vfov = observer.getFOV();
10278         float h = (float) tan(vfov / 2);
10279         float w = (float) (h * viewAspectRatio);
10280         float diag = std::sqrt(h * h + w * w);
10281 
10282         Vec3f posf((float) position.x, (float) position.y, (float) position.z);
10283         posf *= (1.0f / cursorDistance);
10284         float x = u * posf;
10285         float y = v * posf;
10286         float angle = std::atan2(y, x);
10287         float c = std::cos(angle);
10288         float s = std::sin(angle);
10289 
10290         float t = 1.0f;
10291         float x0 = c * diag;
10292         float y0 = s * diag;
10293         if (std::abs(x0) < w)
10294             t = h / std::abs(y0);
10295         else
10296             t = w / std::abs(x0);
10297         x0 *= t;
10298         y0 *= t;
10299         glColor(SelectionCursorColor, 0.6f);
10300         Vec3f center = Vec3f(0, 0, -1) * m;
10301 
10302         glPushMatrix();
10303         glTranslatef((float) center.x, (float) center.y, (float) center.z);
10304 
10305         Vec3f p0(0.0f, 0.0f, 0.0f);
10306         Vec3f p1(-20.0f * pixelSize,  6.0f * pixelSize, 0.0f);
10307         Vec3f p2(-20.0f * pixelSize, -6.0f * pixelSize, 0.0f);
10308 
10309         glBegin(GL_TRIANGLES);
10310         glVertex((p0.x * c - p0.y * s + x0) * u + (p0.x * s + p0.y * c + y0) * v);
10311         glVertex((p1.x * c - p1.y * s + x0) * u + (p1.x * s + p1.y * c + y0) * v);
10312         glVertex((p2.x * c - p2.y * s + x0) * u + (p2.x * s + p2.y * c + y0) * v);
10313         glEnd();
10314 
10315         glPopMatrix();
10316     }
10317 
10318 #ifdef USE_HDR
10319     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
10320 #endif
10321 
10322     glEnable(GL_TEXTURE_2D);
10323 }
10324 
10325 
labelConstellations(const AsterismList & asterisms,const Observer & observer)10326 void Renderer::labelConstellations(const AsterismList& asterisms,
10327                                    const Observer& observer)
10328 {
10329     Point3f observerPos = (Point3f) observer.getPosition();
10330 
10331     for (AsterismList::const_iterator iter = asterisms.begin();
10332          iter != asterisms.end(); iter++)
10333     {
10334         Asterism* ast = *iter;
10335         if (ast->getChainCount() > 0 && ast->getActive())
10336         {
10337             const Asterism::Chain& chain = ast->getChain(0);
10338 
10339             if (chain.size() > 0)
10340             {
10341                 // The constellation label is positioned at the average
10342                 // position of all stars in the first chain.  This usually
10343                 // gives reasonable results.
10344                 Vec3f avg(0, 0, 0);
10345                 for (Asterism::Chain::const_iterator iter = chain.begin();
10346                      iter != chain.end(); iter++)
10347                     avg += (*iter - Point3f(0, 0, 0));
10348 
10349                 avg = avg / (float) chain.size();
10350 
10351                 // Draw all constellation labels at the same distance
10352                 avg.normalize();
10353                 avg = avg * 1.0e10f;
10354 
10355                 Vec3f rpos = Point3f(avg.x, avg.y, avg.z) - observerPos;
10356 
10357                 if ((observer.getOrientationf().toMatrix3() * rpos).z < 0)
10358                 {
10359                     // We'll linearly fade the labels as a function of the
10360                     // observer's distance to the origin of coordinates:
10361                     float opacity = 1.0f;
10362                     float dist = observerPos.distanceFromOrigin();
10363                     if (dist > MaxAsterismLabelsConstDist)
10364                     {
10365                         opacity = clamp((MaxAsterismLabelsConstDist - dist) /
10366                                         (MaxAsterismLabelsDist - MaxAsterismLabelsConstDist) + 1);
10367                     }
10368 
10369                     // Use the default label color unless the constellation has an
10370                     // override color set.
10371                     Color labelColor = ConstellationLabelColor;
10372                     if (ast->isColorOverridden())
10373                         labelColor = ast->getOverrideColor();
10374 
10375                     addBackgroundAnnotation(NULL,
10376                                             ast->getName((labelMode & I18nConstellationLabels) != 0),
10377                                             Color(labelColor, opacity),
10378                                             Point3f(rpos.x, rpos.y, rpos.z),
10379                                             AlignCenter, VerticalAlignCenter);
10380                 }
10381             }
10382         }
10383     }
10384 }
10385 
10386 
renderParticles(const vector<Particle> & particles,Quatf orientation)10387 void Renderer::renderParticles(const vector<Particle>& particles,
10388                                Quatf orientation)
10389 {
10390     int nParticles = particles.size();
10391 
10392     {
10393         Mat3f m = orientation.toMatrix3();
10394         Vec3f v0 = Vec3f(-1, -1, 0) * m;
10395         Vec3f v1 = Vec3f( 1, -1, 0) * m;
10396         Vec3f v2 = Vec3f( 1,  1, 0) * m;
10397         Vec3f v3 = Vec3f(-1,  1, 0) * m;
10398 
10399         glBegin(GL_QUADS);
10400         for (int i = 0; i < nParticles; i++)
10401         {
10402             Point3f center = particles[i].center;
10403             float size = particles[i].size;
10404 
10405             glColor(particles[i].color);
10406             glTexCoord2f(0, 1);
10407             glVertex(center + (v0 * size));
10408             glTexCoord2f(1, 1);
10409             glVertex(center + (v1 * size));
10410             glTexCoord2f(1, 0);
10411             glVertex(center + (v2 * size));
10412             glTexCoord2f(0, 0);
10413             glVertex(center + (v3 * size));
10414         }
10415         glEnd();
10416     }
10417 }
10418 
10419 
renderCrosshair(float pixelSize,double tsec)10420 static void renderCrosshair(float pixelSize, double tsec)
10421 {
10422     const float cursorMinRadius = 6.0f;
10423     const float cursorRadiusVariability = 4.0f;
10424     const float minCursorWidth = 7.0f;
10425     const float cursorPulsePeriod = 1.5f;
10426 
10427     float selectionSizeInPixels = pixelSize;
10428     float cursorRadius = selectionSizeInPixels + cursorMinRadius;
10429     cursorRadius += cursorRadiusVariability * (float) (0.5 + 0.5 * std::sin(tsec * 2 * PI / cursorPulsePeriod));
10430 
10431     // Enlarge the size of the cross hair sligtly when the selection
10432     // has a large apparent size
10433     float cursorGrow = max(1.0f, min(2.5f, (selectionSizeInPixels - 10.0f) / 100.0f));
10434 
10435     float h = 2.0f * cursorGrow;
10436     float cursorWidth = minCursorWidth * cursorGrow;
10437     float r0 = cursorRadius;
10438     float r1 = cursorRadius + cursorWidth;
10439 
10440     const unsigned int markCount = 4;
10441     Vec3f p0(r0, 0.0f, 0.0f);
10442     Vec3f p1(r1, -h, 0.0f);
10443     Vec3f p2(r1,  h, 0.0f);
10444 
10445     glBegin(GL_TRIANGLES);
10446     for (unsigned int i = 0; i < markCount; i++)
10447     {
10448         float theta = (float) (PI / 4.0) + (float) i / (float) markCount * (float) (2 * PI);
10449         float c = std::cos(theta);
10450         float s = std::sin(theta);
10451         glVertex3f(p0.x * c - p0.y * s, p0.x * s + p0.y * c, 0.0f);
10452         glVertex3f(p1.x * c - p1.y * s, p1.x * s + p1.y * c, 0.0f);
10453         glVertex3f(p2.x * c - p2.y * s, p2.x * s + p2.y * c, 0.0f);
10454     }
10455     glEnd();
10456 }
10457 
10458 
renderAnnotations(const vector<Annotation> & annotations,FontStyle fs)10459 void Renderer::renderAnnotations(const vector<Annotation>& annotations, FontStyle fs)
10460 {
10461     if (font[fs] == NULL)
10462         return;
10463 
10464     // Enable line smoothing for rendering symbols
10465     if ((renderFlags & ShowSmoothLines) != 0)
10466         enableSmoothLines();
10467 
10468 #ifdef USE_HDR
10469     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
10470 #endif
10471     glEnable(GL_TEXTURE_2D);
10472     font[fs]->bind();
10473     glEnable(GL_BLEND);
10474     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
10475 
10476     glMatrixMode(GL_PROJECTION);
10477     glPushMatrix();
10478     glLoadIdentity();
10479     gluOrtho2D(0, windowWidth, 0, windowHeight);
10480     glMatrixMode(GL_MODELVIEW);
10481     glPushMatrix();
10482     glLoadIdentity();
10483     glTranslatef(GLfloat((int) (windowWidth / 2)),
10484                  GLfloat((int) (windowHeight / 2)), 0);
10485 
10486     for (int i = 0; i < (int) annotations.size(); i++)
10487     {
10488         if (annotations[i].markerRep != NULL)
10489         {
10490             glPushMatrix();
10491             const MarkerRepresentation& markerRep = *annotations[i].markerRep;
10492 
10493             float size = markerRep.size();
10494             if (annotations[i].size > 0.0f)
10495             {
10496                 size = annotations[i].size;
10497             }
10498 
10499             glColor(annotations[i].color);
10500             glTranslatef((GLfloat) (int) annotations[i].position.x,
10501                          (GLfloat) (int) annotations[i].position.y, 0.0f);
10502 
10503             glDisable(GL_TEXTURE_2D);
10504             if (markerRep.symbol() == MarkerRepresentation::Crosshair)
10505                 renderCrosshair(size, realTime);
10506             else
10507                 markerRep.render(size);
10508             glEnable(GL_TEXTURE_2D);
10509 
10510             if (!markerRep.label().empty())
10511             {
10512                 int labelOffset = (int) markerRep.size() / 2;
10513                 glTranslatef(labelOffset + PixelOffset, -labelOffset - font[fs]->getHeight() + PixelOffset, 0.0f);
10514                 font[fs]->render(markerRep.label());
10515             }
10516             glPopMatrix();
10517         }
10518 
10519         if (annotations[i].labelText[0] != '\0')
10520         {
10521             glPushMatrix();
10522             int labelWidth = 0;
10523             int hOffset = 2;
10524             int vOffset = 0;
10525 
10526             switch (annotations[i].halign)
10527             {
10528             case AlignCenter:
10529                 labelWidth = (font[fs]->getWidth(annotations[i].labelText));
10530                 hOffset = -labelWidth / 2;
10531                 break;
10532 
10533             case AlignRight:
10534                 labelWidth = (font[fs]->getWidth(annotations[i].labelText));
10535                 hOffset = -(labelWidth + 2);
10536                 break;
10537 
10538             case AlignLeft:
10539                 if (annotations[i].markerRep != NULL)
10540                     hOffset = 2 + (int) annotations[i].markerRep->size() / 2;
10541                 break;
10542             }
10543 
10544             switch (annotations[i].valign)
10545             {
10546             case AlignCenter:
10547                 vOffset = -font[fs]->getHeight() / 2;
10548                 break;
10549             case VerticalAlignTop:
10550                 vOffset = -font[fs]->getHeight();
10551                 break;
10552             case VerticalAlignBottom:
10553                 vOffset = 0;
10554                 break;
10555             }
10556 
10557             glColor(annotations[i].color);
10558             glTranslatef((int) annotations[i].position.x + hOffset + PixelOffset,
10559                          (int) annotations[i].position.y + vOffset + PixelOffset, 0.0f);
10560             // EK TODO: Check where to replace (see '_(' above)
10561             font[fs]->render(annotations[i].labelText);
10562             glPopMatrix();
10563         }
10564     }
10565 
10566     glPopMatrix();
10567     glMatrixMode(GL_PROJECTION);
10568     glPopMatrix();
10569     glMatrixMode(GL_MODELVIEW);
10570 #ifdef USE_HDR
10571     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
10572 #endif
10573 
10574     if ((renderFlags & ShowSmoothLines) != 0)
10575         disableSmoothLines();
10576 }
10577 
10578 
10579 void
renderBackgroundAnnotations(FontStyle fs)10580 Renderer::renderBackgroundAnnotations(FontStyle fs)
10581 {
10582     glEnable(GL_DEPTH_TEST);
10583     renderAnnotations(backgroundAnnotations, fs);
10584     glDisable(GL_DEPTH_TEST);
10585 
10586     clearAnnotations(backgroundAnnotations);
10587 }
10588 
10589 
10590 void
renderForegroundAnnotations(FontStyle fs)10591 Renderer::renderForegroundAnnotations(FontStyle fs)
10592 {
10593     glDisable(GL_DEPTH_TEST);
10594     renderAnnotations(foregroundAnnotations, fs);
10595 
10596     clearAnnotations(foregroundAnnotations);
10597 }
10598 
10599 
10600 vector<Renderer::Annotation>::iterator
renderSortedAnnotations(vector<Annotation>::iterator iter,float nearDist,float farDist,FontStyle fs)10601 Renderer::renderSortedAnnotations(vector<Annotation>::iterator iter,
10602                                   float nearDist,
10603                                   float farDist,
10604                                   FontStyle fs)
10605 {
10606     if (font[fs] == NULL)
10607         return iter;
10608 
10609     glEnable(GL_DEPTH_TEST);
10610     glEnable(GL_TEXTURE_2D);
10611     font[fs]->bind();
10612     glEnable(GL_BLEND);
10613     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
10614 
10615     glMatrixMode(GL_PROJECTION);
10616     glPushMatrix();
10617     glLoadIdentity();
10618     gluOrtho2D(0, windowWidth, 0, windowHeight);
10619     glMatrixMode(GL_MODELVIEW);
10620     glPushMatrix();
10621     glLoadIdentity();
10622     glTranslatef(GLfloat((int) (windowWidth / 2)),
10623                  GLfloat((int) (windowHeight / 2)), 0);
10624 
10625     // Precompute values that will be used to generate the normalized device z value;
10626     // we're effectively just handling the projection instead of OpenGL. We use an orthographic
10627     // projection matrix in order to get the label text position exactly right but need to mimic
10628     // the depth coordinate generation of a perspective projection.
10629     float d1 = -(farDist + nearDist) / (farDist - nearDist);
10630     float d2 = -2.0f * nearDist * farDist / (farDist - nearDist);
10631 
10632     for (; iter != depthSortedAnnotations.end() && iter->position.z > nearDist; iter++)
10633     {
10634         // Compute normalized device z
10635         float ndc_z = d1 + d2 / -iter->position.z;
10636         ndc_z = min(1.0f, max(-1.0f, ndc_z)); // Clamp to [-1,1]
10637 
10638         // Offsets to left align label
10639         int labelHOffset = 0;
10640         int labelVOffset = 0;
10641 
10642         glPushMatrix();
10643         if (iter->markerRep != NULL)
10644         {
10645             const MarkerRepresentation& markerRep = *iter->markerRep;
10646             float size = markerRep.size();
10647             if (iter->size > 0.0f)
10648             {
10649                 size = iter->size;
10650             }
10651 
10652             glTranslatef((GLfloat) (int) iter->position.x, (GLfloat) (int) iter->position.y, ndc_z);
10653             glColor(iter->color);
10654 
10655             glDisable(GL_TEXTURE_2D);
10656             if (markerRep.symbol() == MarkerRepresentation::Crosshair)
10657                 renderCrosshair(size, realTime);
10658             else
10659                 markerRep.render(size);
10660             glEnable(GL_TEXTURE_2D);
10661 
10662             if (!markerRep.label().empty())
10663             {
10664                 int labelOffset = (int) markerRep.size() / 2;
10665                 glTranslatef(labelOffset + PixelOffset, -labelOffset - font[fs]->getHeight() + PixelOffset, 0.0f);
10666                 font[fs]->render(markerRep.label());
10667             }
10668         }
10669         else
10670         {
10671             glTranslatef((int) iter->position.x + PixelOffset + labelHOffset,
10672                          (int) iter->position.y + PixelOffset + labelVOffset,
10673                          ndc_z);
10674             glColor(iter->color);
10675             font[fs]->render(iter->labelText);
10676         }
10677         glPopMatrix();
10678     }
10679 
10680     glPopMatrix();
10681     glMatrixMode(GL_PROJECTION);
10682     glPopMatrix();
10683     glMatrixMode(GL_MODELVIEW);
10684     glDisable(GL_DEPTH_TEST);
10685 
10686     return iter;
10687 }
10688 
10689 
10690 vector<Renderer::Annotation>::iterator
renderAnnotations(vector<Annotation>::iterator startIter,vector<Annotation>::iterator endIter,float nearDist,float farDist,FontStyle fs)10691 Renderer::renderAnnotations(vector<Annotation>::iterator startIter,
10692                             vector<Annotation>::iterator endIter,
10693                             float nearDist,
10694                             float farDist,
10695                             FontStyle fs)
10696 {
10697     if (font[fs] == NULL)
10698         return endIter;
10699 
10700     glEnable(GL_DEPTH_TEST);
10701     glEnable(GL_TEXTURE_2D);
10702     font[fs]->bind();
10703     glEnable(GL_BLEND);
10704     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
10705 
10706     glMatrixMode(GL_PROJECTION);
10707     glPushMatrix();
10708     glLoadIdentity();
10709     gluOrtho2D(0, windowWidth, 0, windowHeight);
10710     glMatrixMode(GL_MODELVIEW);
10711     glPushMatrix();
10712     glLoadIdentity();
10713     glTranslatef(GLfloat((int) (windowWidth / 2)),
10714                  GLfloat((int) (windowHeight / 2)), 0);
10715 
10716     // Precompute values that will be used to generate the normalized device z value;
10717     // we're effectively just handling the projection instead of OpenGL. We use an orthographic
10718     // projection matrix in order to get the label text position exactly right but need to mimic
10719     // the depth coordinate generation of a perspective projection.
10720     float d1 = -(farDist + nearDist) / (farDist - nearDist);
10721     float d2 = -2.0f * nearDist * farDist / (farDist - nearDist);
10722 
10723     vector<Annotation>::iterator iter = startIter;
10724     for (; iter != endIter && iter->position.z > nearDist; iter++)
10725     {
10726         // Compute normalized device z
10727         float ndc_z = d1 + d2 / -iter->position.z;
10728         ndc_z = min(1.0f, max(-1.0f, ndc_z)); // Clamp to [-1,1]
10729 
10730         // Offsets to left align label
10731         int labelHOffset = 0;
10732         int labelVOffset = 0;
10733 
10734         if (iter->markerRep != NULL)
10735         {
10736             glPushMatrix();
10737             const MarkerRepresentation& markerRep = *iter->markerRep;
10738             float size = markerRep.size();
10739             if (iter->size > 0.0f)
10740             {
10741                 size = iter->size;
10742             }
10743 
10744             glTranslatef((GLfloat) (int) iter->position.x, (GLfloat) (int) iter->position.y, ndc_z);
10745             glColor(iter->color);
10746 
10747             glDisable(GL_TEXTURE_2D);
10748             if (markerRep.symbol() == MarkerRepresentation::Crosshair)
10749                 renderCrosshair(size, realTime);
10750             else
10751                 markerRep.render(size);
10752             glEnable(GL_TEXTURE_2D);
10753 
10754             if (!markerRep.label().empty())
10755             {
10756                 int labelOffset = (int) markerRep.size() / 2;
10757                 glTranslatef(labelOffset + PixelOffset, -labelOffset - font[fs]->getHeight() + PixelOffset, 0.0f);
10758                 font[fs]->render(markerRep.label());
10759             }
10760             glPopMatrix();
10761         }
10762 
10763         if (iter->labelText[0] != '\0')
10764         {
10765             if (iter->markerRep != NULL)
10766                 labelHOffset += (int) iter->markerRep->size() / 2 + 3;
10767 
10768             glPushMatrix();
10769             glTranslatef((int) iter->position.x + PixelOffset + labelHOffset,
10770                          (int) iter->position.y + PixelOffset + labelVOffset,
10771                          ndc_z);
10772             glColor(iter->color);
10773             font[fs]->render(iter->labelText);
10774             glPopMatrix();
10775         }
10776     }
10777 
10778     glPopMatrix();
10779     glMatrixMode(GL_PROJECTION);
10780     glPopMatrix();
10781     glMatrixMode(GL_MODELVIEW);
10782     glDisable(GL_DEPTH_TEST);
10783 
10784     return iter;
10785 }
10786 
10787 
renderMarkers(const MarkerList & markers,const UniversalCoord & cameraPosition,const Quatd & cameraOrientation,double jd)10788 void Renderer::renderMarkers(const MarkerList& markers,
10789                              const UniversalCoord& cameraPosition,
10790 			                 const Quatd& cameraOrientation,
10791                              double jd)
10792 {
10793     // Calculate the cosine of half the maximum field of view. We'll use this for
10794     // fast testing of marker visibility. The stored field of view is the
10795     // vertical field of view; we want the field of view as measured on the
10796     // diagonal between viewport corners.
10797     double h = tan(degToRad(fov / 2));
10798     double diag = sqrt(1.0 + square(h) + square(h * (double) windowWidth / (double) windowHeight));
10799     double cosFOV = 1.0 / diag;
10800 
10801     Vec3d viewVector = Vec3d(0.0, 0.0, -1.0) * cameraOrientation.toMatrix3();
10802 
10803     for (MarkerList::const_iterator iter = markers.begin(); iter != markers.end(); iter++)
10804     {
10805         UniversalCoord uc = iter->position(jd);
10806         Vec3d offset = (uc - cameraPosition) * astro::microLightYearsToKilometers(1.0);
10807 
10808         // Only render those markers that lie withing the field of view.
10809         if ((offset * viewVector) > cosFOV * offset.length())
10810         {
10811             double distance = offset.length();
10812             float symbolSize = 0.0f;
10813             if (iter->sizing() == DistanceBasedSize)
10814             {
10815                 symbolSize = (float) (iter->representation().size() / distance) / pixelSize;
10816             }
10817 
10818             if (iter->occludable())
10819             {
10820                 // If the marker is occludable, add it to the sorted annotation list if it's relatively
10821                 // nearby, and to the background list if it's very distant.
10822                 if (distance < astro::lightYearsToKilometers(1.0))
10823                 {
10824                     // Modify the marker position so that it is always in front of the marked object.
10825                     double boundingRadius;
10826                     if (iter->object().body() != NULL)
10827                         boundingRadius = iter->object().body()->getBoundingRadius();
10828                     else
10829                         boundingRadius = iter->object().radius();
10830                     offset *= (1.0 - boundingRadius * 1.01 / distance);
10831 
10832                     addSortedAnnotation(&(iter->representation()), EMPTY_STRING, iter->representation().color(),
10833                                         Point3f((float) offset.x, (float) offset.y, (float) offset.z),
10834                                         AlignLeft, VerticalAlignTop, symbolSize);
10835                 }
10836                 else
10837                 {
10838                     addAnnotation(backgroundAnnotations,
10839                                   &(iter->representation()), EMPTY_STRING, iter->representation().color(),
10840                                   Point3f((float) offset.x, (float) offset.y, (float) offset.z),
10841                                   AlignLeft, VerticalAlignTop, symbolSize);
10842                 }
10843             }
10844             else
10845             {
10846                 addAnnotation(foregroundAnnotations,
10847                               &(iter->representation()), EMPTY_STRING, iter->representation().color(),
10848                               Point3f((float) offset.x, (float) offset.y, (float) offset.z),
10849                               AlignLeft, VerticalAlignTop, symbolSize);
10850             }
10851         }
10852     }
10853 }
10854 
10855 
setStarStyle(StarStyle style)10856 void Renderer::setStarStyle(StarStyle style)
10857 {
10858     starStyle = style;
10859     markSettingsChanged();
10860 }
10861 
10862 
getStarStyle() const10863 Renderer::StarStyle Renderer::getStarStyle() const
10864 {
10865     return starStyle;
10866 }
10867 
10868 
StarVertexBuffer(unsigned int _capacity)10869 Renderer::StarVertexBuffer::StarVertexBuffer(unsigned int _capacity) :
10870     capacity(_capacity),
10871     vertices(NULL),
10872     texCoords(NULL),
10873     colors(NULL)
10874 {
10875     nStars = 0;
10876     vertices = new float[capacity * 12];
10877     texCoords = new float[capacity * 8];
10878     colors = new unsigned char[capacity * 16];
10879 
10880     // Fill the texture coordinate array now, since it will always have
10881     // the same contents.
10882     for (unsigned int i = 0; i < capacity; i++)
10883     {
10884         unsigned int n = i * 8;
10885         texCoords[n    ] = 0; texCoords[n + 1] = 0;
10886         texCoords[n + 2] = 1; texCoords[n + 3] = 0;
10887         texCoords[n + 4] = 1; texCoords[n + 5] = 1;
10888         texCoords[n + 6] = 0; texCoords[n + 7] = 1;
10889     }
10890 }
10891 
~StarVertexBuffer()10892 Renderer::StarVertexBuffer::~StarVertexBuffer()
10893 {
10894     if (vertices != NULL)
10895         delete[] vertices;
10896     if (colors != NULL)
10897         delete[] colors;
10898     if (texCoords != NULL)
10899         delete[] texCoords;
10900 }
10901 
start()10902 void Renderer::StarVertexBuffer::start()
10903 {
10904     glEnableClientState(GL_VERTEX_ARRAY);
10905     glVertexPointer(3, GL_FLOAT, 0, vertices);
10906     glEnableClientState(GL_COLOR_ARRAY);
10907     glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
10908     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
10909     glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
10910     glDisableClientState(GL_NORMAL_ARRAY);
10911 }
10912 
render()10913 void Renderer::StarVertexBuffer::render()
10914 {
10915     if (nStars != 0)
10916     {
10917         glDrawArrays(GL_QUADS, 0, nStars * 4);
10918         nStars = 0;
10919     }
10920 }
10921 
finish()10922 void Renderer::StarVertexBuffer::finish()
10923 {
10924     render();
10925     glDisableClientState(GL_COLOR_ARRAY);
10926     glDisableClientState(GL_VERTEX_ARRAY);
10927     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
10928 }
10929 
addStar(const Vec3f & pos,const Color & color,float size)10930 void Renderer::StarVertexBuffer::addStar(const Vec3f& pos,
10931                                          const Color& color,
10932                                          float size)
10933 {
10934     if (nStars < capacity)
10935     {
10936         int n = nStars * 12;
10937         vertices[n + 0]  = pos.x + v0.x * size;
10938         vertices[n + 1]  = pos.y + v0.y * size;
10939         vertices[n + 2]  = pos.z + v0.z * size;
10940         vertices[n + 3]  = pos.x + v1.x * size;
10941         vertices[n + 4]  = pos.y + v1.y * size;
10942         vertices[n + 5]  = pos.z + v1.z * size;
10943         vertices[n + 6]  = pos.x + v2.x * size;
10944         vertices[n + 7]  = pos.y + v2.y * size;
10945         vertices[n + 8]  = pos.z + v2.z * size;
10946         vertices[n + 9]  = pos.x + v3.x * size;
10947         vertices[n + 10] = pos.y + v3.y * size;
10948         vertices[n + 11] = pos.z + v3.z * size;
10949         n = nStars * 16;
10950         color.get(colors + n);
10951         color.get(colors + n + 4);
10952         color.get(colors + n + 8);
10953         color.get(colors + n + 12);
10954 
10955         nStars++;
10956     }
10957 
10958     if (nStars == capacity)
10959     {
10960         render();
10961         nStars = 0;
10962     }
10963 }
10964 
setBillboardOrientation(const Quatf & q)10965 void Renderer::StarVertexBuffer::setBillboardOrientation(const Quatf& q)
10966 {
10967     Mat3f m = q.toMatrix3();
10968     v0 = Vec3f(-1, -1, 0) * m;
10969     v1 = Vec3f( 1, -1, 0) * m;
10970     v2 = Vec3f( 1,  1, 0) * m;
10971     v3 = Vec3f(-1,  1, 0) * m;
10972 }
10973 
10974 
PointStarVertexBuffer(unsigned int _capacity)10975 Renderer::PointStarVertexBuffer::PointStarVertexBuffer(unsigned int _capacity) :
10976     capacity(_capacity),
10977     nStars(0),
10978     vertices(NULL),
10979     context(NULL),
10980     useSprites(false),
10981 	texture(NULL)
10982 {
10983     vertices = new StarVertex[capacity];
10984 }
10985 
~PointStarVertexBuffer()10986 Renderer::PointStarVertexBuffer::~PointStarVertexBuffer()
10987 {
10988     if (vertices != NULL)
10989         delete[] vertices;
10990 }
10991 
startSprites(const GLContext & _context)10992 void Renderer::PointStarVertexBuffer::startSprites(const GLContext& _context)
10993 {
10994     context = &_context;
10995     assert(context->getVertexProcessor() != NULL || !useSprites); // vertex shaders required for new star rendering
10996 
10997     unsigned int stride = sizeof(StarVertex);
10998     glEnableClientState(GL_VERTEX_ARRAY);
10999     glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
11000     glEnableClientState(GL_COLOR_ARRAY);
11001     glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
11002 
11003     VertexProcessor* vproc = context->getVertexProcessor();
11004     vproc->enable();
11005     vproc->use(vp::starDisc);
11006     vproc->enableAttribArray(6);
11007     vproc->attribArray(6, 1, GL_FLOAT, stride, &vertices[0].size);
11008 
11009     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
11010     glDisableClientState(GL_NORMAL_ARRAY);
11011 
11012     glEnable(GL_POINT_SPRITE_ARB);
11013     glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
11014 
11015     useSprites = true;
11016 }
11017 
startPoints(const GLContext & _context)11018 void Renderer::PointStarVertexBuffer::startPoints(const GLContext& _context)
11019 {
11020     context = &_context;
11021 
11022     unsigned int stride = sizeof(StarVertex);
11023     glEnableClientState(GL_VERTEX_ARRAY);
11024     glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
11025     glEnableClientState(GL_COLOR_ARRAY);
11026     glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
11027 
11028     // An option to control the size of the stars would be helpful.
11029     // Which size looks best depends a lot on the resolution and the
11030     // type of display device.
11031     // glPointSize(2.0f);
11032     // glEnable(GL_POINT_SMOOTH);
11033     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
11034     glDisable(GL_TEXTURE_2D);
11035 
11036     glDisableClientState(GL_NORMAL_ARRAY);
11037 
11038     useSprites = false;
11039 }
11040 
render()11041 void Renderer::PointStarVertexBuffer::render()
11042 {
11043     if (nStars != 0)
11044     {
11045         unsigned int stride = sizeof(StarVertex);
11046         if (useSprites)
11047         {
11048             glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
11049             glEnable(GL_TEXTURE_2D);
11050         }
11051         else
11052         {
11053             glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
11054             glDisable(GL_TEXTURE_2D);
11055             glPointSize(1.0f);
11056         }
11057         glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
11058         glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
11059 
11060         if (useSprites)
11061         {
11062             VertexProcessor* vproc = context->getVertexProcessor();
11063             vproc->attribArray(6, 1, GL_FLOAT, stride, &vertices[0].size);
11064         }
11065 
11066         if (texture != NULL)
11067             texture->bind();
11068         glDrawArrays(GL_POINTS, 0, nStars);
11069         nStars = 0;
11070     }
11071 }
11072 
finish()11073 void Renderer::PointStarVertexBuffer::finish()
11074 {
11075     render();
11076     glDisableClientState(GL_COLOR_ARRAY);
11077     glDisableClientState(GL_VERTEX_ARRAY);
11078     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
11079 
11080     if (useSprites)
11081     {
11082         VertexProcessor* vproc = context->getVertexProcessor();
11083         vproc->disableAttribArray(6);
11084         vproc->disable();
11085 
11086         glDisable(GL_POINT_SPRITE_ARB);
11087     }
11088     else
11089     {
11090         glEnable(GL_TEXTURE_2D);
11091     }
11092 }
11093 
addStar(const Vec3f & pos,const Color & color,float size)11094 void Renderer::PointStarVertexBuffer::addStar(const Vec3f& pos,
11095                                               const Color& color,
11096                                               float size)
11097 {
11098     if (nStars < capacity)
11099     {
11100         vertices[nStars].position = pos;
11101         vertices[nStars].size = size;
11102         color.get(vertices[nStars].color);
11103         nStars++;
11104     }
11105 
11106     if (nStars == capacity)
11107     {
11108         render();
11109         nStars = 0;
11110     }
11111 }
11112 
setTexture(Texture * _texture)11113 void Renderer::PointStarVertexBuffer::setTexture(Texture* _texture)
11114 {
11115 	texture = _texture;
11116 }
11117 
11118 
loadTextures(Body * body)11119 void Renderer::loadTextures(Body* body)
11120 {
11121     Surface& surface = body->getSurface();
11122 
11123     if (surface.baseTexture.tex[textureResolution] != InvalidResource)
11124         surface.baseTexture.find(textureResolution);
11125     if ((surface.appearanceFlags & Surface::ApplyBumpMap) != 0 &&
11126         context->bumpMappingSupported() &&
11127         surface.bumpTexture.tex[textureResolution] != InvalidResource)
11128         surface.bumpTexture.find(textureResolution);
11129     if ((surface.appearanceFlags & Surface::ApplyNightMap) != 0 &&
11130         (renderFlags & ShowNightMaps) != 0)
11131         surface.nightTexture.find(textureResolution);
11132     if ((surface.appearanceFlags & Surface::SeparateSpecularMap) != 0 &&
11133         surface.specularTexture.tex[textureResolution] != InvalidResource)
11134         surface.specularTexture.find(textureResolution);
11135 
11136     if ((renderFlags & ShowCloudMaps) != 0 &&
11137         body->getAtmosphere() != NULL &&
11138         body->getAtmosphere()->cloudTexture.tex[textureResolution] != InvalidResource)
11139     {
11140         body->getAtmosphere()->cloudTexture.find(textureResolution);
11141     }
11142 
11143     if (body->getRings() != NULL &&
11144         body->getRings()->texture.tex[textureResolution] != InvalidResource)
11145     {
11146         body->getRings()->texture.find(textureResolution);
11147     }
11148 
11149     if (body->getGeometry() != InvalidResource)
11150     {
11151         GetGeometryManager()->find(body->getGeometry());
11152     }
11153 }
11154 
11155 
invalidateOrbitCache()11156 void Renderer::invalidateOrbitCache()
11157 {
11158     orbitCache.clear();
11159 }
11160 
11161 
settingsHaveChanged() const11162 bool Renderer::settingsHaveChanged() const
11163 {
11164     return settingsChanged;
11165 }
11166 
11167 
markSettingsChanged()11168 void Renderer::markSettingsChanged()
11169 {
11170     settingsChanged = true;
11171     notifyWatchers();
11172 }
11173 
11174 
addWatcher(RendererWatcher * watcher)11175 void Renderer::addWatcher(RendererWatcher* watcher)
11176 {
11177     assert(watcher != NULL);
11178     watchers.insert(watchers.end(), watcher);
11179 }
11180 
removeWatcher(RendererWatcher * watcher)11181 void Renderer::removeWatcher(RendererWatcher* watcher)
11182 {
11183     list<RendererWatcher*>::iterator iter =
11184         find(watchers.begin(), watchers.end(), watcher);
11185     if (iter != watchers.end())
11186         watchers.erase(iter);
11187 }
11188 
notifyWatchers() const11189 void Renderer::notifyWatchers() const
11190 {
11191     for (list<RendererWatcher*>::const_iterator iter = watchers.begin();
11192          iter != watchers.end(); iter++)
11193     {
11194         (*iter)->notifyRenderSettingsChanged(this);
11195     }
11196 }
11197