]> git.lizzy.rs Git - irrlicht.git/blob - examples/01.HelloWorld_Android/main.cpp
Fix bug introduced in last merge from svn trunk
[irrlicht.git] / examples / 01.HelloWorld_Android / main.cpp
1 /** Example 027 Helloworld_Android\r
2         This example shows a simple application for Android.\r
3 */\r
4 \r
5 #include <irrlicht.h>\r
6 \r
7 #ifdef _IRR_ANDROID_PLATFORM_\r
8 \r
9 #include <android_native_app_glue.h>\r
10 #include "android_tools.h"\r
11 #include "android/window.h"\r
12 \r
13 using namespace irr;\r
14 using namespace core;\r
15 using namespace scene;\r
16 using namespace video;\r
17 using namespace io;\r
18 using namespace gui;\r
19 \r
20 \r
21 enum GUI_IDS\r
22 {\r
23         GUI_INFO_FPS,\r
24         GUI_IRR_LOGO,\r
25 };\r
26 \r
27 \r
28 /*\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
31 */\r
32 class MyEventReceiver : public IEventReceiver\r
33 {\r
34 public:\r
35         MyEventReceiver(android_app* app )\r
36         : Device(0), AndroidApp(app), SpriteToMove(0), TouchID(-1)\r
37         {\r
38         }\r
39 \r
40         void Init(IrrlichtDevice *device)\r
41         {\r
42                 Device = device;\r
43         }\r
44 \r
45         virtual bool OnEvent(const SEvent& event)\r
46         {\r
47                 if (event.EventType == EET_TOUCH_INPUT_EVENT)\r
48                 {\r
49                         /*\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
54                         */\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
63 \r
64                         switch (event.TouchInput.Event)\r
65                         {\r
66                                 case ETIE_PRESSED_DOWN:\r
67                                 {\r
68                                         // We only work with the first for now.force opengl error\r
69                                         if ( TouchID == -1 )\r
70                                         {\r
71                                                 fakeMouseEvent.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;\r
72 \r
73                                                 if (Device)\r
74                                                 {\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
78                                                         {\r
79                                                                 TouchID = event.TouchInput.ID;\r
80                                                                 SpriteToMove = logo;\r
81                                                                 SpriteStartRect =  SpriteToMove->getRelativePosition();\r
82                                                                 TouchStartPos = touchPoint;\r
83                                                         }\r
84                                                 }\r
85                                         }\r
86                                         break;\r
87                                 }\r
88                                 case ETIE_MOVED:\r
89                                         if ( TouchID == event.TouchInput.ID )\r
90                                         {\r
91                                                 fakeMouseEvent.MouseInput.Event = EMIE_MOUSE_MOVED;\r
92                                                 fakeMouseEvent.MouseInput.ButtonStates = EMBSM_LEFT;\r
93 \r
94                                                 if ( SpriteToMove && TouchID == event.TouchInput.ID )\r
95                                                 {\r
96 \r
97                                                         position2d<s32> touchPoint(event.TouchInput.X, event.TouchInput.Y);\r
98                                                         MoveSprite(touchPoint);\r
99                                                 }\r
100                                         }\r
101                                         break;\r
102                                 case ETIE_LEFT_UP:\r
103                                         if ( TouchID == event.TouchInput.ID )\r
104                                         {\r
105                                                 fakeMouseEvent.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;\r
106 \r
107                                                 if ( SpriteToMove )\r
108                                                 {\r
109                                                         TouchID = -1;\r
110                                                         position2d<s32> touchPoint(event.TouchInput.X, event.TouchInput.Y);\r
111                                                         MoveSprite(touchPoint);\r
112                                                         SpriteToMove = 0;\r
113                                                 }\r
114                                         }\r
115                                         break;\r
116                                 default:\r
117                                         break;\r
118                         }\r
119 \r
120                         if ( fakeMouseEvent.MouseInput.Event != EMIE_COUNT && Device )\r
121                         {\r
122                                 Device->postEventFromUser(fakeMouseEvent);\r
123                         }\r
124                 }\r
125                 else if ( event.EventType == EET_GUI_EVENT )\r
126                 {\r
127                         /*\r
128                                 Show and hide the soft input keyboard when an edit-box get's the focus.\r
129                         */\r
130                         switch(event.GUIEvent.EventType)\r
131                         {\r
132                                 case EGET_EDITBOX_ENTER:\r
133                                         if ( event.GUIEvent.Caller->getType() == EGUIET_EDIT_BOX )\r
134                                         {\r
135                                                 if( Device->getGUIEnvironment() )\r
136                                                         Device->getGUIEnvironment()->setFocus(NULL);\r
137                                                 android::setSoftInputVisibility(AndroidApp, false);\r
138                                         }\r
139                                 break;\r
140                 case EGET_ELEMENT_FOCUS_LOST:\r
141                                         if ( event.GUIEvent.Caller->getType() == EGUIET_EDIT_BOX )\r
142                                         {\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
148                                                 */\r
149                                                 android::setSoftInputVisibility(AndroidApp, false);\r
150                                         }\r
151                 break;\r
152                 case EGET_ELEMENT_FOCUSED:\r
153                                         if ( event.GUIEvent.Caller->getType() == EGUIET_EDIT_BOX )\r
154                                         {\r
155                                                 android::setSoftInputVisibility(AndroidApp, true);\r
156                                         }\r
157                 break;\r
158                                 default:\r
159                                         break;\r
160                         }\r
161                 }\r
162 \r
163                 return false;\r
164         }\r
165 \r
166         void MoveSprite(const irr::core::position2d<irr::s32> &touchPos)\r
167         {\r
168                 irr::core::position2d<irr::s32> move(touchPos-TouchStartPos);\r
169                 SpriteToMove->setRelativePosition(SpriteStartRect.UpperLeftCorner + move);\r
170         }\r
171 \r
172 private:\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
178         s32 TouchID;\r
179 };\r
180 \r
181 /* Mainloop.\r
182 */\r
183 void mainloop( IrrlichtDevice *device, IGUIStaticText * infoText )\r
184 {\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
188         {\r
189                 /*\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
192                 */\r
193                 if (device->isWindowActive())\r
194                 {\r
195                         /*\r
196                                 Show FPS and some counters to show which parts of an app run\r
197                                 in different app-lifecycle states.\r
198                         */\r
199                         if ( infoText )\r
200                         {\r
201                                 stringw str = L"FPS:";\r
202                                 str += (s32)device->getVideoDriver()->getFPS();\r
203                                 str += L" r:";\r
204                                 str += runCounter;\r
205                                 str += L" l:";\r
206                                 str += loop;\r
207                                 infoText->setText ( str.c_str() );\r
208                         }\r
209 \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
214                 }\r
215                 device->yield(); // probably nicer to the battery\r
216                 ++runCounter;\r
217                 ++loop;\r
218         }\r
219 }\r
220 \r
221 /* Main application code. */\r
222 void android_main(android_app* app)\r
223 {\r
224         // Make sure glue isn't stripped.\r
225         app_dummy();\r
226 \r
227         /*\r
228                 The receiver can already receive system events while createDeviceEx is called.\r
229                 So we create it first.\r
230         */\r
231         MyEventReceiver receiver(app);\r
232 \r
233         /*\r
234                 Create the device.\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
241         */\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
247         param.Bits = 24;\r
248         param.ZBufferBits = 16;\r
249         param.AntiAlias  = 0;\r
250         param. EventReceiver = &receiver;\r
251 \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
254         */\r
255 #ifndef _DEBUG\r
256         param.LoggingLevel = ELL_NONE;\r
257 #endif\r
258 \r
259         IrrlichtDevice *device = createDeviceEx(param);\r
260         if (device == 0)\r
261         return;\r
262 \r
263         receiver.Init(device);\r
264 \r
265 //      ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_FULLSCREEN, 0);\r
266 \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
272 \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
276         */\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
280 \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
286 \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
294         */\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
298 \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
302 \r
303 \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
307 \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
312         {\r
313                 IFileArchive* archive = fs->getFileArchive(i);\r
314                 if ( archive->getType() == EFAT_ANDROID_ASSET )\r
315                 {\r
316                         archive->addDirectoryToFileList(mediaPath);\r
317                         break;\r
318                 }\r
319         }\r
320 \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
327         else\r
328                 font = guienv->getFont(mediaPath + "bigfont.png");\r
329         if (font)\r
330                 skin->setFont(font);\r
331 \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
336 \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
342         {\r
343                 /* Scale to make it better visible on high-res devices (we could also work with dpi here).\r
344                 */\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
351         }\r
352 \r
353         /*\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
357         */\r
358         IAnimatedMesh* mesh = smgr->getMesh(mediaPath + "dwarf.x");\r
359         if (!mesh)\r
360         {\r
361                 device->closeDevice();\r
362                 device->drop();\r
363         return;\r
364         }\r
365         smgr->addAnimatedMeshSceneNode( mesh );\r
366 \r
367 \r
368         /*\r
369         To look at the mesh, we place a camera.\r
370         */\r
371         smgr->addCameraSceneNode(0, vector3df(15,40,-90), vector3df(0,30,0));\r
372 \r
373         /*\r
374                 Mainloop. Applications usually never quit themself in Android. The OS is responsible for that.\r
375         */\r
376         mainloop(device, text);\r
377 \r
378         /* Cleanup */\r
379         device->setEventReceiver(0);\r
380         device->closeDevice();\r
381         device->drop();\r
382 }\r
383 \r
384 #endif  // defined(_IRR_ANDROID_PLATFORM_)\r
385 \r
386 /*\r
387 **/\r