Torque3D Documentation / _generateds / precipitation.cpp

precipitation.cpp

Engine/source/T3D/fx/precipitation.cpp

More...

Public Functions

ConsoleDocClass(Precipitation , "@brief Defines <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> precipitation based storm  , or <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> remain in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> fixed position(<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> simulate " "localized precipitation). When #followCam is true, the box containing the " "droplets can be thought of as centered on the camera then pushed slightly " "forward in the direction the camera is facing so most of the box is in " "front of the camera(allowing more drops <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be visible on screen at once).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "The effect can also be configured <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> create <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> small 'splash' whenever <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> drop " "hits another world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//The following is added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> level <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a>(.mis) by the <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classprecipitation/">Precipitation</a>(TheRain)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " dropSize=\"0.5\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   splashSize = \"0.5\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   splashMS = \"250\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   animateSplashes = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   dropAnimateMS = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   fadeDist = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   fadeDistEnd = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   useTrueBillboards = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   useLighting = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   glowIntensity = \"0 0 0 0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   reflect = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   rotateWithCamVel = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   doCollision = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   hitPlayers = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   hitVehicles = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   followCam = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   useWind = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   minSpeed = \"1.5\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   maxSpeed = \"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   minMass = \"0.75\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   maxMass = \"0.85\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   useTurbulence = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   maxTurbulence = \"0.1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   turbulenceSpeed = \"0.2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   numDrops = \"1024\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   boxWidth = \"200\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   boxHeight = \"100\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   dataBlock = \"HeavyRain\";\n" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Atmosphere\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PrecipitationData\n</a>" )
ConsoleDocClass(PrecipitationData , "@brief Defines the droplets used in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> storm (raindrops, snowflakes, etc).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classprecipitationdata/">PrecipitationData</a>( HeavyRain )\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   soundProfile = \"HeavyRainSound\";\n" "   dropTexture = \"art/environment/precipitation/rain\";\n" "   splashTexture = \"art/environment/precipitation/water_splash\";\n" "   dropsPerSide = 4;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   splashesPerSide = 2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Atmosphere\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Precipitation\n</a>" )
DefineEngineMethod(Precipitation , modifyStorm , void , (F32 percentage, F32 seconds) , (1.0f, 5.0f) , "Smoothly change the maximum number of drops in the effect (from current " "<a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> #numDrops * @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> percentage).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "This method can be used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> simulate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> storm building or fading in intensity " "as the number of drops in the <a href="/coding/class/classprecipitation/">Precipitation</a> box <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">changes.\n</a>" "@param percentage New maximum number of drops <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> (as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> percentage of " "#numDrops). Valid range is 0-1.\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param seconds Length of time (in seconds) over which <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> increase the drops " "percentage value. Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 0 <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> change <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">instantly.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "% percentage, from 0 <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 1)
DefineEngineMethod(Precipitation , setPercentage , void , (F32 percentage) , (1.0f) , "Sets the maximum number of drops in the effect)
DefineEngineMethod(Precipitation , setTurbulence , void , (F32 max, F32 speed, F32 seconds) , (1.0f, 5.0f, 5.0f) )

Detailed Description

Public Variables

const U32 dropHitMask 
IRangeValidator ValidNumDropsRange (1, 100000)

Public Functions

ConsoleDocClass(Precipitation , "@brief Defines <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> precipitation based storm  , or <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> remain in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> fixed position(<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> simulate " "localized precipitation). When #followCam is true, the box containing the " "droplets can be thought of as centered on the camera then pushed slightly " "forward in the direction the camera is facing so most of the box is in " "front of the camera(allowing more drops <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be visible on screen at once).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "The effect can also be configured <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> create <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> small 'splash' whenever <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> drop " "hits another world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//The following is added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> level <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a>(.mis) by the <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classprecipitation/">Precipitation</a>(TheRain)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " dropSize=\"0.5\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   splashSize = \"0.5\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   splashMS = \"250\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   animateSplashes = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   dropAnimateMS = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   fadeDist = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   fadeDistEnd = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   useTrueBillboards = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   useLighting = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   glowIntensity = \"0 0 0 0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   reflect = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   rotateWithCamVel = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   doCollision = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   hitPlayers = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   hitVehicles = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   followCam = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   useWind = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   minSpeed = \"1.5\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   maxSpeed = \"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   minMass = \"0.75\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   maxMass = \"0.85\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   useTurbulence = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   maxTurbulence = \"0.1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   turbulenceSpeed = \"0.2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   numDrops = \"1024\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   boxWidth = \"200\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   boxHeight = \"100\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   dataBlock = \"HeavyRain\";\n" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Atmosphere\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PrecipitationData\n</a>" )

ConsoleDocClass(PrecipitationData , "@brief Defines the droplets used in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> storm (raindrops, snowflakes, etc).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classprecipitationdata/">PrecipitationData</a>( HeavyRain )\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   soundProfile = \"HeavyRainSound\";\n" "   dropTexture = \"art/environment/precipitation/rain\";\n" "   splashTexture = \"art/environment/precipitation/water_splash\";\n" "   dropsPerSide = 4;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   splashesPerSide = 2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Atmosphere\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Precipitation\n</a>" )

DefineEngineMethod(Precipitation , modifyStorm , void , (F32 percentage, F32 seconds) , (1.0f, 5.0f) , "Smoothly change the maximum number of drops in the effect (from current " "<a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> #numDrops * @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> percentage).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "This method can be used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> simulate <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> storm building or fading in intensity " "as the number of drops in the <a href="/coding/class/classprecipitation/">Precipitation</a> box <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">changes.\n</a>" "@param percentage New maximum number of drops <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> (as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> percentage of " "#numDrops). Valid range is 0-1.\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param seconds Length of time (in seconds) over which <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> increase the drops " "percentage value. Set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 0 <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> change <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">instantly.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "% percentage, from 0 <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 1)

DefineEngineMethod(Precipitation , setPercentage , void , (F32 percentage) , (1.0f) , "Sets the maximum number of drops in the effect)

DefineEngineMethod(Precipitation , setTurbulence , void , (F32 max, F32 speed, F32 seconds) , (1.0f, 5.0f, 5.0f) )

IMPLEMENT_CO_DATABLOCK_V1(PrecipitationData )

IMPLEMENT_CO_NETOBJECT_V1(Precipitation )

   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 "T3D/fx/precipitation.h"
  26
  27#include "math/mathIO.h"
  28#include "console/consoleTypes.h"
  29#include "console/typeValidators.h"
  30#include "scene/sceneManager.h"
  31#include "scene/sceneRenderState.h"
  32#include "lighting/lightInfo.h"
  33#include "lighting/lightManager.h"
  34#include "materials/shaderData.h"
  35#include "T3D/gameBase/gameConnection.h"
  36#include "T3D/player.h"
  37#include "core/stream/bitStream.h"
  38#include "platform/profiler.h"
  39#include "renderInstance/renderPassManager.h"
  40#include "sfx/sfxSystem.h"
  41#include "sfx/sfxTrack.h"
  42#include "sfx/sfxSource.h"
  43#include "sfx/sfxTypes.h"
  44#include "console/engineAPI.h"
  45#include "particleEmitter.h"
  46
  47static const U32 dropHitMask = 
  48   TerrainObjectType |
  49   WaterObjectType |
  50   StaticShapeObjectType;
  51
  52IMPLEMENT_CO_NETOBJECT_V1(Precipitation);
  53IMPLEMENT_CO_DATABLOCK_V1(PrecipitationData);
  54
  55ConsoleDocClass( Precipitation,
  56   "@brief Defines a precipitation based storm (rain, snow, etc).\n\n"
  57
  58   "The Precipitation effect works by creating many 'drops' within a fixed size "
  59   "box. This box can be configured to move around with the camera (to simulate "
  60   "level-wide precipitation), or to remain in a fixed position (to simulate "
  61   "localized precipitation). When #followCam is true, the box containing the "
  62   "droplets can be thought of as centered on the camera then pushed slightly "
  63   "forward in the direction the camera is facing so most of the box is in "
  64   "front of the camera (allowing more drops to be visible on screen at once).\n\n"
  65
  66   "The effect can also be configured to create a small 'splash' whenever a drop "
  67   "hits another world object.\n\n"
  68
  69   "@tsexample\n"
  70   "// The following is added to a level file (.mis) by the World Editor\n"
  71   "new Precipitation( TheRain )\n"
  72   "{\n"
  73   "   dropSize = \"0.5\";\n"
  74   "   splashSize = \"0.5\";\n"
  75   "   splashMS = \"250\";\n"
  76   "   animateSplashes = \"1\";\n"
  77   "   dropAnimateMS = \"0\";\n"
  78   "   fadeDist = \"0\";\n"
  79   "   fadeDistEnd = \"0\";\n"
  80   "   useTrueBillboards = \"0\";\n"
  81   "   useLighting = \"0\";\n"
  82   "   glowIntensity = \"0 0 0 0\";\n"
  83   "   reflect = \"0\";\n"
  84   "   rotateWithCamVel = \"1\";\n"
  85   "   doCollision = \"1\";\n"
  86   "   hitPlayers = \"0\";\n"
  87   "   hitVehicles = \"0\";\n"
  88   "   followCam = \"1\";\n"
  89   "   useWind = \"0\";\n"
  90   "   minSpeed = \"1.5\";\n"
  91   "   maxSpeed = \"2\";\n"
  92   "   minMass = \"0.75\";\n"
  93   "   maxMass = \"0.85\";\n"
  94   "   useTurbulence = \"0\";\n"
  95   "   maxTurbulence = \"0.1\";\n"
  96   "   turbulenceSpeed = \"0.2\";\n"
  97   "   numDrops = \"1024\";\n"
  98   "   boxWidth = \"200\";\n"
  99   "   boxHeight = \"100\";\n"
 100   "   dataBlock = \"HeavyRain\";\n"
 101   "};\n"
 102   "@endtsexample\n"
 103   "@ingroup FX\n"
 104   "@ingroup Atmosphere\n"
 105   "@see PrecipitationData\n"
 106);
 107
 108ConsoleDocClass( PrecipitationData,
 109   "@brief Defines the droplets used in a storm (raindrops, snowflakes, etc).\n\n"
 110   "@tsexample\n"
 111   "datablock PrecipitationData( HeavyRain )\n"
 112   "{\n"
 113   "   soundProfile = \"HeavyRainSound\";\n"
 114   "   dropTexture = \"art/environment/precipitation/rain\";\n"
 115   "   splashTexture = \"art/environment/precipitation/water_splash\";\n"
 116   "   dropsPerSide = 4;\n"
 117   "   splashesPerSide = 2;\n"
 118   "};\n"
 119   "@endtsexample\n"
 120   "@ingroup FX\n"
 121   "@ingroup Atmosphere\n"
 122   "@see Precipitation\n"
 123);
 124
 125
 126//----------------------------------------------------------
 127// PrecipitationData
 128//----------------------------------------------------------
 129PrecipitationData::PrecipitationData()
 130{
 131   soundProfile      = NULL;
 132
 133   mDropName         = StringTable->EmptyString();
 134   mDropShaderName   = StringTable->EmptyString();
 135   mSplashName       = StringTable->EmptyString();
 136   mSplashShaderName = StringTable->EmptyString();
 137
 138   mDropsPerSide     = 4;
 139   mSplashesPerSide  = 2;
 140}
 141
 142void PrecipitationData::initPersistFields()
 143{
 144   addField( "soundProfile", TYPEID< SFXTrack >(), Offset(soundProfile, PrecipitationData),
 145      "Looping SFXProfile effect to play while Precipitation is active." );
 146   addField( "dropTexture", TypeFilename, Offset(mDropName, PrecipitationData),
 147      "@brief Texture filename for drop particles.\n\n"
 148      "The drop texture can contain several different drop sub-textures "
 149      "arranged in a grid. There must be the same number of rows as columns. A "
 150      "random frame will be chosen for each drop." );
 151   addField( "dropShader", TypeString, Offset(mDropShaderName, PrecipitationData),
 152      "The name of the shader used for raindrops." );
 153   addField( "splashTexture", TypeFilename, Offset(mSplashName, PrecipitationData),
 154      "@brief Texture filename for splash particles.\n\n"
 155      "The splash texture can contain several different splash sub-textures "
 156      "arranged in a grid. There must be the same number of rows as columns. A "
 157      "random frame will be chosen for each splash." );
 158   addField( "splashShader", TypeString, Offset(mSplashShaderName, PrecipitationData),
 159      "The name of the shader used for splashes." );
 160   addField( "dropsPerSide", TypeS32, Offset(mDropsPerSide, PrecipitationData),
 161      "@brief How many rows and columns are in the raindrop texture.\n\n"
 162      "For example, if the texture has 16 raindrops arranged in a grid, this "
 163      "field should be set to 4." );
 164   addField( "splashesPerSide", TypeS32, Offset(mSplashesPerSide, PrecipitationData),
 165      "@brief How many rows and columns are in the splash texture.\n\n"
 166      "For example, if the texture has 9 splashes arranged in a grid, this "
 167      "field should be set to 3." );
 168
 169   Parent::initPersistFields();
 170}
 171
 172bool PrecipitationData::preload( bool server, String &errorStr )
 173{
 174   if( Parent::preload( server, errorStr) == false)
 175      return false;
 176
 177   if( !server && !sfxResolve( &soundProfile, errorStr ) )
 178      return false;
 179
 180   return true;
 181}
 182
 183void PrecipitationData::packData(BitStream* stream)
 184{
 185   Parent::packData(stream);
 186
 187   sfxWrite( stream, soundProfile );
 188
 189   stream->writeString(mDropName);
 190   stream->writeString(mDropShaderName);
 191   stream->writeString(mSplashName);
 192   stream->writeString(mSplashShaderName);
 193   stream->write(mDropsPerSide);
 194   stream->write(mSplashesPerSide);
 195}
 196
 197void PrecipitationData::unpackData(BitStream* stream)
 198{
 199   Parent::unpackData(stream);
 200
 201   sfxRead( stream, &soundProfile );
 202
 203   mDropName = stream->readSTString();
 204   mDropShaderName = stream->readSTString();
 205   mSplashName = stream->readSTString();
 206   mSplashShaderName = stream->readSTString();
 207   stream->read(&mDropsPerSide);
 208   stream->read(&mSplashesPerSide);
 209}
 210
 211//----------------------------------------------------------
 212// Precipitation!
 213//----------------------------------------------------------
 214Precipitation::Precipitation()
 215{
 216   mTypeMask |= ProjectileObjectType;
 217
 218   mDataBlock = NULL;
 219
 220   mTexCoords = NULL;
 221   mSplashCoords = NULL;
 222
 223   mDropShader = NULL;
 224   mDropHandle = NULL;
 225
 226   mSplashShader = NULL;
 227   mSplashHandle = NULL;
 228
 229   mDropHead   = NULL;
 230   mSplashHead = NULL;
 231   mNumDrops   = 1024;
 232   mPercentage = 1.0;
 233
 234   mMinSpeed   = 1.5;
 235   mMaxSpeed   = 2.0;
 236
 237   mFollowCam = true;
 238
 239   mLastRenderFrame = 0;
 240
 241   mDropHitMask = 0;
 242
 243   mDropSize          = 0.5;
 244   mSplashSize        = 0.5;
 245   mUseTrueBillboards = false;
 246   mSplashMS          = 250;
 247
 248   mAnimateSplashes  = true;
 249   mDropAnimateMS    = 0;
 250
 251   mUseLighting = false;
 252   mGlowIntensity = LinearColorF( 0,0,0,0 );
 253
 254   mReflect = false;
 255
 256   mUseWind = false;
 257
 258   mBoxWidth   = 200;
 259   mBoxHeight  = 100;
 260   mFadeDistance = 0;
 261   mFadeDistanceEnd = 0;
 262
 263   mMinMass    = 0.75f;
 264   mMaxMass    = 0.85f;
 265
 266   mMaxTurbulence = 0.1f;
 267   mTurbulenceSpeed = 0.2f;
 268   mUseTurbulence = false;
 269
 270   mRotateWithCamVel = true;
 271
 272   mDoCollision = true;
 273   mDropHitPlayers = false;
 274   mDropHitVehicles = false;
 275
 276   mStormData.valid = false;
 277   mStormData.startPct = 0;
 278   mStormData.endPct = 0;
 279   mStormData.startTime = 0;
 280   mStormData.totalTime = 0;
 281
 282   mTurbulenceData.valid = false;
 283   mTurbulenceData.startTime = 0;
 284   mTurbulenceData.totalTime = 0;
 285   mTurbulenceData.startMax = 0;
 286   mTurbulenceData.startSpeed = 0;
 287   mTurbulenceData.endMax = 0;
 288   mTurbulenceData.endSpeed = 0;
 289
 290   mAmbientSound = NULL;
 291
 292   mDropShaderModelViewSC = NULL;
 293   mDropShaderFadeStartEndSC = NULL;
 294   mDropShaderCameraPosSC = NULL;
 295   mDropShaderAmbientSC = NULL;
 296
 297   mSplashShaderModelViewSC = NULL;
 298   mSplashShaderFadeStartEndSC = NULL;
 299   mSplashShaderCameraPosSC = NULL;
 300   mSplashShaderAmbientSC = NULL;
 301
 302   mMaxVBDrops = 5000;
 303}
 304
 305Precipitation::~Precipitation()
 306{
 307   SAFE_DELETE_ARRAY(mTexCoords);
 308   SAFE_DELETE_ARRAY(mSplashCoords);
 309}
 310
 311void Precipitation::inspectPostApply()
 312{
 313   if (mFollowCam)
 314   {
 315      setGlobalBounds();
 316   }
 317   else
 318   {
 319      mObjBox.minExtents = -Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2);
 320      mObjBox.maxExtents = Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2);
 321   }
 322
 323   resetWorldBox();
 324   setMaskBits(DataMask);
 325}
 326
 327void Precipitation::setTransform(const MatrixF & mat)
 328{
 329   Parent::setTransform(mat);
 330
 331   setMaskBits(TransformMask);
 332}
 333
 334//--------------------------------------------------------------------------
 335// Console stuff...
 336//--------------------------------------------------------------------------
 337
 338IRangeValidator ValidNumDropsRange(1, 100000);
 339
 340void Precipitation::initPersistFields()
 341{
 342   addGroup("Precipitation");
 343
 344      addFieldV( "numDrops", TypeS32, Offset(mNumDrops, Precipitation), &ValidNumDropsRange,
 345         "@brief Maximum number of drops allowed to exist in the precipitation "
 346         "box at any one time.\n\n"
 347         "The actual number of drops in the effect depends on the current "
 348         "percentage, which can change over time using modifyStorm()." );
 349
 350      addField( "boxWidth", TypeF32, Offset(mBoxWidth, Precipitation),
 351         "Width and depth (horizontal dimensions) of the precipitation box." );
 352
 353      addField( "boxHeight", TypeF32, Offset(mBoxHeight, Precipitation),
 354         "Height (vertical dimension) of the precipitation box." );
 355
 356   endGroup("Precipitation");
 357
 358   addGroup("Rendering");
 359
 360      addField( "dropSize", TypeF32, Offset(mDropSize, Precipitation),
 361         "Size of each drop of precipitation. This will scale the texture." );
 362
 363      addField( "splashSize", TypeF32, Offset(mSplashSize, Precipitation),
 364         "Size of each splash animation when a drop collides with another surface." );
 365
 366      addField( "splashMS", TypeS32, Offset(mSplashMS, Precipitation),
 367         "Lifetime of splashes in milliseconds." );
 368
 369      addField( "animateSplashes", TypeBool, Offset(mAnimateSplashes, Precipitation),
 370         "Set to true to enable splash animations when drops collide with other surfaces." );
 371
 372      addField( "dropAnimateMS", TypeS32, Offset(mDropAnimateMS, Precipitation),
 373         "@brief Length (in milliseconds) to display each drop frame.\n\n"
 374         "If #dropAnimateMS <= 0, drops select a single random frame at creation "
 375         "that does not change throughout the drop's lifetime. If #dropAnimateMS "
 376         "> 0, each drop cycles through the the available frames in the drop "
 377         "texture at the given rate." );
 378
 379      addField( "fadeDist", TypeF32, Offset(mFadeDistance, Precipitation),
 380         "The distance at which drops begin to fade out." );
 381
 382      addField( "fadeDistEnd", TypeF32, Offset(mFadeDistanceEnd, Precipitation),
 383         "The distance at which drops are completely faded out." );
 384
 385      addField( "useTrueBillboards", TypeBool, Offset(mUseTrueBillboards, Precipitation),
 386         "Set to true to make drops true (non axis-aligned) billboards." );
 387
 388      addField( "useLighting", TypeBool, Offset(mUseLighting, Precipitation),
 389         "Set to true to enable shading of the drops and splashes by the sun color." );
 390
 391      addField( "glowIntensity", TypeColorF, Offset(mGlowIntensity, Precipitation),
 392         "Set to 0 to disable the glow or or use it to control the intensity of each channel." );
 393
 394      addField( "reflect", TypeBool, Offset(mReflect, Precipitation),
 395         "@brief This enables precipitation rendering during reflection passes.\n\n"
 396         "@note This is expensive." );
 397
 398      addField( "rotateWithCamVel",  TypeBool, Offset(mRotateWithCamVel, Precipitation),
 399         "Set to true to include the camera velocity when calculating drop "
 400         "rotation speed." );
 401
 402   endGroup("Rendering");
 403
 404   addGroup("Collision");
 405
 406      addField( "doCollision", TypeBool, Offset(mDoCollision, Precipitation),
 407         "@brief Allow drops to collide with world objects.\n\n"
 408         "If #animateSplashes is true, drops that collide with another object "
 409         "will produce a simple splash animation.\n"
 410         "@note This can be expensive as each drop will perform a raycast when "
 411         "it is created to determine where it will hit." );
 412
 413      addField( "hitPlayers", TypeBool, Offset(mDropHitPlayers, Precipitation),
 414         "Allow drops to collide with Player objects; only valid if #doCollision is true." );
 415
 416      addField( "hitVehicles", TypeBool, Offset(mDropHitVehicles, Precipitation),
 417         "Allow drops to collide with Vehicle objects; only valid if #doCollision is true." );
 418
 419   endGroup("Collision");
 420
 421   addGroup("Movement");
 422
 423      addField( "followCam", TypeBool, Offset(mFollowCam, Precipitation),
 424         "@brief Controls whether the Precipitation system follows the camera "
 425         "or remains where it is first placed in the scene.\n\n"
 426         "Set to true to make it seem like it is raining everywhere in the "
 427         "level (ie. the Player will always be in the rain). Set to false "
 428         "to have a single area affected by rain (ie. the Player can move in "
 429         "and out of the rainy area)." );
 430
 431      addField( "useWind", TypeBool, Offset(mUseWind, Precipitation),
 432         "Controls whether drops are affected by wind.\n"
 433         "@see ForestWindEmitter" );
 434
 435      addField( "minSpeed", TypeF32, Offset(mMinSpeed, Precipitation),
 436         "@brief Minimum speed at which a drop will fall.\n\n"
 437         "On creation, the drop will be assigned a random speed between #minSpeed "
 438         "and #maxSpeed." );
 439
 440      addField( "maxSpeed", TypeF32, Offset(mMaxSpeed, Precipitation),
 441         "@brief Maximum speed at which a drop will fall.\n\n"
 442         "On creation, the drop will be assigned a random speed between #minSpeed "
 443         "and #maxSpeed." );
 444
 445      addField( "minMass", TypeF32, Offset(mMinMass, Precipitation),
 446         "@brief Minimum mass of a drop.\n\n"
 447         "Drop mass determines how strongly the drop is affected by wind and "
 448         "turbulence. On creation, the drop will be assigned a random speed "
 449         "between #minMass and #minMass." );
 450
 451      addField( "maxMass", TypeF32, Offset(mMaxMass, Precipitation),
 452         "@brief Maximum mass of a drop.\n\n"
 453         "Drop mass determines how strongly the drop is affected by wind and "
 454         "turbulence. On creation, the drop will be assigned a random speed "
 455         "between #minMass and #minMass." );
 456
 457   endGroup("Movement");
 458
 459   addGroup("Turbulence");
 460
 461      addField( "useTurbulence", TypeBool, Offset(mUseTurbulence, Precipitation),
 462         "Check to enable turbulence. This causes precipitation drops to spiral "
 463         "while falling." );
 464
 465      addField( "maxTurbulence", TypeF32, Offset(mMaxTurbulence, Precipitation),
 466         "Radius at which precipitation drops spiral when turbulence is enabled." );
 467
 468      addField( "turbulenceSpeed", TypeF32, Offset(mTurbulenceSpeed, Precipitation),
 469         "Speed at which precipitation drops spiral when turbulence is enabled." );
 470
 471   endGroup("Turbulence");
 472
 473   Parent::initPersistFields();
 474}
 475
 476//-----------------------------------
 477// Console methods...
 478DefineEngineMethod(Precipitation, setPercentage, void, (F32 percentage), (1.0f),
 479   "Sets the maximum number of drops in the effect, as a percentage of #numDrops.\n"
 480   "The change occurs instantly (use modifyStorm() to change the number of drops "
 481   "over a period of time.\n"
 482   "@param percentage New maximum number of drops value (as a percentage of "
 483   "#numDrops). Valid range is 0-1.\n"
 484   "@tsexample\n"
 485   "%percentage = 0.5;  // The percentage, from 0 to 1, of the maximum drops to display\n"
 486   "%precipitation.setPercentage( %percentage );\n"
 487   "@endtsexample\n"
 488   "@see modifyStorm\n" )
 489{
 490   object->setPercentage(percentage);
 491}
 492
 493DefineEngineMethod(Precipitation, modifyStorm, void, (F32 percentage, F32 seconds), (1.0f, 5.0f),
 494   "Smoothly change the maximum number of drops in the effect (from current "
 495   "value to #numDrops * @a percentage).\n"
 496   "This method can be used to simulate a storm building or fading in intensity "
 497   "as the number of drops in the Precipitation box changes.\n"
 498   "@param percentage New maximum number of drops value (as a percentage of "
 499   "#numDrops). Valid range is 0-1.\n"
 500   "@param seconds Length of time (in seconds) over which to increase the drops "
 501   "percentage value. Set to 0 to change instantly.\n"
 502   "@tsexample\n"
 503   "%percentage = 0.5;  // The percentage, from 0 to 1, of the maximum drops to display\n"
 504   "%seconds = 5.0;     // The length of time over which to make the change.\n"
 505   "%precipitation.modifyStorm( %percentage, %seconds );\n"
 506   "@endtsexample\n" )
 507{
 508   object->modifyStorm(percentage, S32(seconds * 1000.0f));
 509}
 510
 511DefineEngineMethod(Precipitation, setTurbulence, void, (F32 max, F32 speed, F32 seconds), (1.0f, 5.0f, 5.0f),
 512   "Smoothly change the turbulence parameters over a period of time.\n"
 513   "@param max New #maxTurbulence value. Set to 0 to disable turbulence.\n"
 514   "@param speed New #turbulenceSpeed value.\n"
 515   "@param seconds Length of time (in seconds) over which to interpolate the "
 516   "turbulence settings. Set to 0 to change instantly.\n"
 517   "@tsexample\n"
 518   "%turbulence = 0.5;     // Set the new turbulence value. Set to 0 to disable turbulence.\n"
 519   "%speed = 5.0;          // The new speed of the turbulance effect.\n"
 520   "%seconds = 5.0;        // The length of time over which to make the change.\n"
 521   "%precipitation.setTurbulence( %turbulence, %speed, %seconds );\n"
 522   "@endtsexample\n" )
 523{
 524   object->setTurbulence( max, speed, S32(seconds * 1000.0f));
 525}
 526
 527//--------------------------------------------------------------------------
 528// Backend
 529//--------------------------------------------------------------------------
 530bool Precipitation::onAdd()
 531{
 532   if(!Parent::onAdd())
 533      return false;
 534
 535   if (mFollowCam)
 536   {
 537      setGlobalBounds();
 538   }
 539   else
 540   {
 541      mObjBox.minExtents = -Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2);
 542      mObjBox.maxExtents = Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2);
 543   }
 544   resetWorldBox();
 545
 546   if (isClientObject())
 547   {
 548      fillDropList();
 549      initRenderObjects();
 550      initMaterials();
 551   }
 552
 553   addToScene();
 554
 555   return true;
 556}
 557
 558void Precipitation::onRemove()
 559{
 560   removeFromScene();
 561   Parent::onRemove();
 562
 563   SFX_DELETE( mAmbientSound );
 564
 565   if (isClientObject())
 566      killDropList();
 567}
 568
 569bool Precipitation::onNewDataBlock( GameBaseData *dptr, bool reload )
 570{
 571   mDataBlock = dynamic_cast<PrecipitationData*>( dptr );
 572   if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
 573      return false;
 574
 575   if (isClientObject())
 576   {
 577      SFX_DELETE( mAmbientSound );
 578
 579      if ( mDataBlock->soundProfile )
 580      {
 581         mAmbientSound = SFX->createSource( mDataBlock->soundProfile, &getTransform() );
 582         if ( mAmbientSound )
 583            mAmbientSound->play();
 584      }
 585
 586      initRenderObjects();
 587      initMaterials();
 588   }
 589
 590   scriptOnNewDataBlock();
 591   return true;
 592}
 593
 594void Precipitation::initMaterials()
 595{
 596   AssertFatal(isClientObject(), "Precipitation is setting materials on the server - BAD!");
 597
 598   if(!mDataBlock)
 599      return;
 600
 601   PrecipitationData *pd = (PrecipitationData*)mDataBlock;
 602
 603   mDropHandle = NULL;
 604   mSplashHandle = NULL;
 605   mDropShader = NULL;
 606   mSplashShader = NULL;
 607
 608   if( dStrlen(pd->mDropName) > 0 && !mDropHandle.set(pd->mDropName, &GFXStaticTextureSRGBProfile, avar("%s() - mDropHandle (line %d)", __FUNCTION__, __LINE__)) )
 609      Con::warnf("Precipitation::initMaterials - failed to locate texture '%s'!", pd->mDropName);
 610
 611   if ( dStrlen(pd->mDropShaderName) > 0 )
 612   {
 613      ShaderData *shaderData;
 614      if ( Sim::findObject( pd->mDropShaderName, shaderData ) )
 615         mDropShader = shaderData->getShader();
 616
 617      if( !mDropShader )
 618         Con::warnf( "Precipitation::initMaterials - could not find shader '%s'!", pd->mDropShaderName );
 619      else
 620      {
 621         mDropShaderConsts = mDropShader->allocConstBuffer();
 622         mDropShaderModelViewSC = mDropShader->getShaderConstHandle("$modelView");
 623         mDropShaderFadeStartEndSC = mDropShader->getShaderConstHandle("$fadeStartEnd");
 624         mDropShaderCameraPosSC = mDropShader->getShaderConstHandle("$cameraPos");
 625         mDropShaderAmbientSC = mDropShader->getShaderConstHandle("$ambient");
 626      }
 627   }
 628
 629   if( dStrlen(pd->mSplashName) > 0 && !mSplashHandle.set(pd->mSplashName, &GFXStaticTextureSRGBProfile, avar("%s() - mSplashHandle (line %d)", __FUNCTION__, __LINE__)) )
 630      Con::warnf("Precipitation::initMaterials - failed to locate texture '%s'!", pd->mSplashName);
 631
 632   if ( dStrlen(pd->mSplashShaderName) > 0 )
 633   {
 634      ShaderData *shaderData;
 635      if ( Sim::findObject( pd->mSplashShaderName, shaderData ) )
 636         mSplashShader = shaderData->getShader();
 637
 638      if( !mSplashShader )
 639         Con::warnf( "Precipitation::initMaterials - could not find shader '%s'!", pd->mSplashShaderName );
 640      else
 641      {
 642         mSplashShaderConsts = mSplashShader->allocConstBuffer();
 643         mSplashShaderModelViewSC = mSplashShader->getShaderConstHandle("$modelView");
 644         mSplashShaderFadeStartEndSC = mSplashShader->getShaderConstHandle("$fadeStartEnd");
 645         mSplashShaderCameraPosSC = mSplashShader->getShaderConstHandle("$cameraPos");
 646         mSplashShaderAmbientSC = mSplashShader->getShaderConstHandle("$ambient");
 647      }
 648   }
 649}
 650
 651U32 Precipitation::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
 652{
 653   Parent::packUpdate(con, mask, stream);
 654
 655   if (stream->writeFlag( !mFollowCam && mask & TransformMask))
 656      stream->writeAffineTransform(mObjToWorld);
 657
 658   if (stream->writeFlag(mask & DataMask))
 659   {
 660      stream->write(mDropSize);
 661      stream->write(mSplashSize);
 662      stream->write(mSplashMS);
 663      stream->write(mDropAnimateMS);
 664      stream->write(mNumDrops);
 665      stream->write(mMinSpeed);
 666      stream->write(mMaxSpeed);
 667      stream->write(mBoxWidth);
 668      stream->write(mBoxHeight);
 669      stream->write(mMinMass);
 670      stream->write(mMaxMass);
 671      stream->write(mMaxTurbulence);
 672      stream->write(mTurbulenceSpeed);
 673      stream->write(mFadeDistance);
 674      stream->write(mFadeDistanceEnd);
 675      stream->write(mGlowIntensity.red);
 676      stream->write(mGlowIntensity.green);
 677      stream->write(mGlowIntensity.blue);
 678      stream->write(mGlowIntensity.alpha);
 679      stream->writeFlag(mReflect);
 680      stream->writeFlag(mRotateWithCamVel);
 681      stream->writeFlag(mDoCollision);
 682      stream->writeFlag(mDropHitPlayers);
 683      stream->writeFlag(mDropHitVehicles);
 684      stream->writeFlag(mUseTrueBillboards);
 685      stream->writeFlag(mUseTurbulence);
 686      stream->writeFlag(mUseLighting);
 687      stream->writeFlag(mUseWind);
 688      stream->writeFlag(mFollowCam);
 689      stream->writeFlag(mAnimateSplashes);
 690   }
 691
 692   if (stream->writeFlag(!(mask & DataMask) && (mask & TurbulenceMask)))
 693   {
 694      stream->write(mTurbulenceData.endMax);
 695      stream->write(mTurbulenceData.endSpeed);
 696      stream->write(mTurbulenceData.totalTime);
 697   }
 698
 699   if (stream->writeFlag(mask & PercentageMask))
 700   {
 701      stream->write(mPercentage);
 702   }
 703
 704   if (stream->writeFlag(!(mask & ~(DataMask | PercentageMask | StormMask)) && (mask & StormMask)))
 705   {
 706      stream->write(mStormData.endPct);
 707      stream->write(mStormData.totalTime);
 708   }
 709
 710   return 0;
 711}
 712
 713void Precipitation::unpackUpdate(NetConnection* con, BitStream* stream)
 714{
 715   Parent::unpackUpdate(con, stream);
 716
 717   if (stream->readFlag())
 718   {
 719      MatrixF mat;
 720      stream->readAffineTransform(&mat);
 721      Parent::setTransform(mat);
 722   }
 723
 724   U32 oldDrops = U32(mNumDrops * mPercentage);
 725   if (stream->readFlag())
 726   {
 727      stream->read(&mDropSize);
 728      stream->read(&mSplashSize);
 729      stream->read(&mSplashMS);
 730      stream->read(&mDropAnimateMS);
 731      stream->read(&mNumDrops);
 732      stream->read(&mMinSpeed);
 733      stream->read(&mMaxSpeed);
 734      stream->read(&mBoxWidth);
 735      stream->read(&mBoxHeight);
 736      stream->read(&mMinMass);
 737      stream->read(&mMaxMass);
 738      stream->read(&mMaxTurbulence);
 739      stream->read(&mTurbulenceSpeed);
 740      stream->read(&mFadeDistance);
 741      stream->read(&mFadeDistanceEnd);
 742      stream->read(&mGlowIntensity.red);
 743      stream->read(&mGlowIntensity.green);
 744      stream->read(&mGlowIntensity.blue);
 745      stream->read(&mGlowIntensity.alpha);
 746      mReflect = stream->readFlag();
 747      mRotateWithCamVel = stream->readFlag();
 748      mDoCollision = stream->readFlag();
 749      mDropHitPlayers = stream->readFlag();
 750      mDropHitVehicles = stream->readFlag();
 751      mUseTrueBillboards = stream->readFlag();
 752      mUseTurbulence = stream->readFlag();
 753      mUseLighting = stream->readFlag();
 754      mUseWind = stream->readFlag();
 755      mFollowCam = stream->readFlag();
 756      mAnimateSplashes = stream->readFlag();
 757
 758      mDropHitMask = dropHitMask | 
 759         ( mDropHitPlayers ? PlayerObjectType : 0 ) | 
 760         ( mDropHitVehicles ? VehicleObjectType : 0 );
 761
 762      mTurbulenceData.valid = false;
 763   }
 764
 765   if (stream->readFlag())
 766   {
 767      F32 max, speed;
 768      U32 ms;
 769      stream->read(&max);
 770      stream->read(&speed);
 771      stream->read(&ms);
 772      setTurbulence( max, speed, ms );
 773   }
 774
 775   if (stream->readFlag())
 776   {
 777      F32 pct;
 778      stream->read(&pct);
 779      setPercentage(pct);
 780   }
 781
 782   if (stream->readFlag())
 783   {
 784      F32 pct;
 785      U32 time;
 786      stream->read(&pct);
 787      stream->read(&time);
 788      modifyStorm(pct, time);
 789   }
 790
 791   AssertFatal(isClientObject(), "Precipitation::unpackUpdate() should only be called on the client!");
 792
 793   U32 newDrops = U32(mNumDrops * mPercentage);
 794   if (oldDrops != newDrops)
 795   {
 796      fillDropList();
 797      initRenderObjects();
 798   }
 799
 800   if (mFollowCam)
 801   {
 802      setGlobalBounds();
 803   }
 804   else
 805   {
 806      mObjBox.minExtents = -Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2);
 807      mObjBox.maxExtents = Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2);
 808   }
 809
 810   resetWorldBox();
 811}
 812
 813//--------------------------------------------------------------------------
 814// Support functions
 815//--------------------------------------------------------------------------
 816VectorF Precipitation::getWindVelocity()
 817{
 818   // The WindManager happens to set global-wind velocity here, it is not just for particles.
 819   return mUseWind ? ParticleEmitter::mWindVelocity : Point3F::Zero;
 820}
 821
 822void Precipitation::fillDropList()
 823{
 824   AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
 825
 826   F32 density = Con::getFloatVariable("$pref::precipitationDensity", 1.0f);
 827   U32 newDropCount = (U32)(mNumDrops * mPercentage * density);
 828   U32 dropCount = 0;
 829
 830   if (newDropCount == 0)
 831      killDropList();
 832
 833   if (mDropHead)
 834   {
 835      Raindrop* curr = mDropHead;
 836      while (curr)
 837      {
 838         dropCount++;
 839         curr = curr->next;
 840         if (dropCount == newDropCount && curr)
 841         {
 842            //delete the remaining drops
 843            Raindrop* next = curr->next;
 844            curr->next = NULL;
 845            while (next)
 846            {
 847               Raindrop* last = next;
 848               next = next->next;
 849               last->next = NULL;
 850               destroySplash(last);
 851               delete last;
 852            }
 853            break;
 854         }
 855      }
 856   }
 857
 858   if (dropCount < newDropCount)
 859   {
 860      //move to the end
 861      Raindrop* curr = mDropHead;
 862      if (curr)
 863      {
 864         while (curr->next)
 865            curr = curr->next;
 866      }
 867      else
 868      {
 869         mDropHead = curr = new Raindrop;
 870         spawnNewDrop(curr);
 871         dropCount++;
 872      }
 873
 874      //and add onto it
 875      while (dropCount < newDropCount)
 876      {
 877         curr->next = new Raindrop;
 878         curr = curr->next;
 879         spawnNewDrop(curr);
 880         dropCount++;
 881      }
 882   }
 883}
 884
 885void Precipitation::initRenderObjects()
 886{
 887   AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
 888
 889   SAFE_DELETE_ARRAY(mTexCoords);
 890   SAFE_DELETE_ARRAY(mSplashCoords);
 891
 892   if (!mDataBlock)
 893      return;
 894
 895   mTexCoords = new Point2F[4*mDataBlock->mDropsPerSide*mDataBlock->mDropsPerSide];
 896
 897   // Setup the texcoords for the drop texture.
 898   // The order of the coords when animating is...
 899   //
 900   //   +---+---+---+
 901   //   | 1 | 2 | 3 |
 902   //   |---|---|---+
 903   //   | 4 | 5 | 6 |
 904   //   +---+---+---+
 905   //   | 7 | etc...
 906   //   +---+
 907   //
 908   U32 count = 0;
 909   for (U32 v = 0; v < mDataBlock->mDropsPerSide; v++)
 910   {
 911      F32 y1 = (F32) v / mDataBlock->mDropsPerSide;
 912      F32 y2 = (F32)(v+1) / mDataBlock->mDropsPerSide;
 913      for (U32 u = 0; u < mDataBlock->mDropsPerSide; u++)
 914      {
 915         F32 x1 = (F32) u / mDataBlock->mDropsPerSide;
 916         F32 x2 = (F32)(u+1) / mDataBlock->mDropsPerSide;
 917
 918         mTexCoords[4*count+0].x = x1;
 919         mTexCoords[4*count+0].y = y1;
 920
 921         mTexCoords[4*count+1].x = x2;
 922         mTexCoords[4*count+1].y = y1;
 923
 924         mTexCoords[4*count+2].x = x2;
 925         mTexCoords[4*count+2].y = y2;
 926
 927         mTexCoords[4*count+3].x = x1;
 928         mTexCoords[4*count+3].y = y2;
 929         count++;
 930      }
 931   }
 932
 933   count = 0;
 934   mSplashCoords = new Point2F[4*mDataBlock->mSplashesPerSide*mDataBlock->mSplashesPerSide];
 935   for (U32 v = 0; v < mDataBlock->mSplashesPerSide; v++)
 936   {
 937      F32 y1 = (F32) v / mDataBlock->mSplashesPerSide;
 938      F32 y2 = (F32)(v+1) / mDataBlock->mSplashesPerSide;
 939      for (U32 u = 0; u < mDataBlock->mSplashesPerSide; u++)
 940      {
 941         F32 x1 = (F32) u / mDataBlock->mSplashesPerSide;
 942         F32 x2 = (F32)(u+1) / mDataBlock->mSplashesPerSide;
 943
 944         mSplashCoords[4*count+0].x = x1;
 945         mSplashCoords[4*count+0].y = y1;
 946
 947         mSplashCoords[4*count+1].x = x2;
 948         mSplashCoords[4*count+1].y = y1;
 949
 950         mSplashCoords[4*count+2].x = x2;
 951         mSplashCoords[4*count+2].y = y2;
 952
 953         mSplashCoords[4*count+3].x = x1;
 954         mSplashCoords[4*count+3].y = y2;
 955         count++;
 956      }
 957   }
 958
 959   // Cap the number of precipitation drops so that we don't blow out the max verts
 960   mMaxVBDrops = getMin( (U32)mNumDrops, ( GFX->getMaxDynamicVerts() / 4 ) - 1 );
 961
 962   // If we have no drops then skip allocating anything!
 963   if ( mMaxVBDrops == 0 )
 964      return;
 965
 966   // Create a volitile vertex buffer which
 967   // we'll lock and fill every frame.
 968   mRainVB.set(GFX, mMaxVBDrops * 4, GFXBufferTypeDynamic);
 969
 970   // Init the index buffer for rendering the
 971   // entire or a partially filled vb.
 972   mRainIB.set(GFX, mMaxVBDrops * 6, 0, GFXBufferTypeStatic);
 973   U16 *idxBuff;
 974   mRainIB.lock(&idxBuff, NULL, NULL, NULL);
 975   for( U32 i=0; i < mMaxVBDrops; i++ )
 976   {
 977      //
 978      // The vertex pattern in the VB for each 
 979      // particle is as follows...
 980      //
 981      //     0----1
 982      //     |\   |
 983      //     | \  |
 984      //     |  \ |
 985      //     |   \|
 986      //     3----2
 987      //
 988      // We setup the index order below to ensure
 989      // sequential, cache friendly, access.
 990      //
 991      U32 offset = i * 4;
 992      idxBuff[i*6+0] = 0 + offset;
 993      idxBuff[i*6+1] = 1 + offset;
 994      idxBuff[i*6+2] = 2 + offset;
 995      idxBuff[i*6+3] = 2 + offset;
 996      idxBuff[i*6+4] = 3 + offset;
 997      idxBuff[i*6+5] = 0 + offset;
 998   }
 999   mRainIB.unlock();
1000}
1001
1002void Precipitation::killDropList()
1003{
1004   AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
1005
1006   Raindrop* curr = mDropHead;
1007   while (curr)
1008   {
1009      Raindrop* next = curr->next;
1010      delete curr;
1011      curr = next;
1012   }
1013   mDropHead = NULL;
1014   mSplashHead = NULL;
1015}
1016
1017void Precipitation::spawnDrop(Raindrop *drop)
1018{
1019   PROFILE_START(PrecipSpawnDrop);
1020   AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
1021
1022   drop->velocity = Platform::getRandom() * (mMaxSpeed - mMinSpeed) + mMinSpeed;
1023
1024   drop->position.x = Platform::getRandom() * mBoxWidth;
1025   drop->position.y = Platform::getRandom() * mBoxWidth;
1026
1027   // The start time should be randomized so that 
1028   // all the drops are not animating at the same time.
1029   drop->animStartTime = (SimTime)(Platform::getVirtualMilliseconds() * Platform::getRandom());
1030
1031   if (mDropAnimateMS <= 0 && mDataBlock)
1032      drop->texCoordIndex = (U32)(Platform::getRandom() * ((F32)mDataBlock->mDropsPerSide*mDataBlock->mDropsPerSide - 0.5));
1033
1034   drop->valid = true;
1035   drop->time = Platform::getRandom() * M_2PI;
1036   drop->mass = Platform::getRandom() * (mMaxMass - mMinMass) + mMinMass;
1037   PROFILE_END();
1038}
1039
1040void Precipitation::spawnNewDrop(Raindrop *drop)
1041{
1042   AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
1043
1044   spawnDrop(drop);
1045   drop->position.z = Platform::getRandom() * mBoxHeight - (mBoxHeight / 2);
1046}
1047
1048void Precipitation::wrapDrop(Raindrop *drop, const Box3F &box, const U32 currTime, const VectorF &windVel)
1049{
1050   //could probably be slightly optimized to get rid of the while loops
1051   if (drop->position.z < box.minExtents.z)
1052   {
1053      spawnDrop(drop);
1054      drop->position.x += box.minExtents.x;
1055      drop->position.y += box.minExtents.y;
1056      while (drop->position.z < box.minExtents.z)
1057         drop->position.z += mBoxHeight;
1058      findDropCutoff(drop, box, windVel);
1059   }
1060   else if (drop->position.z > box.maxExtents.z)
1061   {
1062      while (drop->position.z > box.maxExtents.z)
1063         drop->position.z -= mBoxHeight;
1064      findDropCutoff(drop, box, windVel);
1065   }
1066   else if (drop->position.x < box.minExtents.x)
1067   {
1068      while (drop->position.x < box.minExtents.x)
1069         drop->position.x += mBoxWidth;
1070      findDropCutoff(drop, box, windVel);
1071   }
1072   else if (drop->position.x > box.maxExtents.x)
1073   {
1074      while (drop->position.x > box.maxExtents.x)
1075         drop->position.x -= mBoxWidth;
1076      findDropCutoff(drop, box, windVel);
1077   }
1078   else if (drop->position.y < box.minExtents.y)
1079   {
1080      while (drop->position.y < box.minExtents.y)
1081         drop->position.y += mBoxWidth;
1082      findDropCutoff(drop, box, windVel);
1083   }
1084   else if (drop->position.y > box.maxExtents.y)
1085   {
1086      while (drop->position.y > box.maxExtents.y)
1087         drop->position.y -= mBoxWidth;
1088      findDropCutoff(drop, box, windVel);
1089   }
1090}
1091
1092void Precipitation::findDropCutoff(Raindrop *drop, const Box3F &box, const VectorF &windVel)
1093{
1094   PROFILE_START(PrecipFindDropCutoff);
1095   AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
1096
1097   if (mDoCollision)
1098   {
1099      VectorF velocity = windVel / drop->mass - VectorF(0, 0, drop->velocity);
1100      velocity.normalize();
1101
1102      Point3F end   = drop->position + 100 * velocity;
1103      Point3F start = drop->position - (mFollowCam ? 500.0f : 0.0f) * velocity;
1104
1105      if (!mFollowCam)
1106      {
1107         mObjToWorld.mulP(start);
1108         mObjToWorld.mulP(end);
1109      }
1110
1111      // Look for a collision... make sure we don't 
1112      // collide with backfaces.
1113      RayInfo rInfo;
1114      if (getContainer()->castRay(start, end, mDropHitMask, &rInfo))
1115      {
1116         // TODO: Add check to filter out hits on backfaces.
1117
1118         if (!mFollowCam)
1119            mWorldToObj.mulP(rInfo.point);
1120
1121         drop->hitPos = rInfo.point;
1122         drop->hitType = rInfo.object->getTypeMask();
1123      }
1124      else
1125         drop->hitPos = Point3F(0,0,-1000);
1126
1127      drop->valid = drop->position.z > drop->hitPos.z;
1128   }
1129   else
1130   {
1131      drop->hitPos = Point3F(0,0,-1000);
1132      drop->valid = true;
1133   }
1134   PROFILE_END();
1135}
1136
1137void Precipitation::createSplash(Raindrop *drop)
1138{
1139   if (!mDataBlock)
1140      return;
1141
1142   PROFILE_START(PrecipCreateSplash);
1143   if (drop != mSplashHead && !(drop->nextSplashDrop || drop->prevSplashDrop))
1144   {
1145      if (!mSplashHead)
1146      {
1147         mSplashHead = drop;
1148         drop->prevSplashDrop = NULL;
1149         drop->nextSplashDrop = NULL;
1150      }
1151      else
1152      {
1153         mSplashHead->prevSplashDrop = drop;
1154         drop->nextSplashDrop = mSplashHead;
1155         drop->prevSplashDrop = NULL;
1156         mSplashHead = drop;
1157      }
1158   }
1159
1160   drop->animStartTime = Platform::getVirtualMilliseconds();
1161
1162   if (!mAnimateSplashes)
1163      drop->texCoordIndex = (U32)(Platform::getRandom() * ((F32)mDataBlock->mSplashesPerSide*mDataBlock->mSplashesPerSide - 0.5));
1164
1165   PROFILE_END();
1166}
1167
1168void Precipitation::destroySplash(Raindrop *drop)
1169{
1170   PROFILE_START(PrecipDestroySplash);
1171   if (drop == mSplashHead)
1172   {
1173      mSplashHead = mSplashHead->nextSplashDrop;
1174   }
1175
1176   if (drop->nextSplashDrop)
1177      drop->nextSplashDrop->prevSplashDrop = drop->prevSplashDrop;
1178   if (drop->prevSplashDrop)
1179      drop->prevSplashDrop->nextSplashDrop = drop->nextSplashDrop;
1180
1181   drop->nextSplashDrop = NULL;
1182   drop->prevSplashDrop = NULL;
1183
1184   PROFILE_END();
1185}
1186
1187//--------------------------------------------------------------------------
1188// Processing
1189//--------------------------------------------------------------------------
1190void Precipitation::setPercentage(F32 pct)
1191{
1192   mPercentage = mClampF(pct, 0, 1);
1193   mStormData.valid = false;
1194
1195   if (isServerObject()) 
1196   {
1197      setMaskBits(PercentageMask);
1198   }
1199}
1200
1201void Precipitation::modifyStorm(F32 pct, U32 ms)
1202{
1203   if ( ms == 0 ) 
1204   {
1205      setPercentage( pct );
1206      return;
1207   }
1208
1209   pct = mClampF(pct, 0, 1);
1210   mStormData.endPct = pct;
1211   mStormData.totalTime = ms;
1212
1213   if (isServerObject())
1214   {
1215      setMaskBits(StormMask);
1216      return;
1217   }
1218
1219   mStormData.startTime = Platform::getVirtualMilliseconds();
1220   mStormData.startPct = mPercentage;
1221   mStormData.valid = true;
1222}
1223
1224void Precipitation::setTurbulence(F32 max, F32 speed, U32 ms)
1225{
1226   if ( ms == 0 && !isServerObject() ) 
1227   {
1228      mUseTurbulence = max > 0;
1229      mMaxTurbulence = max;
1230      mTurbulenceSpeed = speed;
1231      return;
1232   }
1233
1234   mTurbulenceData.endMax = max;
1235   mTurbulenceData.endSpeed = speed;
1236   mTurbulenceData.totalTime = ms;
1237
1238   if (isServerObject())
1239   {
1240      setMaskBits(TurbulenceMask);
1241      return;
1242   }
1243
1244   mTurbulenceData.startTime = Platform::getVirtualMilliseconds();
1245   mTurbulenceData.startMax = mMaxTurbulence;
1246   mTurbulenceData.startSpeed = mTurbulenceSpeed;
1247   mTurbulenceData.valid = true;
1248}
1249
1250void Precipitation::interpolateTick(F32 delta)
1251{
1252   AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
1253
1254   // If we're not being seen then the simulation
1255   // is paused and we don't need any interpolation.
1256   if (mLastRenderFrame != ShapeBase::sLastRenderFrame)
1257      return;
1258
1259   PROFILE_START(PrecipInterpolate);
1260
1261   const F32 dt = 1-delta;
1262   const VectorF windVel = dt * getWindVelocity();
1263   const F32 turbSpeed = dt * mTurbulenceSpeed;
1264
1265   Raindrop* curr = mDropHead;
1266   VectorF turbulence;
1267   F32 renderTime;
1268
1269   while (curr)
1270   {
1271      if (!curr->valid || !curr->toRender)
1272      {
1273         curr = curr->next;
1274         continue;
1275      }
1276
1277      if (mUseTurbulence)
1278      {
1279         renderTime = curr->time + turbSpeed;
1280         turbulence.x = windVel.x + ( mSin(renderTime) * mMaxTurbulence );
1281         turbulence.y = windVel.y + ( mCos(renderTime) * mMaxTurbulence );
1282         turbulence.z = windVel.z;
1283         curr->renderPosition = curr->position + turbulence / curr->mass;
1284      }
1285      else
1286         curr->renderPosition = curr->position + windVel / curr->mass;
1287
1288      curr->renderPosition.z -= dt * curr->velocity;
1289
1290      curr = curr->next;
1291   }
1292   PROFILE_END();
1293}
1294
1295void Precipitation::processTick(const Move *)
1296{
1297   //nothing to do on the server
1298   if (isServerObject() || mDataBlock == NULL || isHidden())
1299      return;
1300
1301   const U32 currTime = Platform::getVirtualMilliseconds();
1302
1303   // Update the storm if necessary
1304   if (mStormData.valid)
1305   {
1306      F32 t = (currTime - mStormData.startTime) / (F32)mStormData.totalTime;
1307      if (t >= 1)
1308      {
1309         mPercentage = mStormData.endPct;
1310         mStormData.valid = false;
1311      }
1312      else
1313         mPercentage = mStormData.startPct * (1-t) + mStormData.endPct * t;
1314
1315      fillDropList();
1316   }
1317
1318   // Do we need to update the turbulence?
1319   if ( mTurbulenceData.valid )
1320   {
1321      F32 t = (currTime - mTurbulenceData.startTime) / (F32)mTurbulenceData.totalTime;
1322      if (t >= 1)
1323      {
1324         mMaxTurbulence = mTurbulenceData.endMax;
1325         mTurbulenceSpeed = mTurbulenceData.endSpeed;
1326         mTurbulenceData.valid = false;
1327      }
1328      else
1329      {
1330         mMaxTurbulence = mTurbulenceData.startMax * (1-t) + mTurbulenceData.endMax * t;
1331         mTurbulenceSpeed = mTurbulenceData.startSpeed * (1-t) + mTurbulenceData.endSpeed * t;
1332      }
1333
1334      mUseTurbulence = mMaxTurbulence > 0;
1335   }
1336
1337   // If we're not being seen then pause the 
1338   // simulation.  Precip is generally noisy 
1339   // enough that no one should notice.
1340   if (mLastRenderFrame != ShapeBase::sLastRenderFrame)
1341      return;
1342
1343   //we need to update positions and do some collision here
1344   GameConnection* conn = GameConnection::getConnectionToServer();
1345   if (!conn)
1346      return; //need connection to server
1347
1348   ShapeBase* camObj = dynamic_cast<ShapeBase*>(conn->getCameraObject());
1349   if (!camObj)
1350      return;
1351
1352   PROFILE_START(PrecipProcess);
1353
1354   MatrixF camMat;
1355   camObj->getEyeTransform(&camMat);
1356
1357   const F32 camFov = camObj->getCameraFov();
1358
1359   Point3F camPos, camDir;
1360   Box3F box;
1361
1362   if (mFollowCam)
1363   {
1364      camMat.getColumn(3, &camPos);
1365
1366      box = Box3F(camPos.x - mBoxWidth / 2, camPos.y - mBoxWidth / 2, camPos.z - mBoxHeight / 2,
1367         camPos.x + mBoxWidth / 2, camPos.y + mBoxWidth / 2, camPos.z + mBoxHeight / 2);
1368
1369      camMat.getColumn(1, &camDir);
1370      camDir.normalize();
1371   }
1372   else
1373   {
1374      box = mObjBox;
1375
1376      camMat.getColumn(3, &camPos);
1377      mWorldToObj.mulP(camPos);
1378
1379      camMat.getColumn(1, &camDir);
1380      camDir.normalize();
1381      mWorldToObj.mulV(camDir);
1382   }
1383
1384   const VectorF windVel = getWindVelocity();
1385   const F32 fovDot = camFov / 180;
1386
1387   Raindrop* curr = mDropHead;
1388
1389   //offset the renderbox in the direction of the camera direction
1390   //in order to have more of the drops actually rendered
1391   if (mFollowCam) 
1392   {
1393      box.minExtents.x += camDir.x * mBoxWidth / 4;
1394      box.maxExtents.x += camDir.x * mBoxWidth / 4;
1395      box.minExtents.y += camDir.y * mBoxWidth / 4;
1396      box.maxExtents.y += camDir.y * mBoxWidth / 4;
1397      box.minExtents.z += camDir.z * mBoxHeight / 4;
1398      box.maxExtents.z += camDir.z * mBoxHeight / 4;
1399   }
1400
1401   VectorF lookVec;
1402   F32 pct;
1403   const S32 dropCount = mDataBlock->mDropsPerSide*mDataBlock->mDropsPerSide;
1404   while (curr)
1405   {
1406      // Update the position.  This happens even if this
1407      // is a splash so that the drop respawns when it wraps
1408      // around to the top again.
1409      if (mUseTurbulence)
1410         curr->time += mTurbulenceSpeed;
1411      curr->position += windVel / curr->mass;
1412      curr->position.z -= curr->velocity;
1413
1414      // Wrap the drop if it reaches an edge of the box.
1415      wrapDrop(curr, box, currTime, windVel);
1416
1417      // Did the drop pass below the hit position?
1418      if (curr->valid && curr->position.z < curr->hitPos.z)
1419      {
1420         // If this drop was to hit a player or vehicle double
1421         // check to see if the object has moved out of the way.
1422         // This keeps us from leaving phantom trails of splashes
1423         // behind a moving player/vehicle.
1424         if (curr->hitType & (PlayerObjectType | VehicleObjectType))
1425         {
1426            findDropCutoff(curr, box, windVel);
1427
1428            if (curr->position.z > curr->hitPos.z)
1429               goto NO_SPLASH; // Ugly, yet simple.
1430         }
1431
1432         // The drop is dead.
1433         curr->valid = false;
1434
1435         // Convert the drop into a splash or let it 
1436         // wrap around and respawn in wrapDrop().
1437         if (mSplashMS > 0)
1438            createSplash(curr);
1439
1440         // So ugly... yet simple.
1441NO_SPLASH:;
1442      }
1443
1444      // We do not do cull individual drops when we're not
1445      // following as it is usually a tight box and all of 
1446      // the particles are in view.
1447      if (!mFollowCam)
1448         curr->toRender = true;
1449      else
1450      {
1451         lookVec = curr->position - camPos;
1452         curr->toRender = mDot(lookVec, camDir) > fovDot;
1453      }
1454
1455      // Do we need to animate the drop?
1456      if (curr->valid && mDropAnimateMS > 0 && curr->toRender)
1457      {
1458         pct = (F32)(currTime - curr->animStartTime) / mDropAnimateMS;
1459         pct = mFmod(pct, 1);
1460         curr->texCoordIndex = (U32)(dropCount * pct);
1461      }
1462
1463      curr = curr->next;
1464   }
1465
1466   //update splashes
1467   curr = mSplashHead;
1468   Raindrop *next;
1469   const S32 splashCount = mDataBlock->mSplashesPerSide * mDataBlock->mSplashesPerSide;
1470   while (curr)
1471   {
1472      pct = (F32)(currTime - curr->animStartTime) / mSplashMS;
1473      if (pct >= 1.0f)
1474      {
1475         next = curr->nextSplashDrop;
1476         destroySplash(curr);
1477         curr = next;
1478         continue;
1479      }
1480
1481      if (mAnimateSplashes)
1482         curr->texCoordIndex = (U32)(splashCount * pct);
1483
1484      curr = curr->nextSplashDrop;
1485   }
1486
1487   PROFILE_END_NAMED(PrecipProcess);
1488}
1489
1490//--------------------------------------------------------------------------
1491// Rendering
1492//--------------------------------------------------------------------------
1493void Precipitation::prepRenderImage(SceneRenderState* state)
1494{
1495   PROFILE_SCOPE(Precipitation_prepRenderImage);
1496
1497   // We we have no drops then skip rendering
1498   // and don't bother with the sound.
1499   if (mMaxVBDrops == 0)
1500      return;
1501
1502   // We do nothing if we're not supposed to be reflected.
1503   if ( state->isReflectPass() && !mReflect )
1504      return;
1505
1506   // This should be sufficient for most objects that don't manage zones, and
1507   // don't need to return a specialized RenderImage...
1508   ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
1509   ri->renderDelegate.bind(this, &Precipitation::renderObject);
1510   ri->type = RenderPassManager::RIT_Foliage;
1511   state->getRenderPass()->addInst( ri );
1512}
1513
1514void Precipitation::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat)
1515{
1516   if (overrideMat)
1517      return;
1518
1519   GameConnection* conn = GameConnection::getConnectionToServer();
1520   if (!conn)
1521      return; //need connection to server
1522
1523   ShapeBase* camObj = dynamic_cast<ShapeBase*>(conn->getCameraObject());
1524   if (!camObj)
1525      return; // need camera object
1526
1527   PROFILE_START(PrecipRender);
1528
1529   GFX->pushWorldMatrix();
1530
1531   MatrixF world = GFX->getWorldMatrix();
1532   MatrixF proj = GFX->getProjectionMatrix();
1533   if (!mFollowCam)
1534   {
1535      world.mul( getRenderTransform() );
1536      world.scale( getScale() );
1537      GFX->setWorldMatrix( world );
1538   }    
1539   proj.mul(world);
1540
1541   //GFX2 doesn't require transpose?
1542   //proj.transpose();
1543
1544   Point3F camPos  = state->getCameraPosition();
1545   VectorF camVel  = camObj->getVelocity();
1546   if (!mFollowCam)
1547   {
1548      getRenderWorldTransform().mulP(camPos);
1549      getRenderWorldTransform().mulV(camVel);
1550   }
1551   const VectorF windVel = getWindVelocity();
1552   const bool useBillboards = mUseTrueBillboards;
1553   const F32 dropSize = mDropSize;
1554
1555   Point3F pos;
1556   VectorF orthoDir, velocity, right, up, rightUp(0.0f, 0.0f, 0.0f), leftUp(0.0f, 0.0f, 0.0f);
1557   F32 distance = 0;
1558   GFXVertexPCT* vertPtr = NULL;
1559   const Point2F *tc;
1560
1561   // Do this here and we won't have to in the loop!
1562   if (useBillboards)
1563   {
1564      MatrixF camMat = state->getCameraTransform();
1565      camMat.inverse();
1566      camMat.getRow(0,&right);
1567      camMat.getRow(2,&up);
1568      if (!mFollowCam)
1569      {
1570         mWorldToObj.mulV(right);
1571         mWorldToObj.mulV(up);
1572      }
1573      right.normalize();
1574      up.normalize();
1575      right *= mDropSize;
1576      up    *= mDropSize;
1577      rightUp = right + up;
1578      leftUp = -right + up;
1579   }
1580
1581   // We pass the sunlight as a constant to the 
1582   // shader.  Once the lighting and shadow systems
1583   // are added into TSE we can expand this to include
1584   // the N nearest lights to the camera + the ambient.
1585   LinearColorF ambient( 1, 1, 1 );
1586   if ( mUseLighting )
1587   {
1588      const LightInfo *sunlight = LIGHTMGR->getSpecialLight(LightManager::slSunLightType);
1589      ambient = sunlight->getColor();
1590   }
1591
1592   if ( mGlowIntensity.red > 0 ||
1593      mGlowIntensity.green > 0 ||
1594      mGlowIntensity.blue > 0 )
1595   {
1596      ambient *= mGlowIntensity;
1597   }
1598
1599   // Setup render state
1600
1601   if (mDefaultSB.isNull())
1602   {
1603      GFXStateBlockDesc desc;
1604
1605      desc.zWriteEnable = false;
1606      desc.setAlphaTest(true, GFXCmpGreaterEqual, 1);
1607      desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
1608
1609      mDefaultSB = GFX->createStateBlock(desc);
1610
1611      desc.samplersDefined = true;
1612
1613      mDistantSB = GFX->createStateBlock(desc);
1614   }
1615
1616   GFX->setStateBlock(mDefaultSB);
1617
1618   // Everything is rendered from these buffers.
1619   GFX->setPrimitiveBuffer(mRainIB);
1620   GFX->setVertexBuffer(mRainVB);
1621
1622   // Set the constants used by the shaders.
1623   if (mDropShader) 
1624   {
1625      Point2F fadeStartEnd( mFadeDistance, mFadeDistanceEnd );
1626
1627      mDropShaderConsts->setSafe(mDropShaderModelViewSC, proj);
1628      mDropShaderConsts->setSafe(mDropShaderFadeStartEndSC, fadeStartEnd);
1629      mDropShaderConsts->setSafe(mDropShaderCameraPosSC, camPos);
1630      mDropShaderConsts->setSafe(mDropShaderAmbientSC, Point3F(ambient.red, ambient.green, ambient.blue));
1631   }
1632
1633
1634   if (mSplashShader) 
1635   {
1636      Point2F fadeStartEnd( mFadeDistance, mFadeDistanceEnd );
1637
1638      mSplashShaderConsts->setSafe(mSplashShaderModelViewSC, proj);
1639      mSplashShaderConsts->setSafe(mSplashShaderFadeStartEndSC, fadeStartEnd);
1640      mSplashShaderConsts->setSafe(mSplashShaderCameraPosSC, camPos);
1641      mSplashShaderConsts->setSafe(mSplashShaderAmbientSC, Point3F(ambient.red, ambient.green, ambient.blue));
1642   }
1643
1644   // Time to render the drops...
1645   const Raindrop *curr = mDropHead;
1646   U32 vertCount = 0;
1647
1648   GFX->setTexture(0, mDropHandle);
1649
1650   // Use the shader or setup the pipeline 
1651   // for fixed function rendering.
1652   if (mDropShader)
1653   {
1654      GFX->setShader( mDropShader );
1655      GFX->setShaderConstBuffer( mDropShaderConsts );
1656   }
1657   else
1658   {
1659      GFX->setupGenericShaders(GFXDevice::GSTexture);
1660
1661      // We don't support distance fade or lighting without shaders.
1662      GFX->setStateBlock(mDistantSB);
1663   }
1664
1665   while (curr)
1666   {
1667      // Skip ones that are not drops (hit something and 
1668      // may have been converted into a splash) or they 
1669      // are behind the camera.
1670      if (!curr->valid || !curr->toRender)
1671      {
1672         curr = curr->next;
1673         continue;
1674      }
1675
1676      pos = curr->renderPosition;
1677
1678      // two forms of billboards - true billboards (which we set 
1679      // above outside this loop) or axis-aligned with velocity
1680      // (this codeblock) the axis-aligned billboards are aligned
1681      // with the velocity of the raindrop, and tilted slightly 
1682      // towards the camera
1683      if (!useBillboards)
1684      {
1685         orthoDir = camPos - pos;
1686         distance = orthoDir.len();
1687
1688         // Inline the normalize so we don't 
1689         // calculate the ortho len twice.
1690         if (distance > 0.0)
1691            orthoDir *= 1.0f / distance;
1692         else
1693            orthoDir.set( 0, 0, 1 );
1694
1695         velocity = windVel / curr->mass;
1696
1697         // We do not optimize this for the "still" case
1698         // because its not a typical scenario.
1699         if (mRotateWithCamVel)
1700            velocity -= camVel / (distance > 2.0f ? distance : 2.0f) * 0.3f;
1701
1702         velocity.z -= curr->velocity;
1703         velocity.normalize();
1704
1705         right = mCross(-velocity, orthoDir);
1706         right.normalize();
1707         up    = mCross(orthoDir, right) * 0.5 - velocity * 0.5;
1708         up.normalize();
1709         right *= dropSize;
1710         up    *= dropSize;
1711         rightUp = right + up;
1712         leftUp = -right + up;
1713      }
1714
1715      // Do we need to relock the buffer?
1716      if ( !vertPtr )
1717         vertPtr = mRainVB.lock();
1718      if(!vertPtr) return;
1719
1720      // Set the proper texture coords... (it's fun!)
1721      tc = &mTexCoords[4*curr->texCoordIndex];
1722      vertPtr->point = pos + leftUp;
1723      vertPtr->texCoord = *tc;
1724      tc++;
1725      vertPtr++;
1726
1727      vertPtr->point = pos + rightUp;
1728      vertPtr->texCoord = *tc;
1729      tc++;
1730      vertPtr++;
1731
1732      vertPtr->point = pos - leftUp;
1733      vertPtr->texCoord = *tc;
1734      tc++;
1735      vertPtr++;
1736
1737      vertPtr->point = pos - rightUp;
1738      vertPtr->texCoord = *tc;
1739      tc++;
1740      vertPtr++;
1741
1742      // Do we need to render to clear the buffer?
1743      vertCount += 4;
1744      if ( (vertCount + 4) >= mRainVB->mNumVerts ) {
1745
1746         mRainVB.unlock();
1747         GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, vertCount, 0, vertCount / 2);
1748         vertPtr = NULL;
1749         vertCount = 0;
1750      }
1751
1752      curr = curr->next;
1753   }
1754
1755   // Do we have stuff left to render?
1756   if ( vertCount > 0 ) {
1757
1758      mRainVB.unlock();
1759      GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, vertCount, 0, vertCount / 2);
1760      vertCount = 0;
1761      vertPtr = NULL;
1762   }
1763
1764   // Setup the billboard for the splashes.
1765   MatrixF camMat = state->getCameraTransform();
1766   camMat.inverse();
1767   camMat.getRow(0, &right);
1768   camMat.getRow(2, &up);
1769   if (!mFollowCam)
1770   {
1771      mWorldToObj.mulV(right);
1772      mWorldToObj.mulV(up);
1773   }
1774   right.normalize();
1775   up.normalize();
1776   right *= mSplashSize;
1777   up *= mSplashSize;
1778   rightUp = right + up;
1779   leftUp = -right + up;
1780
1781   // Render the visible splashes.
1782   curr = mSplashHead;
1783
1784   GFX->setTexture(0, mSplashHandle);
1785
1786   if (mSplashShader)
1787   {
1788      GFX->setShader( mSplashShader );
1789      GFX->setShaderConstBuffer(mSplashShaderConsts);
1790   }
1791   else
1792      GFX->setupGenericShaders(GFXDevice::GSTexture);
1793
1794   while (curr)
1795   {
1796      if (!curr->toRender)
1797      {
1798         curr = curr->nextSplashDrop;
1799         continue;
1800      }
1801
1802      pos = curr->hitPos;
1803
1804      tc = &mSplashCoords[4*curr->texCoordIndex];
1805
1806      // Do we need to relock the buffer?
1807      if ( !vertPtr )
1808         vertPtr = mRainVB.lock();
1809      if(!vertPtr) return;
1810
1811      vertPtr->point = pos + leftUp;
1812      vertPtr->texCoord = *tc;
1813      tc++;
1814      vertPtr++;
1815
1816      vertPtr->point = pos + rightUp;
1817      vertPtr->texCoord = *tc;
1818      tc++;
1819      vertPtr++;
1820
1821      vertPtr->point = pos - leftUp;
1822      vertPtr->texCoord = *tc;
1823      tc++;
1824      vertPtr++;
1825
1826      vertPtr->point = pos - rightUp;
1827      vertPtr->texCoord = *tc;
1828      tc++;
1829      vertPtr++;
1830
1831      // Do we need to flush the buffer by rendering?
1832      vertCount += 4;
1833      if ( (vertCount + 4) >= mRainVB->mNumVerts ) {
1834
1835         mRainVB.unlock();
1836         GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, vertCount, 0, vertCount / 2);
1837         vertPtr = NULL;
1838         vertCount = 0;
1839      }
1840
1841      curr = curr->nextSplashDrop;
1842   }
1843
1844   // Do we have stuff left to render?
1845   if ( vertCount > 0 ) {
1846
1847      mRainVB.unlock();
1848      GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, vertCount, 0, vertCount / 2);
1849   }
1850
1851   mLastRenderFrame = ShapeBase::sLastRenderFrame;
1852
1853   GFX->popWorldMatrix();
1854
1855   PROFILE_END();
1856}
1857