+1 vote

I have a 3D character that walks around 3D terrain using navmesh. I want the character to be perpendicular to the terrain at all times. I can get a normal to the terrain mesh right below the character with raycasting but how do I rotate the character so he is aligned to that normal?
I got confused with all this euler, quaternions, basis stuff and my vector-fu is not good to solve this.

in Engine

+1 vote

my vector-fu is not good to solve this.

Look, I don't want to be rude since you phrased it in such a funny way. However to quote Rick "Let's do it the dumbest way possible because it's easier for you."

This is something that if you do it half, will cause you lots of problems in the long run. The right thing to do for you is to learn linear algebra and how cross product works.

That said, what you want only works with animals that have long bodies, like a lizard, and aligns them self with the terrain.
Other animals, like humans, will always align inverse towards gravity. Inverse Kinematics is used to make it look like they are walking on the terrain.

Easy hack solution:
We need what a cross product gives us, not only a up direction align to the normal, but also a forwards directions; without this your object can't rotate after it is aligned to the normal.
The easy way we use 2 objects, one to align to the normal and the player to rotate.

1.) Make KinematicBody, give it a basic collision shape. This is the body that will be aligned to the floor. Attach an RayCast node to it.

``````    #First move the body to the collision point
get_node("..").transform.origin = self.get_collision_point()

#Next adjust the normal using the collision point
var RelativeNormal = \
get_node("..").transform.origin + self.get_collision_normal();

#Last use the godot look_at function to rotate the object
get_node("..").look_at(RelativeNormal, Vector3.UP)
``````

So the above body rotates itself based on the normal of the mesh, Next you add your Player object as a child of that object.
Now your player can do everything it does normally, including rotate. However physics has to be done using the first node.

The math used:
RelativeNormal: to look at a normal, we need to add it to the objects already existing position.
If the Object is at (0, 0, 0) and the Normal is (0, 1, 0) then (0, 0, 0) + (0, 1, 0) = (0, 1, 0) meaning it needs to look up.
(2, 3, 4) + (0, 1, 0) = (2, 4, 4) and (2, 4, 4) is (0, 1, 0) higher than (2, 3, 4). Meaning it needs to look at the point above it self.

by (1,492 points)

Seems like I'm missing something but if normal to the terrain is (0, 1, 0) then my player object should look somewhere at (x, 0, z), no? I mean long bodied animal cannot even look at (0, 1, 0) lying on a horizontal plane unless it's some sort of a flatfish?

I found "void rotate( Vector3 axis, float angle )" function of spatial and now I'm thinking of plugging cross product of normal and player UP vector as an axis and then somehow finding an angle between normal and player's UP for rotation angle.

By the way, is there a difference between using transform.basis and spatial.translation? I always used translation but you use basis.

Sorry for the very late reply, I don't get emails on all the questions for some reason.

Seems like I'm missing something but if normal to the terrain is (0, 1, 0) then my player object should look somewhere at (x, 0, z), no?

The problem is that (0,1,0) just means up, that is all it means. It tells you what direction of the surface is pointing at. In this case up.
Normally you calculate "forward" with cross product, but even that needs a second direction, or by rotating the vector like a Rubik cube.

The formula I gave you makes a empty node that always looks up. So you can manually position the player as you want, after the up vector is calculated. You see the script rotates a object to the red arrow. All you need to do is make the player a child of the object with the script.
Then rotate the player so it is standing up. Then it will always be rotated in the same direction as the surface.

By the way, is there a difference between using transform.basis and
spatial.translation? I always used translation but you use basis.

Yes. Spatial.translation moves the object in physics space, so the object can collide and has delta time.
Transform.basis is like teleporting the object to the assigned location. It will not collide and I have to manually add delta time.