• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //--------------------------------------------------------------------------------
18 // Include files
19 //--------------------------------------------------------------------------------
20 #include <jni.h>
21 #include <errno.h>
22 
23 #include <vector>
24 #include <EGL/egl.h>
25 #include <GLES/gl.h>
26 
27 #include <android/sensor.h>
28 #include <android/log.h>
29 #include <android_native_app_glue.h>
30 #include <android/native_window_jni.h>
31 #include <cpu-features.h>
32 
33 #include "MoreTeapotsRenderer.h"
34 
35 //-------------------------------------------------------------------------
36 //Preprocessor
37 //-------------------------------------------------------------------------
38 #define HELPER_CLASS_NAME "com/sample/helper/NDKHelper" //Class name of helper function
39 //-------------------------------------------------------------------------
40 //Constants
41 //-------------------------------------------------------------------------
42 const int32_t NUM_TEAPOTS_X = 8;
43 const int32_t NUM_TEAPOTS_Y = 8;
44 const int32_t NUM_TEAPOTS_Z = 8;
45 
46 //-------------------------------------------------------------------------
47 //Shared state for our app.
48 //-------------------------------------------------------------------------
49 struct android_app;
50 class Engine
51 {
52     MoreTeapotsRenderer renderer_;
53 
54     ndk_helper::GLContext* gl_context_;
55 
56     bool initialized_resources_;
57     bool has_focus_;
58 
59     ndk_helper::DoubletapDetector doubletap_detector_;
60     ndk_helper::PinchDetector pinch_detector_;
61     ndk_helper::DragDetector drag_detector_;
62     ndk_helper::PerfMonitor monitor_;
63 
64     ndk_helper::TapCamera tap_camera_;
65 
66     android_app* app_;
67 
68     ASensorManager* sensor_manager_;
69     const ASensor* accelerometer_sensor_;
70     ASensorEventQueue* sensor_event_queue_;
71 
72     void UpdateFPS( float fps );
73     void ShowUI();
74     void TransformPosition( ndk_helper::Vec2& vec );
75 
76 public:
77     static void HandleCmd( struct android_app* app,
78             int32_t cmd );
79     static int32_t HandleInput( android_app* app,
80             AInputEvent* event );
81 
82     Engine();
83     ~Engine();
84     void SetState( android_app* state );
85     int InitDisplay();
86     void LoadResources();
87     void UnloadResources();
88     void DrawFrame();
89     void TermDisplay();
90     void TrimMemory();
91     bool IsReady();
92 
93     void UpdatePosition( AInputEvent* event,
94             int32_t index,
95             float& x,
96             float& y );
97 
98     void InitSensors();
99     void ProcessSensors( int32_t id );
100     void SuspendSensors();
101     void ResumeSensors();
102 };
103 
104 //-------------------------------------------------------------------------
105 //Ctor
106 //-------------------------------------------------------------------------
Engine()107 Engine::Engine() :
108                 initialized_resources_( false ),
109                 has_focus_( false ),
110                 app_( NULL ),
111                 sensor_manager_( NULL ),
112                 accelerometer_sensor_( NULL ),
113                 sensor_event_queue_( NULL )
114 {
115     gl_context_ = ndk_helper::GLContext::GetInstance();
116 }
117 
118 //-------------------------------------------------------------------------
119 //Dtor
120 //-------------------------------------------------------------------------
~Engine()121 Engine::~Engine()
122 {
123 }
124 
125 /**
126  * Load resources
127  */
LoadResources()128 void Engine::LoadResources()
129 {
130     renderer_.Init( NUM_TEAPOTS_X, NUM_TEAPOTS_Y, NUM_TEAPOTS_Z );
131     renderer_.Bind( &tap_camera_ );
132 }
133 
134 /**
135  * Unload resources
136  */
UnloadResources()137 void Engine::UnloadResources()
138 {
139     renderer_.Unload();
140 }
141 
142 /**
143  * Initialize an EGL context for the current display.
144  */
InitDisplay()145 int Engine::InitDisplay()
146 {
147     if( !initialized_resources_ )
148     {
149         gl_context_->Init( app_->window );
150         LoadResources();
151         initialized_resources_ = true;
152     }
153     else
154     {
155         // initialize OpenGL ES and EGL
156         if( EGL_SUCCESS != gl_context_->Resume( app_->window ) )
157         {
158             UnloadResources();
159             LoadResources();
160         }
161     }
162 
163     ShowUI();
164 
165     // Initialize GL state.
166     glEnable( GL_CULL_FACE );
167     glEnable( GL_DEPTH_TEST );
168     glDepthFunc( GL_LEQUAL );
169 
170     //Note that screen size might have been changed
171     glViewport( 0, 0, gl_context_->GetScreenWidth(), gl_context_->GetScreenHeight() );
172     renderer_.UpdateViewport();
173 
174     tap_camera_.SetFlip( 1.f, -1.f, -1.f );
175     tap_camera_.SetPinchTransformFactor( 10.f, 10.f, 8.f );
176 
177     return 0;
178 }
179 
180 /**
181  * Just the current frame in the display.
182  */
DrawFrame()183 void Engine::DrawFrame()
184 {
185     float fps;
186     if( monitor_.Update( fps ) )
187     {
188         UpdateFPS( fps );
189     }
190     double dTime = monitor_.GetCurrentTime();
191     renderer_.Update( dTime );
192 
193     // Just fill the screen with a color.
194     glClearColor( 0.5f, 0.5f, 0.5f, 1.f );
195     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
196     renderer_.Render();
197 
198     // Swap
199     if( EGL_SUCCESS != gl_context_->Swap() )
200     {
201         UnloadResources();
202         LoadResources();
203     }
204 }
205 
206 /**
207  * Tear down the EGL context currently associated with the display.
208  */
TermDisplay()209 void Engine::TermDisplay()
210 {
211     gl_context_->Suspend();
212 
213 }
214 
TrimMemory()215 void Engine::TrimMemory()
216 {
217     LOGI( "Trimming memory" );
218     gl_context_->Invalidate();
219 }
220 /**
221  * Process the next input event.
222  */
HandleInput(android_app * app,AInputEvent * event)223 int32_t Engine::HandleInput( android_app* app,
224         AInputEvent* event )
225 {
226     Engine* eng = (Engine*) app->userData;
227     if( AInputEvent_getType( event ) == AINPUT_EVENT_TYPE_MOTION )
228     {
229         ndk_helper::GESTURE_STATE doubleTapState = eng->doubletap_detector_.Detect( event );
230         ndk_helper::GESTURE_STATE dragState = eng->drag_detector_.Detect( event );
231         ndk_helper::GESTURE_STATE pinchState = eng->pinch_detector_.Detect( event );
232 
233         //Double tap detector has a priority over other detectors
234         if( doubleTapState == ndk_helper::GESTURE_STATE_ACTION )
235         {
236             //Detect double tap
237             eng->tap_camera_.Reset( true );
238         }
239         else
240         {
241             //Handle drag state
242             if( dragState & ndk_helper::GESTURE_STATE_START )
243             {
244                 //Otherwise, start dragging
245                 ndk_helper::Vec2 v;
246                 eng->drag_detector_.GetPointer( v );
247                 eng->TransformPosition( v );
248                 eng->tap_camera_.BeginDrag( v );
249             }
250             else if( dragState & ndk_helper::GESTURE_STATE_MOVE )
251             {
252                 ndk_helper::Vec2 v;
253                 eng->drag_detector_.GetPointer( v );
254                 eng->TransformPosition( v );
255                 eng->tap_camera_.Drag( v );
256             }
257             else if( dragState & ndk_helper::GESTURE_STATE_END )
258             {
259                 eng->tap_camera_.EndDrag();
260             }
261 
262             //Handle pinch state
263             if( pinchState & ndk_helper::GESTURE_STATE_START )
264             {
265                 //Start new pinch
266                 ndk_helper::Vec2 v1;
267                 ndk_helper::Vec2 v2;
268                 eng->pinch_detector_.GetPointers( v1, v2 );
269                 eng->TransformPosition( v1 );
270                 eng->TransformPosition( v2 );
271                 eng->tap_camera_.BeginPinch( v1, v2 );
272             }
273             else if( pinchState & ndk_helper::GESTURE_STATE_MOVE )
274             {
275                 //Multi touch
276                 //Start new pinch
277                 ndk_helper::Vec2 v1;
278                 ndk_helper::Vec2 v2;
279                 eng->pinch_detector_.GetPointers( v1, v2 );
280                 eng->TransformPosition( v1 );
281                 eng->TransformPosition( v2 );
282                 eng->tap_camera_.Pinch( v1, v2 );
283             }
284         }
285         return 1;
286     }
287     return 0;
288 }
289 
290 /**
291  * Process the next main command.
292  */
HandleCmd(struct android_app * app,int32_t cmd)293 void Engine::HandleCmd( struct android_app* app,
294         int32_t cmd )
295 {
296     Engine* eng = (Engine*) app->userData;
297     switch( cmd )
298     {
299     case APP_CMD_SAVE_STATE:
300         break;
301     case APP_CMD_INIT_WINDOW:
302         // The window is being shown, get it ready.
303         if( app->window != NULL )
304         {
305             eng->InitDisplay();
306             eng->DrawFrame();
307         }
308         break;
309     case APP_CMD_TERM_WINDOW:
310         // The window is being hidden or closed, clean it up.
311         eng->TermDisplay();
312         eng->has_focus_ = false;
313         break;
314     case APP_CMD_STOP:
315         break;
316     case APP_CMD_GAINED_FOCUS:
317         eng->ResumeSensors();
318         //Start animation
319         eng->has_focus_ = true;
320         break;
321     case APP_CMD_LOST_FOCUS:
322         eng->SuspendSensors();
323         // Also stop animating.
324         eng->has_focus_ = false;
325         eng->DrawFrame();
326         break;
327     case APP_CMD_LOW_MEMORY:
328         //Free up GL resources
329         eng->TrimMemory();
330         break;
331     }
332 }
333 
334 //-------------------------------------------------------------------------
335 //Sensor handlers
336 //-------------------------------------------------------------------------
InitSensors()337 void Engine::InitSensors()
338 {
339     sensor_manager_ = ASensorManager_getInstance();
340     accelerometer_sensor_ = ASensorManager_getDefaultSensor( sensor_manager_,
341             ASENSOR_TYPE_ACCELEROMETER );
342     sensor_event_queue_ = ASensorManager_createEventQueue( sensor_manager_, app_->looper,
343             LOOPER_ID_USER, NULL, NULL );
344 }
345 
ProcessSensors(int32_t id)346 void Engine::ProcessSensors( int32_t id )
347 {
348     // If a sensor has data, process it now.
349     if( id == LOOPER_ID_USER )
350     {
351         if( accelerometer_sensor_ != NULL )
352         {
353             ASensorEvent event;
354             while( ASensorEventQueue_getEvents( sensor_event_queue_, &event, 1 ) > 0 )
355             {
356             }
357         }
358     }
359 }
360 
ResumeSensors()361 void Engine::ResumeSensors()
362 {
363     // When our app gains focus, we start monitoring the accelerometer.
364     if( accelerometer_sensor_ != NULL )
365     {
366         ASensorEventQueue_enableSensor( sensor_event_queue_, accelerometer_sensor_ );
367         // We'd like to get 60 events per second (in us).
368         ASensorEventQueue_setEventRate( sensor_event_queue_, accelerometer_sensor_,
369                 (1000L / 60) * 1000 );
370     }
371 }
372 
SuspendSensors()373 void Engine::SuspendSensors()
374 {
375     // When our app loses focus, we stop monitoring the accelerometer.
376     // This is to avoid consuming battery while not being used.
377     if( accelerometer_sensor_ != NULL )
378     {
379         ASensorEventQueue_disableSensor( sensor_event_queue_, accelerometer_sensor_ );
380     }
381 }
382 
383 //-------------------------------------------------------------------------
384 //Misc
385 //-------------------------------------------------------------------------
SetState(android_app * state)386 void Engine::SetState( android_app* state )
387 {
388     app_ = state;
389     doubletap_detector_.SetConfiguration( app_->config );
390     drag_detector_.SetConfiguration( app_->config );
391     pinch_detector_.SetConfiguration( app_->config );
392 }
393 
IsReady()394 bool Engine::IsReady()
395 {
396     if( has_focus_ )
397         return true;
398 
399     return false;
400 }
401 
TransformPosition(ndk_helper::Vec2 & vec)402 void Engine::TransformPosition( ndk_helper::Vec2& vec )
403 {
404     vec = ndk_helper::Vec2( 2.0f, 2.0f ) * vec
405             / ndk_helper::Vec2( gl_context_->GetScreenWidth(), gl_context_->GetScreenHeight() )
406             - ndk_helper::Vec2( 1.f, 1.f );
407 }
408 
ShowUI()409 void Engine::ShowUI()
410 {
411     JNIEnv *jni;
412     app_->activity->vm->AttachCurrentThread( &jni, NULL );
413 
414     //Default class retrieval
415     jclass clazz = jni->GetObjectClass( app_->activity->clazz );
416     jmethodID methodID = jni->GetMethodID( clazz, "showUI", "()V" );
417     jni->CallVoidMethod( app_->activity->clazz, methodID );
418 
419     app_->activity->vm->DetachCurrentThread();
420     return;
421 }
422 
UpdateFPS(float fps)423 void Engine::UpdateFPS( float fps )
424 {
425     JNIEnv *jni;
426     app_->activity->vm->AttachCurrentThread( &jni, NULL );
427 
428     //Default class retrieval
429     jclass clazz = jni->GetObjectClass( app_->activity->clazz );
430     jmethodID methodID = jni->GetMethodID( clazz, "updateFPS", "(F)V" );
431     jni->CallVoidMethod( app_->activity->clazz, methodID, fps );
432 
433     app_->activity->vm->DetachCurrentThread();
434     return;
435 }
436 
437 Engine g_engine;
438 
439 /**
440  * This is the main entry point of a native application that is using
441  * android_native_app_glue.  It runs in its own thread, with its own
442  * event loop for receiving input events and doing other things.
443  */
android_main(android_app * state)444 void android_main( android_app* state )
445 {
446     app_dummy();
447 
448     g_engine.SetState( state );
449 
450     //Init helper functions
451     ndk_helper::JNIHelper::GetInstance()->Init( state->activity, HELPER_CLASS_NAME );
452 
453     state->userData = &g_engine;
454     state->onAppCmd = Engine::HandleCmd;
455     state->onInputEvent = Engine::HandleInput;
456 
457 #ifdef USE_NDK_PROFILER
458     monstartup("libMoreTeapotsNativeActivity.so");
459 #endif
460 
461     // Prepare to monitor accelerometer
462     g_engine.InitSensors();
463 
464     // loop waiting for stuff to do.
465     while( 1 )
466     {
467         // Read all pending events.
468         int id;
469         int events;
470         android_poll_source* source;
471 
472         // If not animating, we will block forever waiting for events.
473         // If animating, we loop until all events are read, then continue
474         // to draw the next frame of animation.
475         while( (id = ALooper_pollAll( g_engine.IsReady() ? 0 : -1, NULL, &events, (void**) &source ))
476                 >= 0 )
477         {
478             // Process this event.
479             if( source != NULL )
480                 source->process( state, source );
481 
482             g_engine.ProcessSensors( id );
483 
484             // Check if we are exiting.
485             if( state->destroyRequested != 0 )
486             {
487                 g_engine.TermDisplay();
488                 return;
489             }
490         }
491 
492         if( g_engine.IsReady() )
493         {
494             // Drawing is throttled to the screen update rate, so there
495             // is no need to do timing here.
496             g_engine.DrawFrame();
497         }
498     }
499 }
500 
501