0
\$\begingroup\$

So, I'm interested in building a Transform class similar to the one used in Unity Engine, but in C++. My intent is for it to be user-friendly to change the position, rotation, and scale of a variable by specifying vec3s for each, where the rotation vector specifies Euler angles or some equivalent. However, the math library I have written uses quaternions, and I'd like rotations to be represented internally by quaternions. Since quaternions are a bit difficult to use on their own, or without an axis-angle conversion of some sort, I was wondering what's the best way to do this conversion somewhat efficiently on the fly, so the user doesn't need to fiddle around with quaternions? My current header file is as follows (I haven't written the implementation yet, aside from referencing the private global vectors to the public const ones):

class Transform
{
public:

Transform();
~Transform();

//// Local Information ////

// Position relative to parent
vec3 LocalPosition;

// Scale relative to parent
vec3 LocalScale;

// Rotation about x,y,z axes relative to parent
vec3 LocalRotation;

// Transformation matrix from worldspace to local space
mat4 WorldToLocal() const;

//// Global Information ////

// Position relative to world (const)
const vec3 &WorldPosition;

// Scale relative to world (const)
const vec3 &WorldScale;

// Rotation about x,y,z axes relative to world (const)
const vec3 &WorldRotation;

// Transformation matrix from local space to world space
mat4 LocalToWorld() const;

private:

vec3 _WorldPosition, _WorldScale, _WorldRotation;

// Stores rotation as a quaternion
// ??? Do I need one for global and local? How does this relate to rotation vec3? ???
quat _RotationRaw;

};

All help is greatly appreciated. I'm usually pretty good at solving these issues but I'm at a loss on this one.

Edit: Forgot to mention that I've read over the other GameDev Stack posts on the Unity Transform class, but I couldn't glean any info about this issue from them.

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

While you're right to use the quaternion as the underlying type for rotation, I'm concerned that you're exposing other fields in C++ which should be handled using getters/setters.

The convention for C++ is to expose get and set methods, and leave the particulars of how the field is handled to the class. That is, excepting TDA since this is a component, something like this:

public:
    set_EulerAngles(vec3);
    set_Rotation(quat);
    set_LocalPosition(vec3);
    set_Position(vec3);
    // +matching gets...
private:
    vec3 _WorldPosition, _WorldScale, _WorldRotation;
    quat _Rotation;

and is more like what Unity actually uses underneath:

private void INTERNAL_set_localRotation(ref Quaternion value);
private void INTERNAL_set_rotation(ref Quaternion value);
private void INTERNAL_set_localEulerAngles(ref Vector3 value);

private void INTERNAL_set_localPosition(ref Vector3 value);
private void INTERNAL_set_position(ref Vector3 value);

This frees you up to use alter how things are stored and which math is used later on.

Now, your set_EulerAngles will look like (and I'm using C# here because my C++ is rusty):

var halfPitch = value.x * 0.5f;
var sinPitch = Mathf.Sin(halfPitch);
var cosPitch = Mathf.Cos(halfPitch);

var halfYaw = value.y * 0.5f;
var sinYaw = Mathf.Sin(halfYaw);
var cosYaw = Mathf.Cos(halfYaw);

var halfRoll = value.z * 0.5f;
var sinRoll = Mathf.Sin(halfRoll);
var cosRoll = Mathf.Cos(halfRoll);

_Rotation = new Quaternion();
_Rotation.x = ((cosYaw * sinPitch) * cosRoll) + ((sinYaw * cosPitch) * sinRoll);
_Rotation.y = ((sinYaw * cosPitch) * cosRoll) - ((cosYaw * sinPitch) * sinRoll);
_Rotation.z = ((cosYaw * cosPitch) * sinRoll) - ((sinYaw * sinPitch) * cosRoll);
_Rotation.w = ((cosYaw * cosPitch) * cosRoll) + ((sinYaw * sinPitch) * sinRoll);

and get_EulerAngles would be something like:

var roll = Mathf.Atan2(2*(_Rotation.x*_Rotation.y + _Rotation.z*_Rotation.w),
                       (1 - 2*(_Rotation.y*_Rotation.y + _Rotation.z*_Rotation.z)));
var pitch = Mathf.Asin(2*(_Rotation.x*_Rotation.z - _Rotation.w*_Rotation.y));
var yaw = Mathf.Atan2(2*(_Rotation.x*_Rotation.w + _Rotation.y*_Rotation.z),
                      (1 - 2*(_Rotation.z*_Rotation.z + _Rotation.w*_Rotation.w)));
return new Vector3(pitch, yaw, roll);
\$\endgroup\$
1
  • \$\begingroup\$ You make a good point. I was mostly trying to mimic the Unity syntax to a certain degree, since I don't like Setters/Getters too much, but I definitely see why they're important here. Thank you very much. \$\endgroup\$
    – dyeo
    Commented Feb 23, 2015 at 23:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .