-
Notifications
You must be signed in to change notification settings - Fork 10
/
GPUTrail3D.gd
186 lines (146 loc) · 5.9 KB
/
GPUTrail3D.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
@tool
@icon("bounce.svg")
class_name GPUTrail3D extends GPUParticles3D
## [br]A node for creating a ribbon trail effect.
## [br][color=purple]Made by celyk[/color]
##
## This node serves as an alternative to CPU based trails.[br]
# TODO:
# Add categories for parameters
# Add flipbook support
# Hide the actual GPUParticles3D node
# Restructure code, use enums for flags
# Add more polygons, make trail smoother
# Add an acceleration parameter
# Add a gizmo/visual indicator of the emmission line
# Design an icon.svg
# Port to Godot 3.5
# Port to 2D
# Allow custom material
# PUBLIC
## Length is the number of steps in the trail
@export var length = 100 : set = _set_length
## The main texture of the trail.[br]
## [br]Set [member vertical_texture] to adjust for orientation[br]
##
## [br]Enable [member use_red_as_alpha] to use the red color channel as alpha
@export var texture : Texture : set = _set_texture
## A color ramp for modulating the color along the length of the trail
@export var color_ramp : GradientTexture1D : set = _set_color_ramp
## A curve for modulating the width along the length of the trail
@export var curve : CurveTexture : set = _set_curve
## Set [member vertical_texture] to adjust for orientation
@export var vertical_texture := false : set = _set_vertical_texture
## Enable [member use_red_as_alpha] to use the red color channel of [member texture] as alpha
@export var use_red_as_alpha := false : set = _set_use_red_as_alpha
## Makes trail face camera. I haven't finished this yet
@export var billboard := false : set = _set_billboard
## Enable to improve the mapping of [member texture] to the trail
@export var dewiggle := true : set = _set_dewiggle
## Enable to improve the mapping of [member texture] to the trail
@export var clip_overlaps := true : set = _set_clip_overlaps
## Enable [member snap_to_transform] to snap the start of the trail to the nodes position. This may not be noticeable unless you
## have changed [member fixed_fps], which you can use to optimize the trail
@export var snap_to_transform := false : set = _set_snap_to_transform
# PRIVATE
const _DEFAULT_TEXTURE = "defaults/texture.tres"
const _DEFAULT_CURVE = "defaults/curve.tres"
var _defaults_have_been_set = false
func _get_property_list():
return [{"name": "_defaults_have_been_set","type": TYPE_BOOL,"usage": PROPERTY_USAGE_NO_EDITOR}]
func _ready():
if not _defaults_have_been_set:
_defaults_have_been_set = true
amount = length
lifetime = length
explosiveness = 1 # emits all particles at once
fixed_fps = 0 # the main fps is default
process_material = ShaderMaterial.new()
process_material.shader = preload("shaders/trail.gdshader")
draw_pass_1 = QuadMesh.new()
draw_pass_1.material = ShaderMaterial.new()
draw_pass_1.material.shader = preload("shaders/trail_draw_pass.gdshader")
color_ramp = preload(_DEFAULT_TEXTURE)
curve = preload(_DEFAULT_CURVE)
draw_pass_1.material.resource_local_to_scene = true
length = length
vertical_texture = vertical_texture
use_red_as_alpha = use_red_as_alpha
billboard = billboard
dewiggle = dewiggle
clip_overlaps = clip_overlaps
snap_to_transform = snap_to_transform
func _set_length(value):
length = value
if _defaults_have_been_set:
amount = value
lifetime = value
restart()
func _set_texture(value):
texture = value
if value:
draw_pass_1.material.set_shader_parameter("tex", texture)
else:
draw_pass_1.material.set_shader_parameter("tex", preload(_DEFAULT_TEXTURE))
func _set_color_ramp(value):
color_ramp = value
draw_pass_1.material.set_shader_parameter("color_ramp", color_ramp)
func _set_curve(value):
curve = value
if value:
draw_pass_1.material.set_shader_parameter("curve", curve)
else:
draw_pass_1.material.set_shader_parameter("curve", preload(_DEFAULT_CURVE))
func _set_vertical_texture(value):
vertical_texture = value
_flags = _set_flag(_flags,0,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
func _set_use_red_as_alpha(value):
use_red_as_alpha = value
_flags = _set_flag(_flags,1,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
func _set_billboard(value):
billboard = value
_flags = _set_flag(_flags,2,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
if value && _defaults_have_been_set:
_update_billboard_transform( global_transform.basis[0] )
restart()
func _set_dewiggle(value):
dewiggle = value
_flags = _set_flag(_flags,3,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
func _set_snap_to_transform(value):
snap_to_transform = value
_flags = _set_flag(_flags,4,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
func _set_clip_overlaps(value):
clip_overlaps = value
_flags = _set_flag(_flags,5,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
@onready var _old_pos : Vector3 = global_position
@onready var _billboard_transform : Transform3D = global_transform
func _process(delta):
if(snap_to_transform):
draw_pass_1.material.set_shader_parameter("emmission_transform", global_transform)
await RenderingServer.frame_pre_draw
if(billboard):
var delta_position = global_position - _old_pos
if delta_position:
var tangent = global_transform.basis[1].length() * (delta_position).normalized()
_update_billboard_transform(tangent)
RenderingServer.instance_set_transform(get_instance(), _billboard_transform)
_old_pos = global_position
func _update_billboard_transform(tangent):
_billboard_transform = global_transform
var p = _billboard_transform.basis[1]
var x = tangent
var angle = p.angle_to(x)
var rotation_axis = p.cross(x).normalized()
if rotation_axis:
_billboard_transform.basis = _billboard_transform.basis.rotated(rotation_axis,angle)
_billboard_transform.basis = _billboard_transform.basis.scaled(Vector3(0.5,0.5,0.5))
_billboard_transform.origin += _billboard_transform.basis[1]
var _flags = 0
func _set_flag(i, idx : int, value : bool):
return (i & ~(1 << idx)) | (int(value) << idx)