mimicing retro dungeon crawler UI (RTT?)

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.
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

mimicing retro dungeon crawler UI (RTT?)

Post by Cube_ »

so I want to mimic older dungeon crawler style guis, such as Eye of the Beholder
Image

I figure the best way to do this is RTT to a screensize-n% quad (with another quad behind it that hosts a background)

problem is, I don't know any good method for calculating a screensize quad, subtracting percentages is easy but the quad size is the hard part. (not to mention it has to be placed flush with the camera)

currently I have

Code: Select all

rt = driver->addRenderTargetTexture(core::dimension2d<u32>(res.Width*2, res.Height*2), "RTT1"); //RTT gives me free supersampling too so that's nice, or subsampling if I want that.
        test->setMaterialTexture(0, rt); // set material of cube to render target
 
        // add fixed camera
        fixedCam = smgr->addCameraSceneNode(0, core::vector3df(-15,2000,-105), core::vector3df(-10,200,-100))
I figure I should only have to add a quad with programmatically determined sizes - just not entirely sure how to accomplish that.

also not sure if RTT is the best way to do it but it seems reasonable enough, I use a moving camera in the level - render to texture, then render the quad that's way outside the level, the entire level's culled so that's only +16 or so drawcalls per frame (taking into account gui elements).
"this is not the bottleneck you are looking for"
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: mimicing retro dungeon crawler UI (RTT?)

Post by mongoose7 »

You know you can just draw it?

driver->draw2DImage(rt, destrect, srcrect);
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

Re: mimicing retro dungeon crawler UI (RTT?)

Post by Cube_ »

Yes, I am aware that I can draw it to a quad.
I was more wondering what the best way to create said quad with appropriate dimensions is.

as in programmatically picking the arbitrary points in space - I assume camera location is from the center of the camera, or is it from a corner?

unless of course you can draw to a core::rect<s32> in which case it's just a matter of screenspace fractions.
"this is not the bottleneck you are looking for"
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: mimicing retro dungeon crawler UI (RTT?)

Post by mongoose7 »

It's not a quad! It is simply drawing to the screen. An image and two 2-dimensional rectangles. In screen space. Two-dimensional.
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

Re: mimicing retro dungeon crawler UI (RTT?)

Post by Cube_ »

... I see, I will have to attempt this.

docu'd the function for syntax.
const video::ITexture * texture,
const core::position2d< s32 > & destPos

Very convenient, just a position2d.

then there's

const video::ITexture * texture,
const core::position2d< s32 > & destPos,
const core::rect< s32 > & sourceRect,
const core::rect< s32 > * clipRect = 0,
SColor color = SColor(255, 255, 255, 255),
bool useAlphaChannelOfTexture = false

that looks like the one I actually want (there's a third one too but since I'm drawing one image I'd only presumably need the texture and the rect I want to draw it on) - in any case, I have what I need I think.
"this is not the bottleneck you are looking for"
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

Re: mimicing retro dungeon crawler UI (RTT?)

Post by Cube_ »

my eyes are in disagreement with the yielded results.

Code: Select all

video::ITexture* rt = 0;
    scene::ICameraSceneNode* fixedCam = 0;
 
    if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET))
    {
        rt = driver->addRenderTargetTexture(core::dimension2d<u32>(res.Width*2, res.Height*2), "RTT1");
 
        fixedCam = smgr->addCameraSceneNode(0, core::vector3df(-15,2000,-105), core::vector3df(-10,200,-100));
 
    }

Code: Select all

    while(iDevice->run())
    {
        driver->beginScene(true, true, SColor(255,140,255,140));
        if (rt)
        {
            driver->setRenderTarget(rt, true, true, video::SColor(0,0,0,255));
 
            smgr->setActiveCamera(playerCamera);
            smgr->drawAll();
            driver->setRenderTarget(0, true, true, 0);
 
            smgr->setActiveCamera(fixedCam);
        }
 
        driver->draw2DImage(rt, vector2d<s32>(res.Width,res.Height),core::rect<s32>(0,0,res.Width/1.5f,res.Height/1.5f), &core::rect<s32>(0,0,res.Width/1.5f,res.Height/1.5f), SColor(255,255,255,255), true);
 
        smgr->drawAll(); //go away!
        driver->endScene();
    }
I'm probably doing something wrong but my eyes hurt now.
EDIT: Fixed the horrid flickering but I'm just getting a solid black screen, not even my framebuffer color (which is mint green)
any obvious reason?
"this is not the bottleneck you are looking for"
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: mimicing retro dungeon crawler UI (RTT?)

Post by mongoose7 »

Why do you have drawAll after draw2DImage? Presumably things will not go well. Also, I don't understand your draw2DImage, though you have gone to the docs. I would just use draw2DImage(rt, rect(0, 0, w/1.5, h/1.5), rect(0, 0, w, h)), as I said. (I don't know why you are taking only part of the RT.)
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

Re: mimicing retro dungeon crawler UI (RTT?)

Post by Cube_ »

I'm just trying to get ANYTHING to show up at all.

as for
smgr->drawAll(); //go away!

the comment //go away! is to indicate that that was the solution to the flickering.

Anywho, that snippet worked (meaning I must have been typoing somewhere - I kept getting errors saying there was no match for draw2DImage( *ITexture .... very very long error here)
now all I see is blue but that's fine, I can work with this.
probably a misplaced camera.
"this is not the bottleneck you are looking for"
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

Re: mimicing retro dungeon crawler UI (RTT?)

Post by Cube_ »

quasi related camera nonsense.
Welp RTT works, but I'm not getting useful output because I can't quite pinpoint how to move the camera as I want.

the mesh node is pretty simple, I built every face at a scale of 1 unit, I then scaled it up by 10 so each tile is 10.0fx10.0fx10.0f in volume.

scene::ICameraSceneNode* playerCamera = smgr->addCameraSceneNode(0, core::vector3df(70,5,80), core::vector3df(100,5,80));

this SHOULD put this camera in tile 7,8 - in the exact vertical center, pointing at tile 10,8 (or 3 tiles ahead, same height).

this works ok, but I don't want to hardcode it - it also does not seem to actually put the camera at tile center, it seems to be on the seam between tiles. (this is easily proven by testing tile 12,8 - this is the opening to a hallway) - so fine, I'll just add 0.5 tiles to everything. ez...

so let's write a function to do this for me then.

... and done.
time to assign, this should have the camera in tile x,y facing x+30,y.
vector3df pos = spawnPlayer(playerCamera, level);
playerCamera->setPosition(pos);
playerCamera->setTarget(pos+vector3df(30.0f, 0.0f, 0.0f));

expected result: I should have a face of brick... I think, arguably my original test case is ambiguous since the map is a mirror of itself.
actual result: I see the corridor behind me.. wait what? that means my idea of what direction it's facing is wrong.
(EDIT: of course... I build the level from the top left corner and build upwards, this creates a vertically mirrored version :P welp that solves that mystery, still need to remember to update the target every time - I don't know a good way to do this but I just set it to +3 tiles(+30.0f) in the current direction which SEEMS to work)

so I expected this direction:
t
|
|
c

but I got the inverse (c denotes camera, t denotes target). Fine, that I can deal with.

now I just have to remember to update the target appropriately for each of the four cardinal directions (NORTH/SOUTH/EAST/WEST).

also: when using RTT like this the FPS camera can't move, very strange.

assuming, of course, that I'm doing it right, which I'll know soon because I'm just about to write a simple function that moves the camera one tile in the current direction if triggered.

or in other words: the camera isn't behaving as I expected and I felt like a <rant> was in order.
oh and </rant>

in better news: I noticed that if you draw a rect with a texture id of NULL you get a black background (which is ideal, I wanted to keep the GUI background null for now).

the only real question in this rant is really: is it normal for the FPS camera to not be able to move if you've set another camera as the active camera? I expected it should still be able to move but it sorta makes sense that it doesn't - so I'm pretty sure it's on purpose.

EDIT2: as I sorta expected, \mainGame.cpp|63|warning: the address of 'eReceiver' will always evaluate as 'true' [-Waddress]| prevents me from actually fetching input (this proven by debug printing not printing even when the requested key was pressed, or held down for that matter).

hmm, welp time to figure it out.

Code: Select all

MyEventReceiver eReceiver;
    IrrlichtDevice* iDevice = createDevice(EDT_OPENGL, res, 32, fullscreen, vsync, &eReceiver);
 
    //SNIP
    while(iDevice->run())
    {
        if(eReceiver.isKeyDown(irr::KEY_KEY_W))
        {
            std::cout << "moving player" <<std::endl;
            playerCamera->setPosition(movePlayer(direction, playerCamera->getPosition(), keys::KEY_W));
        }
        //SNIP
and of course the event receiver.

Code: Select all

class MyEventReceiver : public IEventReceiver
{
public:
    virtual bool OnEvent(const SEvent& event)
    {
        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
            keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
            return false;
    }
    virtual bool isKeyDown(EKEY_CODE keyCode) const
    {
        return keyIsDown[keyCode];
    }
    MyEventReceiver()
    {
        for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
            keyIsDown[i] = false;
    }
private:
    bool keyIsDown[KEY_KEY_CODES_COUNT];
};
switching both of them to true causes the player to race into the distance, so the moving function WORKS.
that means one of the two false statements has to be true since
false + false = no movement
true + true = always moving
true + false = no movement.
false + true = always moving... wait what?

I don't get it.
"this is not the bottleneck you are looking for"
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: mimicing retro dungeon crawler UI (RTT?)

Post by chronologicaldot »

Interestingly enough, cout isn't exactly a great method for getting real-time output. It just fills a buffer to write out and then does it at its convenience (in a manner of speaking). You can flush the buffer, but who wants to dink around with cout when you could be debugging, right? No pun intended...
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

Re: mimicing retro dungeon crawler UI (RTT?)

Post by Cube_ »

I have tried flushing the buffers and tried printf as well.
I even put a breakpoint on it, gdb never gets there (and if it did it would trigger movement because if I replace the key event with true it moves forward once per frame - clearly working).

if(eReceiver.IsKeyDown(irr::KEY_KEY_W))
literally won't ever evaluate to true.
I'd wager the issue is with my code. probably my event receiver.

making IsKeyDown non-const doesn't help either, so I'm at loss. this code SHOULD work (I mean, it's almost line-for-line identical to the example code from tutorial 4: movement - and that code should work... on paper, I've never built the example).

also in the event it did get there, it doesn't matter if cout doesn't give realtime output.
it doesn't crash and since it doesn't crash cout IS guaranteed to print the output at its earliest convenience (and in any case, what happens after irr::KEY_KEY_W evaluates to true is that it moves in one of four direction, no movement happens ergo something's wrong.
"this is not the bottleneck you are looking for"
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: mimicing retro dungeon crawler UI (RTT?)

Post by CuteAlien »

Haven't read the whole thread now (sry), but just a quick hint - example 18 SplitScreen does to that render to texture stuff and then display it only on a part of the screen. So might help to take a look at that.
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
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

Re: mimicing retro dungeon crawler UI (RTT?)

Post by Cube_ »

mmm, I suppose that might give me an idea as to why my event receiver doesn't work here.
I'll have a look.

Although I don't quite see a solution in there, the only difference is that it doesn't use isKeyDown, it uses a boolean for the only key it checks for and just checks in the event receiver - this shouldn't be the reason but I'm not familiar enough with receivers (and I'd rather not change it, it lives in my utility header that's included in a lot of projects. changing it takes forever to compile - that's probably bad design but it's the only way I can have neat functions like a knuth shuffle and a platform agnostic sleep function available whenever needed, also event receivers that may or may not be required in more than one module)
"this is not the bottleneck you are looking for"
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: mimicing retro dungeon crawler UI (RTT?)

Post by CuteAlien »

Sorry, my post was about the first screen, not the lastest troubles. You are switching so fast from problem to problem - it's hard to follow you.

First about cout and flushing - "std::endl" will fllush - meaning if you don't see a message for that it wasn't written.

The warning "the address of 'eReceiver' will always evaluate as 'true'" sounds serious and shouldn't be ignored. But I don't see in your code above any reason why it would happen. Did you show us the exact line where you got that? Or did you solve that already?

Making something const or not const will not change the runtime behavior of code at all (well, it allows some compiler optimizations in release, but the code should still run the same). const is something that allows the compiler to help you coding. It helps you to figure out which functions change variables and which don't to makes it easier to read code once it get's complex. So if you have bugs - it's not because of const or not const. That can only ever give you compile errors.

Your OnEvent doesn't seem to return anything in the default case. This means you have undefined behavior - and the compiler should give you a warning about that. Don't ignore such warnings -undefined behavior means the compiler can mess up your code in *any* way it wants. The moment you have that *anything* can happen - if the compiler feels like it he can start hacking your facebook account and posting nude pictures it took with your webcam when you were looking away! That's what undefined behavior might mean (it usually doesn't, but don't complain I didn't warn you when it does!).

Add debug-output in your event-receiver to see if it even sets that bool flag.
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
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

Re: mimicing retro dungeon crawler UI (RTT?)

Post by Cube_ »

The warning "the address of 'eReceiver' will always evaluate as 'true'" sounds serious and shouldn't be ignored. But I don't see in your code above any reason why it would happen. Did you show us the exact line where you got that? Or did you solve that already?
sure right here (including surrounding lines for context)

Code: Select all

int main(int argc, char* argv[])
{
    IrrlichtDevice *nulliDevice = createDevice(video::EDT_NULL);
 
    core::dimension2d<u32> res = nulliDevice->getVideoModeList()->getDesktopResolution();
    res = dimension2d<u32>(800,600);
 
    nulliDevice -> drop();
    MyEventReceiver eReceiver; 
 
//here, right when I try to use it in a device, in other words &eReceiver will always be true.
 
    IrrlichtDevice* iDevice = createDevice(EDT_OPENGL, res, 32, fullscreen, vsync, &eReceiver);
    driver = iDevice->getVideoDriver();
    smgr = iDevice->getSceneManager();
Your OnEvent doesn't seem to return anything in the default case. This means you have undefined behavior - and the compiler should give you a warning about that. Don't ignore such warnings -undefined behavior means the compiler can mess up your code in *any* way it wants. The moment you have that *anything* can happen - if the compiler feels like it he can start hacking your facebook account and posting nude pictures it took with your webcam when you were looking away! That's what undefined behavior might mean (it usually doesn't, but don't complain I didn't warn you when it does!).
not getting any warning about that.
and if that's the case then tutorial 4 needs updating, this code is lifted right from there. (and the example code provided with the engine really ought to be correct methinks).

problem is, I don't really know when OnEvent is called - it's probably called by irrlicht itself since the tutorial code doesn't ever invoke it neither does my code.

Code: Select all

 
    virtual bool OnEvent(const SEvent& event)
    {
        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
            return false;
    }
    else return true;
 
something like that ought to hypothetically solve it.
my theory of how it functions:
OnEvent runs, it detects that no keys are down and pushes false to all of them... I think.
Then irrlicht will detect keystrokes and return true for that key, then it all gets looped back to false.

I don't actually know, but here's the code from tutorial 04:

Code: Select all

/*
To receive events like mouse and keyboard input, or GUI events like "the OK
button has been clicked", we need an object which is derived from the
irr::IEventReceiver object. There is only one method to override:
irr::IEventReceiver::OnEvent(). This method will be called by the engine once
when an event happens. What we really want to know is whether a key is being
held down, and so we will remember the current state of each key.
*/
class MyEventReceiver : public IEventReceiver
{
public:
    // This is the one method that we have to implement
    virtual bool OnEvent(const SEvent& event)
    {
        // Remember whether each key is down or up
        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
 
        return false;
    }
 
    // This is used to check whether a key is being held down
    virtual bool IsKeyDown(EKEY_CODE keyCode) const
    {
        return KeyIsDown[keyCode];
    }
    
    MyEventReceiver()
    {
        for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
            KeyIsDown[i] = false;
    }
 
private:
    // We use this array to store the current state of each key
    bool KeyIsDown[KEY_KEY_CODES_COUNT];
};
 
 
/*
The event receiver for keeping the pressed keys is ready, the actual responses
will be made inside the render loop, right before drawing the scene. So lets
just create an irr::IrrlichtDevice and the scene node we want to move. We also
create some other additional scene nodes, to show that there are also some
different possibilities to move and animate scene nodes.
*/
int main()
{
    // ask user for driver
    video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (driverType==video::EDT_COUNT)
        return 1;
 
    // create device
    MyEventReceiver receiver;
 
    IrrlichtDevice* device = createDevice(driverType,
            core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver);
 
    if (device == 0)
        return 1; // could not create selected driver.
 
        //SNIP
 
            while(device->run())
    {
        // Work out a frame delta time.
        const u32 now = device->getTimer()->getTime();
        const f32 frameDeltaTime = (f32)(now - then) / 1000.f; // Time in seconds
        then = now;
 
        /* Check if keys W, S, A or D are being held down, and move the
        sphere node around respectively. */
        core::vector3df nodePosition = node->getPosition();
 
        if(receiver.IsKeyDown(irr::KEY_KEY_W))
            nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime;
        else if(receiver.IsKeyDown(irr::KEY_KEY_S))
            nodePosition.Y -= MOVEMENT_SPEED * frameDeltaTime;
 
        if(receiver.IsKeyDown(irr::KEY_KEY_A))
            nodePosition.X -= MOVEMENT_SPEED * frameDeltaTime;
        else if(receiver.IsKeyDown(irr::KEY_KEY_D))
            nodePosition.X += MOVEMENT_SPEED * frameDeltaTime;
as mentioned, I haven't ever built this tutorial but it stands to reason that it's likely to not work - given that it has the same problem as my code in the parts that are contextually relevant.

I'll mess about with the OnEvent function to see if I can solve it, I'll post the solution here as well if I find it.

EDIT: I just noticed a thing, that's the ugliest code I've ever seen.
have a look at this:

Code: Select all

        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
            return false;
it does return false. but it's hard to tell.

Code: Select all

        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
        {
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
        }    
        return false;
that's a bit easier.

so from what I can tell.
OnEvent checks if a key is down, and sets that key to down. Then it returns false.... I don't know why it returns false, it probably shouldn't.
I'll edit this as many times as needed as I actually take a good stare at the event receiver and change it around until the warning goes away - if it goes away.

first attempt: make it return true if a key event has been recorded.

EDIT: I don't know, but if we were to brainstorm I'd probably look at this first

Code: Select all

keys[event.KeyInput.Key] = event.KeyInput.PressedDown
presumably, if the engine isn't getting a constant signal of the key being pressed down that might cause it to never return true in the first place - I mean, that SHOULDN'T happen but it is happening and I can't tell why.
Also that theory isn't exactly waterproof - replacing event.KeyInput.PressedDown with true still causes the behavior.

this implies that the problem is either with one of these two statements:
The event receiver resides in util.h - this SHOULDN'T cause problems, it's just a class - I create the instance in the main function anyway.
Something's very wrong but I can't see what.
"this is not the bottleneck you are looking for"
Post Reply