Addd the Godot Trail System Add-on.

remotes/1698854768171617226/layered_gunshot
unfa 2021-04-01 23:30:10 +02:00
parent e6366f8fea
commit 0427e9dd2f
10 changed files with 846 additions and 0 deletions

21
Game/addons/Trail/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Oussama
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,7 @@
[plugin]
name="Trail System"
description="Advanced 2D/3D Trail system."
author="Oussama BOUKHELF"
version="0.1"
script="plugin.gd"

View File

@ -0,0 +1,12 @@
tool
extends EditorPlugin
func _enter_tree():
add_custom_type("Trail3D","ImmediateGeometry",preload("res://addons/Trail/trail_3d.gd"),preload("res://addons/Trail/trail3d_icon.svg"))
add_custom_type("Trail2D","Line2D",preload("res://addons/Trail/trail_2d.gd"),preload("res://addons/Trail/trail2d_icon.svg"))
pass
func _exit_tree():
remove_custom_type("Trail3D")
remove_custom_type("Trail2D")
pass

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8"
version="1.1"
viewBox="0 0 4.2333332 4.2333334"
height="16"
width="16">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1">
<path
d="m 2.3792718,293.03275 c -0.066383,-0.003 -0.1328569,-0.002 -0.1990224,0.002 -0.3186668,0.0183 -0.6312182,0.1085 -0.9125781,0.26693 -0.73836421,0.4467 -1.07809046,1.28277 -0.92563728,2.0456 0.17028391,0.76048 0.80425238,1.29996 1.49449678,1.38009 0.692005,0.0713 1.3427391,-0.32717 1.5947914,-0.88489 -0.3569657,0.49715 -1.0210943,0.69443 -1.5237493,0.52812 -0.5044012,-0.15751 -0.8564865,-0.67125 -0.8451925,-1.1184 -0.00653,-0.4448 0.3338894,-0.88113 0.6879595,-0.97317 0.1010793,-0.0343 0.2112059,-0.041 0.320212,-0.0303 a 1.1446979,1.1446979 0 0 1 -0.015149,-0.1823 1.1446979,1.1446979 0 0 1 0.5944555,-1.00191 c -0.089937,-0.0156 -0.1801976,-0.0276 -0.2705869,-0.0313 z m 1.5477998,1.03344 a 0.7268029,0.7268029 0 0 1 -0.7268029,0.7268 0.7268029,0.7268029 0 0 1 -0.7268029,-0.7268 0.7268029,0.7268029 0 0 1 0.7268029,-0.72681 0.7268029,0.7268029 0 0 1 0.7268029,0.72681 z"
style="vector-effect:none;fill:#a5b7f3;fill-opacity:1;stroke:none;stroke-width:0.26499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="icon" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/trail2d_icon.svg-607ea772beb499297607579128e70a1c.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Addons/addons/Trail/trail2d_icon.svg"
dest_files=[ "res://.import/trail2d_icon.svg-607ea772beb499297607579128e70a1c.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333334"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="trail3d_icon.svg">
<defs
id="defs2">
<inkscape:path-effect
effect="skeletal"
id="path-effect847"
is_visible="true"
pattern="m 0.45166302,293.46861 1.24543248,0.71906 v -1.43811 z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
fuse_tolerance="0" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="32"
inkscape:cx="7.1053991"
inkscape:cy="10.736157"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1366"
inkscape:window-height="712"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1"
inkscape:snap-global="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-center="true"
inkscape:snap-object-midpoints="true" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
id="icon"
style="vector-effect:none;fill:#fc9c9c;fill-opacity:1;stroke:none;stroke-width:0.26499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.3792718,293.03275 c -0.066383,-0.003 -0.1328569,-0.002 -0.1990224,0.002 -0.3186668,0.0183 -0.6312182,0.1085 -0.9125781,0.26693 -0.73836421,0.4467 -1.07809046,1.28277 -0.92563728,2.0456 0.17028391,0.76048 0.80425238,1.29996 1.49449678,1.38009 0.692005,0.0713 1.3427391,-0.32717 1.5947914,-0.88489 -0.3569657,0.49715 -1.0210943,0.69443 -1.5237493,0.52812 -0.5044012,-0.15751 -0.8564865,-0.67125 -0.8451925,-1.1184 -0.00653,-0.4448 0.3338894,-0.88113 0.6879595,-0.97317 0.1010793,-0.0343 0.2112059,-0.041 0.320212,-0.0303 a 1.1446979,1.1446979 0 0 1 -0.015149,-0.1823 1.1446979,1.1446979 0 0 1 0.5944555,-1.00191 c -0.089937,-0.0156 -0.1801976,-0.0276 -0.2705869,-0.0313 z m 1.5477998,1.03344 a 0.7268029,0.7268029 0 0 1 -0.7268029,0.7268 0.7268029,0.7268029 0 0 1 -0.7268029,-0.7268 0.7268029,0.7268029 0 0 1 0.7268029,-0.72681 0.7268029,0.7268029 0 0 1 0.7268029,0.72681 z"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/trail3d_icon.svg-8b210038e0dc69dfd0c78cba261254df.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Addons/addons/Trail/trail3d_icon.svg"
dest_files=[ "res://.import/trail3d_icon.svg-8b210038e0dc69dfd0c78cba261254df.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

View File

@ -0,0 +1,77 @@
"""
Author: Oussama BOUKHELF
License: MIT
Version: 0.1
Email: o.boukhelf@gmail.com
Description: Advanced 2D/3D Trail system.
Note: This is a simple implementation, I will update it later on.
"""
extends Line2D
export(bool) var emit := true
export(float) var lifetime := 0.5
export(float) var distance := 20.0
export(int) var segments := 20
var target
var trail_points := []
var offset := Vector2()
class Point:
var position := Vector2()
var age := 0.0
func _init(position :Vector2, age :float) -> void:
self.position = position
self.age = age
func update(delta :float, points :Array) -> void:
self.age -= delta
if self.age <= 0:
points.erase(self)
func _ready():
offset = position
show_behind_parent = true
target = get_parent()
clear_points()
set_as_toplevel(true)
position = Vector2()
func _emit():
var _position :Vector2 = target.global_transform.origin + offset
var point = Point.new(_position, lifetime)
if trail_points.size() < 1:
trail_points.push_back(point)
return
if trail_points[-1].position.distance_squared_to(_position) > distance*distance:
trail_points.push_back(point)
update_points()
func update_points() -> void:
var delta = get_process_delta_time()
if trail_points.size() > segments:
trail_points.invert()
trail_points.resize(segments)
trail_points.invert()
clear_points()
for point in trail_points:
point.update(delta, trail_points)
# if point:
add_point(point.position)
func _process(delta):
if emit:
_emit()

View File

@ -0,0 +1,349 @@
"""
Author: Oussama BOUKHELF
License: MIT
Version: 0.1
Email: o.boukhelf@gmail.com
Description: Advanced 2D/3D Trail system.
"""
extends ImmediateGeometry
export(bool) var emit := true
export(float) var distance := 0.1
export(int, 0, 99999) var segments := 20
export(float) var lifetime := 0.5
export(float, 0, 99999) var base_width := 0.5
export(bool) var tiled_texture := false
export(int) var tiling := 0
export(Curve) var width_profile
export(Gradient) var color_gradient
export(int, 0, 3) var smoothing_iterations := 0
export(float, 0, 0.5) var smoothing_ratio := 0.25
export(String, "View", "Normal", "Object") var alignment := "View"
export(String, "X", "Y", "Z") var axe := "Y"
export(bool) var show_wireframe := false
export(Color) var wireframe_color := Color(1, 1, 1, 1)
export(float, 0, 100, 0.1) var wire_line_width := 1.0
var points := []
var color := Color(1, 1, 1, 1)
var always_update = false
var _target :Spatial
var _wire_obj :ImmediateGeometry = ImmediateGeometry.new()
var _wire_mat :SpatialMaterial = SpatialMaterial.new()
var _A: Point
var _B: Point
var _C: Point
var _temp_segment := []
var _points := []
class Point:
"""
Class for the 3D point that will be emmited when the object move.
"""
var transform := Transform()
var age := 0.0
func _init(transform :Transform, age :float) -> void:
self.transform = transform
self.age = age
func update(delta :float, points :Array) -> void:
self.age -= delta
if self.age <= 0:
points.erase(self)
func add_point(transform :Transform) -> void:
"""
Add a point to the list of points.
This function is called programmatically.
"""
var point = Point.new(transform, lifetime)
points.push_back(point)
func clear_points() -> void:
"""
Cleat points list.
This function is called programmatically.
"""
points.clear()
func _prepare_geometry(point_prev :Point, point :Point, half_width :float, factor :float) -> Array:
"""
Generate and transform the trail geometry based on the path points that
the target object generated.
"""
var normal := Vector3()
if alignment == "View":
if get_viewport().get_camera():
var cam_pos = get_viewport().get_camera().get_global_transform().origin
var path_direction :Vector3 = (point.transform.origin - point_prev.transform.origin).normalized()
normal = (cam_pos - (point.transform.origin + point_prev.transform.origin)/2).cross(path_direction).normalized()
else:
print("There is no camera in the scene")
elif alignment == "Normal":
if axe == "X":
normal = point.transform.basis.x.normalized()
elif axe == "Y":
normal = point.transform.basis.y.normalized()
else:
normal = point.transform.basis.z.normalized()
else:
if axe == "X":
normal = _target.global_transform.basis.x.normalized()
elif axe == "Y":
normal = _target.global_transform.basis.y.normalized()
else:
normal = _target.global_transform.basis.z.normalized()
var width = half_width
if width_profile:
width = half_width * width_profile.interpolate(factor)
var p1 = point.transform.origin-normal*width
var p2 = point.transform.origin+normal*width
return [p1, p2]
func render(update := false) -> void:
"""
Render the points.
This function is called programmatically.
"""
if update:
always_update = update
else:
_render_geometry(points)
func _render_realtime() -> void:
"""
Render the points every frame when "emit" is set to True.
"""
var render_points = _points+_temp_segment+[_C]
_render_geometry(render_points)
func _render_geometry(source: Array) -> void:
"""
Base function for rendering the generated geometry to the screen.
Renders the trail, and the wireframe if set in parameters.
"""
var points_count = source.size()
if points_count < 2:
return
# The following section is a hack to make orientation "view" work.
# but it may cause an artifact at the end of the trail.
# You can use transparency in the gradient to hide it for now.
var _d :Vector3 = source[0].transform.origin - source[1].transform.origin
var _t :Transform = source[0].transform
_t.origin = _t.origin + _d
var point = Point.new(_t, source[0].age)
var to_be_rendered = [point]+source
points_count += 1
var half_width :float = base_width/2.0
var wire_points = []
var u := 0.0
clear()
begin(Mesh.PRIMITIVE_TRIANGLE_STRIP, null)
for i in range(1, points_count):
var factor :float = float(i)/(points_count-1)
var _color = color
if color_gradient:
_color = color * color_gradient.interpolate(1.0-factor)
var vertices = _prepare_geometry(to_be_rendered[i-1], to_be_rendered[i], half_width, 1.0-factor)
if tiled_texture:
if tiling > 0:
factor *= tiling
else:
var travel = (to_be_rendered[i-1].transform.origin - to_be_rendered[i].transform.origin).length()
u += travel/base_width
factor = u
set_color(_color)
set_uv(Vector2(factor, 0))
add_vertex(vertices[0])
set_uv(Vector2(factor, 1))
add_vertex(vertices[1])
if show_wireframe:
wire_points += vertices
end()
# For some reason I had to add a second Meshinstance as a child to make the
# wireframe to render, normally you can just draw on top.
if show_wireframe:
_wire_mat.params_line_width = wire_line_width
_wire_obj.clear()
_wire_obj.begin(Mesh.PRIMITIVE_LINE_STRIP, null)
_wire_obj.set_color(wireframe_color)
_wire_obj.set_uv(Vector2(0.5, 0.5))
for i in range(1, wire_points.size()-2, 2):
## order: i-1, i+1, i, i+2
_wire_obj.add_vertex(wire_points[i-1])
_wire_obj.add_vertex(wire_points[i+1])
_wire_obj.add_vertex(wire_points[i])
_wire_obj.add_vertex(wire_points[i+2])
_wire_obj.end()
func _update_points() -> void:
"""
Update ages of the points and remove extra ones.
"""
var delta = get_process_delta_time()
_A.update(delta, _points)
_B.update(delta, _points)
_C.update(delta, _points)
for point in _points:
point.update(delta, _points)
var size_multiplier = [1, 2, 4, 6][smoothing_iterations]
var max_points_count :int = segments * size_multiplier
if _points.size() > max_points_count:
_points.invert()
_points.resize(max_points_count)
_points.invert()
func smooth() -> void:
"""
Smooth the given path.
This function is called programmatically.
"""
if points.size() < 3:
return
var output := [points[0]]
for i in range(1, points.size()-1):
output += _chaikin(points[i-1], points[i], points[i+1])
output.push_back(points[-1])
points = output
func _chaikin(A, B, C) -> Array:
"""
Chaikins smoothing Algorithm
https://www.cs.unc.edu/~dm/UNC/COMP258/LECTURES/Chaikins-Algorithm.pdf
Ps: I could have avoided a lot of trouble automating this function using FOR loop,
but I opted for a more optimized approach which maybe helpful when dealing with a
large amount of objects.
"""
if smoothing_iterations == 0:
return [B]
var out := []
var x :float = smoothing_ratio
# Pre-calculate some parameters to improve performance
var xi :float = (1-x)
var xpa :float = (x*x-2*x+1)
var xpb :float = (-x*x+2*x)
# transforms
var A1_t :Transform = A.transform.interpolate_with(B.transform, xi)
var B1_t :Transform = B.transform.interpolate_with(C.transform, x)
# ages
var A1_a :float = lerp(A.age, B.age, xi)
var B1_a :float = lerp(B.age, C.age, x)
if smoothing_iterations == 1:
out = [Point.new(A1_t, A1_a), Point.new(B1_t, B1_a)]
else:
# transforms
var A2_t :Transform = A.transform.interpolate_with(B.transform, xpa)
var B2_t :Transform = B.transform.interpolate_with(C.transform, xpb)
var A11_t :Transform = A1_t.interpolate_with(B1_t, x)
var B11_t :Transform = A1_t.interpolate_with(B1_t, xi)
# ages
var A2_a :float = lerp(A.age, B.age, xpa)
var B2_a :float = lerp(B.age, C.age, xpb)
var A11_a :float = lerp(A1_a, B1_a, x)
var B11_a :float = lerp(A1_a, B1_a, xi)
if smoothing_iterations == 2:
out += [Point.new(A2_t, A2_a), Point.new(A11_t, A11_a),
Point.new(B11_t, B11_a), Point.new(B2_t, B2_a)]
elif smoothing_iterations == 3:
# transforms
var A12_t :Transform = A1_t.interpolate_with(B1_t, xpb)
var B12_t :Transform = A1_t.interpolate_with(B1_t, xpa)
var A121_t :Transform = A11_t.interpolate_with(A2_t, x)
var B121_t :Transform = B11_t.interpolate_with(B2_t, x)
# ages
var A12_a :float = lerp(A1_a, B1_a, xpb)
var B12_a :float = lerp(A1_a, B1_a, xpa)
var A121_a :float = lerp(A11_a, A2_a, x)
var B121_a :float = lerp(B11_a, B2_a, x)
out += [Point.new(A2_t, A2_a), Point.new(A121_t, A121_a), Point.new(A12_t, A12_a),
Point.new(B12_t, B12_a), Point.new(B121_t, B121_a), Point.new(B2_t, B2_a)]
return out
func _emit(delta) -> void:
"""
Adding points to be rendered, called every frame when "emit" is set to True.
"""
var _transform :Transform = _target.global_transform
var point = Point.new(_transform, lifetime)
if not _A:
_A = point
return
elif not _B:
_A.update(delta, _points)
_B = point
return
if _B.transform.origin.distance_squared_to(_transform.origin) >= distance*distance:
_A = _B
_B = point
_points += _temp_segment
_C = point
_update_points()
_temp_segment = _chaikin(_A, _B, _C)
_render_realtime()
func _ready() -> void:
_target = get_parent()
_wire_mat.flags_unshaded = true
_wire_mat.flags_use_point_size = true
_wire_mat.vertex_color_use_as_albedo = true
_wire_mat.params_line_width = 10.0
_wire_obj.material_override = _wire_mat
add_child(_wire_obj)
set_as_toplevel(true)
global_transform = Transform()
func _process(delta) -> void:
if emit:
_emit(delta)
elif always_update:
# This is needed for alignment == view, so it can be updated every frame.
_render_geometry(points)

View File

@ -0,0 +1,194 @@
#tool
extends ImmediateGeometry
export(bool) var emit = true
export(float) var max_distance = 0.5
export(int, 0, 99999) var segments = 20
export(float) var life_time = 5.0
export(float, 0, 99999) var base_width = 1.0
export(bool) var tiled_texture = false
export(int) var tiling = 0
export(Curve) var width_profile
export(Curve) var width_over_time
export(Gradient) var color_gradient
export(float, 0, 0.5) var smoothing_ratio = 0.2
export(int, 4) var smoothing_iterations = 1
export(String, "View", "Motion", "Object") var alignment = "View"
export(String, "Idle", "Fixed") var prcess_mode = "Idle"
export(bool) var show_wireframe = false
export(Color) var wireframe_color = Color(1, 1, 1)
var target
var path_points = []
class Point:
var position = Vector3()
var normal = Vector3()
var age = 0
func _init(position, normal, age):
self.position = position
self.normal = normal
self.age = age
func update(delta):
age -= delta
func _ready():
set_as_toplevel(true)
target = get_parent()
global_transform = Transform()
func _process(delta):
if emit:
add_point()
update_points()
render()
func add_point():
if target:
var pos = target.global_transform.origin
var normal = target.global_transform.basis.y.normalized()
if emit:
var points_count = path_points.size()
if points_count < 1:
var point = Point.new(pos, normal, life_time)
path_points.append(point)
else:
var distance = path_points[points_count-2].position.distance_squared_to(pos)
if distance > (max_distance * max_distance):
var point = Point.new(pos, normal, life_time)
path_points.append(point)
if points_count > 1:
path_points[points_count-1].position = pos
func update_points():
var delta = 0
if prcess_mode == "Fixed":
delta = get_physics_process_delta_time()
else:
delta = get_process_delta_time()
var points_count = path_points.size()
if points_count > segments:
path_points.pop_front()
for i in range(path_points.size()-1):
path_points[i].update(delta)
if path_points[i].age <= 0:
path_points.remove(i)
func render():
if path_points.size() < 2:
clear()
return
# path_points = [Vector3(-5, 2, 0),Vector3(-5, 2, 0),Vector3(5, 2, 0)]
var to_be_rendered: Array = []
for point in path_points:
to_be_rendered.append(point.position)
to_be_rendered = chaikin(to_be_rendered, smoothing_iterations)
var points_to_render: int = to_be_rendered.size()
# var tiling_factor: float = segments*max_distance/base_width if tiled_texture else 0
var step: float = 1.0/(points_to_render-1)
var factor: float = 0
var wire_points: Array = []
var _u = 0
clear()
begin(Mesh.PRIMITIVE_TRIANGLE_STRIP, null)
for i in range(1, to_be_rendered.size()):
var mapped_index = floor(float(i) / points_to_render * path_points.size())
var normal = Vector3()
if alignment == "Motion":
normal = path_points[mapped_index].normal
elif alignment == "View":
var path_direction = (to_be_rendered[i] - to_be_rendered[i-1]).normalized()
var cam_pos = get_viewport().get_camera().get_global_transform().origin
normal = (cam_pos - (to_be_rendered[i] + to_be_rendered[i-1])/2).cross(path_direction).normalized()
else:
normal = target.get_global_transform().basis.y.normalized()
var rr = 1-factor
var width = base_width
if width_profile:
width = base_width * width_profile.interpolate(rr)
if width_over_time:
var fact = 1 - path_points[mapped_index].age/life_time
width = width * width_over_time.interpolate(fact)
var color = Color(1, 1, 1)
if color_gradient:
color = color_gradient.interpolate(rr)
# --------------------------RENDERING----------------------------
var p1 = to_be_rendered[i] - normal*width/2
var p2 = to_be_rendered[i] + normal*width/2
var u: float = factor
if tiled_texture:
if tiling:
u *= tiling
else:
_u += (to_be_rendered[i] - to_be_rendered[i-1]).length()/base_width
u = _u
set_color(color)
set_uv(Vector2(u, 0))
add_vertex(p1)
set_uv(Vector2(u, 1))
add_vertex(p2)
factor += step
wire_points += [p1, p2]
end()
if show_wireframe:
begin(Mesh.PRIMITIVE_LINE_STRIP, null)
set_color(wireframe_color)
for i in range(1, wire_points.size()-2, 2):
## i-1, i+1, i, i+2
add_vertex(wire_points[i-1])
add_vertex(wire_points[i+1])
add_vertex(wire_points[i])
add_vertex(wire_points[i+2])
end()
func chaikin(points, iterations):
""" Chaikins Algorithms for curves """
if points.size() > 1:
if (iterations == 0):
return points
var result = [points[0]]
for i in range(0, points.size()-1):
result += chaikin_cut(points[i], points[i+1])
result += [points[points.size()-1]]
return chaikin(result, iterations-1)
return points
func chaikin_cut(a, b):
""" Cutting one segment """
var ratio = clamp(smoothing_ratio, 0, 1)
if (ratio > 0.5): ratio = 1 - ratio;
# Find point at a given ratio going from A to B
var p1 = a.linear_interpolate(b, ratio)
# Find point at a given ratio going from B to A
var p2 = b.linear_interpolate(a, ratio)
return [p1, p2]