giants-godot/addons/Trail/trail_3d_v1.gd

195 lines
5.0 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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]