Calculate object rotation based on terrain normal

Hello I’m programming a 3d game in c# monogame and currently I’m working on foliage.

I’m wanting foliage to face upwards as the same direction as the terrain tile its on, I have the normal of the tile represented by the green arrows. But the foliage is not correctly facing upwards as the same direction as the tiles Normal.

Currently I’m creating a world matrix using the Tiles Normal for the EULER rotation.XYZ like below

Matrix.Transpose(Matrix.CreateScale(size.X, size.Y, size.Z) * 
(Matrix.CreateRotationX(tileNormal.X) * Matrix.CreateRotationY(tileNormal.Y) * Matrix.CreateRotationZ(tileNormal.Z)) * 
Matrix.CreateTranslation(position));

As you can see within the red circles in the gameplay picture below the terrain normal (the green arrow) is not matching the direction the foliage is facing.

Thankyou for your time.

The issue will likely be because CreateRotation is expecting an angle in radians not a component of a normalized vector.

https://docs.monogame.net/api/Microsoft.Xna.Framework.Matrix.html#Microsoft_Xna_Framework_Matrix_CreateRotationX_System_Single_

Rather than convert to angles a simple fix would be to use CreateFromAxisAngle instead with the normalized vector as the axis parameter.

https://docs.monogame.net/api/Microsoft.Xna.Framework.Matrix.html#Microsoft_Xna_Framework_Matrix_CreateFromAxisAngle_Microsoft_Xna_Framework_Vector3_System_Single_

Alternatively a rotation matrix could be created by setting Matrix.Up, Matrix.Forward and Matrix.Right directly if you have all three vectors already.

1 Like

Hello Squarebananas,

Thankyou for your reply. I tried using Matrix.CreateFromAxisAngle I set the normal as a Axis but I don’t know what angle parameter to enter as I’m using the normal to find the terrains angle.

I tried using Matrix.CreateFromAxisAngle using Up, Forward, Right etc like below

Matrix.Transpose(Matrix.CreateScale(size.X, size.Y, size.Z) *
   (Matrix.CreateFromAxisAngle(Vector3.Right, rotation.X) *
    Matrix.CreateFromAxisAngle(Vector3.Up, rotation.Y) *
    Matrix.CreateFromAxisAngle(Vector3.Backward, rotation.Z)) *
    Matrix.CreateTranslation(position));

But it wasn’t 100% always aligning correctly to the normal.

The first method I was describing was this:

float directionInRadians = 0f;
Matrix.Transpose(Matrix.CreateScale(size.X, size.Y, size.Z) * 
Matrix.CreateFromAxisAngle(tileNormal, directionInRadians) * 
Matrix.CreateTranslation(position));

The object’s up direction will match the terrain’s normal with this. The angle is the roll around this axis which should be set to which direction you want the object to face, it isn’t related to any terrain normal/angle calculations.

The second method would look something like:

Matrix rotationMatrix = Matrix.Identity;
rotationMatrix.Up = tileNormal;
rotationMatrix.Forward = tileForward; // or face forward using Vector3.Normalize(Vector3.Cross(tileNormal, Vector3.Right))
rotationMatrix.Right = tileRight; // or calculate using Vector3.Normalize(Vector3.Cross(rotationMatrix.Forward, tileNormal))

Matrix.Transpose(Matrix.CreateScale(size.X, size.Y, size.Z) * 
rotationMatrix * 
Matrix.CreateTranslation(position));
1 Like

Thankyou Squarebananas,

I used the second method and it aligns the model with the terrains normal perfectly.

1 Like