user3164141 user3164141 - 17 days ago 5
C++ Question

Controling OpenSceneGraph camera with Cartesian coordinates and Euler angles

I am attempting to control an osg::Camera with Cartesian coordinates (X, Y, Z) and Euler angles (Yaw, Pitch, Roll) that I read in from file.

Part 1

For some reason setting my roll will cause the camera to rotate around the z-axis and not the y-axis as expected.

The following is the previously written camera manipulator that I am feeding my positional data to.

class EulerManipulator : public osgGA::CameraManipulator
{
public:
EulerManipulator(osg::ref_ptr<osg::View> x) :
view(x),
camView(new osg::CameraView)
{
}

virtual ~EulerManipulator(){}

virtual osg::Matrixd getMatrix() const
{
// Note: (x, y, z) and (yaw, pitch, roll) are read in from file and saved in memory

// Position works fine
this->camView->setPosition(osg::Vec3d(x, y, z));

// Possibly something wrong with rotation
osg::Quat rotationQuat;

const osg::Vec3d headingAxis(0.0, 0.0, 1.0);
const osg::Vec3d pitchAxis(1.0, 0.0, 0.0);
const osg::Vec3d rollAxis(0.0, 1.0, 0.0);

std::array<double, 3> rotation = {yaw, pitch, roll};

rotationQuat.makeRotate(
deg2rad(rotation[2]), rollAxis,
deg2rad(rotation[1] + 90.0f), pitchAxis,
-deg2rad(rotation[0]), headingAxis);

this->camView->setAttitude(rotationQuat);

// Not 100% sure what this does but assume it works as Heading and Pitch seem to work fine.
auto nodePathList = this->camView->getParentalNodePaths();
return osg::computeLocalToWorld(nodePathList[0]);
}

private:
osg::ref_ptr<osg::View> view;
osg::ref_ptr<osg::CameraView> camView;
};


Note: The above code is not my own but is the code that is being used to drive the camera. My goal is to grab the Cartesian position and Euler angles from my own camera and write out to file. But before I can do this, I need to figure out why this code isn't behaving as expected.

Is there anything obviously wrong with the above code?

Part 2

For the second part of this problem I am capturing the camera's positional data and writing it to a file.

I have had a little success but it still isn't working quite correctly. Below is the overloaded handle function I wrote for an event handler that is attached to my view. The goal is to capture the camera's positional data as I move the camera around within the scene.

Here I run my program and rotate the camera horizontally (yaw) but for some reason the pitch angle decreases drastically. If I go from 0 degree yaw to 90 degree yaw. My pitch angle will decrease from 0 to about 90 degrees. I would have expected my pitch to remain roughly the same as I rotate the camera horizontally.

virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&, osg::Object*, osg::NodeVisitor*) override
{
switch(ea.getEventType())
{
case osgGA::GUIEventAdapter::FRAME:
{
if((this->camera != nullptr))
{
osg::Vec3d center;
osg::Vec3d vecDontCare;
this->camera->getViewMatrixAsLookAt(vecDontCare, center, vecDontCare);

auto rotation = this->camera->getViewMatrix().getRotate();

std::array<double, 4> quat = {{rotation.x(), rotation.y(), rotation.z(), rotation.w()}};

// Assuming this conversion is correct
auto ypr = Quaternion2YawPitchRoll(quat);

// Convert the rotation into my coordinate system.
ypr[0] *= -1.0;
ypr[1] -= 90.0;

// The output angles for YPR don't appear to align with the manual rotation I perform with the camera
Debug << "Y: " << ypr[0] << " P: " << ypr[1] << " R: " << ypr[2];
// Write XYZ and YPR to file
}
}
break;

default:
break;
}


The question is, is there anything obviously wrong with the way i'm grabbing the camera's positional data? Is there a better way to do so?

Answer

Part 1

The issue was the order in which I added my angles to the quaternion.

rotationQuat.makeRotate(
    deg2rad(rotation[1] + 90.0f), pitchAxis,
    deg2rad(rotation[2]), rollAxis, 
    -deg2rad(rotation[0]), headingAxis);

Pitch had to be added first as it rotates around the X-Axis

Part 2

A couple of things were wrong here.

First, I needed to grab the inverse view matrix. I'm not 100% sure why, maybe someone with more knowledge can leave a comment, but it works.

auto rotation = this->camera->getViewInverseMatrix().getRotate();

Secondly, Assuming the conversion function was correct turned out to be bad. I wrote a unit test that was missing for this function and it turns out it was wrong.

// Assuming this conversion is correct
auto ypr = Quaternion2YawPitchRoll(quat);