giants-godot/addons/Trail/trail_3d_v1.gd

195 lines
5.0 KiB
GDScript3
Raw Normal View History

2021-09-08 18:36:41 +02:00
#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]