1 // renderglsl.cpp
2 //
3 // Functions for rendering objects using dynamically generated GLSL shaders.
4 //
5 // Copyright (C) 2006-2007, Chris Laurel <claurel@shatters.net>
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 
12 #include <algorithm>
13 #include <cstdio>
14 #include <cstring>
15 #include <cassert>
16 
17 #ifndef _WIN32
18 #ifndef TARGET_OS_MAC
19 #include <config.h>
20 #endif
21 #endif /* _WIN32 */
22 
23 #include <celutil/debug.h>
24 #include <celmath/frustum.h>
25 #include <celmath/distance.h>
26 #include <celmath/intersect.h>
27 #include <celutil/utf8.h>
28 #include <celutil/util.h>
29 #include "gl.h"
30 #include "astro.h"
31 #include "glext.h"
32 #include "vecgl.h"
33 #include "glshader.h"
34 #include "shadermanager.h"
35 #include "spheremesh.h"
36 #include "lodspheremesh.h"
37 #include "model.h"
38 #include "regcombine.h"
39 #include "vertexprog.h"
40 #include "texmanager.h"
41 #include "meshmanager.h"
42 #include "render.h"
43 #include "renderinfo.h"
44 #include "renderglsl.h"
45 
46 
47 using namespace std;
48 
49 
50 const double AtmosphereExtinctionThreshold = 0.05;
51 
52 
53 // Render a planet sphere with GLSL shaders
renderSphere_GLSL(const RenderInfo & ri,const LightingState & ls,RingSystem * rings,Atmosphere * atmosphere,float cloudTexOffset,float radius,unsigned int textureRes,int renderFlags,const Mat4f & planetMat,const Frustum & frustum,const GLContext & context)54 void renderSphere_GLSL(const RenderInfo& ri,
55                        const LightingState& ls,
56                        RingSystem* rings,
57                        Atmosphere* atmosphere,
58                        float cloudTexOffset,
59                        float radius,
60                        unsigned int textureRes,
61                        int renderFlags,
62                        const Mat4f& planetMat,
63                        const Frustum& frustum,
64                        const GLContext& context)
65 {
66     Texture* textures[MAX_SPHERE_MESH_TEXTURES] =
67         { NULL, NULL, NULL, NULL, NULL, NULL };
68     unsigned int nTextures = 0;
69 
70     glDisable(GL_LIGHTING);
71 
72     ShaderProperties shadprop;
73     shadprop.nLights = min(ls.nLights, MaxShaderLights);
74 
75     // Set up the textures used by this object
76     if (ri.baseTex != NULL)
77     {
78         shadprop.texUsage = ShaderProperties::DiffuseTexture;
79         textures[nTextures++] = ri.baseTex;
80     }
81 
82     if (ri.bumpTex != NULL)
83     {
84         shadprop.texUsage |= ShaderProperties::NormalTexture;
85         textures[nTextures++] = ri.bumpTex;
86         if (ri.bumpTex->getFormatOptions() & Texture::DXT5NormalMap)
87             shadprop.texUsage |= ShaderProperties::CompressedNormalTexture;
88     }
89 
90     if (ri.specularColor != Color::Black)
91     {
92         shadprop.lightModel = ShaderProperties::PerPixelSpecularModel;
93         if (ri.glossTex == NULL)
94         {
95             shadprop.texUsage |= ShaderProperties::SpecularInDiffuseAlpha;
96         }
97         else
98         {
99             shadprop.texUsage |= ShaderProperties::SpecularTexture;
100             textures[nTextures++] = ri.glossTex;
101         }
102     }
103     else if (ri.lunarLambert != 0.0f)
104     {
105         // TODO: Lunar-Lambert model and specular color should not be mutually exclusive
106         shadprop.lightModel = ShaderProperties::LunarLambertModel;
107     }
108 
109     if (ri.nightTex != NULL)
110     {
111         shadprop.texUsage |= ShaderProperties::NightTexture;
112         textures[nTextures++] = ri.nightTex;
113     }
114 
115     if (ri.overlayTex != NULL)
116     {
117         shadprop.texUsage |= ShaderProperties::OverlayTexture;
118         textures[nTextures++] = ri.overlayTex;
119     }
120 
121     if (rings != NULL && (renderFlags & Renderer::ShowRingShadows) != 0)
122     {
123         Texture* ringsTex = rings->texture.find(textureRes);
124         if (ringsTex != NULL)
125         {
126             glx::glActiveTextureARB(GL_TEXTURE0_ARB + nTextures);
127             ringsTex->bind();
128             nTextures++;
129 
130             // Tweak the texture--set clamp to border and a border color with
131             // a zero alpha.
132             float bc[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
133             glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bc);
134             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
135                             GL_CLAMP_TO_BORDER_ARB);
136             glx::glActiveTextureARB(GL_TEXTURE0_ARB);
137 
138             shadprop.texUsage |= ShaderProperties::RingShadowTexture;
139         }
140     }
141 
142     if (atmosphere != NULL)
143     {
144         if (renderFlags & Renderer::ShowAtmospheres)
145         {
146             // Only use new atmosphere code in OpenGL 2.0 path when new style parameters are defined.
147             // ... but don't show atmospheres when there are no light sources.
148             if (atmosphere->mieScaleHeight > 0.0f && shadprop.nLights > 0)
149                 shadprop.texUsage |= ShaderProperties::Scattering;
150         }
151 
152         if ((renderFlags & Renderer::ShowCloudMaps) != 0 &&
153             (renderFlags & Renderer::ShowCloudShadows) != 0)
154         {
155             Texture* cloudTex = NULL;
156             if (atmosphere->cloudTexture.tex[textureRes] != InvalidResource)
157                 cloudTex = atmosphere->cloudTexture.find(textureRes);
158 
159             // The current implementation of cloud shadows is not compatible
160             // with virtual or split textures.
161             bool allowCloudShadows = true;
162             for (unsigned int i = 0; i < nTextures; i++)
163             {
164                 if (textures[i] != NULL &&
165                     (textures[i]->getLODCount() > 1 ||
166                      textures[i]->getUTileCount(0) > 1 ||
167                      textures[i]->getVTileCount(0) > 1))
168                 {
169                     allowCloudShadows = false;
170                 }
171             }
172 
173             // Split cloud shadows can't cast shadows
174             if (cloudTex != NULL)
175             {
176                 if (cloudTex->getLODCount() > 1 ||
177                     cloudTex->getUTileCount(0) > 1 ||
178                     cloudTex->getVTileCount(0) > 1)
179                 {
180                     allowCloudShadows = false;
181                 }
182             }
183 
184             if (cloudTex != NULL && allowCloudShadows && atmosphere->cloudShadowDepth > 0.0f)
185             {
186                 shadprop.texUsage |= ShaderProperties::CloudShadowTexture;
187                 textures[nTextures++] = cloudTex;
188                 glx::glActiveTextureARB(GL_TEXTURE0_ARB + nTextures);
189                 cloudTex->bind();
190                 glx::glActiveTextureARB(GL_TEXTURE0_ARB);
191             }
192         }
193     }
194 
195     // Set the shadow information.
196     // Track the total number of shadows; if there are too many, we'll have
197     // to fall back to multipass.
198     unsigned int totalShadows = 0;
199     for (unsigned int li = 0; li < ls.nLights; li++)
200     {
201         if (ls.shadows[li] && !ls.shadows[li]->empty())
202         {
203             unsigned int nShadows = (unsigned int) min((size_t) MaxShaderShadows, ls.shadows[li]->size());
204             shadprop.setShadowCountForLight(li, nShadows);
205             totalShadows += nShadows;
206         }
207     }
208 
209     // Get a shader for the current rendering configuration
210     CelestiaGLProgram* prog = GetShaderManager().getShader(shadprop);
211     if (prog == NULL)
212         return;
213 
214     prog->use();
215 
216 #ifdef USE_HDR
217     prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black, ri.nightLightScale);
218 #else
219     prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black);
220 #endif
221 
222     prog->eyePosition = ls.eyePos_obj;
223     prog->shininess = ri.specularPower;
224     if (shadprop.lightModel == ShaderProperties::LunarLambertModel)
225         prog->lunarLambert = ri.lunarLambert;
226 
227     if (shadprop.texUsage & ShaderProperties::RingShadowTexture)
228     {
229         float ringWidth = rings->outerRadius - rings->innerRadius;
230         prog->ringRadius = rings->innerRadius / radius;
231         prog->ringWidth = radius / ringWidth;
232     }
233 
234     if (shadprop.texUsage & ShaderProperties::CloudShadowTexture)
235     {
236         prog->shadowTextureOffset = cloudTexOffset;
237         prog->cloudHeight = 1.0f + atmosphere->cloudHeight / radius;
238     }
239 
240     if (shadprop.hasScattering())
241     {
242         prog->setAtmosphereParameters(*atmosphere, radius, radius);
243     }
244 
245     if (shadprop.shadowCounts != 0)
246         prog->setEclipseShadowParameters(ls, radius, planetMat);
247 
248     glColor(ri.color);
249 
250     unsigned int attributes = LODSphereMesh::Normals;
251     if (ri.bumpTex != NULL)
252         attributes |= LODSphereMesh::Tangents;
253     g_lodSphere->render(context,
254                         attributes,
255                         frustum, ri.pixWidth,
256                         textures[0], textures[1], textures[2], textures[3]);
257 
258     glx::glUseProgramObjectARB(0);
259 }
260 
261 
262 /*! Render a mesh object
263  *  Parameters:
264  *    tsec : animation clock time in seconds
265  */
renderGeometry_GLSL(Geometry * geometry,const RenderInfo & ri,ResourceHandle texOverride,const LightingState & ls,const Atmosphere * atmosphere,float geometryScale,int renderFlags,const Mat4f & planetMat,double tsec)266 void renderGeometry_GLSL(Geometry* geometry,
267                          const RenderInfo& ri,
268                          ResourceHandle texOverride,
269                          const LightingState& ls,
270                          const Atmosphere* atmosphere,
271                          float geometryScale,
272                          int renderFlags,
273                          const Mat4f& planetMat,
274                          double tsec)
275 {
276     glDisable(GL_LIGHTING);
277 
278     GLSL_RenderContext rc(ls, geometryScale, planetMat);
279 
280     if (renderFlags & Renderer::ShowAtmospheres)
281     {
282         rc.setAtmosphere(atmosphere);
283     }
284 
285     rc.setCameraOrientation(ri.orientation);
286     rc.setPointScale(ri.pointScale);
287 
288     // Handle extended material attributes (per model only, not per submesh)
289     rc.setLunarLambert(ri.lunarLambert);
290 
291     // Handle material override; a texture specified in an ssc file will
292     // override all materials specified in the geometry file.
293     if (texOverride != InvalidResource)
294     {
295         Mesh::Material m;
296         m.diffuse = ri.color;
297         m.specular = ri.specularColor;
298         m.specularPower = ri.specularPower;
299         m.maps[Mesh::DiffuseMap] = texOverride;
300         rc.setMaterial(&m);
301         rc.lock();
302         geometry->render(rc, tsec);
303     }
304     else
305     {
306         geometry->render(rc, tsec);
307     }
308 
309     glx::glUseProgramObjectARB(0);
310 }
311 
312 
313 /*! Render a mesh object without lighting.
314  *  Parameters:
315  *    tsec : animation clock time in seconds
316  */
renderGeometry_GLSL_Unlit(Geometry * geometry,const RenderInfo & ri,ResourceHandle texOverride,float geometryScale,int,const Mat4f &,double tsec)317 void renderGeometry_GLSL_Unlit(Geometry* geometry,
318                                const RenderInfo& ri,
319                                ResourceHandle texOverride,
320                                float geometryScale,
321                                int /* renderFlags */,
322                                const Mat4f& /* planetMat */,
323                                double tsec)
324 {
325     glDisable(GL_LIGHTING);
326 
327     GLSLUnlit_RenderContext rc(geometryScale);
328 
329     rc.setPointScale(ri.pointScale);
330 
331     // Handle material override; a texture specified in an ssc file will
332     // override all materials specified in the model file.
333     if (texOverride != InvalidResource)
334     {
335         Mesh::Material m;
336         m.diffuse = ri.color;
337         m.specular = ri.specularColor;
338         m.specularPower = ri.specularPower;
339         m.maps[Mesh::DiffuseMap] = texOverride;
340         rc.setMaterial(&m);
341         rc.lock();
342         geometry->render(rc, tsec);
343     }
344     else
345     {
346         geometry->render(rc, tsec);
347     }
348 
349     glx::glUseProgramObjectARB(0);
350 }
351 
352 
353 // Render the cloud sphere for a world a cloud layer defined
renderClouds_GLSL(const RenderInfo & ri,const LightingState & ls,Atmosphere * atmosphere,Texture * cloudTex,Texture * cloudNormalMap,float texOffset,RingSystem * rings,float radius,unsigned int textureRes,int renderFlags,const Mat4f & planetMat,const Frustum & frustum,const GLContext & context)354 void renderClouds_GLSL(const RenderInfo& ri,
355                        const LightingState& ls,
356                        Atmosphere* atmosphere,
357                        Texture* cloudTex,
358                        Texture* cloudNormalMap,
359                        float texOffset,
360                        RingSystem* rings,
361                        float radius,
362                        unsigned int textureRes,
363                        int renderFlags,
364                        const Mat4f& planetMat,
365                        const Frustum& frustum,
366                        const GLContext& context)
367 {
368     Texture* textures[MAX_SPHERE_MESH_TEXTURES] =
369         { NULL, NULL, NULL, NULL, NULL, NULL };
370     unsigned int nTextures = 0;
371 
372     glDisable(GL_LIGHTING);
373 
374     ShaderProperties shadprop;
375     shadprop.nLights = ls.nLights;
376 
377     // Set up the textures used by this object
378     if (cloudTex != NULL)
379     {
380         shadprop.texUsage = ShaderProperties::DiffuseTexture;
381         textures[nTextures++] = cloudTex;
382     }
383 
384     if (cloudNormalMap != NULL)
385     {
386         shadprop.texUsage |= ShaderProperties::NormalTexture;
387         textures[nTextures++] = cloudNormalMap;
388         if (cloudNormalMap->getFormatOptions() & Texture::DXT5NormalMap)
389             shadprop.texUsage |= ShaderProperties::CompressedNormalTexture;
390     }
391 
392     if (rings != NULL && (renderFlags & Renderer::ShowRingShadows) != 0)
393     {
394         Texture* ringsTex = rings->texture.find(textureRes);
395         if (ringsTex != NULL)
396         {
397             glx::glActiveTextureARB(GL_TEXTURE0_ARB + nTextures);
398             ringsTex->bind();
399             nTextures++;
400 
401             // Tweak the texture--set clamp to border and a border color with
402             // a zero alpha.
403             float bc[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
404             glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bc);
405             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
406                             GL_CLAMP_TO_BORDER_ARB);
407             glx::glActiveTextureARB(GL_TEXTURE0_ARB);
408 
409             shadprop.texUsage |= ShaderProperties::RingShadowTexture;
410         }
411     }
412 
413     if (atmosphere != NULL)
414     {
415         if (renderFlags & Renderer::ShowAtmospheres)
416         {
417             // Only use new atmosphere code in OpenGL 2.0 path when new style parameters are defined.
418             // ... but don't show atmospheres when there are no light sources.
419             if (atmosphere->mieScaleHeight > 0.0f && shadprop.nLights > 0)
420                 shadprop.texUsage |= ShaderProperties::Scattering;
421         }
422     }
423 
424     // Set the shadow information.
425     // Track the total number of shadows; if there are too many, we'll have
426     // to fall back to multipass.
427     unsigned int totalShadows = 0;
428     for (unsigned int li = 0; li < ls.nLights; li++)
429     {
430         if (ls.shadows[li] && !ls.shadows[li]->empty())
431         {
432             unsigned int nShadows = (unsigned int) min((size_t) MaxShaderShadows, ls.shadows[li]->size());
433             shadprop.setShadowCountForLight(li, nShadows);
434             totalShadows += nShadows;
435         }
436     }
437 
438     // Get a shader for the current rendering configuration
439     CelestiaGLProgram* prog = GetShaderManager().getShader(shadprop);
440     if (prog == NULL)
441         return;
442 
443     prog->use();
444 
445     prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black);
446     prog->eyePosition = ls.eyePos_obj;
447     prog->ambientColor = Vec3f(ri.ambientColor.red(), ri.ambientColor.green(),
448                                ri.ambientColor.blue());
449     prog->textureOffset = texOffset;
450 
451     float cloudRadius = radius + atmosphere->cloudHeight;
452 
453     if (shadprop.hasScattering())
454     {
455         prog->setAtmosphereParameters(*atmosphere, radius, cloudRadius);
456     }
457 
458     if (shadprop.texUsage & ShaderProperties::RingShadowTexture)
459     {
460         float ringWidth = rings->outerRadius - rings->innerRadius;
461         prog->ringRadius = rings->innerRadius / cloudRadius;
462         prog->ringWidth = 1.0f / (ringWidth / cloudRadius);
463     }
464 
465     if (shadprop.shadowCounts != 0)
466         prog->setEclipseShadowParameters(ls, cloudRadius, planetMat);
467 
468     unsigned int attributes = LODSphereMesh::Normals;
469     if (cloudNormalMap != NULL)
470         attributes |= LODSphereMesh::Tangents;
471     g_lodSphere->render(context,
472                         attributes,
473                         frustum, ri.pixWidth,
474                         textures[0], textures[1], textures[2], textures[3]);
475 
476     prog->textureOffset = 0.0f;
477 
478     glx::glUseProgramObjectARB(0);
479 }
480 
481 
482 // Render the sky sphere for a world with an atmosphere
483 void
renderAtmosphere_GLSL(const RenderInfo & ri,const LightingState & ls,Atmosphere * atmosphere,float radius,const Mat4f &,const Frustum & frustum,const GLContext & context)484 renderAtmosphere_GLSL(const RenderInfo& ri,
485                       const LightingState& ls,
486                       Atmosphere* atmosphere,
487                       float radius,
488                       const Mat4f& /*planetMat*/,
489                       const Frustum& frustum,
490                       const GLContext& context)
491 {
492     // Currently, we just skip rendering an atmosphere when there are no
493     // light sources, even though the atmosphere would still the light
494     // of planets and stars behind it.
495     if (ls.nLights == 0)
496         return;
497 
498     glDisable(GL_LIGHTING);
499 
500     ShaderProperties shadprop;
501     shadprop.nLights = ls.nLights;
502 
503     shadprop.texUsage |= ShaderProperties::Scattering;
504     shadprop.lightModel = ShaderProperties::AtmosphereModel;
505 
506     // Get a shader for the current rendering configuration
507     CelestiaGLProgram* prog = GetShaderManager().getShader(shadprop);
508     if (prog == NULL)
509         return;
510 
511     prog->use();
512 
513     prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black);
514     prog->ambientColor = Vec3f(0.0f, 0.0f, 0.0f);
515 
516     float atmosphereRadius = radius + -atmosphere->mieScaleHeight * (float) log(AtmosphereExtinctionThreshold);
517     float atmScale = atmosphereRadius / radius;
518 
519     prog->eyePosition = Point3f(ls.eyePos_obj.x / atmScale, ls.eyePos_obj.y / atmScale, ls.eyePos_obj.z / atmScale);
520     prog->setAtmosphereParameters(*atmosphere, radius, atmosphereRadius);
521 
522 #if 0
523     // Currently eclipse shadows are ignored when rendering atmospheres
524     if (shadprop.shadowCounts != 0)
525         prog->setEclipseShadowParameters(ls, radius, planetMat);
526 #endif
527 
528     glPushMatrix();
529     glScalef(atmScale, atmScale, atmScale);
530     glFrontFace(GL_CW);
531     glEnable(GL_BLEND);
532     glDepthMask(GL_FALSE);
533     glBlendFunc(GL_ONE, GL_SRC_ALPHA);
534 
535     g_lodSphere->render(context,
536                         LODSphereMesh::Normals,
537                         frustum,
538                         ri.pixWidth,
539                         NULL);
540 
541     glDisable(GL_BLEND);
542     glDepthMask(GL_TRUE);
543     glFrontFace(GL_CCW);
544     glPopMatrix();
545 
546 
547     glx::glUseProgramObjectARB(0);
548 
549     //glx::glActiveTextureARB(GL_TEXTURE0_ARB);
550     //glEnable(GL_TEXTURE_2D);
551 }
552 
553 
renderRingSystem(float innerRadius,float outerRadius,float beginAngle,float endAngle,unsigned int nSections)554 static void renderRingSystem(float innerRadius,
555                              float outerRadius,
556                              float beginAngle,
557                              float endAngle,
558                              unsigned int nSections)
559 {
560     float angle = endAngle - beginAngle;
561 
562     glBegin(GL_QUAD_STRIP);
563     for (unsigned int i = 0; i <= nSections; i++)
564     {
565         float t = (float) i / (float) nSections;
566         float theta = beginAngle + t * angle;
567         float s = (float) sin(theta);
568         float c = (float) cos(theta);
569         glTexCoord2f(0, 0.5f);
570         glVertex3f(c * innerRadius, 0, s * innerRadius);
571         glTexCoord2f(1, 0.5f);
572         glVertex3f(c * outerRadius, 0, s * outerRadius);
573     }
574     glEnd();
575 }
576 
577 
578 // Render a planetary ring system
renderRings_GLSL(RingSystem & rings,RenderInfo & ri,const LightingState & ls,float planetRadius,float planetOblateness,unsigned int textureResolution,bool renderShadow,unsigned int nSections)579 void renderRings_GLSL(RingSystem& rings,
580                       RenderInfo& ri,
581                       const LightingState& ls,
582                       float planetRadius,
583                       float planetOblateness,
584                       unsigned int textureResolution,
585                       bool renderShadow,
586                       unsigned int nSections)
587 {
588     float inner = rings.innerRadius / planetRadius;
589     float outer = rings.outerRadius / planetRadius;
590     Texture* ringsTex = rings.texture.find(textureResolution);
591 
592     ShaderProperties shadprop;
593     // Set up the shader properties for ring rendering
594     {
595         shadprop.lightModel = ShaderProperties::RingIllumModel;
596         shadprop.nLights = min(ls.nLights, MaxShaderLights);
597 
598         if (renderShadow)
599         {
600             // Set one shadow (the planet's) per light
601             for (unsigned int li = 0; li < ls.nLights; li++)
602                 shadprop.setShadowCountForLight(li, 1);
603         }
604 
605         if (ringsTex)
606             shadprop.texUsage = ShaderProperties::DiffuseTexture;
607     }
608 
609 
610     // Get a shader for the current rendering configuration
611     CelestiaGLProgram* prog = GetShaderManager().getShader(shadprop);
612     if (prog == NULL)
613         return;
614 
615     prog->use();
616 
617     prog->eyePosition = ls.eyePos_obj;
618     prog->ambientColor = Vec3f(ri.ambientColor.red(), ri.ambientColor.green(),
619                                ri.ambientColor.blue());
620     prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black);
621 
622     for (unsigned int li = 0; li < ls.nLights; li++)
623     {
624         const DirectionalLight& light = ls.lights[li];
625 
626         // Compute the projection vectors based on the sun direction.
627         // I'm being a little careless here--if the sun direction lies
628         // along the y-axis, this will fail.  It's unlikely that a
629         // planet would ever orbit underneath its sun (an orbital
630         // inclination of 90 degrees), but this should be made
631         // more robust anyway.
632         Vec3f axis = Vec3f(0, 1, 0) ^ light.direction_obj;
633         float cosAngle = Vec3f(0.0f, 1.0f, 0.0f) * light.direction_obj;
634         /*float angle = (float) acos(cosAngle);     Unused*/
635         axis.normalize();
636 
637         float tScale = 1.0f;
638         if (planetOblateness != 0.0f)
639         {
640             // For oblate planets, the size of the shadow volume will vary
641             // based on the light direction.
642 
643             // A vertical slice of the planet is an ellipse
644             float a = 1.0f;                          // semimajor axis
645             float b = a * (1.0f - planetOblateness); // semiminor axis
646             float ecc2 = 1.0f - (b * b) / (a * a);   // square of eccentricity
647 
648             // Calculate the radius of the ellipse at the incident angle of the
649             // light on the ring plane + 90 degrees.
650             float r = a * (float) sqrt((1.0f - ecc2) /
651                                        (1.0f - ecc2 * square(cosAngle)));
652 
653             tScale *= a / r;
654         }
655 
656         // The s axis is perpendicular to the shadow axis in the plane of the
657         // of the rings, and the t axis completes the orthonormal basis.
658         Vec3f sAxis = axis * 0.5f;
659         Vec3f tAxis = (axis ^ light.direction_obj) * 0.5f * tScale;
660         Vec4f texGenS(sAxis.x, sAxis.y, sAxis.z, 0.5f);
661         Vec4f texGenT(tAxis.x, tAxis.y, tAxis.z, 0.5f);
662 
663         // r0 and r1 determine the size of the planet's shadow and penumbra
664         // on the rings.
665         // TODO: A more accurate ring shadow calculation would set r1 / r0
666         // to the ratio of the apparent sizes of the planet and sun as seen
667         // from the rings. Even more realism could be attained by letting
668         // this ratio vary across the rings, though it may not make enough
669         // of a visual difference to be worth the extra effort.
670         float r0 = 0.24f;
671         float r1 = 0.25f;
672         float bias = 1.0f / (1.0f - r1 / r0);
673 
674         prog->shadows[li][0].texGenS = texGenS;
675         prog->shadows[li][0].texGenT = texGenT;
676         prog->shadows[li][0].maxDepth = 1.0f;
677         prog->shadows[li][0].falloff = bias / r0;
678     }
679 
680     glEnable(GL_BLEND);
681     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
682 
683     if (ringsTex != NULL)
684         ringsTex->bind();
685     else
686         glDisable(GL_TEXTURE_2D);
687 
688     renderRingSystem(inner, outer, 0, (float) PI * 2.0f, nSections);
689     renderRingSystem(inner, outer, (float) PI * 2.0f, 0, nSections);
690 
691     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
692 
693     glx::glUseProgramObjectARB(0);
694 }
695