Utility/BeamRenderer.cs
public sealed class BeamRenderer : Renderer, Component.ExecuteInEditor, Component.ITemporaryEffect
{
[Header( "Position" )]
[Property] public WorldPoint EndPoint { get; set; }
[Header( "Position" )]
[Property] public WorldPoint? MiddlePoint { get; set; }
[Header( "Rendering" )]
[Property] public Gradient LineColor { get; set; } = new Gradient( new Gradient.ColorFrame( 0, Color.White ), new Gradient.ColorFrame( 1, Color.White.WithAlpha( 0 ) ) );
[Property] public Curve LineWidth { get; set; } = new Curve( new Curve.Frame( 0, 2 ), new Curve.Frame( 1, 0 ) );
[Property] public SceneLineObject.CapStyle StartCap { get; set; }
[Property] public SceneLineObject.CapStyle EndCap { get; set; }
[Property] public float SectionLength { get; set; } = 1.0f;
[Header( "Randomness" )]
[Property, Feature( "Noise" )] public ParticleFloat RandomAmount { get; set; }
[Property, Feature( "Noise" )] public float RandomScale { get; set; } = 100;
[Header( "Wave" )]
[Property, Feature( "Noise" )] public ParticleFloat SinFrequency { get; set; } = 0.5f;
[Property, Feature( "Noise" )] public ParticleFloat SinAmplitude { get; set; } = 10;
[Property, Feature( "Noise" )] public ParticleFloat SinSpeed { get; set; } = 10;
[Property, Feature( "Noise" )] public ParticleFloat CosFrequency { get; set; } = 0.5f;
[Property, Feature( "Noise" )] public ParticleFloat CosAmplitude { get; set; } = 10;
[Property, Feature( "Noise" )] public ParticleFloat CosSpeed { get; set; } = 10;
[Property]
public Material Material { get; set; }
[Group( "Rendering" )]
[Property] public bool Opaque { get; set; } = true;
[Group( "Rendering" )]
[Property] public bool CastShadows { get; set; } = true;
bool ITemporaryEffect.IsActive => !_finished;
bool _finished = false;
SceneLineObject _so;
SceneLight _light;
protected override void OnEnabled()
{
_so = new SceneLineObject( Scene.SceneWorld );
_so.Transform = Transform.World;
_so.Material = Material;
}
protected override void OnDisabled()
{
_so?.Delete();
_so = null;
_light?.Delete();
_light = null;
}
protected override void OnPreRender()
{
if ( !_so.IsValid() )
return;
if ( SectionLength < 0.1f )
SectionLength = 0.1f;
var endPoint = EndPoint.Get();
var travel = endPoint - WorldPosition;
var maxlen = travel.Length;
_so.StartCap = StartCap;
_so.EndCap = EndCap;
_so.Opaque = Opaque;
_so.RenderingEnabled = true;
_so.Transform = WorldTransform;
_so.Flags.CastShadows = CastShadows;
_so.Attributes.Set( "BaseTexture", Texture.White );
_so.Attributes.SetCombo( "D_BLEND", Opaque ? 0 : 1 );
_so.StartLine();
var rot = Rotation.LookAt( travel );
var up = rot.Up;
var left = rot.Left;
var midPoint = (WorldPosition + endPoint) * 0.5f;
if ( MiddlePoint.HasValue )
{
midPoint = MiddlePoint.Value.Get();
}
// Generate points along the curve
int segments = Math.Max( 2, (int)(maxlen / SectionLength) );
for ( int i = 0; i <= segments; i++ )
{
float t = i / (float)segments;
var bezierPoint = Vector3.CubicBezier( WorldPosition, endPoint, midPoint, midPoint, t );
AddPoint( t, t * maxlen, bezierPoint, up, left );
}
_so.EndLine();
RenderOptions.Apply( _so );
}
void AddPoint( float delta, float beampos, Vector3 position, in Vector3 up, in Vector3 left )
{
var rand = RandomAmount.Evaluate( delta, Random.Shared.Float() ) * RandomScale;
position += Vector3.Random * rand;
{
var wave = MathF.Sin( (beampos * SinFrequency.Evaluate( delta, Random.Shared.Float() )) + Time.Now * SinSpeed.Evaluate( delta, Random.Shared.Float() ) ) * SinAmplitude.Evaluate( delta, Random.Shared.Float() );
position += wave * up;
}
{
var wave = MathF.Cos( (beampos * CosFrequency.Evaluate( delta, Random.Shared.Float() )) + Time.Now * CosSpeed.Evaluate( delta, Random.Shared.Float() ) ) * CosAmplitude.Evaluate( delta, Random.Shared.Float() );
position += wave * left;
}
_so.AddLinePoint( position, LineColor.Evaluate( delta ), LineWidth.Evaluate( delta ) );
}
}