195 lines
5.0 KiB
GDScript
195 lines
5.0 KiB
GDScript
#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):
|
||
""" Chaikin’s 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]
|
||
|