timeOfDay.cpp
Engine/source/environment/timeOfDay.cpp
Public Functions
ConsoleDocClass(TimeOfDay , "@brief Environmental object that triggers <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> day/night cycle in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">level.\n\n</a>" "@note <a href="/coding/class/classtimeofday/">TimeOfDay</a> only works in Advanced Lighting with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Sub object or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ScatterSky\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classtimeofday/">TimeOfDay</a>(tod)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " axisTilt = \"23.44\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " dayLength = \"120\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " startTime = \"0.15\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " time = \"0.15\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " play = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " azimuthOverride = \"572.958\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " dayScale = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " nightScale = \"1.5\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " position = \"598.399 550.652 196.297\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " rotation = \"1 0 0 0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " scale = \"1 1 1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " canSave = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " canSaveDynamicFields = \"1\";\<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\n</a>" "@ingroup enviroMisc" )
DefineEngineMethod(TimeOfDay , addTimeOfDayEvent , void , (F32 elevation, const char *identifier) , "" )
DefineEngineMethod(TimeOfDay , animate , void , (F32 elevation, F32 degreesPerSecond) , "" )
DefineEngineMethod(TimeOfDay , setDayLength , void , (F32 seconds) , "" )
DefineEngineMethod(TimeOfDay , setPlay , void , (bool enabled) , "" )
DefineEngineMethod(TimeOfDay , setTimeOfDay , void , (F32 time) , "" )
Detailed Description
Public Functions
ConsoleDocClass(TimeOfDay , "@brief Environmental object that triggers <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> day/night cycle in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">level.\n\n</a>" "@note <a href="/coding/class/classtimeofday/">TimeOfDay</a> only works in Advanced Lighting with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Sub object or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ScatterSky\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classtimeofday/">TimeOfDay</a>(tod)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " axisTilt = \"23.44\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " dayLength = \"120\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " startTime = \"0.15\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " time = \"0.15\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " play = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " azimuthOverride = \"572.958\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " dayScale = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " nightScale = \"1.5\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " position = \"598.399 550.652 196.297\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " rotation = \"1 0 0 0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " scale = \"1 1 1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " canSave = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " canSaveDynamicFields = \"1\";\<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\n</a>" "@ingroup enviroMisc" )
DefineEngineMethod(TimeOfDay , addTimeOfDayEvent , void , (F32 elevation, const char *identifier) , "" )
DefineEngineMethod(TimeOfDay , animate , void , (F32 elevation, F32 degreesPerSecond) , "" )
DefineEngineMethod(TimeOfDay , setDayLength , void , (F32 seconds) , "" )
DefineEngineMethod(TimeOfDay , setPlay , void , (bool enabled) , "" )
DefineEngineMethod(TimeOfDay , setTimeOfDay , void , (F32 time) , "" )
IMPLEMENT_CO_NETOBJECT_V1(TimeOfDay )
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 "environment/timeOfDay.h" 26 27#include "console/consoleTypes.h" 28#include "core/stream/bitStream.h" 29#include "T3D/gameBase/gameConnection.h" 30#include "environment/sun.h" 31#include "console/engineAPI.h" 32 33 34TimeOfDayUpdateSignal TimeOfDay::smTimeOfDayUpdateSignal; 35 36IMPLEMENT_CO_NETOBJECT_V1(TimeOfDay); 37 38ConsoleDocClass( TimeOfDay, 39 "@brief Environmental object that triggers a day/night cycle in level.\n\n" 40 41 "@note TimeOfDay only works in Advanced Lighting with a Sub object or ScatterSky\n\n" 42 43 "@tsexample\n" 44 "new TimeOfDay(tod)\n" 45 "{\n" 46 " axisTilt = \"23.44\";\n" 47 " dayLength = \"120\";\n" 48 " startTime = \"0.15\";\n" 49 " time = \"0.15\";\n" 50 " play = \"0\";\n" 51 " azimuthOverride = \"572.958\";\n" 52 " dayScale = \"1\";\n" 53 " nightScale = \"1.5\";\n" 54 " position = \"598.399 550.652 196.297\";\n" 55 " rotation = \"1 0 0 0\";\n" 56 " scale = \"1 1 1\";\n" 57 " canSave = \"1\";\n" 58 " canSaveDynamicFields = \"1\";\n" 59 "};\n" 60 "@endtsexample\n\n" 61 "@ingroup enviroMisc" 62); 63 64TimeOfDay::TimeOfDay() 65 : mStartTimeOfDay( 0.5f ), // High noon 66 mDayLen( 120.0f ), // 2 minutes 67 mAxisTilt( 23.44f ), // 35 degree tilt 68 mAzimuth( 0.0f ), 69 mElevation( 0.0f ), 70 mTimeOfDay( 0.0f ), // initialized to StartTimeOfDay in onAdd 71 mDayScale( 1.0f ), 72 mPlay( true ), 73 mNightScale( 1.5f ), 74 mAnimateTime( 0.0f ), 75 mAnimateSpeed( 0.0f ), 76 mAnimate( false ) 77{ 78 mNetFlags.set( Ghostable | ScopeAlways ); 79 mTypeMask = EnvironmentObjectType; 80 81 // Sets the sun vector directly overhead for lightmap generation 82 // The value of mSunVector is grabbed by the terrain lighting stuff. 83 /* 84 F32 ele, azi; 85 ele = azi = TORADIANS(90); 86 MathUtils::getVectorFromAngles(mSunVector, azi, ele); 87 */ 88 mPrevElevation = 0; 89 mNextElevation = 0; 90 mAzimuthOverride = 1.0f; 91 92 _initColors(); 93} 94 95TimeOfDay::~TimeOfDay() 96{ 97} 98 99bool TimeOfDay::setTimeOfDay( void *object, const char *index, const char *data ) 100{ 101 TimeOfDay *tod = static_cast<TimeOfDay*>(object); 102 tod->setTimeOfDay( dAtof( data ) ); 103 104 return false; 105} 106 107bool TimeOfDay::setPlay( void *object, const char *index, const char *data ) 108{ 109 TimeOfDay *tod = static_cast<TimeOfDay*>(object); 110 tod->setPlay( dAtob( data ) ); 111 112 return false; 113} 114 115bool TimeOfDay::setDayLength( void *object, const char *index, const char *data ) 116{ 117 TimeOfDay *tod = static_cast<TimeOfDay*>(object); 118 F32 length = dAtof( data ); 119 if( length != 0 ) 120 tod->setDayLength( length ); 121 122 return false; 123 124} 125 126void TimeOfDay::initPersistFields() 127{ 128 addGroup( "TimeOfDay" ); 129 130 addField( "axisTilt", TypeF32, Offset( mAxisTilt, TimeOfDay ), 131 "The angle in degrees between global equator and tropic." ); 132 133 addProtectedField( "dayLength", TypeF32, Offset( mDayLen, TimeOfDay ), &setDayLength, &defaultProtectedGetFn, 134 "The length of a virtual day in real world seconds." ); 135 136 addField( "startTime", TypeF32, Offset( mStartTimeOfDay, TimeOfDay ), 137 "" ); 138 139 addProtectedField( "time", TypeF32, Offset( mTimeOfDay, TimeOfDay ), &setTimeOfDay, &defaultProtectedGetFn, "Current time of day." ); 140 141 addProtectedField( "play", TypeBool, Offset( mPlay, TimeOfDay ), &setPlay, &defaultProtectedGetFn, "True when the TimeOfDay object is operating." ); 142 143 addField( "azimuthOverride", TypeF32, Offset( mAzimuthOverride, TimeOfDay ), "" ); 144 145 addField( "dayScale", TypeF32, Offset( mDayScale, TimeOfDay ), "Scalar applied to time that elapses while the sun is up." ); 146 147 addField( "nightScale", TypeF32, Offset( mNightScale, TimeOfDay ), "Scalar applied to time that elapses while the sun is down." ); 148 149 endGroup( "TimeOfDay" ); 150 151 Parent::initPersistFields(); 152} 153 154void TimeOfDay::consoleInit() 155{ 156 Parent::consoleInit(); 157 158 //addVariable( "$TimeOfDay::currentTime", &TimeOfDay::smCurrentTime ); 159 //addVariable( "$TimeOfDay::timeScale", TypeF32, &TimeOfDay::smTimeScale ); 160} 161 162void TimeOfDay::inspectPostApply() 163{ 164 _updatePosition(); 165 setMaskBits( OrbitMask ); 166} 167 168void TimeOfDay::_onGhostAlwaysDone() 169{ 170 _updatePosition(); 171} 172 173bool TimeOfDay::onAdd() 174{ 175 if ( !Parent::onAdd() ) 176 return false; 177 178 // The server initializes to the specified starting values. 179 // The client initializes itself to the server time from 180 // unpackUpdate. 181 if ( isServerObject() ) 182 { 183 mTimeOfDay = mStartTimeOfDay; 184 _updatePosition(); 185 } 186 187 // We don't use a bounds. 188 setGlobalBounds(); 189 resetWorldBox(); 190 addToScene(); 191 192 // Lets receive ghost events so we can resolve 193 // the sun object. 194 if ( isClientObject() ) 195 NetConnection::smGhostAlwaysDone.notify( this, &TimeOfDay::_onGhostAlwaysDone ); 196 197 if ( isServerObject() ) 198 Con::executef( this, "onAdd" ); 199 200 setProcessTick( true ); 201 202 return true; 203} 204 205void TimeOfDay::onRemove() 206{ 207 if ( isClientObject() ) 208 NetConnection::smGhostAlwaysDone.remove( this, &TimeOfDay::_onGhostAlwaysDone ); 209 210 removeFromScene(); 211 Parent::onRemove(); 212} 213 214U32 TimeOfDay::packUpdate(NetConnection *conn, U32 mask, BitStream *stream ) 215{ 216 U32 retMask = Parent::packUpdate( conn, mask, stream ); 217 218 if ( stream->writeFlag( mask & OrbitMask ) ) 219 { 220 stream->write( mStartTimeOfDay ); 221 stream->write( mDayLen ); 222 stream->write( mTimeOfDay ); 223 stream->write( mAxisTilt ); 224 stream->write( mAzimuthOverride ); 225 226 stream->write( mDayScale ); 227 stream->write( mNightScale ); 228 229 stream->writeFlag( mPlay ); 230 } 231 232 if ( stream->writeFlag( mask & AnimateMask ) ) 233 { 234 stream->write( mAnimateTime ); 235 stream->write( mAnimateSpeed ); 236 } 237 238 return retMask; 239} 240 241void TimeOfDay::unpackUpdate( NetConnection *conn, BitStream *stream ) 242{ 243 Parent::unpackUpdate( conn, stream ); 244 245 if ( stream->readFlag() ) // OrbitMask 246 { 247 stream->read( &mStartTimeOfDay ); 248 stream->read( &mDayLen ); 249 stream->read( &mTimeOfDay ); 250 stream->read( &mAxisTilt ); 251 stream->read( &mAzimuthOverride ); 252 253 stream->read( &mDayScale ); 254 stream->read( &mNightScale ); 255 256 mPlay = stream->readFlag(); 257 258 _updatePosition(); 259 } 260 261 if ( stream->readFlag() ) // AnimateMask 262 { 263 F32 time, speed; 264 stream->read( &time ); 265 stream->read( &speed ); 266 267 if( isProperlyAdded() ) 268 animate( time, speed ); 269 } 270} 271 272void TimeOfDay::processTick( const Move *move ) 273{ 274 if ( mAnimate ) 275 { 276 F32 current = mTimeOfDay * 360.0f; 277 F32 next = current + (mAnimateSpeed * TickSec); 278 279 // Protect for wrap around. 280 while ( next > 360.0f ) 281 next -= 360.0f; 282 283 // Clamp to make sure we don't pass the target time. 284 if ( next >= mAnimateTime ) 285 { 286 next = mAnimateTime; 287 mAnimate = false; 288 } 289 290 // Set the new time of day. 291 mTimeOfDay = next / 360.0f; 292 293 _updatePosition(); 294 _updateTimeEvents(); 295 296 if ( !mAnimate && isServerObject() ) 297 Con::executef( this, "onAnimateDone" ); 298 } 299 else if ( mPlay ) 300 { 301 F32 dt = TickSec; 302 F32 current = mRadToDeg( mNextElevation ); 303 304 if ( current > 350.0f || ( 0.0f <= current && current < 190.0f ) ) 305 dt *= mDayScale; 306 else 307 dt *= mNightScale; 308 309 mTimeOfDay += dt / mDayLen; 310 311 // It could be possible for more than a full day to 312 // pass in a single advance time, so I put this inside a loop 313 // but timeEvents will not actually be called for the 314 // skipped day. 315 while ( mTimeOfDay > 1.0f ) 316 mTimeOfDay -= 1.0f; 317 318 _updatePosition(); 319 _updateTimeEvents(); 320 } 321 else 322 _updatePosition(); 323} 324 325void TimeOfDay::_updatePosition() 326{ 327 mPrevElevation = mNextElevation; 328 329 if ( mFabs( mAzimuthOverride ) ) 330 { 331 mElevation = mDegToRad( mTimeOfDay * 360.0f ); 332 mAzimuth = mAzimuthOverride; 333 334 mNextElevation = mElevation; // already normalized 335 } 336 else 337 { 338 //// Full azimuth/elevation calculation. 339 //// calculate sun decline and meridian angle (in radians) 340 //F32 sunDecline = mSin( M_2PI * mTimeOfYear ) * mDegToRad( mAxisTilt ); 341 //F32 meridianAngle = mTimeOfDay * M_2PI - mDegToRad( mLongitude ); 342 343 //// calculate the elevation and azimuth (in radians) 344 //mElevation = _calcElevation( mDegToRad( mLatitude ), sunDecline, meridianAngle ); 345 //mAzimuth = _calcAzimuth( mDegToRad( mLatitude ), sunDecline, meridianAngle ); 346 347 // Simplified azimuth/elevation calculation. 348 // calculate sun decline and meridian angle (in radians) 349 F32 sunDecline = mDegToRad( mAxisTilt ); 350 F32 meridianAngle = mTimeOfDay * M_2PI; 351 352 // calculate the elevation and azimuth (in radians) 353 mElevation = _calcElevation( 0.0f, sunDecline, meridianAngle ); 354 mAzimuth = _calcAzimuth( 0.0f, sunDecline, meridianAngle ); 355 356 // calculate 'normalized' elevation (0=sunrise, PI/2=zenith, PI=sunset, 3PI/4=nadir) 357 F32 normElevation = M_PI_F * mElevation / ( 2 * _calcElevation( 0.0f, sunDecline, 0.0f ) ); 358 if ( mAzimuth > M_PI_F ) 359 normElevation = M_PI_F - normElevation; 360 else if ( mElevation < 0 ) 361 normElevation = M_2PI_F + normElevation; 362 363 mNextElevation = normElevation; 364 } 365 366 // Only the client updates the sun position! 367 if ( isClientObject() ) 368 smTimeOfDayUpdateSignal.trigger( this, mTimeOfDay ); 369} 370 371F32 TimeOfDay::_calcElevation( F32 lat, F32 dec, F32 mer ) 372{ 373 return mAsin( mSin(lat) * mSin(dec) + mCos(lat) * mCos(dec) * mCos(mer) ); 374} 375 376F32 TimeOfDay::_calcAzimuth( F32 lat, F32 dec, F32 mer ) 377{ 378 // Add PI to normalize this from the range of -PI/2 to PI/2 to 0 to 2 * PI; 379 return mAtan2( mSin(mer), mCos(mer) * mSin(lat) - mTan(dec) * mCos(lat) ) + M_PI_F; 380} 381 382void TimeOfDay::_getSunColor( LinearColorF *outColor ) const 383{ 384 const COLOR_TARGET *ct = NULL; 385 386 F32 ele = mClampF( M_2PI_F - mNextElevation, 0.0f, M_PI_F ); 387 F32 phase = -1.0f; 388 F32 div; 389 390 if (!mColorTargets.size()) 391 { 392 outColor->set(1.0f,1.0f,1.0f); 393 return; 394 } 395 396 if (mColorTargets.size() == 1) 397 { 398 ct = &mColorTargets[0]; 399 outColor->set(ct->color.red, ct->color.green, ct->color.blue); 400 return; 401 } 402 403 //simple check 404 if ( mColorTargets[0].elevation != 0.0f ) 405 { 406 AssertFatal(0, "TimeOfDay::GetColor() - First elevation must be 0.0 radians"); 407 outColor->set(1.0f, 1.0f, 1.0f); 408 //mBandMod = 1.0f; 409 //mCurrentBandColor = color; 410 return; 411 } 412 413 if ( mColorTargets[mColorTargets.size()-1].elevation != M_PI_F ) 414 { 415 AssertFatal(0, "Celestails::GetColor() - Last elevation must be PI"); 416 outColor->set(1.0f, 1.0f, 1.0f); 417 //mBandMod = 1.0f; 418 //mCurrentBandColor = color; 419 return; 420 } 421 422 //we need to find the phase and interp... also loop back around 423 U32 count=0; 424 for (;count < mColorTargets.size() - 1; count++) 425 { 426 const COLOR_TARGET *one = &mColorTargets[count]; 427 const COLOR_TARGET *two = &mColorTargets[count+1]; 428 429 if (ele >= one->elevation && ele <= two->elevation) 430 { 431 div = two->elevation - one->elevation; 432 433 //catch bad input divide by zero 434 if ( mFabs( div ) < 0.01f ) 435 div = 0.01f; 436 437 phase = (ele - one->elevation) / div; 438 outColor->interpolate( one->color, two->color, phase ); 439 440 //mCurrentBandColor.interpolate(one->bandColor, two->bandColor, phase); 441 //mBandMod = one->bandMod * (1.0f - phase) + two->bandMod * phase; 442 443 return; 444 } 445 } 446 447 AssertFatal(0,"This isn't supposed to happen"); 448} 449 450void TimeOfDay::_initColors() 451{ 452 // NOTE: The elevation targets represent distances 453 // from PI/2 radians (strait up). 454 455 LinearColorF c; 456 LinearColorF bc; 457 458 // e is for elevation 459 F32 e = M_PI_F / 13.0f; // (semicircle in radians)/(number of color target entries); 460 461 // Day 462 c.set(1.0f,1.0f,1.0f); 463 _addColorTarget(0, c, 1.0f, c); // High noon at equanox 464 c.set(.9f,.9f,.9f); 465 _addColorTarget(e * 1.0f, c, 1.0f, c); 466 c.set(.9f,.9f,.9f); 467 _addColorTarget(e * 2.0f, c, 1.0f, c); 468 c.set(.8f,.75f,.75f); 469 _addColorTarget(e * 3.0f, c, 1.0f, c); 470 c.set(.7f,.65f,.65f); 471 _addColorTarget(e * 4.0f, c, 1.0f, c); 472 473 //Dawn and Dusk (3 entries) 474 c.set(.7f,.65f,.65f); 475 bc.set(.8f,.6f,.3f); 476 _addColorTarget(e * 5.0f, c, 3.0f, bc); 477 c.set(.65f,.54f,.4f); 478 bc.set(.75f,.5f,.4f); 479 _addColorTarget(e * 6.0f, c, 2.75f, bc); 480 c.set(.55f,.45f,.25f); 481 bc.set(.65f,.3f,.3f); 482 _addColorTarget(e * 7.0f, c, 2.5f, bc); 483 484 //NIGHT 485 c.set(.3f,.3f,.3f); 486 bc.set(.7f,.4f,.2f); 487 _addColorTarget(e * 8.0f, c, 1.25f, bc); 488 c.set(.25f,.25f,.3f); 489 bc.set(.8f,.3f,.2f); 490 _addColorTarget(e * 9.0f, c, 1.00f, bc); 491 c.set(.25f,.25f,.4f); 492 _addColorTarget(e * 10.0f, c, 1.0f, c); 493 c.set(.2f,.2f,.35f); 494 _addColorTarget(e * 11.0f, c, 1.0f, c); 495 c.set(.15f,.15f,.2f); 496 _addColorTarget(M_PI_F, c, 1.0f, c); // Midnight at equanox. 497} 498 499void TimeOfDay::_addColorTarget( F32 ele, const LinearColorF &color, F32 bandMod, const LinearColorF &bandColor ) 500{ 501 COLOR_TARGET newTarget; 502 503 newTarget.elevation = ele; 504 newTarget.color = color; 505 newTarget.bandMod = bandMod; 506 newTarget.bandColor = bandColor; 507 508 mColorTargets.push_back(newTarget); 509} 510 511void TimeOfDay::_updateTimeEvents() 512{ 513 if ( mTimeEvents.empty() ) 514 return; 515 516 // Get the prev, next elevation in degrees since TimeOfDayEvent is specified 517 // in degrees. 518 F32 prevElevation = mRadToDeg( mPrevElevation ); 519 F32 nextElevation = mRadToDeg( mNextElevation ); 520 521 // If prevElevation is less than nextElevation then its the next day. 522 // Unroll it so we can just loop forward in time and simplify our loop. 523 if ( nextElevation < prevElevation ) 524 nextElevation += 360.0f; 525 526 const U32 evtCount = mTimeEvents.size(); 527 528 // Find where in the event list we need to start... 529 // The first timeEvent with elevation greater than our previous elevation. 530 531 U32 start = 0; 532 for ( ; start < evtCount; start++ ) 533 { 534 if ( mTimeEvents[start].triggerElevation > prevElevation ) 535 break; 536 } 537 538 bool onNextDay = false; 539 540 // Nothing between prevElevation and the end of the day... 541 // Check between start of the day and nextElevation... 542 if ( start == evtCount ) 543 { 544 start = 0; 545 for ( ; start < evtCount; start++ ) 546 { 547 if ( mTimeEvents[start].triggerElevation <= nextElevation ) 548 { 549 onNextDay = true; 550 break; 551 } 552 } 553 } 554 555 // No events were hit... 556 if ( start == evtCount ) 557 return; 558 559 U32 itr = start; 560 while ( true ) 561 { 562 TimeOfDayEvent &timeEvent = mTimeEvents[itr]; 563 564 F32 elev = timeEvent.triggerElevation; 565 if ( onNextDay ) 566 elev += 360.0f; 567 568 // Hit an event that happens later after nextElevation so we 569 // have checked everything within the range and are done. 570 if ( elev > nextElevation ) 571 break; 572 573 // If its not greater than the nextElevation it must be less, and if 574 // we are here we already know its greater than prevElevation. 575 576 AssertFatal( elev >= prevElevation && elev <= nextElevation, "TimeOfDay::_updateTimeEvents - Logical error in here!" ); 577 AssertFatal( !timeEvent.deleteMe, "TimeOfDay::_updateTimeEvents - tried to fire the same event twice!" ); 578 579 _onTimeEvent( timeEvent.identifier ); 580 581 if ( timeEvent.oneShot ) 582 timeEvent.deleteMe = true; 583 584 // On to the next time event... 585 itr++; 586 587 // We hit the end of the day? 588 if ( itr == evtCount ) 589 { 590 // We are already on the next day so we have checked everything. 591 if ( onNextDay ) 592 break; 593 // Check events for the next day 594 else 595 { 596 itr = 0; 597 onNextDay = true; 598 } 599 } 600 } 601 602 // Cleanup one-shot events that fired... 603 604 for ( S32 i = 0; i < mTimeEvents.size(); i++ ) 605 { 606 if ( mTimeEvents[i].deleteMe ) 607 { 608 // Don't use erase_fast, there are ordered. 609 mTimeEvents.erase( i ); 610 i--; 611 } 612 } 613} 614 615void TimeOfDay::addTimeEvent( F32 triggerElevation, const UTF8 *identifier ) 616{ 617 // Insert in ascending order of elevation. 618 // Note that having more than one TimeEvent with the same triggerElevation 619 // may cause undefined behavior. 620 621 TimeOfDayEvent *pEvent = NULL; 622 623 if ( mTimeEvents.empty() || mTimeEvents.last().triggerElevation <= triggerElevation ) 624 { 625 mTimeEvents.increment(); 626 pEvent = &mTimeEvents.last(); 627 } 628 else 629 { 630 for ( S32 i = 0; i < mTimeEvents.size(); i++ ) 631 { 632 if ( mTimeEvents[i].triggerElevation > triggerElevation ) 633 { 634 mTimeEvents.insert( i ); 635 pEvent = &mTimeEvents[i]; 636 break; 637 } 638 } 639 } 640 641 AssertFatal( pEvent, "TimeOfDay::addTimeEvent - could not find place to insert event." ); 642 643 pEvent->triggerElevation = triggerElevation; 644 pEvent->identifier = identifier; 645 pEvent->oneShot = false; 646 647 pEvent->deleteMe = false; 648} 649 650void TimeOfDay::setTimeOfDay( F32 time ) 651{ 652 mTimeOfDay = time; 653 654 while ( mTimeOfDay > 1.0f ) 655 mTimeOfDay -= 1.0f; 656 while ( mTimeOfDay < 0.0f ) 657 mTimeOfDay += 1.0f; 658 659 _updatePosition(); 660 661 //if ( isServerObject() ) 662 _updateTimeEvents(); 663 664 setMaskBits( OrbitMask ); 665} 666 667void TimeOfDay::_onTimeEvent( const String &identifier ) 668{ 669 // Client doesn't do onTimeEvent callbacks. 670 if ( isClientObject() ) 671 return; 672 673 String strCurrentTime = String::ToString( "%g", mTimeOfDay ); 674 675 F32 elevation = mRadToDeg( mNextElevation ); 676 while( elevation < 0 ) 677 elevation += 360.0f; 678 while( elevation > 360.0f ) 679 elevation -= 360.0f; 680 681 String strCurrentElevation = String::ToString( "%g", elevation ); 682 683 Con::executef( this, "onTimeEvent", identifier.c_str(), strCurrentTime.c_str(), strCurrentElevation.c_str() ); 684} 685 686void TimeOfDay::animate( F32 time, F32 speed ) 687{ 688 // Stop any existing animation... this one 689 // becomes the new one. 690 mAnimate = false; 691 692 // Set the target time to hit. 693 mAnimateTime = mClamp(time, 0.0f, 360.0f); 694 695 F32 current = mTimeOfDay * 360.0f; 696 F32 target = mAnimateTime; 697 if ( target < current ) 698 target += 360.0f; 699 700 // If we're already at the current time then 701 // we have nothing more to do... the animation is here. 702 F32 dif = target - current; 703 if ( mIsZero( dif ) ) 704 return; 705 706 // Start playback. 707 mAnimateSpeed = speed; 708 mAnimate = true; 709 710 if ( isServerObject() ) 711 { 712 Con::executef( this, "onAnimateStart" ); 713 setMaskBits( AnimateMask ); 714 } 715} 716 717DefineEngineMethod( TimeOfDay, addTimeOfDayEvent, void, (F32 elevation, const char *identifier ),, 718 "" ) 719{ 720 object->addTimeEvent( elevation, identifier ); 721} 722 723DefineEngineMethod( TimeOfDay, setTimeOfDay, void, ( F32 time ),, 724 "" ) 725{ 726 object->setTimeOfDay( time ); 727} 728 729DefineEngineMethod( TimeOfDay, setPlay, void, ( bool enabled ),, 730 "") 731{ 732 object->setPlay( enabled ); 733} 734 735DefineEngineMethod( TimeOfDay, setDayLength, void, ( F32 seconds ),, 736 "" ) 737{ 738 if ( seconds > 0.0f ) 739 object->setDayLength( seconds ); 740} 741 742DefineEngineMethod( TimeOfDay, animate, void, ( F32 elevation, F32 degreesPerSecond ),, 743 "") 744{ 745 object->animate( elevation, degreesPerSecond ); 746} 747