Get rotation on Y axis from btRigidBody

Post Reply
paokakis
Posts: 29
Joined: Mon Jan 04, 2021 10:31 am

Get rotation on Y axis from btRigidBody

Post by paokakis »

Hello to the group,

Is it possible to get the rotation on Y axis of a btRigidBody? I've tried the following without success:

Code: Select all

btScalar yaw;
btScalar pitch;
btScalar roll;
body->getCenterOfMassTransform().getBasis().getEulerZYX(yaw, pitch, roll, 1);
and

Code: Select all

glm::vec4 getRotation(btRigidBody* body)
{
	glm::vec4 Rotation;
	btQuaternion q = body->getCenterOfMassTransform().getRotation();

	btVector3 v = q.getAxis();
	Rotation.x = v.x();
	Rotation.y = v.y();
	Rotation.z = v.z();
	Rotation.w = q.getAngle();
	
	return Rotation;
}

float GetRotationY() {
	glm::vec4 Rotation = getRotation(mBody);
	return Rotation.y * Rotation.w; 
}
Both without success. Is there any other way of getting the rotations in degrees of the 3 different axes?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Get rotation on Y axis from btRigidBody

Post by drleviathan »

Is it possible to get the rotation on Y axis of a btRigidBody?
Body rotations are not necessarily about the Y-axis, and since rotations don't commute then it doesn't make sense to ask "How much of some general rotation is about the just the Y-axis?", unless you had a very clear recipe for how the general rotation is decomposed into partial rotation (e.g. the Euler angle decomposition which is (I think): yaw about local Y, X, Z in that order, unless the lib was compiled with BT_EULER_DEFAULT_ZYX defined). This is why your question doesn't make sense in general.

However, if you knew your rotation really was only about the Y-axis then you could ask: "How do it measure the angle of the rotation?", and for such a narrow subset of all possible rotations your question would make sense.

Please rephrase your question, or elaborate about what you really want to do. For example something like: "I have a character and I want to rotate it such that its local-y is parallel to world-y."
paokakis
Posts: 29
Joined: Mon Jan 04, 2021 10:31 am

Re: Get rotation on Y axis from btRigidBody

Post by paokakis »

My problem is that I have a Camera that follows a spaceship and I need to feed to the Camera X,Y,Z rotations of the object to make it rotate and follow the body in every movement. So I need to retrieve the separate rotations of the body to feed them into the Camera...
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Get rotation on Y axis from btRigidBody

Post by drleviathan »

It is a Bad Idea to decompose rotations to Euler angles. It will be source of frustration and fragility. If you can change your Camera API then I would recommend you do so and have it accept a rotation, or better yet a transform.

This is how I would do it. Dunno if this will work for you but maybe someone will find it useful:

Consider the trivial case where the Camera is always at a fixed transform behind the Spaceship in the Spaceship's local frame. That is, the Camera is locked onto the Spaceship no matter how it moves. In this case the Camera's world-frame transform can be computed from the Spaceship's:

Code: Select all

cameraTransform = spaceshipTransform * cameraTransformLocalFrame;
Where for berevity the world-frame transforms are understood to be without a "WorldFrame" qualifier in their names.

Note, if done with btTransforms then that math solves both the linear and angular parts.

Consider the more interesting case where the Camera's linear transform relative to the Spaceship is fixed, but its rotation lags a little bit as if it were on a torsional spring. This is trickier. The pseudo code looks something like this (I did not test, it might have bugs, etc):

Code: Select all

// Compute where the Camera is going
cameraTargetRotation = spaceshipRotation * cameraLocalRotation;

// Compute the deltaRotation between where the Camera is, and where it wants to be
deltaRotation = cameraTargetRotation * cameraRotation.inverse();

// Note: how did I know how to compute deltaRotation?
// The trick is to imagine these rotations are operating FROM THE LEFT on a
// hypothetical vector on the far RIGHT.  The inverse rotation operates first
// and would rotate the vector into camera-local.  The second rotation would
// rotate that result back into the world-frame exactly where it is targeted.
// In other words: the end result would be deltaRotation.

// We want to move the Camera toward its destination, but not the full delta.
// This is where Quaternion's really shine.  We can calculate a "partial rotation"
// in the right direction by interpolating on the 4D hypersphere.
// We can either lerp (linear interpolate) which is fast or slerp (spherically interpolate)
// which is slightly more CPU expensive.
//
// In any case we have to compute the the fraction of interpolation between 0 and 1.
// Best to do this dynamically using the timestep between last frame since otherwise
// the rate of Camera movement will be affected by the framerate of the simulation.
// This is easy to tune by setting the natural exponential decay time.  Think of this
// like a "half-life" however it is the time for the delta to reduce to 1/e of what
// it was.  For fast slewing use a short timescale and for slow slewing increase the
// timescale.
const CAMERA_SLEW_TIMESCALE = 0.3; // seconds, tune this as necessary
fraction = timestep / CAMERA_SLEW_TIMESCSALE;
if (fraction > 1.0) {
    // DANGER! fractions greater than 1.0 are not stable
    fraction = 1.0;
}

// Compute new cameraRotation
//
// DANGER! Quaternions have a discrepancy of two: for every valid rotation there
// are two Quaternions on the hypersphere that map to it.  Normally this is not
// a problem but it IS when interpolating between two Quaternions.  Always interpolate
// between the nearest of the two.  To do this we check their dot-product and if it
// is negative then they are on opposite hypersphers and we negate one of them
// to the other side.
if (deltaRotation.dot(cameraRotation) < 0.0) {
    deltaRotation *= -1.0;
}
partialDeltaRotaton = identityRotation.slerp(deltaRotation, fraction);
cameraRotation = partialDeltaRotation * cameraRotation;

// DANGER! When multiplying two Quaternions together the result can be slightly non-normal
// (unit length != 1.0).  This is not a problem when the result will be discarded after
// it is used.  However, when a result is saved and used to recompute itself, like we're
// doing here for for cameraRotation, then eventually floating point error will creep in
// and we'll end up with Quaternions that cause linear distortions.  Therefore, it is
// important to nomalize cameraRotation after each fresh computation.
cameraRotation.normalize();
paokakis
Posts: 29
Joined: Mon Jan 04, 2021 10:31 am

Re: Get rotation on Y axis from btRigidBody

Post by paokakis »

Thank you for the reply and the code. I will try your code. In the meantime this is how the camera works if you think you can make it work with bullet :

This is for the position behind the spaceship calculation:

Code: Select all

float CalculateHorizontalDistance() {
	if (!mpPlayer) return 0.f;

	return distanceFromPlayer * glm::cos(glm::radians(-UpAngle));
}

float CalculateVerticalDistance() {
	if (!mpPlayer) return 0.f;

	return distanceFromPlayer * glm::sin(glm::radians(-UpAngle));
}

void calculateCameraPosition(float horizDistance, float verticDistance)
{
	float theta = mpPlayer->GetRotationY() + angleAroundPlayer;
	float offsetX = horizDistance * glm::sin(glm::radians(theta));
	float offsetZ = horizDistance * glm::cos(glm::radians(theta));

	Position.x = mpPlayer->GetPosition().x - offsetX;
	Position.y = mpPlayer->GetPosition().y + verticDistance;
	Position.z = mpPlayer->GetPosition().z - offsetZ;
}
this is how the orientation and different vectors are calculated :

Code: Select all

void updateCameraVectors()
{
	// Yaw
	glm::quat aroundY = glm::angleAxis(glm::radians(-RightAngle), glm::vec3(0, 1, 0));

	// Pitch
	glm::quat aroundX = glm::angleAxis(glm::radians(UpAngle), glm::vec3(1, 0, 0));

	// Roll
	glm::quat aroundZ = glm::angleAxis(glm::radians(RollAngle), glm::vec3(0, 0, 1));

	Orientation = aroundY * aroundX * aroundZ;

	glm::quat qF = Orientation * glm::quat(0, 0, 0, -1) * glm::conjugate(Orientation);
	Front = { qF.x, qF.y, qF.z };
	Right = glm::normalize(glm::cross(Front, WorldUp));
	Up = glm::normalize(glm::cross(Right, Front));

}
and this is how the view matrix is calculated :

Code: Select all

glm::mat4 GetViewMatrix()
{
	// You should know the camera move reversely relative to the user input.
	// That's the point of Graphics Camera

	glm::quat reverseOrient = glm::conjugate(Orientation);
	glm::mat4 rot = glm::mat4_cast(reverseOrient);
	glm::mat4 translation = glm::translate(glm::mat4(1.0), -Position);

	return rot * translation;
}
All code is C++ and I work with OpenGL. Hopefully you can understand the quaternions used in the Camera and maybe you have a few ideas on how they can be combined with bullet.
Post Reply