From fa2277cd996f48e7bb78dfc157b9413ca79f4da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sat, 24 Jun 2023 23:45:56 +0200 Subject: [PATCH] Start of shader like vectors + added 16 bit float --- GlitchyEngine/src/Math/FancyMath/FancyMath.bf | 459 ++++++++++++++++++ .../src/Math/FancyMath/VectorAttribute.bf | 416 ++++++++++++++++ GlitchyEngine/src/Math/FancyMath/bool.bf | 24 + GlitchyEngine/src/Math/FancyMath/float.bf | 58 +++ GlitchyEngine/src/Math/FancyMath/half.bf | 43 ++ GlitchyEngine/src/Math/FancyMath/int.bf | 43 ++ GlitchyEngine/src/Math/half.bf | 312 ++++++++++++ 7 files changed, 1355 insertions(+) create mode 100644 GlitchyEngine/src/Math/FancyMath/FancyMath.bf create mode 100644 GlitchyEngine/src/Math/FancyMath/VectorAttribute.bf create mode 100644 GlitchyEngine/src/Math/FancyMath/bool.bf create mode 100644 GlitchyEngine/src/Math/FancyMath/float.bf create mode 100644 GlitchyEngine/src/Math/FancyMath/half.bf create mode 100644 GlitchyEngine/src/Math/FancyMath/int.bf create mode 100644 GlitchyEngine/src/Math/half.bf diff --git a/GlitchyEngine/src/Math/FancyMath/FancyMath.bf b/GlitchyEngine/src/Math/FancyMath/FancyMath.bf new file mode 100644 index 0000000..46d33f0 --- /dev/null +++ b/GlitchyEngine/src/Math/FancyMath/FancyMath.bf @@ -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 + +} \ No newline at end of file diff --git a/GlitchyEngine/src/Math/FancyMath/VectorAttribute.bf b/GlitchyEngine/src/Math/FancyMath/VectorAttribute.bf new file mode 100644 index 0000000..e5e8c15 --- /dev/null +++ b/GlitchyEngine/src/Math/FancyMath/VectorAttribute.bf @@ -0,0 +1,416 @@ +using System; + +namespace GlitchyEngine.Math.FancyMath; + +[AttributeUsage(.Struct | .Class)] +struct VectorAttribute : 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 : Attribute, IComptimeTypeApply where ComponentCount : const int +{ + [Comptime] + public void ApplyToType(Type type) + { + VectorAttribute.GenerateComparison(type, ">"); + VectorAttribute.GenerateComparison(type, ">="); + VectorAttribute.GenerateComparison(type, "<"); + VectorAttribute.GenerateComparison(type, "<="); + } +} + +[AttributeUsage(.Struct | .Class)] +struct VectorMathAttribute : 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); + } +} diff --git a/GlitchyEngine/src/Math/FancyMath/bool.bf b/GlitchyEngine/src/Math/FancyMath/bool.bf new file mode 100644 index 0000000..26fa3d9 --- /dev/null +++ b/GlitchyEngine/src/Math/FancyMath/bool.bf @@ -0,0 +1,24 @@ +using Bon; + +namespace GlitchyEngine.Math.FancyMath; + +[BonTarget] +[Vector] +struct bool2 +{ + +} + +[BonTarget] +[Vector] +struct bool3 +{ + +} + +[BonTarget] +[Vector] +struct bool4 +{ + +} \ No newline at end of file diff --git a/GlitchyEngine/src/Math/FancyMath/float.bf b/GlitchyEngine/src/Math/FancyMath/float.bf new file mode 100644 index 0000000..1d0bd82 --- /dev/null +++ b/GlitchyEngine/src/Math/FancyMath/float.bf @@ -0,0 +1,58 @@ +using Bon; +using System; + +namespace GlitchyEngine.Math.FancyMath; + +[BonTarget] +[Vector] +[ComparableVector] +[VectorMath] +[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] +[ComparableVector] +[VectorMath] +[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] +[ComparableVector] +[VectorMath] +[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); + } +} \ No newline at end of file diff --git a/GlitchyEngine/src/Math/FancyMath/half.bf b/GlitchyEngine/src/Math/FancyMath/half.bf new file mode 100644 index 0000000..5cf55b8 --- /dev/null +++ b/GlitchyEngine/src/Math/FancyMath/half.bf @@ -0,0 +1,43 @@ +using Bon; +using System; + +namespace GlitchyEngine.Math.FancyMath; + +[BonTarget] +[Vector] +[ComparableVector] +[VectorMath] +[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] +[ComparableVector] +[VectorMath] +[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] +[ComparableVector] +[VectorMath] +[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); + } +} diff --git a/GlitchyEngine/src/Math/FancyMath/int.bf b/GlitchyEngine/src/Math/FancyMath/int.bf new file mode 100644 index 0000000..bf7a601 --- /dev/null +++ b/GlitchyEngine/src/Math/FancyMath/int.bf @@ -0,0 +1,43 @@ +using Bon; +using System; + +namespace GlitchyEngine.Math.FancyMath; + +[BonTarget] +[Vector] +[ComparableVector] +[VectorMath] +[SwizzleVector(2, "GlitchyEngine.Math.FancyMath.int")] +public struct int2 +{ + public static implicit operator float2(int2 value) + { + return float2(value.X, value.Y); + } +} + +[BonTarget] +[Vector] +[ComparableVector] +[VectorMath] +[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] +[ComparableVector] +[VectorMath] +[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); + } +} \ No newline at end of file diff --git a/GlitchyEngine/src/Math/half.bf b/GlitchyEngine/src/Math/half.bf new file mode 100644 index 0000000..dfad962 --- /dev/null +++ b/GlitchyEngine/src/Math/half.bf @@ -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, 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; + } +} \ No newline at end of file