Line thickness in 3D

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Afely

I’m trying to find a way to draw 3D lines with variable thickness, without impacting performance too much. I don’t want to resort to a bunch of Sprite3Ds or something, because I feel like that would be really laggy. Right now I’m using ImmediateGeometry, but it’s limited to only 1px thickness. I’ll take pretty much any solution. It doesn’t have to be a perfect line, it doesn’t have to be affected by lighting or anything. It just has to look like a line that’s wider than 1px.

Can’t you set the Spatial.scale property to make the ImmediateGeometry line wider?

timothybrentwood | 2021-05-03 16:57

That won’t work – line width is not affected by the Spatial scale property.

Calinou | 2021-05-03 17:01

:bust_in_silhouette: Reply From: Calinou

There are a few solutions here. One of them is to use a MeshInstance that contains a CylinderMesh with a low number of subdivisions (4-12 depending on the maximum expected thickness). You can change its scale as desired to make lines thicker or longer.

You can assign any kind of material you want to the MeshInstance in question.

Would that be very laggy? I’m gonna have a lot of lines, so performance is a pretty big concern of mine

Afely | 2021-05-04 00:13

Thank you for your comment! I tried it, and it turns out it’s not nearly as much of a hit to performance as I thought it would be. It took me a while to get it to work right, but I’m really happy with how it looks now.

Afely | 2021-05-04 06:16

I didn’t understand some stuff, but I played around with it and I think I have a better understanding of this. So, to elaborate on how to make this, this is how I did it:

  • Create a scene with a MeshInstance with a cylinder mesh 1 unit high and 1 unit wide, and save it. (Also with a material_override with relevant flags: e.g. unshaded, no depth test, etc – don’t use fixed size, it will look wrong when the camera moves around).

  • To make it long enough to go from point A to point B you just scale it by the distance between A and B, and for the thickness you multiply the thickness value by a scalar to shrink it (e.g. 0.01), and use the result as the scale.

  • then you place the mesh at the center between the two points of the line and rotate the mesh appropriately.

I’m using the code below.

Note: the only way I figured out to rotate the mesh appropriately was using Spatial.look_at(), but for this I had to create the mesh in Blender, so that it extends along the Z axis by default (in Godot, cylinders are upright along the Y). This is not needed if you use CubeMeshes made in Godot, instead of cylinders (and you can easily make a CubeMesh through code).

onready var MeshObj := preload("res://resources/LineMeshInstance.tscn")
func line(a, b, color, thickness):
	var line = MeshObj.instance()
	add_child(line)
	line.material_override.albedo_color = color
	line.scale = Vector3(0.01*thickness, 0.01*thickness, a.distance_to(b))
	line.look_at_from_position((a+b)/2, b, Vector3.UP)

I haven’t tested whether the material and mesh need to be made unique (using duplicate())

woopdeedoo | 2021-10-26 18:06