timeOfDay.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "environment/timeOfDay.h"
  24. #include "console/consoleTypes.h"
  25. #include "core/stream/bitStream.h"
  26. #include "T3D/gameBase/gameConnection.h"
  27. #include "environment/sun.h"
  28. #include "console/engineAPI.h"
  29. #include "console/typeValidators.h"
  30. TimeOfDayUpdateSignal TimeOfDay::smTimeOfDayUpdateSignal;
  31. IMPLEMENT_CO_NETOBJECT_V1(TimeOfDay);
  32. ConsoleDocClass( TimeOfDay,
  33. "@brief Environmental object that triggers a day/night cycle in level.\n\n"
  34. "@note TimeOfDay only works in Advanced Lighting with a Sub object or ScatterSky\n\n"
  35. "@tsexample\n"
  36. "new TimeOfDay(tod)\n"
  37. "{\n"
  38. " axisTilt = \"23.44\";\n"
  39. " dayLength = \"120\";\n"
  40. " startTime = \"0.15\";\n"
  41. " time = \"0.15\";\n"
  42. " play = \"0\";\n"
  43. " azimuthOverride = \"572.958\";\n"
  44. " dayScale = \"1\";\n"
  45. " nightScale = \"1.5\";\n"
  46. " position = \"598.399 550.652 196.297\";\n"
  47. " rotation = \"1 0 0 0\";\n"
  48. " scale = \"1 1 1\";\n"
  49. " canSave = \"1\";\n"
  50. " canSaveDynamicFields = \"1\";\n"
  51. "};\n"
  52. "@endtsexample\n\n"
  53. "@ingroup enviroMisc"
  54. );
  55. TimeOfDay::TimeOfDay()
  56. : mStartTimeOfDay( 0.5f ), // High noon
  57. mDayLen( 120.0f ), // 2 minutes
  58. mAxisTilt( 23.44f ), // 35 degree tilt
  59. mAzimuth( 0.0f ),
  60. mElevation( 0.0f ),
  61. mTimeOfDay( 0.0f ), // initialized to StartTimeOfDay in onAdd
  62. mDayScale( 1.0f ),
  63. mPlay( true ),
  64. mNightScale( 1.5f ),
  65. mAnimateTime( 0.0f ),
  66. mAnimateSpeed( 0.0f ),
  67. mAnimate( false )
  68. {
  69. mNetFlags.set( Ghostable | ScopeAlways );
  70. mTypeMask = EnvironmentObjectType;
  71. // Sets the sun vector directly overhead for lightmap generation
  72. // The value of mSunVector is grabbed by the terrain lighting stuff.
  73. /*
  74. F32 ele, azi;
  75. ele = azi = TORADIANS(90);
  76. MathUtils::getVectorFromAngles(mSunVector, azi, ele);
  77. */
  78. mPrevElevation = 0;
  79. mNextElevation = 0;
  80. mAzimuthOverride = 1.0f;
  81. _initColors();
  82. }
  83. TimeOfDay::~TimeOfDay()
  84. {
  85. }
  86. bool TimeOfDay::setTimeOfDay( void *object, const char *index, const char *data )
  87. {
  88. TimeOfDay *tod = static_cast<TimeOfDay*>(object);
  89. tod->setTimeOfDay( dAtof( data ) );
  90. return false;
  91. }
  92. bool TimeOfDay::setPlay( void *object, const char *index, const char *data )
  93. {
  94. TimeOfDay *tod = static_cast<TimeOfDay*>(object);
  95. tod->setPlay( dAtob( data ) );
  96. return false;
  97. }
  98. bool TimeOfDay::setDayLength( void *object, const char *index, const char *data )
  99. {
  100. TimeOfDay *tod = static_cast<TimeOfDay*>(object);
  101. F32 length = dAtof( data );
  102. if( length != 0 )
  103. tod->setDayLength( length );
  104. return false;
  105. }
  106. void TimeOfDay::initPersistFields()
  107. {
  108. docsURL;
  109. addGroup( "TimeOfDay" );
  110. addFieldV( "axisTilt", TypeRangedF32, Offset( mAxisTilt, TimeOfDay ), &CommonValidators::DegreeRange,
  111. "The angle in degrees between global equator and tropic." );
  112. addProtectedFieldV( "dayLength", TypeRangedF32, Offset( mDayLen, TimeOfDay ), &setDayLength, &defaultProtectedGetFn, &CommonValidators::PositiveFloat,
  113. "The length of a virtual day in real world seconds." );
  114. addFieldV( "startTime", TypeRangedF32, Offset( mStartTimeOfDay, TimeOfDay ), &CommonValidators::NormalizedFloat,
  115. "" );
  116. addProtectedFieldV( "time", TypeRangedF32, Offset( mTimeOfDay, TimeOfDay ), &setTimeOfDay, &defaultProtectedGetFn, &CommonValidators::NormalizedFloat, "Current time of day." );
  117. addProtectedField( "play", TypeBool, Offset( mPlay, TimeOfDay ), &setPlay, &defaultProtectedGetFn, "True when the TimeOfDay object is operating." );
  118. addFieldV( "azimuthOverride", TypeRangedF32, Offset( mAzimuthOverride, TimeOfDay ), &CommonValidators::PosDegreeRange, "" );
  119. addFieldV( "dayScale", TypeRangedF32, Offset( mDayScale, TimeOfDay ), &CommonValidators::PositiveFloat, "Scalar applied to time that elapses while the sun is up." );
  120. addFieldV( "nightScale", TypeRangedF32, Offset( mNightScale, TimeOfDay ), &CommonValidators::PositiveFloat, "Scalar applied to time that elapses while the sun is down." );
  121. endGroup( "TimeOfDay" );
  122. Parent::initPersistFields();
  123. }
  124. void TimeOfDay::consoleInit()
  125. {
  126. Parent::consoleInit();
  127. //addVariable( "$TimeOfDay::currentTime", &TimeOfDay::smCurrentTime );
  128. //addVariable( "$TimeOfDay::timeScale", TypeF32, &TimeOfDay::smTimeScale );
  129. }
  130. void TimeOfDay::inspectPostApply()
  131. {
  132. _updatePosition();
  133. setMaskBits( OrbitMask );
  134. }
  135. void TimeOfDay::_onGhostAlwaysDone()
  136. {
  137. _updatePosition();
  138. }
  139. bool TimeOfDay::onAdd()
  140. {
  141. if ( !Parent::onAdd() )
  142. return false;
  143. // The server initializes to the specified starting values.
  144. // The client initializes itself to the server time from
  145. // unpackUpdate.
  146. if ( isServerObject() )
  147. {
  148. mTimeOfDay = mStartTimeOfDay;
  149. _updatePosition();
  150. }
  151. // We don't use a bounds.
  152. setGlobalBounds();
  153. resetWorldBox();
  154. addToScene();
  155. // Lets receive ghost events so we can resolve
  156. // the sun object.
  157. if ( isClientObject() )
  158. NetConnection::smGhostAlwaysDone.notify( this, &TimeOfDay::_onGhostAlwaysDone );
  159. if ( isServerObject() )
  160. Con::executef( this, "onAdd" );
  161. setProcessTick( true );
  162. return true;
  163. }
  164. void TimeOfDay::onRemove()
  165. {
  166. if ( isClientObject() )
  167. NetConnection::smGhostAlwaysDone.remove( this, &TimeOfDay::_onGhostAlwaysDone );
  168. removeFromScene();
  169. Parent::onRemove();
  170. }
  171. U32 TimeOfDay::packUpdate(NetConnection *conn, U32 mask, BitStream *stream )
  172. {
  173. U32 retMask = Parent::packUpdate( conn, mask, stream );
  174. if ( stream->writeFlag( mask & OrbitMask ) )
  175. {
  176. stream->write( mStartTimeOfDay );
  177. stream->write( mDayLen );
  178. stream->write( mTimeOfDay );
  179. stream->write( mAxisTilt );
  180. stream->write( mAzimuthOverride );
  181. stream->write( mDayScale );
  182. stream->write( mNightScale );
  183. stream->writeFlag( mPlay );
  184. }
  185. if ( stream->writeFlag( mask & AnimateMask ) )
  186. {
  187. stream->write( mAnimateTime );
  188. stream->write( mAnimateSpeed );
  189. }
  190. return retMask;
  191. }
  192. void TimeOfDay::unpackUpdate( NetConnection *conn, BitStream *stream )
  193. {
  194. Parent::unpackUpdate( conn, stream );
  195. if ( stream->readFlag() ) // OrbitMask
  196. {
  197. stream->read( &mStartTimeOfDay );
  198. stream->read( &mDayLen );
  199. stream->read( &mTimeOfDay );
  200. stream->read( &mAxisTilt );
  201. stream->read( &mAzimuthOverride );
  202. stream->read( &mDayScale );
  203. stream->read( &mNightScale );
  204. mPlay = stream->readFlag();
  205. _updatePosition();
  206. }
  207. if ( stream->readFlag() ) // AnimateMask
  208. {
  209. F32 time, speed;
  210. stream->read( &time );
  211. stream->read( &speed );
  212. if( isProperlyAdded() )
  213. animate( time, speed );
  214. }
  215. }
  216. void TimeOfDay::processTick( const Move *move )
  217. {
  218. if ( mAnimate )
  219. {
  220. F32 current = mTimeOfDay * 360.0f;
  221. F32 next = current + (mAnimateSpeed * TickSec);
  222. // Protect for wrap around.
  223. while ( next > 360.0f )
  224. next -= 360.0f;
  225. // Clamp to make sure we don't pass the target time.
  226. if ( next >= mAnimateTime )
  227. {
  228. next = mAnimateTime;
  229. mAnimate = false;
  230. }
  231. // Set the new time of day.
  232. mTimeOfDay = next / 360.0f;
  233. _updatePosition();
  234. _updateTimeEvents();
  235. if ( !mAnimate && isServerObject() )
  236. Con::executef( this, "onAnimateDone" );
  237. }
  238. else if ( mPlay )
  239. {
  240. F32 dt = TickSec;
  241. F32 current = mRadToDeg( mNextElevation );
  242. if ( current > 350.0f || ( 0.0f <= current && current < 190.0f ) )
  243. dt *= mDayScale;
  244. else
  245. dt *= mNightScale;
  246. mTimeOfDay += dt / mDayLen;
  247. // It could be possible for more than a full day to
  248. // pass in a single advance time, so I put this inside a loop
  249. // but timeEvents will not actually be called for the
  250. // skipped day.
  251. while ( mTimeOfDay > 1.0f )
  252. mTimeOfDay -= 1.0f;
  253. _updatePosition();
  254. _updateTimeEvents();
  255. }
  256. else
  257. _updatePosition();
  258. }
  259. void TimeOfDay::_updatePosition()
  260. {
  261. mPrevElevation = mNextElevation;
  262. if ( mFabs( mAzimuthOverride ) )
  263. {
  264. mElevation = mDegToRad( mTimeOfDay * 360.0f );
  265. mAzimuth = mAzimuthOverride;
  266. mNextElevation = mElevation; // already normalized
  267. }
  268. else
  269. {
  270. //// Full azimuth/elevation calculation.
  271. //// calculate sun decline and meridian angle (in radians)
  272. //F32 sunDecline = mSin( M_2PI * mTimeOfYear ) * mDegToRad( mAxisTilt );
  273. //F32 meridianAngle = mTimeOfDay * M_2PI - mDegToRad( mLongitude );
  274. //// calculate the elevation and azimuth (in radians)
  275. //mElevation = _calcElevation( mDegToRad( mLatitude ), sunDecline, meridianAngle );
  276. //mAzimuth = _calcAzimuth( mDegToRad( mLatitude ), sunDecline, meridianAngle );
  277. // Simplified azimuth/elevation calculation.
  278. // calculate sun decline and meridian angle (in radians)
  279. F32 sunDecline = mDegToRad( mAxisTilt );
  280. F32 meridianAngle = mTimeOfDay * M_2PI;
  281. // calculate the elevation and azimuth (in radians)
  282. mElevation = _calcElevation( 0.0f, sunDecline, meridianAngle );
  283. mAzimuth = _calcAzimuth( 0.0f, sunDecline, meridianAngle );
  284. // calculate 'normalized' elevation (0=sunrise, PI/2=zenith, PI=sunset, 3PI/4=nadir)
  285. F32 normElevation = M_PI_F * mElevation / ( 2 * _calcElevation( 0.0f, sunDecline, 0.0f ) );
  286. if ( mAzimuth > M_PI_F )
  287. normElevation = M_PI_F - normElevation;
  288. else if ( mElevation < 0 )
  289. normElevation = M_2PI_F + normElevation;
  290. mNextElevation = normElevation;
  291. }
  292. // Only the client updates the sun position!
  293. if ( isClientObject() )
  294. smTimeOfDayUpdateSignal.trigger( this, mTimeOfDay );
  295. }
  296. F32 TimeOfDay::_calcElevation( F32 lat, F32 dec, F32 mer )
  297. {
  298. return mAsin( mSin(lat) * mSin(dec) + mCos(lat) * mCos(dec) * mCos(mer) );
  299. }
  300. F32 TimeOfDay::_calcAzimuth( F32 lat, F32 dec, F32 mer )
  301. {
  302. // Add PI to normalize this from the range of -PI/2 to PI/2 to 0 to 2 * PI;
  303. return mAtan2( mSin(mer), mCos(mer) * mSin(lat) - mTan(dec) * mCos(lat) ) + M_PI_F;
  304. }
  305. void TimeOfDay::_getSunColor( LinearColorF *outColor ) const
  306. {
  307. const COLOR_TARGET *ct = NULL;
  308. F32 ele = mClampF( M_2PI_F - mNextElevation, 0.0f, M_PI_F );
  309. F32 phase = -1.0f;
  310. F32 div;
  311. if (!mColorTargets.size())
  312. {
  313. outColor->set(1.0f,1.0f,1.0f);
  314. return;
  315. }
  316. if (mColorTargets.size() == 1)
  317. {
  318. ct = &mColorTargets[0];
  319. outColor->set(ct->color.red, ct->color.green, ct->color.blue);
  320. return;
  321. }
  322. //simple check
  323. if ( mColorTargets[0].elevation != 0.0f )
  324. {
  325. AssertFatal(0, "TimeOfDay::GetColor() - First elevation must be 0.0 radians");
  326. outColor->set(1.0f, 1.0f, 1.0f);
  327. //mBandMod = 1.0f;
  328. //mCurrentBandColor = color;
  329. return;
  330. }
  331. if ( mColorTargets[mColorTargets.size()-1].elevation != M_PI_F )
  332. {
  333. AssertFatal(0, "Celestails::GetColor() - Last elevation must be PI");
  334. outColor->set(1.0f, 1.0f, 1.0f);
  335. //mBandMod = 1.0f;
  336. //mCurrentBandColor = color;
  337. return;
  338. }
  339. //we need to find the phase and interp... also loop back around
  340. U32 count=0;
  341. for (;count < mColorTargets.size() - 1; count++)
  342. {
  343. const COLOR_TARGET *one = &mColorTargets[count];
  344. const COLOR_TARGET *two = &mColorTargets[count+1];
  345. if (ele >= one->elevation && ele <= two->elevation)
  346. {
  347. div = two->elevation - one->elevation;
  348. //catch bad input divide by zero
  349. if ( mFabs( div ) < 0.01f )
  350. div = 0.01f;
  351. phase = (ele - one->elevation) / div;
  352. outColor->interpolate( one->color, two->color, phase );
  353. //mCurrentBandColor.interpolate(one->bandColor, two->bandColor, phase);
  354. //mBandMod = one->bandMod * (1.0f - phase) + two->bandMod * phase;
  355. return;
  356. }
  357. }
  358. AssertFatal(0,"This isn't supposed to happen");
  359. }
  360. void TimeOfDay::_initColors()
  361. {
  362. // NOTE: The elevation targets represent distances
  363. // from PI/2 radians (strait up).
  364. LinearColorF c;
  365. LinearColorF bc;
  366. // e is for elevation
  367. F32 e = M_PI_F / 13.0f; // (semicircle in radians)/(number of color target entries);
  368. // Day
  369. c.set(1.0f,1.0f,1.0f);
  370. _addColorTarget(0, c, 1.0f, c); // High noon at equanox
  371. c.set(.9f,.9f,.9f);
  372. _addColorTarget(e * 1.0f, c, 1.0f, c);
  373. c.set(.9f,.9f,.9f);
  374. _addColorTarget(e * 2.0f, c, 1.0f, c);
  375. c.set(.8f,.75f,.75f);
  376. _addColorTarget(e * 3.0f, c, 1.0f, c);
  377. c.set(.7f,.65f,.65f);
  378. _addColorTarget(e * 4.0f, c, 1.0f, c);
  379. //Dawn and Dusk (3 entries)
  380. c.set(.7f,.65f,.65f);
  381. bc.set(.8f,.6f,.3f);
  382. _addColorTarget(e * 5.0f, c, 3.0f, bc);
  383. c.set(.65f,.54f,.4f);
  384. bc.set(.75f,.5f,.4f);
  385. _addColorTarget(e * 6.0f, c, 2.75f, bc);
  386. c.set(.55f,.45f,.25f);
  387. bc.set(.65f,.3f,.3f);
  388. _addColorTarget(e * 7.0f, c, 2.5f, bc);
  389. //NIGHT
  390. c.set(.3f,.3f,.3f);
  391. bc.set(.7f,.4f,.2f);
  392. _addColorTarget(e * 8.0f, c, 1.25f, bc);
  393. c.set(.25f,.25f,.3f);
  394. bc.set(.8f,.3f,.2f);
  395. _addColorTarget(e * 9.0f, c, 1.00f, bc);
  396. c.set(.25f,.25f,.4f);
  397. _addColorTarget(e * 10.0f, c, 1.0f, c);
  398. c.set(.2f,.2f,.35f);
  399. _addColorTarget(e * 11.0f, c, 1.0f, c);
  400. c.set(.15f,.15f,.2f);
  401. _addColorTarget(M_PI_F, c, 1.0f, c); // Midnight at equanox.
  402. }
  403. void TimeOfDay::_addColorTarget( F32 ele, const LinearColorF &color, F32 bandMod, const LinearColorF &bandColor )
  404. {
  405. COLOR_TARGET newTarget;
  406. newTarget.elevation = ele;
  407. newTarget.color = color;
  408. newTarget.bandMod = bandMod;
  409. newTarget.bandColor = bandColor;
  410. mColorTargets.push_back(newTarget);
  411. }
  412. void TimeOfDay::_updateTimeEvents()
  413. {
  414. if ( mTimeEvents.empty() )
  415. return;
  416. // Get the prev, next elevation in degrees since TimeOfDayEvent is specified
  417. // in degrees.
  418. F32 prevElevation = mRadToDeg( mPrevElevation );
  419. F32 nextElevation = mRadToDeg( mNextElevation );
  420. // If prevElevation is less than nextElevation then its the next day.
  421. // Unroll it so we can just loop forward in time and simplify our loop.
  422. if ( nextElevation < prevElevation )
  423. nextElevation += 360.0f;
  424. const U32 evtCount = mTimeEvents.size();
  425. // Find where in the event list we need to start...
  426. // The first timeEvent with elevation greater than our previous elevation.
  427. U32 start = 0;
  428. for ( ; start < evtCount; start++ )
  429. {
  430. if ( mTimeEvents[start].triggerElevation > prevElevation )
  431. break;
  432. }
  433. bool onNextDay = false;
  434. // Nothing between prevElevation and the end of the day...
  435. // Check between start of the day and nextElevation...
  436. if ( start == evtCount )
  437. {
  438. start = 0;
  439. for ( ; start < evtCount; start++ )
  440. {
  441. if ( mTimeEvents[start].triggerElevation <= nextElevation )
  442. {
  443. onNextDay = true;
  444. break;
  445. }
  446. }
  447. }
  448. // No events were hit...
  449. if ( start == evtCount )
  450. return;
  451. U32 itr = start;
  452. while ( true )
  453. {
  454. TimeOfDayEvent &timeEvent = mTimeEvents[itr];
  455. F32 elev = timeEvent.triggerElevation;
  456. if ( onNextDay )
  457. elev += 360.0f;
  458. // Hit an event that happens later after nextElevation so we
  459. // have checked everything within the range and are done.
  460. if ( elev > nextElevation )
  461. break;
  462. // If its not greater than the nextElevation it must be less, and if
  463. // we are here we already know its greater than prevElevation.
  464. AssertFatal( elev >= prevElevation && elev <= nextElevation, "TimeOfDay::_updateTimeEvents - Logical error in here!" );
  465. AssertFatal( !timeEvent.deleteMe, "TimeOfDay::_updateTimeEvents - tried to fire the same event twice!" );
  466. _onTimeEvent( timeEvent.identifier );
  467. if ( timeEvent.oneShot )
  468. timeEvent.deleteMe = true;
  469. // On to the next time event...
  470. itr++;
  471. // We hit the end of the day?
  472. if ( itr == evtCount )
  473. {
  474. // We are already on the next day so we have checked everything.
  475. if ( onNextDay )
  476. break;
  477. // Check events for the next day
  478. else
  479. {
  480. itr = 0;
  481. onNextDay = true;
  482. }
  483. }
  484. }
  485. // Cleanup one-shot events that fired...
  486. for ( S32 i = 0; i < mTimeEvents.size(); i++ )
  487. {
  488. if ( mTimeEvents[i].deleteMe )
  489. {
  490. // Don't use erase_fast, there are ordered.
  491. mTimeEvents.erase( i );
  492. i--;
  493. }
  494. }
  495. }
  496. void TimeOfDay::addTimeEvent( F32 triggerElevation, const UTF8 *identifier )
  497. {
  498. // Insert in ascending order of elevation.
  499. // Note that having more than one TimeEvent with the same triggerElevation
  500. // may cause undefined behavior.
  501. TimeOfDayEvent *pEvent = NULL;
  502. if ( mTimeEvents.empty() || mTimeEvents.last().triggerElevation <= triggerElevation )
  503. {
  504. mTimeEvents.increment();
  505. pEvent = &mTimeEvents.last();
  506. }
  507. else
  508. {
  509. for ( S32 i = 0; i < mTimeEvents.size(); i++ )
  510. {
  511. if ( mTimeEvents[i].triggerElevation > triggerElevation )
  512. {
  513. mTimeEvents.insert( i );
  514. pEvent = &mTimeEvents[i];
  515. break;
  516. }
  517. }
  518. }
  519. AssertFatal( pEvent, "TimeOfDay::addTimeEvent - could not find place to insert event." );
  520. pEvent->triggerElevation = triggerElevation;
  521. pEvent->identifier = identifier;
  522. pEvent->oneShot = false;
  523. pEvent->deleteMe = false;
  524. }
  525. void TimeOfDay::setTimeOfDay( F32 time )
  526. {
  527. mTimeOfDay = time;
  528. while ( mTimeOfDay > 1.0f )
  529. mTimeOfDay -= 1.0f;
  530. while ( mTimeOfDay < 0.0f )
  531. mTimeOfDay += 1.0f;
  532. _updatePosition();
  533. //if ( isServerObject() )
  534. _updateTimeEvents();
  535. setMaskBits( OrbitMask );
  536. }
  537. void TimeOfDay::_onTimeEvent( const String &identifier )
  538. {
  539. // Client doesn't do onTimeEvent callbacks.
  540. if ( isClientObject() )
  541. return;
  542. String strCurrentTime = String::ToString( "%g", mTimeOfDay );
  543. F32 elevation = mRadToDeg( mNextElevation );
  544. while( elevation < 0 )
  545. elevation += 360.0f;
  546. while( elevation > 360.0f )
  547. elevation -= 360.0f;
  548. String strCurrentElevation = String::ToString( "%g", elevation );
  549. Con::executef( this, "onTimeEvent", identifier.c_str(), strCurrentTime.c_str(), strCurrentElevation.c_str() );
  550. }
  551. void TimeOfDay::animate( F32 time, F32 speed )
  552. {
  553. // Stop any existing animation... this one
  554. // becomes the new one.
  555. mAnimate = false;
  556. // Set the target time to hit.
  557. mAnimateTime = mClamp(time, 0.0f, 360.0f);
  558. F32 current = mTimeOfDay * 360.0f;
  559. F32 target = mAnimateTime;
  560. if ( target < current )
  561. target += 360.0f;
  562. // If we're already at the current time then
  563. // we have nothing more to do... the animation is here.
  564. F32 dif = target - current;
  565. if ( mIsZero( dif ) )
  566. return;
  567. // Start playback.
  568. mAnimateSpeed = speed;
  569. mAnimate = true;
  570. if ( isServerObject() )
  571. {
  572. Con::executef( this, "onAnimateStart" );
  573. setMaskBits( AnimateMask );
  574. }
  575. }
  576. DefineEngineMethod( TimeOfDay, addTimeOfDayEvent, void, (F32 elevation, const char *identifier ),,
  577. "" )
  578. {
  579. object->addTimeEvent( elevation, identifier );
  580. }
  581. DefineEngineMethod( TimeOfDay, setTimeOfDay, void, ( F32 time ),,
  582. "" )
  583. {
  584. object->setTimeOfDay( time );
  585. }
  586. DefineEngineMethod( TimeOfDay, setPlay, void, ( bool enabled ),,
  587. "")
  588. {
  589. object->setPlay( enabled );
  590. }
  591. DefineEngineMethod( TimeOfDay, setDayLength, void, ( F32 seconds ),,
  592. "" )
  593. {
  594. if ( seconds > 0.0f )
  595. object->setDayLength( seconds );
  596. }
  597. DefineEngineMethod( TimeOfDay, animate, void, ( F32 elevation, F32 degreesPerSecond ),,
  598. "")
  599. {
  600. object->animate( elevation, degreesPerSecond );
  601. }