Page 1 of 1

How do I make a gun turret that is child of a node point at another node in the root scene?

Posted: Sun Sep 08, 2024 10:25 pm
by SomeGuyWithAComputer
i'm trying to make it so that a turret on a ship points at another irrlicht ISceneNode.

The turret node is a child of the ship. The targets are at the same level as the ship, there is never a situation where a turret needs to target an item that the ship is a child of or that is a child of the ship.

I can easily do this:

Code: Select all

core::vector3df parentThingRot = getParentThing()->getNode()->getAbsoluteTransformation().getRotationDegrees();
core::vector3df toTarget(pos2 - pos1);
core::vector3df requiredRotation = toTarget.getHorizontalAngle();
this gets me the rotation to set the turret to if it wasn't a child of the ship i think. I've tried a great number of different things, I can't even remember it all but I know i'm not even close to getting it figured out. I've tried to calculate what to set the turret's angle to based on the rotation of the ship so that it's still pointing at a target despite being a child node of the ship and.. i've gotten absolutely nowhere.

I'm using these old functions I found for context:

Code: Select all

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// Given 2 euler rotations find a quaternion describing the relative rotation from the old to the new rotation
irr::core::quaternion getRelativeRotation(const irr::core::vector3df &oldRotationEulerDeg, const irr::core::vector3df &newRotationEulerDeg)
{
	irr::core::quaternion q1(oldRotationEulerDeg * irr::core::DEGTORAD);
	irr::core::quaternion q2(newRotationEulerDeg * irr::core::DEGTORAD);
	irr::core::quaternion qRel(q2 * q1.makeInverse());
	return qRel;
}

//inputs have to be in radians otherwise it won't work. note that reactphysics3d ususally does quaternion stuff in radians
Quaternion getRelativeRotation_rp3d(Vector3 &oldRotationEulerDeg, Vector3 &newRotationEulerDeg)
{
	irr::core::quaternion q1(core::vector3df(oldRotationEulerDeg.x, oldRotationEulerDeg.y, oldRotationEulerDeg.z));
	irr::core::quaternion q2(core::vector3df(newRotationEulerDeg.x, newRotationEulerDeg.y, newRotationEulerDeg.z));
	irr::core::quaternion qRel(q2 * q1.makeInverse());
	Quaternion newQuat(qRel.X,qRel.Y,qRel.Z,qRel.W);

	return newQuat;
}

// Given an euler angle + a quaternion with a relative rotation do return an euler angle describing the combined absolute rotation
irr::core::vector3df applyRelativeRotation(const irr::core::vector3df &oldRotationEulerDeg, const irr::core::quaternion &relativeRotation)
{
	// Add relative rotation
	irr::core::quaternion qt(oldRotationEulerDeg * irr::core::DEGTORAD);
	irr::core::quaternion qt2(relativeRotation * qt);

	irr::core::vector3df rotateTarget;
	qt2.toEuler(rotateTarget);
	rotateTarget *= irr::core::RADTODEG;
	return rotateTarget;
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
irr::core::vector3df rotateAxesXYZToEuler(const irr::core::vector3df &oldRotation, const irr::core::vector3df &rotationAngles, bool useLocalAxes)
{
	irr::core::matrix4 transformation;
	transformation.setRotationDegrees(oldRotation);
	irr::core::vector3df axisX(1, 0, 0), axisY(0, 1, 0), axisZ(0, 0, 1);
	irr::core::matrix4 matRotX, matRotY, matRotZ;

	if (useLocalAxes)
	{
		transformation.rotateVect(axisX);
		transformation.rotateVect(axisY);
		transformation.rotateVect(axisZ);
	}

	matRotX.setRotationAxisRadians(rotationAngles.X * irr::core::DEGTORAD, axisX);
	matRotY.setRotationAxisRadians(rotationAngles.Y * irr::core::DEGTORAD, axisY);
	matRotZ.setRotationAxisRadians(rotationAngles.Z * irr::core::DEGTORAD, axisZ);

	irr::core::matrix4 newTransform = matRotX * matRotY * matRotZ * transformation;
	return newTransform.getRotationDegrees();
}
Now my attempts at getting this right aren't even close, but here's what i've been trying to do today:

Code: Select all

core::quaternion idontknow = getRelativeRotation(parentThingRot+getNode()->getAbsoluteTransformation().getRotationDegrees(), requiredRotation+parentThingRot);
core::vector3df combinedStuff = applyRelativeRotation(parentThingRot+getNode()->getAbsoluteTransformation().getRotationDegrees(), idontknow);
getNode()->getJointNode("yaw_gimbal")->setRotation(core::vector3df(combinedStuff.X,combinedStuff.Y,combinedStuff.Z));//this is split into components for more rapid trial-and-error testing of stuff (sometimes setting x and z to 0 yields dfferent incorrect results which is worth checking each time I get a new idea)
I feel like if I could JUST get the solution to this one problem somehow, it would help me with all or some of my other unsolved rotational angle problems. Any ideas?

Re: How do I make a gun turret that is child of a node point at another node in the root scene?

Posted: Sun Sep 08, 2024 11:13 pm
by CuteAlien
If you use svn trunk version of Irrlicht things are slightly easier. It has a function ISceneNode::setUpdateAbsolutePosBehavior which you can set to irr::scene::ESNUA_TRANSFORM_POSITION. Then child-nodes simply ignores the parent rotation and only updates the position based on parent. So child has it's own rotations (so the position still rotates around the ship, but it's own rotation won't be affected anymore - you have full control).

Then the rest is probably the stuff described in this thread: viewtopic.php?t=1325
(sorry too late for me to write code to check if that code actually works, but using buildCameraLookAtMatrixLH followed by getRotationDegrees would also have been my first try).

With Irrlicht 1.8 this is a bit more complicated as you have to get rid of the parent rotation yourself. Meaning you first update the matrix of the parent (updateAbsolutePosition()) and then get it's transformation (getAbsoluteTransformation). Next get rid of the positions in the matrix (set last row in the 4x4 matrix to 0) as you only need rotations. Then get the inverse matrix (matrix class has a function for that - inverse rotation matrix simply rotates everything backwards) and then multiply the matrix you got from buildCameraLookAtMatrixLH by that inverse matrix. Be careful in matrix multiplication order matters so a*b is not the same as b*a ... just try both and one should work. And I hope there's no scaling involved or things get more complicated (easiest then to just build the absolute transformation matrix of the parent yourself just from the rotations of all node parents).

Re: How do I make a gun turret that is child of a node point at another node in the root scene?

Posted: Mon Sep 09, 2024 12:52 am
by SomeGuyWithAComputer
Do you know how to change the last row of a matrix to 0 like that in irrlicht 1.8? I think setTranslation() should do it. Is there a better way to do it?

Thanks for the warning about scaling, I've been really careful to make sure nothing having to do with scaling ever happens in my project.

Anyway i'm trying this now:

Code: Select all

core::matrix4 parentTransform = getParentThing()->getNode()->getAbsoluteTransformation();
parentTransform.setTranslation(core::vector3df(0,0,0));//set last row to 0
core::matrix4 newMatrix;
newMatrix.getInverse(parentTransform);
core::vector3df shipUpVector = getParentThing()->getNode()->getRotation().rotationToDirection(core::vector3df(0,1,0));
newMatrix.buildCameraLookAtMatrixLH(getNode()->getAbsolutePosition(), pos2, shipUpVector);//pos2 is the position of the target
core::matrix4 combined = newMatrix*parentTransform;
core::vector3df combinedVector = combined.getRotationDegrees();
getNode()->getJointNode("yaw_gimbal")->setRotation(core::vector3df(0,combinedVector.Y,0));
getNode()->getJointNode("pitch_gimbal")->setRotation(core::vector3df(combinedVector.X,0,combinedVector.Z));
This particular attempt isn't quite doing it. I haven't trial and errored all of the stuff im unsure about yet, I'll update after i've done all that. Who knows, maybe i'm just 1 subtract 180 operation away from it working.

Edit: Ok, the closest I got it is this.

Code: Select all

core::matrix4 parentTransform = getParentThing()->getNode()->getAbsoluteTransformation();
parentTransform[12] = 0;
parentTransform[13] = 0;
parentTransform[14] = 0;
parentTransform[15] = 0;
core::matrix4 newMatrix;
parentTransform.getInverse(parentTransform);
core::vector3df shipUpVector = getParentThing()->getNode()->getRotation().rotationToDirection(core::vector3df(0,1,0));
newMatrix.buildCameraLookAtMatrixLH(pos2, getNode()->getAbsolutePosition(), shipUpVector);//pos2 is the position of the target
core::matrix4 combined = newMatrix*parentTransform;
core::vector3df combinedVector = combined.getRotationDegrees();
getNode()->getJointNode("yaw_gimbal")->setRotation(core::vector3df(0,combinedVector.Y,0));
getNode()->getJointNode("pitch_gimbal")->setRotation(core::vector3df(combinedVector.X,0,combinedVector.Z));
If I do this, it's almost as if there are certain edge cases where the turret actually does point in the same direction as the target except for it getting the up/down inverted. I don't know the exact conditions that cause it to point at the target but it's less than 25% of the time. I've tried buildCameraLookAtMatrixLH vs buildCameraLookAtMatrixRH, swapping the position and target parameters of that function and then trying newMatrix*parentTransform vs parentTransform*newMatrix. Maybe I'm using the inverse function incorrectly.

Re: How do I make a gun turret that is child of a node point at another node in the root scene?

Posted: Mon Sep 09, 2024 8:15 am
by CuteAlien
setPosition and manually setting to 0 both should work enough for this (as [15] should be 0 already).

Your code kinda looks like what I'd expect to work. Except for having yaw and pitch separated. I'm not sure what that is doing and maybe it's messing up things. With euler rotations the order matters. So in Irrlicht I think rotation order is x->y->z. You can't split this up or change the order or you get another result (just try it with your hand - rotate x 90° followed by z 90° and then try again with z followed by x instead - your hand will have a very different end-result).

Re: How do I make a gun turret that is child of a node point at another node in the root scene?

Posted: Thu Sep 12, 2024 9:56 pm
by SomeGuyWithAComputer
hmm, i had already tried separating them vs not separating them before posting that. What visually happens and the rotational axis values are often useless and inconclusive. (x and z axises are combined based on whatever the quaternion algorithms say to do and whatnot). In my above attempt (with combinedVector x,y,z all in the same one and not separated), the y axis of the turret never changes as the ship rotates in addition to whatever other indescribably incorrect stuff happens.

Maybe I'll make a video of it if weeks go by and I discover nothing new. Hopefully I'll make some kind of breakthrough soon and at least get enough information about the problem to get better help. All of my unsolved problems right now are angle math related, I never knew angle math could be this difficult.

Re: How do I make a gun turret that is child of a node point at another node in the root scene?

Posted: Fri Sep 13, 2024 1:09 am
by SomeGuyWithAComputer
hold on, parentTransform.setTranslation(core::vector3df(0,0,0)) doesn't result in the same behavior as this

Code: Select all

parentTransform[12] = 0;
parentTransform[13] = 0;
parentTransform[14] = 0;
parentTransform[15] = 0;
i'll start trying both methods every time i change and check something.
The following code makes it so that the turret points 90 degrees to the right of the target but it still points in an actually predictable direction once the ship rotates. Being consistently 90 degrees off no matter what is practically the same as correct behavior. Too bad when I start moving the ship by going to any position besides 0,0,0 it of course stops being *almost* correct and starts going way off target. That's an improvement at least.

Code: Select all

core::matrix4 parentTransform = getNode()->getParent()->getAbsoluteTransformation();
parentTransform.setTranslation(core::vector3df(0,0,0));//set last row to 0
core::matrix4 inverseParentTransform;
core::matrix4 newMatrix;
parentTransform.getInverse(inverseParentTransform);
core::vector3df shipUpVector = getParentThing()->getNode()->getRotation().rotationToDirection(core::vector3df(0,1,0));
newMatrix.buildCameraLookAtMatrixLH(getNode()->getAbsolutePosition(), pos2, shipUpVector);
core::matrix4 combined = inverseParentTransform*newMatrix;
core::vector3df combinedVector = combined.getRotationDegrees();

getNode()->getJointNode("yaw_gimbal")->setRotation(core::vector3df(combinedVector.X,combinedVector.Y,combinedVector.Z));
Edit: you know, on second thought I think that was just a coincidence. If I rotate the ship enough, it gets out of whack and stops pointing at the target except for being 90 degrees off and instead points in entirely nonsense directions. Wow, I thought I was on to something. I'm not.

Is there a different or better way to get the inverse of a matrix? It's really hard to tell with 3d angles, you can't even go by the numbers, but visually I'm pretty sure I get the same results when getting the inverse of parentTransform as when not doing the inverse. This suggests its not getting the inverse, but matrix4.h talks about cramer's rule and looks fine, it's just that it's not changing the behavior in an obvious way.

Is it possible to set a position of a node to the value of a transformation matrix? idk if that would work but i'm starting to get really desperate for new ideas here.

Edit 2: i discovered something significant. First of all, changing the rotation of the bone isn't going to work because it's a child of another bone. Once I realized this I changed it to only change the rotation of the turret object and started brute force trying everything all over again.

I arrived upon code that points the turret at the target when I rotate the ship on the y-axis only meaning no pitch or rolling at all and when I move around on the x and z axis only (meaning position translations of the ship) it still works. Tilting the ship on the x or y axis or translating on the y axis causes the turret to point in what appears to be the opposite direction of where it's supposed to go. Here's the code.

Code: Select all

getNode()->getParent()->updateAbsolutePosition();
getNode()->updateAbsolutePosition();
core::matrix4 parentTransform = getNode()->getParent()->getAbsoluteTransformation();
//parentTransform.setTranslation(core::vector3df(0,0,0));//set last row to 0
parentTransform[12] = 0;
parentTransform[13] = 0;
parentTransform[14] = 0;
//parentTransform[15] = 0;
core::matrix4 inverseParentTransform;
core::matrix4 inverseNewMatrix;
core::matrix4 newMatrix;
newMatrix.getInverse(inverseParentTransform);
parentTransform.getInverse(inverseParentTransform);
core::vector3df shipUpVector = (getParentThing()->getNode()->getRotation()).rotationToDirection(core::vector3df(0,1,0));
cout << "up vector = " << shipUpVector << endl;
newMatrix.buildCameraLookAtMatrixLH(getNode()->getAbsolutePosition(), pos2, shipUpVector);
core::matrix4 combined = parentTransform*newMatrix;
core::matrix4 inverseCombined;
combined.getInverse(inverseCombined);
core::vector3df combinedVector = inverseCombined.getRotationDegrees();
            
getNode()->setRotation(combinedVector);
This is at least sensible enough behavior to actually describe without a video, that's significant.