Page 1 of 1
How to add a 3D overlay node
Posted: Mon Sep 28, 2020 6:53 pm
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
Re: How to add a 3D overlay node
Posted: Tue Sep 29, 2020 9:13 am
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...
Re: How to add a 3D overlay node
Posted: Tue Sep 29, 2020 11:07 am
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.
Re: How to add a 3D overlay node
Posted: Tue Sep 29, 2020 11:52 am
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???
Re: How to add a 3D overlay node
Posted: Tue Sep 29, 2020 8:26 pm
by CuteAlien
Sorry, don't know like that. Do you have a quick test for me?
Re: How to add a 3D overlay node
Posted: Wed Sep 30, 2020 12:08 pm
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.
Re: How to add a 3D overlay node
Posted: Thu Oct 01, 2020 12:12 am
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;
}
Re: How to add a 3D overlay node
Posted: Thu Oct 01, 2020 3:12 pm
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.
Re: How to add a 3D overlay node
Posted: Thu Oct 01, 2020 3:54 pm
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).
Re: How to add a 3D overlay node
Posted: Fri Oct 02, 2020 5:50 pm
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...
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
Re: How to add a 3D overlay node
Posted: Fri Oct 02, 2020 5:53 pm
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??
Re: How to add a 3D overlay node
Posted: Fri Oct 02, 2020 11:15 pm
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).
Re: How to add a 3D overlay node
Posted: Sun Oct 04, 2020 2:13 am
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!