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 //------------------------------------------------------------------------- 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 //------------------------------------------------------------------------- 111 Engine::~Engine() 112 { 113 } 114 115 /** 116 * Load resources 117 */ 118 void Engine::LoadResources() 119 { 120 renderer_.Init(); 121 renderer_.Bind( &tap_camera_ ); 122 } 123 124 /** 125 * Unload resources 126 */ 127 void Engine::UnloadResources() 128 { 129 renderer_.Unload(); 130 } 131 132 /** 133 * Initialize an EGL context for the current display. 134 */ 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 */ 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 */ 198 void Engine::TermDisplay() 199 { 200 gl_context_->Suspend(); 201 202 } 203 204 void Engine::TrimMemory() 205 { 206 LOGI( "Trimming memory" ); 207 gl_context_->Invalidate(); 208 } 209 /** 210 * Process the next input event. 211 */ 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 */ 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 //------------------------------------------------------------------------- 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 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 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 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 //------------------------------------------------------------------------- 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 383 bool Engine::IsReady() 384 { 385 if( has_focus_ ) 386 return true; 387 388 return false; 389 } 390 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 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 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 */ 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