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//------------------------------------------------------------------------- 42const int32_t NUM_TEAPOTS_X = 8; 43const int32_t NUM_TEAPOTS_Y = 8; 44const int32_t NUM_TEAPOTS_Z = 8; 45 46//------------------------------------------------------------------------- 47//Shared state for our app. 48//------------------------------------------------------------------------- 49struct android_app; 50class 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 76public: 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//------------------------------------------------------------------------- 107Engine::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//------------------------------------------------------------------------- 121Engine::~Engine() 122{ 123} 124 125/** 126 * Load resources 127 */ 128void 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 */ 137void Engine::UnloadResources() 138{ 139 renderer_.Unload(); 140} 141 142/** 143 * Initialize an EGL context for the current display. 144 */ 145int 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 */ 183void 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 */ 209void Engine::TermDisplay() 210{ 211 gl_context_->Suspend(); 212 213} 214 215void Engine::TrimMemory() 216{ 217 LOGI( "Trimming memory" ); 218 gl_context_->Invalidate(); 219} 220/** 221 * Process the next input event. 222 */ 223int32_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 */ 293void 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//------------------------------------------------------------------------- 337void 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 346void 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 361void 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 373void 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//------------------------------------------------------------------------- 386void 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 394bool Engine::IsReady() 395{ 396 if( has_focus_ ) 397 return true; 398 399 return false; 400} 401 402void 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 409void 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 423void 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 437Engine 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 */ 444void 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