render.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:390k
- ri.baseTex->bind();
- glx::glActiveTextureARB(GL_TEXTURE0_ARB);
- ri.bumpTex->bind();
- setupBumpTexenv();
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::Tangents |
- LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
- frustum, ri.pixWidth,
- ri.bumpTex, ri.baseTex);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- }
- }
- else
- {
- if (ls.nLights > 1)
- vproc->use(vp::diffuse_2light);
- else
- vproc->use(vp::diffuse);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
- LODSphereMesh::VertexProgParams,
- frustum, ri.pixWidth,
- ri.baseTex);
- }
- // Render a specular pass; can't be done in one pass because
- // specular needs to be modulated with a gloss map.
- if (ri.specularColor != Color::Black)
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
- vproc->use(vp::glossMap);
- if (ri.glossTex != NULL)
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- else
- setupTexenvGlossMapAlpha();
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
- frustum, ri.pixWidth,
- ri.glossTex != NULL ? ri.glossTex : ri.baseTex);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- glDisable(GL_BLEND);
- }
- if (ri.nightTex != NULL)
- {
- ri.nightTex->bind();
- #ifdef USE_HDR
- // Scale night light intensity
- #ifdef HDR_COMPRESS
- Color nightColor0(ls.lights[0].color.red() *ri.color.red() *2.f,
- ls.lights[0].color.green()*ri.color.green()*2.f,
- ls.lights[0].color.blue() *ri.color.blue() *2.f,
- ri.nightLightScale); // Modulate brightness with alpha
- #else
- Color nightColor0(ls.lights[0].color.red() *ri.color.red(),
- ls.lights[0].color.green()*ri.color.green(),
- ls.lights[0].color.blue() *ri.color.blue(),
- ri.nightLightScale); // Modulate brightness with alpha
- #endif
- vproc->parameter(vp::DiffuseColor0, nightColor0);
- #endif
- if (ls.nLights > 1)
- {
- #ifdef USE_HDR
- #ifdef HDR_COMPRESS
- Color nightColor1(ls.lights[1].color.red() *ri.color.red() *2.f,
- ls.lights[1].color.green()*ri.color.green()*2.f,
- ls.lights[1].color.blue() *ri.color.blue() *2.f,
- ri.nightLightScale);
- #else
- Color nightColor1(ls.lights[1].color.red() *ri.color.red(),
- ls.lights[1].color.green()*ri.color.green(),
- ls.lights[1].color.blue() *ri.color.blue(),
- ri.nightLightScale);
- #endif
- vproc->parameter(vp::DiffuseColor0, nightColor1);
- #endif
- #ifdef HDR_COMPRESS
- vproc->use(vp::nightLights_2lightHDR);
- #else
- vproc->use(vp::nightLights_2light);
- #endif
- }
- else
- {
- #ifdef HDR_COMPRESS
- vproc->use(vp::nightLightsHDR);
- #else
- vproc->use(vp::nightLights);
- #endif
- }
- #ifdef USE_HDR
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- #else
- setupNightTextureCombine();
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
- #endif
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
- frustum, ri.pixWidth,
- ri.nightTex);
- #ifdef USE_HDR
- vproc->parameter(vp::DiffuseColor0, ls.lights[0].color * ri.color);
- if (ls.nLights > 1)
- vproc->parameter(vp::DiffuseColor1, ls.lights[1].color * ri.color);
- #endif
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- }
- if (ri.overlayTex != NULL)
- {
- ri.overlayTex->bind();
- vproc->use(vp::diffuse);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
- frustum, ri.pixWidth,
- ri.overlayTex);
- glBlendFunc(GL_ONE, GL_ONE);
- }
- vproc->disable();
- }
- static void renderSphere_Combiners_VP(const RenderInfo& ri,
- const LightingState& ls,
- const Frustum& frustum,
- const GLContext& context)
- {
- Texture* textures[4];
- VertexProcessor* vproc = context.getVertexProcessor();
- assert(vproc != NULL);
- if (ri.baseTex == NULL)
- {
- glDisable(GL_TEXTURE_2D);
- }
- else
- {
- glEnable(GL_TEXTURE_2D);
- ri.baseTex->bind();
- }
- // Set up the fog parameters if the haze density is non-zero
- float hazeDensity = ri.hazeColor.alpha();
- #ifdef HDR_COMPRESS
- Color hazeColor(ri.hazeColor.red() * 0.5f,
- ri.hazeColor.green() * 0.5f,
- ri.hazeColor.blue() * 0.5f,
- hazeDensity);
- #else
- Color hazeColor = ri.hazeColor;
- #endif
- if (hazeDensity > 0.0f && !buggyVertexProgramEmulation)
- {
- glEnable(GL_FOG);
- float fogColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
- fogColor[0] = hazeColor.red();
- fogColor[1] = hazeColor.green();
- fogColor[2] = hazeColor.blue();
- glFogfv(GL_FOG_COLOR, fogColor);
- glFogi(GL_FOG_MODE, GL_LINEAR);
- glFogf(GL_FOG_START, 0.0);
- glFogf(GL_FOG_END, 1.0f / hazeDensity);
- }
- vproc->enable();
- vproc->parameter(vp::EyePosition, ri.eyePos_obj);
- setLightParameters_VP(*vproc, ls, ri.color, ri.specularColor);
- vproc->parameter(vp::SpecularExponent, 0.0f, 1.0f, 0.5f, ri.specularPower);
- vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
- vproc->parameter(vp::HazeColor, hazeColor);
- // Don't use a normal map if it's a dxt5nm map--only the GLSL path
- // can handle them.
- if (ri.bumpTex != NULL &&
- (ri.bumpTex->getFormatOptions() & Texture::DXT5NormalMap) == 0)
- {
- if (hazeDensity > 0.0f)
- {
- #ifdef HDR_COMPRESS
- vproc->use(vp::diffuseBumpHazeHDR);
- #else
- vproc->use(vp::diffuseBumpHaze);
- #endif
- }
- else
- {
- #ifdef HDR_COMPRESS
- vproc->use(vp::diffuseBumpHDR);
- #else
- vproc->use(vp::diffuseBump);
- #endif
- }
- SetupCombinersDecalAndBumpMap(*(ri.bumpTex),
- ri.ambientColor * ri.color,
- ri.sunColor * ri.color);
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::Tangents |
- LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
- frustum, ri.pixWidth,
- ri.baseTex, ri.bumpTex);
- DisableCombiners();
- // Render a specular pass
- if (ri.specularColor != Color::Black)
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
- glEnable(GL_COLOR_SUM_EXT);
- vproc->use(vp::specular);
- // Disable ambient and diffuse
- vproc->parameter(vp::AmbientColor, Color::Black);
- vproc->parameter(vp::DiffuseColor0, Color::Black);
- SetupCombinersGlossMap(ri.glossTex != NULL ? GL_TEXTURE0_ARB : 0);
- textures[0] = ri.glossTex != NULL ? ri.glossTex : ri.baseTex;
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
- frustum, ri.pixWidth,
- textures, 1);
- // re-enable diffuse
- vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
- DisableCombiners();
- glDisable(GL_COLOR_SUM_EXT);
- glDisable(GL_BLEND);
- }
- }
- else if (ri.specularColor != Color::Black)
- {
- glEnable(GL_COLOR_SUM_EXT);
- if (ls.nLights > 1)
- vproc->use(vp::specular_2light);
- else
- vproc->use(vp::specular);
- SetupCombinersGlossMapWithFog(ri.glossTex != NULL ? GL_TEXTURE1_ARB : 0);
- unsigned int attributes = LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
- LODSphereMesh::VertexProgParams;
- g_lodSphere->render(context,
- attributes, frustum, ri.pixWidth,
- ri.baseTex, ri.glossTex);
- DisableCombiners();
- glDisable(GL_COLOR_SUM_EXT);
- }
- else
- {
- if (ls.nLights > 1)
- {
- if (hazeDensity > 0.0f)
- vproc->use(vp::diffuseHaze_2light);
- else
- vproc->use(vp::diffuse_2light);
- }
- else
- {
- if (hazeDensity > 0.0f)
- vproc->use(vp::diffuseHaze);
- else
- vproc->use(vp::diffuse);
- }
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
- LODSphereMesh::VertexProgParams,
- frustum, ri.pixWidth,
- ri.baseTex);
- }
- if (hazeDensity > 0.0f)
- glDisable(GL_FOG);
- if (ri.nightTex != NULL)
- {
- ri.nightTex->bind();
- #ifdef USE_HDR
- // Scale night light intensity
- #ifdef HDR_COMPRESS
- Color nightColor0(ls.lights[0].color.red() *ri.color.red() *2.f,
- ls.lights[0].color.green()*ri.color.green()*2.f,
- ls.lights[0].color.blue() *ri.color.blue() *2.f,
- ri.nightLightScale); // Modulate brightness with alpha
- #else
- Color nightColor0(ls.lights[0].color.red() *ri.color.red(),
- ls.lights[0].color.green()*ri.color.green(),
- ls.lights[0].color.blue() *ri.color.blue(),
- ri.nightLightScale); // Modulate brightness with alpha
- #endif
- vproc->parameter(vp::DiffuseColor0, nightColor0);
- #endif
- if (ls.nLights > 1)
- {
- #ifdef USE_HDR
- #ifdef HDR_COMPRESS
- Color nightColor1(ls.lights[1].color.red() *ri.color.red() *2.f,
- ls.lights[1].color.green()*ri.color.green()*2.f,
- ls.lights[1].color.blue() *ri.color.blue() *2.f,
- ri.nightLightScale);
- #else
- Color nightColor1(ls.lights[1].color.red() *ri.color.red(),
- ls.lights[1].color.green()*ri.color.green(),
- ls.lights[1].color.blue() *ri.color.blue(),
- ri.nightLightScale);
- #endif
- vproc->parameter(vp::DiffuseColor0, nightColor1);
- #endif
- #ifdef HDR_COMPRESS
- vproc->use(vp::nightLights_2lightHDR);
- #else
- vproc->use(vp::nightLights_2light);
- #endif
- }
- else
- {
- #ifdef HDR_COMPRESS
- vproc->use(vp::nightLightsHDR);
- #else
- vproc->use(vp::nightLights);
- #endif
- }
- #ifdef USE_HDR
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- #else
- setupNightTextureCombine();
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
- #endif
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
- frustum, ri.pixWidth,
- ri.nightTex);
- #ifdef USE_HDR
- vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
- #endif
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- }
- if (ri.overlayTex != NULL)
- {
- ri.overlayTex->bind();
- vproc->use(vp::diffuse);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
- frustum, ri.pixWidth,
- ri.overlayTex);
- glBlendFunc(GL_ONE, GL_ONE);
- }
- vproc->disable();
- }
- // Render a planet sphere using both fragment and vertex programs
- static void renderSphere_FP_VP(const RenderInfo& ri,
- const Frustum& frustum,
- const GLContext& context)
- {
- Texture* textures[4];
- VertexProcessor* vproc = context.getVertexProcessor();
- FragmentProcessor* fproc = context.getFragmentProcessor();
- assert(vproc != NULL && fproc != NULL);
- if (ri.baseTex == NULL)
- {
- glDisable(GL_TEXTURE_2D);
- }
- else
- {
- glEnable(GL_TEXTURE_2D);
- ri.baseTex->bind();
- }
- // Compute the half angle vector required for specular lighting
- Vec3f halfAngle_obj = ri.eyeDir_obj + ri.sunDir_obj;
- if (halfAngle_obj.length() != 0.0f)
- halfAngle_obj.normalize();
- // Set up the fog parameters if the haze density is non-zero
- float hazeDensity = ri.hazeColor.alpha();
- if (hazeDensity > 0.0f)
- {
- glEnable(GL_FOG);
- float fogColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
- fogColor[0] = ri.hazeColor.red();
- fogColor[1] = ri.hazeColor.green();
- fogColor[2] = ri.hazeColor.blue();
- glFogfv(GL_FOG_COLOR, fogColor);
- glFogi(GL_FOG_MODE, GL_LINEAR);
- glFogf(GL_FOG_START, 0.0);
- glFogf(GL_FOG_END, 1.0f / hazeDensity);
- }
- vproc->enable();
- vproc->parameter(vp::EyePosition, ri.eyePos_obj);
- vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
- vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
- vproc->parameter(vp::SpecularExponent, 0.0f, 1.0f, 0.5f, ri.specularPower);
- vproc->parameter(vp::SpecularColor0, ri.sunColor * ri.specularColor);
- vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
- vproc->parameter(vp::HazeColor, ri.hazeColor);
- if (ri.bumpTex != NULL)
- {
- fproc->enable();
- if (hazeDensity > 0.0f)
- vproc->use(vp::diffuseBumpHaze);
- else
- vproc->use(vp::diffuseBump);
- fproc->use(fp::texDiffuseBump);
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::Tangents |
- LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
- frustum, ri.pixWidth,
- ri.baseTex, ri.bumpTex);
- fproc->disable();
- // Render a specular pass
- if (ri.specularColor != Color::Black)
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
- glEnable(GL_COLOR_SUM_EXT);
- vproc->use(vp::specular);
- // Disable ambient and diffuse
- vproc->parameter(vp::AmbientColor, Color::Black);
- vproc->parameter(vp::DiffuseColor0, Color::Black);
- SetupCombinersGlossMap(ri.glossTex != NULL ? GL_TEXTURE0_ARB : 0);
- textures[0] = ri.glossTex != NULL ? ri.glossTex : ri.baseTex;
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
- frustum, ri.pixWidth,
- textures, 1);
- // re-enable diffuse
- vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
- DisableCombiners();
- glDisable(GL_COLOR_SUM_EXT);
- glDisable(GL_BLEND);
- }
- }
- else if (ri.specularColor != Color::Black)
- {
- fproc->enable();
- if (ri.glossTex == NULL)
- {
- vproc->use(vp::perFragmentSpecularAlpha);
- fproc->use(fp::texSpecularAlpha);
- }
- else
- {
- vproc->use(vp::perFragmentSpecular);
- fproc->use(fp::texSpecular);
- }
- fproc->parameter(fp::DiffuseColor, ri.sunColor * ri.color);
- fproc->parameter(fp::SunDirection, ri.sunDir_obj);
- fproc->parameter(fp::SpecularColor, ri.specularColor);
- fproc->parameter(fp::SpecularExponent, ri.specularPower, 0.0f, 0.0f, 0.0f);
- fproc->parameter(fp::AmbientColor, ri.ambientColor);
- unsigned int attributes = LODSphereMesh::Normals |
- LODSphereMesh::TexCoords0 |
- LODSphereMesh::VertexProgParams;
- g_lodSphere->render(context,
- attributes, frustum, ri.pixWidth,
- ri.baseTex, ri.glossTex);
- fproc->disable();
- }
- else
- {
- fproc->enable();
- if (hazeDensity > 0.0f)
- vproc->use(vp::diffuseHaze);
- else
- vproc->use(vp::diffuse);
- fproc->use(fp::texDiffuse);
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
- LODSphereMesh::VertexProgParams,
- frustum, ri.pixWidth,
- ri.baseTex);
- fproc->disable();
- }
- if (hazeDensity > 0.0f)
- glDisable(GL_FOG);
- if (ri.nightTex != NULL)
- {
- ri.nightTex->bind();
- vproc->use(vp::nightLights);
- setupNightTextureCombine();
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
- frustum, ri.pixWidth,
- ri.nightTex);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- }
- if (ri.overlayTex != NULL)
- {
- ri.overlayTex->bind();
- vproc->use(vp::diffuse);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
- frustum, ri.pixWidth,
- ri.overlayTex);
- glBlendFunc(GL_ONE, GL_ONE);
- }
- vproc->disable();
- }
- static void texGenPlane(GLenum coord, GLenum mode, const Vec4f& plane)
- {
- float f[4];
- f[0] = plane.x; f[1] = plane.y; f[2] = plane.z; f[3] = plane.w;
- glTexGenfv(coord, mode, f);
- }
- static void renderShadowedGeometryDefault(Geometry* geometry,
- const RenderInfo& ri,
- const Frustum& frustum,
- float *sPlane,
- float *tPlane,
- const Vec3f& lightDir,
- bool useShadowMask,
- const GLContext& context)
- {
- glEnable(GL_TEXTURE_GEN_S);
- glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
- glTexGenfv(GL_S, GL_OBJECT_PLANE, sPlane);
- //texGenPlane(GL_S, GL_OBJECT_PLANE, sPlane);
- glEnable(GL_TEXTURE_GEN_T);
- glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
- glTexGenfv(GL_T, GL_OBJECT_PLANE, tPlane);
- if (useShadowMask)
- {
- glx::glActiveTextureARB(GL_TEXTURE1_ARB);
- glEnable(GL_TEXTURE_GEN_S);
- glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
- texGenPlane(GL_S, GL_OBJECT_PLANE,
- Vec4f(lightDir.x, lightDir.y, lightDir.z, 0.5f));
- glx::glActiveTextureARB(GL_TEXTURE0_ARB);
- }
- glColor4f(1, 1, 1, 1);
- glDisable(GL_LIGHTING);
- if (geometry == NULL)
- {
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::Multipass,
- frustum, ri.pixWidth, NULL);
- }
- else
- {
- FixedFunctionRenderContext rc;
- geometry->render(rc);
- }
- glEnable(GL_LIGHTING);
- if (useShadowMask)
- {
- glx::glActiveTextureARB(GL_TEXTURE1_ARB);
- glDisable(GL_TEXTURE_GEN_S);
- glx::glActiveTextureARB(GL_TEXTURE0_ARB);
- }
- glDisable(GL_TEXTURE_GEN_S);
- glDisable(GL_TEXTURE_GEN_T);
- }
- static void renderShadowedGeometryVertexShader(const RenderInfo& ri,
- const Frustum& frustum,
- float* sPlane, float* tPlane,
- Vec3f& lightDir,
- const GLContext& context)
- {
- VertexProcessor* vproc = context.getVertexProcessor();
- assert(vproc != NULL);
- vproc->enable();
- vproc->parameter(vp::LightDirection0, lightDir);
- vproc->parameter(vp::TexGen_S, sPlane);
- vproc->parameter(vp::TexGen_T, tPlane);
- vproc->use(vp::shadowTexture);
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::Multipass, frustum,
- ri.pixWidth, NULL);
- vproc->disable();
- }
- static void renderRings(RingSystem& rings,
- RenderInfo& ri,
- float planetRadius,
- float planetOblateness,
- unsigned int textureResolution,
- bool renderShadow,
- const GLContext& context,
- unsigned int nSections)
- {
- float inner = rings.innerRadius / planetRadius;
- float outer = rings.outerRadius / planetRadius;
- // Ring Illumination:
- // Since a ring system is composed of millions of individual
- // particles, it's not at all realistic to model it as a flat
- // Lambertian surface. We'll approximate the llumination
- // function by assuming that the ring system contains Lambertian
- // particles, and that the brightness at some point in the ring
- // system is proportional to the illuminated fraction of a
- // particle there. In fact, we'll simplify things further and
- // set the illumination of the entire ring system to the same
- // value, computing the illuminated fraction of a hypothetical
- // particle located at the center of the planet. This
- // approximation breaks down when you get close to the planet.
- float ringIllumination = 0.0f;
- {
- float illumFraction = (1.0f + ri.eyeDir_obj * ri.sunDir_obj) / 2.0f;
- // Just use the illuminated fraction for now . . .
- ringIllumination = illumFraction;
- }
- GLContext::VertexPath vpath = context.getVertexPath();
- VertexProcessor* vproc = context.getVertexProcessor();
- FragmentProcessor* fproc = context.getFragmentProcessor();
- if (vproc != NULL)
- {
- vproc->enable();
- vproc->use(vp::ringIllum);
- vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
- vproc->parameter(vp::DiffuseColor0, ri.sunColor * rings.color);
- vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
- vproc->parameter(vp::Constant0, Vec3f(0, 0.5, 1.0));
- }
- // If we have multi-texture support, we'll use the second texture unit
- // to render the shadow of the planet on the rings. This is a bit of
- // a hack, and assumes that the planet is ellipsoidal in shape,
- // and only works for a planet illuminated by a single sun where the
- // distance to the sun is very large relative to its diameter.
- if (renderShadow)
- {
- glx::glActiveTextureARB(GL_TEXTURE1_ARB);
- glEnable(GL_TEXTURE_2D);
- shadowTex->bind();
- float sPlane[4] = { 0, 0, 0, 0.5f };
- float tPlane[4] = { 0, 0, 0, 0.5f };
- // Compute the projection vectors based on the sun direction.
- // I'm being a little careless here--if the sun direction lies
- // along the y-axis, this will fail. It's unlikely that a
- // planet would ever orbit underneath its sun (an orbital
- // inclination of 90 degrees), but this should be made
- // more robust anyway.
- Vec3f axis = Vec3f(0, 1, 0) ^ ri.sunDir_obj;
- float cosAngle = Vec3f(0.0f, 1.0f, 0.0f) * ri.sunDir_obj;
- /*float angle = (float) acos(cosAngle); Unused*/
- axis.normalize();
- float sScale = 1.0f;
- float tScale = 1.0f;
- if (fproc == NULL)
- {
- // When fragment programs aren't used, we render shadows with circular
- // textures. We scale up the texture slightly to account for the
- // padding pixels near the texture borders.
- sScale *= ShadowTextureScale;
- tScale *= ShadowTextureScale;
- }
- if (planetOblateness != 0.0f)
- {
- // For oblate planets, the size of the shadow volume will vary based
- // on the light direction.
- // A vertical slice of the planet is an ellipse
- float a = 1.0f; // semimajor axis
- float b = a * (1.0f - planetOblateness); // semiminor axis
- float ecc2 = 1.0f - (b * b) / (a * a); // square of eccentricity
- // Calculate the radius of the ellipse at the incident angle of the
- // light on the ring plane + 90 degrees.
- float r = a * (float) sqrt((1.0f - ecc2) /
- (1.0f - ecc2 * square(cosAngle)));
- tScale *= a / r;
- }
- // The s axis is perpendicular to the shadow axis in the plane of the
- // of the rings, and the t axis completes the orthonormal basis.
- Vec3f sAxis = axis * 0.5f;
- Vec3f tAxis = (axis ^ ri.sunDir_obj) * 0.5f * tScale;
- sPlane[0] = sAxis.x; sPlane[1] = sAxis.y; sPlane[2] = sAxis.z;
- tPlane[0] = tAxis.x; tPlane[1] = tAxis.y; tPlane[2] = tAxis.z;
- if (vproc != NULL)
- {
- vproc->parameter(vp::TexGen_S, sPlane);
- vproc->parameter(vp::TexGen_T, tPlane);
- }
- else
- {
- glEnable(GL_TEXTURE_GEN_S);
- glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
- glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
- glEnable(GL_TEXTURE_GEN_T);
- glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
- glTexGenfv(GL_T, GL_EYE_PLANE, tPlane);
- }
- glx::glActiveTextureARB(GL_TEXTURE0_ARB);
- if (fproc != NULL)
- {
- float r0 = 0.24f;
- float r1 = 0.25f;
- float bias = 1.0f / (1.0f - r1 / r0);
- float scale = -bias / r0;
- fproc->enable();
- fproc->use(fp::sphereShadowOnRings);
- fproc->parameter(fp::ShadowParams0, scale, bias, 0.0f, 0.0f);
- fproc->parameter(fp::AmbientColor, ri.ambientColor * ri.color);
- }
- }
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- Texture* ringsTex = rings.texture.find(textureResolution);
- if (ringsTex != NULL)
- {
- glEnable(GL_TEXTURE_2D);
- ringsTex->bind();
- }
- else
- {
- glDisable(GL_TEXTURE_2D);
- }
- // Perform our own lighting for the rings.
- // TODO: Don't forget about light source color (required when we
- // paying attention to star color.)
- if (vpath == GLContext::VPath_Basic)
- {
- glDisable(GL_LIGHTING);
- Vec3f litColor(rings.color.red(), rings.color.green(), rings.color.blue());
- litColor = litColor * ringIllumination +
- Vec3f(ri.ambientColor.red(), ri.ambientColor.green(),
- ri.ambientColor.blue());
- glColor4f(litColor.x, litColor.y, litColor.z, 1.0f);
- }
- // This gets tricky . . . we render the rings in two parts. One
- // part is potentially shadowed by the planet, and we need to
- // render that part with the projected shadow texture enabled.
- // The other part isn't shadowed, but will appear so if we don't
- // first disable the shadow texture. The problem is that the
- // shadow texture will affect anything along the line between the
- // sun and the planet, regardless of whether it's in front or
- // behind the planet.
- // Compute the angle of the sun projected on the ring plane
- float sunAngle = (float) atan2(ri.sunDir_obj.z, ri.sunDir_obj.x);
- // If there's a fragment program, it will handle the ambient term--make
- // sure that we don't add it both in the fragment and vertex programs.
- if (vproc != NULL && fproc != NULL)
- glAmbientLightColor(Color::Black);
- renderRingSystem(inner, outer,
- (float) (sunAngle + PI / 2),
- (float) (sunAngle + 3 * PI / 2),
- nSections / 2);
- renderRingSystem(inner, outer,
- (float) (sunAngle + 3 * PI / 2),
- (float) (sunAngle + PI / 2),
- nSections / 2);
- if (vproc != NULL && fproc != NULL)
- glAmbientLightColor(ri.ambientColor * ri.color);
- // Disable the second texture unit if it was used
- if (renderShadow)
- {
- glx::glActiveTextureARB(GL_TEXTURE1_ARB);
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_TEXTURE_GEN_S);
- glDisable(GL_TEXTURE_GEN_T);
- glx::glActiveTextureARB(GL_TEXTURE0_ARB);
- if (fproc != NULL)
- fproc->disable();
- }
- // Render the unshadowed side
- renderRingSystem(inner, outer,
- (float) (sunAngle - PI / 2),
- (float) (sunAngle + PI / 2),
- nSections / 2);
- renderRingSystem(inner, outer,
- (float) (sunAngle + PI / 2),
- (float) (sunAngle - PI / 2),
- nSections / 2);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- if (vproc != NULL)
- vproc->disable();
- }
- static void
- renderEclipseShadows(Geometry* geometry,
- vector<EclipseShadow>& eclipseShadows,
- RenderInfo& ri,
- float planetRadius,
- Mat4f& planetMat,
- Frustum& viewFrustum,
- const GLContext& context)
- {
- // Eclipse shadows on mesh objects aren't working yet.
- if (geometry != NULL)
- return;
- for (vector<EclipseShadow>::iterator iter = eclipseShadows.begin();
- iter != eclipseShadows.end(); iter++)
- {
- EclipseShadow shadow = *iter;
- #ifdef DEBUG_ECLIPSE_SHADOWS
- // Eclipse debugging: render the central axis of the eclipse
- // shadow volume.
- glDisable(GL_TEXTURE_2D);
- glColor4f(1, 0, 0, 1);
- Point3f blorp = shadow.origin * planetMat;
- Vec3f blah = shadow.direction * planetMat;
- blorp.x /= planetRadius; blorp.y /= planetRadius; blorp.z /= planetRadius;
- float foo = blorp.distanceFromOrigin();
- glBegin(GL_LINES);
- glVertex(blorp);
- glVertex(blorp + foo * blah);
- glEnd();
- glEnable(GL_TEXTURE_2D);
- #endif
- // Determine which eclipse shadow texture to use. This is only
- // a very rough approximation to reality. Since there are an
- // infinite number of possible eclipse volumes, what we should be
- // doing is generating the eclipse textures on the fly using
- // render-to-texture. But for now, we'll just choose from a fixed
- // set of eclipse shadow textures based on the relative size of
- // the umbra and penumbra.
- Texture* eclipseTex = NULL;
- float umbra = shadow.umbraRadius / shadow.penumbraRadius;
- if (umbra < 0.1f)
- eclipseTex = eclipseShadowTextures[0];
- else if (umbra < 0.35f)
- eclipseTex = eclipseShadowTextures[1];
- else if (umbra < 0.6f)
- eclipseTex = eclipseShadowTextures[2];
- else if (umbra < 0.9f)
- eclipseTex = eclipseShadowTextures[3];
- else
- eclipseTex = shadowTex;
- // Compute the transformation to use for generating texture
- // coordinates from the object vertices.
- Point3f origin = shadow.origin * planetMat;
- Vec3f dir = shadow.direction * planetMat;
- float scale = planetRadius / shadow.penumbraRadius;
- Vec3f axis = Vec3f(0, 1, 0) ^ dir;
- float angle = (float) acos(Vec3f(0, 1, 0) * dir);
- axis.normalize();
- Mat4f mat = Mat4f::rotation(axis, -angle);
- Vec3f sAxis = Vec3f(0.5f * scale, 0, 0) * mat;
- Vec3f tAxis = Vec3f(0, 0, 0.5f * scale) * mat;
- float sPlane[4] = { 0, 0, 0, 0 };
- float tPlane[4] = { 0, 0, 0, 0 };
- sPlane[0] = sAxis.x; sPlane[1] = sAxis.y; sPlane[2] = sAxis.z;
- tPlane[0] = tAxis.x; tPlane[1] = tAxis.y; tPlane[2] = tAxis.z;
- sPlane[3] = (Point3f(0, 0, 0) - origin) * sAxis / planetRadius + 0.5f;
- tPlane[3] = (Point3f(0, 0, 0) - origin) * tAxis / planetRadius + 0.5f;
- // TODO: Multiple eclipse shadows should be rendered in a single
- // pass using multitexture.
- if (eclipseTex != NULL)
- eclipseTex->bind();
- // shadowMaskTexture->bind();
- glEnable(GL_BLEND);
- glBlendFunc(GL_ZERO, GL_SRC_COLOR);
- // If the ambient light level is greater than zero, reduce the
- // darkness of the shadows.
- if (ri.useTexEnvCombine)
- {
- float color[4] = { ri.ambientColor.red(), ri.ambientColor.green(),
- ri.ambientColor.blue(), 1.0f };
- glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_CONSTANT_EXT);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
- // The second texture unit has the shadow 'mask'
- glx::glActiveTextureARB(GL_TEXTURE1_ARB);
- glEnable(GL_TEXTURE_2D);
- shadowMaskTexture->bind();
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
- glx::glActiveTextureARB(GL_TEXTURE0_ARB);
- }
- // Since invariance between nVidia's vertex programs and the
- // standard transformation pipeline isn't guaranteed, we have to
- // make sure to use the same transformation engine on subsequent
- // rendering passes as we did on the initial one.
- if (context.getVertexPath() != GLContext::VPath_Basic && geometry == NULL)
- {
- renderShadowedGeometryVertexShader(ri, viewFrustum,
- sPlane, tPlane,
- dir,
- context);
- }
- else
- {
- renderShadowedGeometryDefault(geometry, ri, viewFrustum,
- sPlane, tPlane,
- dir,
- ri.useTexEnvCombine,
- context);
- }
- if (ri.useTexEnvCombine)
- {
- // Disable second texture unit
- glx::glActiveTextureARB(GL_TEXTURE1_ARB);
- glDisable(GL_TEXTURE_2D);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- glx::glActiveTextureARB(GL_TEXTURE0_ARB);
- float color[4] = { 0, 0, 0, 0 };
- glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- }
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- glDisable(GL_BLEND);
- }
- }
- static void
- renderEclipseShadows_Shaders(Geometry* geometry,
- vector<EclipseShadow>& eclipseShadows,
- RenderInfo& ri,
- float planetRadius,
- Mat4f& planetMat,
- Frustum& viewFrustum,
- const GLContext& context)
- {
- // Eclipse shadows on mesh objects aren't working yet.
- if (geometry != NULL)
- return;
- glEnable(GL_TEXTURE_2D);
- penumbraFunctionTexture->bind();
- glEnable(GL_BLEND);
- glBlendFunc(GL_ZERO, GL_SRC_COLOR);
- float sPlanes[4][4];
- float tPlanes[4][4];
- float shadowParams[4][4];
- int n = 0;
- for (vector<EclipseShadow>::iterator iter = eclipseShadows.begin();
- iter != eclipseShadows.end() && n < 4; iter++, n++)
- {
- EclipseShadow shadow = *iter;
- float R2 = 0.25f;
- float umbra = shadow.umbraRadius / shadow.penumbraRadius;
- umbra = umbra * umbra;
- if (umbra < 0.0001f)
- umbra = 0.0001f;
- else if (umbra > 0.99f)
- umbra = 0.99f;
- float umbraRadius = R2 * umbra;
- float penumbraRadius = R2;
- float shadowBias = 1.0f / (1.0f - penumbraRadius / umbraRadius);
- float shadowScale = -shadowBias / umbraRadius;
- shadowParams[n][0] = shadowScale;
- shadowParams[n][1] = shadowBias;
- shadowParams[n][2] = 0.0f;
- shadowParams[n][3] = 0.0f;
- // Compute the transformation to use for generating texture
- // coordinates from the object vertices.
- Point3f origin = shadow.origin * planetMat;
- Vec3f dir = shadow.direction * planetMat;
- float scale = planetRadius / shadow.penumbraRadius;
- Vec3f axis = Vec3f(0, 1, 0) ^ dir;
- float angle = (float) acos(Vec3f(0, 1, 0) * dir);
- axis.normalize();
- Mat4f mat = Mat4f::rotation(axis, -angle);
- Vec3f sAxis = Vec3f(0.5f * scale, 0, 0) * mat;
- Vec3f tAxis = Vec3f(0, 0, 0.5f * scale) * mat;
- sPlanes[n][0] = sAxis.x;
- sPlanes[n][1] = sAxis.y;
- sPlanes[n][2] = sAxis.z;
- sPlanes[n][3] = (Point3f(0, 0, 0) - origin) * sAxis / planetRadius + 0.5f;
- tPlanes[n][0] = tAxis.x;
- tPlanes[n][1] = tAxis.y;
- tPlanes[n][2] = tAxis.z;
- tPlanes[n][3] = (Point3f(0, 0, 0) - origin) * tAxis / planetRadius + 0.5f;
- }
- VertexProcessor* vproc = context.getVertexProcessor();
- FragmentProcessor* fproc = context.getFragmentProcessor();
- vproc->enable();
- vproc->use(vp::multiShadow);
- fproc->enable();
- if (n == 1)
- fproc->use(fp::eclipseShadow1);
- else
- fproc->use(fp::eclipseShadow2);
- fproc->parameter(fp::ShadowParams0, shadowParams[0]);
- vproc->parameter(vp::TexGen_S, sPlanes[0]);
- vproc->parameter(vp::TexGen_T, tPlanes[0]);
- if (n >= 2)
- {
- fproc->parameter(fp::ShadowParams1, shadowParams[1]);
- vproc->parameter(vp::TexGen_S2, sPlanes[1]);
- vproc->parameter(vp::TexGen_T2, tPlanes[1]);
- }
- if (n >= 3)
- {
- //fproc->parameter(fp::ShadowParams2, shadowParams[2]);
- vproc->parameter(vp::TexGen_S3, sPlanes[2]);
- vproc->parameter(vp::TexGen_T3, tPlanes[2]);
- }
- if (n >= 4)
- {
- //fproc->parameter(fp::ShadowParams3, shadowParams[3]);
- vproc->parameter(vp::TexGen_S4, sPlanes[3]);
- vproc->parameter(vp::TexGen_T4, tPlanes[3]);
- }
- //vproc->parameter(vp::LightDirection0, lightDir);
- g_lodSphere->render(context,
- LODSphereMesh::Normals | LODSphereMesh::Multipass,
- viewFrustum,
- ri.pixWidth, NULL);
- vproc->disable();
- fproc->disable();
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- glDisable(GL_BLEND);
- }
- static void
- renderRingShadowsVS(Geometry* /*geometry*/, //TODO: Remove unused parameters??
- const RingSystem& rings,
- const Vec3f& /*sunDir*/,
- RenderInfo& ri,
- float planetRadius,
- float /*oblateness*/,
- Mat4f& /*planetMat*/,
- Frustum& viewFrustum,
- const GLContext& context)
- {
- // Compute the transformation to use for generating texture
- // coordinates from the object vertices.
- float ringWidth = rings.outerRadius - rings.innerRadius;
- float s = ri.sunDir_obj.y;
- float scale = (abs(s) < 0.001f) ? 1000.0f : 1.0f / s;
- if (abs(s) > 1.0f - 1.0e-4f)
- {
- // Planet is illuminated almost directly from above, so
- // no ring shadow will be cast on the planet. Conveniently
- // avoids some potential division by zero when ray-casting.
- return;
- }
- glEnable(GL_BLEND);
- glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
- // If the ambient light level is greater than zero, reduce the
- // darkness of the shadows.
- float color[4] = { ri.ambientColor.red(), ri.ambientColor.green(),
- ri.ambientColor.blue(), 1.0f };
- glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_CONSTANT_EXT);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
- // Tweak the texture--set clamp to border and a border color with
- // a zero alpha. If a graphics card doesn't support clamp to border,
- // it doesn't get to play. It's possible to get reasonable behavior
- // by turning off mipmaps and assuming transparent rows of pixels for
- // the top and bottom of the ring textures . . . maybe later.
- float bc[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
- glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bc);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB);
- // Ring shadows look strange if they're always completely black. Vary
- // the darkness of the shadow based on the angle between the sun and the
- // ring plane. There's some justification for this--the larger the angle
- // between the sun and the ring plane (normal), the more ring material
- // there is to travel through.
- //float alpha = (1.0f - abs(ri.sunDir_obj.y)) * 1.0f;
- // ...but, images from Cassini are showing very dark ring shadows, so we'll
- // go with that.
- float alpha = 1.0f;
- VertexProcessor* vproc = context.getVertexProcessor();
- assert(vproc != NULL);
- vproc->enable();
- vproc->use(vp::ringShadow);
- vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
- vproc->parameter(vp::DiffuseColor0, 1, 1, 1, alpha); // color = white
- vproc->parameter(vp::TexGen_S,
- rings.innerRadius / planetRadius,
- 1.0f / (ringWidth / planetRadius),
- 0.0f, 0.5f);
- vproc->parameter(vp::TexGen_T, scale, 0, 0, 0);
- g_lodSphere->render(context, LODSphereMesh::Multipass,
- viewFrustum, ri.pixWidth, NULL);
- vproc->disable();
- // Restore the texture combiners
- if (ri.useTexEnvCombine)
- {
- float color[4] = { 0, 0, 0, 0 };
- glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- }
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- glDisable(GL_BLEND);
- }
- void Renderer::renderLocations(const Body& body,
- const Vec3d& bodyPosition,
- const Quatd& bodyOrientation)
- {
- const vector<Location*>* locations = body.getLocations();
- if (locations == NULL)
- return;
-
- Vec3f semiAxes = body.getSemiAxes();
-
- float nearDist = getNearPlaneDistance();
- double boundingRadius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
- Vec3d bodyCenter(bodyPosition.x, bodyPosition.y, bodyPosition.z);
- Vec3d viewRayOrigin = -bodyCenter * (~bodyOrientation).toMatrix3();
- double labelOffset = 0.0001;
- Vec3f vn = Vec3f(0.0f, 0.0f, -1.0f) * getCameraOrientation().toMatrix3();
- Vec3d viewNormal(vn.x, vn.y, vn.z);
-
- Ellipsoidd bodyEllipsoid(Vec3d(semiAxes.x, semiAxes.y, semiAxes.z));
-
- Mat3d bodyMatrix = bodyOrientation.toMatrix3();
-
- for (vector<Location*>::const_iterator iter = locations->begin();
- iter != locations->end(); iter++)
- {
- const Location& location = **iter;
-
- if (location.getFeatureType() & locationFilter)
- {
- // Get the position of the location with respect to the planet center
- Vec3f ppos = location.getPosition();
-
- // Compute the bodycentric position of the location
- Vec3d locPos = Vec3d(ppos.x, ppos.y, ppos.z);
-
- // Get the planetocentric position of the label. Add a slight scale factor
- // to keep the point from being exactly on the surface.
- Vec3d pcLabelPos = locPos * (1.0 + labelOffset);
-
- // Get the camera space label position
- Vec3d labelPos = bodyCenter + locPos * bodyMatrix;
-
- float effSize = location.getImportance();
- if (effSize < 0.0f)
- effSize = location.getSize();
-
- float pixSize = effSize / (float) (labelPos.length() * pixelSize);
-
- if (pixSize > minFeatureSize && labelPos * viewNormal > 0.0)
- {
- // Labels on non-ellipsoidal bodies need special handling; the
- // ellipsoid visibility test will always fail for them, since they
- // will lie on the surface of the mesh, which is inside the
- // the bounding ellipsoid. The following code projects location positions
- // onto the bounding sphere.
- if (!body.isEllipsoid())
- {
- double r = locPos.length();
- if (r < boundingRadius)
- pcLabelPos = locPos * (boundingRadius * 1.01 / r);
- }
-
- double t = 0.0;
-
- // Test for an intersection of the eye-to-location ray with
- // the planet ellipsoid. If we hit the planet first, then
- // the label is obscured by the planet. An exact calculation
- // for irregular objects would be too expensive, and the
- // ellipsoid approximation works reasonably well for them.
- Ray3d testRay(Point3d(viewRayOrigin.x, viewRayOrigin.y, viewRayOrigin.z),
- pcLabelPos - viewRayOrigin);
- bool hit = testIntersection(testRay, bodyEllipsoid, t);
- Vec3d blah = labelPos - viewRayOrigin;
- if (!hit || t >= 1.0)
- {
- // Calculate the intersection of the eye-to-label ray with the plane perpendicular to
- // the view normal that touches the front of the object's bounding sphere
- double planetZ = viewNormal * bodyCenter - boundingRadius;
- if (planetZ < -nearDist * 1.001)
- planetZ = -nearDist * 1.001;
- double z = viewNormal * labelPos;
- labelPos *= planetZ / z;
-
- uint32 featureType = location.getFeatureType();
- MarkerRepresentation* locationMarker = NULL;
- if (featureType & Location::City)
- locationMarker = &cityRep;
- else if (featureType & (Location::LandingSite | Location::Observatory))
- locationMarker = &observatoryRep;
- else if (featureType & (Location::Crater | Location::Patera))
- locationMarker = &craterRep;
- else if (featureType & (Location::Mons | Location::Tholus))
- locationMarker = &mountainRep;
- else if (featureType & (Location::EruptiveCenter))
- locationMarker = &genericLocationRep;
- Color labelColor = location.isLabelColorOverridden() ? location.getLabelColor() : LocationLabelColor;
- addObjectAnnotation(locationMarker,
- location.getName(true),
- labelColor,
- Point3f((float) labelPos.x, (float) labelPos.y, (float) labelPos.z));
- }
- }
- }
- }
- }
- // Estimate the fraction of light reflected from a sphere that
- // reaches an object at the specified position relative to that
- // sphere.
- //
- // This is function is just a rough approximation to the actual
- // lighting integral, but it reproduces the important features
- // of the way that phase and distance affect reflected light:
- // - Higher phase angles mean less reflected light
- // - The closer an object is to the reflector, the less
- // area of the reflector that is visible.
- //
- // We approximate the reflected light by taking a weighted average
- // of the reflected light at three points on the reflector: the
- // light receiver's sub-point, and the two horizon points in the
- // plane of the light vector and receiver-to-reflector vector.
- //
- // The reflecting object is assumed to be spherical and perfectly
- // Lambertian.
- static float
- estimateReflectedLightFraction(const Vec3d& toSun,
- const Vec3d& toObject,
- float radius)
- {
- // Theta is half the arc length visible to the reflector
- double d = toObject.length();
- float cosTheta = (float) (radius / d);
- if (cosTheta > 0.999f)
- cosTheta = 0.999f;
- // Phi is the angle between the light vector and receiver-to-reflector vector.
- // cos(phi) is thus the illumination at the sub-point. The horizon points are
- // at phi+theta and phi-theta.
- float cosPhi = (float) ((toSun * toObject) / (d * toSun.length()));
- // Use a trigonometric identity to compute cos(phi +/- theta):
- // cos(phi + theta) = cos(phi) * cos(theta) - sin(phi) * sin(theta)
- // s = sin(phi) * sin(theta)
- float s = (float) sqrt((1.0f - cosPhi * cosPhi) * (1.0f - cosTheta * cosTheta));
-
- float cosPhi1 = cosPhi * cosTheta - s; // cos(phi + theta)
- float cosPhi2 = cosPhi * cosTheta + s; // cos(phi - theta)
- // Calculate a weighted average of illumination at the three points
- return (2.0f * max(cosPhi, 0.0f) + max(cosPhi1, 0.0f) + max(cosPhi2, 0.0f)) * 0.25f;
- }
- static void
- setupObjectLighting(const vector<LightSource>& suns,
- const vector<SecondaryIlluminator>& secondaryIlluminators,
- const Quatf& objOrientation,
- const Vec3f& objScale,
- const Point3f& objPosition_eye,
- bool isNormalized,
- #ifdef USE_HDR
- const float faintestMag,
- const float saturationMag,
- const float appMag,
- #endif
- LightingState& ls)
- {
- unsigned int nLights = min(MaxLights, (unsigned int) suns.size());
- if (nLights == 0)
- return;
- #ifdef USE_HDR
- float exposureFactor = (faintestMag - appMag)/(faintestMag - saturationMag + 0.001f);
- #endif
- unsigned int i;
- for (i = 0; i < nLights; i++)
- {
- Vec3d dir = suns[i].position - Vec3d(objPosition_eye.x, objPosition_eye.y, objPosition_eye.z);
- ls.lights[i].direction_eye =
- Vec3f((float) dir.x, (float) dir.y, (float) dir.z);
- float distance = ls.lights[i].direction_eye.length();
- ls.lights[i].direction_eye *= 1.0f / distance;
- distance = astro::kilometersToAU((float) dir.length());
- ls.lights[i].irradiance = suns[i].luminosity / (distance * distance);
- ls.lights[i].color = suns[i].color;
- // Store the position and apparent size because we'll need them for
- // testing for eclipses.
- ls.lights[i].position = dir;
- ls.lights[i].apparentSize = (float) (suns[i].radius / dir.length());
- ls.lights[i].castsShadows = true;
- }
- // Include effects of secondary illumination (i.e. planetshine)
- if (!secondaryIlluminators.empty() && i < MaxLights - 1)
- {
- float maxIrr = 0.0f;
- unsigned int maxIrrSource = 0;
- Vec3d objpos(objPosition_eye.x, objPosition_eye.y, objPosition_eye.z);
- // Only account for light from the brightest secondary source
- for (vector<SecondaryIlluminator>::const_iterator iter = secondaryIlluminators.begin();
- iter != secondaryIlluminators.end(); iter++)
- {
- Vec3d toIllum = iter->position_v - objpos; // reflector-to-object vector
- float distSquared = (float) toIllum.lengthSquared() / square(iter->radius);
- if (distSquared > 0.01f)
- {
- // Irradiance falls off with distance^2
- float irr = iter->reflectedIrradiance / distSquared;
- // Phase effects will always leave the irradiance unaffected or reduce it;
- // don't bother calculating them if we've already found a brighter secondary
- // source.
- if (irr > maxIrr)
- {
- // Account for the phase
- Vec3d toSun = objpos - suns[0].position;
- irr *= estimateReflectedLightFraction(toSun, toIllum, iter->radius);
- if (irr > maxIrr)
- {
- maxIrr = irr;
- maxIrrSource = iter - secondaryIlluminators.begin();
- }
- }
- }
- }
- #if DEBUG_SECONDARY_ILLUMINATION
- clog << "maxIrr = " << maxIrr << ", "
- << secondaryIlluminators[maxIrrSource].body->getName() << ", "
- << secondaryIlluminators[maxIrrSource].reflectedIrradiance << endl;
- #endif
- if (maxIrr > 0.0f)
- {
- Vec3d toIllum = secondaryIlluminators[maxIrrSource].position_v - objpos;
- ls.lights[i].direction_eye = Vec3f((float) toIllum.x, (float) toIllum.y, (float) toIllum.z);
- ls.lights[i].direction_eye.normalize();
- ls.lights[i].irradiance = maxIrr;
- ls.lights[i].color = secondaryIlluminators[maxIrrSource].body->getSurface().color;
- ls.lights[i].apparentSize = 0.0f;
- ls.lights[i].castsShadows = false;
- i++;
- nLights++;
- }
- }
- // Sort light sources by brightness. Light zero should always be the
- // brightest. Optimize common cases of one and two lights.
- if (nLights == 2)
- {
- if (ls.lights[0].irradiance < ls.lights[1].irradiance)
- swap(ls.lights[0], ls.lights[1]);
- }
- else if (nLights > 2)
- {
- sort(ls.lights, ls.lights + nLights, LightIrradiancePredicate());
- }
- // Compute the total irradiance
- float totalIrradiance = 0.0f;
- for (i = 0; i < nLights; i++)
- totalIrradiance += ls.lights[i].irradiance;
- // Compute a gamma factor to make dim light sources visible. This is
- // intended to approximate what we see with our eyes--for example,
- // Earth-shine is visible on the night side of the Moon, even though
- // the amount of reflected light from the Earth is 1/10000 of what
- // the Moon receives directly from the Sun.
- //
- // TODO: Skip this step when high dynamic range rendering to floating point
- // buffers is enabled.
- float minVisibleFraction = 1.0f / 10000.0f;
- float minDisplayableValue = 1.0f / 255.0f;
- float gamma = (float) (log(minDisplayableValue) / log(minVisibleFraction));
- float minVisibleIrradiance = minVisibleFraction * totalIrradiance;
- Mat3f m = (~objOrientation).toMatrix3();
- // Gamma scale and normalize the light sources; cull light sources that
- // aren't bright enough to contribute the final pixels rendered into the
- // frame buffer.
- ls.nLights = 0;
- for (i = 0; i < nLights && ls.lights[i].irradiance > minVisibleIrradiance; i++)
- {
- #ifdef USE_HDR
- ls.lights[i].irradiance *= exposureFactor / totalIrradiance;
- #else
- ls.lights[i].irradiance =
- (float) pow(ls.lights[i].irradiance / totalIrradiance, gamma);
- #endif
- // Compute the direction of the light in object space
- ls.lights[i].direction_obj = ls.lights[i].direction_eye * m;
- ls.nLights++;
- }
- ls.eyePos_obj = Point3f(-objPosition_eye.x / objScale.x,
- -objPosition_eye.y / objScale.y,
- -objPosition_eye.z / objScale.z) * m;
- ls.eyeDir_obj = (Point3f(0.0f, 0.0f, 0.0f) - objPosition_eye) * m;
- ls.eyeDir_obj.normalize();
- // When the camera is very far from the object, some view-dependent
- // calculations in the shaders can exhibit precision problems. This
- // occurs with atmospheres, where the scale height of the atmosphere
- // is very small relative to the planet radius. To address the problem,
- // we'll clamp the eye distance to some maximum value. The effect of the
- // adjustment should be impercetible, since at large distances rays from
- // the camera to object vertices are all nearly parallel to each other.
- float eyeFromCenterDistance = ls.eyePos_obj.distanceFromOrigin();
- if (eyeFromCenterDistance > 100.0f && isNormalized)
- {
- float s = 100.0f / eyeFromCenterDistance;
- ls.eyePos_obj.x *= s;
- ls.eyePos_obj.y *= s;
- ls.eyePos_obj.z *= s;
- }
- ls.ambientColor = Vec3f(0.0f, 0.0f, 0.0f);
- #if 0
- // Old code: linear scaling approach
- // After sorting, the first light is always the brightest
- float maxIrradiance = ls.lights[0].irradiance;
- // Normalize the brightnesses of the light sources.
- // TODO: Investigate logarithmic functions for scaling light brightness, to
- // better simulate what the human eye would see.
- ls.nLights = 0;
- for (i = 0; i < nLights; i++)
- {
- ls.lights[i].irradiance /= maxIrradiance;
- // Cull light sources that don't contribute significantly (less than
- // the resolution of an 8-bit color channel.)
- if (ls.lights[i].irradiance < 1.0f / 255.0f)
- break;
- // Compute the direction of the light in object space
- ls.lights[i].direction_obj = ls.lights[i].direction_eye * m;
- ls.nLights++;
- }
- #endif
- }
- void Renderer::renderObject(Point3f pos,
- float distance,
- double now,
- Quatf cameraOrientation,
- float nearPlaneDistance,
- float farPlaneDistance,
- RenderProperties& obj,
- const LightingState& ls)
- {
- RenderInfo ri;
- float altitude = distance - obj.radius;
- float discSizeInPixels = obj.radius /
- (max(nearPlaneDistance, altitude) * pixelSize);
- ri.sunDir_eye = Vec3f(0.0f, 1.0f, 0.0f);
- ri.sunDir_obj = Vec3f(0.0f, 1.0f, 0.0f);
- ri.sunColor = Color(0.0f, 0.0f, 0.0f);
- if (ls.nLights > 0)
- {
- ri.sunDir_eye = ls.lights[0].direction_eye;
- ri.sunDir_obj = ls.lights[0].direction_obj;
- ri.sunColor = ls.lights[0].color;// * ls.lights[0].intensity;
- }
- // Enable depth buffering
- glEnable(GL_DEPTH_TEST);
- glDepthMask(GL_TRUE);
- glDisable(GL_BLEND);
- // Get the object's geometry; NULL indicates that object is an
- // ellipsoid.
- Geometry* geometry = NULL;
- if (obj.geometry != InvalidResource)
- {
- // This is a model loaded from a file
- geometry = GetGeometryManager()->find(obj.geometry);
- }
- // Get the textures . . .
- if (obj.surface->baseTexture.tex[textureResolution] != InvalidResource)
- ri.baseTex = obj.surface->baseTexture.find(textureResolution);
- if ((obj.surface->appearanceFlags & Surface::ApplyBumpMap) != 0 &&
- context->bumpMappingSupported() &&
- obj.surface->bumpTexture.tex[textureResolution] != InvalidResource)
- ri.bumpTex = obj.surface->bumpTexture.find(textureResolution);
- if ((obj.surface->appearanceFlags & Surface::ApplyNightMap) != 0 &&
- (renderFlags & ShowNightMaps) != 0)
- ri.nightTex = obj.surface->nightTexture.find(textureResolution);
- if ((obj.surface->appearanceFlags & Surface::SeparateSpecularMap) != 0)
- ri.glossTex = obj.surface->specularTexture.find(textureResolution);
- if ((obj.surface->appearanceFlags & Surface::ApplyOverlay) != 0)
- ri.overlayTex = obj.surface->overlayTexture.find(textureResolution);
- // Apply the modelview transform for the object
- glPushMatrix();
- glTranslate(pos);
- glRotate(~obj.orientation);
- // Scaling will be nonuniform for nonspherical planets. As long as the
- // deviation from spherical isn't too large, the nonuniform scale factor
- // shouldn't mess up the lighting calculations enough to be noticeable
- // (and we turn on renormalization anyhow, which most graphics cards
- // support.)
- float radius = obj.radius;
- Vec3f scaleFactors;
- float geometryScale;
- if (geometry == NULL || geometry->isNormalized())
- {
- geometryScale = obj.radius;
- scaleFactors = obj.radius * obj.semiAxes;
- ri.pointScale = 2.0f * obj.radius / pixelSize;
- }
- else
- {
- geometryScale = obj.geometryScale;
- scaleFactors = Vec3f(geometryScale, geometryScale, geometryScale);
- ri.pointScale = 2.0f * geometryScale / pixelSize;
- }
- glScale(scaleFactors);
- Mat4f planetMat = (~obj.orientation).toMatrix4();
- ri.eyeDir_obj = (Point3f(0, 0, 0) - pos) * planetMat;
- ri.eyeDir_obj.normalize();
- ri.eyePos_obj = Point3f(-pos.x / scaleFactors.x,
- -pos.y / scaleFactors.y,
- -pos.z / scaleFactors.z) * planetMat;
- ri.orientation = cameraOrientation * ~obj.orientation;
- ri.pixWidth = discSizeInPixels;
- // Set up the colors
- if (ri.baseTex == NULL ||
- (obj.surface->appearanceFlags & Surface::BlendTexture) != 0)
- {
- ri.color = obj.surface->color;
- }
- ri.ambientColor = ambientColor;
- ri.hazeColor = obj.surface->hazeColor;
- ri.specularColor = obj.surface->specularColor;
- ri.specularPower = obj.surface->specularPower;
- ri.useTexEnvCombine = context->getRenderPath() != GLContext::GLPath_Basic;
- ri.lunarLambert = obj.surface->lunarLambert;
- #ifdef USE_HDR
- ri.nightLightScale = obj.surface->nightLightRadiance * exposure * 1.e5f * .5f;
- #endif
- // See if the surface should be lit
- bool lit = (obj.surface->appearanceFlags & Surface::Emissive) == 0;
- // Set the OpenGL light state
- unsigned int i;
- for (i = 0; i < ls.nLights; i++)
- {
- const DirectionalLight& light = ls.lights[i];
- glLightDirection(GL_LIGHT0 + i, ls.lights[i].direction_obj);
- // RANT ALERT!
- // This sucks, but it's necessary. glScale is used to scale a unit
- // sphere up to planet size. Since normals are transformed by the
- // inverse transpose of the model matrix, this means they end up
- // getting scaled by a factor of 1.0 / planet radius (in km). This
- // has terrible effects on lighting: the planet appears almost
- // completely dark. To get around this, the GL_rescale_normal
- // extension was introduced and eventually incorporated into into the
- // OpenGL 1.2 standard. Of course, not everyone implemented this
- // incredibly simple and essential little extension. Microsoft is
- // notorious for half-assed support of OpenGL, but 3dfx should have
- // known better: no Voodoo 1/2/3 drivers seem to support this
- // extension. The following is an attempt to get around the problem by
- // scaling the light brightness by the planet radius. According to the
- // OpenGL spec, this should work fine, as clamping of colors to [0, 1]
- // occurs *after* lighting. It works fine on my GeForce3 when I
- // disable EXT_rescale_normal, but I'm not certain whether other
- // drivers are as well behaved as nVidia's.
- //
- // Addendum: Unsurprisingly, using color values outside [0, 1] produces
- // problems on Savage4 cards.
- Vec3f lightColor = Vec3f(light.color.red(),
- light.color.green(),
- light.color.blue()) * light.irradiance;
- if (useRescaleNormal)
- {
- glLightColor(GL_LIGHT0 + i, GL_DIFFUSE, lightColor);
- glLightColor(GL_LIGHT0 + i, GL_SPECULAR, lightColor);
- }
- else
- {
- glLightColor(GL_LIGHT0 + i, GL_DIFFUSE, lightColor * radius);
- }
- glEnable(GL_LIGHT0 + i);
- }
- // Compute the inverse model/view matrix
- Mat4f invMV = (cameraOrientation.toMatrix4() *
- Mat4f::translation(Point3f(-pos.x, -pos.y, -pos.z)) *
- planetMat *
- Mat4f::scaling(1.0f / radius));
- // The sphere rendering code uses the view frustum to determine which
- // patches are visible. In order to avoid rendering patches that can't
- // be seen, make the far plane of the frustum as close to the viewer
- // as possible.
- float frustumFarPlane = farPlaneDistance;
- if (obj.geometry == InvalidResource)
- {
- // Only adjust the far plane for ellipsoidal objects
- float d = pos.distanceFromOrigin();
- // Account for non-spherical objects
- float eradius = min(scaleFactors.x, min(scaleFactors.y, scaleFactors.z));
- if (d > eradius)
- {
- // Include a fudge factor to eliminate overaggressive clipping
- // due to limited floating point precision
- frustumFarPlane = (float) sqrt(square(d) - square(eradius)) * 1.1f;
- }
- else
- {
- // We're inside the bounding sphere; leave the far plane alone
- }
- if (obj.atmosphere != NULL)
- {
- float atmosphereHeight = max(obj.atmosphere->cloudHeight,
- obj.atmosphere->mieScaleHeight * (float) -log(AtmosphereExtinctionThreshold));
- if (atmosphereHeight > 0.0f)
- {
- // If there's an atmosphere, we need to move the far plane
- // out so that the clouds and atmosphere shell aren't clipped.
- float atmosphereRadius = eradius + atmosphereHeight;
- frustumFarPlane += (float) sqrt(square(atmosphereRadius) -
- square(eradius));
- }
- }
- }
- // Transform the frustum into object coordinates using the
- // inverse model/view matrix.
- Frustum viewFrustum(degToRad(fov),
- (float) windowWidth / (float) windowHeight,
- nearPlaneDistance, frustumFarPlane);
- viewFrustum.transform(invMV);
- // Get cloud layer parameters
- Texture* cloudTex = NULL;
- Texture* cloudNormalMap = NULL;
- float cloudTexOffset = 0.0f;
- if (obj.atmosphere != NULL)
- {
- Atmosphere* atmosphere = const_cast<Atmosphere*>(obj.atmosphere); // Ugly cast required because MultiResTexture::find() is non-const
- if ((renderFlags & ShowCloudMaps) != 0)
- {
- if (atmosphere->cloudTexture.tex[textureResolution] != InvalidResource)
- cloudTex = atmosphere->cloudTexture.find(textureResolution);
- if (atmosphere->cloudNormalMap.tex[textureResolution] != InvalidResource)
- cloudNormalMap = atmosphere->cloudNormalMap.find(textureResolution);
- }
- if (atmosphere->cloudSpeed != 0.0f)
- cloudTexOffset = (float) (-pfmod(now * atmosphere->cloudSpeed / (2 * PI), 1.0));
- }
- if (obj.geometry == InvalidResource)
- {
- // A null model indicates that this body is a sphere
- if (lit)
- {
- switch (context->getRenderPath())
- {
- case GLContext::GLPath_GLSL:
- renderSphere_GLSL(ri, ls, obj.rings,
- const_cast<Atmosphere*>(obj.atmosphere), cloudTexOffset,
- obj.radius,
- textureResolution,
- renderFlags,
- planetMat, viewFrustum, *context);
- break;
- case GLContext::GLPath_NV30:
- renderSphere_FP_VP(ri, viewFrustum, *context);
- break;
- case GLContext::GLPath_NvCombiner_ARBVP:
- case GLContext::GLPath_NvCombiner_NvVP:
- renderSphere_Combiners_VP(ri, ls, viewFrustum, *context);
- break;
- case GLContext::GLPath_NvCombiner:
- renderSphere_Combiners(ri, viewFrustum, *context);
- break;
- case GLContext::GLPath_DOT3_ARBVP:
- renderSphere_DOT3_VP(ri, ls, viewFrustum, *context);
- break;
- default:
- renderSphereDefault(ri, viewFrustum, true, *context);
- }
- }
- else
- {
- renderSphereDefault(ri, viewFrustum, false, *context);
- }
- }
- else
- {
- if (geometry != NULL)
- {
- ResourceHandle texOverride = obj.surface->baseTexture.tex[textureResolution];
- if (context->getRenderPath() == GLContext::GLPath_GLSL)
- {
- if (lit)
- {
- renderGeometry_GLSL(geometry,
- ri,
- texOverride,
- ls,
- obj.atmosphere,
- geometryScale,
- renderFlags,
- planetMat,
- astro::daysToSecs(now - astro::J2000));
- }
- else
- {
- renderGeometry_GLSL_Unlit(geometry,
- ri,
- texOverride,
- geometryScale,
- renderFlags,
- planetMat,
- astro::daysToSecs(now - astro::J2000));
- }
- for (unsigned int i = 1; i < 8;/*context->getMaxTextures();*/ i++)
- {
- glx::glActiveTextureARB(GL_TEXTURE0_ARB + i);
- glDisable(GL_TEXTURE_2D);
- }
- glx::glActiveTextureARB(GL_TEXTURE0_ARB);
- glEnable(GL_TEXTURE_2D);
- glx::glUseProgramObjectARB(0);
- }
- else
- {
- renderModelDefault(geometry, ri, lit, texOverride);
- }
- }
- }
- if (obj.rings != NULL && distance <= obj.rings->innerRadius)
- {
- if (context->getRenderPath() == GLContext::GLPath_GLSL)
- {
- renderRings_GLSL(*obj.rings, ri, ls,
- radius, 1.0f - obj.semiAxes.y,
- textureResolution,
- (renderFlags & ShowRingShadows) != 0 && lit,
- detailOptions.ringSystemSections);
- }
- else
- {
- renderRings(*obj.rings, ri, radius, 1.0f - obj.semiAxes.y,
- textureResolution,
- context->getMaxTextures() > 1 &&
- (renderFlags & ShowRingShadows) != 0 && lit,
- *context,
- detailOptions.ringSystemSections);
- }
- }
- if (obj.atmosphere != NULL)
- {
- Atmosphere* atmosphere = const_cast<Atmosphere *>(obj.atmosphere);
- // Compute the apparent thickness in pixels of the atmosphere.
- // If it's only one pixel thick, it can look quite unsightly
- // due to aliasing. To avoid popping, we gradually fade in the
- // atmosphere as it grows from two to three pixels thick.
- float fade;
- float thicknessInPixels = 0.0f;
- if (distance - radius > 0.0f)
- {
- thicknessInPixels = atmosphere->height /
- ((distance - radius) * pixelSize);
- fade = clamp(thicknessInPixels - 2);
- }
- else
- {
- fade = 1.0f;
- }
- if (fade > 0 && (renderFlags & ShowAtmospheres) != 0)
- {
- // Only use new atmosphere code in OpenGL 2.0 path when new style parameters are defined.
- // TODO: convert old style atmopshere parameters
- if (context->getRenderPath() == GLContext::GLPath_GLSL &&
- atmosphere->mieScaleHeight > 0.0f)
- {
- float atmScale = 1.0f + atmosphere->height / radius;
- renderAtmosphere_GLSL(ri, ls,
- atmosphere,
- radius * atmScale,
- planetMat,
- viewFrustum,
- *context);
- }
- else
- {
- glPushMatrix();
- glLoadIdentity();
- glDisable(GL_LIGHTING);
- glDisable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glRotate(cameraOrientation);
- renderEllipsoidAtmosphere(*atmosphere,
- pos,
- obj.orientation,
- scaleFactors,
- ri.sunDir_eye,
- ls,
- thicknessInPixels,
- lit);
- glEnable(GL_TEXTURE_2D);
- glPopMatrix();
- }
- }
- // If there's a cloud layer, we'll render it now.
- if (cloudTex != NULL)
- {
- glPushMatrix();
- float cloudScale = 1.0f + atmosphere->cloudHeight / radius;
- glScalef(cloudScale, cloudScale, cloudScale);
- // If we're beneath the cloud level, render the interior of
- // the cloud sphere.
- if (distance - radius < atmosphere->cloudHeight)
- glFrontFace(GL_CW);
- if (atmosphere->cloudSpeed != 0.0f)
- {
- // Make the clouds appear to rotate above the surface of
- // the planet. This is easier to do with the texture
- // matrix than the model matrix because changing the
- // texture matrix doesn't require us to compute a second
- // set of model space rendering parameters.
- glMatrixMode(GL_TEXTURE);
- glTranslatef(cloudTexOffset, 0.0f, 0.0f);
- glMatrixMode(GL_MODELVIEW);
- }
- glEnable(GL_LIGHTING);
- glDepthMask(GL_FALSE);
- cloudTex->bind();
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- #ifdef HDR_COMPRESS
- glColor4f(0.5f, 0.5f, 0.5f, 1);
- #else
- glColor4f(1, 1, 1, 1);
- #endif
- // Cloud layers can be trouble for the depth buffer, since they tend
- // to be very close to the surface of a planet relative to the radius
- // of the planet. We'll help out by offsetting the cloud layer toward
- // the viewer.
- if (distance > radius * 1.1f)
- {
- glEnable(GL_POLYGON_OFFSET_FILL);
- glPolygonOffset(-1.0f, -1.0f);
- }
- if (lit)
- {
- if (context->getRenderPath() == GLContext::GLPath_GLSL)
- {
- renderClouds_GLSL(ri, ls,
- atmosphere,
- cloudTex,
- cloudNormalMap,
- cloudTexOffset,
- obj.rings,
- radius,
- textureResolution,
- renderFlags,
- planetMat,
- viewFrustum,
- *context);
- }
- else
- {
- VertexProcessor* vproc = context->getVertexProcessor();
- if (vproc != NULL)
- {
- vproc->enable();
- vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
- vproc->parameter(vp::TextureTranslation,
- cloudTexOffset, 0.0f, 0.0f, 0.0f);
- if (ls.nLights > 1)
- vproc->use(vp::diffuseTexOffset_2light);
- else
- vproc->use(vp::diffuseTexOffset);
- setLightParameters_VP(*vproc, ls, ri.color, Color::Black);
- }
- g_lodSphere->render(*context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
- viewFrustum,
- ri.pixWidth,
- cloudTex);
- if (vproc != NULL)
- vproc->disable();
- }
- }
- else
- {
- glDisable(GL_LIGHTING);
- g_lodSphere->render(*context,
- LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
- viewFrustum,
- ri.pixWidth,
- cloudTex);
- glEnable(GL_LIGHTING);
- }
- glDisable(GL_POLYGON_OFFSET_FILL);
- // Reset the texture matrix
- glMatrixMode(GL_TEXTURE);
- glLoadIdentity();
- glMatrixMode(GL_MODELVIEW);
- glDepthMask(GL_TRUE);
- glFrontFace(GL_CCW);
- glPopMatrix();
- }
- }
- // No separate shadow rendering pass required for GLSL path
- if (ls.shadows[0] != NULL &&
- ls.shadows[0]->size() != 0 &&
- (obj.surface->appearanceFlags & Surface::Emissive) == 0 &&
- context->getRenderPath() != GLContext::GLPath_GLSL)
- {
- if (context->getVertexProcessor() != NULL &&
- context->getFragmentProcessor() != NULL)
- {
- renderEclipseShadows_Shaders(geometry,
- *ls.shadows[0],
- ri,
- radius, planetMat, viewFrustum,
- *context);
- }
- else
- {
- renderEclipseShadows(geometry,
- *ls.shadows[0],
- ri,
- radius, planetMat, viewFrustum,
- *context);
- }
- }
- if (obj.rings != NULL &&
- (obj.surface->appearanceFlags & Surface::Emissive) == 0 &&
- (renderFlags & ShowRingShadows) != 0)
- {
- Texture* ringsTex = obj.rings->texture.find(textureResolution);
- if (ringsTex != NULL)
- {
- Vec3f sunDir = pos - Point3f(0, 0, 0);
- sunDir.normalize();
- glEnable(GL_TEXTURE_2D);
- ringsTex->bind();
- if (useClampToBorder &&
- context->getVertexPath() != GLContext::VPath_Basic &&
- context->getRenderPath() != GLContext::GLPath_GLSL)
- {
- renderRingShadowsVS(geometry,
- *obj.rings,
- sunDir,
- ri,
- radius, 1.0f - obj.semiAxes.y,
- planetMat, viewFrustum,
- *context);
- }
- }
- }
- if (obj.rings != NULL && distance > obj.rings->innerRadius)
- {
- glDepthMask(GL_FALSE);
- if (context->getRenderPath() == GLContext::GLPath_GLSL)
- {
- renderRings_GLSL(*obj.rings, ri, ls,
- radius, 1.0f - obj.semiAxes.y,
- textureResolution,
- (renderFlags & ShowRingShadows) != 0 && lit,
- detailOptions.ringSystemSections);
- }
- else
- {
- renderRings(*obj.rings, ri, radius, 1.0f - obj.semiAxes.y,
- textureResolution,
- (context->hasMultitexture() &&
- (renderFlags & ShowRingShadows) != 0 && lit),
- *context,
- detailOptions.ringSystemSections);
- }
- }
- // Disable all light sources other than the first
- for (i = 0; i < ls.nLights; i++)
- glDisable(GL_LIGHT0 + i);
- glPopMatrix();
- glDisable(GL_DEPTH_TEST);
- glDepthMask(GL_FALSE);
- glDisable(GL_LIGHTING);
- glEnable(GL_BLEND);
- }
- bool Renderer::testEclipse(const Body& receiver,
- const Body& caster,
- const DirectionalLight& light,
- double now,
- vector<EclipseShadow>& shadows)
- {
- // Ignore situations where the shadow casting body is much smaller than
- // the receiver, as these shadows aren't likely to be relevant. Also,
- // ignore eclipses where the caster is not an ellipsoid, since we can't
- // generate correct shadows in this case.
- if (caster.getRadius() >= receiver.getRadius() * MinRelativeOccluderRadius &&
- caster.hasVisibleGeometry() &&
- caster.extant(now) &&
- caster.isEllipsoid())
- {
- // All of the eclipse related code assumes that both the caster
- // and receiver are spherical. Irregular receivers will work more
- // or less correctly, but casters that are sufficiently non-spherical
- // will produce obviously incorrect shadows. Another assumption we
- // make is that the distance between the caster and receiver is much
- // less than the distance between the sun and the receiver. This
- // approximation works everywhere in the solar system, and is likely
- // valid for any orbitally stable pair of objects orbiting a star.
- Point3d posReceiver = receiver.getAstrocentricPosition(now);
- Point3d posCaster = caster.getAstrocentricPosition(now);
- //const Star* sun = receiver.getSystem()->getStar();
- //assert(sun != NULL);
- //double distToSun = posReceiver.distanceFromOrigin();
- //float appSunRadius = (float) (sun->getRadius() / distToSun);
- float appSunRadius = light.apparentSize;
- Vec3d dir = posCaster - posReceiver;
- double distToCaster = dir.length() - receiver.getRadius();
- float appOccluderRadius = (float) (caster.getRadius() / distToCaster);
- // The shadow radius is the radius of the occluder plus some additional
- // amount that depends upon the apparent radius of the sun. For
- // a sun that's distant/small and effectively a point, the shadow
- // radius will be the same as the radius of the occluder.
- float shadowRadius = (1 + appSunRadius / appOccluderRadius) *
- caster.getRadius();
- // Test whether a shadow is cast on the receiver. We want to know
- // if the receiver lies within the shadow volume of the caster. Since
- // we're assuming that everything is a sphere and the sun is far
- // away relative to the caster, the shadow volume is a
- // cylinder capped at one end. Testing for the intersection of a
- // singly capped cylinder is as simple as checking the distance
- // from the center of the receiver to the axis of the shadow cylinder.
- // If the distance is less than the sum of the caster's and receiver's
- // radii, then we have an eclipse. We also need to verify that the
- // receiver is behind the caster when seen from the light source.
- float R = receiver.getRadius() + shadowRadius;
- // The stored light position is receiver-relative; thus the caster-to-light
- // direction is casterPos - (receiverPos + lightPos)
- Point3d lightPosition = posReceiver + light.position;
- Vec3d lightToCasterDir = posCaster - lightPosition;
- Vec3d receiverToCasterDir = posReceiver - posCaster;
- double dist = distance(posReceiver,
- Ray3d(posCaster, lightToCasterDir));
- if (dist < R && lightToCasterDir * receiverToCasterDir > 0.0)
- {
- Vec3d sunDir = lightToCasterDir;
- sunDir.normalize();
- EclipseShadow shadow;
- shadow.origin = Point3f((float) dir.x,
- (float) dir.y,
- (float) dir.z);
- shadow.direction = Vec3f((float) sunDir.x,
- (float) sunDir.y,
- (float) sunDir.z);
- shadow.penumbraRadius = shadowRadius;
- // The umbra radius will be positive if the apparent size of the occluder
- // is greater than the apparent size of the sun, zero if they're equal,
- // and negative when the eclipse is partial. The absolute value of the
- // umbra radius is the radius of the shadow region with constant depth:
- // for total eclipses, this area is actually the umbra, with a depth of
- // 1. For annular eclipses and transits, it is less than 1.
- shadow.umbraRadius = caster.getRadius() *
- (appOccluderRadius - appSunRadius) / appOccluderRadius;
- shadow.maxDepth = std::min(1.0f, square(appOccluderRadius / appSunRadius));
- // Ignore transits that don't produce a visible shadow.
- if (shadow.maxDepth > 1.0f / 256.0f)
- shadows.push_back(shadow);
- return true;
- }
- }
- return false;
- }
- void Renderer::renderPlanet(Body& body,
- Point3f pos,
- float distance,
- float appMag,
- const Observer& observer,
- const Quatf& cameraOrientation,
- float nearPlaneDistance,
- float farPlaneDistance)
- {
- double now = observer.getTime();
- float altitude = distance - body.getRadius();
- float discSizeInPixels = body.getRadius() /
- (max(nearPlaneDistance, altitude) * pixelSize);
- if (discSizeInPixels > 1 && body.hasVisibleGeometry())
- {
- RenderProperties rp;
- if (displayedSurface.empty())
- {
- rp.surface = const_cast<Surface*>(&body.getSurface());
- }
- else
- {
- rp.surface = body.getAlternateSurface(displayedSurface);
- if (rp.surface == NULL)
- rp.surface = const_cast<Surface*>(&body.getSurface());
- }
- rp.atmosphere = body.getAtmosphere();
- rp.rings = body.getRings();
- rp.radius = body.getRadius();
- rp.geometry = body.getGeometry();
- rp.semiAxes = body.getSemiAxes() * (1.0f / rp.radius);
- rp.geometryScale = body.getGeometryScale();
- Quatd q = body.getRotationModel(now)->spin(now) *
- body.getEclipticToEquatorial(now);
- rp.orientation = body.getOrientation() *
- Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
- if (body.getLocations() != NULL && (labelMode & LocationLabels) != 0)
- body.computeLocations();
- Vec3f scaleFactors;
- bool isNormalized = false;
- Geometry* geometry = NULL;
- if (rp.geometry != InvalidResource)
- geometry = GetGeometryManager()->find(rp.geometry);
- if (geometry == NULL || geometry->isNormalized())
- {
- scaleFactors = rp.semiAxes * rp.radius;
- isNormalized = true;
- }
- else
- {
- float scale = rp.geometryScale;
- scaleFactors = Vec3f(scale, scale, scale);
- }
- LightingState lights;
- setupObjectLighting(lightSourceList,
- secondaryIlluminators,
- rp.orientation,
- scaleFactors,
- pos,
- isNormalized,
- #ifdef USE_HDR
- faintestMag,
- DEFAULT_EXPOSURE + brightPlus, //exposure + brightPlus,
- appMag,
- #endif
- lights);
- assert(lights.nLights <= MaxLights);
- lights.ambientColor = Vec3f(ambientColor.red(),
- ambientColor.green(),
- ambientColor.blue());
- {
- // Clear out the list of eclipse shadows
- for (unsigned int li = 0; li < lights.nLights; li++)
- {
- eclipseShadows[li].clear();
- lights.shadows[li] = &eclipseShadows[li];
- }
- }
- // Calculate eclipse circumstances
- if ((renderFlags & ShowEclipseShadows) != 0 &&
- body.getSystem() != NULL)
- {
- PlanetarySystem* system = body.getSystem();
- if (system->getPrimaryBody() == NULL &&
- body.getSatellites() != NULL)
- {
- // The body is a planet. Check for eclipse shadows
- // from all of its satellites.
- PlanetarySystem* satellites = body.getSatellites();
- if (satellites != NULL)
- {
- int nSatellites = satellites->getSystemSize();
- for (unsigned int li = 0; li < lights.nLights; li++)
- {
- if (lights.lights[li].castsShadows)
- {
- for (int i = 0; i < nSatellites; i++)
- {
- testEclipse(body, *satellites->getBody(i),
- lights.lights[li],
- now, *lights.shadows[li]);
- }
- }
- }
- }
- }
- else if (system->getPrimaryBody() != NULL)
- {
- for (unsigned int li = 0; li < lights.nLights; li++)
- {
- if (lights.lights[li].castsShadows)
- {
- // The body is a moon. Check for eclipse shadows from
- // the parent planet and all satellites in the system.
- // Traverse up the hierarchy so that any parent objects
- // of the parent are also considered (TODO: their child
- // objects will not be checked for shadows.)
- Body* planet = system->getPrimaryBody();
- while (planet != NULL)
- {
- testEclipse(body, *planet, lights.lights[li],
- now, *lights.shadows[li]);
- if (planet->getSystem() != NULL)
- planet = planet->getSystem()->getPrimaryBody();
- else
- planet = NULL;
- }
- int nSatellites = system->getSystemSize();
- for (int i = 0; i < nSatellites; i++)
- {
- if (system->getBody(i) != &body)
- {
- testEclipse(body, *system->getBody(i),
- lights.lights[li],
- now, *lights.shadows[li]);
- }
- }
- }
- }
- }
- }
- renderObject(pos, distance, now,
- cameraOrientation, nearPlaneDistance, farPlaneDistance,
- rp, lights);
- if (body.getLocations() != NULL && (labelMode & LocationLabels) != 0)
- {
- // Set up location markers for this body
- mountainRep = MarkerRepresentation(MarkerRepresentation::Triangle, 8.0f, LocationLabelColor);
- craterRep = MarkerRepresentation(MarkerRepresentation::Circle, 8.0f, LocationLabelColor);
- observatoryRep = MarkerRepresentation(MarkerRepresentation::Plus, 8.0f, LocationLabelColor);
- cityRep = MarkerRepresentation(MarkerRepresentation::X, 3.0f, LocationLabelColor);
- genericLocationRep = MarkerRepresentation(MarkerRepresentation::Square, 8.0f, LocationLabelColor);
-
- glEnable(GL_DEPTH_TEST);
- glDepthMask(GL_FALSE);
- glDisable(GL_BLEND);
- // We need a double precision body-relative position of the
- // observer, otherwise location labels will tend to jitter.
- Vec3d posd = (body.getPosition(observer.getTime()) -
- observer.getPosition()) * astro::microLightYearsToKilometers(1.0);
- renderLocations(body, posd, q);
- glDisable(GL_DEPTH_TEST);
- }
- }
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- #ifdef USE_HDR
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
- #endif
- if (body.isVisibleAsPoint())
- {
- if (useNewStarRendering)
- {
- renderObjectAsPoint(pos,
- body.getRadius(),
- appMag,
- faintestMag,
- discSizeInPixels,
- body.getSurface().color,
- cameraOrientation,
- false, false);
- }
- else
- {
- renderObjectAsPoint_nosprite(pos,
- body.getRadius(),
- appMag,
- faintestMag,
- discSizeInPixels,
- body.getSurface().color,
- cameraOrientation,
- false);
- }
- }
- #ifdef USE_HDR
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- #endif
- }
- void Renderer::renderStar(const Star& star,
- Point3f pos,
- float distance,
- float appMag,
- Quatf cameraOrientation,
- double now,
- float nearPlaneDistance,
- float farPlaneDistance)
- {
- if (!star.getVisibility())
- return;
- Color color = colorTemp->lookupColor(star.getTemperature());
- float radius = star.getRadius();
- float discSizeInPixels = radius / (distance * pixelSize);
- if (discSizeInPixels > 1)
- {
- Surface surface;
- RenderProperties rp;
- surface.color = color;
- MultiResTexture mtex = star.getTexture();
- if (mtex.tex[textureResolution] != InvalidResource)
- {
- surface.baseTexture = mtex;
- }
- else
- {
- surface.baseTexture = InvalidResource;
- }
- surface.appearanceFlags |= Surface::ApplyBaseTexture;
- surface.appearanceFlags |= Surface::Emissive;
- rp.surface = &surface;
- rp.rings = NULL;
- rp.radius = star.getRadius();
- rp.semiAxes = star.getEllipsoidSemiAxes();
- rp.geometry = star.getGeometry();
- #ifndef USE_HDR
- Atmosphere atmosphere;
- Color atmColor(color.red() * 0.5f, color.green() * 0.5f, color.blue() * 0.5f);
- atmosphere.height = radius * CoronaHeight;
- atmosphere.lowerColor = atmColor;
- atmosphere.upperColor = atmColor;
- atmosphere.skyColor = atmColor;
- // Use atmosphere effect to give stars a fuzzy fringe
- if (rp.geometry == InvalidResource)
- rp.atmosphere = &atmosphere;
- else
- #endif
- rp.atmosphere = NULL;
- Quatd q = star.getRotationModel()->orientationAtTime(now);
- rp.orientation = Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
- renderObject(pos, distance, now,
- cameraOrientation, nearPlaneDistance, farPlaneDistance,
- rp, LightingState());
- }
- glEnable(GL_TEXTURE_2D);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- #ifdef USE_HDR
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
- #endif
- #ifndef USE_HDR
- if (useNewStarRendering)
- {
- #endif
- renderObjectAsPoint(pos,
- star.getRadius(),
- appMag,
- faintestMag,
- discSizeInPixels,
- color,
- cameraOrientation,
- true, true);
- #ifndef USE_HDR
- }
- else
- {
- renderObjectAsPoint_nosprite(pos,
- star.getRadius(),
- appMag,
- faintestPlanetMag,
- discSizeInPixels,
- color,
- cameraOrientation,
- true);
- }
- #endif
- #ifdef USE_HDR
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- #endif
- }
- static const int MaxCometTailPoints = 120;
- static const int CometTailSlices = 48;
- struct CometTailVertex
- {
- Point3f point;
- Vec3f normal;
- Point3f paraboloidPoint;
- float brightness;
- };
- static CometTailVertex cometTailVertices[CometTailSlices * MaxCometTailPoints];
- static void ProcessCometTailVertex(const CometTailVertex& v,
- const Vec3f& viewDir,
- float fadeDistFromSun)
- {
- // If fadeDistFromSun = x/x0 >= 1.0, comet tail starts fading,
- // i.e. fadeFactor quickly transits from 1 to 0.
- float fadeFactor = 0.5f - 0.5f * (float) tanh(fadeDistFromSun - 1.0f / fadeDistFromSun);
- float shade = abs(viewDir * v.normal * v.brightness * fadeFactor);
- glColor4f(0.5f, 0.5f, 0.75f, shade);
- glVertex(v.point);
- }
- #if 0
- static void ProcessCometTailVertex(const CometTailVertex& v,
- const Point3f& cameraPos)
- {
- Vec3f viewDir = v.point - cameraPos;
- viewDir.normalize();
- float shade = abs(viewDir * v.normal * v.brightness);
- glColor4f(0.0f, 0.5f, 1.0f, shade);
- glVertex(v.point);
- }
- #endif
- #if 0
- static void ProcessCometTailVertex(const CometTailVertex& v,
- const Point3f& eyePos_obj,
- float b,
- float h)
- {
- float shade = 0.0f;
- Vec3f R = v.paraboloidPoint - eyePos_obj;
- float c0 = b * (square(eyePos_obj.x) + square(eyePos_obj.y)) + eyePos_obj.z;
- float c1 = 2 * b * (R.x * eyePos_obj.x + R.y * eyePos_obj.y) - R.z;
- float c2 = b * (square(R.x) + square(R.y));
- float disc = square(c1) - 4 * c0 * c2;
- if (disc < 0.0f)
- {
- shade = 0.0f;
- }
- else
- {
- disc = (float) sqrt(disc);
- float s = 1.0f / (2 * c2);
- float t0 = (h - eyePos_obj.z) / R.z;
- float t1 = (c1 - disc) * s;
- float t2 = (c1 + disc) * s;
- /*float u0 = max(t0, 0.0f); Unused*/
- float u1, u2;
- if (R.z < 0.0f)
- {
- u1 = max(t1, t0);
- u2 = max(t2, t0);
- }
- else
- {
- u1 = min(t1, t0);
- u2 = min(t2, t0);
- }
- u1 = max(0.0f, u1);
- u2 = max(0.0f, u2);
- shade = u2 - u1;
- }
- glColor4f(0.0f, 0.5f, 1.0f, shade);
- glVertex(v.point);
- }
- #endif
- // Compute a rough estimate of the visible length of the dust tail.
- // TODO: This is old code that needs to be rewritten. For one thing,
- // the length is inversely proportional to the distance from the sun,
- // whereas the 1/distance^2 is probably more realistic. There should
- // also be another parameter that specifies how active the comet is.
- static float cometDustTailLength(float distanceToSun,
- float radius)
- {
- return (1.0e8f / distanceToSun) * (radius / 5.0f) * 1.0e7f;
- }
- // TODO: Remove unused parameters??
- void Renderer::renderCometTail(const Body& body,
- Point3f pos,
- double now,
- float discSizeInPixels)
- {
- Point3f cometPoints[MaxCometTailPoints];
- Point3d pos0 = body.getOrbit(now)->positionAtTime(now);
- Point3d pos1 = body.getOrbit(now)->positionAtTime(now - 0.01);
- Vec3d vd = pos1 - pos0;
- double t = now;
- /*float f = 1.0e15f; Unused*/
- /*int nSteps = MaxCometTailPoints; Unused*/
- /*float dt = 10000000.0f / (nSteps * (float) vd.length() * 100.0f); Unused*/
- float distanceFromSun, irradiance_max = 0.0f;
- unsigned int li_eff = 0; // Select the first sun as default to
- // shut up compiler warnings
- // Adjust the amount of triangles used for the comet tail based on
- // the screen size of the comet.
- float lod = min(1.0f, max(0.2f, discSizeInPixels / 1000.0f));
- int nTailPoints = (int) (MaxCometTailPoints * lod);
- int nTailSlices = (int) (CometTailSlices * lod);
- // Find the sun with the largest irrradiance of light onto the comet
- // as function of the comet's position;
- // irradiance = sun's luminosity / square(distanceFromSun);
- for (unsigned int li = 0; li < lightSourceList.size(); li++)
- {
- distanceFromSun = (float) (Vec3d(pos.x, pos.y, pos.z) - lightSourceList[li].position).length();
- float irradiance = lightSourceList[li].luminosity / square(distanceFromSun);
- if (irradiance > irradiance_max)
- {
- li_eff = li;
- irradiance_max = irradiance;
- }
- }
- float fadeDistance = 1.0f / (float) (COMET_TAIL_ATTEN_DIST_SOL * sqrt(irradiance_max));
- // direction to sun with dominant light irradiance:
- Vec3d sd = Vec3d(pos.x, pos.y, pos.z) - lightSourceList[li_eff].position;
- Vec3f sunDir = Vec3f((float) sd.x, (float) sd.y, (float) sd.z);
- sunDir.normalize();
- float dustTailLength = cometDustTailLength((float) pos0.distanceFromOrigin(), body.getRadius());
- float dustTailRadius = dustTailLength * 0.1f;
- Point3f origin(0, 0, 0);
- origin -= sunDir * (body.getRadius() * 100);
- int i;
- for (i = 0; i < nTailPoints; i++)
- {
- float alpha = (float) i / (float) nTailPoints;
- alpha = alpha * alpha;
- cometPoints[i] = origin + sunDir * (dustTailLength * alpha);
- }
- // We need three axes to define the coordinate system for rendering the
- // comet. The first axis is the velocity. We choose the second one
- // based on the orientation of the comet. And the third is just the cross
- // product of the first and second axes.
- Quatd qd = body.getEclipticToEquatorial(t);
- Quatf q((float) qd.w, (float) qd.x, (float) qd.y, (float) qd.z);
- Vec3f v = cometPoints[1] - cometPoints[0];
- v.normalize();
- Vec3f u0 = Vec3f(0, 1, 0) * q.toMatrix3();
- Vec3f u1 = Vec3f(1, 0, 0) * q.toMatrix3();
- Vec3f u;
- if (abs(u0 * v) < abs(u1 * v))
- u = v ^ u0;
- else
- u = v ^ u1;
- u.normalize();
- Vec3f w = u ^ v;
- glColor4f(0.0f, 1.0f, 1.0f, 0.5f);
- glPushMatrix();
- glTranslate(pos);
- // glx::glActiveTextureARB(GL_TEXTURE0_ARB);
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_LIGHTING);
- glDepthMask(GL_FALSE);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- for (i = 0; i < nTailPoints; i++)
- {
- float brightness = 1.0f - (float) i / (float) (nTailPoints - 1);
- Vec3f v0, v1;
- float sectionLength;
- if (i != 0 && i != nTailPoints - 1)
- {
- v0 = cometPoints[i] - cometPoints[i - 1];
- v1 = cometPoints[i + 1] - cometPoints[i];
- sectionLength = v0.length();
- v0.normalize();
- v1.normalize();
- Vec3f axis = v1 ^ v0;
- float axisLength = axis.length();
- if (axisLength > 1e-5f)
- {
- axis *= 1.0f / axisLength;
- q.setAxisAngle(axis, (float) asin(axisLength));
- Mat3f m = q.toMatrix3();
- u = u * m;
- v = v * m;
- w = w * m;
- }
- }
- else if (i == 0)
- {
- v0 = cometPoints[i + 1] - cometPoints[i];
- sectionLength = v0.length();