I want to draw a thick line between 2 points that are being updated in physics_process(). I've thought of using a thin, long plane and then using materials and shaders to draw the line with a texture and extra effects. However, I'm finding a lot of trouble in finding how to position the plane so it starts at point A and ends at B. Note that A and B are tridimensional points, so they won't be located at the same height most of the time. The distance from A to B is not fixed either, so it can be 0.1m at one moment and 50m the next one.

Some visual aid:

Maybe using a plane for this is not the best approach, I'm very new to shaders and Godot so I don't know the usual way to do stuff like this.

So I need help with finding how to position the plane to start at A and end at B at every moment, or to learn how this is usually done if my method just doesn't make any sense...please help!

  • correojon I think your issue is just generating them upside down. Here I made it right-side-up and added an inspector button so you can see what it's doing from the editor:

    @tool
    extends MeshInstance3D
    
    @export var endpoint:Node3D
    
    @export var generate:bool:
    	get: return false;
    	set(value): _generate(value);
    
    func _generate(value):
    	if (value):
    		print("generate!")
    		update(endpoint.global_position);
    
    # Called when the node enters the scene tree for the first time.
    func _ready() -> void:
    	update(endpoint.global_position)
    
    # Called every frame. 'delta' is the elapsed time since the previous frame.
    func _process(delta: float) -> void:
    	pass
    
    func update(end: Vector3) -> void:
    	var start: Vector3 = global_position + Vector3(0.0, 0.5, 0.0)
    	var trail: Vector3 = end - start
    	var direction: Vector3 = trail.normalized()
    	var distance: float = trail.length()
    	var dir90: Vector3 = direction.rotated(Vector3.UP, TAU/4)
    
    	var thickness: float = 4.0
    	var points: int = 3
    
    	mesh.clear_surfaces()
    
    	mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
    
    	for i in range(0, points + 1):
    		var x: float = float(i) / float(points)
    		var d: Vector3 = (x * distance) * direction
    
    		mesh.surface_set_normal(Vector3.UP)
    		mesh.surface_set_uv(Vector2(1.0, x))
    		mesh.surface_add_vertex(start + d - (thickness * dir90))
    
    		mesh.surface_set_normal(Vector3.UP)
    		mesh.surface_set_uv(Vector2(0.0, x))
    		mesh.surface_add_vertex(start + d + (thickness * dir90))
    
    	mesh.surface_end()

    The important part was just switching which point gets added before the other. I'm not sure if there's a way to just add backfaces.

I'm trying to use an immediate mesh since that looks like a much better solution, but I'm finding some problems. This is the code I'm using to generate the mesh:

func update(end: Vector3) -> void:
	var start: Vector3 = global_position + Vector3(0.0, 0.5, 0.0)
	var trail: Vector3 = end - start
	var direction: Vector3 = trail.normalized()
	var distance: float = trail.length()
	var dir90: Vector3 = direction.rotated(Vector3.UP, TAU/4)
	
	var thickness: float = 4.0
	var points: int = 3
	
	mesh.clear_surfaces()
	
	mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
	
	for i in range(0, points + 1):
		var x: float = float(i) / float(points)
		var d: Vector3 = (x * distance) * direction
		
		mesh.surface_set_normal(Vector3.UP)
		mesh.surface_set_uv(Vector2(0.0, x))
		mesh.surface_add_vertex(start + d + (thickness * dir90))
		
		mesh.surface_set_normal(Vector3.UP)
		mesh.surface_set_uv(Vector2(1.0, x))
		mesh.surface_add_vertex(start + d - (thickness * dir90))
	
	mesh.surface_end()

Nothing at all shows. In some positions I get a very buggy gray rectangle, but most of the times I don't get anything at all:

However, if I use a LINE_STRIP instead of a TRIANGLE_STRIP, the lines are drawn correctly, showing the correct positions of all the points and the triangles they should be creating:

The idea is to draw a plane from the capsule in the image to the white circles. The lines show that it is more or less what I want, but I can't understand why TRIANGLE_STRIP just draws something completely different with the same vertex positions.

    correojon I think your issue is just generating them upside down. Here I made it right-side-up and added an inspector button so you can see what it's doing from the editor:

    @tool
    extends MeshInstance3D
    
    @export var endpoint:Node3D
    
    @export var generate:bool:
    	get: return false;
    	set(value): _generate(value);
    
    func _generate(value):
    	if (value):
    		print("generate!")
    		update(endpoint.global_position);
    
    # Called when the node enters the scene tree for the first time.
    func _ready() -> void:
    	update(endpoint.global_position)
    
    # Called every frame. 'delta' is the elapsed time since the previous frame.
    func _process(delta: float) -> void:
    	pass
    
    func update(end: Vector3) -> void:
    	var start: Vector3 = global_position + Vector3(0.0, 0.5, 0.0)
    	var trail: Vector3 = end - start
    	var direction: Vector3 = trail.normalized()
    	var distance: float = trail.length()
    	var dir90: Vector3 = direction.rotated(Vector3.UP, TAU/4)
    
    	var thickness: float = 4.0
    	var points: int = 3
    
    	mesh.clear_surfaces()
    
    	mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
    
    	for i in range(0, points + 1):
    		var x: float = float(i) / float(points)
    		var d: Vector3 = (x * distance) * direction
    
    		mesh.surface_set_normal(Vector3.UP)
    		mesh.surface_set_uv(Vector2(1.0, x))
    		mesh.surface_add_vertex(start + d - (thickness * dir90))
    
    		mesh.surface_set_normal(Vector3.UP)
    		mesh.surface_set_uv(Vector2(0.0, x))
    		mesh.surface_add_vertex(start + d + (thickness * dir90))
    
    	mesh.surface_end()

    The important part was just switching which point gets added before the other. I'm not sure if there's a way to just add backfaces.

      award Works like a charm, thanks a lot!

      Apart of what award said, I did some small changes to the original script, like sliding the dir90 vector on an horizontal plane so the final plane is not inclined. I also removed the addition of the start vector, as the coordinates need to be relative:

      func update(target: Vector3) -> void:
      	# Settings
      	var offset: Vector3 = Vector3.UP
      	var thickness: float = 0.1
      	var points: int = 3
      	
      	# Adjust positions
      	var start: Vector3 = global_position - offset
      	var end: Vector3 = target - offset
      	
      	# Calculate line
      	var trail: Vector3 = end - start
      	var direction: Vector3 = trail.normalized()
      	var distance: float = trail.length()
      	
      	# Calculate width
      	var dir90: Vector3 = direction.slide(Vector3.UP).rotated(Vector3.UP, TAU/4)
      	var width: Vector3 = thickness * dir90
      	
              # Generate mesh
      	mesh.clear_surfaces()	
      	mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
      	
      	for i in range(0, points + 1):
      		var x: float = float(i) / float(points)
      		var d: Vector3 = (x * distance) * direction
      		
      		mesh.surface_set_normal(Vector3.UP)
      		mesh.surface_set_uv(Vector2(1.0, x))
      		mesh.surface_add_vertex(d - width)
      		
      		mesh.surface_set_normal(Vector3.UP)
      		mesh.surface_set_uv(Vector2(0.0, x))
      		mesh.surface_add_vertex(d + width)
      	
      	mesh.surface_end()