1 /*****************************************************************************
2  * $LastChangedDate: 2011-02-04 19:23:35 -0500 (Fri, 04 Feb 2011) $
3  * @file
4  * @author  Jim E. Brooks  http://www.palomino3d.org
5  * @brief   Cloud particle system.
6  *//*
7  * LEGAL:   COPYRIGHT (C) 2008 JIM E. BROOKS
8  *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
9  *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
10  *****************************************************************************/
11 
12 #define SKY_CLOUDS_CC 1
13 #include "base/module.hh"
14 #include "base/random.hh"
15 using namespace base;
16 #include "math/module.hh"
17 #include "math/funcs_trig.hh"
18 #include "math/graphics.hh"
19 using namespace math;
20 #include "glue/module.hh"
21 #include "glue/sim_time.hh"
22 using namespace glue;
23 #include "view/module.hh"
24 #include "view/view.hh"
25 #include "view/events.hh"
26 using namespace view;
27 #include "fx/module.hh"
28 #include "fx/texture.hh"
29 using namespace fx;
30 #include "world/module.hh"
31 using namespace world;
32 #include "sky/module.hh"
33 #include "sky/clouds.hh"
34 #include "sky/conf.hh"
35 #include "sky/sky_dome.hh"
36 #include "sky/weather.hh"
37 
38 namespace sky {
39 
40 // Area of sky populated with clouds is reduced to avoid overlapping sky dome.
41 INTERN const fp SKY_WIDTH_RATIO = 0.85f;
42 
43 INTERN const NodeSort::Attribs CLOUD_NODE_SORT_ATTRIBS( NodeSort::Attribs::TRANSPARENT );
44 
45 ////////////////////////////////////////////////////////////////////////////////
46 /// @brief Listener to rotates clouds for every view.
47 ///
CLASS_EVENT_LISTENER(CloudPreDrawViewListener,shptr<View>,CloudParticleSystem,mCloudParticleSystem)48 CLASS_EVENT_LISTENER( CloudPreDrawViewListener, shptr<View>, CloudParticleSystem, mCloudParticleSystem )
49 
50 ////////////////////////////////////////////////////////////////////////////////
51 ////////////////////////////  CloudParticle  ///////////////////////////////////
52 ////////////////////////////////////////////////////////////////////////////////
53 
54 /*****************************************************************************
55  * ctor/dtor.
56  *****************************************************************************/
57 CloudParticle::CloudParticle( const Sprite::Args& spriteArgs )
58 :   BigParticle(spriteArgs,fx::defs::LIFETIME_FOREVER)
59 {
60     // NOP
61 }
62 
~CloudParticle()63 CloudParticle::~CloudParticle()
64 {
65     // NOP
66 }
67 
68 ////////////////////////////////////////////////////////////////////////////////
69 ////////////////////////////  Clouds (plural)  /////////////////////////////////
70 ////////////////////////////////////////////////////////////////////////////////
71 
72 /*****************************************************************************
73  * ctor/dtor.
74  * For speed, timer-tick is disabled (so clouds won't move).
75  *****************************************************************************/
CloudParticleSystem(SkyDome & skyDome,const BoxVolume & skyVolume,const uint cloudCountDivisor,const string & textureName)76 CloudParticleSystem::CloudParticleSystem( SkyDome& skyDome, const BoxVolume& skyVolume,
77                                           const uint cloudCountDivisor, const string& textureName )
78 :   BigParticleSystem(BigParticleSystem::Args(fx::defs::LIFETIME_FOREVER,
79                                               TimerDefs::TIMER_DISABLED,  // default stateBits won't be used, since
80                                               NodeSort(CLOUD_NODE_SORT_ATTRIBS,
81                                               Texture::NewTextureAsStateSet(textureName)))),
82     mPreDrawViewListener(new CloudPreDrawViewListener(*this)),   // passed for specific cloud textures later
83     mRollDeg(0.0f)
84 {
85 CHECK_TYPESIG(&skyDome,TYPESIG_SKY_DOME);
86 
87     // Clouds are too white-and-bright for night.
88     if ( SKY_CONF.mCloudsEnabled and not GET_SIM_TIME().IfNightTime() )
89     {
90         MakeClouds( skyDome, skyVolume, cloudCountDivisor );
91     }
92 
93     // Listener to rotate cloud sprites before rendering a view.
94     // Register functors to be called before rendering this View.
95     EVENT_VIEW_PRE_DRAW_ONE_VIEW.Listen( mPreDrawViewListener.PTR() );
96 }
97 
~CloudParticleSystem()98 CloudParticleSystem::~CloudParticleSystem()
99 {
100     // Unregister or crash into destroyed object!
101     EVENT_VIEW_PRE_DRAW_ONE_VIEW.Unlisten( mPreDrawViewListener.PTR() );
102 }
103 
104 /*****************************************************************************
105  * ctor subroutine.
106  *****************************************************************************/
107 void
MakeClouds(SkyDome & skyDome,const BoxVolume & skyVolume,const uint cloudCountDivisor)108 CloudParticleSystem::MakeClouds( SkyDome& skyDome, const BoxVolume& skyVolume,
109                                  const uint cloudCountDivisor )
110 {
111 CHECK_TYPESIG(&skyDome,TYPESIG_SKY_DOME);
112 
113     const bool      flatWorld       = GET_WORLD().IfFlat();
114     sky::Conf::CloudConf& cloudConf = flatWorld ? SKY_CONF.mCloudConfFlat
115                                                 : SKY_CONF.mCloudConfSphere;
116     const uint      cloudCount      = cloudConf.mCloudCount / cloudCountDivisor;
117     const fp        skyWidth        = ABS( skyVolume.mMax[LAT] - skyVolume.mMin[LAT] ) * SKY_WIDTH_RATIO;
118     const fp        skyWidthHalf    = skyWidth * 0.5f;
119     const fp        altMin          = flatWorld ? world::conv::Meters2Sim( cloudConf.mCloudMinAlt ) + skyVolume.mMin[ALT]
120                                                 : world::conv::Meters2Sim( cloudConf.mCloudMinAlt );
121     const fp        altMax          = flatWorld ? world::conv::Meters2Sim( cloudConf.mCloudMaxAlt ) + skyVolume.mMax[ALT]
122                                                 : world::conv::Meters2Sim( cloudConf.mCloudMaxAlt );
123     const fp        altDelta        = altMax - altMin;
124     const fp        widthMin        = world::conv::Meters2Sim( cloudConf.mCloudMinWidth );
125     const fp        widthDelta      = world::conv::Meters2Sim( cloudConf.mCloudMaxWidth ) - widthMin;
126     RefPtr<osg::StateSet> stateSet  = GetNodeSort().GetStateSet();
127     const RGBA      color           = GET_WEATHER().IfSnowOrRain() ? RGBA( 0x50, 0x50, 0x80, 0xc0 )
128                                                                    : RGBA( 0xff, 0xff, 0xff, 0x80 );
129     WorldVertex     pos;
130 
131 ASSERT( cloudCount < 50*THOUSAND );
132 ASSERT( skyWidth > 0 );
133 ASSERT( altDelta > 0 );
134 ASSERT( widthDelta > 0 );
135 
136     // Loop to create cloud sprites.
137     // Since some cloud args will be rejected (outside skydome),
138     // the number of loops will be greater than cloudCount.
139     uint curCloudCount, i;  // curCloudCount counts each actual cloud
140     for ( curCloudCount = i = 0;
141           (curCloudCount < cloudCount) and (i < cloudCount * 4);
142           ++i )
143     {
144         if ( flatWorld )
145         {
146             pos[XX] = Random::random_f(skyWidth) - skyWidthHalf;
147             pos[YY] = Random::random_f(skyWidth) - skyWidthHalf;
148             pos[ZZ] = altMin + Random::random_f(altDelta);
149         }
150         else
151         {
152             SphereVertex sv( Random::random_f(180.0f) - 90.0f,
153                              Random::random_f(360.0f) - 180.0f,
154                              Meter(altMin + Random::random_f(altDelta)) );
155             pos = world::conv::SphereVertex2WorldVertex( sv );
156         }
157 
158         // Skip if cloud would be outside sky-dome.
159         const fp width = widthMin + Random::random_f( widthDelta );
160         if ( skyDome.IfInside( pos, width * 0.5f ) )
161         {
162             // Create a cloud sprite.
163             ++curCloudCount;
164 
165             const Sprite::Args spriteArgs( pos,
166                                            width,
167                                            stateSet,
168                                            color );
169 
170             AttachParticle( new CloudParticle( spriteArgs ) );
171         }
172     }
173 }
174 
175 /*****************************************************************************
176  * Rotate clouds according to view.
177  * As an illusion, clouds sprites are counter-rolled as player rolls.
178  *****************************************************************************/
179 void
operator ()(shptr<View> view)180 CloudPreDrawViewListener::operator()( shptr<View> view )
181 {
182     // Reflect.
183     mCloudParticleSystem.PreDraw( view );
184 }
185 
186 void
PreDraw(shptr<View> view)187 CloudParticleSystem::PreDraw( shptr<View> view )
188 {
189     // Every View tracks its "roll degree" when its view matrix is recomputed.
190     // Rotating all the Sprites is very slow, so bypass if change in roll degree is nearly zero.
191     const Degree rollDeg = view->GetRollDegree();
192     if ( ABS( rollDeg - mRollDeg ) > 0.1f )
193     {
194         mRollDeg = rollDeg;  // current degree of roll of all cloud sprites
195         const Radian rollRad = Deg2Rad( rollDeg );
196 
197         // Rotate (or not if the roll angle is zero) all the cloud sprites.
198         for ( BigParticles::iterator iter = mBigParticles.begin();
199               iter != mBigParticles.end();
200               ++iter )
201         {
202             // As an illusion, clouds sprites are counter-rolled as player rolls.
203             (*iter)->RotateRoll( rollRad, Sprite::ROTATE_ABS );  // Particle->RotateRoll()
204         }
205     }
206 }
207 
208 } // namespace sky
209