1 /*
2   -----------------------------------------------------------------------------
3   This source file is part of OGRE
4   (Object-oriented Graphics Rendering Engine)
5   For the latest info, see http://www.ogre3d.org/
6 
7 Copyright (c) 2000-2014 Torus Knot Software Ltd
8 
9   Permission is hereby granted, free of charge, to any person obtaining a copy
10   of this software and associated documentation files (the "Software"), to deal
11   in the Software without restriction, including without limitation the rights
12   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13   copies of the Software, and to permit persons to whom the Software is
14   furnished to do so, subject to the following conditions:
15 
16   The above copyright notice and this permission notice shall be included in
17   all copies or substantial portions of the Software.
18 
19   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25   THE SOFTWARE.
26   -----------------------------------------------------------------------------
27 */
28 
29 #ifndef __TestContext_H__
30 #define __TestContext_H__
31 
32 #include "VisualTest.h"
33 #include "SampleContext.h"
34 #include "SamplePlugin.h"
35 
36 #include <iostream> // for Apple
37 
38 class TestBatch;
39 using namespace Ogre;
40 
41 typedef std::map<String, OgreBites::SamplePlugin *> PluginMap;
42 
43 /** The common environment that all of the tests run in */
44 class TestContext : public OgreBites::SampleContext
45 {
46  public:
47 
48     TestContext(int argc = 0, char** argv = 0);
49     virtual ~TestContext();
50 
51     /** Does basic setup for the context */
52     virtual void setup();
53 
54     /** Frame listener callback, handles updating of the tests at the start of frames
55      *        @param evt The frame event (passed in for the framelistener) */
56     virtual bool frameStarted(const FrameEvent& evt);
57 
58     /** Frame listener callback, handles updating of the tests at the end of frames
59      *        @param evt The frame event (passed in for the framelistener) */
60     virtual bool frameEnded(const FrameEvent& evt);
61 
62     /** Runs a given test or sample
63      *        @param s The OgreBites::Sample to run
64      *        @remarks If s is a VisualTest, then timing and rand will be setup for
65      *            determinism. */
66     virtual void runSample(OgreBites::Sample* s);
67 
68     /** Loads test plugins
69      *        @param set The name of the test set to load
70      *        @return The initial tets or sample to run */
71     OgreBites::Sample* loadTests(String set);
72 
73     /** Setup the Root */
74     virtual void createRoot();
75 
76     /** Start it up */
77     virtual void go(OgreBites::Sample* initialSample = 0);
78 
79     /** Handles the config dialog */
80     virtual bool oneTimeConfig();
81 
82     /** Set up directories for the tests to output to */
83     virtual void setupDirectories(String batchName);
84 
85     /** Called after tests successfully complete, generates output */
86     virtual void finishedTests();
87 
88     /** Sets the timstep value
89      *        @param timestep The time to simulate elapsed between each frame
90      *        @remarks Use with care! Screenshots produced at different timesteps
91      *            will almost certainly turn out different. */
92     void setTimestep(Real timestep);
93 
94     /** Gets the current timestep value */
95     Real getTimestep();
96 
getCurrentTest()97     VisualTest* getCurrentTest() { return mCurrentTest; }
98 
99     /// Returns whether the entire test was successful or not.
wasSuccessful()100     bool wasSuccessful() const {
101         return mSuccess;
102     }
103 
104  protected:
105     bool mSuccess;
106 
107     /// The timestep
108     Real mTimestep;
109 
110     /// Path to the test plugin directory
111     String mPluginDirectory;
112 
113     /// List of available test sets
114     std::map<String, StringVector> mTestSets;
115 
116     /// The tests to be run
117     std::deque<OgreBites::Sample*> mTests;
118 
119     /// Path to the output directory for the running test
120     String mOutputDir;
121 
122     /// Path to the reference set location
123     String mReferenceSetPath;
124 
125     /// The active test (0 if none is active)
126     VisualTest* mCurrentTest;
127 
128     /// The current frame of a running test
129     unsigned int mCurrentFrame;
130 
131     /// Info about the running batch of tests
132     TestBatch* mBatch;
133 
134     // A structure to map plugin names to class types
135     PluginMap mPluginNameMap;
136 
137     // command line options
138     // Is a reference set being generated?
139     bool mReferenceSet;
140     // Should html output be created?
141     bool mGenerateHtml;
142     // Force the config dialog
143     bool mForceConfig;
144     // Do not confine mouse to window
145     bool mNoGrabMouse;
146     // Show usage details
147     bool mHelp;
148     // Render system to use
149     String mRenderSystemName;
150     // Optional name for this batch
151     String mBatchName;
152     // Set to compare against
153     String mCompareWith;
154     // Optional comment
155     String mComment;
156     // Name of the test set to use
157     String mTestSetName;
158     // Location to output a test summary (used for CTest)
159     String mSummaryOutputDir;
160 };
161 
162 #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS && defined(__OBJC__)
163 #import <UIKit/UIKit.h>
164 #import <QuartzCore/QuartzCore.h>
165 
166 @interface AppDelegate : NSObject <UIApplicationDelegate>
167 {
168     TestContext *tc;
169 
170     CADisplayLink *mDisplayLink;
171     NSDate* mDate;
172     NSTimeInterval mLastFrameTime;
173 }
174 
175 - (void)go;
176 - (void)renderOneFrame:(id)sender;
177 
178 @property (nonatomic) NSTimeInterval mLastFrameTime;
179 
180                       @end
181 
182                       @implementation AppDelegate
183 
184                       @dynamic mLastFrameTime;
185 
186                       - (NSTimeInterval)mLastFrameTime
187 {
188     return mLastFrameTime;
189 }
190 
191 - (void)setLastFrameTime:(NSTimeInterval)frameInterval
192 {
193     // Frame interval defines how many display frames must pass between each time the
194     // display link fires. The display link will only fire 30 times a second when the
195     // frame internal is two on a display that refreshes 60 times a second. The default
196     // frame interval setting of one will fire 60 times a second when the display refreshes
197     // at 60 times a second. A frame interval setting of less than one results in undefined
198     // behavior.
199     if (frameInterval >= 1)
200     {
201         mLastFrameTime = frameInterval;
202     }
203 }
204 
205 - (void)go {
206 
207     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
208     NSArray * arguments = [[NSProcessInfo processInfo] arguments];
209     char *argv[[arguments count]+1];
210     int i = 0;
211     for (NSString *str in arguments)
212     {
213         argv[i++] = (char *)[str UTF8String];
214     }
215     argv[i] = NULL;
216 
217     try {
218         tc = new TestContext([arguments count], &argv[0]);
219         tc->go();
220 
221         Root::getSingleton().getRenderSystem()->_initRenderTargets();
222 
223         // Clear event times
224         Root::getSingleton().clearEventTimes();
catch(Exception & e)225     } catch( Exception& e ) {
226         std::cerr << "An exception has occurred: " <<
227             e.getFullDescription().c_str() << std::endl;
228     }
229 
230     [pool release];
231 }
232 
233 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
234 {
235     // Defaulting to 2 means that we run at 30 frames per second. For 60 frames, use a value of 1.
236     // 30 FPS is usually sufficient and results in lower power consumption.
237     mLastFrameTime = 2;
238     mDisplayLink = nil;
239 
240     [self go];
241 
242     return YES;
243 }
244 
245 - (void)applicationWillTerminate:(UIApplication *)application
246 {
247     tc->finishedTests();
248 }
249 
250 - (void)applicationDidBecomeActive:(UIApplication *)application
251 {
252     // Reset event times and reallocate the date and displaylink objects
253     Root::getSingleton().clearEventTimes();
254     mDate = [[NSDate alloc] init];
255     mLastFrameTime = 2; // Reset the timer
256 
257     mDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(renderOneFrame:)];
258     [mDisplayLink setFrameInterval:mLastFrameTime];
259     [mDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
260 }
261 
262 - (void)applicationWillResignActive:(UIApplication *)application
263 {
264     Root::getSingleton().saveConfig();
265 
266     [mDate release];
267     mDate = nil;
268 
269     [mDisplayLink invalidate];
270     mDisplayLink = nil;
271 }
272 
273 - (void)renderOneFrame:(id)sender
274 {
275     // NSTimeInterval is a simple typedef for double
276     NSTimeInterval currentFrameTime = -[mDate timeIntervalSinceNow];
277     NSTimeInterval differenceInSeconds = currentFrameTime - mLastFrameTime;
278     mLastFrameTime = currentFrameTime;
279 
280     dispatch_async(dispatch_get_main_queue(), ^(void)
281                    {
282                        Root::getSingleton().renderOneFrame((Real)differenceInSeconds);
283                    });
284 
285     if(Root::getSingletonPtr() && Root::getSingleton().isInitialised() && !tc->getCurrentTest())
286     {
287         tc->finishedTests();
288 
289         // Force the app to exit. Acts like a crash which isn't very elegant but good enough in this case.
290         exit(0);
291     }
292 }
293 
294 @end
295 
296 #endif // OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS && defined(__OBJC__)
297 
298 #endif
299