0

I'm trying to make a controllable ball in OpenGL. I'm using my own matrix class to transform the object matrix, but I can't seem to get the Rotation right. I always end up with the ball rotating around the local axis. This is how it looks right now https://gfycat.com/LongKindBassethound . The long line are the local axis.

So when the ball moves forward the next side movement will be wrong. Theres a function in the matrix class that allows rotation around any axis:

Matrix& Matrix::rotationAxis(const Vector& Axis, float Angle) {
const float Si = sin(Angle);
const float Co = cos(Angle);
const float OMCo = 1 - Co;
Vector Ax = Axis;
Ax.normalize();

m00= (Ax.X * Ax.X) * OMCo + Co;
m01= (Ax.X * Ax.Y) * OMCo - (Ax.Z * Si);
m02= (Ax.X * Ax.Z) * OMCo + (Ax.Y * Si);
m03= 0;

m10= (Ax.Y * Ax.X) * OMCo + (Ax.Z * Si);
m11= (Ax.Y * Ax.Y) * OMCo + Co;
m12= (Ax.Y * Ax.Z) * OMCo - (Ax.X * Si);
m13= 0;

m20= (Ax.Z * Ax.X) * OMCo - (Ax.Y * Si);
m21= (Ax.Z * Ax.Y) * OMCo + (Ax.X * Si);
m22= (Ax.Z * Ax.Z) * OMCo + Co;
m23= 0;

m30= 0;
m31= 0;
m32= 0;
m33= 1;

return *this;
}

I think with this I can take the world direction vectors and transform them to the local space of the object and then rotate around the result. I don't really know how to do that though (matrix of the ball * world vector? That doesn't work). I would really like to avoid quaternions, but if I can't do that I would appreciate suggestions in that direction too.

EDIT: More Info

The transforamtion Code. As you can see I tried different methods that all do the same... So no surprise there that it doesnt work.

Matrix transM, rotX, rotZ;
rotationX = straight;
rotationZ = side;
if (velocity != Vector(0, 0, 0)) {
    velocity.X = -0.0005 * DeltaTime;
    velocity.X = clamp(velocity.X, 0, FLT_MAX);

    velocity.Z = -0.0005 * DeltaTime;
    velocity.Z = clamp(velocity.Z, 0, FLT_MAX);
}

velocity.X += speed * -side * DeltaTime;
velocity.Z += speed * straight * DeltaTime;
transM.translation(velocity.X, 0, velocity.Z);

if (velocity.Z != 0 || velocity.X != 0) {
    //http://mathworld.wolfram.com/EulerAngles.html
    //http://gamedev.stackexchange.com/questions/67199/how-to-rotate-an-object-around-world-aligned-axes

    Vector localAxisX =  m_Ball * Vector(1, 0, 0);
    Vector localAxisZ = m_Ball * Vector(0, 0, 1);
    rotX.rotationAxis(Vector(1, 0, 0), 0.5* M_PI * straight * DeltaTime);
    rotZ.rotationAxis(Vector(0, 0, 1), 0.5* M_PI * side * DeltaTime);
    //rotX.rotationX(0.5* M_PI * straight * DeltaTime * 3);
    //rotZ.rotationZ(0.5* M_PI * side * DeltaTime * 3);
    //Matrix fullRotation.rotationYawPitchRoll(Vector(0,  0.5* M_PI * straight * DeltaTime, 0.5* M_PI * side * DeltaTime));
    m_Ball = transM * m_Ball * (rotX*rotZ);
}
else {
    m_Ball = transM * m_Ball;
}

Draw code with my previous attempt trying to use glRotatef (obviously commented out right now)

void Ball::draw(float DeltaTime) {
  glPushMatrix();
  glMultMatrixf(m_Ball);
  if(rotationX)
    glRotatef(0.5* M_PI * rotationX * DeltaTime, 1.0, 0.0, 0.0);
  if(rotationZ)
    glRotatef(0.5* M_PI * rotationZ * DeltaTime, 0.0, 0.0, 1.0);
  g_Model_ball.drawTriangles();
  glPopMatrix();
  drawAxis();
}
lega
  • 13
  • 7
  • 1
    Please post your driver code from where you invoke this function, and secondly do you have this requirement to right your own rotation function? if not you can use glRotatef() – A.B. Aug 23 '16 at 13:46
  • Edit with more info – lega Aug 23 '16 at 13:55
  • Please have a look at this article to understand how OpenGL Transformations work https://open.gl/transformations – A.B. Aug 23 '16 at 14:40
  • 1
    see [Understanding 4x4 homogenous transform matrices](http://stackoverflow.com/a/28084380/2521214) and look for the difference between local and global rotations in bullet **#5** – Spektre Aug 25 '16 at 06:09
  • THIS is what I searched for! Thanks man it works now. I'm gonna update with a solution. I'm still doing some weird stuff but it works! – lega Aug 25 '16 at 13:23

2 Answers2

1

I highly suggest using quaternions to easily handle compound rotations and avoid gimbal lock.

https://en.wikipedia.org/wiki/Gimbal_lock

Ok with regards to your comments and video, You want to rotate around the ball's center. It seems you accumulate your rotations in m_Ball but do a weird transM multiplication. Also you are probably accumulating translations in transM.

Try not to mix your translations and rotations and avoid accumulating them in your m_Ball. You can do something like this.

//transformation matrix
transM.translation(velocity.X, 0, velocity.Z);

//accumulate translations in m_BallT
m_BallT = transM * m_BallT;

//final Rotation
Matrix rotation = rotX * rotZ;

//accumulate rotations in m_BallR
m_BallR = rotation * m_BallR;

//now compute your final transformation matrix from accumulated rotations and translation
m_Ball = m_BallT * m_BallR;

note how m_BallR is just rotations accumulated. Post multiplication will ensure new rotation is applied after accumulated rotations in m_BallR. Finally translate to the final position accumulated in m_BallT. Your ball will rotate about its center and move according to m_BallT.

You could also simply replace the transformation component on your m_BallR to avoid extra matrix multiplications.

Harish
  • 964
  • 6
  • 17
  • I edited my post. If I understand you correctly I'm pretty sure I'm allready doing that. – lega Aug 23 '16 at 14:00
  • I don't think you are doing that, Extract the world space position from your ball, rotate that position vector with the rotation matrix and then move the ball to the new position. Don't multiply compound rotation matrices with the ball's matrix. – Harish Aug 23 '16 at 14:07
  • https://gfycat.com/BowedCaringArkshell this is the result when I use your approach. My goal is this: https://gfycat.com/LongKindBassethound with proper rotation – lega Aug 23 '16 at 14:37
  • @lega OK It was not clear from the question. Check the updated answer. – Harish Aug 23 '16 at 15:31
0
Vector newPos(m_Ball.translation().X + velocity.X, terrainNoise.GetHeight(m_Ball.translation().X, m_Ball.translation().Z) + 0.5, m_Ball.translation().Z + velocity.Z);

rotX.rotationAxis(Vector(1, 0, 0), 0.5* M_PI * straight * DeltaTime * abs(velocity.Z) * 100);
rotZ.rotationAxis(Vector(0, 0, 1), 0.5* M_PI * side * DeltaTime * abs(velocity.X) * 100);

m_Rotation = (rotX*rotZ);

m_Ball = (m_Ball.invert() * m_Rotation).invert();


m_Ball.m03 = newPos.X;
m_Ball.m13 = newPos.Y;
m_Ball.m23 = newPos.Z;

This is the solution I came up with after reading this link provided by @Spektre. Basically you just invert the ModelMatrix of the ball to get it into world position, do your rotation and then transform it back into local space.

You have to set the newPos Vector before you rotate, otherwise it would affect future transformations.

Community
  • 1
  • 1
lega
  • 13
  • 7
  • You can avoid the matrix inversions by accumulating rotations and translations separately. It will be more efficient. Since you overwrite the translation vector anyways you are actually only inverting the rotations and any scale. – Harish Aug 29 '16 at 12:52
  • @Harish that is not more efficient in matter of fact it is the opposite for many reasons ... and you can not stack arbitrary transformations that way . btw inversion is really easy on orthogonal homogenouos transform matrices as it is just `transpose()` ... – Spektre Aug 31 '16 at 04:44
  • @Spektre If you do A = (A' x B)' you say its more efficient than A = B x A? What am I missing here? How are you not accumulating in the first case? You still are accumulating the rotation just in a different way whether A' = transpose(A) or not does not make a difference. – Harish Aug 31 '16 at 06:20
  • @Harish once you separate translation and rotation matrices then you got 2 separate matrices instead of 1 making impossible to stack arbitrary transformations. In the final rendering you need to apply the transformations to all the vertexes so it is faster to multiply by single matrix then to apply 2 of them on per vertex basis. That is why the 2 matrix case is not more efficient. It was used in the past in special cases where no complicated movement was present and all the computations where done on CPU/FPU. This approach does not behave well on modern GPU architectures – Spektre Aug 31 '16 at 08:14
  • @Spektre It doesn't have to be two matrix multiplications, I just transferred transformation matrix code from his original implementation in order to simplify understanding for him. You can also simple accumulate rotations and replace the transformation part like he is doing in this answer anyways. I prefer not doing unnecessary transpose or inverse as one cl always keep rotations separately and apply them before translation to get the effect he is looking for. – Harish Aug 31 '16 at 08:59
  • @Harish I understand but then you can not combine arbitrary number of transforms without a fix code... for example I want to follow `n-th` child/bone of some object with camera follow with some degree of freedom movement with mouse around the base view. To handle that you need to make a program that computes it not just simply multiply the matrices in your way ... Yes you have less operation per final matrix computation (which is a bit meaningless if you realize how many times per frame you are computing this) but the non univerzality is too big of a price (at least for me) – Spektre Aug 31 '16 at 09:54
  • @Spektre You can't follow `n-th` child by doing `A = (A' * B)'` either. I don't get if you suggest that this is not a good solution in general or not a good solution for the problem in question. I am arguing that his answer is unnecessarily expensive compared to mine considering the object is just one ball that needs to be rotated. I don't think double inverse is better unless that is the only way one can do operations like if you had a pixel rendered from a camera and want to project it back to world and into another camera or light, I understand why one would need inverse matrices. – Harish Aug 31 '16 at 10:23