Repository: celyk/GPUTrail
Branch: main
Commit: 00a764775941
Files: 16
Total size: 21.8 KB
Directory structure:
gitextract_2dp5sfum/
├── .gitattributes
├── .github/
│ └── FUNDING.yml
├── GPUTrail3D.gd
├── Godot.gitignore
├── LICENSE
├── README.md
├── defaults/
│ ├── curve.tres
│ └── texture.tres
├── gizmos/
│ └── gizmo.gd
├── plugin.cfg
├── plugin.gd
└── shaders/
├── custom_example_alpha_erosion.gdshader
├── custom_example_blank.gdshader
├── gputrail_lib.gdshaderinc
├── trail.gdshader
└── trail_draw_pass.gdshader
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: celyk
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: GPUTrail3D.gd
================================================
@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]
## @tutorial(celyk's repo): https://github.com/celyk/godot-useful-stuff
##
## 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
## A mask texture, used to modify the alpha of trail
@export var mask : Texture : set = _set_mask
## A float value from 0.0 to 1.0 for determing how intense the mask texture is
@export var mask_strength : float = 1.0 : set = _set_mask_strength
## 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_mask(value):
mask = value
if value:
draw_pass_1.material.set_shader_parameter("mask", mask)
else:
draw_pass_1.material.set_shader_parameter("mask", null)
func _set_mask_strength(value):
mask_strength = clamp(value,0.0,1.0)
if value:
draw_pass_1.material.set_shader_parameter("mask_strength", mask_strength)
else:
draw_pass_1.material.set_shader_parameter("mask_strength", 1.0)
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)
================================================
FILE: Godot.gitignore
================================================
# Godot 4+ specific ignores
.godot/
# Godot-specific ignores
.import/
export.cfg
export_presets.cfg
# Imported translations (automatically generated from CSV files)
*.translation
# Mono-specific ignores
.mono/
data_*/
mono_crash.*.json
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 celyk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# GPUTrail
GPUTrail is a GPU-based trail plugin for Godot 4, offering an efficient alternative to CPU-based trails for creating ribbon trail effects in your games and applications.

## Features
- GPU-accelerated trail rendering for improved performance
- Customizable trail length, texture, and color
- Support for color ramps and width curves
- Options for texture orientation and alpha channel handling
- Billboard mode for camera-facing trails
- Dewiggle and clip overlaps features for improved trail appearance
## Installation
1. Clone or download this repository
2. Copy the `addons/GPUTrail` folder into your Godot project's `addons` folder
3. Enable the plugin in your project settings: Project -> Project Settings -> Plugins -> GPUTrail
## Usage
1. Add a new `GPUTrail3D` node to your scene
2. Customize the trail properties in the Inspector panel
3. Attach the `GPUTrail3D` node to the object you want to trail
## Properties
- `length`: Number of steps in the trail
- `texture`: Main texture of the trail
- `color_ramp`: Color gradient along the trail's length
- `curve`: Width modulation along the trail's length
- `vertical_texture`: Adjust texture orientation
- `use_red_as_alpha`: Use the red channel of the texture as alpha
- `billboard`: Make the trail face the camera (experimental)
- `dewiggle`: Improve texture mapping to the trail
- `clip_overlaps`: Prevent trail self-intersectionis
- `snap_to_transform`: Snap the trail start to the node's position, and regardless of the particles own framerate
## Example
```gdscript
var trail = GPUTrail3D.new()
trail.length = 100
trail.texture = preload("res://trail_texture.png")
add_child(trail)
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Acknowledgements
- Tutorial by Le Lu: [YouTube Video](https://www.youtube.com/watch?v=0VsEfP4XFCM)
## 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
================================================
FILE: defaults/curve.tres
================================================
[gd_resource type="CurveTexture" load_steps=2 format=3 uid="uid://ct31fhxvcragr"]
[sub_resource type="Curve" id="Curve_7ufs5"]
bake_resolution = 16
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0.1), 0.0, 0.0, 0, 0]
point_count = 2
[resource]
curve = SubResource("Curve_7ufs5")
================================================
FILE: defaults/texture.tres
================================================
[gd_resource type="GradientTexture1D" load_steps=2 format=3 uid="uid://crk6pkb7e5rwc"]
[sub_resource type="Gradient" id="Gradient_nk704"]
colors = PackedColorArray(1, 1, 1, 1, 1, 1, 1, 0)
[resource]
gradient = SubResource("Gradient_nk704")
================================================
FILE: gizmos/gizmo.gd
================================================
@tool
extends EditorNode3DGizmoPlugin
var editor_plugin : EditorPlugin
func _init(_editor_plugin:EditorPlugin):
editor_plugin = _editor_plugin
create_material("main", Color(1,1,1), false, true, true)
func _has_gizmo(node):
return node is GPUTrail3D
# show gizmo name in visibility list
func _get_gizmo_name():
return "GPUTrail3DGizmo"
func _redraw(gizmo):
gizmo.clear()
var node3d : Node3D = gizmo.get_node_3d()
var lines = PackedVector3Array()
lines.push_back(Vector3(0,1,0))
lines.push_back(Vector3(0,-1,0))
gizmo.add_lines(lines, get_material("main", gizmo), false, Color(1,1,1,1))
================================================
FILE: plugin.cfg
================================================
[plugin]
name="GPUTrail"
description="temporary desc"
author="celyk"
version="0.1"
script="plugin.gd"
================================================
FILE: plugin.gd
================================================
@tool
extends EditorPlugin
const MyCustomGizmoPlugin = preload("gizmos/gizmo.gd")
var gizmo_plugin = MyCustomGizmoPlugin.new(self)
func _enter_tree():
# Initialization of the plugin goes here.
# Add the new type with a name, a parent type, a script and an icon.
add_custom_type("GPUTrail3D", "GPUParticles3D", preload("GPUTrail3D.gd"), preload("bounce.svg"))
add_node_3d_gizmo_plugin(gizmo_plugin)
func _exit_tree():
# Clean-up of the plugin goes here.
# Always remember to remove it from the engine when deactivated.
remove_custom_type("GPUTrail3D")
remove_node_3d_gizmo_plugin(gizmo_plugin)
================================================
FILE: shaders/custom_example_alpha_erosion.gdshader
================================================
shader_type spatial;
#define GPU_TRAIL
#include "res://addons/GPUTrail/shaders/gputrail_lib.gdshaderinc"
varying vec2 trail_uv;
void vertex(){
PREPARE_TRAIL();
// Save the trail UV coordaintes for later
trail_uv = UV;
// Scroll textures slowly along the length of the trail
//UV.x -= fract(TIME*.4);
}
void fragment(){
PREPARE_TRAIL_TEXTURES();
// Alpha erosion
ALPHA = max(ALPHA - trail_uv.x, 0.0);
}
================================================
FILE: shaders/custom_example_blank.gdshader
================================================
shader_type spatial;
#define GPU_TRAIL
#include "res://addons/GPUTrail/shaders/gputrail_lib.gdshaderinc"
varying vec2 trail_uv;
void vertex(){
PREPARE_TRAIL();
}
void fragment(){
PREPARE_TRAIL_TEXTURES();
}
================================================
FILE: shaders/gputrail_lib.gdshaderinc
================================================
#ifdef GPU_TRAIL
//#define GPU_TRAIL
// TODO make optional
render_mode unshaded,world_vertex_coords,cull_disabled;
uniform sampler2D tex : repeat_enable, source_color, hint_default_white;
uniform vec2 uv_offset = vec2(0);
uniform sampler2D color_ramp : repeat_disable, source_color, hint_default_white;
uniform sampler2D curve : repeat_disable, hint_default_white;
uniform mat4 emmission_transform = mat4(1);
uniform int flags = 0;
/*uniform bool vertical_texture = false;
uniform bool use_red_as_alpha = false;
uniform bool billboard = false;
uniform bool dewiggle = false;
uniform bool snap_to_transform = false;*/
#define vertical_texture bool(flags & 1)
#define use_red_as_alpha bool(flags & 2)
#define billboard bool(flags & 4)
#define dewiggle bool(flags & 8)
#define snap_to_transform bool(flags & 16)
#define clip_overlaps bool(flags & 32)
varying float scale_interp;
varying vec2 clip;
varying vec2 mesh_uv;
void missing_parens(){}
#define PREPARE_TRAIL { \
mesh_uv = UV; \
\
mat4 my_model_matrix = MODEL_MATRIX; \
if(snap_to_transform && INSTANCE_CUSTOM.w==2.0){ \
my_model_matrix[1] = emmission_transform * vec4(0,1,0,1); \
my_model_matrix[2] = emmission_transform * vec4(0,-1,0,1); \
} \
\
if(billboard){ \
vec3 t0 = my_model_matrix[0].xyz-my_model_matrix[3].xyz; \
vec3 t1 = my_model_matrix[1].xyz-my_model_matrix[2].xyz; \
\
vec3 up0 = length(t0)*normalize( \
cross( \
my_model_matrix[3].xyz-INV_VIEW_MATRIX[3].xyz, \
t0)); \
vec3 up1 = length(t1)*normalize( \
cross( \
my_model_matrix[2].xyz-INV_VIEW_MATRIX[3].xyz, \
t1)); \
\
my_model_matrix[0] = my_model_matrix[3]; \
my_model_matrix[1] = my_model_matrix[2]; \
\
my_model_matrix[0].xyz += up0; \
my_model_matrix[3].xyz -= up0; \
\
my_model_matrix[1].xyz += up1; \
my_model_matrix[2].xyz -= up1; \
} \
\
vec3 a = mix(my_model_matrix[1].xyz,my_model_matrix[0].xyz,UV.x); \
vec3 b = mix(my_model_matrix[2].xyz,my_model_matrix[3].xyz,UV.x); \
\
UV.x = (UV.x + INSTANCE_CUSTOM.w-1.0 - 2.0)/(INSTANCE_CUSTOM.z-1.0); \
\
\
float h = textureLod(curve, vec2(UV.x), 0.0).x; \
\
VERTEX = mix(a,b,(UV.y-0.5)*h + 0.5); \
\
if(dewiggle){ \
scale_interp = h; \
UV *= scale_interp; \
} \
\
\
clip.x = dot(VERTEX - INV_VIEW_MATRIX[3].xyz,cross(my_model_matrix[1].xyz - INV_VIEW_MATRIX[3].xyz,my_model_matrix[2].xyz - INV_VIEW_MATRIX[3].xyz)); \
clip.y = dot(VERTEX - INV_VIEW_MATRIX[3].xyz,cross(my_model_matrix[3].xyz - INV_VIEW_MATRIX[3].xyz,my_model_matrix[0].xyz - INV_VIEW_MATRIX[3].xyz)); \
} missing_parens
#define PREPARE_TRAIL_TEXTURES { \
vec2 clip0 = clip; \
float ababab = clip0.x*clip0.y; \
\
if(clip_overlaps && ababab < 0.0) { \
if(abs(mesh_uv.x-0.5)<0.5) \
discard; \
} \
\
vec2 base_uv = UV; \
\
if(dewiggle){ \
base_uv /= scale_interp; \
} \
\
vec2 raw_uv = base_uv; \
\
base_uv -= uv_offset; \
\
if(vertical_texture){ \
base_uv = base_uv.yx; \
} \
\
vec4 T = textureLod(tex, base_uv, 0.0); \
ALBEDO = T.rgb; \
ALPHA = T.a; \
\
if(use_red_as_alpha){ \
ALBEDO = vec3(1); \
ALPHA = T.r; \
} \
\
T = textureLod(color_ramp, raw_uv, 0.0); \
ALBEDO *= T.rgb; \
ALPHA *= T.a; \
} missing_parens
#endif /*GPU_TRAIL*/
================================================
FILE: shaders/trail.gdshader
================================================
shader_type particles;
render_mode keep_data,disable_force,disable_velocity;
void process() {
// CUSTOM.w tracks the particles place in the trail, in range (0..LIFETIME]
// requires that LIFETIME = number of particles
float amount = LIFETIME;
vec4 a = EMISSION_TRANSFORM * vec4(0,1,0,1);
vec4 b = EMISSION_TRANSFORM * vec4(0,-1,0,1);
// start
if(CUSTOM.w == 0.0){
CUSTOM.w = float(INDEX)+1.0;
// needed to pass to draw pass
CUSTOM.z = amount;
// needed to initialize in case of CUSTOM.w == 2.0
TRANSFORM = mat4(a,a,b,b);
}
// restart
if(CUSTOM.w == amount+1.0){
CUSTOM.w = 1.0;
}
if(CUSTOM.w == 1.0){
// sets the quad to the line to cache this frame, it is not yet visible
TRANSFORM = mat4(a,a,b,b);
}
if(CUSTOM.w == 2.0){
// sets the right edge of the quad
TRANSFORM[1] = a;
TRANSFORM[2] = b;
}
CUSTOM.w++;
}
================================================
FILE: shaders/trail_draw_pass.gdshader
================================================
shader_type spatial;
render_mode unshaded,world_vertex_coords,cull_disabled;
uniform sampler2D tex : repeat_enable, source_color, hint_default_white;
uniform sampler2D mask : hint_default_white;
uniform float mask_strength = 1.0;
uniform vec2 uv_offset = vec2(0);
uniform sampler2D color_ramp : repeat_disable, source_color, hint_default_white;
uniform sampler2D curve : repeat_disable, hint_default_white;
uniform mat4 emmission_transform = mat4(1);
uniform int flags = 0;
/*uniform bool vertical_texture = false;
uniform bool use_red_as_alpha = false;
uniform bool billboard = false;
uniform bool dewiggle = false;
uniform bool snap_to_transform = false;*/
#define vertical_texture bool(flags & 1)
#define use_red_as_alpha bool(flags & 2)
#define billboard bool(flags & 4)
#define dewiggle bool(flags & 8)
#define snap_to_transform bool(flags & 16)
#define clip_overlaps bool(flags & 32)
varying float scale_interp;
varying vec2 clip;
varying vec2 mesh_uv;
void vertex(){
mesh_uv = UV;
mat4 my_model_matrix = MODEL_MATRIX;
if(snap_to_transform && INSTANCE_CUSTOM.w==2.0){
my_model_matrix[1] = emmission_transform * vec4(0,1,0,1);
my_model_matrix[2] = emmission_transform * vec4(0,-1,0,1);
}
if(billboard){
vec3 t0 = my_model_matrix[0].xyz-my_model_matrix[3].xyz;
vec3 t1 = my_model_matrix[1].xyz-my_model_matrix[2].xyz;
//vec3 up1 = up0;
vec3 up0 = length(t0)*normalize(
cross(
my_model_matrix[3].xyz-INV_VIEW_MATRIX[3].xyz,
//-INV_VIEW_MATRIX[2].xyz,
t0));
vec3 up1 = length(t1)*normalize(
cross(
my_model_matrix[2].xyz-INV_VIEW_MATRIX[3].xyz,
//-INV_VIEW_MATRIX[2].xyz,
t1));
my_model_matrix[0] = my_model_matrix[3];
my_model_matrix[1] = my_model_matrix[2];
my_model_matrix[0].xyz += up0;
my_model_matrix[3].xyz -= up0;
my_model_matrix[1].xyz += up1;
my_model_matrix[2].xyz -= up1;
}
vec3 a = mix(my_model_matrix[1].xyz,my_model_matrix[0].xyz,UV.x);
vec3 b = mix(my_model_matrix[2].xyz,my_model_matrix[3].xyz,UV.x);
UV.x = (UV.x + INSTANCE_CUSTOM.w-1.0 - 2.0)/(INSTANCE_CUSTOM.z-1.0);
float h = textureLod(curve, vec2(UV.x), 0.0).x;//h=1.0;
VERTEX = mix(a,b,(UV.y-0.5)*h + 0.5);
if(dewiggle){
scale_interp = h;
UV *= scale_interp;
}
clip.x = dot(VERTEX - INV_VIEW_MATRIX[3].xyz,cross(my_model_matrix[1].xyz - INV_VIEW_MATRIX[3].xyz,my_model_matrix[2].xyz - INV_VIEW_MATRIX[3].xyz));
clip.y = dot(VERTEX - INV_VIEW_MATRIX[3].xyz,cross(my_model_matrix[3].xyz - INV_VIEW_MATRIX[3].xyz,my_model_matrix[0].xyz - INV_VIEW_MATRIX[3].xyz));
}
void fragment(){
//if(billboard && !FRONT_FACING) discard;
vec2 clip0 = clip;
float ababab = clip0.x*clip0.y;
//ababab += dFdx(ababab) + dFdy(ababab);
//clip0 -= fwidth(clip0);
if(clip_overlaps && ababab < 0.0) {
if(abs(mesh_uv.x-0.5)<0.5)
discard;
}
vec2 base_uv = UV;
if(dewiggle){
base_uv /= scale_interp;
}
vec2 raw_uv = base_uv;
// Handle UV scrolling
base_uv -= uv_offset;
if(vertical_texture){
base_uv = base_uv.yx;
}
vec4 T = textureLod(tex, base_uv, 0.0);
ALBEDO = T.rgb;
ALPHA = T.a;
if(use_red_as_alpha){
ALBEDO = vec3(1);
ALPHA = T.r;
}
T = textureLod(color_ramp, raw_uv, 0.0);
ALBEDO *= T.rgb;
ALPHA *= T.a;
vec4 M = textureLod(mask, base_uv, 0.0);
ALPHA *= (M.r * mask_strength);
//ALBEDO = vec3(UV,0);
if((base_uv.x < .01) || (.99 < base_uv.x)){
//ALBEDO = vec3(1,0,1);
}
}
gitextract_2dp5sfum/
├── .gitattributes
├── .github/
│ └── FUNDING.yml
├── GPUTrail3D.gd
├── Godot.gitignore
├── LICENSE
├── README.md
├── defaults/
│ ├── curve.tres
│ └── texture.tres
├── gizmos/
│ └── gizmo.gd
├── plugin.cfg
├── plugin.gd
└── shaders/
├── custom_example_alpha_erosion.gdshader
├── custom_example_blank.gdshader
├── gputrail_lib.gdshaderinc
├── trail.gdshader
└── trail_draw_pass.gdshader
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (25K chars).
[
{
"path": ".gitattributes",
"chars": 66,
"preview": "# Auto detect text files and perform LF normalization\n* text=auto\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 888,
"preview": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [u"
},
{
"path": "GPUTrail3D.gd",
"chars": 7800,
"preview": "@tool\n@icon(\"bounce.svg\")\nclass_name GPUTrail3D extends GPUParticles3D\n\n## [br]A node for creating a ribbon trail effect"
},
{
"path": "Godot.gitignore",
"chars": 239,
"preview": "# Godot 4+ specific ignores\n.godot/\n\n# Godot-specific ignores\n.import/\nexport.cfg\nexport_presets.cfg\n\n# Imported transla"
},
{
"path": "LICENSE",
"chars": 1062,
"preview": "MIT License\n\nCopyright (c) 2023 celyk\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
},
{
"path": "README.md",
"chars": 2307,
"preview": "# GPUTrail\n\nGPUTrail is a GPU-based trail plugin for Godot 4, offering an efficient alternative to CPU-based trails for "
},
{
"path": "defaults/curve.tres",
"chars": 285,
"preview": "[gd_resource type=\"CurveTexture\" load_steps=2 format=3 uid=\"uid://ct31fhxvcragr\"]\n\n[sub_resource type=\"Curve\" id=\"Curve_"
},
{
"path": "defaults/texture.tres",
"chars": 242,
"preview": "[gd_resource type=\"GradientTexture1D\" load_steps=2 format=3 uid=\"uid://crk6pkb7e5rwc\"]\n\n[sub_resource type=\"Gradient\" id"
},
{
"path": "gizmos/gizmo.gd",
"chars": 609,
"preview": "@tool\nextends EditorNode3DGizmoPlugin\n\nvar editor_plugin : EditorPlugin\n\nfunc _init(_editor_plugin:EditorPlugin):\n\tedito"
},
{
"path": "plugin.cfg",
"chars": 103,
"preview": "[plugin]\n\nname=\"GPUTrail\"\ndescription=\"temporary desc\"\nauthor=\"celyk\"\nversion=\"0.1\"\nscript=\"plugin.gd\"\n"
},
{
"path": "plugin.gd",
"chars": 608,
"preview": "@tool\nextends EditorPlugin\n\nconst MyCustomGizmoPlugin = preload(\"gizmos/gizmo.gd\")\nvar gizmo_plugin = MyCustomGizmoPlugi"
},
{
"path": "shaders/custom_example_alpha_erosion.gdshader",
"chars": 415,
"preview": "shader_type spatial;\n\n#define GPU_TRAIL\n#include \"res://addons/GPUTrail/shaders/gputrail_lib.gdshaderinc\"\n\nvarying vec2 "
},
{
"path": "shaders/custom_example_blank.gdshader",
"chars": 213,
"preview": "shader_type spatial;\n\n#define GPU_TRAIL\n#include \"res://addons/GPUTrail/shaders/gputrail_lib.gdshaderinc\"\n\nvarying vec2 "
},
{
"path": "shaders/gputrail_lib.gdshaderinc",
"chars": 3241,
"preview": "#ifdef GPU_TRAIL\n//#define GPU_TRAIL\n\n// TODO make optional\nrender_mode unshaded,world_vertex_coords,cull_disabled;\n\nuni"
},
{
"path": "shaders/trail.gdshader",
"chars": 869,
"preview": "shader_type particles;\n\nrender_mode keep_data,disable_force,disable_velocity;\n\n\n\nvoid process() {\n\t// CUSTOM.w tracks th"
},
{
"path": "shaders/trail_draw_pass.gdshader",
"chars": 3389,
"preview": "shader_type spatial;\n\nrender_mode unshaded,world_vertex_coords,cull_disabled;\n\nuniform sampler2D tex : repeat_enable, so"
}
]
About this extraction
This page contains the full source code of the celyk/GPUTrail GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (21.8 KB), approximately 6.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.