[SOLVED!] Ball rolling problem

Started by
15 comments, last by ch1haya 13 years, 1 month ago

Does matrix stack help about this?

The matrix stack is an orthogonal concern. (That is, the choice of method for updating the ball's orientation probably won't determine whether or not a matrix stack needs to be used.)

Are there any method to rotate an object without rotating the axes?
I'm not sure what you mean by 'without rotating the axes'. Regardless, the method I and Burnt_Fyr referred to previously is pretty much the way to do it, so if you're still having trouble with it, perhaps you could explain what part you're stuck on.
Advertisement
OK, I better explain all algorithms, matrices I have, which may help in dealing with ball rolling problem.

I have this utility for rotating along arbitrary axis

D3DXMATRIX D3DUtil_ArbitraryRotation(D3DXVECTOR3 axis, FLOAT angle){
FLOAT nx = axis.x;
FLOAT ny = axis.y;
FLOAT nz = axis.z;
FLOAT c = cos(angle);
FLOAT s = sin(angle);
return D3DXMATRIX(
c+(1+c)*nx*nx, (1-c)*nx*ny-s*nz, s*ny+(1-c)*nx*nz, 0,
s*nz+(1-c)*nx*ny, c+(1-c)*ny*ny, (1-c)*ny*nz-s*nx, 0,
(1-c)*nz*nx-s*ny, s*nx+(1-c)*nz*ny, c+(1-c)*nz*nz, 0,
0, 0, 0, 1
);
}

Algorithm:

some declarations in D3D application class:

FLOAT m_fElapsedTime; // 1/fps, time interval of rendering one frame
D3DXMATRIX matRotation; //the matrix of rotation

FLOAT m_fBallRotX; // Ball rotation state X-axis
FLOAT m_fBallRotZ; // Ball rotation state Z-axis
FLOAT m_fBallTransX; // Ball position along X-axis
FLOAT m_fBallTransZ; // Ball position along Z-axis

//********************************************************************************************************************************************************************
// Below are the unit vectors pointing to X, Y, Z axes that is seen in camera. NOT the axes of object! So they are no longer (1,0,0),(0,1,0),(0,0,1) if rotated.
// I think that these are the keys of problem.
D3DXVECTOR3 BallZAxisVec(); // apparent Z axis unit vector, according to the rotated axes
D3DXVECTOR3 BallYAxisVec(); // apparent Y axis unit vector, according to the rotated axes
D3DXVECTOR3 BallXAxisVec(); // apparent X axis unit vector, according to the rotated axes
//*******************************************************************************************************************************************************************


In the Render() function which runs with time interval m_fElapsedTime:

D3DXVECTOR3 RotX, RotZ, RotDelta, NextRotation;
if( keydown(D) ){
if ( m_fBallTransX > m_fPlatformSizeX/(-2.0f) + m_fBallRadius ){
m_fBallRotZ += m_fElapsedTime;
m_fBallTransX -= m_fElapsedTime;
RotZ = D3DUtil_ArbitraryRotation( BallZAxisVec(), 0-m_fElapsedTime );
}
}
else if( keydown(A) ){
if ( m_fBallTransX < m_fPlatformSizeX/(2.0f) - m_fBallRadius ){
m_fBallRotZ -= m_fElapsedTime;
m_fBallTransX += m_fElapsedTime;
RotZ = D3DUtil_ArbitraryRotation( BallZAxisVec(), m_fElapsedTime );
}
}
if( keydown(W) ){
if ( m_fBallTransZ < m_fPlatformSizeX/(2.0f) - m_fBallRadius ){
m_fBallRotX += m_fElapsedTime;
m_fBallTransZ += m_fElapsedTime;
RotX = D3DUtil_ArbitraryRotation( BallXAxisVec(), m_fElapsedTime );
}
}
else if( keydown(S) ){
if ( m_fBallTransZ > m_fPlatformSizeX/(-2.0f) + m_fBallRadius ){
m_fBallRotX -= m_fElapsedTime;
m_fBallTransZ -= m_fElapsedTime;
RotX = D3DUtil_ArbitraryRotation( BallXAxisVec(), 0-m_fElapsedTime );
}
}
D3DXMatrixMultiply( &RotDelta, &RotX, &RotZ);
D3DXMatrixMultiply( &NextRotation, &RotDelta, &matRotation );
matRotation = NextRotation;
}

As I said above, the computation of the unit vectors for apparent X,Y,Z axes are not implemented, because I don't even know how to calculate them.
Please tell me about how to compute their unit vectors. Some good diagrams may also help.

For example:
After the ball rotated along Z-axis 45 degrees, apparent Z-axis is the real Z-axis(0,0,1), but apparent X-axis is (1/sqrt(2),-1/sqrt(2),0)

But I need a formula. If anyone can give me a formula, this will be a great help.
Also, please tell me if the calculations for axes require incremental approach.
Could you repost your code using 'code' tags? (Also, make sure the indentation is preserved by the formatting.)
I have changed the post now. Read it again. Thanks.
I have this utility for rotating along arbitrary axis
Doesn't the DirectX math library already have a function for this?

FLOAT m_fBallRotX;
FLOAT m_fBallRotZ;
As long as you have anything like the above in your code, you're doing it wrong. What you want to do is something like I described earlier, which I'll quote here for convenience:

1. Store the orientation of the ball as a quaternion or matrix
2. For each update, if the speed of the ball is greater than a specified epsilon:
3. The axis of rotation is the normalized cross product of the ball's velocity vector and the current 'up' vector
4. The angle of rotation (in radians) is the distance traveled by the ball during that update divided by the ball's radius
5. Apply this axis-angle rotation to the ball's orientation
6. Re-normalize the orientation to prevent drift (normalization for quaternions, orthogonalization for matrices)
I'm not sure what you're asking about rotating axes and so forth, but if you use the method described above, you shouldn't have to rotate any vectors 'manually', so to speak.

What I'd recommend is going through the steps above, and then when you get to a step you don't fully understand, post back with any questions you have about it.
Actually I don't know about how to do step 3. Please explain about "the current 'up' vector".
Make use of D3DXMATRIX, D3DXVECTOR3 for calculations. Or explain in form of Maths, and I try to convert into D3DX9 code
Actually I don't know about how to do step 3. Please explain about "the current 'up' vector".
Sure. Assuming you have an 'up' vector available, the axis of rotation is:

vector3 axis = normalize(cross(velocity, up));
If that ends up pointing the 'wrong way', just swap the arguments to cross(). (Also, note that this will fail is the speed of the ball - i.e. the length of the velocity vector - is small, so you'll want to check for that case and skip the whole process if the speed is below some specified threshold value.)

Now, by 'up' vector, I mean the normal of the surface that the ball is currently 'rolling' on. I can't tell you where to get this vector, because that depends on the context. For example, if you just have a ball rolling around on a flat plane, the 'up' vector will always be the plane normal. If the ball is rolling around on a terrain, the 'up' vector will likely be the normal at the point where the ball touches the terrain (this holds for any complex surface, more or less). And so on.

In other words, *you* have to decide where the 'up' vector comes from.
Thanks all, with help of you plus the notes from Prof. Andrew J. Hanson, I solved this problem
I'd like to share the code and help anyone who have the same problem.
Note that this function only returns the correct incremental rotation matrix when the ball is rolling on flat platform.

D3DXVECTOR3 dir: the current direction (But actually velocity)
FLOAT ball_radius: trivial from its name
D3DXMATRIX cur: current orientation of the ball
FLOAT time: time interval


D3DXMATRIX D3DUtil_BallRolling(D3DXVECTOR3 dir, FLOAT ball_radius, D3DXMATRIX cur, FLOAT time){
//Neglect y-axis.
FLOAT dr = sqrt(dir.x*dir.x+dir.z*dir.z);

//Rotation axis initialization. This is the apparent one.
D3DXVECTOR4 axis;
axis.x = -dir.z/dr;
axis.y = 0.0f;
axis.z = dir.x/dr;
axis.w = 1.0f;

//--------------------------------------------------------------------------------
// Desc:
// Since the x,y,z axes of the ball are rotated with the ball,
// We have to make use of the current orientation matrix of the ball.
// Transform the rotation axis with inverse matrix of current orientation matrix.
//--------------------------------------------------------------------------------
D3DXMATRIX axistransform;
D3DXMatrixInverse(&axistransform, NULL, &cur);
D3DXVec4Transform(&axis, &axis, &axistransform);

FLOAT nx = axis.x;
FLOAT ny = axis.y;
FLOAT nz = axis.z;

FLOAT angle = dr/ball_radius * time;
// Minimize the computation steps.
FLOAT c = cos(angle);
FLOAT s = sin(angle);

D3DXMATRIX result;
if (dr != 0 && nx <=1 && ny <=1 && nz <=1) result = D3DXMATRIX(
c+nx*nx*(1-c), nx*ny*(1-c)-nz*s, nx*nz*(1-c)+ny*s, 0,
ny*nx*(1-c)+nz*s, c+ny*ny*(1-c), ny*nz*(1-c)-nx*s, 0,
nz*nx*(1-c)-ny*s, nz*ny*(1-c)+nx*s, c+nz*nz*(1-c), 0,
0, 0, 0, 1
);
else D3DXMatrixIdentity(&result);
return result;
}

This topic is closed to new replies.

Advertisement