지난글에서 만들었던 libirrlicht.a를 이용해서 안드로이드 irrlicht 프로젝트를 만드는 과정을 정리한다.

0. 먼저 이클립스에서 Android 프로젝트를 하나 생성한다. 이름같은건 적당히 지어주고, 사용 SDK 버전은 8이상으로 해줘야 한다(irrlicht 빌드할때 사용한 버전과 맞추는게 좋다)

사용자 삽입 이미지


1. 지난글에서 사용했던 프로젝트를 재활용할 것이다.(새로 예제 만들긴 귀찮...;;;) 다음 소스들을 프로젝트 폴더로 복사해온다. 복사할때는 그냥 탐색기로 해당 폴더로 이동한 다음 드래그&드롭으로 이클립스 프로젝트에 떨구고 대화상자에서 copy를 선택하면 된다.
 
 . irrlicht 폴더의 include 폴더를 복사해온다.
 . jni폴더를 생성하고 irrlicht/jni 폴더안의 다음 파일들을 복사해온다.
  - android-activity.cpp
    : JNI 함수 이름들을 변경해야 한다. 정석대로라면 javah로 생성하는게 맞는거겠지만.. 그냥 find&replace로 바꿔버리겠다.
함수이름의 앞부분이 "Java_com_ellismarkov_irrlicht_" 이런식으로 되있을텐데 본인이 생성한 프로젝트의 패키지 이름으로 변경하면 된다. 예를들어 패키지 이름이 kr.newbie.irrlichtTest 라면
Java_kr_newbie_irrlichtTest_ 라고 변경하면 된다.
     nativeInitGL 함수에서 createDevice 함수를 호출하는 부분을 보면 irrlicht 엔진을 OpenGL ES2로 생성하게 되어있는데 ES1으로 바꾸자(irrlicht를 ES1으로만 컴파일 해놔서리... ㅡㅅㅡ;;)
  - android-receiver.cpp
  - android-receiver.h
  - app-android.cpp
    : app-android.cpp 파일의 앞부분에서 다음 네 라인을 찾아서 주석처리하고,
#include <android-logger.h>
#include <importgl.h>
#include <os.h>
using namespace os;
      line. 161의
Printer::Logger = new CAndroidLogger();
      이 부분도 주석처리한다.

  . src 폴더에 IrrlichtGLView.java, IrrlichtTest.java, Utils.java 파일을 복사해온다.

2. src 폴더에 java 파일들을 복사해넣으면 아마 에러가 뜰 것이다. 소스 제일 앞부분의 package를 새로 만든 프로젝트의 패키지 이름과 맞춰준다. 패키지 이름을 잊었다면 AndroidManifest.xml 파일을 참고.

3. 새로생성된 프로젝트의 액티비티는 사용하지 않을 것인다. AndroidManifest.xml 파일을 열어서

<activity android:name=".IrrlichtTestActivity"
                  android:label="@string/app_name">

   android:name <- 이부분의 값을 .IrrlichtTest로 바꾸자.

4. irrlicht/obj/local.armeabi 폴더에 가면 libirrlicht.a 파일이 있다. jni 폴더로 복사해온다.

5. 중요한걸 하나 뻬먹었다. irrlicht/jni폴더의 Android.mk 파일도 복사해온다. ㅋㅋ;
   그리고 Android.mk 파일을 다음과 같이 수정한다.

#
# COSOperator.cpp was removed from IRROTHEROBJ
LOCAL_PATH := $(call my-dir)
#
# 1. Load Static library (.a)
#
include $(CLEAR_VARS)
LOCAL_MODULE := static_irrlicht
LOCAL_SRC_FILES = ./libirrlicht.a
include $(PREBUILT_STATIC_LIBRARY)
#
# 2. Build Shared library (.so)
#
include $(CLEAR_VARS)
LOCAL_STATIC_LIBRARIES := static_irrlicht
ANDROID = android-activity.cpp android-receiver.cpp app-android.cpp
LOCAL_MODULE := irrlicht
LOCAL_ARM_MODE   := arm
LOCAL_CFLAGS := -O3 -DANDROID_NDK -I./../include/
LOCAL_SRC_FILES := $(ANDROID)
LOCAL_LDLIBS := -ldl -llog -lGLESv1_CM -lGLESv2
include $(BUILD_SHARED_LIBRARY)


6. cygwin 에서 jni 폴더로 이동한 다음에 ndk-build 커맨드를 실행한다.

7. 이클립스 패키지 익스플로러를 새로고침하면 obj 폴더가 추가된것이 보일것이다. F11키를 눌러서 Debug Run을 실행시키면 짜잔~! 열심히 뛰고 있는 시드니가 보인다 ^0^/

사용자 삽입 이미지


2011/12/04 21:50 2011/12/04 21:50

JNI를 이용하면 C++로 작성된 irrlicht 엔진을 Android 플랫폼에서도 돌아가게 할 수 있다.

인터넷 검색 및 삽질(-_-)끝에 알아낸 것들을 정리해둔다.

--------------------------------------------------------------

1. 프로그램 및 SDK 준비
 . Cygwin 설치
  - http://www.cygwin.com/
  - 설치시 git 플러그인을 같이 설치하는게 좋다.
    git를 이용해서 android로 포팅된 irrlicht 엔진 소스를 얻어오는데
    외부 프로그램으로 하려면 좀 귀찮아서... ㅡ,.ㅡ;
 . JDK 설치
  - http://www.oracle.com/technetwork/java/javase/downloads/index.html
 . Android SDK 설치
  - http://developer.android.com/sdk/index.html
 . Android NDK 설치
  - http://developer.android.com/sdk/ndk/index.html
  - NDK 설치 후 설치 경로를 시스템 환경변수 PATH에 추가시키자. 나중에 커맨드 칠때 편하다;

2. Android Irrlcht 소스 받기
 . Cygwin을 실행시킨 후 소스를 받을 디렉토리로 이동 후에 다음 커맨드를 입력한다.

git clone git://gitorious.org/irrlichtandroid/irrlichtandroid.git

 좀 기다리면 irrlichtandroid란 폴더에 소스가 다운받아져 있을 것이다.


3. Static Library로 빌드
 . irrlichtandroid/project/include/IrrCompileConfig.h 파일을 열어서 102번째 줄의 선언문을 다음같이 변경한다.

//++ ellis ++ // ANDROID NDK CAUSES TO BE HERE
//++ ellis ++ #define _IRR_POSIX_API_
//++ ellis ++ #define _IRR_COMPILE_WITH_X11_DEVICE_
//++ ellis ++ // Three lines added for android
#define _IRR_ANDROID_PLATEFORM_
#define _IRR_COMPILE_WITH_ANDROID_DEVICE_
#define _IRR_COMPILE_WITH_OGLES1_
// #define _IRR_COMPILE_WITH_OGLES2_  // <- 주석처리!
#define _DEBUG

  - OpenGL ES1 이나 ES2중 하나만 선택해야 빌드가 될텐데 일단 ES1으로 선택한다.
    (나중에 CreateDevice 함수로 Irrlicht Device를 만들때도 이때 선택한 그래픽옵션으로 만들어야 한다)

. irrlichtandroid/project/jni/Android.mk 파일을 다음과 같이 수정한다.

#
# COSOperator.cpp was removed from IRROTHEROBJ
LOCAL_PATH := $(call my-dir)
#
# 1. Build Static library (.a)
#
include $(CLEAR_VARS)
IRRMESHLOADER = CBSPMeshFileLoader.cpp CMD2MeshFileLoader.cpp CMD3MeshFileLoader.cpp CMS3DMeshFileLoader.cpp CB3DMeshFileLoader.cpp C3DSMeshFileLoader.cpp COgreMeshFileLoader.cpp COBJMeshFileLoader.cpp CColladaFileLoader.cpp CCSMLoader.cpp CDMFLoader.cpp CLMTSMeshFileLoader.cpp CMY3DMeshFileLoader.cpp COCTLoader.cpp CXMeshFileLoader.cpp CIrrMeshFileLoader.cpp CSTLMeshFileLoader.cpp CLWOMeshFileLoader.cpp CPLYMeshFileLoader.cpp
IRRMESHWRITER = CColladaMeshWriter.cpp CIrrMeshWriter.cpp CSTLMeshWriter.cpp COBJMeshWriter.cpp CPLYMeshWriter.cpp
IRRMESHOBJ = $(IRRMESHLOADER) $(IRRMESHWRITER) CSkinnedMesh.cpp CBoneSceneNode.cpp CMeshSceneNode.cpp CAnimatedMeshSceneNode.cpp CAnimatedMeshMD2.cpp CAnimatedMeshMD3.cpp CQ3LevelMesh.cpp CQuake3ShaderSceneNode.cpp
IRROBJ = CBillboardSceneNode.cpp CCameraSceneNode.cpp CDummyTransformationSceneNode.cpp CEmptySceneNode.cpp CGeometryCreator.cpp CLightSceneNode.cpp CMeshManipulator.cpp CMetaTriangleSelector.cpp COctreeSceneNode.cpp COctreeTriangleSelector.cpp CSceneCollisionManager.cpp CSceneManager.cpp CShadowVolumeSceneNode.cpp CSkyBoxSceneNode.cpp CSkyDomeSceneNode.cpp CTerrainSceneNode.cpp CTerrainTriangleSelector.cpp CVolumeLightSceneNode.cpp CCubeSceneNode.cpp CSphereSceneNode.cpp CTextSceneNode.cpp CTriangleBBSelector.cpp CTriangleSelector.cpp CWaterSurfaceSceneNode.cpp CMeshCache.cpp CDefaultSceneNodeAnimatorFactory.cpp CDefaultSceneNodeFactory.cpp
IRRPARTICLEOBJ = CParticleAnimatedMeshSceneNodeEmitter.cpp CParticleBoxEmitter.cpp CParticleCylinderEmitter.cpp CParticleMeshEmitter.cpp CParticlePointEmitter.cpp CParticleRingEmitter.cpp CParticleSphereEmitter.cpp CParticleAttractionAffector.cpp CParticleFadeOutAffector.cpp CParticleGravityAffector.cpp CParticleRotationAffector.cpp CParticleSystemSceneNode.cpp CParticleScaleAffector.cpp
IRRANIMOBJ = CSceneNodeAnimatorCameraFPS.cpp CSceneNodeAnimatorCameraMaya.cpp CSceneNodeAnimatorCollisionResponse.cpp CSceneNodeAnimatorDelete.cpp CSceneNodeAnimatorFlyCircle.cpp CSceneNodeAnimatorFlyStraight.cpp CSceneNodeAnimatorFollowSpline.cpp CSceneNodeAnimatorRotation.cpp CSceneNodeAnimatorTexture.cpp
IRRDRVROBJ = CNullDriver.cpp COGLESDriver.cpp COGLESTexture.cpp COGLESExtensionHandler.cpp
IRRDRVOBJ2 = COGLES2Driver.cpp COGLES2FixedPipelineShader.cpp COGLES2ParallaxMapRenderer.cpp COGLES2SLMaterialRenderer.cpp COGLES2ExtensionHandler.cpp COGLES2NormalMapRenderer.cpp COGLES2Renderer2D.cpp COGLES2Texture.cpp
IRRIMAGEOBJ = CColorConverter.cpp CImage.cpp CImageLoaderBMP.cpp CImageLoaderJPG.cpp CImageLoaderPCX.cpp CImageLoaderPNG.cpp CImageLoaderPSD.cpp CImageLoaderTGA.cpp CImageLoaderPPM.cpp CImageLoaderWAL.cpp CImageLoaderRGB.cpp CImageWriterBMP.cpp CImageWriterJPG.cpp CImageWriterPCX.cpp CImageWriterPNG.cpp CImageWriterPPM.cpp CImageWriterPSD.cpp CImageWriterTGA.cpp
IRRVIDEOOBJ = CVideoModeList.cpp CFPSCounter.cpp $(IRRDRVROBJ) $(IRRIMAGEOBJ)
IRRSWRENDEROBJ = CSoftwareDriver.cpp CSoftwareTexture.cpp CTRFlat.cpp CTRFlatWire.cpp CTRGouraud.cpp CTRGouraudWire.cpp CTRTextureFlat.cpp CTRTextureFlatWire.cpp CTRTextureGouraud.cpp CTRTextureGouraudAdd.cpp CTRTextureGouraudNoZ.cpp CTRTextureGouraudWire.cpp CZBuffer.cpp CTRTextureGouraudVertexAlpha2.cpp CTRTextureGouraudNoZ2.cpp CTRTextureLightMap2_M2.cpp CTRTextureLightMap2_M4.cpp CTRTextureLightMap2_M1.cpp CSoftwareDriver2.cpp CSoftwareTexture2.cpp CTRTextureGouraud2.cpp CTRGouraud2.cpp CTRGouraudAlpha2.cpp CTRGouraudAlphaNoZ2.cpp CTRTextureDetailMap2.cpp CTRTextureGouraudAdd2.cpp CTRTextureGouraudAddNoZ2.cpp CTRTextureWire2.cpp CTRTextureLightMap2_Add.cpp CTRTextureLightMapGouraud2_M4.cpp IBurningShader.cpp CTRTextureBlend.cpp CTRTextureGouraudAlpha.cpp CTRTextureGouraudAlphaNoZ.cpp CDepthBuffer.cpp CBurningShader_Raster_Reference.cpp
IRRIOOBJ = CFileList.cpp CFileSystem.cpp CLimitReadFile.cpp CMemoryFile.cpp CReadFile.cpp CWriteFile.cpp CXMLReader.cpp CXMLWriter.cpp CZipReader.cpp CPakReader.cpp CNPKReader.cpp CTarReader.cpp CMountPointReader.cpp irrXML.cpp CAttributes.cpp lzma/LzmaDec.c
IRROTHEROBJ = CIrrDeviceConsole.cpp CIrrDeviceStub.cpp CLogger.cpp Irrlicht.cpp os.cpp
IRRGUIOBJ = CGUIButton.cpp CGUICheckBox.cpp CGUIComboBox.cpp CGUIContextMenu.cpp CGUIEditBox.cpp CGUIEnvironment.cpp CGUIFileOpenDialog.cpp CGUIFont.cpp CGUIImage.cpp CGUIInOutFader.cpp CGUIListBox.cpp CGUIMenu.cpp CGUIMeshViewer.cpp CGUIMessageBox.cpp CGUIModalScreen.cpp CGUIScrollBar.cpp CGUISpinBox.cpp CGUISkin.cpp CGUIStaticText.cpp CGUITabControl.cpp CGUITable.cpp CGUIToolBar.cpp CGUIWindow.cpp CGUIColorSelectDialog.cpp CDefaultGUIElementFactory.cpp CGUISpriteBank.cpp CGUIImageList.cpp CGUITreeView.cpp
ZLIBOBJ = zlib/adler32.c zlib/compress.c zlib/crc32.c zlib/deflate.c zlib/inffast.c zlib/inflate.c zlib/inftrees.c zlib/trees.c zlib/uncompr.c zlib/zutil.c
JPEGLIBOBJ = jpeglib/jcapimin.c jpeglib/jcapistd.c jpeglib/jccoefct.c jpeglib/jccolor.c jpeglib/jcdctmgr.c jpeglib/jchuff.c jpeglib/jcinit.c jpeglib/jcmainct.c jpeglib/jcmarker.c jpeglib/jcmaster.c jpeglib/jcomapi.c jpeglib/jcparam.c jpeglib/jcprepct.c jpeglib/jcsample.c jpeglib/jctrans.c jpeglib/jdapimin.c jpeglib/jdapistd.c jpeglib/jdatadst.c jpeglib/jdatasrc.c jpeglib/jdcoefct.c jpeglib/jdcolor.c jpeglib/jddctmgr.c jpeglib/jdhuff.c jpeglib/jdinput.c jpeglib/jdmainct.c jpeglib/jdmarker.c jpeglib/jdmaster.c jpeglib/jdmerge.c jpeglib/jdpostct.c jpeglib/jdsample.c jpeglib/jdtrans.c jpeglib/jerror.c jpeglib/jfdctflt.c jpeglib/jfdctfst.c jpeglib/jfdctint.c jpeglib/jidctflt.c jpeglib/jidctfst.c jpeglib/jidctint.c jpeglib/jmemmgr.c jpeglib/jmemnobs.c jpeglib/jquant1.c jpeglib/jquant2.c jpeglib/jutils.c jpeglib/jcarith.c jpeglib/jdarith.c jpeglib/jaricom.c
LIBPNGOBJ = libpng/png.c libpng/pngerror.c libpng/pngget.c libpng/pngmem.c libpng/pngpread.c libpng/pngread.c libpng/pngrio.c libpng/pngrtran.c libpng/pngrutil.c libpng/pngset.c libpng/pngtrans.c libpng/pngwio.c libpng/pngwrite.c libpng/pngwtran.c libpng/pngwutil.c
LIBAESGM = aesGladman/aescrypt.cpp aesGladman/aeskey.cpp aesGladman/aestab.cpp aesGladman/fileenc.cpp aesGladman/hmac.cpp aesGladman/prng.cpp aesGladman/pwd2key.cpp aesGladman/sha1.cpp aesGladman/sha2.cpp
BZIP2OBJ = bzip2/blocksort.c bzip2/huffman.c bzip2/crctable.c bzip2/randtable.c bzip2/bzcompress.c bzip2/decompress.c bzip2/bzlib.c
ANDROID = importgl.cpp CIrrDeviceAndroid.cpp
LOCAL_MODULE := irrlicht
LOCAL_ARM_MODE   := arm
LOCAL_CFLAGS := -O3 -DANDROID_NDK -DDISABLE_IMPORTGL -I./../include/ -I./include/
LOCAL_SRC_FILES := \
     $(ANDROID) \
     $(IRRVIDEOOBJ) $(IRRDRVOBJ2) \
     $(IRRMESHOBJ) $(IRROBJ) $(IRRPARTICLEOBJ) $(IRRANIMOBJ) \
     $(IRRSWRENDEROBJ) $(IRRIOOBJ) $(IRROTHEROBJ) \
     $(IRRGUIOBJ) $(ZLIBOBJ) $(JPEGLIBOBJ) $(LIBPNGOBJ) $(LIBAESGM) \
     $(BZIP2OBJ)
LOCAL_LDLIBS := -lGLESv1_CM -ldl -llog -lGLESv2
include $(BUILD_STATIC_LIBRARY)
#
# 2. Build Shared library (.so)
#
include $(CLEAR_VARS)
LOCAL_MODULE    := fakeIrrlicht
LOCAL_STATIC_LIBRARIES := irrlicht
include $(BUILD_SHARED_LIBRARY)

 . irrlichtandroid/project/jni 폴더로 이동한 후 ndk로 빌드하면 .a 파일이 생성!

ndk-bulid --jobs <CPU 코어수>

  - 멀티코어를 이용해서 빌드하면 빌드속도가 꽤 많이 단축되니 꼭 멀티코어 빌드로 하자.
    다만, 멀티코어 빌드시 별 이유없이 실패할 때가 있는데 그땐 그냥 obj 폴더를 지우고
    다시 빌드하면 된다. (아마도 버그인듯;)
 
 . 빌드가 성공하면 irrlichtandroid/project/obj/local/armeabi 폴더에 libirrlicht.a 파일이 생성되있을것이다.
   같이 생성된 so파일(libfakeIrrlicht.so)는 사용하지 않으니 무시할 것! (어차피 빈 껍데기 ㅋ)
 
 . irrlicht 프로젝트에서 사용하는 방법은 다음글에 계속.. 

2011/11/20 21:57 2011/11/20 21:57