mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-16 02:09:20 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
381 lines
8.8 KiB
C#
381 lines
8.8 KiB
C#
using System;
|
|
using System.Collections.Immutable;
|
|
|
|
namespace Sandbox.UI
|
|
{
|
|
[SkipHotload]
|
|
public partial struct PanelTransform
|
|
{
|
|
// Considering how many entries these are gonna have in reality
|
|
// we might be best to just hardcode like 6 entries
|
|
public ImmutableList<Entry> List;
|
|
|
|
public Matrix BuildTransform( float width, float height, Vector2 perspectiveOrigin )
|
|
{
|
|
var tx = Matrix.Identity;
|
|
|
|
if ( List == null )
|
|
return tx;
|
|
|
|
foreach ( var e in List )
|
|
{
|
|
Matrix m = Matrix.Identity;
|
|
if ( e.Type == EntryType.Perspective )
|
|
{
|
|
m *= Matrix.CreateTranslation( new Vector3( perspectiveOrigin, 0.0f ) );
|
|
m *= e.ToMatrix( width, height );
|
|
m *= Matrix.CreateTranslation( new Vector3( -perspectiveOrigin, 0.0f ) );
|
|
}
|
|
else
|
|
{
|
|
m = e.ToMatrix( width, height );
|
|
}
|
|
|
|
tx = m * tx;
|
|
}
|
|
|
|
return tx;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if this is empty.
|
|
/// </summary>
|
|
public bool IsEmpty()
|
|
{
|
|
return Entries == 0;
|
|
}
|
|
|
|
public readonly int Entries => List?.Count ?? 0;
|
|
|
|
readonly Entry GetEntry( int i )
|
|
{
|
|
if ( i >= Entries ) return new Entry { Type = EntryType.Invalid };
|
|
|
|
return List[i];
|
|
}
|
|
|
|
readonly Entry GetEntry( EntryType type )
|
|
{
|
|
if ( List == null )
|
|
return new Entry { Type = EntryType.Invalid };
|
|
|
|
foreach ( var e in List )
|
|
{
|
|
if ( e.Type == type )
|
|
return e;
|
|
}
|
|
|
|
return new Entry { Type = EntryType.Invalid };
|
|
}
|
|
|
|
public bool AddTranslate( Length? lengthX, Length? lengthY, Length? lengthZ = null )
|
|
{
|
|
if ( !lengthX.HasValue || !lengthY.HasValue )
|
|
return false;
|
|
|
|
if ( List == null )
|
|
List = ImmutableList.Create<Entry>();
|
|
|
|
List = List.Add( new Entry { Type = EntryType.Translate, X = lengthX.Value, Y = lengthY.Value, Z = lengthZ ?? new Length() } );
|
|
return true;
|
|
}
|
|
|
|
public bool AddTranslateX( Length? length )
|
|
{
|
|
if ( !length.HasValue )
|
|
return false;
|
|
|
|
if ( List == null )
|
|
List = ImmutableList.Create<Entry>();
|
|
|
|
List = List.Add( new Entry { Type = EntryType.Translate, X = length.Value } );
|
|
return true;
|
|
}
|
|
|
|
public bool AddTranslateY( Length? length )
|
|
{
|
|
if ( !length.HasValue )
|
|
return false;
|
|
|
|
if ( List == null )
|
|
List = ImmutableList.Create<Entry>();
|
|
|
|
List = List.Add( new Entry { Type = EntryType.Translate, Y = length.Value } );
|
|
return true;
|
|
}
|
|
|
|
public bool AddTranslateZ( Length? length )
|
|
{
|
|
if ( !length.HasValue )
|
|
return false;
|
|
|
|
if ( List == null )
|
|
List = ImmutableList.Create<Entry>();
|
|
|
|
List = List.Add( new Entry { Type = EntryType.Translate, Z = length.Value } );
|
|
return true;
|
|
}
|
|
|
|
public bool AddScale( float scale )
|
|
{
|
|
if ( List == null )
|
|
List = ImmutableList.Create<Entry>();
|
|
|
|
List = List.Add( new Entry { Type = EntryType.Scale, Data = Vector3.One * scale } );
|
|
return true;
|
|
}
|
|
|
|
public bool AddScale( Vector3 scale )
|
|
{
|
|
if ( List == null )
|
|
List = ImmutableList.Create<Entry>();
|
|
|
|
List = List.Add( new Entry { Type = EntryType.Scale, Data = scale } );
|
|
return true;
|
|
}
|
|
|
|
public bool AddSkew( float x, float y, float z )
|
|
{
|
|
if ( List == null )
|
|
List = ImmutableList.Create<Entry>();
|
|
|
|
List = List.Add( new Entry { Type = EntryType.Skew, Data = new Vector3( x, y, z ) } );
|
|
return true;
|
|
}
|
|
|
|
public bool AddRotation( float x, float y, float z )
|
|
{
|
|
if ( List == null )
|
|
List = ImmutableList.Create<Entry>();
|
|
|
|
List = List.Add( new Entry { Type = EntryType.Rotation, Data = new Vector3( x, y, z ) } );
|
|
return true;
|
|
}
|
|
|
|
public bool AddRotation( Vector3 angles ) => AddRotation( angles.x, angles.y, angles.z );
|
|
|
|
public bool AddMatrix3D( Matrix matrix )
|
|
{
|
|
if ( List == null )
|
|
List = ImmutableList.Create<Entry>();
|
|
|
|
List = List.Add( new Entry { Type = EntryType.Matrix, Matrix = matrix } );
|
|
return true;
|
|
}
|
|
|
|
public bool AddPerspective( Length d )
|
|
{
|
|
if ( List == null )
|
|
List = ImmutableList.Create<Entry>();
|
|
|
|
List = List.Add( new Entry { Type = EntryType.Perspective, X = d } );
|
|
return true;
|
|
}
|
|
|
|
internal static PanelTransform? Lerp( PanelTransform? a, PanelTransform? b, float delta, Vector2? dimensions = null )
|
|
{
|
|
var ma = a ?? new PanelTransform();
|
|
var mb = b ?? new PanelTransform();
|
|
var entries = Math.Max( ma.Entries, mb.Entries );
|
|
if ( entries <= 0 ) return new PanelTransform();
|
|
|
|
var builder = ImmutableList.CreateBuilder<Entry>();
|
|
|
|
for ( int i = 0; i < ma.Entries; i++ )
|
|
{
|
|
var from = ma.GetEntry( i );
|
|
var to = mb.GetEntry( from.Type );
|
|
|
|
if ( to.Type == EntryType.Invalid )
|
|
to = from.GetDefault();
|
|
|
|
if ( dimensions != null )
|
|
builder.Add( Entry.Lerp( from, to, delta, dimensions.Value ) );
|
|
else
|
|
builder.Add( Entry.Lerp( from, to, delta ) );
|
|
}
|
|
|
|
for ( int i = 0; i < mb.Entries; i++ )
|
|
{
|
|
var to = mb.GetEntry( i );
|
|
var from = ma.GetEntry( to.Type );
|
|
if ( from.Type != EntryType.Invalid )
|
|
continue;
|
|
|
|
if ( dimensions != null )
|
|
builder.Add( Entry.Lerp( to.GetDefault(), to, delta, dimensions.Value ) );
|
|
else
|
|
builder.Add( Entry.Lerp( to.GetDefault(), to, delta ) );
|
|
}
|
|
|
|
var o = new PanelTransform();
|
|
o.List = builder.ToImmutable();
|
|
|
|
return o;
|
|
}
|
|
|
|
internal readonly PanelTransform GetScaled( float scale )
|
|
{
|
|
if ( Entries <= 0 ) return new PanelTransform();
|
|
|
|
var builder = ImmutableList.CreateBuilder<Entry>();
|
|
|
|
for ( int i = 0; i < Entries; i++ )
|
|
{
|
|
var e = GetEntry( i );
|
|
|
|
Length.Scale( ref e.X, scale );
|
|
Length.Scale( ref e.Y, scale );
|
|
Length.Scale( ref e.Z, scale );
|
|
|
|
builder.Add( e );
|
|
}
|
|
|
|
var o = new PanelTransform();
|
|
o.List = builder.ToImmutable();
|
|
|
|
return o;
|
|
}
|
|
|
|
public override bool Equals( object obj )
|
|
{
|
|
return obj is PanelTransform transform &&
|
|
EqualityComparer<ImmutableList<Entry>>.Default.Equals( List, transform.List );
|
|
}
|
|
|
|
public readonly override int GetHashCode()
|
|
{
|
|
return HashCode.Combine( List );
|
|
}
|
|
|
|
public static bool operator ==( PanelTransform a, PanelTransform b )
|
|
{
|
|
return a.Equals( b );
|
|
}
|
|
|
|
public static bool operator !=( PanelTransform a, PanelTransform b )
|
|
{
|
|
return !a.Equals( b );
|
|
}
|
|
|
|
public struct Entry
|
|
{
|
|
public EntryType Type;
|
|
public Vector3 Data;
|
|
public Matrix Matrix;
|
|
|
|
public Length X;
|
|
public Length Y;
|
|
public Length Z;
|
|
|
|
internal static Entry Lerp( Entry a, Entry b, float delta )
|
|
{
|
|
var data = a.Type == EntryType.Rotation || a.Type == EntryType.Skew ?
|
|
Angles.Lerp( new Angles( a.Data ), new Angles( b.Data ), delta ).AsVector3() :
|
|
Vector3.Lerp( a.Data, b.Data, delta, false );
|
|
|
|
return new Entry
|
|
{
|
|
Type = a.Type,
|
|
Data = data,
|
|
Matrix = Matrix.Lerp( a.Matrix, b.Matrix, delta ),
|
|
X = Length.Lerp( a.X, b.X, delta ) ?? b.X,
|
|
Y = Length.Lerp( a.Y, b.Y, delta ) ?? b.Y,
|
|
Z = Length.Lerp( a.Z, b.Z, delta ) ?? b.Z,
|
|
};
|
|
}
|
|
|
|
internal static Entry Lerp( Entry a, Entry b, float delta, Vector2 dimensions )
|
|
{
|
|
var data = a.Type == EntryType.Rotation || a.Type == EntryType.Skew ?
|
|
Angles.Lerp( new Angles( a.Data ), new Angles( b.Data ), delta ).AsVector3() :
|
|
Vector3.Lerp( a.Data, b.Data, delta, false );
|
|
|
|
return new Entry
|
|
{
|
|
Type = a.Type,
|
|
Data = data,
|
|
Matrix = Matrix.Lerp( a.Matrix, b.Matrix, delta ),
|
|
X = Length.Lerp( a.X, b.X, delta, dimensions.x ) ?? b.X,
|
|
Y = Length.Lerp( a.Y, b.Y, delta, dimensions.y ) ?? b.Y,
|
|
Z = Length.Lerp( a.Z, b.Z, delta ) ?? b.Z,
|
|
};
|
|
}
|
|
|
|
public Matrix ToMatrix( float width, float height )
|
|
{
|
|
switch ( Type )
|
|
{
|
|
case EntryType.Rotation:
|
|
{
|
|
return Matrix.CreateRotation( Data );
|
|
}
|
|
|
|
case EntryType.Scale:
|
|
{
|
|
return Matrix.CreateScale( Data );
|
|
}
|
|
|
|
case EntryType.Translate:
|
|
{
|
|
return Matrix.CreateTranslation( new Vector3( X.GetPixels( width ), Y.GetPixels( height ), Z.GetPixels( 0 ) ) );
|
|
}
|
|
|
|
case EntryType.Skew:
|
|
{
|
|
var ax = MathF.Tan( Data.x.DegreeToRadian() );
|
|
var ay = MathF.Tan( Data.y.DegreeToRadian() );
|
|
|
|
return Matrix.CreateMatrix3D( [
|
|
1.0f, ay, 0.0f, 0.0f,
|
|
ax, 1.0f, 0.0f, 0.0f,
|
|
0.0f, 0.0f, 1.0f, 0.0f,
|
|
0.0f, 0.0f, 0.0f, 1.0f
|
|
] );
|
|
}
|
|
case EntryType.Matrix:
|
|
{
|
|
return Matrix;
|
|
}
|
|
case EntryType.Perspective:
|
|
{
|
|
return Matrix.CreateMatrix3D( [
|
|
1.0f, 0.0f, 0.0f, 0.0f,
|
|
0.0f, 1.0f, 0.0f, 0.0f,
|
|
0.0f, 0.0f, 1.0f, -1.0f / MathF.Max(X.GetPixels( width ), 1.0f),
|
|
0.0f, 0.0f, 0.0f, 1.0f
|
|
] );
|
|
}
|
|
default:
|
|
return Matrix.Identity;
|
|
}
|
|
}
|
|
|
|
public Entry GetDefault()
|
|
{
|
|
return new Entry
|
|
{
|
|
Type = this.Type,
|
|
Data = Vector3.One,
|
|
Matrix = Matrix.Identity,
|
|
X = new Length { Unit = X.Unit },
|
|
Y = new Length { Unit = Y.Unit },
|
|
Z = new Length { Unit = Z.Unit },
|
|
};
|
|
}
|
|
}
|
|
|
|
public enum EntryType
|
|
{
|
|
Invalid,
|
|
Rotation,
|
|
Scale,
|
|
Translate,
|
|
Skew,
|
|
Matrix,
|
|
Perspective,
|
|
}
|
|
|
|
}
|
|
}
|