scatterSky.cpp

Engine/source/environment/scatterSky.cpp

More...

Public Functions

ConsoleDocClass(ScatterSky , "@brief Represents both the sun and sky <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> scenes with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> dynamic time of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">day.\n\n</a>" "%<a href="/coding/class/classscattersky/">ScatterSky</a> renders as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> dome shaped mesh which is camera relative and always overhead. " "It is intended <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be part of the background of your scene and renders before all " "other objects <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">types.\n\n</a>" "%<a href="/coding/class/classscattersky/">ScatterSky</a> is designed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> outdoor scenes which need <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transition fluidly " "between radically different times of day. It will respond <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> time changes " "originating from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classtimeofday/">TimeOfDay</a> object or the elevation field can be directly " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">adjusted.\n\n</a>" "During day, %<a href="/coding/class/classscattersky/">ScatterSky</a> uses atmosphereic sunlight scattering " "aproximations <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> generate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sky gradient and sun corona. It also calculates " "the fog color, ambient color, and sun color, which are used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> scene " "lighting. This is user controlled by fields within the <a href="/coding/class/classscattersky/">ScatterSky</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">group.\n\n</a>" "During night, %<a href="/coding/class/classscattersky/">ScatterSky</a> supports can transition <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> night sky cubemap and " "moon sprite. The user can <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> this and night time colors used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> scene " "lighting with fields within the Night <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">group.\n\n</a>" "A scene with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classscattersky/">ScatterSky</a> should not have any other sky or sun objects " "as it already fulfills both <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">roles.\n\n</a>" "%<a href="/coding/class/classscattersky/">ScatterSky</a> is intended <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be used with <a href="/coding/class/classcloudlayer/">CloudLayer</a> and <a href="/coding/class/classtimeofday/">TimeOfDay</a> as part of " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scene with dynamic lighting. Having <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> %<a href="/coding/class/classscattersky/">ScatterSky</a> without <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> changing " "time of day would unnecessarily give up artistic <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> compared and fillrate " "compared <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classskybox/">SkyBox</a>+<a href="/coding/class/classsun/">Sun</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">setup.\n\n</a>" " @ingroup Atmosphere" )
DefineEngineMethod(ScatterSky , applyChanges , void , () , "Apply <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> full network update of all fields <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> all clients." )

Detailed Description

Public Functions

ConsoleDocClass(ScatterSky , "@brief Represents both the sun and sky <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> scenes with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> dynamic time of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">day.\n\n</a>" "%<a href="/coding/class/classscattersky/">ScatterSky</a> renders as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> dome shaped mesh which is camera relative and always overhead. " "It is intended <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be part of the background of your scene and renders before all " "other objects <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">types.\n\n</a>" "%<a href="/coding/class/classscattersky/">ScatterSky</a> is designed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> outdoor scenes which need <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transition fluidly " "between radically different times of day. It will respond <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> time changes " "originating from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classtimeofday/">TimeOfDay</a> object or the elevation field can be directly " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">adjusted.\n\n</a>" "During day, %<a href="/coding/class/classscattersky/">ScatterSky</a> uses atmosphereic sunlight scattering " "aproximations <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> generate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sky gradient and sun corona. It also calculates " "the fog color, ambient color, and sun color, which are used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> scene " "lighting. This is user controlled by fields within the <a href="/coding/class/classscattersky/">ScatterSky</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">group.\n\n</a>" "During night, %<a href="/coding/class/classscattersky/">ScatterSky</a> supports can transition <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> night sky cubemap and " "moon sprite. The user can <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> this and night time colors used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> scene " "lighting with fields within the Night <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">group.\n\n</a>" "A scene with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classscattersky/">ScatterSky</a> should not have any other sky or sun objects " "as it already fulfills both <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">roles.\n\n</a>" "%<a href="/coding/class/classscattersky/">ScatterSky</a> is intended <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be used with <a href="/coding/class/classcloudlayer/">CloudLayer</a> and <a href="/coding/class/classtimeofday/">TimeOfDay</a> as part of " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scene with dynamic lighting. Having <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> %<a href="/coding/class/classscattersky/">ScatterSky</a> without <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> changing " "time of day would unnecessarily give up artistic <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> compared and fillrate " "compared <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classskybox/">SkyBox</a>+<a href="/coding/class/classsun/">Sun</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">setup.\n\n</a>" " @ingroup Atmosphere" )

DefineEngineMethod(ScatterSky , applyChanges , void , () , "Apply <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> full network update of all fields <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> all clients." )

IMPLEMENT_CO_NETOBJECT_V1(ScatterSky )

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platform.h"
  25#include "scatterSky.h"
  26
  27#include "core/stream/bitStream.h"
  28#include "console/consoleTypes.h"
  29#include "console/engineAPI.h"
  30#include "sim/netConnection.h"
  31#include "math/util/sphereMesh.h"
  32#include "math/mathUtils.h"
  33#include "math/util/matrixSet.h"
  34#include "scene/sceneRenderState.h"
  35#include "lighting/lightInfo.h"
  36#include "gfx/sim/gfxStateBlockData.h"
  37#include "gfx/gfxTransformSaver.h"
  38#include "gfx/gfxDrawUtil.h"
  39#include "gfx/sim/cubemapData.h"
  40#include "materials/shaderData.h"
  41#include "materials/materialManager.h"
  42#include "materials/baseMatInstance.h"
  43#include "materials/sceneData.h"
  44#include "environment/timeOfDay.h"
  45
  46
  47ConsoleDocClass( ScatterSky,
  48   "@brief Represents both the sun and sky for scenes with a dynamic time of day.\n\n"
  49
  50   "%ScatterSky renders as a dome shaped mesh which is camera relative and always overhead. "
  51   "It is intended to be part of the background of your scene and renders before all "
  52   "other objects types.\n\n"
  53
  54   "%ScatterSky is designed for outdoor scenes which need to transition fluidly "
  55   "between radically different times of day. It will respond to time changes "
  56   "originating from a TimeOfDay object or the elevation field can be directly "
  57   "adjusted.\n\n"
  58
  59   "During day, %ScatterSky uses atmosphereic sunlight scattering "
  60   "aproximations to generate a sky gradient and sun corona. It also calculates "
  61   "the fog color, ambient color, and sun color, which are used for scene "
  62   "lighting. This is user controlled by fields within the ScatterSky group.\n\n"
  63
  64   "During night, %ScatterSky supports can transition to a night sky cubemap and "
  65   "moon sprite. The user can control this and night time colors used for scene "
  66   "lighting with fields within the Night group.\n\n"
  67
  68   "A scene with a ScatterSky should not have any other sky or sun objects "
  69   "as it already fulfills both roles.\n\n"
  70
  71   "%ScatterSky is intended to be used with CloudLayer and TimeOfDay as part of "
  72   "a scene with dynamic lighting. Having a %ScatterSky without a changing "
  73   "time of day would unnecessarily give up artistic control compared and fillrate "
  74   "compared to a SkyBox + Sun setup.\n\n"
  75
  76   "@ingroup Atmosphere"
  77);
  78
  79
  80IMPLEMENT_CO_NETOBJECT_V1(ScatterSky);
  81
  82const F32 ScatterSky::smEarthRadius = (6378.0f * 1000.0f);
  83const F32 ScatterSky::smAtmosphereRadius = 200000.0f;
  84const F32 ScatterSky::smViewerHeight = 1.0f;
  85
  86ScatterSky::ScatterSky()
  87{
  88   mPrimCount = 0;
  89   mVertCount = 0;
  90
  91
  92   // Rayleigh scattering constant.
  93   mRayleighScattering = 0.0035f;
  94   mRayleighScattering4PI = mRayleighScattering * 4.0f * M_PI_F;
  95
  96   // Mie scattering constant.
  97   mMieScattering = 0.0045f;
  98   mMieScattering4PI = mMieScattering * 4.0f * M_PI_F;
  99
 100   // Overall scatter scalar.
 101   mSkyBrightness = 25.0f;
 102
 103   // The Mie phase asymmetry factor.
 104   mMiePhaseAssymetry = -0.75f;
 105
 106   mSphereInnerRadius = 1.0f;
 107   mSphereOuterRadius = 1.0f * 1.025f;
 108   mScale = 1.0f / (mSphereOuterRadius - mSphereInnerRadius);
 109
 110   // 650 nm for red
 111   // 570 nm for green
 112   // 475 nm for blue
 113   mWavelength.set( 0.650f, 0.570f, 0.475f, 0 );
 114
 115   mWavelength4[0] = mPow(mWavelength[0], 4.0f);
 116   mWavelength4[1] = mPow(mWavelength[1], 4.0f);
 117   mWavelength4[2] = mPow(mWavelength[2], 4.0f);
 118
 119   mRayleighScaleDepth = 0.25f;
 120   mMieScaleDepth = 0.1f;
 121
 122   mAmbientColor.set( 0, 0, 0, 1.0f );
 123   mAmbientScale.set( 1.0f, 1.0f, 1.0f, 1.0f );
 124
 125   mSunColor.set( 0, 0, 0, 1.0f );
 126   mSunScale = LinearColorF::WHITE;
 127
 128   mFogColor.set( 0, 0, 0, 1.0f );
 129   mFogScale = LinearColorF::WHITE;
 130
 131   mExposure = 1.0f;
 132   mNightInterpolant = 0;
 133   mZOffset = 0.0f;
 134
 135   mShader = NULL;
 136
 137   mTimeOfDay = 0;
 138
 139   mSunAzimuth = 0.0f;
 140   mSunElevation = 35.0f;
 141
 142   mMoonAzimuth = 0.0f;
 143   mMoonElevation = 45.0f;
 144
 145   mBrightness = 1.0f;
 146
 147   mCastShadows = true;
 148   mStaticRefreshFreq = 8;
 149   mDynamicRefreshFreq = 8;
 150   mDirty = true;
 151
 152   mLight = LightManager::createLightInfo();
 153   mLight->setType( LightInfo::Vector );
 154
 155   mFlareData = NULL;
 156   mFlareState.clear();
 157   mFlareScale = 1.0f;
 158
 159   mMoonEnabled = true;
 160   mMoonScale = 0.2f;
 161   mMoonTint.set( 0.192157f, 0.192157f, 0.192157f, 1.0f );
 162   MathUtils::getVectorFromAngles( mMoonLightDir, 0.0f, 45.0f );
 163   mMoonLightDir.normalize();
 164   mMoonLightDir = -mMoonLightDir;
 165   mNightCubemap = NULL;
 166   mNightColor.set( 0.0196078f, 0.0117647f, 0.109804f, 1.0f );
 167   mNightFogColor = mNightColor;
 168   mUseNightCubemap = false;
 169   mSunSize = 1.0f;
 170
 171   mMoonMatInst = NULL;
 172
 173   mNetFlags.set( Ghostable | ScopeAlways );
 174   mTypeMask |= EnvironmentObjectType | LightObjectType | StaticObjectType;
 175
 176   _generateSkyPoints();
 177
 178   mMatrixSet = reinterpret_cast<MatrixSet *>(dMalloc_aligned(sizeof(MatrixSet), 16));
 179   constructInPlace(mMatrixSet);
 180
 181   mColorizeAmt = 0;
 182   mColorize.set(0,0,0);
 183}
 184
 185ScatterSky::~ScatterSky()
 186{
 187   SAFE_DELETE( mLight );
 188   SAFE_DELETE( mMoonMatInst );
 189
 190   dFree_aligned(mMatrixSet);
 191}
 192
 193bool ScatterSky::onAdd()
 194{
 195   PROFILE_SCOPE(ScatterSky_onAdd);
 196
 197   // onNewDatablock for the server is called here
 198   // for the client it is called in unpackUpdate
 199
 200   if ( !Parent::onAdd() )
 201      return false;
 202
 203   if ( isClientObject() )
 204      TimeOfDay::getTimeOfDayUpdateSignal().notify( this, &ScatterSky::_updateTimeOfDay );
 205
 206   setGlobalBounds();
 207   resetWorldBox();
 208
 209   addToScene();
 210
 211   if ( isClientObject() )
 212   {
 213      _initMoon();
 214      Sim::findObject( mNightCubemapName, mNightCubemap );
 215   }
 216
 217   return true;
 218}
 219
 220void ScatterSky::onRemove()
 221{
 222   removeFromScene();
 223
 224   if ( isClientObject() )
 225      TimeOfDay::getTimeOfDayUpdateSignal().remove( this, &ScatterSky::_updateTimeOfDay );
 226
 227   Parent::onRemove();
 228}
 229
 230void ScatterSky::_conformLights()
 231{
 232   _initCurves();
 233
 234   F32 val = mCurves[0].getVal( mTimeOfDay );
 235   mNightInterpolant = 1.0f - val;
 236
 237   VectorF lightDirection;
 238   F32 brightness;
 239
 240   // Build the light direction from the azimuth and elevation.
 241   F32 yaw = mDegToRad(mClampF(mSunAzimuth,0,359));
 242   F32 pitch = mDegToRad(mClampF(mSunElevation,-360,+360));
 243   MathUtils::getVectorFromAngles(lightDirection, yaw, pitch);
 244   lightDirection.normalize();
 245   mSunDir = -lightDirection;
 246
 247   yaw = mDegToRad(mClampF(mMoonAzimuth,0,359));
 248   pitch = mDegToRad(mClampF(mMoonElevation,-360,+360));
 249   MathUtils::getVectorFromAngles( mMoonLightDir, yaw, pitch );
 250   mMoonLightDir.normalize();
 251   mMoonLightDir = -mMoonLightDir;
 252
 253   brightness = mCurves[2].getVal( mTimeOfDay );
 254
 255   if ( mNightInterpolant >= 1.0f )
 256      lightDirection = -mMoonLightDir;
 257
 258   mLight->setDirection( -lightDirection );
 259   mLight->setBrightness( brightness * mBrightness );
 260   mLightDir = lightDirection;
 261
 262   // Have to do interpolation
 263   // after the light direction is set
 264   // otherwise the sun color will be invalid.
 265   _interpolateColors();
 266
 267   mLight->setAmbient( mAmbientColor );
 268   mLight->setColor( mSunColor );
 269   mLight->setCastShadows( mCastShadows );
 270   mLight->setStaticRefreshFreq(mStaticRefreshFreq);
 271   mLight->setDynamicRefreshFreq(mDynamicRefreshFreq);
 272
 273   FogData fog = getSceneManager()->getFogData();
 274   fog.color = mFogColor;
 275   getSceneManager()->setFogData( fog );
 276}
 277
 278void ScatterSky::submitLights( LightManager *lm, bool staticLighting )
 279{
 280   if ( mDirty )
 281   {
 282      _conformLights();
 283      mDirty = false;
 284   }
 285
 286   // The sun is a special light and needs special registration.
 287   lm->setSpecialLight( LightManager::slSunLightType, mLight );
 288}
 289
 290void ScatterSky::setAzimuth( F32 azimuth )
 291{
 292   mSunAzimuth = azimuth;
 293   mDirty = true;
 294   setMaskBits( TimeMask );
 295}
 296
 297void ScatterSky::setElevation( F32 elevation )
 298{
 299   mSunElevation = elevation;
 300
 301   while( elevation < 0 )
 302      elevation += 360.0f;
 303
 304   while( elevation >= 360.0f )
 305      elevation -= 360.0f;
 306
 307   mTimeOfDay = elevation / 180.0f;
 308   mDirty = true;
 309   setMaskBits( TimeMask );
 310}
 311
 312void ScatterSky::inspectPostApply()
 313{
 314   mDirty = true;
 315   setMaskBits( 0xFFFFFFFF );
 316}
 317
 318void ScatterSky::initPersistFields()
 319{
 320   addGroup( "ScatterSky",
 321      "Only azimuth and elevation are networked fields. To trigger a full update of all other fields use the applyChanges ConsoleMethod." );
 322
 323      addField( "skyBrightness",       TypeF32,    Offset( mSkyBrightness, ScatterSky ),
 324         "Global brightness and intensity applied to the sky and objects in the level." );
 325
 326      addField( "sunSize",             TypeF32,    Offset( mSunSize, ScatterSky ),
 327         "Affects the size of the sun's disk." );
 328
 329      addField( "colorizeAmount",      TypeF32,    Offset( mColorizeAmt, ScatterSky ),
 330         "Controls how much the alpha component of colorize brigthens the sky. Setting to 0 returns default behavior." );
 331
 332      addField( "colorize",            TypeColorF, Offset( mColorize, ScatterSky ),
 333         "Tints the sky the color specified, the alpha controls the brigthness. The brightness is multipled by the value of colorizeAmt." );
 334
 335      addField( "rayleighScattering",  TypeF32,    Offset( mRayleighScattering, ScatterSky ),
 336         "Controls how blue the atmosphere is during the day." );
 337
 338      addField( "sunScale",            TypeColorF, Offset( mSunScale, ScatterSky ),
 339         "Modulates the directional color of sunlight." );
 340
 341      addField( "ambientScale",        TypeColorF, Offset( mAmbientScale, ScatterSky ),
 342         "Modulates the ambient color of sunlight." );
 343
 344      addField( "fogScale",            TypeColorF, Offset( mFogScale, ScatterSky ),
 345         "Modulates the fog color. Note that this overrides the LevelInfo.fogColor "
 346         "property, so you should not use LevelInfo.fogColor if the level contains "
 347         "a ScatterSky object." );
 348
 349      addField( "exposure",            TypeF32,    Offset( mExposure, ScatterSky ),
 350         "Controls the contrast of the sky and sun during daytime." );
 351
 352      addField( "zOffset",             TypeF32,     Offset( mZOffset, ScatterSky ),  
 353         "Offsets the scatterSky to avoid canvas rendering. Use 5000 or greater for the initial adjustment" );  
 354
 355   endGroup( "ScatterSky" );
 356
 357   addGroup( "Orbit" );
 358
 359      addProtectedField( "azimuth", TypeF32, Offset( mSunAzimuth, ScatterSky ), &ScatterSky::ptSetAzimuth, &defaultProtectedGetFn,
 360         "The horizontal angle of the sun measured clockwise from the positive Y world axis. This field is networked." );
 361
 362      addProtectedField( "elevation", TypeF32, Offset( mSunElevation, ScatterSky ), &ScatterSky::ptSetElevation, &defaultProtectedGetFn,
 363         "The elevation angle of the sun above or below the horizon. This field is networked." );
 364
 365      addField( "moonAzimuth", TypeF32, Offset( mMoonAzimuth, ScatterSky ),
 366         "The horizontal angle of the moon measured clockwise from the positive Y world axis. This is not animated by time or networked." );
 367
 368      addField( "moonElevation", TypeF32, Offset( mMoonElevation, ScatterSky ),
 369         "The elevation angle of the moon above or below the horizon. This is not animated by time or networked." );
 370
 371   endGroup( "Orbit" );
 372
 373   // We only add the basic lighting options that all lighting
 374   // systems would use... the specific lighting system options
 375   // are injected at runtime by the lighting system itself.
 376
 377   addGroup( "Lighting" );
 378
 379      addField( "castShadows", TypeBool, Offset( mCastShadows, ScatterSky ),
 380         "Enables/disables shadows cast by objects due to ScatterSky light." );
 381
 382      addField("staticRefreshFreq", TypeS32, Offset(mStaticRefreshFreq, ScatterSky), "static shadow refresh rate (milliseconds)");
 383      addField("dynamicRefreshFreq", TypeS32, Offset(mDynamicRefreshFreq, ScatterSky), "dynamic shadow refresh rate (milliseconds)");
 384
 385      addField( "brightness", TypeF32, Offset( mBrightness, ScatterSky ),
 386         "The brightness of the ScatterSky's light object." );
 387
 388   endGroup( "Lighting" );
 389
 390   addGroup( "Misc" );
 391
 392      addField( "flareType", TYPEID< LightFlareData >(), Offset( mFlareData, ScatterSky ),
 393         "Datablock for the flare produced by the ScatterSky." );
 394
 395      addField( "flareScale", TypeF32, Offset( mFlareScale, ScatterSky ),
 396         "Changes the size and intensity of the flare." );
 397
 398   endGroup( "Misc" );
 399
 400   addGroup( "Night" );
 401
 402      addField( "nightColor", TypeColorF, Offset( mNightColor, ScatterSky ),
 403         "The ambient color during night. Also used for the sky color if useNightCubemap is false." );
 404
 405      addField( "nightFogColor", TypeColorF, Offset( mNightFogColor, ScatterSky ),
 406         "The fog color during night." );
 407
 408      addField( "moonEnabled", TypeBool, Offset( mMoonEnabled, ScatterSky ),
 409         "Enable or disable rendering of the moon sprite during night." );
 410
 411      addField( "moonMat", TypeMaterialName, Offset( mMoonMatName, ScatterSky ),
 412         "Material for the moon sprite." );
 413
 414      addField( "moonScale", TypeF32, Offset( mMoonScale, ScatterSky ),
 415         "Controls size the moon sprite renders, specified as a fractional amount of the screen height." );
 416
 417      addField( "moonLightColor", TypeColorF, Offset( mMoonTint, ScatterSky ),
 418         "Color of light cast by the directional light during night." );
 419
 420      addField( "useNightCubemap", TypeBool, Offset( mUseNightCubemap, ScatterSky ),
 421         "Transition to the nightCubemap during night. If false we use nightColor." );
 422
 423      addField( "nightCubemap", TypeCubemapName, Offset( mNightCubemapName, ScatterSky ),
 424         "Cubemap visible during night." );
 425
 426   endGroup( "Night" );
 427
 428   // Now inject any light manager specific fields.
 429   LightManager::initLightFields();
 430
 431   Parent::initPersistFields();
 432}
 433
 434U32 ScatterSky::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
 435{
 436   U32 retMask = Parent::packUpdate(con, mask, stream);
 437
 438   if ( stream->writeFlag( mask & TimeMask ) )
 439   {
 440      stream->write( mSunAzimuth );
 441      stream->write( mSunElevation );
 442   }
 443
 444   if ( stream->writeFlag( mask & UpdateMask ) )
 445   {
 446      stream->write( mRayleighScattering );
 447      mRayleighScattering4PI = mRayleighScattering * 4.0f * M_PI_F;
 448
 449      stream->write( mRayleighScattering4PI );
 450
 451      stream->write( mMieScattering );
 452      mMieScattering4PI = mMieScattering * 4.0f * M_PI_F;
 453
 454      stream->write( mMieScattering4PI );
 455
 456      stream->write( mSunSize );
 457
 458      stream->write( mSkyBrightness );
 459
 460      stream->write( mMiePhaseAssymetry );
 461
 462      stream->write( mSphereInnerRadius );
 463      stream->write( mSphereOuterRadius );
 464
 465      stream->write( mScale );
 466
 467      stream->write( mWavelength );
 468
 469      stream->write( mWavelength4[0] );
 470      stream->write( mWavelength4[1] );
 471      stream->write( mWavelength4[2] );
 472
 473      stream->write( mRayleighScaleDepth );
 474      stream->write( mMieScaleDepth );
 475
 476      stream->write( mNightColor );
 477      stream->write( mNightFogColor );
 478      stream->write( mAmbientScale );
 479      stream->write( mSunScale );
 480      stream->write( mFogScale );
 481      stream->write( mColorizeAmt );
 482      stream->write( mColorize );
 483
 484      stream->write( mExposure );
 485
 486      stream->write( mZOffset );
 487
 488      stream->write( mBrightness );
 489
 490      stream->writeFlag( mCastShadows );
 491      stream->write(mStaticRefreshFreq);
 492      stream->write(mDynamicRefreshFreq);
 493
 494      stream->write( mFlareScale );
 495
 496      if ( stream->writeFlag( mFlareData ) )
 497      {
 498         stream->writeRangedU32( mFlareData->getId(),
 499                                 DataBlockObjectIdFirst,
 500                                 DataBlockObjectIdLast );
 501      }
 502
 503      stream->writeFlag( mMoonEnabled );
 504      stream->write( mMoonMatName );
 505      stream->write( mMoonScale );
 506      stream->write( mMoonTint );
 507      stream->writeFlag( mUseNightCubemap );
 508      stream->write( mNightCubemapName );
 509
 510      stream->write( mMoonAzimuth );
 511      stream->write( mMoonElevation );
 512
 513      mLight->packExtended( stream );
 514   }
 515
 516   return retMask;
 517}
 518
 519void ScatterSky::unpackUpdate(NetConnection *con, BitStream *stream)
 520{
 521   Parent::unpackUpdate(con, stream);
 522
 523   if ( stream->readFlag() ) // TimeMask
 524   {
 525      F32 temp = 0;
 526      stream->read( &temp );
 527      setAzimuth( temp );
 528
 529      stream->read( &temp );
 530      setElevation( temp );
 531   }
 532
 533   if ( stream->readFlag() ) // UpdateMask
 534   {
 535      stream->read( &mRayleighScattering );
 536      stream->read( &mRayleighScattering4PI );
 537
 538      stream->read( &mMieScattering );
 539      stream->read( &mMieScattering4PI );
 540
 541      stream->read( &mSunSize );
 542
 543      stream->read( &mSkyBrightness );
 544
 545      stream->read( &mMiePhaseAssymetry );
 546
 547      stream->read( &mSphereInnerRadius );
 548      stream->read( &mSphereOuterRadius );
 549
 550      stream->read( &mScale );
 551
 552      LinearColorF tmpColor( 0, 0, 0 );
 553
 554      stream->read( &tmpColor );
 555
 556      stream->read( &mWavelength4[0] );
 557      stream->read( &mWavelength4[1] );
 558      stream->read( &mWavelength4[2] );
 559
 560      stream->read( &mRayleighScaleDepth );
 561      stream->read( &mMieScaleDepth );
 562
 563      stream->read( &mNightColor );
 564      stream->read( &mNightFogColor );
 565      stream->read( &mAmbientScale );
 566      stream->read( &mSunScale );
 567      stream->read( &mFogScale );
 568      F32 colorizeAmt;
 569      stream->read( &colorizeAmt );
 570
 571      if(mColorizeAmt != colorizeAmt) {
 572         mColorizeAmt = colorizeAmt;
 573         mShader = NULL; //forces shader refresh
 574      }
 575
 576      stream->read( &mColorize );
 577
 578
 579      if ( tmpColor != mWavelength )
 580      {
 581         mWavelength = tmpColor;
 582         mWavelength4[0] = mPow(mWavelength[0], 4.0f);
 583         mWavelength4[1] = mPow(mWavelength[1], 4.0f);
 584         mWavelength4[2] = mPow(mWavelength[2], 4.0f);
 585      }
 586
 587      stream->read( &mExposure );
 588
 589      stream->read( &mZOffset );
 590
 591      stream->read( &mBrightness );
 592
 593      mCastShadows = stream->readFlag();
 594      stream->read(&mStaticRefreshFreq);
 595      stream->read(&mDynamicRefreshFreq);
 596
 597      stream->read( &mFlareScale );
 598
 599      if ( stream->readFlag() )
 600      {
 601         SimObjectId id = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
 602         LightFlareData *datablock = NULL;
 603
 604         if ( Sim::findObject( id, datablock ) )
 605            mFlareData = datablock;
 606         else
 607         {
 608            con->setLastError( "ScatterSky::unpackUpdate() - invalid LightFlareData!" );
 609            mFlareData = NULL;
 610         }
 611      }
 612      else
 613         mFlareData = NULL;
 614
 615      mMoonEnabled = stream->readFlag();
 616      stream->read( &mMoonMatName );
 617      stream->read( &mMoonScale );
 618      stream->read( &mMoonTint );
 619      mUseNightCubemap = stream->readFlag();
 620      stream->read( &mNightCubemapName );
 621
 622      stream->read( &mMoonAzimuth );
 623      stream->read( &mMoonElevation );
 624
 625      mLight->unpackExtended( stream );
 626
 627      if ( isProperlyAdded() )
 628      {
 629         mDirty = true;
 630         _initMoon();
 631         Sim::findObject( mNightCubemapName, mNightCubemap );
 632      }
 633   }
 634}
 635
 636void ScatterSky::prepRenderImage( SceneRenderState *state )
 637{
 638   // Only render into diffuse and reflect passes.
 639
 640   if( !state->isDiffusePass() &&
 641       !state->isReflectPass() )
 642      return;
 643
 644   // Regular sky render instance.
 645   RenderPassManager* renderPass = state->getRenderPass();
 646   ObjectRenderInst *ri = renderPass->allocInst<ObjectRenderInst>();
 647   ri->renderDelegate.bind( this, &ScatterSky::_render );
 648   ri->type = RenderPassManager::RIT_Sky;
 649   ri->defaultKey = 10;
 650   ri->defaultKey2 = 0;
 651   renderPass->addInst(ri);
 652
 653   // Debug render instance.
 654   /*
 655   if ( Con::getBoolVariable( "$ScatterSky::debug", false ) )
 656   {
 657      ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
 658      ri->renderDelegate.bind( this, &ScatterSky::_debugRender );
 659      ri->type = RenderPassManager::RIT_Editor;
 660      state->getRenderPass()->addInst( ri );
 661   }
 662   */
 663
 664   // Light flare effect render instance.
 665   if ( mFlareData && mNightInterpolant != 1.0f )
 666   {
 667      mFlareState.fullBrightness = mBrightness;
 668      mFlareState.scale = mFlareScale;
 669      mFlareState.lightInfo = mLight;
 670
 671      Point3F lightPos = state->getDiffuseCameraPosition() - state->getFarPlane() * mLight->getDirection() * 0.9f;
 672      mFlareState.lightMat.identity();
 673      mFlareState.lightMat.setPosition( lightPos );
 674
 675      F32 dist = ( lightPos - state->getDiffuseCameraPosition( ) ).len( );
 676      F32 coronaScale = 0.5f;
 677      F32 screenRadius = GFX->getViewport( ).extent.y * coronaScale * 0.5f;
 678      mFlareState.worldRadius = screenRadius * dist / state->getWorldToScreenScale( ).y;
 679
 680      mFlareData->prepRender( state, &mFlareState );
 681   }
 682
 683   // Render instances for Night effects.
 684   if ( mNightInterpolant <= 0.0f )
 685      return;
 686
 687   // Render instance for Moon sprite.
 688   if ( mMoonEnabled && mMoonMatInst )
 689   {
 690      mMatrixSet->setSceneView(GFX->getWorldMatrix());
 691      mMatrixSet->setSceneProjection(GFX->getProjectionMatrix());
 692      mMatrixSet->setWorld(GFX->getWorldMatrix());
 693
 694      ObjectRenderInst *moonRI = renderPass->allocInst<ObjectRenderInst>();
 695     moonRI->renderDelegate.bind( this, &ScatterSky::_renderMoon );
 696     moonRI->type = RenderPassManager::RIT_Sky;
 697      // Render after sky objects and before CloudLayer!
 698     moonRI->defaultKey = 5;
 699     moonRI->defaultKey2 = 0;
 700      renderPass->addInst(moonRI);
 701   }
 702}
 703
 704bool ScatterSky::_initShader()
 705{
 706   ShaderData *shaderData;
 707   if ( !Sim::findObject( "ScatterSkyShaderData", shaderData ) )
 708   {
 709      Con::warnf( "ScatterSky::_initShader - failed to locate shader ScatterSkyShaderData!" );
 710      return false;
 711   }
 712      Vector<GFXShaderMacro> macros;
 713   if ( mColorizeAmt )
 714      macros.push_back( GFXShaderMacro( "USE_COLORIZE" ) );
 715
 716   mShader = shaderData->getShader( macros );
 717
 718   if ( !mShader )
 719      return false;
 720
 721   if ( mStateBlock.isNull() )
 722   {
 723      GFXStateBlockData *data = NULL;
 724      if ( !Sim::findObject( "ScatterSkySBData", data ) )
 725         Con::warnf( "ScatterSky::_initShader - failed to locate ScatterSkySBData!" );
 726      else
 727         mStateBlock = GFX->createStateBlock( data->getState() );
 728     }
 729
 730   if ( !mStateBlock )
 731      return false;
 732
 733   mShaderConsts = mShader->allocConstBuffer();
 734   mModelViewProjSC = mShader->getShaderConstHandle( "$modelView" );
 735
 736   // Camera height, cam height squared, scale and scale over depth.
 737   mMiscSC = mShader->getShaderConstHandle( "$misc" );
 738
 739   // Inner and out radius, and inner and outer radius squared.
 740   mSphereRadiiSC = mShader->getShaderConstHandle( "$sphereRadii" );
 741
 742   // Rayleigh sun brightness, mie sun brightness and 4 * PI * coefficients.
 743   mScatteringCoefficientsSC = mShader->getShaderConstHandle( "$scatteringCoeffs" );
 744   mCamPosSC = mShader->getShaderConstHandle( "$camPos" );
 745   mLightDirSC = mShader->getShaderConstHandle( "$lightDir" );
 746   mSunDirSC = mShader->getShaderConstHandle( "$sunDir" );
 747   mNightColorSC = mShader->getShaderConstHandle( "$nightColor" );
 748   mInverseWavelengthSC = mShader->getShaderConstHandle( "$invWaveLength" );
 749   mNightInterpolantAndExposureSC = mShader->getShaderConstHandle( "$nightInterpAndExposure" );
 750   mUseCubemapSC = mShader->getShaderConstHandle( "$useCubemap" );
 751   mColorizeSC = mShader->getShaderConstHandle( "$colorize" );
 752
 753   return true;
 754}
 755
 756void ScatterSky::_initVBIB()
 757{
 758   // Vertex Buffer...
 759   U32 vertStride = 50;
 760   U32 strideMinusOne = vertStride - 1;
 761   mVertCount = vertStride * vertStride;
 762   mPrimCount = strideMinusOne * strideMinusOne * 2;
 763
 764   Point3F vertScale( 16.0f, 16.0f, 4.0f );
 765
 766   F32 zOffset = -( mCos( mSqrt( 1.0f ) ) + 0.01f );
 767
 768   mVB.set( GFX, mVertCount, GFXBufferTypeStatic );
 769   GFXVertexP *pVert = mVB.lock();
 770   if(!pVert) return;
 771
 772   for ( U32 y = 0; y < vertStride; y++ )
 773   {
 774      F32 v = ( (F32)y / (F32)strideMinusOne - 0.5f ) * 2.0f;
 775
 776      for ( U32 x = 0; x < vertStride; x++ )
 777      {
 778         F32 u = ( (F32)x / (F32)strideMinusOne - 0.5f ) * 2.0f;
 779
 780         F32 sx = u;
 781         F32 sy = v;
 782         F32 sz = (mCos( mSqrt( sx*sx + sy*sy ) ) * 1.0f) + zOffset;
 783         //F32 sz = 1.0f;
 784         pVert->point.set( sx, sy, sz );
 785         pVert->point *= vertScale;
 786
 787         pVert->point.normalize();
 788         pVert->point *= 200000.0f;
 789
 790         pVert++;
 791      }
 792   }
 793
 794   mVB.unlock();
 795
 796   // Primitive Buffer...
 797   mPrimBuffer.set( GFX, mPrimCount * 3, mPrimCount, GFXBufferTypeStatic );
 798
 799   U16 *pIdx = NULL;
 800   mPrimBuffer.lock(&pIdx);
 801   U32 curIdx = 0;
 802
 803   for ( U32 y = 0; y < strideMinusOne; y++ )
 804   {
 805      for ( U32 x = 0; x < strideMinusOne; x++ )
 806      {
 807         U32 offset = x + y * vertStride;
 808
 809         pIdx[curIdx] = offset;
 810         curIdx++;
 811         pIdx[curIdx] = offset + 1;
 812         curIdx++;
 813         pIdx[curIdx] = offset + vertStride + 1;
 814         curIdx++;
 815
 816         pIdx[curIdx] = offset;
 817         curIdx++;
 818         pIdx[curIdx] = offset + vertStride + 1;
 819         curIdx++;
 820         pIdx[curIdx] = offset + vertStride;
 821         curIdx++;
 822      }
 823   }
 824
 825   mPrimBuffer.unlock();
 826}
 827
 828void ScatterSky::_initMoon()
 829{
 830   if ( isServerObject() )
 831      return;
 832
 833   if ( mMoonMatInst )
 834      SAFE_DELETE( mMoonMatInst );
 835
 836   if ( mMoonMatName.isNotEmpty() )
 837      mMoonMatInst = MATMGR->createMatInstance( mMoonMatName, MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPCT>() );
 838}
 839
 840void ScatterSky::_initCurves()
 841{
 842   if ( mCurves->getSampleCount() > 0 )
 843      return;
 844
 845   // Takes time of day (0-2) and returns
 846   // the night interpolant (0-1) day/night factor.
 847   // moonlight = 0, sunlight > 0
 848   mCurves[0].clear();
 849   mCurves[0].addPoint( 0.0f, 0.5f );// Sunrise
 850   mCurves[0].addPoint( 0.025f, 1.0f );//
 851   mCurves[0].addPoint( 0.975f, 1.0f );//
 852   mCurves[0].addPoint( 1.0f, 0.5f );//Sunset
 853   mCurves[0].addPoint( 1.02f, 0.0f );//Sunlight ends
 854   mCurves[0].addPoint( 1.98f, 0.0f );//Sunlight begins
 855   mCurves[0].addPoint( 2.0f, 0.5f );// Sunrise
 856
 857    //  Takes time of day (0-2) and returns mieScattering factor
 858   //   Regulates the size of the sun's disk
 859   mCurves[1].clear();
 860   mCurves[1].addPoint( 0.0f, 0.0006f );
 861   mCurves[1].addPoint( 0.01f, 0.00035f );
 862   mCurves[1].addPoint( 0.03f, 0.00023f );
 863   mCurves[1].addPoint( 0.1f, 0.00022f );
 864   mCurves[1].addPoint( 0.2f, 0.00043f );
 865   mCurves[1].addPoint( 0.3f, 0.00062f );
 866   mCurves[1].addPoint( 0.4f, 0.0008f );
 867   mCurves[1].addPoint( 0.5f, 0.00086f );// High noon
 868   mCurves[1].addPoint( 0.6f, 0.0008f );
 869   mCurves[1].addPoint( 0.7f, 0.00062f );
 870   mCurves[1].addPoint( 0.8f, 0.00043f );
 871   mCurves[1].addPoint( 0.9f, 0.00022f );
 872   mCurves[1].addPoint( 0.97f, 0.00023f );
 873   mCurves[1].addPoint( 0.99f, 0.00035f );
 874   mCurves[1].addPoint( 1.0f, 0.0006f );
 875   mCurves[1].addPoint( 2.0f, 0.0006f );
 876
 877   // Takes time of day and returns brightness
 878   // Controls sunlight and moonlight brightness
 879   mCurves[2].clear();
 880   mCurves[2].addPoint( 0.0f, 0.2f );// Sunrise
 881   mCurves[2].addPoint( 0.1f, 1.0f );
 882   mCurves[2].addPoint( 0.9f, 1.0f );// Sunset
 883   mCurves[2].addPoint( 1.008f, 0.0f );//Adjust end of sun's reflection
 884   mCurves[2].addPoint( 1.02001f, 0.0f );
 885   mCurves[2].addPoint( 1.05f, 0.5f );// Turn brightness up for moonlight
 886   mCurves[2].addPoint( 1.93f, 0.5f );
 887   mCurves[2].addPoint( 1.97999f, 0.0f );// No brightness when sunlight starts
 888   mCurves[2].addPoint( 1.992f, 0.0f );//Adjust start of sun's reflection
 889   mCurves[2].addPoint( 2.0f, 0.2f ); // Sunrise
 890
 891   // Interpolation of day/night color sets
 892   // 0/1  ambient/nightcolor
 893   // 0 = day colors only anytime
 894   // 1 = night colors only anytime
 895   // between 0 and 1 renders both color sets anytime
 896
 897   mCurves[3].clear();
 898   mCurves[3].addPoint( 0.0f, 0.8f );//Sunrise
 899   mCurves[3].addPoint( 0.1f, 0.0f );
 900   mCurves[3].addPoint( 0.99f, 0.0f );
 901   mCurves[3].addPoint( 1.0f, 0.8f );// Sunset
 902   mCurves[3].addPoint( 1.01999f, 1.0f );//
 903   mCurves[3].addPoint( 1.98001f, 1.0f );// Sunlight begins with full night colors
 904   mCurves[3].addPoint( 2.0f, 0.8f );  //Sunrise
 905
 906   //  Takes time of day (0-2) and returns smoothing factor
 907   //  Interpolates between mMoonTint color and mNightColor
 908
 909   mCurves[4].clear();
 910   mCurves[4].addPoint( 0.0f, 1.0f );
 911   mCurves[4].addPoint( 0.96f, 1.0f );
 912   mCurves[4].addPoint( 1.01999f, 0.5f );
 913   mCurves[4].addPoint( 1.02001f, 0.5f );
 914   mCurves[4].addPoint( 1.08f, 1.0f );
 915   mCurves[4].addPoint( 1.92f, 1.0f );
 916   mCurves[4].addPoint( 1.97999f, 0.5f );
 917   mCurves[4].addPoint( 1.98001f, 0.5f );
 918   mCurves[4].addPoint( 2.0f, 1.0f );
 919}
 920void ScatterSky::_updateTimeOfDay( TimeOfDay *timeOfDay, F32 time )
 921{
 922   setElevation( timeOfDay->getElevationDegrees() );
 923   setAzimuth( timeOfDay->getAzimuthDegrees() );
 924}
 925
 926void ScatterSky::_render( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
 927{
 928   if ( overrideMat || (!mShader && !_initShader()) )
 929      return;
 930
 931   GFXTransformSaver saver;
 932
 933   if ( mVB.isNull() || mPrimBuffer.isNull() )
 934      _initVBIB();
 935
 936   GFX->setShader( mShader );
 937   GFX->setShaderConstBuffer( mShaderConsts );
 938
 939   Point4F sphereRadii( mSphereOuterRadius, mSphereOuterRadius * mSphereOuterRadius,
 940                        mSphereInnerRadius, mSphereInnerRadius * mSphereInnerRadius );
 941
 942   Point4F scatteringCoeffs( mRayleighScattering * mSkyBrightness, mRayleighScattering4PI,
 943                             mMieScattering * mSkyBrightness, mMieScattering4PI );
 944
 945   Point4F invWavelength(  1.0f / mWavelength4[0],
 946                           1.0f / mWavelength4[1],
 947                           1.0f / mWavelength4[2], 1.0f );
 948
 949   Point3F camPos( 0, 0, smViewerHeight );
 950   Point4F miscParams( camPos.z, camPos.z * camPos.z, mScale, mScale / mRayleighScaleDepth );
 951
 952   Frustum frust = state->getCameraFrustum();
 953   frust.setFarDist( smEarthRadius + smAtmosphereRadius );
 954   MatrixF proj( true );
 955   frust.getProjectionMatrix( &proj );
 956
 957   Point3F camPos2 = state->getCameraPosition();
 958   MatrixF xfm(true);
 959   xfm.setPosition(camPos2 - Point3F(0, 0, mZOffset));
 960   GFX->multWorld(xfm);
 961
 962   MatrixF xform(proj);//GFX->getProjectionMatrix());
 963   xform *= GFX->getViewMatrix();
 964   xform *=  GFX->getWorldMatrix();
 965
 966   if(state->isReflectPass())
 967   {
 968      static MatrixF rotMat(EulerF(0.0, 0.0, M_PI_F));
 969      xform.mul(rotMat);
 970      rotMat.set(EulerF(M_PI_F, 0.0, 0.0));
 971      xform.mul(rotMat);
 972   }
 973
 974   mShaderConsts->setSafe( mModelViewProjSC, xform );
 975   mShaderConsts->setSafe( mMiscSC, miscParams );
 976   mShaderConsts->setSafe( mSphereRadiiSC, sphereRadii );
 977   mShaderConsts->setSafe( mScatteringCoefficientsSC, scatteringCoeffs );
 978   mShaderConsts->setSafe( mCamPosSC, camPos );
 979   mShaderConsts->setSafe( mLightDirSC, mLightDir );
 980   mShaderConsts->setSafe( mSunDirSC, mSunDir );
 981   mShaderConsts->setSafe( mNightColorSC, mNightColor );
 982   mShaderConsts->setSafe( mInverseWavelengthSC, invWavelength );
 983   mShaderConsts->setSafe( mNightInterpolantAndExposureSC, Point2F( mExposure, mNightInterpolant ) );
 984   mShaderConsts->setSafe( mColorizeSC, mColorize*mColorizeAmt );
 985
 986   if ( GFXDevice::getWireframe() )
 987   {
 988      GFXStateBlockDesc desc( mStateBlock->getDesc() );
 989      desc.setFillModeWireframe();
 990      GFX->setStateBlockByDesc( desc );
 991   }
 992   else
 993      GFX->setStateBlock( mStateBlock );
 994
 995   if ( mUseNightCubemap && mNightCubemap )
 996   {
 997      mShaderConsts->setSafe( mUseCubemapSC, 1.0f );
 998
 999      if ( !mNightCubemap->mCubemap )
1000         mNightCubemap->createMap();
1001
1002      GFX->setCubeTexture( 0, mNightCubemap->mCubemap );
1003   }
1004   else
1005   {
1006      GFX->setCubeTexture( 0, NULL );
1007      mShaderConsts->setSafe( mUseCubemapSC, 0.0f );
1008   }
1009
1010   GFX->setPrimitiveBuffer( mPrimBuffer );
1011   GFX->setVertexBuffer( mVB );
1012
1013   GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, mVertCount, 0, mPrimCount );
1014}
1015
1016void ScatterSky::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
1017{
1018   GFXStateBlockDesc desc;
1019   desc.fillMode = GFXFillSolid;
1020   desc.setBlend( false, GFXBlendOne, GFXBlendZero );
1021   desc.setZReadWrite( false, false );
1022   GFXStateBlockRef sb = GFX->GFX->createStateBlock( desc );
1023
1024   GFX->setStateBlock( sb );
1025
1026   PrimBuild::begin( GFXLineStrip, mSkyPoints.size() );
1027   PrimBuild::color3i( 255, 0, 255 );
1028
1029   for ( U32 i = 0; i < mSkyPoints.size(); i++ )
1030   {
1031      Point3F pnt = mSkyPoints[i];
1032      pnt.normalize();
1033      pnt *= 500;
1034      pnt += state->getCameraPosition();
1035      PrimBuild::vertex3fv( pnt );
1036   }
1037
1038   PrimBuild::end();
1039}
1040
1041void ScatterSky::_renderMoon( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
1042{
1043   if ( !mMoonMatInst )
1044      return;
1045
1046   Point3F moonlightPosition = state->getCameraPosition() - /*mLight->getDirection()*/ mMoonLightDir * state->getFarPlane() * 0.9f;
1047   F32 dist = (moonlightPosition - state->getCameraPosition()).len();
1048
1049   // worldRadius = screenRadius * dist / worldToScreen
1050   // screenRadius = worldRadius / dist * worldToScreen
1051
1052   //
1053   F32 screenRadius = GFX->getViewport().extent.y * mMoonScale * 0.5f;
1054   F32 worldRadius = screenRadius * dist / state->getWorldToScreenScale().y;
1055
1056   // Calculate Billboard Radius (in world units) to be constant, independent of distance.
1057   // Takes into account distance, viewport size, and specified size in editor
1058
1059   F32 BBRadius = worldRadius;
1060
1061
1062   mMatrixSet->restoreSceneViewProjection();
1063
1064   if ( state->isReflectPass() )
1065      mMatrixSet->setProjection( state->getSceneManager()->getNonClipProjection() );
1066
1067   mMatrixSet->setWorld( MatrixF::Identity );
1068
1069   // Initialize points with basic info
1070   Point3F points[4];
1071   points[0] = Point3F( -BBRadius, 0.0, -BBRadius);
1072   points[1] = Point3F( -BBRadius, 0.0, BBRadius);
1073   points[2] = Point3F( BBRadius, 0.0, -BBRadius);
1074   points[3] = Point3F( BBRadius, 0.0, BBRadius);
1075
1076   static const Point2F sCoords[4] =
1077   {
1078      Point2F( 0.0f, 0.0f ),
1079      Point2F( 0.0f, 1.0f ),
1080      Point2F( 1.0f, 0.0f ),
1081      Point2F( 1.0f, 1.0f )
1082   };
1083
1084   // Get info we need to adjust points
1085   const MatrixF &camView = state->getCameraTransform();
1086
1087   // Finalize points
1088   for(S32 i = 0; i < 4; i++)
1089   {
1090      // align with camera
1091      camView.mulV(points[i]);
1092      // offset
1093      points[i] += moonlightPosition;
1094   }
1095
1096   // Vertex color.
1097   LinearColorF moonVertColor( 1.0f, 1.0f, 1.0f, mNightInterpolant );
1098
1099   // Copy points to buffer.
1100
1101   GFXVertexBufferHandle< GFXVertexPCT> vb;
1102   vb.set( GFX, 4, GFXBufferTypeVolatile );
1103   GFXVertexPCT *pVert = vb.lock();
1104   if(!pVert) return;
1105
1106   for ( S32 i = 0; i < 4; i++ )
1107   {
1108      pVert->color.set( moonVertColor.toColorI());
1109      pVert->point.set( points[i] );
1110      pVert->texCoord.set( sCoords[i].x, sCoords[i].y );
1111      pVert++;
1112   }
1113
1114   vb.unlock();
1115
1116   // Setup SceneData struct.
1117
1118   SceneData sgData;
1119   sgData.wireframe = GFXDevice::getWireframe();
1120   sgData.visibility = 1.0f;
1121
1122   // Draw it
1123
1124   while ( mMoonMatInst->setupPass( state, sgData ) )
1125   {
1126      mMoonMatInst->setTransforms( *mMatrixSet, state );
1127      mMoonMatInst->setSceneInfo( state, sgData );
1128
1129      GFX->setVertexBuffer( vb );
1130      GFX->drawPrimitive( GFXTriangleStrip, 0, 2 );
1131   }
1132}
1133
1134void ScatterSky::_generateSkyPoints()
1135{
1136   U32 rings=60, segments=20;//rings=160, segments=20;
1137
1138   Point3F tmpPoint( 0, 0, 0 );
1139
1140   // Establish constants used in sphere generation.
1141   F32 deltaRingAngle = ( M_PI_F / (F32)(rings * 2) );
1142   F32 deltaSegAngle = ( 2.0f * M_PI_F / (F32)segments );
1143
1144   // Generate the group of rings for the sphere.
1145   for( S32 ring = 0; ring < 2; ring++ )
1146   {
1147      F32 r0 = mSin( ring * deltaRingAngle );
1148      F32 y0 = mCos( ring * deltaRingAngle );
1149
1150      // Generate the group of segments for the current ring.
1151      for( S32 seg = 0; seg < segments + 1 ; seg++ )
1152      {
1153         F32 x0 = r0 * sinf( seg * deltaSegAngle );
1154         F32 z0 = r0 * cosf( seg * deltaSegAngle );
1155
1156         tmpPoint.set( x0, z0, y0 );
1157         tmpPoint.normalizeSafe();
1158
1159         tmpPoint.x *= smEarthRadius + smAtmosphereRadius;
1160         tmpPoint.y *= smEarthRadius + smAtmosphereRadius;
1161         tmpPoint.z *= smEarthRadius + smAtmosphereRadius;
1162         tmpPoint.z -= smEarthRadius;
1163
1164         if ( ring == 1 )
1165            mSkyPoints.push_back( tmpPoint );
1166      }
1167   }
1168}
1169
1170void ScatterSky::_interpolateColors()
1171{
1172   mFogColor.set( 0, 0, 0, 0 );
1173   mAmbientColor.set( 0, 0, 0, 0 );
1174   mSunColor.set( 0, 0, 0, 0 );
1175
1176   _getFogColor( &mFogColor );
1177   _getAmbientColor( &mAmbientColor );
1178   _getSunColor( &mSunColor );
1179
1180   mAmbientColor *= mAmbientScale;
1181   mSunColor *= mSunScale;
1182   mFogColor *= mFogScale;
1183
1184   mMieScattering = (mCurves[1].getVal( mTimeOfDay) * mSunSize ); //Scale the size of the sun's disk
1185
1186   LinearColorF moonTemp = mMoonTint;
1187   LinearColorF nightTemp = mNightColor;
1188
1189   moonTemp.interpolate( mNightColor, mMoonTint, mCurves[4].getVal( mTimeOfDay ) );
1190   nightTemp.interpolate( mMoonTint, mNightColor, mCurves[4].getVal( mTimeOfDay ) );
1191
1192   mFogColor.interpolate( mFogColor, mNightFogColor, mCurves[3].getVal( mTimeOfDay ) );//mNightInterpolant );
1193   mFogColor.alpha = 1.0f;
1194
1195   mAmbientColor.interpolate( mAmbientColor, mNightColor, mCurves[3].getVal( mTimeOfDay ) );//mNightInterpolant );
1196   mSunColor.interpolate( mSunColor, mMoonTint, mCurves[3].getVal( mTimeOfDay ) );//mNightInterpolant );
1197}
1198
1199void ScatterSky::_getSunColor( LinearColorF *outColor )
1200{
1201   PROFILE_SCOPE( ScatterSky_GetSunColor );
1202
1203   U32 count = 0;
1204   LinearColorF tmpColor( 0, 0, 0 );
1205   VectorF tmpVec( 0, 0, 0 );
1206
1207   tmpVec = mLightDir;
1208   tmpVec.x *= smEarthRadius + smAtmosphereRadius;
1209   tmpVec.y *= smEarthRadius + smAtmosphereRadius;
1210   tmpVec.z *= smEarthRadius + smAtmosphereRadius;
1211   tmpVec.z -= smAtmosphereRadius;
1212
1213   for ( U32 i = 0; i < 10; i++ )
1214   {
1215      _getColor( tmpVec, &tmpColor );
1216      (*outColor) += tmpColor;
1217      tmpVec.x += (smEarthRadius * 0.5f) + (smAtmosphereRadius * 0.5f);
1218      count++;
1219   }
1220
1221   if ( count > 0 )
1222      (*outColor) /= count;
1223}
1224
1225void ScatterSky::_getAmbientColor( LinearColorF *outColor )
1226{
1227   PROFILE_SCOPE( ScatterSky_GetAmbientColor );
1228
1229   LinearColorF tmpColor( 0, 0, 0, 0 );
1230   U32 count = 0;
1231
1232   // Disable mieScattering for purposes of calculating the ambient color.
1233   F32 oldMieScattering = mMieScattering;
1234   mMieScattering = 0.0f;
1235
1236   for ( U32 i = 0; i < mSkyPoints.size(); i++ )
1237   {
1238      Point3F pnt( mSkyPoints[i] );
1239
1240      _getColor( pnt, &tmpColor );
1241      (*outColor) += tmpColor;
1242      count++;
1243   }
1244
1245   if ( count > 0 )
1246      (*outColor) /= count;
1247   mMieScattering = oldMieScattering;
1248}
1249
1250void ScatterSky::_getFogColor( LinearColorF *outColor )
1251{
1252   PROFILE_SCOPE( ScatterSky_GetFogColor );
1253
1254   VectorF scatterPos( 0, 0, 0 );
1255
1256   F32 sunBrightness = mSkyBrightness;
1257   mSkyBrightness *= 0.25f;
1258
1259   F32 yaw = 0, pitch = 0, originalYaw = 0;
1260   VectorF fwd( 0, 1.0f, 0 );
1261   MathUtils::getAnglesFromVector( fwd, yaw, pitch );
1262   originalYaw = yaw;
1263   pitch = mDegToRad( 10.0f );
1264
1265   LinearColorF tmpColor( 0, 0, 0 );
1266
1267   U32 i = 0;
1268   for ( i = 0; i < 10; i++ )
1269   {
1270      MathUtils::getVectorFromAngles( scatterPos, yaw, pitch );
1271
1272      scatterPos.x *= smEarthRadius + smAtmosphereRadius;
1273      scatterPos.y *= smEarthRadius + smAtmosphereRadius;
1274      scatterPos.z *= smEarthRadius + smAtmosphereRadius;
1275      scatterPos.y -= smEarthRadius;
1276
1277      _getColor( scatterPos, &tmpColor );
1278      (*outColor) += tmpColor;
1279
1280      if ( i <= 5 )
1281         yaw += mDegToRad( 5.0f );
1282      else
1283      {
1284         originalYaw += mDegToRad( -5.0f );
1285         yaw = originalYaw;
1286      }
1287
1288      yaw = mFmod( yaw, M_2PI_F );
1289   }
1290
1291   if ( i > 0 )
1292      (*outColor) /= i;
1293
1294   mSkyBrightness = sunBrightness;
1295}
1296
1297F32 ScatterSky::_vernierScale( F32 fCos )
1298{
1299   F32 x = 1.0 - fCos;
1300   return 0.25f * exp( -0.00287f + x * (0.459f + x * (3.83f + x * ((-6.80f + (x * 5.25f))))) );
1301}
1302
1303F32 ScatterSky::_getMiePhase( F32 fCos, F32 fCos2, F32 g, F32 g2)
1304{
1305   return 1.5f * ((1.0f - g2) / (2.0f + g2)) * (1.0f + fCos2) / mPow(mFabs(1.0f + g2 - 2.0f*g*fCos), 1.5f);
1306}
1307
1308F32 ScatterSky::_getRayleighPhase( F32 fCos2 )
1309{
1310   return 0.75 + 0.75 * fCos2;
1311}
1312
1313void ScatterSky::_getColor( const Point3F &pos, LinearColorF *outColor )
1314{
1315   PROFILE_SCOPE( ScatterSky_GetColor );
1316
1317   F32 scaleOverScaleDepth = mScale / mRayleighScaleDepth;
1318   F32 rayleighBrightness = mRayleighScattering * mSkyBrightness;
1319   F32 mieBrightness = mMieScattering * mSkyBrightness;
1320
1321   Point3F invWaveLength(  1.0f / mWavelength4[0],
1322                           1.0f / mWavelength4[1],
1323                           1.0f / mWavelength4[2] );
1324
1325   Point3F v3Pos = pos / 6378000.0f;
1326   v3Pos.z += mSphereInnerRadius;
1327
1328   Point3F newCamPos( 0, 0, smViewerHeight );
1329
1330   VectorF v3Ray = v3Pos - newCamPos;
1331   F32 fFar = v3Ray.len();
1332   v3Ray / fFar;
1333   v3Ray.normalizeSafe();
1334
1335   Point3F v3Start = newCamPos;
1336   F32 fDepth = mExp( scaleOverScaleDepth * (mSphereInnerRadius - smViewerHeight ) );
1337   F32 fStartAngle = mDot( v3Ray, v3Start );
1338
1339   F32 fStartOffset = fDepth * _vernierScale( fStartAngle );
1340
1341   F32 fSampleLength = fFar / 2.0f;
1342   F32 fScaledLength = fSampleLength * mScale;
1343   VectorF v3SampleRay = v3Ray * fSampleLength;
1344   Point3F v3SamplePoint = v3Start + v3SampleRay * 0.5f;
1345
1346   Point3F v3FrontColor( 0, 0, 0 );
1347   for ( U32 i = 0; i < 2; i++ )
1348   {
1349      F32 fHeight = v3SamplePoint.len();
1350      fDepth = mExp( scaleOverScaleDepth * (mSphereInnerRadius - smViewerHeight) );
1351      F32 fLightAngle = mDot( mLightDir, v3SamplePoint ) / fHeight;
1352      F32 fCameraAngle = mDot( v3Ray, v3SamplePoint ) / fHeight;
1353
1354      F32 fScatter = (fStartOffset + fDepth * ( _vernierScale( fLightAngle ) - _vernierScale( fCameraAngle ) ));
1355      Point3F v3Attenuate( 0, 0, 0 );
1356
1357      F32 tmp = mExp( -fScatter * (invWaveLength[0] * mRayleighScattering4PI + mMieScattering4PI) );
1358      v3Attenuate.x = tmp;
1359
1360      tmp = mExp( -fScatter * (invWaveLength[1] * mRayleighScattering4PI + mMieScattering4PI) );
1361      v3Attenuate.y = tmp;
1362
1363      tmp = mExp( -fScatter * (invWaveLength[2] * mRayleighScattering4PI + mMieScattering4PI) );
1364      v3Attenuate.z = tmp;
1365
1366      v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
1367      v3SamplePoint += v3SampleRay;
1368   }
1369
1370   Point3F mieColor = v3FrontColor * mieBrightness;
1371   Point3F rayleighColor = v3FrontColor * (invWaveLength * rayleighBrightness);
1372   Point3F v3Direction = newCamPos - v3Pos;
1373   v3Direction.normalize();
1374
1375   F32 fCos = mDot( mLightDir, v3Direction ) / v3Direction.len();
1376   F32 fCos2 = fCos * fCos;
1377
1378   F32 g = -0.991f;
1379   F32 g2 = g * g;
1380   F32 miePhase = _getMiePhase( fCos, fCos2, g, g2 );
1381
1382   Point3F color = rayleighColor + (miePhase * mieColor);
1383   LinearColorF tmp( color.x, color.y, color.z, color.y );
1384
1385   Point3F expColor( 0, 0, 0 );
1386   expColor.x = 1.0f - exp(-mExposure * color.x);
1387   expColor.y = 1.0f - exp(-mExposure * color.y);
1388   expColor.z = 1.0f - exp(-mExposure * color.z);
1389
1390   tmp.set( expColor.x, expColor.y, expColor.z, 1.0f );
1391
1392   if ( !tmp.isClamped() )
1393   {
1394      F32 len = expColor.len();
1395      if ( len > 0 )
1396         expColor /= len;
1397   }
1398
1399   outColor->set( expColor.x, expColor.y, expColor.z, 1.0f );
1400}
1401
1402// Static protected field set methods
1403
1404bool ScatterSky::ptSetElevation( void *object, const char *index, const char *data )
1405{
1406   ScatterSky *sky = static_cast<ScatterSky*>( object );
1407   F32 val = dAtof( data );
1408
1409   sky->setElevation( val );
1410
1411   // we already set the field
1412   return false;
1413}
1414
1415bool ScatterSky::ptSetAzimuth( void *object, const char *index, const char *data )
1416{
1417   ScatterSky *sky = static_cast<ScatterSky*>( object );
1418   F32 val = dAtof( data );
1419
1420   sky->setAzimuth( val );
1421
1422   // we already set the field
1423   return false;
1424}
1425
1426void ScatterSky::_onSelected()
1427{
1428#ifdef TORQUE_DEBUG
1429   // Enable debug rendering on the light.
1430   if( isClientObject() )
1431      mLight->enableDebugRendering( true );
1432#endif
1433
1434   Parent::_onSelected();
1435}
1436
1437void ScatterSky::_onUnselected()
1438{
1439#ifdef TORQUE_DEBUG
1440   // Disable debug rendering on the light.
1441   if( isClientObject() )
1442      mLight->enableDebugRendering( false );
1443#endif
1444
1445   Parent::_onUnselected();
1446}
1447
1448// ConsoleMethods
1449
1450DefineEngineMethod( ScatterSky, applyChanges, void, (),,
1451                   "Apply a full network update of all fields to all clients."
1452                  )
1453{
1454   object->inspectPostApply();
1455}
1456