How to align an object with a normal?

question
#1

I’m just trying to figure out how to rotate an object to line it up with a normal from a cast physics ray node.

I can get the normal.
I can rotate the object.

I’m just not sure how to convert the normal into a rotation for the Set Rotation node.

How could we go about this?

1 Like

Set Rotation showing limited range
#2

Did you try to use look at node? Maybe you even don’t need to cast the ray

0 Likes

#3

Hi,

here’s how you align one vector with another using quaternions and haxe:

var v1 = new Vec4(0, 1, 0, 0);
var v2 = new Vec4(0.707, 0.707, 0, 0);

q = new Quat();
// calculate Quaternion which transforms v1 to v2
q.fromTo(v1,v2);
q.normalize();

// sanity check, transform v1 using the calculated quaterion
var v = new Vec3(v1.x, v1.y, v1.z);

n = qMult(q, v);
// result must be v2
trace(n);

I did not find a haxe method to apply the quaternion to a Vec3 so I implemented qMult as follows:

public function qMult(quat:Quat, vec:Vec3): Vec3{
     var num = quat.x * 2.0;
     var num2 = quat.y * 2.0;
     var  num3 = quat.z * 2.0;
     var num4 = quat.x * num;
     var num5 = quat.y * num2;
     var num6 = quat.z * num3;
     var num7 = quat.x * num2;
     var num8 = quat.x * num3;
     var num9 = quat.y * num3;
     var num10 = quat.w * num;
     var num11 = quat.w * num2;
     var num12 = quat.w * num3;
     var result = new Vec3();
     result.x = (1.0 - (num5 + num6)) * vec.x + (num7 - num12) * vec.y + (num8 + num11) * vec.z;
     result.y = (num7 + num12) * vec.x + (1.0 - (num4 + num6)) * vec.y + (num9 - num10) * vec.z;
     result.z = (num8 - num11) * vec.x + (num9 + num10) * vec.y + (1.0 - (num4 + num5)) * vec.z;
     return result;
 }	

source: https://answers.unity.com/questions/372371/multiply-quaternion-by-vector3-how-is-done.html

0 Likes

#4

Thanks for the reply. The issue with this as I understand it, is that this will make my object point to the centre of the other object, rather than aligning with it’s surface normal. That’s fine for a sphere, but not for other shapes.

EDIT: Hang on… maybe you’re on to something… I was only thinking about “Look at” in terms of looking at the terrain object coordinates… But if I used the Raycast Hit co-ordinates instead… it just… might… work…

0 Likes

#5

Maybe I’m misunderstanding something… but I don’t have two vectors. I’m trying to align a player’s rotation (quaternion), with the surface normal of the terrain below them (vector3).

o I’m trying to get from the vector3 I have, to a rotation (quaternion) that I can set to make the character align to the terrain.

0 Likes

#6

Armored_Blob:

sorry, I had read your question correctly but thought that your problem was to align to vectors. Now, I do not fully get what you want to achieve.

You have an object (your player) and it has a quaternion describing it’s rotation. You also have a terrain wiht a normal. How would you like your player to be oriented (probabaly perpendicular to the surface, but facing which direction)?

0 Likes

#7

Ideally, the object would rotate around its local x and y axis’ to align with the terrain surface normal while keeping the rotation around the z axis (the direction the player is facing) untouched.

I posted in Logic Nodes, because I was hoping that there was some kind of “Vectors to Rotation” type node, where you just plugged in two vectors (an align and an up/forward vector) and it output a rotation.

Maybe this needs to be done with Haxe, which is fine, or maybe this is a good case for a custom node.

1 Like

#8

I think I figured it out. Try this ( I haven’t tested myself ):

So the normal is a vector that points in the direction of the normal, so adding that to the objects current location will yield a point in space that is in the direction of the normal, away from the object. Telling the object to look at that point, from its current location should point the Z axis of the object in the direction of the normal.

0 Likes

#9

So, in this case, both the Get Location and the Set Rotation nodes are set to the player object, and the from and to fields in Cast Physics Ray are the beginning and end points of the ray beneath the character object, right?

0 Likes

#10

Yep. When you leave any object selector blank in nodes, it defaults to the object that the trait is applied to.

0 Likes

#11

Hmmm… so I’m still getting strange behaviour from your idea above @zicklag

Here’s my node setup (Suzanne is the character):

  1. The “move” group that moves the character forwards / backwards / left / right on its local axis works fine by itself.
  2. The group casting a ray below the character to find the ground works fine. When I set up a cube, setting it’s location to the ray hit location, it correctly always places the object at the surface of the ground object under the character.
  3. The “Align to ground” group, is straight from your post above.

The weird thing is… when the normal from the ray group is plugged in to your alignment group, suddenly all movement stops completely, too. The character doesn’t move one pixel. If the normal isn’t plugged in, movement works fine. I’m at a loss to understand why… I mean, the only thing your setup sets is the rotation…

0 Likes

#12

Do you know if there are any errors in the console? Maybe that is the cause of the complete lack of movement?

0 Likes

#13

Yes, actually there is:

Uncaught exception:
                        _this1.x = hitNormalWorld.x;
                        ^
TypeError: Unable to get property 'x' of undefined or null reference
   at get (krom.js:735:4)
   at armory_logicnode_LogicNodeInput.prototype.get (krom.js:874:3)
   at get (krom.js:1425:3)
   at armory_logicnode_LogicNodeInput.prototype.get (krom.js:874:3)
   at get (krom.js:896:3)
   at armory_logicnode_LogicNodeInput.prototype.get (krom.js:874:3)
   at run (krom.js:1157:3)
   at armory_logicnode_LogicNode.prototype.runOutput (krom.js:663:6)
   at update (krom.js:1132:3)
   at iron_App.update (krom.js:2267:3)

Here’s the file itself:
look node test1.blend (981.5 KB)

Does this look like an Armory bug?

0 Likes

#14

It’s actually happening because, whenever you do a ray-cast that doesn’t hit anything, the hit and normal sockets are undefined which throws an exception when attempting to plug that in to any node expecting a vector.

You can try using the Is Not None node to guard for those conditions. I experimented a little, but it was still running into that exception. I’m probably missing something. I might try a little more, but that should give you a start to figuring it out.

As a general troubleshooting rule, if you ever run into crashes like this when working with nodes that say TypeError...undefined or null reference, then it means that some type of value you are trying to use is set to nothing for one reason or another, and a different node is trying to use that value when it doesn’t exist.

Technically it is just a detail of the way that the node works, but it might be something that could be improved on.

0 Likes

#15

Gah. So, technically, after a lot of testing and fixing, I’ve gotten this to work, however due to a quirk of the LookAt node, it doesn’t really work for what I need. :cry:

I’m trying to get a Super Mario Galaxy type physics working where the character aligns with the ground normal underneath. Well, this approach works to align them, however the LookAt node as it works now, has issues. Specifically, if you ask it to align one axis (in my case the Z axis), it takes over control of all three axis’ rather than just the other two.

So, in my case, the characters feet face the ground, however you can’t make the character turn left or right as the LookAt node is controlling the Z rotation and not just X and Y to get Z aligned to the ground.

I wish that in “LookAt”, the axis chosen to align wasn’t also constrained (ie if you want the z axis to point at something, that Armory didn’t also change the Z axis rotation). It seems that Trsh has also been running into the same issue.

1 Like

#16

I’m pretty sure that the “Looking At” node ( I think that’s what its name is ) from the logic node pack has more options to control stuff like which axes are effected/locked, etc. You might want to look at that.

0 Likes

#17

While the Looking At node actually contains an answer to your problem, it probably won’t solve it. The issue is that the Looking At node also takes control over all three axis. You could say that it assumes to have exclusive control over an objects rotation, which is obviously not the case for a player controlled character.

tl;dr:

  • calculate v = cross(up, normal)
  • calculate β = arcsin( || v || ), invert if necessary
  • rotate your object by β around v (not yet exposed in nodes, but I think it should be)

Let’s assume that both vectors, the surface normal and your characters up vector are unit vectors (this simplifies calculations a little).
If you calculate the cross product of these two vectors (let’s call the result v), you will get a vector, which is perpendicular to both, your normal and your up vector. Now imagine those three vectors. If you want your up vector to align with the normal vector, you would have to rotate it around v by a certain amount. In other words, v is your rotation axis.
Now we need to calculate the angle β between your normal and up vector. For this, the following equation can be used:
|| v || = sin(β) • || normal || • || up || , where || ~ || represents the norm (or length) of a vector.
If up and normal are unit vectors are unit vectors, this simplifies to:
|| v || = sin(β), or β = arcsin( || v || ).
However, β will always be positive, but I think this might not be an issue (try inverting if necessary)

After you got your axis and your angle you should be able to rotate your object by β around v. The transform class contains a method to do exactly that, but it is not exposed in nodes… yet.
Personally I think it should be possible to easily rotate an object around an arbitrary axis in nodes, so I will look into creating a corresponding node tomorrow.

Edit:
You could also use
β = arccos( <normal, up> / ( || normal || • || up || ) ) = arccos( <normal, up> ), for unit vectors.
<•, •> represents the dot product.

0 Likes

#18

I created a pull request with a rotate around object node.
Using that node, this might work:

substitute the “normal” node with your normal setup and the “up” node with the object’s up vector

1 Like