00001 /*************************************************************************** 00002 * * 00003 * (c) Art Tevs, MPI Informatik Saarbruecken * 00004 * mailto: <tevs@mpi-sb.mpg.de> * 00005 * * 00006 * This program is free software; you can redistribute it and/or modify * 00007 * it under the terms of the GNU General Public License as published by * 00008 * the Free Software Foundation; either version 2 of the License, or * 00009 * (at your option) any later version. * 00010 * * 00011 ***************************************************************************/ 00012 00013 00014 #ifndef _NR_C_CLOCK_H_ 00015 #define _NR_C_CLOCK_H_ 00016 00017 00018 //---------------------------------------------------------------------------------- 00019 // Includes 00020 //---------------------------------------------------------------------------------- 00021 #include "Prerequisities.h" 00022 #include "TimeSource.h" 00023 #include "ITimeObserver.h" 00024 #include "Timer.h" 00025 #include "ITask.h" 00026 #include "TimeSourceVirtual.h" 00027 #include "ScriptEngine.h" 00028 00029 namespace nrEngine{ 00030 00031 //! Global clock of the engine used to retrieve time or create timers 00032 /** 00033 * \par 00034 * Clock allows you to create timers from it. Clock is always ticks if any 00035 * time source is bounded. In contrast to Einstein's relativity theory we have 00036 * only one global clock. So your complete application has one time which runs 00037 * with the same speed. But to be able to use Einstein's theory you can create 00038 * timers from each clock, which allows you to use different time speeds. 00039 * Clock can also derive/create timers. But if you want to create the timer by 00040 * yourself you can do this. 00041 * 00042 * @note 00043 * - Our clock works internal with 64Bit float64 values to stay precise. 00044 * Because using of float32 values is enough for every simulation you will get 00045 * 32Bit float32 values as frame interval time. 00046 * - If you compile the engine on 64Bit systems, so you can change this and use 00047 * 64Bit doubles instead. 00048 * - Each created timer dies when clock dies. 00049 * - You can only destroy timers through the clock interface or it will be 00050 * deleted automaticly from the memory when clock is deleted 00051 * - Deleting the timer outside from the clock has no effect 00052 * 00053 * \ingroup time 00054 **/ 00055 class _NRExport Clock : public ITask { 00056 public: 00057 00058 /** 00059 * Set the time source to be used to get current time. Our clock class 00060 * is platform independent but the time get code depends mostly on the 00061 * platform you using. So just derive your time source from ITimeSource 00062 * interface and add this source into our clock. 00063 * @param timeSource smart pointer containing to the time source 00064 **/ 00065 void setTimeSource(SharedPtr<TimeSource> timeSource); 00066 00067 /** 00068 * Returns true if any time source is bounded to our clock. 00069 **/ 00070 bool isAnySourceBounded(); 00071 00072 /** 00073 * Return a smart pointer to the current time source 00074 * @return a smart pointer pointing to the current time source or NULL if 00075 * no time source is bound at now. 00076 **/ 00077 SharedPtr<TimeSource> getTimeSource() { return timeSource; } 00078 00079 /** 00080 * This method will be called by the kernel to start the clock. It is called 00081 * when clock is added to the kernel pipeline. 00082 **/ 00083 Result onStartTask(); 00084 00085 /** 00086 * Updates the clock task by getting time values and calculating of 00087 * frame rate and frame interval. 00088 * Also each connected time observer will be notified so it can do 00089 * own updates. This allows you just to create any observer (like timer) 00090 * bind them to the clock and forgett it. 00091 **/ 00092 Result updateTask(); 00093 00094 /** 00095 * Stop the clock task and release all used objects. 00096 * So all observers will be deleted from the observer list and also 00097 * the time source is removed. After this function you still able to access 00098 * to the clock and timers (because of smart pointers), but the time will 00099 * hold on and no updates of the time will be made. 00100 **/ 00101 Result stopTask(); 00102 00103 /** 00104 * Same as taskUpdate(). This name is choosen to separate calls 00105 * to the clock by not knowing, that it is a task. 00106 * Use this function from somewhere else than a kernel. 00107 **/ 00108 void Ticks(){ updateTask();} 00109 00110 /** 00111 * Returns current time since clock start ticking. 00112 **/ 00113 float64 getTime() const; 00114 00115 00116 /** 00117 * Returns filtered interval time between two or more frames. nrEngine's clock 00118 * does not only calculate the interval between two following frames, but it uses 00119 * a arithmetic average to compute the frame interval between a lot of frames. 00120 * This helps you to ignore some frames if their update time was to big or to less. 00121 */ 00122 float32 getFrameInterval() const; 00123 00124 /** 00125 * Returns number of frames drawn since clock start ticking 00126 **/ 00127 int32 getFrameNumber() const; 00128 00129 /** 00130 * Returns FPS. Like the frameinterval this value is also computed as average 00131 * between a lot of frames. So this value is more smooth. 00132 **/ 00133 float32 getFrameRate() const; 00134 00135 /** 00136 * Get the frame interval time of a real time. If you use fix frame rate, so you 00137 * get as an interval a 1.0 / fix frame rate. This method allows you to get 00138 * the time which your frame really need to render. 00139 **/ 00140 float32 getRealFrameInterval() const; 00141 00142 /** 00143 * Get real frame rate based on getRealFrameInterval() method 00144 **/ 00145 float32 getRealFrameRate() const; 00146 00147 /** 00148 * This method allows you to set the maximal frame rate at which the clock 00149 * shoudl run. It will use this frame rate as an upper bound. If applications 00150 * runs faster than this time, so it will retard to this speed. If app runs 00151 * much slower so nothing happens. At speed above the given boundaries 00152 * the clock runs as for fixed frame rate. So you would retrieve fixed 00153 * frame interval values. Function which retrieves real values are not 00154 * affected by them, so you are still able to get information about the 00155 * real time, which you frame need 00156 **/ 00157 void setMaxFrameRate(bool set, float32 maxFPS = 60.0); 00158 00159 /** 00160 * Add new time observer to our list of observers. Each observer will 00161 * be notified on each frame after clock calculations are done. With this 00162 * system we can separate our timers from game loop, but they still be 00163 * updated on each frame. 00164 * @param timeObserver smart pointers to the observer 00165 * @return internal ID of observer to allow of removing them from the list 00166 * @note Each observer has to implement the ITimeObserver interface 00167 **/ 00168 int32 addObserver(SharedPtr<ITimeObserver> timeObserver); 00169 00170 /** 00171 * Removes an observer from the list by given ID 00172 * @param observerID id of the observer returned by addObserver() 00173 * @return either OK or: 00174 * - CLOCK_OBSERVER_NOT_FOUND if such id is not valid 00175 **/ 00176 Result removeObserver(int32 observerID); 00177 00178 /** 00179 * Add an time observer by a name. So you can delete them also by using 00180 * this name. 00181 * @param obsName name of the observer 00182 * @param timeObserver smart pointer pointing to the observer 00183 * @return either OK or: 00184 * - CLOCK_OBSERVER_ALREADY_ADDED if such is already added 00185 **/ 00186 Result addObserver(const std::string& obsName, SharedPtr<ITimeObserver> timeObserver); 00187 00188 /** 00189 * Removes an observer by using it's name. 00190 * @param obsName name of the observer 00191 * @return either OK or: 00192 * - CLOCK_OBSERVER_NOT_FOUND if such an observer not exists 00193 **/ 00194 Result removeObserver(const std::string& obsName); 00195 00196 /** 00197 * Set the count of frames to be computed to calculate frame rate/interval. 00198 * We need this to smooth big peaks on frame rates to allow application 00199 * run smoother than without this window. Here we just using averaging 00200 * of the frame intervals during certain number of frames. 00201 * @param frameCount size of such frame calculation window 00202 * @param defaultFrameTime is a frame interval added by default as if there 00203 * was a frame rendered with this speed. This allows us to start smoothly 00204 **/ 00205 void setFrameWindow(int32 frameCount = 10, float32 defaultFrameTime = 0.03333); 00206 00207 /** 00208 * If you set this to true so fix frame rate will be used. This helps us 00209 * to run our application on console or do internal calculations based 00210 * on fix frame rate like Doom3 does. 00211 * @param setFixRate if true fix frame rate will be used. 00212 * @param fixFrameRate frame rate to be used 00213 **/ 00214 void setFixFrameRate(bool setFixRate, float32 fixFrameRate = 60.0f); 00215 00216 00217 /** 00218 * Create a new timer which does use this clock as reference. 00219 **/ 00220 SharedPtr<Timer> createTimer(); 00221 00222 /** 00223 * Setup the syncing interval. The clock will be synced with system time 00224 * so it helps to prevent timing errors produced by high performance time 00225 * counters in addidition to CPU speed stepping functionality of the processor unit. 00226 **/ 00227 void setSyncInterval(uint32 milliseconds); 00228 00229 /** 00230 * Reset the clock. The clock time will be resetted to the given time. 00231 * @param resetToTime Reset the clock to the given time value 00232 * @param resetAllObservers if true all registered obseervers would be also resetted 00233 **/ 00234 void reset(float64 resetToTime = 0.0f, bool resetAllObservers = false); 00235 00236 /** 00237 * Script-Function to reset the clock. You can specify in parameters if all timers, which were 00238 * created before should be also reseted. 00239 **/ 00240 ScriptFunctionDef(scriptResetClock); 00241 00242 00243 private: 00244 00245 //! Only engine's core object can create instance 00246 friend class Engine; 00247 00248 /** 00249 * After the object is created and before it runs the returned value 00250 * for the frame rate will be 0. 00251 * The value for frame window is set to 7 per default. 00252 **/ 00253 Clock(); 00254 00255 /** 00256 * Release all used memory. 00257 **/ 00258 ~Clock(); 00259 00260 // observers 00261 typedef std::vector< SharedPtr<ITimeObserver> > ObserverList; 00262 std::map < std::string, int32> observerIDList; 00263 ObserverList observers; 00264 00265 // frame filtering 00266 std::deque<float64> frameDurationHistory; 00267 float64 frameDefaultTime; 00268 int32 frameFilteringWindow; 00269 00270 // time source 00271 SharedPtr<TimeSource> timeSource; 00272 SharedPtr<TimeSource> realTimeSource; 00273 float64 currentTime; 00274 float64 frameTime; 00275 int32 frameNumber; 00276 bool useFixRate; 00277 float32 fixFrameRate; 00278 00279 float32 maxFrameRate; 00280 float64 lastCurrentTime; 00281 float64 invMaxFrameRate; 00282 bool useMaxRate; 00283 00284 // values to hold real frame information if we use fix frame rate 00285 float64 realFrameTime; 00286 00287 // methods 00288 void _addToFrameHistory(float64 duration); 00289 float64 _getFilteredFrameDuration() const; 00290 float64 _getExactFrameDuration(); 00291 00292 // helping variables to find out the frame rate 00293 float64 sourceStartValue; 00294 float64 sourceLastValue; 00295 float64 sourceLastRealValue; 00296 00297 // Next time when the clock has to be synced with system time 00298 float64 nextSyncTime; 00299 uint32 syncInterval; 00300 00301 }; 00302 00303 }; // end namespace 00304 #endif