Basic Android Studio Template for irrlicht

Post your questions, suggestions and experiences regarding game design, integration of external libraries here. For irrEdit, irrXML and irrKlang, see the
ambiera forums
Post Reply
Noiecity
Posts: 407
Joined: Wed Aug 23, 2023 7:22 pm
Contact:

Basic Android Studio Template for irrlicht

Post by Noiecity »

https://drive.google.com/file/d/1QF7TrI ... sp=sharing

Image

Get there, unzip, open the unzipped folder with android studio as if you were going to open a project, sync, run. The main.cpp is compiled and the apk is produced.


Image

Image

Image

Image

Image

Image

Image

I didn't touch the example code; I just tried to create a template that I could easily reuse. I don't know if it will work on other computers. I followed all of chatgpt's instructions to make it as accessible as possible, lmao.

It comes with support for arm v7 and v8.

I see a small dwarf, I can't see its texture and I haven't touched the code. I remember compiling without libpng using neon... maybe that's it, maybe not (should libpng be compiled normally?
...No, the irrlicht logo loads correctly and uses PNG.)
Irrlicht is love, Irrlicht is life, long live to Irrlicht
n00bc0de
Posts: 140
Joined: Tue Oct 04, 2022 1:21 am

Re: Basic Android Studio Template for irrlicht

Post by n00bc0de »

I was having an issue with textures not rendering on a model and I fixed it by disabling the use mishaps flag on the scene node.
Noiecity
Posts: 407
Joined: Wed Aug 23, 2023 7:22 pm
Contact:

Re: Basic Android Studio Template for irrlicht

Post by Noiecity »

I was just thinking about using your engine and switching to Linux, considering it's based on Irrlicht. It's ironic; the comment left that you recommended is similar
Irrlicht is love, Irrlicht is life, long live to Irrlicht
Noiecity
Posts: 407
Joined: Wed Aug 23, 2023 7:22 pm
Contact:

Re: Basic Android Studio Template for irrlicht

Post by Noiecity »

n00bc0de wrote: Thu Jul 30, 2026 7:27 pm I was having an issue with textures not rendering on a model and I fixed it by disabling the use mishaps flag on the scene node.
thanks, now it's work, but I can only load the first texture slot
Image

Code: Select all

/** Example 027 Helloworld_Android
	This example shows a simple application for Android.
	Modified to disable mipmaps and optimize texture display.
*/

#include <irrlicht/irrlicht.h>

#ifdef _IRR_ANDROID_PLATFORM_

#include <android_native_app_glue/android_native_app_glue.h>
#include "android_tools.h"
#include "android/window.h"

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

// GUI Element IDs
enum GUI_IDS
{
    GUI_INFO_FPS,
    GUI_IRR_LOGO,
};

//=============================================================================
// Event Receiver Class
//=============================================================================
class MyEventReceiver : public IEventReceiver
{
public:
    MyEventReceiver(android_app* app)
            : Device(0), AndroidApp(app), SpriteToMove(0), TouchID(-1)
    {
    }

    void Init(IrrlichtDevice *device)
    {
        Device = device;
    }

    virtual bool OnEvent(const SEvent& event)
    {
        if (event.EventType == EET_TOUCH_INPUT_EVENT)
        {
            // Fake mouse-events for GUI compatibility
            SEvent fakeMouseEvent;
            fakeMouseEvent.EventType = EET_MOUSE_INPUT_EVENT;
            fakeMouseEvent.MouseInput.X = event.TouchInput.X;
            fakeMouseEvent.MouseInput.Y = event.TouchInput.Y;
            fakeMouseEvent.MouseInput.Shift = false;
            fakeMouseEvent.MouseInput.Control = false;
            fakeMouseEvent.MouseInput.ButtonStates = 0;
            fakeMouseEvent.MouseInput.Event = EMIE_COUNT;

            switch (event.TouchInput.Event)
            {
                case ETIE_PRESSED_DOWN:
                {
                    if (TouchID == -1)
                    {
                        fakeMouseEvent.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;

                        if (Device)
                        {
                            position2d<s32> touchPoint(event.TouchInput.X, event.TouchInput.Y);
                            IGUIElement * logo = Device->getGUIEnvironment()->getRootGUIElement()->getElementFromId(GUI_IRR_LOGO);
                            if (logo && logo->isPointInside(touchPoint))
                            {
                                TouchID = event.TouchInput.ID;
                                SpriteToMove = logo;
                                SpriteStartRect = SpriteToMove->getRelativePosition();
                                TouchStartPos = touchPoint;
                            }
                        }
                    }
                    break;
                }
                case ETIE_MOVED:
                    if (TouchID == event.TouchInput.ID)
                    {
                        fakeMouseEvent.MouseInput.Event = EMIE_MOUSE_MOVED;
                        fakeMouseEvent.MouseInput.ButtonStates = EMBSM_LEFT;

                        if (SpriteToMove && TouchID == event.TouchInput.ID)
                        {
                            position2d<s32> touchPoint(event.TouchInput.X, event.TouchInput.Y);
                            MoveSprite(touchPoint);
                        }
                    }
                    break;
                case ETIE_LEFT_UP:
                    if (TouchID == event.TouchInput.ID)
                    {
                        fakeMouseEvent.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;

                        if (SpriteToMove)
                        {
                            TouchID = -1;
                            position2d<s32> touchPoint(event.TouchInput.X, event.TouchInput.Y);
                            MoveSprite(touchPoint);
                            SpriteToMove = 0;
                        }
                    }
                    break;
                default:
                    break;
            }

            if (fakeMouseEvent.MouseInput.Event != EMIE_COUNT && Device)
            {
                Device->postEventFromUser(fakeMouseEvent);
            }
        }
        else if (event.EventType == EET_GUI_EVENT)
        {
            // Handle soft keyboard visibility
            switch(event.GUIEvent.EventType)
            {
                case EGET_EDITBOX_ENTER:
                    if (event.GUIEvent.Caller->getType() == EGUIET_EDIT_BOX)
                    {
                        if(Device->getGUIEnvironment())
                            Device->getGUIEnvironment()->setFocus(NULL);
                        android::setSoftInputVisibility(AndroidApp, false);
                    }
                    break;
                case EGET_ELEMENT_FOCUS_LOST:
                    if (event.GUIEvent.Caller->getType() == EGUIET_EDIT_BOX)
                    {
                        android::setSoftInputVisibility(AndroidApp, false);
                    }
                    break;
                case EGET_ELEMENT_FOCUSED:
                    if (event.GUIEvent.Caller->getType() == EGUIET_EDIT_BOX)
                    {
                        android::setSoftInputVisibility(AndroidApp, true);
                    }
                    break;
                default:
                    break;
            }
        }

        return false;
    }

    void MoveSprite(const irr::core::position2d<irr::s32> &touchPos)
    {
        irr::core::position2d<irr::s32> move(touchPos - TouchStartPos);
        SpriteToMove->setRelativePosition(SpriteStartRect.UpperLeftCorner + move);
    }

private:
    IrrlichtDevice * Device;
    android_app* AndroidApp;
    gui::IGUIElement * SpriteToMove;
    core::rect<s32> SpriteStartRect;
    core::position2d<irr::s32> TouchStartPos;
    s32 TouchID;
};

//=============================================================================
// Main Loop
//=============================================================================
void mainloop(IrrlichtDevice *device, IGUIStaticText * infoText)
{
    u32 loop = 0;
    static u32 runCounter = 0;

    while(device->run())
    {
        if (device->isWindowActive())
        {
            // Force full screen viewport
            IVideoDriver* driver = device->getVideoDriver();
            if (driver)
            {
                core::dimension2d<u32> screenSize = driver->getScreenSize();
                driver->setViewPort(core::rect<s32>(0, 0, screenSize.Width, screenSize.Height));
            }

            // Update FPS counter
            if (infoText)
            {
                stringw str = L"FPS:";
                str += (s32)device->getVideoDriver()->getFPS();
                str += L" r:";
                str += runCounter;
                str += L" l:";
                str += loop;
                infoText->setText(str.c_str());
            }

            device->getVideoDriver()->beginScene(true, true, SColor(0,100,100,100));
            device->getSceneManager()->drawAll();
            device->getGUIEnvironment()->drawAll();
            device->getVideoDriver()->endScene();
        }
        device->yield();
        ++runCounter;
        ++loop;
    }
}

//=============================================================================
// Configure Texture Settings - DISABLE MIPMAPS
//=============================================================================
void configureTextureSettings(IVideoDriver* driver)
{
    if (!driver) return;

    // *** DISABLE MIPMAPS GLOBALLY ***
    driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

    // Note: ETCF_ALLOW_ANISOTROPIC_FILTER doesn't exist in some versions
    // Use material flags instead for anisotropic filtering
}

//=============================================================================
// Create GUI Elements
//=============================================================================
void createGUI(IGUIEnvironment* guienv, IVideoDriver* driver,
               IGUIStaticText** outInfoText, IGUIImage** outLogo,
               s32 windowWidth, const stringc& mediaPath)
{
    if (!guienv || !driver) return;

    // Create info text
    *outInfoText = guienv->addStaticText(
            L"Loading...",
            rect<s32>(5, 5, 635, 35),
            false, false, 0, GUI_INFO_FPS
    );

    // Create edit box
    guienv->addEditBox(L"", rect<s32>(5, 40, 475, 80));

    // Create Irrlicht logo without mipmaps
    // Note: getTexture with bool parameter may not be available in all versions
    ITexture* logoTexture = driver->getTexture((mediaPath + "irrlichtlogo3.png").c_str());
    if (logoTexture)
    {
        *outLogo = guienv->addImage(
                logoTexture,
                core::position2d<s32>(5, 85),
                true, 0, GUI_IRR_LOGO
        );

        // Scale logo for high-res displays
        if (*outLogo)
        {
            s32 minLogoWidth = windowWidth / 3;
            if ((*outLogo)->getRelativePosition().getWidth() < minLogoWidth)
            {
                (*outLogo)->setScaleImage(true);
                core::rect<s32> logoPos((*outLogo)->getRelativePosition());
                f32 scale = (f32)minLogoWidth / (f32)logoPos.getWidth();
                logoPos.LowerRightCorner.X = logoPos.UpperLeftCorner.X + minLogoWidth;
                logoPos.LowerRightCorner.Y = logoPos.UpperLeftCorner.Y +
                                             (s32)((f32)logoPos.getHeight() * scale);
                (*outLogo)->setRelativePosition(logoPos);
            }
        }
    }
}

//=============================================================================
// Create 3D Scene with Character
//=============================================================================
void createScene(ISceneManager* smgr, IVideoDriver* driver,
                 ILogger* logger, const stringc& mediaPath,
                 s32 windowWidth, s32 windowHeight)
{
    if (!smgr || !driver) return;

    // Load character mesh
    IAnimatedMesh* mesh = smgr->getMesh((mediaPath + "dwarf.x").c_str());
    if (!mesh)
    {
        logger->log("Failed to load dwarf.x mesh", ELL_ERROR);
        return;
    }

    // Create character node
    IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh);
    if (!node)
    {
        logger->log("Failed to create animated mesh scene node", ELL_ERROR);
        return;
    }

    // Load texture
    ITexture* texture = driver->getTexture((mediaPath + "dwarf.jpg").c_str());
    ITexture* texture2 = driver->getTexture((mediaPath + "axe.jpg").c_str());
    if (texture&&texture2)
    {
        // Apply texture to material
        node->setMaterialTexture(0, texture);
        node->setMaterialTexture(1, texture2);
        node->setMaterialFlag(video::EMF_LIGHTING, false);

        // Configure material for optimal display without mipmaps
        SMaterial& material = node->getMaterial(0);
        material.TextureLayer[0].BilinearFilter = false;
        material.TextureLayer[0].TrilinearFilter = false;
        material.TextureLayer[0].AnisotropicFilter = 0;
        material.UseMipMaps = false;

        logger->log("Texture dwarf.jpg loaded successfully without mipmaps", ELL_INFORMATION);
    }
    else
    {
        logger->log("Failed to load dwarf.jpg texture", ELL_ERROR);
    }

    // Configure character
    node->setScale(core::vector3df(0.4f, 0.4f, 0.4f));
    node->setAnimationSpeed(30);
    node->setLoopMode(true);

    // Add ambient light
    smgr->setAmbientLight(video::SColorf(0.8f, 0.8f, 0.8f, 1.0f));

    // Create camera with correct aspect ratio
    ICameraSceneNode* camera = smgr->addCameraSceneNode(
            0,
            vector3df(0, 15, -40),  // Camera position
            vector3df(0, 10, 0)     // Look-at target
    );

    if (camera)
    {
        f32 aspectRatio = (f32)windowWidth / (f32)windowHeight;
        camera->setAspectRatio(aspectRatio);
        camera->setFOV(core::PI / 3.5f);

        // Log using string concatenation
        core::stringc logMsg = "Camera configured with aspect ratio: ";
        logMsg += aspectRatio;
        logger->log(logMsg.c_str(), ELL_INFORMATION);
    }
}

//=============================================================================
// Main Application Entry Point
//=============================================================================
void android_main(android_app* app)
{
    // Make sure glue isn't stripped
    app_dummy();

    // Create event receiver
    MyEventReceiver receiver(app);

    // Configure device creation parameters
    SIrrlichtCreationParameters param;
    param.DriverType = EDT_OGLES2;              // Use OpenGL ES 2.0
    param.WindowSize = dimension2d<u32>(0, 0);  // Auto-detect size
    param.Fullscreen = true;                    // Full screen
    param.PrivateData = app;
    param.Bits = 24;
    param.ZBufferBits = 16;
    param.AntiAlias = 0;
    param.EventReceiver = &receiver;

#ifndef _DEBUG
    param.LoggingLevel = ELL_NONE;
#endif

    // Create Irrlicht device
    IrrlichtDevice *device = createDeviceEx(param);
    if (!device)
        return;

    // Initialize event receiver
    receiver.Init(device);

    // Get core components
    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
    IGUIEnvironment* guienv = device->getGUIEnvironment();
    ILogger* logger = device->getLogger();
    IFileSystem* fs = device->getFileSystem();

    // Configure texture settings - DISABLE MIPMAPS
    configureTextureSettings(driver);
    logger->log("Mipmaps disabled globally", ELL_INFORMATION);

    // Get window and display information
    ANativeWindow* nativeWindow = static_cast<ANativeWindow*>(
            driver->getExposedVideoData().OGLESAndroid.Window
    );
    int32_t windowWidth = ANativeWindow_getWidth(app->window);
    int32_t windowHeight = ANativeWindow_getHeight(app->window);

    // Get display metrics
    irr::android::SDisplayMetrics displayMetrics;
    memset(&displayMetrics, 0, sizeof(displayMetrics));
    irr::android::getDisplayMetrics(app, displayMetrics);

    // Log system information using string concatenation
    core::stringc logMsg;
    logMsg = "Window: ";
    logMsg += windowWidth;
    logMsg += "x";
    logMsg += windowHeight;
    logMsg += ", Display: ";
    logMsg += displayMetrics.widthPixels;
    logMsg += "x";
    logMsg += displayMetrics.heightPixels;
    logMsg += ", DPI: ";
    logMsg += displayMetrics.xdpi;
    logger->log(logMsg.c_str(), ELL_INFORMATION);

    // Set initial viewport
    core::dimension2d<s32> dim(driver->getScreenSize());
    driver->setViewPort(core::rect<s32>(0, 0, dim.Width, dim.Height));
    logger->log("Viewport set to full screen", ELL_INFORMATION);

    // Configure Android assets file system
    stringc mediaPath = "media/";
    for (u32 i = 0; i < fs->getFileArchiveCount(); ++i)
    {
        IFileArchive* archive = fs->getFileArchive(i);
        if (archive->getType() == EFAT_ANDROID_ASSET)
        {
            archive->addDirectoryToFileList(mediaPath);
            logger->log("Added media directory to asset archive", ELL_INFORMATION);
            break;
        }
    }

    // Set font based on DPI
    IGUISkin* skin = guienv->getSkin();
    IGUIFont* font = 0;
    if (displayMetrics.xdpi < 100)
        font = guienv->getFont((mediaPath + "fonthaettenschweiler.bmp").c_str());
    else
        font = guienv->getFont((mediaPath + "bigfont.png").c_str());

    if (font)
        skin->setFont(font);

    // Create GUI elements
    IGUIStaticText* infoText = nullptr;
    IGUIImage* logo = nullptr;
    createGUI(guienv, driver, &infoText, &logo, windowWidth, mediaPath);

    // Create 3D scene
    createScene(smgr, driver, logger, mediaPath, windowWidth, windowHeight);

    // Enter main loop
    logger->log("Entering main loop", ELL_INFORMATION);
    mainloop(device, infoText);

    // Cleanup
    logger->log("Cleaning up", ELL_INFORMATION);
    device->setEventReceiver(0);
    device->closeDevice();
    device->drop();
}

#endif // defined(_IRR_ANDROID_PLATFORM_)
Maybe I wrote the code wrong? I'm not sure.

Image
Irrlicht is love, Irrlicht is life, long live to Irrlicht
n00bc0de
Posts: 140
Joined: Tue Oct 04, 2022 1:21 am

Re: Basic Android Studio Template for irrlicht

Post by n00bc0de »

I am away from my development pc for the next week so I can't test mult-texture materials right now. If you are having that issue then I might end up having that issue myself. I am glad to see that my suggestion did give you some good results.

I want to test this out myself when I get back. I would appreciate it if you could let me know if you find a solution.
Post Reply