• 3D
  • Transform.looking_at() not working

I've been experimenting with @Calinou's suggestion of using 3D meshes to compensate no line thickness in GLES2, and I've managed to get some good results using CubeMeshes and a MultiMesh.

Problem is the function Transform.looking_at isn't working in some cases. In my case, it failed when trying to draw three lines that represent the axes of the 3D space in my editor. You can see the problem in the image below: the Y-axis line should be upright, and the reason it's not is because it failed to rotate and I'm getting this error: set_look_at: Condition "v_x.length() == 0" is true (here in godot's source code).

The two points for that line are a = (8, 0, 8) and b = (8, 3, 8). I'm positioning the line at the center between them before using Transform.looking_at. My code looks like this:

func line(a:Vector3, b:Vector3, color:Color, thickness:float) -> void:
	var idx = mm.visible_instance_count  # 'mm' is the MultiMesh
	_add_instance_and_check_buffer_size()

	var transform := mm.get_instance_transform(idx)
	transform.origin = (a+b)/2
	transform = transform.looking_at(b, Vector3.UP)
	transform.basis.x *= _width_factor*thickness    # set the width of the "line"
	transform.basis.y *= _width_factor*thickness
	transform.basis.z *= a.distance_to(b)        # set the length of the "line"

	mm.set_instance_transform(idx, transform)
	mm.set_instance_color(idx, color)


In order for lookat to work, the up vector must not coincide with the target vector. One of the rotation axes is calculated by the cross product of these two vectors. If they are parallel, their cross product is zero and rotation axis cannot be properly calculated.

So check if angle between these two vectors is (near) zero, and it it is then choose different up axis when calling looking_at()

I presume you mean something like this, but it's not working:

if is_zero_approx( b.angle_to(Vector3.UP) ):
	transform = transform.looking_at(b, Vector3.BACK)    # Vector3.RIGHT doesn't work either
else:
	transform = transform.looking_at(b, Vector3.UP)

I don't use vector math often, so I tend to forget most of it...


EDIT: actually I just made it work with (a-b).normalized().abs().angle_to(Vector3.UP)

I used .abs() because my first attempt at (a-b).normalized() was giving me a (0, -1, 0) and the so the angle was still not anywhere near zero (because a.y is 0 there).

Am I on the right track, though?

Target vector is b-transform.origin, b is the target point. So your code will still end up in the else block with given values. Testing with angle_to() is also problematic as it'll not catch parallel vectors that go into opposite direction. Better to use dot product to check it.

const dot_threshold = .999

var target_direction = (b-transform.origin).normalized()
if abs(target_direction.dot(Vector3.UP)) < dot_threshold:
	#use Vector3.UP
else: 
	#use Vector3.RIGHT

@woopdeedoo said: Am I on the right track, though?

Yep, good thinking, but see my post above.

I see. I wasn't too sure about using angle_to, but for now that blue line is my only test case.

Everything seems to be working nicely now, though. Thank you. :)

You could use angle_to() but then need to check both cases separately. Dot product is more elegant. Generally speaking dot product is something you should familiarize yourself with as it's ubiquitous in computer graphics. It's very useful in solving various types of problems.