render.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:390k
源码类别:

OpenGL

开发平台:

Visual C++

  1.             ri.baseTex->bind();
  2.             glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  3.             ri.bumpTex->bind();
  4.             setupBumpTexenv();
  5.             g_lodSphere->render(context,
  6.                                 LODSphereMesh::Normals | LODSphereMesh::Tangents |
  7.                                 LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
  8.                                 frustum, ri.pixWidth,
  9.                                 ri.bumpTex, ri.baseTex);
  10.             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  11.         }
  12.     }
  13.     else
  14.     {
  15.         if (ls.nLights > 1)
  16.             vproc->use(vp::diffuse_2light);
  17.         else
  18.             vproc->use(vp::diffuse);
  19.         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  20.         g_lodSphere->render(context,
  21.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
  22.                             LODSphereMesh::VertexProgParams,
  23.                             frustum, ri.pixWidth,
  24.                             ri.baseTex);
  25.     }
  26.     // Render a specular pass; can't be done in one pass because
  27.     // specular needs to be modulated with a gloss map.
  28.     if (ri.specularColor != Color::Black)
  29.     {
  30.         glEnable(GL_BLEND);
  31.         glBlendFunc(GL_ONE, GL_ONE);
  32.         vproc->use(vp::glossMap);
  33.         if (ri.glossTex != NULL)
  34.             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  35.         else
  36.             setupTexenvGlossMapAlpha();
  37.         g_lodSphere->render(context,
  38.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  39.                             frustum, ri.pixWidth,
  40.                             ri.glossTex != NULL ? ri.glossTex : ri.baseTex);
  41.         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  42.         glDisable(GL_BLEND);
  43.     }
  44.     if (ri.nightTex != NULL)
  45.     {
  46.         ri.nightTex->bind();
  47. #ifdef USE_HDR
  48.         // Scale night light intensity
  49. #ifdef HDR_COMPRESS
  50.         Color nightColor0(ls.lights[0].color.red()  *ri.color.red()  *2.f,
  51.                           ls.lights[0].color.green()*ri.color.green()*2.f,
  52.                           ls.lights[0].color.blue() *ri.color.blue() *2.f,
  53.                           ri.nightLightScale); // Modulate brightness with alpha
  54. #else
  55.         Color nightColor0(ls.lights[0].color.red()  *ri.color.red(),
  56.                           ls.lights[0].color.green()*ri.color.green(),
  57.                           ls.lights[0].color.blue() *ri.color.blue(),
  58.                           ri.nightLightScale); // Modulate brightness with alpha
  59. #endif
  60.         vproc->parameter(vp::DiffuseColor0, nightColor0);
  61. #endif
  62.         if (ls.nLights > 1)
  63.         {
  64. #ifdef USE_HDR
  65. #ifdef HDR_COMPRESS
  66.             Color nightColor1(ls.lights[1].color.red()  *ri.color.red()  *2.f,
  67.                               ls.lights[1].color.green()*ri.color.green()*2.f,
  68.                               ls.lights[1].color.blue() *ri.color.blue() *2.f,
  69.                               ri.nightLightScale);
  70. #else
  71.             Color nightColor1(ls.lights[1].color.red()  *ri.color.red(),
  72.                               ls.lights[1].color.green()*ri.color.green(),
  73.                               ls.lights[1].color.blue() *ri.color.blue(),
  74.                               ri.nightLightScale);
  75. #endif
  76.             vproc->parameter(vp::DiffuseColor0, nightColor1);
  77. #endif
  78. #ifdef HDR_COMPRESS
  79.             vproc->use(vp::nightLights_2lightHDR);
  80. #else
  81.             vproc->use(vp::nightLights_2light);
  82. #endif
  83.         }
  84.         else
  85.         {
  86. #ifdef HDR_COMPRESS
  87.             vproc->use(vp::nightLightsHDR);
  88. #else
  89.             vproc->use(vp::nightLights);
  90. #endif
  91.         }
  92. #ifdef USE_HDR
  93.         glEnable(GL_BLEND);
  94.         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  95. #else
  96.         setupNightTextureCombine();
  97.         glEnable(GL_BLEND);
  98.         glBlendFunc(GL_ONE, GL_ONE);
  99. #endif
  100.         g_lodSphere->render(context,
  101.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  102.                             frustum, ri.pixWidth,
  103.                             ri.nightTex);
  104. #ifdef USE_HDR
  105.         vproc->parameter(vp::DiffuseColor0, ls.lights[0].color * ri.color);
  106.         if (ls.nLights > 1)
  107.             vproc->parameter(vp::DiffuseColor1, ls.lights[1].color * ri.color);
  108. #endif
  109.         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  110.     }
  111.     if (ri.overlayTex != NULL)
  112.     {
  113.         ri.overlayTex->bind();
  114.         vproc->use(vp::diffuse);
  115.         glEnable(GL_BLEND);
  116.         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  117.         g_lodSphere->render(context,
  118.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  119.                             frustum, ri.pixWidth,
  120.                             ri.overlayTex);
  121.         glBlendFunc(GL_ONE, GL_ONE);
  122.     }
  123.     vproc->disable();
  124. }
  125. static void renderSphere_Combiners_VP(const RenderInfo& ri,
  126.                                       const LightingState& ls,
  127.                                       const Frustum& frustum,
  128.                                       const GLContext& context)
  129. {
  130.     Texture* textures[4];
  131.     VertexProcessor* vproc = context.getVertexProcessor();
  132.     assert(vproc != NULL);
  133.     if (ri.baseTex == NULL)
  134.     {
  135.         glDisable(GL_TEXTURE_2D);
  136.     }
  137.     else
  138.     {
  139.         glEnable(GL_TEXTURE_2D);
  140.         ri.baseTex->bind();
  141.     }
  142.     // Set up the fog parameters if the haze density is non-zero
  143.     float hazeDensity = ri.hazeColor.alpha();
  144. #ifdef HDR_COMPRESS
  145.     Color hazeColor(ri.hazeColor.red()   * 0.5f,
  146.                     ri.hazeColor.green() * 0.5f,
  147.                     ri.hazeColor.blue()  * 0.5f,
  148.                     hazeDensity);
  149. #else
  150.     Color hazeColor = ri.hazeColor;
  151. #endif
  152.     if (hazeDensity > 0.0f && !buggyVertexProgramEmulation)
  153.     {
  154.         glEnable(GL_FOG);
  155.         float fogColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
  156.         fogColor[0] = hazeColor.red();
  157.         fogColor[1] = hazeColor.green();
  158.         fogColor[2] = hazeColor.blue();
  159.         glFogfv(GL_FOG_COLOR, fogColor);
  160.         glFogi(GL_FOG_MODE, GL_LINEAR);
  161.         glFogf(GL_FOG_START, 0.0);
  162.         glFogf(GL_FOG_END, 1.0f / hazeDensity);
  163.     }
  164.     vproc->enable();
  165.     vproc->parameter(vp::EyePosition, ri.eyePos_obj);
  166.     setLightParameters_VP(*vproc, ls, ri.color, ri.specularColor);
  167.     vproc->parameter(vp::SpecularExponent, 0.0f, 1.0f, 0.5f, ri.specularPower);
  168.     vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
  169.     vproc->parameter(vp::HazeColor, hazeColor);
  170.     // Don't use a normal map if it's a dxt5nm map--only the GLSL path
  171.     // can handle them.
  172.     if (ri.bumpTex != NULL &&
  173.         (ri.bumpTex->getFormatOptions() & Texture::DXT5NormalMap) == 0)
  174.     {
  175.         if (hazeDensity > 0.0f)
  176.         {
  177. #ifdef HDR_COMPRESS
  178.             vproc->use(vp::diffuseBumpHazeHDR);
  179. #else
  180.             vproc->use(vp::diffuseBumpHaze);
  181. #endif
  182.         }
  183.         else
  184.         {
  185. #ifdef HDR_COMPRESS
  186.             vproc->use(vp::diffuseBumpHDR);
  187. #else
  188.             vproc->use(vp::diffuseBump);
  189. #endif
  190.         }
  191.         SetupCombinersDecalAndBumpMap(*(ri.bumpTex),
  192.                                       ri.ambientColor * ri.color,
  193.                                       ri.sunColor * ri.color);
  194.         g_lodSphere->render(context,
  195.                             LODSphereMesh::Normals | LODSphereMesh::Tangents |
  196.                             LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
  197.                             frustum, ri.pixWidth,
  198.                             ri.baseTex, ri.bumpTex);
  199.         DisableCombiners();
  200.         // Render a specular pass
  201.         if (ri.specularColor != Color::Black)
  202.         {
  203.             glEnable(GL_BLEND);
  204.             glBlendFunc(GL_ONE, GL_ONE);
  205.             glEnable(GL_COLOR_SUM_EXT);
  206.             vproc->use(vp::specular);
  207.             // Disable ambient and diffuse
  208.             vproc->parameter(vp::AmbientColor, Color::Black);
  209.             vproc->parameter(vp::DiffuseColor0, Color::Black);
  210.             SetupCombinersGlossMap(ri.glossTex != NULL ? GL_TEXTURE0_ARB : 0);
  211.             textures[0] = ri.glossTex != NULL ? ri.glossTex : ri.baseTex;
  212.             g_lodSphere->render(context,
  213.                                 LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  214.                                 frustum, ri.pixWidth,
  215.                                 textures, 1);
  216.             // re-enable diffuse
  217.             vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
  218.             DisableCombiners();
  219.             glDisable(GL_COLOR_SUM_EXT);
  220.             glDisable(GL_BLEND);
  221.         }
  222.     }
  223.     else if (ri.specularColor != Color::Black)
  224.     {
  225.         glEnable(GL_COLOR_SUM_EXT);
  226.         if (ls.nLights > 1)
  227.             vproc->use(vp::specular_2light);
  228.         else
  229.             vproc->use(vp::specular);
  230.         SetupCombinersGlossMapWithFog(ri.glossTex != NULL ? GL_TEXTURE1_ARB : 0);
  231.         unsigned int attributes = LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
  232.             LODSphereMesh::VertexProgParams;
  233.         g_lodSphere->render(context,
  234.                             attributes, frustum, ri.pixWidth,
  235.                             ri.baseTex, ri.glossTex);
  236.         DisableCombiners();
  237.         glDisable(GL_COLOR_SUM_EXT);
  238.     }
  239.     else
  240.     {
  241.         if (ls.nLights > 1)
  242.         {
  243.             if (hazeDensity > 0.0f)
  244.                 vproc->use(vp::diffuseHaze_2light);
  245.             else
  246.                 vproc->use(vp::diffuse_2light);
  247.         }
  248.         else
  249.         {
  250.             if (hazeDensity > 0.0f)
  251.                 vproc->use(vp::diffuseHaze);
  252.             else
  253.                 vproc->use(vp::diffuse);
  254.         }
  255.         g_lodSphere->render(context,
  256.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
  257.                             LODSphereMesh::VertexProgParams,
  258.                             frustum, ri.pixWidth,
  259.                             ri.baseTex);
  260.     }
  261.     if (hazeDensity > 0.0f)
  262.         glDisable(GL_FOG);
  263.     if (ri.nightTex != NULL)
  264.     {
  265.         ri.nightTex->bind();
  266. #ifdef USE_HDR
  267.         // Scale night light intensity
  268. #ifdef HDR_COMPRESS
  269.         Color nightColor0(ls.lights[0].color.red()  *ri.color.red()  *2.f,
  270.                           ls.lights[0].color.green()*ri.color.green()*2.f,
  271.                           ls.lights[0].color.blue() *ri.color.blue() *2.f,
  272.                           ri.nightLightScale); // Modulate brightness with alpha
  273. #else
  274.         Color nightColor0(ls.lights[0].color.red()  *ri.color.red(),
  275.                           ls.lights[0].color.green()*ri.color.green(),
  276.                           ls.lights[0].color.blue() *ri.color.blue(),
  277.                           ri.nightLightScale); // Modulate brightness with alpha
  278. #endif
  279.         vproc->parameter(vp::DiffuseColor0, nightColor0);
  280. #endif
  281.         if (ls.nLights > 1)
  282.         {
  283. #ifdef USE_HDR
  284. #ifdef HDR_COMPRESS
  285.             Color nightColor1(ls.lights[1].color.red()  *ri.color.red()  *2.f,
  286.                               ls.lights[1].color.green()*ri.color.green()*2.f,
  287.                               ls.lights[1].color.blue() *ri.color.blue() *2.f,
  288.                               ri.nightLightScale);
  289. #else
  290.             Color nightColor1(ls.lights[1].color.red()  *ri.color.red(),
  291.                               ls.lights[1].color.green()*ri.color.green(),
  292.                               ls.lights[1].color.blue() *ri.color.blue(),
  293.                               ri.nightLightScale);
  294. #endif
  295.             vproc->parameter(vp::DiffuseColor0, nightColor1);
  296. #endif
  297. #ifdef HDR_COMPRESS
  298.             vproc->use(vp::nightLights_2lightHDR);
  299. #else
  300.             vproc->use(vp::nightLights_2light);
  301. #endif
  302.         }
  303.         else
  304.         {
  305. #ifdef HDR_COMPRESS
  306.             vproc->use(vp::nightLightsHDR);
  307. #else
  308.             vproc->use(vp::nightLights);
  309. #endif
  310.         }
  311. #ifdef USE_HDR
  312.         glEnable(GL_BLEND);
  313.         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  314. #else
  315.         setupNightTextureCombine();
  316.         glEnable(GL_BLEND);
  317.         glBlendFunc(GL_ONE, GL_ONE);
  318. #endif
  319.         g_lodSphere->render(context,
  320.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  321.                             frustum, ri.pixWidth,
  322.                             ri.nightTex);
  323. #ifdef USE_HDR
  324.         vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
  325. #endif
  326.         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  327.     }
  328.     if (ri.overlayTex != NULL)
  329.     {
  330.         ri.overlayTex->bind();
  331.         vproc->use(vp::diffuse);
  332.         glEnable(GL_BLEND);
  333.         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  334.         g_lodSphere->render(context,
  335.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  336.                             frustum, ri.pixWidth,
  337.                             ri.overlayTex);
  338.         glBlendFunc(GL_ONE, GL_ONE);
  339.     }
  340.     vproc->disable();
  341. }
  342. // Render a planet sphere using both fragment and vertex programs
  343. static void renderSphere_FP_VP(const RenderInfo& ri,
  344.                                const Frustum& frustum,
  345.                                const GLContext& context)
  346. {
  347.     Texture* textures[4];
  348.     VertexProcessor* vproc = context.getVertexProcessor();
  349.     FragmentProcessor* fproc = context.getFragmentProcessor();
  350.     assert(vproc != NULL && fproc != NULL);
  351.     if (ri.baseTex == NULL)
  352.     {
  353.         glDisable(GL_TEXTURE_2D);
  354.     }
  355.     else
  356.     {
  357.         glEnable(GL_TEXTURE_2D);
  358.         ri.baseTex->bind();
  359.     }
  360.     // Compute the half angle vector required for specular lighting
  361.     Vec3f halfAngle_obj = ri.eyeDir_obj + ri.sunDir_obj;
  362.     if (halfAngle_obj.length() != 0.0f)
  363.         halfAngle_obj.normalize();
  364.     // Set up the fog parameters if the haze density is non-zero
  365.     float hazeDensity = ri.hazeColor.alpha();
  366.     if (hazeDensity > 0.0f)
  367.     {
  368.         glEnable(GL_FOG);
  369.         float fogColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
  370.         fogColor[0] = ri.hazeColor.red();
  371.         fogColor[1] = ri.hazeColor.green();
  372.         fogColor[2] = ri.hazeColor.blue();
  373.         glFogfv(GL_FOG_COLOR, fogColor);
  374.         glFogi(GL_FOG_MODE, GL_LINEAR);
  375.         glFogf(GL_FOG_START, 0.0);
  376.         glFogf(GL_FOG_END, 1.0f / hazeDensity);
  377.     }
  378.     vproc->enable();
  379.     vproc->parameter(vp::EyePosition, ri.eyePos_obj);
  380.     vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
  381.     vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
  382.     vproc->parameter(vp::SpecularExponent, 0.0f, 1.0f, 0.5f, ri.specularPower);
  383.     vproc->parameter(vp::SpecularColor0, ri.sunColor * ri.specularColor);
  384.     vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
  385.     vproc->parameter(vp::HazeColor, ri.hazeColor);
  386.     if (ri.bumpTex != NULL)
  387.     {
  388.         fproc->enable();
  389.         if (hazeDensity > 0.0f)
  390.             vproc->use(vp::diffuseBumpHaze);
  391.         else
  392.             vproc->use(vp::diffuseBump);
  393.         fproc->use(fp::texDiffuseBump);
  394.         g_lodSphere->render(context,
  395.                             LODSphereMesh::Normals | LODSphereMesh::Tangents |
  396.                             LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
  397.                             frustum, ri.pixWidth,
  398.                             ri.baseTex, ri.bumpTex);
  399.         fproc->disable();
  400.         // Render a specular pass
  401.         if (ri.specularColor != Color::Black)
  402.         {
  403.             glEnable(GL_BLEND);
  404.             glBlendFunc(GL_ONE, GL_ONE);
  405.             glEnable(GL_COLOR_SUM_EXT);
  406.             vproc->use(vp::specular);
  407.             // Disable ambient and diffuse
  408.             vproc->parameter(vp::AmbientColor, Color::Black);
  409.             vproc->parameter(vp::DiffuseColor0, Color::Black);
  410.             SetupCombinersGlossMap(ri.glossTex != NULL ? GL_TEXTURE0_ARB : 0);
  411.             textures[0] = ri.glossTex != NULL ? ri.glossTex : ri.baseTex;
  412.             g_lodSphere->render(context,
  413.                                 LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  414.                                 frustum, ri.pixWidth,
  415.                                 textures, 1);
  416.             // re-enable diffuse
  417.             vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
  418.             DisableCombiners();
  419.             glDisable(GL_COLOR_SUM_EXT);
  420.             glDisable(GL_BLEND);
  421.         }
  422.     }
  423.     else if (ri.specularColor != Color::Black)
  424.     {
  425.         fproc->enable();
  426.         if (ri.glossTex == NULL)
  427.         {
  428.             vproc->use(vp::perFragmentSpecularAlpha);
  429.             fproc->use(fp::texSpecularAlpha);
  430.         }
  431.         else
  432.         {
  433.             vproc->use(vp::perFragmentSpecular);
  434.             fproc->use(fp::texSpecular);
  435.         }
  436.         fproc->parameter(fp::DiffuseColor, ri.sunColor * ri.color);
  437.         fproc->parameter(fp::SunDirection, ri.sunDir_obj);
  438.         fproc->parameter(fp::SpecularColor, ri.specularColor);
  439.         fproc->parameter(fp::SpecularExponent, ri.specularPower, 0.0f, 0.0f, 0.0f);
  440.         fproc->parameter(fp::AmbientColor, ri.ambientColor);
  441.         unsigned int attributes = LODSphereMesh::Normals |
  442.                                   LODSphereMesh::TexCoords0 |
  443.                                   LODSphereMesh::VertexProgParams;
  444.         g_lodSphere->render(context,
  445.                             attributes, frustum, ri.pixWidth,
  446.                             ri.baseTex, ri.glossTex);
  447.         fproc->disable();
  448.     }
  449.     else
  450.     {
  451.         fproc->enable();
  452.         if (hazeDensity > 0.0f)
  453.             vproc->use(vp::diffuseHaze);
  454.         else
  455.             vproc->use(vp::diffuse);
  456.         fproc->use(fp::texDiffuse);
  457.         g_lodSphere->render(context,
  458.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
  459.                             LODSphereMesh::VertexProgParams,
  460.                             frustum, ri.pixWidth,
  461.                             ri.baseTex);
  462.         fproc->disable();
  463.     }
  464.     if (hazeDensity > 0.0f)
  465.         glDisable(GL_FOG);
  466.     if (ri.nightTex != NULL)
  467.     {
  468.         ri.nightTex->bind();
  469.         vproc->use(vp::nightLights);
  470.         setupNightTextureCombine();
  471.         glEnable(GL_BLEND);
  472.         glBlendFunc(GL_ONE, GL_ONE);
  473.         g_lodSphere->render(context,
  474.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  475.                             frustum, ri.pixWidth,
  476.                             ri.nightTex);
  477.         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  478.     }
  479.     if (ri.overlayTex != NULL)
  480.     {
  481.         ri.overlayTex->bind();
  482.         vproc->use(vp::diffuse);
  483.         glEnable(GL_BLEND);
  484.         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  485.         g_lodSphere->render(context,
  486.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  487.                             frustum, ri.pixWidth,
  488.                             ri.overlayTex);
  489.         glBlendFunc(GL_ONE, GL_ONE);
  490.     }
  491.     vproc->disable();
  492. }
  493. static void texGenPlane(GLenum coord, GLenum mode, const Vec4f& plane)
  494. {
  495.     float f[4];
  496.     f[0] = plane.x; f[1] = plane.y; f[2] = plane.z; f[3] = plane.w;
  497.     glTexGenfv(coord, mode, f);
  498. }
  499. static void renderShadowedGeometryDefault(Geometry* geometry,
  500.                                        const RenderInfo& ri,
  501.                                        const Frustum& frustum,
  502.                                        float *sPlane,
  503.                                        float *tPlane,
  504.                                        const Vec3f& lightDir,
  505.                                        bool useShadowMask,
  506.                                        const GLContext& context)
  507. {
  508.     glEnable(GL_TEXTURE_GEN_S);
  509.     glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  510.     glTexGenfv(GL_S, GL_OBJECT_PLANE, sPlane);
  511.     //texGenPlane(GL_S, GL_OBJECT_PLANE, sPlane);
  512.     glEnable(GL_TEXTURE_GEN_T);
  513.     glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  514.     glTexGenfv(GL_T, GL_OBJECT_PLANE, tPlane);
  515.     if (useShadowMask)
  516.     {
  517.         glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  518.         glEnable(GL_TEXTURE_GEN_S);
  519.         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  520.         texGenPlane(GL_S, GL_OBJECT_PLANE,
  521.                     Vec4f(lightDir.x, lightDir.y, lightDir.z, 0.5f));
  522.         glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  523.     }
  524.     glColor4f(1, 1, 1, 1);
  525.     glDisable(GL_LIGHTING);
  526.     if (geometry == NULL)
  527.     {
  528.         g_lodSphere->render(context,
  529.                             LODSphereMesh::Normals | LODSphereMesh::Multipass,
  530.                             frustum, ri.pixWidth, NULL);
  531.     }
  532.     else
  533.     {
  534.         FixedFunctionRenderContext rc;
  535.         geometry->render(rc);
  536.     }
  537.     glEnable(GL_LIGHTING);
  538.     if (useShadowMask)
  539.     {
  540.         glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  541.         glDisable(GL_TEXTURE_GEN_S);
  542.         glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  543.     }
  544.     glDisable(GL_TEXTURE_GEN_S);
  545.     glDisable(GL_TEXTURE_GEN_T);
  546. }
  547. static void renderShadowedGeometryVertexShader(const RenderInfo& ri,
  548.                                             const Frustum& frustum,
  549.                                             float* sPlane, float* tPlane,
  550.                                             Vec3f& lightDir,
  551.                                             const GLContext& context)
  552. {
  553.     VertexProcessor* vproc = context.getVertexProcessor();
  554.     assert(vproc != NULL);
  555.     vproc->enable();
  556.     vproc->parameter(vp::LightDirection0, lightDir);
  557.     vproc->parameter(vp::TexGen_S, sPlane);
  558.     vproc->parameter(vp::TexGen_T, tPlane);
  559.     vproc->use(vp::shadowTexture);
  560.     g_lodSphere->render(context,
  561.                         LODSphereMesh::Normals | LODSphereMesh::Multipass, frustum,
  562.                         ri.pixWidth, NULL);
  563.     vproc->disable();
  564. }
  565. static void renderRings(RingSystem& rings,
  566.                         RenderInfo& ri,
  567.                         float planetRadius,
  568.                         float planetOblateness,
  569.                         unsigned int textureResolution,
  570.                         bool renderShadow,
  571.                         const GLContext& context,
  572.                         unsigned int nSections)
  573. {
  574.     float inner = rings.innerRadius / planetRadius;
  575.     float outer = rings.outerRadius / planetRadius;
  576.     // Ring Illumination:
  577.     // Since a ring system is composed of millions of individual
  578.     // particles, it's not at all realistic to model it as a flat
  579.     // Lambertian surface.  We'll approximate the llumination
  580.     // function by assuming that the ring system contains Lambertian
  581.     // particles, and that the brightness at some point in the ring
  582.     // system is proportional to the illuminated fraction of a
  583.     // particle there.  In fact, we'll simplify things further and
  584.     // set the illumination of the entire ring system to the same
  585.     // value, computing the illuminated fraction of a hypothetical
  586.     // particle located at the center of the planet.  This
  587.     // approximation breaks down when you get close to the planet.
  588.     float ringIllumination = 0.0f;
  589.     {
  590.         float illumFraction = (1.0f + ri.eyeDir_obj * ri.sunDir_obj) / 2.0f;
  591.         // Just use the illuminated fraction for now . . .
  592.         ringIllumination = illumFraction;
  593.     }
  594.     GLContext::VertexPath vpath = context.getVertexPath();
  595.     VertexProcessor* vproc = context.getVertexProcessor();
  596.     FragmentProcessor* fproc = context.getFragmentProcessor();
  597.     if (vproc != NULL)
  598.     {
  599.         vproc->enable();
  600.         vproc->use(vp::ringIllum);
  601.         vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
  602.         vproc->parameter(vp::DiffuseColor0, ri.sunColor * rings.color);
  603.         vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
  604.         vproc->parameter(vp::Constant0, Vec3f(0, 0.5, 1.0));
  605.     }
  606.     // If we have multi-texture support, we'll use the second texture unit
  607.     // to render the shadow of the planet on the rings.  This is a bit of
  608.     // a hack, and assumes that the planet is ellipsoidal in shape,
  609.     // and only works for a planet illuminated by a single sun where the
  610.     // distance to the sun is very large relative to its diameter.
  611.     if (renderShadow)
  612.     {
  613.         glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  614.         glEnable(GL_TEXTURE_2D);
  615.         shadowTex->bind();
  616.         float sPlane[4] = { 0, 0, 0, 0.5f };
  617.         float tPlane[4] = { 0, 0, 0, 0.5f };
  618.         // Compute the projection vectors based on the sun direction.
  619.         // I'm being a little careless here--if the sun direction lies
  620.         // along the y-axis, this will fail.  It's unlikely that a
  621.         // planet would ever orbit underneath its sun (an orbital
  622.         // inclination of 90 degrees), but this should be made
  623.         // more robust anyway.
  624.         Vec3f axis = Vec3f(0, 1, 0) ^ ri.sunDir_obj;
  625.         float cosAngle = Vec3f(0.0f, 1.0f, 0.0f) * ri.sunDir_obj;
  626.         /*float angle = (float) acos(cosAngle);     Unused*/
  627.         axis.normalize();
  628.         float sScale = 1.0f;
  629.         float tScale = 1.0f;
  630.         if (fproc == NULL)
  631.         {
  632.             // When fragment programs aren't used, we render shadows with circular
  633.             // textures.  We scale up the texture slightly to account for the
  634.             // padding pixels near the texture borders.
  635.             sScale *= ShadowTextureScale;
  636.             tScale *= ShadowTextureScale;
  637.         }
  638.         if (planetOblateness != 0.0f)
  639.         {
  640.             // For oblate planets, the size of the shadow volume will vary based
  641.             // on the light direction.
  642.             // A vertical slice of the planet is an ellipse
  643.             float a = 1.0f;                          // semimajor axis
  644.             float b = a * (1.0f - planetOblateness); // semiminor axis
  645.             float ecc2 = 1.0f - (b * b) / (a * a);   // square of eccentricity
  646.             // Calculate the radius of the ellipse at the incident angle of the
  647.             // light on the ring plane + 90 degrees.
  648.             float r = a * (float) sqrt((1.0f - ecc2) /
  649.                                        (1.0f - ecc2 * square(cosAngle)));
  650.             tScale *= a / r;
  651.         }
  652.         // The s axis is perpendicular to the shadow axis in the plane of the
  653.         // of the rings, and the t axis completes the orthonormal basis.
  654.         Vec3f sAxis = axis * 0.5f;
  655.         Vec3f tAxis = (axis ^ ri.sunDir_obj) * 0.5f * tScale;
  656.         sPlane[0] = sAxis.x; sPlane[1] = sAxis.y; sPlane[2] = sAxis.z;
  657.         tPlane[0] = tAxis.x; tPlane[1] = tAxis.y; tPlane[2] = tAxis.z;
  658.         if (vproc != NULL)
  659.         {
  660.             vproc->parameter(vp::TexGen_S, sPlane);
  661.             vproc->parameter(vp::TexGen_T, tPlane);
  662.         }
  663.         else
  664.         {
  665.             glEnable(GL_TEXTURE_GEN_S);
  666.             glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
  667.             glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
  668.             glEnable(GL_TEXTURE_GEN_T);
  669.             glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
  670.             glTexGenfv(GL_T, GL_EYE_PLANE, tPlane);
  671.         }
  672.         glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  673.         if (fproc != NULL)
  674.         {
  675.             float r0 = 0.24f;
  676.             float r1 = 0.25f;
  677.             float bias = 1.0f / (1.0f - r1 / r0);
  678.             float scale = -bias / r0;
  679.             fproc->enable();
  680.             fproc->use(fp::sphereShadowOnRings);
  681.             fproc->parameter(fp::ShadowParams0, scale, bias, 0.0f, 0.0f);
  682.             fproc->parameter(fp::AmbientColor, ri.ambientColor * ri.color);
  683.         }
  684.     }
  685.     glEnable(GL_BLEND);
  686.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  687.     Texture* ringsTex = rings.texture.find(textureResolution);
  688.     if (ringsTex != NULL)
  689.     {
  690.         glEnable(GL_TEXTURE_2D);
  691.         ringsTex->bind();
  692.     }
  693.     else
  694.     {
  695.         glDisable(GL_TEXTURE_2D);
  696.     }
  697.     // Perform our own lighting for the rings.
  698.     // TODO: Don't forget about light source color (required when we
  699.     // paying attention to star color.)
  700.     if (vpath == GLContext::VPath_Basic)
  701.     {
  702.         glDisable(GL_LIGHTING);
  703.         Vec3f litColor(rings.color.red(), rings.color.green(), rings.color.blue());
  704.         litColor = litColor * ringIllumination +
  705.             Vec3f(ri.ambientColor.red(), ri.ambientColor.green(),
  706.                   ri.ambientColor.blue());
  707.         glColor4f(litColor.x, litColor.y, litColor.z, 1.0f);
  708.     }
  709.     // This gets tricky . . .  we render the rings in two parts.  One
  710.     // part is potentially shadowed by the planet, and we need to
  711.     // render that part with the projected shadow texture enabled.
  712.     // The other part isn't shadowed, but will appear so if we don't
  713.     // first disable the shadow texture.  The problem is that the
  714.     // shadow texture will affect anything along the line between the
  715.     // sun and the planet, regardless of whether it's in front or
  716.     // behind the planet.
  717.     // Compute the angle of the sun projected on the ring plane
  718.     float sunAngle = (float) atan2(ri.sunDir_obj.z, ri.sunDir_obj.x);
  719.     // If there's a fragment program, it will handle the ambient term--make
  720.     // sure that we don't add it both in the fragment and vertex programs.
  721.     if (vproc != NULL && fproc != NULL)
  722.         glAmbientLightColor(Color::Black);
  723.     renderRingSystem(inner, outer,
  724.                      (float) (sunAngle + PI / 2),
  725.                      (float) (sunAngle + 3 * PI / 2),
  726.                      nSections / 2);
  727.     renderRingSystem(inner, outer,
  728.                      (float) (sunAngle +  3 * PI / 2),
  729.                      (float) (sunAngle + PI / 2),
  730.                      nSections / 2);
  731.     if (vproc != NULL && fproc != NULL)
  732.         glAmbientLightColor(ri.ambientColor * ri.color);
  733.     // Disable the second texture unit if it was used
  734.     if (renderShadow)
  735.     {
  736.         glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  737.         glDisable(GL_TEXTURE_2D);
  738.         glDisable(GL_TEXTURE_GEN_S);
  739.         glDisable(GL_TEXTURE_GEN_T);
  740.         glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  741.         if (fproc != NULL)
  742.             fproc->disable();
  743.     }
  744.     // Render the unshadowed side
  745.     renderRingSystem(inner, outer,
  746.                      (float) (sunAngle - PI / 2),
  747.                      (float) (sunAngle + PI / 2),
  748.                      nSections / 2);
  749.     renderRingSystem(inner, outer,
  750.                      (float) (sunAngle + PI / 2),
  751.                      (float) (sunAngle - PI / 2),
  752.                      nSections / 2);
  753.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  754.     if (vproc != NULL)
  755.         vproc->disable();
  756. }
  757. static void
  758. renderEclipseShadows(Geometry* geometry,
  759.                      vector<EclipseShadow>& eclipseShadows,
  760.                      RenderInfo& ri,
  761.                      float planetRadius,
  762.                      Mat4f& planetMat,
  763.                      Frustum& viewFrustum,
  764.                      const GLContext& context)
  765. {
  766.     // Eclipse shadows on mesh objects aren't working yet.
  767.     if (geometry != NULL)
  768.         return;
  769.     for (vector<EclipseShadow>::iterator iter = eclipseShadows.begin();
  770.          iter != eclipseShadows.end(); iter++)
  771.     {
  772.         EclipseShadow shadow = *iter;
  773. #ifdef DEBUG_ECLIPSE_SHADOWS
  774.         // Eclipse debugging: render the central axis of the eclipse
  775.         // shadow volume.
  776.         glDisable(GL_TEXTURE_2D);
  777.         glColor4f(1, 0, 0, 1);
  778.         Point3f blorp = shadow.origin * planetMat;
  779.         Vec3f blah = shadow.direction * planetMat;
  780.         blorp.x /= planetRadius; blorp.y /= planetRadius; blorp.z /= planetRadius;
  781.         float foo = blorp.distanceFromOrigin();
  782.         glBegin(GL_LINES);
  783.         glVertex(blorp);
  784.         glVertex(blorp + foo * blah);
  785.         glEnd();
  786.         glEnable(GL_TEXTURE_2D);
  787. #endif
  788.         // Determine which eclipse shadow texture to use.  This is only
  789.         // a very rough approximation to reality.  Since there are an
  790.         // infinite number of possible eclipse volumes, what we should be
  791.         // doing is generating the eclipse textures on the fly using
  792.         // render-to-texture.  But for now, we'll just choose from a fixed
  793.         // set of eclipse shadow textures based on the relative size of
  794.         // the umbra and penumbra.
  795.         Texture* eclipseTex = NULL;
  796.         float umbra = shadow.umbraRadius / shadow.penumbraRadius;
  797.         if (umbra < 0.1f)
  798.             eclipseTex = eclipseShadowTextures[0];
  799.         else if (umbra < 0.35f)
  800.             eclipseTex = eclipseShadowTextures[1];
  801.         else if (umbra < 0.6f)
  802.             eclipseTex = eclipseShadowTextures[2];
  803.         else if (umbra < 0.9f)
  804.             eclipseTex = eclipseShadowTextures[3];
  805.         else
  806.             eclipseTex = shadowTex;
  807.         // Compute the transformation to use for generating texture
  808.         // coordinates from the object vertices.
  809.         Point3f origin = shadow.origin * planetMat;
  810.         Vec3f dir = shadow.direction * planetMat;
  811.         float scale = planetRadius / shadow.penumbraRadius;
  812.         Vec3f axis = Vec3f(0, 1, 0) ^ dir;
  813.         float angle = (float) acos(Vec3f(0, 1, 0) * dir);
  814.         axis.normalize();
  815.         Mat4f mat = Mat4f::rotation(axis, -angle);
  816.         Vec3f sAxis = Vec3f(0.5f * scale, 0, 0) * mat;
  817.         Vec3f tAxis = Vec3f(0, 0, 0.5f * scale) * mat;
  818.         float sPlane[4] = { 0, 0, 0, 0 };
  819.         float tPlane[4] = { 0, 0, 0, 0 };
  820.         sPlane[0] = sAxis.x; sPlane[1] = sAxis.y; sPlane[2] = sAxis.z;
  821.         tPlane[0] = tAxis.x; tPlane[1] = tAxis.y; tPlane[2] = tAxis.z;
  822.         sPlane[3] = (Point3f(0, 0, 0) - origin) * sAxis / planetRadius + 0.5f;
  823.         tPlane[3] = (Point3f(0, 0, 0) - origin) * tAxis / planetRadius + 0.5f;
  824.         // TODO: Multiple eclipse shadows should be rendered in a single
  825.         // pass using multitexture.
  826.         if (eclipseTex != NULL)
  827.             eclipseTex->bind();
  828.         // shadowMaskTexture->bind();
  829.         glEnable(GL_BLEND);
  830.         glBlendFunc(GL_ZERO, GL_SRC_COLOR);
  831.         // If the ambient light level is greater than zero, reduce the
  832.         // darkness of the shadows.
  833.         if (ri.useTexEnvCombine)
  834.         {
  835.             float color[4] = { ri.ambientColor.red(), ri.ambientColor.green(),
  836.                                ri.ambientColor.blue(), 1.0f };
  837.             glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
  838.             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
  839.             glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_CONSTANT_EXT);
  840.             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
  841.             glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
  842.             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
  843.             glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
  844.             // The second texture unit has the shadow 'mask'
  845.             glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  846.             glEnable(GL_TEXTURE_2D);
  847.             shadowMaskTexture->bind();
  848.             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
  849.             glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
  850.             glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
  851.             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
  852.             glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
  853.             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
  854.             glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  855.         }
  856.         // Since invariance between nVidia's vertex programs and the
  857.         // standard transformation pipeline isn't guaranteed, we have to
  858.         // make sure to use the same transformation engine on subsequent
  859.         // rendering passes as we did on the initial one.
  860.         if (context.getVertexPath() != GLContext::VPath_Basic && geometry == NULL)
  861.         {
  862.             renderShadowedGeometryVertexShader(ri, viewFrustum,
  863.                                            sPlane, tPlane,
  864.                                            dir,
  865.                                            context);
  866.         }
  867.         else
  868.         {
  869.             renderShadowedGeometryDefault(geometry, ri, viewFrustum,
  870.                                        sPlane, tPlane,
  871.                                        dir,
  872.                                        ri.useTexEnvCombine,
  873.                                        context);
  874.         }
  875.         if (ri.useTexEnvCombine)
  876.         {
  877.             // Disable second texture unit
  878.             glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  879.             glDisable(GL_TEXTURE_2D);
  880.             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  881.             glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  882.             float color[4] = { 0, 0, 0, 0 };
  883.             glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
  884.             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  885.         }
  886.         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  887.         glDisable(GL_BLEND);
  888.     }
  889. }
  890. static void
  891. renderEclipseShadows_Shaders(Geometry* geometry,
  892.                              vector<EclipseShadow>& eclipseShadows,
  893.                              RenderInfo& ri,
  894.                              float planetRadius,
  895.                              Mat4f& planetMat,
  896.                              Frustum& viewFrustum,
  897.                              const GLContext& context)
  898. {
  899.     // Eclipse shadows on mesh objects aren't working yet.
  900.     if (geometry != NULL)
  901.         return;
  902.     glEnable(GL_TEXTURE_2D);
  903.     penumbraFunctionTexture->bind();
  904.     glEnable(GL_BLEND);
  905.     glBlendFunc(GL_ZERO, GL_SRC_COLOR);
  906.     float sPlanes[4][4];
  907.     float tPlanes[4][4];
  908.     float shadowParams[4][4];
  909.     int n = 0;
  910.     for (vector<EclipseShadow>::iterator iter = eclipseShadows.begin();
  911.          iter != eclipseShadows.end() && n < 4; iter++, n++)
  912.     {
  913.         EclipseShadow shadow = *iter;
  914.         float R2 = 0.25f;
  915.         float umbra = shadow.umbraRadius / shadow.penumbraRadius;
  916.         umbra = umbra * umbra;
  917.         if (umbra < 0.0001f)
  918.             umbra = 0.0001f;
  919.         else if (umbra > 0.99f)
  920.             umbra = 0.99f;
  921.         float umbraRadius = R2 * umbra;
  922.         float penumbraRadius = R2;
  923.         float shadowBias = 1.0f / (1.0f - penumbraRadius / umbraRadius);
  924.         float shadowScale = -shadowBias / umbraRadius;
  925.         shadowParams[n][0] = shadowScale;
  926.         shadowParams[n][1] = shadowBias;
  927.         shadowParams[n][2] = 0.0f;
  928.         shadowParams[n][3] = 0.0f;
  929.         // Compute the transformation to use for generating texture
  930.         // coordinates from the object vertices.
  931.         Point3f origin = shadow.origin * planetMat;
  932.         Vec3f dir = shadow.direction * planetMat;
  933.         float scale = planetRadius / shadow.penumbraRadius;
  934.         Vec3f axis = Vec3f(0, 1, 0) ^ dir;
  935.         float angle = (float) acos(Vec3f(0, 1, 0) * dir);
  936.         axis.normalize();
  937.         Mat4f mat = Mat4f::rotation(axis, -angle);
  938.         Vec3f sAxis = Vec3f(0.5f * scale, 0, 0) * mat;
  939.         Vec3f tAxis = Vec3f(0, 0, 0.5f * scale) * mat;
  940.         sPlanes[n][0] = sAxis.x;
  941.         sPlanes[n][1] = sAxis.y;
  942.         sPlanes[n][2] = sAxis.z;
  943.         sPlanes[n][3] = (Point3f(0, 0, 0) - origin) * sAxis / planetRadius + 0.5f;
  944.         tPlanes[n][0] = tAxis.x;
  945.         tPlanes[n][1] = tAxis.y;
  946.         tPlanes[n][2] = tAxis.z;
  947.         tPlanes[n][3] = (Point3f(0, 0, 0) - origin) * tAxis / planetRadius + 0.5f;
  948.     }
  949.     VertexProcessor* vproc = context.getVertexProcessor();
  950.     FragmentProcessor* fproc = context.getFragmentProcessor();
  951.     vproc->enable();
  952.     vproc->use(vp::multiShadow);
  953.     fproc->enable();
  954.     if (n == 1)
  955.         fproc->use(fp::eclipseShadow1);
  956.     else
  957.         fproc->use(fp::eclipseShadow2);
  958.     fproc->parameter(fp::ShadowParams0, shadowParams[0]);
  959.     vproc->parameter(vp::TexGen_S, sPlanes[0]);
  960.     vproc->parameter(vp::TexGen_T, tPlanes[0]);
  961.     if (n >= 2)
  962.     {
  963.         fproc->parameter(fp::ShadowParams1, shadowParams[1]);
  964.         vproc->parameter(vp::TexGen_S2, sPlanes[1]);
  965.         vproc->parameter(vp::TexGen_T2, tPlanes[1]);
  966.     }
  967.     if (n >= 3)
  968.     {
  969.         //fproc->parameter(fp::ShadowParams2, shadowParams[2]);
  970.         vproc->parameter(vp::TexGen_S3, sPlanes[2]);
  971.         vproc->parameter(vp::TexGen_T3, tPlanes[2]);
  972.     }
  973.     if (n >= 4)
  974.     {
  975.         //fproc->parameter(fp::ShadowParams3, shadowParams[3]);
  976.         vproc->parameter(vp::TexGen_S4, sPlanes[3]);
  977.         vproc->parameter(vp::TexGen_T4, tPlanes[3]);
  978.     }
  979.     //vproc->parameter(vp::LightDirection0, lightDir);
  980.     g_lodSphere->render(context,
  981.                         LODSphereMesh::Normals | LODSphereMesh::Multipass,
  982.                         viewFrustum,
  983.                         ri.pixWidth, NULL);
  984.     vproc->disable();
  985.     fproc->disable();
  986.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  987.     glDisable(GL_BLEND);
  988. }
  989. static void
  990. renderRingShadowsVS(Geometry* /*geometry*/,           //TODO: Remove unused parameters??
  991.                     const RingSystem& rings,
  992.                     const Vec3f& /*sunDir*/,
  993.                     RenderInfo& ri,
  994.                     float planetRadius,
  995.                     float /*oblateness*/,
  996.                     Mat4f& /*planetMat*/,
  997.                     Frustum& viewFrustum,
  998.                     const GLContext& context)
  999. {
  1000.     // Compute the transformation to use for generating texture
  1001.     // coordinates from the object vertices.
  1002.     float ringWidth = rings.outerRadius - rings.innerRadius;
  1003.     float s = ri.sunDir_obj.y;
  1004.     float scale = (abs(s) < 0.001f) ? 1000.0f : 1.0f / s;
  1005.     if (abs(s) > 1.0f - 1.0e-4f)
  1006.     {
  1007.         // Planet is illuminated almost directly from above, so
  1008.         // no ring shadow will be cast on the planet.  Conveniently
  1009.         // avoids some potential division by zero when ray-casting.
  1010.         return;
  1011.     }
  1012.     glEnable(GL_BLEND);
  1013.     glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
  1014.     // If the ambient light level is greater than zero, reduce the
  1015.     // darkness of the shadows.
  1016.     float color[4] = { ri.ambientColor.red(), ri.ambientColor.green(),
  1017.                        ri.ambientColor.blue(), 1.0f };
  1018.     glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
  1019.     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
  1020.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_CONSTANT_EXT);
  1021.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
  1022.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
  1023.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
  1024.     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
  1025.     // Tweak the texture--set clamp to border and a border color with
  1026.     // a zero alpha.  If a graphics card doesn't support clamp to border,
  1027.     // it doesn't get to play.  It's possible to get reasonable behavior
  1028.     // by turning off mipmaps and assuming transparent rows of pixels for
  1029.     // the top and bottom of the ring textures . . . maybe later.
  1030.     float bc[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
  1031.     glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bc);
  1032.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB);
  1033.     // Ring shadows look strange if they're always completely black.  Vary
  1034.     // the darkness of the shadow based on the angle between the sun and the
  1035.     // ring plane.  There's some justification for this--the larger the angle
  1036.     // between the sun and the ring plane (normal), the more ring material
  1037.     // there is to travel through.
  1038.     //float alpha = (1.0f - abs(ri.sunDir_obj.y)) * 1.0f;
  1039.     // ...but, images from Cassini are showing very dark ring shadows, so we'll
  1040.     // go with that.
  1041.     float alpha = 1.0f;
  1042.     VertexProcessor* vproc = context.getVertexProcessor();
  1043.     assert(vproc != NULL);
  1044.     vproc->enable();
  1045.     vproc->use(vp::ringShadow);
  1046.     vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
  1047.     vproc->parameter(vp::DiffuseColor0, 1, 1, 1, alpha); // color = white
  1048.     vproc->parameter(vp::TexGen_S,
  1049.                      rings.innerRadius / planetRadius,
  1050.                      1.0f / (ringWidth / planetRadius),
  1051.                      0.0f, 0.5f);
  1052.     vproc->parameter(vp::TexGen_T, scale, 0, 0, 0);
  1053.     g_lodSphere->render(context, LODSphereMesh::Multipass,
  1054.                         viewFrustum, ri.pixWidth, NULL);
  1055.     vproc->disable();
  1056.     // Restore the texture combiners
  1057.     if (ri.useTexEnvCombine)
  1058.     {
  1059.         float color[4] = { 0, 0, 0, 0 };
  1060.         glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
  1061.         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  1062.     }
  1063.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  1064.     glDisable(GL_BLEND);
  1065. }
  1066. void Renderer::renderLocations(const Body& body,
  1067.                                const Vec3d& bodyPosition,
  1068.                                const Quatd& bodyOrientation)
  1069. {
  1070.     const vector<Location*>* locations = body.getLocations();
  1071.     if (locations == NULL)
  1072.         return;
  1073.     
  1074.     Vec3f semiAxes = body.getSemiAxes();
  1075.     
  1076.     float nearDist = getNearPlaneDistance();
  1077.     double boundingRadius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
  1078.     Vec3d bodyCenter(bodyPosition.x, bodyPosition.y, bodyPosition.z);
  1079.     Vec3d viewRayOrigin = -bodyCenter * (~bodyOrientation).toMatrix3();
  1080.     double labelOffset = 0.0001;
  1081.     Vec3f vn  = Vec3f(0.0f, 0.0f, -1.0f) * getCameraOrientation().toMatrix3();
  1082.     Vec3d viewNormal(vn.x, vn.y, vn.z);
  1083.     
  1084.     Ellipsoidd bodyEllipsoid(Vec3d(semiAxes.x, semiAxes.y, semiAxes.z));
  1085.     
  1086.     Mat3d bodyMatrix = bodyOrientation.toMatrix3();
  1087.     
  1088.     for (vector<Location*>::const_iterator iter = locations->begin();
  1089.          iter != locations->end(); iter++)
  1090.     {
  1091.         const Location& location = **iter;
  1092.         
  1093.         if (location.getFeatureType() & locationFilter)
  1094.         {
  1095.             // Get the position of the location with respect to the planet center
  1096.             Vec3f ppos = location.getPosition();
  1097.             
  1098.             // Compute the bodycentric position of the location
  1099.             Vec3d locPos = Vec3d(ppos.x, ppos.y, ppos.z);
  1100.             
  1101.             // Get the planetocentric position of the label.  Add a slight scale factor
  1102.             // to keep the point from being exactly on the surface.
  1103.             Vec3d pcLabelPos = locPos * (1.0 + labelOffset);
  1104.             
  1105.             // Get the camera space label position
  1106.             Vec3d labelPos = bodyCenter + locPos * bodyMatrix;
  1107.             
  1108.             float effSize = location.getImportance();
  1109.             if (effSize < 0.0f)
  1110.                 effSize = location.getSize();
  1111.             
  1112.             float pixSize = effSize / (float) (labelPos.length() * pixelSize);
  1113.             
  1114.             if (pixSize > minFeatureSize && labelPos * viewNormal > 0.0)
  1115.             {
  1116.                 // Labels on non-ellipsoidal bodies need special handling; the
  1117.                 // ellipsoid visibility test will always fail for them, since they
  1118.                 // will lie on the surface of the mesh, which is inside the
  1119.                 // the bounding ellipsoid. The following code projects location positions
  1120.                 // onto the bounding sphere.
  1121.                 if (!body.isEllipsoid())
  1122.                 {
  1123.                     double r = locPos.length();
  1124.                     if (r < boundingRadius)
  1125.                         pcLabelPos = locPos * (boundingRadius * 1.01 / r);
  1126.                 }
  1127.                 
  1128.                 double t = 0.0;
  1129.                 
  1130.                 // Test for an intersection of the eye-to-location ray with
  1131.                 // the planet ellipsoid.  If we hit the planet first, then
  1132.                 // the label is obscured by the planet.  An exact calculation
  1133.                 // for irregular objects would be too expensive, and the
  1134.                 // ellipsoid approximation works reasonably well for them.
  1135.                 Ray3d testRay(Point3d(viewRayOrigin.x, viewRayOrigin.y, viewRayOrigin.z),
  1136.                               pcLabelPos - viewRayOrigin);
  1137.                 bool hit = testIntersection(testRay, bodyEllipsoid, t);
  1138.                 Vec3d blah = labelPos - viewRayOrigin;
  1139.                 if (!hit || t >= 1.0)
  1140.                 {                    
  1141.                     // Calculate the intersection of the eye-to-label ray with the plane perpendicular to
  1142.                     // the view normal that touches the front of the object's bounding sphere
  1143.                     double planetZ = viewNormal * bodyCenter - boundingRadius;
  1144.                     if (planetZ < -nearDist * 1.001)
  1145.                         planetZ = -nearDist * 1.001;
  1146.                     double z = viewNormal * labelPos;
  1147.                     labelPos *= planetZ / z;
  1148.                                         
  1149.                     uint32 featureType = location.getFeatureType();
  1150.                     MarkerRepresentation* locationMarker = NULL;
  1151.                     if (featureType & Location::City)
  1152.                         locationMarker = &cityRep;
  1153.                     else if (featureType & (Location::LandingSite | Location::Observatory))
  1154.                         locationMarker = &observatoryRep;
  1155.                     else if (featureType & (Location::Crater | Location::Patera))
  1156.                         locationMarker = &craterRep;
  1157.                     else if (featureType & (Location::Mons | Location::Tholus))
  1158.                         locationMarker = &mountainRep;
  1159.                     else if (featureType & (Location::EruptiveCenter))
  1160.                         locationMarker = &genericLocationRep;
  1161.                     Color labelColor = location.isLabelColorOverridden() ? location.getLabelColor() : LocationLabelColor;
  1162.                     addObjectAnnotation(locationMarker,
  1163.                                         location.getName(true),
  1164.                                         labelColor,
  1165.                                         Point3f((float) labelPos.x, (float) labelPos.y, (float) labelPos.z));
  1166.                 }
  1167.             }
  1168.         }
  1169.     }    
  1170. }
  1171. // Estimate the fraction of light reflected from a sphere that
  1172. // reaches an object at the specified position relative to that
  1173. // sphere.
  1174. //
  1175. // This is function is just a rough approximation to the actual
  1176. // lighting integral, but it reproduces the important features
  1177. // of the way that phase and distance affect reflected light:
  1178. //    - Higher phase angles mean less reflected light
  1179. //    - The closer an object is to the reflector, the less
  1180. //      area of the reflector that is visible.
  1181. //
  1182. // We approximate the reflected light by taking a weighted average
  1183. // of the reflected light at three points on the reflector: the
  1184. // light receiver's sub-point, and the two horizon points in the
  1185. // plane of the light vector and receiver-to-reflector vector.
  1186. //
  1187. // The reflecting object is assumed to be spherical and perfectly
  1188. // Lambertian.
  1189. static float
  1190. estimateReflectedLightFraction(const Vec3d& toSun,
  1191.                                const Vec3d& toObject,
  1192.                                float radius)
  1193. {
  1194.     // Theta is half the arc length visible to the reflector
  1195.     double d = toObject.length();
  1196.     float cosTheta = (float) (radius / d);
  1197.     if (cosTheta > 0.999f)
  1198.         cosTheta = 0.999f;
  1199.     // Phi is the angle between the light vector and receiver-to-reflector vector.
  1200.     // cos(phi) is thus the illumination at the sub-point. The horizon points are
  1201.     // at phi+theta and phi-theta.
  1202.     float cosPhi = (float) ((toSun * toObject) / (d * toSun.length()));
  1203.     // Use a trigonometric identity to compute cos(phi +/- theta):
  1204.     //   cos(phi + theta) = cos(phi) * cos(theta) - sin(phi) * sin(theta)
  1205.     // s = sin(phi) * sin(theta)
  1206.     float s = (float) sqrt((1.0f - cosPhi * cosPhi) * (1.0f - cosTheta * cosTheta));
  1207.     
  1208.     float cosPhi1 = cosPhi * cosTheta - s;  // cos(phi + theta)
  1209.     float cosPhi2 = cosPhi * cosTheta + s;  // cos(phi - theta)
  1210.     // Calculate a weighted average of illumination at the three points
  1211.     return (2.0f * max(cosPhi, 0.0f) + max(cosPhi1, 0.0f) + max(cosPhi2, 0.0f)) * 0.25f;
  1212. }
  1213. static void
  1214. setupObjectLighting(const vector<LightSource>& suns,
  1215.                     const vector<SecondaryIlluminator>& secondaryIlluminators,
  1216.                     const Quatf& objOrientation,
  1217.                     const Vec3f& objScale,
  1218.                     const Point3f& objPosition_eye,
  1219.                     bool isNormalized,
  1220. #ifdef USE_HDR
  1221.                     const float faintestMag,
  1222.                     const float saturationMag,
  1223.                     const float appMag,
  1224. #endif
  1225.                     LightingState& ls)
  1226. {
  1227.     unsigned int nLights = min(MaxLights, (unsigned int) suns.size());
  1228.     if (nLights == 0)
  1229.         return;
  1230. #ifdef USE_HDR
  1231.     float exposureFactor = (faintestMag - appMag)/(faintestMag - saturationMag + 0.001f);
  1232. #endif
  1233.     unsigned int i;
  1234.     for (i = 0; i < nLights; i++)
  1235.     {
  1236.         Vec3d dir = suns[i].position - Vec3d(objPosition_eye.x, objPosition_eye.y, objPosition_eye.z);
  1237.         ls.lights[i].direction_eye =
  1238.             Vec3f((float) dir.x, (float) dir.y, (float) dir.z);
  1239.         float distance = ls.lights[i].direction_eye.length();
  1240.         ls.lights[i].direction_eye *= 1.0f / distance;
  1241.         distance = astro::kilometersToAU((float) dir.length());
  1242.         ls.lights[i].irradiance = suns[i].luminosity / (distance * distance);
  1243.         ls.lights[i].color = suns[i].color;
  1244.         // Store the position and apparent size because we'll need them for
  1245.         // testing for eclipses.
  1246.         ls.lights[i].position = dir;
  1247.         ls.lights[i].apparentSize = (float) (suns[i].radius / dir.length());
  1248.         ls.lights[i].castsShadows = true;
  1249.     }
  1250.     // Include effects of secondary illumination (i.e. planetshine)
  1251.     if (!secondaryIlluminators.empty() && i < MaxLights - 1)
  1252.     {
  1253.         float maxIrr = 0.0f;
  1254.         unsigned int maxIrrSource = 0;
  1255.         Vec3d objpos(objPosition_eye.x, objPosition_eye.y, objPosition_eye.z);
  1256.         // Only account for light from the brightest secondary source
  1257.         for (vector<SecondaryIlluminator>::const_iterator iter = secondaryIlluminators.begin();
  1258.              iter != secondaryIlluminators.end(); iter++)
  1259.         {
  1260.             Vec3d toIllum = iter->position_v - objpos;  // reflector-to-object vector
  1261.             float distSquared = (float) toIllum.lengthSquared() / square(iter->radius);
  1262.             if (distSquared > 0.01f)
  1263.             {
  1264.                 // Irradiance falls off with distance^2
  1265.                 float irr = iter->reflectedIrradiance / distSquared;
  1266.                 // Phase effects will always leave the irradiance unaffected or reduce it;
  1267.                 // don't bother calculating them if we've already found a brighter secondary
  1268.                 // source.
  1269.                 if (irr > maxIrr)
  1270.                 {
  1271.                     // Account for the phase
  1272.                     Vec3d toSun = objpos - suns[0].position;
  1273.                     irr *= estimateReflectedLightFraction(toSun, toIllum, iter->radius);              
  1274.                     if (irr > maxIrr)
  1275.                     {
  1276.                         maxIrr = irr;
  1277.                         maxIrrSource = iter - secondaryIlluminators.begin();
  1278.                     }
  1279.                 }
  1280.             }
  1281.         }
  1282. #if DEBUG_SECONDARY_ILLUMINATION      
  1283.         clog << "maxIrr = " << maxIrr << ", "
  1284.              << secondaryIlluminators[maxIrrSource].body->getName() << ", "
  1285.              << secondaryIlluminators[maxIrrSource].reflectedIrradiance << endl;
  1286. #endif
  1287.         if (maxIrr > 0.0f)
  1288.         {
  1289.             Vec3d toIllum = secondaryIlluminators[maxIrrSource].position_v - objpos;
  1290.             ls.lights[i].direction_eye = Vec3f((float) toIllum.x, (float) toIllum.y, (float) toIllum.z);
  1291.             ls.lights[i].direction_eye.normalize();
  1292.             ls.lights[i].irradiance = maxIrr;
  1293.             ls.lights[i].color = secondaryIlluminators[maxIrrSource].body->getSurface().color;        
  1294.             ls.lights[i].apparentSize = 0.0f;
  1295.             ls.lights[i].castsShadows = false;
  1296.             i++;
  1297.             nLights++;
  1298.         }
  1299.     }
  1300.     // Sort light sources by brightness.  Light zero should always be the
  1301.     // brightest.  Optimize common cases of one and two lights.
  1302.     if (nLights == 2)
  1303.     {
  1304.         if (ls.lights[0].irradiance < ls.lights[1].irradiance)
  1305.             swap(ls.lights[0], ls.lights[1]);
  1306.     }
  1307.     else if (nLights > 2)
  1308.     {
  1309.         sort(ls.lights, ls.lights + nLights, LightIrradiancePredicate());
  1310.     }
  1311.     // Compute the total irradiance
  1312.     float totalIrradiance = 0.0f;
  1313.     for (i = 0; i < nLights; i++)
  1314.         totalIrradiance += ls.lights[i].irradiance;
  1315.     // Compute a gamma factor to make dim light sources visible.  This is
  1316.     // intended to approximate what we see with our eyes--for example,
  1317.     // Earth-shine is visible on the night side of the Moon, even though
  1318.     // the amount of reflected light from the Earth is 1/10000 of what
  1319.     // the Moon receives directly from the Sun.
  1320.     //
  1321.     // TODO: Skip this step when high dynamic range rendering to floating point
  1322.     //   buffers is enabled.
  1323.     float minVisibleFraction = 1.0f / 10000.0f;
  1324.     float minDisplayableValue = 1.0f / 255.0f;
  1325.     float gamma = (float) (log(minDisplayableValue) / log(minVisibleFraction));
  1326.     float minVisibleIrradiance = minVisibleFraction * totalIrradiance;
  1327.     Mat3f m = (~objOrientation).toMatrix3();
  1328.     // Gamma scale and normalize the light sources; cull light sources that
  1329.     // aren't bright enough to contribute the final pixels rendered into the
  1330.     // frame buffer.
  1331.     ls.nLights = 0;
  1332.     for (i = 0; i < nLights && ls.lights[i].irradiance > minVisibleIrradiance; i++)
  1333.     {
  1334. #ifdef USE_HDR
  1335.         ls.lights[i].irradiance *= exposureFactor / totalIrradiance;
  1336. #else
  1337.         ls.lights[i].irradiance =
  1338.             (float) pow(ls.lights[i].irradiance / totalIrradiance, gamma);
  1339. #endif
  1340.         // Compute the direction of the light in object space
  1341.         ls.lights[i].direction_obj = ls.lights[i].direction_eye * m;
  1342.         ls.nLights++;
  1343.     }
  1344.     ls.eyePos_obj = Point3f(-objPosition_eye.x / objScale.x,
  1345.                             -objPosition_eye.y / objScale.y,
  1346.                             -objPosition_eye.z / objScale.z) * m;
  1347.     ls.eyeDir_obj = (Point3f(0.0f, 0.0f, 0.0f) - objPosition_eye) * m;
  1348.     ls.eyeDir_obj.normalize();
  1349.     // When the camera is very far from the object, some view-dependent
  1350.     // calculations in the shaders can exhibit precision problems. This
  1351.     // occurs with atmospheres, where the scale height of the atmosphere
  1352.     // is very small relative to the planet radius. To address the problem,
  1353.     // we'll clamp the eye distance to some maximum value. The effect of the
  1354.     // adjustment should be impercetible, since at large distances rays from
  1355.     // the camera to object vertices are all nearly parallel to each other.
  1356.     float eyeFromCenterDistance = ls.eyePos_obj.distanceFromOrigin();
  1357.     if (eyeFromCenterDistance > 100.0f && isNormalized)
  1358.     {
  1359.         float s = 100.0f / eyeFromCenterDistance;
  1360.         ls.eyePos_obj.x *= s;
  1361.         ls.eyePos_obj.y *= s;
  1362.         ls.eyePos_obj.z *= s;
  1363.     }
  1364.     ls.ambientColor = Vec3f(0.0f, 0.0f, 0.0f);
  1365. #if 0
  1366.     // Old code: linear scaling approach
  1367.     // After sorting, the first light is always the brightest
  1368.     float maxIrradiance = ls.lights[0].irradiance;
  1369.     // Normalize the brightnesses of the light sources.
  1370.     // TODO: Investigate logarithmic functions for scaling light brightness, to
  1371.     //   better simulate what the human eye would see.
  1372.     ls.nLights = 0;
  1373.     for (i = 0; i < nLights; i++)
  1374.     {
  1375.         ls.lights[i].irradiance /= maxIrradiance;
  1376.         // Cull light sources that don't contribute significantly (less than
  1377.         // the resolution of an 8-bit color channel.)
  1378.         if (ls.lights[i].irradiance < 1.0f / 255.0f)
  1379.             break;
  1380.         // Compute the direction of the light in object space
  1381.         ls.lights[i].direction_obj = ls.lights[i].direction_eye * m;
  1382.         ls.nLights++;
  1383.     }
  1384. #endif
  1385. }
  1386. void Renderer::renderObject(Point3f pos,
  1387.                             float distance,
  1388.                             double now,
  1389.                             Quatf cameraOrientation,
  1390.                             float nearPlaneDistance,
  1391.                             float farPlaneDistance,
  1392.                             RenderProperties& obj,
  1393.                             const LightingState& ls)
  1394. {
  1395.     RenderInfo ri;
  1396.     float altitude = distance - obj.radius;
  1397.     float discSizeInPixels = obj.radius /
  1398.         (max(nearPlaneDistance, altitude) * pixelSize);
  1399.     ri.sunDir_eye = Vec3f(0.0f, 1.0f, 0.0f);
  1400.     ri.sunDir_obj = Vec3f(0.0f, 1.0f, 0.0f);
  1401.     ri.sunColor = Color(0.0f, 0.0f, 0.0f);
  1402.     if (ls.nLights > 0)
  1403.     {
  1404.         ri.sunDir_eye = ls.lights[0].direction_eye;
  1405.         ri.sunDir_obj = ls.lights[0].direction_obj;
  1406.         ri.sunColor   = ls.lights[0].color;// * ls.lights[0].intensity;
  1407.     }
  1408.     // Enable depth buffering
  1409.     glEnable(GL_DEPTH_TEST);
  1410.     glDepthMask(GL_TRUE);
  1411.     glDisable(GL_BLEND);
  1412.     // Get the object's geometry; NULL indicates that object is an
  1413.     // ellipsoid.
  1414.     Geometry* geometry = NULL;
  1415.     if (obj.geometry != InvalidResource)
  1416.     {
  1417.         // This is a model loaded from a file
  1418.         geometry = GetGeometryManager()->find(obj.geometry);
  1419.     }
  1420.     // Get the textures . . .
  1421.     if (obj.surface->baseTexture.tex[textureResolution] != InvalidResource)
  1422.         ri.baseTex = obj.surface->baseTexture.find(textureResolution);
  1423.     if ((obj.surface->appearanceFlags & Surface::ApplyBumpMap) != 0 &&
  1424.         context->bumpMappingSupported() &&
  1425.         obj.surface->bumpTexture.tex[textureResolution] != InvalidResource)
  1426.         ri.bumpTex = obj.surface->bumpTexture.find(textureResolution);
  1427.     if ((obj.surface->appearanceFlags & Surface::ApplyNightMap) != 0 &&
  1428.         (renderFlags & ShowNightMaps) != 0)
  1429.         ri.nightTex = obj.surface->nightTexture.find(textureResolution);
  1430.     if ((obj.surface->appearanceFlags & Surface::SeparateSpecularMap) != 0)
  1431.         ri.glossTex = obj.surface->specularTexture.find(textureResolution);
  1432.     if ((obj.surface->appearanceFlags & Surface::ApplyOverlay) != 0)
  1433.         ri.overlayTex = obj.surface->overlayTexture.find(textureResolution);
  1434.     // Apply the modelview transform for the object
  1435.     glPushMatrix();
  1436.     glTranslate(pos);
  1437.     glRotate(~obj.orientation);
  1438.     // Scaling will be nonuniform for nonspherical planets. As long as the
  1439.     // deviation from spherical isn't too large, the nonuniform scale factor
  1440.     // shouldn't mess up the lighting calculations enough to be noticeable
  1441.     // (and we turn on renormalization anyhow, which most graphics cards
  1442.     // support.)
  1443.     float radius = obj.radius;
  1444.     Vec3f scaleFactors;
  1445.     float geometryScale;
  1446.     if (geometry == NULL || geometry->isNormalized())
  1447.     {
  1448.         geometryScale = obj.radius;
  1449.         scaleFactors = obj.radius * obj.semiAxes;
  1450.         ri.pointScale = 2.0f * obj.radius / pixelSize;
  1451.     }
  1452.     else
  1453.     {
  1454.         geometryScale = obj.geometryScale;
  1455.         scaleFactors = Vec3f(geometryScale, geometryScale, geometryScale);
  1456.         ri.pointScale = 2.0f * geometryScale / pixelSize;
  1457.     }
  1458.     glScale(scaleFactors);
  1459.     Mat4f planetMat = (~obj.orientation).toMatrix4();
  1460.     ri.eyeDir_obj = (Point3f(0, 0, 0) - pos) * planetMat;
  1461.     ri.eyeDir_obj.normalize();
  1462.     ri.eyePos_obj = Point3f(-pos.x / scaleFactors.x,
  1463.                             -pos.y / scaleFactors.y,
  1464.                             -pos.z / scaleFactors.z) * planetMat;
  1465.     ri.orientation = cameraOrientation * ~obj.orientation;
  1466.     ri.pixWidth = discSizeInPixels;
  1467.     // Set up the colors
  1468.     if (ri.baseTex == NULL ||
  1469.         (obj.surface->appearanceFlags & Surface::BlendTexture) != 0)
  1470.     {
  1471.         ri.color = obj.surface->color;
  1472.     }
  1473.     ri.ambientColor = ambientColor;
  1474.     ri.hazeColor = obj.surface->hazeColor;
  1475.     ri.specularColor = obj.surface->specularColor;
  1476.     ri.specularPower = obj.surface->specularPower;
  1477.     ri.useTexEnvCombine = context->getRenderPath() != GLContext::GLPath_Basic;
  1478.     ri.lunarLambert = obj.surface->lunarLambert;
  1479. #ifdef USE_HDR
  1480.     ri.nightLightScale = obj.surface->nightLightRadiance * exposure * 1.e5f * .5f;
  1481. #endif
  1482.     // See if the surface should be lit
  1483.     bool lit = (obj.surface->appearanceFlags & Surface::Emissive) == 0;
  1484.     // Set the OpenGL light state
  1485.     unsigned int i;
  1486.     for (i = 0; i < ls.nLights; i++)
  1487.     {
  1488.         const DirectionalLight& light = ls.lights[i];
  1489.         glLightDirection(GL_LIGHT0 + i, ls.lights[i].direction_obj);
  1490.         // RANT ALERT!
  1491.         // This sucks, but it's necessary.  glScale is used to scale a unit
  1492.         // sphere up to planet size.  Since normals are transformed by the
  1493.         // inverse transpose of the model matrix, this means they end up
  1494.         // getting scaled by a factor of 1.0 / planet radius (in km).  This
  1495.         // has terrible effects on lighting: the planet appears almost
  1496.         // completely dark.  To get around this, the GL_rescale_normal
  1497.         // extension was introduced and eventually incorporated into into the
  1498.         // OpenGL 1.2 standard.  Of course, not everyone implemented this
  1499.         // incredibly simple and essential little extension.  Microsoft is
  1500.         // notorious for half-assed support of OpenGL, but 3dfx should have
  1501.         // known better: no Voodoo 1/2/3 drivers seem to support this
  1502.         // extension.  The following is an attempt to get around the problem by
  1503.         // scaling the light brightness by the planet radius.  According to the
  1504.         // OpenGL spec, this should work fine, as clamping of colors to [0, 1]
  1505.         // occurs *after* lighting.  It works fine on my GeForce3 when I
  1506.         // disable EXT_rescale_normal, but I'm not certain whether other
  1507.         // drivers are as well behaved as nVidia's.
  1508.         //
  1509.         // Addendum: Unsurprisingly, using color values outside [0, 1] produces
  1510.         // problems on Savage4 cards.
  1511.         Vec3f lightColor = Vec3f(light.color.red(),
  1512.                                  light.color.green(),
  1513.                                  light.color.blue()) * light.irradiance;
  1514.         if (useRescaleNormal)
  1515.         {
  1516.             glLightColor(GL_LIGHT0 + i, GL_DIFFUSE, lightColor);
  1517.             glLightColor(GL_LIGHT0 + i, GL_SPECULAR, lightColor);
  1518.         }
  1519.         else
  1520.         {
  1521.             glLightColor(GL_LIGHT0 + i, GL_DIFFUSE, lightColor * radius);
  1522.         }
  1523.         glEnable(GL_LIGHT0 + i);
  1524.     }
  1525.     // Compute the inverse model/view matrix
  1526.     Mat4f invMV = (cameraOrientation.toMatrix4() *
  1527.                    Mat4f::translation(Point3f(-pos.x, -pos.y, -pos.z)) *
  1528.                    planetMat *
  1529.                    Mat4f::scaling(1.0f / radius));
  1530.     // The sphere rendering code uses the view frustum to determine which
  1531.     // patches are visible. In order to avoid rendering patches that can't
  1532.     // be seen, make the far plane of the frustum as close to the viewer
  1533.     // as possible.
  1534.     float frustumFarPlane = farPlaneDistance;
  1535.     if (obj.geometry == InvalidResource)
  1536.     {
  1537.         // Only adjust the far plane for ellipsoidal objects
  1538.         float d = pos.distanceFromOrigin();
  1539.         // Account for non-spherical objects
  1540.         float eradius = min(scaleFactors.x, min(scaleFactors.y, scaleFactors.z));
  1541.         if (d > eradius)
  1542.         {
  1543.             // Include a fudge factor to eliminate overaggressive clipping
  1544.             // due to limited floating point precision
  1545.             frustumFarPlane = (float) sqrt(square(d) - square(eradius)) * 1.1f;
  1546.         }
  1547.         else
  1548.         {
  1549.             // We're inside the bounding sphere; leave the far plane alone
  1550.         }
  1551.         if (obj.atmosphere != NULL)
  1552.         {
  1553.             float atmosphereHeight = max(obj.atmosphere->cloudHeight,
  1554.                                          obj.atmosphere->mieScaleHeight * (float) -log(AtmosphereExtinctionThreshold));
  1555.             if (atmosphereHeight > 0.0f)
  1556.             {
  1557.                 // If there's an atmosphere, we need to move the far plane
  1558.                 // out so that the clouds and atmosphere shell aren't clipped.
  1559.                 float atmosphereRadius = eradius + atmosphereHeight;
  1560.                 frustumFarPlane += (float) sqrt(square(atmosphereRadius) -
  1561.                                                 square(eradius));
  1562.             }
  1563.         }
  1564.     }
  1565.     // Transform the frustum into object coordinates using the
  1566.     // inverse model/view matrix.
  1567.     Frustum viewFrustum(degToRad(fov),
  1568.                         (float) windowWidth / (float) windowHeight,
  1569.                         nearPlaneDistance, frustumFarPlane);
  1570.     viewFrustum.transform(invMV);
  1571.     // Get cloud layer parameters
  1572.     Texture* cloudTex       = NULL;
  1573.     Texture* cloudNormalMap = NULL;
  1574.     float cloudTexOffset    = 0.0f;
  1575.     if (obj.atmosphere != NULL)
  1576.     {
  1577.         Atmosphere* atmosphere = const_cast<Atmosphere*>(obj.atmosphere); // Ugly cast required because MultiResTexture::find() is non-const
  1578.         if ((renderFlags & ShowCloudMaps) != 0)
  1579.         {
  1580.             if (atmosphere->cloudTexture.tex[textureResolution] != InvalidResource)
  1581.                 cloudTex = atmosphere->cloudTexture.find(textureResolution);
  1582.             if (atmosphere->cloudNormalMap.tex[textureResolution] != InvalidResource)
  1583.                 cloudNormalMap = atmosphere->cloudNormalMap.find(textureResolution);
  1584.         }
  1585.         if (atmosphere->cloudSpeed != 0.0f)
  1586.             cloudTexOffset = (float) (-pfmod(now * atmosphere->cloudSpeed / (2 * PI), 1.0));
  1587.     }
  1588.     if (obj.geometry == InvalidResource)
  1589.     {
  1590.         // A null model indicates that this body is a sphere
  1591.         if (lit)
  1592.         {
  1593.             switch (context->getRenderPath())
  1594.             {
  1595.             case GLContext::GLPath_GLSL:
  1596.                 renderSphere_GLSL(ri, ls, obj.rings,
  1597.                                   const_cast<Atmosphere*>(obj.atmosphere), cloudTexOffset,
  1598.                                   obj.radius,
  1599.                                   textureResolution,
  1600.                                   renderFlags,
  1601.                                   planetMat, viewFrustum, *context);
  1602.                 break;
  1603.             case GLContext::GLPath_NV30:
  1604.                 renderSphere_FP_VP(ri, viewFrustum, *context);
  1605.                 break;
  1606.             case GLContext::GLPath_NvCombiner_ARBVP:
  1607.             case GLContext::GLPath_NvCombiner_NvVP:
  1608.                 renderSphere_Combiners_VP(ri, ls, viewFrustum, *context);
  1609.                 break;
  1610.             case GLContext::GLPath_NvCombiner:
  1611.                 renderSphere_Combiners(ri, viewFrustum, *context);
  1612.                 break;
  1613.             case GLContext::GLPath_DOT3_ARBVP:
  1614.                 renderSphere_DOT3_VP(ri, ls, viewFrustum, *context);
  1615.                 break;
  1616.             default:
  1617.                 renderSphereDefault(ri, viewFrustum, true, *context);
  1618.             }
  1619.         }
  1620.         else
  1621.         {
  1622.             renderSphereDefault(ri, viewFrustum, false, *context);
  1623.         }
  1624.     }
  1625.     else
  1626.     {
  1627.         if (geometry != NULL)
  1628.         {
  1629.             ResourceHandle texOverride = obj.surface->baseTexture.tex[textureResolution];
  1630.             if (context->getRenderPath() == GLContext::GLPath_GLSL)
  1631.             {
  1632.                 if (lit)
  1633.                 {
  1634.                     renderGeometry_GLSL(geometry,
  1635.                                         ri,
  1636.                                         texOverride,
  1637.                                         ls,
  1638.                                         obj.atmosphere,
  1639.                                         geometryScale,
  1640.                                         renderFlags,
  1641.                                         planetMat,
  1642.                                         astro::daysToSecs(now - astro::J2000));
  1643.                 }
  1644.                 else
  1645.                 {
  1646.                     renderGeometry_GLSL_Unlit(geometry,
  1647.                                               ri,
  1648.                                               texOverride,
  1649.                                               geometryScale,
  1650.                                               renderFlags,
  1651.                                               planetMat,
  1652.                                               astro::daysToSecs(now - astro::J2000));
  1653.                 }
  1654.                 for (unsigned int i = 1; i < 8;/*context->getMaxTextures();*/ i++)
  1655.                 {
  1656.                     glx::glActiveTextureARB(GL_TEXTURE0_ARB + i);
  1657.                     glDisable(GL_TEXTURE_2D);
  1658.                 }
  1659.                 glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  1660.                 glEnable(GL_TEXTURE_2D);
  1661.                 glx::glUseProgramObjectARB(0);
  1662.             }
  1663.             else
  1664.             {
  1665.                 renderModelDefault(geometry, ri, lit, texOverride);
  1666.             }
  1667.         }
  1668.     }
  1669.     if (obj.rings != NULL && distance <= obj.rings->innerRadius)
  1670.     {
  1671.         if (context->getRenderPath() == GLContext::GLPath_GLSL)
  1672.         {
  1673.             renderRings_GLSL(*obj.rings, ri, ls,
  1674.                              radius, 1.0f - obj.semiAxes.y,
  1675.                              textureResolution,
  1676.                              (renderFlags & ShowRingShadows) != 0 && lit,
  1677.                              detailOptions.ringSystemSections);
  1678.         }
  1679.         else
  1680.         {
  1681.             renderRings(*obj.rings, ri, radius, 1.0f - obj.semiAxes.y,
  1682.                         textureResolution,
  1683.                         context->getMaxTextures() > 1 &&
  1684.                         (renderFlags & ShowRingShadows) != 0 && lit,
  1685.                         *context,
  1686.                         detailOptions.ringSystemSections);
  1687.         }
  1688.     }
  1689.     if (obj.atmosphere != NULL)
  1690.     {
  1691.         Atmosphere* atmosphere = const_cast<Atmosphere *>(obj.atmosphere);
  1692.         // Compute the apparent thickness in pixels of the atmosphere.
  1693.         // If it's only one pixel thick, it can look quite unsightly
  1694.         // due to aliasing.  To avoid popping, we gradually fade in the
  1695.         // atmosphere as it grows from two to three pixels thick.
  1696.         float fade;
  1697.         float thicknessInPixels = 0.0f;
  1698.         if (distance - radius > 0.0f)
  1699.         {
  1700.             thicknessInPixels = atmosphere->height /
  1701.                 ((distance - radius) * pixelSize);
  1702.             fade = clamp(thicknessInPixels - 2);
  1703.         }
  1704.         else
  1705.         {
  1706.             fade = 1.0f;
  1707.         }
  1708.         if (fade > 0 && (renderFlags & ShowAtmospheres) != 0)
  1709.         {
  1710.             // Only use new atmosphere code in OpenGL 2.0 path when new style parameters are defined.
  1711.             // TODO: convert old style atmopshere parameters
  1712.             if (context->getRenderPath() == GLContext::GLPath_GLSL &&
  1713.                 atmosphere->mieScaleHeight > 0.0f)
  1714.             {
  1715.                 float atmScale = 1.0f + atmosphere->height / radius;
  1716.                 renderAtmosphere_GLSL(ri, ls,
  1717.                                       atmosphere,
  1718.                                       radius * atmScale,
  1719.                                       planetMat,
  1720.                                       viewFrustum,
  1721.                                       *context);
  1722.             }
  1723.             else
  1724.             {
  1725.                 glPushMatrix();
  1726.                 glLoadIdentity();
  1727.                 glDisable(GL_LIGHTING);
  1728.                 glDisable(GL_TEXTURE_2D);
  1729.                 glEnable(GL_BLEND);
  1730.                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  1731.                 glRotate(cameraOrientation);
  1732.                 renderEllipsoidAtmosphere(*atmosphere,
  1733.                                           pos,
  1734.                                           obj.orientation,
  1735.                                           scaleFactors,
  1736.                                           ri.sunDir_eye,
  1737.                                           ls,
  1738.                                           thicknessInPixels,
  1739.                                           lit);
  1740.                 glEnable(GL_TEXTURE_2D);
  1741.                 glPopMatrix();
  1742.             }
  1743.         }
  1744.         // If there's a cloud layer, we'll render it now.
  1745.         if (cloudTex != NULL)
  1746.         {
  1747.             glPushMatrix();
  1748.             float cloudScale = 1.0f + atmosphere->cloudHeight / radius;
  1749.             glScalef(cloudScale, cloudScale, cloudScale);
  1750.             // If we're beneath the cloud level, render the interior of
  1751.             // the cloud sphere.
  1752.             if (distance - radius < atmosphere->cloudHeight)
  1753.                 glFrontFace(GL_CW);
  1754.             if (atmosphere->cloudSpeed != 0.0f)
  1755.             {
  1756.                 // Make the clouds appear to rotate above the surface of
  1757.                 // the planet.  This is easier to do with the texture
  1758.                 // matrix than the model matrix because changing the
  1759.                 // texture matrix doesn't require us to compute a second
  1760.                 // set of model space rendering parameters.
  1761.                 glMatrixMode(GL_TEXTURE);
  1762.                 glTranslatef(cloudTexOffset, 0.0f, 0.0f);
  1763.                 glMatrixMode(GL_MODELVIEW);
  1764.             }
  1765.             glEnable(GL_LIGHTING);
  1766.             glDepthMask(GL_FALSE);
  1767.             cloudTex->bind();
  1768.             glEnable(GL_BLEND);
  1769.             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1770. #ifdef HDR_COMPRESS
  1771.             glColor4f(0.5f, 0.5f, 0.5f, 1);
  1772. #else
  1773.             glColor4f(1, 1, 1, 1);
  1774. #endif
  1775.             // Cloud layers can be trouble for the depth buffer, since they tend
  1776.             // to be very close to the surface of a planet relative to the radius
  1777.             // of the planet. We'll help out by offsetting the cloud layer toward
  1778.             // the viewer.
  1779.             if (distance > radius * 1.1f)
  1780.             {
  1781.                 glEnable(GL_POLYGON_OFFSET_FILL);
  1782.                 glPolygonOffset(-1.0f, -1.0f);
  1783.             }
  1784.             if (lit)
  1785.             {
  1786.                 if (context->getRenderPath() == GLContext::GLPath_GLSL)
  1787.                 {
  1788.                     renderClouds_GLSL(ri, ls,
  1789.                                       atmosphere,
  1790.                                       cloudTex,
  1791.                                       cloudNormalMap,
  1792.                                       cloudTexOffset,
  1793.                                       obj.rings,
  1794.                                       radius,
  1795.                                       textureResolution,
  1796.                                       renderFlags,
  1797.                                       planetMat,
  1798.                                       viewFrustum,
  1799.                                       *context);
  1800.                 }
  1801.                 else
  1802.                 {
  1803.                     VertexProcessor* vproc = context->getVertexProcessor();
  1804.                     if (vproc != NULL)
  1805.                     {
  1806.                         vproc->enable();
  1807.                         vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
  1808.                         vproc->parameter(vp::TextureTranslation,
  1809.                                          cloudTexOffset, 0.0f, 0.0f, 0.0f);
  1810.                         if (ls.nLights > 1)
  1811.                             vproc->use(vp::diffuseTexOffset_2light);
  1812.                         else
  1813.                             vproc->use(vp::diffuseTexOffset);
  1814.                         setLightParameters_VP(*vproc, ls, ri.color, Color::Black);
  1815.                     }
  1816.                     g_lodSphere->render(*context,
  1817.                                         LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  1818.                                         viewFrustum,
  1819.                                         ri.pixWidth,
  1820.                                         cloudTex);
  1821.                     if (vproc != NULL)
  1822.                         vproc->disable();
  1823.                 }
  1824.             }
  1825.             else
  1826.             {
  1827.                 glDisable(GL_LIGHTING);
  1828.                 g_lodSphere->render(*context,
  1829.                                     LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  1830.                                     viewFrustum,
  1831.                                     ri.pixWidth,
  1832.                                     cloudTex);
  1833.                 glEnable(GL_LIGHTING);
  1834.             }
  1835.             glDisable(GL_POLYGON_OFFSET_FILL);
  1836.             // Reset the texture matrix
  1837.             glMatrixMode(GL_TEXTURE);
  1838.             glLoadIdentity();
  1839.             glMatrixMode(GL_MODELVIEW);
  1840.             glDepthMask(GL_TRUE);
  1841.             glFrontFace(GL_CCW);
  1842.             glPopMatrix();
  1843.         }
  1844.     }
  1845.     // No separate shadow rendering pass required for GLSL path
  1846.     if (ls.shadows[0] != NULL &&
  1847.         ls.shadows[0]->size() != 0 &&
  1848.         (obj.surface->appearanceFlags & Surface::Emissive) == 0 &&
  1849.         context->getRenderPath() != GLContext::GLPath_GLSL)
  1850.     {
  1851.         if (context->getVertexProcessor() != NULL &&
  1852.             context->getFragmentProcessor() != NULL)
  1853.         {
  1854.             renderEclipseShadows_Shaders(geometry,
  1855.                                          *ls.shadows[0],
  1856.                                          ri,
  1857.                                          radius, planetMat, viewFrustum,
  1858.                                          *context);
  1859.         }
  1860.         else
  1861.         {
  1862.             renderEclipseShadows(geometry,
  1863.                                  *ls.shadows[0],
  1864.                                  ri,
  1865.                                  radius, planetMat, viewFrustum,
  1866.                                  *context);
  1867.         }
  1868.     }
  1869.     if (obj.rings != NULL &&
  1870.         (obj.surface->appearanceFlags & Surface::Emissive) == 0 &&
  1871.         (renderFlags & ShowRingShadows) != 0)
  1872.     {
  1873.         Texture* ringsTex = obj.rings->texture.find(textureResolution);
  1874.         if (ringsTex != NULL)
  1875.         {
  1876.             Vec3f sunDir = pos - Point3f(0, 0, 0);
  1877.             sunDir.normalize();
  1878.             glEnable(GL_TEXTURE_2D);
  1879.             ringsTex->bind();
  1880.             if (useClampToBorder &&
  1881.                 context->getVertexPath() != GLContext::VPath_Basic &&
  1882.                 context->getRenderPath() != GLContext::GLPath_GLSL)
  1883.             {
  1884.                 renderRingShadowsVS(geometry,
  1885.                                     *obj.rings,
  1886.                                     sunDir,
  1887.                                     ri,
  1888.                                     radius, 1.0f - obj.semiAxes.y,
  1889.                                     planetMat, viewFrustum,
  1890.                                     *context);
  1891.             }
  1892.         }
  1893.     }
  1894.     if (obj.rings != NULL && distance > obj.rings->innerRadius)
  1895.     {
  1896.         glDepthMask(GL_FALSE);
  1897.         if (context->getRenderPath() == GLContext::GLPath_GLSL)
  1898.         {
  1899.             renderRings_GLSL(*obj.rings, ri, ls,
  1900.                              radius, 1.0f - obj.semiAxes.y,
  1901.                              textureResolution,
  1902.                              (renderFlags & ShowRingShadows) != 0 && lit,
  1903.                              detailOptions.ringSystemSections);
  1904.         }
  1905.         else
  1906.         {
  1907.             renderRings(*obj.rings, ri, radius, 1.0f - obj.semiAxes.y,
  1908.                         textureResolution,
  1909.                         (context->hasMultitexture() &&
  1910.                          (renderFlags & ShowRingShadows) != 0 && lit),
  1911.                         *context,
  1912.                         detailOptions.ringSystemSections);
  1913.         }
  1914.     }
  1915.     // Disable all light sources other than the first
  1916.     for (i = 0; i < ls.nLights; i++)
  1917.         glDisable(GL_LIGHT0 + i);
  1918.     glPopMatrix();
  1919.     glDisable(GL_DEPTH_TEST);
  1920.     glDepthMask(GL_FALSE);
  1921.     glDisable(GL_LIGHTING);
  1922.     glEnable(GL_BLEND);
  1923. }
  1924. bool Renderer::testEclipse(const Body& receiver,
  1925.                            const Body& caster,
  1926.                            const DirectionalLight& light,
  1927.                            double now,
  1928.                            vector<EclipseShadow>& shadows)
  1929. {
  1930.     // Ignore situations where the shadow casting body is much smaller than
  1931.     // the receiver, as these shadows aren't likely to be relevant.  Also,
  1932.     // ignore eclipses where the caster is not an ellipsoid, since we can't
  1933.     // generate correct shadows in this case.
  1934.     if (caster.getRadius() >= receiver.getRadius() * MinRelativeOccluderRadius &&
  1935.         caster.hasVisibleGeometry() &&
  1936.         caster.extant(now) &&
  1937.         caster.isEllipsoid())
  1938.     {
  1939.         // All of the eclipse related code assumes that both the caster
  1940.         // and receiver are spherical.  Irregular receivers will work more
  1941.         // or less correctly, but casters that are sufficiently non-spherical
  1942.         // will produce obviously incorrect shadows.  Another assumption we
  1943.         // make is that the distance between the caster and receiver is much
  1944.         // less than the distance between the sun and the receiver.  This
  1945.         // approximation works everywhere in the solar system, and is likely
  1946.         // valid for any orbitally stable pair of objects orbiting a star.
  1947.         Point3d posReceiver = receiver.getAstrocentricPosition(now);
  1948.         Point3d posCaster = caster.getAstrocentricPosition(now);
  1949.         //const Star* sun = receiver.getSystem()->getStar();
  1950.         //assert(sun != NULL);
  1951.         //double distToSun = posReceiver.distanceFromOrigin();
  1952.         //float appSunRadius = (float) (sun->getRadius() / distToSun);
  1953.         float appSunRadius = light.apparentSize;
  1954.         Vec3d dir = posCaster - posReceiver;
  1955.         double distToCaster = dir.length() - receiver.getRadius();
  1956.         float appOccluderRadius = (float) (caster.getRadius() / distToCaster);
  1957.         // The shadow radius is the radius of the occluder plus some additional
  1958.         // amount that depends upon the apparent radius of the sun.  For
  1959.         // a sun that's distant/small and effectively a point, the shadow
  1960.         // radius will be the same as the radius of the occluder.
  1961.         float shadowRadius = (1 + appSunRadius / appOccluderRadius) *
  1962.             caster.getRadius();
  1963.         // Test whether a shadow is cast on the receiver.  We want to know
  1964.         // if the receiver lies within the shadow volume of the caster.  Since
  1965.         // we're assuming that everything is a sphere and the sun is far
  1966.         // away relative to the caster, the shadow volume is a
  1967.         // cylinder capped at one end.  Testing for the intersection of a
  1968.         // singly capped cylinder is as simple as checking the distance
  1969.         // from the center of the receiver to the axis of the shadow cylinder.
  1970.         // If the distance is less than the sum of the caster's and receiver's
  1971.         // radii, then we have an eclipse. We also need to verify that the
  1972.         // receiver is behind the caster when seen from the light source.
  1973.         float R = receiver.getRadius() + shadowRadius;
  1974.         // The stored light position is receiver-relative; thus the caster-to-light
  1975.         // direction is casterPos - (receiverPos + lightPos)
  1976.         Point3d lightPosition = posReceiver + light.position;
  1977.         Vec3d lightToCasterDir = posCaster - lightPosition;
  1978.         Vec3d receiverToCasterDir = posReceiver - posCaster;
  1979.         double dist = distance(posReceiver,
  1980.                                Ray3d(posCaster, lightToCasterDir));
  1981.         if (dist < R && lightToCasterDir * receiverToCasterDir > 0.0)
  1982.         {
  1983.             Vec3d sunDir = lightToCasterDir;
  1984.             sunDir.normalize();
  1985.             EclipseShadow shadow;
  1986.             shadow.origin = Point3f((float) dir.x,
  1987.                                     (float) dir.y,
  1988.                                     (float) dir.z);
  1989.             shadow.direction = Vec3f((float) sunDir.x,
  1990.                                      (float) sunDir.y,
  1991.                                      (float) sunDir.z);
  1992.             shadow.penumbraRadius = shadowRadius;
  1993.             // The umbra radius will be positive if the apparent size of the occluder
  1994.             // is greater than the apparent size of the sun, zero if they're equal,
  1995.             // and negative when the eclipse is partial. The absolute value of the
  1996.             // umbra radius is the radius of the shadow region with constant depth:
  1997.             // for total eclipses, this area is actually the umbra, with a depth of
  1998.             // 1. For annular eclipses and transits, it is less than 1.
  1999.             shadow.umbraRadius = caster.getRadius() *
  2000.                 (appOccluderRadius - appSunRadius) / appOccluderRadius;
  2001.             shadow.maxDepth = std::min(1.0f, square(appOccluderRadius / appSunRadius));
  2002.             // Ignore transits that don't produce a visible shadow.
  2003.             if (shadow.maxDepth > 1.0f / 256.0f)
  2004.                 shadows.push_back(shadow);
  2005.             return true;
  2006.         }
  2007.     }
  2008.     return false;
  2009. }
  2010. void Renderer::renderPlanet(Body& body,
  2011.                             Point3f pos,
  2012.                             float distance,
  2013.                             float appMag,
  2014.                             const Observer& observer,
  2015.                             const Quatf& cameraOrientation,
  2016.                             float nearPlaneDistance,
  2017.                             float farPlaneDistance)
  2018. {
  2019.     double now = observer.getTime();
  2020.     float altitude = distance - body.getRadius();
  2021.     float discSizeInPixels = body.getRadius() /
  2022.         (max(nearPlaneDistance, altitude) * pixelSize);
  2023.     if (discSizeInPixels > 1 && body.hasVisibleGeometry())
  2024.     {
  2025.         RenderProperties rp;
  2026.         if (displayedSurface.empty())
  2027.         {
  2028.             rp.surface = const_cast<Surface*>(&body.getSurface());
  2029.         }
  2030.         else
  2031.         {
  2032.             rp.surface = body.getAlternateSurface(displayedSurface);
  2033.             if (rp.surface == NULL)
  2034.                 rp.surface = const_cast<Surface*>(&body.getSurface());
  2035.         }
  2036.         rp.atmosphere = body.getAtmosphere();
  2037.         rp.rings = body.getRings();
  2038.         rp.radius = body.getRadius();
  2039.         rp.geometry = body.getGeometry();
  2040.         rp.semiAxes = body.getSemiAxes() * (1.0f / rp.radius);
  2041.         rp.geometryScale = body.getGeometryScale();
  2042.         Quatd q = body.getRotationModel(now)->spin(now) *
  2043.             body.getEclipticToEquatorial(now);
  2044.         rp.orientation = body.getOrientation() *
  2045.             Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
  2046.         if (body.getLocations() != NULL && (labelMode & LocationLabels) != 0)
  2047.             body.computeLocations();
  2048.         Vec3f scaleFactors;
  2049.         bool isNormalized = false;
  2050.         Geometry* geometry = NULL;
  2051.         if (rp.geometry != InvalidResource)
  2052.             geometry = GetGeometryManager()->find(rp.geometry);
  2053.         if (geometry == NULL || geometry->isNormalized())
  2054.         {
  2055.             scaleFactors = rp.semiAxes * rp.radius;
  2056.             isNormalized = true;
  2057.         }
  2058.         else
  2059.         {
  2060.             float scale = rp.geometryScale;
  2061.             scaleFactors = Vec3f(scale, scale, scale);
  2062.         }
  2063.         LightingState lights;
  2064.         setupObjectLighting(lightSourceList,
  2065.                             secondaryIlluminators,
  2066.                             rp.orientation,
  2067.                             scaleFactors,
  2068.                             pos,
  2069.                             isNormalized,
  2070. #ifdef USE_HDR
  2071.                             faintestMag,
  2072.                             DEFAULT_EXPOSURE + brightPlus, //exposure + brightPlus,
  2073.                             appMag,
  2074. #endif
  2075.                             lights);
  2076.         assert(lights.nLights <= MaxLights);
  2077.         lights.ambientColor = Vec3f(ambientColor.red(),
  2078.                                     ambientColor.green(),
  2079.                                     ambientColor.blue());
  2080.         {
  2081.             // Clear out the list of eclipse shadows
  2082.             for (unsigned int li = 0; li < lights.nLights; li++)
  2083.             {
  2084.                 eclipseShadows[li].clear();
  2085.                 lights.shadows[li] = &eclipseShadows[li];
  2086.             }
  2087.         }
  2088.         // Calculate eclipse circumstances
  2089.         if ((renderFlags & ShowEclipseShadows) != 0 &&
  2090.             body.getSystem() != NULL)
  2091.         {
  2092.             PlanetarySystem* system = body.getSystem();
  2093.             if (system->getPrimaryBody() == NULL &&
  2094.                 body.getSatellites() != NULL)
  2095.             {
  2096.                 // The body is a planet.  Check for eclipse shadows
  2097.                 // from all of its satellites.
  2098.                 PlanetarySystem* satellites = body.getSatellites();
  2099.                 if (satellites != NULL)
  2100.                 {
  2101.                     int nSatellites = satellites->getSystemSize();
  2102.                     for (unsigned int li = 0; li < lights.nLights; li++)
  2103.                     {
  2104.                         if (lights.lights[li].castsShadows)
  2105.                         {
  2106.                             for (int i = 0; i < nSatellites; i++)
  2107.                             {
  2108.                                 testEclipse(body, *satellites->getBody(i),
  2109.                                             lights.lights[li],
  2110.                                             now, *lights.shadows[li]);
  2111.                             }
  2112.                         }
  2113.                     }
  2114.                 }
  2115.             }
  2116.             else if (system->getPrimaryBody() != NULL)
  2117.             {
  2118.                 for (unsigned int li = 0; li < lights.nLights; li++)
  2119.                 {
  2120.                     if (lights.lights[li].castsShadows)
  2121.                     {
  2122.                         // The body is a moon.  Check for eclipse shadows from
  2123.                         // the parent planet and all satellites in the system.
  2124.                         // Traverse up the hierarchy so that any parent objects
  2125.                         // of the parent are also considered (TODO: their child
  2126.                         // objects will not be checked for shadows.)
  2127.                         Body* planet = system->getPrimaryBody();
  2128.                         while (planet != NULL)
  2129.                         {
  2130.                             testEclipse(body, *planet, lights.lights[li],
  2131.                                         now, *lights.shadows[li]);
  2132.                             if (planet->getSystem() != NULL)
  2133.                                 planet = planet->getSystem()->getPrimaryBody();
  2134.                             else
  2135.                                 planet = NULL;
  2136.                         }
  2137.                         int nSatellites = system->getSystemSize();
  2138.                         for (int i = 0; i < nSatellites; i++)
  2139.                         {
  2140.                             if (system->getBody(i) != &body)
  2141.                             {
  2142.                                 testEclipse(body, *system->getBody(i),
  2143.                                             lights.lights[li],
  2144.                                             now, *lights.shadows[li]);
  2145.                             }
  2146.                         }
  2147.                     }
  2148.                 }
  2149.             }
  2150.         }
  2151.         renderObject(pos, distance, now,
  2152.                      cameraOrientation, nearPlaneDistance, farPlaneDistance,
  2153.                      rp, lights);
  2154.         if (body.getLocations() != NULL && (labelMode & LocationLabels) != 0)
  2155.         {
  2156.             // Set up location markers for this body
  2157.             mountainRep    = MarkerRepresentation(MarkerRepresentation::Triangle, 8.0f, LocationLabelColor);
  2158.             craterRep      = MarkerRepresentation(MarkerRepresentation::Circle,   8.0f, LocationLabelColor);
  2159.             observatoryRep = MarkerRepresentation(MarkerRepresentation::Plus,     8.0f, LocationLabelColor);
  2160.             cityRep        = MarkerRepresentation(MarkerRepresentation::X,        3.0f, LocationLabelColor);
  2161.             genericLocationRep = MarkerRepresentation(MarkerRepresentation::Square, 8.0f, LocationLabelColor);
  2162.             
  2163.             glEnable(GL_DEPTH_TEST);
  2164.             glDepthMask(GL_FALSE);
  2165.             glDisable(GL_BLEND);
  2166.             // We need a double precision body-relative position of the
  2167.             // observer, otherwise location labels will tend to jitter.
  2168.             Vec3d posd = (body.getPosition(observer.getTime()) -
  2169.                           observer.getPosition()) * astro::microLightYearsToKilometers(1.0);
  2170.             renderLocations(body, posd, q);
  2171.             glDisable(GL_DEPTH_TEST);
  2172.         }
  2173.     }
  2174.     glEnable(GL_TEXTURE_2D);
  2175.     glEnable(GL_BLEND);
  2176.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  2177. #ifdef USE_HDR
  2178.     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
  2179. #endif
  2180.     if (body.isVisibleAsPoint())
  2181.     {
  2182.         if (useNewStarRendering)
  2183.         {
  2184.             renderObjectAsPoint(pos,
  2185.                                 body.getRadius(),
  2186.                                 appMag,
  2187.                                 faintestMag,
  2188.                                 discSizeInPixels,
  2189.                                 body.getSurface().color,
  2190.                                 cameraOrientation,
  2191.                                 false, false);
  2192.         }
  2193.         else
  2194.         {
  2195.             renderObjectAsPoint_nosprite(pos,
  2196.                                  body.getRadius(),
  2197.                                  appMag,
  2198.                                  faintestMag,
  2199.                                  discSizeInPixels,
  2200.                                  body.getSurface().color,
  2201.                                  cameraOrientation,
  2202.                                  false);
  2203.         }
  2204.     }
  2205. #ifdef USE_HDR
  2206.     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  2207. #endif
  2208. }
  2209. void Renderer::renderStar(const Star& star,
  2210.                           Point3f pos,
  2211.                           float distance,
  2212.                           float appMag,
  2213.                           Quatf cameraOrientation,
  2214.                           double now,
  2215.                           float nearPlaneDistance,
  2216.                           float farPlaneDistance)
  2217. {
  2218.     if (!star.getVisibility())
  2219.         return;
  2220.     Color color = colorTemp->lookupColor(star.getTemperature());
  2221.     float radius = star.getRadius();
  2222.     float discSizeInPixels = radius / (distance * pixelSize);
  2223.     if (discSizeInPixels > 1)
  2224.     {
  2225.         Surface surface;
  2226.         RenderProperties rp;
  2227.         surface.color = color;
  2228.         MultiResTexture mtex = star.getTexture();
  2229.         if (mtex.tex[textureResolution] != InvalidResource)
  2230.         {
  2231.             surface.baseTexture = mtex;
  2232.         }
  2233.         else
  2234.         {
  2235.             surface.baseTexture = InvalidResource;
  2236.         }
  2237.         surface.appearanceFlags |= Surface::ApplyBaseTexture;
  2238.         surface.appearanceFlags |= Surface::Emissive;
  2239.         rp.surface = &surface;
  2240.         rp.rings = NULL;
  2241.         rp.radius = star.getRadius();
  2242.         rp.semiAxes = star.getEllipsoidSemiAxes();
  2243.         rp.geometry = star.getGeometry();
  2244. #ifndef USE_HDR
  2245.         Atmosphere atmosphere;
  2246.         Color atmColor(color.red() * 0.5f, color.green() * 0.5f, color.blue() * 0.5f);
  2247.         atmosphere.height = radius * CoronaHeight;
  2248.         atmosphere.lowerColor = atmColor;
  2249.         atmosphere.upperColor = atmColor;
  2250.         atmosphere.skyColor = atmColor;
  2251.         // Use atmosphere effect to give stars a fuzzy fringe
  2252.         if (rp.geometry == InvalidResource)
  2253.             rp.atmosphere = &atmosphere;
  2254.         else
  2255. #endif
  2256.         rp.atmosphere = NULL;
  2257.         Quatd q = star.getRotationModel()->orientationAtTime(now);
  2258.         rp.orientation = Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
  2259.         renderObject(pos, distance, now,
  2260.                      cameraOrientation, nearPlaneDistance, farPlaneDistance,
  2261.                      rp, LightingState());
  2262.     }
  2263.     glEnable(GL_TEXTURE_2D);
  2264.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  2265. #ifdef USE_HDR
  2266.     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
  2267. #endif
  2268. #ifndef USE_HDR
  2269.     if (useNewStarRendering)
  2270.     {
  2271. #endif
  2272.         renderObjectAsPoint(pos,
  2273.                             star.getRadius(),
  2274.                             appMag,
  2275.                             faintestMag,
  2276.                             discSizeInPixels,
  2277.                             color,
  2278.                             cameraOrientation,
  2279.                             true, true);
  2280. #ifndef USE_HDR
  2281.     }
  2282.     else
  2283.     {
  2284.         renderObjectAsPoint_nosprite(pos,
  2285.                                      star.getRadius(),
  2286.                                      appMag,
  2287.                                      faintestPlanetMag,
  2288.                                      discSizeInPixels,
  2289.                                      color,
  2290.                                      cameraOrientation,
  2291.                                      true);
  2292.     }
  2293. #endif
  2294. #ifdef USE_HDR
  2295.     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  2296. #endif
  2297. }
  2298. static const int MaxCometTailPoints = 120;
  2299. static const int CometTailSlices = 48;
  2300. struct CometTailVertex
  2301. {
  2302.     Point3f point;
  2303.     Vec3f normal;
  2304.     Point3f paraboloidPoint;
  2305.     float brightness;
  2306. };
  2307. static CometTailVertex cometTailVertices[CometTailSlices * MaxCometTailPoints];
  2308. static void ProcessCometTailVertex(const CometTailVertex& v,
  2309.                                    const Vec3f& viewDir,
  2310.                                    float fadeDistFromSun)
  2311. {
  2312.     // If fadeDistFromSun = x/x0 >= 1.0, comet tail starts fading,
  2313.     // i.e. fadeFactor quickly transits from 1 to 0.
  2314.     float fadeFactor = 0.5f - 0.5f * (float) tanh(fadeDistFromSun - 1.0f / fadeDistFromSun);
  2315.     float shade = abs(viewDir * v.normal * v.brightness * fadeFactor);
  2316.     glColor4f(0.5f, 0.5f, 0.75f, shade);
  2317.     glVertex(v.point);
  2318. }
  2319. #if 0
  2320. static void ProcessCometTailVertex(const CometTailVertex& v,
  2321.                                    const Point3f& cameraPos)
  2322. {
  2323.     Vec3f viewDir = v.point - cameraPos;
  2324.     viewDir.normalize();
  2325.     float shade = abs(viewDir * v.normal * v.brightness);
  2326.     glColor4f(0.0f, 0.5f, 1.0f, shade);
  2327.     glVertex(v.point);
  2328. }
  2329. #endif
  2330. #if 0
  2331. static void ProcessCometTailVertex(const CometTailVertex& v,
  2332.                                    const Point3f& eyePos_obj,
  2333.                                    float b,
  2334.                                    float h)
  2335. {
  2336.     float shade = 0.0f;
  2337.     Vec3f R = v.paraboloidPoint - eyePos_obj;
  2338.     float c0 = b * (square(eyePos_obj.x) + square(eyePos_obj.y)) + eyePos_obj.z;
  2339.     float c1 = 2 * b * (R.x * eyePos_obj.x + R.y * eyePos_obj.y) - R.z;
  2340.     float c2 = b * (square(R.x) + square(R.y));
  2341.     float disc = square(c1) - 4 * c0 * c2;
  2342.     if (disc < 0.0f)
  2343.     {
  2344.         shade = 0.0f;
  2345.     }
  2346.     else
  2347.     {
  2348.         disc = (float) sqrt(disc);
  2349.         float s = 1.0f / (2 * c2);
  2350.         float t0 = (h - eyePos_obj.z) / R.z;
  2351.         float t1 = (c1 - disc) * s;
  2352.         float t2 = (c1 + disc) * s;
  2353.         /*float u0 = max(t0, 0.0f);     Unused*/
  2354.         float u1, u2;
  2355.         if (R.z < 0.0f)
  2356.         {
  2357.             u1 = max(t1, t0);
  2358.             u2 = max(t2, t0);
  2359.         }
  2360.         else
  2361.         {
  2362.             u1 = min(t1, t0);
  2363.             u2 = min(t2, t0);
  2364.         }
  2365.         u1 = max(0.0f, u1);
  2366.         u2 = max(0.0f, u2);
  2367.         shade = u2 - u1;
  2368.     }
  2369.     glColor4f(0.0f, 0.5f, 1.0f, shade);
  2370.     glVertex(v.point);
  2371. }
  2372. #endif
  2373. // Compute a rough estimate of the visible length of the dust tail.
  2374. // TODO: This is old code that needs to be rewritten. For one thing,
  2375. // the length is inversely proportional to the distance from the sun,
  2376. // whereas the 1/distance^2 is probably more realistic. There should
  2377. // also be another parameter that specifies how active the comet is.
  2378. static float cometDustTailLength(float distanceToSun,
  2379.                                  float radius)
  2380. {
  2381.     return (1.0e8f / distanceToSun) * (radius / 5.0f) * 1.0e7f;
  2382. }
  2383. // TODO: Remove unused parameters??
  2384. void Renderer::renderCometTail(const Body& body,
  2385.                                Point3f pos,
  2386.                                double now,
  2387.                                float discSizeInPixels)
  2388. {
  2389.     Point3f cometPoints[MaxCometTailPoints];
  2390.     Point3d pos0 = body.getOrbit(now)->positionAtTime(now);
  2391.     Point3d pos1 = body.getOrbit(now)->positionAtTime(now - 0.01);
  2392.     Vec3d vd = pos1 - pos0;
  2393.     double t = now;
  2394.     /*float f = 1.0e15f;    Unused*/
  2395.     /*int nSteps = MaxCometTailPoints;  Unused*/
  2396.     /*float dt = 10000000.0f / (nSteps * (float) vd.length() * 100.0f);     Unused*/
  2397.     float distanceFromSun, irradiance_max = 0.0f;
  2398.     unsigned int li_eff = 0;    // Select the first sun as default to
  2399.                                 // shut up compiler warnings
  2400.     // Adjust the amount of triangles used for the comet tail based on
  2401.     // the screen size of the comet.
  2402.     float lod = min(1.0f, max(0.2f, discSizeInPixels / 1000.0f));
  2403.     int nTailPoints = (int) (MaxCometTailPoints * lod);
  2404.     int nTailSlices = (int) (CometTailSlices * lod);
  2405.     // Find the sun with the largest irrradiance of light onto the comet
  2406.     // as function of the comet's position;
  2407.     // irradiance = sun's luminosity / square(distanceFromSun);
  2408.     for (unsigned int li = 0; li < lightSourceList.size(); li++)
  2409.     {
  2410.         distanceFromSun = (float) (Vec3d(pos.x, pos.y, pos.z) - lightSourceList[li].position).length();
  2411.         float irradiance = lightSourceList[li].luminosity / square(distanceFromSun);
  2412.         if (irradiance > irradiance_max)
  2413.         {
  2414.             li_eff = li;
  2415.             irradiance_max = irradiance;
  2416.         }
  2417.     }
  2418.     float fadeDistance = 1.0f / (float) (COMET_TAIL_ATTEN_DIST_SOL * sqrt(irradiance_max));
  2419.     // direction to sun with dominant light irradiance:
  2420.     Vec3d sd = Vec3d(pos.x, pos.y, pos.z) - lightSourceList[li_eff].position;
  2421.     Vec3f sunDir = Vec3f((float) sd.x, (float) sd.y, (float) sd.z);
  2422.     sunDir.normalize();
  2423.     float dustTailLength = cometDustTailLength((float) pos0.distanceFromOrigin(), body.getRadius());
  2424.     float dustTailRadius = dustTailLength * 0.1f;
  2425.     Point3f origin(0, 0, 0);
  2426.     origin -= sunDir * (body.getRadius() * 100);
  2427.     int i;
  2428.     for (i = 0; i < nTailPoints; i++)
  2429.     {
  2430.         float alpha = (float) i / (float) nTailPoints;
  2431.         alpha = alpha * alpha;
  2432.         cometPoints[i] = origin + sunDir * (dustTailLength * alpha);
  2433.     }
  2434.     // We need three axes to define the coordinate system for rendering the
  2435.     // comet.  The first axis is the velocity.  We choose the second one
  2436.     // based on the orientation of the comet.  And the third is just the cross
  2437.     // product of the first and second axes.
  2438.     Quatd qd = body.getEclipticToEquatorial(t);
  2439.     Quatf q((float) qd.w, (float) qd.x, (float) qd.y, (float) qd.z);
  2440.     Vec3f v = cometPoints[1] - cometPoints[0];
  2441.     v.normalize();
  2442.     Vec3f u0 = Vec3f(0, 1, 0) * q.toMatrix3();
  2443.     Vec3f u1 = Vec3f(1, 0, 0) * q.toMatrix3();
  2444.     Vec3f u;
  2445.     if (abs(u0 * v) < abs(u1 * v))
  2446.         u = v ^ u0;
  2447.     else
  2448.         u = v ^ u1;
  2449.     u.normalize();
  2450.     Vec3f w = u ^ v;
  2451.     glColor4f(0.0f, 1.0f, 1.0f, 0.5f);
  2452.     glPushMatrix();
  2453.     glTranslate(pos);
  2454.     // glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  2455.     glDisable(GL_TEXTURE_2D);
  2456.     glDisable(GL_LIGHTING);
  2457.     glDepthMask(GL_FALSE);
  2458.     glEnable(GL_BLEND);
  2459.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  2460.     for (i = 0; i < nTailPoints; i++)
  2461.     {
  2462.         float brightness = 1.0f - (float) i / (float) (nTailPoints - 1);
  2463.         Vec3f v0, v1;
  2464.         float sectionLength;
  2465.         if (i != 0 && i != nTailPoints - 1)
  2466.         {
  2467.             v0 = cometPoints[i] - cometPoints[i - 1];
  2468.             v1 = cometPoints[i + 1] - cometPoints[i];
  2469.             sectionLength = v0.length();
  2470.             v0.normalize();
  2471.             v1.normalize();
  2472.             Vec3f axis = v1 ^ v0;
  2473.             float axisLength = axis.length();
  2474.             if (axisLength > 1e-5f)
  2475.             {
  2476.                 axis *= 1.0f / axisLength;
  2477.                 q.setAxisAngle(axis, (float) asin(axisLength));
  2478.                 Mat3f m = q.toMatrix3();
  2479.                 u = u * m;
  2480.                 v = v * m;
  2481.                 w = w * m;
  2482.             }
  2483.         }
  2484.         else if (i == 0)
  2485.         {
  2486.             v0 = cometPoints[i + 1] - cometPoints[i];
  2487.             sectionLength = v0.length();