1 /** Example 027 Helloworld_Android
\r
2 This example shows a simple application for Android.
\r
5 #include <irrlicht.h>
\r
7 #ifdef _IRR_ANDROID_PLATFORM_
\r
9 #include <android_native_app_glue.h>
\r
10 #include "android_tools.h"
\r
11 #include "android/window.h"
\r
13 using namespace irr;
\r
14 using namespace core;
\r
15 using namespace scene;
\r
16 using namespace video;
\r
18 using namespace gui;
\r
29 Android is using multitouch events.
\r
30 We allow users to move around the Irrlicht logo as example of how to use those.
\r
32 class MyEventReceiver : public IEventReceiver
\r
35 MyEventReceiver(android_app* app )
\r
36 : Device(0), AndroidApp(app), SpriteToMove(0), TouchID(-1)
\r
40 void Init(IrrlichtDevice *device)
\r
45 virtual bool OnEvent(const SEvent& event)
\r
47 if (event.EventType == EET_TOUCH_INPUT_EVENT)
\r
50 For now we fake mouse-events. Touch-events will be handled inside Irrlicht in the future, but until
\r
51 that is implemented you can use this workaround to get a GUI which works at least for simple elements like
\r
52 buttons. That workaround does ignore multi-touch events - if you need several buttons pressed at the same
\r
53 time you have to handle that yourself.
\r
55 SEvent fakeMouseEvent;
\r
56 fakeMouseEvent.EventType = EET_MOUSE_INPUT_EVENT;
\r
57 fakeMouseEvent.MouseInput.X = event.TouchInput.X;
\r
58 fakeMouseEvent.MouseInput.Y = event.TouchInput.Y;
\r
59 fakeMouseEvent.MouseInput.Shift = false;
\r
60 fakeMouseEvent.MouseInput.Control = false;
\r
61 fakeMouseEvent.MouseInput.ButtonStates = 0;
\r
62 fakeMouseEvent.MouseInput.Event = EMIE_COUNT;
\r
64 switch (event.TouchInput.Event)
\r
66 case ETIE_PRESSED_DOWN:
\r
68 // We only work with the first for now.force opengl error
\r
69 if ( TouchID == -1 )
\r
71 fakeMouseEvent.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
\r
75 position2d<s32> touchPoint(event.TouchInput.X, event.TouchInput.Y);
\r
76 IGUIElement * logo = Device->getGUIEnvironment()->getRootGUIElement()->getElementFromId ( GUI_IRR_LOGO );
\r
77 if ( logo && logo->isPointInside (touchPoint) )
\r
79 TouchID = event.TouchInput.ID;
\r
80 SpriteToMove = logo;
\r
81 SpriteStartRect = SpriteToMove->getRelativePosition();
\r
82 TouchStartPos = touchPoint;
\r
89 if ( TouchID == event.TouchInput.ID )
\r
91 fakeMouseEvent.MouseInput.Event = EMIE_MOUSE_MOVED;
\r
92 fakeMouseEvent.MouseInput.ButtonStates = EMBSM_LEFT;
\r
94 if ( SpriteToMove && TouchID == event.TouchInput.ID )
\r
97 position2d<s32> touchPoint(event.TouchInput.X, event.TouchInput.Y);
\r
98 MoveSprite(touchPoint);
\r
103 if ( TouchID == event.TouchInput.ID )
\r
105 fakeMouseEvent.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
\r
107 if ( SpriteToMove )
\r
110 position2d<s32> touchPoint(event.TouchInput.X, event.TouchInput.Y);
\r
111 MoveSprite(touchPoint);
\r
120 if ( fakeMouseEvent.MouseInput.Event != EMIE_COUNT && Device )
\r
122 Device->postEventFromUser(fakeMouseEvent);
\r
125 else if ( event.EventType == EET_GUI_EVENT )
\r
128 Show and hide the soft input keyboard when an edit-box get's the focus.
\r
130 switch(event.GUIEvent.EventType)
\r
132 case EGET_EDITBOX_ENTER:
\r
133 if ( event.GUIEvent.Caller->getType() == EGUIET_EDIT_BOX )
\r
135 if( Device->getGUIEnvironment() )
\r
136 Device->getGUIEnvironment()->setFocus(NULL);
\r
137 android::setSoftInputVisibility(AndroidApp, false);
\r
140 case EGET_ELEMENT_FOCUS_LOST:
\r
141 if ( event.GUIEvent.Caller->getType() == EGUIET_EDIT_BOX )
\r
143 /* Unfortunatly this only works on some android devices.
\r
144 On other devices Android passes through touch-input events when the virtual keyboard is clicked while blocking those events in areas where the keyboard isn't.
\r
145 Very likely an Android bug as it only happens in certain cases (like Android Lollipop with landscape mode on MotoG, but also some reports from other devices).
\r
146 Or maybe Irrlicht still does something wrong.
\r
147 Can't figure it out so far - so be warned - with landscape mode you might be better off writing your own keyboard.
\r
149 android::setSoftInputVisibility(AndroidApp, false);
\r
152 case EGET_ELEMENT_FOCUSED:
\r
153 if ( event.GUIEvent.Caller->getType() == EGUIET_EDIT_BOX )
\r
155 android::setSoftInputVisibility(AndroidApp, true);
\r
166 void MoveSprite(const irr::core::position2d<irr::s32> &touchPos)
\r
168 irr::core::position2d<irr::s32> move(touchPos-TouchStartPos);
\r
169 SpriteToMove->setRelativePosition(SpriteStartRect.UpperLeftCorner + move);
\r
173 IrrlichtDevice * Device;
\r
174 android_app* AndroidApp;
\r
175 gui::IGUIElement * SpriteToMove;
\r
176 core::rect<s32> SpriteStartRect;
\r
177 core::position2d<irr::s32> TouchStartPos;
\r
183 void mainloop( IrrlichtDevice *device, IGUIStaticText * infoText )
\r
185 u32 loop = 0; // loop is reset when the app is destroyed unlike runCounter
\r
186 static u32 runCounter = 0; // static's seem to survive even an app-destroy message (not sure if that's guaranteed).
\r
187 while(device->run())
\r
190 The window seems to be always active in this setup.
\r
191 That's because when it's not active Android will stop the code from running.
\r
193 if (device->isWindowActive())
\r
196 Show FPS and some counters to show which parts of an app run
\r
197 in different app-lifecycle states.
\r
201 stringw str = L"FPS:";
\r
202 str += (s32)device->getVideoDriver()->getFPS();
\r
207 infoText->setText ( str.c_str() );
\r
210 device->getVideoDriver()->beginScene(true, true, SColor(0,100,100,100));
\r
211 device->getSceneManager()->drawAll();
\r
212 device->getGUIEnvironment()->drawAll();
\r
213 device->getVideoDriver()->endScene ();
\r
215 device->yield(); // probably nicer to the battery
\r
221 /* Main application code. */
\r
222 void android_main(android_app* app)
\r
224 // Make sure glue isn't stripped.
\r
228 The receiver can already receive system events while createDeviceEx is called.
\r
229 So we create it first.
\r
231 MyEventReceiver receiver(app);
\r
235 You have currently the choice between 2 drivers:
\r
236 EDT_OGLES1 is basically a opengl fixed function pipeline.
\r
237 EDT_OGLES2 is a shader pipeline. Irrlicht comes with shaders to simulate
\r
238 typical fixed function materials. For this to work the
\r
239 corresponding shaders from the Irrlicht media/Shaders folder are
\r
240 copied to the application assets folder (done in the Makefile).
\r
242 SIrrlichtCreationParameters param;
\r
243 // param.DriverType = EDT_OGLES1; // android:glEsVersion in AndroidManifest.xml should be "0x00010000" (requesting 0x00020000 will also guarantee that ES1 works)
\r
244 param.DriverType = EDT_OGLES2; // android:glEsVersion in AndroidManifest.xml should be "0x00020000"
\r
245 param.WindowSize = dimension2d<u32>(300,300); // using 0,0 it will automatically set it to the maximal size
\r
246 param.PrivateData = app;
\r
248 param.ZBufferBits = 16;
\r
249 param.AntiAlias = 0;
\r
250 param. EventReceiver = &receiver;
\r
252 /* Logging is written to a file. So your application should disable all logging when you distribute your
\r
253 application or it can fill up that file over time.
\r
256 param.LoggingLevel = ELL_NONE;
\r
259 IrrlichtDevice *device = createDeviceEx(param);
\r
263 receiver.Init(device);
\r
265 // ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_FULLSCREEN, 0);
\r
267 IVideoDriver* driver = device->getVideoDriver();
\r
268 ISceneManager* smgr = device->getSceneManager();
\r
269 IGUIEnvironment* guienv = device->getGUIEnvironment();
\r
270 ILogger* logger = device->getLogger();
\r
271 IFileSystem * fs = device->getFileSystem();
\r
273 /* Access to the Android native window. You often need this when accessing NDK functions like we are doing here.
\r
274 Note that windowWidth/windowHeight have already subtracted things like the taskbar which your device might have,
\r
275 so you get the real size of your render-window.
\r
277 ANativeWindow* nativeWindow = static_cast<ANativeWindow*>(driver->getExposedVideoData().OGLESAndroid.Window);
\r
278 int32_t windowWidth = ANativeWindow_getWidth(app->window);
\r
279 int32_t windowHeight = ANativeWindow_getHeight(app->window);
\r
281 /* Get display metrics. We are accessing the Java functions of the JVM directly in this case as there is no NDK function for that yet.
\r
282 Checkout android_tools.cpp if you want to know how that is done. */
\r
283 irr::android::SDisplayMetrics displayMetrics;
\r
284 memset(&displayMetrics, 0, sizeof displayMetrics);
\r
285 irr::android::getDisplayMetrics(app, displayMetrics);
\r
287 /* For troubleshooting you can use the Irrlicht logger.
\r
288 The Irrlicht logging messages are send to the Android logging system using the tag "Irrlicht".
\r
289 They stay in a file there, so you can check them even after running your app.
\r
290 You can watch them with the command: "adb logcat Irrlicht:V DEBUG:V *:S"
\r
291 This means Irrlicht _V_erbose, debug messages verbose (p.E callstack on crashes) and all other messages _S_ilent.
\r
292 Clean the logging file with: "adb logcat -c".
\r
293 See http://developer.android.com/tools/debugging/debugging-log.html for more advanced log options.
\r
295 char strDisplay[1000];
\r
296 sprintf(strDisplay, "Window size:(%d/%d)\nDisplay size:(%d/%d)", windowWidth, windowHeight, displayMetrics.widthPixels, displayMetrics.heightPixels);
\r
297 logger->log(strDisplay);
\r
299 core::dimension2d<s32> dim(driver->getScreenSize());
\r
300 sprintf(strDisplay, "getScreenSize:(%d/%d)", dim.Width, dim.Height);
\r
301 logger->log(strDisplay);
\r
304 /* Your media must be somewhere inside the assets folder. The assets folder is the root for the file system.
\r
305 This example copies the media in the Android.mk makefile. */
\r
306 stringc mediaPath = "media/";
\r
308 // The Android assets file-system does not know which sub-directories it has (blame google).
\r
309 // So we have to add all sub-directories in assets manually. Otherwise we could still open the files,
\r
310 // but existFile checks will fail (which are for example needed by getFont).
\r
311 for ( u32 i=0; i < fs->getFileArchiveCount(); ++i )
\r
313 IFileArchive* archive = fs->getFileArchive(i);
\r
314 if ( archive->getType() == EFAT_ANDROID_ASSET )
\r
316 archive->addDirectoryToFileList(mediaPath);
\r
321 /* Set the font-size depending on your device.
\r
322 dpi=dots per inch. 1 inch = 2.54 cm. */
\r
323 IGUISkin* skin = guienv->getSkin();
\r
324 IGUIFont* font = 0;
\r
325 if ( displayMetrics.xdpi < 100 ) // just guessing some value where fontsize might start to get too small
\r
326 font = guienv->getFont(mediaPath + "fonthaettenschweiler.bmp");
\r
328 font = guienv->getFont(mediaPath + "bigfont.png");
\r
330 skin->setFont(font);
\r
332 // A field to show some text. Comment out stat->setText in run() if you want to see the dpi instead of the fps.
\r
333 IGUIStaticText *text = guienv->addStaticText(stringw(displayMetrics.xdpi).c_str(),
\r
334 rect<s32>(5,5,635,35), false, false, 0, GUI_INFO_FPS );
\r
335 guienv->addEditBox( L"", rect<s32>(5,40,475,80));
\r
337 // add irrlicht logo
\r
338 IGUIImage * logo = guienv->addImage(driver->getTexture(mediaPath + "irrlichtlogo3.png"),
\r
339 core::position2d<s32>(5,85), true, 0, GUI_IRR_LOGO);
\r
340 s32 minLogoWidth = windowWidth/3;
\r
341 if ( logo && logo->getRelativePosition().getWidth() < minLogoWidth )
\r
343 /* Scale to make it better visible on high-res devices (we could also work with dpi here).
\r
345 logo->setScaleImage(true);
\r
346 core::rect<s32> logoPos(logo->getRelativePosition());
\r
347 f32 scale = (f32)minLogoWidth/(f32)logoPos.getWidth();
\r
348 logoPos.LowerRightCorner.X = logoPos.UpperLeftCorner.X + minLogoWidth;
\r
349 logoPos.LowerRightCorner.Y = logoPos.UpperLeftCorner.Y + (s32)((f32)logoPos.getHeight()*scale);
\r
350 logo->setRelativePosition(logoPos);
\r
354 Add a 3d model. Note that you might need to add light when using other models.
\r
355 A copy of the model and it's textures must be inside the assets folder to be installed to Android.
\r
356 In this example we do copy it to the assets folder in the Makefile jni/Android.mk
\r
358 IAnimatedMesh* mesh = smgr->getMesh(mediaPath + "dwarf.x");
\r
361 device->closeDevice();
\r
365 smgr->addAnimatedMeshSceneNode( mesh );
\r
369 To look at the mesh, we place a camera.
\r
371 smgr->addCameraSceneNode(0, vector3df(15,40,-90), vector3df(0,30,0));
\r
374 Mainloop. Applications usually never quit themself in Android. The OS is responsible for that.
\r
376 mainloop(device, text);
\r
379 device->setEventReceiver(0);
\r
380 device->closeDevice();
\r
384 #endif // defined(_IRR_ANDROID_PLATFORM_)
\r