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