• 3D
  • Drawing a cylinder between two points: need help with look_at function

Hello. I want to be able to connect two 3D points with a cylinder.

This topic helped me in general, but I made a test project to be sure everything works. This project simply randomizes points in X, Y, Z in between -2 and 2. Sometimes resulting rotation is invalid, so I want to know how to fix that.

I am changing CSGCylinder's length for each new connection, then I am updating it with look_at and rotate_object_local functions, as it was done in original topic. There's the important part:

func update_connection(ao, bo):
	$connection.transform.origin = midpoint(ao, bo)
	$connection.height = ao.distance_to(bo)
	$connection.look_at(bo, Vector3(0, 1, 0))
	$connection.rotate_object_local(Vector3(1,0,0), -PI/2)

This code places the connection cylinder between ao and bo points, transform origins for marker A and marker B, calculates distance and changes cylinder length, and then applies look_at to turn the connection.

Test code on GitHub.

Animation showing mixed results.

Screenshot:


UPD: I think the reason behind the broken rotations is this error:

Up vector and direction between node origin and target are aligned, look_at() failed.

It probably happened, preventing the rotation.

Based on the error, you probably need to check if the dot product of the two vectors are zero , and if they are, not call look_at. I think that will fix the error from showing up, which may also fix the rotation not being applied.

TwistedTwigleg, if I understand you correctly, your idea is this one, right?

if Vector3(0, 1, 0).cross(bo - midpoint(ao, bo)) != Vector3():
	$connection.look_at(bo, Vector3(0, 1, 0))
	$connection.rotate_object_local(Vector3(1,0,0), -PI/2)

Sometimes I still get incorrect results. Instead, I did a hack, where I do the same check, but I also update the target vector:

if Vector3(0, 1, 0).cross(bo - midpoint(ao, bo)) == Vector3():
	$connection.look_at(bo + Vector3(0.0, 0.0, 0.0001), Vector3(0, 1, 0))
else:
	$connection.look_at(bo, Vector3(0, 1, 0))
$connection.rotate_object_local(Vector3(1,0,0), -PI/2)

While it feels like a terrible solution, it works in each case.

I did the same thing(except not a cylinder, it was a chain made of two perpendicular quads), this is how I did it:

  • I moved the mesh to the position of the first point
  • rotated the mesh towards the second point with look_at()
  • re-positioned it to be halfway between point 1 and point 2 using lerp()
  • set the size based on the distance from point 1 to point 2

And in my case, I also scaled the UVs so the chain texture didn't get stretched. It may sound like a lot, but it's pretty simple and only 5 lines of code.

@Grid said: ... While it feels like a terrible solution, it works in each case.

Well, if it works, then I’d say that’s a usable solution :)

It may sound like a lot, but it's pretty simple and only 5 lines of code. Thanks, and no, it does not sound like a lot. The possibility of look_at glitching and some object having its old rotation is still there, though, is it not? I think look_at needs some slight change, like an optional attribute to handle that.

What do you mean? look_at() always points towards the position defined by the first parameter, I never had any problems with it.

@Dschoonmaker said: What do you mean? look_at() always points towards the position defined by the first parameter, I never had any problems with it.

Well, you are lucky if you haven't seen it yet! What I mean is this exact error:

Up vector and direction between node origin and target are aligned, look_at() failed.

And there's an example I made to show how it appears.

I think you never saw this issue, because it needs a certain condition:

p_up.cross(p_target - p_pos) == Vector3()

This condition appeared when I fed GDScript randomized integer vectors. While an error for two similar points is understandable, error for two points on the same line or something like that is not.

I've had that error many times. I never had any problems from it, I don't know why it shows up. look_at() doesn't seem to fail in my experience, but the error says it does.

@Dschoonmaker said:

I've had that error many times. I never had any problems from it, I don't know why it shows up. As far as I understand it, it means there's more than one possible rotation to do and Godot engine doesn't pick any. It's not my idea, it was mentioned in this issue by CptPotato.

Object remains the same, i.e. if you created a cylinder to orient it, this cylinder will remain in its previous position instead; if you made an eye that tracks an object, you won't notice this error often, because an update will happen on the next frame.

This error is helpful, but an optimal way would be some flag to pick a solution.

For me it gets ugly, because I'm experimenting with a first person shooter. I don't want some of the trails to be randomly aligned. So I did a hack I mentioned above.

look_at() doesn't seem to fail in my experience, but the error says it does. Well, it still does fail or I'm being wrong, though I put some effort into an experiment. ;-) There's an animation to see how it fails for some points. Also, there's a test code to demonstrate it.

Divide by 0 error? As far as I understand, what we are looking at is Node3D::look_at method, which then calls Node3D::look_at_from_position.

I think that zero division can't happen there, because of ERR_FAIL_COND_MSG, which checks if a cross-product of two vectors is an empty vector. I think it might be linked to the fact those vectors are located on the same horizontal or vertical plane. After all, I used random integers in [-2 : 2] range in my test above.

If it's the case, I wonder if it deserves a proposal about making the function handle all rotations.

If I remember correctly, it's the same vertical plane(if you use Vector3.UP as your up vector, second parameter).