Disable viewport adaptation in irrlicht

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
Noiecity
Posts: 315
Joined: Wed Aug 23, 2023 7:22 pm
Contact:

Disable viewport adaptation in irrlicht

Post by Noiecity »

When I change the resolution of my window, the viewport changes with it, does irrlicht have a way to disable it?

Code: Select all

#include <windows.h>
#include <irrlicht.h>

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

#pragma comment(lib, "Irrlicht.lib")

static HWND g_hWnd;
static HBRUSH g_bgBrush = NULL;

static IrrlichtDevice* device = nullptr;
static IVideoDriver* driver = nullptr;
static ISceneManager* smgr = nullptr;
static IGUIEnvironment* guienv = nullptr;


// -------------------
// Main
// -------------------
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    static char buffer[100];

    switch (msg) {
		
		case WM_ERASEBKGND:
			return 1;
        case WM_TIMER:
            return 0;

        case WM_CHAR: {
            return 0;
        }

        case WM_KEYDOWN:
            return 0;

        case WM_CLOSE:
            DestroyWindow(hWnd);
            return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }
}

// -------------------
// Loop with rows funcs
// -------------------
typedef void (*LoopFunc)();
static MSG g_msg;
static unsigned char g_exitFlag = 0;

void loop_func();
void end_func();

LoopFunc jump_targets[] = { loop_func, end_func };

void loop_func() {
    BOOL hasMessage = GetMessage(&g_msg, NULL, 0, 0) * (1 - g_exitFlag);
    unsigned char isQuitMsg = (g_msg.message == WM_QUIT);
    g_exitFlag += (hasMessage <= 0) | isQuitMsg;

    switch (!!hasMessage * (1 - isQuitMsg)) {
        case 1:
			device->run();
            TranslateMessage(&g_msg);
            DispatchMessage(&g_msg);
			driver->beginScene(true, true, SColor(255, 30, 30, 30));
            
			smgr->drawAll();
            guienv->drawAll();
            driver->endScene();
    }

    jump_targets[!!g_exitFlag]();
}

void end_func() {
    KillTimer(g_hWnd, 1);
    UnregisterClass("TestWindow", GetModuleHandle(NULL));
    ExitProcess((UINT)g_msg.wParam);
}

// -------------------
// WinMain
// -------------------
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) {
    g_bgBrush = CreateSolidBrush(RGB(30, 30, 30));

    WNDCLASSEX wc = {
        sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_OWNDC, WndProc,
        0, 0, hInst, NULL, LoadCursor(NULL, IDC_ARROW), g_bgBrush,
        NULL, "TestWindow", NULL
    };
    RegisterClassEx(&wc);

    g_hWnd = CreateWindowEx(0, "TestWindow", "Irrlicht master race",
                            WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                            512, 512, NULL, NULL, hInst, NULL);

    SetTimer(g_hWnd, 1, 16, NULL);
    ShowWindow(g_hWnd, nShow);
    UpdateWindow(g_hWnd);
	    SIrrlichtCreationParameters params;
    params.DriverType = EDT_SOFTWARE;
    params.WindowId = g_hWnd;
    params.Bits = 16;
    params.WindowSize = dimension2d<u32>(512, 512);
    params.AntiAlias = false;
    params.Fullscreen = false;
    params.ZBufferBits = 16;
    params.Stencilbuffer = false;

    device = createDeviceEx(params);
    if (!device) return 1;

    device->setWindowCaption(L"Irrlicht win32");

    driver = device->getVideoDriver();
    smgr = device->getSceneManager();
    guienv = device->getGUIEnvironment();

    // --- Loading assets ---
    IAnimatedMesh* mesh = smgr->getMesh("../../media/Monster3_forirrlicht.md2");
    IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh);
    if (node) {
        node->setScale(vector3df(14, 14, 14));
        node->setRotation(vector3df(0, -60, 0));
        node->setMD2Animation("salute");
        node->setMaterialTexture(0, driver->getTexture("../../media/Monster3_Albedo.jpg"));
        node->setMaterialFlag(EMF_LIGHTING, true);
        node->addShadowVolumeSceneNode();
    }

    ISceneNode* box = smgr->addCubeSceneNode(10.0f, 0, -1, vector3df(0, -2.0f, 0),
                                             vector3df(0, 0, 0), vector3df(10, 0.3f, 10));
    if (box) {
        box->setMaterialFlag(EMF_LIGHTING, true);
        box->setMaterialTexture(0, driver->getTexture("../../media/rockwall.jpg"));
    }
	
    smgr->addLightSceneNode(0, vector3df(-20, 29, 3), SColorf(1.0f, 1.0f, 1.0f), 100.0f);
    smgr->addCameraSceneNode(0, vector3df(0, 30, -80), vector3df(0, 5, 0));


    jump_targets[0]();  // start loop

    return 0;
}

**
If you are looking for people with whom to develop your game, even to try functionalities, I can help you, for free. CC0 man.

Image
**
CuteAlien
Admin
Posts: 9926
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Disable viewport adaptation in irrlicht

Post by CuteAlien »

In Irrlicht trunk you can disable viewport clipping since a while when working with OpenGL (second parameter in setViewPort). It won't work with other drivers. So you can set it to old values after changing resolution. Note that I've not yet worked myself with this (it was a feature wish on the forum a while ago), so I've not much experience with this.

I'm not sure what you are trying to do. But note that often people change the FOV of the camera after resolution changes to adapt to the screen. So if that works for your case, that's the better solution.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Noiecity
Posts: 315
Joined: Wed Aug 23, 2023 7:22 pm
Contact:

Re: Disable viewport adaptation in irrlicht

Post by Noiecity »

cuteGod wrote: Thu Jun 12, 2025 10:33 am In Irrlicht trunk you can disable viewport clipping since a while when working with OpenGL (second parameter in setViewPort). It won't work with other drivers. So you can set it to old values after changing resolution. Note that I've not yet worked myself with this (it was a feature wish on the forum a while ago), so I've not much experience with this.

I'm not sure what you are trying to do. But note that often people change the FOV of the camera after resolution changes to adapt to the screen. So if that works for your case, that's the better solution.
Thanks cuteGod, I am using SVNTrunk, excellent work on it. For now I'll settle for solving it as example 14 from the official irrlicht tutorials. I intended to do a low resolution render to texture, hide irrlicht and scale it using the win32 api to keep CPU consumption low.
**
If you are looking for people with whom to develop your game, even to try functionalities, I can help you, for free. CC0 man.

Image
**
Noiecity
Posts: 315
Joined: Wed Aug 23, 2023 7:22 pm
Contact:

Re: Disable viewport adaptation in irrlicht

Post by Noiecity »

Here is the code to auto-scale the image without scaling Irrlicht, i.e., it is rendered at 128x128 and then using the Windows API it is auto-scaled while preserving Irrlicht's low consumption at 128x128. A double buffer is used to avoid flickering.
It's work.

Code: Select all

#include <windows.h>
#include <irrlicht.h>
#pragma comment(lib, "Irrlicht.lib")

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

// --- Globals for Win32 and Double Buffering ---
static HWND g_hWnd;
static HWND g_hIrrlichtWnd;
static HBRUSH g_bgBrush = NULL;
static HDC g_hMemDC = NULL;
static HBITMAP g_hMemBitmap = NULL;
static HBITMAP g_hOldBitmap = NULL;

// --- Globals for Irrlicht ---
static IrrlichtDevice* device = nullptr;
static IVideoDriver* driver = nullptr;
static ISceneManager* smgr = nullptr;
static ITexture* renderTexture = nullptr;

// --- Globals for the Irrlicht Bitmap ---
static BITMAPINFO bmi = {};
static HBITMAP hBitmap = nullptr;
static void* bmpBits = nullptr;

// --- Globals for the Loop and Timing ---
typedef void (*LoopFunc)();
static MSG g_msg;
static unsigned char g_exitFlag = 0;
static DWORD g_lastTick = 0;
const DWORD FRAME_INTERVAL_MS = 1000 / 30; // ~30 FPS

// --- Function Declarations ---
void loop_func();
void end_func();
LoopFunc jump_targets[] = { loop_func, end_func };

// --- Bitmap Surface Creation/Update (unchanged) ---
void CreateBitmapSurface() {
    ZeroMemory(&bmi, sizeof(BITMAPINFO));
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = 128;
    bmi.bmiHeader.biHeight = -128;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 24;
    bmi.bmiHeader.biCompression = BI_RGB;

    HDC hdc = GetDC(NULL);
    hBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bmpBits, NULL, 0);
    ReleaseDC(NULL, hdc);
}

void UpdateBitmapFromTexture() {
    if (!driver || !renderTexture || !bmpBits) return;

    IImage* image = driver->createImage(renderTexture, position2d<s32>(0, 0), dimension2d<u32>(128, 128));
    if (!image) return;

    u8* dst = (u8*)bmpBits;
    for (u32 y = 0; y < 128; ++y) {
        for (u32 x = 0; x < 128; ++x) {
            SColor c = image->getPixel(x, y);
            int offset = (y * 128 + x) * 3;
            dst[offset] = c.getBlue();
            dst[offset + 1] = c.getGreen();
            dst[offset + 2] = c.getRed();
        }
    }
    image->drop();
}

// -------------------
// Main Window Procedure (With Double Buffering)
// -------------------
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_CREATE: // Create memory DC on startup
            g_hMemDC = CreateCompatibleDC(NULL);
            return 0;

        case WM_SIZE: { // Resize the double buffer bitmap
            if (g_hMemBitmap) DeleteObject(g_hMemBitmap);
            HDC hdc = GetDC(hWnd);
            int width = LOWORD(lParam);
            int height = HIWORD(lParam);
            g_hMemBitmap = CreateCompatibleBitmap(hdc, width, height);
            ReleaseDC(hWnd, hdc);
            g_hOldBitmap = (HBITMAP)SelectObject(g_hMemDC, g_hMemBitmap);
            return 0;
        }

        case WM_ERASEBKGND: // Prevent background erasure, double buffer handles it
            return 1;

        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // --- START DOUBLE BUFFER ---
            // 1. Draw everything on the memory HDC (g_hMemDC)
            RECT rc;
            GetClientRect(hWnd, &rc);
            FillRect(g_hMemDC, &rc, g_bgBrush); // Clear the buffer background
            if (hBitmap && bmpBits) {
                int winWidth = rc.right - rc.left;
                int winHeight = rc.bottom - rc.top;
                int size = min(winWidth, winHeight);
                int x = (winWidth - size) / 2;
                int y = (winHeight - size) / 2;
                SetStretchBltMode(g_hMemDC, COLORONCOLOR);
                StretchDIBits(g_hMemDC, x, y, size, size, 0, 0, 128, 128,
                              bmpBits, &bmi, DIB_RGB_COLORS, SRCCOPY);
            }
            // 2. Copy the memory buffer to the screen in one go
            BitBlt(hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top, g_hMemDC, 0, 0, SRCCOPY);
            // --- END DOUBLE BUFFER ---
            EndPaint(hWnd, &ps);
            return 0;
        }

        case WM_DESTROY:
            // Cleanup double buffer
            SelectObject(g_hMemDC, g_hOldBitmap);
            DeleteObject(g_hMemBitmap);
            DeleteDC(g_hMemDC);
            PostQuitMessage(0);
            return 0;

        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }
}

// -------------------
// Main Loop (Restored to your style)
// -------------------
void loop_func() {
    // Process messages without blocking and without 'if'
    unsigned char hasMessage = !!PeekMessage(&g_msg, NULL, 0, 0, PM_REMOVE);
    g_exitFlag |= (g_msg.message == WM_QUIT) * hasMessage;

    // Use the result of hasMessage to decide whether to dispatch
    // This avoids an 'if', as in your original design.
    switch(hasMessage * (1-g_exitFlag)) {
        case 1:
            TranslateMessage(&g_msg);
            DispatchMessage(&g_msg);
    }

    // --- Time control with arithmetic, without 'if' ---
    DWORD currentTick = GetTickCount();
    DWORD deltaTime = currentTick - g_lastTick;

    // 'shouldRender' will be 1 if enough time has passed, 0 otherwise.
    unsigned char shouldRender = (deltaTime >= FRAME_INTERVAL_MS);

    // Update 'lastTick' only if we render, using multiplication
    g_lastTick += FRAME_INTERVAL_MS * shouldRender;

    // Use switch, which is closer to your original design than 'if'
    switch (shouldRender * (!!device->run())) {
        case 1: // Only executes if shouldRender is 1 and device->run() is true
            driver->beginScene(false, false);
            driver->setRenderTarget(renderTexture, true, true, SColor(255, 30, 30, 30));
            smgr->drawAll();
            driver->setRenderTarget(0);
            driver->endScene();
            UpdateBitmapFromTexture();
            InvalidateRect(g_hWnd, NULL, FALSE); // FALSE to not erase background
            Sleep(32); // ~30 FPS
            break;
    }

    // The Irrlicht engine might want to close the app
    g_exitFlag |= (!device->run());

    // Jump to the next state (loop or end)
    jump_targets[g_exitFlag]();
}

void end_func() {
    if (device) {
        device->closeDevice();
        device->drop();
    }
    if (hBitmap) DeleteObject(hBitmap);
    if (g_bgBrush) DeleteObject(g_bgBrush);
    UnregisterClass("TestWindow", GetModuleHandle(NULL));
    ExitProcess((UINT)g_msg.wParam);
}

// -------------------
// WinMain (with minor adjustments)
// -------------------
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) {
    g_bgBrush = CreateSolidBrush(RGB(0, 0, 0));

    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0,
                      hInst, NULL, LoadCursor(NULL, IDC_ARROW), NULL, NULL, "TestWindow", NULL };
    RegisterClassEx(&wc);

    g_hWnd = CreateWindowEx(0, "TestWindow", "Irrlicht Bitmap Render", WS_OVERLAPPEDWINDOW,
                            CW_USEDEFAULT, CW_USEDEFAULT, 512, 512, NULL, NULL, hInst, NULL);

    g_hIrrlichtWnd = CreateWindowEx(0, "STATIC", NULL, WS_CHILD, 0, 0, 128, 128, g_hWnd, NULL, hInst, NULL);

    // --- Irrlicht Initialization ---
    SIrrlichtCreationParameters params;
    params.DriverType = EDT_SOFTWARE;
    params.WindowId = g_hIrrlichtWnd;
    params.WindowSize = dimension2d<u32>(128, 128);
    params.Bits = 8;

    device = createDeviceEx(params);
    if (!device) return 1;

    driver = device->getVideoDriver();
    smgr = device->getSceneManager();
    ShowWindow(g_hIrrlichtWnd, SW_HIDE);

    renderTexture = driver->addRenderTargetTexture(dimension2d<u32>(128, 128), "RTT1", ECF_R8G8B8);

    // --- Asset Loading ---
    IAnimatedMesh* mesh = smgr->getMesh("../../media/Monster3_forirrlicht.md2");
    IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh);
    if (node) {
        node->setMaterialFlag(EMF_LIGHTING, false);
        node->setMaterialTexture(0, driver->getTexture("../../media/Monster3_Albedo.jpg"));
        node->setMD2Animation("salute");
        node->setScale(vector3df(14, 14, 14));
        node->setRotation(vector3df(0, -60, 0));
    }

    smgr->addCameraSceneNode(0, vector3df(0, 30, -80), vector3df(0, 5, 0));

    // --- Final Initialization ---
    CreateBitmapSurface();
    ShowWindow(g_hWnd, nShow);
    UpdateWindow(g_hWnd);

    g_lastTick = GetTickCount(); // Start the timer
    jump_targets[0](); // Start the loop
    return 0;
}
For more "blur" results, change this line:

Code: Select all

SetStretchBltMode(g_hMemDC, COLORONCOLOR);
For this line:

Code: Select all

SetStretchBltMode(g_hMemDC, HALFTONE);

At the moment it is functional and consumes very little CPU, but it could consume much less if it continued to predict the behavior of the application with bitwise operators or arithmetic operators instead of conditions and loops at runtime... but for now I give up.
**
If you are looking for people with whom to develop your game, even to try functionalities, I can help you, for free. CC0 man.

Image
**
Post Reply