How to add a 3D overlay node

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

How to add a 3D overlay node

Post by robmar »

I've been trying to lock a "logo" node so that it stays on-screen (always in front of the camera), and the techniques mentioned in the forum work fine to lock the rotation of the object to the camera until the camera goes upside-down, which then flips the logo object on its head, whereas it needs to stay still, being a screen overlay (but in 3D space).

The objective is to lock the rotation of the object to the camera, so it stays top-right in from of the camera wherever the camera is moved including if it inverts.
As its a 3D object, I can't used the 2D screen overlay.

It seems more complicated that it should be, using getHorizontalAngle or quaternions the logo node can be rotated to stay oriented to the camera, but if the camera goes upsidedown, so does the logo node, as if it was lacking the tilt correction.

I've no idea how to correct for the tilt. If the camera stays upwards, the logo is locked to the top right of the monitor, so it works apart from the tilt.
It seems getHorizontalAngle does not account for tilt, which would cause the problem, but then does the tilt have to be added to the rotated Z-axis of the node by the look-at vector, not just the .z of the rotation...?

Code: Select all

 
                // Rotate node so that it always faces the camera (node is also position offset locked to the camera)
                vector3df direction = pCamera->getTarget() - pCamera->getAbsolutePosition();
                vector3df requiredRotation = direction.getHorizontalAngle();        // Calculate node XY rotation to direction of camera
 
                vector3df CamUp = pObjectToFace->pCamera->getUpVector();
 
            bool bOk = 1;
            f32 ar = CamUp.dotProduct(vector3df(0,1,0); // Angle between default up and camera up
            if (fabs(ar) > 1.0f)
                bOk = 0;
 
            f32 z = acos(ar) * RADTODEG;
            requiredRotation.Z = z;                     // Add camera tilt to rotation
            node->setRotation(requiredRotation + adjRot);       // Set rotation with tilt so it tracks camera even if upsidedown
 
 
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: How to add a 3D overlay node

Post by CuteAlien »

You could try:
1. Make it a child of the camera object
2. In the camera set bindTargetAndRotation (true)

I didn't test...
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
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: How to add a 3D overlay node

Post by robmar »

I tired with it parented, had to use bind, but any camera roll is not matched to the child.

Also tried with the camera bound and getting the matrix, it partially works, but then in some positions the rotation to the tracked node goes completely wrong.

Code: Select all

            pCamera->updateAbsolutePosition();
            core::vector3df rot;
            matrix4 m = pCamera->getViewMatrix();
            //matrix4 m = pCamera->getAbsoluteTransformation();
            rot = m.getRotationDegrees();
            node->setRotation(rot);     // Set rotation to keep overlay node from rotating
 
I'm using Irrlicht 1.7.1, were there any camera bug fixes since?

It's a little weird, positioning the node at a spot in front of the camera, and rotating it to match, should result in the object rendering in the same place without changing rotation as the camera flys about, but the Roll isn't matching at all.
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: How to add a 3D overlay node

Post by robmar »

I checked and there are no cmatrix.* or ccamera.* changes up to 1.8.4., and I updated to the math routine fixes from 1.8.4 a while ago.

I also tried using the rotation from this but it looks like there is a limitation on the matrix that doesn't include the camera tilt properly.

Code: Select all

            matrix4 m = m.buildCameraLookAtMatrixLH(
                pos,
                pCamera->getTarget(),
                up);
 
Straight forward parenting of the node to the camera (bound) reproduces the problem, an unreliable/buggy tilt rotation.

The code in buildCameraLookAtMatrixLH looks simple enough, so where is the problem???
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: How to add a 3D overlay node

Post by CuteAlien »

Sorry, don't know like that. Do you have a quick test for me?
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
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: How to add a 3D overlay node

Post by robmar »

To reproduce add a cube node top right of the screen near to the camera, and parent it to the camera.
Then use your mouse to pan and tilt the camera.
You´ll see the cube tilt over when the camera tilt's, whereas if it was properly parented it would not move being locked to the camera.
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: How to add a 3D overlay node

Post by CuteAlien »

If it's that easy to reproduce something - then please just post a simple example doing that! I'll copy-paste it and give it a shot. Worst case - you just spend 10 minutes reducing your bug to a reproducible example and while doing so saved me some time trying to write the same from scratch. Best case - you even find the bug while doing so.

And if I sound a bit annoyed - I just spend way too much time writing a comment like that. Then I thought - ah well - I'll just do it, I probably sound like a grumpy old man otherwise insisting on a working example. So I deleted my comment and spend half an hour trying to reproduce this problem. And as so often I failed. Because I start from scratch with the tiny info I got and code into the darkness. Missing info because I skipped it while reading or because not all was posted or simple because as usual I'm way too tired. Happens always.

Anyway - here's my example - which does not reproduce the problem. My guess by now - because fps-cams don't flip over (doh... yeah, it's obvious to you who worked with your rolling cam all the time and I realize now you mentioned it - after re-reading the post). So probably you use another camera. With likely your own controls which you wrote and I know nothing about. I could try on, but I need my sleep and I think you really are experienced enough to give me something reproducible to test which compiles.

Code: Select all

 
#include <irrlicht.h>
 
using namespace irr;
using namespace core;
 
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
 
 
int main()
{
    IrrlichtDevice *  Device = createDevice(irr::video::EDT_OPENGL, irr::core::dimension2d<irr::u32>(800,600));
    if (!Device)
        return false;
 
    scene::ISceneManager* smgr = Device->getSceneManager();
    video::IVideoDriver * videoDriver = Device->getVideoDriver ();
 
    scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
        "../../media/terrain-heightmap.bmp",
        0,                  // parent node
        -1,                 // node id
        core::vector3df(0.f, 0.f, 0.f),     // position
        core::vector3df(0.f, 0.f, 0.f),     // rotation
        core::vector3df(40.f, 4.4f, 40.f),  // scale
        video::SColor ( 255, 255, 255, 255 ),   // vertexColor
        5,                  // maxLOD
        scene::ETPS_17,             // patchSize
        4                   // smoothFactor
        );
 
    terrain->setMaterialFlag(video::EMF_LIGHTING, false);
 
    terrain->setMaterialTexture(0, videoDriver->getTexture("../../media/terrain-texture.jpg"));
 
    scene::ICameraSceneNode * camera = smgr->addCameraSceneNodeFPS(0, 20.f, 0.1f );
    camera->setPosition(vector3df(100, 600, 100));
 
    scene::IMeshSceneNode * cubeNode = smgr->addCubeSceneNode(20.0f, camera, -1, vector3df(30, 30, 50));
    cubeNode->getMaterial(0).Lighting = false;
 
    while ( Device->run() )
    {
        if ( Device->isWindowActive() )
        {
            videoDriver->beginScene(true, true);
 
            smgr->drawAll();
 
            videoDriver->endScene();
        }
 
        Device->sleep( 5 );
    }
 
    Device->closeDevice();
    Device->drop();
    Device = NULL;
 
    return 0;
}
 
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
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: How to add a 3D overlay node

Post by CuteAlien »

OK, awake again and just tried with maya-cam and can reproduce. I'll try if I can figure out what it does.
edit: Looks like Irrlicht camera ignores the up-vector, so even after TargetAndRotationAreBound it will not realize there is some roll going on.
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
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: How to add a 3D overlay node

Post by CuteAlien »

Got some workaround - not super nice, but maybe good enough for now. Rest is todo's for another year (1. camera bindTargetAndRotation should not ignore upvector, aka roll. 2. Should be possible to animate cameras before drawAll() without having to animate them twice).

Code: Select all

 
#include <irrlicht.h>
 
using namespace irr;
 
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
 
int main()
{
    IrrlichtDevice *  Device = createDevice(irr::video::EDT_OPENGL, irr::core::dimension2d<irr::u32>(800,600));
    if (!Device)
        return false;
 
    scene::ISceneManager* smgr = Device->getSceneManager();
    video::IVideoDriver * videoDriver = Device->getVideoDriver ();
 
    scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(  // adding some terrain to see camera moves
        "../../media/terrain-heightmap.bmp",
        0,                  // parent node
        -1,                 // node id
        core::vector3df(0.f, 0.f, 0.f),     // position
        core::vector3df(0.f, 0.f, 0.f),     // rotation
        core::vector3df(40.f, 4.4f, 40.f),  // scale
        video::SColor ( 255, 255, 255, 255 ),   // vertexColor
        5,                  // maxLOD
        scene::ETPS_17,             // patchSize
        4                   // smoothFactor
        );
    terrain->setMaterialFlag(video::EMF_LIGHTING, false);
    terrain->setMaterialTexture(0, videoDriver->getTexture("../../media/terrain-texture.jpg"));
 
    scene::ICameraSceneNode * camera = smgr->addCameraSceneNodeMaya();
    camera->setPosition(core::vector3df(100, 600, 100));
 
    scene::IDummyTransformationSceneNode * parent = smgr->addDummyTransformationSceneNode();
    const irr::core::vector3df cubeOffset(30, 30, 50);
    scene::IMeshSceneNode * cubeNode = smgr->addCubeSceneNode(20.0f, parent, -1, cubeOffset);
    cubeNode->getMaterial(0).Lighting = false;
 
    while ( Device->run() )
    {
        if ( Device->isWindowActive() )
        {
            camera->OnAnimate(Device->getTimer()->getTime());   // smgr->drawAll does that, but we need the result already here, so it will be calculated twice now - it's cheap
            camera->updateAbsolutePosition();
 
            // calculate real transformation matrix of camera - as camera->bindTargetAndRotation ignores the roll for some reason.
            irr::core::vector3df camI, camJ, camK;
            camJ = camera->getUpVector();
            camJ.normalize();
            camK = camera->getTarget() - camera->getAbsolutePosition();
            camK.normalize();
            camI = camJ.crossProduct(camK);
            irr::core::matrix4 mCam;
            mCam.setTranslation(camera->getAbsolutePosition());
            mCam[0] = camI.X;
            mCam[1] = camI.Y;
            mCam[2] = camI.Z;
            mCam[4] = camJ.X;
            mCam[5] = camJ.Y;
            mCam[6] = camJ.Z;
            mCam[8] = camK.X;
            mCam[9] = camK.Y;
            mCam[10] = camK.Z;
            
            // set our dummy parent to the same rotation camera has
            parent->getRelativeTransformationMatrix() = mCam;
 
            videoDriver->beginScene(true, true);
            smgr->drawAll();
            videoDriver->endScene();
        }
         Device->sleep( 5 );
    }
 
    Device->closeDevice();
    Device->drop();
  
    return 0;
}
 
Only tested with Irrlicht 1.8... hope 1.7 is the same.

edit: Also depending on what exactly you need this for there might eventually be simpler solutions (for example rendering it after drawAll, then you can set any camera you want and just render it always at the same place).
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
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: How to add a 3D overlay node

Post by robmar »

Thanks so much man, that's really good of you!!
and sorry :? should have said it was an ICameraSceneNode, or maybe posted an example... but it seemed so simple, and as you're the expert... :oops:

I'm going to look thro your example carefully as I'd like to understand where the tilt is getting lost!! Not understanding is so frustrating hey!

I solved the inversion issue with this code, but the tilt math I've not got right...:

Code: Select all

 
            // Rotate the child node to match camera up
            quaternion qd, qr, qtd;
            qd.set(direction);  // Actual Z-plane (lookat)
            qr = qd.rotationFromTo(vector3df(0,1,0), up);       // Rotate node to match current camera up and direction!
 
            // Add Y rotation so node matches camera
            vector3df y(direction.getHorizontalAngle());    // Angle to camera lookat from 0,0,1
            y.X = 0;            // Remove up/down as already in qr
            if (up.Y < 0.0)     // If inverted, Y rotation swaps around
                y.Y = fmod(180.0 + y.Y, 360.0);
            matrix4 mYRot;
            mYRot.setRotationDegrees(y);
 
// TILT ADJUSTMENT NOT WORKING - TODO // Need to add the tilt rotation along the direction-Z axis...
            qtd.set(right);
            qt = qtd.rotationFromTo(vector3df(0,1,0), up);      // Rotate to match current up along side axis to direction
            vector3df z = qt.getMatrix().getRotationDegrees();
            if (up.Y < 0.0)     // If inverted, Y rotation swaps around
                z.Z = fmod(180.0 + z.Z, 360.0);
            matrix4 mTilt;
            mTilt.setRotationDegrees(z);
 
            rot = (qr.getMatrix() * mYRot).getRotationDegrees();        // Add Up and Y Spin rotations (no tilt correction) to "child" node - its not actually parented as that doesn't work
 
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: How to add a 3D overlay node

Post by robmar »

So why do you need to "set our dummy parent to the same rotation camera has"?
Can you just set the node`s matrix directly and forget the dummy altogether?
What have I missed??
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: How to add a 3D overlay node

Post by CuteAlien »

The problem is that the camera usually doesn't calculate a transformation matrix, but is based on position + target + upvector (easier to use for most cases and it does allow those to be independent which is maybe useful in rare cases). When you set bindTargetAndRotation(true) is should calculate a correct matrix from those - but unfortunately it doesn't because it ignores the upvector, so it doesn't have the "roll". I'd consider that a bug in Irrlicht, so it's on my todo now (but not for near future).
That's why I use those 3 variables to calculate the real transformation matrix.

You can probably also set bindTargetAndRotation(false) and use the rotation from my transformation matrix and set the camera to that with setRotation - then you should be able to use the camera as parent instead of a dummy node (position should be correct already). Untested.

Setting node directly is slightly more tricky and I was too lazy for that ;-) But certainly can also be done (it's more tricky because nodes don't have matrices like the dummy-node, but you have to go back to position/rotation - and it makes it more complicated as you also have your relative offset already in position as the node is not exactly in the same place as the camera and you don't want to lose that relative offset).

The other option I mentioned might be easier - render your overlay node manually after drawAll (with a render() call). Then you can set another simple camera after drawAll and before rendering that node which is always looking directly at your node (or in 0,0,1 direction with up vector 0,1,0 and position your node in front of it). And you don't have to transform that node or your second camera at all (camera also needs a render() call).
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
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: How to add a 3D overlay node

Post by robmar »

Understood, I have everything working well now, which is great.
You're right about rendering the overlay nodes with their own camera, its the perfect solution really.
I did have to add the bindtotarget code to the icamerascenenode::setposition function, because moving the camera wasn't changing the rotation.
Thanks for your help!
Post Reply