render.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:390k
- // render.cpp
- //
- // Copyright (C) 2001-2008, the Celestia Development Team
- // Original version by Chris Laurel <claurel@gmail.com>
- //
- // This program is free software; you can redistribute it and/or
- // modify it under the terms of the GNU General Public License
- // as published by the Free Software Foundation; either version 2
- // of the License, or (at your option) any later version.
- #include <algorithm>
- #include <cstdio>
- #include <cstring>
- #include <cassert>
- #include <sstream>
- #include <iomanip>
- #define DEBUG_COALESCE 0
- //#define DEBUG_HDR
- #ifdef DEBUG_HDR
- //#define DEBUG_HDR_FILE
- //#define DEBUG_HDR_ADAPT
- //#define DEBUG_HDR_TONEMAP
- #endif
- #ifdef DEBUG_HDR_FILE
- #include <fstream>
- std::ofstream hdrlog;
- #define HDR_LOG hdrlog
- #else
- #define HDR_LOG cout
- #endif
- #ifdef USE_HDR
- #define BLUR_PASS_COUNT 2
- #define BLUR_SIZE 128
- #define DEFAULT_EXPOSURE -23.35f
- #define EXPOSURE_HALFLIFE 0.4f
- //#define USE_BLOOM_LISTS
- #endif
- #ifndef _WIN32
- #ifndef TARGET_OS_MAC
- #include <config.h>
- #endif
- #endif /* _WIN32 */
- #include <celutil/debug.h>
- #include <celmath/frustum.h>
- #include <celmath/distance.h>
- #include <celmath/intersect.h>
- #include <celutil/utf8.h>
- #include <celutil/util.h>
- #include <celutil/timer.h>
- #include "gl.h"
- #include "astro.h"
- #include "glext.h"
- #include "vecgl.h"
- #include "glshader.h"
- #include "shadermanager.h"
- #include "spheremesh.h"
- #include "lodspheremesh.h"
- #include "model.h"
- #include "regcombine.h"
- #include "vertexprog.h"
- #include "texmanager.h"
- #include "meshmanager.h"
- #include "render.h"
- #include "renderinfo.h"
- #include "renderglsl.h"
- #include "axisarrow.h"
- #include "frametree.h"
- #include "timelinephase.h"
- #include "skygrid.h"
- using namespace std;
- #define FOV 45.0f
- #define NEAR_DIST 0.5f
- #define FAR_DIST 1.0e9f
- // This should be in the GL headers, but where?
- #ifndef GL_COLOR_SUM_EXT
- #define GL_COLOR_SUM_EXT 0x8458
- #endif
- static const float STAR_DISTANCE_LIMIT = 1.0e6f;
- static const int REF_DISTANCE_TO_SCREEN = 400; //[mm]
- // Contribution from planetshine beyond this distance (in units of object radius)
- // is considered insignificant.
- static const float PLANETSHINE_DISTANCE_LIMIT_FACTOR = 100.0f;
- // Planetshine from objects less than this pixel size is treated as insignificant
- // and will be ignored.
- static const float PLANETSHINE_PIXEL_SIZE_LIMIT = 0.1f;
- // Distance from the Sun at which comet tails will start to fade out
- static const float COMET_TAIL_ATTEN_DIST_SOL = astro::AUtoKilometers(5.0f);
- static const int StarVertexListSize = 1024;
- // Fractional pixel offset used when rendering text as texture mapped
- // quads to ensure consistent mapping of texels to pixels.
- static const float PixelOffset = 0.125f;
- // These two values constrain the near and far planes of the view frustum
- // when rendering planet and object meshes. The near plane will never be
- // closer than MinNearPlaneDistance, and the far plane is set so that far/near
- // will not exceed MaxFarNearRatio.
- static const float MinNearPlaneDistance = 0.0001f; // km
- static const float MaxFarNearRatio = 2000000.0f;
- static const float RenderDistance = 50.0f;
- // Star disc size in pixels
- static const float BaseStarDiscSize = 5.0f;
- static const float MaxScaledDiscStarSize = 8.0f;
- static const float GlareOpacity = 0.65f;
- static const float MinRelativeOccluderRadius = 0.005f;
- static const float CubeCornerToCenterDistance = (float) sqrt(3.0);
- // The minimum apparent size of an objects orbit in pixels before we display
- // a label for it. This minimizes label clutter.
- static const float MinOrbitSizeForLabel = 20.0f;
- // The minimum apparent size of a surface feature in pixels before we display
- // a label for it.
- static const float MinFeatureSizeForLabel = 20.0f;
- /* The maximum distance of the observer to the origin of coordinates before
- asterism lines and labels start to linearly fade out (in uLY) */
- static const float MaxAsterismLabelsConstDist = 6.0e6f;
- static const float MaxAsterismLinesConstDist = 6.0e8f;
- /* The maximum distance of the observer to the origin of coordinates before
- asterisms labels and lines fade out completely (in uLY) */
- static const float MaxAsterismLabelsDist = 2.0e7f;
- static const float MaxAsterismLinesDist = 6.52e10f;
- // Maximum size of a solar system in light years. Features beyond this distance
- // will not necessarily be rendered correctly. This limit is used for
- // visibility culling of solar systems.
- static const float MaxSolarSystemSize = 1.0f;
- // Static meshes and textures used by all instances of Simulation
- static bool commonDataInitialized = false;
- static const string EMPTY_STRING("");
- LODSphereMesh* g_lodSphere = NULL;
- static Texture* normalizationTex = NULL;
- static Texture* starTex = NULL;
- static Texture* glareTex = NULL;
- static Texture* shadowTex = NULL;
- static Texture* gaussianDiscTex = NULL;
- static Texture* gaussianGlareTex = NULL;
- // Shadow textures are scaled down slightly to leave some extra blank pixels
- // near the border. This keeps axis aligned streaks from appearing on hardware
- // that doesn't support clamp to border color.
- static const float ShadowTextureScale = 15.0f / 16.0f;
- static Texture* eclipseShadowTextures[4];
- static Texture* shadowMaskTexture = NULL;
- static Texture* penumbraFunctionTexture = NULL;
- Texture* rectToSphericalTexture = NULL;
- static const Color compassColor(0.4f, 0.4f, 1.0f);
- static const float CoronaHeight = 0.2f;
- static bool buggyVertexProgramEmulation = true;
- static const int MaxSkyRings = 32;
- static const int MaxSkySlices = 180;
- static const int MinSkySlices = 30;
- // Size at which the orbit cache will be flushed of old orbit paths
- static const unsigned int OrbitCacheCullThreshold = 200;
- // Age in frames at which unused orbit paths may be eliminated from the cache
- static const uint32 OrbitCacheRetireAge = 16;
- Color Renderer::StarLabelColor (0.471f, 0.356f, 0.682f);
- Color Renderer::PlanetLabelColor (0.407f, 0.333f, 0.964f);
- Color Renderer::DwarfPlanetLabelColor (0.407f, 0.333f, 0.964f);
- Color Renderer::MoonLabelColor (0.231f, 0.733f, 0.792f);
- Color Renderer::MinorMoonLabelColor (0.231f, 0.733f, 0.792f);
- Color Renderer::AsteroidLabelColor (0.596f, 0.305f, 0.164f);
- Color Renderer::CometLabelColor (0.768f, 0.607f, 0.227f);
- Color Renderer::SpacecraftLabelColor (0.93f, 0.93f, 0.93f);
- Color Renderer::LocationLabelColor (0.24f, 0.89f, 0.43f);
- Color Renderer::GalaxyLabelColor (0.0f, 0.45f, 0.5f);
- Color Renderer::GlobularLabelColor (0.8f, 0.45f, 0.5f);
- Color Renderer::NebulaLabelColor (0.541f, 0.764f, 0.278f);
- Color Renderer::OpenClusterLabelColor (0.239f, 0.572f, 0.396f);
- Color Renderer::ConstellationLabelColor (0.225f, 0.301f, 0.36f);
- Color Renderer::EquatorialGridLabelColor(0.64f, 0.72f, 0.88f);
- Color Renderer::PlanetographicGridLabelColor(0.8f, 0.8f, 0.8f);
- Color Renderer::GalacticGridLabelColor (0.88f, 0.72f, 0.64f);
- Color Renderer::EclipticGridLabelColor (0.72f, 0.64f, 0.88f);
- Color Renderer::HorizonGridLabelColor (0.72f, 0.72f, 0.72f);
- Color Renderer::StarOrbitColor (0.5f, 0.5f, 0.8f);
- Color Renderer::PlanetOrbitColor (0.3f, 0.323f, 0.833f);
- Color Renderer::DwarfPlanetOrbitColor (0.3f, 0.323f, 0.833f);
- Color Renderer::MoonOrbitColor (0.08f, 0.407f, 0.392f);
- Color Renderer::MinorMoonOrbitColor (0.08f, 0.407f, 0.392f);
- Color Renderer::AsteroidOrbitColor (0.58f, 0.152f, 0.08f);
- Color Renderer::CometOrbitColor (0.639f, 0.487f, 0.168f);
- Color Renderer::SpacecraftOrbitColor (0.4f, 0.4f, 0.4f);
- Color Renderer::SelectionOrbitColor (1.0f, 0.0f, 0.0f);
- Color Renderer::ConstellationColor (0.0f, 0.24f, 0.36f);
- Color Renderer::BoundaryColor (0.24f, 0.10f, 0.12f);
- Color Renderer::EquatorialGridColor (0.28f, 0.28f, 0.38f);
- Color Renderer::PlanetographicGridColor (0.8f, 0.8f, 0.8f);
- Color Renderer::PlanetEquatorColor (0.5f, 1.0f, 1.0f);
- Color Renderer::GalacticGridColor (0.38f, 0.38f, 0.28f);
- Color Renderer::EclipticGridColor (0.38f, 0.28f, 0.38f);
- Color Renderer::HorizonGridColor (0.38f, 0.38f, 0.38f);
- Color Renderer::EclipticColor (0.5f, 0.1f, 0.1f);
- Color Renderer::SelectionCursorColor (1.0f, 0.0f, 0.0f);
- // Some useful unit conversions
- inline float mmToInches(float mm)
- {
- return mm * (1.0f / 25.4f);
- }
- inline float inchesToMm(float in)
- {
- return in * 25.4f;
- }
- // Fade function for objects that shouldn't be shown when they're too small
- // on screen such as orbit paths and some object labels. The will fade linearly
- // from invisible at minSize pixels to full visibility at opaqueScale*minSize.
- inline float sizeFade(float screenSize, float minScreenSize, float opaqueScale)
- {
- return min(1.0f, (screenSize - minScreenSize) / (minScreenSize * (opaqueScale - 1)));
- }
- // Calculate the cosine of half the maximum field of view. We'll use this for
- // fast testing of object visibility. The function takes the vertical FOV (in
- // degrees) as an argument. When computing the view cone, we want the field of
- // view as measured on the diagonal between viewport corners.
- double computeCosViewConeAngle(double verticalFOV, double width, double height)
- {
- double h = tan(degToRad(verticalFOV / 2));
- double diag = sqrt(1.0 + square(h) + square(h * width / height));
- return 1.0 / diag;
- }
- Renderer::Renderer() :
- context(0),
- windowWidth(0),
- windowHeight(0),
- fov(FOV),
- cosViewConeAngle(computeCosViewConeAngle(fov, 1, 1)),
- screenDpi(96),
- corrFac(1.12f),
- faintestAutoMag45deg(7.0f),
- renderMode(GL_FILL),
- labelMode(NoLabels),
- renderFlags(ShowStars | ShowPlanets),
- orbitMask(Body::Planet | Body::Moon | Body::Stellar),
- ambientLightLevel(0.1f),
- fragmentShaderEnabled(false),
- vertexShaderEnabled(false),
- brightnessBias(0.0f),
- saturationMagNight(1.0f),
- saturationMag(1.0f),
- starStyle(FuzzyPointStars),
- starVertexBuffer(NULL),
- pointStarVertexBuffer(NULL),
- glareVertexBuffer(NULL),
- useVertexPrograms(false),
- useRescaleNormal(false),
- usePointSprite(false),
- textureResolution(medres),
- useNewStarRendering(false),
- frameCount(0),
- lastOrbitCacheFlush(0),
- minOrbitSize(MinOrbitSizeForLabel),
- distanceLimit(1.0e6f),
- minFeatureSize(MinFeatureSizeForLabel),
- locationFilter(~0u),
- colorTemp(NULL),
- #ifdef USE_HDR
- sceneTexture(0),
- blurFormat(GL_RGBA),
- useBlendSubtract(true),
- useLuminanceAlpha(false),
- bloomEnabled(true),
- maxBodyMag(100.0f),
- exposure(1.0f),
- exposurePrev(1.0f),
- brightPlus(0.0f),
- #endif
- videoSync(false),
- settingsChanged(true),
- objectAnnotationSetOpen(false)
- {
- starVertexBuffer = new StarVertexBuffer(2048);
- pointStarVertexBuffer = new PointStarVertexBuffer(2048);
- glareVertexBuffer = new PointStarVertexBuffer(2048);
- skyVertices = new SkyVertex[MaxSkySlices * (MaxSkyRings + 1)];
- skyIndices = new uint32[(MaxSkySlices + 1) * 2 * MaxSkyRings];
- skyContour = new SkyContourPoint[MaxSkySlices + 1];
- colorTemp = GetStarColorTable(ColorTable_Enhanced);
- #ifdef DEBUG_HDR_FILE
- HDR_LOG.open("hdr.log", ios_base::app);
- #endif
- #ifdef USE_HDR
- blurTextures = new Texture*[BLUR_PASS_COUNT];
- blurTempTexture = NULL;
- for (size_t i = 0; i < BLUR_PASS_COUNT; ++i)
- {
- blurTextures[i] = NULL;
- }
- #endif
- #ifdef USE_BLOOM_LISTS
- for (size_t i = 0; i < (sizeof gaussianLists/sizeof(GLuint)); ++i)
- {
- gaussianLists[i] = 0;
- }
- #endif
- for (int i = 0; i < (int) FontCount; i++)
- {
- font[i] = NULL;
- }
- }
- Renderer::~Renderer()
- {
- if (starVertexBuffer != NULL)
- delete starVertexBuffer;
- if (pointStarVertexBuffer != NULL)
- delete pointStarVertexBuffer;
- delete[] skyVertices;
- delete[] skyIndices;
- delete[] skyContour;
- #ifdef USE_BLOOM_LISTS
- for (size_t i = 0; i < (sizeof gaussianLists/sizeof(GLuint)); ++i)
- {
- if (gaussianLists[i] != 0)
- glDeleteLists(gaussianLists[i], 1);
- }
- #endif
- #ifdef USE_HDR
- for (size_t i = 0; i < BLUR_PASS_COUNT; ++i)
- {
- if (blurTextures[i] != NULL)
- delete blurTextures[i];
- }
- delete [] blurTextures;
- if (blurTempTexture)
- delete blurTempTexture;
- if (sceneTexture != 0)
- glDeleteTextures(1, &sceneTexture);
- #endif
- }
- Renderer::DetailOptions::DetailOptions() :
- ringSystemSections(100),
- orbitPathSamplePoints(100),
- shadowTextureSize(256),
- eclipseTextureSize(128)
- {
- }
- static void StarTextureEval(float u, float v, float,
- unsigned char *pixel)
- {
- float r = 1 - (float) sqrt(u * u + v * v);
- if (r < 0)
- r = 0;
- else if (r < 0.5f)
- r = 2.0f * r;
- else
- r = 1;
- int pixVal = (int) (r * 255.99f);
- pixel[0] = pixVal;
- pixel[1] = pixVal;
- pixel[2] = pixVal;
- }
- static void GlareTextureEval(float u, float v, float,
- unsigned char *pixel)
- {
- float r = 0.9f - (float) sqrt(u * u + v * v);
- if (r < 0)
- r = 0;
- int pixVal = (int) (r * 255.99f);
- pixel[0] = 65;
- pixel[1] = 64;
- pixel[2] = 65;
- pixel[3] = pixVal;
- }
- static void ShadowTextureEval(float u, float v, float,
- unsigned char *pixel)
- {
- float r = (float) sqrt(u * u + v * v);
- // Leave some white pixels around the edges to the shadow doesn't
- // 'leak'. We'll also set the maximum mip map level for this texture to 3
- // so we don't have problems with the edge texels at high mip map levels.
- int pixVal = r < 15.0f / 16.0f ? 0 : 255;
- pixel[0] = pixVal;
- pixel[1] = pixVal;
- pixel[2] = pixVal;
- }
- //! Lookup function for eclipse penumbras--the input is the amount of overlap
- // between the occluder and sun disc, and the output is the fraction of
- // full brightness.
- static void PenumbraFunctionEval(float u, float, float,
- unsigned char *pixel)
- {
- u = (u + 1.0f) * 0.5f;
- // Using the cube root produces a good visual result
- unsigned char pixVal = (unsigned char) (::pow((double) u, 0.33) * 255.99);
- pixel[0] = pixVal;
- }
- // ShadowTextureFunction is a function object for creating shadow textures
- // used for rendering eclipses.
- class ShadowTextureFunction : public TexelFunctionObject
- {
- public:
- ShadowTextureFunction(float _umbra) : umbra(_umbra) {};
- virtual void operator()(float u, float v, float w, unsigned char* pixel);
- float umbra;
- };
- void ShadowTextureFunction::operator()(float u, float v, float,
- unsigned char* pixel)
- {
- float r = (float) sqrt(u * u + v * v);
- int pixVal = 255;
- // Leave some white pixels around the edges to the shadow doesn't
- // 'leak'. We'll also set the maximum mip map level for this texture to 3
- // so we don't have problems with the edge texels at high mip map levels.
- r = r / (15.0f / 16.0f);
- if (r < 1)
- {
- // The pixel value should depend on the area of the sun which is
- // occluded. We just fudge it here and use the square root of the
- // radius.
- if (r <= umbra)
- pixVal = 0;
- else
- pixVal = (int) (sqrt((r - umbra) / (1 - umbra)) * 255.99f);
- }
- pixel[0] = pixVal;
- pixel[1] = pixVal;
- pixel[2] = pixVal;
- };
- class ShadowMaskTextureFunction : public TexelFunctionObject
- {
- public:
- ShadowMaskTextureFunction() {};
- virtual void operator()(float u, float v, float w, unsigned char* pixel);
- float dummy;
- };
- void ShadowMaskTextureFunction::operator()(float u, float, float,
- unsigned char* pixel)
- {
- unsigned char a = u > 0.0f ? 255 : 0;
- pixel[0] = a;
- pixel[1] = a;
- pixel[2] = a;
- pixel[3] = a;
- }
- static void IllumMapEval(float x, float y, float z,
- unsigned char* pixel)
- {
- Vec3f v(x, y, z);
- pixel[0] = 128 + (int) (127 * v.x);
- pixel[1] = 128 + (int) (127 * v.y);
- pixel[2] = 128 + (int) (127 * v.z);
- }
- #if 0
- // Not used yet.
- // The RectToSpherical map converts XYZ coordinates to UV coordinates
- // via a cube map lookup. However, a lot of GPUs don't support linear
- // interpolation of textures with > 8 bits per component, which is
- // inadequate precision for storing texture coordinates. To work around
- // this, we'll store the u and v texture coordinates with two 8 bit
- // coordinates each: rg for u, ba for v. The coordinates are unpacked
- // as: u = r * 255/256 + g * 1/255
- // v = b * 255/256 + a * 1/255
- // This gives an effective precision of 16 bits for each texture coordinate.
- static void RectToSphericalMapEval(float x, float y, float z,
- unsigned char* pixel)
- {
- // Compute spherical coodinates (r is always 1)
- double phi = asin(y);
- double theta = atan2(z, -x);
-
- // Convert to texture coordinates
- double u = (theta / PI + 1.0) * 0.5;
- double v = (-phi / PI + 0.5);
-
- // Pack texture coordinates in red/green and blue/alpha
- // u = red + green/256
- // v = blue* + alpha/256
- uint16 rg = (uint16) (u * 65535.99);
- uint16 ba = (uint16) (v * 65535.99);
- pixel[0] = rg >> 8;
- pixel[1] = rg & 0xff;
- pixel[2] = ba >> 8;
- pixel[3] = ba & 0xff;
- }
- #endif
- static void BuildGaussianDiscMipLevel(unsigned char* mipPixels,
- unsigned int log2size,
- float fwhm,
- float power)
- {
- unsigned int size = 1 << log2size;
- float sigma = fwhm / 2.3548f;
- float isig2 = 1.0f / (2.0f * sigma * sigma);
- float s = 1.0f / (sigma * (float) sqrt(2.0 * PI));
- for (unsigned int i = 0; i < size; i++)
- {
- float y = (float) i - size / 2;
- for (unsigned int j = 0; j < size; j++)
- {
- float x = (float) j - size / 2;
- float r2 = x * x + y * y;
- float f = s * (float) exp(-r2 * isig2) * power;
- mipPixels[i * size + j] = (unsigned char) (255.99f * min(f, 1.0f));
- }
- }
- }
- static void BuildGlareMipLevel(unsigned char* mipPixels,
- unsigned int log2size,
- float scale,
- float base)
- {
- unsigned int size = 1 << log2size;
- for (unsigned int i = 0; i < size; i++)
- {
- float y = (float) i - size / 2;
- for (unsigned int j = 0; j < size; j++)
- {
- float x = (float) j - size / 2;
- float r = (float) sqrt(x * x + y * y);
- float f = (float) pow(base, r * scale);
- mipPixels[i * size + j] = (unsigned char) (255.99f * min(f, 1.0f));
- }
- }
- }
- #if 0
- // An alternate glare function, based roughly on results in Spencer, G. et al,
- // 1995, "Physically-Based Glare Effects for Digital Images"
- static void BuildGlareMipLevel2(unsigned char* mipPixels,
- unsigned int log2size,
- float scale)
- {
- unsigned int size = 1 << log2size;
- for (unsigned int i = 0; i < size; i++)
- {
- float y = (float) i - size / 2;
- for (unsigned int j = 0; j < size; j++)
- {
- float x = (float) j - size / 2;
- float r = (float) sqrt(x * x + y * y);
- float f = 0.3f / (0.3f + r * r * scale * scale * 100);
- /*
- if (i == 0 || j == 0 || i == size - 1 || j == size - 1)
- f = 1.0f;
- */
- mipPixels[i * size + j] = (unsigned char) (255.99f * min(f, 1.0f));
- }
- }
- }
- #endif
- static Texture* BuildGaussianDiscTexture(unsigned int log2size)
- {
- unsigned int size = 1 << log2size;
- Image* img = new Image(GL_LUMINANCE, size, size, log2size + 1);
- for (unsigned int mipLevel = 0; mipLevel <= log2size; mipLevel++)
- {
- float fwhm = (float) pow(2.0f, (float) (log2size - mipLevel)) * 0.3f;
- BuildGaussianDiscMipLevel(img->getMipLevel(mipLevel),
- log2size - mipLevel,
- fwhm,
- (float) pow(2.0f, (float) (log2size - mipLevel)));
- }
- ImageTexture* texture = new ImageTexture(*img,
- Texture::BorderClamp,
- Texture::DefaultMipMaps);
- texture->setBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
- delete img;
- return texture;
- }
- static Texture* BuildGaussianGlareTexture(unsigned int log2size)
- {
- unsigned int size = 1 << log2size;
- Image* img = new Image(GL_LUMINANCE, size, size, log2size + 1);
- for (unsigned int mipLevel = 0; mipLevel <= log2size; mipLevel++)
- {
- /*
- // Optional gaussian glare
- float fwhm = (float) pow(2.0f, (float) (log2size - mipLevel)) * 0.15f;
- float power = (float) pow(2.0f, (float) (log2size - mipLevel)) * 0.15f;
- BuildGaussianDiscMipLevel(img->getMipLevel(mipLevel),
- log2size - mipLevel,
- fwhm,
- power);
- */
- BuildGlareMipLevel(img->getMipLevel(mipLevel),
- log2size - mipLevel,
- 25.0f / (float) pow(2.0f, (float) (log2size - mipLevel)),
- 0.66f);
- /*
- BuildGlareMipLevel2(img->getMipLevel(mipLevel),
- log2size - mipLevel,
- 1.0f / (float) pow(2.0f, (float) (log2size - mipLevel)));
- */
- }
- ImageTexture* texture = new ImageTexture(*img,
- Texture::BorderClamp,
- Texture::DefaultMipMaps);
- texture->setBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
- delete img;
- return texture;
- }
- static int translateLabelModeToClassMask(int labelMode)
- {
- int classMask = 0;
- if (labelMode & Renderer::PlanetLabels)
- classMask |= Body::Planet;
- if (labelMode & Renderer::DwarfPlanetLabels)
- classMask |= Body::DwarfPlanet;
- if (labelMode & Renderer::MoonLabels)
- classMask |= Body::Moon;
- if (labelMode & Renderer::MinorMoonLabels)
- classMask |= Body::MinorMoon;
- if (labelMode & Renderer::AsteroidLabels)
- classMask |= Body::Asteroid;
- if (labelMode & Renderer::CometLabels)
- classMask |= Body::Comet;
- if (labelMode & Renderer::SpacecraftLabels)
- classMask |= Body::Spacecraft;
- return classMask;
- }
- // Depth comparison function for render list entries
- bool operator<(const RenderListEntry& a, const RenderListEntry& b)
- {
- // Operation is reversed because -z axis points into the screen
- return a.centerZ - a.radius > b.centerZ - b.radius;
- }
- // Depth comparison for labels
- // Note that it's essential to declare this operator as a member
- // function of Renderer::Label; if it's not a class member, C++'s
- // argument dependent lookup will not find the operator when it's
- // used as a predicate for STL algorithms.
- bool Renderer::Annotation::operator<(const Annotation& a) const
- {
- // Operation is reversed because -z axis points into the screen
- return position.z > a.position.z;
- }
- // Depth comparison for orbit paths
- bool Renderer::OrbitPathListEntry::operator<(const Renderer::OrbitPathListEntry& o) const
- {
- // Operation is reversed because -z axis points into the screen
- return centerZ - radius > o.centerZ - o.radius;
- }
- bool Renderer::init(GLContext* _context,
- int winWidth, int winHeight,
- DetailOptions& _detailOptions)
- {
- context = _context;
- detailOptions = _detailOptions;
- // Initialize static meshes and textures common to all instances of Renderer
- if (!commonDataInitialized)
- {
- g_lodSphere = new LODSphereMesh();
- starTex = CreateProceduralTexture(64, 64, GL_RGB, StarTextureEval);
- glareTex = LoadTextureFromFile("textures/flare.jpg");
- if (glareTex == NULL)
- glareTex = CreateProceduralTexture(64, 64, GL_RGB, GlareTextureEval);
- // Max mipmap level doesn't work reliably on all graphics
- // cards. In particular, Rage 128 and TNT cards resort to software
- // rendering when this feature is enabled. The only workaround is to
- // disable mipmapping completely unless texture border clamping is
- // supported, which solves the problem much more elegantly than all
- // the mipmap level nonsense.
- // shadowTex->setMaxMipMapLevel(3);
- Texture::AddressMode shadowTexAddress = Texture::EdgeClamp;
- Texture::MipMapMode shadowTexMip = Texture::NoMipMaps;
- useClampToBorder = context->extensionSupported("GL_ARB_texture_border_clamp");
- if (useClampToBorder)
- {
- shadowTexAddress = Texture::BorderClamp;
- shadowTexMip = Texture::DefaultMipMaps;
- }
- shadowTex = CreateProceduralTexture(detailOptions.shadowTextureSize,
- detailOptions.shadowTextureSize,
- GL_RGB,
- ShadowTextureEval,
- shadowTexAddress, shadowTexMip);
- shadowTex->setBorderColor(Color::White);
- if (gaussianDiscTex == NULL)
- gaussianDiscTex = BuildGaussianDiscTexture(8);
- if (gaussianGlareTex == NULL)
- gaussianGlareTex = BuildGaussianGlareTexture(9);
- // Create the eclipse shadow textures
- {
- for (int i = 0; i < 4; i++)
- {
- ShadowTextureFunction func(i * 0.25f);
- eclipseShadowTextures[i] =
- CreateProceduralTexture(detailOptions.eclipseTextureSize,
- detailOptions.eclipseTextureSize,
- GL_RGB, func,
- shadowTexAddress, shadowTexMip);
- if (eclipseShadowTextures[i] != NULL)
- {
- // eclipseShadowTextures[i]->setMaxMipMapLevel(2);
- eclipseShadowTextures[i]->setBorderColor(Color::White);
- }
- }
- }
- // Create the shadow mask texture
- {
- ShadowMaskTextureFunction func;
- shadowMaskTexture = CreateProceduralTexture(128, 2, GL_RGBA, func);
- //shadowMaskTexture->bindName();
- }
- // Create a function lookup table in a texture for use with
- // fragment program eclipse shadows.
- penumbraFunctionTexture = CreateProceduralTexture(512, 1, GL_LUMINANCE,
- PenumbraFunctionEval,
- Texture::EdgeClamp);
- if (context->extensionSupported("GL_ARB_texture_cube_map"))
- {
- normalizationTex = CreateProceduralCubeMap(64, GL_RGB, IllumMapEval);
- #if ADVANCED_CLOUD_SHADOWS
- rectToSphericalTexture = CreateProceduralCubeMap(128, GL_RGBA, RectToSphericalMapEval);
- #endif
- }
- #ifdef USE_HDR
- genSceneTexture();
- genBlurTextures();
- #endif
- commonDataInitialized = true;
- }
- #if 0
- if (context->extensionSupported("GL_ARB_multisample"))
- {
- int nSamples = 0;
- int sampleBuffers = 0;
- int enabled = (int) glIsEnabled(GL_MULTISAMPLE_ARB);
- glGetIntegerv(GL_SAMPLE_BUFFERS_ARB, &sampleBuffers);
- glGetIntegerv(GL_SAMPLES_ARB, &nSamples);
- clog << "AA samples: " << nSamples
- << ", enabled=" << (int) enabled
- << ", sample buffers=" << (sampleBuffers)
- << "n";
- glEnable(GL_MULTISAMPLE_ARB);
- }
- #endif
- if (context->extensionSupported("GL_EXT_rescale_normal"))
- {
- // We need this enabled because we use glScale, but only
- // with uniform scale factors.
- DPRINTF(1, "Renderer: EXT_rescale_normal supported.n");
- useRescaleNormal = true;
- glEnable(GL_RESCALE_NORMAL_EXT);
- }
- if (context->extensionSupported("GL_ARB_point_sprite"))
- {
- DPRINTF(1, "Renderer: point sprites supported.n");
- usePointSprite = true;
- }
- if (context->extensionSupported("GL_EXT_separate_specular_color"))
- {
- glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT, GL_SEPARATE_SPECULAR_COLOR_EXT);
- }
- // Ugly renderer-specific bug workarounds follow . . .
- char* glRenderer = (char*) glGetString(GL_RENDERER);
- if (glRenderer != NULL)
- {
- // Fog is broken with vertex program emulation in most versions of
- // the GF 1 and 2 drivers; we need to detect this and disable
- // vertex programs which output fog coordinates
- if (strstr(glRenderer, "GeForce3") != NULL ||
- strstr(glRenderer, "GeForce4") != NULL)
- {
- buggyVertexProgramEmulation = false;
- }
- if (strstr(glRenderer, "Savage4") != NULL ||
- strstr(glRenderer, "ProSavage") != NULL)
- {
- // S3 Savage4 drivers appear to rescale normals without reporting
- // EXT_rescale_normal. Lighting will be messed up unless
- // we set the useRescaleNormal flag.
- useRescaleNormal = true;
- }
- #ifdef TARGET_OS_MAC
- if (strstr(glRenderer, "ATI") != NULL ||
- strstr(glRenderer, "GMA 900") != NULL)
- {
- // Some drivers on the Mac appear to limit point sprite size.
- // This causes an abrupt size transition when going from billboards
- // to sprites. Rather than incur overhead accounting for the size limit,
- // do not use sprites on these renderers.
- // Affected cards: ATI (various), etc
- // Renderer strings are not unique.
- usePointSprite = false;
- }
- #endif
- }
- // More ugly hacks; according to Matt Craighead at NVIDIA, an NVIDIA
- // OpenGL driver that reports version 1.3.1 or greater will have working
- // fog in emulated vertex programs.
- char* glVersion = (char*) glGetString(GL_VERSION);
- if (glVersion != NULL)
- {
- int major = 0, minor = 0, extra = 0;
- int nScanned = sscanf(glVersion, "%d.%d.%d", &major, &minor, &extra);
- if (nScanned >= 2)
- {
- if (major > 1 || minor > 3 || (minor == 3 && extra >= 1))
- buggyVertexProgramEmulation = false;
- }
- }
- #ifdef USE_HDR
- useBlendSubtract = context->extensionSupported("GL_EXT_blend_subtract");
- Image *testImg = new Image(GL_LUMINANCE_ALPHA, 1, 1);
- ImageTexture *testTex = new ImageTexture(*testImg,
- Texture::EdgeClamp,
- Texture::NoMipMaps);
- delete testImg;
- GLint actualTexFormat = 0;
- glEnable(GL_TEXTURE_2D);
- testTex->bind();
- glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &actualTexFormat);
- glBindTexture(GL_TEXTURE_2D, 0);
- glDisable(GL_TEXTURE_2D);
- switch (actualTexFormat)
- {
- case 2:
- case GL_LUMINANCE_ALPHA:
- case GL_LUMINANCE4_ALPHA4:
- case GL_LUMINANCE6_ALPHA2:
- case GL_LUMINANCE8_ALPHA8:
- case GL_LUMINANCE12_ALPHA4:
- case GL_LUMINANCE12_ALPHA12:
- case GL_LUMINANCE16_ALPHA16:
- useLuminanceAlpha = true;
- break;
- default:
- useLuminanceAlpha = false;
- break;
- }
- blurFormat = useLuminanceAlpha ? GL_LUMINANCE_ALPHA : GL_RGBA;
- delete testTex;
- #endif
- glLoadIdentity();
- glEnable(GL_CULL_FACE);
- glCullFace(GL_BACK);
- glEnable(GL_COLOR_MATERIAL);
- glEnable(GL_LIGHTING);
- glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
- // LEQUAL rather than LESS required for multipass rendering
- glDepthFunc(GL_LEQUAL);
- resize(winWidth, winHeight);
- return true;
- }
- void Renderer::resize(int width, int height)
- {
- #ifdef USE_HDR
- if (width == windowWidth && height == windowHeight)
- return;
- #endif
- windowWidth = width;
- windowHeight = height;
- cosViewConeAngle = computeCosViewConeAngle(fov, windowWidth, windowHeight);
- // glViewport(windowWidth, windowHeight);
- #ifdef USE_HDR
- if (commonDataInitialized)
- {
- genSceneTexture();
- genBlurTextures();
- }
- #endif
- }
- float Renderer::calcPixelSize(float fovY, float windowHeight)
- {
- return 2 * (float) tan(degToRad(fovY / 2.0)) / (float) windowHeight;
- }
- void Renderer::setFieldOfView(float _fov)
- {
- fov = _fov;
- corrFac = (0.12f * fov/FOV * fov/FOV + 1.0f);
- cosViewConeAngle = computeCosViewConeAngle(fov, windowWidth, windowHeight);
- }
- int Renderer::getScreenDpi() const
- {
- return screenDpi;
- }
- void Renderer::setScreenDpi(int _dpi)
- {
- screenDpi = _dpi;
- }
- void Renderer::setFaintestAM45deg(float _faintestAutoMag45deg)
- {
- faintestAutoMag45deg = _faintestAutoMag45deg;
- markSettingsChanged();
- }
- float Renderer::getFaintestAM45deg() const
- {
- return faintestAutoMag45deg;
- }
- unsigned int Renderer::getResolution() const
- {
- return textureResolution;
- }
- void Renderer::setResolution(unsigned int resolution)
- {
- if (resolution < TEXTURE_RESOLUTION)
- textureResolution = resolution;
- markSettingsChanged();
- }
- TextureFont* Renderer::getFont(FontStyle fs) const
- {
- return font[(int) fs];
- }
- void Renderer::setFont(FontStyle fs, TextureFont* txf)
- {
- font[(int) fs] = txf;
- markSettingsChanged();
- }
- void Renderer::setRenderMode(int _renderMode)
- {
- renderMode = _renderMode;
- markSettingsChanged();
- }
- int Renderer::getRenderFlags() const
- {
- return renderFlags;
- }
- void Renderer::setRenderFlags(int _renderFlags)
- {
- renderFlags = _renderFlags;
- markSettingsChanged();
- }
- int Renderer::getLabelMode() const
- {
- return labelMode;
- }
- void Renderer::setLabelMode(int _labelMode)
- {
- labelMode = _labelMode;
- markSettingsChanged();
- }
- int Renderer::getOrbitMask() const
- {
- return orbitMask;
- }
- void Renderer::setOrbitMask(int mask)
- {
- orbitMask = mask;
- markSettingsChanged();
- }
- const ColorTemperatureTable*
- Renderer::getStarColorTable() const
- {
- return colorTemp;
- }
- void
- Renderer::setStarColorTable(const ColorTemperatureTable* ct)
- {
- colorTemp = ct;
- markSettingsChanged();
- }
- bool Renderer::getVideoSync() const
- {
- return videoSync;
- }
- void Renderer::setVideoSync(bool sync)
- {
- videoSync = sync;
- markSettingsChanged();
- }
- float Renderer::getAmbientLightLevel() const
- {
- return ambientLightLevel;
- }
- void Renderer::setAmbientLightLevel(float level)
- {
- ambientLightLevel = level;
- markSettingsChanged();
- }
- float Renderer::getMinimumFeatureSize() const
- {
- return minFeatureSize;
- }
- void Renderer::setMinimumFeatureSize(float pixels)
- {
- minFeatureSize = pixels;
- markSettingsChanged();
- }
- float Renderer::getMinimumOrbitSize() const
- {
- return minOrbitSize;
- }
- // Orbits and labels are only rendered when the orbit of the object
- // occupies some minimum number of pixels on screen.
- void Renderer::setMinimumOrbitSize(float pixels)
- {
- minOrbitSize = pixels;
- markSettingsChanged();
- }
- float Renderer::getDistanceLimit() const
- {
- return distanceLimit;
- }
- void Renderer::setDistanceLimit(float distanceLimit_)
- {
- distanceLimit = distanceLimit_;
- markSettingsChanged();
- }
- bool Renderer::getFragmentShaderEnabled() const
- {
- return fragmentShaderEnabled;
- }
- void Renderer::setFragmentShaderEnabled(bool enable)
- {
- fragmentShaderEnabled = enable && fragmentShaderSupported();
- markSettingsChanged();
- }
- bool Renderer::fragmentShaderSupported() const
- {
- return context->bumpMappingSupported();
- }
- bool Renderer::getVertexShaderEnabled() const
- {
- return vertexShaderEnabled;
- }
- void Renderer::setVertexShaderEnabled(bool enable)
- {
- vertexShaderEnabled = enable && vertexShaderSupported();
- markSettingsChanged();
- }
- bool Renderer::vertexShaderSupported() const
- {
- return useVertexPrograms;
- }
- void Renderer::addAnnotation(vector<Annotation>& annotations,
- const MarkerRepresentation* markerRep,
- const string& labelText,
- Color color,
- const Point3f& pos,
- LabelAlignment halign,
- LabelVerticalAlignment valign,
- float size)
- {
- double winX, winY, winZ;
- int view[4] = { 0, 0, 0, 0 };
- view[0] = -windowWidth / 2;
- view[1] = -windowHeight / 2;
- view[2] = windowWidth;
- view[3] = windowHeight;
- float depth = (float) (pos.x * modelMatrix[2] +
- pos.y * modelMatrix[6] +
- pos.z * modelMatrix[10]);
- if (gluProject(pos.x, pos.y, pos.z,
- modelMatrix,
- projMatrix,
- (const GLint*) view,
- &winX, &winY, &winZ) != GL_FALSE)
- {
- Annotation a;
- a.labelText[0] = ' ';
- ReplaceGreekLetterAbbr(a.labelText, MaxLabelLength, labelText.c_str(), labelText.length());
- a.labelText[MaxLabelLength - 1] = ' ';
- a.markerRep = markerRep;
- a.color = color;
- a.position = Point3f((float) winX, (float) winY, -depth);
- a.halign = halign;
- a.valign = valign;
- a.size = size;
- annotations.push_back(a);
- }
- }
- void Renderer::addForegroundAnnotation(const MarkerRepresentation* markerRep,
- const string& labelText,
- Color color,
- const Point3f& pos,
- LabelAlignment halign,
- LabelVerticalAlignment valign,
- float size)
- {
- addAnnotation(foregroundAnnotations, markerRep, labelText, color, pos, halign, valign, size);
- }
- void Renderer::addBackgroundAnnotation(const MarkerRepresentation* markerRep,
- const string& labelText,
- Color color,
- const Point3f& pos,
- LabelAlignment halign,
- LabelVerticalAlignment valign,
- float size)
- {
- addAnnotation(backgroundAnnotations, markerRep, labelText, color, pos, halign, valign, size);
- }
- void Renderer::addSortedAnnotation(const MarkerRepresentation* markerRep,
- const string& labelText,
- Color color,
- const Point3f& pos,
- LabelAlignment halign,
- LabelVerticalAlignment valign,
- float size)
- {
- double winX, winY, winZ;
- int view[4] = { 0, 0, 0, 0 };
- view[0] = -windowWidth / 2;
- view[1] = -windowHeight / 2;
- view[2] = windowWidth;
- view[3] = windowHeight;
- float depth = (float) (pos.x * modelMatrix[2] +
- pos.y * modelMatrix[6] +
- pos.z * modelMatrix[10]);
- if (gluProject(pos.x, pos.y, pos.z,
- modelMatrix,
- projMatrix,
- (const GLint*) view,
- &winX, &winY, &winZ) != GL_FALSE)
- {
- Annotation a;
-
- a.labelText[0] = ' ';
- if (markerRep == NULL)
- {
- //l.text = ReplaceGreekLetterAbbr(_(text.c_str()));
- strncpy(a.labelText, labelText.c_str(), MaxLabelLength);
- a.labelText[MaxLabelLength - 1] = ' ';
- }
- a.markerRep = markerRep;
- a.color = color;
- a.position = Point3f((float) winX, (float) winY, -depth);
- a.halign = halign;
- a.valign = valign;
- a.size = size;
- depthSortedAnnotations.push_back(a);
- }
- }
- void Renderer::clearAnnotations(vector<Annotation>& annotations)
- {
- annotations.clear();
- }
- void Renderer::clearSortedAnnotations()
- {
- depthSortedAnnotations.clear();
- }
- // Return the orientation of the camera used to render the current
- // frame. Available only while rendering a frame.
- Quatf Renderer::getCameraOrientation() const
- {
- return m_cameraOrientation;
- }
- float Renderer::getNearPlaneDistance() const
- {
- return depthPartitions[currentIntervalIndex].nearZ;
- }
- void Renderer::beginObjectAnnotations()
- {
- // It's an error to call beginObjectAnnotations a second time
- // without first calling end.
- assert(!objectAnnotationSetOpen);
- assert(objectAnnotations.empty());
- objectAnnotations.clear();
- objectAnnotationSetOpen = true;
- }
- void Renderer::endObjectAnnotations()
- {
- objectAnnotationSetOpen = false;
-
- if (!objectAnnotations.empty())
- {
- renderAnnotations(objectAnnotations.begin(),
- objectAnnotations.end(),
- -depthPartitions[currentIntervalIndex].nearZ,
- -depthPartitions[currentIntervalIndex].farZ,
- FontNormal);
- objectAnnotations.clear();
- }
- }
- void Renderer::addObjectAnnotation(const MarkerRepresentation* markerRep,
- const string& labelText,
- Color color,
- const Point3f& pos)
- {
- assert(objectAnnotationSetOpen);
- if (objectAnnotationSetOpen)
- {
- double winX, winY, winZ;
- int view[4] = { 0, 0, 0, 0 };
- view[0] = -windowWidth / 2;
- view[1] = -windowHeight / 2;
- view[2] = windowWidth;
- view[3] = windowHeight;
- float depth = (float) (pos.x * modelMatrix[2] +
- pos.y * modelMatrix[6] +
- pos.z * modelMatrix[10]);
- if (gluProject(pos.x, pos.y, pos.z,
- modelMatrix,
- projMatrix,
- (const GLint*) view,
- &winX, &winY, &winZ) != GL_FALSE)
- {
- Annotation a;
-
- a.labelText[0] = ' ';
- if (!labelText.empty())
- {
- strncpy(a.labelText, labelText.c_str(), MaxLabelLength);
- a.labelText[MaxLabelLength - 1] = ' ';
- }
- a.markerRep = markerRep;
- a.color = color;
- a.position = Point3f((float) winX, (float) winY, -depth);
- a.size = 0.0f;
- objectAnnotations.push_back(a);
- }
- }
- }
- static void enableSmoothLines()
- {
- // glEnable(GL_BLEND);
- #ifdef USE_HDR
- glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
- #else
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- #endif
- glEnable(GL_LINE_SMOOTH);
- glLineWidth(1.5f);
- }
- static void disableSmoothLines()
- {
- // glDisable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- glDisable(GL_LINE_SMOOTH);
- glLineWidth(1.0f);
- }
- class OrbitSampler : public OrbitSampleProc
- {
- public:
- vector<Renderer::OrbitSample>* samples;
- OrbitSampler(vector<Renderer::OrbitSample>* _samples) : samples(_samples) {};
- void sample(double t, const Point3d& p)
- {
- Renderer::OrbitSample samp;
- samp.pos = p;
- samp.t = t;
- samples->push_back(samp);
- };
- };
- void renderOrbitColor(const Body *body, bool selected, float opacity)
- {
- Color orbitColor;
- if (selected)
- {
- // Highlight the orbit of the selected object in red
- orbitColor = Renderer::SelectionOrbitColor;
- }
- else if (body != NULL && body->isOrbitColorOverridden())
- {
- orbitColor = body->getOrbitColor();
- }
- else
- {
- int classification;
- if (body != NULL)
- classification = body->getOrbitClassification();
- else
- classification = Body::Stellar;
- switch (classification)
- {
- case Body::Moon:
- orbitColor = Renderer::MoonOrbitColor;
- break;
- case Body::MinorMoon:
- orbitColor = Renderer::MinorMoonOrbitColor;
- break;
- case Body::Asteroid:
- orbitColor = Renderer::AsteroidOrbitColor;
- break;
- case Body::Comet:
- orbitColor = Renderer::CometOrbitColor;
- break;
- case Body::Spacecraft:
- orbitColor = Renderer::SpacecraftOrbitColor;
- break;
- case Body::Stellar:
- orbitColor = Renderer::StarOrbitColor;
- break;
- case Body::DwarfPlanet:
- orbitColor = Renderer::DwarfPlanetOrbitColor;
- break;
- case Body::Planet:
- default:
- orbitColor = Renderer::PlanetOrbitColor;
- break;
- }
- }
- #ifdef USE_HDR
- glColor(orbitColor, 1.f - opacity * orbitColor.alpha());
- #else
- glColor(orbitColor, opacity * orbitColor.alpha());
- #endif
- }
- // Subdivide the orbit into sections and compute a bounding volume for each section. The bounding
- // volumes used are capsules, the set of all points less than some constant distance from a line
- // segment.
- static void computeOrbitSectionBoundingVolumes(Renderer::CachedOrbit& orbit)
- {
- const unsigned int MinOrbitSections = 6;
- const unsigned int MinSamplesPerSection = 32;
- // Determine the number of trajectory samples to include in each bounding volume; typically,
- // the last volume will contain any leftover samples.
- unsigned int nSections = max((unsigned int) orbit.trajectory.size() / MinSamplesPerSection, MinOrbitSections);
- unsigned int samplesPerSection = orbit.trajectory.size() / nSections;
- if (samplesPerSection <= 1)
- {
- if (orbit.trajectory.size() == 0)
- nSections = 0;
- else
- nSections = 1;
- }
- for (unsigned int i = 0; i < nSections; i++)
- {
- unsigned int nSamples;
- if (i != nSections - 1)
- nSamples = samplesPerSection;
- else
- nSamples = orbit.trajectory.size() - (nSections - 1) * samplesPerSection;
- Renderer::OrbitSection section;
- section.firstSample = samplesPerSection * i;
- unsigned int lastSample = min((unsigned int) orbit.trajectory.size() - 1, section.firstSample + nSamples + 1);
- // Set the initial axis and origin of the capsule bounding volume; they will be adjusted
- // to contain all points in the trajectory. The length of the axis may change, but the
- // direction will remain the same.
- Vec3d axis = orbit.trajectory[section.firstSample].pos - orbit.trajectory[lastSample].pos;
- Point3d orig = orbit.trajectory[section.firstSample].pos;
- double d = 1.0 / (axis * axis);
- double minT = 0.0;
- double maxT = 0.0;
- double maxDistSquared = 0.0;
- for (unsigned int j = section.firstSample; j <= lastSample; j++)
- {
- Point3d p = orbit.trajectory[j].pos;
- double t = ((p - orig) * axis) * d;
- Vec3d pointToAxis = p - (orig + axis * t);
- double distSquared = pointToAxis * pointToAxis;
- if (t < minT)
- minT = t;
- if (t > maxT)
- maxT = t;
- if (distSquared > maxDistSquared)
- maxDistSquared = distSquared;
- }
- section.boundingVolume.origin = orig + axis * minT;
- section.boundingVolume.axis = axis * (maxT - minT);
- // Make the bounding volume a bit thicker to avoid roundoff problems, and
- // to account cases when interpolation adds points slightly outside the
- // volume defined by the sampled points.
- section.boundingVolume.radius = sqrt(maxDistSquared) * 1.1f;;
- orbit.sections.push_back(section);
- }
- }
- static Point3d cubicInterpolate(const Point3d& p0, const Vec3d& v0,
- const Point3d& p1, const Vec3d& v1,
- double t)
- {
- return p0 + (((2.0 * (p0 - p1) + v1 + v0) * (t * t * t)) +
- ((3.0 * (p1 - p0) - 2.0 * v0 - v1) * (t * t)) +
- (v0 * t));
- }
- static int splinesRendered = 0;
- static int orbitsRendered = 0;
- static int orbitsSkipped = 0;
- static int sectionsCulled = 0;
- static Point3d renderOrbitSplineSegment(const Renderer::OrbitSample& p0,
- const Renderer::OrbitSample& p1,
- const Renderer::OrbitSample& p2,
- const Renderer::OrbitSample& p3,
- double nearZ,
- double farZ,
- unsigned int subdivisions,
- int lastOutcode,
- bool drawLastSegment)
- {
- Vec3d v0 = (p2.pos - p0.pos) * ((p2.t - p1.t) / (p2.t - p0.t));
- Vec3d v1 = (p3.pos - p1.pos) * ((p2.t - p1.t) / (p3.t - p1.t));
- double dt = 1.0 / (double) subdivisions;
- if (drawLastSegment)
- subdivisions++;
- splinesRendered++;
- Point3d lastP = p1.pos;
- for (unsigned int i = 0; i < subdivisions; i++)
- {
- Point3d p = cubicInterpolate(p1.pos, v0, p2.pos, v1, i * dt);
- int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
- if ((outcode | lastOutcode) == 0)
- {
- glVertex3d(p.x, p.y, p.z);
- }
- else if ((outcode & lastOutcode) == 0)
- {
- // Need to clip
- Point3d q0 = lastP;
- Point3d q1 = p;
- if (lastOutcode != 0)
- {
- glBegin(GL_LINE_STRIP);
- double t;
- if (lastOutcode == 1)
- t = (nearZ - lastP.z) / (p.z - lastP.z);
- else
- t = (farZ - lastP.z) / (p.z - lastP.z);
- q0 = lastP + t * (p - lastP);
- }
- if (outcode != 0)
- {
- double t;
- if (outcode == 1)
- t = (nearZ - lastP.z) / (p.z - lastP.z);
- else
- t = (farZ - lastP.z) / (p.z - lastP.z);
- q1 = lastP + t * (p - lastP);
- }
- glVertex3d(q0.x, q0.y, q0.z);
- glVertex3d(q1.x, q1.y, q1.z);
- if (outcode != 0)
- {
- glEnd();
- }
- }
- lastOutcode = outcode;
- lastP = p;
- }
- return lastP;
- }
- #if 0
- // Not yet used
- static Point3d renderOrbitSplineAdaptive(const Renderer::OrbitSample& p0,
- const Renderer::OrbitSample& p1,
- const Renderer::OrbitSample& p2,
- const Renderer::OrbitSample& p3,
- double nearZ,
- double farZ,
- unsigned int minSubdivisions,
- unsigned int maxSubdivisions,
- int lastOutcode,
- bool drawLastSegment)
- {
- Vec3d v0 = (p2.pos - p0.pos) * ((p2.t - p1.t) / (p2.t - p0.t));
- Vec3d v1 = (p3.pos - p1.pos) * ((p2.t - p1.t) / (p3.t - p1.t));
- double minDt = 1.0 / (double) maxSubdivisions;
- double maxDt = 1.0 / (double) minSubdivisions;
- double g = (p2.pos - p1.pos).length() * maxSubdivisions;
- double t = 0.0;
- splinesRendered += 10000;
- Point3d lastP = p1.pos;
- while (t < 1.0)
- {
- t += max(minDt, max(lastP.distanceFromOrigin() / g, maxDt));
- if (drawLastSegment && t > 1.0)
- t = 1.0;
- else
- break;
- Point3d p = cubicInterpolate(p1.pos, v0, p2.pos, v1, t);
- int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
- if ((outcode | lastOutcode) == 0)
- {
- glVertex3d(p.x, p.y, p.z);
- }
- else if ((outcode & lastOutcode) == 0)
- {
- // Need to clip
- Point3d q0 = lastP;
- Point3d q1 = p;
- if (lastOutcode != 0)
- {
- glBegin(GL_LINE_STRIP);
- double t;
- if (lastOutcode == 1)
- t = (nearZ - lastP.z) / (p.z - lastP.z);
- else
- t = (farZ - lastP.z) / (p.z - lastP.z);
- q0 = lastP + t * (p - lastP);
- }
- if (outcode != 0)
- {
- double t;
- if (outcode == 1)
- t = (nearZ - lastP.z) / (p.z - lastP.z);
- else
- t = (farZ - lastP.z) / (p.z - lastP.z);
- q1 = lastP + t * (p - lastP);
- }
- glVertex3d(q0.x, q0.y, q0.z);
- glVertex3d(q1.x, q1.y, q1.z);
- if (outcode != 0)
- {
- glEnd();
- }
- }
- lastOutcode = outcode;
- lastP = p;
- }
- return lastP;
- }
- #endif
- static Point3d renderOrbitSection(const Orbit& orbit,
- Renderer::CachedOrbit& cachedOrbit,
- unsigned int sectionNumber,
- const Mat4d& modelview,
- Point3d lastP,
- int lastOutcode,
- double nearZ,
- double farZ,
- uint32 /* renderFlags */)
- {
- vector<Renderer::OrbitSample>& trajectory(cachedOrbit.trajectory);
- unsigned int nPoints = cachedOrbit.trajectory.size();
- unsigned int firstPoint = cachedOrbit.sections[sectionNumber].firstSample + 1;
- unsigned int lastPoint;
- if (sectionNumber != cachedOrbit.sections.size() - 1)
- lastPoint = cachedOrbit.sections[sectionNumber + 1].firstSample;
- else
- lastPoint = nPoints - 1;
- double sectionRadius = cachedOrbit.sections[sectionNumber].boundingVolume.radius;
- double minSmoothZ = -sectionRadius * 8;
- double maxSmoothZ = nearZ + sectionRadius * 8;
- for (unsigned int i = firstPoint; i <= lastPoint; i++)
- {
- Point3d p = trajectory[i].pos * modelview;
- int outcode;
- // This segment of the orbit is very close to the camera and may appear
- // jagged. We'll add extra segments with cubic spline interpolation.
- // TODO: This calculation should depend on the field of view as well
- unsigned int splineSubdivisions = 0;
- if ((p.z > minSmoothZ || lastP.z > minSmoothZ) &&
- !(p.z > maxSmoothZ && lastP.z > maxSmoothZ))
- {
- double distFromEye = distanceToSegment(Point3d(0.0, 0.0, 0.0), lastP, p - lastP);
- if (distFromEye < sectionRadius)
- splineSubdivisions = 64;
- else if (distFromEye < sectionRadius * 8)
- splineSubdivisions = (unsigned int) (sectionRadius / distFromEye * 64);
- }
- if (splineSubdivisions > 1)
- {
- // Render this part of the orbit as a spline instead of a line segment
- Renderer::OrbitSample s0, s1, s2, s3;
- if (i > 1)
- {
- s0 = trajectory[i - 2];
- }
- else if (orbit.isPeriodic())
- {
- // Careful: use second to last sample, since first sample is duplicate of last
- s0 = trajectory[nPoints - 2];
- s0.t -= orbit.getPeriod();
- }
- else
- {
- s0 = trajectory[i - 1];
- }
- if (i < trajectory.size() - 1)
- {
- s3 = trajectory[i + 1];
- }
- else if (orbit.isPeriodic())
- {
- // Careful: use second sample, since first sample is duplicate of last
- s3 = trajectory[1];
- s3.t += orbit.getPeriod();
- }
- else
- {
- s3 = trajectory[i];
- }
- s1 = Renderer::OrbitSample(lastP, trajectory[i - 1].t);
- s2 = Renderer::OrbitSample(p, trajectory[i].t);
- s0.pos = s0.pos * modelview;
- s3.pos = s3.pos * modelview;
- bool drawLastSegment = i == nPoints - 1;
- p = renderOrbitSplineSegment(s0, s1, s2, s3,
- nearZ, farZ,
- splineSubdivisions,
- lastOutcode,
- drawLastSegment);
- outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
- }
- else
- {
- // Just draw a line segment
- // Compute the outcode mask for clipping:
- // 00 = between near and far
- // 01 = greater than nearZ (nearZ and farZ are always negative)
- // 10 = less than farZ
- // Given two outcodes o1 and o2 of line segment endpoints:
- // o1 | o2 == 0 means segment lies completely between near and far plans
- // o2 & o2 != 0 means segment lies completely outside planes
- // else we have to clip the line.
- outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
- if ((outcode | lastOutcode) == 0)
- {
- // Segment is completely between near and far planes
- glVertex3d(p.x, p.y, p.z);
- }
- else if ((outcode & lastOutcode) == 0)
- {
- // Need to clip
- Point3d q0 = lastP;
- Point3d q1 = p;
- // Clip against the enter plane
- if (lastOutcode != 0)
- {
- glBegin(GL_LINE_STRIP);
- double t;
- if (lastOutcode == 1)
- t = (nearZ - lastP.z) / (p.z - lastP.z);
- else
- t = (farZ - lastP.z) / (p.z - lastP.z);
- q0 = lastP + t * (p - lastP);
- }
- // Clip against the exit plane
- if (outcode != 0)
- {
- double t;
- if (outcode == 1)
- t = (nearZ - lastP.z) / (p.z - lastP.z);
- else
- t = (farZ - lastP.z) / (p.z - lastP.z);
- q1 = lastP + t * (p - lastP);
- }
- glVertex3d(q0.x, q0.y, q0.z);
- glVertex3d(q1.x, q1.y, q1.z);
- if (outcode != 0)
- {
- glEnd();
- }
- }
- }
- lastOutcode = outcode;
- lastP = p;
- }
- return lastP;
- }
- void Renderer::renderOrbit(const OrbitPathListEntry& orbitPath,
- double t,
- const Quatf& cameraOrientationf,
- const Frustum& frustum,
- float nearDist,
- float farDist)
- {
- Body* body = orbitPath.body;
- Quatd cameraOrientation(cameraOrientationf.w, cameraOrientationf.x, cameraOrientationf.y, cameraOrientationf.z);
- double nearZ = -nearDist; // negate, becase z is into the screen in camera space
- double farZ = -farDist;
- const Orbit* orbit = NULL;
- if (body != NULL)
- orbit = body->getOrbit(t);
- else
- orbit = orbitPath.star->getOrbit();
- CachedOrbit* cachedOrbit = NULL;
- OrbitCache::iterator cached = orbitCache.find(orbit);
- if (cached != orbitCache.end())
- {
- cachedOrbit = cached->second;
- cachedOrbit->lastUsed = frameCount;
- }
- // If it's not in the cache already
- if (cachedOrbit == NULL)
- {
- double startTime = t;
- int nSamples = detailOptions.orbitPathSamplePoints;
- // Adjust the number of samples used for aperiodic orbits--these aren't
- // true orbits, but are sampled trajectories, generally of spacecraft.
- // Better control is really needed--some sort of adaptive sampling would
- // be ideal.
- if (!orbit->isPeriodic())
- {
- double begin = 0.0, end = 0.0;
- orbit->getValidRange(begin, end);
- if (begin != end)
- {
- startTime = begin;
- nSamples = (int) (orbit->getPeriod() * 100.0);
- nSamples = max(min(nSamples, 1000), 100);
- }
- else
- {
- // If the orbit is aperiodic and doesn't have a
- // finite duration, we don't render it. A compromise
- // would be to pick some time window centered at the
- // current time, but we'd have to pick some arbitrary
- // duration.
- nSamples = 0;
- }
- }
- cachedOrbit = new CachedOrbit();
- cachedOrbit->lastUsed = frameCount;
- OrbitSampler sampler(&cachedOrbit->trajectory);
- orbit->sample(startTime,
- orbit->getPeriod(),
- nSamples,
- sampler);
- // Add an extra sample to close a periodic orbit
- if (orbit->isPeriodic())
- {
- if (!cachedOrbit->trajectory.empty())
- {
- double lastSampleTime = cachedOrbit->trajectory[0].t + orbit->getPeriod();
- cachedOrbit->trajectory.push_back(OrbitSample(cachedOrbit->trajectory[0].pos, lastSampleTime));
- }
- }
- computeOrbitSectionBoundingVolumes(*cachedOrbit);
- // If the orbit cache is full, first try and eliminate some old orbits
- if (orbitCache.size() > OrbitCacheCullThreshold)
- {
- // Check for old orbits at most once per frame
- if (lastOrbitCacheFlush != frameCount)
- {
- for (OrbitCache::iterator iter = orbitCache.begin(); iter != orbitCache.end();)
- {
- // Tricky code to eliminate a node in the orbit cache without screwing
- // up the iterator. Should work in all STL implementations.
- if (frameCount - iter->second->lastUsed > OrbitCacheRetireAge)
- orbitCache.erase(iter++);
- else
- ++iter;
- }
- lastOrbitCacheFlush = frameCount;
- }
- }
- orbitCache[orbit] = cachedOrbit;
- }
- vector<OrbitSample>* trajectory = &cachedOrbit->trajectory;
- // The rest of the function isn't designed for empty trajectories
- if (trajectory->empty())
- return;
- // We perform vertex tranformations on the CPU because double precision is necessary to
- // render orbits properly. Start by computing the modelview matrix, to transform orbit
- // vertices into camera space.
- Mat4d modelview;
- {
- Quatd orientation(1.0);
- if (body != NULL)
- {
- orientation = body->getOrbitFrame(t)->getOrientation(t);
- }
- // Equivalent to:
- // glRotate(cameraOrientation);
- // glTranslate(orbitPath.origin);
- // glRotate(~orientation);
- modelview =
- (orientation).toMatrix4() *
- Mat4d::translation(Point3d(orbitPath.origin.x, orbitPath.origin.y, orbitPath.origin.z)) *
- (~cameraOrientation).toMatrix4();
- }
- glPushMatrix();
- glLoadIdentity();
- bool highlight;
- if (body != NULL)
- highlight = highlightObject.body() == body;
- else
- highlight = highlightObject.star() == orbitPath.star;
- renderOrbitColor(body, highlight, orbitPath.opacity);
- if ((renderFlags & ShowPartialTrajectories) == 0 || orbit->isPeriodic())
- {
- // Show the complete trajectory
- Point3d p;
- p = (*trajectory)[0].pos * modelview;
- int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
- if (outcode == 0)
- {
- glBegin(GL_LINE_STRIP);
- glVertex3d(p.x, p.y, p.z);
- }
- Point3d lastP = p;
- int lastOutcode = outcode;
- // The trajectory is subdivided into sections that each contain a number of samples.
- // Process each section in the trajectory, using its precomputed bounding volume to
- // quickly check for visibility.
- for (unsigned int i = 0; i < cachedOrbit->sections.size(); i++)
- {
- Capsuled& bv = cachedOrbit->sections[i].boundingVolume;
- Point3d orig = bv.origin * modelview;
- Vec3d axis = bv.axis * modelview;
- Capsulef bvf(Point3f((float) orig.x, (float) orig.y, (float) orig.z),
- Vec3f((float) axis.x, (float) axis.y, (float) axis.z),
- (float) bv.radius);
- // TODO: Create a fast path for the case when the bounding volume lies completely
- // within the frustum and clipping can be ignored.
- if (frustum.testCapsule(bvf) != Frustum::Outside)
- {
- lastP = renderOrbitSection(*orbit,
- *cachedOrbit, i,
- modelview,
- lastP, lastOutcode,
- nearZ, farZ,
- renderFlags);
- lastOutcode = (lastP.z > nearZ ? 1 : 0) | (lastP.z < farZ ? 2 : 0);
- }
- else
- {
- // The section was culled because it lies completely outside the view frustum,
- // but we still need to do some work to keep the begin/end state of the line
- // strip current. We just need to process the final point in the section.
- unsigned int lastSample;
- if (i < cachedOrbit->sections.size() - 1)
- lastSample = cachedOrbit->sections[i + 1].firstSample;
- else
- lastSample = cachedOrbit->trajectory.size() - 1;
- p = (*trajectory)[lastSample].pos * modelview;
- outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
- if ((outcode | lastOutcode) == 0)
- {
- // Segment is completely between near and far planes
- glVertex3d(p.x, p.y, p.z);
- }
- else if ((outcode & lastOutcode) == 0)
- {
- // Need to clip
- Point3d q0 = lastP;
- Point3d q1 = p;
- // Clip against the enter plane
- if (lastOutcode != 0)
- {
- glBegin(GL_LINE_STRIP);
- double t;
- if (lastOutcode == 1)
- t = (nearZ - lastP.z) / (p.z - lastP.z);
- else
- t = (farZ - lastP.z) / (p.z - lastP.z);
- q0 = lastP + t * (p - lastP);
- }
- // Clip against the exit plane
- if (outcode != 0)
- {
- double t;
- if (outcode == 1)
- t = (nearZ - lastP.z) / (p.z - lastP.z);
- else
- t = (farZ - lastP.z) / (p.z - lastP.z);
- q1 = lastP + t * (p - lastP);
- }
- glVertex3d(q0.x, q0.y, q0.z);
- glVertex3d(q1.x, q1.y, q1.z);
- if (outcode != 0)
- {
- glEnd();
- }
- }
- lastP = p;
- lastOutcode = outcode;
- sectionsCulled++;
- }
- }
- if (lastOutcode == 0)
- {
- glEnd();
- }
- }
- else
- {
- double endTime = t;
- bool endTimeReached = false;
- Point3d p;
- p = (*trajectory)[0].pos * modelview;
- int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
- if (outcode == 0)
- {
- glBegin(GL_LINE_STRIP);
- glVertex3d(p.x, p.y, p.z);
- }
- Point3d lastP = p;
- int lastOutcode = outcode;
- unsigned int nPoints = trajectory->size();
- for (unsigned int i = 1; i < nPoints && !endTimeReached; i++)
- {
- if ((*trajectory)[i].t > endTime)
- {
- p = orbit->positionAtTime(endTime) * modelview;
- endTimeReached = true;
- }
- else
- {
- p = (*trajectory)[i].pos * modelview;
- }
- outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
- if ((outcode | lastOutcode) == 0)
- {
- glVertex3d(p.x, p.y, p.z);
- }
- else if ((outcode & lastOutcode) == 0)
- {
- // Need to clip
- Point3d p0 = lastP;
- Point3d p1 = p;
- if (lastOutcode != 0)
- {
- glBegin(GL_LINE_STRIP);
- double t;
- if (lastOutcode == 1)
- t = (nearZ - lastP.z) / (p.z - lastP.z);
- else
- t = (farZ - lastP.z) / (p.z - lastP.z);
- p0 = lastP + t * (p - lastP);
- }
- if (outcode != 0)
- {
- double t;
- if (outcode == 1)
- t = (nearZ - lastP.z) / (p.z - lastP.z);
- else
- t = (farZ - lastP.z) / (p.z - lastP.z);
- p1 = lastP + t * (p - lastP);
- }
- glVertex3d(p0.x, p0.y, p0.z);
- glVertex3d(p1.x, p1.y, p1.z);
- if (outcode != 0)
- {
- glEnd();
- }
- }
- lastOutcode = outcode;
- lastP = p;
- }
- if (lastOutcode == 0)
- {
- glEnd();
- }
- }
- glPopMatrix();
- }
- // Convert a position in the universal coordinate system to astrocentric
- // coordinates, taking into account possible orbital motion of the star.
- static Point3d astrocentricPosition(const UniversalCoord& pos,
- const Star& star,
- double t)
- {
- UniversalCoord starPos = star.getPosition(t);
- Vec3d v = pos - starPos;
- return Point3d(astro::microLightYearsToKilometers(v.x),
- astro::microLightYearsToKilometers(v.y),
- astro::microLightYearsToKilometers(v.z));
- }
- void Renderer::autoMag(float& faintestMag)
- {
- float fieldCorr = 2.0f * FOV/(fov + FOV);
- faintestMag = (float) (faintestAutoMag45deg * sqrt(fieldCorr));
- saturationMag = saturationMagNight * (1.0f + fieldCorr * fieldCorr);
- }
- // Set up the light sources for rendering a solar system. The positions of
- // all nearby stars are converted from universal to viewer-centered
- // coordinates.
- static void
- setupLightSources(const vector<const Star*>& nearStars,
- const UniversalCoord& observerPos,
- double t,
- vector<LightSource>& lightSources)
- {
- lightSources.clear();
- for (vector<const Star*>::const_iterator iter = nearStars.begin();
- iter != nearStars.end(); iter++)
- {
- if ((*iter)->getVisibility())
- {
- Vec3d v = ((*iter)->getPosition(t) - observerPos) *
- astro::microLightYearsToKilometers(1.0);
- LightSource ls;
- ls.position = v;
- ls.luminosity = (*iter)->getLuminosity();
- ls.radius = (*iter)->getRadius();
- // If the star is sufficiently cool, change the light color
- // from white. Though our sun appears yellow, we still make
- // it and all hotter stars emit white light, as this is the
- // 'natural' light to which our eyes are accustomed. We also
- // assign a slight bluish tint to light from O and B type stars,
- // though these will almost never have planets for their light
- // to shine upon.
- float temp = (*iter)->getTemperature();
- if (temp > 30000.0f)
- ls.color = Color(0.8f, 0.8f, 1.0f);
- else if (temp > 10000.0f)
- ls.color = Color(0.9f, 0.9f, 1.0f);
- else if (temp > 5400.0f)
- ls.color = Color(1.0f, 1.0f, 1.0f);
- else if (temp > 3900.0f)
- ls.color = Color(1.0f, 0.9f, 0.8f);
- else if (temp > 2000.0f)
- ls.color = Color(1.0f, 0.7f, 0.7f);
- else
- ls.color = Color(1.0f, 0.4f, 0.4f);
- lightSources.push_back(ls);
- }
- }
- }
- // Set up the potential secondary light sources for rendering solar system
- // bodies.
- static void
- setupSecondaryLightSources(vector<SecondaryIlluminator>& secondaryIlluminators,
- const vector<LightSource>& primaryIlluminators)
- {
- float au2 = square(astro::kilometersToAU(1.0f));
- for (vector<SecondaryIlluminator>::iterator i = secondaryIlluminators.begin();
- i != secondaryIlluminators.end(); i++)
- {
- i->reflectedIrradiance = 0.0f;
- for (vector<LightSource>::const_iterator j = primaryIlluminators.begin();
- j != primaryIlluminators.end(); j++)
- {
- i->reflectedIrradiance += j->luminosity / ((float) (i->position_v - j->position).lengthSquared() * au2);
- }
- i->reflectedIrradiance *= i->body->getAlbedo();
- }
- }
- // Render an item from the render list
- void Renderer::renderItem(const RenderListEntry& rle,
- const Observer& observer,
- const Quatf& cameraOrientation,
- float nearPlaneDistance,
- float farPlaneDistance)
- {
- switch (rle.renderableType)
- {
- case RenderListEntry::RenderableStar:
- renderStar(*rle.star,
- rle.position,
- rle.distance,
- rle.appMag,
- cameraOrientation,
- observer.getTime(),
- nearPlaneDistance, farPlaneDistance);
- break;
- case RenderListEntry::RenderableBody:
- renderPlanet(*rle.body,
- rle.position,
- rle.distance,
- rle.appMag,
- observer,
- cameraOrientation,
- nearPlaneDistance, farPlaneDistance);
- break;
- case RenderListEntry::RenderableCometTail:
- renderCometTail(*rle.body,
- rle.position,
- observer.getTime(),
- rle.discSizeInPixels);
- break;
- case RenderListEntry::RenderableReferenceMark:
- renderReferenceMark(*rle.refMark,
- rle.position,
- rle.distance,
- observer.getTime(),
- nearPlaneDistance);
- break;
- default:
- break;
- }
- }
- #ifdef USE_HDR
- void Renderer::genBlurTextures()
- {
- for (size_t i = 0; i < BLUR_PASS_COUNT; ++i)
- {
- if (blurTextures[i] != NULL)
- {
- delete blurTextures[i];
- blurTextures[i] = NULL;
- }
- }
- if (blurTempTexture)
- {
- delete blurTempTexture;
- blurTempTexture = NULL;
- }
- blurBaseWidth = sceneTexWidth, blurBaseHeight = sceneTexHeight;
- if (blurBaseWidth > blurBaseHeight)
- {
- while (blurBaseWidth > BLUR_SIZE)
- {
- blurBaseWidth >>= 1;
- blurBaseHeight >>= 1;
- }
- }
- else
- {
- while (blurBaseHeight > BLUR_SIZE)
- {
- blurBaseWidth >>= 1;
- blurBaseHeight >>= 1;
- }
- }
- genBlurTexture(0);
- genBlurTexture(1);
- Image *tempImg;
- ImageTexture *tempTexture;
- tempImg = new Image(GL_LUMINANCE, blurBaseWidth, blurBaseHeight);
- tempTexture = new ImageTexture(*tempImg, Texture::EdgeClamp, Texture::DefaultMipMaps);
- delete tempImg;
- if (tempTexture && tempTexture->getName() != 0)
- blurTempTexture = tempTexture;
- }
- void Renderer::genBlurTexture(int blurLevel)
- {
- Image *img;
- ImageTexture *texture;
- #ifdef DEBUG_HDR
- HDR_LOG <<
- "Window width = " << windowWidth << ", " <<
- "Window height = " << windowHeight << ", " <<
- "Blur tex width = " << (blurBaseWidth>>blurLevel) << ", " <<
- "Blur tex height = " << (blurBaseHeight>>blurLevel) << endl;
- #endif
- img = new Image(blurFormat,
- blurBaseWidth>>blurLevel,
- blurBaseHeight>>blurLevel);
- texture = new ImageTexture(*img,
- Texture::EdgeClamp,
- Texture::NoMipMaps);
- delete img;
- if (texture && texture->getName() != 0)
- blurTextures[blurLevel] = texture;
- }
- void Renderer::genSceneTexture()
- {
- unsigned int *data;
- if (sceneTexture != 0)
- glDeleteTextures(1, &sceneTexture);
- sceneTexWidth = 1;
- sceneTexHeight = 1;
- while (sceneTexWidth < windowWidth)
- sceneTexWidth <<= 1;
- while (sceneTexHeight < windowHeight)
- sceneTexHeight <<= 1;
- sceneTexWScale = (windowWidth > 0) ? (GLfloat)sceneTexWidth / (GLfloat)windowWidth :
- 1.0f;
- sceneTexHScale = (windowHeight > 0) ? (GLfloat)sceneTexHeight / (GLfloat)windowHeight :
- 1.0f;
- data = (unsigned int* )malloc(sceneTexWidth*sceneTexHeight*4*sizeof(unsigned int));
- memset(data, 0, sceneTexWidth*sceneTexHeight*4*sizeof(unsigned int));
-
- glGenTextures(1, &sceneTexture);
- glBindTexture(GL_TEXTURE_2D, sceneTexture);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, sceneTexWidth, sceneTexHeight, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, data);
-
- free(data);
- #ifdef DEBUG_HDR
- static int genSceneTexCounter = 1;
- HDR_LOG <<
- "[" << genSceneTexCounter++ << "] " <<
- "Window width = " << windowWidth << ", " <<
- "Window height = " << windowHeight << ", " <<
- "Tex width = " << sceneTexWidth << ", " <<
- "Tex height = " << sceneTexHeight << endl;
- #endif
- }
- void Renderer::renderToBlurTexture(int blurLevel)
- {
- if (blurTextures[blurLevel] == NULL)
- return;
- GLsizei blurTexWidth = blurBaseWidth>>blurLevel;
- GLsizei blurTexHeight = blurBaseHeight>>blurLevel;
- GLsizei blurDrawWidth = (GLfloat)windowWidth/(GLfloat)sceneTexWidth * blurTexWidth;
- GLsizei blurDrawHeight = (GLfloat)windowHeight/(GLfloat)sceneTexHeight * blurTexHeight;
- GLfloat blurWScale = 1.f;
- GLfloat blurHScale = 1.f;
- GLfloat savedWScale = 1.f;
- GLfloat savedHScale = 1.f;
- glPushAttrib(GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
- glClearColor(0, 0, 0, 1.f);
- glViewport(0, 0, blurDrawWidth, blurDrawHeight);
- glBindTexture(GL_TEXTURE_2D, sceneTexture);
- if (useBlendSubtract)
- {
- glBegin(GL_QUADS);
- drawBlendedVertices(0.0f, 0.0f, 1.0f);
- glEnd();
- // Do not need to scale alpha so mask it off
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
- glEnable(GL_BLEND);
- savedWScale = sceneTexWScale;
- savedHScale = sceneTexHScale;
- // Remove ldr part of image
- {
- const GLfloat bias = -0.5f;
- glBlendFunc(GL_ONE, GL_ONE);
- glx::glBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
- glColor4f(-bias, -bias, -bias, 0.0f);
- glDisable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
- glVertex2f(0.0f, 0.0f);
- glVertex2f(1.f, 0.0f);
- glVertex2f(1.f, 1.f);
- glVertex2f(0.0f, 1.f);
- glEnd();
- glEnable(GL_TEXTURE_2D);
- blurTextures[blurLevel]->bind();
- glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
- blurTexWidth, blurTexHeight, 0);
- }
- // Scale back up hdr part
- {
- glx::glBlendEquationEXT(GL_FUNC_ADD_EXT);
- glBlendFunc(GL_DST_COLOR, GL_ONE);
- glBegin(GL_QUADS);
- drawBlendedVertices(0.f, 0.f, 1.f); //x2
- drawBlendedVertices(0.f, 0.f, 1.f); //x2
- glEnd();
- }
- glDisable(GL_BLEND);
- if (!useLuminanceAlpha)
- {
- blurTempTexture->bind();
- glCopyTexImage2D(GL_TEXTURE_2D, blurLevel, GL_LUMINANCE, 0, 0,
- blurTexWidth, blurTexHeight, 0);
- // Erase color, replace with luminance image
- glBegin(GL_QUADS);
- glColor4f(0.f, 0.f, 0.f, 1.f);
- glVertex2f(0.0f, 0.0f);
- glVertex2f(1.0f, 0.0f);
- glVertex2f(1.0f, 1.0f);
- glVertex2f(0.0f, 1.0f);
- glEnd();
- glBegin(GL_QUADS);
- drawBlendedVertices(0.f, 0.f, 1.f);
- glEnd();
- }
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- blurTextures[blurLevel]->bind();
- glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
- blurTexWidth, blurTexHeight, 0);
- }
- else
- {
- // GL_EXT_blend_subtract not supported
- // Use compatible (but slow) glPixelTransfer instead
- glBegin(GL_QUADS);
- drawBlendedVertices(0.0f, 0.0f, 1.0f);
- glEnd();
- savedWScale = sceneTexWScale;
- savedHScale = sceneTexHScale;
- sceneTexWScale = blurWScale;
- sceneTexHScale = blurHScale;
- blurTextures[blurLevel]->bind();
- glPixelTransferf(GL_RED_SCALE, 8.f);
- glPixelTransferf(GL_GREEN_SCALE, 8.f);
- glPixelTransferf(GL_BLUE_SCALE, 8.f);
- glPixelTransferf(GL_RED_BIAS, -0.5f);
- glPixelTransferf(GL_GREEN_BIAS, -0.5f);
- glPixelTransferf(GL_BLUE_BIAS, -0.5f);
- glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
- blurTexWidth, blurTexHeight, 0);
- glPixelTransferf(GL_RED_SCALE, 1.f);
- glPixelTransferf(GL_GREEN_SCALE, 1.f);
- glPixelTransferf(GL_BLUE_SCALE, 1.f);
- glPixelTransferf(GL_RED_BIAS, 0.f);
- glPixelTransferf(GL_GREEN_BIAS, 0.f);
- glPixelTransferf(GL_BLUE_BIAS, 0.f);
- }
- glClear(GL_COLOR_BUFFER_BIT);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- GLfloat xdelta = 1.0f / (GLfloat)blurTexWidth;
- GLfloat ydelta = 1.0f / (GLfloat)blurTexHeight;
- blurWScale = ((GLfloat)blurTexWidth / (GLfloat)blurDrawWidth);
- blurHScale = ((GLfloat)blurTexHeight / (GLfloat)blurDrawHeight);
- sceneTexWScale = blurWScale;
- sceneTexHScale = blurHScale;
- // Butterworth low pass filter to reduce flickering dots
- {
- glBegin(GL_QUADS);
- drawBlendedVertices(0.0f, 0.0f, .5f*1.f);
- drawBlendedVertices(-xdelta, 0.0f, .5f*0.333f);
- drawBlendedVertices( xdelta, 0.0f, .5f*0.25f);
- glEnd();
- glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
- blurTexWidth, blurTexHeight, 0);
- glBegin(GL_QUADS);
- drawBlendedVertices(0.0f, -ydelta, .5f*0.667f);
- drawBlendedVertices(0.0f, ydelta, .5f*0.333f);
- glEnd();
- glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
- blurTexWidth, blurTexHeight, 0);
- glClear(GL_COLOR_BUFFER_BIT);
- }
- // Gaussian blur
- switch (blurLevel)
- {
- /*
- case 0:
- drawGaussian3x3(xdelta, ydelta, blurTexWidth, blurTexHeight, 1.f);
- break;
- */
- #ifdef TARGET_OS_MAC
- case 0:
- drawGaussian5x5(xdelta, ydelta, blurTexWidth, blurTexHeight, 1.f);
- break;
- case 1:
- drawGaussian9x9(xdelta, ydelta, blurTexWidth, blurTexHeight, .3f);
- break;
- #else
- // Gamma correct: windows=(mac^1.8)^(1/2.2)
- case 0:
- drawGaussian5x5(xdelta, ydelta, blurTexWidth, blurTexHeight, 1.f);
- break;
- case 1:
- drawGaussian9x9(xdelta, ydelta, blurTexWidth, blurTexHeight, .373f);
- break;
- #endif
- default:
- break;
- }
- blurTextures[blurLevel]->bind();
- glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
- blurTexWidth, blurTexHeight, 0);
- glDisable(GL_BLEND);
- glClear(GL_COLOR_BUFFER_BIT);
- glPopAttrib();
- sceneTexWScale = savedWScale;
- sceneTexHScale = savedHScale;
- }
- void Renderer::renderToTexture(const Observer& observer,
- const Universe& universe,
- float faintestMagNight,
- const Selection& sel)
- {
- if (sceneTexture == 0)
- return;
- glPushAttrib(GL_COLOR_BUFFER_BIT);
- draw(observer, universe, faintestMagNight, sel);
- glBindTexture(GL_TEXTURE_2D, sceneTexture);
- glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0,
- sceneTexWidth, sceneTexHeight, 0);
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glPopAttrib();
- }
- void Renderer::drawSceneTexture()
- {
- if (sceneTexture == 0)
- return;
- glBindTexture(GL_TEXTURE_2D, sceneTexture);
- glBegin(GL_QUADS);
- drawBlendedVertices(0.0f, 0.0f, 1.0f);
- glEnd();
- }
- void Renderer::drawBlendedVertices(float xdelta, float ydelta, float blend)
- {
- glColor4f(1.0f, 1.0f, 1.0f, blend);
- glTexCoord2i(0, 0); glVertex2f(xdelta, ydelta);
- glTexCoord2i(1, 0); glVertex2f(sceneTexWScale+xdelta, ydelta);
- glTexCoord2i(1, 1); glVertex2f(sceneTexWScale+xdelta, sceneTexHScale+ydelta);
- glTexCoord2i(0, 1); glVertex2f(xdelta, sceneTexHScale+ydelta);
- }
- void Renderer::drawGaussian3x3(float xdelta, float ydelta, GLsizei width, GLsizei height, float blend)
- {
- #ifdef USE_BLOOM_LISTS
- if (gaussianLists[0] == 0)
- {
- gaussianLists[0] = glGenLists(1);
- glNewList(gaussianLists[0], GL_COMPILE);
- #endif
- glBegin(GL_QUADS);
- drawBlendedVertices(0.0f, 0.0f, blend);
- drawBlendedVertices(-xdelta, 0.0f, 0.25f*blend);
- drawBlendedVertices( xdelta, 0.0f, 0.20f*blend);
- glEnd();
- // Take result of horiz pass and apply vertical pass
- glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
- width, height, 0);
- glBegin(GL_QUADS);
- drawBlendedVertices(0.0f, -ydelta, 0.429f);
- drawBlendedVertices(0.0f, ydelta, 0.300f);
- glEnd();
- #ifdef USE_BLOOM_LISTS
- glEndList();
- }
- glCallList(gaussianLists[0]);
- #endif
- }
- void Renderer::drawGaussian5x5(float xdelta, float ydelta, GLsizei width, GLsizei height, float blend)
- {
- #ifdef USE_BLOOM_LISTS
- if (gaussianLists[1] == 0)
- {
- gaussianLists[1] = glGenLists(1);
- glNewList(gaussianLists[1], GL_COMPILE);
- #endif
- glBegin(GL_QUADS);
- drawBlendedVertices(0.0f, 0.0f, blend);
- drawBlendedVertices(-xdelta, 0.0f, 0.475f*blend);
- drawBlendedVertices( xdelta, 0.0f, 0.475f*blend);
- drawBlendedVertices(-2.0f*xdelta, 0.0f, 0.075f*blend);
- drawBlendedVertices( 2.0f*xdelta, 0.0f, 0.075f*blend);
- glEnd();
- glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
- width, height, 0);
- glBegin(GL_QUADS);
- drawBlendedVertices(0.0f, -ydelta, 0.475f);
- drawBlendedVertices(0.0f, ydelta, 0.475f);
- drawBlendedVertices(0.0f, -2.0f*ydelta, 0.075f);
- drawBlendedVertices(0.0f, 2.0f*ydelta, 0.075f);
- glEnd();
- #ifdef USE_BLOOM_LISTS
- glEndList();
- }
- glCallList(gaussianLists[1]);
- #endif
- }
- void Renderer::drawGaussian9x9(float xdelta, float ydelta, GLsizei width, GLsizei height, float blend)
- {
- #ifdef USE_BLOOM_LISTS
- if (gaussianLists[2] == 0)
- {
- gaussianLists[2] = glGenLists(1);
- glNewList(gaussianLists[2], GL_COMPILE);
- #endif
- glBegin(GL_QUADS);
- drawBlendedVertices(0.0f, 0.0f, blend);
- drawBlendedVertices(-xdelta, 0.0f, 0.632f*blend);
- drawBlendedVertices( xdelta, 0.0f, 0.632f*blend);
- drawBlendedVertices(-2.0f*xdelta, 0.0f, 0.159f*blend);
- drawBlendedVertices( 2.0f*xdelta, 0.0f, 0.159f*blend);
- drawBlendedVertices(-3.0f*xdelta, 0.0f, 0.016f*blend);
- drawBlendedVertices( 3.0f*xdelta, 0.0f, 0.016f*blend);
- glEnd();
- glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,