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