EmittersShapes/ConchplexSphereEmitter.cs
using Sandbox;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConchplexEmitters;
/// <summary>
/// Emits particles within a ring
/// </summary>
[Title( "Conchplex Sphere Emitter" ), Category( "Particles" ), Icon( "panorama_photosphere" )]
public sealed class ConchplexSphereEmitter : ConchplexEmitter
{
/// <summary>
/// 1 = spawns on edge, 0 = spawns in centre
/// </summary>
[Property, Group("Spawn Bias"), Order(0)]
[Range(0f, 1f), Step(0.01f)]
public float DistanceBias { get; set; } = 0.5f;
/// <summary>
/// Direction + intensity to bias particles to spawn towards
/// </summary>
[Property, Group("Spawn Bias"), Order(0)]
[Range(-1, 1)]
public Vector3 PositionBias { get; set; } = 0f;
[Property, Group("Spawn Bias"), Order(0)]
[Range(-1, 1)]
public Vector3 AxisBias { get; set; } = 0f;
/// <summary>
/// Particles only spawn on the axes marked
/// </summary>
[Property, Group("Spawn Bias"), Order(0)]
public AxisClampFlags AxisClamp { get; set; }
[Property]
[Range( 0f, 100f), Step(0.01f)]
public float Radius { get; set; } = 25f;
[Property]
[Range(0f, 1f), Step(0.01f)]
public float Thickness { get; set; } = 0f;
protected override void DrawGizmos()
{
if (Gizmo.IsSelected)
{
Gizmo.Draw.Color = Color.White.WithAlpha(0.5f);
Gizmo.GizmoDraw draw = Gizmo.Draw;
// Draw max bounds
Vector3 point = Vector3.Zero;
float radius = Radius;
int rings = 8;
draw.LineSphere(in point, in radius, in rings);
// Draw min bounds
Gizmo.Draw.Color = Color.White.WithAlpha(0.6f);
float minRadius = Radius * Thickness;
draw.LineSphere(point, minRadius, rings);
// Draw Distance Bias
Gizmo.Draw.Color = Color.Red.WithAlpha(0.5f);
float biasRadius = MathX.Lerp(minRadius, radius, DistanceBias);
draw.LineSphere(point, biasRadius, rings);
// draw Position Bias
if (PositionBias.IsNearlyZero()) return;
Gizmo.Draw.Color = Color.Red.WithAlpha(0.8f);
Vector3 biasClamped = Vector3.Clamp(PositionBias, new Vector3(-1f), new Vector3(1f)) * radius * 1.1f;
draw.Arrow(point, biasClamped, biasClamped.Length*0.1f, biasClamped.Length * 0.1f);
}
}
public override Particle Emit(ParticleEffect target )
{
Vector3 random = Vector3.Random;
Vector3 radius = Radius;
Vector3 radiusMin = Radius * Thickness;
// POSITION BIAS
var posBias = PositionBias.ClampLength(1);
var randomFlipChance = Random.Shared.Float();
random = new Vector3(
MathF.Abs(posBias.x) > randomFlipChance & Vector3.Dot(random, posBias) < 0f ? -random.x : random.x,
MathF.Abs(posBias.y) > randomFlipChance & Vector3.Dot(random, posBias) < 0f ? -random.y : random.y,
MathF.Abs(posBias.z) > randomFlipChance & Vector3.Dot(random, posBias) < 0f ? -random.z : random.z
);
random = Vector3.Lerp( random, posBias + random, posBias.Length);
// AXIS BIAS
random = ApplyAxisBias(random, AxisBias);
// MIN / MAX / TRUE RANDOM
var targetRandomLenth = MathX.Lerp( Radius * Thickness, Radius, random.Length );
random = random.Normal * targetRandomLenth;
var maxEdgePos = random.Normal * radius;
var minEdgePos = random.Normal * radiusMin;
// DISTANCE BIAS
if ( DistanceBias <= 0.5f )
{
float scaledBias = DistanceBias / 0.5f;
random = Vector3.Lerp( minEdgePos, random, scaledBias );
}
else
{
float scaledBias = (DistanceBias - 0.5f) / 0.5f;
random = Vector3.Lerp( random, maxEdgePos, scaledBias );
}
// APPLY SCALE / POSITION / ROTATION
var finalPos = ProcessClampPosition( random, AxisClamp);
finalPos = (finalPos * LocalScale * LocalRotation) + WorldPosition;
return target.Emit(finalPos, base.Delta );
}
}