diff options
Diffstat (limited to 'docs/html-ndk/ndk/samples/sample_teapot.jd')
-rw-r--r-- | docs/html-ndk/ndk/samples/sample_teapot.jd | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/docs/html-ndk/ndk/samples/sample_teapot.jd b/docs/html-ndk/ndk/samples/sample_teapot.jd new file mode 100644 index 0000000..6d19de6 --- /dev/null +++ b/docs/html-ndk/ndk/samples/sample_teapot.jd @@ -0,0 +1,360 @@ +page.title=Sample: Teapot +@jd:body + +<div id="qv-wrapper"> + <div id="qv"> + <h2>On this page</h2> + + <ol> + <li><a href="#am">AndroidManifest.xml</a></li> + <li><a href="#ap">Application.mk</a></li> + <li><a href="#ji">Java-side Implementation</a></li> + <li><a href="#ni">Native-side Implementation</a></li> + </ol> + </li> + </ol> + </div> + </div> + +<p>The Teapot sample is located under in the {@code samples/Teapot/} directory, under the NDK +installation's root directory. This sample uses the OpenGL library to render the iconic +<a href="http://math.hws.edu/bridgeman/courses/324/s06/doc/opengl.html#basic">Utah +teapot</a>. In particular, it showcases the {@code ndk_helper} helper class, +a collection of native helper functions required for implementing games and +similar applications as native applications. This class provides:</p> + +<ul> +<li>An abstraction layer, {@code GLContext}, that handles certain NDK-specific behaviors.</li> +<li>Helper functions that are useful but not present in the NDK, such as tap detection.</li> +<li>Wrappers for JNI calls for platform features such as texture loading.</li> +</ul> + +<h2 id="am">AndroidManifest.xml</h2> +<p>The activity declaration here is not {@link android.app.NativeActivity} itself, but +a subclass of it: {@code TeapotNativeActivity}.</p> + +<pre class="no-pretty-print"> + <activity android:name="com.sample.teapot.TeapotNativeActivity" + android:label="@string/app_name" + android:configChanges="orientation|keyboardHidden"> +</pre> + +<p>Ultimately, the name of the shared-object file that the build system builds is +{@code libTeapotNativeActivity.so}. The build system adds the {@code lib} prefix and the {@code .so} +extension; neither is part of the value that the manifest originally assigns to +{@code android:value}.</p> + +<pre class="no-pretty-print"> + <meta-data android:name="android.app.lib_name" + android:value="TeapotNativeActivity" /> +</pre> + +<h2 id="ap">Application.mk</h2> +<p>An app that uses the {@link android.app.NativeActivity} framework class must not specify an +Android API level lower than 9, which introduced that class. For more information about the +{@link android.app.NativeActivity} class, see +<a href="{@docRoot}ndk/guides/concepts.html#naa">Native Activities and Applications</a>. +</p> + +<pre class="no-pretty-print"> +APP_PLATFORM := android-9 +</pre> + +<p>The next line tells the build system to build for all supported architectures.</p> +<pre class="no-pretty-print"> +APP_ABI := all +</pre> + +<p>Next, the file tells the build system which +<a href="{@docRoot}ndk/guides/cpp-support.html">C++ runtime support library</a> to use. </p> + +<pre class="no-pretty-print"> +APP_STL := stlport_static +</pre> + +<h2 id="ji">Java-side Implementation</h2> +<p>The {@code TeapotNativeActivity.java} file is located in +{@code samples/Teapot/src/com/sample/teapot}, under the NDK installation root directory. It handles +activity lifecycle events, and also enables the app to display text on the screen. The following +block of code is most important from the perspective of the native-side implementation: The native +code calls it to display a popup window for displaying text.</p> + +<pre class="no-pretty-print"> + +void setImmersiveSticky() { + View decorView = getWindow().getDecorView(); + decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); +} +</pre> + +<h2 id="ni">Native-side Implementation</h2> + +<p>This section explores the part of the Teapot app implemented in C++.</p> + +<h3>TeapotRenderer.h</h3> + +<p>These function calls perform the actual rendering of the teapot. It uses +{@code ndk_helper} for matrix calculation and to reposition the camera +based on where the user taps.</p> + +<pre class="no-pretty-print"> +ndk_helper::Mat4 mat_projection_; +ndk_helper::Mat4 mat_view_; +ndk_helper::Mat4 mat_model_; + + +ndk_helper::TapCamera* camera_; +</pre> + +<h3>TeapotNativeActivity.cpp</h3> + +<p>The following lines include {@code ndk_helper} in the native source file, and define the +helper-class name.</p> + +<pre class="no-pretty-print"> + +#include "NDKHelper.h" + +//------------------------------------------------------------------------- +//Preprocessor +//------------------------------------------------------------------------- +#define HELPER_CLASS_NAME "com/sample/helper/NDKHelper" //Class name of helper +function +</pre> + +<p>The first use of the {@code ndk_helper} class is to handle the +EGL-related lifecycle, associating EGL context states (created/lost) with +Android lifecycle events. The {@code ndk_helper} class enables the application to preserve context +information so that the system can restore a destroyed activity. This ability is useful, for +example, when the target machine is rotated (causing an activity to be +destroyed, then immediately restored in the new orientation), or when the lock +screen appears.</p> + +<pre class="no-pretty-print"> +ndk_helper::GLContext* gl_context_; // handles EGL-related lifecycle. +</pre> + +<p>Next, {@code ndk_helper} provides touch control.</p> + +<pre class="no-pretty-print"> +ndk_helper::DoubletapDetector doubletap_detector_; +ndk_helper::PinchDetector pinch_detector_; +ndk_helper::DragDetector drag_detector_; +ndk_helper::PerfMonitor monitor_; +</pre> + +<p>It also provides camera control (openGL view frustum).</p> + +<pre class="no-pretty-print"> +ndk_helper::TapCamera tap_camera_; +</pre> + +<p>The app then prepares to use the device's sensors, using the native APIs provided in the NDK.</p> + +<pre class="no-pretty-print"> +ASensorManager* sensor_manager_; +const ASensor* accelerometer_sensor_; +ASensorEventQueue* sensor_event_queue_; +</pre> + +<p>The app calls the following functions in response to various Android +lifecycle events and EGL context state changes, using various functionalities +provided by {@code ndk_helper} via the {@code Engine} class.</p> + +<pre class="no-pretty-print"> + +void LoadResources(); +void UnloadResources(); +void DrawFrame(); +void TermDisplay(); +void TrimMemory(); +bool IsReady(); +</pre> + +<p>Then, the following function calls back to the Java side to update the UI display.</p> + +<pre class="no-pretty-print"> +void Engine::ShowUI() +{ + JNIEnv *jni; + app_->activity->vm->AttachCurrentThread( &jni, NULL ); + + + //Default class retrieval + jclass clazz = jni->GetObjectClass( app_->activity->clazz ); + jmethodID methodID = jni->GetMethodID( clazz, "showUI", "()V" ); + jni->CallVoidMethod( app_->activity->clazz, methodID ); + + + app_->activity->vm->DetachCurrentThread(); + return; +} +</pre> + +<p>Next, this function calls back to the Java side to draw a text box +superimposed on the screen rendered on the native side, and showing frame +count.</p> + +<pre class="no-pretty-print"> +void Engine::UpdateFPS( float fFPS ) +{ + JNIEnv *jni; + app_->activity->vm->AttachCurrentThread( &jni, NULL ); + + + //Default class retrieval + jclass clazz = jni->GetObjectClass( app_->activity->clazz ); + jmethodID methodID = jni->GetMethodID( clazz, "updateFPS", "(F)V" ); + jni->CallVoidMethod( app_->activity->clazz, methodID, fFPS ); + + + app_->activity->vm->DetachCurrentThread(); + return; +} +</pre> + +<p>The application gets the system clock and supplies it to the renderer +for time-based animation based on real-time clock. This information is used, for example, in +calculating momentum, where speed declines as a function of time.</p> + +<pre class="no-pretty-print"> +renderer_.Update( monitor_.GetCurrentTime() ); +</pre> + +<p>The application now checks whether the context information that {@code GLcontext} holds is still +valid. If not, {@code ndk-helper} swaps the buffer, reinstantiating the GL context.</p> + +<pre class="no-pretty-print"> +if( EGL_SUCCESS != gl_context_->Swap() ) // swaps +buffer. +</pre> + +<p>The program passes touch-motion events to the gesture detector defined +in the {@code ndk_helper} class. The gesture detector tracks multitouch +gestures, such as pinch-and-drag, and sends a notification when triggered by +any of these events.</p> + +<pre class="no-pretty-print"> + if( AInputEvent_getType( event ) == AINPUT_EVENT_TYPE_MOTION ) + { + ndk_helper::GESTURE_STATE doubleTapState = + eng->doubletap_detector_.Detect( event ); + ndk_helper::GESTURE_STATE dragState = eng->drag_detector_.Detect( event ); + ndk_helper::GESTURE_STATE pinchState = eng->pinch_detector_.Detect( event ); + + //Double tap detector has a priority over other detectors + if( doubleTapState == ndk_helper::GESTURE_STATE_ACTION ) + { + //Detect double tap + eng->tap_camera_.Reset( true ); + } + else + { + //Handle drag state + if( dragState & ndk_helper::GESTURE_STATE_START ) + { + //Otherwise, start dragging + ndk_helper::Vec2 v; + eng->drag_detector_.GetPointer( v ); + eng->TransformPosition( v ); + eng->tap_camera_.BeginDrag( v ); + } + // ...else other possible drag states... + + //Handle pinch state + if( pinchState & ndk_helper::GESTURE_STATE_START ) + { + //Start new pinch + ndk_helper::Vec2 v1; + ndk_helper::Vec2 v2; + eng->pinch_detector_.GetPointers( v1, v2 ); + eng->TransformPosition( v1 ); + eng->TransformPosition( v2 ); + eng->tap_camera_.BeginPinch( v1, v2 ); + } + // ...else other possible pinch states... + } + return 1; + } +</pre> + +<p>The {@code ndk_helper} class also provides access to a vector-math library +({@code vecmath.h}), using it here to transform touch coordinates.</p> + +<pre class="no-pretty-print"> +void Engine::TransformPosition( ndk_helper::Vec2& vec ) +{ + vec = ndk_helper::Vec2( 2.0f, 2.0f ) * vec + / ndk_helper::Vec2( gl_context_->GetScreenWidth(), + gl_context_->GetScreenHeight() ) - ndk_helper::Vec2( 1.f, 1.f ); +} +</pre> +</ul> + +<p>The {@code HandleCmd()} method handles commands posted from the +android_native_app_glue library. For more information about what the messages +mean, refer to the comments in the {@code android_native_app_glue.h} and +{@code .c} source files.</p> + +<pre class="no-pretty-print"> +void Engine::HandleCmd( struct android_app* app, + int32_t cmd ) +{ + Engine* eng = (Engine*) app->userData; + switch( cmd ) + { + case APP_CMD_SAVE_STATE: + break; + case APP_CMD_INIT_WINDOW: + // The window is being shown, get it ready. + if( app->window != NULL ) + { + eng->InitDisplay(); + eng->DrawFrame(); + } + break; + case APP_CMD_TERM_WINDOW: + // The window is being hidden or closed, clean it up. + eng->TermDisplay(); + eng->has_focus_ = false; + break; + case APP_CMD_STOP: + break; + case APP_CMD_GAINED_FOCUS: + eng->ResumeSensors(); + //Start animation + eng->has_focus_ = true; + break; + case APP_CMD_LOST_FOCUS: + eng->SuspendSensors(); + // Also stop animating. + eng->has_focus_ = false; + eng->DrawFrame(); + break; + case APP_CMD_LOW_MEMORY: + //Free up GL resources + eng->TrimMemory(); + break; + } +} +</pre> + +<p>The {@code ndk_helper} class posts {@code APP_CMD_INIT_WINDOW} when {@code android_app_glue} +receives an {@code onNativeWindowCreated()} callback from the system. +Applications can normally perform window initializations, such as EGL +initialization. They do this outside of the activity lifecycle, since the +activity is not yet ready.</p> + +<pre class="no-pretty-print"> + //Init helper functions + ndk_helper::JNIHelper::Init( state->activity, HELPER_CLASS_NAME ); + + state->userData = &g_engine; + state->onAppCmd = Engine::HandleCmd; + state->onInputEvent = Engine::HandleInput; +</pre> |