save code
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
@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 flipbook support
|
||||
# Hide the actual GPUParticles3D node
|
||||
# Restructure code, use enums for flags
|
||||
# Add more polygons, make trail smoother
|
||||
# Add an acceleration parameter
|
||||
# Port to Godot 3.5
|
||||
# Port to 2D
|
||||
# Allow custom material
|
||||
|
||||
# PUBLIC
|
||||
|
||||
## Length is the number of steps in the trail
|
||||
@export var length : int = 100 : set = _set_length
|
||||
@export var length_seconds : float : set = _set_length
|
||||
|
||||
@export_category("Color / Texture")
|
||||
|
||||
## 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
|
||||
|
||||
## Scolls the texture by applying an offset to the UV
|
||||
@export var scroll : Vector2 : set = _set_scroll
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
@export_category("Mesh tweaks")
|
||||
|
||||
## 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
|
||||
|
||||
# the main fps is default
|
||||
fixed_fps = int( DisplayServer.screen_get_refresh_rate(DisplayServer.MAIN_WINDOW_ID) )
|
||||
|
||||
if DisplayServer.screen_get_refresh_rate(DisplayServer.MAIN_WINDOW_ID) < 0.0:
|
||||
push_warning("Could not find screen refresh rate. Using fixed_fps = 60")
|
||||
fixed_fps = 60
|
||||
|
||||
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).duplicate(true)
|
||||
curve = preload(_DEFAULT_CURVE).duplicate(true)
|
||||
|
||||
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):
|
||||
if value is int: # length is being set
|
||||
length = value
|
||||
length = max(length, 1)
|
||||
length_seconds = float(length) / get_fixed_fps()
|
||||
elif value is float: # length_seconds is being set
|
||||
length = int(value * get_fixed_fps())
|
||||
length = max(length, 1)
|
||||
length_seconds = float(length) / get_fixed_fps()
|
||||
|
||||
if _defaults_have_been_set:
|
||||
amount = length
|
||||
lifetime = length
|
||||
|
||||
restart()
|
||||
|
||||
func _set_texture(value):
|
||||
texture = value
|
||||
_uv_offset = Vector2(0,0) # Reset the scroll when a new texture is assigned
|
||||
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_scroll(value):
|
||||
scroll = value
|
||||
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
|
||||
var _uv_offset : Vector2
|
||||
func _process(delta):
|
||||
if(snap_to_transform):
|
||||
draw_pass_1.material.set_shader_parameter("emmission_transform", global_transform)
|
||||
|
||||
# Handle UV scrolling
|
||||
_uv_offset += scroll * delta
|
||||
_uv_offset = _uv_offset.posmod(1.0)
|
||||
draw_pass_1.material.set_shader_parameter("uv_offset", _uv_offset)
|
||||
|
||||
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 : Vector3):
|
||||
_billboard_transform = global_transform
|
||||
var p : Vector3 = _billboard_transform.basis[1]
|
||||
var x : Vector3 = tangent
|
||||
var angle : float = p.angle_to(x)
|
||||
var rotation_axis : Vector3 = p.cross(x).normalized()
|
||||
if rotation_axis != Vector3():
|
||||
_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)
|
||||
Reference in New Issue
Block a user