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