Posts
Search
Contact
Cookies
About
RSS

Aligning a model with a terrain (raylib)

Added 8 Jul 2019, 11:24 a.m. edited 18 Jun 2023, 1:12 a.m.

There are a number of reasons you may not wish to use a physics engine, but instead, use very simple collisions as it may well be all you need. This leaves you in a position where you might need to align a model to a terrain, for example you might have a character or especially a vehicle that need to look like it is actually traversing the terrain, rather than standing perfectly upright all the time...

I'll be honest, when I started to implement this I thought it would be the work of moments, however, it was a little bit of a head scratcher, so I thought it was worth posting for others...

I'm keeping track of a boxes position and rotation, and making use of the models (box) transform matrix...

box.transform = MatrixRotateY(boxRot.y);

// 0 left   1 right     2 front     3 back
for (int i = 0; i < 4; i++) {
    corners[i].position = boxPos;
    corners[i].direction = (Vector3){ 0, -1, 0};
    Vector3 addon;

    if (i==0) addon = (Vector3){-(boxSize.x)/2.0,0,0};
    if (i==1) addon = (Vector3){ (boxSize.x)/2.0,0,0};
    if (i==2) addon = (Vector3){ 0,0,boxSize.z/2.0};
    if (i==3) addon = (Vector3){ 0,0,-(boxSize.z)/2.0};
    addon = Vector3Transform(addon, box.transform);
    addon.y += 10.0;
    corners[i].position = Vector3Add(corners[i].position, addon);
    hi[i] = GetCollisionRayModel(corners[i], &landModel);
}

The Y axis, is our heading or compass direction the box is facing on the terrain, initially we set the model transform so it is only rotated to our heading. In the loop above, we create 4 rays (corners) which aren't actually corners (but were going to be!) These rays are positioned directly to the sides and front and back of our model, being careful to position them above the highest possible point of the terrain.

This leaves us with an array of hit locations, points where the ray hits the terrain (landModel) around our box model.

Vector2 v1,v2;
v1 = (Vector2){ -(boxSize.x)/2.0, hi[0].distance};
v2 = (Vector2){  (boxSize.x)/2.0, hi[1].distance};
boxRot.z = -Vector2Angle(v1, v2)*DEG2RAD;

v1 = (Vector2){  (boxSize.z)/2.0, hi[2].distance};
v2 = (Vector2){ -(boxSize.z)/2.0, hi[3].distance};
boxRot.x = -Vector2Angle(v1, v2)*DEG2RAD;

Matrix mx,my,mz; // TODO can do this without as many matrices !
box.transform = MatrixIdentity();
mx = MatrixRotateX(boxRot.x);
my = MatrixRotateY(boxRot.y);
mz = MatrixRotateZ(boxRot.z);

Matrix r = MatrixMultiply(mx, my);
box.transform = MatrixMultiply(mz, r);

Using the distance of the hit ray, and the offset of each rays position, we can work out the orientation of the terrain at the boxes position, now we have all three angles of orientation for our box we can multiply these up and set our box transform.

boxPos.y = (hi[0].position.y + hi[1].position.y +
            hi[2].position.y + hi[3].position.y)/ 4.0 + .75;

Finally by using the average height of the collision points we can hover our model just slightly above the terrain, for extra credit if you were using a vehicle with wheels, you could draw 4 wheels so they exactly touch the terrain, as the distance will vary a little between the average height of the model and the terrain, it will look like your vehicle has suspension!

Enjoy!