2001-11-27 18:50:04 -07:00
|
|
|
// render.cpp
|
|
|
|
//
|
2006-09-07 02:08:32 -06:00
|
|
|
// Copyright (C) 2001-2006, Chris Laurel <claurel@shatters.net>
|
2001-11-27 18:50:04 -07:00
|
|
|
//
|
|
|
|
// 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>
|
2002-02-17 21:00:07 -07:00
|
|
|
#include <cassert>
|
2002-01-03 14:01:11 -07:00
|
|
|
|
2001-12-18 16:00:26 -07:00
|
|
|
#ifndef _WIN32
|
2002-06-14 09:34:22 -06:00
|
|
|
#ifndef MACOSX_PB
|
2002-01-03 14:01:11 -07:00
|
|
|
#include <config.h>
|
2002-06-14 09:34:22 -06:00
|
|
|
#endif
|
2002-01-03 14:01:11 -07:00
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
|
|
|
#include <celutil/debug.h>
|
|
|
|
#include <celmath/frustum.h>
|
2002-02-17 21:00:07 -07:00
|
|
|
#include <celmath/distance.h>
|
2002-11-03 21:14:07 -07:00
|
|
|
#include <celmath/intersect.h>
|
2005-07-19 15:31:04 -06:00
|
|
|
#include <celutil/utf8.h>
|
|
|
|
#include <celutil/util.h>
|
2001-11-27 18:50:04 -07:00
|
|
|
#include "gl.h"
|
|
|
|
#include "astro.h"
|
|
|
|
#include "glext.h"
|
|
|
|
#include "vecgl.h"
|
2004-10-15 03:55:12 -06:00
|
|
|
#include "glshader.h"
|
|
|
|
#include "shadermanager.h"
|
2001-11-27 18:50:04 -07:00
|
|
|
#include "spheremesh.h"
|
|
|
|
#include "lodspheremesh.h"
|
2004-02-17 02:58:52 -07:00
|
|
|
#include "model.h"
|
2001-11-27 18:50:04 -07:00
|
|
|
#include "regcombine.h"
|
|
|
|
#include "vertexprog.h"
|
|
|
|
#include "texmanager.h"
|
|
|
|
#include "meshmanager.h"
|
|
|
|
#include "render.h"
|
2006-08-26 17:58:48 -06:00
|
|
|
#include "renderinfo.h"
|
|
|
|
#include "renderglsl.h"
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
#define FOV 45.0f
|
|
|
|
#define NEAR_DIST 0.5f
|
2005-07-21 10:31:43 -06:00
|
|
|
#define FAR_DIST 1.0e9f
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2003-02-20 02:48:47 -07:00
|
|
|
// This should be in the GL headers, but where?
|
|
|
|
#ifndef GL_COLOR_SUM_EXT
|
|
|
|
#define GL_COLOR_SUM_EXT 0x8458
|
|
|
|
#endif
|
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
static const float STAR_DISTANCE_LIMIT = 1.0e6f;
|
2005-12-19 16:04:28 -07:00
|
|
|
static const double DSO_DISTANCE_LIMIT = 1.8e9;
|
2005-12-11 08:34:35 -07:00
|
|
|
static const int REF_DISTANCE_TO_SCREEN = 400; //[mm]
|
2005-11-18 02:00:39 -07:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
// 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);
|
2005-11-18 02:00:39 -07:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
static const int StarVertexListSize = 1024;
|
|
|
|
|
2002-02-19 17:38:55 -07:00
|
|
|
// Fractional pixel offset used when rendering text as texture mapped
|
2005-07-12 11:37:00 -06:00
|
|
|
// quads to ensure consistent mapping of texels to pixels.
|
2004-07-21 01:12:45 -06:00
|
|
|
static const float PixelOffset = 0.125f;
|
2002-02-19 17:38:55 -07:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
// 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
|
2006-09-07 02:08:32 -06:00
|
|
|
static const float MaxFarNearRatio = 2000000.0f;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2002-02-19 17:38:55 -07:00
|
|
|
static const float RenderDistance = 50.0f;
|
|
|
|
|
2006-12-28 12:29:42 -07:00
|
|
|
// Star disc size in pixels
|
|
|
|
static const float BaseStarDiscSize = 5.0f;
|
2003-07-08 09:31:34 -06:00
|
|
|
static const float MaxScaledDiscStarSize = 8.0f;
|
2006-12-20 11:56:43 -07:00
|
|
|
static const float GlareOpacity = 0.65f;
|
2003-07-08 09:31:34 -06:00
|
|
|
|
2006-09-01 05:14:37 -06:00
|
|
|
static const float MinRelativeOccluderRadius = 0.005f;
|
|
|
|
|
2002-02-20 23:39:15 -07:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2003-06-18 12:35:59 -06:00
|
|
|
// The minimum apparent size of a surface feature in pixels before we display
|
|
|
|
// a label for it.
|
|
|
|
static const float MinFeatureSizeForLabel = 20.0f;
|
2002-02-20 23:39:15 -07:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
// Static meshes and textures used by all instances of Simulation
|
|
|
|
|
|
|
|
static bool commonDataInitialized = false;
|
|
|
|
|
2006-08-26 17:58:48 -06:00
|
|
|
|
|
|
|
LODSphereMesh* g_lodSphere = NULL;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
static Texture* normalizationTex = NULL;
|
|
|
|
|
|
|
|
static Texture* starTex = NULL;
|
|
|
|
static Texture* glareTex = NULL;
|
|
|
|
static Texture* shadowTex = NULL;
|
2006-03-08 11:29:10 -07:00
|
|
|
static Texture* gaussianDiscTex = NULL;
|
|
|
|
static Texture* gaussianGlareTex = NULL;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2004-03-12 01:31:10 -07:00
|
|
|
// 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;
|
|
|
|
|
2002-02-18 15:57:22 -07:00
|
|
|
static Texture* eclipseShadowTextures[4];
|
2003-02-14 01:39:58 -07:00
|
|
|
static Texture* shadowMaskTexture = NULL;
|
2004-03-12 01:31:10 -07:00
|
|
|
static Texture* penumbraFunctionTexture = NULL;
|
2002-02-18 15:57:22 -07:00
|
|
|
|
2004-08-26 23:39:52 -06:00
|
|
|
static const Color compassColor(0.4f, 0.4f, 1.0f);
|
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
static const float CoronaHeight = 0.2f;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2002-03-28 19:53:32 -07:00
|
|
|
static bool buggyVertexProgramEmulation = true;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
struct SphericalCoordLabel
|
|
|
|
{
|
|
|
|
string label;
|
|
|
|
float ra;
|
|
|
|
float dec;
|
|
|
|
|
|
|
|
SphericalCoordLabel() : ra(0), dec(0) {};
|
|
|
|
SphericalCoordLabel(float _ra, float _dec) : ra(_ra), dec(_dec)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2006-11-05 02:08:44 -07:00
|
|
|
static int nCoordLabels = 38;
|
2001-11-27 18:50:04 -07:00
|
|
|
static SphericalCoordLabel* coordLabels = NULL;
|
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
static const int MaxSkyRings = 32;
|
|
|
|
static const int MaxSkySlices = 180;
|
|
|
|
static const int MinSkySlices = 30;
|
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
|
2006-08-07 00:40:45 -06:00
|
|
|
#if 0
|
2006-03-08 11:29:10 -07:00
|
|
|
struct DisplayDevice
|
|
|
|
{
|
|
|
|
float faintestVisibleLevel;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Detector
|
|
|
|
{
|
2006-09-12 06:56:47 -06:00
|
|
|
float saturationLevel;
|
2006-03-08 11:29:10 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
DisplayDevice displayDevice =
|
|
|
|
{
|
|
|
|
0.1f,
|
|
|
|
};
|
|
|
|
|
|
|
|
Detector detector =
|
|
|
|
{
|
|
|
|
1.0f,
|
|
|
|
};
|
2006-08-07 00:40:45 -06:00
|
|
|
#endif
|
2006-03-08 11:29:10 -07:00
|
|
|
|
|
|
|
// Some useful unit conversions
|
|
|
|
inline float mmToInches(float mm)
|
|
|
|
{
|
|
|
|
return mm * (1.0f / 25.4f);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline float inchesToMm(float in)
|
|
|
|
{
|
|
|
|
return in * 25.4f;
|
|
|
|
}
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
Renderer::Renderer() :
|
2003-02-19 10:48:25 -07:00
|
|
|
context(0),
|
2001-11-27 18:50:04 -07:00
|
|
|
windowWidth(0),
|
|
|
|
windowHeight(0),
|
|
|
|
fov(FOV),
|
2005-12-11 08:34:35 -07:00
|
|
|
screenDpi(96),
|
2002-09-22 08:49:49 -06:00
|
|
|
corrFac(1.12f),
|
2005-11-18 02:00:39 -07:00
|
|
|
faintestAutoMag45deg(7.0f),
|
2001-11-27 18:50:04 -07:00
|
|
|
renderMode(GL_FILL),
|
|
|
|
labelMode(NoLabels),
|
|
|
|
renderFlags(ShowStars | ShowPlanets),
|
2003-07-08 09:31:34 -06:00
|
|
|
orbitMask(Body::Planet | Body::Moon),
|
2001-11-27 18:50:04 -07:00
|
|
|
ambientLightLevel(0.1f),
|
|
|
|
fragmentShaderEnabled(false),
|
|
|
|
vertexShaderEnabled(false),
|
|
|
|
brightnessBias(0.0f),
|
2001-12-11 00:40:53 -07:00
|
|
|
saturationMagNight(1.0f),
|
|
|
|
saturationMag(1.0f),
|
2003-07-08 09:31:34 -06:00
|
|
|
starStyle(FuzzyPointStars),
|
2001-11-27 18:50:04 -07:00
|
|
|
starVertexBuffer(NULL),
|
2006-03-08 11:29:10 -07:00
|
|
|
pointStarVertexBuffer(NULL),
|
2006-12-20 11:56:43 -07:00
|
|
|
glareVertexBuffer(NULL),
|
2001-11-27 18:50:04 -07:00
|
|
|
useVertexPrograms(false),
|
2002-02-13 13:38:49 -07:00
|
|
|
useRescaleNormal(false),
|
2005-11-25 15:57:20 -07:00
|
|
|
usePointSprite(false),
|
2002-04-25 21:49:43 -06:00
|
|
|
textureResolution(medres),
|
2006-03-08 11:29:10 -07:00
|
|
|
useNewStarRendering(false),
|
2002-09-08 21:50:37 -06:00
|
|
|
minOrbitSize(MinOrbitSizeForLabel),
|
2005-12-11 08:34:35 -07:00
|
|
|
distanceLimit(1.0e6f),
|
2003-06-18 12:35:59 -06:00
|
|
|
minFeatureSize(MinFeatureSizeForLabel),
|
2006-11-27 12:11:55 -07:00
|
|
|
locationFilter(~0u),
|
2006-12-11 18:29:27 -07:00
|
|
|
colorTemp(NULL),
|
|
|
|
videoSync(false)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
starVertexBuffer = new StarVertexBuffer(2048);
|
2006-03-08 11:29:10 -07:00
|
|
|
pointStarVertexBuffer = new PointStarVertexBuffer(2048);
|
2006-12-20 11:56:43 -07:00
|
|
|
glareVertexBuffer = new PointStarVertexBuffer(2048);
|
2003-05-25 20:27:27 -06:00
|
|
|
skyVertices = new SkyVertex[MaxSkySlices * (MaxSkyRings + 1)];
|
|
|
|
skyIndices = new uint32[(MaxSkySlices + 1) * 2 * MaxSkyRings];
|
2003-05-25 22:11:14 -06:00
|
|
|
skyContour = new SkyContourPoint[MaxSkySlices + 1];
|
2004-09-19 21:27:50 -06:00
|
|
|
colorTemp = GetStarColorTable(ColorTable_Enhanced);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
for (int i = 0; i < (int) FontCount; i++)
|
|
|
|
{
|
|
|
|
font[i] = NULL;
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Renderer::~Renderer()
|
|
|
|
{
|
|
|
|
if (starVertexBuffer != NULL)
|
|
|
|
delete starVertexBuffer;
|
2006-03-08 11:29:10 -07:00
|
|
|
if (pointStarVertexBuffer != NULL)
|
|
|
|
delete pointStarVertexBuffer;
|
2003-05-25 20:27:27 -06:00
|
|
|
delete[] skyVertices;
|
|
|
|
delete[] skyIndices;
|
|
|
|
delete[] skyContour;
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-15 11:51:16 -07:00
|
|
|
Renderer::DetailOptions::DetailOptions() :
|
|
|
|
ringSystemSections(100),
|
|
|
|
orbitPathSamplePoints(100),
|
|
|
|
shadowTextureSize(256),
|
|
|
|
eclipseTextureSize(128)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-16 17:02:55 -06:00
|
|
|
static void StarTextureEval(float u, float v, float,
|
2001-11-27 18:50:04 -07:00
|
|
|
unsigned char *pixel)
|
|
|
|
{
|
|
|
|
float r = 1 - (float) sqrt(u * u + v * v);
|
|
|
|
if (r < 0)
|
|
|
|
r = 0;
|
2003-07-08 09:31:34 -06:00
|
|
|
else if (r < 0.5f)
|
|
|
|
r = 2.0f * r;
|
2006-09-12 06:56:47 -06:00
|
|
|
else
|
2001-11-27 18:50:04 -07:00
|
|
|
r = 1;
|
|
|
|
|
|
|
|
int pixVal = (int) (r * 255.99f);
|
|
|
|
pixel[0] = pixVal;
|
|
|
|
pixel[1] = pixVal;
|
|
|
|
pixel[2] = pixVal;
|
|
|
|
}
|
|
|
|
|
2006-09-16 17:02:55 -06:00
|
|
|
static void GlareTextureEval(float u, float v, float,
|
2001-11-27 18:50:04 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-09-16 17:02:55 -06:00
|
|
|
static void ShadowTextureEval(float u, float v, float,
|
2001-11-27 18:50:04 -07:00
|
|
|
unsigned char *pixel)
|
|
|
|
{
|
|
|
|
float r = (float) sqrt(u * u + v * v);
|
|
|
|
|
2002-02-17 21:00:07 -07:00
|
|
|
// 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;
|
2001-11-27 18:50:04 -07:00
|
|
|
pixel[0] = pixVal;
|
|
|
|
pixel[1] = pixVal;
|
|
|
|
pixel[2] = pixVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-12 01:31:10 -07:00
|
|
|
//! 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.
|
2006-09-16 17:02:55 -06:00
|
|
|
static void PenumbraFunctionEval(float u, float, float,
|
2004-03-12 01:31:10 -07:00
|
|
|
unsigned char *pixel)
|
|
|
|
{
|
|
|
|
u = (u + 1.0f) * 0.5f;
|
|
|
|
|
|
|
|
// Using the cube root produces a good visual result
|
2004-10-26 03:12:00 -06:00
|
|
|
unsigned char pixVal = (unsigned char) (::pow((double) u, 0.33) * 255.99);
|
2004-03-12 01:31:10 -07:00
|
|
|
|
|
|
|
pixel[0] = pixVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-02-18 15:57:22 -07:00
|
|
|
// ShadowTextureFunction is a function object for creating shadow textures
|
|
|
|
// used for rendering eclipses.
|
|
|
|
class ShadowTextureFunction : public TexelFunctionObject
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ShadowTextureFunction(float _umbra) : umbra(_umbra) {};
|
2002-02-26 19:37:40 -07:00
|
|
|
virtual void operator()(float u, float v, float w, unsigned char* pixel);
|
2002-02-18 15:57:22 -07:00
|
|
|
float umbra;
|
|
|
|
};
|
|
|
|
|
2006-09-16 17:02:55 -06:00
|
|
|
void ShadowTextureFunction::operator()(float u, float v, float,
|
2002-02-18 15:57:22 -07:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2003-02-14 01:39:58 -07:00
|
|
|
|
|
|
|
class ShadowMaskTextureFunction : public TexelFunctionObject
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ShadowMaskTextureFunction() {};
|
|
|
|
virtual void operator()(float u, float v, float w, unsigned char* pixel);
|
|
|
|
float dummy;
|
|
|
|
};
|
|
|
|
|
2006-09-16 17:02:55 -06:00
|
|
|
void ShadowMaskTextureFunction::operator()(float u, float, float,
|
2003-02-14 01:39:58 -07:00
|
|
|
unsigned char* pixel)
|
|
|
|
{
|
|
|
|
unsigned char a = u > 0.0f ? 255 : 0;
|
|
|
|
pixel[0] = a;
|
|
|
|
pixel[1] = a;
|
|
|
|
pixel[2] = a;
|
|
|
|
pixel[3] = a;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
static void IllumMapEval(float x, float y, float z,
|
|
|
|
unsigned char* pixel)
|
|
|
|
{
|
|
|
|
Vec3f v(x, y, z);
|
|
|
|
Vec3f u(0, 0, 1);
|
2002-02-08 14:55:26 -07:00
|
|
|
|
|
|
|
#if 0
|
2002-02-13 13:38:49 -07:00
|
|
|
Vec3f n(0, 0, 1);
|
2002-02-08 14:55:26 -07:00
|
|
|
// Experimental illumination function
|
2001-11-27 18:50:04 -07:00
|
|
|
float c = v * n;
|
|
|
|
if (c < 0.0f)
|
|
|
|
{
|
|
|
|
u = v;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
c = (1 - ((1 - c))) * 1.0f;
|
|
|
|
u = v + (c * n);
|
|
|
|
u.normalize();
|
|
|
|
}
|
2002-02-08 14:55:26 -07:00
|
|
|
#else
|
|
|
|
u = v;
|
|
|
|
#endif
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
pixel[0] = 128 + (int) (127 * u.x);
|
|
|
|
pixel[1] = 128 + (int) (127 * u.y);
|
|
|
|
pixel[2] = 128 + (int) (127 * u.z);
|
|
|
|
}
|
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
static void BuildGaussianDiscMipLevel(unsigned char* mipPixels,
|
2006-12-08 12:02:20 -07:00
|
|
|
unsigned int log2size,
|
|
|
|
float fwhm,
|
|
|
|
float power)
|
2006-03-08 11:29:10 -07:00
|
|
|
{
|
2006-12-08 12:02:20 -07:00
|
|
|
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;
|
2007-01-05 17:52:32 -07:00
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
mipPixels[i * size + j] = (unsigned char) (255.99f * min(f, 1.0f));
|
|
|
|
}
|
|
|
|
}
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
|
2006-12-20 11:56:43 -07:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-28 12:29:42 -07:00
|
|
|
// 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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
static Texture* BuildGaussianDiscTexture(unsigned int log2size)
|
|
|
|
{
|
2006-12-08 12:02:20 -07:00
|
|
|
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;
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static Texture* BuildGaussianGlareTexture(unsigned int log2size)
|
|
|
|
{
|
2006-12-08 12:02:20 -07:00
|
|
|
unsigned int size = 1 << log2size;
|
|
|
|
Image* img = new Image(GL_LUMINANCE, size, size, log2size + 1);
|
2006-03-08 11:29:10 -07:00
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
for (unsigned int mipLevel = 0; mipLevel <= log2size; mipLevel++)
|
|
|
|
{
|
2006-12-20 11:56:43 -07:00
|
|
|
/*
|
|
|
|
// 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;
|
2006-12-08 12:02:20 -07:00
|
|
|
BuildGaussianDiscMipLevel(img->getMipLevel(mipLevel),
|
|
|
|
log2size - mipLevel,
|
|
|
|
fwhm,
|
2006-12-20 11:56:43 -07:00
|
|
|
power);
|
|
|
|
*/
|
|
|
|
BuildGlareMipLevel(img->getMipLevel(mipLevel),
|
|
|
|
log2size - mipLevel,
|
|
|
|
25.0f / (float) pow(2.0f, (float) (log2size - mipLevel)),
|
|
|
|
0.66f);
|
2006-12-28 12:29:42 -07:00
|
|
|
/*
|
|
|
|
BuildGlareMipLevel2(img->getMipLevel(mipLevel),
|
|
|
|
log2size - mipLevel,
|
|
|
|
1.0f / (float) pow(2.0f, (float) (log2size - mipLevel)));
|
|
|
|
*/
|
2006-12-08 12:02:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
ImageTexture* texture = new ImageTexture(*img,
|
|
|
|
Texture::BorderClamp,
|
|
|
|
Texture::DefaultMipMaps);
|
|
|
|
texture->setBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
|
2007-01-05 17:52:32 -07:00
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
delete img;
|
|
|
|
|
|
|
|
return texture;
|
|
|
|
}
|
2006-03-08 11:29:10 -07:00
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
|
|
|
|
// Depth comparison function for render list entries
|
2002-11-03 21:14:07 -07:00
|
|
|
bool operator<(const RenderListEntry& a, const RenderListEntry& b)
|
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
// Operation is reversed because -z axis points into the screen
|
|
|
|
return a.centerZ - a.radius > b.centerZ - b.radius;
|
2002-11-03 21:14:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
// Depth comparison for labels
|
2004-05-18 11:31:26 -06:00
|
|
|
bool operator<(const Renderer::Label& a, const Renderer::Label& b)
|
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
// Operation is reversed because -z axis points into the screen
|
2004-05-18 11:31:26 -06:00
|
|
|
return a.position.z > b.position.z;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
// Depth comparison for orbit paths
|
|
|
|
bool operator<(const Renderer::OrbitPathListEntry& a, const Renderer::OrbitPathListEntry& b)
|
|
|
|
{
|
|
|
|
// Operation is reversed because -z axis points into the screen
|
|
|
|
return a.centerZ - a.radius > b.centerZ - b.radius;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-15 11:51:16 -07:00
|
|
|
bool Renderer::init(GLContext* _context,
|
|
|
|
int winWidth, int winHeight,
|
|
|
|
DetailOptions& _detailOptions)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2003-02-19 10:48:25 -07:00
|
|
|
context = _context;
|
2004-03-15 11:51:16 -07:00
|
|
|
detailOptions = _detailOptions;
|
2003-02-19 10:48:25 -07:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
// Initialize static meshes and textures common to all instances of Renderer
|
|
|
|
if (!commonDataInitialized)
|
|
|
|
{
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere = new LODSphereMesh();
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
starTex = CreateProceduralTexture(64, 64, GL_RGB, StarTextureEval);
|
|
|
|
|
2003-07-29 23:34:04 -06:00
|
|
|
glareTex = LoadTextureFromFile("textures/flare.jpg");
|
2001-11-27 18:50:04 -07:00
|
|
|
if (glareTex == NULL)
|
|
|
|
glareTex = CreateProceduralTexture(64, 64, GL_RGB, GlareTextureEval);
|
|
|
|
|
2002-04-16 03:32:55 -06:00
|
|
|
// 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);
|
2003-07-29 23:34:04 -06:00
|
|
|
Texture::AddressMode shadowTexAddress = Texture::EdgeClamp;
|
|
|
|
Texture::MipMapMode shadowTexMip = Texture::NoMipMaps;
|
2003-02-19 10:48:25 -07:00
|
|
|
useClampToBorder = context->extensionSupported("GL_ARB_texture_border_clamp");
|
2003-07-29 23:34:04 -06:00
|
|
|
if (useClampToBorder)
|
|
|
|
{
|
|
|
|
shadowTexAddress = Texture::BorderClamp;
|
|
|
|
shadowTexMip = Texture::DefaultMipMaps;
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-03-15 11:51:16 -07:00
|
|
|
shadowTex = CreateProceduralTexture(detailOptions.shadowTextureSize,
|
|
|
|
detailOptions.shadowTextureSize,
|
|
|
|
GL_RGB,
|
2003-07-29 23:34:04 -06:00
|
|
|
ShadowTextureEval,
|
|
|
|
shadowTexAddress, shadowTexMip);
|
2002-04-16 03:32:55 -06:00
|
|
|
shadowTex->setBorderColor(Color::White);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-12-20 11:56:43 -07:00
|
|
|
if (gaussianDiscTex == NULL)
|
|
|
|
gaussianDiscTex = BuildGaussianDiscTexture(8);
|
|
|
|
if (gaussianGlareTex == NULL)
|
|
|
|
gaussianGlareTex = BuildGaussianGlareTexture(9);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2002-02-18 15:57:22 -07:00
|
|
|
// Create the eclipse shadow textures
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
{
|
2002-02-26 19:37:40 -07:00
|
|
|
ShadowTextureFunction func(i * 0.25f);
|
2002-02-18 15:57:22 -07:00
|
|
|
eclipseShadowTextures[i] =
|
2004-03-15 11:51:16 -07:00
|
|
|
CreateProceduralTexture(detailOptions.eclipseTextureSize,
|
|
|
|
detailOptions.eclipseTextureSize,
|
|
|
|
GL_RGB, func,
|
2003-07-29 23:34:04 -06:00
|
|
|
shadowTexAddress, shadowTexMip);
|
2002-02-18 15:57:22 -07:00
|
|
|
if (eclipseShadowTextures[i] != NULL)
|
|
|
|
{
|
2002-04-16 03:32:55 -06:00
|
|
|
// eclipseShadowTextures[i]->setMaxMipMapLevel(2);
|
|
|
|
eclipseShadowTextures[i]->setBorderColor(Color::White);
|
2002-02-18 15:57:22 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-02-14 01:39:58 -07:00
|
|
|
// Create the shadow mask texture
|
2003-02-14 21:25:39 -07:00
|
|
|
{
|
|
|
|
ShadowMaskTextureFunction func;
|
2004-03-12 01:31:10 -07:00
|
|
|
shadowMaskTexture = CreateProceduralTexture(128, 2, GL_RGBA, func);
|
2003-07-29 23:34:04 -06:00
|
|
|
//shadowMaskTexture->bindName();
|
2003-02-14 21:25:39 -07:00
|
|
|
}
|
2003-02-14 01:39:58 -07:00
|
|
|
|
2004-03-12 01:31:10 -07:00
|
|
|
// Create a function lookup table in a texture for use with
|
|
|
|
// fragment program eclipse shadows.
|
|
|
|
penumbraFunctionTexture = CreateProceduralTexture(512, 1, GL_LUMINANCE,
|
|
|
|
PenumbraFunctionEval,
|
|
|
|
Texture::EdgeClamp);
|
|
|
|
|
2003-02-19 10:48:25 -07:00
|
|
|
if (context->extensionSupported("GL_EXT_texture_cube_map"))
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
// normalizationTex = CreateNormalizationCubeMap(64);
|
|
|
|
normalizationTex = CreateProceduralCubeMap(64, GL_RGB, IllumMapEval);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create labels for celestial sphere
|
|
|
|
{
|
|
|
|
char buf[10];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
coordLabels = new SphericalCoordLabel[nCoordLabels];
|
|
|
|
for (i = 0; i < 12; i++)
|
|
|
|
{
|
2004-02-28 02:30:17 -07:00
|
|
|
coordLabels[i].ra = float(i * 2);
|
2001-11-27 18:50:04 -07:00
|
|
|
coordLabels[i].dec = 0;
|
|
|
|
sprintf(buf, "%dh", i * 2);
|
|
|
|
coordLabels[i].label = string(buf);
|
|
|
|
}
|
|
|
|
|
2006-11-05 02:08:44 -07:00
|
|
|
coordLabels[12] = SphericalCoordLabel(0, -80);
|
|
|
|
coordLabels[13] = SphericalCoordLabel(0, -70);
|
|
|
|
coordLabels[14] = SphericalCoordLabel(0, -60);
|
|
|
|
coordLabels[15] = SphericalCoordLabel(0, -50);
|
|
|
|
coordLabels[16] = SphericalCoordLabel(0, -40);
|
|
|
|
coordLabels[17] = SphericalCoordLabel(0, -30);
|
|
|
|
coordLabels[18] = SphericalCoordLabel(0, -20);
|
|
|
|
coordLabels[19] = SphericalCoordLabel(0, -10);
|
|
|
|
coordLabels[20] = SphericalCoordLabel(0, 10);
|
|
|
|
coordLabels[21] = SphericalCoordLabel(0, 20);
|
|
|
|
coordLabels[22] = SphericalCoordLabel(0, 30);
|
|
|
|
coordLabels[23] = SphericalCoordLabel(0, 40);
|
|
|
|
coordLabels[24] = SphericalCoordLabel(0, 50);
|
|
|
|
coordLabels[25] = SphericalCoordLabel(0, 60);
|
|
|
|
coordLabels[26] = SphericalCoordLabel(0, 70);
|
|
|
|
coordLabels[27] = SphericalCoordLabel(0, 80);
|
|
|
|
for (i = 28; i < nCoordLabels; i++)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
coordLabels[i].ra = 12;
|
2006-11-05 02:08:44 -07:00
|
|
|
coordLabels[i].dec = coordLabels[i - 16].dec;
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 12; i < nCoordLabels; i++)
|
|
|
|
{
|
|
|
|
char buf[10];
|
|
|
|
sprintf(buf, "%d", (int) coordLabels[i].dec);
|
|
|
|
coordLabels[i].label = string(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
commonDataInitialized = true;
|
|
|
|
}
|
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
#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
|
|
|
|
|
2003-02-19 10:48:25 -07:00
|
|
|
if (context->extensionSupported("GL_EXT_rescale_normal"))
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
// We need this enabled because we use glScale, but only
|
|
|
|
// with uniform scale factors.
|
2002-02-06 14:00:33 -07:00
|
|
|
DPRINTF(1, "Renderer: EXT_rescale_normal supported.\n");
|
2001-11-27 18:50:04 -07:00
|
|
|
useRescaleNormal = true;
|
|
|
|
glEnable(GL_RESCALE_NORMAL_EXT);
|
|
|
|
}
|
2005-11-25 15:57:20 -07:00
|
|
|
|
|
|
|
if (context->extensionSupported("GL_ARB_point_sprite"))
|
2002-05-14 11:02:04 -06:00
|
|
|
{
|
2005-11-25 15:57:20 -07:00
|
|
|
DPRINTF(1, "Renderer: point sprites supported.\n");
|
|
|
|
usePointSprite = true;
|
2002-05-14 11:02:04 -06:00
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-07-08 20:03:29 -06:00
|
|
|
if (context->extensionSupported("GL_EXT_separate_specular_color"))
|
|
|
|
{
|
|
|
|
glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT, GL_SEPARATE_SPECULAR_COLOR_EXT);
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// 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
|
2002-03-28 19:53:32 -07:00
|
|
|
if (strstr(glRenderer, "GeForce3") != NULL ||
|
|
|
|
strstr(glRenderer, "GeForce4") != NULL)
|
|
|
|
{
|
|
|
|
buggyVertexProgramEmulation = false;
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2002-09-28 03:21:26 -06:00
|
|
|
if (strstr(glRenderer, "Savage4") != NULL ||
|
|
|
|
strstr(glRenderer, "ProSavage") != NULL)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-11-23 11:25:28 -07:00
|
|
|
// 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)
|
|
|
|
{
|
2002-11-23 17:48:08 -07:00
|
|
|
if (major > 1 || minor > 3 || (minor == 3 && extra >= 1))
|
2002-11-23 11:25:28 -07:00
|
|
|
buggyVertexProgramEmulation = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
glLoadIdentity();
|
|
|
|
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glCullFace(GL_BACK);
|
|
|
|
|
|
|
|
glEnable(GL_COLOR_MATERIAL);
|
|
|
|
glEnable(GL_LIGHTING);
|
2002-01-09 16:00:36 -07:00
|
|
|
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// LEQUAL rather than LESS required for multipass rendering
|
|
|
|
glDepthFunc(GL_LEQUAL);
|
|
|
|
|
|
|
|
resize(winWidth, winHeight);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::resize(int width, int height)
|
|
|
|
{
|
|
|
|
windowWidth = width;
|
|
|
|
windowHeight = height;
|
|
|
|
// glViewport(windowWidth, windowHeight);
|
|
|
|
}
|
|
|
|
|
2003-01-01 16:48:15 -07:00
|
|
|
float Renderer::calcPixelSize(float fovY, float windowHeight)
|
|
|
|
{
|
|
|
|
return 2 * (float) tan(degToRad(fovY / 2.0)) / (float) windowHeight;
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
void Renderer::setFieldOfView(float _fov)
|
|
|
|
{
|
|
|
|
fov = _fov;
|
2002-09-22 08:49:49 -06:00
|
|
|
corrFac = (0.12f * fov/FOV * fov/FOV + 1.0f);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2005-12-11 08:34:35 -07:00
|
|
|
int Renderer::getScreenDpi() const
|
|
|
|
{
|
|
|
|
return screenDpi;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::setScreenDpi(int _dpi)
|
|
|
|
{
|
|
|
|
screenDpi = _dpi;
|
|
|
|
}
|
|
|
|
|
2002-12-11 13:27:35 -07:00
|
|
|
void Renderer::setFaintestAM45deg(float _faintestAutoMag45deg)
|
|
|
|
{
|
|
|
|
faintestAutoMag45deg = _faintestAutoMag45deg;
|
|
|
|
}
|
|
|
|
|
|
|
|
float Renderer::getFaintestAM45deg()
|
|
|
|
{
|
|
|
|
return faintestAutoMag45deg;
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2002-02-13 13:38:49 -07:00
|
|
|
unsigned int Renderer::getResolution()
|
|
|
|
{
|
|
|
|
return textureResolution;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::setResolution(unsigned int resolution)
|
|
|
|
{
|
2002-02-18 01:19:23 -07:00
|
|
|
if (resolution < TEXTURE_RESOLUTION)
|
2002-02-13 13:38:49 -07:00
|
|
|
textureResolution = resolution;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
TextureFont* Renderer::getFont(FontStyle fs) const
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
return font[(int) fs];
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
void Renderer::setFont(FontStyle fs, TextureFont* txf)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
font[(int) fs] = txf;
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::setRenderMode(int _renderMode)
|
|
|
|
{
|
|
|
|
renderMode = _renderMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Renderer::getRenderFlags() const
|
|
|
|
{
|
|
|
|
return renderFlags;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::setRenderFlags(int _renderFlags)
|
|
|
|
{
|
|
|
|
renderFlags = _renderFlags;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Renderer::getLabelMode() const
|
|
|
|
{
|
|
|
|
return labelMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::setLabelMode(int _labelMode)
|
|
|
|
{
|
|
|
|
labelMode = _labelMode;
|
|
|
|
}
|
|
|
|
|
2003-07-08 09:31:34 -06:00
|
|
|
int Renderer::getOrbitMask() const
|
|
|
|
{
|
|
|
|
return orbitMask;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::setOrbitMask(int mask)
|
|
|
|
{
|
|
|
|
orbitMask = mask;
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2004-09-19 21:27:50 -06:00
|
|
|
|
|
|
|
const ColorTemperatureTable*
|
|
|
|
Renderer::getStarColorTable() const
|
|
|
|
{
|
|
|
|
return colorTemp;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Renderer::setStarColorTable(const ColorTemperatureTable* ct)
|
|
|
|
{
|
|
|
|
colorTemp = ct;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-11 18:29:27 -07:00
|
|
|
bool Renderer::getVideoSync() const
|
|
|
|
{
|
|
|
|
return videoSync;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::setVideoSync(bool sync)
|
|
|
|
{
|
|
|
|
videoSync = sync;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
float Renderer::getAmbientLightLevel() const
|
|
|
|
{
|
|
|
|
return ambientLightLevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::setAmbientLightLevel(float level)
|
|
|
|
{
|
|
|
|
ambientLightLevel = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-06-18 12:35:59 -06:00
|
|
|
float Renderer::getMinimumFeatureSize() const
|
|
|
|
{
|
|
|
|
return minFeatureSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::setMinimumFeatureSize(float pixels)
|
|
|
|
{
|
|
|
|
minFeatureSize = pixels;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-21 12:26:54 -07:00
|
|
|
float Renderer::getMinimumOrbitSize() const
|
|
|
|
{
|
|
|
|
return minOrbitSize;
|
|
|
|
}
|
|
|
|
|
2002-04-25 21:49:43 -06:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-08 21:50:37 -06:00
|
|
|
float Renderer::getDistanceLimit() const
|
|
|
|
{
|
|
|
|
return distanceLimit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::setDistanceLimit(float distanceLimit_)
|
|
|
|
{
|
|
|
|
distanceLimit = distanceLimit_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
bool Renderer::getFragmentShaderEnabled() const
|
|
|
|
{
|
|
|
|
return fragmentShaderEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::setFragmentShaderEnabled(bool enable)
|
|
|
|
{
|
|
|
|
fragmentShaderEnabled = enable && fragmentShaderSupported();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Renderer::fragmentShaderSupported() const
|
|
|
|
{
|
2003-02-19 10:48:25 -07:00
|
|
|
return context->bumpMappingSupported();
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Renderer::getVertexShaderEnabled() const
|
|
|
|
{
|
|
|
|
return vertexShaderEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::setVertexShaderEnabled(bool enable)
|
|
|
|
{
|
|
|
|
vertexShaderEnabled = enable && vertexShaderSupported();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Renderer::vertexShaderSupported() const
|
|
|
|
{
|
|
|
|
return useVertexPrograms;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
void Renderer::addLabel(const char* text,
|
|
|
|
Color color,
|
|
|
|
const Point3f& pos,
|
|
|
|
float depth)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
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;
|
2004-05-18 11:31:26 -06:00
|
|
|
depth = (float) (pos.x * modelMatrix[2] +
|
|
|
|
pos.y * modelMatrix[6] +
|
|
|
|
pos.z * modelMatrix[10]);
|
2001-11-27 18:50:04 -07:00
|
|
|
if (gluProject(pos.x, pos.y, pos.z,
|
|
|
|
modelMatrix,
|
|
|
|
projMatrix,
|
2002-03-26 16:40:16 -07:00
|
|
|
(const GLint*) view,
|
2001-11-27 18:50:04 -07:00
|
|
|
&winX, &winY, &winZ) != GL_FALSE)
|
|
|
|
{
|
|
|
|
Label l;
|
2006-03-08 11:29:10 -07:00
|
|
|
ReplaceGreekLetterAbbr(l.text, MaxLabelLength, text, strlen(text));
|
|
|
|
// Might be nice to use abbreviations instead of Greek letters
|
|
|
|
// strncpy(l.text, text, MaxLabelLength);
|
|
|
|
l.text[MaxLabelLength - 1] = '\0';
|
2001-11-27 18:50:04 -07:00
|
|
|
l.color = color;
|
2004-05-18 11:31:26 -06:00
|
|
|
l.position = Point3f((float) winX, (float) winY, -depth);
|
2001-11-27 18:50:04 -07:00
|
|
|
labels.insert(labels.end(), l);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
void Renderer::addLabel(const string& text,
|
|
|
|
Color color,
|
|
|
|
const Point3f& pos,
|
|
|
|
float depth)
|
|
|
|
{
|
|
|
|
addLabel(text.c_str(), color, pos, depth);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::addSortedLabel(const string& text, Color color, const Point3f& pos)
|
2004-05-18 11:31:26 -06:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
Label l;
|
2006-03-08 11:29:10 -07:00
|
|
|
//l.text = ReplaceGreekLetterAbbr(_(text.c_str()));
|
|
|
|
strncpy(l.text, text.c_str(), MaxLabelLength);
|
|
|
|
l.text[MaxLabelLength - 1] = '\0';
|
2004-05-18 11:31:26 -06:00
|
|
|
l.color = color;
|
|
|
|
l.position = Point3f((float) winX, (float) winY, -depth);
|
|
|
|
depthSortedLabels.insert(depthSortedLabels.end(), l);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
void Renderer::clearLabels()
|
|
|
|
{
|
|
|
|
labels.clear();
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::clearSortedLabels()
|
|
|
|
{
|
2004-05-18 11:31:26 -06:00
|
|
|
depthSortedLabels.clear();
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-01-25 02:37:54 -07:00
|
|
|
static void enableSmoothLines()
|
|
|
|
{
|
|
|
|
// glEnable(GL_BLEND);
|
2002-03-28 19:04:57 -07:00
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2002-01-25 02:37:54 -07:00
|
|
|
glEnable(GL_LINE_SMOOTH);
|
|
|
|
glLineWidth(1.5f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void disableSmoothLines()
|
|
|
|
{
|
|
|
|
// glDisable(GL_BLEND);
|
2002-03-28 19:04:57 -07:00
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
2002-01-25 02:37:54 -07:00
|
|
|
glDisable(GL_LINE_SMOOTH);
|
|
|
|
glLineWidth(1.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-03-19 17:28:43 -07:00
|
|
|
class OrbitSampler : public OrbitSampleProc
|
|
|
|
{
|
|
|
|
public:
|
2004-06-01 01:27:17 -06:00
|
|
|
vector<Renderer::OrbitSample>* samples;
|
2002-03-19 17:28:43 -07:00
|
|
|
|
2004-06-01 01:27:17 -06:00
|
|
|
OrbitSampler(vector<Renderer::OrbitSample>* _samples) : samples(_samples) {};
|
|
|
|
void sample(double t, const Point3d& p)
|
2002-03-19 17:28:43 -07:00
|
|
|
{
|
2004-06-01 01:27:17 -06:00
|
|
|
Renderer::OrbitSample samp;
|
|
|
|
samp.pos = Point3f((float) p.x, (float) p.y, (float) p.z);
|
|
|
|
samp.t = t;
|
2006-09-20 01:40:13 -06:00
|
|
|
samples->push_back(samp);
|
2002-03-19 17:28:43 -07:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
void renderOrbitColor(int classification, bool selected)
|
2005-07-25 10:38:09 -06:00
|
|
|
{
|
|
|
|
if (selected)
|
|
|
|
{
|
|
|
|
// Highlight the orbit of the selected object in red
|
|
|
|
glColor4f(1, 0, 0, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-09-07 23:59:38 -06:00
|
|
|
switch (classification)
|
2005-07-25 10:38:09 -06:00
|
|
|
{
|
|
|
|
case Body::Moon:
|
2006-12-03 01:20:55 -07:00
|
|
|
glColor4f(0.3f, 0.2f, 0.4f, 1.0f);
|
2005-07-25 10:38:09 -06:00
|
|
|
break;
|
|
|
|
case Body::Asteroid:
|
|
|
|
glColor4f(0.35f, 0.2f, 0.0f, 1.0f);
|
|
|
|
break;
|
|
|
|
case Body::Comet:
|
|
|
|
glColor4f(0.0f, 0.5f, 0.5f, 1.0f);
|
|
|
|
break;
|
|
|
|
case Body::Spacecraft:
|
|
|
|
glColor4f(0.4f, 0.4f, 0.4f, 1.0f);
|
|
|
|
break;
|
2006-12-03 01:49:52 -07:00
|
|
|
case Body::Stellar:
|
|
|
|
glColor4f(0.5f, 0.5f, 0.8f, 1.0f);
|
|
|
|
break;
|
2005-07-25 10:38:09 -06:00
|
|
|
case Body::Planet:
|
|
|
|
default:
|
|
|
|
glColor4f(0.0f, 0.4f, 1.0f, 1.0f);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-03-19 17:28:43 -07:00
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
void Renderer::renderOrbit(const OrbitPathListEntry& orbitPath, double t)
|
2002-03-19 17:28:43 -07:00
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
Body* body = orbitPath.body;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
// Ugly cast here because orbit cache needs to be rewritten to use an STL map
|
|
|
|
Body* cacheKey = body != NULL ? body : reinterpret_cast<Body*>(const_cast<Star*>(orbitPath.star));
|
2004-06-01 01:27:17 -06:00
|
|
|
vector<OrbitSample>* trajectory = NULL;
|
2002-03-19 17:28:43 -07:00
|
|
|
for (vector<CachedOrbit*>::const_iterator iter = orbitCache.begin();
|
|
|
|
iter != orbitCache.end(); iter++)
|
|
|
|
{
|
2006-09-07 23:59:38 -06:00
|
|
|
if ((*iter)->body == cacheKey)
|
2002-03-19 17:28:43 -07:00
|
|
|
{
|
|
|
|
(*iter)->keep = true;
|
|
|
|
trajectory = &((*iter)->trajectory);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
const Orbit* orbit = NULL;
|
|
|
|
if (body != NULL)
|
|
|
|
orbit = body->getOrbit();
|
|
|
|
else
|
|
|
|
orbit = orbitPath.star->getOrbit();
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-03-19 17:28:43 -07:00
|
|
|
// If it's not in the cache already
|
|
|
|
if (trajectory == NULL)
|
|
|
|
{
|
2006-09-07 23:59:38 -06:00
|
|
|
CachedOrbit* cachedOrbit = NULL;
|
2002-03-19 17:28:43 -07:00
|
|
|
|
|
|
|
// Search the cache an see if we can reuse an old orbit
|
|
|
|
for (vector<CachedOrbit*>::const_iterator iter = orbitCache.begin();
|
|
|
|
iter != orbitCache.end(); iter++)
|
|
|
|
{
|
|
|
|
if ((*iter)->body == NULL)
|
|
|
|
{
|
2006-09-07 23:59:38 -06:00
|
|
|
cachedOrbit = *iter;
|
|
|
|
cachedOrbit->trajectory.clear();
|
2002-03-19 17:28:43 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we can't reuse an old orbit, allocate a new one.
|
|
|
|
bool reuse = true;
|
2006-09-07 23:59:38 -06:00
|
|
|
if (cachedOrbit == NULL)
|
2002-03-19 17:28:43 -07:00
|
|
|
{
|
2006-09-07 23:59:38 -06:00
|
|
|
cachedOrbit = new CachedOrbit();
|
2002-03-19 17:28:43 -07:00
|
|
|
reuse = false;
|
|
|
|
}
|
|
|
|
|
2003-07-04 18:22:01 -06:00
|
|
|
double startTime = t;
|
2004-03-15 11:51:16 -07:00
|
|
|
int nSamples = detailOptions.orbitPathSamplePoints;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2003-07-04 18:22:01 -06:00
|
|
|
// 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.
|
2006-09-07 23:59:38 -06:00
|
|
|
if (!orbit->isPeriodic())
|
2003-07-04 18:22:01 -06:00
|
|
|
{
|
|
|
|
double begin = 0.0, end = 0.0;
|
2006-09-07 23:59:38 -06:00
|
|
|
orbit->getValidRange(begin, end);
|
2003-07-04 18:22:01 -06:00
|
|
|
|
|
|
|
if (begin != end)
|
|
|
|
{
|
|
|
|
startTime = begin;
|
2006-09-07 23:59:38 -06:00
|
|
|
nSamples = (int) (orbit->getPeriod() * 100.0);
|
2003-07-04 18:22:01 -06:00
|
|
|
nSamples = max(min(nSamples, 1000), 100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
cachedOrbit->body = cacheKey;
|
|
|
|
cachedOrbit->keep = true;
|
|
|
|
OrbitSampler sampler(&cachedOrbit->trajectory);
|
|
|
|
orbit->sample(startTime,
|
|
|
|
orbit->getPeriod(),
|
|
|
|
nSamples,
|
|
|
|
sampler);
|
|
|
|
trajectory = &cachedOrbit->trajectory;
|
2002-03-19 17:28:43 -07:00
|
|
|
|
|
|
|
// If the orbit is new, put it back in the cache
|
|
|
|
if (!reuse)
|
2006-09-07 23:59:38 -06:00
|
|
|
orbitCache.insert(orbitCache.end(), cachedOrbit);
|
2002-03-19 17:28:43 -07:00
|
|
|
}
|
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
glPushMatrix();
|
|
|
|
glTranslate(orbitPath.origin);
|
2006-10-31 01:23:04 -07:00
|
|
|
if (body != NULL)
|
2006-09-07 02:08:32 -06:00
|
|
|
{
|
2006-10-31 01:23:04 -07:00
|
|
|
Quatd orientation(1.0);
|
|
|
|
if (body->getOrbitFrame() != NULL)
|
|
|
|
{
|
|
|
|
orientation = body->getOrbitFrame()->getOrientation(t);
|
|
|
|
}
|
|
|
|
else if (body->getOrbitBarycenter() != NULL)
|
|
|
|
{
|
|
|
|
orientation = body->getOrbitBarycenter()->getEclipticalToEquatorial(t);
|
|
|
|
}
|
2006-09-07 02:08:32 -06:00
|
|
|
glRotate(~orientation);
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
bool highlight;
|
|
|
|
if (body != NULL)
|
|
|
|
highlight = highlightObject.body() == body;
|
|
|
|
else
|
2006-09-12 06:56:47 -06:00
|
|
|
highlight = highlightObject.star() == orbitPath.star;
|
2006-12-03 01:49:52 -07:00
|
|
|
renderOrbitColor(body != NULL ? body->getClassification() : Body::Stellar, highlight);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-03-19 17:28:43 -07:00
|
|
|
// Actually render the orbit
|
2006-09-07 23:59:38 -06:00
|
|
|
if (orbit->isPeriodic())
|
2003-07-04 18:22:01 -06:00
|
|
|
glBegin(GL_LINE_LOOP);
|
|
|
|
else
|
|
|
|
glBegin(GL_LINE_STRIP);
|
2004-06-01 01:27:17 -06:00
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
if ((renderFlags & ShowPartialTrajectories) == 0 || orbit->isPeriodic())
|
2002-03-19 17:28:43 -07:00
|
|
|
{
|
2004-06-01 01:27:17 -06:00
|
|
|
// Show the complete trajectory
|
2006-12-12 03:24:48 -07:00
|
|
|
// TODO: for much greater efficiency, use vertex arrays rather than
|
|
|
|
// immediate commands to draw orbit paths.
|
2004-06-01 01:27:17 -06:00
|
|
|
for (vector<OrbitSample>::const_iterator p = trajectory->begin();
|
|
|
|
p != trajectory->end(); p++)
|
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
glVertex3f(p->pos.x, p->pos.y, p->pos.z);
|
2004-06-01 01:27:17 -06:00
|
|
|
}
|
2002-03-19 17:28:43 -07:00
|
|
|
}
|
2004-06-01 01:27:17 -06:00
|
|
|
else
|
|
|
|
{
|
2004-06-04 01:37:25 -06:00
|
|
|
vector<OrbitSample>::const_iterator p;
|
|
|
|
|
2004-06-01 01:27:17 -06:00
|
|
|
// Show the portion of the trajectory travelled up to this point
|
2004-06-04 01:37:25 -06:00
|
|
|
for (p = trajectory->begin(); p != trajectory->end() && p->t < t; p++)
|
2004-06-01 01:27:17 -06:00
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
glVertex3f(p->pos.x, p->pos.y, p->pos.z);
|
2004-06-01 01:27:17 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// If we're midway through a non-periodic trajectory, we will need
|
|
|
|
// to render a partial orbit segment.
|
|
|
|
if (p != trajectory->end())
|
|
|
|
{
|
2006-09-07 23:59:38 -06:00
|
|
|
Point3d pos = orbit->positionAtTime(t);
|
2006-09-07 02:08:32 -06:00
|
|
|
glVertex3f((float) pos.x, (float) pos.y, (float) pos.z);
|
2004-06-01 01:27:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-03-19 17:28:43 -07:00
|
|
|
glEnd();
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
glPopMatrix();
|
2002-03-21 15:38:35 -07:00
|
|
|
}
|
|
|
|
|
2004-09-28 01:44:34 -06:00
|
|
|
|
2004-09-29 10:20:21 -06:00
|
|
|
// Convert a position in the universal coordinate system to astrocentric
|
|
|
|
// coordinates, taking into account possible orbital motion of the star.
|
2004-10-02 10:55:33 -06:00
|
|
|
static Point3d astrocentricPosition(const UniversalCoord& pos,
|
2004-09-28 01:44:34 -06:00
|
|
|
const Star& star,
|
|
|
|
double t)
|
|
|
|
{
|
2004-09-29 10:20:21 -06:00
|
|
|
UniversalCoord starPos = star.getPosition(t);
|
2004-09-28 01:44:34 -06:00
|
|
|
|
2004-09-29 10:20:21 -06:00
|
|
|
Vec3d v = pos - starPos;
|
|
|
|
return Point3d(astro::microLightYearsToKilometers(v.x),
|
|
|
|
astro::microLightYearsToKilometers(v.y),
|
|
|
|
astro::microLightYearsToKilometers(v.z));
|
2004-09-28 01:44:34 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-22 08:49:49 -06:00
|
|
|
void Renderer::autoMag(float& faintestMag)
|
2002-06-22 09:15:44 -06:00
|
|
|
{
|
2004-02-28 02:30:17 -07:00
|
|
|
float fieldCorr = 2.0f * FOV/(fov + FOV);
|
|
|
|
faintestMag = (float) (faintestAutoMag45deg * sqrt(fieldCorr));
|
2002-09-22 08:49:49 -06:00
|
|
|
saturationMag = saturationMagNight * (1.0f + fieldCorr * fieldCorr);
|
2002-06-22 09:15:44 -06:00
|
|
|
}
|
2002-03-21 15:38:35 -07:00
|
|
|
|
2002-11-03 21:14:07 -07:00
|
|
|
|
2004-10-04 00:35:33 -06:00
|
|
|
// Set up the light sources for rendering a solar system. The positions of
|
|
|
|
// all nearby stars are converted from universal to solar system coordinates.
|
|
|
|
static void
|
|
|
|
setupLightSources(const vector<const Star*>& nearStars,
|
|
|
|
const Star& sun,
|
|
|
|
double t,
|
|
|
|
vector<Renderer::LightSource>& lightSources)
|
|
|
|
{
|
|
|
|
UniversalCoord center = sun.getPosition(t);
|
|
|
|
|
|
|
|
lightSources.clear();
|
|
|
|
|
|
|
|
for (vector<const Star*>::const_iterator iter = nearStars.begin();
|
|
|
|
iter != nearStars.end(); iter++)
|
|
|
|
{
|
|
|
|
if ((*iter)->getVisibility())
|
|
|
|
{
|
|
|
|
Vec3d v = ((*iter)->getPosition(t) - center) *
|
|
|
|
astro::microLightYearsToKilometers(1.0);
|
2004-11-11 02:41:51 -07:00
|
|
|
|
2004-10-04 00:35:33 -06:00
|
|
|
Renderer::LightSource ls;
|
|
|
|
ls.position = Point3d(v.x, v.y, v.z);
|
|
|
|
ls.luminosity = (*iter)->getLuminosity();
|
2004-10-19 11:52:43 -06:00
|
|
|
ls.radius = (*iter)->getRadius();
|
2004-10-08 10:35:45 -06:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2004-10-04 00:35:33 -06:00
|
|
|
lightSources.push_back(ls);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-10 18:37:20 -06:00
|
|
|
// Render an item from the render list
|
2006-12-08 12:02:20 -07:00
|
|
|
// TODO: change the way the observer class works so that it is more efficient;
|
|
|
|
// we should only have to recompute the position and attitude in universal
|
|
|
|
// coordinates once per time step. Then, we wouldn't have to resort to passing
|
|
|
|
// the camera orientation in order to avoid extra calculation.
|
2006-09-10 18:37:20 -06:00
|
|
|
void Renderer::renderItem(const RenderListEntry& rle,
|
|
|
|
const Observer& observer,
|
2006-12-08 12:02:20 -07:00
|
|
|
const Quatf& cameraOrientation,
|
2006-09-10 18:37:20 -06:00
|
|
|
float nearPlaneDistance,
|
|
|
|
float farPlaneDistance)
|
|
|
|
{
|
|
|
|
if (rle.body != NULL)
|
2006-09-12 06:56:47 -06:00
|
|
|
{
|
2006-09-10 18:37:20 -06:00
|
|
|
if (rle.isCometTail)
|
|
|
|
{
|
|
|
|
renderCometTail(*rle.body,
|
|
|
|
rle.position,
|
|
|
|
rle.distance,
|
|
|
|
rle.appMag,
|
|
|
|
observer.getTime(),
|
2006-12-08 12:02:20 -07:00
|
|
|
cameraOrientation,
|
2006-09-10 18:37:20 -06:00
|
|
|
lightSourceLists[rle.solarSysIndex],
|
|
|
|
nearPlaneDistance, farPlaneDistance);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
renderPlanet(*rle.body,
|
|
|
|
rle.position,
|
|
|
|
rle.distance,
|
|
|
|
rle.appMag,
|
2006-12-08 12:02:20 -07:00
|
|
|
observer,
|
|
|
|
cameraOrientation,
|
2006-09-10 18:37:20 -06:00
|
|
|
lightSourceLists[rle.solarSysIndex],
|
|
|
|
nearPlaneDistance, farPlaneDistance);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (rle.star != NULL)
|
|
|
|
{
|
|
|
|
renderStar(*rle.star,
|
|
|
|
rle.position,
|
|
|
|
rle.distance,
|
|
|
|
rle.appMag,
|
2006-12-08 12:02:20 -07:00
|
|
|
cameraOrientation,
|
2006-09-10 18:37:20 -06:00
|
|
|
observer.getTime(),
|
|
|
|
nearPlaneDistance, farPlaneDistance);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
void Renderer::render(const Observer& observer,
|
2002-01-07 15:48:32 -07:00
|
|
|
const Universe& universe,
|
2001-12-11 00:40:53 -07:00
|
|
|
float faintestMagNight,
|
2003-02-21 23:49:37 -07:00
|
|
|
const Selection& sel)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2003-02-21 23:49:37 -07:00
|
|
|
// Get the observer's time
|
|
|
|
double now = observer.getTime();
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
// Compute the size of a pixel
|
2003-02-14 11:07:47 -07:00
|
|
|
setFieldOfView(radToDeg(observer.getFOV()));
|
2004-02-28 02:30:17 -07:00
|
|
|
pixelSize = calcPixelSize(fov, (float) windowHeight);
|
2001-11-27 18:50:04 -07:00
|
|
|
// Set up the projection we'll use for rendering stars.
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glLoadIdentity();
|
|
|
|
gluPerspective(fov,
|
|
|
|
(float) windowWidth / (float) windowHeight,
|
|
|
|
NEAR_DIST, FAR_DIST);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
// Set the modelview matrix
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
|
2003-06-08 21:57:43 -06:00
|
|
|
// Get the displayed surface texture set to use from the observer
|
|
|
|
displayedSurface = observer.getDisplayedSurface();
|
|
|
|
|
2003-06-23 08:04:28 -06:00
|
|
|
locationFilter = observer.getLocationFilter();
|
|
|
|
|
2006-12-03 01:49:52 -07:00
|
|
|
if (usePointSprite && getGLContext()->getVertexProcessor() != NULL)
|
2006-03-08 11:29:10 -07:00
|
|
|
{
|
|
|
|
useNewStarRendering = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
useNewStarRendering = false;
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
// Highlight the selected object
|
|
|
|
highlightObject = sel;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
Quatf cameraOrientation = observer.getOrientation();
|
|
|
|
|
|
|
|
// Set up the camera for star rendering; the units of this phase
|
|
|
|
// are light years.
|
|
|
|
Point3f observerPosLY = (Point3f) observer.getPosition();
|
|
|
|
observerPosLY.x *= 1e-6f;
|
|
|
|
observerPosLY.y *= 1e-6f;
|
|
|
|
observerPosLY.z *= 1e-6f;
|
2001-11-27 18:50:04 -07:00
|
|
|
glPushMatrix();
|
2006-12-08 12:02:20 -07:00
|
|
|
glRotate(cameraOrientation);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// Get the model matrix *before* translation. We'll use this for
|
|
|
|
// positioning star and planet labels.
|
|
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
|
|
|
|
glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
clearLabels();
|
2006-11-21 06:02:16 -07:00
|
|
|
clearSortedLabels();
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// Put all solar system bodies into the render list. Stars close and
|
|
|
|
// large enough to have discernible surface detail are also placed in
|
|
|
|
// renderList.
|
|
|
|
renderList.clear();
|
2006-09-07 02:08:32 -06:00
|
|
|
orbitPathList.clear();
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2002-09-22 08:49:49 -06:00
|
|
|
// See if we want to use AutoMag.
|
|
|
|
if ((renderFlags & ShowAutoMag) != 0)
|
2004-03-12 01:31:10 -07:00
|
|
|
{
|
2002-09-22 08:49:49 -06:00
|
|
|
autoMag(faintestMag);
|
2004-03-12 01:31:10 -07:00
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
else
|
2002-09-22 08:49:49 -06:00
|
|
|
{
|
|
|
|
faintestMag = faintestMagNight;
|
|
|
|
saturationMag = saturationMagNight;
|
|
|
|
}
|
2004-03-12 01:31:10 -07:00
|
|
|
|
|
|
|
faintestPlanetMag = faintestMag;
|
|
|
|
|
2004-10-02 04:44:49 -06:00
|
|
|
if (renderFlags & ShowPlanets)
|
|
|
|
{
|
|
|
|
nearStars.clear();
|
|
|
|
universe.getNearStars(observer.getPosition(), 1.0f, nearStars);
|
2004-11-11 02:41:51 -07:00
|
|
|
|
|
|
|
unsigned int solarSysIndex = 0;
|
2004-10-02 04:44:49 -06:00
|
|
|
for (vector<const Star*>::const_iterator iter = nearStars.begin();
|
|
|
|
iter != nearStars.end(); iter++)
|
|
|
|
{
|
|
|
|
const Star* sun = *iter;
|
|
|
|
SolarSystem* solarSystem = universe.getSolarSystem(sun);
|
|
|
|
if (solarSystem != NULL)
|
|
|
|
{
|
2004-11-11 02:41:51 -07:00
|
|
|
setupLightSources(nearStars, *sun, now,
|
|
|
|
lightSourceLists[solarSysIndex]);
|
2006-09-07 23:59:38 -06:00
|
|
|
buildRenderLists(*sun,
|
|
|
|
solarSystem->getPlanets(),
|
|
|
|
observer,
|
|
|
|
now,
|
|
|
|
solarSysIndex,
|
|
|
|
(labelMode & (BodyLabelMask)) != 0);
|
2004-11-11 02:41:51 -07:00
|
|
|
solarSysIndex++;
|
2004-10-02 04:44:49 -06:00
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
addStarOrbitToRenderList(*sun, observer, now);
|
2004-10-02 04:44:49 -06:00
|
|
|
}
|
2004-10-04 00:35:33 -06:00
|
|
|
starTex->bind();
|
2004-10-02 04:44:49 -06:00
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
Color skyColor(0.0f, 0.0f, 0.0f);
|
|
|
|
|
|
|
|
// Scan through the render list to see if we're inside a planetary
|
|
|
|
// atmosphere. If so, we need to adjust the sky color as well as the
|
|
|
|
// limiting magnitude of stars (so stars aren't visible in the daytime
|
|
|
|
// on planets with thick atmospheres.)
|
2003-06-03 11:16:00 -06:00
|
|
|
if ((renderFlags & ShowAtmospheres) != 0)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
for (vector<RenderListEntry>::iterator iter = renderList.begin();
|
|
|
|
iter != renderList.end(); iter++)
|
|
|
|
{
|
|
|
|
if (iter->body != NULL && iter->body->getAtmosphere() != NULL)
|
|
|
|
{
|
2003-06-03 11:16:00 -06:00
|
|
|
// Compute the density of the atmosphere, and from that
|
|
|
|
// the amount light scattering. It's complicated by the
|
|
|
|
// possibility that the planet is oblate and a simple distance
|
|
|
|
// to sphere calculation will not suffice.
|
2001-11-27 18:50:04 -07:00
|
|
|
const Atmosphere* atmosphere = iter->body->getAtmosphere();
|
|
|
|
float radius = iter->body->getRadius();
|
2003-06-03 11:16:00 -06:00
|
|
|
float oblateness = iter->body->getOblateness();
|
|
|
|
Vec3f recipSemiAxes(1.0f, 1.0f / (1.0f - oblateness), 1.0f);
|
|
|
|
Mat3f A = Mat3f::scaling(recipSemiAxes);
|
|
|
|
Vec3f eyeVec = iter->position - Point3f(0.0f, 0.0f, 0.0f);
|
|
|
|
eyeVec *= (1.0f / radius);
|
|
|
|
|
|
|
|
// Compute the orientation of the planet before axial rotation
|
|
|
|
Quatd qd = iter->body->getEclipticalToEquatorial(now);
|
|
|
|
Quatf q((float) qd.w, (float) qd.x, (float) qd.y, (float) qd.z);
|
|
|
|
eyeVec = eyeVec * conjugate(q).toMatrix3();
|
|
|
|
|
|
|
|
// ellipDist is not the true distance from the surface unless
|
|
|
|
// the planet is spherical. The quantity that we do compute
|
|
|
|
// is the distance to the surface along a line from the eye
|
|
|
|
// position to the center of the ellipsoid.
|
|
|
|
float ellipDist = (float) sqrt((eyeVec * A) * (eyeVec * A)) - 1.0f;
|
|
|
|
if (ellipDist < atmosphere->height / radius &&
|
2001-11-27 18:50:04 -07:00
|
|
|
atmosphere->height > 0.0f)
|
|
|
|
{
|
2003-06-03 11:16:00 -06:00
|
|
|
float density = 1.0f - ellipDist /
|
|
|
|
(atmosphere->height / radius);
|
2001-11-27 18:50:04 -07:00
|
|
|
if (density > 1.0f)
|
|
|
|
density = 1.0f;
|
|
|
|
|
|
|
|
Vec3f sunDir = iter->sun;
|
|
|
|
Vec3f normal = Point3f(0.0f, 0.0f, 0.0f) - iter->position;
|
|
|
|
sunDir.normalize();
|
|
|
|
normal.normalize();
|
|
|
|
float illumination = Math<float>::clamp((sunDir * normal) + 0.2f);
|
|
|
|
|
|
|
|
float lightness = illumination * density;
|
2003-06-03 11:16:00 -06:00
|
|
|
faintestMag = faintestMag - 15.0f * lightness;
|
|
|
|
saturationMag = saturationMag - 15.0f * lightness;
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now we need to determine how to scale the brightness of stars. The
|
|
|
|
// brightness will be proportional to the apparent magnitude, i.e.
|
|
|
|
// a logarithmic function of the stars apparent brightness. This mimics
|
|
|
|
// the response of the human eye. We sort of fudge things here and
|
2006-03-08 11:29:10 -07:00
|
|
|
// maintain a minimum range of six magnitudes between faintest visible
|
2001-11-27 18:50:04 -07:00
|
|
|
// and saturation; this keeps stars from popping in or out as the sun
|
|
|
|
// sets or rises.
|
2006-03-08 11:29:10 -07:00
|
|
|
if (faintestMag - saturationMag >= 6.0f)
|
2001-11-27 18:50:04 -07:00
|
|
|
brightnessScale = 1.0f / (faintestMag - saturationMag);
|
|
|
|
else
|
2006-03-08 11:29:10 -07:00
|
|
|
brightnessScale = 0.1667f;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
ambientColor = Color(ambientLightLevel, ambientLightLevel, ambientLightLevel);
|
|
|
|
|
|
|
|
// Create the ambient light source. For realistic scenes in space, this
|
|
|
|
// should be black.
|
|
|
|
glAmbientLightColor(ambientColor);
|
|
|
|
|
|
|
|
glClearColor(skyColor.red(), skyColor.green(), skyColor.blue(), 1);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
|
|
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
if ((renderFlags & ShowCelestialSphere) != 0)
|
|
|
|
{
|
2002-07-25 23:41:25 -06:00
|
|
|
glColor4f(0.3f, 0.7f, 0.7f, 0.55f);
|
2001-11-27 18:50:04 -07:00
|
|
|
glDisable(GL_TEXTURE_2D);
|
2002-01-25 02:37:54 -07:00
|
|
|
if ((renderFlags & ShowSmoothLines) != 0)
|
|
|
|
enableSmoothLines();
|
2001-11-27 18:50:04 -07:00
|
|
|
renderCelestialSphere(observer);
|
2002-01-25 02:37:54 -07:00
|
|
|
if ((renderFlags & ShowSmoothLines) != 0)
|
|
|
|
disableSmoothLines();
|
|
|
|
glEnable(GL_BLEND);
|
2001-11-27 18:50:04 -07:00
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
}
|
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
if (universe.getDSOCatalog() != NULL)
|
|
|
|
renderDeepSkyObjects(universe, observer, faintestMag);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// Translate the camera before rendering the stars
|
|
|
|
glPushMatrix();
|
2006-12-08 12:02:20 -07:00
|
|
|
glTranslatef(-observerPosLY.x, -observerPosLY.y, -observerPosLY.z);
|
2001-11-27 18:50:04 -07:00
|
|
|
// Render stars
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
2002-01-07 15:48:32 -07:00
|
|
|
if ((renderFlags & ShowStars) != 0 && universe.getStarCatalog() != NULL)
|
2006-11-22 00:08:19 -07:00
|
|
|
{
|
|
|
|
if (useNewStarRendering)
|
|
|
|
renderPointStars(*universe.getStarCatalog(), faintestMag, observer);
|
|
|
|
else
|
|
|
|
renderStars(*universe.getStarCatalog(), faintestMag, observer);
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// Render asterisms
|
2002-01-07 15:48:32 -07:00
|
|
|
if ((renderFlags & ShowDiagrams) != 0 && universe.getAsterisms() != NULL)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2002-06-22 09:15:44 -06:00
|
|
|
glColor4f(0.28f, 0.0f, 0.66f, 0.96f);
|
2001-11-27 18:50:04 -07:00
|
|
|
glDisable(GL_TEXTURE_2D);
|
2002-01-25 02:37:54 -07:00
|
|
|
if ((renderFlags & ShowSmoothLines) != 0)
|
|
|
|
enableSmoothLines();
|
2002-01-07 15:48:32 -07:00
|
|
|
AsterismList* asterisms = universe.getAsterisms();
|
2001-11-27 18:50:04 -07:00
|
|
|
for (AsterismList::const_iterator iter = asterisms->begin();
|
|
|
|
iter != asterisms->end(); iter++)
|
|
|
|
{
|
|
|
|
Asterism* ast = *iter;
|
|
|
|
|
|
|
|
for (int i = 0; i < ast->getChainCount(); i++)
|
|
|
|
{
|
|
|
|
const Asterism::Chain& chain = ast->getChain(i);
|
|
|
|
|
|
|
|
glBegin(GL_LINE_STRIP);
|
|
|
|
for (Asterism::Chain::const_iterator iter = chain.begin();
|
|
|
|
iter != chain.end(); iter++)
|
|
|
|
glVertex(*iter);
|
|
|
|
glEnd();
|
|
|
|
}
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-01-25 02:37:54 -07:00
|
|
|
if ((renderFlags & ShowSmoothLines) != 0)
|
|
|
|
disableSmoothLines();
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2002-06-04 23:27:53 -06:00
|
|
|
if ((renderFlags & ShowBoundaries) != 0)
|
|
|
|
{
|
2002-07-25 23:41:25 -06:00
|
|
|
glColor4f(0.8f, 0.33f, 0.63f, 0.35f);
|
2002-06-04 23:27:53 -06:00
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
if ((renderFlags & ShowSmoothLines) != 0)
|
|
|
|
enableSmoothLines();
|
|
|
|
if (universe.getBoundaries() != NULL)
|
|
|
|
universe.getBoundaries()->render();
|
|
|
|
if ((renderFlags & ShowSmoothLines) != 0)
|
|
|
|
disableSmoothLines();
|
|
|
|
}
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
renderLabels(FontNormal);
|
|
|
|
clearLabels();
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
if ((labelMode & ConstellationLabels) != 0 && universe.getAsterisms() != NULL)
|
|
|
|
{
|
2002-01-07 15:48:32 -07:00
|
|
|
labelConstellations(*universe.getAsterisms(), observer);
|
2006-03-08 11:29:10 -07:00
|
|
|
renderLabels(FontLarge);
|
|
|
|
clearLabels();
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
glPopMatrix();
|
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
// Clear the keep flag for all orbits in the cache; if they're not
|
|
|
|
// used when rendering this frame, they'll get marked for
|
|
|
|
// recycling.
|
2004-10-04 00:35:33 -06:00
|
|
|
if ((renderFlags & ShowOrbits) != 0 && orbitMask != 0)
|
2001-12-09 12:08:12 -07:00
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
for (vector<CachedOrbit*>::const_iterator iter = orbitCache.begin(); iter != orbitCache.end(); iter++)
|
2002-03-19 17:28:43 -07:00
|
|
|
(*iter)->keep = false;
|
2001-12-09 12:08:12 -07:00
|
|
|
}
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
renderLabels(FontNormal);
|
2004-05-18 11:31:26 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
glPolygonMode(GL_FRONT, (GLenum) renderMode);
|
|
|
|
glPolygonMode(GL_BACK, (GLenum) renderMode);
|
|
|
|
|
|
|
|
{
|
|
|
|
Frustum frustum(degToRad(fov),
|
|
|
|
(float) windowWidth / (float) windowHeight,
|
|
|
|
MinNearPlaneDistance);
|
|
|
|
Mat3f viewMat = conjugate(observer.getOrientation()).toMatrix3();
|
|
|
|
|
|
|
|
// Remove objects from the render list that lie completely outside the
|
|
|
|
// view frustum.
|
|
|
|
vector<RenderListEntry>::iterator notCulled = renderList.begin();
|
|
|
|
for (vector<RenderListEntry>::iterator iter = renderList.begin();
|
|
|
|
iter != renderList.end(); iter++)
|
|
|
|
{
|
|
|
|
Point3f center = iter->position * viewMat;
|
|
|
|
|
|
|
|
bool convex = true;
|
|
|
|
float radius = 1.0f;
|
|
|
|
float cullRadius = 1.0f;
|
2002-01-11 12:26:35 -07:00
|
|
|
float cloudHeight = 0.0f;
|
2006-11-27 12:11:55 -07:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
if (iter->body != NULL)
|
|
|
|
{
|
2006-09-20 01:40:13 -06:00
|
|
|
if (iter->isCometTail)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2006-09-20 01:40:13 -06:00
|
|
|
radius = iter->radius;
|
|
|
|
cullRadius = radius;
|
2001-11-27 18:50:04 -07:00
|
|
|
convex = false;
|
|
|
|
}
|
2006-09-20 01:40:13 -06:00
|
|
|
else
|
|
|
|
{
|
|
|
|
radius = iter->body->getBoundingRadius();
|
|
|
|
if (iter->body->getRings() != NULL)
|
|
|
|
{
|
|
|
|
radius = iter->body->getRings()->outerRadius;
|
|
|
|
convex = false;
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-09-20 01:40:13 -06:00
|
|
|
if (iter->body->getModel() != InvalidResource)
|
|
|
|
convex = false;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-09-20 01:40:13 -06:00
|
|
|
cullRadius = radius;
|
|
|
|
if (iter->body->getAtmosphere() != NULL)
|
|
|
|
{
|
|
|
|
cullRadius += iter->body->getAtmosphere()->height;
|
|
|
|
cloudHeight = max(iter->body->getAtmosphere()->cloudHeight,
|
|
|
|
iter->body->getAtmosphere()->mieScaleHeight * (float) -log(AtmosphereExtinctionThreshold));
|
|
|
|
}
|
2002-01-11 12:26:35 -07:00
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
else if (iter->star != NULL)
|
|
|
|
{
|
|
|
|
radius = iter->star->getRadius();
|
2002-02-08 14:55:26 -07:00
|
|
|
cullRadius = radius * (1.0f + CoronaHeight);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Test the object's bounding sphere against the view frustum
|
|
|
|
if (frustum.testSphere(center, cullRadius) != Frustum::Outside)
|
|
|
|
{
|
|
|
|
float nearZ = center.distanceFromOrigin() - radius;
|
|
|
|
float maxSpan = (float) sqrt(square((float) windowWidth) +
|
|
|
|
square((float) windowHeight));
|
|
|
|
|
2002-01-10 00:39:04 -07:00
|
|
|
nearZ = -nearZ * (float) cos(degToRad(fov / 2)) *
|
2001-11-27 18:50:04 -07:00
|
|
|
((float) windowHeight / maxSpan);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
if (nearZ > -MinNearPlaneDistance)
|
2006-09-07 02:08:32 -06:00
|
|
|
iter->nearZ = -max(MinNearPlaneDistance, radius / 2000.0f);
|
2001-11-27 18:50:04 -07:00
|
|
|
else
|
|
|
|
iter->nearZ = nearZ;
|
|
|
|
|
|
|
|
if (!convex)
|
|
|
|
{
|
|
|
|
iter->farZ = center.z - radius;
|
2006-06-27 01:51:20 -06:00
|
|
|
if (iter->farZ / iter->nearZ > MaxFarNearRatio * 0.5f)
|
|
|
|
iter->nearZ = iter->farZ / (MaxFarNearRatio * 0.5f);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2002-01-10 00:39:04 -07:00
|
|
|
// Make the far plane as close as possible
|
2001-11-27 18:50:04 -07:00
|
|
|
float d = center.distanceFromOrigin();
|
|
|
|
|
2002-01-10 00:39:04 -07:00
|
|
|
// Account for the oblateness
|
|
|
|
float eradius = radius;
|
|
|
|
if (iter->body != NULL)
|
|
|
|
eradius *= 1.0f - iter->body->getOblateness();
|
|
|
|
|
|
|
|
if (d > eradius)
|
|
|
|
{
|
2006-09-21 23:51:51 -06:00
|
|
|
iter->farZ = iter->centerZ - iter->radius;
|
2002-01-10 00:39:04 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We're inside the bounding sphere (and, if the planet
|
|
|
|
// is spherical, inside the planet.)
|
|
|
|
iter->farZ = iter->nearZ * 2.0f;
|
|
|
|
}
|
2002-01-11 12:26:35 -07:00
|
|
|
|
2006-08-26 17:58:48 -06:00
|
|
|
if (cloudHeight > 0.0f)
|
2002-01-11 12:26:35 -07:00
|
|
|
{
|
2006-09-12 06:56:47 -06:00
|
|
|
// If there's a cloud layer, we need to move the
|
2002-01-11 12:26:35 -07:00
|
|
|
// far plane out so that the clouds aren't clipped
|
|
|
|
float cloudLayerRadius = eradius + cloudHeight;
|
|
|
|
iter->farZ -= (float) sqrt(square(cloudLayerRadius) -
|
|
|
|
square(eradius));
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
*notCulled = *iter;
|
|
|
|
notCulled++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
renderList.resize(notCulled - renderList.begin());
|
|
|
|
|
2006-12-20 12:34:47 -07:00
|
|
|
// The calls to buildRenderLists/renderStars filled renderList
|
|
|
|
// with visible bodies. Sort it front to back, then
|
2006-09-07 02:08:32 -06:00
|
|
|
// render each entry in reverse order (TODO: convenient, but not
|
|
|
|
// ideal for performance; should render opaque objects front to
|
|
|
|
// back, then translucent objects back to front. However, the
|
|
|
|
// amount of overdraw in Celestia is typically low.)
|
|
|
|
sort(renderList.begin(), renderList.end());
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2004-05-18 11:31:26 -06:00
|
|
|
// Sort the labels
|
|
|
|
sort(depthSortedLabels.begin(), depthSortedLabels.end());
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
// Sort the orbit paths
|
|
|
|
sort(orbitPathList.begin(), orbitPathList.end());
|
2004-05-18 11:31:26 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
int nEntries = renderList.size();
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-06-27 01:51:20 -06:00
|
|
|
#define DEBUG_COALESCE 0
|
2006-09-07 02:08:32 -06:00
|
|
|
// Since we're rendering objects of a huge range of sizes spread over
|
|
|
|
// vast distances, we can't just rely on the hardware depth buffer to
|
|
|
|
// handle hidden surface removal without a little help. We'll partition
|
2006-11-21 06:02:16 -07:00
|
|
|
// the depth buffer into spans that can be rendered without running
|
|
|
|
// into terrible depth buffer precision problems. Typically, each body
|
2004-06-26 02:22:20 -06:00
|
|
|
// with an apparent size greater than one pixel is allocated its own
|
2006-11-21 06:02:16 -07:00
|
|
|
// depth buffer interval. However, this will not correctly handle
|
|
|
|
// overlapping objects. If two objects overlap in depth, we must
|
|
|
|
// assign them to the same interval.
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
depthPartitions.clear();
|
2006-11-21 06:02:16 -07:00
|
|
|
int nIntervals = 0;
|
2006-09-07 02:08:32 -06:00
|
|
|
float prevNear = 0.0f;
|
|
|
|
if (nEntries > 0)
|
|
|
|
prevNear = renderList[nEntries - 1].farZ * 1.01f;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
int i;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
// Completely partition the depth buffer. Scan from back to front
|
|
|
|
// through all the renderable items that passed the culling test.
|
2006-09-07 02:08:32 -06:00
|
|
|
for (i = nEntries - 1; i >= 0; i--)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2006-09-21 23:51:51 -06:00
|
|
|
// Only consider renderables that will occupy more than one pixel.
|
2006-09-07 02:08:32 -06:00
|
|
|
if (renderList[i].discSizeInPixels > 1)
|
2004-06-26 02:22:20 -06:00
|
|
|
{
|
2006-11-21 06:02:16 -07:00
|
|
|
if (nIntervals == 0 || renderList[i].farZ >= depthPartitions[nIntervals - 1].nearZ)
|
2004-06-26 02:22:20 -06:00
|
|
|
{
|
2006-11-21 06:02:16 -07:00
|
|
|
// This object spans a depth interval that's disjoint with
|
|
|
|
// the current interval, so create a new one for it, and
|
|
|
|
// another interval to fill the gap between the last
|
|
|
|
// interval.
|
2006-09-07 02:08:32 -06:00
|
|
|
DepthBufferPartition partition;
|
2006-11-21 06:02:16 -07:00
|
|
|
partition.index = nIntervals;
|
2006-09-07 02:08:32 -06:00
|
|
|
partition.nearZ = renderList[i].farZ;
|
|
|
|
partition.farZ = prevNear;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
// Omit null intervals
|
2006-09-07 02:08:32 -06:00
|
|
|
// TODO: Is this necessary? Shouldn't the >= test prevent this?
|
|
|
|
if (partition.nearZ != partition.farZ)
|
2004-06-26 02:22:20 -06:00
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
depthPartitions.push_back(partition);
|
2006-11-21 06:02:16 -07:00
|
|
|
nIntervals++;
|
2004-06-26 02:22:20 -06:00
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
partition.index = nIntervals;
|
2006-09-07 02:08:32 -06:00
|
|
|
partition.nearZ = renderList[i].nearZ;
|
|
|
|
partition.farZ = renderList[i].farZ;
|
|
|
|
depthPartitions.push_back(partition);
|
2006-11-21 06:02:16 -07:00
|
|
|
nIntervals++;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
prevNear = partition.nearZ;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-11-21 06:02:16 -07:00
|
|
|
// This object overlaps the current span; expand the
|
|
|
|
// interval so that it completely contains the object.
|
|
|
|
DepthBufferPartition& partition = depthPartitions[nIntervals - 1];
|
2006-09-12 06:56:47 -06:00
|
|
|
partition.nearZ = max(partition.nearZ, renderList[i].nearZ);
|
2006-09-07 02:08:32 -06:00
|
|
|
partition.farZ = min(partition.farZ, renderList[i].farZ);
|
|
|
|
prevNear = partition.nearZ;
|
2004-06-26 02:22:20 -06:00
|
|
|
}
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-12-12 03:24:48 -07:00
|
|
|
// Scan the list of orbit paths and find the closest one. We'll need
|
|
|
|
// adjust the nearest interval to accommodate it.
|
|
|
|
float orbitPathNear = prevNear;
|
|
|
|
for (i = 0; i < (int) orbitPathList.size(); i++)
|
|
|
|
{
|
|
|
|
const OrbitPathListEntry& o = orbitPathList[i];
|
|
|
|
float minNearDistance = min(-o.radius * 0.0001f, o.centerZ + o.radius);
|
|
|
|
if (minNearDistance > orbitPathNear)
|
|
|
|
orbitPathNear = minNearDistance;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the nearest orbit path distance wasn't set, nothing should appear
|
|
|
|
// in the frontmost depth buffer interval (so we can set the near plane
|
|
|
|
// of the front interval to whatever we want as long as it's less than
|
|
|
|
// the far plane distance.
|
|
|
|
if (orbitPathNear == prevNear)
|
|
|
|
orbitPathNear = 0.0f;
|
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
// Add one last interval for the span from 0 to the front of the
|
|
|
|
// nearest object
|
2006-09-07 02:08:32 -06:00
|
|
|
{
|
2006-11-21 06:02:16 -07:00
|
|
|
// TODO: closest object may not be at entry 0, since objects are
|
|
|
|
// sorted by far distance.
|
2006-12-12 03:24:48 -07:00
|
|
|
float closest = orbitPathNear;
|
2006-09-07 02:08:32 -06:00
|
|
|
if (nEntries > 0)
|
|
|
|
closest = max(closest, renderList[0].nearZ);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
DepthBufferPartition partition;
|
2006-11-21 06:02:16 -07:00
|
|
|
partition.index = nIntervals;
|
2006-09-07 02:08:32 -06:00
|
|
|
partition.nearZ = closest;
|
|
|
|
partition.farZ = prevNear;
|
|
|
|
depthPartitions.push_back(partition);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
nIntervals++;
|
2006-09-07 02:08:32 -06:00
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-12-12 03:24:48 -07:00
|
|
|
// If orbits are enabled, adjust the farthest partition so that it
|
|
|
|
// can contain the orbit.
|
2006-09-07 02:08:32 -06:00
|
|
|
if (!orbitPathList.empty())
|
|
|
|
{
|
|
|
|
depthPartitions[0].farZ = min(depthPartitions[0].farZ,
|
2006-09-12 06:56:47 -06:00
|
|
|
orbitPathList[orbitPathList.size() - 1].centerZ -
|
2006-09-07 02:08:32 -06:00
|
|
|
orbitPathList[orbitPathList.size() - 1].radius);
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
// We want to avoid overpartitioning the depth buffer. In this stage, we coalesce
|
|
|
|
// partitions that have small spans in the depth buffer.
|
|
|
|
// TODO: Implement this step!
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2004-05-18 11:31:26 -06:00
|
|
|
vector<Label>::iterator label = depthSortedLabels.begin();
|
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
// Render everything that wasn't culled.
|
2006-11-21 06:02:16 -07:00
|
|
|
float intervalSize = 1.0f / (float) max(1, nIntervals);
|
2006-09-12 06:56:47 -06:00
|
|
|
i = nEntries - 1;
|
2006-11-21 06:02:16 -07:00
|
|
|
for (int interval = 0; interval < nIntervals; interval++)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2006-11-21 06:02:16 -07:00
|
|
|
float nearPlaneDistance = -depthPartitions[interval].nearZ;
|
|
|
|
float farPlaneDistance = -depthPartitions[interval].farZ;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
// Set the depth range for this interval--each interval is allocated an
|
2006-09-07 02:08:32 -06:00
|
|
|
// equal section of the depth buffer.
|
2006-11-21 06:02:16 -07:00
|
|
|
glDepthRange(1.0f - (float) (interval + 1) * intervalSize,
|
|
|
|
1.0f - (float) interval * intervalSize);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
// Set up a perspective projection using the current interval's near and
|
2006-09-07 23:59:38 -06:00
|
|
|
// far clip planes.
|
2006-09-07 02:08:32 -06:00
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glLoadIdentity();
|
|
|
|
gluPerspective(fov,
|
|
|
|
(float) windowWidth / (float) windowHeight,
|
|
|
|
nearPlaneDistance,
|
|
|
|
farPlaneDistance);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
2004-05-18 11:31:26 -06:00
|
|
|
|
2006-09-12 06:56:47 -06:00
|
|
|
#if DEBUG_COALESCE
|
2006-11-21 06:02:16 -07:00
|
|
|
clog << "interval: " << interval <<
|
|
|
|
", near: " << -depthPartitions[interval].nearZ <<
|
|
|
|
", far: " << -depthPartitions[interval].farZ <<
|
2006-09-07 02:08:32 -06:00
|
|
|
"\n";
|
2006-09-12 06:56:47 -06:00
|
|
|
#endif
|
2006-11-21 06:02:16 -07:00
|
|
|
int firstInInterval = i;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-10 18:37:20 -06:00
|
|
|
// Render just the opaque objects in the first pass
|
2006-11-21 06:02:16 -07:00
|
|
|
while (i >= 0 && renderList[i].farZ < depthPartitions[interval].nearZ)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2006-11-21 06:02:16 -07:00
|
|
|
// This interval should completely contain the item
|
2006-09-07 02:08:32 -06:00
|
|
|
// Unless it's just a point?
|
2006-11-21 06:02:16 -07:00
|
|
|
//assert(renderList[i].nearZ <= depthPartitions[interval].near);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
|
|
|
#if DEBUG_COALESCE
|
2006-09-07 02:08:32 -06:00
|
|
|
if (renderList[i].body != NULL)
|
2002-06-16 20:41:11 -06:00
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
if (renderList[i].discSizeInPixels > 1)
|
|
|
|
{
|
|
|
|
clog << renderList[i].body->getName() << "\n";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-09-21 23:51:51 -06:00
|
|
|
clog << "point: " << renderList[i].body->getName() << "\n";
|
2006-09-07 02:08:32 -06:00
|
|
|
}
|
2002-06-16 20:41:11 -06:00
|
|
|
}
|
2006-09-10 18:37:20 -06:00
|
|
|
#endif
|
|
|
|
// Treat objects that are smaller than one pixel as transparent and render
|
|
|
|
// them in the second pass.
|
|
|
|
if (renderList[i].isOpaque && renderList[i].discSizeInPixels > 1.0f)
|
2006-12-08 12:02:20 -07:00
|
|
|
renderItem(renderList[i], observer, cameraOrientation, nearPlaneDistance, farPlaneDistance);
|
2006-09-07 02:08:32 -06:00
|
|
|
|
|
|
|
i--;
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2006-09-10 18:37:20 -06:00
|
|
|
// Render orbit paths
|
2006-09-07 02:08:32 -06:00
|
|
|
if (!orbitPathList.empty())
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
2006-12-12 03:24:48 -07:00
|
|
|
glDepthMask(GL_TRUE);
|
2006-09-07 02:08:32 -06:00
|
|
|
if ((renderFlags & ShowSmoothLines) != 0)
|
2006-11-21 06:02:16 -07:00
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
enableSmoothLines();
|
2006-11-21 06:02:16 -07:00
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
// Scan through the list of orbits and render any that overlap this interval
|
2006-09-07 02:08:32 -06:00
|
|
|
for (vector<OrbitPathListEntry>::const_iterator orbitIter = orbitPathList.begin();
|
|
|
|
orbitIter != orbitPathList.end(); orbitIter++)
|
|
|
|
{
|
|
|
|
// Test for overlap
|
|
|
|
float nearZ = -orbitIter->centerZ - orbitIter->radius;
|
|
|
|
float farZ = -orbitIter->centerZ + orbitIter->radius;
|
|
|
|
if (nearZ < farPlaneDistance && farZ > nearPlaneDistance)
|
|
|
|
{
|
|
|
|
renderOrbit(*orbitIter, now);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((renderFlags & ShowSmoothLines) != 0)
|
|
|
|
disableSmoothLines();
|
2006-12-12 03:24:48 -07:00
|
|
|
glDepthMask(GL_FALSE);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-10 18:37:20 -06:00
|
|
|
// Render transparent objects in the second pass
|
2006-11-21 06:02:16 -07:00
|
|
|
i = firstInInterval;
|
|
|
|
while (i >= 0 && renderList[i].farZ < depthPartitions[interval].nearZ)
|
2006-09-10 18:37:20 -06:00
|
|
|
{
|
|
|
|
if (!renderList[i].isOpaque || renderList[i].discSizeInPixels <= 1.0f)
|
2006-12-08 12:02:20 -07:00
|
|
|
renderItem(renderList[i], observer, cameraOrientation, nearPlaneDistance, farPlaneDistance);
|
2006-09-10 18:37:20 -06:00
|
|
|
|
|
|
|
i--;
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
// Render labels in this interval
|
|
|
|
label = renderSortedLabels(label, -depthPartitions[interval].nearZ, FontNormal);
|
2006-09-07 02:08:32 -06:00
|
|
|
glDisable(GL_DEPTH_TEST);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
// reset the depth range
|
2006-09-12 06:56:47 -06:00
|
|
|
glDepthRange(0, 1);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
// Pop camera orientation matrix
|
2001-11-27 18:50:04 -07:00
|
|
|
glPopMatrix();
|
|
|
|
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
glPolygonMode(GL_FRONT, GL_FILL);
|
|
|
|
glPolygonMode(GL_BACK, GL_FILL);
|
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
// Mark for recycling all unused orbits in the cache
|
|
|
|
if ((renderFlags & ShowOrbits) != 0 && orbitMask != 0)
|
2004-05-18 11:31:26 -06:00
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
for (vector<CachedOrbit*>::const_iterator iter = orbitCache.begin(); iter != orbitCache.end(); iter++)
|
2004-05-18 11:31:26 -06:00
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
if (!(*iter)->keep)
|
|
|
|
(*iter)->body = NULL;
|
2004-05-18 11:31:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-01-31 21:42:21 -07:00
|
|
|
if ((renderFlags & ShowMarkers) != 0)
|
|
|
|
{
|
|
|
|
renderMarkers(*universe.getMarkers(),
|
|
|
|
observer.getPosition(),
|
|
|
|
observer.getOrientation(),
|
|
|
|
now);
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
glDepthMask(GL_TRUE);
|
|
|
|
glEnable(GL_LIGHTING);
|
2002-04-25 21:49:43 -06:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
int errCode = glGetError();
|
|
|
|
if (errCode != GL_NO_ERROR)
|
|
|
|
{
|
|
|
|
cout << "glError: " << (char*) gluErrorString(errCode) << '\n';
|
|
|
|
}
|
|
|
|
#endif
|
2006-12-11 18:29:27 -07:00
|
|
|
|
|
|
|
if (videoSync && glx::glXWaitVideoSyncSGI != NULL)
|
|
|
|
{
|
|
|
|
unsigned int count;
|
|
|
|
glx::glXGetVideoSyncSGI(&count);
|
|
|
|
glx::glXWaitVideoSyncSGI(2, (count+1) & 1, &count);
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void renderRingSystem(float innerRadius,
|
|
|
|
float outerRadius,
|
|
|
|
float beginAngle,
|
|
|
|
float endAngle,
|
2004-03-15 11:51:16 -07:00
|
|
|
unsigned int nSections)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
float angle = endAngle - beginAngle;
|
|
|
|
|
|
|
|
glBegin(GL_QUAD_STRIP);
|
2004-03-15 11:51:16 -07:00
|
|
|
for (unsigned int i = 0; i <= nSections; i++)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
float t = (float) i / (float) nSections;
|
|
|
|
float theta = beginAngle + t * angle;
|
|
|
|
float s = (float) sin(theta);
|
|
|
|
float c = (float) cos(theta);
|
2002-09-22 19:59:15 -06:00
|
|
|
glTexCoord2f(0, 0.5f);
|
2001-11-27 18:50:04 -07:00
|
|
|
glVertex3f(c * innerRadius, 0, s * innerRadius);
|
2002-09-22 19:59:15 -06:00
|
|
|
glTexCoord2f(1, 0.5f);
|
2001-11-27 18:50:04 -07:00
|
|
|
glVertex3f(c * outerRadius, 0, s * outerRadius);
|
|
|
|
}
|
|
|
|
glEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If the an object occupies a pixel or less of screen space, we don't
|
|
|
|
// render its mesh at all and just display a starlike point instead.
|
|
|
|
// Switching between the particle and mesh renderings of an object is
|
|
|
|
// jarring, however . . . so we'll blend in the particle view of the
|
2003-07-08 09:31:34 -06:00
|
|
|
// object to smooth things out, making it dimmer as the disc size exceeds the
|
|
|
|
// max disc size.
|
2001-11-27 18:50:04 -07:00
|
|
|
void Renderer::renderBodyAsParticle(Point3f position,
|
|
|
|
float appMag,
|
2002-01-23 18:21:53 -07:00
|
|
|
float _faintestMag,
|
2001-11-27 18:50:04 -07:00
|
|
|
float discSizeInPixels,
|
|
|
|
Color color,
|
|
|
|
const Quatf& orientation,
|
|
|
|
float renderZ,
|
2006-12-20 11:56:43 -07:00
|
|
|
bool useHalos)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2003-07-08 09:31:34 -06:00
|
|
|
float maxDiscSize = (starStyle == ScaledDiscStars) ? MaxScaledDiscStarSize : 1.0f;
|
|
|
|
float maxBlendDiscSize = maxDiscSize + 3.0f;
|
|
|
|
float discSize = 1.0f;
|
|
|
|
|
2006-12-20 11:56:43 -07:00
|
|
|
if (discSizeInPixels < maxBlendDiscSize || useHalos)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
float a = 1;
|
2003-07-08 09:31:34 -06:00
|
|
|
float fade = 1.0f;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2003-07-08 09:31:34 -06:00
|
|
|
if (discSizeInPixels > maxDiscSize)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2003-07-08 09:31:34 -06:00
|
|
|
fade = (maxBlendDiscSize - discSizeInPixels) /
|
|
|
|
(maxBlendDiscSize - maxDiscSize - 1.0f);
|
|
|
|
if (fade > 1)
|
|
|
|
fade = 1;
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2003-07-08 09:31:34 -06:00
|
|
|
a = (_faintestMag - appMag) * brightnessScale + brightnessBias;
|
|
|
|
if (starStyle == ScaledDiscStars && a > 1.0f)
|
|
|
|
discSize = min(discSize * (2.0f * a - 1.0f), maxDiscSize);
|
|
|
|
a = clamp(a) * fade;
|
|
|
|
|
2006-09-12 06:56:47 -06:00
|
|
|
// We scale up the particle by a factor of 1.6 (at fov = 45deg)
|
2006-09-27 11:23:54 -06:00
|
|
|
// so that it's more visible--the texture we use has fuzzy edges,
|
|
|
|
// and if we render it in just one pixel, it's likely to disappear.
|
|
|
|
Mat3f m = orientation.toMatrix3();
|
2006-09-26 11:30:49 -06:00
|
|
|
Point3f center = position;
|
2006-09-27 11:23:54 -06:00
|
|
|
float centerZ = (center * m.transpose()).z;
|
|
|
|
float size = discSize * pixelSize * 1.6f * centerZ / corrFac;
|
2006-11-27 12:11:55 -07:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
Vec3f v0 = Vec3f(-1, -1, 0) * m;
|
|
|
|
Vec3f v1 = Vec3f( 1, -1, 0) * m;
|
|
|
|
Vec3f v2 = Vec3f( 1, 1, 0) * m;
|
|
|
|
Vec3f v3 = Vec3f(-1, 1, 0) * m;
|
|
|
|
|
2006-09-26 11:30:49 -06:00
|
|
|
glEnable(GL_DEPTH_TEST);
|
2006-11-27 12:11:55 -07:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
starTex->bind();
|
|
|
|
glColor(color, a);
|
|
|
|
glBegin(GL_QUADS);
|
2006-03-08 11:29:10 -07:00
|
|
|
glTexCoord2f(0, 1);
|
2001-11-27 18:50:04 -07:00
|
|
|
glVertex(center + (v0 * size));
|
|
|
|
glTexCoord2f(1, 1);
|
2006-03-08 11:29:10 -07:00
|
|
|
glVertex(center + (v1 * size));
|
|
|
|
glTexCoord2f(1, 0);
|
2001-11-27 18:50:04 -07:00
|
|
|
glVertex(center + (v2 * size));
|
2006-03-08 11:29:10 -07:00
|
|
|
glTexCoord2f(0, 0);
|
2001-11-27 18:50:04 -07:00
|
|
|
glVertex(center + (v3 * size));
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
// If the object is brighter than magnitude 1, add a halo around it to
|
|
|
|
// make it appear more brilliant. This is a hack to compensate for the
|
|
|
|
// limited dynamic range of monitors.
|
2006-12-20 11:56:43 -07:00
|
|
|
if (useHalos && appMag < saturationMag)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2006-12-20 11:56:43 -07:00
|
|
|
a = GlareOpacity * clamp((appMag - saturationMag) * -0.8f);
|
|
|
|
float s = center.distanceFromOrigin() * 0.001f * (3 - (appMag - saturationMag)) * 2;
|
2001-11-27 18:50:04 -07:00
|
|
|
if (s > size * 3)
|
2002-06-22 09:15:44 -06:00
|
|
|
size = s * 2.0f/(1.0f +FOV/fov);
|
2001-11-27 18:50:04 -07:00
|
|
|
else
|
|
|
|
size = size * 3;
|
|
|
|
float realSize = discSizeInPixels * pixelSize * renderZ;
|
|
|
|
if (size < realSize * 10)
|
|
|
|
size = realSize * 10;
|
2006-12-20 11:56:43 -07:00
|
|
|
gaussianGlareTex->bind();
|
2001-11-27 18:50:04 -07:00
|
|
|
glColor(color, a);
|
|
|
|
glBegin(GL_QUADS);
|
2006-03-08 11:29:10 -07:00
|
|
|
glTexCoord2f(0, 1);
|
2001-11-27 18:50:04 -07:00
|
|
|
glVertex(center + (v0 * size));
|
|
|
|
glTexCoord2f(1, 1);
|
2006-03-08 11:29:10 -07:00
|
|
|
glVertex(center + (v1 * size));
|
|
|
|
glTexCoord2f(1, 0);
|
2001-11-27 18:50:04 -07:00
|
|
|
glVertex(center + (v2 * size));
|
2006-03-08 11:29:10 -07:00
|
|
|
glTexCoord2f(0, 0);
|
2001-11-27 18:50:04 -07:00
|
|
|
glVertex(center + (v3 * size));
|
|
|
|
glEnd();
|
|
|
|
}
|
2006-11-27 12:11:55 -07:00
|
|
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
// If the an object occupies a pixel or less of screen space, we don't
|
|
|
|
// render its mesh at all and just display a starlike point instead.
|
|
|
|
// Switching between the particle and mesh renderings of an object is
|
|
|
|
// jarring, however . . . so we'll blend in the particle view of the
|
|
|
|
// object to smooth things out, making it dimmer as the disc size exceeds the
|
|
|
|
// max disc size.
|
|
|
|
void Renderer::renderObjectAsPoint(Point3f position,
|
2006-12-20 11:56:43 -07:00
|
|
|
float radius,
|
2006-03-08 11:29:10 -07:00
|
|
|
float appMag,
|
|
|
|
float _faintestMag,
|
|
|
|
float discSizeInPixels,
|
|
|
|
Color color,
|
2006-11-05 01:13:34 -07:00
|
|
|
const Quatf& cameraOrientation,
|
2006-12-20 11:56:43 -07:00
|
|
|
bool useHalos,
|
|
|
|
bool emissive)
|
2006-03-08 11:29:10 -07:00
|
|
|
{
|
|
|
|
float maxDiscSize = (starStyle == ScaledDiscStars) ? MaxScaledDiscStarSize : 1.0f;
|
|
|
|
float maxBlendDiscSize = maxDiscSize + 3.0f;
|
2006-09-07 23:59:38 -06:00
|
|
|
bool useScaledDiscs = starStyle == ScaledDiscStars;
|
2006-03-08 11:29:10 -07:00
|
|
|
|
2006-12-20 11:56:43 -07:00
|
|
|
if (discSizeInPixels < maxBlendDiscSize || useHalos)
|
2006-03-08 11:29:10 -07:00
|
|
|
{
|
|
|
|
float alpha = 1.0f;
|
|
|
|
float fade = 1.0f;
|
2006-12-28 12:29:42 -07:00
|
|
|
float size = BaseStarDiscSize;
|
|
|
|
float satPoint = _faintestMag - (1.0f - brightnessBias) / brightnessScale;
|
2006-03-08 11:29:10 -07:00
|
|
|
|
|
|
|
if (discSizeInPixels > maxDiscSize)
|
|
|
|
{
|
|
|
|
fade = (maxBlendDiscSize - discSizeInPixels) /
|
2006-12-05 06:02:16 -07:00
|
|
|
(maxBlendDiscSize - maxDiscSize);
|
2006-03-08 11:29:10 -07:00
|
|
|
if (fade > 1)
|
|
|
|
fade = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
alpha = (_faintestMag - appMag) * brightnessScale * 2.0f + brightnessBias;
|
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
float pointSize = size;
|
|
|
|
float glareSize = 0.0f;
|
|
|
|
float glareAlpha = 0.0f;
|
2006-10-31 02:41:57 -07:00
|
|
|
if (useScaledDiscs)
|
|
|
|
{
|
|
|
|
if (alpha < 0.0f)
|
|
|
|
{
|
|
|
|
alpha = 0.0f;
|
|
|
|
}
|
|
|
|
else if (alpha > 1.0f)
|
|
|
|
{
|
2006-12-20 11:56:43 -07:00
|
|
|
float discScale = min(MaxScaledDiscStarSize, (float) pow(2.0f, 0.3f * (satPoint - appMag)));
|
2006-10-31 02:41:57 -07:00
|
|
|
pointSize *= discScale;
|
2006-11-27 12:11:55 -07:00
|
|
|
|
2006-10-31 02:41:57 -07:00
|
|
|
glareAlpha = min(0.5f, discScale / 4.0f);
|
|
|
|
glareSize = pointSize * 3.0f;
|
2006-11-27 12:11:55 -07:00
|
|
|
|
2006-10-31 02:41:57 -07:00
|
|
|
alpha = 1.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (alpha < 0.0f)
|
|
|
|
{
|
|
|
|
alpha = 0.0f;
|
|
|
|
}
|
|
|
|
else if (alpha > 1.0f)
|
|
|
|
{
|
|
|
|
float discScale = min(100.0f, satPoint - appMag + 2.0f);
|
2006-12-20 11:56:43 -07:00
|
|
|
glareAlpha = min(GlareOpacity, (discScale - 2.0f) / 4.0f);
|
2006-11-05 01:13:34 -07:00
|
|
|
glareSize = pointSize * discScale * 2.0f ;
|
2006-12-20 11:56:43 -07:00
|
|
|
if (emissive)
|
|
|
|
glareSize = max(glareSize, pointSize * discSizeInPixels * 3.0f);
|
2006-10-31 02:41:57 -07:00
|
|
|
}
|
|
|
|
}
|
2006-03-08 11:29:10 -07:00
|
|
|
|
2006-10-31 02:41:57 -07:00
|
|
|
alpha *= fade;
|
2006-12-20 11:56:43 -07:00
|
|
|
if (!emissive)
|
|
|
|
{
|
|
|
|
glareSize = max(glareSize, pointSize * discSizeInPixels * 3.0f);
|
|
|
|
glareAlpha *= fade;
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-12-20 11:56:43 -07:00
|
|
|
Mat3f m = cameraOrientation.toMatrix3();
|
2006-09-07 02:08:32 -06:00
|
|
|
Point3f center = position;
|
2006-03-08 11:29:10 -07:00
|
|
|
|
2006-12-20 11:56:43 -07:00
|
|
|
// Offset the glare sprite so that it lies in front of the object
|
|
|
|
Vec3f direction(center.x, center.y, center.z);
|
|
|
|
direction.normalize();
|
|
|
|
|
|
|
|
// Position the sprite on the the line between the viewer and the
|
|
|
|
// object, and on a plane normal to the view direction.
|
|
|
|
center = center + direction * (radius / ((Vec3f(0, 0, 1.0f) * m) * direction));
|
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
glEnable(GL_DEPTH_TEST);
|
2006-11-05 01:13:34 -07:00
|
|
|
#if !defined(NO_MAX_POINT_SIZE)
|
|
|
|
// TODO: OpenGL appears to limit the max point size unless we
|
|
|
|
// actually set up a shader that writes the pointsize values. To get
|
|
|
|
// around this, we'll use billboards.
|
|
|
|
Vec3f v0 = Vec3f(-1, -1, 0) * m;
|
|
|
|
Vec3f v1 = Vec3f( 1, -1, 0) * m;
|
|
|
|
Vec3f v2 = Vec3f( 1, 1, 0) * m;
|
|
|
|
Vec3f v3 = Vec3f(-1, 1, 0) * m;
|
|
|
|
float distanceAdjust = pixelSize * center.distanceFromOrigin() * 0.5f;
|
|
|
|
|
2006-12-28 12:29:42 -07:00
|
|
|
if (starStyle == PointStars)
|
|
|
|
{
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
glBegin(GL_POINTS);
|
|
|
|
glColor(color, alpha);
|
|
|
|
glVertex(center);
|
|
|
|
glEnd();
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gaussianDiscTex->bind();
|
2006-11-05 01:13:34 -07:00
|
|
|
|
2006-12-28 12:29:42 -07:00
|
|
|
pointSize *= distanceAdjust;
|
|
|
|
glBegin(GL_QUADS);
|
|
|
|
glColor(color, alpha);
|
|
|
|
glTexCoord2f(0, 1);
|
|
|
|
glVertex(center + (v0 * pointSize));
|
|
|
|
glTexCoord2f(1, 1);
|
|
|
|
glVertex(center + (v1 * pointSize));
|
|
|
|
glTexCoord2f(1, 0);
|
|
|
|
glVertex(center + (v2 * pointSize));
|
|
|
|
glTexCoord2f(0, 0);
|
|
|
|
glVertex(center + (v3 * pointSize));
|
|
|
|
glEnd();
|
|
|
|
}
|
2006-11-05 01:13:34 -07:00
|
|
|
|
|
|
|
// If the object is brighter than magnitude 1, add a halo around it to
|
|
|
|
// make it appear more brilliant. This is a hack to compensate for the
|
|
|
|
// limited dynamic range of monitors.
|
|
|
|
//
|
|
|
|
// TODO: Stars look fine but planets look unrealistically bright
|
2006-12-20 11:56:43 -07:00
|
|
|
// with halos.
|
2006-12-28 12:29:42 -07:00
|
|
|
if (useHalos && glareAlpha > 0.0f)
|
2006-11-05 01:13:34 -07:00
|
|
|
{
|
|
|
|
gaussianGlareTex->bind();
|
|
|
|
|
|
|
|
glareSize *= distanceAdjust;
|
|
|
|
glBegin(GL_QUADS);
|
|
|
|
glColor(color, glareAlpha);
|
|
|
|
glTexCoord2f(0, 1);
|
|
|
|
glVertex(center + (v0 * glareSize));
|
|
|
|
glTexCoord2f(1, 1);
|
|
|
|
glVertex(center + (v1 * glareSize));
|
|
|
|
glTexCoord2f(1, 0);
|
|
|
|
glVertex(center + (v2 * glareSize));
|
|
|
|
glTexCoord2f(0, 0);
|
|
|
|
glVertex(center + (v3 * glareSize));
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
// Disabled because of point size limits
|
|
|
|
glEnable(GL_POINT_SPRITE_ARB);
|
|
|
|
glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
gaussianDiscTex->bind();
|
|
|
|
glColor(color, alpha);
|
2006-11-05 01:13:34 -07:00
|
|
|
glPointSize(pointSize);
|
2006-03-08 11:29:10 -07:00
|
|
|
glBegin(GL_POINTS);
|
|
|
|
glVertex(center);
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
// If the object is brighter than magnitude 1, add a halo around it to
|
|
|
|
// make it appear more brilliant. This is a hack to compensate for the
|
|
|
|
// limited dynamic range of monitors.
|
|
|
|
//
|
2006-11-05 01:13:34 -07:00
|
|
|
// TODO: Stars look fine but planets look unrealistically bright
|
2006-12-20 11:56:43 -07:00
|
|
|
// with halos.
|
2006-12-28 12:29:42 -07:00
|
|
|
if (useHalos && glareAlpha > 0.0f)
|
2006-03-08 11:29:10 -07:00
|
|
|
{
|
|
|
|
gaussianGlareTex->bind();
|
2006-11-05 01:13:34 -07:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
glColor(color, glareAlpha);
|
2006-11-05 01:13:34 -07:00
|
|
|
glPointSize(glareSize);
|
2006-03-08 11:29:10 -07:00
|
|
|
glBegin(GL_POINTS);
|
|
|
|
glVertex(center);
|
|
|
|
glEnd();
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-05 01:13:34 -07:00
|
|
|
glDisable(GL_POINT_SPRITE_ARB);
|
2006-09-07 02:08:32 -06:00
|
|
|
glDisable(GL_DEPTH_TEST);
|
2006-11-05 01:13:34 -07:00
|
|
|
#endif // NO_MAX_POINT_SIZE
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-03-28 11:39:23 -07:00
|
|
|
static void renderBumpMappedMesh(const GLContext& context,
|
|
|
|
Texture& baseTexture,
|
2002-09-02 15:13:10 -06:00
|
|
|
Texture& bumpTexture,
|
2001-11-27 18:50:04 -07:00
|
|
|
Vec3f lightDirection,
|
|
|
|
Quatf orientation,
|
|
|
|
Color ambientColor,
|
|
|
|
const Frustum& frustum,
|
|
|
|
float lod)
|
|
|
|
{
|
|
|
|
// We're doing our own per-pixel lighting, so disable GL's lighting
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
|
2002-09-02 15:13:10 -06:00
|
|
|
// Render the base texture on the first pass . . . The color
|
|
|
|
// should have already been set up by the caller.
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, lod,
|
|
|
|
&baseTexture);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// The 'default' light vector for the bump map is (0, 0, 1). Determine
|
|
|
|
// a rotation transformation that will move the sun direction to
|
|
|
|
// this vector.
|
|
|
|
Quatf lightOrientation;
|
|
|
|
{
|
|
|
|
Vec3f zeroLightDirection(0, 0, 1);
|
|
|
|
Vec3f axis = lightDirection ^ zeroLightDirection;
|
|
|
|
float cosAngle = zeroLightDirection * lightDirection;
|
|
|
|
float angle = 0.0f;
|
|
|
|
float epsilon = 1e-5f;
|
|
|
|
|
|
|
|
if (cosAngle + 1 < epsilon)
|
|
|
|
{
|
|
|
|
axis = Vec3f(0, 1, 0);
|
|
|
|
angle = (float) PI;
|
|
|
|
}
|
|
|
|
else if (cosAngle - 1 > -epsilon)
|
|
|
|
{
|
|
|
|
axis = Vec3f(0, 1, 0);
|
|
|
|
angle = 0.0f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
axis.normalize();
|
|
|
|
angle = (float) acos(cosAngle);
|
|
|
|
}
|
|
|
|
lightOrientation.setAxisAngle(axis, angle);
|
|
|
|
}
|
|
|
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_DST_COLOR, GL_ZERO);
|
|
|
|
|
|
|
|
// Set up the bump map with one directional light source
|
|
|
|
SetupCombinersBumpMap(bumpTexture, *normalizationTex, ambientColor);
|
|
|
|
|
|
|
|
// The second set texture coordinates will contain the light
|
|
|
|
// direction in tangent space. We'll generate the texture coordinates
|
|
|
|
// from the surface normals using GL_NORMAL_MAP_EXT and then
|
|
|
|
// use the texture matrix to rotate them into tangent space.
|
|
|
|
// This method of generating tangent space light direction vectors
|
|
|
|
// isn't as general as transforming the light direction by an
|
|
|
|
// orthonormal basis for each mesh vertex, but it works well enough
|
|
|
|
// for spheres illuminated by directional light sources.
|
2002-11-23 11:25:28 -07:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE1_ARB);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// Set up GL_NORMAL_MAP_EXT texture coordinate generation. This
|
|
|
|
// mode is part of the cube map extension.
|
|
|
|
glEnable(GL_TEXTURE_GEN_R);
|
|
|
|
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT);
|
|
|
|
glEnable(GL_TEXTURE_GEN_S);
|
|
|
|
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT);
|
|
|
|
glEnable(GL_TEXTURE_GEN_T);
|
|
|
|
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT);
|
|
|
|
|
|
|
|
// Set up the texture transformation--the light direction and the
|
|
|
|
// viewer orientation both need to be considered.
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
2003-04-25 03:06:02 -06:00
|
|
|
glScalef(-1.0f, 1.0f, 1.0f);
|
2001-11-27 18:50:04 -07:00
|
|
|
glRotate(lightOrientation * ~orientation);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
2002-11-23 11:25:28 -07:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, lod,
|
|
|
|
&bumpTexture);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// Reset the second texture unit
|
2002-11-23 11:25:28 -07:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE1_ARB);
|
2001-11-27 18:50:04 -07:00
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
glLoadIdentity();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glDisable(GL_TEXTURE_GEN_R);
|
|
|
|
glDisable(GL_TEXTURE_GEN_S);
|
|
|
|
glDisable(GL_TEXTURE_GEN_T);
|
|
|
|
|
|
|
|
DisableCombiners();
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-03-28 11:39:23 -07:00
|
|
|
static void renderSmoothMesh(const GLContext& context,
|
|
|
|
Texture& baseTexture,
|
2001-11-27 18:50:04 -07:00
|
|
|
Vec3f lightDirection,
|
|
|
|
Quatf orientation,
|
|
|
|
Color ambientColor,
|
|
|
|
float lod,
|
|
|
|
const Frustum& frustum,
|
|
|
|
bool invert = false)
|
|
|
|
{
|
2002-08-20 03:09:35 -06:00
|
|
|
Texture* textures[4];
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
// We're doing our own per-pixel lighting, so disable GL's lighting
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
|
|
|
|
// The 'default' light vector for the bump map is (0, 0, 1). Determine
|
|
|
|
// a rotation transformation that will move the sun direction to
|
|
|
|
// this vector.
|
|
|
|
Quatf lightOrientation;
|
|
|
|
{
|
|
|
|
Vec3f zeroLightDirection(0, 0, 1);
|
|
|
|
Vec3f axis = lightDirection ^ zeroLightDirection;
|
|
|
|
float cosAngle = zeroLightDirection * lightDirection;
|
|
|
|
float angle = 0.0f;
|
|
|
|
float epsilon = 1e-5f;
|
|
|
|
|
|
|
|
if (cosAngle + 1 < epsilon)
|
|
|
|
{
|
|
|
|
axis = Vec3f(0, 1, 0);
|
|
|
|
angle = (float) PI;
|
|
|
|
}
|
|
|
|
else if (cosAngle - 1 > -epsilon)
|
|
|
|
{
|
|
|
|
axis = Vec3f(0, 1, 0);
|
|
|
|
angle = 0.0f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
axis.normalize();
|
|
|
|
angle = (float) acos(cosAngle);
|
|
|
|
}
|
|
|
|
lightOrientation.setAxisAngle(axis, angle);
|
|
|
|
}
|
|
|
|
|
|
|
|
SetupCombinersSmooth(baseTexture, *normalizationTex, ambientColor, invert);
|
|
|
|
|
|
|
|
// The second set texture coordinates will contain the light
|
|
|
|
// direction in tangent space. We'll generate the texture coordinates
|
|
|
|
// from the surface normals using GL_NORMAL_MAP_EXT and then
|
|
|
|
// use the texture matrix to rotate them into tangent space.
|
|
|
|
// This method of generating tangent space light direction vectors
|
|
|
|
// isn't as general as transforming the light direction by an
|
|
|
|
// orthonormal basis for each mesh vertex, but it works well enough
|
|
|
|
// for spheres illuminated by directional light sources.
|
2002-11-23 11:25:28 -07:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE1_ARB);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// Set up GL_NORMAL_MAP_EXT texture coordinate generation. This
|
|
|
|
// mode is part of the cube map extension.
|
|
|
|
glEnable(GL_TEXTURE_GEN_R);
|
|
|
|
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT);
|
|
|
|
glEnable(GL_TEXTURE_GEN_S);
|
|
|
|
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT);
|
|
|
|
glEnable(GL_TEXTURE_GEN_T);
|
|
|
|
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT);
|
|
|
|
|
|
|
|
// Set up the texture transformation--the light direction and the
|
|
|
|
// viewer orientation both need to be considered.
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
glRotate(lightOrientation * ~orientation);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
2002-11-23 11:25:28 -07:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2002-08-20 03:09:35 -06:00
|
|
|
textures[0] = &baseTexture;
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, lod,
|
|
|
|
textures, 1);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// Reset the second texture unit
|
2002-11-23 11:25:28 -07:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE1_ARB);
|
2001-11-27 18:50:04 -07:00
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
glLoadIdentity();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glDisable(GL_TEXTURE_GEN_R);
|
|
|
|
glDisable(GL_TEXTURE_GEN_S);
|
|
|
|
glDisable(GL_TEXTURE_GEN_T);
|
|
|
|
|
|
|
|
DisableCombiners();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-08 10:35:45 -06:00
|
|
|
// Used to sort light sources in order of decreasing irradiance
|
|
|
|
struct LightIrradiancePredicate
|
2004-10-04 00:35:33 -06:00
|
|
|
{
|
|
|
|
int unused;
|
|
|
|
|
2004-10-08 10:35:45 -06:00
|
|
|
LightIrradiancePredicate() {};
|
2004-10-04 00:35:33 -06:00
|
|
|
|
|
|
|
bool operator()(const DirectionalLight& l0,
|
|
|
|
const DirectionalLight& l1) const
|
|
|
|
{
|
2004-10-08 10:35:45 -06:00
|
|
|
return (l0.irradiance > l1.irradiance);
|
2004-10-04 00:35:33 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
void renderAtmosphere(const Atmosphere& atmosphere,
|
|
|
|
Point3f center,
|
|
|
|
float radius,
|
|
|
|
const Vec3f& sunDirection,
|
|
|
|
Color ambientColor,
|
2002-02-08 14:55:26 -07:00
|
|
|
float fade,
|
|
|
|
bool lit)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
if (atmosphere.height == 0.0f)
|
|
|
|
return;
|
|
|
|
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
|
|
|
|
Vec3f eyeVec = center - Point3f(0.0f, 0.0f, 0.0f);
|
|
|
|
double centerDist = eyeVec.length();
|
|
|
|
// double surfaceDist = (double) centerDist - (double) radius;
|
|
|
|
|
|
|
|
Vec3f normal = eyeVec;
|
|
|
|
normal = normal / (float) centerDist;
|
|
|
|
|
|
|
|
float tangentLength = (float) sqrt(square(centerDist) - square(radius));
|
|
|
|
float atmRadius = tangentLength * radius / (float) centerDist;
|
|
|
|
float atmOffsetFromCenter = square(radius) / (float) centerDist;
|
|
|
|
Point3f atmCenter = center - atmOffsetFromCenter * normal;
|
|
|
|
|
|
|
|
Vec3f uAxis, vAxis;
|
|
|
|
if (abs(normal.x) < abs(normal.y) && abs(normal.x) < abs(normal.z))
|
|
|
|
{
|
|
|
|
uAxis = Vec3f(1, 0, 0) ^ normal;
|
|
|
|
uAxis.normalize();
|
|
|
|
}
|
|
|
|
else if (abs(eyeVec.y) < abs(normal.z))
|
|
|
|
{
|
|
|
|
uAxis = Vec3f(0, 1, 0) ^ normal;
|
|
|
|
uAxis.normalize();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
uAxis = Vec3f(0, 0, 1) ^ normal;
|
|
|
|
uAxis.normalize();
|
|
|
|
}
|
|
|
|
vAxis = uAxis ^ normal;
|
|
|
|
|
|
|
|
float height = atmosphere.height / radius;
|
|
|
|
|
|
|
|
glBegin(GL_QUAD_STRIP);
|
|
|
|
int divisions = 180;
|
|
|
|
for (int i = 0; i <= divisions; i++)
|
|
|
|
{
|
|
|
|
float theta = (float) i / (float) divisions * 2 * (float) PI;
|
|
|
|
Vec3f v = (float) cos(theta) * uAxis + (float) sin(theta) * vAxis;
|
|
|
|
Point3f base = atmCenter + v * atmRadius;
|
|
|
|
Vec3f toCenter = base - center;
|
|
|
|
|
|
|
|
float cosSunAngle = (toCenter * sunDirection) / radius;
|
|
|
|
float brightness = 1.0f;
|
|
|
|
float botColor[3];
|
|
|
|
float topColor[3];
|
|
|
|
botColor[0] = atmosphere.lowerColor.red();
|
|
|
|
botColor[1] = atmosphere.lowerColor.green();
|
|
|
|
botColor[2] = atmosphere.lowerColor.blue();
|
|
|
|
topColor[0] = atmosphere.upperColor.red();
|
|
|
|
topColor[1] = atmosphere.upperColor.green();
|
|
|
|
topColor[2] = atmosphere.upperColor.blue();
|
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
if (cosSunAngle < 0.2f && lit)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
if (cosSunAngle < -0.2f)
|
|
|
|
{
|
|
|
|
brightness = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float t = (0.2f + cosSunAngle) * 2.5f;
|
|
|
|
brightness = t;
|
|
|
|
botColor[0] = Mathf::lerp(t, 1.0f, botColor[0]);
|
|
|
|
botColor[1] = Mathf::lerp(t, 0.3f, botColor[1]);
|
|
|
|
botColor[2] = Mathf::lerp(t, 0.0f, botColor[2]);
|
|
|
|
topColor[0] = Mathf::lerp(t, 1.0f, topColor[0]);
|
|
|
|
topColor[1] = Mathf::lerp(t, 0.3f, topColor[1]);
|
|
|
|
topColor[2] = Mathf::lerp(t, 0.0f, topColor[2]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
glColor4f(botColor[0], botColor[1], botColor[2],
|
|
|
|
0.85f * fade * brightness + ambientColor.red());
|
|
|
|
glVertex(base - toCenter * height * 0.05f);
|
|
|
|
glColor4f(topColor[0], topColor[1], topColor[2], 0.0f);
|
|
|
|
glVertex(base + toCenter * height);
|
|
|
|
}
|
|
|
|
glEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
static Vec3f ellipsoidTangent(const Vec3f& recipSemiAxes,
|
|
|
|
const Vec3f& w,
|
|
|
|
const Vec3f& e,
|
|
|
|
const Vec3f& e_,
|
|
|
|
float ee)
|
|
|
|
{
|
|
|
|
// We want to find t such that -E(1-t) + Wt is the direction of a ray
|
|
|
|
// tangent to the ellipsoid. A tangent ray will intersect the ellipsoid
|
|
|
|
// at exactly one point. Finding the intersection between a ray and an
|
|
|
|
// ellipsoid ultimately requires using the quadratic formula, which has
|
|
|
|
// one solution when the discriminant (b^2 - 4ac) is zero. The code below
|
|
|
|
// computes the value of t that results in a discriminant of zero.
|
|
|
|
Vec3f w_(w.x * recipSemiAxes.x, w.y * recipSemiAxes.y, w.z * recipSemiAxes.z);
|
|
|
|
float ww = w_ * w_;
|
|
|
|
float ew = w_ * e_;
|
|
|
|
|
|
|
|
// Before elimination of terms:
|
|
|
|
// float a = 4 * square(ee + ew) - 4 * (ee + 2 * ew + ww) * (ee - 1.0f);
|
|
|
|
// float b = -8 * ee * (ee + ew) - 4 * (-2 * (ee + ew) * (ee - 1.0f));
|
|
|
|
// float c = 4 * ee * ee - 4 * (ee * (ee - 1.0f));
|
|
|
|
|
|
|
|
float a = 4 * square(ee + ew) - 4 * (ee + 2 * ew + ww) * (ee - 1.0f);
|
|
|
|
float b = -8 * (ee + ew);
|
|
|
|
float c = 4 * ee;
|
|
|
|
|
|
|
|
float t = 0.0f;
|
|
|
|
float discriminant = b * b - 4 * a * c;
|
|
|
|
|
|
|
|
if (discriminant < 0.0f)
|
|
|
|
t = (-b + (float) sqrt(-discriminant)) / (2 * a); // Bad!
|
|
|
|
else
|
|
|
|
t = (-b + (float) sqrt(discriminant)) / (2 * a);
|
|
|
|
|
|
|
|
// V is the direction vector. We now need the point of intersection,
|
|
|
|
// which we obtain by solving the quadratic equation for the ray-ellipse
|
|
|
|
// intersection. Since we already know that the discriminant is zero,
|
|
|
|
// the solution is just -b/2a
|
|
|
|
Vec3f v = -e * (1 - t) + w * t;
|
|
|
|
Vec3f v_(v.x * recipSemiAxes.x, v.y * recipSemiAxes.y, v.z * recipSemiAxes.z);
|
|
|
|
float a1 = v_ * v_;
|
|
|
|
float b1 = 2.0f * v_ * e_;
|
|
|
|
float t1 = -b1 / (2 * a1);
|
|
|
|
|
|
|
|
return e + v * t1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::renderEllipsoidAtmosphere(const Atmosphere& atmosphere,
|
|
|
|
Point3f center,
|
|
|
|
const Quatf& orientation,
|
|
|
|
Vec3f semiAxes,
|
|
|
|
const Vec3f& sunDirection,
|
2006-09-16 17:02:55 -06:00
|
|
|
Color /*ambientColor*/,
|
2003-05-25 20:27:27 -06:00
|
|
|
float pixSize,
|
|
|
|
bool lit)
|
2003-05-13 03:53:07 -06:00
|
|
|
{
|
|
|
|
if (atmosphere.height == 0.0f)
|
|
|
|
return;
|
|
|
|
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
|
2003-05-30 02:28:07 -06:00
|
|
|
// Gradually fade in the atmosphere if it's thickness on screen is just
|
|
|
|
// over one pixel.
|
2003-05-25 20:27:27 -06:00
|
|
|
float fade = clamp(pixSize - 2);
|
|
|
|
|
2003-05-13 03:53:07 -06:00
|
|
|
Mat3f rot = orientation.toMatrix3();
|
|
|
|
Mat3f irot = conjugate(orientation).toMatrix3();
|
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
Point3f eyePos(0.0f, 0.0f, 0.0f);
|
2003-05-25 22:11:14 -06:00
|
|
|
float radius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
|
2003-05-25 20:27:27 -06:00
|
|
|
Vec3f eyeVec = center - eyePos;
|
2003-05-14 01:48:57 -06:00
|
|
|
eyeVec = eyeVec * irot;
|
2003-05-13 03:53:07 -06:00
|
|
|
double centerDist = eyeVec.length();
|
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
float height = atmosphere.height / radius;
|
|
|
|
Vec3f recipSemiAxes(1.0f / semiAxes.x, 1.0f / semiAxes.y, 1.0f / semiAxes.z);
|
|
|
|
|
|
|
|
Vec3f recipAtmSemiAxes = recipSemiAxes / (1.0f + height);
|
|
|
|
Mat3f A = Mat3f::scaling(recipAtmSemiAxes);
|
|
|
|
Mat3f A1 = Mat3f::scaling(recipSemiAxes);
|
|
|
|
|
|
|
|
// ellipDist is not the true distance from the surface unless the
|
|
|
|
// planet is spherical. Computing the true distance requires finding
|
|
|
|
// the roots of a sixth degree polynomial, and isn't actually what we
|
|
|
|
// want anyhow since the atmosphere region is just the planet ellipsoid
|
2003-05-30 02:28:07 -06:00
|
|
|
// multiplied by a uniform scale factor. The value that we do compute
|
|
|
|
// is the distance to the surface along a line from the eye position to
|
|
|
|
// the center of the ellipsoid.
|
2003-05-25 20:27:27 -06:00
|
|
|
float ellipDist = (float) sqrt((eyeVec * A1) * (eyeVec * A1)) - 1.0f;
|
|
|
|
bool within = ellipDist < height;
|
|
|
|
|
|
|
|
// Adjust the tesselation of the sky dome/ring based on distance from the
|
|
|
|
// planet surface.
|
|
|
|
int nSlices = MaxSkySlices;
|
|
|
|
if (ellipDist < 0.25f)
|
|
|
|
{
|
|
|
|
nSlices = MinSkySlices + max(0, (int) ((ellipDist / 0.25f) * (MaxSkySlices - MinSkySlices)));
|
|
|
|
nSlices &= ~1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nRings = min(1 + (int) pixSize / 5, 6);
|
|
|
|
int nHorizonRings = nRings;
|
|
|
|
if (within)
|
|
|
|
nRings += 12;
|
|
|
|
|
|
|
|
float horizonHeight = height;
|
|
|
|
if (within)
|
|
|
|
{
|
|
|
|
if (ellipDist <= 0.0f)
|
|
|
|
horizonHeight = 0.0f;
|
|
|
|
else
|
2003-05-30 02:28:07 -06:00
|
|
|
horizonHeight *= max((float) pow(ellipDist / height, 0.33f), 0.001f);
|
2003-05-25 20:27:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
Vec3f e = -eyeVec;
|
|
|
|
Vec3f e_(e.x * recipSemiAxes.x, e.y * recipSemiAxes.y, e.z * recipSemiAxes.z);
|
|
|
|
float ee = e_ * e_;
|
|
|
|
|
2003-05-30 02:28:07 -06:00
|
|
|
// Compute the cosine of the altitude of the sun. This is used to compute
|
|
|
|
// the degree of sunset/sunrise coloration.
|
2003-05-25 20:27:27 -06:00
|
|
|
float cosSunAltitude = 0.0f;
|
|
|
|
{
|
|
|
|
// Check for a sun either directly behind or in front of the viewer
|
2004-02-28 02:30:17 -07:00
|
|
|
float cosSunAngle = (float) ((sunDirection * e) / centerDist);
|
2003-05-25 20:27:27 -06:00
|
|
|
if (cosSunAngle < -1.0f + 1.0e-6f)
|
|
|
|
{
|
|
|
|
cosSunAltitude = 0.0f;
|
|
|
|
}
|
|
|
|
else if (cosSunAngle > 1.0f - 1.0e-6f)
|
|
|
|
{
|
|
|
|
cosSunAltitude = 0.0f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Point3f tangentPoint = center +
|
|
|
|
ellipsoidTangent(recipSemiAxes,
|
|
|
|
(-sunDirection * irot) * (float) centerDist,
|
|
|
|
e, e_, ee) * rot;
|
|
|
|
Vec3f tangentDir = tangentPoint - eyePos;
|
|
|
|
tangentDir.normalize();
|
|
|
|
cosSunAltitude = sunDirection * tangentDir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-05-13 03:53:07 -06:00
|
|
|
Vec3f normal = eyeVec;
|
|
|
|
normal = normal / (float) centerDist;
|
|
|
|
|
|
|
|
Vec3f uAxis, vAxis;
|
|
|
|
if (abs(normal.x) < abs(normal.y) && abs(normal.x) < abs(normal.z))
|
|
|
|
{
|
|
|
|
uAxis = Vec3f(1, 0, 0) ^ normal;
|
|
|
|
uAxis.normalize();
|
|
|
|
}
|
|
|
|
else if (abs(eyeVec.y) < abs(normal.z))
|
|
|
|
{
|
|
|
|
uAxis = Vec3f(0, 1, 0) ^ normal;
|
|
|
|
uAxis.normalize();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
uAxis = Vec3f(0, 0, 1) ^ normal;
|
|
|
|
uAxis.normalize();
|
|
|
|
}
|
|
|
|
vAxis = uAxis ^ normal;
|
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
// Compute the contour of the ellipsoid
|
|
|
|
int i;
|
|
|
|
for (i = 0; i <= nSlices; i++)
|
2003-05-13 03:53:07 -06:00
|
|
|
{
|
|
|
|
// We want rays with an origin at the eye point and tangent to the the
|
|
|
|
// ellipsoid.
|
2003-05-25 20:27:27 -06:00
|
|
|
float theta = (float) i / (float) nSlices * 2 * (float) PI;
|
2003-05-13 03:53:07 -06:00
|
|
|
Vec3f w = (float) cos(theta) * uAxis + (float) sin(theta) * vAxis;
|
|
|
|
w = w * (float) centerDist;
|
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
Vec3f toCenter = ellipsoidTangent(recipSemiAxes, w, e, e_, ee);
|
|
|
|
skyContour[i].v = toCenter * rot;
|
|
|
|
skyContour[i].centerDist = skyContour[i].v.length();
|
|
|
|
skyContour[i].eyeDir = skyContour[i].v + (center - eyePos);
|
|
|
|
skyContour[i].eyeDist = skyContour[i].eyeDir.length();
|
|
|
|
skyContour[i].eyeDir.normalize();
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2003-05-25 22:11:14 -06:00
|
|
|
float skyCapDist = (float) sqrt(square(skyContour[i].eyeDist) +
|
|
|
|
square(horizonHeight * radius));
|
|
|
|
skyContour[i].cosSkyCapAltitude = skyContour[i].eyeDist /
|
2003-05-25 20:27:27 -06:00
|
|
|
skyCapDist;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Vec3f botColor(atmosphere.lowerColor.red(),
|
|
|
|
atmosphere.lowerColor.green(),
|
|
|
|
atmosphere.lowerColor.blue());
|
|
|
|
Vec3f topColor(atmosphere.upperColor.red(),
|
|
|
|
atmosphere.upperColor.green(),
|
|
|
|
atmosphere.upperColor.blue());
|
2003-05-25 22:43:54 -06:00
|
|
|
Vec3f sunsetColor(atmosphere.sunsetColor.red(),
|
|
|
|
atmosphere.sunsetColor.green(),
|
|
|
|
atmosphere.sunsetColor.blue());
|
2003-05-25 20:27:27 -06:00
|
|
|
if (within)
|
|
|
|
{
|
|
|
|
Vec3f skyColor(atmosphere.skyColor.red(),
|
|
|
|
atmosphere.skyColor.green(),
|
|
|
|
atmosphere.skyColor.blue());
|
|
|
|
if (ellipDist < 0.0f)
|
|
|
|
topColor = skyColor;
|
2003-05-13 03:53:07 -06:00
|
|
|
else
|
2003-05-25 20:27:27 -06:00
|
|
|
topColor = skyColor + (topColor - skyColor) * (ellipDist / height);
|
|
|
|
}
|
2003-05-25 22:43:54 -06:00
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
Vec3f zenith = (skyContour[0].v + skyContour[nSlices / 2].v);
|
|
|
|
zenith.normalize();
|
2003-05-30 02:28:07 -06:00
|
|
|
zenith *= skyContour[0].centerDist * (1.0f + horizonHeight * 2.0f);
|
2003-05-13 03:53:07 -06:00
|
|
|
|
2003-07-03 18:08:57 -06:00
|
|
|
float minOpacity = within ? (1.0f - ellipDist / height) * 0.75f : 0.0f;
|
2003-05-25 20:27:27 -06:00
|
|
|
float sunset = cosSunAltitude < 0.9f ? 0.0f : (cosSunAltitude - 0.9f) * 10.0f;
|
2003-05-13 03:53:07 -06:00
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
// Build the list of vertices
|
|
|
|
SkyVertex* vtx = skyVertices;
|
|
|
|
for (i = 0; i <= nRings; i++)
|
|
|
|
{
|
|
|
|
float h = min(1.0f, (float) i / (float) nHorizonRings);
|
|
|
|
float hh = (float) sqrt(h);
|
|
|
|
float u = i <= nHorizonRings ? 0.0f :
|
|
|
|
(float) (i - nHorizonRings) / (float) (nRings - nHorizonRings);
|
|
|
|
float r = Mathf::lerp(h, 1.0f - (horizonHeight * 0.05f), 1.0f + horizonHeight);
|
|
|
|
float atten = 1.0f - hh;
|
2003-05-14 01:48:57 -06:00
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
for (int j = 0; j < nSlices; j++)
|
2003-05-13 03:53:07 -06:00
|
|
|
{
|
2003-05-25 20:27:27 -06:00
|
|
|
Vec3f v;
|
|
|
|
if (i <= nHorizonRings)
|
|
|
|
v = skyContour[j].v * r;
|
|
|
|
else
|
|
|
|
v = (skyContour[j].v * (1.0f - u) + zenith * u) * r;
|
|
|
|
Point3f p = center + v;
|
|
|
|
|
2003-05-30 11:53:26 -06:00
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
Vec3f viewDir(p.x, p.y, p.z);
|
|
|
|
viewDir.normalize();
|
|
|
|
float cosSunAngle = viewDir * sunDirection;
|
2003-05-25 22:11:14 -06:00
|
|
|
float cosAltitude = viewDir * skyContour[j].eyeDir;
|
2003-05-30 11:53:26 -06:00
|
|
|
float brightness = 1.0f;
|
2003-05-30 02:28:07 -06:00
|
|
|
float coloration = 0.0f;
|
2003-05-30 11:53:26 -06:00
|
|
|
if (lit)
|
2003-05-13 03:53:07 -06:00
|
|
|
{
|
2003-05-30 11:53:26 -06:00
|
|
|
if (sunset > 0.0f && cosSunAngle > 0.7f && cosAltitude > 0.98f)
|
|
|
|
{
|
|
|
|
coloration = (1.0f / 0.30f) * (cosSunAngle - 0.70f);
|
|
|
|
coloration *= 50.0f * (cosAltitude - 0.98f);
|
|
|
|
coloration *= sunset;
|
2006-09-12 06:56:47 -06:00
|
|
|
}
|
|
|
|
|
2003-05-30 11:53:26 -06:00
|
|
|
cosSunAngle = (skyContour[j].v * sunDirection) / skyContour[j].centerDist;
|
|
|
|
if (cosSunAngle > -0.2f)
|
|
|
|
{
|
|
|
|
if (cosSunAngle < 0.3f)
|
|
|
|
brightness = (cosSunAngle + 0.2f) * 2.0f;
|
|
|
|
else
|
|
|
|
brightness = 1.0f;
|
|
|
|
}
|
2003-05-25 20:27:27 -06:00
|
|
|
else
|
2003-05-30 11:53:26 -06:00
|
|
|
{
|
|
|
|
brightness = 0.0f;
|
|
|
|
}
|
2003-05-13 03:53:07 -06:00
|
|
|
}
|
2003-05-25 20:27:27 -06:00
|
|
|
|
|
|
|
vtx->x = p.x;
|
|
|
|
vtx->y = p.y;
|
|
|
|
vtx->z = p.z;
|
|
|
|
|
2003-05-25 22:11:14 -06:00
|
|
|
#if 0
|
2006-09-12 06:56:47 -06:00
|
|
|
// Better way of generating sky color gradients--based on
|
2003-05-30 02:28:07 -06:00
|
|
|
// altitude angle.
|
2003-05-25 22:11:14 -06:00
|
|
|
if (!within)
|
|
|
|
{
|
|
|
|
hh = (1.0f - cosAltitude) / (1.0f - skyContour[j].cosSkyCapAltitude);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-05-30 02:28:07 -06:00
|
|
|
float top = pow((ellipDist / height), 0.125f) * skyContour[j].cosSkyCapAltitude;
|
|
|
|
if (cosAltitude < top)
|
|
|
|
hh = 1.0f;
|
|
|
|
else
|
|
|
|
hh = (1.0f - cosAltitude) / (1.0f - top);
|
2003-05-25 22:11:14 -06:00
|
|
|
}
|
2003-05-30 02:28:07 -06:00
|
|
|
hh = sqrt(hh);
|
|
|
|
//hh = (float) pow(hh, 0.25f);
|
2003-05-25 22:11:14 -06:00
|
|
|
#endif
|
|
|
|
|
|
|
|
atten = 1.0f - hh;
|
2003-05-25 20:27:27 -06:00
|
|
|
Vec3f color = (1.0f - hh) * botColor + hh * topColor;
|
2003-05-30 11:53:26 -06:00
|
|
|
brightness *= minOpacity + (1.0f - minOpacity) * fade * atten;
|
2003-05-30 02:28:07 -06:00
|
|
|
if (coloration != 0.0f)
|
|
|
|
color = (1.0f - coloration) * color + coloration * sunsetColor;
|
2003-05-30 11:53:26 -06:00
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
Color(brightness * color.x,
|
|
|
|
brightness * color.y,
|
|
|
|
brightness * color.z,
|
2003-07-03 18:08:57 -06:00
|
|
|
fade * (minOpacity + (1.0f - minOpacity)) * atten).get(vtx->color);
|
2003-05-25 20:27:27 -06:00
|
|
|
vtx++;
|
2003-05-13 03:53:07 -06:00
|
|
|
}
|
2003-05-25 20:27:27 -06:00
|
|
|
}
|
2003-05-14 01:48:57 -06:00
|
|
|
|
2003-05-25 20:27:27 -06:00
|
|
|
// Create the index list
|
|
|
|
int index = 0;
|
|
|
|
for (i = 0; i < nRings; i++)
|
|
|
|
{
|
|
|
|
int baseVertex = i * nSlices;
|
|
|
|
for (int j = 0; j < nSlices; j++)
|
|
|
|
{
|
|
|
|
skyIndices[index++] = baseVertex + j;
|
|
|
|
skyIndices[index++] = baseVertex + nSlices + j;
|
|
|
|
}
|
|
|
|
skyIndices[index++] = baseVertex;
|
|
|
|
skyIndices[index++] = baseVertex + nSlices;
|
2003-05-13 03:53:07 -06:00
|
|
|
}
|
2003-05-25 20:27:27 -06:00
|
|
|
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glVertexPointer(3, GL_FLOAT, sizeof(SkyVertex), &skyVertices[0].x);
|
|
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(SkyVertex),
|
|
|
|
static_cast<void*>(&skyVertices[0].color));
|
|
|
|
|
|
|
|
for (i = 0; i < nRings; i++)
|
|
|
|
{
|
|
|
|
glDrawElements(GL_QUAD_STRIP,
|
|
|
|
(nSlices + 1) * 2,
|
|
|
|
GL_UNSIGNED_INT,
|
|
|
|
&skyIndices[(nSlices + 1) * 2 * i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
2003-05-13 03:53:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-25 11:19:44 -06:00
|
|
|
void renderCompass(Point3f center,
|
|
|
|
const Quatf& orientation,
|
|
|
|
Vec3f semiAxes,
|
2004-08-26 23:39:52 -06:00
|
|
|
float pixelSize)
|
2004-08-25 11:19:44 -06:00
|
|
|
{
|
|
|
|
Mat3f rot = orientation.toMatrix3();
|
|
|
|
Mat3f irot = conjugate(orientation).toMatrix3();
|
|
|
|
|
|
|
|
Point3f eyePos(0.0f, 0.0f, 0.0f);
|
|
|
|
float radius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
|
|
|
|
Vec3f eyeVec = center - eyePos;
|
|
|
|
eyeVec = eyeVec * irot;
|
|
|
|
double centerDist = eyeVec.length();
|
|
|
|
|
|
|
|
float height = 1.0f / radius;
|
|
|
|
Vec3f recipSemiAxes(1.0f / semiAxes.x,
|
|
|
|
1.0f / semiAxes.y,
|
|
|
|
1.0f / semiAxes.z);
|
|
|
|
|
|
|
|
Vec3f recipAtmSemiAxes = recipSemiAxes / (1.0f + height);
|
|
|
|
Mat3f A = Mat3f::scaling(recipAtmSemiAxes);
|
|
|
|
Mat3f A1 = Mat3f::scaling(recipSemiAxes);
|
|
|
|
|
|
|
|
const int nCompassPoints = 16;
|
|
|
|
Vec3f compassPoints[nCompassPoints];
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-08-25 11:19:44 -06:00
|
|
|
|
|
|
|
// ellipDist is not the true distance from the surface unless the
|
|
|
|
// planet is spherical. Computing the true distance requires finding
|
|
|
|
// the roots of a sixth degree polynomial, and isn't actually what we
|
|
|
|
// want anyhow since the atmosphere region is just the planet ellipsoid
|
|
|
|
// multiplied by a uniform scale factor. The value that we do compute
|
|
|
|
// is the distance to the surface along a line from the eye position to
|
|
|
|
// the center of the ellipsoid.
|
2006-09-12 06:56:47 -06:00
|
|
|
|
|
|
|
/*float ellipDist = (float) sqrt((eyeVec * A1) * (eyeVec * A1)) - 1.0f; Unused*/
|
2004-08-25 11:19:44 -06:00
|
|
|
|
|
|
|
Vec3f e = -eyeVec;
|
|
|
|
Vec3f e_(e.x * recipSemiAxes.x, e.y * recipSemiAxes.y, e.z * recipSemiAxes.z);
|
|
|
|
float ee = e_ * e_;
|
|
|
|
|
|
|
|
Vec3f normal = eyeVec;
|
|
|
|
normal = normal / (float) centerDist;
|
|
|
|
|
|
|
|
Vec3f uAxis, vAxis;
|
2004-08-26 23:39:52 -06:00
|
|
|
Vec3f northPole(0.0f, 1.0f, 0.0f);
|
|
|
|
vAxis = normal ^ northPole;
|
2004-08-25 11:19:44 -06:00
|
|
|
vAxis.normalize();
|
|
|
|
uAxis = vAxis ^ normal;
|
|
|
|
|
|
|
|
// Compute the compass points
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < nCompassPoints; i++)
|
|
|
|
{
|
|
|
|
// We want rays with an origin at the eye point and tangent to the the
|
|
|
|
// ellipsoid.
|
|
|
|
float theta = (float) i / (float) nCompassPoints * 2 * (float) PI;
|
|
|
|
Vec3f w = (float) cos(theta) * uAxis + (float) sin(theta) * vAxis;
|
|
|
|
w = w * (float) centerDist;
|
|
|
|
|
|
|
|
Vec3f toCenter = ellipsoidTangent(recipSemiAxes, w, e, e_, ee);
|
|
|
|
compassPoints[i] = toCenter * rot;
|
|
|
|
}
|
|
|
|
|
2004-08-26 23:39:52 -06:00
|
|
|
glColor(compassColor);
|
2004-08-25 11:19:44 -06:00
|
|
|
glBegin(GL_LINES);
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
for (i = 0; i < nCompassPoints; i++)
|
|
|
|
{
|
2004-08-26 23:39:52 -06:00
|
|
|
float distance = (center + compassPoints[i]).distanceFromOrigin();
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-08-26 23:39:52 -06:00
|
|
|
float length = distance * pixelSize * 8.0f;
|
2004-08-25 11:19:44 -06:00
|
|
|
if (i % 4 == 0)
|
2004-08-26 23:39:52 -06:00
|
|
|
length *= 3.0f;
|
2004-08-25 11:19:44 -06:00
|
|
|
else if (i % 2 == 0)
|
|
|
|
length *= 2.0f;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-08-25 11:19:44 -06:00
|
|
|
glVertex(center + compassPoints[i]);
|
|
|
|
glVertex(center + compassPoints[i] * (1.0f + length));
|
|
|
|
}
|
|
|
|
glEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-02-07 21:09:49 -07:00
|
|
|
static void setupNightTextureCombine()
|
|
|
|
{
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_EXT);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_ONE_MINUS_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_MODULATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-21 01:28:29 -07:00
|
|
|
static void setupBumpTexenv()
|
|
|
|
{
|
|
|
|
// Set up the texenv_combine extension to do DOT3 bump mapping.
|
|
|
|
// No support for ambient light yet.
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
|
|
|
|
|
|
|
|
// The primary color contains the light direction in surface
|
|
|
|
// space, and texture0 is a normal map. The lighting is
|
|
|
|
// calculated by computing the dot product.
|
2004-11-11 23:50:04 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_DOT3_RGB_ARB);
|
2003-02-21 01:28:29 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_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);
|
|
|
|
|
2003-11-28 21:48:32 -07:00
|
|
|
// In the final stage, modulate the lighting value by the
|
2003-02-21 01:28:29 -07:00
|
|
|
// base texture color.
|
|
|
|
glx::glActiveTextureARB(GL_TEXTURE1_ARB);
|
2005-07-12 11:37:00 -06:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
|
2003-02-21 01:28:29 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-12 15:59:05 -06:00
|
|
|
#if 0
|
2003-11-28 21:48:32 -07:00
|
|
|
static void setupBumpTexenvAmbient(Color ambientColor)
|
|
|
|
{
|
|
|
|
float texenvConst[4];
|
|
|
|
texenvConst[0] = ambientColor.red();
|
|
|
|
texenvConst[1] = ambientColor.green();
|
|
|
|
texenvConst[2] = ambientColor.blue();
|
|
|
|
texenvConst[3] = ambientColor.alpha();
|
|
|
|
|
|
|
|
// Set up the texenv_combine extension to do DOT3 bump mapping.
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
|
|
|
|
|
|
|
|
// The primary color contains the light direction in surface
|
|
|
|
// space, and texture0 is a normal map. The lighting is
|
|
|
|
// calculated by computing the dot product.
|
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
2004-11-11 23:50:04 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_DOT3_RGB_ARB);
|
2003-11-28 21:48:32 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_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);
|
|
|
|
|
|
|
|
// Add in the ambient color
|
|
|
|
glx::glActiveTextureARB(GL_TEXTURE1_ARB);
|
|
|
|
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, texenvConst);
|
|
|
|
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_CONSTANT_EXT);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
// In the final stage, modulate the lighting value by the
|
|
|
|
// base texture color.
|
|
|
|
glx::glActiveTextureARB(GL_TEXTURE2_ARB);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
|
|
|
|
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);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
|
|
|
}
|
2006-09-12 15:59:05 -06:00
|
|
|
#endif
|
2003-11-28 21:48:32 -07:00
|
|
|
|
|
|
|
|
|
|
|
static void setupTexenvAmbient(Color ambientColor)
|
|
|
|
{
|
|
|
|
float texenvConst[4];
|
|
|
|
texenvConst[0] = ambientColor.red();
|
|
|
|
texenvConst[1] = ambientColor.green();
|
|
|
|
texenvConst[2] = ambientColor.blue();
|
|
|
|
texenvConst[3] = ambientColor.alpha();
|
|
|
|
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
|
|
|
|
|
|
|
|
// The primary color contains the light direction in surface
|
|
|
|
// space, and texture0 is a normal map. The lighting is
|
|
|
|
// calculated by computing the dot product.
|
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
|
|
|
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, texenvConst);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_CONSTANT_EXT);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-21 01:28:29 -07:00
|
|
|
static void setupTexenvGlossMapAlpha()
|
|
|
|
{
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_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_ALPHA);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-27 03:48:39 -06:00
|
|
|
static void setLightParameters_VP(VertexProcessor& vproc,
|
|
|
|
const LightingState& ls,
|
|
|
|
Color materialDiffuse,
|
|
|
|
Color materialSpecular)
|
|
|
|
{
|
|
|
|
Vec3f diffuseColor(materialDiffuse.red(),
|
|
|
|
materialDiffuse.green(),
|
|
|
|
materialDiffuse.blue());
|
|
|
|
Vec3f specularColor(materialSpecular.red(),
|
|
|
|
materialSpecular.green(),
|
|
|
|
materialSpecular.blue());
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < ls.nLights; i++)
|
|
|
|
{
|
|
|
|
const DirectionalLight& light = ls.lights[i];
|
|
|
|
|
|
|
|
Vec3f lightColor = Vec3f(light.color.red(),
|
|
|
|
light.color.green(),
|
|
|
|
light.color.blue()) * light.irradiance;
|
|
|
|
Vec3f diffuse(diffuseColor.x * lightColor.x,
|
|
|
|
diffuseColor.y * lightColor.y,
|
|
|
|
diffuseColor.z * lightColor.z);
|
|
|
|
Vec3f specular(specularColor.x * lightColor.x,
|
|
|
|
specularColor.y * lightColor.y,
|
|
|
|
specularColor.z * lightColor.z);
|
|
|
|
|
|
|
|
// Just handle two light sources for now
|
|
|
|
if (i == 0)
|
|
|
|
{
|
|
|
|
vproc.parameter(vp::LightDirection0, ls.lights[0].direction_obj);
|
|
|
|
vproc.parameter(vp::DiffuseColor0, diffuse);
|
|
|
|
vproc.parameter(vp::SpecularColor0, specular);
|
|
|
|
}
|
|
|
|
else if (i == 1)
|
|
|
|
{
|
|
|
|
vproc.parameter(vp::LightDirection1, ls.lights[1].direction_obj);
|
|
|
|
vproc.parameter(vp::DiffuseColor1, diffuse);
|
|
|
|
vproc.parameter(vp::SpecularColor1, specular);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-17 02:58:52 -07:00
|
|
|
static void renderModelDefault(Model* model,
|
|
|
|
const RenderInfo& ri,
|
|
|
|
bool lit)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2004-07-07 03:53:09 -06:00
|
|
|
FixedFunctionRenderContext rc;
|
|
|
|
//rc.makeCurrent();
|
2004-02-21 18:44:38 -07:00
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
if (lit)
|
|
|
|
glEnable(GL_LIGHTING);
|
|
|
|
else
|
|
|
|
glDisable(GL_LIGHTING);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
if (ri.baseTex == NULL)
|
|
|
|
{
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
ri.baseTex->bind();
|
|
|
|
}
|
|
|
|
|
|
|
|
glColor(ri.color);
|
|
|
|
|
2004-02-21 18:44:38 -07:00
|
|
|
if (ri.baseTex != NULL)
|
|
|
|
rc.lock();
|
|
|
|
|
|
|
|
model->render(rc);
|
2005-03-09 09:38:45 -07:00
|
|
|
if (model->usesTextureType(Mesh::EmissiveMap))
|
|
|
|
{
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
2007-01-05 03:09:59 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
2005-03-09 09:38:45 -07:00
|
|
|
rc.setRenderPass(RenderContext::EmissivePass);
|
|
|
|
rc.setMaterial(NULL);
|
|
|
|
|
|
|
|
model->render(rc);
|
2007-01-05 03:09:59 -07:00
|
|
|
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
2005-03-09 09:38:45 -07:00
|
|
|
}
|
|
|
|
|
2004-03-12 01:31:10 -07:00
|
|
|
// Reset the material
|
|
|
|
float black[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
|
|
|
float zero = 0.0f;
|
|
|
|
glColor4fv(black);
|
|
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, black);
|
|
|
|
glMaterialfv(GL_FRONT, GL_SPECULAR, black);
|
|
|
|
glMaterialfv(GL_FRONT, GL_SHININESS, &zero);
|
2002-02-07 21:09:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-02-17 21:00:07 -07:00
|
|
|
static void renderSphereDefault(const RenderInfo& ri,
|
2002-02-08 14:55:26 -07:00
|
|
|
const Frustum& frustum,
|
2003-03-28 11:39:23 -07:00
|
|
|
bool lit,
|
|
|
|
const GLContext& context)
|
2002-02-07 21:09:49 -07:00
|
|
|
{
|
2002-02-08 14:55:26 -07:00
|
|
|
if (lit)
|
|
|
|
glEnable(GL_LIGHTING);
|
|
|
|
else
|
|
|
|
glDisable(GL_LIGHTING);
|
2002-02-07 21:09:49 -07:00
|
|
|
|
|
|
|
if (ri.baseTex == NULL)
|
|
|
|
{
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
ri.baseTex->bind();
|
|
|
|
}
|
|
|
|
|
|
|
|
glColor(ri.color);
|
|
|
|
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.baseTex);
|
2001-11-27 18:50:04 -07:00
|
|
|
if (ri.nightTex != NULL && ri.useTexEnvCombine)
|
|
|
|
{
|
|
|
|
ri.nightTex->bind();
|
2002-02-07 21:09:49 -07:00
|
|
|
setupNightTextureCombine();
|
2001-11-27 18:50:04 -07:00
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
2002-03-14 14:48:51 -07:00
|
|
|
glAmbientLightColor(Color::Black); // Disable ambient light
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.nightTex);
|
2002-03-14 14:48:51 -07:00
|
|
|
glAmbientLightColor(ri.ambientColor);
|
2001-11-27 18:50:04 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
}
|
2003-06-06 11:13:16 -06:00
|
|
|
|
|
|
|
if (ri.overlayTex != NULL)
|
|
|
|
{
|
|
|
|
ri.overlayTex->bind();
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.overlayTex);
|
2003-06-06 11:13:16 -06:00
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 03:27:09 -06:00
|
|
|
// DEPRECATED -- renderSphere_Combiners_VP should be used instead; only
|
|
|
|
// very old drivers don't support vertex programs.
|
2003-02-19 10:48:25 -07:00
|
|
|
static void renderSphere_Combiners(const RenderInfo& ri,
|
2003-03-28 11:39:23 -07:00
|
|
|
const Frustum& frustum,
|
|
|
|
const GLContext& context)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
|
|
|
|
if (ri.baseTex == NULL)
|
|
|
|
{
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
ri.baseTex->bind();
|
|
|
|
}
|
|
|
|
|
2002-01-07 23:04:05 -07:00
|
|
|
glColor(ri.color * ri.sunColor);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-10-21 18:13:18 -06:00
|
|
|
// 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)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2003-03-28 11:39:23 -07:00
|
|
|
renderBumpMappedMesh(context,
|
|
|
|
*(ri.baseTex),
|
2002-09-02 15:13:10 -06:00
|
|
|
*(ri.bumpTex),
|
2001-11-27 18:50:04 -07:00
|
|
|
ri.sunDir_eye,
|
|
|
|
ri.orientation,
|
|
|
|
ri.ambientColor,
|
|
|
|
frustum,
|
2003-07-30 11:07:46 -06:00
|
|
|
ri.pixWidth);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
else if (ri.baseTex != NULL)
|
|
|
|
{
|
2003-03-28 11:39:23 -07:00
|
|
|
renderSmoothMesh(context,
|
|
|
|
*(ri.baseTex),
|
2001-11-27 18:50:04 -07:00
|
|
|
ri.sunDir_eye,
|
|
|
|
ri.orientation,
|
|
|
|
ri.ambientColor,
|
2003-07-30 11:07:46 -06:00
|
|
|
ri.pixWidth,
|
2001-11-27 18:50:04 -07:00
|
|
|
frustum);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glEnable(GL_LIGHTING);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context, frustum, ri.pixWidth, NULL, 0);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2002-12-23 00:58:58 -07:00
|
|
|
if (ri.nightTex != NULL)
|
|
|
|
{
|
|
|
|
ri.nightTex->bind();
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
2003-03-28 11:39:23 -07:00
|
|
|
renderSmoothMesh(context,
|
|
|
|
*(ri.nightTex),
|
2006-09-12 06:56:47 -06:00
|
|
|
ri.sunDir_eye,
|
2002-12-23 00:58:58 -07:00
|
|
|
ri.orientation,
|
|
|
|
Color::Black,
|
2003-07-30 11:07:46 -06:00
|
|
|
ri.pixWidth,
|
2002-12-23 00:58:58 -07:00
|
|
|
frustum,
|
|
|
|
true);
|
|
|
|
}
|
|
|
|
|
2003-06-06 11:13:16 -06:00
|
|
|
if (ri.overlayTex != NULL)
|
|
|
|
{
|
|
|
|
glEnable(GL_LIGHTING);
|
|
|
|
ri.overlayTex->bind();
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
2006-09-12 06:56:47 -06:00
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
2006-08-26 17:58:48 -06:00
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.overlayTex);
|
2003-06-06 11:13:16 -06:00
|
|
|
#if 0
|
|
|
|
renderSmoothMesh(context,
|
|
|
|
*(ri.overlayTex),
|
|
|
|
ri.sunDir_eye,
|
|
|
|
ri.orientation,
|
|
|
|
ri.ambientColor,
|
2003-07-30 11:07:46 -06:00
|
|
|
ri.pixWidth,
|
2003-06-06 11:13:16 -06:00
|
|
|
frustum);
|
|
|
|
#endif
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
}
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-20 02:48:47 -07:00
|
|
|
static void renderSphere_DOT3_VP(const RenderInfo& ri,
|
2004-10-05 03:27:09 -06:00
|
|
|
const LightingState& ls,
|
2003-02-20 02:48:47 -07:00
|
|
|
const Frustum& frustum,
|
|
|
|
const GLContext& context)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2003-02-20 02:48:47 -07:00
|
|
|
VertexProcessor* vproc = context.getVertexProcessor();
|
|
|
|
assert(vproc != NULL);
|
2002-08-20 03:09:35 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
if (ri.baseTex == NULL)
|
|
|
|
{
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
ri.baseTex->bind();
|
|
|
|
}
|
|
|
|
|
2003-02-21 01:28:29 -07:00
|
|
|
vproc->enable();
|
2003-02-20 02:48:47 -07:00
|
|
|
vproc->parameter(vp::EyePosition, ri.eyePos_obj);
|
2004-10-27 03:48:39 -06:00
|
|
|
setLightParameters_VP(*vproc, ls, ri.color, ri.specularColor);
|
2004-10-05 03:27:09 -06:00
|
|
|
|
2003-02-20 02:48:47 -07:00
|
|
|
vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
|
2004-10-05 03:27:09 -06:00
|
|
|
vproc->parameter(vp::SpecularExponent, 0.0f, 1.0f, 0.5f, ri.specularPower);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-10-21 18:13:18 -06:00
|
|
|
// 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 &&
|
|
|
|
ri.baseTex != NULL)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2003-02-21 01:28:29 -07:00
|
|
|
// We don't yet handle the case where there's a bump map but no
|
|
|
|
// base texture.
|
2003-02-20 02:48:47 -07:00
|
|
|
vproc->use(vp::diffuseBump);
|
2003-11-28 21:48:32 -07:00
|
|
|
if (ri.ambientColor != Color::Black)
|
|
|
|
{
|
|
|
|
// If there's ambient light, we'll need to render in two passes:
|
|
|
|
// one for the ambient light, and the second for light from the star.
|
|
|
|
// We could do this in a single pass using three texture stages, but
|
|
|
|
// this isn't won't work with hardware that only supported two
|
|
|
|
// texture stages.
|
|
|
|
|
|
|
|
// Render the base texture modulated by the ambient color
|
|
|
|
setupTexenvAmbient(ri.ambientColor);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.baseTex);
|
2003-11-28 21:48:32 -07:00
|
|
|
|
|
|
|
// Add the light from the sun
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
setupBumpTexenv();
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::Tangents |
|
|
|
|
LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.bumpTex, ri.baseTex);
|
2003-11-28 21:48:32 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glx::glActiveTextureARB(GL_TEXTURE1_ARB);
|
|
|
|
ri.baseTex->bind();
|
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
|
|
|
ri.bumpTex->bind();
|
|
|
|
setupBumpTexenv();
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::Tangents |
|
|
|
|
LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.bumpTex, ri.baseTex);
|
2003-11-28 21:48:32 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-10-05 03:27:09 -06:00
|
|
|
if (ls.nLights > 1)
|
|
|
|
vproc->use(vp::diffuse_2light);
|
|
|
|
else
|
|
|
|
vproc->use(vp::diffuse);
|
2003-02-21 01:28:29 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
|
|
|
|
LODSphereMesh::VertexProgParams,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.baseTex);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2003-02-21 01:28:29 -07:00
|
|
|
// 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();
|
|
|
|
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.glossTex != NULL ? ri.glossTex : ri.baseTex);
|
2003-02-21 01:28:29 -07:00
|
|
|
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
|
|
|
|
2003-02-20 02:48:47 -07:00
|
|
|
if (ri.nightTex != NULL)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
ri.nightTex->bind();
|
2004-10-09 20:07:51 -06:00
|
|
|
if (ls.nLights > 1)
|
|
|
|
vproc->use(vp::nightLights_2light);
|
|
|
|
else
|
|
|
|
vproc->use(vp::nightLights);
|
2002-02-07 21:09:49 -07:00
|
|
|
setupNightTextureCombine();
|
2001-11-27 18:50:04 -07:00
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
2004-02-17 02:58:52 -07:00
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
2003-07-30 11:07:46 -06:00
|
|
|
frustum, ri.pixWidth,
|
2002-02-25 13:29:32 -07:00
|
|
|
ri.nightTex);
|
2001-11-27 18:50:04 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
}
|
|
|
|
|
2003-06-06 11:13:16 -06:00
|
|
|
if (ri.overlayTex != NULL)
|
|
|
|
{
|
|
|
|
ri.overlayTex->bind();
|
|
|
|
vproc->use(vp::diffuse);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.overlayTex);
|
2003-06-06 11:13:16 -06:00
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
}
|
2003-02-20 02:48:47 -07:00
|
|
|
|
2003-06-06 11:13:16 -06:00
|
|
|
vproc->disable();
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-20 02:48:47 -07:00
|
|
|
static void renderSphere_Combiners_VP(const RenderInfo& ri,
|
2004-10-05 03:27:09 -06:00
|
|
|
const LightingState& ls,
|
2003-02-20 02:48:47 -07:00
|
|
|
const Frustum& frustum,
|
|
|
|
const GLContext& context)
|
2003-02-19 10:48:25 -07:00
|
|
|
{
|
|
|
|
Texture* textures[4];
|
2003-02-20 02:48:47 -07:00
|
|
|
VertexProcessor* vproc = context.getVertexProcessor();
|
|
|
|
assert(vproc != NULL);
|
2003-02-19 10:48:25 -07:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2003-07-25 02:01:16 -06:00
|
|
|
if (hazeDensity > 0.0f && !buggyVertexProgramEmulation)
|
2003-02-19 10:48:25 -07:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2003-02-20 02:48:47 -07:00
|
|
|
vproc->enable();
|
2003-02-19 10:48:25 -07:00
|
|
|
|
2003-02-20 02:48:47 -07:00
|
|
|
vproc->parameter(vp::EyePosition, ri.eyePos_obj);
|
2004-10-27 03:48:39 -06:00
|
|
|
setLightParameters_VP(*vproc, ls, ri.color, ri.specularColor);
|
2004-10-05 03:27:09 -06:00
|
|
|
|
2003-02-20 02:48:47 -07:00
|
|
|
vproc->parameter(vp::SpecularExponent, 0.0f, 1.0f, 0.5f, ri.specularPower);
|
|
|
|
vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
|
|
|
|
vproc->parameter(vp::HazeColor, ri.hazeColor);
|
2003-02-19 10:48:25 -07:00
|
|
|
|
2006-10-21 18:13:18 -06:00
|
|
|
// 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)
|
2003-02-19 10:48:25 -07:00
|
|
|
{
|
|
|
|
if (hazeDensity > 0.0f)
|
2003-02-20 02:48:47 -07:00
|
|
|
vproc->use(vp::diffuseBumpHaze);
|
2003-02-19 10:48:25 -07:00
|
|
|
else
|
2003-02-20 02:48:47 -07:00
|
|
|
vproc->use(vp::diffuseBump);
|
2003-02-19 10:48:25 -07:00
|
|
|
SetupCombinersDecalAndBumpMap(*(ri.bumpTex),
|
|
|
|
ri.ambientColor * ri.color,
|
|
|
|
ri.sunColor * ri.color);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::Tangents |
|
|
|
|
LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.baseTex, ri.bumpTex);
|
2003-02-19 10:48:25 -07:00
|
|
|
DisableCombiners();
|
|
|
|
|
|
|
|
// Render a specular pass
|
|
|
|
if (ri.specularColor != Color::Black)
|
|
|
|
{
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
2003-02-20 02:48:47 -07:00
|
|
|
glEnable(GL_COLOR_SUM_EXT);
|
|
|
|
vproc->use(vp::specular);
|
2003-02-19 10:48:25 -07:00
|
|
|
|
|
|
|
// Disable ambient and diffuse
|
2003-02-20 02:48:47 -07:00
|
|
|
vproc->parameter(vp::AmbientColor, Color::Black);
|
2004-10-05 03:27:09 -06:00
|
|
|
vproc->parameter(vp::DiffuseColor0, Color::Black);
|
2003-02-19 10:48:25 -07:00
|
|
|
SetupCombinersGlossMap(ri.glossTex != NULL ? GL_TEXTURE0_ARB : 0);
|
|
|
|
|
|
|
|
textures[0] = ri.glossTex != NULL ? ri.glossTex : ri.baseTex;
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
textures, 1);
|
2003-02-19 10:48:25 -07:00
|
|
|
|
|
|
|
// re-enable diffuse
|
2004-10-05 03:27:09 -06:00
|
|
|
vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
|
2003-02-19 10:48:25 -07:00
|
|
|
|
|
|
|
DisableCombiners();
|
2003-02-20 02:48:47 -07:00
|
|
|
glDisable(GL_COLOR_SUM_EXT);
|
2003-02-19 10:48:25 -07:00
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ri.specularColor != Color::Black)
|
|
|
|
{
|
2003-02-20 02:48:47 -07:00
|
|
|
glEnable(GL_COLOR_SUM_EXT);
|
2004-10-05 03:27:09 -06:00
|
|
|
if (ls.nLights > 1)
|
|
|
|
vproc->use(vp::specular_2light);
|
|
|
|
else
|
|
|
|
vproc->use(vp::specular);
|
2003-02-19 10:48:25 -07:00
|
|
|
SetupCombinersGlossMapWithFog(ri.glossTex != NULL ? GL_TEXTURE1_ARB : 0);
|
2004-02-17 02:58:52 -07:00
|
|
|
unsigned int attributes = LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
|
|
|
|
LODSphereMesh::VertexProgParams;
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
attributes, frustum, ri.pixWidth,
|
|
|
|
ri.baseTex, ri.glossTex);
|
2003-02-19 10:48:25 -07:00
|
|
|
DisableCombiners();
|
2003-02-20 02:48:47 -07:00
|
|
|
glDisable(GL_COLOR_SUM_EXT);
|
2003-02-19 10:48:25 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-10-05 03:27:09 -06:00
|
|
|
if (ls.nLights > 1)
|
|
|
|
{
|
|
|
|
if (hazeDensity > 0.0f)
|
|
|
|
vproc->use(vp::diffuseHaze_2light);
|
|
|
|
else
|
|
|
|
vproc->use(vp::diffuse_2light);
|
|
|
|
}
|
2003-02-19 10:48:25 -07:00
|
|
|
else
|
2004-10-05 03:27:09 -06:00
|
|
|
{
|
|
|
|
if (hazeDensity > 0.0f)
|
|
|
|
vproc->use(vp::diffuseHaze);
|
|
|
|
else
|
|
|
|
vproc->use(vp::diffuse);
|
|
|
|
}
|
|
|
|
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
|
|
|
|
LODSphereMesh::VertexProgParams,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.baseTex);
|
2003-02-19 10:48:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hazeDensity > 0.0f)
|
|
|
|
glDisable(GL_FOG);
|
|
|
|
|
|
|
|
if (ri.nightTex != NULL)
|
|
|
|
{
|
|
|
|
ri.nightTex->bind();
|
2004-10-09 20:07:51 -06:00
|
|
|
if (ls.nLights > 1)
|
|
|
|
vproc->use(vp::nightLights_2light);
|
|
|
|
else
|
|
|
|
vproc->use(vp::nightLights);
|
2003-02-19 10:48:25 -07:00
|
|
|
setupNightTextureCombine();
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.nightTex);
|
2003-02-19 10:48:25 -07:00
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
}
|
|
|
|
|
2003-06-06 11:13:16 -06:00
|
|
|
if (ri.overlayTex != NULL)
|
|
|
|
{
|
|
|
|
ri.overlayTex->bind();
|
|
|
|
vproc->use(vp::diffuse);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.overlayTex);
|
2003-06-06 11:13:16 -06:00
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
}
|
|
|
|
|
2003-02-20 02:48:47 -07:00
|
|
|
vproc->disable();
|
2003-02-19 10:48:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-04-19 11:28:39 -06:00
|
|
|
// 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);
|
2004-10-05 03:27:09 -06:00
|
|
|
vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
|
|
|
|
vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
|
2004-04-19 11:28:39 -06:00
|
|
|
vproc->parameter(vp::SpecularExponent, 0.0f, 1.0f, 0.5f, ri.specularPower);
|
2004-10-05 03:27:09 -06:00
|
|
|
vproc->parameter(vp::SpecularColor0, ri.sunColor * ri.specularColor);
|
2004-04-19 11:28:39 -06:00
|
|
|
vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
|
|
|
|
vproc->parameter(vp::HazeColor, ri.hazeColor);
|
|
|
|
|
|
|
|
if (ri.bumpTex != NULL)
|
|
|
|
{
|
|
|
|
fproc->enable();
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-04-19 11:28:39 -06:00
|
|
|
if (hazeDensity > 0.0f)
|
|
|
|
vproc->use(vp::diffuseBumpHaze);
|
|
|
|
else
|
|
|
|
vproc->use(vp::diffuseBump);
|
|
|
|
fproc->use(fp::texDiffuseBump);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::Tangents |
|
|
|
|
LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.baseTex, ri.bumpTex);
|
2004-04-19 11:28:39 -06:00
|
|
|
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);
|
2004-10-05 03:27:09 -06:00
|
|
|
vproc->parameter(vp::DiffuseColor0, Color::Black);
|
2004-04-19 11:28:39 -06:00
|
|
|
SetupCombinersGlossMap(ri.glossTex != NULL ? GL_TEXTURE0_ARB : 0);
|
|
|
|
|
|
|
|
textures[0] = ri.glossTex != NULL ? ri.glossTex : ri.baseTex;
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
textures, 1);
|
2004-04-19 11:28:39 -06:00
|
|
|
|
|
|
|
// re-enable diffuse
|
2004-10-05 03:27:09 -06:00
|
|
|
vproc->parameter(vp::DiffuseColor0, ri.sunColor * ri.color);
|
2004-04-19 11:28:39 -06:00
|
|
|
|
|
|
|
DisableCombiners();
|
|
|
|
glDisable(GL_COLOR_SUM_EXT);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ri.specularColor != Color::Black)
|
|
|
|
{
|
|
|
|
fproc->enable();
|
2004-08-03 02:57:13 -06:00
|
|
|
if (ri.glossTex == NULL)
|
|
|
|
{
|
|
|
|
vproc->use(vp::perFragmentSpecularAlpha);
|
|
|
|
fproc->use(fp::texSpecularAlpha);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vproc->use(vp::perFragmentSpecular);
|
|
|
|
fproc->use(fp::texSpecular);
|
|
|
|
}
|
2004-04-19 11:28:39 -06:00
|
|
|
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;
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
attributes, frustum, ri.pixWidth,
|
|
|
|
ri.baseTex, ri.glossTex);
|
2004-04-19 11:28:39 -06:00
|
|
|
fproc->disable();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fproc->enable();
|
|
|
|
if (hazeDensity > 0.0f)
|
|
|
|
vproc->use(vp::diffuseHaze);
|
|
|
|
else
|
|
|
|
vproc->use(vp::diffuse);
|
|
|
|
fproc->use(fp::texDiffuse);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0 |
|
|
|
|
LODSphereMesh::VertexProgParams,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.baseTex);
|
2004-04-19 11:28:39 -06:00
|
|
|
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);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.nightTex);
|
2004-04-19 11:28:39 -06:00
|
|
|
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);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
frustum, ri.pixWidth,
|
|
|
|
ri.overlayTex);
|
2004-04-19 11:28:39 -06:00
|
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
vproc->disable();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-14 01:39:58 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2004-02-17 02:58:52 -07:00
|
|
|
static void renderShadowedModelDefault(Model* model,
|
|
|
|
const RenderInfo& ri,
|
|
|
|
const Frustum& frustum,
|
|
|
|
float *sPlane,
|
|
|
|
float *tPlane,
|
|
|
|
const Vec3f& lightDir,
|
|
|
|
bool useShadowMask,
|
|
|
|
const GLContext& context)
|
2002-02-21 14:55:34 -07:00
|
|
|
{
|
|
|
|
glEnable(GL_TEXTURE_GEN_S);
|
|
|
|
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
|
|
|
glTexGenfv(GL_S, GL_OBJECT_PLANE, sPlane);
|
2005-07-12 11:37:00 -06:00
|
|
|
//texGenPlane(GL_S, GL_OBJECT_PLANE, sPlane);
|
2002-02-21 14:55:34 -07:00
|
|
|
glEnable(GL_TEXTURE_GEN_T);
|
|
|
|
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
|
|
|
glTexGenfv(GL_T, GL_OBJECT_PLANE, tPlane);
|
2003-02-14 01:39:58 -07:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2002-02-21 14:55:34 -07:00
|
|
|
glColor4f(1, 1, 1, 1);
|
|
|
|
glDisable(GL_LIGHTING);
|
2003-02-14 01:39:58 -07:00
|
|
|
|
2004-02-17 02:58:52 -07:00
|
|
|
if (model == NULL)
|
2002-03-05 18:15:07 -07:00
|
|
|
{
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::Multipass,
|
|
|
|
frustum, ri.pixWidth, NULL);
|
2002-03-05 18:15:07 -07:00
|
|
|
}
|
2002-02-25 13:29:32 -07:00
|
|
|
else
|
2002-03-05 18:15:07 -07:00
|
|
|
{
|
2004-07-07 03:53:09 -06:00
|
|
|
FixedFunctionRenderContext rc;
|
2004-02-21 18:44:38 -07:00
|
|
|
model->render(rc);
|
2002-03-05 18:15:07 -07:00
|
|
|
}
|
2002-02-21 14:55:34 -07:00
|
|
|
glEnable(GL_LIGHTING);
|
2003-02-14 01:39:58 -07:00
|
|
|
|
|
|
|
if (useShadowMask)
|
|
|
|
{
|
|
|
|
glx::glActiveTextureARB(GL_TEXTURE1_ARB);
|
|
|
|
glDisable(GL_TEXTURE_GEN_S);
|
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
|
|
|
}
|
2002-02-21 14:55:34 -07:00
|
|
|
glDisable(GL_TEXTURE_GEN_S);
|
|
|
|
glDisable(GL_TEXTURE_GEN_T);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-17 02:58:52 -07:00
|
|
|
static void renderShadowedModelVertexShader(const RenderInfo& ri,
|
|
|
|
const Frustum& frustum,
|
|
|
|
float* sPlane, float* tPlane,
|
|
|
|
Vec3f& lightDir,
|
|
|
|
const GLContext& context)
|
2002-02-21 14:55:34 -07:00
|
|
|
{
|
2003-02-19 10:48:25 -07:00
|
|
|
VertexProcessor* vproc = context.getVertexProcessor();
|
|
|
|
assert(vproc != NULL);
|
|
|
|
|
|
|
|
vproc->enable();
|
2004-10-05 03:27:09 -06:00
|
|
|
vproc->parameter(vp::LightDirection0, lightDir);
|
2003-02-19 10:48:25 -07:00
|
|
|
vproc->parameter(vp::TexGen_S, sPlane);
|
|
|
|
vproc->parameter(vp::TexGen_T, tPlane);
|
|
|
|
vproc->use(vp::shadowTexture);
|
|
|
|
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::Multipass, frustum,
|
|
|
|
ri.pixWidth, NULL);
|
2003-02-19 10:48:25 -07:00
|
|
|
|
|
|
|
vproc->disable();
|
2002-02-21 14:55:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-02-26 17:25:39 -07:00
|
|
|
static void renderRings(RingSystem& rings,
|
|
|
|
RenderInfo& ri,
|
|
|
|
float planetRadius,
|
2004-03-12 01:31:10 -07:00
|
|
|
float planetOblateness,
|
2002-02-26 17:25:39 -07:00
|
|
|
unsigned int textureResolution,
|
2002-09-13 01:35:50 -06:00
|
|
|
bool renderShadow,
|
2004-03-15 11:51:16 -07:00
|
|
|
const GLContext& context,
|
|
|
|
unsigned int nSections)
|
2002-02-26 17:25:39 -07:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2003-02-19 10:48:25 -07:00
|
|
|
GLContext::VertexPath vpath = context.getVertexPath();
|
|
|
|
VertexProcessor* vproc = context.getVertexProcessor();
|
2004-03-03 03:21:04 -07:00
|
|
|
FragmentProcessor* fproc = context.getFragmentProcessor();
|
2003-02-19 10:48:25 -07:00
|
|
|
|
|
|
|
if (vproc != NULL)
|
2002-09-13 01:35:50 -06:00
|
|
|
{
|
2003-02-19 10:48:25 -07:00
|
|
|
vproc->enable();
|
|
|
|
vproc->use(vp::ringIllum);
|
2004-10-05 03:27:09 -06:00
|
|
|
vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
|
|
|
|
vproc->parameter(vp::DiffuseColor0, ri.sunColor * rings.color);
|
2003-02-19 10:48:25 -07:00
|
|
|
vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
|
|
|
|
vproc->parameter(vp::Constant0, Vec3f(0, 0.5, 1.0));
|
2002-09-13 01:35:50 -06:00
|
|
|
}
|
|
|
|
|
2002-02-26 17:25:39 -07:00
|
|
|
// 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
|
2004-03-12 01:31:10 -07:00
|
|
|
// a hack, and assumes that the planet is ellipsoidal in shape,
|
2002-02-26 17:25:39 -07:00
|
|
|
// 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)
|
|
|
|
{
|
2002-11-23 11:25:28 -07:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE1_ARB);
|
2002-02-26 17:25:39 -07:00
|
|
|
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;
|
2004-03-12 01:31:10 -07:00
|
|
|
float cosAngle = Vec3f(0.0f, 1.0f, 0.0f) * ri.sunDir_obj;
|
2006-09-12 06:56:47 -06:00
|
|
|
/*float angle = (float) acos(cosAngle); Unused*/
|
2002-02-26 18:06:11 -07:00
|
|
|
axis.normalize();
|
2004-03-12 01:31:10 -07:00
|
|
|
|
|
|
|
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)));
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-03-12 01:31:10 -07:00
|
|
|
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;
|
2002-02-26 17:25:39 -07:00
|
|
|
|
|
|
|
sPlane[0] = sAxis.x; sPlane[1] = sAxis.y; sPlane[2] = sAxis.z;
|
|
|
|
tPlane[0] = tAxis.x; tPlane[1] = tAxis.y; tPlane[2] = tAxis.z;
|
|
|
|
|
2003-02-19 10:48:25 -07:00
|
|
|
if (vproc != NULL)
|
2002-09-13 01:35:50 -06:00
|
|
|
{
|
2003-02-19 10:48:25 -07:00
|
|
|
vproc->parameter(vp::TexGen_S, sPlane);
|
|
|
|
vproc->parameter(vp::TexGen_T, tPlane);
|
2002-09-13 01:35:50 -06:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
2002-02-26 17:25:39 -07:00
|
|
|
|
2002-11-23 11:25:28 -07:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
2004-03-03 03:21:04 -07:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2002-02-26 17:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
Texture* ringsTex = rings.texture.find(textureResolution);
|
|
|
|
|
|
|
|
if (ringsTex != NULL)
|
|
|
|
ringsTex->bind();
|
|
|
|
else
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-02-26 17:25:39 -07:00
|
|
|
// Perform our own lighting for the rings.
|
|
|
|
// TODO: Don't forget about light source color (required when we
|
|
|
|
// paying attention to star color.)
|
2003-02-19 10:48:25 -07:00
|
|
|
if (vpath == GLContext::VPath_Basic)
|
2002-02-26 17:25:39 -07:00
|
|
|
{
|
2002-09-13 01:35:50 -06:00
|
|
|
glDisable(GL_LIGHTING);
|
2002-02-26 17:25:39 -07:00
|
|
|
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);
|
|
|
|
|
2004-03-03 03:21:04 -07:00
|
|
|
// 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);
|
|
|
|
|
2002-02-26 17:25:39 -07:00
|
|
|
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);
|
|
|
|
|
2004-03-03 03:21:04 -07:00
|
|
|
if (vproc != NULL && fproc != NULL)
|
|
|
|
glAmbientLightColor(ri.ambientColor * ri.color);
|
|
|
|
|
2002-02-26 17:25:39 -07:00
|
|
|
// Disable the second texture unit if it was used
|
|
|
|
if (renderShadow)
|
|
|
|
{
|
2002-11-23 11:25:28 -07:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE1_ARB);
|
2002-02-26 17:25:39 -07:00
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
glDisable(GL_TEXTURE_GEN_S);
|
|
|
|
glDisable(GL_TEXTURE_GEN_T);
|
2002-11-23 11:25:28 -07:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
2004-03-03 03:21:04 -07:00
|
|
|
|
|
|
|
if (fproc != NULL)
|
|
|
|
fproc->disable();
|
2002-02-26 17:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2002-09-13 01:35:50 -06:00
|
|
|
|
2003-02-19 10:48:25 -07:00
|
|
|
if (vproc != NULL)
|
|
|
|
vproc->disable();
|
2002-02-26 17:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2004-02-17 02:58:52 -07:00
|
|
|
renderEclipseShadows(Model* model,
|
2004-10-19 11:52:43 -06:00
|
|
|
vector<EclipseShadow>& eclipseShadows,
|
2002-02-26 17:25:39 -07:00
|
|
|
RenderInfo& ri,
|
|
|
|
float planetRadius,
|
|
|
|
Mat4f& planetMat,
|
|
|
|
Frustum& viewFrustum,
|
2003-02-19 10:48:25 -07:00
|
|
|
const GLContext& context)
|
2002-02-26 17:25:39 -07:00
|
|
|
{
|
2004-02-19 00:49:34 -07:00
|
|
|
// Eclipse shadows on mesh objects aren't working yet.
|
|
|
|
if (model != NULL)
|
|
|
|
return;
|
|
|
|
|
2004-10-19 11:52:43 -06:00
|
|
|
for (vector<EclipseShadow>::iterator iter = eclipseShadows.begin();
|
2002-02-26 17:25:39 -07:00
|
|
|
iter != eclipseShadows.end(); iter++)
|
|
|
|
{
|
2004-10-19 11:52:43 -06:00
|
|
|
EclipseShadow shadow = *iter;
|
2002-02-26 17:25:39 -07:00
|
|
|
|
2002-09-13 02:06:22 -06:00
|
|
|
#ifdef DEBUG_ECLIPSE_SHADOWS
|
2002-02-26 17:25:39 -07:00
|
|
|
// 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();
|
2003-02-14 01:39:58 -07:00
|
|
|
// shadowMaskTexture->bind();
|
2002-02-26 17:25:39 -07:00
|
|
|
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);
|
2003-02-14 01:39:58 -07:00
|
|
|
|
|
|
|
// 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);
|
2002-02-26 17:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Since invariance between nVidia's vertex programs and the
|
2002-09-13 02:06:22 -06:00
|
|
|
// standard transformation pipeline isn't guaranteed, we have to
|
2002-02-26 17:25:39 -07:00
|
|
|
// make sure to use the same transformation engine on subsequent
|
|
|
|
// rendering passes as we did on the initial one.
|
2004-02-17 02:58:52 -07:00
|
|
|
if (context.getVertexPath() != GLContext::VPath_Basic && model == NULL)
|
2002-02-26 17:25:39 -07:00
|
|
|
{
|
2004-02-17 02:58:52 -07:00
|
|
|
renderShadowedModelVertexShader(ri, viewFrustum,
|
2003-02-14 01:39:58 -07:00
|
|
|
sPlane, tPlane,
|
|
|
|
dir,
|
2003-02-19 10:48:25 -07:00
|
|
|
context);
|
2002-02-26 17:25:39 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-02-17 02:58:52 -07:00
|
|
|
renderShadowedModelDefault(model, ri, viewFrustum,
|
|
|
|
sPlane, tPlane,
|
|
|
|
dir,
|
|
|
|
ri.useTexEnvCombine,
|
|
|
|
context);
|
2002-02-26 17:25:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ri.useTexEnvCombine)
|
|
|
|
{
|
2003-02-14 01:39:58 -07:00
|
|
|
// 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);
|
|
|
|
|
2002-02-26 17:25:39 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-12 01:31:10 -07:00
|
|
|
static void
|
|
|
|
renderEclipseShadows_Shaders(Model* model,
|
2004-10-19 11:52:43 -06:00
|
|
|
vector<EclipseShadow>& eclipseShadows,
|
2004-03-12 01:31:10 -07:00
|
|
|
RenderInfo& ri,
|
|
|
|
float planetRadius,
|
|
|
|
Mat4f& planetMat,
|
|
|
|
Frustum& viewFrustum,
|
|
|
|
const GLContext& context)
|
|
|
|
{
|
|
|
|
// Eclipse shadows on mesh objects aren't working yet.
|
|
|
|
if (model != 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;
|
2004-10-19 11:52:43 -06:00
|
|
|
for (vector<EclipseShadow>::iterator iter = eclipseShadows.begin();
|
2004-03-12 01:31:10 -07:00
|
|
|
iter != eclipseShadows.end() && n < 4; iter++, n++)
|
|
|
|
{
|
2004-10-19 11:52:43 -06:00
|
|
|
EclipseShadow shadow = *iter;
|
2004-03-12 01:31:10 -07:00
|
|
|
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2004-10-05 03:27:09 -06:00
|
|
|
//vproc->parameter(vp::LightDirection0, lightDir);
|
2004-03-12 01:31:10 -07:00
|
|
|
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::Multipass,
|
|
|
|
viewFrustum,
|
|
|
|
ri.pixWidth, NULL);
|
2004-03-12 01:31:10 -07:00
|
|
|
|
|
|
|
vproc->disable();
|
|
|
|
fproc->disable();
|
|
|
|
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-22 19:59:15 -06:00
|
|
|
static void
|
2006-09-16 17:02:55 -06:00
|
|
|
renderRingShadowsVS(Model* /*model*/, //TODO: Remove unused parameters??
|
2002-09-22 19:59:15 -06:00
|
|
|
const RingSystem& rings,
|
2006-09-16 17:02:55 -06:00
|
|
|
const Vec3f& /*sunDir*/,
|
2002-09-22 19:59:15 -06:00
|
|
|
RenderInfo& ri,
|
|
|
|
float planetRadius,
|
2006-09-16 17:02:55 -06:00
|
|
|
float /*oblateness*/,
|
|
|
|
Mat4f& /*planetMat*/,
|
2003-02-19 10:48:25 -07:00
|
|
|
Frustum& viewFrustum,
|
|
|
|
const GLContext& context)
|
2002-09-22 19:59:15 -06:00
|
|
|
{
|
|
|
|
// 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.
|
2003-02-19 10:48:25 -07:00
|
|
|
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);
|
2002-09-22 19:59:15 -06:00
|
|
|
|
|
|
|
// 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.
|
2004-03-12 01:31:10 -07:00
|
|
|
//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;
|
2002-09-22 19:59:15 -06:00
|
|
|
|
2003-02-19 10:48:25 -07:00
|
|
|
VertexProcessor* vproc = context.getVertexProcessor();
|
|
|
|
assert(vproc != NULL);
|
|
|
|
|
|
|
|
vproc->enable();
|
|
|
|
vproc->use(vp::ringShadow);
|
2004-10-05 03:27:09 -06:00
|
|
|
vproc->parameter(vp::LightDirection0, ri.sunDir_obj);
|
|
|
|
vproc->parameter(vp::DiffuseColor0, 1, 1, 1, alpha); // color = white
|
2003-02-19 10:48:25 -07:00
|
|
|
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);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(context, LODSphereMesh::Multipass,
|
|
|
|
viewFrustum, ri.pixWidth, NULL);
|
2003-02-19 10:48:25 -07:00
|
|
|
vproc->disable();
|
2002-09-22 19:59:15 -06:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-06-17 11:40:49 -06:00
|
|
|
void Renderer::renderLocations(const vector<Location*>& locations,
|
2003-08-01 00:32:43 -06:00
|
|
|
const Quatf& cameraOrientation,
|
2006-12-08 12:02:20 -07:00
|
|
|
const Point3d& bodyPosition,
|
|
|
|
const Quatd& bodyOrientation,
|
2003-06-17 11:40:49 -06:00
|
|
|
float scale)
|
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
if (font[FontNormal] == NULL)
|
2003-06-17 11:40:49 -06:00
|
|
|
return;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2003-08-01 00:32:43 -06:00
|
|
|
Vec3f viewNormal = Vec3f(0.0f, 0.0f, -1.0f) *
|
|
|
|
cameraOrientation.toMatrix3();
|
2004-07-21 10:58:30 -06:00
|
|
|
Vec3d viewNormald = Vec3d(viewNormal.x, viewNormal.y, viewNormal.z);
|
2003-08-01 00:32:43 -06:00
|
|
|
|
2003-06-17 11:40:49 -06:00
|
|
|
double modelview[16];
|
|
|
|
double projection[16];
|
|
|
|
glGetDoublev(GL_PROJECTION_MATRIX, projection);
|
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
// Get the camera matrix GL-style for gluProject
|
|
|
|
{
|
|
|
|
Mat3f cameraMatrix = cameraOrientation.toMatrix3();
|
|
|
|
modelview[0] = cameraMatrix[0][0];
|
|
|
|
modelview[1] = cameraMatrix[1][0];
|
|
|
|
modelview[2] = cameraMatrix[2][0];
|
|
|
|
modelview[3] = 0.0f;
|
|
|
|
modelview[4] = cameraMatrix[0][1];
|
|
|
|
modelview[5] = cameraMatrix[1][1];
|
|
|
|
modelview[6] = cameraMatrix[2][1];
|
|
|
|
modelview[7] = 0.0f;
|
|
|
|
modelview[8] = cameraMatrix[0][2];
|
|
|
|
modelview[9] = cameraMatrix[1][2];
|
|
|
|
modelview[10] = cameraMatrix[2][2];
|
|
|
|
modelview[11] = 0.0;
|
|
|
|
modelview[12] = 0.0;
|
|
|
|
modelview[13] = 0.0;
|
|
|
|
modelview[14] = 0.0;
|
|
|
|
modelview[15] = 1.0;
|
|
|
|
}
|
|
|
|
|
2003-06-17 11:40:49 -06:00
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
2006-03-08 11:29:10 -07:00
|
|
|
font[FontNormal]->bind();
|
2003-06-17 11:40:49 -06:00
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadIdentity();
|
|
|
|
glOrtho(0, windowWidth, 0, windowHeight, 1.0f, -1.0f);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadIdentity();
|
2004-01-29 10:28:10 -07:00
|
|
|
|
|
|
|
// Render the labels very close to the near plane with z=-0.999f. In fact,
|
|
|
|
// z=-1.0f should work, but I'm concerned that some OpenGL implementations
|
|
|
|
// might clip things placed right on the near plane.
|
2004-02-28 02:30:17 -07:00
|
|
|
glTranslatef(GLfloat((int) (windowWidth / 2)),
|
|
|
|
GLfloat((int) (windowHeight / 2)), -0.999f);
|
2003-06-17 11:40:49 -06:00
|
|
|
|
2004-07-21 10:58:30 -06:00
|
|
|
Point3d origin(0.0, 0.0, 0.0);
|
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
Ellipsoidd ellipsoid(bodyPosition, Vec3d(scale, scale, scale));
|
2003-06-17 11:40:49 -06:00
|
|
|
|
2007-01-05 17:52:32 -07:00
|
|
|
//float iScale = 1.0f / scale;
|
2006-12-08 12:02:20 -07:00
|
|
|
Mat3d mat = bodyOrientation.toMatrix3();
|
2003-06-17 11:40:49 -06:00
|
|
|
|
|
|
|
for (vector<Location*>::const_iterator iter = locations.begin();
|
|
|
|
iter != locations.end(); iter++)
|
|
|
|
{
|
2003-06-23 08:04:28 -06:00
|
|
|
if ((*iter)->getFeatureType() & locationFilter)
|
2003-06-17 11:40:49 -06:00
|
|
|
{
|
2004-07-21 10:58:30 -06:00
|
|
|
// Get the position of the label with respect to the planet center
|
|
|
|
Vec3f ppos = (*iter)->getPosition();
|
2006-12-08 12:02:20 -07:00
|
|
|
|
|
|
|
// Compute the body-centric position of the location
|
|
|
|
Vec3d locPos = Vec3d(ppos.x, ppos.y, ppos.z) * mat;
|
|
|
|
|
2004-07-21 10:58:30 -06:00
|
|
|
// Get the position in camera space. Add a slight scale factor
|
|
|
|
// to keep the point from being exactly on the surface.
|
2006-12-08 12:02:20 -07:00
|
|
|
Point3d cpos(bodyPosition + locPos * 1.0000001);
|
2003-06-23 08:04:28 -06:00
|
|
|
|
|
|
|
float effSize = (*iter)->getImportance();
|
|
|
|
if (effSize < 0.0f)
|
|
|
|
effSize = (*iter)->getSize();
|
2004-07-21 10:58:30 -06:00
|
|
|
float pixSize = effSize / (float) (cpos.distanceFromOrigin() * pixelSize);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2003-08-01 00:32:43 -06:00
|
|
|
if (pixSize > minFeatureSize &&
|
2004-07-21 10:58:30 -06:00
|
|
|
(cpos - origin) * viewNormald > 0.0)
|
2003-06-17 11:40:49 -06:00
|
|
|
{
|
2006-12-08 12:02:20 -07:00
|
|
|
double r = locPos.length();
|
2004-07-21 10:58:30 -06:00
|
|
|
if (r < scale * 0.99)
|
2006-12-08 12:02:20 -07:00
|
|
|
cpos = bodyPosition + locPos * (scale * 1.01 / r);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-07-21 10:58:30 -06:00
|
|
|
double t = 0.0f;
|
|
|
|
|
|
|
|
// Test for a 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.
|
|
|
|
bool hit = testIntersection(Ray3d(origin, cpos - origin),
|
2003-06-23 08:04:28 -06:00
|
|
|
ellipsoid, t);
|
2004-07-21 10:58:30 -06:00
|
|
|
if (!hit || t >= 1.0)
|
2003-06-23 08:04:28 -06:00
|
|
|
{
|
2006-12-08 12:02:20 -07:00
|
|
|
if (gluProject(bodyPosition.x + locPos.x,
|
|
|
|
bodyPosition.y + locPos.y,
|
|
|
|
bodyPosition.z + locPos.z,
|
2003-06-23 08:04:28 -06:00
|
|
|
modelview,
|
|
|
|
projection,
|
|
|
|
(const GLint*) view,
|
|
|
|
&winX, &winY, &winZ) != GL_FALSE)
|
|
|
|
{
|
|
|
|
glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
|
|
|
|
glPushMatrix();
|
|
|
|
glTranslatef((int) winX + PixelOffset,
|
|
|
|
(int) winY + PixelOffset,
|
|
|
|
0.0f);
|
2006-03-08 11:29:10 -07:00
|
|
|
font[FontNormal]->render((*iter)->getName(true));
|
2003-06-23 08:04:28 -06:00
|
|
|
glPopMatrix();
|
|
|
|
}
|
|
|
|
}
|
2003-06-17 11:40:49 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-04 00:35:33 -06:00
|
|
|
static void
|
|
|
|
setupObjectLighting(const vector<Renderer::LightSource>& suns,
|
|
|
|
const Point3d& objPosition,
|
2004-10-05 03:27:09 -06:00
|
|
|
const Quatf& objOrientation,
|
2004-10-23 03:30:14 -06:00
|
|
|
const Vec3f& objScale,
|
|
|
|
const Point3f& objPosition_eye,
|
2004-10-04 00:35:33 -06:00
|
|
|
LightingState& ls)
|
|
|
|
{
|
2004-10-19 22:12:10 -06:00
|
|
|
unsigned int nLights = min(MaxLights, (unsigned int) suns.size());
|
2004-10-04 00:35:33 -06:00
|
|
|
if (nLights == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < nLights; i++)
|
|
|
|
{
|
|
|
|
Vec3d dir = suns[i].position - objPosition;
|
2004-10-05 03:27:09 -06:00
|
|
|
ls.lights[i].direction_eye =
|
2004-10-04 00:35:33 -06:00
|
|
|
Vec3f((float) dir.x, (float) dir.y, (float) dir.z);
|
2004-10-05 03:27:09 -06:00
|
|
|
float distance = ls.lights[i].direction_eye.length();
|
|
|
|
ls.lights[i].direction_eye *= 1.0f / distance;
|
|
|
|
distance = astro::kilometersToAU((float) dir.length());
|
2004-10-08 10:35:45 -06:00
|
|
|
ls.lights[i].irradiance = suns[i].luminosity / (distance * distance);
|
2004-10-04 00:35:33 -06:00
|
|
|
ls.lights[i].color = suns[i].color;
|
2004-10-19 11:52:43 -06:00
|
|
|
|
|
|
|
// Store the position and apparent size because we'll need them for
|
|
|
|
// testing for eclipses.
|
|
|
|
ls.lights[i].position = suns[i].position;
|
|
|
|
ls.lights[i].apparentSize = (float) (suns[i].radius / dir.length());
|
2004-10-04 00:35:33 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sort light sources by brightness. Light zero should always be the
|
|
|
|
// brightest. Optimize common cases of one and two lights.
|
|
|
|
if (nLights == 2)
|
|
|
|
{
|
2004-10-08 10:35:45 -06:00
|
|
|
if (ls.lights[0].irradiance < ls.lights[1].irradiance)
|
2004-10-04 00:35:33 -06:00
|
|
|
swap(ls.lights[0], ls.lights[1]);
|
|
|
|
}
|
|
|
|
else if (nLights > 2)
|
|
|
|
{
|
2004-10-08 10:35:45 -06:00
|
|
|
sort(ls.lights, ls.lights + nLights, LightIrradiancePredicate());
|
2004-10-04 00:35:33 -06:00
|
|
|
}
|
|
|
|
|
2004-10-08 10:35:45 -06:00
|
|
|
// 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;
|
2004-10-09 14:52:59 -06:00
|
|
|
float gamma = (float) (log(minDisplayableValue) / log(minVisibleFraction));
|
2004-10-08 10:35:45 -06:00
|
|
|
float minVisibleIrradiance = minVisibleFraction * totalIrradiance;
|
|
|
|
|
2004-10-05 03:27:09 -06:00
|
|
|
Mat3f m = (~objOrientation).toMatrix3();
|
2004-10-04 00:35:33 -06:00
|
|
|
|
2004-10-08 10:35:45 -06:00
|
|
|
// 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++)
|
|
|
|
{
|
|
|
|
ls.lights[i].irradiance =
|
|
|
|
(float) pow(ls.lights[i].irradiance / totalIrradiance, gamma);
|
|
|
|
|
|
|
|
// Compute the direction of the light in object space
|
|
|
|
ls.lights[i].direction_obj = ls.lights[i].direction_eye * m;
|
|
|
|
|
|
|
|
ls.nLights++;
|
|
|
|
}
|
|
|
|
|
2004-10-23 03:30:14 -06:00
|
|
|
Point3f pos((float) objPosition.x,
|
|
|
|
(float) objPosition.y,
|
|
|
|
(float) objPosition.z);
|
|
|
|
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();
|
|
|
|
|
2006-09-02 12:56:41 -06:00
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
float s = 100.0f / eyeFromCenterDistance;
|
|
|
|
ls.eyePos_obj.x *= s;
|
|
|
|
ls.eyePos_obj.y *= s;
|
|
|
|
ls.eyePos_obj.z *= s;
|
|
|
|
}
|
|
|
|
|
2006-07-03 23:03:27 -06:00
|
|
|
ls.ambientColor = Vec3f(0.0f, 0.0f, 0.0f);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-10-08 10:35:45 -06:00
|
|
|
#if 0
|
|
|
|
// Old code: linear scaling approach
|
|
|
|
|
|
|
|
// After sorting, the first light is always the brightest
|
|
|
|
float maxIrradiance = ls.lights[0].irradiance;
|
|
|
|
|
2004-10-04 00:35:33 -06:00
|
|
|
// 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++)
|
|
|
|
{
|
2004-10-08 10:35:45 -06:00
|
|
|
ls.lights[i].irradiance /= maxIrradiance;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-10-04 00:35:33 -06:00
|
|
|
// Cull light sources that don't contribute significantly (less than
|
|
|
|
// the resolution of an 8-bit color channel.)
|
2004-10-08 10:35:45 -06:00
|
|
|
if (ls.lights[i].irradiance < 1.0f / 255.0f)
|
2004-10-04 00:35:33 -06:00
|
|
|
break;
|
|
|
|
|
2004-10-05 03:27:09 -06:00
|
|
|
// Compute the direction of the light in object space
|
|
|
|
ls.lights[i].direction_obj = ls.lights[i].direction_eye * m;
|
|
|
|
|
2004-10-04 00:35:33 -06:00
|
|
|
ls.nLights++;
|
|
|
|
}
|
2004-10-08 10:35:45 -06:00
|
|
|
#endif
|
2004-10-04 00:35:33 -06:00
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-10-04 00:35:33 -06:00
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
void Renderer::renderObject(Point3f pos,
|
2001-11-27 18:50:04 -07:00
|
|
|
float distance,
|
|
|
|
double now,
|
2002-02-08 14:55:26 -07:00
|
|
|
Quatf cameraOrientation,
|
2001-11-27 18:50:04 -07:00
|
|
|
float nearPlaneDistance,
|
2002-02-08 14:55:26 -07:00
|
|
|
float farPlaneDistance,
|
2004-10-04 00:35:33 -06:00
|
|
|
RenderProperties& obj,
|
|
|
|
const LightingState& ls)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2002-02-08 14:55:26 -07:00
|
|
|
RenderInfo ri;
|
|
|
|
|
|
|
|
float altitude = distance - obj.radius;
|
|
|
|
float discSizeInPixels = obj.radius /
|
2001-11-27 18:50:04 -07:00
|
|
|
(max(nearPlaneDistance, altitude) * pixelSize);
|
|
|
|
|
2004-10-05 03:27:09 -06:00
|
|
|
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);
|
2004-10-04 00:35:33 -06:00
|
|
|
if (ls.nLights > 0)
|
|
|
|
{
|
2004-10-05 03:27:09 -06:00
|
|
|
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;
|
2004-10-04 00:35:33 -06:00
|
|
|
}
|
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
// Enable depth buffering
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(GL_TRUE);
|
|
|
|
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
|
|
|
// Get the textures . . .
|
2002-02-13 13:38:49 -07:00
|
|
|
if (obj.surface->baseTexture.tex[textureResolution] != InvalidResource)
|
|
|
|
ri.baseTex = obj.surface->baseTexture.find(textureResolution);
|
2002-02-08 14:55:26 -07:00
|
|
|
if ((obj.surface->appearanceFlags & Surface::ApplyBumpMap) != 0 &&
|
2003-02-19 10:48:25 -07:00
|
|
|
context->bumpMappingSupported() &&
|
2002-02-13 13:38:49 -07:00
|
|
|
obj.surface->bumpTexture.tex[textureResolution] != InvalidResource)
|
|
|
|
ri.bumpTex = obj.surface->bumpTexture.find(textureResolution);
|
2002-02-08 14:55:26 -07:00
|
|
|
if ((obj.surface->appearanceFlags & Surface::ApplyNightMap) != 0 &&
|
|
|
|
(renderFlags & ShowNightMaps) != 0)
|
2002-02-13 13:38:49 -07:00
|
|
|
ri.nightTex = obj.surface->nightTexture.find(textureResolution);
|
2002-07-25 23:41:25 -06:00
|
|
|
if ((obj.surface->appearanceFlags & Surface::SeparateSpecularMap) != 0)
|
|
|
|
ri.glossTex = obj.surface->specularTexture.find(textureResolution);
|
2003-06-06 11:13:16 -06:00
|
|
|
if ((obj.surface->appearanceFlags & Surface::ApplyOverlay) != 0)
|
|
|
|
ri.overlayTex = obj.surface->overlayTexture.find(textureResolution);
|
2002-02-08 14:55:26 -07:00
|
|
|
|
|
|
|
// Apply the modelview transform for the object
|
|
|
|
glPushMatrix();
|
|
|
|
glTranslate(pos);
|
|
|
|
glRotate(~obj.orientation);
|
|
|
|
|
|
|
|
// Apply a scale factor which depends on the size of the planet and
|
|
|
|
// its oblateness. Since the oblateness is usually quite
|
|
|
|
// small, the potentially nonuniform scale factor shouldn't mess up
|
2006-07-26 11:21:23 -06:00
|
|
|
// the lighting calculations enough to be noticeable (and we turn on
|
|
|
|
// renormalization anyhow, which most graphics cards support.)
|
2002-02-08 14:55:26 -07:00
|
|
|
// TODO: Figure out a better way to render ellipsoids than applying
|
2004-10-04 00:35:33 -06:00
|
|
|
// a nonunifom scale factor to a sphere.
|
2002-02-08 14:55:26 -07:00
|
|
|
float radius = obj.radius;
|
2004-10-07 11:52:22 -06:00
|
|
|
Vec3f semiAxes = obj.radius * obj.semiAxes;
|
|
|
|
glScale(semiAxes);
|
2002-02-08 14:55:26 -07:00
|
|
|
|
2002-05-14 11:02:04 -06:00
|
|
|
Mat4f planetMat = (~obj.orientation).toMatrix4();
|
2002-02-08 14:55:26 -07:00
|
|
|
ri.eyeDir_obj = (Point3f(0, 0, 0) - pos) * planetMat;
|
|
|
|
ri.eyeDir_obj.normalize();
|
2004-10-07 11:52:22 -06:00
|
|
|
ri.eyePos_obj = Point3f(-pos.x / semiAxes.x,
|
|
|
|
-pos.y / semiAxes.y,
|
|
|
|
-pos.z / semiAxes.z) * planetMat;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
ri.orientation = cameraOrientation;
|
|
|
|
|
2003-07-30 11:07:46 -06:00
|
|
|
ri.pixWidth = discSizeInPixels;
|
2002-02-08 14:55:26 -07:00
|
|
|
|
|
|
|
// Set up the colors
|
|
|
|
if (ri.baseTex == NULL ||
|
|
|
|
(obj.surface->appearanceFlags & Surface::BlendTexture) != 0)
|
|
|
|
{
|
|
|
|
ri.color = obj.surface->color;
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2004-10-05 03:27:09 -06:00
|
|
|
ri.ambientColor = ambientColor;
|
2002-02-08 14:55:26 -07:00
|
|
|
ri.hazeColor = obj.surface->hazeColor;
|
|
|
|
ri.specularColor = obj.surface->specularColor;
|
|
|
|
ri.specularPower = obj.surface->specularPower;
|
2003-02-19 10:48:25 -07:00
|
|
|
ri.useTexEnvCombine = context->getRenderPath() != GLContext::GLPath_Basic;
|
2006-08-08 11:19:57 -06:00
|
|
|
ri.lunarLambert = obj.surface->lunarLambert;
|
2002-02-08 14:55:26 -07:00
|
|
|
|
|
|
|
// See if the surface should be lit
|
|
|
|
bool lit = (obj.surface->appearanceFlags & Surface::Emissive) == 0;
|
|
|
|
|
2004-10-05 03:27:09 -06:00
|
|
|
// 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(),
|
2004-10-08 10:35:45 -06:00
|
|
|
light.color.blue()) * light.irradiance;
|
2004-10-05 03:27:09 -06:00
|
|
|
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);
|
2002-02-08 14:55:26 -07:00
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
// 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));
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-10-31 02:41:57 -07:00
|
|
|
// 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.model == InvalidResource)
|
|
|
|
{
|
|
|
|
// Only adjust the far plane for ellipsoidal objects
|
|
|
|
float d = pos.distanceFromOrigin();
|
2006-11-27 12:11:55 -07:00
|
|
|
|
2006-10-31 02:41:57 -07:00
|
|
|
// Account for oblateness
|
|
|
|
float eradius = min(semiAxes.x, min(semiAxes.y, semiAxes.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)
|
|
|
|
{
|
2006-11-28 18:29:03 -07:00
|
|
|
float atmosphereHeight = max(obj.atmosphere->cloudHeight,
|
|
|
|
obj.atmosphere->mieScaleHeight * (float) -log(AtmosphereExtinctionThreshold));
|
2006-12-03 01:20:55 -07:00
|
|
|
if (atmosphereHeight > 0.0f)
|
2006-10-31 02:41:57 -07:00
|
|
|
{
|
2006-11-28 18:29:03 -07:00
|
|
|
// 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;
|
2007-01-05 17:52:32 -07:00
|
|
|
frustumFarPlane += (float) sqrt(square(atmosphereRadius) -
|
2006-10-31 02:41:57 -07:00
|
|
|
square(eradius));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
// Transform the frustum into object coordinates using the
|
|
|
|
// inverse model/view matrix.
|
|
|
|
Frustum viewFrustum(degToRad(fov),
|
|
|
|
(float) windowWidth / (float) windowHeight,
|
2006-10-31 02:41:57 -07:00
|
|
|
nearPlaneDistance, frustumFarPlane);
|
2002-02-08 14:55:26 -07:00
|
|
|
viewFrustum.transform(invMV);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-07-12 11:39:24 -06:00
|
|
|
// Temporary hack until we fix culling for ringed planets; prevents
|
|
|
|
// over-tesselation of ringed planet surfaces. The amount of tesselation
|
|
|
|
// should be based on the screen width of the planet sphere, not including
|
|
|
|
// the rings.
|
2002-02-28 15:12:26 -07:00
|
|
|
if (obj.rings != NULL)
|
|
|
|
{
|
2003-07-30 11:07:46 -06:00
|
|
|
if (ri.pixWidth > 5000)
|
|
|
|
ri.pixWidth = 5000;
|
2002-02-28 15:12:26 -07:00
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-07-12 11:39:24 -06:00
|
|
|
// Get cloud layer parameters
|
2006-09-01 04:14:08 -06:00
|
|
|
Texture* cloudTex = NULL;
|
|
|
|
Texture* cloudNormalMap = NULL;
|
|
|
|
float cloudTexOffset = 0.0f;
|
2006-07-12 11:39:24 -06:00
|
|
|
if (obj.atmosphere != NULL)
|
|
|
|
{
|
|
|
|
Atmosphere* atmosphere = const_cast<Atmosphere*>(obj.atmosphere); // Ugly cast required because MultiResTexture::find() is non-const
|
2006-09-01 04:14:08 -06:00
|
|
|
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);
|
|
|
|
}
|
2006-07-12 11:39:24 -06:00
|
|
|
if (atmosphere->cloudSpeed != 0.0f)
|
|
|
|
cloudTexOffset = (float) (-pfmod(now * atmosphere->cloudSpeed / (2 * PI), 1.0));
|
|
|
|
}
|
2002-02-28 15:12:26 -07:00
|
|
|
|
2004-02-17 02:58:52 -07:00
|
|
|
Model* model = NULL;
|
|
|
|
if (obj.model == InvalidResource)
|
2002-02-08 14:55:26 -07:00
|
|
|
{
|
2004-02-17 02:58:52 -07:00
|
|
|
// A null model indicates that this body is a sphere
|
2002-02-08 14:55:26 -07:00
|
|
|
if (lit)
|
|
|
|
{
|
2003-02-19 10:48:25 -07:00
|
|
|
switch (context->getRenderPath())
|
|
|
|
{
|
2004-10-15 03:55:12 -06:00
|
|
|
case GLContext::GLPath_GLSL:
|
2006-09-12 06:56:47 -06:00
|
|
|
renderSphere_GLSL(ri, ls, obj.rings,
|
2006-07-12 11:39:24 -06:00
|
|
|
const_cast<Atmosphere*>(obj.atmosphere), cloudTexOffset,
|
|
|
|
obj.radius,
|
|
|
|
textureResolution,
|
2006-07-13 10:23:06 -06:00
|
|
|
renderFlags,
|
2004-10-19 11:52:43 -06:00
|
|
|
planetMat, viewFrustum, *context);
|
2004-10-15 03:55:12 -06:00
|
|
|
break;
|
|
|
|
|
2004-04-19 11:28:39 -06:00
|
|
|
case GLContext::GLPath_NV30:
|
|
|
|
renderSphere_FP_VP(ri, viewFrustum, *context);
|
|
|
|
break;
|
|
|
|
|
2003-02-19 10:48:25 -07:00
|
|
|
case GLContext::GLPath_NvCombiner_ARBVP:
|
|
|
|
case GLContext::GLPath_NvCombiner_NvVP:
|
2004-10-05 03:27:09 -06:00
|
|
|
renderSphere_Combiners_VP(ri, ls, viewFrustum, *context);
|
2003-02-19 10:48:25 -07:00
|
|
|
break;
|
2003-02-20 02:48:47 -07:00
|
|
|
|
2003-02-19 10:48:25 -07:00
|
|
|
case GLContext::GLPath_NvCombiner:
|
2003-03-28 11:39:23 -07:00
|
|
|
renderSphere_Combiners(ri, viewFrustum, *context);
|
2003-02-19 10:48:25 -07:00
|
|
|
break;
|
2003-02-20 02:48:47 -07:00
|
|
|
|
2003-02-21 01:28:29 -07:00
|
|
|
case GLContext::GLPath_DOT3_ARBVP:
|
2004-10-05 03:27:09 -06:00
|
|
|
renderSphere_DOT3_VP(ri, ls, viewFrustum, *context);
|
2003-02-21 01:28:29 -07:00
|
|
|
break;
|
|
|
|
|
2003-02-19 10:48:25 -07:00
|
|
|
default:
|
2003-03-28 11:39:23 -07:00
|
|
|
renderSphereDefault(ri, viewFrustum, true, *context);
|
2003-02-19 10:48:25 -07:00
|
|
|
}
|
2002-02-08 14:55:26 -07:00
|
|
|
}
|
|
|
|
else
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2003-03-28 11:39:23 -07:00
|
|
|
renderSphereDefault(ri, viewFrustum, false, *context);
|
2002-02-08 14:55:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-02-17 02:58:52 -07:00
|
|
|
// This is a model loaded from a file
|
|
|
|
model = GetModelManager()->find(obj.model);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-02-17 02:58:52 -07:00
|
|
|
if (model != NULL)
|
2006-07-03 23:03:27 -06:00
|
|
|
{
|
2006-07-16 21:58:19 -06:00
|
|
|
if (context->getRenderPath() == GLContext::GLPath_GLSL && lit)
|
2006-07-03 23:03:27 -06:00
|
|
|
{
|
2006-07-16 21:58:19 -06:00
|
|
|
ResourceHandle texOverride = obj.surface->baseTexture.tex[textureResolution];
|
2006-08-29 00:13:41 -06:00
|
|
|
renderModel_GLSL(model, ri, texOverride, ls, obj.atmosphere, obj.radius, renderFlags, planetMat);
|
2006-07-16 21:58:19 -06:00
|
|
|
for (unsigned int i = 1; i < 8;/*context->getMaxTextures();*/ i++)
|
2006-07-03 23:03:27 -06:00
|
|
|
{
|
2006-07-16 21:58:19 -06:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB + i);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
2006-07-03 23:03:27 -06:00
|
|
|
}
|
2006-07-16 21:58:19 -06:00
|
|
|
glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
2006-09-12 06:56:47 -06:00
|
|
|
glx::glUseProgramObjectARB(0);
|
2006-07-16 21:58:19 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-07-03 23:03:27 -06:00
|
|
|
renderModelDefault(model, ri, lit);
|
|
|
|
}
|
|
|
|
}
|
2002-02-08 14:55:26 -07:00
|
|
|
}
|
|
|
|
|
2002-02-26 17:25:39 -07:00
|
|
|
if (obj.rings != NULL && distance <= obj.rings->innerRadius)
|
|
|
|
{
|
2004-10-23 03:30:14 -06:00
|
|
|
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);
|
|
|
|
}
|
2002-02-26 17:25:39 -07:00
|
|
|
}
|
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
if (obj.atmosphere != NULL)
|
|
|
|
{
|
2002-02-13 13:38:49 -07:00
|
|
|
Atmosphere* atmosphere = const_cast<Atmosphere *>(obj.atmosphere);
|
2002-02-08 14:55:26 -07:00
|
|
|
|
|
|
|
// 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;
|
2003-05-25 20:27:27 -06:00
|
|
|
float thicknessInPixels = 0.0f;
|
2002-02-08 14:55:26 -07:00
|
|
|
if (distance - radius > 0.0f)
|
|
|
|
{
|
2003-05-25 20:27:27 -06:00
|
|
|
thicknessInPixels = atmosphere->height /
|
2002-02-08 14:55:26 -07:00
|
|
|
((distance - radius) * pixelSize);
|
|
|
|
fade = clamp(thicknessInPixels - 2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fade = 1.0f;
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
if (fade > 0 && (renderFlags & ShowAtmospheres) != 0)
|
|
|
|
{
|
2006-08-07 00:40:45 -06:00
|
|
|
// 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;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
|
|
|
renderAtmosphere_GLSL(ri, ls,
|
2006-08-07 00:40:45 -06:00
|
|
|
atmosphere,
|
|
|
|
radius * atmScale,
|
|
|
|
planetMat,
|
|
|
|
viewFrustum,
|
|
|
|
*context);
|
|
|
|
}
|
|
|
|
else
|
2006-09-12 06:56:47 -06:00
|
|
|
{
|
2006-08-07 00:40:45 -06:00
|
|
|
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,
|
|
|
|
semiAxes,
|
|
|
|
ri.sunDir_eye,
|
|
|
|
ri.ambientColor,
|
|
|
|
thicknessInPixels,
|
|
|
|
lit);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glPopMatrix();
|
|
|
|
}
|
2002-02-08 14:55:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// If there's a cloud layer, we'll render it now.
|
|
|
|
if (cloudTex != NULL)
|
|
|
|
{
|
|
|
|
glPushMatrix();
|
2004-07-28 10:53:48 -06:00
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
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);
|
2006-07-12 11:39:24 -06:00
|
|
|
glTranslatef(cloudTexOffset, 0.0f, 0.0f);
|
2002-02-08 14:55:26 -07:00
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
}
|
|
|
|
|
|
|
|
glEnable(GL_LIGHTING);
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
cloudTex->bind();
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glColor4f(1, 1, 1, 1);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-11 23:27:29 -06:00
|
|
|
// 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);
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-12-17 11:09:31 -07:00
|
|
|
if (lit)
|
2002-08-27 11:11:35 -06:00
|
|
|
{
|
2004-11-02 11:39:08 -07:00
|
|
|
if (context->getRenderPath() == GLContext::GLPath_GLSL)
|
2002-12-17 11:09:31 -07:00
|
|
|
{
|
2004-11-02 11:39:08 -07:00
|
|
|
renderClouds_GLSL(ri, ls,
|
2006-08-07 00:40:45 -06:00
|
|
|
atmosphere,
|
2004-11-02 11:39:08 -07:00
|
|
|
cloudTex,
|
2006-09-01 04:14:08 -06:00
|
|
|
cloudNormalMap,
|
2006-07-12 11:39:24 -06:00
|
|
|
cloudTexOffset,
|
2004-11-02 11:39:08 -07:00
|
|
|
obj.rings,
|
2006-08-07 00:40:45 -06:00
|
|
|
radius,
|
2006-07-12 11:39:24 -06:00
|
|
|
textureResolution,
|
2006-08-07 00:40:45 -06:00
|
|
|
renderFlags,
|
2004-11-02 11:39:08 -07:00
|
|
|
planetMat,
|
|
|
|
viewFrustum,
|
|
|
|
*context);
|
2002-12-17 11:09:31 -07:00
|
|
|
}
|
2004-11-02 11:39:08 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
VertexProcessor* vproc = context->getVertexProcessor();
|
|
|
|
if (vproc != NULL)
|
|
|
|
{
|
|
|
|
vproc->enable();
|
|
|
|
vproc->parameter(vp::AmbientColor, ri.ambientColor * ri.color);
|
|
|
|
vproc->parameter(vp::TextureTranslation,
|
2006-07-12 11:39:24 -06:00
|
|
|
cloudTexOffset, 0.0f, 0.0f, 0.0f);
|
2004-11-02 11:39:08 -07:00
|
|
|
if (ls.nLights > 1)
|
|
|
|
vproc->use(vp::diffuseTexOffset_2light);
|
|
|
|
else
|
|
|
|
vproc->use(vp::diffuseTexOffset);
|
|
|
|
setLightParameters_VP(*vproc, ls, ri.color, Color::Black);
|
|
|
|
}
|
2003-02-19 10:48:25 -07:00
|
|
|
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(*context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
viewFrustum,
|
|
|
|
ri.pixWidth,
|
|
|
|
cloudTex);
|
2003-02-19 10:48:25 -07:00
|
|
|
|
2004-11-02 11:39:08 -07:00
|
|
|
if (vproc != NULL)
|
|
|
|
vproc->disable();
|
|
|
|
}
|
2002-08-27 11:11:35 -06:00
|
|
|
}
|
2002-12-17 11:09:31 -07:00
|
|
|
else
|
2002-08-27 11:11:35 -06:00
|
|
|
{
|
2002-12-17 11:09:31 -07:00
|
|
|
glDisable(GL_LIGHTING);
|
2006-08-26 17:58:48 -06:00
|
|
|
g_lodSphere->render(*context,
|
|
|
|
LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
|
|
|
|
viewFrustum,
|
|
|
|
ri.pixWidth,
|
|
|
|
cloudTex);
|
2002-12-17 11:09:31 -07:00
|
|
|
glEnable(GL_LIGHTING);
|
2002-08-27 11:11:35 -06:00
|
|
|
}
|
2002-02-08 14:55:26 -07:00
|
|
|
|
2006-09-11 23:27:29 -06:00
|
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
// Reset the texture matrix
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
glLoadIdentity();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
|
|
|
|
glDepthMask(GL_TRUE);
|
|
|
|
glFrontFace(GL_CCW);
|
2004-07-28 10:53:48 -06:00
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
glPopMatrix();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-19 11:52:43 -06:00
|
|
|
// 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)
|
2002-02-17 21:00:07 -07:00
|
|
|
{
|
2004-03-12 01:31:10 -07:00
|
|
|
if (context->getVertexProcessor() != NULL &&
|
|
|
|
context->getFragmentProcessor() != NULL)
|
|
|
|
{
|
|
|
|
renderEclipseShadows_Shaders(model,
|
2004-10-19 11:52:43 -06:00
|
|
|
*ls.shadows[0],
|
2004-03-12 01:31:10 -07:00
|
|
|
ri,
|
|
|
|
radius, planetMat, viewFrustum,
|
|
|
|
*context);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
renderEclipseShadows(model,
|
2004-10-19 11:52:43 -06:00
|
|
|
*ls.shadows[0],
|
2004-03-12 01:31:10 -07:00
|
|
|
ri,
|
|
|
|
radius, planetMat, viewFrustum,
|
|
|
|
*context);
|
|
|
|
}
|
2002-02-17 21:00:07 -07:00
|
|
|
}
|
|
|
|
|
2002-09-28 03:21:26 -06:00
|
|
|
if (obj.rings != NULL &&
|
|
|
|
(obj.surface->appearanceFlags & Surface::Emissive) == 0 &&
|
|
|
|
(renderFlags & ShowRingShadows) != 0)
|
2002-09-22 19:59:15 -06:00
|
|
|
{
|
|
|
|
Texture* ringsTex = obj.rings->texture.find(textureResolution);
|
|
|
|
if (ringsTex != NULL)
|
|
|
|
{
|
|
|
|
Vec3f sunDir = pos - Point3f(0, 0, 0);
|
|
|
|
sunDir.normalize();
|
|
|
|
|
|
|
|
ringsTex->bind();
|
|
|
|
|
2003-02-19 10:48:25 -07:00
|
|
|
if (useClampToBorder &&
|
2004-10-16 19:43:49 -06:00
|
|
|
context->getVertexPath() != GLContext::VPath_Basic &&
|
|
|
|
context->getRenderPath() != GLContext::GLPath_GLSL)
|
2002-09-22 19:59:15 -06:00
|
|
|
{
|
2004-02-17 02:58:52 -07:00
|
|
|
renderRingShadowsVS(model,
|
2002-09-22 19:59:15 -06:00
|
|
|
*obj.rings,
|
|
|
|
sunDir,
|
|
|
|
ri,
|
2004-10-07 11:52:22 -06:00
|
|
|
radius, 1.0f - obj.semiAxes.y,
|
2003-02-19 10:48:25 -07:00
|
|
|
planetMat, viewFrustum,
|
|
|
|
*context);
|
2002-09-22 19:59:15 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-26 17:25:39 -07:00
|
|
|
if (obj.rings != NULL && distance > obj.rings->innerRadius)
|
2002-02-08 14:55:26 -07:00
|
|
|
{
|
2004-07-28 10:53:48 -06:00
|
|
|
glDepthMask(GL_FALSE);
|
2004-10-23 03:30:14 -06:00
|
|
|
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);
|
|
|
|
}
|
2002-02-08 14:55:26 -07:00
|
|
|
}
|
|
|
|
|
2004-10-05 03:27:09 -06:00
|
|
|
// Disable all light sources other than the first
|
|
|
|
for (i = 0; i < ls.nLights; i++)
|
|
|
|
glDisable(GL_LIGHT0 + i);
|
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
glPopMatrix();
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-19 11:52:43 -06:00
|
|
|
bool Renderer::testEclipse(const Body& receiver,
|
|
|
|
const Body& caster,
|
|
|
|
const DirectionalLight& light,
|
|
|
|
double now,
|
|
|
|
vector<EclipseShadow>& shadows)
|
2002-02-17 21:00:07 -07:00
|
|
|
{
|
2004-10-19 11:52:43 -06:00
|
|
|
|
2002-02-17 21:00:07 -07:00
|
|
|
// 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.
|
2006-09-01 05:14:37 -06:00
|
|
|
if (caster.getRadius() >= receiver.getRadius() * MinRelativeOccluderRadius &&
|
2003-04-02 02:34:15 -07:00
|
|
|
caster.getClassification() != Body::Invisible &&
|
2004-07-26 11:10:50 -06:00
|
|
|
caster.extant(now) &&
|
2004-02-17 02:58:52 -07:00
|
|
|
caster.getModel() == InvalidResource)
|
2002-02-17 21:00:07 -07:00
|
|
|
{
|
|
|
|
// 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
|
2006-07-26 11:21:23 -06:00
|
|
|
// approximation works everywhere in the solar system, and is likely
|
|
|
|
// valid for any orbitally stable pair of objects orbiting a star.
|
2002-02-17 21:00:07 -07:00
|
|
|
Point3d posReceiver = receiver.getHeliocentricPosition(now);
|
|
|
|
Point3d posCaster = caster.getHeliocentricPosition(now);
|
|
|
|
|
2004-10-19 11:52:43 -06:00
|
|
|
//const Star* sun = receiver.getSystem()->getStar();
|
|
|
|
//assert(sun != NULL);
|
|
|
|
//double distToSun = posReceiver.distanceFromOrigin();
|
|
|
|
//float appSunRadius = (float) (sun->getRadius() / distToSun);
|
|
|
|
float appSunRadius = light.apparentSize;
|
2002-02-18 15:57:22 -07:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2002-02-17 21:00:07 -07:00
|
|
|
// 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
|
2006-07-26 11:21:23 -06:00
|
|
|
// radii, then we have an eclipse. We also need to verify that the
|
|
|
|
// receiver is behind the caster when seen from the light source.
|
2002-02-18 15:57:22 -07:00
|
|
|
float R = receiver.getRadius() + shadowRadius;
|
2006-07-26 11:21:23 -06:00
|
|
|
Vec3d lightToCasterDir = posCaster - light.position;
|
|
|
|
Vec3d receiverToCasterDir = posReceiver - posCaster;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-02-17 21:00:07 -07:00
|
|
|
double dist = distance(posReceiver,
|
2006-07-26 11:21:23 -06:00
|
|
|
Ray3d(posCaster, lightToCasterDir));
|
|
|
|
if (dist < R && lightToCasterDir * receiverToCasterDir > 0.0)
|
2002-02-17 21:00:07 -07:00
|
|
|
{
|
2004-10-19 11:52:43 -06:00
|
|
|
Vec3d sunDir = posCaster - light.position;
|
2002-02-17 21:00:07 -07:00
|
|
|
sunDir.normalize();
|
|
|
|
|
2004-10-19 11:52:43 -06:00
|
|
|
EclipseShadow shadow;
|
2002-02-17 21:00:07 -07:00
|
|
|
shadow.origin = Point3f((float) dir.x,
|
|
|
|
(float) dir.y,
|
|
|
|
(float) dir.z);
|
|
|
|
shadow.direction = Vec3f((float) sunDir.x,
|
|
|
|
(float) sunDir.y,
|
|
|
|
(float) sunDir.z);
|
2002-02-18 15:57:22 -07:00
|
|
|
shadow.penumbraRadius = shadowRadius;
|
|
|
|
shadow.umbraRadius = caster.getRadius() *
|
2002-02-17 21:00:07 -07:00
|
|
|
(appOccluderRadius - appSunRadius) / appOccluderRadius;
|
2004-10-19 11:52:43 -06:00
|
|
|
shadows.push_back(shadow);
|
2002-02-17 21:00:07 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-06-24 00:31:19 -06:00
|
|
|
void Renderer::renderPlanet(Body& body,
|
2002-02-08 14:55:26 -07:00
|
|
|
Point3f pos,
|
|
|
|
float distance,
|
|
|
|
float appMag,
|
2006-12-08 12:02:20 -07:00
|
|
|
const Observer& observer,
|
|
|
|
const Quatf& cameraOrientation,
|
2004-11-11 02:41:51 -07:00
|
|
|
const vector<LightSource>& lightSources,
|
2002-02-08 14:55:26 -07:00
|
|
|
float nearPlaneDistance,
|
|
|
|
float farPlaneDistance)
|
|
|
|
{
|
2006-12-08 12:02:20 -07:00
|
|
|
double now = observer.getTime();
|
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
float altitude = distance - body.getRadius();
|
|
|
|
float discSizeInPixels = body.getRadius() /
|
|
|
|
(max(nearPlaneDistance, altitude) * pixelSize);
|
|
|
|
|
|
|
|
if (discSizeInPixels > 1)
|
|
|
|
{
|
|
|
|
RenderProperties rp;
|
|
|
|
|
2003-06-08 21:57:43 -06:00
|
|
|
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());
|
|
|
|
}
|
2002-02-08 14:55:26 -07:00
|
|
|
rp.atmosphere = body.getAtmosphere();
|
|
|
|
rp.rings = body.getRings();
|
|
|
|
rp.radius = body.getRadius();
|
2004-10-07 11:52:22 -06:00
|
|
|
rp.semiAxes = Vec3f(1.0f, 1.0f - body.getOblateness(), 1.0f);
|
2004-02-17 02:58:52 -07:00
|
|
|
rp.model = body.getModel();
|
2002-02-08 14:55:26 -07:00
|
|
|
|
|
|
|
// Compute the orientation of the planet before axial rotation
|
2006-10-18 02:34:27 -06:00
|
|
|
Quatd q = body.getRotationModel()->spin(now) *
|
|
|
|
body.getEclipticalToEquatorial(now);
|
2006-11-27 12:11:55 -07:00
|
|
|
|
2004-10-07 11:52:22 -06:00
|
|
|
rp.orientation = body.getOrientation() *
|
|
|
|
Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
|
2002-05-14 11:02:04 -06:00
|
|
|
|
2003-06-17 11:40:49 -06:00
|
|
|
rp.locations = body.getLocations();
|
2003-06-24 00:31:19 -06:00
|
|
|
if (rp.locations != NULL && (labelMode & LocationLabels) != 0)
|
|
|
|
body.computeLocations();
|
2003-06-17 11:40:49 -06:00
|
|
|
|
2004-10-19 11:52:43 -06:00
|
|
|
LightingState lights;
|
|
|
|
setupObjectLighting(lightSources,
|
|
|
|
body.getHeliocentricPosition(now),
|
|
|
|
rp.orientation,
|
2006-07-08 20:03:29 -06:00
|
|
|
rp.semiAxes * rp.radius,
|
2004-10-23 03:30:14 -06:00
|
|
|
pos,
|
2004-10-19 11:52:43 -06:00
|
|
|
lights);
|
|
|
|
assert(lights.nLights < MaxLights);
|
|
|
|
|
2006-07-03 23:03:27 -06:00
|
|
|
lights.ambientColor = Vec3f(ambientColor.red(),
|
|
|
|
ambientColor.green(),
|
|
|
|
ambientColor.blue());
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2004-10-19 11:52:43 -06:00
|
|
|
{
|
|
|
|
// Clear out the list of eclipse shadows
|
|
|
|
for (unsigned int li = 0; li < lights.nLights; li++)
|
|
|
|
{
|
|
|
|
eclipseShadows[li].clear();
|
|
|
|
lights.shadows[li] = &eclipseShadows[li];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-02-17 21:00:07 -07:00
|
|
|
// Calculate eclipse circumstances
|
2003-04-02 02:34:15 -07:00
|
|
|
if ((renderFlags & ShowEclipseShadows) != 0 &&
|
2004-10-19 11:52:43 -06:00
|
|
|
body.getClassification() != Body::Invisible &&
|
|
|
|
body.getSystem() != NULL)
|
2002-02-17 21:00:07 -07:00
|
|
|
{
|
|
|
|
PlanetarySystem* system = body.getSystem();
|
|
|
|
|
2004-10-19 11:52:43 -06:00
|
|
|
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)
|
2002-02-17 21:00:07 -07:00
|
|
|
{
|
2004-10-19 11:52:43 -06:00
|
|
|
int nSatellites = satellites->getSystemSize();
|
|
|
|
for (unsigned int li = 0; li < lights.nLights; li++)
|
2002-02-17 21:00:07 -07:00
|
|
|
{
|
|
|
|
for (int i = 0; i < nSatellites; i++)
|
2004-10-19 11:52:43 -06:00
|
|
|
testEclipse(body, *satellites->getBody(i),
|
|
|
|
lights.lights[li],
|
|
|
|
now, *lights.shadows[li]);
|
2002-02-17 21:00:07 -07:00
|
|
|
}
|
|
|
|
}
|
2004-10-19 11:52:43 -06:00
|
|
|
}
|
|
|
|
else if (system->getPrimaryBody() != NULL)
|
|
|
|
{
|
|
|
|
for (unsigned int li = 0; li < lights.nLights; li++)
|
2002-02-17 21:00:07 -07:00
|
|
|
{
|
|
|
|
// The body is a moon. Check for eclipse shadows from
|
|
|
|
// the parent planet and all satellites in the system.
|
|
|
|
Body* planet = system->getPrimaryBody();
|
2004-10-19 11:52:43 -06:00
|
|
|
testEclipse(body, *planet, lights.lights[li],
|
|
|
|
now, *lights.shadows[li]);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-02-17 21:00:07 -07:00
|
|
|
int nSatellites = system->getSystemSize();
|
|
|
|
for (int i = 0; i < nSatellites; i++)
|
|
|
|
{
|
|
|
|
if (system->getBody(i) != &body)
|
2004-10-19 11:52:43 -06:00
|
|
|
{
|
|
|
|
testEclipse(body, *system->getBody(i),
|
|
|
|
lights.lights[li],
|
|
|
|
now, *lights.shadows[li]);
|
|
|
|
}
|
2002-02-17 21:00:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
renderObject(pos, distance, now,
|
2006-12-08 12:02:20 -07:00
|
|
|
cameraOrientation, nearPlaneDistance, farPlaneDistance,
|
2004-10-04 00:35:33 -06:00
|
|
|
rp, lights);
|
2004-08-25 11:19:44 -06:00
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
if (body.getLocations() != NULL && (labelMode & LocationLabels) != 0)
|
2004-08-25 11:19:44 -06:00
|
|
|
{
|
2006-12-08 12:02:20 -07:00
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
glDisable(GL_BLEND);
|
2004-08-26 23:39:52 -06:00
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
// We need a double precision body-relative position of the
|
|
|
|
// observer, otherwise location labels will tend to jitter.
|
2007-01-05 17:52:32 -07:00
|
|
|
Vec3d posd = (Selection(&body).getPosition(observer.getTime()) -
|
2006-12-08 12:02:20 -07:00
|
|
|
observer.getPosition()) * astro::microLightYearsToKilometers(1.0);
|
|
|
|
renderLocations(*body.getLocations(),
|
|
|
|
cameraOrientation,
|
|
|
|
Point3d(posd.x, posd.y, posd.z),
|
|
|
|
q,
|
|
|
|
rp.radius);
|
2004-08-25 11:19:44 -06:00
|
|
|
|
2006-12-08 12:02:20 -07:00
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
glEnable(GL_BLEND);
|
2004-08-25 11:19:44 -06:00
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2002-09-02 15:13:10 -06:00
|
|
|
glEnable(GL_TEXTURE_2D);
|
2003-07-08 09:31:34 -06:00
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
2006-03-23 00:31:25 -07:00
|
|
|
|
|
|
|
if (useNewStarRendering)
|
|
|
|
{
|
|
|
|
renderObjectAsPoint(pos,
|
2006-12-20 11:56:43 -07:00
|
|
|
body.getRadius(),
|
2006-11-05 01:13:34 -07:00
|
|
|
appMag,
|
|
|
|
faintestPlanetMag,
|
|
|
|
discSizeInPixels,
|
|
|
|
body.getSurface().color,
|
2006-12-08 12:02:20 -07:00
|
|
|
cameraOrientation,
|
2006-12-20 11:56:43 -07:00
|
|
|
false, false);
|
2006-03-23 00:31:25 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
renderBodyAsParticle(pos,
|
|
|
|
appMag,
|
|
|
|
faintestPlanetMag,
|
|
|
|
discSizeInPixels,
|
|
|
|
body.getSurface().color,
|
2006-12-08 12:02:20 -07:00
|
|
|
cameraOrientation,
|
2006-03-23 00:31:25 -07:00
|
|
|
(nearPlaneDistance + farPlaneDistance) / 2.0f,
|
2006-09-12 06:56:47 -06:00
|
|
|
false);
|
2006-03-23 00:31:25 -07:00
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::renderStar(const Star& star,
|
|
|
|
Point3f pos,
|
|
|
|
float distance,
|
|
|
|
float appMag,
|
2006-12-20 11:56:43 -07:00
|
|
|
Quatf cameraOrientation,
|
2001-11-27 18:50:04 -07:00
|
|
|
double now,
|
|
|
|
float nearPlaneDistance,
|
|
|
|
float farPlaneDistance)
|
|
|
|
{
|
2004-10-02 11:21:45 -06:00
|
|
|
if (!star.getVisibility())
|
|
|
|
return;
|
|
|
|
|
2004-09-19 21:27:50 -06:00
|
|
|
Color color = colorTemp->lookupColor(star.getTemperature());
|
2001-11-27 18:50:04 -07:00
|
|
|
float radius = star.getRadius();
|
|
|
|
float discSizeInPixels = radius / (distance * pixelSize);
|
|
|
|
|
|
|
|
if (discSizeInPixels > 1)
|
|
|
|
{
|
2002-02-18 01:19:23 -07:00
|
|
|
Surface surface;
|
2002-02-08 14:55:26 -07:00
|
|
|
RenderProperties rp;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2002-02-18 01:19:23 -07:00
|
|
|
surface.color = color;
|
2004-10-09 20:07:51 -06:00
|
|
|
|
2004-09-21 11:37:17 -06:00
|
|
|
MultiResTexture mtex = star.getTexture();
|
|
|
|
if (mtex.tex[textureResolution] != InvalidResource)
|
|
|
|
{
|
|
|
|
surface.baseTexture = mtex;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-10-09 20:07:51 -06:00
|
|
|
surface.baseTexture = InvalidResource;
|
2004-09-21 11:37:17 -06:00
|
|
|
}
|
2002-02-18 01:19:23 -07:00
|
|
|
surface.appearanceFlags |= Surface::ApplyBaseTexture;
|
|
|
|
surface.appearanceFlags |= Surface::Emissive;
|
2002-02-08 14:55:26 -07:00
|
|
|
|
2002-02-18 01:19:23 -07:00
|
|
|
rp.surface = &surface;
|
2002-02-08 14:55:26 -07:00
|
|
|
rp.rings = NULL;
|
|
|
|
rp.radius = star.getRadius();
|
2004-10-07 11:52:22 -06:00
|
|
|
rp.semiAxes = star.getEllipsoidSemiAxes();
|
2004-09-21 11:37:17 -06:00
|
|
|
rp.model = star.getModel();
|
2002-02-08 14:55:26 -07:00
|
|
|
|
2004-09-23 00:54:28 -06:00
|
|
|
Atmosphere atmosphere;
|
2006-12-20 11:56:43 -07:00
|
|
|
Color atmColor(color.red() * 0.5f, color.green() * 0.5f, color.blue() * 0.5f);
|
2004-09-23 00:54:28 -06:00
|
|
|
atmosphere.height = radius * CoronaHeight;
|
2006-12-20 11:56:43 -07:00
|
|
|
atmosphere.lowerColor = atmColor;
|
|
|
|
atmosphere.upperColor = atmColor;
|
|
|
|
atmosphere.skyColor = atmColor;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// Atmosphere effect may be use to give stars a fuzzy fringe,
|
|
|
|
// but this doesn't seem to be an improvement.
|
2004-09-23 00:54:28 -06:00
|
|
|
if (rp.model == InvalidResource)
|
|
|
|
rp.atmosphere = &atmosphere;
|
|
|
|
else
|
|
|
|
rp.atmosphere = NULL;
|
2006-12-20 11:56:43 -07:00
|
|
|
#endif
|
2004-09-23 00:54:28 -06:00
|
|
|
|
2006-10-21 14:21:12 -06:00
|
|
|
Quatd q = star.getRotationModel()->orientationAtTime(now);
|
2004-10-07 11:52:22 -06:00
|
|
|
rp.orientation = Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
|
2002-02-08 14:55:26 -07:00
|
|
|
|
|
|
|
renderObject(pos, distance, now,
|
2006-12-20 11:56:43 -07:00
|
|
|
cameraOrientation, nearPlaneDistance, farPlaneDistance,
|
2004-10-04 00:35:33 -06:00
|
|
|
rp, LightingState());
|
2002-02-18 01:19:23 -07:00
|
|
|
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2002-02-08 14:55:26 -07:00
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
2006-12-20 11:56:43 -07:00
|
|
|
|
|
|
|
if (useNewStarRendering)
|
|
|
|
{
|
|
|
|
renderObjectAsPoint(pos,
|
|
|
|
star.getRadius(),
|
|
|
|
appMag,
|
|
|
|
faintestMag,
|
|
|
|
discSizeInPixels,
|
|
|
|
color,
|
|
|
|
cameraOrientation,
|
|
|
|
true, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
renderBodyAsParticle(pos,
|
|
|
|
appMag,
|
|
|
|
faintestPlanetMag,
|
|
|
|
discSizeInPixels,
|
|
|
|
color,
|
|
|
|
cameraOrientation,
|
|
|
|
(nearPlaneDistance + farPlaneDistance) / 2.0f,
|
|
|
|
true);
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-07-25 23:41:25 -06:00
|
|
|
static const int MaxCometTailPoints = 20;
|
2002-09-02 15:13:10 -06:00
|
|
|
static const int CometTailSlices = 16;
|
|
|
|
struct CometTailVertex
|
|
|
|
{
|
|
|
|
Point3f point;
|
|
|
|
Vec3f normal;
|
2002-10-10 11:09:46 -06:00
|
|
|
Point3f paraboloidPoint;
|
2002-09-02 15:13:10 -06:00
|
|
|
float brightness;
|
|
|
|
};
|
|
|
|
|
|
|
|
static CometTailVertex cometTailVertices[CometTailSlices * MaxCometTailPoints];
|
|
|
|
|
|
|
|
static void ProcessCometTailVertex(const CometTailVertex& v,
|
2005-12-11 08:34:35 -07:00
|
|
|
const Vec3f& viewDir,
|
|
|
|
float fadeDistFromSun)
|
2002-09-02 15:13:10 -06:00
|
|
|
{
|
2005-12-11 08:34:35 -07:00
|
|
|
// If fadeDistFromSun = x/x0 >= 1.0, comet tail starts fading,
|
|
|
|
// i.e. fadeFactor quickly transits from 1 to 0.
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
float fadeFactor = 0.5f - 0.5f * (float) tanh(fadeDistFromSun - 1.0f / fadeDistFromSun);
|
2005-12-11 08:34:35 -07:00
|
|
|
float shade = abs(viewDir * v.normal * v.brightness * fadeFactor);
|
|
|
|
glColor4f(0.5f, 0.5f, 0.75f, shade);
|
2002-09-02 15:13:10 -06:00
|
|
|
glVertex(v.point);
|
|
|
|
}
|
|
|
|
|
2006-09-12 15:59:05 -06:00
|
|
|
#if 0
|
2002-09-13 01:35:50 -06:00
|
|
|
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);
|
|
|
|
}
|
2006-09-12 15:59:05 -06:00
|
|
|
#endif
|
2002-09-13 01:35:50 -06:00
|
|
|
|
2006-09-12 15:59:05 -06:00
|
|
|
#if 0
|
2002-10-04 11:04:40 -06:00
|
|
|
static void ProcessCometTailVertex(const CometTailVertex& v,
|
|
|
|
const Point3f& eyePos_obj,
|
|
|
|
float b,
|
|
|
|
float h)
|
|
|
|
{
|
|
|
|
float shade = 0.0f;
|
2002-10-10 11:09:46 -06:00
|
|
|
Vec3f R = v.paraboloidPoint - eyePos_obj;
|
2002-10-04 11:04:40 -06:00
|
|
|
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;
|
2006-09-12 06:56:47 -06:00
|
|
|
/*float u0 = max(t0, 0.0f); Unused*/
|
2002-10-04 11:04:40 -06:00
|
|
|
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);
|
2002-10-10 11:09:46 -06:00
|
|
|
|
|
|
|
shade = u2 - u1;
|
2002-10-04 11:04:40 -06:00
|
|
|
}
|
2002-10-10 11:09:46 -06:00
|
|
|
|
|
|
|
glColor4f(0.0f, 0.5f, 1.0f, shade);
|
|
|
|
glVertex(v.point);
|
2002-10-04 11:04:40 -06:00
|
|
|
}
|
2006-09-12 15:59:05 -06:00
|
|
|
#endif
|
2002-10-04 11:04:40 -06:00
|
|
|
|
2006-11-08 10:55:08 -07:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-16 17:02:55 -06:00
|
|
|
// TODO: Remove unused parameters??
|
2002-06-16 20:41:11 -06:00
|
|
|
void Renderer::renderCometTail(const Body& body,
|
|
|
|
Point3f pos,
|
2006-09-16 17:02:55 -06:00
|
|
|
float /*distance*/,
|
|
|
|
float /*appMag*/,
|
2002-06-16 20:41:11 -06:00
|
|
|
double now,
|
2006-09-16 17:02:55 -06:00
|
|
|
Quatf /*orientation*/,
|
2005-12-11 08:34:35 -07:00
|
|
|
const vector<LightSource>& lightSources,
|
2006-09-16 17:02:55 -06:00
|
|
|
float /*nearPlaneDistance*/,
|
|
|
|
float /*farPlaneDistance*/)
|
2002-06-16 20:41:11 -06:00
|
|
|
{
|
2002-07-25 23:41:25 -06:00
|
|
|
Point3f cometPoints[MaxCometTailPoints];
|
|
|
|
Point3d pos0 = body.getOrbit()->positionAtTime(now);
|
|
|
|
Point3d pos1 = body.getOrbit()->positionAtTime(now - 0.01);
|
|
|
|
Vec3d vd = pos1 - pos0;
|
|
|
|
double t = now;
|
2006-09-12 06:56:47 -06:00
|
|
|
/*float f = 1.0e15f; Unused*/
|
|
|
|
/*int nSteps = MaxCometTailPoints; Unused*/
|
|
|
|
/*float dt = 10000000.0f / (nSteps * (float) vd.length() * 100.0f); Unused*/
|
2005-12-11 08:34:35 -07:00
|
|
|
float distanceFromSun, irradiance_max = 0.0f;
|
2006-09-15 12:42:37 -06:00
|
|
|
unsigned int li_eff = 0; // Select the first sun as default to
|
|
|
|
// shut up compiler warnings
|
2002-07-25 23:41:25 -06:00
|
|
|
|
2005-12-11 08:34:35 -07:00
|
|
|
// 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);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2005-12-11 08:34:35 -07:00
|
|
|
for (unsigned int li = 0; li < lightSources.size(); li++)
|
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
distanceFromSun = (float) (body.getHeliocentricPosition(now) -
|
2005-12-11 08:34:35 -07:00
|
|
|
lightSources[li].position).length();
|
|
|
|
float irradiance = lightSources[li].luminosity / square(distanceFromSun);
|
|
|
|
if (irradiance > irradiance_max )
|
|
|
|
{
|
|
|
|
li_eff = li;
|
|
|
|
irradiance_max = irradiance;
|
|
|
|
}
|
|
|
|
}
|
2006-03-08 11:29:10 -07:00
|
|
|
float fadeDistance = 1.0f / (float) (COMET_TAIL_ATTEN_DIST_SOL * sqrt(irradiance_max));
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2005-12-11 08:34:35 -07:00
|
|
|
// direction to sun with dominant light irradiance:
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2005-12-11 08:34:35 -07:00
|
|
|
Vec3d sd = body.getHeliocentricPosition(now) - lightSources[li_eff].position;
|
|
|
|
Vec3f sunDir = Vec3f((float) sd.x, (float) sd.y, (float) sd.z);
|
|
|
|
sunDir.normalize();
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-08-22 02:08:23 -06:00
|
|
|
int i;
|
2002-09-02 15:13:10 -06:00
|
|
|
#if 0
|
2002-08-22 02:08:23 -06:00
|
|
|
for (i = 0; i < MaxCometTailPoints; i++)
|
2002-07-25 23:41:25 -06:00
|
|
|
{
|
|
|
|
Vec3d pd = body.getOrbit()->positionAtTime(t) - pos0;
|
|
|
|
Point3f p = Point3f((float) pd.x, (float) pd.y, (float) pd.z);
|
|
|
|
Vec3f r(p.x + (float) pos0.x,
|
|
|
|
p.y + (float) pos0.y,
|
|
|
|
p.z + (float) pos0.z);
|
|
|
|
float distance = r.length();
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-07-25 23:41:25 -06:00
|
|
|
Vec3f a = r * ((1 / square(distance)) * f * dt);
|
2002-09-02 15:13:10 -06:00
|
|
|
Vec3f dx = a * (square(i * dt) * 0.5f);
|
2002-07-25 23:41:25 -06:00
|
|
|
|
|
|
|
cometPoints[i] = p + dx;
|
|
|
|
|
|
|
|
t -= dt;
|
|
|
|
}
|
2002-09-02 15:13:10 -06:00
|
|
|
#endif
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-08 10:55:08 -07:00
|
|
|
float dustTailLength = cometDustTailLength((float) pos0.distanceFromOrigin(), body.getRadius());
|
2002-09-02 15:13:10 -06:00
|
|
|
float dustTailRadius = dustTailLength * 0.1f;
|
2006-09-12 06:56:47 -06:00
|
|
|
/*float comaRadius = dustTailRadius * 0.5f; Unused*/
|
2002-09-02 15:13:10 -06:00
|
|
|
|
2002-09-22 19:59:15 -06:00
|
|
|
Point3f origin(0, 0, 0);
|
2005-12-11 08:34:35 -07:00
|
|
|
origin -= sunDir * (body.getRadius() * 100);
|
2002-09-02 15:13:10 -06:00
|
|
|
for (i = 0; i < MaxCometTailPoints; i++)
|
|
|
|
{
|
|
|
|
float alpha = (float) i / (float) MaxCometTailPoints;
|
|
|
|
alpha = alpha * alpha;
|
2002-09-22 19:59:15 -06:00
|
|
|
cometPoints[i] = origin + sunDir * (dustTailLength * alpha);
|
2002-09-02 15:13:10 -06:00
|
|
|
}
|
2002-07-25 23:41:25 -06:00
|
|
|
|
|
|
|
// 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.getEclipticalToEquatorial(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;
|
|
|
|
|
2002-09-02 15:13:10 -06:00
|
|
|
glColor4f(0.0f, 1.0f, 1.0f, 0.5f);
|
|
|
|
glPushMatrix();
|
|
|
|
glTranslate(pos);
|
|
|
|
|
2002-11-23 11:25:28 -07:00
|
|
|
// glx::glActiveTextureARB(GL_TEXTURE0_ARB);
|
2002-07-25 23:41:25 -06:00
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
glDisable(GL_LIGHTING);
|
2002-09-02 15:13:10 -06:00
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
2002-07-25 23:41:25 -06:00
|
|
|
|
2002-08-22 02:08:23 -06:00
|
|
|
for (i = 0; i < MaxCometTailPoints; i++)
|
2002-07-25 23:41:25 -06:00
|
|
|
{
|
2002-09-02 15:13:10 -06:00
|
|
|
float brightness = 1.0f - (float) i / (float) (MaxCometTailPoints - 1);
|
|
|
|
Vec3f v0, v1;
|
|
|
|
float sectionLength;
|
2002-07-25 23:41:25 -06:00
|
|
|
if (i != 0 && i != MaxCometTailPoints - 1)
|
|
|
|
{
|
2002-09-02 15:13:10 -06:00
|
|
|
v0 = cometPoints[i] - cometPoints[i - 1];
|
|
|
|
v1 = cometPoints[i + 1] - cometPoints[i];
|
|
|
|
sectionLength = v0.length();
|
2002-07-25 23:41:25 -06:00
|
|
|
v0.normalize();
|
|
|
|
v1.normalize();
|
|
|
|
Vec3f axis = v1 ^ v0;
|
|
|
|
float axisLength = axis.length();
|
|
|
|
if (axisLength > 1e-5f)
|
|
|
|
{
|
|
|
|
axis *= 1.0f / axisLength;
|
2004-02-28 02:30:17 -07:00
|
|
|
q.setAxisAngle(axis, (float) asin(axisLength));
|
2002-07-25 23:41:25 -06:00
|
|
|
Mat3f m = q.toMatrix3();
|
2002-10-10 11:09:46 -06:00
|
|
|
|
2002-07-25 23:41:25 -06:00
|
|
|
u = u * m;
|
|
|
|
v = v * m;
|
|
|
|
w = w * m;
|
|
|
|
}
|
|
|
|
}
|
2002-09-02 15:13:10 -06:00
|
|
|
else if (i == 0)
|
2002-07-25 23:41:25 -06:00
|
|
|
{
|
2002-09-02 15:13:10 -06:00
|
|
|
v0 = cometPoints[i + 1] - cometPoints[i];
|
|
|
|
sectionLength = v0.length();
|
|
|
|
v0.normalize();
|
|
|
|
v1 = v0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
v0 = v1 = cometPoints[i] - cometPoints[i - 1];
|
|
|
|
sectionLength = v0.length();
|
|
|
|
v0.normalize();
|
|
|
|
v1 = v0;
|
2002-07-25 23:41:25 -06:00
|
|
|
}
|
|
|
|
|
2002-09-02 15:13:10 -06:00
|
|
|
float radius = (float) i / (float) MaxCometTailPoints *
|
|
|
|
dustTailRadius;
|
|
|
|
float dr = (dustTailRadius / (float) MaxCometTailPoints) /
|
|
|
|
sectionLength;
|
|
|
|
float w0 = (float) atan(dr);
|
|
|
|
float w1 = (float) sqrt(1.0f - square(w0));
|
2002-07-25 23:41:25 -06:00
|
|
|
|
2002-09-02 15:13:10 -06:00
|
|
|
for (int j = 0; j < CometTailSlices; j++)
|
2002-07-25 23:41:25 -06:00
|
|
|
{
|
2004-02-28 02:30:17 -07:00
|
|
|
float theta = (float) (2 * PI * (float) j / CometTailSlices);
|
2002-09-02 15:13:10 -06:00
|
|
|
float s = (float) sin(theta);
|
|
|
|
float c = (float) cos(theta);
|
|
|
|
CometTailVertex* vtx = &cometTailVertices[i * CometTailSlices + j];
|
|
|
|
vtx->normal = u * (s * w1) + w * (c * w1) + v * w0;
|
|
|
|
s *= radius;
|
|
|
|
c *= radius;
|
2002-10-10 11:09:46 -06:00
|
|
|
|
2002-09-02 15:13:10 -06:00
|
|
|
vtx->point = Point3f(cometPoints[i].x + u.x * s + w.x * c,
|
|
|
|
cometPoints[i].y + u.y * s + w.y * c,
|
|
|
|
cometPoints[i].z + u.z * s + w.z * c);
|
|
|
|
vtx->brightness = brightness;
|
2002-10-10 11:09:46 -06:00
|
|
|
vtx->paraboloidPoint =
|
|
|
|
Point3f(c, s, square((float) i / (float) MaxCometTailPoints));
|
2002-07-25 23:41:25 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-09-02 15:13:10 -06:00
|
|
|
Vec3f viewDir = pos - Point3f(0.0f, 0.0f, 0.0f);
|
|
|
|
viewDir.normalize();
|
|
|
|
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
for (i = 0; i < MaxCometTailPoints - 1; i++)
|
|
|
|
{
|
|
|
|
glBegin(GL_QUAD_STRIP);
|
|
|
|
int n = i * CometTailSlices;
|
|
|
|
for (int j = 0; j < CometTailSlices; j++)
|
2002-07-25 23:41:25 -06:00
|
|
|
{
|
2005-12-11 08:34:35 -07:00
|
|
|
ProcessCometTailVertex(cometTailVertices[n + j], viewDir, fadeDistance);
|
2002-09-02 15:13:10 -06:00
|
|
|
ProcessCometTailVertex(cometTailVertices[n + j + CometTailSlices],
|
2005-12-11 08:34:35 -07:00
|
|
|
viewDir, fadeDistance);
|
2002-07-25 23:41:25 -06:00
|
|
|
}
|
2005-12-11 08:34:35 -07:00
|
|
|
ProcessCometTailVertex(cometTailVertices[n], viewDir, fadeDistance);
|
2002-09-02 15:13:10 -06:00
|
|
|
ProcessCometTailVertex(cometTailVertices[n + CometTailSlices],
|
2005-12-11 08:34:35 -07:00
|
|
|
viewDir, fadeDistance);
|
2002-07-25 23:41:25 -06:00
|
|
|
glEnd();
|
|
|
|
}
|
2002-09-02 15:13:10 -06:00
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
|
|
|
|
glBegin(GL_LINE_STRIP);
|
|
|
|
for (i = 0; i < MaxCometTailPoints; i++)
|
|
|
|
{
|
|
|
|
glVertex(cometPoints[i]);
|
|
|
|
}
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
glDepthMask(GL_TRUE);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glEnable(GL_BLEND);
|
2002-07-25 23:41:25 -06:00
|
|
|
|
|
|
|
glPopMatrix();
|
2002-06-16 20:41:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
// Add solar system bodies, orbits, and labels to the render list. Stars
|
|
|
|
// are handled by other methods.
|
|
|
|
void Renderer::buildRenderLists(const Star& sun,
|
|
|
|
const PlanetarySystem* solSystem,
|
|
|
|
const Observer& observer,
|
|
|
|
double now,
|
|
|
|
unsigned int solarSysIndex,
|
|
|
|
bool showLabels)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
Point3f starPos = sun.getPosition();
|
2004-10-02 04:44:49 -06:00
|
|
|
Point3d observerPos = astrocentricPosition(observer.getPosition(),
|
|
|
|
sun, now);
|
2006-09-07 02:08:32 -06:00
|
|
|
Mat3f viewMat = observer.getOrientation().toMatrix3();
|
|
|
|
Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
int nBodies = solSystem != NULL ? solSystem->getSystemSize() : 0;
|
2001-11-27 18:50:04 -07:00
|
|
|
for (int i = 0; i < nBodies; i++)
|
|
|
|
{
|
2006-09-07 23:59:38 -06:00
|
|
|
Body* body = solSystem->getBody(i);
|
2002-10-05 03:22:05 -06:00
|
|
|
if (!body->extant(now))
|
|
|
|
continue;
|
|
|
|
|
2002-11-04 00:54:56 -07:00
|
|
|
Point3d bodyPos = body->getHeliocentricPosition(now);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
// We now have the positions of the observer and the planet relative
|
|
|
|
// to the sun. From these, compute the position of the planet
|
|
|
|
// relative to the observer.
|
|
|
|
Vec3d posd = bodyPos - observerPos;
|
|
|
|
|
2006-12-20 12:34:47 -07:00
|
|
|
// Compute the size of the planet/moon disc in pixels
|
|
|
|
double distanceFromObserver = posd.length();
|
|
|
|
float discSize = (body->getRadius() / (float) distanceFromObserver) / pixelSize;
|
|
|
|
|
2004-11-11 02:41:51 -07:00
|
|
|
vector<LightSource>& lightSources = lightSourceLists[solarSysIndex];
|
|
|
|
|
2004-10-20 01:55:50 -06:00
|
|
|
// Compute the apparent magnitude; instead of summing the reflected
|
|
|
|
// light from all nearby stars, we just consider the one with the
|
|
|
|
// highest apparent brightness.
|
|
|
|
float appMag = 100.0f;
|
2006-12-20 12:34:47 -07:00
|
|
|
float oppositionMag = 100.0f;
|
2004-10-20 01:55:50 -06:00
|
|
|
for (unsigned int li = 0; li < lightSources.size(); li++)
|
|
|
|
{
|
|
|
|
Vec3d sunPos = bodyPos - lightSources[li].position;
|
|
|
|
appMag = min(appMag, body->getApparentMagnitude(lightSources[li].luminosity, sunPos, posd));
|
2006-12-20 12:34:47 -07:00
|
|
|
oppositionMag = min(oppositionMag, body->getApparentMagnitude(lightSources[li].luminosity, (float) sunPos.length(), (float) distanceFromObserver));
|
2004-10-20 01:55:50 -06:00
|
|
|
}
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
Vec3f pos((float) posd.x, (float) posd.y, (float) posd.z);
|
|
|
|
|
|
|
|
// if (discSize > 1 || appMag < 1.0f / brightnessScale)
|
2003-04-01 00:27:56 -07:00
|
|
|
if ((discSize > 1 || appMag < faintestPlanetMag) &&
|
|
|
|
body->getClassification() != Body::Invisible)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
RenderListEntry rle;
|
|
|
|
rle.body = body;
|
|
|
|
rle.star = NULL;
|
2002-06-11 23:29:34 -06:00
|
|
|
rle.isCometTail = false;
|
2006-09-10 18:37:20 -06:00
|
|
|
// Treat all mesh objects as potentially transparent.
|
|
|
|
// TODO: implement a better test for this
|
|
|
|
rle.isOpaque = body->getModel() == InvalidResource;
|
2001-11-27 18:50:04 -07:00
|
|
|
rle.position = Point3f(pos.x, pos.y, pos.z);
|
|
|
|
rle.sun = Vec3f((float) -bodyPos.x, (float) -bodyPos.y, (float) -bodyPos.z);
|
2004-02-28 02:30:17 -07:00
|
|
|
rle.distance = (float) distanceFromObserver;
|
2006-09-07 02:08:32 -06:00
|
|
|
rle.centerZ = pos * viewMatZ;
|
2001-12-06 22:13:25 -07:00
|
|
|
rle.radius = body->getRadius();
|
2001-11-27 18:50:04 -07:00
|
|
|
rle.discSizeInPixels = discSize;
|
|
|
|
rle.appMag = appMag;
|
2004-11-11 02:41:51 -07:00
|
|
|
rle.solarSysIndex = solarSysIndex;
|
2006-09-11 22:38:12 -06:00
|
|
|
renderList.push_back(rle);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
2002-09-08 21:50:37 -06:00
|
|
|
|
|
|
|
if (body->getClassification() == Body::Comet &&
|
|
|
|
(renderFlags & ShowCometTails) != 0)
|
2002-06-11 23:29:34 -06:00
|
|
|
{
|
2006-11-08 10:55:08 -07:00
|
|
|
Vec3f sunPos = Vec3f((float) -bodyPos.x, (float) -bodyPos.y, (float) -bodyPos.z);
|
|
|
|
float radius = cometDustTailLength(sunPos.length(), body->getRadius());
|
2002-06-11 23:29:34 -06:00
|
|
|
discSize = (radius / (float) distanceFromObserver) / pixelSize;
|
|
|
|
if (discSize > 1)
|
|
|
|
{
|
|
|
|
RenderListEntry rle;
|
|
|
|
rle.body = body;
|
|
|
|
rle.star = NULL;
|
|
|
|
rle.isCometTail = true;
|
2006-09-10 18:37:20 -06:00
|
|
|
rle.isOpaque = false;
|
2002-07-25 23:41:25 -06:00
|
|
|
rle.position = Point3f(pos.x, pos.y, pos.z);
|
2006-11-08 10:55:08 -07:00
|
|
|
rle.sun = sunPos;
|
2004-02-28 02:30:17 -07:00
|
|
|
rle.distance = (float) distanceFromObserver;
|
2006-09-11 22:38:12 -06:00
|
|
|
rle.centerZ = pos * viewMatZ;
|
2002-06-11 23:29:34 -06:00
|
|
|
rle.radius = radius;
|
|
|
|
rle.discSizeInPixels = discSize;
|
|
|
|
rle.appMag = appMag;
|
2004-11-11 02:41:51 -07:00
|
|
|
rle.solarSysIndex = solarSysIndex;
|
2006-09-11 22:38:12 -06:00
|
|
|
renderList.push_back(rle);
|
2002-06-11 23:29:34 -06:00
|
|
|
}
|
|
|
|
}
|
2006-11-27 12:11:55 -07:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
if (showLabels && (pos * conjugate(observer.getOrientation()).toMatrix3()).z < 0)
|
|
|
|
{
|
2002-02-20 23:39:15 -07:00
|
|
|
float boundingRadiusSize = (float) (body->getOrbit()->getBoundingRadius() / distanceFromObserver) / pixelSize;
|
2002-04-25 21:49:43 -06:00
|
|
|
if (boundingRadiusSize > minOrbitSize)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2002-03-14 14:48:51 -07:00
|
|
|
Color labelColor;
|
|
|
|
bool showLabel = false;
|
|
|
|
|
|
|
|
switch (body->getClassification())
|
|
|
|
{
|
|
|
|
case Body::Planet:
|
|
|
|
if ((labelMode & PlanetLabels) != 0)
|
|
|
|
{
|
|
|
|
labelColor = Color(0.0f, 1.0f, 0.0f);
|
|
|
|
showLabel = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Body::Moon:
|
|
|
|
if ((labelMode & MoonLabels) != 0)
|
|
|
|
{
|
|
|
|
labelColor = Color(0.0f, 0.65f, 0.0f);
|
|
|
|
showLabel = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Body::Asteroid:
|
|
|
|
if ((labelMode & AsteroidLabels) != 0)
|
|
|
|
{
|
|
|
|
labelColor = Color(0.7f, 0.4f, 0.0f);
|
|
|
|
showLabel = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Body::Comet:
|
2003-07-04 18:22:01 -06:00
|
|
|
if ((labelMode & CometLabels) != 0)
|
2002-03-14 14:48:51 -07:00
|
|
|
{
|
|
|
|
labelColor = Color(0.0f, 1.0f, 1.0f);
|
|
|
|
showLabel = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Body::Spacecraft:
|
|
|
|
if ((labelMode & SpacecraftLabels) != 0)
|
|
|
|
{
|
|
|
|
labelColor = Color(0.6f, 0.6f, 0.6f);
|
|
|
|
showLabel = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (showLabel)
|
|
|
|
{
|
2006-05-19 18:33:32 -06:00
|
|
|
addSortedLabel(body->getName(true), labelColor,
|
2004-05-18 11:31:26 -06:00
|
|
|
Point3f(pos.x, pos.y, pos.z));
|
2002-03-14 14:48:51 -07:00
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
// Only show orbits for major bodies or selected objects
|
|
|
|
if ((renderFlags & ShowOrbits) != 0 &&
|
2006-09-09 21:54:43 -06:00
|
|
|
((body->getClassification() & orbitMask) != 0 || body == highlightObject.body()))
|
2006-09-07 02:08:32 -06:00
|
|
|
{
|
|
|
|
Point3d orbitOrigin(0.0, 0.0, 0.0);
|
2006-10-31 01:23:04 -07:00
|
|
|
if (body->getOrbitFrame())
|
|
|
|
{
|
|
|
|
Body* centralBody = body->getOrbitFrame()->getCenter().body();
|
|
|
|
if (centralBody != NULL)
|
|
|
|
orbitOrigin = centralBody->getHeliocentricPosition(now);
|
|
|
|
}
|
|
|
|
else if (body->getOrbitBarycenter())
|
|
|
|
{
|
2006-09-07 02:08:32 -06:00
|
|
|
orbitOrigin = body->getOrbitBarycenter()->getHeliocentricPosition(now);
|
2006-10-31 01:23:04 -07:00
|
|
|
}
|
2006-09-07 02:08:32 -06:00
|
|
|
|
2006-09-12 06:56:47 -06:00
|
|
|
// Calculate the origin of the orbit relative to the observer
|
2006-09-07 02:08:32 -06:00
|
|
|
Vec3d relOrigin = orbitOrigin - observerPos;
|
|
|
|
Vec3f origf((float) relOrigin.x, (float) relOrigin.y, (float) relOrigin.z);
|
|
|
|
|
|
|
|
// Compute the size of the orbit in pixels
|
|
|
|
double originDistance = posd.length();
|
2006-09-12 06:56:47 -06:00
|
|
|
double boundingRadius = body->getOrbit()->getBoundingRadius();
|
2006-09-07 02:08:32 -06:00
|
|
|
float orbitRadiusInPixels = (float) (boundingRadius / (originDistance * pixelSize));
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 02:08:32 -06:00
|
|
|
if (orbitRadiusInPixels > minOrbitSize)
|
|
|
|
{
|
|
|
|
// Add the orbit of this body to the list of orbits to be rendered
|
|
|
|
OrbitPathListEntry path;
|
|
|
|
path.body = body;
|
2006-09-07 23:59:38 -06:00
|
|
|
path.star = NULL;
|
2006-09-07 02:08:32 -06:00
|
|
|
path.centerZ = origf * viewMatZ;
|
|
|
|
path.radius = (float) boundingRadius;
|
|
|
|
path.origin = Point3f(origf.x, origf.y, origf.z);
|
|
|
|
orbitPathList.push_back(path);
|
|
|
|
}
|
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-12-20 12:34:47 -07:00
|
|
|
// Only render the satellites of bodies brighter than the threshold
|
|
|
|
// magnitude. Ignore the phase and use the opposition magnitude so
|
|
|
|
// that satellites don't disappear unexpectedly (during a solar transit
|
|
|
|
// for example.)
|
|
|
|
if (oppositionMag < faintestPlanetMag || (body->getClassification() & Body::Invisible) != 0)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
const PlanetarySystem* satellites = body->getSatellites();
|
|
|
|
if (satellites != NULL)
|
|
|
|
{
|
2006-09-07 23:59:38 -06:00
|
|
|
buildRenderLists(sun, satellites, observer,
|
|
|
|
now, solarSysIndex, showLabels);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Add a star orbit to the render list
|
|
|
|
void Renderer::addStarOrbitToRenderList(const Star& star,
|
|
|
|
const Observer& observer,
|
|
|
|
double now)
|
|
|
|
{
|
|
|
|
// If the star isn't fixed, add its orbit to the render list
|
2006-09-09 21:54:43 -06:00
|
|
|
if ((renderFlags & ShowOrbits) != 0 &&
|
|
|
|
((orbitMask & Body::Planet) != 0 || highlightObject.star() == &star))
|
2006-09-07 23:59:38 -06:00
|
|
|
{
|
|
|
|
Mat3f viewMat = observer.getOrientation().toMatrix3();
|
|
|
|
Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
if (star.getOrbit() != NULL)
|
|
|
|
{
|
|
|
|
// Get orbit origin relative to the observer
|
|
|
|
Vec3d orbitOrigin = star.getOrbitBarycenterPosition(now) - observer.getPosition();
|
|
|
|
orbitOrigin *= astro::microLightYearsToKilometers(1.0);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
Vec3f origf((float) orbitOrigin.x, (float) orbitOrigin.y, (float) orbitOrigin.z);
|
|
|
|
|
|
|
|
// Compute the size of the orbit in pixels
|
|
|
|
double originDistance = orbitOrigin.length();
|
2006-09-12 06:56:47 -06:00
|
|
|
double boundingRadius = star.getOrbit()->getBoundingRadius();
|
2006-09-07 23:59:38 -06:00
|
|
|
float orbitRadiusInPixels = (float) (boundingRadius / (originDistance * pixelSize));
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-09-07 23:59:38 -06:00
|
|
|
if (orbitRadiusInPixels > minOrbitSize)
|
|
|
|
{
|
|
|
|
// Add the orbit of this body to the list of orbits to be rendered
|
|
|
|
OrbitPathListEntry path;
|
|
|
|
path.star = ☆
|
|
|
|
path.body = NULL;
|
|
|
|
path.centerZ = origf * viewMatZ;
|
|
|
|
path.radius = (float) boundingRadius;
|
|
|
|
path.origin = Point3f(origf.x, origf.y, origf.z);
|
|
|
|
orbitPathList.push_back(path);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
template <class OBJ, class PREC> class ObjectRenderer : public OctreeProcessor<OBJ, PREC>
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2005-11-18 02:00:39 -07:00
|
|
|
public:
|
|
|
|
ObjectRenderer(const PREC distanceLimit);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
void process(const OBJ&, PREC, float) {};
|
|
|
|
|
|
|
|
public:
|
2001-11-27 18:50:04 -07:00
|
|
|
const Observer* observer;
|
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
GLContext* context;
|
|
|
|
Renderer* renderer;
|
|
|
|
|
|
|
|
Vec3f viewNormal;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2002-06-22 09:15:44 -06:00
|
|
|
float fov;
|
2001-11-27 18:50:04 -07:00
|
|
|
float size;
|
|
|
|
float pixelSize;
|
|
|
|
float faintestMag;
|
2005-11-18 02:00:39 -07:00
|
|
|
float faintestMagNight;
|
2001-11-27 18:50:04 -07:00
|
|
|
float saturationMag;
|
|
|
|
float brightnessScale;
|
|
|
|
float brightnessBias;
|
2002-09-08 21:50:37 -06:00
|
|
|
float distanceLimit;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
// Objects brighter than labelThresholdMag will be labeled
|
|
|
|
float labelThresholdMag;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
// These are not fully used by this template's descendants
|
|
|
|
// but we place them here just in case a more sophisticated
|
|
|
|
// rendering scheme is implemented:
|
2001-11-27 18:50:04 -07:00
|
|
|
int nRendered;
|
|
|
|
int nClose;
|
|
|
|
int nBright;
|
2003-07-08 09:31:34 -06:00
|
|
|
int nProcessed;
|
2005-11-18 02:00:39 -07:00
|
|
|
int nLabelled;
|
|
|
|
|
|
|
|
int renderFlags;
|
|
|
|
int labelMode;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <class OBJ, class PREC>
|
|
|
|
ObjectRenderer<OBJ, PREC>::ObjectRenderer(const PREC _distanceLimit) :
|
|
|
|
distanceLimit((float) _distanceLimit),
|
|
|
|
nRendered (0),
|
|
|
|
nClose (0),
|
|
|
|
nBright (0),
|
|
|
|
nProcessed (0),
|
|
|
|
nLabelled (0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class StarRenderer : public ObjectRenderer<Star, float>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
StarRenderer();
|
|
|
|
|
|
|
|
void process(const Star& star, float distance, float appMag);
|
|
|
|
|
|
|
|
public:
|
|
|
|
Point3f obsPos;
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
vector<Renderer::Particle>* glareParticles;
|
|
|
|
vector<RenderListEntry>* renderList;
|
|
|
|
Renderer::StarVertexBuffer* starVertexBuffer;
|
|
|
|
Renderer::PointStarVertexBuffer* pointStarVertexBuffer;
|
2003-07-08 09:31:34 -06:00
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
const StarDatabase* starDB;
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
bool useScaledDiscs;
|
|
|
|
GLenum starPrimitive;
|
|
|
|
float maxDiscSize;
|
2005-11-18 02:00:39 -07:00
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
float cosFOV;
|
|
|
|
|
2004-09-19 21:27:50 -06:00
|
|
|
const ColorTemperatureTable* colorTemp;
|
2001-11-27 18:50:04 -07:00
|
|
|
};
|
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
|
2003-07-08 09:31:34 -06:00
|
|
|
StarRenderer::StarRenderer() :
|
2005-11-18 02:00:39 -07:00
|
|
|
ObjectRenderer<Star, float>(STAR_DISTANCE_LIMIT),
|
2006-03-08 11:29:10 -07:00
|
|
|
starVertexBuffer (NULL),
|
|
|
|
pointStarVertexBuffer(NULL),
|
|
|
|
useScaledDiscs (false),
|
|
|
|
maxDiscSize (1.0f),
|
2006-11-21 06:02:16 -07:00
|
|
|
cosFOV (1.0f),
|
2006-03-08 11:29:10 -07:00
|
|
|
colorTemp (NULL)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2004-09-27 22:44:26 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
void StarRenderer::process(const Star& star, float distance, float appMag)
|
|
|
|
{
|
|
|
|
nProcessed++;
|
|
|
|
|
|
|
|
Point3f starPos = star.getPosition();
|
2005-11-18 02:00:39 -07:00
|
|
|
Vec3f relPos = starPos - obsPos;
|
|
|
|
float orbitalRadius = star.getOrbitalRadius();
|
|
|
|
bool hasOrbit = orbitalRadius > 0.0f;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2002-09-08 21:50:37 -06:00
|
|
|
if (distance > distanceLimit)
|
|
|
|
return;
|
|
|
|
|
2004-09-27 22:44:26 -06:00
|
|
|
if (relPos * viewNormal > 0 || relPos.x * relPos.x < 0.1f || hasOrbit)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2004-09-19 21:27:50 -06:00
|
|
|
Color starColor = colorTemp->lookupColor(star.getTemperature());
|
2001-11-27 18:50:04 -07:00
|
|
|
float renderDistance = distance;
|
|
|
|
float s = renderDistance * size;
|
|
|
|
float discSizeInPixels = 0.0f;
|
2004-09-27 22:44:26 -06:00
|
|
|
float orbitSizeInPixels = 0.0f;
|
|
|
|
|
|
|
|
if (hasOrbit)
|
|
|
|
orbitSizeInPixels = orbitalRadius / (distance * pixelSize);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// Special handling for stars less than one light year away . . .
|
|
|
|
// We can't just go ahead and render a nearby star in the usual way
|
|
|
|
// for two reasons:
|
|
|
|
// * It may be clipped by the near plane
|
|
|
|
// * It may be large enough that we should render it as a mesh
|
|
|
|
// instead of a particle
|
|
|
|
// It's possible that the second condition might apply for stars
|
|
|
|
// further than one light year away if the star is huge, the fov is
|
|
|
|
// very small and the resolution is high. We'll ignore this for now
|
|
|
|
// and use the most inexpensive test possible . . .
|
2004-09-27 22:44:26 -06:00
|
|
|
if (distance < 1.0f || orbitSizeInPixels > 1.0f)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
// Compute the position of the observer relative to the star.
|
|
|
|
// This is a much more accurate (and expensive) distance
|
|
|
|
// calculation than the previous one which used the observer's
|
|
|
|
// position rounded off to floats.
|
2004-09-29 10:20:21 -06:00
|
|
|
Point3d hPos = astrocentricPosition(observer->getPosition(),
|
2004-09-27 22:44:26 -06:00
|
|
|
star,
|
|
|
|
observer->getTime());
|
2002-03-14 14:48:51 -07:00
|
|
|
relPos = Vec3f((float) hPos.x, (float) hPos.y, (float) hPos.z) *
|
|
|
|
-astro::kilometersToLightYears(1.0f),
|
2001-11-27 18:50:04 -07:00
|
|
|
distance = relPos.length();
|
|
|
|
|
|
|
|
// Recompute apparent magnitude using new distance computation
|
2005-11-18 02:00:39 -07:00
|
|
|
appMag = astro::absToAppMag(star.getAbsoluteMagnitude(), distance);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
float f = RenderDistance / distance;
|
2002-02-19 17:38:55 -07:00
|
|
|
renderDistance = RenderDistance;
|
2005-11-18 02:00:39 -07:00
|
|
|
starPos = obsPos + relPos * f;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
float radius = star.getRadius();
|
2004-09-27 22:44:26 -06:00
|
|
|
discSizeInPixels = radius / astro::lightYearsToKilometers(distance) / pixelSize;
|
2005-11-18 02:00:39 -07:00
|
|
|
++nClose;
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
// Place labels for stars brighter than the specified label threshold brightness
|
|
|
|
if ((labelMode & Renderer::StarLabels) && appMag < labelThresholdMag)
|
|
|
|
{
|
|
|
|
Vec3f starDir = relPos;
|
|
|
|
starDir.normalize();
|
|
|
|
if (dot(starDir, viewNormal) > cosFOV)
|
|
|
|
{
|
|
|
|
char nameBuffer[Renderer::MaxLabelLength];
|
|
|
|
starDB->getStarName(star, nameBuffer, sizeof(nameBuffer));
|
|
|
|
renderer->addLabel(nameBuffer,
|
|
|
|
Color(0.5f, 0.5f, 1.0f, 1.0f),
|
|
|
|
Point3f(relPos.x, relPos.y, relPos.z));
|
|
|
|
nLabelled++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
if (discSizeInPixels <= 1)
|
|
|
|
{
|
2003-07-08 09:31:34 -06:00
|
|
|
float alpha = (faintestMag - appMag) * brightnessScale + brightnessBias;
|
2006-03-08 11:29:10 -07:00
|
|
|
float pointSize;
|
2003-07-08 09:31:34 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
if (useScaledDiscs)
|
2003-07-08 09:31:34 -06:00
|
|
|
{
|
|
|
|
float discSize = size;
|
|
|
|
if (alpha < 0.0f)
|
|
|
|
{
|
|
|
|
alpha = 0.0f;
|
|
|
|
}
|
|
|
|
else if (alpha > 1.0f)
|
|
|
|
{
|
|
|
|
discSize = min(discSize * (2.0f * alpha - 1.0f), maxDiscSize);
|
|
|
|
alpha = 1.0f;
|
|
|
|
}
|
2006-03-08 11:29:10 -07:00
|
|
|
pointSize = discSize;
|
2003-07-08 09:31:34 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
alpha = clamp(alpha);
|
2006-03-08 11:29:10 -07:00
|
|
|
pointSize = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (starPrimitive == GL_POINTS)
|
|
|
|
{
|
|
|
|
pointStarVertexBuffer->addStar(starPos,
|
|
|
|
Color(starColor, alpha),
|
|
|
|
pointSize);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-07-08 09:31:34 -06:00
|
|
|
starVertexBuffer->addStar(starPos,
|
|
|
|
Color(starColor, alpha),
|
2006-03-08 11:29:10 -07:00
|
|
|
pointSize * renderDistance);
|
2003-07-08 09:31:34 -06:00
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
++nRendered;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
// If the star is brighter than the saturation magnitude, add a
|
|
|
|
// halo around it to make it appear more brilliant. This is a
|
|
|
|
// hack to compensate for the limited dynamic range of monitors.
|
|
|
|
if (appMag < saturationMag)
|
|
|
|
{
|
|
|
|
Renderer::Particle p;
|
|
|
|
p.center = starPos;
|
2006-03-08 11:29:10 -07:00
|
|
|
p.size = size;
|
2001-11-27 18:50:04 -07:00
|
|
|
p.color = Color(starColor, alpha);
|
|
|
|
|
2006-12-20 11:56:43 -07:00
|
|
|
alpha = GlareOpacity * clamp((appMag - saturationMag) * -0.8f);
|
|
|
|
s = renderDistance * 0.001f * (3 - (appMag - saturationMag)) * 2;
|
|
|
|
if (s > p.size * 3)
|
2006-03-08 11:29:10 -07:00
|
|
|
{
|
2006-12-20 11:56:43 -07:00
|
|
|
p.size = s * 2.0f/(1.0f + FOV/fov);
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
2001-11-27 18:50:04 -07:00
|
|
|
else
|
2006-03-08 11:29:10 -07:00
|
|
|
{
|
|
|
|
if (s > p.size * 3)
|
|
|
|
p.size = s * 2.0f; //2.0f/(1.0f +FOV/fov);
|
|
|
|
else
|
|
|
|
p.size = p.size * 3;
|
|
|
|
p.size *= 1.6f;
|
|
|
|
}
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
p.color = Color(starColor, alpha);
|
|
|
|
glareParticles->insert(glareParticles->end(), p);
|
2005-11-18 02:00:39 -07:00
|
|
|
++nBright;
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-09-11 22:38:12 -06:00
|
|
|
Mat3f viewMat = observer->getOrientation().toMatrix3();
|
|
|
|
Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2002-11-03 21:14:07 -07:00
|
|
|
RenderListEntry rle;
|
2001-11-27 18:50:04 -07:00
|
|
|
rle.star = ☆
|
|
|
|
rle.body = NULL;
|
2002-06-11 23:29:34 -06:00
|
|
|
rle.isCometTail = false;
|
2006-09-10 18:37:20 -06:00
|
|
|
rle.isOpaque = true;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
// Objects in the render list are always rendered relative to
|
|
|
|
// a viewer at the origin--this is different than for distant
|
|
|
|
// stars.
|
|
|
|
float scale = astro::lightYearsToKilometers(1.0f);
|
|
|
|
rle.position = Point3f(relPos.x * scale, relPos.y * scale, relPos.z * scale);
|
2006-09-11 22:38:12 -06:00
|
|
|
rle.centerZ = Vec3f(rle.position.x, rle.position.y, rle.position.z) * viewMatZ;
|
2001-11-27 18:50:04 -07:00
|
|
|
rle.distance = rle.position.distanceFromOrigin();
|
2001-12-06 22:13:25 -07:00
|
|
|
rle.radius = star.getRadius();
|
2001-11-27 18:50:04 -07:00
|
|
|
rle.discSizeInPixels = discSizeInPixels;
|
|
|
|
rle.appMag = appMag;
|
|
|
|
renderList->insert(renderList->end(), rle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-07-08 09:31:34 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
class PointStarRenderer : public ObjectRenderer<Star, float>
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2005-11-18 02:00:39 -07:00
|
|
|
public:
|
2006-03-08 11:29:10 -07:00
|
|
|
PointStarRenderer();
|
2002-03-28 19:04:57 -07:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
void process(const Star& star, float distance, float appMag);
|
2004-02-21 18:44:38 -07:00
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
public:
|
2006-03-08 11:29:10 -07:00
|
|
|
Point3f obsPos;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
vector<RenderListEntry>* renderList;
|
|
|
|
Renderer::PointStarVertexBuffer* starVertexBuffer;
|
2006-11-21 06:02:16 -07:00
|
|
|
Renderer::PointStarVertexBuffer* glareVertexBuffer;
|
2005-02-10 23:09:37 -07:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
const StarDatabase* starDB;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
bool useScaledDiscs;
|
|
|
|
GLenum starPrimitive;
|
|
|
|
float maxDiscSize;
|
2005-11-18 02:00:39 -07:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
float cosFOV;
|
|
|
|
|
2006-09-12 06:56:47 -06:00
|
|
|
const ColorTemperatureTable* colorTemp;
|
2005-11-18 02:00:39 -07:00
|
|
|
};
|
2005-02-10 23:09:37 -07:00
|
|
|
|
2002-09-22 08:49:49 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
PointStarRenderer::PointStarRenderer() :
|
|
|
|
ObjectRenderer<Star, float>(STAR_DISTANCE_LIMIT),
|
|
|
|
starVertexBuffer (NULL),
|
|
|
|
useScaledDiscs (false),
|
|
|
|
maxDiscSize (1.0f),
|
|
|
|
cosFOV (1.0f),
|
|
|
|
colorTemp (NULL)
|
2005-11-18 02:00:39 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
void PointStarRenderer::process(const Star& star, float distance, float appMag)
|
2005-11-18 02:00:39 -07:00
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
nProcessed++;
|
2005-11-18 02:00:39 -07:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
Point3f starPos = star.getPosition();
|
|
|
|
Vec3f relPos = starPos - obsPos;
|
|
|
|
float orbitalRadius = star.getOrbitalRadius();
|
|
|
|
bool hasOrbit = orbitalRadius > 0.0f;
|
2005-11-18 02:00:39 -07:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
if (distance > distanceLimit)
|
|
|
|
return;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
// A very rough check to see if the star may be visible: is the star in
|
|
|
|
// front of the viewer? If the star might be close (relPos.x^2 < 0.1) or
|
|
|
|
// is moving in an orbit, we'll always regard it as potentially visible.
|
|
|
|
// TODO: consider normalizing relPos and comparing relPos*viewNormal against
|
|
|
|
// cosFOV--this will cull many more stars than relPos*viewNormal, at the
|
|
|
|
// cost of a normalize per star.
|
|
|
|
if (relPos * viewNormal > 0 || relPos.x * relPos.x < 0.1f || hasOrbit)
|
2005-11-18 02:00:39 -07:00
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
Color starColor = colorTemp->lookupColor(star.getTemperature());
|
|
|
|
float renderDistance = distance;
|
2006-09-12 06:56:47 -06:00
|
|
|
/*float s = renderDistance * size; Unused*/
|
2006-03-08 11:29:10 -07:00
|
|
|
float discSizeInPixels = 0.0f;
|
|
|
|
float orbitSizeInPixels = 0.0f;
|
|
|
|
|
|
|
|
if (hasOrbit)
|
|
|
|
orbitSizeInPixels = orbitalRadius / (distance * pixelSize);
|
|
|
|
|
|
|
|
// Special handling for stars less than one light year away . . .
|
|
|
|
// We can't just go ahead and render a nearby star in the usual way
|
|
|
|
// for two reasons:
|
|
|
|
// * It may be clipped by the near plane
|
|
|
|
// * It may be large enough that we should render it as a mesh
|
|
|
|
// instead of a particle
|
|
|
|
// It's possible that the second condition might apply for stars
|
|
|
|
// further than one light year away if the star is huge, the fov is
|
|
|
|
// very small and the resolution is high. We'll ignore this for now
|
|
|
|
// and use the most inexpensive test possible . . .
|
|
|
|
if (distance < 1.0f || orbitSizeInPixels > 1.0f)
|
|
|
|
{
|
|
|
|
// Compute the position of the observer relative to the star.
|
|
|
|
// This is a much more accurate (and expensive) distance
|
|
|
|
// calculation than the previous one which used the observer's
|
|
|
|
// position rounded off to floats.
|
|
|
|
Point3d hPos = astrocentricPosition(observer->getPosition(),
|
|
|
|
star,
|
|
|
|
observer->getTime());
|
|
|
|
relPos = Vec3f((float) hPos.x, (float) hPos.y, (float) hPos.z) *
|
|
|
|
-astro::kilometersToLightYears(1.0f),
|
|
|
|
distance = relPos.length();
|
|
|
|
|
|
|
|
// Recompute apparent magnitude using new distance computation
|
|
|
|
appMag = astro::absToAppMag(star.getAbsoluteMagnitude(), distance);
|
|
|
|
|
|
|
|
float f = RenderDistance / distance;
|
|
|
|
renderDistance = RenderDistance;
|
|
|
|
starPos = obsPos + relPos * f;
|
|
|
|
|
|
|
|
float radius = star.getRadius();
|
|
|
|
discSizeInPixels = radius / astro::lightYearsToKilometers(distance) / pixelSize;
|
|
|
|
++nClose;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Place labels for stars brighter than the specified label threshold brightness
|
|
|
|
if ((labelMode & Renderer::StarLabels) && appMag < labelThresholdMag)
|
|
|
|
{
|
|
|
|
Vec3f starDir = relPos;
|
|
|
|
starDir.normalize();
|
|
|
|
if (dot(starDir, viewNormal) > cosFOV)
|
|
|
|
{
|
|
|
|
char nameBuffer[Renderer::MaxLabelLength];
|
|
|
|
starDB->getStarName(star, nameBuffer, sizeof(nameBuffer));
|
|
|
|
renderer->addLabel(nameBuffer,
|
|
|
|
Color(0.5f, 0.5f, 1.0f, 1.0f),
|
|
|
|
Point3f(relPos.x, relPos.y, relPos.z));
|
|
|
|
nLabelled++;
|
|
|
|
}
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
if (discSizeInPixels <= 1)
|
|
|
|
{
|
2006-11-05 01:13:34 -07:00
|
|
|
float satPoint = faintestMag - (1.0f - brightnessBias) / brightnessScale; // TODO: precompute this value
|
2006-03-08 11:29:10 -07:00
|
|
|
float alpha = (faintestMag - appMag) * brightnessScale + brightnessBias;
|
|
|
|
|
|
|
|
if (useScaledDiscs)
|
|
|
|
{
|
|
|
|
float discSize = size;
|
|
|
|
if (alpha < 0.0f)
|
|
|
|
{
|
|
|
|
alpha = 0.0f;
|
|
|
|
}
|
|
|
|
else if (alpha > 1.0f)
|
|
|
|
{
|
2006-12-20 11:56:43 -07:00
|
|
|
float discScale = min(MaxScaledDiscStarSize, (float) pow(2.0f, 0.3f * (satPoint - appMag)));
|
2006-11-05 01:13:34 -07:00
|
|
|
discSize *= discScale;
|
2006-11-27 12:11:55 -07:00
|
|
|
|
2006-11-05 01:13:34 -07:00
|
|
|
float glareAlpha = min(0.5f, discScale / 4.0f);
|
|
|
|
glareVertexBuffer->addStar(starPos, Color(starColor, glareAlpha), discSize * 3.0f);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-05 01:13:34 -07:00
|
|
|
alpha = 1.0f;
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
2006-11-05 01:13:34 -07:00
|
|
|
starVertexBuffer->addStar(starPos, Color(starColor, alpha), discSize);
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-11-05 01:13:34 -07:00
|
|
|
if (alpha < 0.0f)
|
2006-03-08 11:29:10 -07:00
|
|
|
{
|
|
|
|
alpha = 0.0f;
|
|
|
|
}
|
|
|
|
else if (alpha > 1.0f)
|
|
|
|
{
|
|
|
|
float discScale = min(100.0f, satPoint - appMag + 2.0f);
|
2006-12-20 11:56:43 -07:00
|
|
|
float glareAlpha = min(GlareOpacity, (discScale - 2.0f) / 4.0f);
|
2006-11-05 01:13:34 -07:00
|
|
|
glareVertexBuffer->addStar(starPos, Color(starColor, glareAlpha), 2.0f * discScale * size);
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
2006-11-05 01:13:34 -07:00
|
|
|
starVertexBuffer->addStar(starPos, Color(starColor, alpha), size);
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
++nRendered;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-09-11 22:38:12 -06:00
|
|
|
Mat3f viewMat = observer->getOrientation().toMatrix3();
|
|
|
|
Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
RenderListEntry rle;
|
|
|
|
rle.star = ☆
|
|
|
|
rle.body = NULL;
|
|
|
|
rle.isCometTail = false;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
// Objects in the render list are always rendered relative to
|
|
|
|
// a viewer at the origin--this is different than for distant
|
|
|
|
// stars.
|
|
|
|
float scale = astro::lightYearsToKilometers(1.0f);
|
|
|
|
rle.position = Point3f(relPos.x * scale, relPos.y * scale, relPos.z * scale);
|
2006-09-11 22:38:12 -06:00
|
|
|
rle.centerZ = Vec3f(rle.position.x, rle.position.y, rle.position.z) * viewMatZ;
|
2006-03-08 11:29:10 -07:00
|
|
|
rle.distance = rle.position.distanceFromOrigin();
|
|
|
|
rle.radius = star.getRadius();
|
|
|
|
rle.discSizeInPixels = discSizeInPixels;
|
|
|
|
rle.appMag = appMag;
|
|
|
|
renderList->insert(renderList->end(), rle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static Point3f microLYToLY(const Point3f& p)
|
|
|
|
{
|
|
|
|
return Point3f(p.x * 1e-6f, p.y * 1e-6f, p.z * 1e-6f);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
// Calculate the maximum field of view (from top left corner to bottom right) of
|
|
|
|
// a frustum with the specified aspect ratio (width/height) and vertical field of
|
|
|
|
// view. We follow the convention used elsewhere and use units of degrees for
|
|
|
|
// the field of view angle.
|
|
|
|
static double calcMaxFOV(double fovY_degrees, double aspectRatio)
|
|
|
|
{
|
|
|
|
double l = 1.0 / tan(degToRad(fovY_degrees / 2.0));
|
|
|
|
return radToDeg(atan(sqrt(aspectRatio * aspectRatio + 1.0) / l)) * 2.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
void Renderer::renderStars(const StarDatabase& starDB,
|
|
|
|
float faintestMagNight,
|
|
|
|
const Observer& observer)
|
|
|
|
{
|
|
|
|
StarRenderer starRenderer;
|
|
|
|
Point3f obsPos = microLYToLY((Point3f) observer.getPosition());
|
|
|
|
|
|
|
|
starRenderer.context = context;
|
2006-11-21 06:02:16 -07:00
|
|
|
starRenderer.renderer = this;
|
|
|
|
starRenderer.starDB = &starDB;
|
2006-03-08 11:29:10 -07:00
|
|
|
starRenderer.observer = &observer;
|
|
|
|
starRenderer.obsPos = obsPos;
|
|
|
|
starRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientation().toMatrix3();
|
|
|
|
starRenderer.glareParticles = &glareParticles;
|
|
|
|
starRenderer.renderList = &renderList;
|
|
|
|
starRenderer.starVertexBuffer = starVertexBuffer;
|
|
|
|
starRenderer.pointStarVertexBuffer = pointStarVertexBuffer;
|
|
|
|
starRenderer.fov = fov;
|
2006-11-21 06:02:16 -07:00
|
|
|
starRenderer.cosFOV = (float) cos(degToRad(calcMaxFOV(fov, (float) windowWidth / (float) windowHeight)) / 2.0f);
|
2006-03-08 11:29:10 -07:00
|
|
|
|
|
|
|
// size/pixelSize =0.86 at 120deg, 1.43 at 45deg and 1.6 at 0deg.
|
|
|
|
starRenderer.size = pixelSize * 1.6f / corrFac;
|
|
|
|
starRenderer.pixelSize = pixelSize;
|
|
|
|
starRenderer.brightnessScale = brightnessScale * corrFac;
|
|
|
|
starRenderer.brightnessBias = brightnessBias;
|
|
|
|
starRenderer.faintestMag = faintestMag;
|
|
|
|
starRenderer.faintestMagNight = faintestMagNight;
|
|
|
|
starRenderer.saturationMag = saturationMag;
|
|
|
|
starRenderer.distanceLimit = distanceLimit;
|
2006-11-22 00:08:19 -07:00
|
|
|
starRenderer.labelMode = labelMode;
|
2006-03-08 11:29:10 -07:00
|
|
|
|
2006-11-21 06:02:16 -07:00
|
|
|
// = 1.0 at startup
|
|
|
|
float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
|
|
|
|
starRenderer.labelThresholdMag = max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
if (starStyle == PointStars || useNewStarRendering)
|
|
|
|
{
|
|
|
|
starRenderer.starPrimitive = GL_POINTS;
|
|
|
|
//starRenderer.size = 3.2f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
starRenderer.starPrimitive = GL_QUADS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (starStyle == ScaledDiscStars)
|
|
|
|
{
|
|
|
|
starRenderer.useScaledDiscs = true;
|
|
|
|
starRenderer.brightnessScale *= 2.0f;
|
|
|
|
starRenderer.maxDiscSize = starRenderer.size * MaxScaledDiscStarSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
starRenderer.colorTemp = colorTemp;
|
|
|
|
|
|
|
|
glareParticles.clear();
|
|
|
|
|
|
|
|
starVertexBuffer->setBillboardOrientation(observer.getOrientation());
|
|
|
|
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
if (useNewStarRendering)
|
|
|
|
gaussianDiscTex->bind();
|
|
|
|
else
|
|
|
|
starTex->bind();
|
|
|
|
if (starRenderer.starPrimitive == GL_POINTS)
|
|
|
|
{
|
|
|
|
// Point primitives (either real points or point sprites)
|
|
|
|
if (starStyle == PointStars)
|
|
|
|
starRenderer.pointStarVertexBuffer->startPoints(*context);
|
|
|
|
else
|
|
|
|
starRenderer.pointStarVertexBuffer->startSprites(*context);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Use quad primitives
|
|
|
|
starRenderer.starVertexBuffer->start();
|
|
|
|
}
|
|
|
|
starDB.findVisibleStars(starRenderer,
|
|
|
|
obsPos,
|
|
|
|
observer.getOrientation(),
|
|
|
|
degToRad(fov),
|
|
|
|
(float) windowWidth / (float) windowHeight,
|
|
|
|
faintestMagNight);
|
|
|
|
|
|
|
|
if (starRenderer.starPrimitive == GL_POINTS)
|
|
|
|
starRenderer.pointStarVertexBuffer->finish();
|
|
|
|
else
|
|
|
|
starRenderer.starVertexBuffer->finish();
|
|
|
|
|
2006-12-20 11:56:43 -07:00
|
|
|
gaussianGlareTex->bind();
|
2006-03-08 11:29:10 -07:00
|
|
|
renderParticles(glareParticles, observer.getOrientation());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::renderPointStars(const StarDatabase& starDB,
|
|
|
|
float faintestMagNight,
|
|
|
|
const Observer& observer)
|
|
|
|
{
|
|
|
|
Point3f obsPos = microLYToLY((Point3f) observer.getPosition());
|
|
|
|
|
|
|
|
PointStarRenderer starRenderer;
|
|
|
|
starRenderer.context = context;
|
|
|
|
starRenderer.renderer = this;
|
|
|
|
starRenderer.starDB = &starDB;
|
|
|
|
starRenderer.observer = &observer;
|
|
|
|
starRenderer.obsPos = obsPos;
|
|
|
|
starRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientation().toMatrix3();
|
|
|
|
starRenderer.renderList = &renderList;
|
|
|
|
starRenderer.starVertexBuffer = pointStarVertexBuffer;
|
|
|
|
starRenderer.glareVertexBuffer = glareVertexBuffer;
|
2006-09-12 06:56:47 -06:00
|
|
|
starRenderer.fov = fov;
|
2006-03-08 11:29:10 -07:00
|
|
|
starRenderer.cosFOV = (float) cos(degToRad(calcMaxFOV(fov, (float) windowWidth / (float) windowHeight)) / 2.0f);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
starRenderer.pixelSize = pixelSize;
|
|
|
|
starRenderer.brightnessScale = brightnessScale * corrFac;
|
|
|
|
starRenderer.brightnessBias = brightnessBias;
|
|
|
|
starRenderer.faintestMag = faintestMag;
|
|
|
|
starRenderer.faintestMagNight = faintestMagNight;
|
|
|
|
starRenderer.saturationMag = saturationMag;
|
|
|
|
starRenderer.distanceLimit = distanceLimit;
|
|
|
|
starRenderer.labelMode = labelMode;
|
2006-09-12 06:56:47 -06:00
|
|
|
|
|
|
|
// = 1.0 at startup
|
2006-03-08 11:29:10 -07:00
|
|
|
float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
|
|
|
|
starRenderer.labelThresholdMag = max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-12-28 12:29:42 -07:00
|
|
|
starRenderer.size = BaseStarDiscSize;
|
2006-03-08 11:29:10 -07:00
|
|
|
if (starStyle == ScaledDiscStars)
|
|
|
|
{
|
|
|
|
starRenderer.useScaledDiscs = true;
|
|
|
|
starRenderer.brightnessScale *= 2.0f;
|
|
|
|
starRenderer.maxDiscSize = starRenderer.size * MaxScaledDiscStarSize;
|
|
|
|
}
|
|
|
|
else if (starStyle == FuzzyPointStars)
|
|
|
|
{
|
2006-12-28 12:29:42 -07:00
|
|
|
starRenderer.brightnessScale *= 1.0f;
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
starRenderer.colorTemp = colorTemp;
|
|
|
|
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
2006-11-05 01:13:34 -07:00
|
|
|
gaussianDiscTex->bind();
|
|
|
|
starRenderer.starVertexBuffer->setTexture(gaussianDiscTex);
|
|
|
|
starRenderer.glareVertexBuffer->setTexture(gaussianGlareTex);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-11-05 01:13:34 -07:00
|
|
|
starRenderer.glareVertexBuffer->startSprites(*context);
|
2006-03-08 11:29:10 -07:00
|
|
|
if (starStyle == PointStars)
|
|
|
|
starRenderer.starVertexBuffer->startPoints(*context);
|
|
|
|
else
|
|
|
|
starRenderer.starVertexBuffer->startSprites(*context);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
starDB.findVisibleStars(starRenderer,
|
|
|
|
obsPos,
|
|
|
|
observer.getOrientation(),
|
|
|
|
degToRad(fov),
|
|
|
|
(float) windowWidth / (float) windowHeight,
|
|
|
|
faintestMagNight);
|
|
|
|
|
2006-11-05 01:13:34 -07:00
|
|
|
starRenderer.starVertexBuffer->render();
|
|
|
|
starRenderer.glareVertexBuffer->render();
|
2006-03-08 11:29:10 -07:00
|
|
|
starRenderer.starVertexBuffer->finish();
|
2006-11-05 01:13:34 -07:00
|
|
|
starRenderer.glareVertexBuffer->finish();
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class DSORenderer : public ObjectRenderer<DeepSkyObject*, double>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
DSORenderer();
|
|
|
|
|
|
|
|
void process(DeepSkyObject* const &, double, float);
|
|
|
|
|
|
|
|
public:
|
|
|
|
Point3d obsPos;
|
|
|
|
DSODatabase* dsoDB;
|
|
|
|
Frustum frustum;
|
|
|
|
|
|
|
|
Mat3f orientationMatrix;
|
|
|
|
|
|
|
|
int wWidth;
|
|
|
|
int wHeight;
|
|
|
|
|
|
|
|
double avgAbsMag;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
DSORenderer::DSORenderer() :
|
|
|
|
ObjectRenderer<DeepSkyObject*, double>(DSO_DISTANCE_LIMIT),
|
|
|
|
frustum(degToRad(45.0f), 1.0f, 1.0f)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DSORenderer::process(DeepSkyObject* const & dso,
|
|
|
|
double distanceToDSO,
|
|
|
|
float absMag)
|
|
|
|
{
|
|
|
|
if (distanceToDSO > distanceLimit)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Point3d dsoPos = dso->getPosition();
|
|
|
|
Vec3f relPos = Vec3f((float)(dsoPos.x - obsPos.x),
|
|
|
|
(float)(dsoPos.y - obsPos.y),
|
|
|
|
(float)(dsoPos.z - obsPos.z));
|
|
|
|
|
|
|
|
Point3d center = Point3d(0.0f, 0.0f, 0.0f) + relPos * orientationMatrix;
|
2006-09-11 15:47:01 -06:00
|
|
|
float appMag = astro::absToAppMag(absMag, (float) distanceToDSO);
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
// Test the object's bounding sphere against the view frustum. If we
|
|
|
|
// avoid this stage, overcrowded octree cells may hit performance badly:
|
|
|
|
// each object (even if it's not visible) would be sent to the OpenGL
|
|
|
|
// pipeline.
|
|
|
|
|
|
|
|
if (renderFlags & dso->getRenderMask())
|
|
|
|
{
|
|
|
|
double dsoRadius = dso->getRadius();
|
|
|
|
if (frustum.testSphere(center, dsoRadius) != Frustum::Outside)
|
2005-11-18 02:00:39 -07:00
|
|
|
{
|
2006-01-03 10:22:07 -07:00
|
|
|
// display looks satisfactory for 0.2 < brightness < O(1.0)
|
|
|
|
// Ansatz: brightness = a - b*appMag(distanceToDSO), emulates eye sensitivity...
|
2005-11-18 02:00:39 -07:00
|
|
|
// determine a,b such that
|
|
|
|
// a-b*absMag = absMag/avgAbsMag ~ 1; a-b*faintestMag = 0.2
|
2006-01-03 10:22:07 -07:00
|
|
|
// the 2nd eqn guarantees that the faintest galaxies are still visible.
|
|
|
|
// the parameters in the 'close' correction function are fixed by matching
|
2006-09-12 06:56:47 -06:00
|
|
|
// the gradients at 10 pc and by: close (10 pc) = 0.
|
2006-01-03 10:22:07 -07:00
|
|
|
// ri adjusts the Milky Way brightness as viewed from "inside" (e.g. from Earth).
|
2005-11-18 02:00:39 -07:00
|
|
|
|
2005-12-11 08:34:35 -07:00
|
|
|
double ri = -0.1, pc10 = 32.6167;
|
|
|
|
double r = absMag / avgAbsMag;
|
|
|
|
double num = 5 * (absMag - faintestMag);
|
|
|
|
double a = r * (avgAbsMag - 5 * faintestMag) / num;
|
2006-09-12 06:56:47 -06:00
|
|
|
double b = (1.0 - 5 * r) / num;
|
2006-01-03 10:22:07 -07:00
|
|
|
double close = (distanceToDSO > -10.0)?
|
2006-09-12 06:56:47 -06:00
|
|
|
-4.3429448 * b * log((pc10 + distanceToDSO)/(2 * pc10)): ri;
|
2006-01-03 10:22:07 -07:00
|
|
|
// note: 10.0 / log(10.0) = 4.3429448
|
|
|
|
if (distanceToDSO < 0)
|
|
|
|
distanceToDSO = 0;
|
2006-09-12 06:56:47 -06:00
|
|
|
double brightness = (distanceToDSO >= pc10)? a - b * appMag: r + close;
|
2006-01-03 10:22:07 -07:00
|
|
|
brightness = 2.3 * brightness * (faintestMag - 4.75)/renderer->getFaintestAM45deg();
|
2005-11-18 02:00:39 -07:00
|
|
|
if (brightness < 0.0)
|
|
|
|
brightness = 0.0;
|
|
|
|
|
|
|
|
if (dsoRadius < 1000.0)
|
2003-05-07 01:46:15 -06:00
|
|
|
{
|
|
|
|
// Small objects may be prone to clipping; give them special
|
|
|
|
// handling. We don't want to always set the projection
|
|
|
|
// matrix, since that could be expensive with large galaxy
|
|
|
|
// catalogs.
|
2006-01-03 10:22:07 -07:00
|
|
|
float nearZ = (float) (distanceToDSO / 2);
|
2005-12-11 08:34:35 -07:00
|
|
|
float farZ = (float) (distanceToDSO + dsoRadius * 2);
|
|
|
|
if (nearZ < dsoRadius * 0.001)
|
2003-05-07 01:46:15 -06:00
|
|
|
{
|
2005-11-18 02:00:39 -07:00
|
|
|
nearZ = (float) (dsoRadius * 0.001);
|
|
|
|
farZ = nearZ * 10000.0f;
|
2003-05-07 01:46:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadIdentity();
|
|
|
|
gluPerspective(fov,
|
2005-11-18 02:00:39 -07:00
|
|
|
(float) wWidth / (float) wHeight,
|
|
|
|
nearZ,
|
|
|
|
farZ);
|
2003-05-07 01:46:15 -06:00
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
}
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
glPushMatrix();
|
2005-11-18 02:00:39 -07:00
|
|
|
glTranslate(relPos);
|
|
|
|
|
|
|
|
dso->render(*context,
|
|
|
|
relPos,
|
|
|
|
observer->getOrientation(),
|
|
|
|
(float) brightness,
|
2003-01-28 22:47:50 -07:00
|
|
|
pixelSize);
|
2001-11-27 18:50:04 -07:00
|
|
|
glPopMatrix();
|
2003-05-07 01:46:15 -06:00
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
#if 1
|
|
|
|
if (dsoRadius < 1000.0)
|
2003-05-07 01:46:15 -06:00
|
|
|
{
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
}
|
2005-11-18 02:00:39 -07:00
|
|
|
#endif
|
|
|
|
} // frustum test
|
|
|
|
} // renderFlags check
|
|
|
|
|
2005-12-11 08:34:35 -07:00
|
|
|
// Only render those labels that are in front of the camera:
|
2006-09-11 15:47:01 -06:00
|
|
|
// Place labels for DSOs brighter than the specified label threshold brightness
|
2006-12-03 15:13:14 -07:00
|
|
|
//
|
|
|
|
if ((dso->getLabelMask() & labelMode) && appMag < labelThresholdMag && dot(relPos, viewNormal) > 0)
|
2005-11-18 02:00:39 -07:00
|
|
|
{
|
2006-09-11 15:47:01 -06:00
|
|
|
// introduce distance dependent label transparency.
|
2006-12-03 15:13:14 -07:00
|
|
|
float distr = 6.0f * (labelThresholdMag - appMag)/labelThresholdMag;
|
|
|
|
//(float)log10(1.0f + 1.2e-6f * distanceToDSO);
|
2006-09-11 15:47:01 -06:00
|
|
|
if (distr > 1.0f)
|
2006-09-12 06:56:47 -06:00
|
|
|
distr = 1.0f;
|
2007-01-05 17:52:32 -07:00
|
|
|
|
2006-09-11 15:47:01 -06:00
|
|
|
renderer->addLabel(dsoDB->getDSOName(dso),
|
|
|
|
Color(0.1f, 0.85f, 0.85f, distr),
|
2006-09-12 06:56:47 -06:00
|
|
|
Point3f(relPos.x, relPos.y, relPos.z));
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
2005-11-18 02:00:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::renderDeepSkyObjects(const Universe& universe,
|
|
|
|
const Observer& observer,
|
|
|
|
const float faintestMagNight)
|
|
|
|
{
|
|
|
|
DSORenderer dsoRenderer;
|
|
|
|
|
|
|
|
Point3d obsPos = (Point3d) observer.getPosition();
|
|
|
|
obsPos.x *= 1e-6;
|
|
|
|
obsPos.y *= 1e-6;
|
|
|
|
obsPos.z *= 1e-6;
|
|
|
|
|
|
|
|
DSODatabase* dsoDB = universe.getDSOCatalog();
|
|
|
|
|
|
|
|
dsoRenderer.context = context;
|
|
|
|
dsoRenderer.renderer = this;
|
|
|
|
dsoRenderer.dsoDB = dsoDB;
|
|
|
|
dsoRenderer.orientationMatrix = conjugate(observer.getOrientation()).toMatrix3();
|
|
|
|
dsoRenderer.observer = &observer;
|
|
|
|
dsoRenderer.obsPos = obsPos;
|
|
|
|
dsoRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientation().toMatrix3();
|
|
|
|
dsoRenderer.fov = fov;
|
|
|
|
// size/pixelSize =0.86 at 120deg, 1.43 at 45deg and 1.6 at 0deg.
|
|
|
|
dsoRenderer.size = pixelSize * 1.6f / corrFac;
|
|
|
|
dsoRenderer.pixelSize = pixelSize;
|
|
|
|
dsoRenderer.brightnessScale = brightnessScale * corrFac;
|
|
|
|
dsoRenderer.brightnessBias = brightnessBias;
|
|
|
|
dsoRenderer.avgAbsMag = dsoDB->getAverageAbsoluteMagnitude();
|
|
|
|
dsoRenderer.faintestMag = faintestMag;
|
|
|
|
dsoRenderer.faintestMagNight = faintestMagNight;
|
|
|
|
dsoRenderer.saturationMag = saturationMag;
|
|
|
|
dsoRenderer.renderFlags = renderFlags;
|
|
|
|
dsoRenderer.labelMode = labelMode;
|
|
|
|
dsoRenderer.wWidth = windowWidth;
|
|
|
|
dsoRenderer.wHeight = windowHeight;
|
|
|
|
|
|
|
|
dsoRenderer.frustum = Frustum(degToRad(fov),
|
|
|
|
(float) windowWidth / (float) windowHeight,
|
|
|
|
MinNearPlaneDistance);
|
2006-09-12 06:56:47 -06:00
|
|
|
// Use pixelSize * screenDpi instead of FoV, to eliminate windowHeight dependence.
|
|
|
|
// = 1.0 at startup
|
2006-09-11 15:47:01 -06:00
|
|
|
float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-12-03 15:13:14 -07:00
|
|
|
dsoRenderer.labelThresholdMag = 1.8f * max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2005-11-18 02:00:39 -07:00
|
|
|
// Render any line primitives with smooth lines
|
|
|
|
// (mostly to make graticules look good.)
|
|
|
|
if ((renderFlags & ShowSmoothLines) != 0)
|
|
|
|
enableSmoothLines();
|
|
|
|
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
|
|
|
|
|
|
dsoDB->findVisibleDSOs(dsoRenderer,
|
|
|
|
obsPos,
|
|
|
|
observer.getOrientation(),
|
|
|
|
degToRad(fov),
|
|
|
|
(float) windowWidth / (float) windowHeight,
|
2006-09-11 15:47:01 -06:00
|
|
|
2 * faintestMagNight);
|
2004-02-21 18:44:38 -07:00
|
|
|
|
|
|
|
if ((renderFlags & ShowSmoothLines) != 0)
|
|
|
|
disableSmoothLines();
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-11-05 02:08:44 -07:00
|
|
|
// TODO: Rewrite this function to use a pregenerated vertex buffer and to
|
|
|
|
// be more general so that it can display the equatorial coordinate grid
|
|
|
|
// for any planet.
|
2001-11-27 18:50:04 -07:00
|
|
|
void Renderer::renderCelestialSphere(const Observer& observer)
|
|
|
|
{
|
2006-11-05 02:08:44 -07:00
|
|
|
int raDivisions = 24;
|
|
|
|
int decDivisions = 18;
|
2001-11-27 18:50:04 -07:00
|
|
|
int nSections = 60;
|
|
|
|
float radius = 10.0f;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < raDivisions; i++)
|
|
|
|
{
|
|
|
|
float ra = (float) i / (float) raDivisions * 24.0f;
|
|
|
|
|
|
|
|
glBegin(GL_LINE_STRIP);
|
|
|
|
for (int j = 0; j <= nSections; j++)
|
|
|
|
{
|
|
|
|
float dec = ((float) j / (float) nSections) * 180 - 90;
|
|
|
|
glVertex(astro::equatorialToCelestialCart(ra, dec, radius));
|
|
|
|
}
|
|
|
|
glEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 1; i < decDivisions; i++)
|
|
|
|
{
|
|
|
|
float dec = (float) i / (float) decDivisions * 180 - 90;
|
|
|
|
glBegin(GL_LINE_LOOP);
|
|
|
|
for (int j = 0; j < nSections; j++)
|
|
|
|
{
|
|
|
|
float ra = (float) j / (float) nSections * 24.0f;
|
|
|
|
glVertex(astro::equatorialToCelestialCart(ra, dec, radius));
|
|
|
|
}
|
|
|
|
glEnd();
|
|
|
|
}
|
|
|
|
|
2006-11-05 02:08:44 -07:00
|
|
|
Mat3f m = conjugate(observer.getOrientation()).toMatrix3();
|
2001-11-27 18:50:04 -07:00
|
|
|
for (i = 0; i < nCoordLabels; i++)
|
|
|
|
{
|
|
|
|
Point3f pos = astro::equatorialToCelestialCart(coordLabels[i].ra,
|
|
|
|
coordLabels[i].dec,
|
|
|
|
radius);
|
2006-11-05 02:08:44 -07:00
|
|
|
if ((pos * m).z < 0)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2002-07-25 23:41:25 -06:00
|
|
|
addLabel(coordLabels[i].label, Color(0.3f, 0.7f, 0.7f, 0.85f), pos);
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::labelConstellations(const AsterismList& asterisms,
|
|
|
|
const Observer& observer)
|
|
|
|
{
|
|
|
|
Point3f observerPos = (Point3f) observer.getPosition();
|
|
|
|
|
|
|
|
for (AsterismList::const_iterator iter = asterisms.begin();
|
|
|
|
iter != asterisms.end(); iter++)
|
|
|
|
{
|
|
|
|
Asterism* ast = *iter;
|
|
|
|
if (ast->getChainCount() > 0)
|
|
|
|
{
|
|
|
|
const Asterism::Chain& chain = ast->getChain(0);
|
|
|
|
|
|
|
|
if (chain.size() > 0)
|
|
|
|
{
|
|
|
|
// The constellation label is positioned at the average
|
|
|
|
// position of all stars in the first chain. This usually
|
|
|
|
// gives reasonable results.
|
|
|
|
Vec3f avg(0, 0, 0);
|
|
|
|
for (Asterism::Chain::const_iterator iter = chain.begin();
|
|
|
|
iter != chain.end(); iter++)
|
|
|
|
avg += (*iter - Point3f(0, 0, 0));
|
|
|
|
|
|
|
|
avg = avg / (float) chain.size();
|
2002-03-18 17:17:25 -07:00
|
|
|
avg = avg * 1e6f;
|
2001-11-27 18:50:04 -07:00
|
|
|
Vec3f rpos = Point3f(avg.x, avg.y, avg.z) - observerPos;
|
2005-11-18 02:00:39 -07:00
|
|
|
if ((rpos * conjugate(observer.getOrientation()).toMatrix3()).z < 0)
|
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
addLabel(ast->getName((labelMode & I18nConstellationLabels) != 0),
|
2001-11-27 18:50:04 -07:00
|
|
|
Color(0.5f, 0.0f, 1.0f, 1.0f),
|
|
|
|
Point3f(rpos.x, rpos.y, rpos.z));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Renderer::renderParticles(const vector<Particle>& particles,
|
|
|
|
Quatf orientation)
|
|
|
|
{
|
|
|
|
int nParticles = particles.size();
|
|
|
|
|
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
Mat3f m = orientation.toMatrix3();
|
|
|
|
Vec3f v0 = Vec3f(-1, -1, 0) * m;
|
|
|
|
Vec3f v1 = Vec3f( 1, -1, 0) * m;
|
|
|
|
Vec3f v2 = Vec3f( 1, 1, 0) * m;
|
|
|
|
Vec3f v3 = Vec3f(-1, 1, 0) * m;
|
2001-11-27 18:50:04 -07:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
glBegin(GL_QUADS);
|
|
|
|
for (int i = 0; i < nParticles; i++)
|
|
|
|
{
|
|
|
|
Point3f center = particles[i].center;
|
|
|
|
float size = particles[i].size;
|
|
|
|
|
|
|
|
glColor(particles[i].color);
|
|
|
|
glTexCoord2f(0, 1);
|
|
|
|
glVertex(center + (v0 * size));
|
|
|
|
glTexCoord2f(1, 1);
|
|
|
|
glVertex(center + (v1 * size));
|
|
|
|
glTexCoord2f(1, 0);
|
|
|
|
glVertex(center + (v2 * size));
|
|
|
|
glTexCoord2f(0, 0);
|
|
|
|
glVertex(center + (v3 * size));
|
|
|
|
}
|
|
|
|
glEnd();
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
void Renderer::renderLabels(FontStyle fs)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
if (font[fs] == NULL)
|
2001-11-27 18:50:04 -07:00
|
|
|
return;
|
|
|
|
|
2004-05-18 11:31:26 -06:00
|
|
|
//glEnable(GL_DEPTH_TEST);
|
2001-11-27 18:50:04 -07:00
|
|
|
glEnable(GL_TEXTURE_2D);
|
2006-03-08 11:29:10 -07:00
|
|
|
font[fs]->bind();
|
2001-11-27 18:50:04 -07:00
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadIdentity();
|
|
|
|
gluOrtho2D(0, windowWidth, 0, windowHeight);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadIdentity();
|
2004-02-28 02:30:17 -07:00
|
|
|
glTranslatef(GLfloat((int) (windowWidth / 2)),
|
|
|
|
GLfloat((int) (windowHeight / 2)), 0);
|
2001-11-27 18:50:04 -07:00
|
|
|
|
|
|
|
for (int i = 0; i < (int) labels.size(); i++)
|
|
|
|
{
|
|
|
|
glColor(labels[i].color);
|
|
|
|
glPushMatrix();
|
2004-02-28 02:30:17 -07:00
|
|
|
glTranslatef((int) labels[i].position.x + PixelOffset + 2.0f,
|
2001-11-27 18:50:04 -07:00
|
|
|
(int) labels[i].position.y + PixelOffset,
|
2004-05-18 11:31:26 -06:00
|
|
|
0.0f);
|
2006-12-03 15:13:14 -07:00
|
|
|
// EK TODO: Check where to replace (see '_(' above)
|
|
|
|
font[fs]->render(labels[i].text);
|
2001-11-27 18:50:04 -07:00
|
|
|
glPopMatrix();
|
|
|
|
}
|
|
|
|
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-18 11:31:26 -06:00
|
|
|
vector<Renderer::Label>::iterator
|
2006-03-08 11:29:10 -07:00
|
|
|
Renderer::renderSortedLabels(vector<Label>::iterator iter, float depth, FontStyle fs)
|
2004-05-18 11:31:26 -06:00
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
if (font[fs] == NULL)
|
2004-05-18 11:31:26 -06:00
|
|
|
return iter;
|
|
|
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
2006-03-08 11:29:10 -07:00
|
|
|
font[fs]->bind();
|
2004-05-18 11:31:26 -06:00
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadIdentity();
|
|
|
|
gluOrtho2D(0, windowWidth, 0, windowHeight);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadIdentity();
|
|
|
|
glTranslatef(GLfloat((int) (windowWidth / 2)),
|
|
|
|
GLfloat((int) (windowHeight / 2)), 0);
|
|
|
|
|
|
|
|
for (; iter != depthSortedLabels.end() && iter->position.z > depth; iter++)
|
|
|
|
{
|
|
|
|
glColor(iter->color);
|
|
|
|
glPushMatrix();
|
|
|
|
glTranslatef((int) iter->position.x + PixelOffset + 2.0f,
|
|
|
|
(int) iter->position.y + PixelOffset,
|
|
|
|
0.0f);
|
2006-03-08 11:29:10 -07:00
|
|
|
font[fs]->render(iter->text);
|
2004-05-18 11:31:26 -06:00
|
|
|
glPopMatrix();
|
|
|
|
}
|
|
|
|
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
|
|
|
|
return iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-31 21:42:21 -07:00
|
|
|
void Renderer::renderMarkers(const MarkerList& markers,
|
|
|
|
const UniversalCoord& position,
|
|
|
|
const Quatf& orientation,
|
|
|
|
double jd)
|
|
|
|
{
|
|
|
|
double identity4x4[16] = { 1.0, 0.0, 0.0, 0.0,
|
|
|
|
0.0, 1.0, 0.0, 0.0,
|
|
|
|
0.0, 0.0, 1.0, 0.0,
|
|
|
|
0.0, 0.0, 0.0, 1.0
|
|
|
|
};
|
|
|
|
int view[4] = { 0, 0, 0, 0 };
|
|
|
|
view[0] = -windowWidth / 2;
|
|
|
|
view[1] = -windowHeight / 2;
|
|
|
|
view[2] = windowWidth;
|
|
|
|
view[3] = windowHeight;
|
|
|
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadIdentity();
|
|
|
|
gluOrtho2D(0, windowWidth, 0, windowHeight);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadIdentity();
|
2004-02-28 02:30:17 -07:00
|
|
|
glTranslatef(GLfloat((int) (windowWidth / 2)),
|
|
|
|
GLfloat((int) (windowHeight / 2)), 0);
|
2003-01-31 21:42:21 -07:00
|
|
|
|
|
|
|
Mat3f rot = conjugate(orientation).toMatrix3();
|
|
|
|
|
|
|
|
for (MarkerList::const_iterator iter = markers.begin();
|
|
|
|
iter != markers.end(); iter++)
|
|
|
|
{
|
|
|
|
UniversalCoord uc = iter->getPosition(jd);
|
|
|
|
Vec3d offset = uc - position;
|
2004-02-28 02:30:17 -07:00
|
|
|
Vec3f eyepos = Vec3f((float) offset.x, (float) offset.y, (float) offset.z) * rot;
|
2003-01-31 21:42:21 -07:00
|
|
|
eyepos.normalize();
|
|
|
|
eyepos *= 1000.0f;
|
|
|
|
|
|
|
|
double winX, winY, winZ;
|
|
|
|
if (gluProject(eyepos.x, eyepos.y, eyepos.z,
|
|
|
|
identity4x4,
|
|
|
|
projMatrix,
|
|
|
|
(const GLint*) view,
|
|
|
|
&winX, &winY, &winZ) != GL_FALSE)
|
|
|
|
{
|
|
|
|
if (eyepos.z < 0.0f)
|
|
|
|
{
|
|
|
|
glPushMatrix();
|
2004-02-28 02:30:17 -07:00
|
|
|
glTranslatef((GLfloat) winX, (GLfloat) winY, 0.0f);
|
2003-01-31 21:42:21 -07:00
|
|
|
|
|
|
|
glColor(iter->getColor());
|
2003-02-25 01:03:27 -07:00
|
|
|
iter->render();
|
2003-01-31 21:42:21 -07:00
|
|
|
|
|
|
|
glPopMatrix();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-07-08 09:31:34 -06:00
|
|
|
void Renderer::setStarStyle(StarStyle style)
|
|
|
|
{
|
|
|
|
starStyle = style;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Renderer::StarStyle Renderer::getStarStyle() const
|
|
|
|
{
|
|
|
|
return starStyle;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
Renderer::StarVertexBuffer::StarVertexBuffer(unsigned int _capacity) :
|
|
|
|
capacity(_capacity),
|
|
|
|
vertices(NULL),
|
|
|
|
texCoords(NULL),
|
2006-03-08 11:29:10 -07:00
|
|
|
colors(NULL)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
|
|
|
nStars = 0;
|
|
|
|
vertices = new float[capacity * 12];
|
|
|
|
texCoords = new float[capacity * 8];
|
|
|
|
colors = new unsigned char[capacity * 16];
|
|
|
|
|
|
|
|
// Fill the texture coordinate array now, since it will always have
|
|
|
|
// the same contents.
|
2001-12-18 16:00:26 -07:00
|
|
|
for (unsigned int i = 0; i < capacity; i++)
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2001-12-18 16:00:26 -07:00
|
|
|
unsigned int n = i * 8;
|
2001-11-27 18:50:04 -07:00
|
|
|
texCoords[n ] = 0; texCoords[n + 1] = 0;
|
|
|
|
texCoords[n + 2] = 1; texCoords[n + 3] = 0;
|
|
|
|
texCoords[n + 4] = 1; texCoords[n + 5] = 1;
|
|
|
|
texCoords[n + 6] = 0; texCoords[n + 7] = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Renderer::StarVertexBuffer::~StarVertexBuffer()
|
|
|
|
{
|
|
|
|
if (vertices != NULL)
|
2006-03-08 11:29:10 -07:00
|
|
|
delete[] vertices;
|
2001-11-27 18:50:04 -07:00
|
|
|
if (colors != NULL)
|
2006-03-08 11:29:10 -07:00
|
|
|
delete[] colors;
|
2001-11-27 18:50:04 -07:00
|
|
|
if (texCoords != NULL)
|
2006-03-08 11:29:10 -07:00
|
|
|
delete[] texCoords;
|
2001-11-27 18:50:04 -07:00
|
|
|
}
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
void Renderer::StarVertexBuffer::start()
|
2001-11-27 18:50:04 -07:00
|
|
|
{
|
2002-02-28 15:12:26 -07:00
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glVertexPointer(3, GL_FLOAT, 0, vertices);
|
|
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
|
2006-03-08 11:29:10 -07:00
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
|
2002-02-28 15:12:26 -07:00
|
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::StarVertexBuffer::render()
|
|
|
|
{
|
|
|
|
if (nStars != 0)
|
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
glDrawArrays(GL_QUADS, 0, nStars * 4);
|
2001-11-27 18:50:04 -07:00
|
|
|
nStars = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-25 13:29:32 -07:00
|
|
|
void Renderer::StarVertexBuffer::finish()
|
|
|
|
{
|
|
|
|
render();
|
|
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
}
|
|
|
|
|
2001-11-27 18:50:04 -07:00
|
|
|
void Renderer::StarVertexBuffer::addStar(const Point3f& pos,
|
|
|
|
const Color& color,
|
|
|
|
float size)
|
2005-11-25 15:57:20 -07:00
|
|
|
{
|
|
|
|
if (nStars < capacity)
|
|
|
|
{
|
|
|
|
int n = nStars * 12;
|
|
|
|
vertices[n + 0] = pos.x + v0.x * size;
|
|
|
|
vertices[n + 1] = pos.y + v0.y * size;
|
|
|
|
vertices[n + 2] = pos.z + v0.z * size;
|
|
|
|
vertices[n + 3] = pos.x + v1.x * size;
|
|
|
|
vertices[n + 4] = pos.y + v1.y * size;
|
|
|
|
vertices[n + 5] = pos.z + v1.z * size;
|
|
|
|
vertices[n + 6] = pos.x + v2.x * size;
|
|
|
|
vertices[n + 7] = pos.y + v2.y * size;
|
|
|
|
vertices[n + 8] = pos.z + v2.z * size;
|
|
|
|
vertices[n + 9] = pos.x + v3.x * size;
|
|
|
|
vertices[n + 10] = pos.y + v3.y * size;
|
|
|
|
vertices[n + 11] = pos.z + v3.z * size;
|
|
|
|
n = nStars * 16;
|
|
|
|
color.get(colors + n);
|
|
|
|
color.get(colors + n + 4);
|
|
|
|
color.get(colors + n + 8);
|
|
|
|
color.get(colors + n + 12);
|
2006-03-08 11:29:10 -07:00
|
|
|
|
2005-11-25 15:57:20 -07:00
|
|
|
nStars++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nStars == capacity)
|
|
|
|
{
|
|
|
|
render();
|
|
|
|
nStars = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
void Renderer::StarVertexBuffer::setBillboardOrientation(const Quatf& q)
|
2005-11-25 15:57:20 -07:00
|
|
|
{
|
|
|
|
Mat3f m = q.toMatrix3();
|
|
|
|
v0 = Vec3f(-1, -1, 0) * m;
|
|
|
|
v1 = Vec3f( 1, -1, 0) * m;
|
|
|
|
v2 = Vec3f( 1, 1, 0) * m;
|
|
|
|
v3 = Vec3f(-1, 1, 0) * m;
|
|
|
|
}
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2005-11-25 15:57:20 -07:00
|
|
|
|
|
|
|
Renderer::PointStarVertexBuffer::PointStarVertexBuffer(unsigned int _capacity) :
|
|
|
|
capacity(_capacity),
|
|
|
|
nStars(0),
|
|
|
|
vertices(NULL),
|
2006-03-08 11:29:10 -07:00
|
|
|
context(NULL),
|
|
|
|
useSprites(false),
|
|
|
|
texture(NULL)
|
2005-11-25 15:57:20 -07:00
|
|
|
{
|
|
|
|
vertices = new StarVertex[capacity];
|
|
|
|
}
|
|
|
|
|
|
|
|
Renderer::PointStarVertexBuffer::~PointStarVertexBuffer()
|
|
|
|
{
|
|
|
|
if (vertices != NULL)
|
|
|
|
delete[] vertices;
|
|
|
|
}
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
void Renderer::PointStarVertexBuffer::startSprites(const GLContext& _context)
|
|
|
|
{
|
|
|
|
context = &_context;
|
2006-04-20 10:32:05 -06:00
|
|
|
assert(context->getVertexProcessor() != NULL || !useSprites); // vertex shaders required for new star rendering
|
2006-09-12 06:56:47 -06:00
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
unsigned int stride = sizeof(StarVertex);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
|
|
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
|
|
|
|
|
|
|
|
VertexProcessor* vproc = context->getVertexProcessor();
|
|
|
|
vproc->enable();
|
|
|
|
vproc->use(vp::starDisc);
|
|
|
|
vproc->enableAttribArray(6);
|
|
|
|
vproc->attribArray(6, 1, GL_FLOAT, stride, &vertices[0].size);
|
|
|
|
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
|
|
|
|
|
|
glEnable(GL_POINT_SPRITE_ARB);
|
|
|
|
glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
|
|
|
|
|
|
|
|
useSprites = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::PointStarVertexBuffer::startPoints(const GLContext& _context)
|
2005-11-25 15:57:20 -07:00
|
|
|
{
|
|
|
|
context = &_context;
|
|
|
|
|
|
|
|
unsigned int stride = sizeof(StarVertex);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
|
|
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
|
|
|
|
|
|
|
|
// An option to control the size of the stars would be helpful.
|
|
|
|
// Which size looks best depends a lot on the resolution and the
|
|
|
|
// type of display device.
|
|
|
|
// glPointSize(2.0f);
|
|
|
|
// glEnable(GL_POINT_SMOOTH);
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
useSprites = false;
|
2005-11-25 15:57:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::PointStarVertexBuffer::render()
|
|
|
|
{
|
|
|
|
if (nStars != 0)
|
|
|
|
{
|
2006-03-08 11:29:10 -07:00
|
|
|
unsigned int stride = sizeof(StarVertex);
|
|
|
|
if (useSprites)
|
|
|
|
{
|
|
|
|
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
2006-03-15 11:46:34 -07:00
|
|
|
glPointSize(1.0f);
|
2006-03-08 11:29:10 -07:00
|
|
|
}
|
|
|
|
glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
|
|
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
|
|
|
|
|
2006-04-20 10:32:05 -06:00
|
|
|
if (useSprites)
|
|
|
|
{
|
|
|
|
VertexProcessor* vproc = context->getVertexProcessor();
|
|
|
|
vproc->attribArray(6, 1, GL_FLOAT, stride, &vertices[0].size);
|
|
|
|
}
|
2006-03-08 11:29:10 -07:00
|
|
|
|
|
|
|
if (texture != NULL)
|
|
|
|
texture->bind();
|
2005-11-25 15:57:20 -07:00
|
|
|
glDrawArrays(GL_POINTS, 0, nStars);
|
|
|
|
nStars = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::PointStarVertexBuffer::finish()
|
|
|
|
{
|
|
|
|
render();
|
|
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
2006-03-08 11:29:10 -07:00
|
|
|
|
|
|
|
if (useSprites)
|
|
|
|
{
|
|
|
|
VertexProcessor* vproc = context->getVertexProcessor();
|
|
|
|
vproc->disableAttribArray(6);
|
|
|
|
vproc->disable();
|
|
|
|
|
|
|
|
glDisable(GL_POINT_SPRITE_ARB);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
}
|
2005-11-25 15:57:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::PointStarVertexBuffer::addStar(const Point3f& pos,
|
|
|
|
const Color& color,
|
|
|
|
float size)
|
|
|
|
{
|
|
|
|
if (nStars < capacity)
|
|
|
|
{
|
|
|
|
vertices[nStars].position = pos;
|
|
|
|
vertices[nStars].size = size;
|
|
|
|
color.get(vertices[nStars].color);
|
|
|
|
nStars++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nStars == capacity)
|
|
|
|
{
|
|
|
|
render();
|
|
|
|
nStars = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-08 11:29:10 -07:00
|
|
|
void Renderer::PointStarVertexBuffer::setTexture(Texture* _texture)
|
|
|
|
{
|
|
|
|
texture = _texture;
|
|
|
|
}
|
|
|
|
|
2005-11-25 15:57:20 -07:00
|
|
|
|
2002-05-02 01:57:38 -06:00
|
|
|
void Renderer::loadTextures(Body* body)
|
|
|
|
{
|
|
|
|
Surface& surface = body->getSurface();
|
|
|
|
|
|
|
|
if (surface.baseTexture.tex[textureResolution] != InvalidResource)
|
|
|
|
surface.baseTexture.find(textureResolution);
|
|
|
|
if ((surface.appearanceFlags & Surface::ApplyBumpMap) != 0 &&
|
2003-02-19 10:48:25 -07:00
|
|
|
context->bumpMappingSupported() &&
|
2002-05-02 01:57:38 -06:00
|
|
|
surface.bumpTexture.tex[textureResolution] != InvalidResource)
|
|
|
|
surface.bumpTexture.find(textureResolution);
|
|
|
|
if ((surface.appearanceFlags & Surface::ApplyNightMap) != 0 &&
|
|
|
|
(renderFlags & ShowNightMaps) != 0)
|
|
|
|
surface.nightTexture.find(textureResolution);
|
2004-06-05 06:31:39 -06:00
|
|
|
if ((surface.appearanceFlags & Surface::SeparateSpecularMap) != 0 &&
|
|
|
|
surface.specularTexture.tex[textureResolution] != InvalidResource)
|
|
|
|
surface.specularTexture.find(textureResolution);
|
2002-05-02 01:57:38 -06:00
|
|
|
|
|
|
|
if ((renderFlags & ShowCloudMaps) != 0 &&
|
|
|
|
body->getAtmosphere() != NULL &&
|
|
|
|
body->getAtmosphere()->cloudTexture.tex[textureResolution] != InvalidResource)
|
|
|
|
{
|
|
|
|
body->getAtmosphere()->cloudTexture.find(textureResolution);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body->getRings() != NULL &&
|
|
|
|
body->getRings()->texture.tex[textureResolution] != InvalidResource)
|
|
|
|
{
|
|
|
|
body->getRings()->texture.find(textureResolution);
|
|
|
|
}
|
|
|
|
}
|