Search code examples
geometrylinepolygongodotgdscript

(Godot Engine) Replicating Line2D's joint functionality on the first + last points


I have created the following method to outline an 2D polygon using an Line2D node (in favor of _drawing because of texturing and round jointing capabilities of the Line2D node):

func Set_Polygon_Outline(_polygon_node: Node2D, _width: int = 5, _color: Color = Color.black, _texture: Texture = null) -> void:
if _polygon_node is Polygon2D:
    var _polygon: PoolVector2Array = (_polygon_node as Polygon2D).polygon
    if _polygon.size() >= 3:
        # Line2D node setup
        var _line_node: Line2D = null
        var _line_name: String = str(_polygon_node.name, "_Line")
        if not _polygon_node.has_node(_line_name):
            _line_node = Line2D.new() ; _line_node.name = _line_name ; _polygon_node.add_child(_line_node)
        else: _line_node = _polygon_node.get_node(_line_name) as Line2D
        # Line2D properties setup
        if _line_node != null:
            _line_node.width = _width ; _line_node.default_color = _color ; _line_node.joint_mode = Line2D.LINE_JOINT_ROUND
            if _texture != null:
                _line_node.texture = _texture ; _line_node.texture_mode = Line2D.LINE_TEXTURE_STRETCH
            var _points: PoolVector2Array = _polygon ; _points.append(_polygon[0]) ; _line_node.points = _points

How would it be possible to replicate the round point jointing on point 0 in the same way as the other points?


The result meets expectations except on the closing points (from 4 to 0) enter image description here

One approach I have tried is appending an additional point (point 1) to the _points Array. While the un-textured one looks as desired, the textured variant is slightly off on the additional line because of the two superimposing textures with alpha values making it look "bolder". enter image description here

Another (very unorthodox approach) is creating two polygons: one black and one with an blur shader, using the following method:

func Set_Polygon_Shadow(_polygon_node: Node2D, _size: float = 10.0, _color: Color = Color.black) -> void:
if _polygon_node is Polygon2D:
    var _polygon: PoolVector2Array = (_polygon_node as Polygon2D).polygon
    if _polygon.size() >= 3:
        # Shadow Polygon node setup
        var _shadow_name: String = str(_polygon_node.name, "_Shadow")
        if not _polygon_node.has_node(_shadow_name):
            var _shadow_node: Polygon2D = Polygon2D.new()
            _shadow_node.polygon = Geometry.offset_polygon_2d(_polygon, _size).front() ; _shadow_node.color = _color
            _shadow_node.show_behind_parent = true ; _polygon_node.add_child(_shadow_node)
            # Blur Polygon node setup
            var _blur_node: Polygon2D = Polygon2D.new()
            _blur_node.polygon = Geometry.offset_polygon_2d(_polygon, _size * 2.0).front()
            _blur_node.material = ShaderMaterial.new()
            _blur_node.material.shader = preload("res://shaders/Shader_Blur.shader")
            _blur_node.material.set_shader_param("Strength", 2.0)
            _blur_node.show_behind_parent = true ; _polygon_node.add_child(_blur_node)

The shader code:

shader_type canvas_item;
uniform float Strength : hint_range(0.0, 5.0);
void fragment() {COLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, Strength);}

The result looks good but I can't possibly imagine using this approach on a large number of polygons.

enter image description here

Thank you kindly, Mike.


Solution

  • If using a Line2D is a viable solution, except for fixing the loop, then let us fix the loop.

    For a Line2D to loop seamless, even with transparency, it must close in a straight segment (not a corner). And have no end caps.

    Thus, I suggest to move the first point, half way between its original position and the second point. Then you add at the end the original position of the first point, following by a copy of its moved position...

    Like this:

    # Let o be a copy of the original first point
    var o = _points[0]
    
    # Let m be the middle of the straight segment between the first and second points
    var m = o + (_points[1] - o) * 0.5
    
    _points[0] = m    # The line now starts in m, we are moving the first point forth
    _points.append(o) # Since we moved the first point forth, add its original position back
    _points.append(m) # Add m at the end, so it loops, in the middle of a straight segment
    

    That should result in a Line2D that loops seamlessly.