precipitation.cpp
Engine/source/T3D/fx/precipitation.cpp
Public Variables
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) )
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