নমুনা: চাপানি

Teapot নমুনা NDK ইনস্টলেশনের রুট ডিরেক্টরির অধীনে samples/Teapot/ ডিরেক্টরির অধীনে অবস্থিত। আইকনিক Utah teapot রেন্ডার করতে এই নমুনা OpenGL লাইব্রেরি ব্যবহার করে। বিশেষ করে, এটি ndk_helper হেল্পার শ্রেণী প্রদর্শন করে, গেমগুলি এবং অনুরূপ অ্যাপ্লিকেশনগুলিকে নেটিভ অ্যাপ্লিকেশন হিসাবে প্রয়োগ করার জন্য প্রয়োজনীয় নেটিভ হেল্পার ফাংশনগুলির একটি সংগ্রহ। এই ক্লাস প্রদান করে:

  • একটি বিমূর্ত স্তর, GLContext , যা নির্দিষ্ট NDK-নির্দিষ্ট আচরণ পরিচালনা করে।
  • হেল্পার ফাংশন যা দরকারী কিন্তু NDK-তে উপস্থিত নয়, যেমন ট্যাপ ডিটেকশন।
  • JNI-এর জন্য র‍্যাপারগুলি প্ল্যাটফর্মের বৈশিষ্ট্যগুলি যেমন টেক্সচার লোড করার জন্য কল করে।

AndroidManifest.xml

এখানে ক্রিয়াকলাপের ঘোষণাটি NativeActivity নয়, তবে এটির একটি উপশ্রেণী: TeapotNativeActivity

<activity android:name="com.sample.teapot.TeapotNativeActivity"
        android:label="@string/app_name"
        android:configChanges="orientation|keyboardHidden">

শেষ পর্যন্ত, বিল্ড সিস্টেম তৈরি করা শেয়ার্ড-অবজেক্ট ফাইলের নাম হল libTeapotNativeActivity.so । বিল্ড সিস্টেম lib উপসর্গ এবং .so এক্সটেনশন যোগ করে; ম্যানিফেস্টটি মূলত android:value কে বরাদ্দ করে এমন মানের অংশও নয়।

<meta-data android:name="android.app.lib_name"
        android:value="TeapotNativeActivity" />

Application.mk

NativeActivity ফ্রেমওয়ার্ক ক্লাস ব্যবহার করে এমন একটি অ্যাপ অবশ্যই 9-এর কম একটি Android API লেভেল নির্দিষ্ট করবে না, যা সেই ক্লাসটি চালু করেছে। NativeActivity ক্লাস সম্পর্কে আরও তথ্যের জন্য, নেটিভ অ্যাক্টিভিটিস এবং অ্যাপ্লিকেশন দেখুন।

APP_PLATFORM := android-9

পরবর্তী লাইনটি বিল্ড সিস্টেমকে সমস্ত সমর্থিত আর্কিটেকচারের জন্য তৈরি করতে বলে।

APP_ABI := all

এরপরে, ফাইলটি বিল্ড সিস্টেমকে বলে যে কোন C++ রানটাইম সাপোর্ট লাইব্রেরি ব্যবহার করতে হবে।

APP_STL := stlport_static

জাভা-সাইড বাস্তবায়ন

TeapotNativeActivity ফাইলটি GitHub-এ NDK রেপো রুট ডিরেক্টরির অধীনে teapots/classic-teapot/src/com/sample/teapot এ অবস্থিত। এটি অ্যাক্টিভিটি লাইফসাইকেল ইভেন্টগুলি পরিচালনা করে, ShowUI() ফাংশন সহ স্ক্রিনে পাঠ্য প্রদর্শনের জন্য একটি পপআপ উইন্ডো তৈরি করে এবং ফাংশন updateFPS() এর সাথে গতিশীলভাবে ফ্রেম রেট আপডেট করে। নিম্নলিখিত কোডটি আপনার কাছে আকর্ষণীয় হতে পারে যে এটি অ্যাপের কার্যকলাপকে পূর্ণ স্ক্রীন, নিমজ্জিত এবং সিস্টেম নেভিগেশন বার ছাড়াই প্রস্তুত করে, যাতে পুরো স্ক্রীনটি রেন্ডার করা টিপট ফ্রেমগুলি প্রদর্শনের জন্য ব্যবহার করা যেতে পারে:

কোটলিন

fun setImmersiveSticky() {
    window.decorView.systemUiVisibility = (
            View.SYSTEM_UI_FLAG_FULLSCREEN
                    or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                    or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            )
}

জাভা

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);
}

নেটিভ সাইড ইমপ্লিমেন্টেশন

এই বিভাগটি C++ এ প্রয়োগ করা Teapot অ্যাপের অংশটি অন্বেষণ করে।

TeapotRenderer.h

এই ফাংশন কল চাপাতার প্রকৃত রেন্ডারিং সঞ্চালন. এটি ম্যাট্রিক্স গণনার জন্য ndk_helper ব্যবহার করে এবং ব্যবহারকারী যেখানে ট্যাপ করে তার উপর ভিত্তি করে ক্যামেরাটিকে পুনরায় স্থাপন করতে।

ndk_helper::Mat4 mat_projection_;
ndk_helper::Mat4 mat_view_;
ndk_helper::Mat4 mat_model_;


ndk_helper::TapCamera* camera_;

TeapotNativeActivity.cpp

নিম্নলিখিত লাইনগুলি নেটিভ সোর্স ফাইলে ndk_helper অন্তর্ভুক্ত করে এবং হেল্পার-ক্লাস নামটি সংজ্ঞায়িত করে।

#include "NDKHelper.h"

//-------------------------------------------------------------------------
//Preprocessor
//-------------------------------------------------------------------------
#define HELPER_CLASS_NAME "com/sample/helper/NDKHelper" //Class name of helper
function

ndk_helper ক্লাসের প্রথম ব্যবহার হল EGL-সম্পর্কিত লাইফসাইকেল পরিচালনা করা, Android লাইফসাইকেল ইভেন্টের সাথে EGL কনটেক্সট স্টেটস (সৃষ্ট/হারানো) যুক্ত করা। ndk_helper ক্লাস অ্যাপ্লিকেশনটিকে প্রসঙ্গ তথ্য সংরক্ষণ করতে সক্ষম করে যাতে সিস্টেমটি একটি ধ্বংস হওয়া কার্যকলাপ পুনরুদ্ধার করতে পারে। এই ক্ষমতাটি দরকারী, উদাহরণস্বরূপ, যখন টার্গেট মেশিনটি ঘোরানো হয় (যার ফলে একটি কার্যকলাপ ধ্বংস হয়ে যায়, তারপরে অবিলম্বে নতুন অভিযোজনে পুনরুদ্ধার করা হয়), বা যখন লক স্ক্রিন প্রদর্শিত হয়।

ndk_helper::GLContext* gl_context_; // handles EGL-related lifecycle.

পরবর্তী, ndk_helper স্পর্শ নিয়ন্ত্রণ প্রদান করে।

ndk_helper::DoubletapDetector doubletap_detector_;
ndk_helper::PinchDetector pinch_detector_;
ndk_helper::DragDetector drag_detector_;
ndk_helper::PerfMonitor monitor_;

এটি ক্যামেরা নিয়ন্ত্রণ (ওপেনজিএল ভিউ ফ্রাস্টাম) প্রদান করে।

ndk_helper::TapCamera tap_camera_;

অ্যাপটি তখন NDK-তে প্রদত্ত নেটিভ API ব্যবহার করে ডিভাইসের সেন্সর ব্যবহার করার জন্য প্রস্তুত করে।

ASensorManager* sensor_manager_;
const ASensor* accelerometer_sensor_;
ASensorEventQueue* sensor_event_queue_;

Engine ক্লাসের মাধ্যমে ndk_helper দ্বারা প্রদত্ত বিভিন্ন কার্যকারিতা ব্যবহার করে অ্যাপটি বিভিন্ন Android লাইফসাইকেল ইভেন্ট এবং EGL প্রসঙ্গ স্থিতির পরিবর্তনের প্রতিক্রিয়া হিসাবে নিম্নলিখিত ফাংশনগুলিকে কল করে।

void LoadResources();
void UnloadResources();
void DrawFrame();
void TermDisplay();
void TrimMemory();
bool IsReady();

তারপরে, নিম্নলিখিত ফাংশনটি UI ডিসপ্লে আপডেট করতে জাভা সাইডে ফিরে আসে।

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;
}

এরপরে, এই ফাংশনটি জাভা সাইডে আবার কল করে নেটিভ সাইডে রেন্ডার করা স্ক্রিনে সুপার ইম্পোজ করা একটি টেক্সট বক্স আঁকতে এবং ফ্রেমের সংখ্যা দেখায়।

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;
}

অ্যাপ্লিকেশনটি সিস্টেম ঘড়ি পায় এবং রিয়েল-টাইম ঘড়ির উপর ভিত্তি করে সময়-ভিত্তিক অ্যানিমেশনের জন্য রেন্ডারারকে সরবরাহ করে। এই তথ্য ব্যবহার করা হয়, উদাহরণস্বরূপ, ভরবেগ গণনা করার জন্য, যেখানে সময়ের ফাংশন হিসাবে গতি হ্রাস পায়।

renderer_.Update( monitor_.GetCurrentTime() );

অ্যাপ্লিকেশনটি এখন GLcontext::Swap() ফাংশনের মাধ্যমে প্রদর্শনের জন্য রেন্ডার করা ফ্রেমটিকে সামনের বাফারে ফ্লিপ করে; এটি ফ্লিপিং প্রক্রিয়া চলাকালীন সম্ভাব্য ত্রুটিগুলি পরিচালনা করে।

if( EGL_SUCCESS != gl_context_->Swap() )  // swaps
buffer.

প্রোগ্রামটি ndk_helper ক্লাসে সংজ্ঞায়িত জেসচার ডিটেক্টরে টাচ-মোশন ইভেন্টগুলি পাস করে। অঙ্গভঙ্গি সনাক্তকারী মাল্টিটাচ অঙ্গভঙ্গিগুলিকে ট্র্যাক করে, যেমন পিঞ্চ-এন্ড-টেনে, এবং এই ইভেন্টগুলির যেকোনো একটি দ্বারা ট্রিগার হলে একটি বিজ্ঞপ্তি পাঠায়৷

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;
}

ndk_helper ক্লাস একটি ভেক্টর-ম্যাথ লাইব্রেরিতে ( vecmath.h ) অ্যাক্সেস প্রদান করে, এটি এখানে ব্যবহার করে স্পর্শ স্থানাঙ্ক রূপান্তরিত করে।

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 );
}

HandleCmd() পদ্ধতিটি android_native_app_glue লাইব্রেরি থেকে পোস্ট করা কমান্ড পরিচালনা করে। বার্তাগুলির অর্থ কী সে সম্পর্কে আরও তথ্যের জন্য, android_native_app_glue.h এবং .c সোর্স ফাইলগুলিতে মন্তব্যগুলি পড়ুন৷

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;
    }
}

ndk_helper ক্লাস APP_CMD_INIT_WINDOW পোস্ট করে যখন android_app_glue সিস্টেম থেকে একটি onNativeWindowCreated() কলব্যাক পায়। অ্যাপ্লিকেশনগুলি সাধারণত উইন্ডো ইনিশিয়ালাইজেশন করতে পারে, যেমন EGL ইনিশিয়ালাইজেশন। তারা কার্যকলাপ জীবনচক্রের বাইরে এটি করে, যেহেতু কার্যকলাপ এখনও প্রস্তুত নয়।

//Init helper functions
ndk_helper::JNIHelper::Init( state->activity, HELPER_CLASS_NAME );

state->userData = &g_engine;
state->onAppCmd = Engine::HandleCmd;
state->onInputEvent = Engine::HandleInput;