1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 3 <html xmlns="http://www.w3.org/1999/xhtml"> 4 <head> 5 <meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/> 6 <meta http-equiv="X-UA-Compatible" content="IE=9"/> 7 <meta name="generator" content="Doxygen 1.8.5"/> 8 <title>NDK Programmer's Guide: Teapot</title> 9 <link href="tabs.css" rel="stylesheet" type="text/css"/> 10 <script type="text/javascript" src="jquery.js"></script> 11 <script type="text/javascript" src="dynsections.js"></script> 12 <link href="navtree.css" rel="stylesheet" type="text/css"/> 13 <script type="text/javascript" src="resize.js"></script> 14 <script type="text/javascript" src="navtree.js"></script> 15 <script type="text/javascript"> 16 $(document).ready(initResizable); 17 $(window).load(resizeHeight); 18 </script> 19 <link href="doxygen.css" rel="stylesheet" type="text/css" /> 20 </head> 21 <body> 22 <div id="top"><!-- do not remove this div, it is closed by doxygen! --> 23 <div id="titlearea"> 24 <table cellspacing="0" cellpadding="0"> 25 <tbody> 26 <tr style="height: 56px;"> 27 <td style="padding-left: 0.5em;"> 28 <div id="projectname">NDK Programmer's Guide 29 </div> 30 </td> 31 </tr> 32 </tbody> 33 </table> 34 </div> 35 <!-- end header part --> 36 <!-- Generated by Doxygen 1.8.5 --> 37 </div><!-- top --> 38 <div id="side-nav" class="ui-resizable side-nav-resizable"> 39 <div id="nav-tree"> 40 <div id="nav-tree-contents"> 41 <div id="nav-sync" class="sync"></div> 42 </div> 43 </div> 44 <div id="splitbar" style="-moz-user-select:none;" 45 class="ui-resizable-handle"> 46 </div> 47 </div> 48 <script type="text/javascript"> 49 $(document).ready(function(){initNavTree('md_2__samples_samples-teapot.html','') 50 ;}); 51 </script> 52 <div id="doc-content"> 53 <div class="header"> 54 <div class="headertitle"> 55 <div class="title">Teapot </div> </div> 56 </div><!--header--> 57 <div class="contents"> 58 <div class="textblock"><p>This sample uses the OpenGL library to render the 59 iconic <a 60 href="http://math.hws.edu/bridgeman/courses/324/s06/doc/opengl.html#basic">Utah 61 teapot</a>. It particularly showcases the <code>ndk_helper</code> helper class, 62 a collection of native helper functions required for implementing games and 63 similar applications as native applications. This class provides:</p> 64 <ul> 65 <li>an abstraction layer that handles certain NDK-specific behaviors (e.g., 66 <code>GLContext</code>).</li> 67 <li>some helper functions that are useful but not present in the NDK, itself 68 (e.g., tap detection).</li> 69 <li>wrappers for JNI calls for certain platform features (e.g., texture 70 loading).</li> 71 </ul> 72 <h3>AndroidManifest.xml</h3> 73 <p>The activity declaration here is not <code>NativeActivity</code> itself, but 74 a sublass: <code>TeapotNativeActivity</code>.</p> 75 <pre class="fragment"> <activity 76 android:name="com.sample.teapot.TeapotNativeActivity" 77 android:label="@string/app_name" 78 android:configChanges="orientation|keyboardHidden"> 79 </pre><p>The name of the <code>.so</code> file is 80 <code>libTeapotNativeActivity.so</code>; the <code>lib</code> and 81 <code>.so</code> are stripped off from the value assigned to 82 <code>android:value</code>.</p> 83 <pre class="fragment"> <meta-data android:name="android.app.lib_name" 84 android:value="TeapotNativeActivity" /> 85 </pre><h3><code>Application.mk</code></h3> 86 <p>Define the minimum level of Android API Level support.</p> 87 <pre class="fragment">APP_PLATFORM := android-9 88 </pre><p>Build for all supported architectures.</p> 89 <pre class="fragment">APP_ABI := all 90 </pre><p>Specify the <a 91 href="./md_3__key__topics__libraries__c_p_l_u_s_p_l_u_s-_s_u_p_p_o_r_t.html">C++ 92 runtime support library</a> to use. </p> 93 <pre class="fragment">APP_STL := stlport_static 94 </pre><h3>Java-side implementation: TeapotNativeActivity.java</h3> 95 <p>This file handles activity lifecycle events, as well as displaying text on 96 the screen.</p> 97 <pre class="fragment">// Our popup window, you will call it from your C/C++ 98 code later 99 100 101 void setImmersiveSticky() { 102 View decorView = getWindow().getDecorView(); 103 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN 104 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 105 | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 106 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 107 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 108 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 109 } 110 </pre><h3>Native-side implementation: <code>TeapotRenderer.h/.cpp</code></h3> 111 <p>This code does the actual rendering of the teapot. It uses 112 <code>ndk_helper</code> for matrix calculation, and to reposition the camera 113 based on where the user taps:</p> 114 <pre class="fragment">ndk_helper::Mat4 mat_projection_; 115 ndk_helper::Mat4 mat_view_; 116 ndk_helper::Mat4 mat_model_; 117 118 119 ndk_helper::TapCamera* camera_; 120 </pre><h3>Native-side implementation: <code>TeapotNativeActivity.cpp</code></h3> 121 <p>Include <code>ndk_helper</code> in your native source file, and define the 122 helper-class name:</p> 123 <pre class="fragment">#include "NDKHelper.h" 124 125 126 //------------------------------------------------------------------------- 127 //Preprocessor 128 //------------------------------------------------------------------------- 129 #define HELPER_CLASS_NAME "com/sample/helper/NDKHelper" //Class name of helper 130 function 131 </pre><p>The first use of the <code>ndk_helper</code> class is to handle the 132 EGL-related lifecycle, associating EGL context states (created/lost) with 133 Android lifecycle events. It enables the application to preserve context 134 information so that a destroyed activity can be restored. This is useful, for 135 example, when the target machine is rotated (causing an activity to be 136 destroyed, then immediately restored in the new orientation), or when the lock 137 screen appears.</p> 138 <pre class="fragment">ndk_helper::GLContext* gl_context_; // handles 139 EGL-related lifecycle. 140 </pre><p>Next, <code>ndk_helper</code> provides touch control.</p> 141 <pre class="fragment">ndk_helper::DoubletapDetector doubletap_detector_; 142 ndk_helper::PinchDetector pinch_detector_; 143 ndk_helper::DragDetector drag_detector_; 144 ndk_helper::PerfMonitor monitor_; 145 </pre><p>And camera control (openGL view frustum).</p> 146 <pre class="fragment">ndk_helper::TapCamera tap_camera_; 147 </pre><p>As in the native-activity sample, the application prepares to use the 148 sensors, using the native APIs provided in the NDK.</p> 149 <pre class="fragment">ASensorManager* sensor_manager_; 150 const ASensor* accelerometer_sensor_; 151 ASensorEventQueue* sensor_event_queue_; 152 </pre><p>The following functions are called in response to various Android 153 lifecycle events and EGL context state changes, using various functionalities 154 provided by <code>ndk_helper</code> via the <code>Engine</code> class.</p> 155 <pre class="fragment">void LoadResources(); 156 void UnloadResources(); 157 void DrawFrame(); 158 void TermDisplay(); 159 void TrimMemory(); 160 bool IsReady(); 161 </pre><p>This function calls back to the Java side to update the UI display.</p> 162 <pre class="fragment">void Engine::ShowUI() 163 { 164 JNIEnv *jni; 165 app_->activity->vm->AttachCurrentThread( &jni, NULL ); 166 167 168 //Default class retrieval 169 jclass clazz = jni->GetObjectClass( app_->activity->clazz ); 170 jmethodID methodID = jni->GetMethodID( clazz, "showUI", "()V" ); 171 jni->CallVoidMethod( app_->activity->clazz, methodID ); 172 173 174 app_->activity->vm->DetachCurrentThread(); 175 return; 176 } 177 </pre><p>And this one calls back to the Java side to draw a text box 178 superimposed on the screen rendered on the native side, and showing frame 179 count.</p> 180 <pre class="fragment">void Engine::UpdateFPS( float fFPS ) 181 { 182 JNIEnv *jni; 183 app_->activity->vm->AttachCurrentThread( &jni, NULL ); 184 185 186 //Default class retrieval 187 jclass clazz = jni->GetObjectClass( app_->activity->clazz ); 188 jmethodID methodID = jni->GetMethodID( clazz, "updateFPS", "(F)V" ); 189 jni->CallVoidMethod( app_->activity->clazz, methodID, fFPS ); 190 191 192 app_->activity->vm->DetachCurrentThread(); 193 return; 194 } 195 </pre><p>The application gets the system clock and supplies it to the renderer 196 for time-based animation based on real-time clock. For example, calculating 197 momentum, where speed declines as a function of time.</p> 198 <pre class="fragment">renderer_.Update( monitor_.GetCurrentTime() ); 199 </pre><p>Having earlier been set up to preserve context information, the 200 application now checks whether <code>GLcontext</code> is still valid. If not, 201 <code>ndk-helper</code> swaps the buffer, reinstantiating the GL context.</p> 202 <pre class="fragment">if( EGL_SUCCESS != gl_context_->Swap() ) // swaps 203 buffer. 204 </pre><p>The program passes touch-motion events to the gesture detector defined 205 in the <code>ndk_helper</code> class. The gesture detector tracks multitouch 206 gestures, such as pinch-and-drag, and sends a notification when triggered by 207 any of these events.</p> 208 <pre class="fragment">if( AInputEvent_getType( event ) == 209 AINPUT_EVENT_TYPE_MOTION ) 210 { 211 ndk_helper::GESTURE_STATE doubleTapState = 212 eng->doubletap_detector_.Detect( event ); 213 ndk_helper::GESTURE_STATE dragState = eng->drag_detector_.Detect( event 214 ); 215 ndk_helper::GESTURE_STATE pinchState = eng->pinch_detector_.Detect( 216 event ); 217 218 219 //Double tap detector has a priority over other detectors 220 if( doubleTapState == ndk_helper::GESTURE_STATE_ACTION ) 221 { 222 //Detect double tap 223 eng->tap_camera_.Reset( true ); 224 } 225 else 226 { 227 //Handle pinch state 228 if( pinchState & ndk_helper::GESTURE_STATE_START ) 229 { 230 //Start new pinch 231 ndk_helper::Vec2 v1; 232 ndk_helper::Vec2 v2; 233 eng->pinch_detector_.GetPointers( v1, v2 ); 234 </pre><p><code>ndk_helper</code> also provides access to a vector-math library 235 (<code>vecmath.h</code>), using it here to transform touch coordinates.</p> 236 <pre class="fragment">void Engine::TransformPosition( ndk_helper::Vec2& vec 237 ) { vec = ndk_helper::Vec2( 2.0f, 2.0f ) * vec / ndk_helper::Vec2( 238 gl_context_->GetScreenWidth(), gl_context_->GetScreenHeight() ) 239 240 ndk_helper::Vec2( 1.f, 1.f ); }</li></pre> 241 </ul> 242 <p><code>HandleCmd()</code> handles commands posted from the 243 android_native_app_glue library. For more information about what the messages 244 mean, refer to the comments in the <code>android_native_app_glue.h</code> and 245 <code>.c</code> source files.</p> 246 247 <pre class="fragment">void Engine::HandleCmd( struct android_app* app, int32_t 248 cmd ) { Engine* eng = (Engine*) app->userData; switch( cmd ) { case 249 APP_CMD_SAVE_STATE: break; case APP_CMD_INIT_WINDOW: // The window is being 250 shown, get it ready. if( app->window != NULL )</li></pre> 251 252 <p><code>ndk_helper</code> posts APP_CMD_INIT_WINDOW when android_app_glue 253 receives an <code>onNativeWindowCreated()</code> callback from the system. 254 Applications can normally perform window initializations, such as EGL 255 initialization. They do this outside of the activity lifecycle, since the 256 activity is not yet ready.</p> 257 <pre class="fragment">ndk_helper::JNIHelper::Init( state->activity, 258 HELPER_CLASS_NAME ); 259 260 261 state->userData = &g_engine; 262 state->onAppCmd = Engine::HandleCmd; 263 state->onInputEvent = Engine::HandleInput; </pre> </div></div><!-- contents 264 --> 265 </div><!-- doc-content --> 266 <!-- start footer part --> 267 <div id="nav-path" class="navpath"><!-- id is needed for treeview function! --> 268 <ul> 269 <li class="footer">Generated on Wed Jun 25 2014 00:51:19 for NDK 270 Programmer's Guide by 271 <a href="http://www.doxygen.org/index.html"> 272 <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.5 </li> 273 </ul> 274 </div> 275 </body> 276 </html> 277