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