Particles Udpate

This commit is contained in:
Rokojori 2026-10-05 21:25:04 +02:00
parent 68105b2275
commit 4bbed6ba6c
68 changed files with 1517 additions and 4 deletions

View File

@ -36,6 +36,8 @@ namespace Rokojori
return _dispatchCounter;
}
public bool isRunning => _running.Count > 0;
public readonly EventSlot<SequenceActionFinishedEvent> onSequenceDone = new EventSlot<SequenceActionFinishedEvent>();

View File

@ -0,0 +1,29 @@
using Godot;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
public abstract partial class BehaviorBranch:Node
{
[Export]
public bool invert = false;
protected virtual bool _IsBranchActive( Brain brain )
{
return false;
}
public bool IsBranchActive( Brain brain )
{
var active = _IsBranchActive( brain );
if ( invert )
{
active = ! active;
}
return active;
}
}

View File

@ -0,0 +1 @@
uid://7bflktj7bbw4

View File

@ -0,0 +1,16 @@
using Godot;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
public abstract partial class BehaviorMovement:Node
{
public abstract void GoToPosition( Vector3 worldPosition );
public abstract void LookAtPosition( Vector3 worldPosition );
public abstract void GoTo( Node3D node );
public abstract void Follow( Node3D node );
public abstract void LookAt( Node3D node );
}

View File

@ -0,0 +1 @@
uid://r3oqbmly415i

View File

@ -0,0 +1,46 @@
using Godot;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
public partial class BehaviorSequence:Sequence
{
[Export]
public bool loop = false;
[Export]
public bool isExclusive = true;
[Export]
public bool reinitialize = false;
Brain _brain;
public override void _Ready()
{
base._Ready();
_brain = NodesWalker.Get().GetParentWithType<Brain>( this );
onSequenceDone.AddAction( OnDone );
}
void OnDone( SequenceActionFinishedEvent ev )
{
if ( _brain == null || _brain.activeSequence != this )
{
return;
}
if ( loop )
{
Trigger();
}
else
{
_brain.Trigger();
}
}
}

View File

@ -0,0 +1 @@
uid://dsdwv5u60cen

109
Runtime/Behavior/Brain.cs Normal file
View File

@ -0,0 +1,109 @@
using Godot;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
public partial class Brain:Action
{
[Export]
public Receptor[] receptors;
[Export]
public Node behaviorRoot;
[Export]
public BehaviorSequence activeSequence;
bool _paused = false;
public bool paused => _paused;
public void SetPaused( bool value )
{
_paused = value;
}
protected override void _OnTrigger()
{
if ( _paused )
{
return;
}
if ( activeSequence != null && activeSequence.isExclusive && activeSequence.isRunning )
{
return;
}
var resolved = Resolve();
if ( resolved == null )
{
return;
}
if ( resolved == activeSequence )
{
if ( activeSequence.reinitialize )
{
activeSequence.CancelAllRunning();
activeSequence.Trigger();
}
return;
}
var previous = activeSequence;
activeSequence = resolved;
if ( previous != null )
{
previous.CancelAllRunning();
}
activeSequence.Trigger();
}
BehaviorSequence Resolve()
{
if ( behaviorRoot == null )
{
return null;
}
return ResolveIn( behaviorRoot );
}
BehaviorSequence ResolveIn( Node parent )
{
var walker = NodesWalker.Get();
var numChildren = walker.NumChildren( parent );
for ( int i = 0; i < numChildren; i++ )
{
var child = walker.ChildAt( parent, i );
if ( child is BehaviorSequence sequence )
{
return sequence;
}
if ( child is BehaviorBranch branch )
{
if ( branch.IsBranchActive( this ) )
{
var found = ResolveIn( branch );
if ( found != null )
{
return found;
}
}
}
}
return null;
}
}

View File

@ -0,0 +1 @@
uid://dm2je7akh1jyi

View File

@ -0,0 +1,68 @@
using Godot;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
public partial class BrainControl:Action
{
public enum Mode
{
Start,
Pause,
Resume,
Toggle
}
[Export]
public Brain brain;
[Export]
public Mode mode;
protected override void _OnTrigger()
{
if ( brain == null )
{
return;
}
switch ( mode )
{
case Mode.Start:
StartBrain();
break;
case Mode.Pause:
brain.SetPaused( true );
break;
case Mode.Resume:
brain.SetPaused( false );
break;
case Mode.Toggle:
brain.SetPaused( ! brain.paused );
break;
}
}
void StartBrain()
{
brain.SetPaused( false );
if ( brain.receptors != null )
{
foreach ( var receptor in brain.receptors )
{
if ( receptor != null )
{
receptor.Start();
}
}
}
brain.Trigger();
}
}

View File

@ -0,0 +1 @@
uid://k2rbcvsu64ap

View File

@ -0,0 +1,99 @@
using Godot;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
public partial class CharacterBodyBehaviorMovement:BehaviorMovement
{
[Export]
public CharacterBody3D body;
[Export]
public float moveSpeed = 5f;
[Export]
public float arrivalDistance = 0.1f;
Node3D _followTarget;
Vector3? _targetPosition;
public override void GoToPosition( Vector3 worldPosition )
{
_followTarget = null;
_targetPosition = worldPosition;
}
public override void LookAtPosition( Vector3 worldPosition )
{
if ( body == null )
{
return;
}
var direction = worldPosition - body.GlobalPosition;
direction.Y = 0;
if ( direction.LengthSquared() < 0.0001f )
{
return;
}
body.GlobalTransform = new Transform3D( Basis.LookingAt( direction, Vector3.Up ), body.GlobalPosition );
}
public override void GoTo( Node3D node )
{
_followTarget = null;
_targetPosition = node == null ? null : node.GlobalPosition;
}
public override void Follow( Node3D node )
{
_followTarget = node;
_targetPosition = null;
}
public override void LookAt( Node3D node )
{
if ( node == null )
{
return;
}
LookAtPosition( node.GlobalPosition );
}
public override void _PhysicsProcess( double delta )
{
if ( body == null )
{
return;
}
Vector3? target = _followTarget != null ? _followTarget.GlobalPosition : _targetPosition;
if ( target == null )
{
return;
}
var toTarget = target.Value - body.GlobalPosition;
toTarget.Y = 0;
if ( toTarget.Length() <= arrivalDistance )
{
body.Velocity = Vector3.Zero;
body.MoveAndSlide();
return;
}
var direction = toTarget.Normalized();
body.Velocity = direction * moveSpeed;
body.MoveAndSlide();
LookAtPosition( body.GlobalPosition + direction );
}
}

View File

@ -0,0 +1 @@
uid://btsle0n13r6hy

View File

@ -0,0 +1,17 @@
using Godot;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
public partial class ConditionBranch:BehaviorBranch
{
[Export]
public SceneCondition sceneCondition;
protected override bool _IsBranchActive( Brain brain )
{
return SceneCondition.Evaluate( sceneCondition );
}
}

View File

@ -0,0 +1 @@
uid://1ywnbv1h1gik

View File

@ -0,0 +1,25 @@
using Godot;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
public partial class FollowBehavior:BehaviorSequence
{
[Export]
public BehaviorMovement movement;
[Export]
public Node3D target;
protected override void _OnTrigger()
{
if ( movement != null )
{
movement.Follow( target );
}
base._OnTrigger();
}
}

View File

@ -0,0 +1 @@
uid://32dsw4a65vh0

View File

@ -0,0 +1,95 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
public partial class Receptor:Action
{
[Export]
public SceneCondition sceneCondition;
[Export( PropertyHint.Range, "0.1,100")]
public float receiptionTriggerCoverage = 75f;
[Export( PropertyHint.Range, "10,2000")]
public float receiptionWindowDurationMS = 500f;
[Export( PropertyHint.Range, "10,1000")]
public float tickRateMS = 100f;
[Export]
public Timeline timeline;
int resultSize => Mathf.CeilToInt( receiptionWindowDurationMS / tickRateMS );
RingBuffer<bool> _resultsBuffer;
bool _activated = false;
public bool activated => _activated;
bool _active = false;
public bool active => _active;
int _eventID = -1;
public void Start()
{
if ( _active )
{
return;
}
_active = true;
var duration = tickRateMS / 1000f;
var tle = TimelineManager.ScheduleLoopEvent( timeline, duration, 0, tle => Sample() );
_eventID = tle.id;
}
public void Stop()
{
if ( ! _active )
{
return;
}
_active = false;
TimelineManager.RemoveEvent( timeline, _eventID );
_eventID = -1;
}
protected override void _OnTrigger()
{
Sample();
}
void Sample()
{
if ( sceneCondition == null )
{
return;
}
if ( _resultsBuffer == null || _resultsBuffer.capacity != resultSize )
{
_resultsBuffer = new RingBuffer<bool>( resultSize );
}
_resultsBuffer.Add( sceneCondition.Evaluate() );
if ( _resultsBuffer.count != resultSize )
{
_activated = false;
return;
}
var numActive = _resultsBuffer.CountItems( b => b );
_activated = numActive / (float) resultSize >= receiptionTriggerCoverage / 100f;
}
}

View File

@ -0,0 +1 @@
uid://dr0xqvmp8xu8c

View File

@ -0,0 +1,17 @@
using Godot;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
public partial class ReceptorBranch:BehaviorBranch
{
[Export]
public Receptor receptor;
protected override bool _IsBranchActive( Brain brain )
{
return receptor != null && receptor.activated;
}
}

View File

@ -0,0 +1 @@
uid://5kitnn4mtd1y

View File

@ -35,5 +35,12 @@ namespace Rokojori
return _resource;
}
public T GetDuplicate( bool deep = false )
{
var referenced = Get();
return referenced.Duplicate( deep ) as T;
}
}
}

View File

@ -0,0 +1,73 @@
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
public class RingBuffer<T>
{
protected readonly T[] _buffer;
protected int _head;
protected int _count;
public RingBuffer( int capacity )
{
_buffer = new T[capacity];
}
public int count => _count;
public int capacity => _buffer.Length;
public void ForEach( System.Action<T> callback )
{
for ( int i = 0; i < _count; i++ )
{
callback( this[ i ] );
}
}
public int CountItems( System.Predicate<T> predicate )
{
var num = 0;
for ( int i = 0; i < _count; i++ )
{
if ( predicate( this[ i ] ) )
{
num ++;
}
}
return num;
}
public void Add( T item )
{
int index = (_head + _count) % capacity;
_buffer[index] = item;
if (_count == capacity)
{
_head = (_head + 1) % capacity;
}
else
{
_count ++;
}
}
public T this[int index]
{
get
{
if ( (uint)index >= (uint)_count )
{
throw new System.ArgumentOutOfRangeException( nameof( index ) );
}
return _buffer[ ( _head + index ) % capacity ];
}
}
}

View File

@ -0,0 +1 @@
uid://bpqul7npdcg3q

View File

@ -0,0 +1,39 @@
// NOTE: Shader automatically converted from Godot Engine 4.7.stable.mono's StandardMaterial3D.
shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_disabled, diffuse_burley, specular_schlick_ggx, unshaded;
uniform float meshScale = 1.0;
void vertex()
{
MODELVIEW_MATRIX = VIEW_MATRIX * mat4(
MAIN_CAM_INV_VIEW_MATRIX[0],
MAIN_CAM_INV_VIEW_MATRIX[1],
MAIN_CAM_INV_VIEW_MATRIX[2],
MODEL_MATRIX[3]);
MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(
vec4(length(MODEL_MATRIX[0].xyz) * meshScale, 0.0, 0.0, 0.0),
vec4(0.0, length(MODEL_MATRIX[1].xyz) * meshScale, 0.0, 0.0),
vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz) * meshScale, 0.0),
vec4(0.0, 0.0, 0.0, 1.0));
MODELVIEW_NORMAL_MATRIX = mat3(MODELVIEW_MATRIX);
}
void fragment()
{
float d = distance( UV, vec2(0.5) );
float a = 1.0 - clamp( d / 0.5, 0.0, 1.0);
ALBEDO = COLOR.rgb;
ALPHA *= a * COLOR.a;
ALPHA_SCISSOR_THRESHOLD = 0.01;
}

View File

@ -0,0 +1 @@
uid://c2aykxlioyvu7

Binary file not shown.

View File

@ -0,0 +1,110 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public partial class EmberVFXData:ParticleVFXData
{
public static readonly CachedResource<ParticleProcessMaterial> EmberExplodeMaterial = new CachedResource<ParticleProcessMaterial>(
"res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Shared/ProcessMaterials/Ember-Explode.material"
);
public static readonly CachedResource<ShaderMaterial> EmberCircleMaterial = new CachedResource<ShaderMaterial>(
"res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Embers/Ember Circle.material"
);
public static readonly CachedResource<QuadMesh> QuadMesh = new CachedResource<QuadMesh>(
"res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Shared/Passes/Quad Draw Pass.tres"
);
public static readonly CachedResource<Gradient> GradientDefault = new CachedResource<Gradient>(
" res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Shared/Gradient-Default.tres"
);
public EmberVFXPreset preset;
public GpuParticles3D particles;
public PlayParticles playParticles;
public ParticleProcessMaterial processMaterial;
public Material material;
public void Assign( ParticleVFX vfx )
{
vfx._SetParticlesVFXData( this );
particles = vfx.GetOrCreateChild<GpuParticles3D>();
particles.DrawPass1 = QuadMesh.Get();
playParticles = vfx.GetOrCreateChild<PlayParticles>();
playParticles.particles3D = particles;
playParticles.usePooling = preset.usePooling;
processMaterial = EmberExplodeMaterial.GetDuplicate();
material = EmberCircleMaterial.GetDuplicate();
RJLog.Log( material, EmberCircleMaterial.GetDuplicate() );
particles.ProcessMaterial = processMaterial;
particles.MaterialOverride = material;
UpdateSettings( vfx );
}
public void Play( ParticleVFX vfx )
{
playParticles.Trigger();
}
public static FloatPropertyName meshScale = FloatPropertyName.Create( "meshScale" );
public void UpdateSettings( ParticleVFX vfx )
{
if ( preset == null || processMaterial == null )
{
return;
}
UpdateCoreSettings( preset, particles );
processMaterial.Color = preset.color * preset.colorIntensity;
var rampTexture = processMaterial.ColorRamp as GradientTexture1D;
if ( rampTexture == null )
{
rampTexture = new GradientTexture1D();
processMaterial.ColorRamp = rampTexture;
}
rampTexture.Gradient = preset.gradient ?? GradientDefault.Get();
preset.movement?.UpdateSettings( processMaterial, vfx );
preset.volume?.UpdateSettings( processMaterial, vfx );
meshScale.Set( material, preset.meshScale );
// processMaterial.ScaleMin = preset.meshScale;
// processMaterial.ScaleMax = preset.meshScale;
// processMaterial.InitialVelocityMin = preset.minVelocity + preset.velocityOffset;
// processMaterial.InitialVelocityMax = preset.maxVelocity + preset.velocityOffset;
// processMaterial.Gravity = preset.gravityDirection * preset.gravityMultiplier;
// processMaterial.DampingMin = preset.minDamping + preset.dampingOffset;
// processMaterial.DampingMax = preset.maxDamping + preset.dampingOffset;
}
#if TOOLS
public void DrawGizmo( ParticleVFX vfx, EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
preset.volume?.DrawGizmo( vfx, gizmoPlugin, gizmo );
}
#endif
}

View File

@ -0,0 +1 @@
uid://ycs4v67gaahy

View File

@ -0,0 +1,79 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public partial class EmberVFXPreset:ParticleVFXPreset
{
[Export]
public Color color;
[Export]
public float colorIntensity = 1f ;
[Export]
public Gradient gradient;
[Export]
public float meshScale = 1f;
[Export]
public ParticleVFXVolumeType volume = new SphereParticleVFXVolumeType();
[Export]
public ParticleVFXMovementType movement = new LinearParticleVFXMovementType();
public override void Initialize( ParticleVFX vfx )
{
vfx.DestroyChildren();
var data = new EmberVFXData();
data.preset = this;
data.Assign( vfx );
}
public override void Play( ParticleVFX vfx )
{
var data = vfx.GetDataAs<EmberVFXData>();
if ( data == null )
{
return;
}
data.Play( vfx );
}
public override void UpdateSettings( ParticleVFX vfx )
{
var data = vfx.GetDataAs<EmberVFXData>();
if ( data == null )
{
return;
}
if ( ! GodotObject.IsInstanceValid( data.particles ) )
{
Initialize( vfx );
}
data.UpdateSettings( vfx );
}
#if TOOLS
public override void DrawGizmo( ParticleVFX vfx, EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
var data = vfx.GetDataAs<EmberVFXData>();
data?.DrawGizmo( vfx, gizmoPlugin, gizmo );
}
#endif
}

View File

@ -0,0 +1 @@
uid://caku386ymhxnx

View File

@ -0,0 +1,72 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public partial class ParticleVFX:Node3D
#if TOOLS
, GizmoDrawer
#endif
{
ParticleVFXPreset _preset;
[Export]
public ParticleVFXPreset preset
{
get => _preset;
set
{
_preset = value;
if ( _preset != null )
{
_preset.Initialize( this );
}
}
}
[Export]
public bool edit = false;
#if TOOLS
public override void _Process( double delta )
{
if ( preset == null )
{
return;
}
if ( edit )
{
preset.UpdateSettings( this );
}
UpdateGizmos();
}
public void DrawGizmo( EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
gizmo.Clear();
preset?.DrawGizmo( this, gizmoPlugin, gizmo );
}
#endif
protected ParticleVFXData _data;
public T GetDataAs<T>() where T:ParticleVFXData
{
return _data as T;
}
public void _SetParticlesVFXData( ParticleVFXData data )
{
_data = data;
}
}

View File

@ -0,0 +1 @@
uid://citabajvt3rae

View File

@ -0,0 +1,38 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public abstract partial class ParticleVFXData:Resource
{
protected void UpdateCoreSettings( ParticleVFXPreset preset, GpuParticles3D particles )
{
if ( preset == null || particles == null )
{
return;
}
if ( particles.Amount != preset.maxParticles )
{
particles.Amount = preset.maxParticles;
}
if ( particles.AmountRatio != preset.maxParticlesAmount )
{
particles.AmountRatio = preset.maxParticlesAmount;
}
particles.Lifetime = preset.lifeTimeDuration;
particles.SpeedScale = preset.timeSpeed;
if ( particles.OneShot != preset.oneShot )
{
particles.OneShot = preset.oneShot;
}
particles.Explosiveness = preset.explosiveness;
}
}

View File

@ -0,0 +1 @@
uid://1028ohth6hae

View File

@ -0,0 +1,42 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public abstract partial class ParticleVFXPreset:Resource
{
[ExportGroup("Core")]
[Export]
public int maxParticles = 1000;
[Export( PropertyHint.Range, "0,1" )]
public float maxParticlesAmount = 1f;
[Export]
public float lifeTimeDuration = 0.3f;
[Export]
public float timeSpeed = 1f;
[Export]
public bool oneShot = false;
[Export]
public float explosiveness = 1f;
[Export]
public bool usePooling;
public abstract void Initialize( ParticleVFX vfx );
public abstract void Play( ParticleVFX vfx );
public abstract void UpdateSettings( ParticleVFX vfx );
#if TOOLS
public abstract void DrawGizmo( ParticleVFX vfx, EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo );
#endif
}

View File

@ -0,0 +1 @@
uid://dxoc2c4v20m0k

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
[gd_resource type="GradientTexture1D" format=3 uid="uid://dfg5cmvo1bqt6"]
[sub_resource type="Gradient" id="Gradient_7w6kc"]
offsets = PackedFloat32Array(1)
colors = PackedColorArray(1, 1, 1, 1)
[resource]
gradient = SubResource("Gradient_7w6kc")

View File

@ -0,0 +1,48 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public partial class LinearParticleVFXMovementType:ParticleVFXMovementType
{
[Export]
public float velocityOffset = 0f;
[Export]
public float minVelocity = 0f;
[Export]
public float maxVelocity = 1f;
[Export]
public Vector3 gravityDirection = Vector3.Down * 10;
[Export]
public float gravityMultiplier = 1f;
[Export]
public float dampingOffset = 0f;
[Export]
public float minDamping = 0f;
[Export]
public float maxDamping = 0f;
public override void UpdateSettings( ParticleProcessMaterial processMaterial, ParticleVFX vfx )
{
processMaterial.InitialVelocityMin = minVelocity + velocityOffset;
processMaterial.InitialVelocityMax = maxVelocity + velocityOffset;
processMaterial.Gravity = gravityDirection * gravityMultiplier;
processMaterial.DampingMin = minDamping + dampingOffset;
processMaterial.DampingMax = maxDamping + dampingOffset;
processMaterial.Spread = randomize * 180f;
}
}

View File

@ -0,0 +1 @@
uid://2u3g842w5bc8

View File

@ -0,0 +1,15 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public abstract partial class ParticleVFXMovementType:Resource
{
[Export( PropertyHint.Range, "0,1")]
public float randomize = 0f;
public abstract void UpdateSettings( ParticleProcessMaterial processMaterial, ParticleVFX vfx );
}

View File

@ -0,0 +1 @@
uid://llqsok1kjfeq

View File

@ -0,0 +1,3 @@
[gd_resource type="QuadMesh" format=3 uid="uid://b1gctyuqclkhb"]
[resource]

View File

@ -0,0 +1,35 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public partial class CircleParticleVFXVolumeType:ParticleVFXVolumeType
{
public static readonly CachedResource<Texture2D> CirclePoints = new CachedResource<Texture2D>(
"res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Shared/Emission Shapes/Circle/EmissionShape Circle Points.tres"
);
public static readonly CachedResource<Texture2D> CircleNormals = new CachedResource<Texture2D>(
"res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Shared/Emission Shapes/Circle/EmissionShape Circle Normals.tres"
);
public override void UpdateSettings( ParticleProcessMaterial processMaterial, ParticleVFX vfx )
{
processMaterial.EmissionPointTexture = CirclePoints.Get();
processMaterial.EmissionNormalTexture = CircleNormals.Get();
processMaterial.EmissionPointCount = 4096;
processMaterial.EmissionShapeScale = volumeScale * volumeScaleXYZ;;
}
#if TOOLS
public override void DrawGizmo( ParticleVFX vfx, EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
DrawShapeGizmo( VolumeShapeType.Circle, vfx, gizmoPlugin, gizmo );
}
#endif
}

View File

@ -0,0 +1 @@
uid://1e52ptw7nd7b

View File

@ -0,0 +1,35 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public partial class ConeParticleVFXVolumeType:ParticleVFXVolumeType
{
public static readonly CachedResource<Texture2D> ConePoints = new CachedResource<Texture2D>(
"res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Shared/Emission Shapes/Cone/EmissionShape Cone Points.tres"
);
public static readonly CachedResource<Texture2D> ConeNormals = new CachedResource<Texture2D>(
"res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Shared/Emission Shapes/Cone/EmissionShape Cone Normals.tres"
);
public override void UpdateSettings( ParticleProcessMaterial processMaterial, ParticleVFX vfx )
{
processMaterial.EmissionPointTexture = ConePoints.Get();
processMaterial.EmissionNormalTexture = ConeNormals.Get();
processMaterial.EmissionPointCount = 4096;
processMaterial.EmissionShapeScale = volumeScale * volumeScaleXYZ;
}
#if TOOLS
public override void DrawGizmo( ParticleVFX vfx, EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
DrawShapeGizmo( VolumeShapeType.Cone, vfx, gizmoPlugin, gizmo );
}
#endif
}

View File

@ -0,0 +1 @@
uid://cuxhk0wq4b414

View File

@ -0,0 +1,35 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public partial class DomeParticleVFXVolumeType:ParticleVFXVolumeType
{
public static readonly CachedResource<Texture2D> DomePoints = new CachedResource<Texture2D>(
"res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Shared/Emission Shapes/Dome/EmissionShape Dome Points.tres"
);
public static readonly CachedResource<Texture2D> DomeNormals = new CachedResource<Texture2D>(
"res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Shared/Emission Shapes/Dome/EmissionShape Dome Normals.tres"
);
public override void UpdateSettings( ParticleProcessMaterial processMaterial, ParticleVFX vfx )
{
processMaterial.EmissionPointTexture = DomePoints.Get();
processMaterial.EmissionNormalTexture = DomeNormals.Get();
processMaterial.EmissionPointCount = 4096;
processMaterial.EmissionShapeScale = volumeScale * volumeScaleXYZ;
}
#if TOOLS
public override void DrawGizmo( ParticleVFX vfx, EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
DrawShapeGizmo( VolumeShapeType.Dome, vfx, gizmoPlugin, gizmo );
}
#endif
}

View File

@ -0,0 +1 @@
uid://cn45dw32b13td

View File

@ -0,0 +1,57 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public partial class MixedParticleVFXVolumeType:ParticleVFXVolumeType
{
[Export]
public VolumeShapeType pointsType = VolumeShapeType.Sphere;
[Export]
public VolumeShapeType normalsType = VolumeShapeType.Sphere;
static Texture2D GetPoints( VolumeShapeType type )
{
switch ( type )
{
case VolumeShapeType.Sphere: return SphereParticleVFXVolumeType.SpherePoints.Get();
case VolumeShapeType.Dome: return DomeParticleVFXVolumeType.DomePoints.Get();
case VolumeShapeType.Circle: return CircleParticleVFXVolumeType.CirclePoints.Get();
case VolumeShapeType.Cone: return ConeParticleVFXVolumeType.ConePoints.Get();
}
return null;
}
static Texture2D GetNormals( VolumeShapeType type )
{
switch ( type )
{
case VolumeShapeType.Sphere: return SphereParticleVFXVolumeType.SphereNormals.Get();
case VolumeShapeType.Dome: return DomeParticleVFXVolumeType.DomeNormals.Get();
case VolumeShapeType.Circle: return CircleParticleVFXVolumeType.CircleNormals.Get();
case VolumeShapeType.Cone: return ConeParticleVFXVolumeType.ConeNormals.Get();
}
return null;
}
public override void UpdateSettings( ParticleProcessMaterial processMaterial, ParticleVFX vfx )
{
processMaterial.EmissionPointTexture = GetPoints( pointsType );
processMaterial.EmissionNormalTexture = GetNormals( normalsType );
processMaterial.EmissionPointCount = 4096;
processMaterial.EmissionShapeScale = volumeScale * volumeScaleXYZ;
}
#if TOOLS
public override void DrawGizmo( ParticleVFX vfx, EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
DrawShapeGizmo( pointsType, vfx, gizmoPlugin, gizmo );
}
#endif
}

View File

@ -0,0 +1 @@
uid://ct46je7oga3tb

View File

@ -0,0 +1,55 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public abstract partial class ParticleVFXVolumeType:Resource
{
[Export]
public Vector3 volumeScale = Vector3.One;
[Export]
public float volumeScaleXYZ = 1f;
public abstract void UpdateSettings( ParticleProcessMaterial processMaterial, ParticleVFX vfx );
#if TOOLS
public abstract void DrawGizmo( ParticleVFX vfx, EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo );
protected void DrawShapeGizmo( VolumeShapeType shapeType, ParticleVFX vfx, EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
var scale = volumeScale * volumeScaleXYZ;
var linePoints = new List<Vector3>();
switch ( shapeType )
{
case VolumeShapeType.Sphere:
Gizmos.AddSphere( linePoints, scale );
break;
case VolumeShapeType.Dome:
Gizmos.AddDome( linePoints, scale );
break;
case VolumeShapeType.Circle:
Gizmos.AddDisc( linePoints, scale );
break;
case VolumeShapeType.Cone:
Gizmos.AddCone( linePoints, scale );
break;
}
if ( linePoints.Count == 0 )
{
return;
}
var material = gizmoPlugin.GetMaterial( "main", gizmo );
gizmo.AddLines( linePoints.ToArray(), material, false );
}
#endif
}

View File

@ -0,0 +1 @@
uid://dejjrjhjedje5

View File

@ -0,0 +1,35 @@
using Godot;
using System.Collections.Generic;
using Rokojori.Extensions;
namespace Rokojori;
[Tool]
[GlobalClass ]
public partial class SphereParticleVFXVolumeType:ParticleVFXVolumeType
{
public static readonly CachedResource<Texture2D> SpherePoints = new CachedResource<Texture2D>(
"res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Shared/Emission Shapes/Sphere/EmissionShape Sphere Points.tres"
);
public static readonly CachedResource<Texture2D> SphereNormals = new CachedResource<Texture2D>(
"res://addons/rokojori_action_library/Runtime/VFX/ParticleVFX/Shared/Emission Shapes/Sphere/EmissionShape Sphere Normals.tres"
);
public override void UpdateSettings( ParticleProcessMaterial processMaterial, ParticleVFX vfx )
{
processMaterial.EmissionPointTexture = SpherePoints.Get();
processMaterial.EmissionNormalTexture = SphereNormals.Get();
processMaterial.EmissionPointCount = 4096;
processMaterial.EmissionShapeScale = volumeScale * volumeScaleXYZ;;
}
#if TOOLS
public override void DrawGizmo( ParticleVFX vfx, EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
{
DrawShapeGizmo( VolumeShapeType.Sphere, vfx, gizmoPlugin, gizmo );
}
#endif
}

View File

@ -0,0 +1 @@
uid://cjqsobh6nomul

View File

@ -0,0 +1,9 @@
namespace Rokojori;
public enum VolumeShapeType
{
Sphere,
Dome,
Circle,
Cone
}

View File

@ -0,0 +1 @@
uid://b8xvl8k82gkhy

View File

@ -6,12 +6,77 @@ using System.Collections.Generic;
namespace Rokojori
{
[Tool]
public class Gizmos
public class Gizmos
{
#if TOOLS
#if TOOLS
public static void AddArc( List<Vector3> linePoints, Vector3 center, Vector3 axisA, Vector3 axisB, float angleStart, float angleEnd, int segments )
{
var last = center + axisA * Mathf.Cos( angleStart ) + axisB * Mathf.Sin( angleStart );
#endif
for ( int i = 1; i <= segments; i++ )
{
var t = angleStart + ( angleEnd - angleStart ) * ( i / (float) segments );
var point = center + axisA * Mathf.Cos( t ) + axisB * Mathf.Sin( t );
linePoints.Add( last );
linePoints.Add( point );
last = point;
}
}
public static void AddCircle( List<Vector3> linePoints, Vector3 center, Vector3 axisA, Vector3 axisB, int segments = 32 )
{
AddArc( linePoints, center, axisA, axisB, 0, Mathf.Tau, segments );
}
public static void AddSphere( List<Vector3> linePoints, Vector3 scale, int segments = 32 )
{
var right = Vector3.Right * scale.X;
var up = Vector3.Up * scale.Y;
var forward = Vector3.Forward * scale.Z;
AddCircle( linePoints, Vector3.Zero, right, forward, segments );
AddCircle( linePoints, Vector3.Zero, right, up, segments );
AddCircle( linePoints, Vector3.Zero, forward, up, segments );
}
public static void AddDome( List<Vector3> linePoints, Vector3 scale, int segments = 32 )
{
var right = Vector3.Right * scale.X;
var up = Vector3.Up * scale.Y;
var forward = Vector3.Forward * scale.Z;
AddCircle( linePoints, Vector3.Zero, right, forward, segments );
AddArc( linePoints, Vector3.Zero, right, up, 0, Mathf.Pi, Mathf.Max( 2, segments / 2 ) );
AddArc( linePoints, Vector3.Zero, forward, up, 0, Mathf.Pi, Mathf.Max( 2, segments / 2 ) );
}
public static void AddDisc( List<Vector3> linePoints, Vector3 scale, int segments = 32 )
{
AddCircle( linePoints, Vector3.Zero, Vector3.Right * scale.X, Vector3.Forward * scale.Z, segments );
}
public static void AddCone( List<Vector3> linePoints, Vector3 scale, int segments = 32, int spokes = 8 )
{
var apex = Vector3.Zero;
var baseCenter = Vector3.Up * scale.Y;
var right = Vector3.Right * scale.X;
var forward = Vector3.Forward * scale.Z;
AddCircle( linePoints, baseCenter, right, forward, segments );
for ( int i = 0; i < spokes; i++ )
{
var t = i / (float) spokes * Mathf.Tau;
var basePoint = baseCenter + right * Mathf.Cos( t ) + forward * Mathf.Sin( t );
linePoints.Add( apex );
linePoints.Add( basePoint );
}
}
#endif
}
}