mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Start of shader like vectors + added 16 bit float
This commit is contained in:
@@ -0,0 +1,459 @@
|
|||||||
|
using System;
|
||||||
|
namespace GlitchyEngine.Math.FancyMath;
|
||||||
|
|
||||||
|
static class FancyMath
|
||||||
|
{
|
||||||
|
/// Returns true if at least one of the components is true.
|
||||||
|
public static bool any(bool2 value) => value.X || value.Y;
|
||||||
|
/// Returns true if at least one of the components is true.
|
||||||
|
public static bool any(bool3 value) => value.X || value.Y || value.Z;
|
||||||
|
/// Returns true if at least one of the components is true.
|
||||||
|
public static bool any(bool4 value) => value.X || value.Y || value.Z || value.W;
|
||||||
|
|
||||||
|
/// Returns true if all of the components are true.
|
||||||
|
public static bool all(bool2 value) => value.X && value.Y;
|
||||||
|
/// Returns true if all of the components are true.
|
||||||
|
public static bool all(bool3 value) => value.X && value.Y && value.Z;
|
||||||
|
/// Returns true if all of the components are true.
|
||||||
|
public static bool all(bool4 value) => value.X && value.Y && value.Z && value.W;
|
||||||
|
|
||||||
|
#region abs
|
||||||
|
|
||||||
|
public static float2 abs(float2 value)
|
||||||
|
{
|
||||||
|
return float2(Math.Abs(value.X), Math.Abs(value.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float3 abs(float3 value)
|
||||||
|
{
|
||||||
|
return float3(Math.Abs(value.X), Math.Abs(value.Y), Math.Abs(value.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float4 abs(float4 value)
|
||||||
|
{
|
||||||
|
return float4(Math.Abs(value.X), Math.Abs(value.Y), Math.Abs(value.Z), Math.Abs(value.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int2 abs(int2 value)
|
||||||
|
{
|
||||||
|
return int2(Math.Abs(value.X), Math.Abs(value.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int3 abs(int3 value)
|
||||||
|
{
|
||||||
|
return int3(Math.Abs(value.X), Math.Abs(value.Y), Math.Abs(value.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int4 abs(int4 value)
|
||||||
|
{
|
||||||
|
return int4(Math.Abs(value.X), Math.Abs(value.Y), Math.Abs(value.Z), Math.Abs(value.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ceil / floor
|
||||||
|
|
||||||
|
public static float2 ceil(float2 value)
|
||||||
|
{
|
||||||
|
return float2(Math.Ceiling(value.X), Math.Ceiling(value.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float3 ceil(float3 value)
|
||||||
|
{
|
||||||
|
return float3(Math.Ceiling(value.X), Math.Ceiling(value.Y), Math.Ceiling(value.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float4 ceil(float4 value)
|
||||||
|
{
|
||||||
|
return float4(Math.Ceiling(value.X), Math.Ceiling(value.Y), Math.Ceiling(value.Z), Math.Ceiling(value.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float2 floor(float2 value)
|
||||||
|
{
|
||||||
|
return float2(Math.Floor(value.X), Math.Floor(value.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float3 floor(float3 value)
|
||||||
|
{
|
||||||
|
return float3(Math.Floor(value.X), Math.Floor(value.Y), Math.Floor(value.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float4 floor(float4 value)
|
||||||
|
{
|
||||||
|
return float4(Math.Floor(value.X), Math.Floor(value.Y), Math.Floor(value.Z), Math.Floor(value.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Clamp
|
||||||
|
|
||||||
|
public static float2 clamp(float2 value, float2 min, float2 max)
|
||||||
|
{
|
||||||
|
return float2(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float3 clamp(float3 value, float3 min, float3 max)
|
||||||
|
{
|
||||||
|
return float3(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y), Math.Clamp(value.Z, min.Z, max.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float4 clamp(float4 value, float4 min, float4 max)
|
||||||
|
{
|
||||||
|
return float4(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y), Math.Clamp(value.Z, min.Z, max.Z), Math.Clamp(value.W, min.W, max.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int2 clamp(int2 value, int2 min, int2 max)
|
||||||
|
{
|
||||||
|
return int2(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int3 clamp(int3 value, int3 min, int3 max)
|
||||||
|
{
|
||||||
|
return int3(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y), Math.Clamp(value.Z, min.Z, max.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int4 clamp(int4 value, int4 min, int4 max)
|
||||||
|
{
|
||||||
|
return int4(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y), Math.Clamp(value.Z, min.Z, max.Z), Math.Clamp(value.W, min.W, max.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lerp
|
||||||
|
|
||||||
|
/// Performs a linear interpolation.
|
||||||
|
// @param x The first vector value.
|
||||||
|
// @param y The second vector value.
|
||||||
|
// @param y A value that linearly interpolates between x and y.
|
||||||
|
public static float2 lerp(float2 x, float2 y, float s)
|
||||||
|
{
|
||||||
|
return x + s * (y - x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs a linear interpolation.
|
||||||
|
// @param x The first vector value.
|
||||||
|
// @param y The second vector value.
|
||||||
|
// @param y A value that linearly interpolates between x and y.
|
||||||
|
public static float3 lerp(float3 x, float3 y, float s)
|
||||||
|
{
|
||||||
|
return x + s * (y - x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs a linear interpolation.
|
||||||
|
// @param x The first vector value.
|
||||||
|
// @param y The second vector value.
|
||||||
|
// @param y A value that linearly interpolates between x and y.
|
||||||
|
public static float4 lerp(float4 x, float4 y, float s)
|
||||||
|
{
|
||||||
|
return x + s * (y - x);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// log, log10, log2
|
||||||
|
|
||||||
|
#region min / max
|
||||||
|
|
||||||
|
public static float2 min(float2 x, float2 y)
|
||||||
|
{
|
||||||
|
return float2(Math.Min(x.X, y.X), Math.Min(x.Y, y.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float3 min(float3 x, float3 y)
|
||||||
|
{
|
||||||
|
return float3(Math.Min(x.X, y.X), Math.Min(x.Y, y.Y), Math.Min(x.Z, y.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float4 min(float4 x, float4 y)
|
||||||
|
{
|
||||||
|
return float4(Math.Min(x.X, y.X), Math.Min(x.Y, y.Y), Math.Min(x.Z, y.Z), Math.Min(x.W, y.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float2 max(float2 x, float2 y)
|
||||||
|
{
|
||||||
|
return float2(Math.Max(x.X, y.X), Math.Max(x.Y, y.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float3 max(float3 x, float3 y)
|
||||||
|
{
|
||||||
|
return float3(Math.Max(x.X, y.X), Math.Max(x.Y, y.Y), Math.Max(x.Z, y.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float4 max(float4 x, float4 y)
|
||||||
|
{
|
||||||
|
return float4(Math.Max(x.X, y.X), Math.Max(x.Y, y.Y), Math.Max(x.Z, y.Z), Math.Max(x.W, y.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// mul
|
||||||
|
|
||||||
|
// normalize
|
||||||
|
|
||||||
|
// pow
|
||||||
|
|
||||||
|
// rcp??? (reciprocal)
|
||||||
|
|
||||||
|
// reflect and refract?
|
||||||
|
|
||||||
|
// round
|
||||||
|
|
||||||
|
// rsqrt?
|
||||||
|
|
||||||
|
// sqrt
|
||||||
|
|
||||||
|
// saturate
|
||||||
|
|
||||||
|
// sign
|
||||||
|
|
||||||
|
// step and smoothstep
|
||||||
|
|
||||||
|
// transpose
|
||||||
|
|
||||||
|
#region exp
|
||||||
|
|
||||||
|
/// Returns the base-e exponential, or e^x, of the specified value.
|
||||||
|
public static float2 exp(float2 x)
|
||||||
|
{
|
||||||
|
return float2(Math.Exp(x.X), Math.Exp(x.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the base-e exponential, or e^x, of the specified value.
|
||||||
|
public static float3 exp(float3 x)
|
||||||
|
{
|
||||||
|
return float3(Math.Exp(x.X), Math.Exp(x.Y), Math.Exp(x.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the base-e exponential, or e^x, of the specified value.
|
||||||
|
public static float4 exp(float4 x)
|
||||||
|
{
|
||||||
|
return float4(Math.Exp(x.X), Math.Exp(x.Y), Math.Exp(x.Z), Math.Exp(x.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exp2?
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region modf / frac / trunc
|
||||||
|
|
||||||
|
// Splits the value x into fractional and integer parts, each of which has the same sign as x.
|
||||||
|
public static float2 modf(float2 x, out float2 integerPart)
|
||||||
|
{
|
||||||
|
float2 fracPart;
|
||||||
|
|
||||||
|
fracPart.X = Math.[Friend]modff(x.X, out integerPart.X);
|
||||||
|
fracPart.Y = Math.[Friend]modff(x.Y, out integerPart.Y);
|
||||||
|
|
||||||
|
return fracPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Splits the value x into fractional and integer parts, each of which has the same sign as x.
|
||||||
|
public static float3 modf(float3 x, out float3 integerPart)
|
||||||
|
{
|
||||||
|
float3 fracPart;
|
||||||
|
|
||||||
|
fracPart.X = Math.[Friend]modff(x.X, out integerPart.X);
|
||||||
|
fracPart.Y = Math.[Friend]modff(x.Y, out integerPart.Y);
|
||||||
|
fracPart.Z = Math.[Friend]modff(x.Z, out integerPart.Z);
|
||||||
|
|
||||||
|
return fracPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Splits the value x into fractional and integer parts, each of which has the same sign as x.
|
||||||
|
public static float4 modf(float4 x, out float4 integerPart)
|
||||||
|
{
|
||||||
|
float4 fracPart;
|
||||||
|
|
||||||
|
fracPart.X = Math.[Friend]modff(x.X, out integerPart.X);
|
||||||
|
fracPart.Y = Math.[Friend]modff(x.Y, out integerPart.Y);
|
||||||
|
fracPart.Z = Math.[Friend]modff(x.Z, out integerPart.Z);
|
||||||
|
fracPart.W = Math.[Friend]modff(x.W, out integerPart.W);
|
||||||
|
|
||||||
|
return fracPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns the fractional (or decimal) part of x; which is greater than or equal to 0 and less than 1.
|
||||||
|
public static float2 frac(float2 x) => [Inline]modf(x, let _);
|
||||||
|
|
||||||
|
// Returns the fractional (or decimal) part of x; which is greater than or equal to 0 and less than 1.
|
||||||
|
public static float3 frac(float3 x) => [Inline]modf(x, let _);
|
||||||
|
|
||||||
|
// Returns the fractional (or decimal) part of x; which is greater than or equal to 0 and less than 1.
|
||||||
|
public static float4 frac(float4 x) => [Inline]modf(x, let _);
|
||||||
|
|
||||||
|
|
||||||
|
// Truncates a floating-point value to the integer component.
|
||||||
|
public static float2 trunc(float2 x)
|
||||||
|
{
|
||||||
|
return float2(Math.Truncate(x.X), Math.Truncate(x.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncates a floating-point value to the integer component.
|
||||||
|
public static float3 trunc(float3 x)
|
||||||
|
{
|
||||||
|
return float3(Math.Truncate(x.X), Math.Truncate(x.Y), Math.Truncate(x.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncates a floating-point value to the integer component.
|
||||||
|
public static float4 trunc(float4 x)
|
||||||
|
{
|
||||||
|
return float4(Math.Truncate(x.X), Math.Truncate(x.Y), Math.Truncate(x.Z), Math.Truncate(x.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region infinity and nan check
|
||||||
|
|
||||||
|
/// Determines if the specified floating-point value is finite.
|
||||||
|
public static bool2 isfinite(float2 value)
|
||||||
|
{
|
||||||
|
return bool2(value.X.IsFinite, value.Y.IsFinite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the specified floating-point value is finite.
|
||||||
|
public static bool3 isfinite(float3 value)
|
||||||
|
{
|
||||||
|
return bool3(value.X.IsFinite, value.Y.IsFinite, value.Z.IsFinite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the specified floating-point value is finite.
|
||||||
|
public static bool4 isfinite(float4 value)
|
||||||
|
{
|
||||||
|
return bool4(value.X.IsFinite, value.Y.IsFinite, value.Z.IsFinite, value.W.IsFinite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the specified value is infinite.
|
||||||
|
public static bool2 isinf(float2 value)
|
||||||
|
{
|
||||||
|
return bool2(value.X.IsInfinity, value.Y.IsInfinity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the specified value is infinite.
|
||||||
|
public static bool3 isinf(float3 value)
|
||||||
|
{
|
||||||
|
return bool3(value.X.IsInfinity, value.Y.IsInfinity, value.Z.IsInfinity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the specified value is infinite.
|
||||||
|
public static bool4 isinf(float4 value)
|
||||||
|
{
|
||||||
|
return bool4(value.X.IsInfinity, value.Y.IsInfinity, value.Z.IsInfinity, value.W.IsInfinity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the specified value is infinite.
|
||||||
|
public static bool2 isnan(float2 value)
|
||||||
|
{
|
||||||
|
return bool2(value.X.IsNaN, value.Y.IsNaN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the specified value is infinite.
|
||||||
|
public static bool3 isnan(float3 value)
|
||||||
|
{
|
||||||
|
return bool3(value.X.IsNaN, value.Y.IsNaN, value.Z.IsNaN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the specified value is infinite.
|
||||||
|
public static bool4 isnan(float4 value)
|
||||||
|
{
|
||||||
|
return bool4(value.X.IsNaN, value.Y.IsNaN, value.Z.IsNaN, value.W.IsNaN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region dot
|
||||||
|
|
||||||
|
public static float dot(float2 left, float2 right)
|
||||||
|
{
|
||||||
|
return left.X * right.X + left.Y * right.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float dot(float3 left, float3 right)
|
||||||
|
{
|
||||||
|
return left.X * right.X + left.Y * right.Y + left.Z * right.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float dot(float4 left, float4 right)
|
||||||
|
{
|
||||||
|
return left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int dot(int2 left, int2 right)
|
||||||
|
{
|
||||||
|
return left.X * right.X + left.Y * right.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int dot(int3 left, int3 right)
|
||||||
|
{
|
||||||
|
return left.X * right.X + left.Y * right.Y + left.Z * right.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int dot(int4 left, int4 right)
|
||||||
|
{
|
||||||
|
return left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region lengthSq / length / DistanceSq / Distance
|
||||||
|
|
||||||
|
public static float lengthSq(float2 value) => dot(value, value);
|
||||||
|
|
||||||
|
public static float lengthSq(float3 value) => dot(value, value);
|
||||||
|
|
||||||
|
public static float lengthSq(float4 value) => dot(value, value);
|
||||||
|
|
||||||
|
public static int lengthSq(int2 value) => dot(value, value);
|
||||||
|
|
||||||
|
public static int lengthSq(int3 value) => dot(value, value);
|
||||||
|
|
||||||
|
public static int lengthSq(int4 value) => dot(value, value);
|
||||||
|
|
||||||
|
public static float length(float2 value) => Math.Sqrt(lengthSq(value));
|
||||||
|
|
||||||
|
public static float length(float3 value) => Math.Sqrt(lengthSq(value));
|
||||||
|
|
||||||
|
public static float length(float4 value) => Math.Sqrt(lengthSq(value));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static float distanceSq(float2 left, float2 right) => dot(left, right);
|
||||||
|
|
||||||
|
public static float distanceSq(float3 left, float3 right) => dot(left, right);
|
||||||
|
|
||||||
|
public static float distanceSq(float4 left, float4 right) => dot(left, right);
|
||||||
|
|
||||||
|
public static float distance(float2 left, float2 right) => Math.Sqrt(distanceSq(left, right));
|
||||||
|
|
||||||
|
public static float distance(float3 left, float3 right) => Math.Sqrt(distanceSq(left, right));
|
||||||
|
|
||||||
|
public static float distance(float4 left, float4 right) => Math.Sqrt(distanceSq(left, right));
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public static float3 cross(float3 left, float3 right)
|
||||||
|
{
|
||||||
|
return float3(
|
||||||
|
left.Y * right.Z - left.Z * right.Y,
|
||||||
|
left.Z * right.X - left.X * right.Z,
|
||||||
|
left.X * right.Y - left.Y * right.X);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Degrees / Radians
|
||||||
|
|
||||||
|
public static float2 toDegrees(float2 radians) => radians * MathHelper.RadToDeg;
|
||||||
|
public static float3 toDegrees(float3 radians) => radians * MathHelper.RadToDeg;
|
||||||
|
public static float4 toDegrees(float4 radians) => radians * MathHelper.RadToDeg;
|
||||||
|
|
||||||
|
public static float2 toRadians(float2 degrees) => degrees * MathHelper.DegToRad;
|
||||||
|
public static float3 toRadians(float3 degrees) => degrees * MathHelper.DegToRad;
|
||||||
|
public static float4 toRadians(float4 degrees) => degrees * MathHelper.DegToRad;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// transpose und determinante für Matrizen
|
||||||
|
|
||||||
|
// sin, cos, tan, asin, acos, atan, atan2, cosh, sinh, tanh
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,416 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Math.FancyMath;
|
||||||
|
|
||||||
|
[AttributeUsage(.Struct | .Class)]
|
||||||
|
struct VectorAttribute<T, ComponentCount> : Attribute, IComptimeTypeApply where ComponentCount : const int
|
||||||
|
{
|
||||||
|
public const String[4] ComponentNames = .("X", "Y", "Z", "W");
|
||||||
|
public const String[4] LowerComponentNames = .("x", "y", "z", "w");
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
public void ApplyToType(Type type)
|
||||||
|
{
|
||||||
|
GenerateFields(type);
|
||||||
|
|
||||||
|
GenerateSingleToVectorCast(type);
|
||||||
|
|
||||||
|
GenerateConstructors(type);
|
||||||
|
|
||||||
|
GenerateEqualityOperators(type);
|
||||||
|
|
||||||
|
GenerateArrayAccess(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
private void GenerateFields(Type type)
|
||||||
|
{
|
||||||
|
String fields = scope $"public {typeof(T)} ";
|
||||||
|
|
||||||
|
for (int i < ComponentCount)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
fields.Append(", ");
|
||||||
|
|
||||||
|
fields.Append(ComponentNames[i]);
|
||||||
|
}
|
||||||
|
fields.Append(";\n\n");
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
private void GenerateSingleToVectorCast(Type type)
|
||||||
|
{
|
||||||
|
String constructorBody = scope String();
|
||||||
|
|
||||||
|
for (int i < ComponentCount)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
constructorBody.Append(", ");
|
||||||
|
|
||||||
|
constructorBody.Append("value");
|
||||||
|
}
|
||||||
|
|
||||||
|
String cast = scope $"""
|
||||||
|
public static implicit operator {type}({typeof(T)} value)
|
||||||
|
{{
|
||||||
|
return {type}({constructorBody});
|
||||||
|
}}
|
||||||
|
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, cast);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
private void GenerateConstructors(Type type)
|
||||||
|
{
|
||||||
|
// Default constructor
|
||||||
|
//Compiler.EmitTypeBody(type, "public this() => this = default;\n\n");
|
||||||
|
|
||||||
|
// Single constructor
|
||||||
|
GenerateSingleConstructor(type);
|
||||||
|
|
||||||
|
if (ComponentCount == 3)
|
||||||
|
{
|
||||||
|
GenerateVector3Constructors(type);
|
||||||
|
}
|
||||||
|
else if (ComponentCount == 4)
|
||||||
|
{
|
||||||
|
GenerateVector4Constructors(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
private void GenerateSingleConstructor(Type type)
|
||||||
|
{
|
||||||
|
String parameters = scope .();
|
||||||
|
|
||||||
|
for (int i < ComponentCount)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
parameters.Append(", ");
|
||||||
|
|
||||||
|
parameters.AppendF($"{typeof(T)} {LowerComponentNames[i]}");
|
||||||
|
}
|
||||||
|
|
||||||
|
String body = scope .();
|
||||||
|
|
||||||
|
for (int i < ComponentCount)
|
||||||
|
{
|
||||||
|
body.AppendF($"\t{ComponentNames[i]} = {LowerComponentNames[i]};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
String constructor = scope $"""
|
||||||
|
public this({parameters})
|
||||||
|
{{
|
||||||
|
{body}
|
||||||
|
}}
|
||||||
|
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, constructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
private void GenerateVector3Constructors(Type type)
|
||||||
|
{
|
||||||
|
String baseName = type.GetName(.. scope String());
|
||||||
|
|
||||||
|
// Remove number from name
|
||||||
|
baseName.RemoveFromEnd(1);
|
||||||
|
|
||||||
|
String constructor1 = scope $"""
|
||||||
|
public this({baseName}2 xy, {typeof(T)} z)
|
||||||
|
{{
|
||||||
|
X = xy.X;
|
||||||
|
Y = xy.Y;
|
||||||
|
Z = z;
|
||||||
|
}}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, constructor1);
|
||||||
|
|
||||||
|
String constructor2 = scope $"""
|
||||||
|
public this({typeof(T)} x, {baseName}2 yz)
|
||||||
|
{{
|
||||||
|
X = x;
|
||||||
|
Y = yz.X;
|
||||||
|
Z = yz.Y;
|
||||||
|
}}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, constructor2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
private void GenerateVector4Constructors(Type type)
|
||||||
|
{
|
||||||
|
String baseName = type.GetName(.. scope String());
|
||||||
|
|
||||||
|
// Remove number from name
|
||||||
|
baseName.RemoveFromEnd(1);
|
||||||
|
|
||||||
|
String constructor1 = scope $"""
|
||||||
|
public this({baseName}2 xy, {typeof(T)} z, {typeof(T)} w)
|
||||||
|
{{
|
||||||
|
X = xy.X;
|
||||||
|
Y = xy.Y;
|
||||||
|
Z = z;
|
||||||
|
W = w;
|
||||||
|
}}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, constructor1);
|
||||||
|
|
||||||
|
String constructor2 = scope $"""
|
||||||
|
public this({typeof(T)} x, {baseName}2 yz, {typeof(T)} w)
|
||||||
|
{{
|
||||||
|
X = x;
|
||||||
|
Y = yz.X;
|
||||||
|
Z = yz.Y;
|
||||||
|
W = w;
|
||||||
|
}}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, constructor2);
|
||||||
|
|
||||||
|
String constructor3 = scope $"""
|
||||||
|
public this({typeof(T)} x, {typeof(T)} y, {baseName}2 zw)
|
||||||
|
{{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Z = zw.X;
|
||||||
|
W = zw.Y;
|
||||||
|
}}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, constructor3);
|
||||||
|
|
||||||
|
|
||||||
|
String constructor4 = scope $"""
|
||||||
|
public this({baseName}3 xyz, {typeof(T)} w)
|
||||||
|
{{
|
||||||
|
X = xyz.X;
|
||||||
|
Y = xyz.Y;
|
||||||
|
Z = xyz.Z;
|
||||||
|
W = w;
|
||||||
|
}}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, constructor4);
|
||||||
|
|
||||||
|
String constructor5 = scope $"""
|
||||||
|
public this({typeof(T)} x, {baseName}3 yzw)
|
||||||
|
{{
|
||||||
|
X = x;
|
||||||
|
Y = yzw.X;
|
||||||
|
Z = yzw.Y;
|
||||||
|
W = yzw.Z;
|
||||||
|
}}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, constructor5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Constructors
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
private void GenerateEqualityOperators(Type type)
|
||||||
|
{
|
||||||
|
GenerateComparison(type, "==");
|
||||||
|
GenerateComparison(type, "!=");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
public static void GenerateComparison(Type type, String op)
|
||||||
|
{
|
||||||
|
String boolConstructor = scope .();
|
||||||
|
|
||||||
|
for (int i < ComponentCount)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
boolConstructor.Append(", ");
|
||||||
|
|
||||||
|
boolConstructor.AppendF($"left.{ComponentNames[i]} {op} right.{ComponentNames[i]}");
|
||||||
|
}
|
||||||
|
|
||||||
|
String typeName = type.GetName(.. scope String());
|
||||||
|
|
||||||
|
String func = scope $"""
|
||||||
|
public static bool{ComponentCount} operator{op}({typeName} left, {typeName} right)
|
||||||
|
{{
|
||||||
|
return bool{ComponentCount}({boolConstructor});
|
||||||
|
}}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
private static void GenerateArrayAccess(Type type)
|
||||||
|
{
|
||||||
|
String arrayAccess = scope $"""
|
||||||
|
public {typeof(T)} this[int index]
|
||||||
|
{{
|
||||||
|
get
|
||||||
|
{{
|
||||||
|
if(index < 0 || index >= {ComponentCount})
|
||||||
|
System.Internal.ThrowIndexOutOfRange(1);
|
||||||
|
|
||||||
|
#unwarn
|
||||||
|
return (&X)[index];
|
||||||
|
}}
|
||||||
|
set mut
|
||||||
|
{{
|
||||||
|
if(index < 0 || index >= {ComponentCount})
|
||||||
|
System.Internal.ThrowIndexOutOfRange(1);
|
||||||
|
|
||||||
|
#unwarn
|
||||||
|
(&X)[index] = value;
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, arrayAccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[AttributeUsage(.Struct | .Class)]
|
||||||
|
struct ComparableVectorAttribute<T, ComponentCount> : Attribute, IComptimeTypeApply where ComponentCount : const int
|
||||||
|
{
|
||||||
|
[Comptime]
|
||||||
|
public void ApplyToType(Type type)
|
||||||
|
{
|
||||||
|
VectorAttribute<T, ComponentCount>.GenerateComparison(type, ">");
|
||||||
|
VectorAttribute<T, ComponentCount>.GenerateComparison(type, ">=");
|
||||||
|
VectorAttribute<T, ComponentCount>.GenerateComparison(type, "<");
|
||||||
|
VectorAttribute<T, ComponentCount>.GenerateComparison(type, "<=");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[AttributeUsage(.Struct | .Class)]
|
||||||
|
struct VectorMathAttribute<T, ComponentCount> : Attribute, IComptimeTypeApply where ComponentCount : const int
|
||||||
|
{
|
||||||
|
public const String[4] ComponentNames = .("X", "Y", "Z", "W");
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
public void ApplyToType(Type type)
|
||||||
|
{
|
||||||
|
GenerateUnaryOperatorOverloads(type);
|
||||||
|
|
||||||
|
GenerateOperatorOverloads(type, "+");
|
||||||
|
GenerateOperatorOverloads(type, "-");
|
||||||
|
GenerateOperatorOverloads(type, "*");
|
||||||
|
GenerateOperatorOverloads(type, "/");
|
||||||
|
GenerateOperatorOverloads(type, "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
public static void GenerateUnaryOperatorOverloads(Type type)
|
||||||
|
{
|
||||||
|
String typeName = type.GetName(.. scope String());
|
||||||
|
|
||||||
|
String unaryAdd = scope $"""
|
||||||
|
public static {typeName} operator+({typeName} value) => value;
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, unaryAdd);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String resultArguments = scope .();
|
||||||
|
|
||||||
|
for (int i < ComponentCount)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
resultArguments.Append(", ");
|
||||||
|
|
||||||
|
resultArguments.AppendF($"-value.{ComponentNames[i]}");
|
||||||
|
}
|
||||||
|
|
||||||
|
String unaryMinus = scope $"""
|
||||||
|
public static {typeName} operator-({typeName} value)
|
||||||
|
{{
|
||||||
|
return {typeName}({resultArguments});
|
||||||
|
}}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, unaryMinus);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
public static void GenerateOperatorOverloads(Type type, String op)
|
||||||
|
{
|
||||||
|
String typeName = type.GetName(.. scope String());
|
||||||
|
String componentTypeName = typeof(T).GetName(.. scope String());
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
void EmitOperatorOverload(String leftType, String rightType, String resultArguments)
|
||||||
|
{
|
||||||
|
String func = scope $"""
|
||||||
|
public static {typeName} operator{op}({leftType} left, {rightType} right)
|
||||||
|
{{
|
||||||
|
return {typeName}({resultArguments});
|
||||||
|
}}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vector + Vector
|
||||||
|
String resultArguments = scope .();
|
||||||
|
|
||||||
|
for (int i < ComponentCount)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
resultArguments.Append(", ");
|
||||||
|
|
||||||
|
resultArguments.AppendF($"left.{ComponentNames[i]} {op} right.{ComponentNames[i]}");
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitOperatorOverload(typeName, typeName, resultArguments);
|
||||||
|
|
||||||
|
// Vector + Scalar
|
||||||
|
resultArguments.Clear();
|
||||||
|
|
||||||
|
for (int i < ComponentCount)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
resultArguments.Append(", ");
|
||||||
|
|
||||||
|
resultArguments.AppendF($"left.{ComponentNames[i]} {op} right");
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitOperatorOverload(typeName, componentTypeName, resultArguments);
|
||||||
|
|
||||||
|
// Scalar + Vector
|
||||||
|
resultArguments.Clear();
|
||||||
|
|
||||||
|
for (int i < ComponentCount)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
resultArguments.Append(", ");
|
||||||
|
|
||||||
|
resultArguments.AppendF($"left {op} right.{ComponentNames[i]}");
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitOperatorOverload(componentTypeName, typeName, resultArguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using Bon;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Math.FancyMath;
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<bool, 2>]
|
||||||
|
struct bool2
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<bool, 3>]
|
||||||
|
struct bool3
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<bool, 4>]
|
||||||
|
struct bool4
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using Bon;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Math.FancyMath;
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<float, 2>]
|
||||||
|
[ComparableVector<float, 2>]
|
||||||
|
[VectorMath<float, 2>]
|
||||||
|
[SwizzleVector(2, "GlitchyEngine.Math.FancyMath.float")]
|
||||||
|
public struct float2
|
||||||
|
{
|
||||||
|
public static implicit operator int2(float2 value)
|
||||||
|
{
|
||||||
|
return int2((int32)value.X, (int32)value.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator half2(float2 value)
|
||||||
|
{
|
||||||
|
return half2((half)value.X, (half)value.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<float, 3>]
|
||||||
|
[ComparableVector<float, 3>]
|
||||||
|
[VectorMath<float, 3>]
|
||||||
|
[SwizzleVector(3, "GlitchyEngine.Math.FancyMath.float")]
|
||||||
|
public struct float3
|
||||||
|
{
|
||||||
|
public static implicit operator int3(float3 value)
|
||||||
|
{
|
||||||
|
return int3((int32)value.X, (int32)value.Y, (int32)value.Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator half3(float3 value)
|
||||||
|
{
|
||||||
|
return half3((half)value.X, (half)value.Y, (half)value.Z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<float, 4>]
|
||||||
|
[ComparableVector<float, 4>]
|
||||||
|
[VectorMath<float, 4>]
|
||||||
|
[SwizzleVector(4, "GlitchyEngine.Math.FancyMath.float")]
|
||||||
|
public struct float4
|
||||||
|
{
|
||||||
|
public static implicit operator int4(float4 value)
|
||||||
|
{
|
||||||
|
return int4((int32)value.X, (int32)value.Y, (int32)value.Z, (int32)value.W);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator half4(float4 value)
|
||||||
|
{
|
||||||
|
return half4((half)value.X, (half)value.Y, (half)value.Z, (half)value.W);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using Bon;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Math.FancyMath;
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<half, 2>]
|
||||||
|
[ComparableVector<half, 2>]
|
||||||
|
[VectorMath<half, 2>]
|
||||||
|
[SwizzleVector(2, "GlitchyEngine.Math.FancyMath.half")]
|
||||||
|
public struct half2
|
||||||
|
{
|
||||||
|
public static explicit operator float2(half2 value)
|
||||||
|
{
|
||||||
|
return float2((float)value.X, (float)value.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<half, 3>]
|
||||||
|
[ComparableVector<half, 3>]
|
||||||
|
[VectorMath<half, 3>]
|
||||||
|
[SwizzleVector(3, "GlitchyEngine.Math.FancyMath.half")]
|
||||||
|
public struct half3
|
||||||
|
{
|
||||||
|
public static explicit operator float3(half3 value)
|
||||||
|
{
|
||||||
|
return float3((float)value.X, (float)value.Y, (float)value.Z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<half, 4>]
|
||||||
|
[ComparableVector<half, 4>]
|
||||||
|
[VectorMath<half, 4>]
|
||||||
|
[SwizzleVector(4, "GlitchyEngine.Math.FancyMath.half")]
|
||||||
|
public struct half4
|
||||||
|
{
|
||||||
|
public static explicit operator float4(half4 value)
|
||||||
|
{
|
||||||
|
return float4((float)value.X, (float)value.Y, (float)value.Z, (float)value.W);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using Bon;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Math.FancyMath;
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<int32, 2>]
|
||||||
|
[ComparableVector<int32, 2>]
|
||||||
|
[VectorMath<int32, 2>]
|
||||||
|
[SwizzleVector(2, "GlitchyEngine.Math.FancyMath.int")]
|
||||||
|
public struct int2
|
||||||
|
{
|
||||||
|
public static implicit operator float2(int2 value)
|
||||||
|
{
|
||||||
|
return float2(value.X, value.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<int32, 3>]
|
||||||
|
[ComparableVector<int32, 3>]
|
||||||
|
[VectorMath<int32, 3>]
|
||||||
|
[SwizzleVector(3, "GlitchyEngine.Math.FancyMath.int")]
|
||||||
|
public struct int3
|
||||||
|
{
|
||||||
|
public static implicit operator float3(int3 value)
|
||||||
|
{
|
||||||
|
return float3(value.X, value.Y, value.Z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BonTarget]
|
||||||
|
[Vector<int32, 4>]
|
||||||
|
[ComparableVector<int32, 4>]
|
||||||
|
[VectorMath<int32, 4>]
|
||||||
|
[SwizzleVector(4, "GlitchyEngine.Math.FancyMath.int")]
|
||||||
|
public struct int4
|
||||||
|
{
|
||||||
|
public static implicit operator float4(int4 value)
|
||||||
|
{
|
||||||
|
return float4(value.X, value.Y, value.Z, value.W);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Math;
|
||||||
|
|
||||||
|
/// Represents a 16bit floating point number. (IEEE 754 half-precision binary floating-point (binary16))
|
||||||
|
/// Note: Eventhough it is possible, it is not recommended to perform calculations on this type.
|
||||||
|
/// Most operations will simply convert the halfs to floats, perform the calculation and convert the result back to half.
|
||||||
|
struct half : IFloating, ISigned, IFormattable, IHashable, IEquatable<half>, ICanBeNaN
|
||||||
|
{
|
||||||
|
public const half MinValue = half(-65504); // Should be 0xFBFF
|
||||||
|
public const half MaxValue = half(65504); // Should be 0x7BFF
|
||||||
|
|
||||||
|
// The numbers for Inifnity, NaN and Zero need to be hardcoded as binaries,
|
||||||
|
// because the conversion intself relies on them.
|
||||||
|
|
||||||
|
public const half PositiveInfinity = half(0x7C00);
|
||||||
|
public const half NegativeInfinity = half(0xFC00);
|
||||||
|
public const half NaN = half(0x7CFF);
|
||||||
|
|
||||||
|
public const half Zero = half(0x0000);
|
||||||
|
public const half NegativeZero = half(0x8000);
|
||||||
|
|
||||||
|
private uint16 _data;
|
||||||
|
|
||||||
|
public bool IsNegative => (_data & Half_Sign_Mask) > 0;
|
||||||
|
|
||||||
|
public bool IsFinity => (_data & ~Half_Sign_Mask) < Half_Exponent_Mask;
|
||||||
|
public bool IsInfinity => (_data & ~Half_Sign_Mask) == Half_Exponent_Mask;
|
||||||
|
|
||||||
|
public bool IsPositiveInfinity => _data == PositiveInfinity._data;
|
||||||
|
public bool IsNegativeInfinity => _data == NegativeInfinity._data;
|
||||||
|
|
||||||
|
public bool IsNaN => (_data & ~Half_Sign_Mask) > Half_Exponent_Mask;
|
||||||
|
|
||||||
|
public bool IsSubnormal
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var unsignedBits = _data & ~Half_Sign_Mask;
|
||||||
|
|
||||||
|
// Zero isn't normalized and if exponent is 0 we are unnormalized
|
||||||
|
return (unsignedBits != 0) && ((unsignedBits & Half_Exponent_Mask) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(float value)
|
||||||
|
{
|
||||||
|
this = FromFloat32(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private this(uint16 data)
|
||||||
|
{
|
||||||
|
_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public explicit static operator half(float value) => FromFloat32(value);
|
||||||
|
public explicit static operator float(half value) => ToFloat32(value);
|
||||||
|
|
||||||
|
public static half FromFloat32(float value)
|
||||||
|
{
|
||||||
|
if (value == 0.0f)
|
||||||
|
return half(0x0000);
|
||||||
|
|
||||||
|
if (value == -0.0f)
|
||||||
|
return half(0x8000);
|
||||||
|
|
||||||
|
#unwarn
|
||||||
|
uint32 singleBits = *(uint32*)&value;
|
||||||
|
|
||||||
|
uint32 sign = GetSingleSignBit(singleBits);
|
||||||
|
int32 exponent = (int32)GetSingleExponent(singleBits);
|
||||||
|
uint32 mantissa = GetSingleMantissa(singleBits);
|
||||||
|
|
||||||
|
// Shift 13 so we truncate mantissa from 23 to 10 bits.
|
||||||
|
uint32 halfMantissa = mantissa >> 13;
|
||||||
|
|
||||||
|
if (exponent == 0xFF)
|
||||||
|
{
|
||||||
|
// largest possible single exponent -> either infinity or NaN
|
||||||
|
|
||||||
|
if (mantissa == 0)
|
||||||
|
{
|
||||||
|
// Infinity
|
||||||
|
return (sign == 1) ? NegativeInfinity : PositiveInfinity;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// NaN -> Keeps sign and mantissa intact
|
||||||
|
|
||||||
|
uint16 halfBits = SetHalfSignBit(0, (uint16)sign);
|
||||||
|
// Set all five exponent bits to 1
|
||||||
|
halfBits = SetHalfExponent(halfBits, 0x1F);
|
||||||
|
halfBits = SetHalfMantissa(halfBits, (uint16)halfMantissa);
|
||||||
|
|
||||||
|
return half(halfBits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalized single
|
||||||
|
|
||||||
|
// excess-K decode and encode -127 is k for single, 15 is k for half
|
||||||
|
exponent = exponent - 127 + 15;
|
||||||
|
|
||||||
|
if (exponent < 0)
|
||||||
|
// The given number is too small for a half (even denormalized) -> return 0
|
||||||
|
return (sign == 1) ? NegativeZero : Zero;
|
||||||
|
|
||||||
|
if (exponent > 31)
|
||||||
|
// The given number is too large for a half -> return infinity
|
||||||
|
return (sign == 1) ? NegativeInfinity : PositiveInfinity;
|
||||||
|
|
||||||
|
// The given number fits -> convert (might become denormalized)
|
||||||
|
uint16 halfBits = SetHalfSignBit(0, (uint16)sign);
|
||||||
|
halfBits = SetHalfExponent(halfBits, (uint16)exponent);
|
||||||
|
halfBits = SetHalfMantissa(halfBits, (uint16)halfMantissa);
|
||||||
|
|
||||||
|
return half(halfBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float ToFloat32(half value)
|
||||||
|
{
|
||||||
|
#unwarn
|
||||||
|
uint16 halfBits = *(uint16*)&value;
|
||||||
|
|
||||||
|
if (halfBits == Zero._data)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (halfBits == NegativeZero._data)
|
||||||
|
return -0.0f;
|
||||||
|
|
||||||
|
uint16 sign = GetHalfSignBit(halfBits);
|
||||||
|
int16 exponent = (int16)GetHalfExponent(halfBits);
|
||||||
|
uint16 mantissa = GetHalfMantissa(halfBits);
|
||||||
|
|
||||||
|
// Shift 13 so we extend mantissa from 10 to 23 bits.
|
||||||
|
uint32 singleMantissa = (uint32)mantissa << 13;
|
||||||
|
|
||||||
|
if (exponent == 0x1F)
|
||||||
|
{
|
||||||
|
// largest possible half exponent -> either infinity or NaN
|
||||||
|
|
||||||
|
if (mantissa == 0)
|
||||||
|
{
|
||||||
|
// Infinity
|
||||||
|
return (sign == 1) ? float.NegativeInfinity : float.PositiveInfinity;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// NaN -> Keeps sign and mantissa intact
|
||||||
|
|
||||||
|
uint32 singleBits = SetSingleSignBit(0, sign);
|
||||||
|
// Set all five exponent bits to 1
|
||||||
|
singleBits = SetSingleExponent(halfBits, 0xFF);
|
||||||
|
singleBits = SetSingleMantissa(halfBits, singleMantissa);
|
||||||
|
|
||||||
|
return *(float*)&singleBits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalized half
|
||||||
|
|
||||||
|
// excess-K decode and encode -127 is k for single, 15 is k for half
|
||||||
|
exponent = exponent - 15 + 127;
|
||||||
|
|
||||||
|
uint32 singleBits = SetSingleSignBit(0, sign);
|
||||||
|
singleBits = SetSingleExponent(singleBits, (uint32)exponent);
|
||||||
|
singleBits = SetSingleMantissa(singleBits, singleMantissa);
|
||||||
|
|
||||||
|
return *(float*)&singleBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Single <-> Half Helpers
|
||||||
|
|
||||||
|
const uint32 Single_Sign_Shift = 31;
|
||||||
|
const uint32 Single_Sign_Mask = 0x8000'0000; // 1 bit, offset 31
|
||||||
|
|
||||||
|
const uint32 Single_Exponent_Shift = 23;
|
||||||
|
const uint32 Single_Exponent_Mask = 0x7F80'0000; // 8 bit, offset 23
|
||||||
|
|
||||||
|
const uint32 Single_Mantissa_Mask = 0x007F'FFFF; // 23 bit, offset 0
|
||||||
|
|
||||||
|
const uint16 Half_Sign_Shift = 15;
|
||||||
|
const uint16 Half_Sign_Mask = 0x8000; // 1 bit, offset 15
|
||||||
|
|
||||||
|
const uint16 Half_Exponent_Shift = 10;
|
||||||
|
const uint16 Half_Exponent_Mask = 0x7C00; // 5 bit, offset 10
|
||||||
|
|
||||||
|
const uint16 Half_Mantissa_Mask = 0x03FF; // 10 bit, offset 0
|
||||||
|
|
||||||
|
private static uint32 GetSingleSignBit(uint32 singleData)
|
||||||
|
{
|
||||||
|
return (singleData >> Single_Sign_Shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint32 GetSingleExponent(uint32 singleData)
|
||||||
|
{
|
||||||
|
return (singleData & Single_Exponent_Mask) >> Single_Exponent_Shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint32 GetSingleMantissa(uint32 singleData)
|
||||||
|
{
|
||||||
|
return (singleData & Single_Mantissa_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint32 SetSingleSignBit(uint32 singleData, uint32 signBit)
|
||||||
|
{
|
||||||
|
return ((signBit << Single_Sign_Shift) & Single_Sign_Mask) | (singleData & ~Single_Sign_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint32 SetSingleExponent(uint32 singleData, uint32 exponent)
|
||||||
|
{
|
||||||
|
return ((exponent << Single_Exponent_Shift) & Single_Exponent_Mask) | (singleData & ~Single_Exponent_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint32 SetSingleMantissa(uint32 singleData, uint32 mantissa)
|
||||||
|
{
|
||||||
|
return (mantissa & Single_Mantissa_Mask) | (singleData & ~Single_Mantissa_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint16 GetHalfSignBit(uint16 HalfData)
|
||||||
|
{
|
||||||
|
return (HalfData >> Half_Sign_Shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint16 GetHalfExponent(uint16 HalfData)
|
||||||
|
{
|
||||||
|
return (HalfData & Half_Exponent_Mask) >> Half_Exponent_Shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint16 GetHalfMantissa(uint16 HalfData)
|
||||||
|
{
|
||||||
|
return (HalfData & Half_Mantissa_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint16 SetHalfSignBit(uint16 HalfData, uint16 signBit)
|
||||||
|
{
|
||||||
|
return ((signBit << Half_Sign_Shift) & Half_Sign_Mask) | (HalfData & ~Half_Sign_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint16 SetHalfExponent(uint16 HalfData, uint16 exponent)
|
||||||
|
{
|
||||||
|
return ((exponent << Half_Exponent_Shift) & Half_Exponent_Mask) | (HalfData & ~Half_Exponent_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint16 SetHalfMantissa(uint16 HalfData, uint16 mantissa)
|
||||||
|
{
|
||||||
|
return (mantissa & Half_Mantissa_Mask) | (HalfData & ~Half_Mantissa_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public int GetHashCode()
|
||||||
|
{
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToString(String outString)
|
||||||
|
{
|
||||||
|
ToFloat32(this).ToString(outString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToString(String outString, String format, IFormatProvider formatProvider)
|
||||||
|
{
|
||||||
|
ToFloat32(this).ToString(outString, format, formatProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static half operator +(half lhs, half rhs)
|
||||||
|
{
|
||||||
|
return (half)((float)lhs + (float)rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static half operator +(half value) => value;
|
||||||
|
|
||||||
|
public static half operator -(half lhs, half rhs)
|
||||||
|
{
|
||||||
|
return (half)((float)lhs - (float)rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static half operator -(half value)
|
||||||
|
{
|
||||||
|
uint16 signBit = GetHalfSignBit(value._data);
|
||||||
|
|
||||||
|
// Invert the sign bit (~)
|
||||||
|
return half(SetHalfSignBit(value._data, ~signBit));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static half operator *(half lhs, half rhs)
|
||||||
|
{
|
||||||
|
return (half)((float)lhs * (float)rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static half operator /(half lhs, half rhs)
|
||||||
|
{
|
||||||
|
return (half)((float)lhs / (float)rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static half operator %(half lhs, half rhs)
|
||||||
|
{
|
||||||
|
return (half)((float)lhs % (float)rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator==(half value1, half value2) => value1._data == value2._data;
|
||||||
|
|
||||||
|
public static bool operator!=(half value1, half value2) => value1._data != value2._data;
|
||||||
|
|
||||||
|
public static int operator<=>(half value1, half value2) => (float)value1 <=> (float)value2;
|
||||||
|
|
||||||
|
public bool Equals(half other)
|
||||||
|
{
|
||||||
|
return _data == other._data;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user