2451 lines
60 KiB
C++
2451 lines
60 KiB
C++
/**
|
|
* @file llvosky.cpp
|
|
* @brief LLVOSky class implementation
|
|
*
|
|
* Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
|
|
* $License$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "llvosky.h"
|
|
|
|
#include "imageids.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llframetimer.h"
|
|
#include "timing.h"
|
|
|
|
#include "llagent.h"
|
|
#include "lldrawable.h"
|
|
#include "llface.h"
|
|
#include "llcubemap.h"
|
|
#include "lldrawpoolsky.h"
|
|
#include "lldrawpoolwater.h"
|
|
#include "llglheaders.h"
|
|
#include "llsky.h"
|
|
#include "llviewercamera.h"
|
|
#include "llviewerimagelist.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llviewerregion.h"
|
|
#include "llworld.h"
|
|
#include "pipeline.h"
|
|
#include "viewer.h" // for gSunTextureID
|
|
|
|
const S32 NUM_TILES_X = 8;
|
|
const S32 NUM_TILES_Y = 4;
|
|
const S32 NUM_TILES = NUM_TILES_X * NUM_TILES_Y;
|
|
|
|
// Heavenly body constants
|
|
const F32 SUN_DISK_RADIUS = 0.5f;
|
|
const F32 MOON_DISK_RADIUS = SUN_DISK_RADIUS * 0.9f;
|
|
const F32 SUN_INTENSITY = 1e5;
|
|
const F32 SUN_DISK_INTENSITY = 24.f;
|
|
|
|
|
|
// Texture coordinates:
|
|
const LLVector2 TEX00 = LLVector2(0.f, 0.f);
|
|
const LLVector2 TEX01 = LLVector2(0.f, 1.f);
|
|
const LLVector2 TEX10 = LLVector2(1.f, 0.f);
|
|
const LLVector2 TEX11 = LLVector2(1.f, 1.f);
|
|
|
|
//static
|
|
LLColor3 LLHaze::sAirScaSeaLevel;
|
|
|
|
class LLFastLn
|
|
{
|
|
public:
|
|
LLFastLn()
|
|
{
|
|
mTable[0] = 0;
|
|
for( S32 i = 1; i < 257; i++ )
|
|
{
|
|
mTable[i] = log((F32)i);
|
|
}
|
|
}
|
|
|
|
F32 ln( F32 x )
|
|
{
|
|
const F32 OO_255 = 0.003921568627450980392156862745098f;
|
|
const F32 LN_255 = 5.5412635451584261462455391880218f;
|
|
|
|
if( x < OO_255 )
|
|
{
|
|
return log(x);
|
|
}
|
|
else
|
|
if( x < 1 )
|
|
{
|
|
x *= 255.f;
|
|
S32 index = llfloor(x);
|
|
F32 t = x - index;
|
|
F32 low = mTable[index];
|
|
F32 high = mTable[index + 1];
|
|
return low + t * (high - low) - LN_255;
|
|
}
|
|
else
|
|
if( x <= 255 )
|
|
{
|
|
S32 index = llfloor(x);
|
|
F32 t = x - index;
|
|
F32 low = mTable[index];
|
|
F32 high = mTable[index + 1];
|
|
return low + t * (high - low);
|
|
}
|
|
else
|
|
{
|
|
return log( x );
|
|
}
|
|
}
|
|
|
|
F32 pow( F32 x, F32 y )
|
|
{
|
|
return (F32)LL_FAST_EXP(y * ln(x));
|
|
}
|
|
|
|
|
|
private:
|
|
F32 mTable[257]; // index 0 is unused
|
|
};
|
|
|
|
LLFastLn gFastLn;
|
|
|
|
|
|
// Functions used a lot.
|
|
|
|
inline F32 LLHaze::calcPhase(const F32 cos_theta) const
|
|
{
|
|
const F32 g2 = mG * mG;
|
|
const F32 den = 1 + g2 - 2 * mG * cos_theta;
|
|
return (1 - g2) * gFastLn.pow(den, -1.5);
|
|
}
|
|
|
|
inline void color_pow(LLColor3 &col, const F32 e)
|
|
{
|
|
col.mV[0] = gFastLn.pow(col.mV[0], e);
|
|
col.mV[1] = gFastLn.pow(col.mV[1], e);
|
|
col.mV[2] = gFastLn.pow(col.mV[2], e);
|
|
}
|
|
|
|
inline LLColor3 color_norm(const LLColor3 &col)
|
|
{
|
|
const F32 m = color_max(col);
|
|
if (m > 1.f)
|
|
{
|
|
return 1.f/m * col;
|
|
}
|
|
else return col;
|
|
}
|
|
|
|
inline LLColor3 color_norm_fog(const LLColor3 &col)
|
|
{
|
|
const F32 m = color_max(col);
|
|
if (m > 0.75f)
|
|
{
|
|
return 0.75f/m * col;
|
|
}
|
|
else return col;
|
|
}
|
|
|
|
|
|
inline LLColor4 color_norm_abs(const LLColor4 &col)
|
|
{
|
|
const F32 m = color_max(col);
|
|
if (m > 1e-6)
|
|
{
|
|
return 1.f/m * col;
|
|
}
|
|
else
|
|
{
|
|
return col;
|
|
}
|
|
}
|
|
|
|
|
|
inline F32 color_intens ( const LLColor4 &col )
|
|
{
|
|
return col.mV[0] + col.mV[1] + col.mV[2];
|
|
}
|
|
|
|
|
|
inline F32 color_avg ( const LLColor3 &col )
|
|
{
|
|
return color_intens(col) / 3.f;
|
|
}
|
|
|
|
inline void color_gamma_correct(LLColor3 &col)
|
|
{
|
|
const F32 gamma_inv = 1.f/1.2f;
|
|
if (col.mV[0] != 0.f)
|
|
{
|
|
col.mV[0] = gFastLn.pow(col.mV[0], gamma_inv);
|
|
}
|
|
if (col.mV[1] != 0.f)
|
|
{
|
|
col.mV[1] = gFastLn.pow(col.mV[1], gamma_inv);
|
|
}
|
|
if (col.mV[2] != 0.f)
|
|
{
|
|
col.mV[2] = gFastLn.pow(col.mV[2], gamma_inv);
|
|
}
|
|
}
|
|
|
|
inline F32 min_intens_factor( LLColor3& col, F32 min_intens, BOOL postmultiply = FALSE);
|
|
inline F32 min_intens_factor( LLColor3& col, F32 min_intens, BOOL postmultiply)
|
|
{
|
|
const F32 intens = color_intens(col);
|
|
F32 factor = 1;
|
|
if (0 == intens)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (intens < min_intens)
|
|
{
|
|
factor = min_intens / intens;
|
|
if (postmultiply)
|
|
col *= factor;
|
|
}
|
|
return factor;
|
|
}
|
|
|
|
inline LLVector3 move_vec(const LLVector3& v, const F32 cos_max_angle)
|
|
{
|
|
LLVector3 v_norm = v;
|
|
v_norm.normVec();
|
|
|
|
LLVector2 v_norm_proj(v_norm.mV[0], v_norm.mV[1]);
|
|
const F32 projection2 = v_norm_proj.magVecSquared();
|
|
const F32 scale = sqrt((1 - cos_max_angle * cos_max_angle) / projection2);
|
|
return LLVector3(scale * v_norm_proj.mV[0], scale * v_norm_proj.mV[1], cos_max_angle);
|
|
}
|
|
|
|
|
|
/***************************************
|
|
Transparency Map
|
|
***************************************/
|
|
|
|
void LLTranspMap::init(const F32 elev, const F32 step, const F32 h, const LLHaze* const haze)
|
|
{
|
|
mHaze = haze;
|
|
mAtmHeight = h;
|
|
mElevation = elev;
|
|
mStep = step;
|
|
mStepInv = 1.f / step;
|
|
F32 sin_angle = EARTH_RADIUS/(EARTH_RADIUS + mElevation);
|
|
mCosMaxAngle = -sqrt(1 - sin_angle * sin_angle);
|
|
mMapSize = S32(ceil((1 - mCosMaxAngle) * mStepInv + 1) + 0.5);
|
|
delete mT;
|
|
mT = new LLColor3[mMapSize];
|
|
|
|
for (S32 i = 0; i < mMapSize; ++i)
|
|
{
|
|
const F32 cos_a = 1 - i*mStep;
|
|
const LLVector3 dir(0, sqrt(1-cos_a*cos_a), cos_a);
|
|
mT[i] = calcAirTranspDir(mElevation, dir);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
LLColor3 LLTranspMap::calcAirTranspDir(const F32 elevation, const LLVector3 &dir) const
|
|
{
|
|
LLColor3 opt_depth(0, 0, 0);
|
|
const LLVector3 point(0, 0, EARTH_RADIUS + elevation);
|
|
F32 dist = -dir * point;
|
|
LLVector3 cur_point;
|
|
S32 s;
|
|
|
|
if (dist > 0)
|
|
{
|
|
cur_point = point + dist * dir;
|
|
// const F32 K = log(dist * INV_FIRST_STEP + 1) * INV_NO_STEPS;
|
|
// const F32 e_pow_k = LL_FAST_EXP(K);
|
|
const F32 e_pow_k = gFastLn.pow( dist * INV_FIRST_STEP + 1, INV_NO_STEPS );
|
|
F32 step = FIRST_STEP * (1 - 1 / e_pow_k);
|
|
|
|
for (s = 0; s < NO_STEPS; ++s)
|
|
{
|
|
const F32 h = cur_point.magVec() - EARTH_RADIUS;
|
|
step *= e_pow_k;
|
|
opt_depth += calcSigExt(h) * step;
|
|
cur_point -= dir * step;
|
|
}
|
|
opt_depth *= 2;
|
|
cur_point = point + 2 * dist * dir;
|
|
}
|
|
else
|
|
{
|
|
cur_point = point;
|
|
}
|
|
|
|
dist = hitsAtmEdge(cur_point, dir);
|
|
// const F32 K = log(dist * INV_FIRST_STEP + 1) * INV_NO_STEPS;
|
|
// const F32 e_pow_k = LL_FAST_EXP(K);
|
|
const F32 e_pow_k = gFastLn.pow( dist * INV_FIRST_STEP + 1, INV_NO_STEPS );
|
|
F32 step = FIRST_STEP * (1 - 1 / e_pow_k);
|
|
|
|
for (s = 0; s < NO_STEPS; ++s)
|
|
{
|
|
const F32 h = cur_point.magVec() - EARTH_RADIUS;
|
|
step *= e_pow_k;
|
|
opt_depth += calcSigExt(h) * step;
|
|
cur_point += dir * step;
|
|
}
|
|
|
|
opt_depth *= -4.0f*F_PI;
|
|
opt_depth.exp();
|
|
return opt_depth;
|
|
}
|
|
|
|
|
|
|
|
F32 LLTranspMap::hitsAtmEdge(const LLVector3& X, const LLVector3& dir) const
|
|
{
|
|
const F32 tca = -dir * X;
|
|
const F32 R = EARTH_RADIUS + mAtmHeight;
|
|
const F32 thc2 = R * R - X.magVecSquared() + tca * tca;
|
|
return tca + sqrt ( thc2 );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LLTranspMapSet::init(const S32 size, const F32 first_step, const F32 media_height, const LLHaze* const haze)
|
|
{
|
|
const F32 angle_step = 0.005f;
|
|
mSize = size;
|
|
mMediaHeight = media_height;
|
|
|
|
delete[] mTransp;
|
|
mTransp = new LLTranspMap[mSize];
|
|
|
|
delete[] mHeights;
|
|
mHeights = new F32[mSize];
|
|
|
|
F32 h = 0;
|
|
mHeights[0] = h;
|
|
mTransp[0].init(h, angle_step, mMediaHeight, haze);
|
|
const F32 K = log(mMediaHeight / first_step + 1) / (mSize - 1);
|
|
const F32 e_pow_k = exp(K);
|
|
F32 step = first_step * (e_pow_k - 1);
|
|
|
|
for (S32 s = 1; s < mSize; ++s)
|
|
{
|
|
h += step;
|
|
mHeights[s] = h;
|
|
mTransp[s].init(h, angle_step, mMediaHeight, haze);
|
|
step *= e_pow_k;
|
|
}
|
|
}
|
|
|
|
LLTranspMapSet::~LLTranspMapSet()
|
|
{
|
|
delete[] mTransp;
|
|
mTransp = NULL;
|
|
delete[] mHeights;
|
|
mHeights = NULL;
|
|
}
|
|
|
|
|
|
|
|
/***************************************
|
|
SkyTex
|
|
***************************************/
|
|
|
|
S32 LLSkyTex::sComponents = 4;
|
|
S32 LLSkyTex::sResolution = 64;
|
|
F32 LLSkyTex::sInterpVal = 0.f;
|
|
S32 LLSkyTex::sCurrent = 0;
|
|
|
|
|
|
LLSkyTex::LLSkyTex()
|
|
{
|
|
}
|
|
|
|
void LLSkyTex::init()
|
|
{
|
|
mSkyData = new LLColor3[sResolution * sResolution];
|
|
mSkyDirs = new LLVector3[sResolution * sResolution];
|
|
|
|
for (S32 i = 0; i < 2; ++i)
|
|
{
|
|
mImageGL[i] = new LLImageGL(FALSE);
|
|
mImageGL[i]->setClamp(TRUE, TRUE);
|
|
mImageRaw[i] = new LLImageRaw(sResolution, sResolution, sComponents);
|
|
|
|
initEmpty(i);
|
|
}
|
|
}
|
|
|
|
void LLSkyTex::cleanupGL()
|
|
{
|
|
mImageGL[0] = NULL;
|
|
mImageGL[1] = NULL;
|
|
}
|
|
|
|
void LLSkyTex::restoreGL()
|
|
{
|
|
for (S32 i = 0; i < 2; i++)
|
|
{
|
|
mImageGL[i] = new LLImageGL(FALSE);
|
|
mImageGL[i]->setClamp(TRUE, TRUE);
|
|
}
|
|
}
|
|
|
|
LLSkyTex::~LLSkyTex()
|
|
{
|
|
delete[] mSkyData;
|
|
mSkyData = NULL;
|
|
|
|
delete[] mSkyDirs;
|
|
mSkyDirs = NULL;
|
|
}
|
|
|
|
|
|
void LLSkyTex::initEmpty(const S32 tex)
|
|
{
|
|
U8* data = mImageRaw[tex]->getData();
|
|
for (S32 i = 0; i < sResolution; ++i)
|
|
{
|
|
for (S32 j = 0; j < sResolution; ++j)
|
|
{
|
|
const S32 basic_offset = (i * sResolution + j);
|
|
S32 offset = basic_offset * sComponents;
|
|
data[offset] = 0;
|
|
data[offset+1] = 0;
|
|
data[offset+2] = 0;
|
|
data[offset+3] = 255;
|
|
|
|
mSkyData[basic_offset].setToBlack();
|
|
}
|
|
}
|
|
|
|
createGLImage(tex);
|
|
}
|
|
|
|
|
|
void LLSkyTex::create(const F32 brightness_scale, const LLColor3& multiscatt)
|
|
{
|
|
U8* data = mImageRaw[sCurrent]->getData();
|
|
for (S32 i = 0; i < sResolution; ++i)
|
|
{
|
|
for (S32 j = 0; j < sResolution; ++j)
|
|
{
|
|
const S32 basic_offset = (i * sResolution + j);
|
|
S32 offset = basic_offset * sComponents;
|
|
LLColor3 col(mSkyData[basic_offset]);
|
|
if (getDir(i, j).mV[VZ] >= -0.02f) {
|
|
col += 0.1f * multiscatt;
|
|
col *= brightness_scale;
|
|
col.clamp();
|
|
color_gamma_correct(col);
|
|
}
|
|
|
|
U32* pix = (U32*)(data + offset);
|
|
LLColor4 temp = LLColor4(col, 0);
|
|
LLColor4U temp1 = LLColor4U(temp);
|
|
*pix = temp1.mAll;
|
|
}
|
|
}
|
|
createGLImage(sCurrent);
|
|
}
|
|
|
|
void LLSkyTex::createGLImage(S32 which)
|
|
{
|
|
mImageGL[which]->createGLTexture(0, mImageRaw[which]);
|
|
mImageGL[which]->setClamp(TRUE, TRUE);
|
|
}
|
|
|
|
void LLSkyTex::bindTexture(BOOL curr)
|
|
{
|
|
mImageGL[getWhich(curr)]->bind();
|
|
}
|
|
|
|
/***************************************
|
|
Sky
|
|
***************************************/
|
|
|
|
F32 LLHeavenBody::sInterpVal = 0;
|
|
|
|
F32 LLVOSky::sNighttimeBrightness = 1.5f;
|
|
|
|
S32 LLVOSky::sResolution = LLSkyTex::getResolution();
|
|
S32 LLVOSky::sTileResX = sResolution/NUM_TILES_X;
|
|
S32 LLVOSky::sTileResY = sResolution/NUM_TILES_Y;
|
|
|
|
LLVOSky::LLVOSky(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
|
|
: LLStaticViewerObject(id, pcode, regionp),
|
|
mSun(SUN_DISK_RADIUS), mMoon(MOON_DISK_RADIUS),
|
|
mBrightnessScale(1.f),
|
|
mBrightnessScaleNew(0.f),
|
|
mBrightnessScaleGuess(1.f),
|
|
mWeatherChange(FALSE),
|
|
mCloudDensity(0.2f),
|
|
mWind(0.f),
|
|
mForceUpdate(FALSE),
|
|
mWorldScale(1.f)
|
|
{
|
|
mInitialized = FALSE;
|
|
mbCanSelect = FALSE;
|
|
mUpdateTimer.reset();
|
|
|
|
for (S32 i = 0; i < 6; i++)
|
|
{
|
|
mSkyTex[i].init();
|
|
}
|
|
for (S32 i=0; i<FACE_COUNT; i++)
|
|
{
|
|
mFace[i] = NULL;
|
|
}
|
|
|
|
mCameraPosAgent = gAgent.getCameraPositionAgent();
|
|
mAtmHeight = ATM_HEIGHT;
|
|
mEarthCenter = LLVector3(mCameraPosAgent.mV[0], mCameraPosAgent.mV[1], -EARTH_RADIUS);
|
|
updateHaze();
|
|
|
|
mSunDefaultPosition = gSavedSettings.getVector3("SkySunDefaultPosition");
|
|
if (gSavedSettings.getBOOL("SkyOverrideSimSunPosition"))
|
|
{
|
|
initSunDirection(mSunDefaultPosition, LLVector3(0, 0, 0));
|
|
}
|
|
mAmbientScale = gSavedSettings.getF32("SkyAmbientScale");
|
|
mNightColorShift = gSavedSettings.getColor3("SkyNightColorShift");
|
|
mFogColor.mV[VRED] = mFogColor.mV[VGREEN] = mFogColor.mV[VBLUE] = 0.5f;
|
|
mFogColor.mV[VALPHA] = 0.0f;
|
|
mFogRatio = 1.2f;
|
|
|
|
mSun.setIntensity(SUN_INTENSITY);
|
|
mMoon.setIntensity(0.1f * SUN_INTENSITY);
|
|
|
|
mCubeMap = NULL;
|
|
|
|
mSunTexturep = gImageList.getImage(gSunTextureID, TRUE, TRUE);
|
|
mSunTexturep->setClamp(TRUE, TRUE);
|
|
mMoonTexturep = gImageList.getImage(gMoonTextureID, TRUE, TRUE);
|
|
mMoonTexturep->setClamp(TRUE, TRUE);
|
|
mBloomTexturep = gImageList.getImage(IMG_BLOOM1);
|
|
mBloomTexturep->setClamp(TRUE, TRUE);
|
|
}
|
|
|
|
|
|
LLVOSky::~LLVOSky()
|
|
{
|
|
// Don't delete images - it'll get deleted by gImageList on shutdown
|
|
// This needs to be done for each texture
|
|
|
|
delete mCubeMap;
|
|
mCubeMap = NULL;
|
|
}
|
|
|
|
void LLVOSky::initClass()
|
|
{
|
|
LLHaze::initClass();
|
|
}
|
|
|
|
|
|
void LLVOSky::init()
|
|
{
|
|
// index of refraction calculation.
|
|
mTransp.init(NO_STEPS+1+4, FIRST_STEP, mAtmHeight, &mHaze);
|
|
|
|
const F32 haze_int = color_intens(mHaze.calcSigSca(0));
|
|
mHazeConcentration = haze_int /
|
|
(color_intens(LLHaze::calcAirSca(0)) + haze_int);
|
|
|
|
mBrightnessScaleNew = 0;
|
|
|
|
// Initialize the cached normalized direction vectors
|
|
for (S32 side = 0; side < 6; ++side)
|
|
{
|
|
for (S32 tile = 0; tile < NUM_TILES; ++tile)
|
|
{
|
|
initSkyTextureDirs(side, tile);
|
|
createSkyTexture(side, tile);
|
|
}
|
|
}
|
|
|
|
calcBrightnessScaleAndColors();
|
|
initCubeMap();
|
|
}
|
|
|
|
void LLVOSky::initCubeMap()
|
|
{
|
|
std::vector<LLPointer<LLImageRaw> > images;
|
|
for (S32 side = 0; side < 6; side++)
|
|
{
|
|
images.push_back(mSkyTex[side].getImageRaw());
|
|
}
|
|
if (mCubeMap != NULL)
|
|
{
|
|
mCubeMap->init(images);
|
|
}
|
|
else if (gSavedSettings.getBOOL("RenderWater") && gGLManager.mHasCubeMap)
|
|
{
|
|
mCubeMap = new LLCubeMap();
|
|
mCubeMap->init(images);
|
|
}
|
|
}
|
|
|
|
|
|
void LLVOSky::cleanupGL()
|
|
{
|
|
S32 i;
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
mSkyTex[i].cleanupGL();
|
|
}
|
|
if (getCubeMap())
|
|
{
|
|
getCubeMap()->destroyGL();
|
|
}
|
|
}
|
|
|
|
void LLVOSky::restoreGL()
|
|
{
|
|
S32 i;
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
mSkyTex[i].restoreGL();
|
|
}
|
|
mSunTexturep = gImageList.getImage(gSunTextureID, TRUE, TRUE);
|
|
mSunTexturep->setClamp(TRUE, TRUE);
|
|
mMoonTexturep = gImageList.getImage(gMoonTextureID, TRUE, TRUE);
|
|
mMoonTexturep->setClamp(TRUE, TRUE);
|
|
mBloomTexturep = gImageList.getImage(IMG_BLOOM1);
|
|
mBloomTexturep->setClamp(TRUE, TRUE);
|
|
|
|
calcBrightnessScaleAndColors();
|
|
|
|
if (gSavedSettings.getBOOL("RenderWater") && gGLManager.mHasCubeMap)
|
|
{
|
|
LLCubeMap* cube_map = getCubeMap();
|
|
|
|
std::vector<LLPointer<LLImageRaw> > images;
|
|
for (S32 side = 0; side < 6; side++)
|
|
{
|
|
images.push_back(mSkyTex[side].getImageRaw());
|
|
}
|
|
|
|
if(cube_map)
|
|
{
|
|
cube_map->init(images);
|
|
mForceUpdate = TRUE;
|
|
}
|
|
}
|
|
|
|
if (mDrawable)
|
|
{
|
|
gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void LLVOSky::updateHaze()
|
|
{
|
|
static LLRandLagFib607 weather_generator(LLUUID::getRandomSeed());
|
|
if (gSavedSettings.getBOOL("FixedWeather"))
|
|
{
|
|
weather_generator.seed(8008135);
|
|
}
|
|
|
|
const F32 fo_upper_bound = 5;
|
|
const F32 sca_upper_bound = 6;
|
|
const F32 fo = 1 + (F32)weather_generator() *(fo_upper_bound - 1);
|
|
const static F32 upper = 0.5f / gFastLn.ln(fo_upper_bound);
|
|
mHaze.setFalloff(fo);
|
|
mHaze.setG((F32)weather_generator() * (0.0f + upper * gFastLn.ln(fo)));
|
|
LLColor3 sca;
|
|
const F32 cd = mCloudDensity * 3;
|
|
F32 min_r = cd - 1;
|
|
if (min_r < 0)
|
|
{
|
|
min_r = 0;
|
|
}
|
|
F32 max_r = cd + 1;
|
|
if (max_r > sca_upper_bound)
|
|
{
|
|
max_r = sca_upper_bound;
|
|
}
|
|
|
|
sca.mV[0] = min_r + (F32)weather_generator() * (max_r - min_r);
|
|
|
|
min_r = sca.mV[0] - 0.1f;
|
|
if (min_r < 0)
|
|
{
|
|
min_r = 0;
|
|
}
|
|
max_r = sca.mV[0] + 0.5f;
|
|
if (max_r > sca_upper_bound)
|
|
{
|
|
max_r = sca_upper_bound;
|
|
}
|
|
|
|
sca.mV[1] = min_r + (F32)weather_generator() * (max_r - min_r);
|
|
|
|
min_r = sca.mV[1];
|
|
if (min_r < 0)
|
|
{
|
|
min_r = 0;
|
|
}
|
|
max_r = sca.mV[1] + 1;
|
|
if (max_r > sca_upper_bound)
|
|
{
|
|
max_r = sca_upper_bound;
|
|
}
|
|
|
|
sca.mV[2] = min_r + (F32)weather_generator() * (max_r - min_r);
|
|
|
|
sca = AIR_SCA_AVG * sca;
|
|
|
|
mHaze.setSigSca(sca);
|
|
}
|
|
|
|
void LLVOSky::initSkyTextureDirs(const S32 side, const S32 tile)
|
|
{
|
|
S32 tile_x = tile % NUM_TILES_X;
|
|
S32 tile_y = tile / NUM_TILES_X;
|
|
|
|
S32 tile_x_pos = tile_x * sTileResX;
|
|
S32 tile_y_pos = tile_y * sTileResY;
|
|
|
|
F32 coeff[3] = {0, 0, 0};
|
|
const S32 curr_coef = side >> 1; // 0/1 = Z axis, 2/3 = Y, 4/5 = X
|
|
const S32 side_dir = (((side & 1) << 1) - 1); // even = -1, odd = 1
|
|
const S32 x_coef = (curr_coef + 1) % 3;
|
|
const S32 y_coef = (x_coef + 1) % 3;
|
|
|
|
coeff[curr_coef] = (F32)side_dir;
|
|
|
|
F32 inv_res = 1.f/sResolution;
|
|
S32 x, y;
|
|
for (y = tile_y_pos; y < (tile_y_pos + sTileResY); ++y)
|
|
{
|
|
for (x = tile_x_pos; x < (tile_x_pos + sTileResX); ++x)
|
|
{
|
|
coeff[x_coef] = F32((x<<1) + 1) * inv_res - 1.f;
|
|
coeff[y_coef] = F32((y<<1) + 1) * inv_res - 1.f;
|
|
LLVector3 dir(coeff[0], coeff[1], coeff[2]);
|
|
dir.normVec();
|
|
mSkyTex[side].setDir(dir, x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLVOSky::createSkyTexture(const S32 side, const S32 tile)
|
|
{
|
|
S32 tile_x = tile % NUM_TILES_X;
|
|
S32 tile_y = tile / NUM_TILES_X;
|
|
|
|
S32 tile_x_pos = tile_x * sTileResX;
|
|
S32 tile_y_pos = tile_y * sTileResY;
|
|
|
|
S32 x, y;
|
|
for (y = tile_y_pos; y < (tile_y_pos + sTileResY); ++y)
|
|
{
|
|
for (x = tile_x_pos; x < (tile_x_pos + sTileResX); ++x)
|
|
{
|
|
mSkyTex[side].setPixel(calcSkyColorInDir(mSkyTex[side].getDir(x, y)), x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
LLColor3 LLVOSky::calcSkyColorInDir(const LLVector3 &dir)
|
|
{
|
|
LLColor3 col, transp;
|
|
|
|
if (dir.mV[VZ] < -0.02f)
|
|
{
|
|
col = LLColor3(llmax(mFogColor[0],0.2f), llmax(mFogColor[1],0.2f), llmax(mFogColor[2],0.27f));
|
|
float x = 1.0f-fabsf(-0.1f-dir.mV[VZ]);
|
|
x *= x;
|
|
col.mV[0] *= x*x;
|
|
col.mV[1] *= powf(x, 2.5f);
|
|
col.mV[2] *= x*x*x;
|
|
return col;
|
|
}
|
|
|
|
|
|
calcSkyColorInDir(col, transp, dir);
|
|
F32 br = color_max(col);
|
|
if (br > mBrightnessScaleNew)
|
|
{
|
|
mBrightnessScaleNew = br;
|
|
mBrightestPointNew = col;
|
|
}
|
|
return col;
|
|
}
|
|
|
|
|
|
LLColor4 LLVOSky::calcInScatter(LLColor4& transp, const LLVector3 &point, F32 exager = 1) const
|
|
{
|
|
LLColor3 col, tr;
|
|
calcInScatter(col, tr, point, exager);
|
|
col *= mBrightnessScaleGuess;
|
|
transp = LLColor4(tr);
|
|
return LLColor4(col);
|
|
}
|
|
|
|
|
|
|
|
void LLVOSky::calcSkyColorInDir(LLColor3& res, LLColor3& transp, const LLVector3& dir) const
|
|
{
|
|
const LLVector3& tosun = getToSunLast();
|
|
res.setToBlack();
|
|
LLColor3 haze_res(0.f, 0.f, 0.f);
|
|
transp.setToWhite();
|
|
LLVector3 step_v ;
|
|
LLVector3 cur_pos = mCameraPosAgent;
|
|
F32 h;
|
|
|
|
F32 dist = calcHitsAtmEdge(mCameraPosAgent, dir);
|
|
// const F32 K = log(dist / FIRST_STEP + 1) / NO_STEPS;
|
|
const F32 K = gFastLn.ln(dist / FIRST_STEP + 1) / NO_STEPS;
|
|
const F32 e_pow_k = (F32)LL_FAST_EXP(K);
|
|
F32 step = FIRST_STEP * (1 - 1 / e_pow_k);
|
|
|
|
// Initialize outside the loop because we write into them every iteration. JC
|
|
LLColor3 air_sca_opt_depth;
|
|
LLColor3 haze_sca_opt_depth;
|
|
LLColor3 air_transp;
|
|
|
|
for (S32 s = 0; s < NO_STEPS; ++s)
|
|
{
|
|
h = calcHeight(cur_pos);
|
|
step *= e_pow_k;
|
|
LLHaze::calcAirSca(h, air_sca_opt_depth);
|
|
air_sca_opt_depth *= step;
|
|
|
|
mHaze.calcSigSca(h, haze_sca_opt_depth);
|
|
haze_sca_opt_depth *= step;
|
|
|
|
LLColor3 haze_ext_opt_depth = haze_sca_opt_depth;
|
|
haze_ext_opt_depth *= (1.f + mHaze.getAbsCoef());
|
|
|
|
if (calcHitsEarth(cur_pos, tosun) < 0) // calculates amount of in-scattered light from the sun
|
|
{
|
|
//visibility check is too expensive
|
|
mTransp.calcTransp(calcUpVec(cur_pos) * tosun, h, air_transp);
|
|
air_transp *= transp;
|
|
res += air_sca_opt_depth * air_transp;
|
|
haze_res += haze_sca_opt_depth * air_transp;
|
|
}
|
|
LLColor3 temp(-4.f * F_PI * (air_sca_opt_depth + haze_ext_opt_depth));
|
|
temp.exp();
|
|
transp *= temp;
|
|
step_v = dir * step;
|
|
cur_pos += step_v;
|
|
}
|
|
const F32 cos_dir = dir * tosun;
|
|
res *= calcAirPhaseFunc(cos_dir);
|
|
res += haze_res * mHaze.calcPhase(cos_dir);
|
|
res *= mSun.getIntensity();
|
|
}
|
|
|
|
|
|
|
|
|
|
void LLVOSky::calcInScatter(LLColor3& res, LLColor3& transp,
|
|
const LLVector3& P, const F32 exaggeration) const
|
|
{
|
|
const LLVector3& tosun = getToSunLast();
|
|
res.setToBlack();
|
|
transp.setToWhite();
|
|
|
|
LLVector3 lower, upper;
|
|
LLVector3 dir = P - mCameraPosAgent;
|
|
|
|
F32 dist = exaggeration * dir.normVec();
|
|
|
|
const F32 cos_dir = dir * tosun;
|
|
|
|
if (dir.mV[VZ] > 0)
|
|
{
|
|
lower = mCameraPosAgent;
|
|
upper = P;
|
|
}
|
|
else
|
|
{
|
|
lower = P;
|
|
upper = mCameraPosAgent;
|
|
dir = -dir;
|
|
}
|
|
|
|
const F32 lower_h = calcHeight(lower);
|
|
const F32 upper_h = calcHeight(upper);
|
|
const LLVector3 up_upper = calcUpVec(upper);
|
|
const LLVector3 up_lower = calcUpVec(lower);
|
|
|
|
transp = color_div(mTransp.calcTransp(up_lower * dir, lower_h),
|
|
mTransp.calcTransp(up_upper * dir, upper_h));
|
|
color_pow(transp, exaggeration);
|
|
|
|
if (calcHitsEarth(upper, tosun) > 0)
|
|
{
|
|
const F32 avg = color_avg(transp);
|
|
//const F32 avg = llmin(1.f, 1.2f * color_avg(transp));
|
|
transp.setVec(avg, avg, avg);
|
|
return;
|
|
}
|
|
|
|
LLColor3 air_sca_opt_depth = LLHaze::calcAirSca(upper_h);
|
|
LLColor3 haze_sca_opt_depth = mHaze.calcSigSca(upper_h);
|
|
LLColor3 sun_transp;
|
|
mTransp.calcTransp(up_upper * tosun, upper_h, sun_transp);
|
|
|
|
if (calcHitsEarth(lower, tosun) < 0)
|
|
{
|
|
air_sca_opt_depth += LLHaze::calcAirSca(lower_h);
|
|
air_sca_opt_depth *= 0.5;
|
|
haze_sca_opt_depth += mHaze.calcSigSca(lower_h);
|
|
haze_sca_opt_depth *= 0.5;
|
|
sun_transp += mTransp.calcTransp(up_lower * tosun, lower_h);
|
|
sun_transp *= 0.5;
|
|
}
|
|
|
|
res = calcAirPhaseFunc(cos_dir) * air_sca_opt_depth;
|
|
res += mHaze.calcPhase(cos_dir) * haze_sca_opt_depth;
|
|
res = mSun.getIntensity() * dist * sun_transp * res;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
F32 LLVOSky::calcHitsEarth(const LLVector3& orig, const LLVector3& dir) const
|
|
{
|
|
const LLVector3 from_earth_center = mEarthCenter - orig;
|
|
const F32 tca = dir * from_earth_center;
|
|
if ( tca < 0 )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
const F32 thc2 = EARTH_RADIUS * EARTH_RADIUS -
|
|
from_earth_center.magVecSquared() + tca * tca;
|
|
if (thc2 < 0 )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return tca - sqrt ( thc2 );
|
|
}
|
|
|
|
F32 LLVOSky::calcHitsAtmEdge(const LLVector3& orig, const LLVector3& dir) const
|
|
{
|
|
const LLVector3 from_earth_center = mEarthCenter - orig;
|
|
const F32 tca = dir * from_earth_center;
|
|
|
|
const F32 thc2 = (EARTH_RADIUS + mAtmHeight) * (EARTH_RADIUS + mAtmHeight) -
|
|
from_earth_center.magVecSquared() + tca * tca;
|
|
return tca + sqrt(thc2);
|
|
}
|
|
|
|
|
|
void LLVOSky::updateBrightestDir()
|
|
{
|
|
LLColor3 br_pt, transp;
|
|
const S32 test_no = 5;
|
|
const F32 step = F_PI_BY_TWO / (test_no + 1);
|
|
for (S32 i = 0; i < test_no; ++i)
|
|
{
|
|
F32 cos_dir = cos ((i + 1) * step);
|
|
calcSkyColorInDir(br_pt, transp, move_vec(getToSunLast(), cos_dir));
|
|
const F32 br = color_max(br_pt);
|
|
if (br > mBrightnessScaleGuess)
|
|
{
|
|
mBrightnessScaleGuess = br;
|
|
mBrightestPointGuess = br_pt;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void LLVOSky::calcBrightnessScaleAndColors()
|
|
{
|
|
// new correct normalization.
|
|
if (mBrightnessScaleNew < 1e-7)
|
|
{
|
|
mBrightnessScale = 1;
|
|
mBrightestPoint.setToBlack();
|
|
}
|
|
else
|
|
{
|
|
mBrightnessScale = 1.f/mBrightnessScaleNew;
|
|
mBrightestPoint = mBrightestPointNew;
|
|
}
|
|
|
|
mBrightnessScaleNew = 0;
|
|
// and addition
|
|
|
|
// Calculate Sun and Moon color
|
|
const F32 h = llmax(0.0f, mCameraPosAgent.mV[2]);
|
|
const LLColor3 sun_color = mSun.getIntensity() * mTransp.calcTransp(getToSunLast().mV[2], h);
|
|
const LLColor3 moon_color = mNightColorShift *
|
|
mMoon.getIntensity() * mTransp.calcTransp(getToMoonLast().mV[2], h);
|
|
|
|
F32 intens = color_intens(sun_color);
|
|
F32 increase_sun_br = (intens > 0) ? 1.2f * color_intens(mBrightestPoint) / intens : 1;
|
|
|
|
intens = color_intens(moon_color);
|
|
F32 increase_moon_br = (intens > 0) ? 1.2f * llmax(1.0f, color_intens(mBrightestPoint) / intens) : 1;
|
|
|
|
mSun.setColor(mBrightnessScale * increase_sun_br * sun_color);
|
|
mMoon.setColor(mBrightnessScale * increase_moon_br * moon_color);
|
|
|
|
const LLColor3 haze_col = color_norm_abs(mHaze.getSigSca());
|
|
for (S32 i = 0; i < 6; ++i)
|
|
{
|
|
mSkyTex[i].create(mBrightnessScale, mHazeConcentration * mBrightestPoint * haze_col);
|
|
}
|
|
|
|
mBrightnessScaleGuess = mBrightnessScale;
|
|
mBrightestPointGuess = mBrightestPoint;
|
|
|
|
// calculateColors(); // MSMSM Moving this down to before generateScatterMap(), per Milo Lindens suggestion, to fix orange flashing bug.
|
|
|
|
mSun.renewDirection();
|
|
mSun.renewColor();
|
|
mMoon.renewDirection();
|
|
mMoon.renewColor();
|
|
|
|
LLColor3 transp;
|
|
|
|
if (calcHitsEarth(mCameraPosAgent, getToSunLast()) < 0)
|
|
{
|
|
calcSkyColorInDir(mBrightestPointGuess, transp, getToSunLast());
|
|
mBrightnessScaleGuess = color_max(mBrightestPointGuess);
|
|
updateBrightestDir();
|
|
mBrightnessScaleGuess = 1.f / llmax(1.0f, mBrightnessScaleGuess);
|
|
}
|
|
else if (getToSunLast().mV[2] > -0.5)
|
|
{
|
|
const LLVector3 almost_to_sun = toHorizon(getToSunLast());
|
|
calcSkyColorInDir(mBrightestPointGuess, transp, almost_to_sun);
|
|
mBrightnessScaleGuess = color_max(mBrightestPointGuess);
|
|
updateBrightestDir();
|
|
mBrightnessScaleGuess = 1.f / llmax(1.0f, mBrightnessScaleGuess);
|
|
}
|
|
else
|
|
{
|
|
mBrightestPointGuess.setToBlack();
|
|
mBrightnessScaleGuess = 1;
|
|
}
|
|
|
|
calculateColors(); // MSMSM Moved this down here per Milo Lindens suggestion, to fix orange flashing bug at sunset.
|
|
}
|
|
|
|
|
|
void LLVOSky::calculateColors()
|
|
{
|
|
const F32 h = -0.1f;
|
|
const LLVector3& tosun = getToSunLast();
|
|
|
|
F32 full_on, full_off, on, on_cl;
|
|
F32 sun_factor = 1;
|
|
|
|
// Sun Diffuse
|
|
F32 sun_height = tosun.mV[2];
|
|
|
|
if (sun_height <= 0.0)
|
|
sun_height = 0.0;
|
|
|
|
mSunDiffuse = mBrightnessScaleGuess * mSun.getIntensity() * mTransp.calcTransp(sun_height, h);
|
|
|
|
mSunDiffuse = 1.0f * color_norm(mSunDiffuse);
|
|
|
|
// Sun Ambient
|
|
full_off = -0.3f;
|
|
full_on = -0.03f;
|
|
if (tosun.mV[2] < full_off)
|
|
{
|
|
mSunAmbient.setToBlack();
|
|
}
|
|
else
|
|
{
|
|
on = (tosun.mV[2] - full_off) / (full_on - full_off);
|
|
sun_factor = llmax(0.0f, llmin(on, 1.0f));
|
|
|
|
LLColor3 sun_amb = mAmbientScale * (0.8f * mSunDiffuse +
|
|
0.2f * mBrightnessScaleGuess * mBrightestPointGuess);
|
|
|
|
color_norm_pow(sun_amb, 0.1f, TRUE);
|
|
sun_factor *= min_intens_factor(sun_amb, 1.9f);
|
|
mSunAmbient = LLColor4(sun_factor * sun_amb);
|
|
}
|
|
|
|
|
|
// Moon Diffuse
|
|
full_on = 0.3f;
|
|
full_off = 0.01f;
|
|
if (getToMoonLast().mV[2] < full_off)
|
|
{
|
|
mMoonDiffuse.setToBlack();
|
|
}
|
|
else
|
|
{
|
|
// Steve: Added moonlight diffuse factor scalar (was constant .3)
|
|
F32 diffuse_factor = .1f + sNighttimeBrightness * .2f; // [.1, .5] default = .3
|
|
on = (getToMoonLast().mV[2] - full_off) / (full_on - full_off);
|
|
on_cl = llmin(on, 1.0f);
|
|
mMoonDiffuse = on_cl * mNightColorShift * diffuse_factor;
|
|
}
|
|
|
|
// Moon Ambient
|
|
|
|
F32 moon_amb_factor = 1.f;
|
|
|
|
if (gAgent.inPrelude())
|
|
{
|
|
moon_amb_factor *= 2.0f;
|
|
}
|
|
|
|
full_on = 0.30f;
|
|
full_off = 0.01f;
|
|
if (getToMoonLast().mV[2] < full_off)
|
|
{
|
|
mMoonAmbient.setToBlack();
|
|
}
|
|
else
|
|
{
|
|
on = (getToMoonLast().mV[2] - full_off) / (full_on - full_off);
|
|
on_cl = llmax(0.0f, llmin(on, 1.0f));
|
|
mMoonAmbient = on_cl * moon_amb_factor * mMoonDiffuse;
|
|
}
|
|
|
|
|
|
// Sun Diffuse
|
|
full_off = -0.05f;
|
|
full_on = -0.00f;
|
|
if (tosun.mV[2] < full_off)
|
|
{
|
|
mSunDiffuse.setToBlack();
|
|
}
|
|
else
|
|
{
|
|
on = (getToSunLast().mV[2] - full_off) / (full_on - full_off);
|
|
sun_factor = llmax(0.0f, llmin(on, 1.0f));
|
|
|
|
color_norm_pow(mSunDiffuse, 0.12f, TRUE);
|
|
sun_factor *= min_intens_factor(mSunDiffuse, 2.1f);
|
|
mSunDiffuse *= sun_factor;
|
|
}
|
|
|
|
|
|
mTotalAmbient = mSunAmbient + mMoonAmbient;
|
|
mTotalAmbient.setAlpha(1);
|
|
//llinfos << "MoonDiffuse: " << mMoonDiffuse << llendl;
|
|
//llinfos << "TotalAmbient: " << mTotalAmbient << llendl;
|
|
|
|
mFadeColor = mTotalAmbient + (mSunDiffuse + mMoonDiffuse) * 0.5f;
|
|
mFadeColor.setAlpha(0);
|
|
}
|
|
|
|
|
|
BOOL LLVOSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL LLVOSky::updateSky()
|
|
{
|
|
if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (mDead)
|
|
{
|
|
// It's dead. Don't update it.
|
|
return TRUE;
|
|
}
|
|
if (gGLManager.mIsDisabled)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static S32 next_frame = 0;
|
|
const S32 total_no_tiles = 6 * NUM_TILES;
|
|
const S32 cycle_frame_no = total_no_tiles + 1;
|
|
|
|
// if (mUpdateTimer.getElapsedTimeF32() > 0.1f)
|
|
{
|
|
mUpdateTimer.reset();
|
|
const S32 frame = next_frame;
|
|
|
|
++next_frame;
|
|
next_frame = next_frame % cycle_frame_no;
|
|
|
|
sInterpVal = (!mInitialized) ? 1 : (F32)next_frame / cycle_frame_no;
|
|
LLSkyTex::setInterpVal( sInterpVal );
|
|
LLHeavenBody::setInterpVal( sInterpVal );
|
|
calculateColors();
|
|
if (mForceUpdate || total_no_tiles == frame)
|
|
{
|
|
calcBrightnessScaleAndColors();
|
|
LLSkyTex::stepCurrent();
|
|
|
|
const static F32 LIGHT_DIRECTION_THRESHOLD = (F32) cos(DEG_TO_RAD * 1.f);
|
|
const static F32 COLOR_CHANGE_THRESHOLD = 0.01f;
|
|
|
|
LLVector3 direction = mSun.getDirection();
|
|
direction.normVec();
|
|
const F32 dot_lighting = direction * mLastLightingDirection;
|
|
|
|
LLColor3 delta_color;
|
|
delta_color.setVec(mLastTotalAmbient.mV[0] - mTotalAmbient.mV[0],
|
|
mLastTotalAmbient.mV[1] - mTotalAmbient.mV[1],
|
|
mLastTotalAmbient.mV[2] - mTotalAmbient.mV[2]);
|
|
|
|
if ( mForceUpdate
|
|
|| ((dot_lighting < LIGHT_DIRECTION_THRESHOLD)
|
|
|| (delta_color.magVec() > COLOR_CHANGE_THRESHOLD)
|
|
|| !mInitialized)
|
|
&& !direction.isExactlyZero())
|
|
{
|
|
mLastLightingDirection = direction;
|
|
mLastTotalAmbient = mTotalAmbient;
|
|
mInitialized = TRUE;
|
|
|
|
if (mCubeMap)
|
|
{
|
|
if (mForceUpdate)
|
|
{
|
|
updateFog(gCamera->getFar());
|
|
for (int side = 0; side < 6; side++)
|
|
{
|
|
for (int tile = 0; tile < NUM_TILES; tile++)
|
|
{
|
|
createSkyTexture(side, tile);
|
|
}
|
|
}
|
|
|
|
calcBrightnessScaleAndColors();
|
|
|
|
for (int side = 0; side < 6; side++)
|
|
{
|
|
LLImageRaw* raw1 = mSkyTex[side].getImageRaw(TRUE);
|
|
LLImageRaw* raw2 = mSkyTex[side].getImageRaw(FALSE);
|
|
raw2->copy(raw1);
|
|
mSkyTex[side].createGLImage(mSkyTex[side].getWhich(FALSE));
|
|
}
|
|
next_frame = 0;
|
|
}
|
|
|
|
std::vector<LLPointer<LLImageRaw> > images;
|
|
for (S32 side = 0; side < 6; side++)
|
|
{
|
|
images.push_back(mSkyTex[side].getImageRaw(FALSE));
|
|
}
|
|
mCubeMap->init(images);
|
|
}
|
|
}
|
|
|
|
gPipeline.markRebuild(gSky.mVOGroundp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
|
|
gPipeline.markRebuild(gSky.mVOStarsp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
|
|
|
|
mForceUpdate = FALSE;
|
|
}
|
|
else
|
|
{
|
|
const S32 side = frame / NUM_TILES;
|
|
const S32 tile = frame % NUM_TILES;
|
|
createSkyTexture(side, tile);
|
|
}
|
|
}
|
|
|
|
|
|
if (mDrawable)
|
|
{
|
|
gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void LLVOSky::updateTextures(LLAgent &agent)
|
|
{
|
|
if (mSunTexturep)
|
|
{
|
|
mSunTexturep->addTextureStats( (F32)MAX_IMAGE_AREA );
|
|
mMoonTexturep->addTextureStats( (F32)MAX_IMAGE_AREA );
|
|
mBloomTexturep->addTextureStats( (F32)MAX_IMAGE_AREA );
|
|
}
|
|
}
|
|
|
|
LLDrawable *LLVOSky::createDrawable(LLPipeline *pipeline)
|
|
{
|
|
pipeline->allocDrawable(this);
|
|
mDrawable->setLit(FALSE);
|
|
|
|
LLDrawPoolSky *poolp = (LLDrawPoolSky*) gPipeline.getPool(LLDrawPool::POOL_SKY);
|
|
poolp->setSkyTex(mSkyTex);
|
|
poolp->setSun(&mSun);
|
|
poolp->setMoon(&mMoon);
|
|
mDrawable->setRenderType(LLPipeline::RENDER_TYPE_SKY);
|
|
|
|
for (S32 i = 0; i < 6; ++i)
|
|
{
|
|
mFace[FACE_SIDE0 + i] = mDrawable->addFace(poolp, NULL);
|
|
}
|
|
|
|
mFace[FACE_SUN] = mDrawable->addFace(poolp, mSunTexturep);
|
|
mFace[FACE_MOON] = mDrawable->addFace(poolp, mMoonTexturep);
|
|
mFace[FACE_BLOOM] = mDrawable->addFace(poolp, mBloomTexturep);
|
|
|
|
return mDrawable;
|
|
}
|
|
|
|
BOOL LLVOSky::updateGeometry(LLDrawable *drawable)
|
|
{
|
|
if (mFace[FACE_REFLECTION] == NULL)
|
|
{
|
|
LLDrawPoolWater *poolp = (LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER);
|
|
mFace[FACE_REFLECTION] = drawable->addFace(poolp, NULL);
|
|
}
|
|
|
|
mCameraPosAgent = drawable->getPositionAgent();
|
|
mEarthCenter.mV[0] = mCameraPosAgent.mV[0];
|
|
mEarthCenter.mV[1] = mCameraPosAgent.mV[1];
|
|
|
|
LLVector3 v_agent[8];
|
|
for (S32 i = 0; i < 8; ++i)
|
|
{
|
|
F32 x_sgn = (i&1) ? 1.f : -1.f;
|
|
F32 y_sgn = (i&2) ? 1.f : -1.f;
|
|
F32 z_sgn = (i&4) ? 1.f : -1.f;
|
|
v_agent[i] = HORIZON_DIST*0.25f * LLVector3(x_sgn, y_sgn, z_sgn);
|
|
}
|
|
|
|
LLStrider<LLVector3> verticesp;
|
|
LLStrider<LLVector3> normalsp;
|
|
LLStrider<LLVector2> texCoordsp;
|
|
LLStrider<U32> indicesp;
|
|
S32 index_offset;
|
|
LLFace *face;
|
|
|
|
for (S32 side = 0; side < 6; ++side)
|
|
{
|
|
face = mFace[FACE_SIDE0 + side];
|
|
|
|
if (face->mVertexBuffer.isNull())
|
|
{
|
|
face->setSize(4, 6);
|
|
face->setGeomIndex(0);
|
|
face->setIndicesIndex(0);
|
|
face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolSky::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
|
|
face->mVertexBuffer->allocateBuffer(4, 6, TRUE);
|
|
|
|
index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
|
|
|
|
S32 vtx = 0;
|
|
S32 curr_bit = side >> 1; // 0/1 = Z axis, 2/3 = Y, 4/5 = X
|
|
S32 side_dir = side & 1; // even - 0, odd - 1
|
|
S32 i_bit = (curr_bit + 2) % 3;
|
|
S32 j_bit = (i_bit + 2) % 3;
|
|
|
|
LLVector3 axis;
|
|
axis.mV[curr_bit] = 1;
|
|
face->mCenterAgent = (F32)((side_dir << 1) - 1) * axis * HORIZON_DIST;
|
|
|
|
vtx = side_dir << curr_bit;
|
|
*(verticesp++) = v_agent[vtx];
|
|
*(verticesp++) = v_agent[vtx | 1 << j_bit];
|
|
*(verticesp++) = v_agent[vtx | 1 << i_bit];
|
|
*(verticesp++) = v_agent[vtx | 1 << i_bit | 1 << j_bit];
|
|
|
|
*(texCoordsp++) = TEX00;
|
|
*(texCoordsp++) = TEX01;
|
|
*(texCoordsp++) = TEX10;
|
|
*(texCoordsp++) = TEX11;
|
|
|
|
// Triangles for each side
|
|
*indicesp++ = index_offset + 0;
|
|
*indicesp++ = index_offset + 1;
|
|
*indicesp++ = index_offset + 3;
|
|
|
|
*indicesp++ = index_offset + 0;
|
|
*indicesp++ = index_offset + 3;
|
|
*indicesp++ = index_offset + 2;
|
|
}
|
|
}
|
|
|
|
const LLVector3 &look_at = gCamera->getAtAxis();
|
|
LLVector3 right = look_at % LLVector3::z_axis;
|
|
LLVector3 up = right % look_at;
|
|
right.normVec();
|
|
up.normVec();
|
|
|
|
const static F32 elevation_factor = 0.0f/sResolution;
|
|
const F32 cos_max_angle = cosHorizon(elevation_factor);
|
|
mSun.setDraw(updateHeavenlyBodyGeometry(drawable, FACE_SUN, TRUE, mSun, cos_max_angle, up, right));
|
|
mMoon.setDraw(updateHeavenlyBodyGeometry(drawable, FACE_MOON, FALSE, mMoon, cos_max_angle, up, right));
|
|
|
|
const F32 water_height = gAgent.getRegion()->getWaterHeight() + 0.01f;
|
|
// gWorldPointer->getWaterHeight() + 0.01f;
|
|
const F32 camera_height = mCameraPosAgent.mV[2];
|
|
const F32 height_above_water = camera_height - water_height;
|
|
|
|
BOOL sun = FALSE;
|
|
|
|
if (mSun.isVisible())
|
|
{
|
|
if (mMoon.isVisible())
|
|
{
|
|
sun = look_at * mSun.getDirection() > 0;
|
|
}
|
|
else
|
|
{
|
|
sun = TRUE;
|
|
}
|
|
}
|
|
|
|
if (height_above_water > 0)
|
|
{
|
|
#if 1 //1.9.1
|
|
BOOL render_ref = gPipeline.getPool(LLDrawPool::POOL_WATER)->getVertexShaderLevel() == 0;
|
|
#else
|
|
BOOL render_ref = !(gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) >= LLDrawPoolWater::SHADER_LEVEL_RIPPLE);
|
|
#endif
|
|
if (sun)
|
|
{
|
|
setDrawRefl(0);
|
|
if (render_ref)
|
|
{
|
|
updateReflectionGeometry(drawable, height_above_water, mSun);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
setDrawRefl(1);
|
|
if (render_ref)
|
|
{
|
|
updateReflectionGeometry(drawable, height_above_water, mMoon);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
setDrawRefl(-1);
|
|
}
|
|
|
|
|
|
LLPipeline::sCompiles++;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL LLVOSky::updateHeavenlyBodyGeometry(LLDrawable *drawable, const S32 f, const BOOL is_sun,
|
|
LLHeavenBody& hb, const F32 cos_max_angle,
|
|
const LLVector3 &up, const LLVector3 &right)
|
|
{
|
|
LLStrider<LLVector3> verticesp;
|
|
LLStrider<LLVector3> normalsp;
|
|
LLStrider<LLVector2> texCoordsp;
|
|
LLStrider<U32> indicesp;
|
|
S32 index_offset;
|
|
LLFace *facep;
|
|
|
|
LLVector3 to_dir = hb.getDirection();
|
|
LLVector3 draw_pos = to_dir * HEAVENLY_BODY_DIST;
|
|
|
|
|
|
LLVector3 hb_right = to_dir % LLVector3::z_axis;
|
|
LLVector3 hb_up = hb_right % to_dir;
|
|
hb_right.normVec();
|
|
hb_up.normVec();
|
|
|
|
//const static F32 cos_max_turn = sqrt(3.f) / 2; // 30 degrees
|
|
//const F32 cos_turn_right = 1. / (llmax(cos_max_turn, hb_right * right));
|
|
//const F32 cos_turn_up = 1. / llmax(cos_max_turn, hb_up * up);
|
|
|
|
const F32 enlargm_factor = ( 1 - to_dir.mV[2] );
|
|
F32 horiz_enlargement = 1 + enlargm_factor * 0.3f;
|
|
F32 vert_enlargement = 1 + enlargm_factor * 0.2f;
|
|
|
|
// Parameters for the water reflection
|
|
hb.setU(HEAVENLY_BODY_FACTOR * horiz_enlargement * hb.getDiskRadius() * hb_right);
|
|
hb.setV(HEAVENLY_BODY_FACTOR * vert_enlargement * hb.getDiskRadius() * hb_up);
|
|
// End of parameters for the water reflection
|
|
|
|
const LLVector3 scaled_right = HEAVENLY_BODY_DIST * hb.getU();
|
|
const LLVector3 scaled_up = HEAVENLY_BODY_DIST * hb.getV();
|
|
|
|
//const LLVector3 scaled_right = horiz_enlargement * HEAVENLY_BODY_SCALE * hb.getDiskRadius() * hb_right;//right;
|
|
//const LLVector3 scaled_up = vert_enlargement * HEAVENLY_BODY_SCALE * hb.getDiskRadius() * hb_up;//up;
|
|
LLVector3 v_clipped[4];
|
|
|
|
hb.corner(0) = draw_pos - scaled_right + scaled_up;
|
|
hb.corner(1) = draw_pos - scaled_right - scaled_up;
|
|
hb.corner(2) = draw_pos + scaled_right + scaled_up;
|
|
hb.corner(3) = draw_pos + scaled_right - scaled_up;
|
|
|
|
|
|
F32 t_left, t_right;
|
|
if (!clip_quad_to_horizon(t_left, t_right, v_clipped, hb.corners(), cos_max_angle))
|
|
{
|
|
hb.setVisible(FALSE);
|
|
return FALSE;
|
|
}
|
|
hb.setVisible(TRUE);
|
|
|
|
facep = mFace[f];
|
|
|
|
if (facep->mVertexBuffer.isNull())
|
|
{
|
|
facep->setSize(4, 6);
|
|
facep->mVertexBuffer = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
|
|
facep->mVertexBuffer->allocateBuffer(facep->getGeomCount(), facep->getIndicesCount(), TRUE);
|
|
facep->setGeomIndex(0);
|
|
facep->setIndicesIndex(0);
|
|
}
|
|
|
|
index_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
|
|
if (-1 == index_offset)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
for (S32 vtx = 0; vtx < 4; ++vtx)
|
|
{
|
|
hb.corner(vtx) = v_clipped[vtx];
|
|
*(verticesp++) = hb.corner(vtx) + mCameraPosAgent;
|
|
}
|
|
|
|
*(texCoordsp++) = TEX01;
|
|
*(texCoordsp++) = TEX00;
|
|
//*(texCoordsp++) = (t_left > 0) ? LLVector2(0, t_left) : TEX00;
|
|
*(texCoordsp++) = TEX11;
|
|
*(texCoordsp++) = TEX10;
|
|
//*(texCoordsp++) = (t_right > 0) ? LLVector2(1, t_right) : TEX10;
|
|
|
|
*indicesp++ = index_offset + 0;
|
|
*indicesp++ = index_offset + 2;
|
|
*indicesp++ = index_offset + 1;
|
|
|
|
*indicesp++ = index_offset + 1;
|
|
*indicesp++ = index_offset + 2;
|
|
*indicesp++ = index_offset + 3;
|
|
|
|
if (is_sun)
|
|
{
|
|
if ((t_left > 0) && (t_right > 0))
|
|
{
|
|
F32 t = (t_left + t_right) * 0.5f;
|
|
mSun.setHorizonVisibility(0.5f * (1 + cos(t * F_PI)));
|
|
}
|
|
else
|
|
{
|
|
mSun.setHorizonVisibility();
|
|
}
|
|
updateSunHaloGeometry(drawable);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clips quads with top and bottom sides parallel to horizon.
|
|
|
|
BOOL clip_quad_to_horizon(F32& t_left, F32& t_right, LLVector3 v_clipped[4],
|
|
const LLVector3 v_corner[4], const F32 cos_max_angle)
|
|
{
|
|
t_left = clip_side_to_horizon(v_corner[1], v_corner[0], cos_max_angle);
|
|
t_right = clip_side_to_horizon(v_corner[3], v_corner[2], cos_max_angle);
|
|
|
|
if ((t_left >= 1) || (t_right >= 1))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//const BOOL left_clip = (t_left > 0);
|
|
//const BOOL right_clip = (t_right > 0);
|
|
|
|
//if (!left_clip && !right_clip)
|
|
{
|
|
for (S32 vtx = 0; vtx < 4; ++vtx)
|
|
{
|
|
v_clipped[vtx] = v_corner[vtx];
|
|
}
|
|
}
|
|
/* else
|
|
{
|
|
v_clipped[0] = v_corner[0];
|
|
v_clipped[1] = left_clip ? ((1 - t_left) * v_corner[1] + t_left * v_corner[0])
|
|
: v_corner[1];
|
|
v_clipped[2] = v_corner[2];
|
|
v_clipped[3] = right_clip ? ((1 - t_right) * v_corner[3] + t_right * v_corner[2])
|
|
: v_corner[3];
|
|
}*/
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
F32 clip_side_to_horizon(const LLVector3& V0, const LLVector3& V1, const F32 cos_max_angle)
|
|
{
|
|
const LLVector3 V = V1 - V0;
|
|
const F32 k2 = 1.f/(cos_max_angle * cos_max_angle) - 1;
|
|
const F32 A = V.mV[0] * V.mV[0] + V.mV[1] * V.mV[1] - k2 * V.mV[2] * V.mV[2];
|
|
const F32 B = V0.mV[0] * V.mV[0] + V0.mV[1] * V.mV[1] - k2 * V0.mV[2] * V.mV[2];
|
|
const F32 C = V0.mV[0] * V0.mV[0] + V0.mV[1] * V0.mV[1] - k2 * V0.mV[2] * V0.mV[2];
|
|
|
|
if (fabs(A) < 1e-7)
|
|
{
|
|
return -0.1f; // v0 is cone origin and v1 is on the surface of the cone.
|
|
}
|
|
|
|
const F32 det = sqrt(B*B - A*C);
|
|
const F32 t1 = (-B - det) / A;
|
|
const F32 t2 = (-B + det) / A;
|
|
const F32 z1 = V0.mV[2] + t1 * V.mV[2];
|
|
const F32 z2 = V0.mV[2] + t2 * V.mV[2];
|
|
if (z1 * cos_max_angle < 0)
|
|
{
|
|
return t2;
|
|
}
|
|
else if (z2 * cos_max_angle < 0)
|
|
{
|
|
return t1;
|
|
}
|
|
else if ((t1 < 0) || (t1 > 1))
|
|
{
|
|
return t2;
|
|
}
|
|
else
|
|
{
|
|
return t1;
|
|
}
|
|
}
|
|
|
|
|
|
void LLVOSky::updateSunHaloGeometry(LLDrawable *drawable )
|
|
{
|
|
const LLVector3* v_corner = mSun.corners();
|
|
|
|
LLStrider<LLVector3> verticesp;
|
|
LLStrider<LLVector3> normalsp;
|
|
LLStrider<LLVector2> texCoordsp;
|
|
LLStrider<U32> indicesp;
|
|
S32 index_offset;
|
|
LLFace *face;
|
|
|
|
const LLVector3 right = 2 * (v_corner[2] - v_corner[0]);
|
|
LLVector3 up = 2 * (v_corner[2] - v_corner[3]);
|
|
up.normVec();
|
|
F32 size = right.magVec();
|
|
up = size * up;
|
|
const LLVector3 draw_pos = 0.25 * (v_corner[0] + v_corner[1] + v_corner[2] + v_corner[3]);
|
|
|
|
LLVector3 v_glow_corner[4];
|
|
|
|
v_glow_corner[0] = draw_pos - right + up;
|
|
v_glow_corner[1] = draw_pos - right - up;
|
|
v_glow_corner[2] = draw_pos + right + up;
|
|
v_glow_corner[3] = draw_pos + right - up;
|
|
|
|
face = mFace[FACE_BLOOM];
|
|
|
|
if (face->mVertexBuffer.isNull())
|
|
{
|
|
face->setSize(4, 6);
|
|
face->setGeomIndex(0);
|
|
face->setIndicesIndex(0);
|
|
face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
|
|
face->mVertexBuffer->allocateBuffer(4, 6, TRUE);
|
|
}
|
|
|
|
index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
|
|
if (-1 == index_offset)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (S32 vtx = 0; vtx < 4; ++vtx)
|
|
{
|
|
*(verticesp++) = v_glow_corner[vtx] + mCameraPosAgent;
|
|
}
|
|
|
|
*(texCoordsp++) = TEX01;
|
|
*(texCoordsp++) = TEX00;
|
|
*(texCoordsp++) = TEX11;
|
|
*(texCoordsp++) = TEX10;
|
|
|
|
*indicesp++ = index_offset + 0;
|
|
*indicesp++ = index_offset + 2;
|
|
*indicesp++ = index_offset + 1;
|
|
|
|
*indicesp++ = index_offset + 1;
|
|
*indicesp++ = index_offset + 2;
|
|
*indicesp++ = index_offset + 3;
|
|
}
|
|
|
|
|
|
F32 dtReflection(const LLVector3& p, F32 cos_dir_from_top, F32 sin_dir_from_top, F32 diff_angl_dir)
|
|
{
|
|
LLVector3 P = p;
|
|
P.normVec();
|
|
|
|
const F32 cos_dir_angle = -P.mV[VZ];
|
|
const F32 sin_dir_angle = sqrt(1 - cos_dir_angle * cos_dir_angle);
|
|
|
|
F32 cos_diff_angles = cos_dir_angle * cos_dir_from_top
|
|
+ sin_dir_angle * sin_dir_from_top;
|
|
|
|
F32 diff_angles;
|
|
if (cos_diff_angles > (1 - 1e-7))
|
|
diff_angles = 0;
|
|
else
|
|
diff_angles = acos(cos_diff_angles);
|
|
|
|
const F32 rel_diff_angles = diff_angles / diff_angl_dir;
|
|
const F32 dt = 1 - rel_diff_angles;
|
|
|
|
return (dt < 0) ? 0 : dt;
|
|
}
|
|
|
|
|
|
F32 dtClip(const LLVector3& v0, const LLVector3& v1, F32 far_clip2)
|
|
{
|
|
F32 dt_clip;
|
|
const LLVector3 otrezok = v1 - v0;
|
|
const F32 A = otrezok.magVecSquared();
|
|
const F32 B = v0 * otrezok;
|
|
const F32 C = v0.magVecSquared() - far_clip2;
|
|
const F32 det = sqrt(B*B - A*C);
|
|
dt_clip = (-B - det) / A;
|
|
if ((dt_clip < 0) || (dt_clip > 1))
|
|
dt_clip = (-B + det) / A;
|
|
return dt_clip;
|
|
}
|
|
|
|
|
|
void LLVOSky::updateReflectionGeometry(LLDrawable *drawable, F32 H,
|
|
const LLHeavenBody& HB)
|
|
{
|
|
const LLVector3 &look_at = gCamera->getAtAxis();
|
|
// const F32 water_height = gAgent.getRegion()->getWaterHeight() + 0.001f;
|
|
// gWorldPointer->getWaterHeight() + 0.001f;
|
|
|
|
LLVector3 to_dir = HB.getDirection();
|
|
LLVector3 hb_pos = to_dir * (HORIZON_DIST - 10);
|
|
LLVector3 to_dir_proj = to_dir;
|
|
to_dir_proj.mV[VZ] = 0;
|
|
to_dir_proj.normVec();
|
|
|
|
LLVector3 Right = to_dir % LLVector3::z_axis;
|
|
LLVector3 Up = Right % to_dir;
|
|
Right.normVec();
|
|
Up.normVec();
|
|
|
|
// finding angle between look direction and sprite.
|
|
LLVector3 look_at_right = look_at % LLVector3::z_axis;
|
|
look_at_right.normVec();
|
|
|
|
const static F32 cos_horizon_angle = cosHorizon(0.0f/sResolution);
|
|
//const static F32 horizon_angle = acos(cos_horizon_angle);
|
|
|
|
const F32 enlargm_factor = ( 1 - to_dir.mV[2] );
|
|
F32 horiz_enlargement = 1 + enlargm_factor * 0.3f;
|
|
F32 vert_enlargement = 1 + enlargm_factor * 0.2f;
|
|
|
|
F32 vert_size = vert_enlargement * HEAVENLY_BODY_SCALE * HB.getDiskRadius();
|
|
Right *= /*cos_lookAt_toDir */ horiz_enlargement * HEAVENLY_BODY_SCALE * HB.getDiskRadius();
|
|
Up *= vert_size;
|
|
|
|
LLVector3 v_corner[2];
|
|
LLVector3 stretch_corner[2];
|
|
|
|
LLVector3 top_hb = v_corner[0] = stretch_corner[0] = hb_pos - Right + Up;
|
|
v_corner[1] = stretch_corner[1] = hb_pos - Right - Up;
|
|
|
|
F32 dt_hor, dt;
|
|
dt_hor = clip_side_to_horizon(v_corner[1], v_corner[0], cos_horizon_angle);
|
|
|
|
LLVector2 TEX0t = TEX00;
|
|
LLVector2 TEX1t = TEX10;
|
|
LLVector3 lower_corner = v_corner[1];
|
|
|
|
if ((dt_hor > 0) && (dt_hor < 1))
|
|
{
|
|
TEX0t = LLVector2(0, dt_hor);
|
|
TEX1t = LLVector2(1, dt_hor);
|
|
lower_corner = (1 - dt_hor) * v_corner[1] + dt_hor * v_corner[0];
|
|
}
|
|
else
|
|
dt_hor = llmax(0.0f, llmin(1.0f, dt_hor));
|
|
|
|
top_hb.normVec();
|
|
const F32 cos_angle_of_view = fabs(top_hb.mV[VZ]);
|
|
const F32 extension = llmin (5.0f, 1.0f / cos_angle_of_view);
|
|
|
|
const S32 cols = 1;
|
|
const S32 raws = lltrunc(16 * extension);
|
|
S32 quads = cols * raws;
|
|
|
|
stretch_corner[0] = lower_corner + extension * (stretch_corner[0] - lower_corner);
|
|
stretch_corner[1] = lower_corner + extension * (stretch_corner[1] - lower_corner);
|
|
|
|
dt = dt_hor;
|
|
|
|
|
|
F32 cos_dir_from_top[2];
|
|
|
|
LLVector3 dir = stretch_corner[0];
|
|
dir.normVec();
|
|
cos_dir_from_top[0] = dir.mV[VZ];
|
|
|
|
dir = stretch_corner[1];
|
|
dir.normVec();
|
|
cos_dir_from_top[1] = dir.mV[VZ];
|
|
|
|
const F32 sin_dir_from_top = sqrt(1 - cos_dir_from_top[0] * cos_dir_from_top[0]);
|
|
const F32 sin_dir_from_top2 = sqrt(1 - cos_dir_from_top[1] * cos_dir_from_top[1]);
|
|
const F32 cos_diff_dir = cos_dir_from_top[0] * cos_dir_from_top[1]
|
|
+ sin_dir_from_top * sin_dir_from_top2;
|
|
const F32 diff_angl_dir = acos(cos_diff_dir);
|
|
|
|
v_corner[0] = stretch_corner[0];
|
|
v_corner[1] = lower_corner;
|
|
|
|
|
|
LLVector2 TEX0tt = TEX01;
|
|
LLVector2 TEX1tt = TEX11;
|
|
|
|
LLVector3 v_refl_corner[4];
|
|
LLVector3 v_sprite_corner[4];
|
|
|
|
S32 vtx;
|
|
for (vtx = 0; vtx < 2; ++vtx)
|
|
{
|
|
LLVector3 light_proj = v_corner[vtx];
|
|
light_proj.normVec();
|
|
|
|
const F32 z = light_proj.mV[VZ];
|
|
const F32 sin_angle = sqrt(1 - z * z);
|
|
light_proj *= 1.f / sin_angle;
|
|
light_proj.mV[VZ] = 0;
|
|
const F32 to_refl_point = H * sin_angle / fabs(z);
|
|
|
|
v_refl_corner[vtx] = to_refl_point * light_proj;
|
|
}
|
|
|
|
|
|
for (vtx = 2; vtx < 4; ++vtx)
|
|
{
|
|
const LLVector3 to_dir_vec = (to_dir_proj * v_refl_corner[vtx-2]) * to_dir_proj;
|
|
v_refl_corner[vtx] = v_refl_corner[vtx-2] + 2 * (to_dir_vec - v_refl_corner[vtx-2]);
|
|
}
|
|
|
|
for (vtx = 0; vtx < 4; ++vtx)
|
|
v_refl_corner[vtx].mV[VZ] -= H;
|
|
|
|
S32 side = 0;
|
|
LLVector3 refl_corn_norm[2];
|
|
refl_corn_norm[0] = v_refl_corner[1];
|
|
refl_corn_norm[0].normVec();
|
|
refl_corn_norm[1] = v_refl_corner[3];
|
|
refl_corn_norm[1].normVec();
|
|
|
|
F32 cos_refl_look_at[2];
|
|
cos_refl_look_at[0] = refl_corn_norm[0] * look_at;
|
|
cos_refl_look_at[1] = refl_corn_norm[1] * look_at;
|
|
|
|
if (cos_refl_look_at[1] > cos_refl_look_at[0])
|
|
{
|
|
side = 2;
|
|
}
|
|
|
|
//const F32 far_clip = (gCamera->getFar() - 0.01) / far_clip_factor;
|
|
const F32 far_clip = 512;
|
|
const F32 far_clip2 = far_clip*far_clip;
|
|
|
|
F32 dt_clip;
|
|
F32 vtx_near2, vtx_far2;
|
|
|
|
if ((vtx_far2 = v_refl_corner[side].magVecSquared()) > far_clip2)
|
|
{
|
|
// whole thing is sprite: reflection is beyond far clip plane.
|
|
dt_clip = 1.1f;
|
|
quads = 1;
|
|
}
|
|
else if ((vtx_near2 = v_refl_corner[side+1].magVecSquared()) > far_clip2)
|
|
{
|
|
// part is reflection, the rest is sprite.
|
|
dt_clip = dtClip(v_refl_corner[side + 1], v_refl_corner[side], far_clip2);
|
|
const LLVector3 P = (1 - dt_clip) * v_refl_corner[side + 1] + dt_clip * v_refl_corner[side];
|
|
|
|
F32 dt_tex = dtReflection(P, cos_dir_from_top[0], sin_dir_from_top, diff_angl_dir);
|
|
|
|
dt = dt_tex;
|
|
TEX0tt = LLVector2(0, dt);
|
|
TEX1tt = LLVector2(1, dt);
|
|
quads++;
|
|
}
|
|
else
|
|
{
|
|
// whole thing is correct reflection.
|
|
dt_clip = -0.1f;
|
|
}
|
|
|
|
LLFace *face = mFace[FACE_REFLECTION];
|
|
|
|
if (face->mVertexBuffer.isNull() || quads*4 != face->getGeomCount())
|
|
{
|
|
face->setSize(quads * 4, quads * 6);
|
|
face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
|
|
face->mVertexBuffer->allocateBuffer(face->getGeomCount(), face->getIndicesCount(), TRUE);
|
|
face->setIndicesIndex(0);
|
|
face->setGeomIndex(0);
|
|
}
|
|
|
|
LLStrider<LLVector3> verticesp;
|
|
LLStrider<LLVector3> normalsp;
|
|
LLStrider<LLVector2> texCoordsp;
|
|
LLStrider<U32> indicesp;
|
|
S32 index_offset;
|
|
|
|
index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
|
|
if (-1 == index_offset)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLColor3 hb_col3 = HB.getInterpColor();
|
|
hb_col3.clamp();
|
|
const LLColor4 hb_col = LLColor4(hb_col3);
|
|
|
|
const F32 min_attenuation = 0.4f;
|
|
const F32 max_attenuation = 0.7f;
|
|
const F32 attenuation = min_attenuation
|
|
+ cos_angle_of_view * (max_attenuation - min_attenuation);
|
|
|
|
LLColor4 hb_refl_col = (1-attenuation) * hb_col + attenuation * mFogColor;
|
|
face->setFaceColor(hb_refl_col);
|
|
|
|
LLVector3 v_far[2];
|
|
v_far[0] = v_refl_corner[1];
|
|
v_far[1] = v_refl_corner[3];
|
|
|
|
if(dt_clip > 0)
|
|
{
|
|
if (dt_clip >= 1)
|
|
{
|
|
for (S32 vtx = 0; vtx < 4; ++vtx)
|
|
{
|
|
F32 ratio = far_clip / v_refl_corner[vtx].magVec();
|
|
*(verticesp++) = v_refl_corner[vtx] = ratio * v_refl_corner[vtx] + mCameraPosAgent;
|
|
}
|
|
const LLVector3 draw_pos = 0.25 *
|
|
(v_refl_corner[0] + v_refl_corner[1] + v_refl_corner[2] + v_refl_corner[3]);
|
|
face->mCenterAgent = draw_pos;
|
|
}
|
|
else
|
|
{
|
|
F32 ratio = far_clip / v_refl_corner[1].magVec();
|
|
v_sprite_corner[1] = v_refl_corner[1] * ratio;
|
|
|
|
ratio = far_clip / v_refl_corner[3].magVec();
|
|
v_sprite_corner[3] = v_refl_corner[3] * ratio;
|
|
|
|
v_refl_corner[1] = (1 - dt_clip) * v_refl_corner[1] + dt_clip * v_refl_corner[0];
|
|
v_refl_corner[3] = (1 - dt_clip) * v_refl_corner[3] + dt_clip * v_refl_corner[2];
|
|
v_sprite_corner[0] = v_refl_corner[1];
|
|
v_sprite_corner[2] = v_refl_corner[3];
|
|
|
|
for (S32 vtx = 0; vtx < 4; ++vtx)
|
|
{
|
|
*(verticesp++) = v_sprite_corner[vtx] + mCameraPosAgent;
|
|
}
|
|
|
|
const LLVector3 draw_pos = 0.25 *
|
|
(v_refl_corner[0] + v_sprite_corner[1] + v_refl_corner[2] + v_sprite_corner[3]);
|
|
face->mCenterAgent = draw_pos;
|
|
}
|
|
|
|
*(texCoordsp++) = TEX0tt;
|
|
*(texCoordsp++) = TEX0t;
|
|
*(texCoordsp++) = TEX1tt;
|
|
*(texCoordsp++) = TEX1t;
|
|
|
|
*indicesp++ = index_offset + 0;
|
|
*indicesp++ = index_offset + 2;
|
|
*indicesp++ = index_offset + 1;
|
|
|
|
*indicesp++ = index_offset + 1;
|
|
*indicesp++ = index_offset + 2;
|
|
*indicesp++ = index_offset + 3;
|
|
|
|
index_offset += 4;
|
|
}
|
|
|
|
if (dt_clip < 1)
|
|
{
|
|
if (dt_clip <= 0)
|
|
{
|
|
const LLVector3 draw_pos = 0.25 *
|
|
(v_refl_corner[0] + v_refl_corner[1] + v_refl_corner[2] + v_refl_corner[3]);
|
|
face->mCenterAgent = draw_pos;
|
|
}
|
|
|
|
const F32 raws_inv = 1.f/raws;
|
|
const F32 cols_inv = 1.f/cols;
|
|
LLVector3 left = v_refl_corner[0] - v_refl_corner[1];
|
|
LLVector3 right = v_refl_corner[2] - v_refl_corner[3];
|
|
left *= raws_inv;
|
|
right *= raws_inv;
|
|
|
|
F32 dt_raw = dt;
|
|
|
|
for (S32 raw = 0; raw < raws; ++raw)
|
|
{
|
|
F32 dt_v0 = raw * raws_inv;
|
|
F32 dt_v1 = (raw + 1) * raws_inv;
|
|
const LLVector3 BL = v_refl_corner[1] + (F32)raw * left;
|
|
const LLVector3 BR = v_refl_corner[3] + (F32)raw * right;
|
|
const LLVector3 EL = BL + left;
|
|
const LLVector3 ER = BR + right;
|
|
dt_v0 = dt_raw;
|
|
dt_raw = dt_v1 = dtReflection(EL, cos_dir_from_top[0], sin_dir_from_top, diff_angl_dir);
|
|
for (S32 col = 0; col < cols; ++col)
|
|
{
|
|
F32 dt_h0 = col * cols_inv;
|
|
*(verticesp++) = (1 - dt_h0) * EL + dt_h0 * ER + mCameraPosAgent;
|
|
*(verticesp++) = (1 - dt_h0) * BL + dt_h0 * BR + mCameraPosAgent;
|
|
F32 dt_h1 = (col + 1) * cols_inv;
|
|
*(verticesp++) = (1 - dt_h1) * EL + dt_h1 * ER + mCameraPosAgent;
|
|
*(verticesp++) = (1 - dt_h1) * BL + dt_h1 * BR + mCameraPosAgent;
|
|
|
|
*(texCoordsp++) = LLVector2(dt_h0, dt_v1);
|
|
*(texCoordsp++) = LLVector2(dt_h0, dt_v0);
|
|
*(texCoordsp++) = LLVector2(dt_h1, dt_v1);
|
|
*(texCoordsp++) = LLVector2(dt_h1, dt_v0);
|
|
|
|
*indicesp++ = index_offset + 0;
|
|
*indicesp++ = index_offset + 2;
|
|
*indicesp++ = index_offset + 1;
|
|
|
|
*indicesp++ = index_offset + 1;
|
|
*indicesp++ = index_offset + 2;
|
|
*indicesp++ = index_offset + 3;
|
|
|
|
index_offset += 4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void LLVOSky::updateFog(const F32 distance)
|
|
{
|
|
if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FOG))
|
|
{
|
|
/*gGLSFog.addCap(GL_FOG, FALSE);
|
|
gGLSPipeline.addCap(GL_FOG, FALSE);
|
|
gGLSPipelineAlpha.addCap(GL_FOG, FALSE);
|
|
gGLSPipelinePixieDust.addCap(GL_FOG, FALSE);
|
|
gGLSPipelineSelection.addCap(GL_FOG, FALSE);
|
|
gGLSPipelineAvatar.addCap(GL_FOG, FALSE);
|
|
gGLSPipelineAvatarAlphaOnePass.addCap(GL_FOG, FALSE);
|
|
gGLSPipelineAvatarAlphaPass1.addCap(GL_FOG, FALSE);
|
|
gGLSPipelineAvatarAlphaPass2.addCap(GL_FOG, FALSE);
|
|
gGLSPipelineAvatarAlphaPass3.addCap(GL_FOG, FALSE);*/
|
|
glFogf(GL_FOG_DENSITY, 0);
|
|
glFogfv(GL_FOG_COLOR, (F32 *) &LLColor4::white.mV);
|
|
glFogf(GL_FOG_END, 1000000.f);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
/*gGLSFog.addCap(GL_FOG, TRUE);
|
|
gGLSPipeline.addCap(GL_FOG, TRUE);
|
|
gGLSPipelineAlpha.addCap(GL_FOG, TRUE);
|
|
gGLSPipelinePixieDust.addCap(GL_FOG, TRUE);
|
|
gGLSPipelineSelection.addCap(GL_FOG, TRUE);
|
|
if (!gGLManager.mIsATI)
|
|
{
|
|
gGLSPipelineAvatar.addCap(GL_FOG, TRUE);
|
|
gGLSPipelineAvatarAlphaOnePass.addCap(GL_FOG, TRUE);
|
|
gGLSPipelineAvatarAlphaPass1.addCap(GL_FOG, TRUE);
|
|
gGLSPipelineAvatarAlphaPass2.addCap(GL_FOG, TRUE);
|
|
gGLSPipelineAvatarAlphaPass3.addCap(GL_FOG, TRUE);
|
|
}*/
|
|
}
|
|
|
|
const BOOL hide_clip_plane = TRUE;
|
|
LLColor4 target_fog(0.f, 0.2f, 0.5f, 0.f);
|
|
|
|
const F32 water_height = gAgent.getRegion()->getWaterHeight();
|
|
// gWorldPointer->getWaterHeight();
|
|
F32 camera_height = gAgent.getCameraPositionAgent().mV[2];
|
|
|
|
F32 near_clip_height = gCamera->getAtAxis().mV[VZ] * gCamera->getNear();
|
|
camera_height += near_clip_height;
|
|
|
|
F32 fog_distance = 0.f;
|
|
LLColor3 res_color[3];
|
|
|
|
LLColor3 sky_fog_color = LLColor3::white;
|
|
LLColor3 render_fog_color = LLColor3::white;
|
|
|
|
LLColor3 transp;
|
|
LLVector3 tosun = getToSunLast();
|
|
const F32 tosun_z = tosun.mV[VZ];
|
|
tosun.mV[VZ] = 0.f;
|
|
tosun.normVec();
|
|
LLVector3 perp_tosun;
|
|
perp_tosun.mV[VX] = -tosun.mV[VY];
|
|
perp_tosun.mV[VY] = tosun.mV[VX];
|
|
LLVector3 tosun_45 = tosun + perp_tosun;
|
|
tosun_45.normVec();
|
|
|
|
F32 delta = 0.06f;
|
|
tosun.mV[VZ] = delta;
|
|
perp_tosun.mV[VZ] = delta;
|
|
tosun_45.mV[VZ] = delta;
|
|
tosun.normVec();
|
|
perp_tosun.normVec();
|
|
tosun_45.normVec();
|
|
|
|
// Sky colors, just slightly above the horizon in the direction of the sun, perpendicular to the sun, and at a 45 degree angle to the sun.
|
|
calcSkyColorInDir(res_color[0],transp, tosun);
|
|
calcSkyColorInDir(res_color[1],transp, perp_tosun);
|
|
calcSkyColorInDir(res_color[2],transp, tosun_45);
|
|
|
|
sky_fog_color = color_norm(res_color[0] + res_color[1] + res_color[2]);
|
|
|
|
F32 full_off = -0.25f;
|
|
F32 full_on = 0.00f;
|
|
F32 on = (tosun_z - full_off) / (full_on - full_off);
|
|
on = llclamp(on, 0.01f, 1.f);
|
|
sky_fog_color *= 0.5f * on;
|
|
|
|
|
|
// We need to clamp these to non-zero, in order for the gamma correction to work. 0^y = ???
|
|
S32 i;
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
sky_fog_color.mV[i] = llmax(0.0001f, sky_fog_color.mV[i]);
|
|
}
|
|
|
|
color_gamma_correct(sky_fog_color);
|
|
|
|
render_fog_color = sky_fog_color;
|
|
|
|
if (camera_height > water_height)
|
|
{
|
|
fog_distance = mFogRatio * distance;
|
|
LLColor4 fog(render_fog_color);
|
|
glFogfv(GL_FOG_COLOR, fog.mV);
|
|
mGLFogCol = fog;
|
|
}
|
|
else
|
|
{
|
|
// Interpolate between sky fog and water fog...
|
|
F32 depth = water_height - camera_height;
|
|
F32 depth_frac = 1.f/(1.f + 200.f*depth);
|
|
F32 color_frac = 1.f/(1.f + 0.5f* depth)* 0.2f;
|
|
fog_distance = (mFogRatio * distance) * depth_frac + 30.f * (1.f-depth_frac);
|
|
fog_distance = llmin(75.f, fog_distance);
|
|
|
|
F32 brightness = 1.f/(1.f + 0.05f*depth);
|
|
F32 sun_brightness = getSunDiffuseColor().magVec() * 0.3f;
|
|
brightness = llmin(1.f, brightness);
|
|
brightness = llmin(brightness, sun_brightness);
|
|
color_frac = llmin(0.7f, color_frac);
|
|
|
|
LLColor4 fogCol = brightness * (color_frac * render_fog_color + (1.f - color_frac) * LLColor4(0.f, 0.2f, 0.3f, 1.f));
|
|
fogCol.setAlpha(1);
|
|
glFogfv(GL_FOG_COLOR, (F32 *) &fogCol.mV);
|
|
mGLFogCol = fogCol;
|
|
}
|
|
|
|
mFogColor = sky_fog_color;
|
|
mFogColor.setAlpha(1);
|
|
LLGLSFog gls_fog;
|
|
|
|
F32 fog_density;
|
|
if (hide_clip_plane)
|
|
{
|
|
// For now, set the density to extend to the cull distance.
|
|
const F32 f_log = 2.14596602628934723963618357029f; // sqrt(fabs(log(0.01f)))
|
|
fog_density = f_log/fog_distance;
|
|
glFogi(GL_FOG_MODE, GL_EXP2);
|
|
}
|
|
else
|
|
{
|
|
const F32 f_log = 4.6051701859880913680359829093687f; // fabs(log(0.01f))
|
|
fog_density = (f_log)/fog_distance;
|
|
glFogi(GL_FOG_MODE, GL_EXP);
|
|
}
|
|
|
|
glFogf(GL_FOG_END, fog_distance*2.2f);
|
|
|
|
glFogf(GL_FOG_DENSITY, fog_density);
|
|
|
|
glHint(GL_FOG_HINT, GL_NICEST);
|
|
stop_glerror();
|
|
}
|
|
|
|
// static
|
|
void LLHaze::initClass()
|
|
{
|
|
sAirScaSeaLevel = LLHaze::calcAirScaSeaLevel();
|
|
}
|
|
|
|
|
|
|
|
// Functions used a lot.
|
|
|
|
|
|
F32 color_norm_pow(LLColor3& col, F32 e, BOOL postmultiply)
|
|
{
|
|
F32 mv = color_max(col);
|
|
if (0 == mv)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
col *= 1.f / mv;
|
|
color_pow(col, e);
|
|
if (postmultiply)
|
|
{
|
|
col *= mv;
|
|
}
|
|
return mv;
|
|
}
|
|
|
|
// Returns angle (RADIANs) between the horizontal projection of "v" and the x_axis.
|
|
// Range of output is 0.0f to 2pi //359.99999...f
|
|
// Returns 0.0f when "v" = +/- z_axis.
|
|
F32 azimuth(const LLVector3 &v)
|
|
{
|
|
F32 azimuth = 0.0f;
|
|
if (v.mV[VX] == 0.0f)
|
|
{
|
|
if (v.mV[VY] > 0.0f)
|
|
{
|
|
azimuth = F_PI * 0.5f;
|
|
}
|
|
else if (v.mV[VY] < 0.0f)
|
|
{
|
|
azimuth = F_PI * 1.5f;// 270.f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
azimuth = (F32) atan(v.mV[VY] / v.mV[VX]);
|
|
if (v.mV[VX] < 0.0f)
|
|
{
|
|
azimuth += F_PI;
|
|
}
|
|
else if (v.mV[VY] < 0.0f)
|
|
{
|
|
azimuth += F_PI * 2;
|
|
}
|
|
}
|
|
return azimuth;
|
|
}
|
|
|
|
|
|
#if 0
|
|
// Not currently used
|
|
LLColor3 LLVOSky::calcGroundFog(LLColor3& transp, const LLVector3 &view_dir, F32 obj_dist) const
|
|
{
|
|
LLColor3 col;
|
|
calcGroundFog(col, transp, view_dir, obj_dist);
|
|
col *= mBrightnessScaleGuess;
|
|
return col;
|
|
}
|
|
#endif
|
|
|
|
void LLVOSky::setSunDirection(const LLVector3 &sun_dir, const LLVector3 &sun_ang_velocity)
|
|
{
|
|
LLVector3 sun_direction = (sun_dir.magVec() == 0) ? LLVector3::x_axis : sun_dir;
|
|
sun_direction.normVec();
|
|
F32 dp = mSun.getDirection() * sun_direction;
|
|
mSun.setDirection(sun_direction);
|
|
mSun.setAngularVelocity(sun_ang_velocity);
|
|
mMoon.setDirection(-sun_direction);
|
|
if (dp < 0.995f) { //the sun jumped a great deal, update immediately
|
|
updateHaze();
|
|
mWeatherChange = FALSE;
|
|
mForceUpdate = TRUE;
|
|
}
|
|
else if (mWeatherChange && (mSun.getDirection().mV[VZ] > -0.5) )
|
|
{
|
|
updateHaze();
|
|
init();
|
|
mWeatherChange = FALSE;
|
|
}
|
|
else if (mSun.getDirection().mV[VZ] < -0.5)
|
|
{
|
|
mWeatherChange = TRUE;
|
|
}
|
|
}
|
|
|
|
#define INV_WAVELENGTH_R_POW4 (1.f/0.2401f) // = 1/0.7^4
|
|
#define INV_WAVELENGTH_G_POW4 (1.f/0.0789f) // = 1/0.53^4
|
|
#define INV_WAVELENGTH_B_POW4 (1.f/0.03748f) // = 1/0.44^4
|
|
|
|
// Dummy class for globals used below. Replace when KILLERSKY is merged in.
|
|
class LLKillerSky
|
|
{
|
|
public:
|
|
static F32 sRaleighGroundDensity;
|
|
static F32 sMieFactor;
|
|
static F32 sNearFalloffFactor;
|
|
static F32 sSkyContrib;
|
|
|
|
static void getRaleighCoefficients(float eye_sun_dp, float density, float *coefficients)
|
|
{
|
|
float dp = eye_sun_dp;
|
|
float angle_dep = density*(1 + dp*dp);
|
|
coefficients[0] = angle_dep * INV_WAVELENGTH_R_POW4;
|
|
coefficients[1] = angle_dep * INV_WAVELENGTH_G_POW4;
|
|
coefficients[2] = angle_dep * INV_WAVELENGTH_B_POW4;
|
|
}
|
|
|
|
static void getMieCoefficients(float eye_sun_dp, float density, float *coefficient)
|
|
{
|
|
// TOTALLY ARBITRARY FUNCTION. Seems to work though
|
|
// If anyone can replace this with some *actual* mie function, that'd be great
|
|
float dp = eye_sun_dp;
|
|
float dp_highpower = dp*dp;
|
|
float angle_dep = density * (llclamp(dp_highpower*dp, 0.f, 1.f) + 0.4f);
|
|
*coefficient = angle_dep;
|
|
}
|
|
};
|
|
|
|
F32 LLKillerSky::sRaleighGroundDensity = 0.013f;
|
|
F32 LLKillerSky::sMieFactor = 50;
|
|
F32 LLKillerSky::sNearFalloffFactor = 1.5f;
|
|
F32 LLKillerSky::sSkyContrib = 0.06f;
|
|
|
|
void LLVOSky::generateScatterMap()
|
|
{
|
|
float raleigh[3], mie;
|
|
|
|
mScatterMap = new LLImageGL(FALSE);
|
|
mScatterMapRaw = new LLImageRaw(256, 256, 4);
|
|
U8 *data = mScatterMapRaw->getData();
|
|
|
|
F32 light_brightness = gSky.getSunDirection().mV[VZ]+0.1f;
|
|
LLColor4 light_color;
|
|
LLColor4 sky_color;
|
|
if (light_brightness > 0)
|
|
{
|
|
F32 interp = sqrtf(light_brightness);
|
|
light_brightness = sqrt(sqrtf(interp));
|
|
light_color = lerp(gSky.getSunDiffuseColor(), LLColor4(1,1,1,1), interp) * light_brightness;
|
|
sky_color = lerp(LLColor4(0,0,0,0), LLColor4(0.4f, 0.6f, 1.f, 1.f), light_brightness)*LLKillerSky::sSkyContrib;
|
|
}
|
|
else
|
|
{
|
|
light_brightness = /*0.3f*/sqrt(-light_brightness);
|
|
light_color = gSky.getMoonDiffuseColor() * light_brightness;
|
|
sky_color = LLColor4(0,0,0,1);
|
|
}
|
|
|
|
// x = distance [0..1024m]
|
|
// y = dot product [-1,1]
|
|
for (int y=0;y<256;y++)
|
|
{
|
|
// Accumulate outward
|
|
float accum_r = 0, accum_g = 0, accum_b = 0;
|
|
|
|
float dp = (((float)y)/255.f)*1.95f - 0.975f;
|
|
U8 *scanline = &data[y*256*4];
|
|
for (int x=0;x<256;x++)
|
|
{
|
|
float dist = ((float)x+1)*4; // x -> 2048
|
|
|
|
float raleigh_density = LLKillerSky::sRaleighGroundDensity * 0.05f; // Arbitrary? Perhaps...
|
|
float mie_density = raleigh_density*LLKillerSky::sMieFactor;
|
|
|
|
float extinction_factor = dist/LLKillerSky::sNearFalloffFactor;
|
|
|
|
LLKillerSky::getRaleighCoefficients(dp, raleigh_density, raleigh);
|
|
LLKillerSky::getMieCoefficients(dp, mie_density, &mie);
|
|
|
|
float falloff_r = pow(llclamp(0.985f-raleigh[0],0.f,1.f), extinction_factor);
|
|
float falloff_g = pow(llclamp(0.985f-raleigh[1],0.f,1.f), extinction_factor);
|
|
float falloff_b = pow(llclamp(0.985f-raleigh[2],0.f,1.f), extinction_factor);
|
|
|
|
float light_r = light_color.mV[0] * (raleigh[0]+mie+sky_color.mV[0]) * falloff_r;
|
|
float light_g = light_color.mV[1] * (raleigh[1]+mie+sky_color.mV[1]) * falloff_g;
|
|
float light_b = light_color.mV[2] * (raleigh[2]+mie+sky_color.mV[2]) * falloff_b;
|
|
|
|
accum_r += light_r;
|
|
accum_g += light_g;
|
|
accum_b += light_b;
|
|
|
|
scanline[x*4] = (U8)llclamp(accum_r*255.f, 0.f, 255.f);
|
|
scanline[x*4+1] = (U8)llclamp(accum_g*255.f, 0.f, 255.f);
|
|
scanline[x*4+2] = (U8)llclamp(accum_b*255.f, 0.f, 255.f);
|
|
float alpha = ((falloff_r+falloff_g+falloff_b)*0.33f);
|
|
scanline[x*4+3] = (U8)llclamp(alpha*255.f, 0.f, 255.f); // Avg falloff
|
|
|
|
// Output color Co, Input color Ci, Map channels Mrgb, Ma:
|
|
// Co = (Ci * Ma) + Mrgb
|
|
}
|
|
}
|
|
|
|
mScatterMap->createGLTexture(0, mScatterMapRaw);
|
|
mScatterMap->bind(0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
}
|
|
|
|
#if 0
|
|
// Not currently used
|
|
void LLVOSky::calcGroundFog(LLColor3& res, LLColor3& transp, const LLVector3 view_dir, F32 obj_dist) const
|
|
{
|
|
const LLVector3& tosun = getToSunLast();//use_old_value ? sunDir() : toSunLast();
|
|
res.setToBlack();
|
|
transp.setToWhite();
|
|
const F32 dist = obj_dist * mWorldScale;
|
|
|
|
//LLVector3 view_dir = gCamera->getAtAxis();
|
|
|
|
const F32 cos_dir = view_dir * tosun;
|
|
LLVector3 dir = view_dir;
|
|
LLVector3 virtual_P = mCameraPosAgent + dist * dir;
|
|
|
|
if (dir.mV[VZ] < 0)
|
|
{
|
|
dir = -dir;
|
|
}
|
|
|
|
const F32 z_dir = dir.mV[2];
|
|
|
|
const F32 h = mCameraPosAgent.mV[2];
|
|
|
|
transp = color_div(mTransp.calcTransp(dir * calcUpVec(virtual_P), 0),
|
|
mTransp.calcTransp(z_dir, h));
|
|
|
|
if (calcHitsEarth(mCameraPosAgent, tosun) > 0)
|
|
{
|
|
const F32 avg = llmin(1.f, 1.2f * color_avg(transp));
|
|
transp = LLColor3(avg, avg, avg);
|
|
return;
|
|
}
|
|
|
|
LLColor3 haze_sca_opt_depth = mHaze.getSigSca();
|
|
LLColor3 sun_transp;
|
|
mTransp.calcTransp(tosun.mV[2], -0.1f, sun_transp);
|
|
|
|
res = calcAirPhaseFunc(cos_dir) * LLHaze::getAirScaSeaLevel();
|
|
res += mHaze.calcPhase(cos_dir) * mHaze.getSigSca();
|
|
res = mSun.getIntensity() * dist * sun_transp * res;
|
|
}
|
|
|
|
#endif
|