Physics of a vehicle on a slope


I'm working on implementing gravitational force affecting a vehicle in a game. The vehicle is a bicycle so it is only allowed to move along its local z-axis when it is touching the ground. All is well as long as the plane the bicycle is moving on is level - the velocity vector's y value can just be set to 0. When the bicycle is on a rotated plane, however, things get a bit more complicated. There are some things I now have to take into account:

  1. The velocity vector should always follow the plane the vehicle is standing on. How do I achieve that? Also, what happens if the vehicle falls down and hits a rotated ("not level") plane? Does the full length of the velocity get transferred along the plane if we assume none of it got absorbed by the collision?

  2. How much gravitational acceleration should be applied to the bicycle given the rotation of the bicycle and the plane itself? If the bicycle is rotated 90 degrees around the y-axis relative to the plane, no gravitational acceleration should be applied (level when looked at from the side of the bicycle). If it's rotated 0 degrees, the gravitational acceleration would equal g * sin(a) where a is the full angle relative to the vehicle's x-axis ("how much it's tipping when looked at from the side")

    Up to this point I've tried the following procedure for calculating the local angle the vehicle is standing on by using the normal of the plane:

// Local axis of this Node
Vector3f lx, ly, lz;
lx = new Vector3f(1,0,0);

ly = new Vector3f(0,1,0);

lz = new Vector3f(0,0,1);

// Find local angles
Vector3f normal = plane.getNormal();
Vector3f n = new Vector3f(normal);
Quaternion q = new Quaternion();

float[] a = localRotation.toAngles(null);
q.fromAngles(-0.5f * FastMath.PI, 0, 0); // [1]
q.fromAngleNormalAxis(a[1] + FastMath.PI, normal);

float langlex = n.angleBetween(lx);
float langley = n.angleBetween(ly);
float langlez = n.angleBetween(lz);

This works if the plane in question isn't rotated around the y-axis at all (see [1]). If I display the normal, n, lx, ly and lz vectors on this Node's parent I get the result I was hoping for:

But if I move the Node to a plane which has a different local axis it fails:

On these images the lines represent the following: red - local X axis, green - local Y axis, blue - local Z axis, cyan - plane normal, magenta/purple - n vector for computing the angle of which the vehicle (box) is standing on the plane. As you can see on the first image, the n vector (magenta) is following the plane and the vehicle's local z axis. On the second it fails because the plane is rotated around the y axis and the rotation of n doesn't take this into account (see [1]).

I'm sure there's a better way of doing this? I thought about using rotateUpTo(normal) on the vehicle Node and then reading off the local rotation, but that would result in the vehicle also rotating around it's local z axis which is unrealistic for a bicycle ... But would this work?

Any pointers would be greatly appreciated, as I'm a bit stuck at this point :/

- Johannes