diff --git a/packages/cultmath/README.md b/packages/cultmath/README.md index 8fd3888c..0be46738 100644 --- a/packages/cultmath/README.md +++ b/packages/cultmath/README.md @@ -16,7 +16,9 @@ shader semantics. - `float2`, `float3`, `float4` - component-wise arithmetic - scalar-to-vector splats -- swizzles for common lanes +- every HLSL swizzle (`xyzw` and `rgba`, repeats readable, non-repeating + masks writable) and every HLSL mixed constructor, generated by + `tools/generate-swizzles.ps1` - conversions to and from `System.Numerics` - `Random`, a compact deterministic XORShift stream with scalar and vector draws - `BatchMath` SoA kernels over `Span` / `ReadOnlySpan` using diff --git a/packages/cultmath/THIRD-PARTY-NOTICES.md b/packages/cultmath/THIRD-PARTY-NOTICES.md index c283df2b..06ebb6c3 100644 --- a/packages/cultmath/THIRD-PARTY-NOTICES.md +++ b/packages/cultmath/THIRD-PARTY-NOTICES.md @@ -1,9 +1,9 @@ # Third-Party Notices -## 2D Simplex Noise +## 2D and 3D Simplex Noise -`math.simplex_noise` and `cultmath_simplex_noise` port the textureless 2D -simplex-noise function by Ian McEwan and Ashima Arts, as distributed by Stefan +`math.snoise` and `cultmath_snoise` port the textureless 2D and 3D +simplex-noise functions by Ian McEwan and Ashima Arts, as distributed by Stefan Gustavson's `webgl-noise` project and Unity Mathematics. - Copyright 2011 Ashima Arts. diff --git a/packages/cultmath/docs/design.md b/packages/cultmath/docs/design.md index 8250ba78..de09ee26 100644 --- a/packages/cultmath/docs/design.md +++ b/packages/cultmath/docs/design.md @@ -3,22 +3,136 @@ CultMath exists to make renderer math readable in every place it needs to live: Rust hot kernels, C# host code, and HLSL shader code. +## Parity Target: HLSL + +HLSL is the semantic authority for every type and intrinsic HLSL defines. The +goal is source compatibility: HLSL-shaped math compiles as C# under +`using static CultMath.math;`. C# forces float literal suffixes (`0.5f`); the +rest of the spelling should survive the move. + +That makes these HLSL rules, not options: + +- Every swizzle exists. `float2/3/4`, `int2/3/4`, and `bool2/3/4` expose every + read swizzle of length 2 to 4, repeats included (`v.wwxy`), in both `xyzw` and + `rgba` naming (never mixed), plus single-component `r/g/b/a`. `double2/3` get + lengths 2 and 3 only, because there is no `double4`. Components and swizzles are + writable: `v.xz = f2;`, `c.rgb *= 0.5f;`, and `v[1] = s;` work. Setters exist + only for non-repeating masks, because HLSL rejects `v.xx = ...`. +- Every HLSL mixed constructor exists, as a struct constructor and as a `math` + function: `float4(float2, float2)`, `float4(float, float2, float)`, + `float3(float, float2)`, the copy form `float3(float3)`, and the rest, for the + float, int, and bool families (double gets constructors only; a `math.double3` + function would hide the `double3` type name). +- Swizzles and mixed constructors are generated. `tools/generate-swizzles.ps1` + writes the checked-in `src/CultMath/Swizzles.g.cs` (partial structs plus a + partial `math`). Edit the generator, rerun it, and commit both; never edit the + output by hand. +- Comparison operators (`<`, `>`, `<=`, `>=`, `==`, `!=`) are component-wise and + return `bool2`/`bool3`/`bool4`. Reduce them with `any` and `all`. + `Equals`/`GetHashCode` stay value-based so vectors still work as dictionary + keys. +- `select(condition, whenTrue, whenFalse)` uses HLSL's argument order. +- Matrices are row-major: constructors take rows, `m[i]` returns a reference to + row i (so `m[1][2] = s;` writes one element), `_mRC` is row R column C, and + products go through `mul` (`mul(m, v)` treats `v` as a column, `mul(v, m)` as a + row). `*` on matrices would be component-wise in HLSL, so CultMath does not + define it. +- `m[r][c] = v` writes through locals, array elements, fields of classes, and + ref locals. Through a readonly field, an `in` parameter, a property + (auto-properties included), or a static readonly value such as + `float3x3.identity`, C# takes a defensive copy: the write compiles and is + silently lost. Copy the matrix to a local, write to it, and assign it back. +- Scalar/vector arithmetic and comparisons have explicit overloads on both + sides rather than leaning on implicit scalar splat. +- Intrinsics return HLSL's types and edge cases: `sign` returns `int`/`intN` + (NaN gives 0); `step(y, x)` is `x < y ? 0 : 1` (NaN gives 1), which is how dxc + lowers it, in C# and in the HLSL mirror; float and double `min` and `max`, and + float `clamp` and `saturate`, follow the DXIL `FMin`/`FMax`/`Saturate` operations dxc emits for + them: `min(a, b)` is `a < b ? a : b`, `max(a, b)` is `a >= b ? a : b`, a NaN + operand returns the other (so `saturate(NaN)` is 0), `clamp(x, a, b)` is + `min(max(x, a), b)`, and `saturate(x)` is `min(1, max(0, x))`, which maps -0 + to +0; `any`/`all` accept numeric vectors (a component is true when it is + not 0); `min`, `max`, and `abs` on int vectors return int vectors. + +dxc marks float arithmetic and compares `fast` (no NaNs) unless a value is +`precise`, so drivers may optimize NaN handling away and NaN results on real +GPUs are not guaranteed. CultMath defines the CPU result by dxc's lowering and +the DXIL operation spec (DirectXShaderCompiler `docs/DXIL.rst`). + +NaN does not reliably propagate through CultMath, just as it does not on the +GPU. Because `min`, `max`, and `saturate` return the non-NaN operand, some +functions turn NaN or infinity into numbers: `smoothstep(a, a, a)` is 0; +infinite inputs to `smoothstep` and the intercept helpers give numbers; the +bezier, `smoothstep01`, and `smootherstep` functions return finite values for +NaN input; and `normalize((NaN, 2.41))` is `(NaN, 2.41e20)`, because the NaN +length is clamped to the epsilon and the other component is divided by it. +Callers that must detect bad data should check `isnan` before calling these +functions. + +Deferred until a consumer needs them: `float4x4`, `transpose`, `determinant`, +and HLSL's one-based `_11`..`_33` element names. + +### HLSL Target: Source Transformations + +`HlslSourceCompatibilityTests` compiles the real `shaders/CultMath.hlsl` with +Roslyn against CultMath. These transformations are the complete list; if a body +needs anything else, the test fails: + +1. Include guards (`#ifndef`/`#define`/`#endif`) are dropped. +2. Functions taking `Texture2D`/`SamplerState` are dropped; GPU resources have + no CultMath analog. +3. File-scope `static const` becomes `const`. +4. Floating literals gain `f` (`0.5` becomes `0.5f`). +5. The file body is wrapped in a class, since C# has no free functions; HLSL + functions become private instance methods. + +HLSL constructs that stay outside the target and must not appear in the mirror: +`const` locals of vector type (C# `const` is primitives only; write a plain +local) and writes through a swizzle component (`v.xy.x = s`, which C# rejects as +modifying a temporary). + +The HLSL side is checked too: `tools/get-dxc.ps1` fetches Microsoft's +DirectXShaderCompiler, and +`dxc -T lib_6_3 -HV 2021 shaders/CultMath.hlsl` must compile cleanly. + +The mirror test compares bit patterns (any NaN equals any NaN; -0 differs from +0). It proves the text of `CultMath.hlsl` computes the same float32 results as +C# `math` on the CPU, not that a GPU agrees: driver `sin` precision alone makes +`cultmath_hash` and value noise differ bit for bit on hardware. The mirror is +compiled under `using static CultMath.math;`, so its intrinsics are C# `math` +itself: the test proves the composition in the text, not the intrinsic rules, +which only `HlslSemanticsTests` pins. + +Where HLSL is silent, CultMath keeps its own decisions and does not defer to +Unity.Mathematics: `normalize` is safe (divides by `max(length, 1e-20)`), +`hash` returns float, and `Random` is CultMath's own xorshift32. Engine-shaped +helpers that HLSL lacks (`float2x2.Rotate`, `float3x3.Euler`, `quaternion`, +`snoise`) enter only when a consumer needs them, with their semantics stated in +code. + +Known C# friction: under `using static CultMath.math;` the constructor +functions (`float3(...)`, `float3x3(...)`) hide the type names in member access, +so static members need a qualified type (`CultMath.float3x3.Euler(...)`). + ## Rules -- Keep type names HLSL-shaped: `float2`, `float3`, `float4`, and `math`. +- Keep type names HLSL-shaped: `float2`, `float3`, `float4`, `float3x3`, and `math`. - Keep Rust as the owner of native hot-path kernels and parity fixtures. - Keep C#, shader, and scripting surfaces as runtime wrappers, not competing math authorities. - Keep `shaders/CultMath.hlsl` as the canonical shader mirror for shared - intrinsics. Project-local shader helpers should move here once more than one - organ needs them or once CPU/GPU parity matters. + CultMath functions HLSL does not already provide (HLSL intrinsics such as + `mul`, `any`, and `log` need no mirror). Project-local shader helpers should + move here once more than one organ needs them or once CPU/GPU parity matters. - Implement public shader semantics directly; do not copy Unity.Mathematics. -- Prefer small immutable value types. +- Prefer plain mutable value types with public component fields, as HLSL does. - Prefer component-wise overloads over clever generic machinery until the API earns that complexity. -- Keep the surface boring and exact. Noise primitives are allowed when they are - source-grounded, shader-shaped, and shared by more than one project. Matrices, - quaternions, and packing helpers still need an owner before they enter. +- Keep the surface boring and exact. Fill what a consumer uses, guided by HLSL + semantics; noise primitives, matrices, quaternions, and packing helpers need an + owner before they enter. +- Keep the core assembly engine-free. Unity conversions live in the Unity + package's `CultMath.UnityBridge` assembly. ## Non-Goals diff --git a/packages/cultmath/scripts/build-unity-package.ps1 b/packages/cultmath/scripts/build-unity-package.ps1 index f5fcacc0..b64e9446 100644 --- a/packages/cultmath/scripts/build-unity-package.ps1 +++ b/packages/cultmath/scripts/build-unity-package.ps1 @@ -27,8 +27,10 @@ if (Test-Path -LiteralPath $outputRoot) { Remove-Item -LiteralPath $outputRoot -Recurse -Force } -dotnet build $projectPath -c $Configuration -f netstandard2.1 -o $buildRoot ` - --disable-build-servers -p:UseSharedCompilation=false -p:ContinuousIntegrationBuild=true -m:1 +# The tracked DLL and pdb are committed beside their source, so neither may name a commit: Source Link +# writes the commit SHA into the pdb, and the DLL carries that pdb's content id. +dotnet build $projectPath -c $Configuration -f netstandard2.1 -o $buildRoot --no-incremental ` + --disable-build-servers -p:UseSharedCompilation=false -p:ContinuousIntegrationBuild=true -p:EnableSourceLink=false -m:1 if ($LASTEXITCODE -ne 0) { throw "CultMath Unity assembly build failed with exit code $LASTEXITCODE" } @@ -52,6 +54,10 @@ $requiredTemplateFiles = @( "Runtime\Plugins\CultMath.dll.meta", "Runtime\Plugins\CultMath.pdb", "Runtime\Plugins\CultMath.pdb.meta", + "Runtime\UnityBridge\CultMath.UnityBridge.asmdef", + "Runtime\UnityBridge\CultMath.UnityBridge.asmdef.meta", + "Runtime\UnityBridge\UnityConversions.cs", + "Runtime\UnityBridge\UnityConversions.cs.meta", "Shaders\CultMath.hlsl", "Shaders\CultMath.hlsl.meta" ) @@ -62,7 +68,7 @@ foreach ($relativePath in $requiredTemplateFiles) { } $unityAssetFiles = @(Get-ChildItem -LiteralPath $templateRoot -Recurse -File | Where-Object { - $_.Extension -in @(".asmdef", ".dll", ".pdb", ".hlsl") + $_.Extension -in @(".asmdef", ".cs", ".dll", ".pdb", ".hlsl") }) $missingAssetMetas = @($unityAssetFiles | Where-Object { -not (Test-Path -LiteralPath ($_.FullName + ".meta")) }) $missingDirectoryMetas = @(Get-ChildItem -LiteralPath $templateRoot -Recurse -Directory | Where-Object { @@ -78,9 +84,18 @@ $pluginMeta = Get-Content -LiteralPath ($trackedAssembly + ".meta") -Raw if ($pluginMeta -notmatch '(?m)^\s*isExplicitlyReferenced:\s*0\s*$') { throw "CultMath.dll must be auto-referenced; explicit-only plugins do not reach dependent Unity asmdefs" } -$facadeDefinitions = @(Get-ChildItem -LiteralPath $templateRoot -Recurse -File -Filter "*.asmdef") -if ($facadeDefinitions.Count -ne 0) { - throw "CultMath's precompiled DLL is the Unity assembly authority; empty asmdef facades are forbidden" +# The precompiled DLL is the only authority for CultMath's numeric types. The one +# compiled assembly allowed beside it is the UnityEngine conversion bridge, which +# references that DLL and must not re-declare core sources. +$bridgeRoot = Join-Path $templateRoot "Runtime\UnityBridge" +$bridgeDefinition = Join-Path $bridgeRoot "CultMath.UnityBridge.asmdef" +$definitions = @(Get-ChildItem -LiteralPath $templateRoot -Recurse -File -Filter "*.asmdef") +if ($definitions.Count -ne 1 -or $definitions[0].FullName -ne $bridgeDefinition) { + throw "CultMath's Unity package may define only the Runtime\UnityBridge assembly; found: $($definitions.FullName -join ', ')" +} +$bridge = Get-Content -LiteralPath $bridgeDefinition -Raw | ConvertFrom-Json +if ($bridge.noEngineReferences -or -not $bridge.overrideReferences -or @($bridge.precompiledReferences) -notcontains "CultMath.dll") { + throw "CultMath.UnityBridge must reference UnityEngine and the precompiled CultMath.dll" } $expectedFileVersion = "$($manifest.version).0" $trackedFileVersion = [Diagnostics.FileVersionInfo]::GetVersionInfo($trackedAssembly).FileVersion @@ -98,22 +113,47 @@ foreach ($pair in @(@($freshAssembly, $trackedAssembly), @($freshSymbols, $track } $trackedSourceFiles = @(Get-ChildItem -LiteralPath $templateRoot -Recurse -File -Filter "*.cs") -if ($trackedSourceFiles.Count -ne 0) { - throw "CultMath Unity package must not compile repository C# sources: $($trackedSourceFiles.FullName -join ', ')" +$straySources = @($trackedSourceFiles | Where-Object { $_.DirectoryName -ne $bridgeRoot }) +if ($trackedSourceFiles.Count -eq 0 -or $straySources.Count -ne 0) { + throw "CultMath Unity package may compile only bridge sources under Runtime\UnityBridge: $($straySources.FullName -join ', ')" +} + +# Check what the bridge declares, not what its files are called: it may declare types only in +# CultMath.UnityBridge and never a type name the core CultMath sources declare. +$declarationPattern = '\b(?:struct|class|enum|interface|record|delegate\s+[\w<>.,\s]+?)\s+(\w+)' +function Get-CodeWithoutComments([string]$path) { + $text = Get-Content -LiteralPath $path -Raw + return [regex]::Replace($text, '(?s)/\*.*?\*/|//[^\r\n]*', '') +} +$coreTypeNames = @(Get-ChildItem -LiteralPath (Join-Path $repoRoot "src\CultMath") -File -Filter "*.cs" | ForEach-Object { + [regex]::Matches((Get-CodeWithoutComments $_.FullName), $declarationPattern) | ForEach-Object { $_.Groups[1].Value } +} | Sort-Object -Unique) +if ($coreTypeNames -notcontains "float3" -or $coreTypeNames -notcontains "math") { + throw "Could not read CultMath core type declarations; the bridge check would be vacuous" +} +foreach ($source in $trackedSourceFiles) { + $code = Get-CodeWithoutComments $source.FullName + $redeclared = @([regex]::Matches($code, $declarationPattern) | ForEach-Object { $_.Groups[1].Value } | Where-Object { $coreTypeNames -contains $_ }) + $foreignNamespaces = @([regex]::Matches($code, '\bnamespace\s+([\w.]+)') | ForEach-Object { $_.Groups[1].Value } | Where-Object { $_ -ne "CultMath.UnityBridge" }) + if ($redeclared.Count -ne 0 -or $foreignNamespaces.Count -ne 0) { + throw "CultMath.UnityBridge source $($source.Name) re-declares core types [$($redeclared -join ', ')] or uses namespaces [$($foreignNamespaces -join ', ')]; the precompiled DLL owns CultMath's types" + } } New-Item -ItemType Directory -Force -Path $outputRoot | Out-Null Copy-Item -Path (Join-Path $templateRoot "*") -Destination $outputRoot -Recurse -Force $stagedAssemblies = @(Get-ChildItem -LiteralPath (Join-Path $outputRoot "Runtime\Plugins") -Filter "*.dll") +$stagedBridgeRoot = Join-Path $outputRoot "Runtime\UnityBridge" $stagedSources = @(Get-ChildItem -LiteralPath $outputRoot -Recurse -File -Filter "*.cs") -$stagedFacades = @(Get-ChildItem -LiteralPath $outputRoot -Recurse -File -Filter "*.asmdef") -if ($stagedAssemblies.Count -ne 1 -or $stagedAssemblies[0].Name -ne "CultMath.dll" -or $stagedSources.Count -ne 0 -or $stagedFacades.Count -ne 0) { - throw "CultMath package must contain one owned auto-referenced DLL, no C# source files, and no asmdef facade" +$stagedDefinitions = @(Get-ChildItem -LiteralPath $outputRoot -Recurse -File -Filter "*.asmdef") +$stagedStraySources = @($stagedSources | Where-Object { $_.DirectoryName -ne $stagedBridgeRoot }) +if ($stagedAssemblies.Count -ne 1 -or $stagedAssemblies[0].Name -ne "CultMath.dll" -or $stagedStraySources.Count -ne 0 -or $stagedDefinitions.Count -ne 1) { + throw "CultMath package must contain one owned auto-referenced DLL and only the UnityBridge assembly as source" } Write-Host "CultMath Unity package: $outputRoot" Write-Host "Package: $($manifest.name)@$($manifest.version)" Write-Host "Managed assemblies: $($stagedAssemblies.Count)" -Write-Host "C# source files: $($stagedSources.Count)" -Write-Host "Asmdef facades: $($stagedFacades.Count)" +Write-Host "Bridge C# source files: $($stagedSources.Count)" +Write-Host "Asmdef definitions: $($stagedDefinitions.Count)" diff --git a/packages/cultmath/shaders/CultMath.hlsl b/packages/cultmath/shaders/CultMath.hlsl index 4c00fb69..365705f5 100644 --- a/packages/cultmath/shaders/CultMath.hlsl +++ b/packages/cultmath/shaders/CultMath.hlsl @@ -26,10 +26,11 @@ float2 cultmath_clamp(float2 value, float2 minimum, float2 maximum) { return min float3 cultmath_clamp(float3 value, float3 minimum, float3 maximum) { return min(max(value, minimum), maximum); } float4 cultmath_clamp(float4 value, float4 minimum, float4 maximum) { return min(max(value, minimum), maximum); } -float cultmath_saturate(float value) { return cultmath_clamp(value, 0.0, 1.0); } -float2 cultmath_saturate(float2 value) { return cultmath_clamp(value, float2(0.0, 0.0), float2(1.0, 1.0)); } -float3 cultmath_saturate(float3 value) { return cultmath_clamp(value, float3(0.0, 0.0, 0.0), float3(1.0, 1.0, 1.0)); } -float4 cultmath_saturate(float4 value) { return cultmath_clamp(value, float4(0.0, 0.0, 0.0, 0.0), float4(1.0, 1.0, 1.0, 1.0)); } +// DXIL Saturate is FMin(1, FMax(0, x)); dxc lowers min/max to the same FMin/FMax, NaN rules included. +float cultmath_saturate(float value) { return min(1.0, max(0.0, value)); } +float2 cultmath_saturate(float2 value) { return min(float2(1.0, 1.0), max(float2(0.0, 0.0), value)); } +float3 cultmath_saturate(float3 value) { return min(float3(1.0, 1.0, 1.0), max(float3(0.0, 0.0, 0.0), value)); } +float4 cultmath_saturate(float4 value) { return min(float4(1.0, 1.0, 1.0, 1.0), max(float4(0.0, 0.0, 0.0, 0.0), value)); } float cultmath_lerp(float start, float end, float amount) { return start + (end - start) * amount; } float2 cultmath_lerp(float2 start, float2 end, float2 amount) { return start + (end - start) * amount; } @@ -73,13 +74,66 @@ float cultmath_smootherstep(float value) return value * value * value * (value * (value * 6.0 - 15.0) + 10.0); } -float2 cultmath_simplex_mod289(float2 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } -float3 cultmath_simplex_mod289(float3 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } -float3 cultmath_simplex_permute(float3 value) { return cultmath_simplex_mod289(((value * 34.0) + 1.0) * value); } +float2 cultmath_snoise_mod289(float2 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } +float3 cultmath_snoise_mod289(float3 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } +float4 cultmath_snoise_mod289(float4 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } +float3 cultmath_snoise_permute(float3 value) { return cultmath_snoise_mod289(((value * 34.0) + 1.0) * value); } +float4 cultmath_snoise_permute(float4 value) { return cultmath_snoise_mod289(((value * 34.0) + 1.0) * value); } -float cultmath_simplex_noise(float2 value) +// Ashima Arts / Ian McEwan 3D simplex noise (MIT), same float32 evaluation +// order as the C# math.snoise(float3) mirror. +float cultmath_snoise(float3 value) { - const float4 c = float4( + float2 c = float2(1.0 / 6.0, 1.0 / 3.0); + float3 i = floor(value + dot(value, c.yyy)); + float3 x0 = value - i + dot(i, c.xxx); + float3 g = step(x0.yzx, x0.xyz); + float3 l = 1.0 - g; + float3 i1 = min(g.xyz, l.zxy); + float3 i2 = max(g.xyz, l.zxy); + float3 x1 = x0 - i1 + c.xxx; + float3 x2 = x0 - i2 + c.yyy; + float3 x3 = x0 - 0.5; + + i = cultmath_snoise_mod289(i); + float4 p = cultmath_snoise_permute(cultmath_snoise_permute(cultmath_snoise_permute( + i.z + float4(0.0, i1.z, i2.z, 1.0)) + i.y + float4(0.0, i1.y, i2.y, 1.0)) + i.x + float4(0.0, i1.x, i2.x, 1.0)); + + const float n_ = 0.142857142857; + float3 ns = float3(2.0 * n_, 0.5 * n_ - 1.0, n_); + float4 j = p - 49.0 * floor(p * ns.z * ns.z); + float4 x_ = floor(j * ns.z); + float4 y_ = floor(j - 7.0 * x_); + float4 x = x_ * ns.x + ns.yyyy; + float4 y = y_ * ns.x + ns.yyyy; + float4 h = 1.0 - abs(x) - abs(y); + + float4 b0 = float4(x.xy, y.xy); + float4 b1 = float4(x.zw, y.zw); + float4 s0 = floor(b0) * 2.0 + 1.0; + float4 s1 = floor(b1) * 2.0 + 1.0; + float4 sh = -step(h, 0.0); + float4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + float4 a1 = b1.xzyw + s1.xzyw * sh.zzww; + + float3 p0 = float3(a0.xy, h.x); + float3 p1 = float3(a0.zw, h.y); + float3 p2 = float3(a1.xy, h.z); + float3 p3 = float3(a1.zw, h.w); + float4 norm = 1.79284291400159 - 0.85373472095314 * float4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + float4 m = max(0.6 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); + m = m * m; + return 42.0 * dot(m * m, float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); +} + +float cultmath_snoise(float2 value) +{ + float4 c = float4( 0.211324865405187, 0.366025403784439, -0.577350269189626, @@ -89,9 +143,9 @@ float cultmath_simplex_noise(float2 value) float2 i1 = x0.x > x0.y ? float2(1.0, 0.0) : float2(0.0, 1.0); float4 x12 = float4(x0.x + c.x - i1.x, x0.y + c.x - i1.y, x0.x + c.z, x0.y + c.z); - i = cultmath_simplex_mod289(i); - float3 p = cultmath_simplex_permute( - cultmath_simplex_permute(i.y + float3(0.0, i1.y, 1.0)) + i.x + float3(0.0, i1.x, 1.0)); + i = cultmath_snoise_mod289(i); + float3 p = cultmath_snoise_permute( + cultmath_snoise_permute(i.y + float3(0.0, i1.y, 1.0)) + i.x + float3(0.0, i1.x, 1.0)); float3 m = max(0.5 - float3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0); m *= m; m *= m; diff --git a/packages/cultmath/src/CultMath/CultMath.csproj b/packages/cultmath/src/CultMath/CultMath.csproj index 0ca99063..7c567105 100644 --- a/packages/cultmath/src/CultMath/CultMath.csproj +++ b/packages/cultmath/src/CultMath/CultMath.csproj @@ -8,12 +8,14 @@ CultMath CultMath GameCult.Math - 0.1.2 + 0.2.0 Clean-room C# math shaped for HLSL-style renderer code. GameCult README.md $(NoWarn);CS8981 true + + false diff --git a/packages/cultmath/src/CultMath/Random.cs b/packages/cultmath/src/CultMath/Random.cs index 9931a87f..6892e20c 100644 --- a/packages/cultmath/src/CultMath/Random.cs +++ b/packages/cultmath/src/CultMath/Random.cs @@ -58,5 +58,9 @@ public struct Random return new float2(math.cos(angle), math.sin(angle)); } - public float3 NextFloat3(float3 min, float3 max) => min + new float3(NextFloat(), NextFloat(), NextFloat()) * (max - min); + public float3 NextFloat3() => new(NextFloat(), NextFloat(), NextFloat()); + + public float3 NextFloat3(float3 max) => NextFloat3() * max; + + public float3 NextFloat3(float3 min, float3 max) => min + NextFloat3() * (max - min); } diff --git a/packages/cultmath/src/CultMath/Swizzles.g.cs b/packages/cultmath/src/CultMath/Swizzles.g.cs new file mode 100644 index 00000000..ce84bbc4 --- /dev/null +++ b/packages/cultmath/src/CultMath/Swizzles.g.cs @@ -0,0 +1,3137 @@ +// +// Generated by tools/generate-swizzles.ps1. Do not edit by hand. +// + +namespace CultMath; + +public partial struct float2 +{ + public float2(float2 xy) : this(xy.x, xy.y) { } + public float r { readonly get => x; set => x = value; } + public float g { readonly get => y; set => y = value; } + public readonly float2 xx => new(x, x); + public float2 xy { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public float2 yx { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly float2 yy => new(y, y); + public readonly float3 xxx => new(x, x, x); + public readonly float3 xxy => new(x, x, y); + public readonly float3 xyx => new(x, y, x); + public readonly float3 xyy => new(x, y, y); + public readonly float3 yxx => new(y, x, x); + public readonly float3 yxy => new(y, x, y); + public readonly float3 yyx => new(y, y, x); + public readonly float3 yyy => new(y, y, y); + public readonly float4 xxxx => new(x, x, x, x); + public readonly float4 xxxy => new(x, x, x, y); + public readonly float4 xxyx => new(x, x, y, x); + public readonly float4 xxyy => new(x, x, y, y); + public readonly float4 xyxx => new(x, y, x, x); + public readonly float4 xyxy => new(x, y, x, y); + public readonly float4 xyyx => new(x, y, y, x); + public readonly float4 xyyy => new(x, y, y, y); + public readonly float4 yxxx => new(y, x, x, x); + public readonly float4 yxxy => new(y, x, x, y); + public readonly float4 yxyx => new(y, x, y, x); + public readonly float4 yxyy => new(y, x, y, y); + public readonly float4 yyxx => new(y, y, x, x); + public readonly float4 yyxy => new(y, y, x, y); + public readonly float4 yyyx => new(y, y, y, x); + public readonly float4 yyyy => new(y, y, y, y); + public readonly float2 rr => new(x, x); + public float2 rg { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public float2 gr { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly float2 gg => new(y, y); + public readonly float3 rrr => new(x, x, x); + public readonly float3 rrg => new(x, x, y); + public readonly float3 rgr => new(x, y, x); + public readonly float3 rgg => new(x, y, y); + public readonly float3 grr => new(y, x, x); + public readonly float3 grg => new(y, x, y); + public readonly float3 ggr => new(y, y, x); + public readonly float3 ggg => new(y, y, y); + public readonly float4 rrrr => new(x, x, x, x); + public readonly float4 rrrg => new(x, x, x, y); + public readonly float4 rrgr => new(x, x, y, x); + public readonly float4 rrgg => new(x, x, y, y); + public readonly float4 rgrr => new(x, y, x, x); + public readonly float4 rgrg => new(x, y, x, y); + public readonly float4 rggr => new(x, y, y, x); + public readonly float4 rggg => new(x, y, y, y); + public readonly float4 grrr => new(y, x, x, x); + public readonly float4 grrg => new(y, x, x, y); + public readonly float4 grgr => new(y, x, y, x); + public readonly float4 grgg => new(y, x, y, y); + public readonly float4 ggrr => new(y, y, x, x); + public readonly float4 ggrg => new(y, y, x, y); + public readonly float4 gggr => new(y, y, y, x); + public readonly float4 gggg => new(y, y, y, y); +} + +public partial struct float3 +{ + public float3(float x, float2 yz) : this(x, yz.x, yz.y) { } + public float3(float2 xy, float z) : this(xy.x, xy.y, z) { } + public float3(float3 xyz) : this(xyz.x, xyz.y, xyz.z) { } + public float r { readonly get => x; set => x = value; } + public float g { readonly get => y; set => y = value; } + public float b { readonly get => z; set => z = value; } + public readonly float2 xx => new(x, x); + public float2 xy { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public float2 xz { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public float2 yx { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly float2 yy => new(y, y); + public float2 yz { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public float2 zx { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public float2 zy { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly float2 zz => new(z, z); + public readonly float3 xxx => new(x, x, x); + public readonly float3 xxy => new(x, x, y); + public readonly float3 xxz => new(x, x, z); + public readonly float3 xyx => new(x, y, x); + public readonly float3 xyy => new(x, y, y); + public float3 xyz { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public readonly float3 xzx => new(x, z, x); + public float3 xzy { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly float3 xzz => new(x, z, z); + public readonly float3 yxx => new(y, x, x); + public readonly float3 yxy => new(y, x, y); + public float3 yxz { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public readonly float3 yyx => new(y, y, x); + public readonly float3 yyy => new(y, y, y); + public readonly float3 yyz => new(y, y, z); + public float3 yzx { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly float3 yzy => new(y, z, y); + public readonly float3 yzz => new(y, z, z); + public readonly float3 zxx => new(z, x, x); + public float3 zxy { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly float3 zxz => new(z, x, z); + public float3 zyx { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly float3 zyy => new(z, y, y); + public readonly float3 zyz => new(z, y, z); + public readonly float3 zzx => new(z, z, x); + public readonly float3 zzy => new(z, z, y); + public readonly float3 zzz => new(z, z, z); + public readonly float4 xxxx => new(x, x, x, x); + public readonly float4 xxxy => new(x, x, x, y); + public readonly float4 xxxz => new(x, x, x, z); + public readonly float4 xxyx => new(x, x, y, x); + public readonly float4 xxyy => new(x, x, y, y); + public readonly float4 xxyz => new(x, x, y, z); + public readonly float4 xxzx => new(x, x, z, x); + public readonly float4 xxzy => new(x, x, z, y); + public readonly float4 xxzz => new(x, x, z, z); + public readonly float4 xyxx => new(x, y, x, x); + public readonly float4 xyxy => new(x, y, x, y); + public readonly float4 xyxz => new(x, y, x, z); + public readonly float4 xyyx => new(x, y, y, x); + public readonly float4 xyyy => new(x, y, y, y); + public readonly float4 xyyz => new(x, y, y, z); + public readonly float4 xyzx => new(x, y, z, x); + public readonly float4 xyzy => new(x, y, z, y); + public readonly float4 xyzz => new(x, y, z, z); + public readonly float4 xzxx => new(x, z, x, x); + public readonly float4 xzxy => new(x, z, x, y); + public readonly float4 xzxz => new(x, z, x, z); + public readonly float4 xzyx => new(x, z, y, x); + public readonly float4 xzyy => new(x, z, y, y); + public readonly float4 xzyz => new(x, z, y, z); + public readonly float4 xzzx => new(x, z, z, x); + public readonly float4 xzzy => new(x, z, z, y); + public readonly float4 xzzz => new(x, z, z, z); + public readonly float4 yxxx => new(y, x, x, x); + public readonly float4 yxxy => new(y, x, x, y); + public readonly float4 yxxz => new(y, x, x, z); + public readonly float4 yxyx => new(y, x, y, x); + public readonly float4 yxyy => new(y, x, y, y); + public readonly float4 yxyz => new(y, x, y, z); + public readonly float4 yxzx => new(y, x, z, x); + public readonly float4 yxzy => new(y, x, z, y); + public readonly float4 yxzz => new(y, x, z, z); + public readonly float4 yyxx => new(y, y, x, x); + public readonly float4 yyxy => new(y, y, x, y); + public readonly float4 yyxz => new(y, y, x, z); + public readonly float4 yyyx => new(y, y, y, x); + public readonly float4 yyyy => new(y, y, y, y); + public readonly float4 yyyz => new(y, y, y, z); + public readonly float4 yyzx => new(y, y, z, x); + public readonly float4 yyzy => new(y, y, z, y); + public readonly float4 yyzz => new(y, y, z, z); + public readonly float4 yzxx => new(y, z, x, x); + public readonly float4 yzxy => new(y, z, x, y); + public readonly float4 yzxz => new(y, z, x, z); + public readonly float4 yzyx => new(y, z, y, x); + public readonly float4 yzyy => new(y, z, y, y); + public readonly float4 yzyz => new(y, z, y, z); + public readonly float4 yzzx => new(y, z, z, x); + public readonly float4 yzzy => new(y, z, z, y); + public readonly float4 yzzz => new(y, z, z, z); + public readonly float4 zxxx => new(z, x, x, x); + public readonly float4 zxxy => new(z, x, x, y); + public readonly float4 zxxz => new(z, x, x, z); + public readonly float4 zxyx => new(z, x, y, x); + public readonly float4 zxyy => new(z, x, y, y); + public readonly float4 zxyz => new(z, x, y, z); + public readonly float4 zxzx => new(z, x, z, x); + public readonly float4 zxzy => new(z, x, z, y); + public readonly float4 zxzz => new(z, x, z, z); + public readonly float4 zyxx => new(z, y, x, x); + public readonly float4 zyxy => new(z, y, x, y); + public readonly float4 zyxz => new(z, y, x, z); + public readonly float4 zyyx => new(z, y, y, x); + public readonly float4 zyyy => new(z, y, y, y); + public readonly float4 zyyz => new(z, y, y, z); + public readonly float4 zyzx => new(z, y, z, x); + public readonly float4 zyzy => new(z, y, z, y); + public readonly float4 zyzz => new(z, y, z, z); + public readonly float4 zzxx => new(z, z, x, x); + public readonly float4 zzxy => new(z, z, x, y); + public readonly float4 zzxz => new(z, z, x, z); + public readonly float4 zzyx => new(z, z, y, x); + public readonly float4 zzyy => new(z, z, y, y); + public readonly float4 zzyz => new(z, z, y, z); + public readonly float4 zzzx => new(z, z, z, x); + public readonly float4 zzzy => new(z, z, z, y); + public readonly float4 zzzz => new(z, z, z, z); + public readonly float2 rr => new(x, x); + public float2 rg { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public float2 rb { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public float2 gr { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly float2 gg => new(y, y); + public float2 gb { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public float2 br { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public float2 bg { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly float2 bb => new(z, z); + public readonly float3 rrr => new(x, x, x); + public readonly float3 rrg => new(x, x, y); + public readonly float3 rrb => new(x, x, z); + public readonly float3 rgr => new(x, y, x); + public readonly float3 rgg => new(x, y, y); + public float3 rgb { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public readonly float3 rbr => new(x, z, x); + public float3 rbg { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly float3 rbb => new(x, z, z); + public readonly float3 grr => new(y, x, x); + public readonly float3 grg => new(y, x, y); + public float3 grb { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public readonly float3 ggr => new(y, y, x); + public readonly float3 ggg => new(y, y, y); + public readonly float3 ggb => new(y, y, z); + public float3 gbr { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly float3 gbg => new(y, z, y); + public readonly float3 gbb => new(y, z, z); + public readonly float3 brr => new(z, x, x); + public float3 brg { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly float3 brb => new(z, x, z); + public float3 bgr { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly float3 bgg => new(z, y, y); + public readonly float3 bgb => new(z, y, z); + public readonly float3 bbr => new(z, z, x); + public readonly float3 bbg => new(z, z, y); + public readonly float3 bbb => new(z, z, z); + public readonly float4 rrrr => new(x, x, x, x); + public readonly float4 rrrg => new(x, x, x, y); + public readonly float4 rrrb => new(x, x, x, z); + public readonly float4 rrgr => new(x, x, y, x); + public readonly float4 rrgg => new(x, x, y, y); + public readonly float4 rrgb => new(x, x, y, z); + public readonly float4 rrbr => new(x, x, z, x); + public readonly float4 rrbg => new(x, x, z, y); + public readonly float4 rrbb => new(x, x, z, z); + public readonly float4 rgrr => new(x, y, x, x); + public readonly float4 rgrg => new(x, y, x, y); + public readonly float4 rgrb => new(x, y, x, z); + public readonly float4 rggr => new(x, y, y, x); + public readonly float4 rggg => new(x, y, y, y); + public readonly float4 rggb => new(x, y, y, z); + public readonly float4 rgbr => new(x, y, z, x); + public readonly float4 rgbg => new(x, y, z, y); + public readonly float4 rgbb => new(x, y, z, z); + public readonly float4 rbrr => new(x, z, x, x); + public readonly float4 rbrg => new(x, z, x, y); + public readonly float4 rbrb => new(x, z, x, z); + public readonly float4 rbgr => new(x, z, y, x); + public readonly float4 rbgg => new(x, z, y, y); + public readonly float4 rbgb => new(x, z, y, z); + public readonly float4 rbbr => new(x, z, z, x); + public readonly float4 rbbg => new(x, z, z, y); + public readonly float4 rbbb => new(x, z, z, z); + public readonly float4 grrr => new(y, x, x, x); + public readonly float4 grrg => new(y, x, x, y); + public readonly float4 grrb => new(y, x, x, z); + public readonly float4 grgr => new(y, x, y, x); + public readonly float4 grgg => new(y, x, y, y); + public readonly float4 grgb => new(y, x, y, z); + public readonly float4 grbr => new(y, x, z, x); + public readonly float4 grbg => new(y, x, z, y); + public readonly float4 grbb => new(y, x, z, z); + public readonly float4 ggrr => new(y, y, x, x); + public readonly float4 ggrg => new(y, y, x, y); + public readonly float4 ggrb => new(y, y, x, z); + public readonly float4 gggr => new(y, y, y, x); + public readonly float4 gggg => new(y, y, y, y); + public readonly float4 gggb => new(y, y, y, z); + public readonly float4 ggbr => new(y, y, z, x); + public readonly float4 ggbg => new(y, y, z, y); + public readonly float4 ggbb => new(y, y, z, z); + public readonly float4 gbrr => new(y, z, x, x); + public readonly float4 gbrg => new(y, z, x, y); + public readonly float4 gbrb => new(y, z, x, z); + public readonly float4 gbgr => new(y, z, y, x); + public readonly float4 gbgg => new(y, z, y, y); + public readonly float4 gbgb => new(y, z, y, z); + public readonly float4 gbbr => new(y, z, z, x); + public readonly float4 gbbg => new(y, z, z, y); + public readonly float4 gbbb => new(y, z, z, z); + public readonly float4 brrr => new(z, x, x, x); + public readonly float4 brrg => new(z, x, x, y); + public readonly float4 brrb => new(z, x, x, z); + public readonly float4 brgr => new(z, x, y, x); + public readonly float4 brgg => new(z, x, y, y); + public readonly float4 brgb => new(z, x, y, z); + public readonly float4 brbr => new(z, x, z, x); + public readonly float4 brbg => new(z, x, z, y); + public readonly float4 brbb => new(z, x, z, z); + public readonly float4 bgrr => new(z, y, x, x); + public readonly float4 bgrg => new(z, y, x, y); + public readonly float4 bgrb => new(z, y, x, z); + public readonly float4 bggr => new(z, y, y, x); + public readonly float4 bggg => new(z, y, y, y); + public readonly float4 bggb => new(z, y, y, z); + public readonly float4 bgbr => new(z, y, z, x); + public readonly float4 bgbg => new(z, y, z, y); + public readonly float4 bgbb => new(z, y, z, z); + public readonly float4 bbrr => new(z, z, x, x); + public readonly float4 bbrg => new(z, z, x, y); + public readonly float4 bbrb => new(z, z, x, z); + public readonly float4 bbgr => new(z, z, y, x); + public readonly float4 bbgg => new(z, z, y, y); + public readonly float4 bbgb => new(z, z, y, z); + public readonly float4 bbbr => new(z, z, z, x); + public readonly float4 bbbg => new(z, z, z, y); + public readonly float4 bbbb => new(z, z, z, z); +} + +public partial struct float4 +{ + public float4(float x, float y, float2 zw) : this(x, y, zw.x, zw.y) { } + public float4(float x, float2 yz, float w) : this(x, yz.x, yz.y, w) { } + public float4(float x, float3 yzw) : this(x, yzw.x, yzw.y, yzw.z) { } + public float4(float2 xy, float z, float w) : this(xy.x, xy.y, z, w) { } + public float4(float2 xy, float2 zw) : this(xy.x, xy.y, zw.x, zw.y) { } + public float4(float3 xyz, float w) : this(xyz.x, xyz.y, xyz.z, w) { } + public float4(float4 xyzw) : this(xyzw.x, xyzw.y, xyzw.z, xyzw.w) { } + public float r { readonly get => x; set => x = value; } + public float g { readonly get => y; set => y = value; } + public float b { readonly get => z; set => z = value; } + public float a { readonly get => w; set => w = value; } + public readonly float2 xx => new(x, x); + public float2 xy { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public float2 xz { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public float2 xw { readonly get => new(x, w); set { x = value.x; w = value.y; } } + public float2 yx { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly float2 yy => new(y, y); + public float2 yz { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public float2 yw { readonly get => new(y, w); set { y = value.x; w = value.y; } } + public float2 zx { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public float2 zy { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly float2 zz => new(z, z); + public float2 zw { readonly get => new(z, w); set { z = value.x; w = value.y; } } + public float2 wx { readonly get => new(w, x); set { w = value.x; x = value.y; } } + public float2 wy { readonly get => new(w, y); set { w = value.x; y = value.y; } } + public float2 wz { readonly get => new(w, z); set { w = value.x; z = value.y; } } + public readonly float2 ww => new(w, w); + public readonly float3 xxx => new(x, x, x); + public readonly float3 xxy => new(x, x, y); + public readonly float3 xxz => new(x, x, z); + public readonly float3 xxw => new(x, x, w); + public readonly float3 xyx => new(x, y, x); + public readonly float3 xyy => new(x, y, y); + public float3 xyz { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public float3 xyw { readonly get => new(x, y, w); set { x = value.x; y = value.y; w = value.z; } } + public readonly float3 xzx => new(x, z, x); + public float3 xzy { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly float3 xzz => new(x, z, z); + public float3 xzw { readonly get => new(x, z, w); set { x = value.x; z = value.y; w = value.z; } } + public readonly float3 xwx => new(x, w, x); + public float3 xwy { readonly get => new(x, w, y); set { x = value.x; w = value.y; y = value.z; } } + public float3 xwz { readonly get => new(x, w, z); set { x = value.x; w = value.y; z = value.z; } } + public readonly float3 xww => new(x, w, w); + public readonly float3 yxx => new(y, x, x); + public readonly float3 yxy => new(y, x, y); + public float3 yxz { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public float3 yxw { readonly get => new(y, x, w); set { y = value.x; x = value.y; w = value.z; } } + public readonly float3 yyx => new(y, y, x); + public readonly float3 yyy => new(y, y, y); + public readonly float3 yyz => new(y, y, z); + public readonly float3 yyw => new(y, y, w); + public float3 yzx { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly float3 yzy => new(y, z, y); + public readonly float3 yzz => new(y, z, z); + public float3 yzw { readonly get => new(y, z, w); set { y = value.x; z = value.y; w = value.z; } } + public float3 ywx { readonly get => new(y, w, x); set { y = value.x; w = value.y; x = value.z; } } + public readonly float3 ywy => new(y, w, y); + public float3 ywz { readonly get => new(y, w, z); set { y = value.x; w = value.y; z = value.z; } } + public readonly float3 yww => new(y, w, w); + public readonly float3 zxx => new(z, x, x); + public float3 zxy { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly float3 zxz => new(z, x, z); + public float3 zxw { readonly get => new(z, x, w); set { z = value.x; x = value.y; w = value.z; } } + public float3 zyx { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly float3 zyy => new(z, y, y); + public readonly float3 zyz => new(z, y, z); + public float3 zyw { readonly get => new(z, y, w); set { z = value.x; y = value.y; w = value.z; } } + public readonly float3 zzx => new(z, z, x); + public readonly float3 zzy => new(z, z, y); + public readonly float3 zzz => new(z, z, z); + public readonly float3 zzw => new(z, z, w); + public float3 zwx { readonly get => new(z, w, x); set { z = value.x; w = value.y; x = value.z; } } + public float3 zwy { readonly get => new(z, w, y); set { z = value.x; w = value.y; y = value.z; } } + public readonly float3 zwz => new(z, w, z); + public readonly float3 zww => new(z, w, w); + public readonly float3 wxx => new(w, x, x); + public float3 wxy { readonly get => new(w, x, y); set { w = value.x; x = value.y; y = value.z; } } + public float3 wxz { readonly get => new(w, x, z); set { w = value.x; x = value.y; z = value.z; } } + public readonly float3 wxw => new(w, x, w); + public float3 wyx { readonly get => new(w, y, x); set { w = value.x; y = value.y; x = value.z; } } + public readonly float3 wyy => new(w, y, y); + public float3 wyz { readonly get => new(w, y, z); set { w = value.x; y = value.y; z = value.z; } } + public readonly float3 wyw => new(w, y, w); + public float3 wzx { readonly get => new(w, z, x); set { w = value.x; z = value.y; x = value.z; } } + public float3 wzy { readonly get => new(w, z, y); set { w = value.x; z = value.y; y = value.z; } } + public readonly float3 wzz => new(w, z, z); + public readonly float3 wzw => new(w, z, w); + public readonly float3 wwx => new(w, w, x); + public readonly float3 wwy => new(w, w, y); + public readonly float3 wwz => new(w, w, z); + public readonly float3 www => new(w, w, w); + public readonly float4 xxxx => new(x, x, x, x); + public readonly float4 xxxy => new(x, x, x, y); + public readonly float4 xxxz => new(x, x, x, z); + public readonly float4 xxxw => new(x, x, x, w); + public readonly float4 xxyx => new(x, x, y, x); + public readonly float4 xxyy => new(x, x, y, y); + public readonly float4 xxyz => new(x, x, y, z); + public readonly float4 xxyw => new(x, x, y, w); + public readonly float4 xxzx => new(x, x, z, x); + public readonly float4 xxzy => new(x, x, z, y); + public readonly float4 xxzz => new(x, x, z, z); + public readonly float4 xxzw => new(x, x, z, w); + public readonly float4 xxwx => new(x, x, w, x); + public readonly float4 xxwy => new(x, x, w, y); + public readonly float4 xxwz => new(x, x, w, z); + public readonly float4 xxww => new(x, x, w, w); + public readonly float4 xyxx => new(x, y, x, x); + public readonly float4 xyxy => new(x, y, x, y); + public readonly float4 xyxz => new(x, y, x, z); + public readonly float4 xyxw => new(x, y, x, w); + public readonly float4 xyyx => new(x, y, y, x); + public readonly float4 xyyy => new(x, y, y, y); + public readonly float4 xyyz => new(x, y, y, z); + public readonly float4 xyyw => new(x, y, y, w); + public readonly float4 xyzx => new(x, y, z, x); + public readonly float4 xyzy => new(x, y, z, y); + public readonly float4 xyzz => new(x, y, z, z); + public float4 xyzw { readonly get => new(x, y, z, w); set { x = value.x; y = value.y; z = value.z; w = value.w; } } + public readonly float4 xywx => new(x, y, w, x); + public readonly float4 xywy => new(x, y, w, y); + public float4 xywz { readonly get => new(x, y, w, z); set { x = value.x; y = value.y; w = value.z; z = value.w; } } + public readonly float4 xyww => new(x, y, w, w); + public readonly float4 xzxx => new(x, z, x, x); + public readonly float4 xzxy => new(x, z, x, y); + public readonly float4 xzxz => new(x, z, x, z); + public readonly float4 xzxw => new(x, z, x, w); + public readonly float4 xzyx => new(x, z, y, x); + public readonly float4 xzyy => new(x, z, y, y); + public readonly float4 xzyz => new(x, z, y, z); + public float4 xzyw { readonly get => new(x, z, y, w); set { x = value.x; z = value.y; y = value.z; w = value.w; } } + public readonly float4 xzzx => new(x, z, z, x); + public readonly float4 xzzy => new(x, z, z, y); + public readonly float4 xzzz => new(x, z, z, z); + public readonly float4 xzzw => new(x, z, z, w); + public readonly float4 xzwx => new(x, z, w, x); + public float4 xzwy { readonly get => new(x, z, w, y); set { x = value.x; z = value.y; w = value.z; y = value.w; } } + public readonly float4 xzwz => new(x, z, w, z); + public readonly float4 xzww => new(x, z, w, w); + public readonly float4 xwxx => new(x, w, x, x); + public readonly float4 xwxy => new(x, w, x, y); + public readonly float4 xwxz => new(x, w, x, z); + public readonly float4 xwxw => new(x, w, x, w); + public readonly float4 xwyx => new(x, w, y, x); + public readonly float4 xwyy => new(x, w, y, y); + public float4 xwyz { readonly get => new(x, w, y, z); set { x = value.x; w = value.y; y = value.z; z = value.w; } } + public readonly float4 xwyw => new(x, w, y, w); + public readonly float4 xwzx => new(x, w, z, x); + public float4 xwzy { readonly get => new(x, w, z, y); set { x = value.x; w = value.y; z = value.z; y = value.w; } } + public readonly float4 xwzz => new(x, w, z, z); + public readonly float4 xwzw => new(x, w, z, w); + public readonly float4 xwwx => new(x, w, w, x); + public readonly float4 xwwy => new(x, w, w, y); + public readonly float4 xwwz => new(x, w, w, z); + public readonly float4 xwww => new(x, w, w, w); + public readonly float4 yxxx => new(y, x, x, x); + public readonly float4 yxxy => new(y, x, x, y); + public readonly float4 yxxz => new(y, x, x, z); + public readonly float4 yxxw => new(y, x, x, w); + public readonly float4 yxyx => new(y, x, y, x); + public readonly float4 yxyy => new(y, x, y, y); + public readonly float4 yxyz => new(y, x, y, z); + public readonly float4 yxyw => new(y, x, y, w); + public readonly float4 yxzx => new(y, x, z, x); + public readonly float4 yxzy => new(y, x, z, y); + public readonly float4 yxzz => new(y, x, z, z); + public float4 yxzw { readonly get => new(y, x, z, w); set { y = value.x; x = value.y; z = value.z; w = value.w; } } + public readonly float4 yxwx => new(y, x, w, x); + public readonly float4 yxwy => new(y, x, w, y); + public float4 yxwz { readonly get => new(y, x, w, z); set { y = value.x; x = value.y; w = value.z; z = value.w; } } + public readonly float4 yxww => new(y, x, w, w); + public readonly float4 yyxx => new(y, y, x, x); + public readonly float4 yyxy => new(y, y, x, y); + public readonly float4 yyxz => new(y, y, x, z); + public readonly float4 yyxw => new(y, y, x, w); + public readonly float4 yyyx => new(y, y, y, x); + public readonly float4 yyyy => new(y, y, y, y); + public readonly float4 yyyz => new(y, y, y, z); + public readonly float4 yyyw => new(y, y, y, w); + public readonly float4 yyzx => new(y, y, z, x); + public readonly float4 yyzy => new(y, y, z, y); + public readonly float4 yyzz => new(y, y, z, z); + public readonly float4 yyzw => new(y, y, z, w); + public readonly float4 yywx => new(y, y, w, x); + public readonly float4 yywy => new(y, y, w, y); + public readonly float4 yywz => new(y, y, w, z); + public readonly float4 yyww => new(y, y, w, w); + public readonly float4 yzxx => new(y, z, x, x); + public readonly float4 yzxy => new(y, z, x, y); + public readonly float4 yzxz => new(y, z, x, z); + public float4 yzxw { readonly get => new(y, z, x, w); set { y = value.x; z = value.y; x = value.z; w = value.w; } } + public readonly float4 yzyx => new(y, z, y, x); + public readonly float4 yzyy => new(y, z, y, y); + public readonly float4 yzyz => new(y, z, y, z); + public readonly float4 yzyw => new(y, z, y, w); + public readonly float4 yzzx => new(y, z, z, x); + public readonly float4 yzzy => new(y, z, z, y); + public readonly float4 yzzz => new(y, z, z, z); + public readonly float4 yzzw => new(y, z, z, w); + public float4 yzwx { readonly get => new(y, z, w, x); set { y = value.x; z = value.y; w = value.z; x = value.w; } } + public readonly float4 yzwy => new(y, z, w, y); + public readonly float4 yzwz => new(y, z, w, z); + public readonly float4 yzww => new(y, z, w, w); + public readonly float4 ywxx => new(y, w, x, x); + public readonly float4 ywxy => new(y, w, x, y); + public float4 ywxz { readonly get => new(y, w, x, z); set { y = value.x; w = value.y; x = value.z; z = value.w; } } + public readonly float4 ywxw => new(y, w, x, w); + public readonly float4 ywyx => new(y, w, y, x); + public readonly float4 ywyy => new(y, w, y, y); + public readonly float4 ywyz => new(y, w, y, z); + public readonly float4 ywyw => new(y, w, y, w); + public float4 ywzx { readonly get => new(y, w, z, x); set { y = value.x; w = value.y; z = value.z; x = value.w; } } + public readonly float4 ywzy => new(y, w, z, y); + public readonly float4 ywzz => new(y, w, z, z); + public readonly float4 ywzw => new(y, w, z, w); + public readonly float4 ywwx => new(y, w, w, x); + public readonly float4 ywwy => new(y, w, w, y); + public readonly float4 ywwz => new(y, w, w, z); + public readonly float4 ywww => new(y, w, w, w); + public readonly float4 zxxx => new(z, x, x, x); + public readonly float4 zxxy => new(z, x, x, y); + public readonly float4 zxxz => new(z, x, x, z); + public readonly float4 zxxw => new(z, x, x, w); + public readonly float4 zxyx => new(z, x, y, x); + public readonly float4 zxyy => new(z, x, y, y); + public readonly float4 zxyz => new(z, x, y, z); + public float4 zxyw { readonly get => new(z, x, y, w); set { z = value.x; x = value.y; y = value.z; w = value.w; } } + public readonly float4 zxzx => new(z, x, z, x); + public readonly float4 zxzy => new(z, x, z, y); + public readonly float4 zxzz => new(z, x, z, z); + public readonly float4 zxzw => new(z, x, z, w); + public readonly float4 zxwx => new(z, x, w, x); + public float4 zxwy { readonly get => new(z, x, w, y); set { z = value.x; x = value.y; w = value.z; y = value.w; } } + public readonly float4 zxwz => new(z, x, w, z); + public readonly float4 zxww => new(z, x, w, w); + public readonly float4 zyxx => new(z, y, x, x); + public readonly float4 zyxy => new(z, y, x, y); + public readonly float4 zyxz => new(z, y, x, z); + public float4 zyxw { readonly get => new(z, y, x, w); set { z = value.x; y = value.y; x = value.z; w = value.w; } } + public readonly float4 zyyx => new(z, y, y, x); + public readonly float4 zyyy => new(z, y, y, y); + public readonly float4 zyyz => new(z, y, y, z); + public readonly float4 zyyw => new(z, y, y, w); + public readonly float4 zyzx => new(z, y, z, x); + public readonly float4 zyzy => new(z, y, z, y); + public readonly float4 zyzz => new(z, y, z, z); + public readonly float4 zyzw => new(z, y, z, w); + public float4 zywx { readonly get => new(z, y, w, x); set { z = value.x; y = value.y; w = value.z; x = value.w; } } + public readonly float4 zywy => new(z, y, w, y); + public readonly float4 zywz => new(z, y, w, z); + public readonly float4 zyww => new(z, y, w, w); + public readonly float4 zzxx => new(z, z, x, x); + public readonly float4 zzxy => new(z, z, x, y); + public readonly float4 zzxz => new(z, z, x, z); + public readonly float4 zzxw => new(z, z, x, w); + public readonly float4 zzyx => new(z, z, y, x); + public readonly float4 zzyy => new(z, z, y, y); + public readonly float4 zzyz => new(z, z, y, z); + public readonly float4 zzyw => new(z, z, y, w); + public readonly float4 zzzx => new(z, z, z, x); + public readonly float4 zzzy => new(z, z, z, y); + public readonly float4 zzzz => new(z, z, z, z); + public readonly float4 zzzw => new(z, z, z, w); + public readonly float4 zzwx => new(z, z, w, x); + public readonly float4 zzwy => new(z, z, w, y); + public readonly float4 zzwz => new(z, z, w, z); + public readonly float4 zzww => new(z, z, w, w); + public readonly float4 zwxx => new(z, w, x, x); + public float4 zwxy { readonly get => new(z, w, x, y); set { z = value.x; w = value.y; x = value.z; y = value.w; } } + public readonly float4 zwxz => new(z, w, x, z); + public readonly float4 zwxw => new(z, w, x, w); + public float4 zwyx { readonly get => new(z, w, y, x); set { z = value.x; w = value.y; y = value.z; x = value.w; } } + public readonly float4 zwyy => new(z, w, y, y); + public readonly float4 zwyz => new(z, w, y, z); + public readonly float4 zwyw => new(z, w, y, w); + public readonly float4 zwzx => new(z, w, z, x); + public readonly float4 zwzy => new(z, w, z, y); + public readonly float4 zwzz => new(z, w, z, z); + public readonly float4 zwzw => new(z, w, z, w); + public readonly float4 zwwx => new(z, w, w, x); + public readonly float4 zwwy => new(z, w, w, y); + public readonly float4 zwwz => new(z, w, w, z); + public readonly float4 zwww => new(z, w, w, w); + public readonly float4 wxxx => new(w, x, x, x); + public readonly float4 wxxy => new(w, x, x, y); + public readonly float4 wxxz => new(w, x, x, z); + public readonly float4 wxxw => new(w, x, x, w); + public readonly float4 wxyx => new(w, x, y, x); + public readonly float4 wxyy => new(w, x, y, y); + public float4 wxyz { readonly get => new(w, x, y, z); set { w = value.x; x = value.y; y = value.z; z = value.w; } } + public readonly float4 wxyw => new(w, x, y, w); + public readonly float4 wxzx => new(w, x, z, x); + public float4 wxzy { readonly get => new(w, x, z, y); set { w = value.x; x = value.y; z = value.z; y = value.w; } } + public readonly float4 wxzz => new(w, x, z, z); + public readonly float4 wxzw => new(w, x, z, w); + public readonly float4 wxwx => new(w, x, w, x); + public readonly float4 wxwy => new(w, x, w, y); + public readonly float4 wxwz => new(w, x, w, z); + public readonly float4 wxww => new(w, x, w, w); + public readonly float4 wyxx => new(w, y, x, x); + public readonly float4 wyxy => new(w, y, x, y); + public float4 wyxz { readonly get => new(w, y, x, z); set { w = value.x; y = value.y; x = value.z; z = value.w; } } + public readonly float4 wyxw => new(w, y, x, w); + public readonly float4 wyyx => new(w, y, y, x); + public readonly float4 wyyy => new(w, y, y, y); + public readonly float4 wyyz => new(w, y, y, z); + public readonly float4 wyyw => new(w, y, y, w); + public float4 wyzx { readonly get => new(w, y, z, x); set { w = value.x; y = value.y; z = value.z; x = value.w; } } + public readonly float4 wyzy => new(w, y, z, y); + public readonly float4 wyzz => new(w, y, z, z); + public readonly float4 wyzw => new(w, y, z, w); + public readonly float4 wywx => new(w, y, w, x); + public readonly float4 wywy => new(w, y, w, y); + public readonly float4 wywz => new(w, y, w, z); + public readonly float4 wyww => new(w, y, w, w); + public readonly float4 wzxx => new(w, z, x, x); + public float4 wzxy { readonly get => new(w, z, x, y); set { w = value.x; z = value.y; x = value.z; y = value.w; } } + public readonly float4 wzxz => new(w, z, x, z); + public readonly float4 wzxw => new(w, z, x, w); + public float4 wzyx { readonly get => new(w, z, y, x); set { w = value.x; z = value.y; y = value.z; x = value.w; } } + public readonly float4 wzyy => new(w, z, y, y); + public readonly float4 wzyz => new(w, z, y, z); + public readonly float4 wzyw => new(w, z, y, w); + public readonly float4 wzzx => new(w, z, z, x); + public readonly float4 wzzy => new(w, z, z, y); + public readonly float4 wzzz => new(w, z, z, z); + public readonly float4 wzzw => new(w, z, z, w); + public readonly float4 wzwx => new(w, z, w, x); + public readonly float4 wzwy => new(w, z, w, y); + public readonly float4 wzwz => new(w, z, w, z); + public readonly float4 wzww => new(w, z, w, w); + public readonly float4 wwxx => new(w, w, x, x); + public readonly float4 wwxy => new(w, w, x, y); + public readonly float4 wwxz => new(w, w, x, z); + public readonly float4 wwxw => new(w, w, x, w); + public readonly float4 wwyx => new(w, w, y, x); + public readonly float4 wwyy => new(w, w, y, y); + public readonly float4 wwyz => new(w, w, y, z); + public readonly float4 wwyw => new(w, w, y, w); + public readonly float4 wwzx => new(w, w, z, x); + public readonly float4 wwzy => new(w, w, z, y); + public readonly float4 wwzz => new(w, w, z, z); + public readonly float4 wwzw => new(w, w, z, w); + public readonly float4 wwwx => new(w, w, w, x); + public readonly float4 wwwy => new(w, w, w, y); + public readonly float4 wwwz => new(w, w, w, z); + public readonly float4 wwww => new(w, w, w, w); + public readonly float2 rr => new(x, x); + public float2 rg { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public float2 rb { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public float2 ra { readonly get => new(x, w); set { x = value.x; w = value.y; } } + public float2 gr { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly float2 gg => new(y, y); + public float2 gb { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public float2 ga { readonly get => new(y, w); set { y = value.x; w = value.y; } } + public float2 br { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public float2 bg { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly float2 bb => new(z, z); + public float2 ba { readonly get => new(z, w); set { z = value.x; w = value.y; } } + public float2 ar { readonly get => new(w, x); set { w = value.x; x = value.y; } } + public float2 ag { readonly get => new(w, y); set { w = value.x; y = value.y; } } + public float2 ab { readonly get => new(w, z); set { w = value.x; z = value.y; } } + public readonly float2 aa => new(w, w); + public readonly float3 rrr => new(x, x, x); + public readonly float3 rrg => new(x, x, y); + public readonly float3 rrb => new(x, x, z); + public readonly float3 rra => new(x, x, w); + public readonly float3 rgr => new(x, y, x); + public readonly float3 rgg => new(x, y, y); + public float3 rgb { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public float3 rga { readonly get => new(x, y, w); set { x = value.x; y = value.y; w = value.z; } } + public readonly float3 rbr => new(x, z, x); + public float3 rbg { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly float3 rbb => new(x, z, z); + public float3 rba { readonly get => new(x, z, w); set { x = value.x; z = value.y; w = value.z; } } + public readonly float3 rar => new(x, w, x); + public float3 rag { readonly get => new(x, w, y); set { x = value.x; w = value.y; y = value.z; } } + public float3 rab { readonly get => new(x, w, z); set { x = value.x; w = value.y; z = value.z; } } + public readonly float3 raa => new(x, w, w); + public readonly float3 grr => new(y, x, x); + public readonly float3 grg => new(y, x, y); + public float3 grb { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public float3 gra { readonly get => new(y, x, w); set { y = value.x; x = value.y; w = value.z; } } + public readonly float3 ggr => new(y, y, x); + public readonly float3 ggg => new(y, y, y); + public readonly float3 ggb => new(y, y, z); + public readonly float3 gga => new(y, y, w); + public float3 gbr { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly float3 gbg => new(y, z, y); + public readonly float3 gbb => new(y, z, z); + public float3 gba { readonly get => new(y, z, w); set { y = value.x; z = value.y; w = value.z; } } + public float3 gar { readonly get => new(y, w, x); set { y = value.x; w = value.y; x = value.z; } } + public readonly float3 gag => new(y, w, y); + public float3 gab { readonly get => new(y, w, z); set { y = value.x; w = value.y; z = value.z; } } + public readonly float3 gaa => new(y, w, w); + public readonly float3 brr => new(z, x, x); + public float3 brg { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly float3 brb => new(z, x, z); + public float3 bra { readonly get => new(z, x, w); set { z = value.x; x = value.y; w = value.z; } } + public float3 bgr { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly float3 bgg => new(z, y, y); + public readonly float3 bgb => new(z, y, z); + public float3 bga { readonly get => new(z, y, w); set { z = value.x; y = value.y; w = value.z; } } + public readonly float3 bbr => new(z, z, x); + public readonly float3 bbg => new(z, z, y); + public readonly float3 bbb => new(z, z, z); + public readonly float3 bba => new(z, z, w); + public float3 bar { readonly get => new(z, w, x); set { z = value.x; w = value.y; x = value.z; } } + public float3 bag { readonly get => new(z, w, y); set { z = value.x; w = value.y; y = value.z; } } + public readonly float3 bab => new(z, w, z); + public readonly float3 baa => new(z, w, w); + public readonly float3 arr => new(w, x, x); + public float3 arg { readonly get => new(w, x, y); set { w = value.x; x = value.y; y = value.z; } } + public float3 arb { readonly get => new(w, x, z); set { w = value.x; x = value.y; z = value.z; } } + public readonly float3 ara => new(w, x, w); + public float3 agr { readonly get => new(w, y, x); set { w = value.x; y = value.y; x = value.z; } } + public readonly float3 agg => new(w, y, y); + public float3 agb { readonly get => new(w, y, z); set { w = value.x; y = value.y; z = value.z; } } + public readonly float3 aga => new(w, y, w); + public float3 abr { readonly get => new(w, z, x); set { w = value.x; z = value.y; x = value.z; } } + public float3 abg { readonly get => new(w, z, y); set { w = value.x; z = value.y; y = value.z; } } + public readonly float3 abb => new(w, z, z); + public readonly float3 aba => new(w, z, w); + public readonly float3 aar => new(w, w, x); + public readonly float3 aag => new(w, w, y); + public readonly float3 aab => new(w, w, z); + public readonly float3 aaa => new(w, w, w); + public readonly float4 rrrr => new(x, x, x, x); + public readonly float4 rrrg => new(x, x, x, y); + public readonly float4 rrrb => new(x, x, x, z); + public readonly float4 rrra => new(x, x, x, w); + public readonly float4 rrgr => new(x, x, y, x); + public readonly float4 rrgg => new(x, x, y, y); + public readonly float4 rrgb => new(x, x, y, z); + public readonly float4 rrga => new(x, x, y, w); + public readonly float4 rrbr => new(x, x, z, x); + public readonly float4 rrbg => new(x, x, z, y); + public readonly float4 rrbb => new(x, x, z, z); + public readonly float4 rrba => new(x, x, z, w); + public readonly float4 rrar => new(x, x, w, x); + public readonly float4 rrag => new(x, x, w, y); + public readonly float4 rrab => new(x, x, w, z); + public readonly float4 rraa => new(x, x, w, w); + public readonly float4 rgrr => new(x, y, x, x); + public readonly float4 rgrg => new(x, y, x, y); + public readonly float4 rgrb => new(x, y, x, z); + public readonly float4 rgra => new(x, y, x, w); + public readonly float4 rggr => new(x, y, y, x); + public readonly float4 rggg => new(x, y, y, y); + public readonly float4 rggb => new(x, y, y, z); + public readonly float4 rgga => new(x, y, y, w); + public readonly float4 rgbr => new(x, y, z, x); + public readonly float4 rgbg => new(x, y, z, y); + public readonly float4 rgbb => new(x, y, z, z); + public float4 rgba { readonly get => new(x, y, z, w); set { x = value.x; y = value.y; z = value.z; w = value.w; } } + public readonly float4 rgar => new(x, y, w, x); + public readonly float4 rgag => new(x, y, w, y); + public float4 rgab { readonly get => new(x, y, w, z); set { x = value.x; y = value.y; w = value.z; z = value.w; } } + public readonly float4 rgaa => new(x, y, w, w); + public readonly float4 rbrr => new(x, z, x, x); + public readonly float4 rbrg => new(x, z, x, y); + public readonly float4 rbrb => new(x, z, x, z); + public readonly float4 rbra => new(x, z, x, w); + public readonly float4 rbgr => new(x, z, y, x); + public readonly float4 rbgg => new(x, z, y, y); + public readonly float4 rbgb => new(x, z, y, z); + public float4 rbga { readonly get => new(x, z, y, w); set { x = value.x; z = value.y; y = value.z; w = value.w; } } + public readonly float4 rbbr => new(x, z, z, x); + public readonly float4 rbbg => new(x, z, z, y); + public readonly float4 rbbb => new(x, z, z, z); + public readonly float4 rbba => new(x, z, z, w); + public readonly float4 rbar => new(x, z, w, x); + public float4 rbag { readonly get => new(x, z, w, y); set { x = value.x; z = value.y; w = value.z; y = value.w; } } + public readonly float4 rbab => new(x, z, w, z); + public readonly float4 rbaa => new(x, z, w, w); + public readonly float4 rarr => new(x, w, x, x); + public readonly float4 rarg => new(x, w, x, y); + public readonly float4 rarb => new(x, w, x, z); + public readonly float4 rara => new(x, w, x, w); + public readonly float4 ragr => new(x, w, y, x); + public readonly float4 ragg => new(x, w, y, y); + public float4 ragb { readonly get => new(x, w, y, z); set { x = value.x; w = value.y; y = value.z; z = value.w; } } + public readonly float4 raga => new(x, w, y, w); + public readonly float4 rabr => new(x, w, z, x); + public float4 rabg { readonly get => new(x, w, z, y); set { x = value.x; w = value.y; z = value.z; y = value.w; } } + public readonly float4 rabb => new(x, w, z, z); + public readonly float4 raba => new(x, w, z, w); + public readonly float4 raar => new(x, w, w, x); + public readonly float4 raag => new(x, w, w, y); + public readonly float4 raab => new(x, w, w, z); + public readonly float4 raaa => new(x, w, w, w); + public readonly float4 grrr => new(y, x, x, x); + public readonly float4 grrg => new(y, x, x, y); + public readonly float4 grrb => new(y, x, x, z); + public readonly float4 grra => new(y, x, x, w); + public readonly float4 grgr => new(y, x, y, x); + public readonly float4 grgg => new(y, x, y, y); + public readonly float4 grgb => new(y, x, y, z); + public readonly float4 grga => new(y, x, y, w); + public readonly float4 grbr => new(y, x, z, x); + public readonly float4 grbg => new(y, x, z, y); + public readonly float4 grbb => new(y, x, z, z); + public float4 grba { readonly get => new(y, x, z, w); set { y = value.x; x = value.y; z = value.z; w = value.w; } } + public readonly float4 grar => new(y, x, w, x); + public readonly float4 grag => new(y, x, w, y); + public float4 grab { readonly get => new(y, x, w, z); set { y = value.x; x = value.y; w = value.z; z = value.w; } } + public readonly float4 graa => new(y, x, w, w); + public readonly float4 ggrr => new(y, y, x, x); + public readonly float4 ggrg => new(y, y, x, y); + public readonly float4 ggrb => new(y, y, x, z); + public readonly float4 ggra => new(y, y, x, w); + public readonly float4 gggr => new(y, y, y, x); + public readonly float4 gggg => new(y, y, y, y); + public readonly float4 gggb => new(y, y, y, z); + public readonly float4 ggga => new(y, y, y, w); + public readonly float4 ggbr => new(y, y, z, x); + public readonly float4 ggbg => new(y, y, z, y); + public readonly float4 ggbb => new(y, y, z, z); + public readonly float4 ggba => new(y, y, z, w); + public readonly float4 ggar => new(y, y, w, x); + public readonly float4 ggag => new(y, y, w, y); + public readonly float4 ggab => new(y, y, w, z); + public readonly float4 ggaa => new(y, y, w, w); + public readonly float4 gbrr => new(y, z, x, x); + public readonly float4 gbrg => new(y, z, x, y); + public readonly float4 gbrb => new(y, z, x, z); + public float4 gbra { readonly get => new(y, z, x, w); set { y = value.x; z = value.y; x = value.z; w = value.w; } } + public readonly float4 gbgr => new(y, z, y, x); + public readonly float4 gbgg => new(y, z, y, y); + public readonly float4 gbgb => new(y, z, y, z); + public readonly float4 gbga => new(y, z, y, w); + public readonly float4 gbbr => new(y, z, z, x); + public readonly float4 gbbg => new(y, z, z, y); + public readonly float4 gbbb => new(y, z, z, z); + public readonly float4 gbba => new(y, z, z, w); + public float4 gbar { readonly get => new(y, z, w, x); set { y = value.x; z = value.y; w = value.z; x = value.w; } } + public readonly float4 gbag => new(y, z, w, y); + public readonly float4 gbab => new(y, z, w, z); + public readonly float4 gbaa => new(y, z, w, w); + public readonly float4 garr => new(y, w, x, x); + public readonly float4 garg => new(y, w, x, y); + public float4 garb { readonly get => new(y, w, x, z); set { y = value.x; w = value.y; x = value.z; z = value.w; } } + public readonly float4 gara => new(y, w, x, w); + public readonly float4 gagr => new(y, w, y, x); + public readonly float4 gagg => new(y, w, y, y); + public readonly float4 gagb => new(y, w, y, z); + public readonly float4 gaga => new(y, w, y, w); + public float4 gabr { readonly get => new(y, w, z, x); set { y = value.x; w = value.y; z = value.z; x = value.w; } } + public readonly float4 gabg => new(y, w, z, y); + public readonly float4 gabb => new(y, w, z, z); + public readonly float4 gaba => new(y, w, z, w); + public readonly float4 gaar => new(y, w, w, x); + public readonly float4 gaag => new(y, w, w, y); + public readonly float4 gaab => new(y, w, w, z); + public readonly float4 gaaa => new(y, w, w, w); + public readonly float4 brrr => new(z, x, x, x); + public readonly float4 brrg => new(z, x, x, y); + public readonly float4 brrb => new(z, x, x, z); + public readonly float4 brra => new(z, x, x, w); + public readonly float4 brgr => new(z, x, y, x); + public readonly float4 brgg => new(z, x, y, y); + public readonly float4 brgb => new(z, x, y, z); + public float4 brga { readonly get => new(z, x, y, w); set { z = value.x; x = value.y; y = value.z; w = value.w; } } + public readonly float4 brbr => new(z, x, z, x); + public readonly float4 brbg => new(z, x, z, y); + public readonly float4 brbb => new(z, x, z, z); + public readonly float4 brba => new(z, x, z, w); + public readonly float4 brar => new(z, x, w, x); + public float4 brag { readonly get => new(z, x, w, y); set { z = value.x; x = value.y; w = value.z; y = value.w; } } + public readonly float4 brab => new(z, x, w, z); + public readonly float4 braa => new(z, x, w, w); + public readonly float4 bgrr => new(z, y, x, x); + public readonly float4 bgrg => new(z, y, x, y); + public readonly float4 bgrb => new(z, y, x, z); + public float4 bgra { readonly get => new(z, y, x, w); set { z = value.x; y = value.y; x = value.z; w = value.w; } } + public readonly float4 bggr => new(z, y, y, x); + public readonly float4 bggg => new(z, y, y, y); + public readonly float4 bggb => new(z, y, y, z); + public readonly float4 bgga => new(z, y, y, w); + public readonly float4 bgbr => new(z, y, z, x); + public readonly float4 bgbg => new(z, y, z, y); + public readonly float4 bgbb => new(z, y, z, z); + public readonly float4 bgba => new(z, y, z, w); + public float4 bgar { readonly get => new(z, y, w, x); set { z = value.x; y = value.y; w = value.z; x = value.w; } } + public readonly float4 bgag => new(z, y, w, y); + public readonly float4 bgab => new(z, y, w, z); + public readonly float4 bgaa => new(z, y, w, w); + public readonly float4 bbrr => new(z, z, x, x); + public readonly float4 bbrg => new(z, z, x, y); + public readonly float4 bbrb => new(z, z, x, z); + public readonly float4 bbra => new(z, z, x, w); + public readonly float4 bbgr => new(z, z, y, x); + public readonly float4 bbgg => new(z, z, y, y); + public readonly float4 bbgb => new(z, z, y, z); + public readonly float4 bbga => new(z, z, y, w); + public readonly float4 bbbr => new(z, z, z, x); + public readonly float4 bbbg => new(z, z, z, y); + public readonly float4 bbbb => new(z, z, z, z); + public readonly float4 bbba => new(z, z, z, w); + public readonly float4 bbar => new(z, z, w, x); + public readonly float4 bbag => new(z, z, w, y); + public readonly float4 bbab => new(z, z, w, z); + public readonly float4 bbaa => new(z, z, w, w); + public readonly float4 barr => new(z, w, x, x); + public float4 barg { readonly get => new(z, w, x, y); set { z = value.x; w = value.y; x = value.z; y = value.w; } } + public readonly float4 barb => new(z, w, x, z); + public readonly float4 bara => new(z, w, x, w); + public float4 bagr { readonly get => new(z, w, y, x); set { z = value.x; w = value.y; y = value.z; x = value.w; } } + public readonly float4 bagg => new(z, w, y, y); + public readonly float4 bagb => new(z, w, y, z); + public readonly float4 baga => new(z, w, y, w); + public readonly float4 babr => new(z, w, z, x); + public readonly float4 babg => new(z, w, z, y); + public readonly float4 babb => new(z, w, z, z); + public readonly float4 baba => new(z, w, z, w); + public readonly float4 baar => new(z, w, w, x); + public readonly float4 baag => new(z, w, w, y); + public readonly float4 baab => new(z, w, w, z); + public readonly float4 baaa => new(z, w, w, w); + public readonly float4 arrr => new(w, x, x, x); + public readonly float4 arrg => new(w, x, x, y); + public readonly float4 arrb => new(w, x, x, z); + public readonly float4 arra => new(w, x, x, w); + public readonly float4 argr => new(w, x, y, x); + public readonly float4 argg => new(w, x, y, y); + public float4 argb { readonly get => new(w, x, y, z); set { w = value.x; x = value.y; y = value.z; z = value.w; } } + public readonly float4 arga => new(w, x, y, w); + public readonly float4 arbr => new(w, x, z, x); + public float4 arbg { readonly get => new(w, x, z, y); set { w = value.x; x = value.y; z = value.z; y = value.w; } } + public readonly float4 arbb => new(w, x, z, z); + public readonly float4 arba => new(w, x, z, w); + public readonly float4 arar => new(w, x, w, x); + public readonly float4 arag => new(w, x, w, y); + public readonly float4 arab => new(w, x, w, z); + public readonly float4 araa => new(w, x, w, w); + public readonly float4 agrr => new(w, y, x, x); + public readonly float4 agrg => new(w, y, x, y); + public float4 agrb { readonly get => new(w, y, x, z); set { w = value.x; y = value.y; x = value.z; z = value.w; } } + public readonly float4 agra => new(w, y, x, w); + public readonly float4 aggr => new(w, y, y, x); + public readonly float4 aggg => new(w, y, y, y); + public readonly float4 aggb => new(w, y, y, z); + public readonly float4 agga => new(w, y, y, w); + public float4 agbr { readonly get => new(w, y, z, x); set { w = value.x; y = value.y; z = value.z; x = value.w; } } + public readonly float4 agbg => new(w, y, z, y); + public readonly float4 agbb => new(w, y, z, z); + public readonly float4 agba => new(w, y, z, w); + public readonly float4 agar => new(w, y, w, x); + public readonly float4 agag => new(w, y, w, y); + public readonly float4 agab => new(w, y, w, z); + public readonly float4 agaa => new(w, y, w, w); + public readonly float4 abrr => new(w, z, x, x); + public float4 abrg { readonly get => new(w, z, x, y); set { w = value.x; z = value.y; x = value.z; y = value.w; } } + public readonly float4 abrb => new(w, z, x, z); + public readonly float4 abra => new(w, z, x, w); + public float4 abgr { readonly get => new(w, z, y, x); set { w = value.x; z = value.y; y = value.z; x = value.w; } } + public readonly float4 abgg => new(w, z, y, y); + public readonly float4 abgb => new(w, z, y, z); + public readonly float4 abga => new(w, z, y, w); + public readonly float4 abbr => new(w, z, z, x); + public readonly float4 abbg => new(w, z, z, y); + public readonly float4 abbb => new(w, z, z, z); + public readonly float4 abba => new(w, z, z, w); + public readonly float4 abar => new(w, z, w, x); + public readonly float4 abag => new(w, z, w, y); + public readonly float4 abab => new(w, z, w, z); + public readonly float4 abaa => new(w, z, w, w); + public readonly float4 aarr => new(w, w, x, x); + public readonly float4 aarg => new(w, w, x, y); + public readonly float4 aarb => new(w, w, x, z); + public readonly float4 aara => new(w, w, x, w); + public readonly float4 aagr => new(w, w, y, x); + public readonly float4 aagg => new(w, w, y, y); + public readonly float4 aagb => new(w, w, y, z); + public readonly float4 aaga => new(w, w, y, w); + public readonly float4 aabr => new(w, w, z, x); + public readonly float4 aabg => new(w, w, z, y); + public readonly float4 aabb => new(w, w, z, z); + public readonly float4 aaba => new(w, w, z, w); + public readonly float4 aaar => new(w, w, w, x); + public readonly float4 aaag => new(w, w, w, y); + public readonly float4 aaab => new(w, w, w, z); + public readonly float4 aaaa => new(w, w, w, w); +} + +public partial struct int2 +{ + public int2(int2 xy) : this(xy.x, xy.y) { } + public int r { readonly get => x; set => x = value; } + public int g { readonly get => y; set => y = value; } + public readonly int2 xx => new(x, x); + public int2 xy { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public int2 yx { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly int2 yy => new(y, y); + public readonly int3 xxx => new(x, x, x); + public readonly int3 xxy => new(x, x, y); + public readonly int3 xyx => new(x, y, x); + public readonly int3 xyy => new(x, y, y); + public readonly int3 yxx => new(y, x, x); + public readonly int3 yxy => new(y, x, y); + public readonly int3 yyx => new(y, y, x); + public readonly int3 yyy => new(y, y, y); + public readonly int4 xxxx => new(x, x, x, x); + public readonly int4 xxxy => new(x, x, x, y); + public readonly int4 xxyx => new(x, x, y, x); + public readonly int4 xxyy => new(x, x, y, y); + public readonly int4 xyxx => new(x, y, x, x); + public readonly int4 xyxy => new(x, y, x, y); + public readonly int4 xyyx => new(x, y, y, x); + public readonly int4 xyyy => new(x, y, y, y); + public readonly int4 yxxx => new(y, x, x, x); + public readonly int4 yxxy => new(y, x, x, y); + public readonly int4 yxyx => new(y, x, y, x); + public readonly int4 yxyy => new(y, x, y, y); + public readonly int4 yyxx => new(y, y, x, x); + public readonly int4 yyxy => new(y, y, x, y); + public readonly int4 yyyx => new(y, y, y, x); + public readonly int4 yyyy => new(y, y, y, y); + public readonly int2 rr => new(x, x); + public int2 rg { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public int2 gr { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly int2 gg => new(y, y); + public readonly int3 rrr => new(x, x, x); + public readonly int3 rrg => new(x, x, y); + public readonly int3 rgr => new(x, y, x); + public readonly int3 rgg => new(x, y, y); + public readonly int3 grr => new(y, x, x); + public readonly int3 grg => new(y, x, y); + public readonly int3 ggr => new(y, y, x); + public readonly int3 ggg => new(y, y, y); + public readonly int4 rrrr => new(x, x, x, x); + public readonly int4 rrrg => new(x, x, x, y); + public readonly int4 rrgr => new(x, x, y, x); + public readonly int4 rrgg => new(x, x, y, y); + public readonly int4 rgrr => new(x, y, x, x); + public readonly int4 rgrg => new(x, y, x, y); + public readonly int4 rggr => new(x, y, y, x); + public readonly int4 rggg => new(x, y, y, y); + public readonly int4 grrr => new(y, x, x, x); + public readonly int4 grrg => new(y, x, x, y); + public readonly int4 grgr => new(y, x, y, x); + public readonly int4 grgg => new(y, x, y, y); + public readonly int4 ggrr => new(y, y, x, x); + public readonly int4 ggrg => new(y, y, x, y); + public readonly int4 gggr => new(y, y, y, x); + public readonly int4 gggg => new(y, y, y, y); +} + +public partial struct int3 +{ + public int3(int x, int2 yz) : this(x, yz.x, yz.y) { } + public int3(int2 xy, int z) : this(xy.x, xy.y, z) { } + public int3(int3 xyz) : this(xyz.x, xyz.y, xyz.z) { } + public int r { readonly get => x; set => x = value; } + public int g { readonly get => y; set => y = value; } + public int b { readonly get => z; set => z = value; } + public readonly int2 xx => new(x, x); + public int2 xy { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public int2 xz { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public int2 yx { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly int2 yy => new(y, y); + public int2 yz { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public int2 zx { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public int2 zy { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly int2 zz => new(z, z); + public readonly int3 xxx => new(x, x, x); + public readonly int3 xxy => new(x, x, y); + public readonly int3 xxz => new(x, x, z); + public readonly int3 xyx => new(x, y, x); + public readonly int3 xyy => new(x, y, y); + public int3 xyz { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public readonly int3 xzx => new(x, z, x); + public int3 xzy { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly int3 xzz => new(x, z, z); + public readonly int3 yxx => new(y, x, x); + public readonly int3 yxy => new(y, x, y); + public int3 yxz { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public readonly int3 yyx => new(y, y, x); + public readonly int3 yyy => new(y, y, y); + public readonly int3 yyz => new(y, y, z); + public int3 yzx { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly int3 yzy => new(y, z, y); + public readonly int3 yzz => new(y, z, z); + public readonly int3 zxx => new(z, x, x); + public int3 zxy { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly int3 zxz => new(z, x, z); + public int3 zyx { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly int3 zyy => new(z, y, y); + public readonly int3 zyz => new(z, y, z); + public readonly int3 zzx => new(z, z, x); + public readonly int3 zzy => new(z, z, y); + public readonly int3 zzz => new(z, z, z); + public readonly int4 xxxx => new(x, x, x, x); + public readonly int4 xxxy => new(x, x, x, y); + public readonly int4 xxxz => new(x, x, x, z); + public readonly int4 xxyx => new(x, x, y, x); + public readonly int4 xxyy => new(x, x, y, y); + public readonly int4 xxyz => new(x, x, y, z); + public readonly int4 xxzx => new(x, x, z, x); + public readonly int4 xxzy => new(x, x, z, y); + public readonly int4 xxzz => new(x, x, z, z); + public readonly int4 xyxx => new(x, y, x, x); + public readonly int4 xyxy => new(x, y, x, y); + public readonly int4 xyxz => new(x, y, x, z); + public readonly int4 xyyx => new(x, y, y, x); + public readonly int4 xyyy => new(x, y, y, y); + public readonly int4 xyyz => new(x, y, y, z); + public readonly int4 xyzx => new(x, y, z, x); + public readonly int4 xyzy => new(x, y, z, y); + public readonly int4 xyzz => new(x, y, z, z); + public readonly int4 xzxx => new(x, z, x, x); + public readonly int4 xzxy => new(x, z, x, y); + public readonly int4 xzxz => new(x, z, x, z); + public readonly int4 xzyx => new(x, z, y, x); + public readonly int4 xzyy => new(x, z, y, y); + public readonly int4 xzyz => new(x, z, y, z); + public readonly int4 xzzx => new(x, z, z, x); + public readonly int4 xzzy => new(x, z, z, y); + public readonly int4 xzzz => new(x, z, z, z); + public readonly int4 yxxx => new(y, x, x, x); + public readonly int4 yxxy => new(y, x, x, y); + public readonly int4 yxxz => new(y, x, x, z); + public readonly int4 yxyx => new(y, x, y, x); + public readonly int4 yxyy => new(y, x, y, y); + public readonly int4 yxyz => new(y, x, y, z); + public readonly int4 yxzx => new(y, x, z, x); + public readonly int4 yxzy => new(y, x, z, y); + public readonly int4 yxzz => new(y, x, z, z); + public readonly int4 yyxx => new(y, y, x, x); + public readonly int4 yyxy => new(y, y, x, y); + public readonly int4 yyxz => new(y, y, x, z); + public readonly int4 yyyx => new(y, y, y, x); + public readonly int4 yyyy => new(y, y, y, y); + public readonly int4 yyyz => new(y, y, y, z); + public readonly int4 yyzx => new(y, y, z, x); + public readonly int4 yyzy => new(y, y, z, y); + public readonly int4 yyzz => new(y, y, z, z); + public readonly int4 yzxx => new(y, z, x, x); + public readonly int4 yzxy => new(y, z, x, y); + public readonly int4 yzxz => new(y, z, x, z); + public readonly int4 yzyx => new(y, z, y, x); + public readonly int4 yzyy => new(y, z, y, y); + public readonly int4 yzyz => new(y, z, y, z); + public readonly int4 yzzx => new(y, z, z, x); + public readonly int4 yzzy => new(y, z, z, y); + public readonly int4 yzzz => new(y, z, z, z); + public readonly int4 zxxx => new(z, x, x, x); + public readonly int4 zxxy => new(z, x, x, y); + public readonly int4 zxxz => new(z, x, x, z); + public readonly int4 zxyx => new(z, x, y, x); + public readonly int4 zxyy => new(z, x, y, y); + public readonly int4 zxyz => new(z, x, y, z); + public readonly int4 zxzx => new(z, x, z, x); + public readonly int4 zxzy => new(z, x, z, y); + public readonly int4 zxzz => new(z, x, z, z); + public readonly int4 zyxx => new(z, y, x, x); + public readonly int4 zyxy => new(z, y, x, y); + public readonly int4 zyxz => new(z, y, x, z); + public readonly int4 zyyx => new(z, y, y, x); + public readonly int4 zyyy => new(z, y, y, y); + public readonly int4 zyyz => new(z, y, y, z); + public readonly int4 zyzx => new(z, y, z, x); + public readonly int4 zyzy => new(z, y, z, y); + public readonly int4 zyzz => new(z, y, z, z); + public readonly int4 zzxx => new(z, z, x, x); + public readonly int4 zzxy => new(z, z, x, y); + public readonly int4 zzxz => new(z, z, x, z); + public readonly int4 zzyx => new(z, z, y, x); + public readonly int4 zzyy => new(z, z, y, y); + public readonly int4 zzyz => new(z, z, y, z); + public readonly int4 zzzx => new(z, z, z, x); + public readonly int4 zzzy => new(z, z, z, y); + public readonly int4 zzzz => new(z, z, z, z); + public readonly int2 rr => new(x, x); + public int2 rg { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public int2 rb { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public int2 gr { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly int2 gg => new(y, y); + public int2 gb { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public int2 br { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public int2 bg { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly int2 bb => new(z, z); + public readonly int3 rrr => new(x, x, x); + public readonly int3 rrg => new(x, x, y); + public readonly int3 rrb => new(x, x, z); + public readonly int3 rgr => new(x, y, x); + public readonly int3 rgg => new(x, y, y); + public int3 rgb { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public readonly int3 rbr => new(x, z, x); + public int3 rbg { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly int3 rbb => new(x, z, z); + public readonly int3 grr => new(y, x, x); + public readonly int3 grg => new(y, x, y); + public int3 grb { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public readonly int3 ggr => new(y, y, x); + public readonly int3 ggg => new(y, y, y); + public readonly int3 ggb => new(y, y, z); + public int3 gbr { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly int3 gbg => new(y, z, y); + public readonly int3 gbb => new(y, z, z); + public readonly int3 brr => new(z, x, x); + public int3 brg { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly int3 brb => new(z, x, z); + public int3 bgr { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly int3 bgg => new(z, y, y); + public readonly int3 bgb => new(z, y, z); + public readonly int3 bbr => new(z, z, x); + public readonly int3 bbg => new(z, z, y); + public readonly int3 bbb => new(z, z, z); + public readonly int4 rrrr => new(x, x, x, x); + public readonly int4 rrrg => new(x, x, x, y); + public readonly int4 rrrb => new(x, x, x, z); + public readonly int4 rrgr => new(x, x, y, x); + public readonly int4 rrgg => new(x, x, y, y); + public readonly int4 rrgb => new(x, x, y, z); + public readonly int4 rrbr => new(x, x, z, x); + public readonly int4 rrbg => new(x, x, z, y); + public readonly int4 rrbb => new(x, x, z, z); + public readonly int4 rgrr => new(x, y, x, x); + public readonly int4 rgrg => new(x, y, x, y); + public readonly int4 rgrb => new(x, y, x, z); + public readonly int4 rggr => new(x, y, y, x); + public readonly int4 rggg => new(x, y, y, y); + public readonly int4 rggb => new(x, y, y, z); + public readonly int4 rgbr => new(x, y, z, x); + public readonly int4 rgbg => new(x, y, z, y); + public readonly int4 rgbb => new(x, y, z, z); + public readonly int4 rbrr => new(x, z, x, x); + public readonly int4 rbrg => new(x, z, x, y); + public readonly int4 rbrb => new(x, z, x, z); + public readonly int4 rbgr => new(x, z, y, x); + public readonly int4 rbgg => new(x, z, y, y); + public readonly int4 rbgb => new(x, z, y, z); + public readonly int4 rbbr => new(x, z, z, x); + public readonly int4 rbbg => new(x, z, z, y); + public readonly int4 rbbb => new(x, z, z, z); + public readonly int4 grrr => new(y, x, x, x); + public readonly int4 grrg => new(y, x, x, y); + public readonly int4 grrb => new(y, x, x, z); + public readonly int4 grgr => new(y, x, y, x); + public readonly int4 grgg => new(y, x, y, y); + public readonly int4 grgb => new(y, x, y, z); + public readonly int4 grbr => new(y, x, z, x); + public readonly int4 grbg => new(y, x, z, y); + public readonly int4 grbb => new(y, x, z, z); + public readonly int4 ggrr => new(y, y, x, x); + public readonly int4 ggrg => new(y, y, x, y); + public readonly int4 ggrb => new(y, y, x, z); + public readonly int4 gggr => new(y, y, y, x); + public readonly int4 gggg => new(y, y, y, y); + public readonly int4 gggb => new(y, y, y, z); + public readonly int4 ggbr => new(y, y, z, x); + public readonly int4 ggbg => new(y, y, z, y); + public readonly int4 ggbb => new(y, y, z, z); + public readonly int4 gbrr => new(y, z, x, x); + public readonly int4 gbrg => new(y, z, x, y); + public readonly int4 gbrb => new(y, z, x, z); + public readonly int4 gbgr => new(y, z, y, x); + public readonly int4 gbgg => new(y, z, y, y); + public readonly int4 gbgb => new(y, z, y, z); + public readonly int4 gbbr => new(y, z, z, x); + public readonly int4 gbbg => new(y, z, z, y); + public readonly int4 gbbb => new(y, z, z, z); + public readonly int4 brrr => new(z, x, x, x); + public readonly int4 brrg => new(z, x, x, y); + public readonly int4 brrb => new(z, x, x, z); + public readonly int4 brgr => new(z, x, y, x); + public readonly int4 brgg => new(z, x, y, y); + public readonly int4 brgb => new(z, x, y, z); + public readonly int4 brbr => new(z, x, z, x); + public readonly int4 brbg => new(z, x, z, y); + public readonly int4 brbb => new(z, x, z, z); + public readonly int4 bgrr => new(z, y, x, x); + public readonly int4 bgrg => new(z, y, x, y); + public readonly int4 bgrb => new(z, y, x, z); + public readonly int4 bggr => new(z, y, y, x); + public readonly int4 bggg => new(z, y, y, y); + public readonly int4 bggb => new(z, y, y, z); + public readonly int4 bgbr => new(z, y, z, x); + public readonly int4 bgbg => new(z, y, z, y); + public readonly int4 bgbb => new(z, y, z, z); + public readonly int4 bbrr => new(z, z, x, x); + public readonly int4 bbrg => new(z, z, x, y); + public readonly int4 bbrb => new(z, z, x, z); + public readonly int4 bbgr => new(z, z, y, x); + public readonly int4 bbgg => new(z, z, y, y); + public readonly int4 bbgb => new(z, z, y, z); + public readonly int4 bbbr => new(z, z, z, x); + public readonly int4 bbbg => new(z, z, z, y); + public readonly int4 bbbb => new(z, z, z, z); +} + +public partial struct int4 +{ + public int4(int x, int y, int2 zw) : this(x, y, zw.x, zw.y) { } + public int4(int x, int2 yz, int w) : this(x, yz.x, yz.y, w) { } + public int4(int x, int3 yzw) : this(x, yzw.x, yzw.y, yzw.z) { } + public int4(int2 xy, int z, int w) : this(xy.x, xy.y, z, w) { } + public int4(int2 xy, int2 zw) : this(xy.x, xy.y, zw.x, zw.y) { } + public int4(int3 xyz, int w) : this(xyz.x, xyz.y, xyz.z, w) { } + public int4(int4 xyzw) : this(xyzw.x, xyzw.y, xyzw.z, xyzw.w) { } + public int r { readonly get => x; set => x = value; } + public int g { readonly get => y; set => y = value; } + public int b { readonly get => z; set => z = value; } + public int a { readonly get => w; set => w = value; } + public readonly int2 xx => new(x, x); + public int2 xy { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public int2 xz { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public int2 xw { readonly get => new(x, w); set { x = value.x; w = value.y; } } + public int2 yx { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly int2 yy => new(y, y); + public int2 yz { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public int2 yw { readonly get => new(y, w); set { y = value.x; w = value.y; } } + public int2 zx { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public int2 zy { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly int2 zz => new(z, z); + public int2 zw { readonly get => new(z, w); set { z = value.x; w = value.y; } } + public int2 wx { readonly get => new(w, x); set { w = value.x; x = value.y; } } + public int2 wy { readonly get => new(w, y); set { w = value.x; y = value.y; } } + public int2 wz { readonly get => new(w, z); set { w = value.x; z = value.y; } } + public readonly int2 ww => new(w, w); + public readonly int3 xxx => new(x, x, x); + public readonly int3 xxy => new(x, x, y); + public readonly int3 xxz => new(x, x, z); + public readonly int3 xxw => new(x, x, w); + public readonly int3 xyx => new(x, y, x); + public readonly int3 xyy => new(x, y, y); + public int3 xyz { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public int3 xyw { readonly get => new(x, y, w); set { x = value.x; y = value.y; w = value.z; } } + public readonly int3 xzx => new(x, z, x); + public int3 xzy { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly int3 xzz => new(x, z, z); + public int3 xzw { readonly get => new(x, z, w); set { x = value.x; z = value.y; w = value.z; } } + public readonly int3 xwx => new(x, w, x); + public int3 xwy { readonly get => new(x, w, y); set { x = value.x; w = value.y; y = value.z; } } + public int3 xwz { readonly get => new(x, w, z); set { x = value.x; w = value.y; z = value.z; } } + public readonly int3 xww => new(x, w, w); + public readonly int3 yxx => new(y, x, x); + public readonly int3 yxy => new(y, x, y); + public int3 yxz { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public int3 yxw { readonly get => new(y, x, w); set { y = value.x; x = value.y; w = value.z; } } + public readonly int3 yyx => new(y, y, x); + public readonly int3 yyy => new(y, y, y); + public readonly int3 yyz => new(y, y, z); + public readonly int3 yyw => new(y, y, w); + public int3 yzx { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly int3 yzy => new(y, z, y); + public readonly int3 yzz => new(y, z, z); + public int3 yzw { readonly get => new(y, z, w); set { y = value.x; z = value.y; w = value.z; } } + public int3 ywx { readonly get => new(y, w, x); set { y = value.x; w = value.y; x = value.z; } } + public readonly int3 ywy => new(y, w, y); + public int3 ywz { readonly get => new(y, w, z); set { y = value.x; w = value.y; z = value.z; } } + public readonly int3 yww => new(y, w, w); + public readonly int3 zxx => new(z, x, x); + public int3 zxy { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly int3 zxz => new(z, x, z); + public int3 zxw { readonly get => new(z, x, w); set { z = value.x; x = value.y; w = value.z; } } + public int3 zyx { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly int3 zyy => new(z, y, y); + public readonly int3 zyz => new(z, y, z); + public int3 zyw { readonly get => new(z, y, w); set { z = value.x; y = value.y; w = value.z; } } + public readonly int3 zzx => new(z, z, x); + public readonly int3 zzy => new(z, z, y); + public readonly int3 zzz => new(z, z, z); + public readonly int3 zzw => new(z, z, w); + public int3 zwx { readonly get => new(z, w, x); set { z = value.x; w = value.y; x = value.z; } } + public int3 zwy { readonly get => new(z, w, y); set { z = value.x; w = value.y; y = value.z; } } + public readonly int3 zwz => new(z, w, z); + public readonly int3 zww => new(z, w, w); + public readonly int3 wxx => new(w, x, x); + public int3 wxy { readonly get => new(w, x, y); set { w = value.x; x = value.y; y = value.z; } } + public int3 wxz { readonly get => new(w, x, z); set { w = value.x; x = value.y; z = value.z; } } + public readonly int3 wxw => new(w, x, w); + public int3 wyx { readonly get => new(w, y, x); set { w = value.x; y = value.y; x = value.z; } } + public readonly int3 wyy => new(w, y, y); + public int3 wyz { readonly get => new(w, y, z); set { w = value.x; y = value.y; z = value.z; } } + public readonly int3 wyw => new(w, y, w); + public int3 wzx { readonly get => new(w, z, x); set { w = value.x; z = value.y; x = value.z; } } + public int3 wzy { readonly get => new(w, z, y); set { w = value.x; z = value.y; y = value.z; } } + public readonly int3 wzz => new(w, z, z); + public readonly int3 wzw => new(w, z, w); + public readonly int3 wwx => new(w, w, x); + public readonly int3 wwy => new(w, w, y); + public readonly int3 wwz => new(w, w, z); + public readonly int3 www => new(w, w, w); + public readonly int4 xxxx => new(x, x, x, x); + public readonly int4 xxxy => new(x, x, x, y); + public readonly int4 xxxz => new(x, x, x, z); + public readonly int4 xxxw => new(x, x, x, w); + public readonly int4 xxyx => new(x, x, y, x); + public readonly int4 xxyy => new(x, x, y, y); + public readonly int4 xxyz => new(x, x, y, z); + public readonly int4 xxyw => new(x, x, y, w); + public readonly int4 xxzx => new(x, x, z, x); + public readonly int4 xxzy => new(x, x, z, y); + public readonly int4 xxzz => new(x, x, z, z); + public readonly int4 xxzw => new(x, x, z, w); + public readonly int4 xxwx => new(x, x, w, x); + public readonly int4 xxwy => new(x, x, w, y); + public readonly int4 xxwz => new(x, x, w, z); + public readonly int4 xxww => new(x, x, w, w); + public readonly int4 xyxx => new(x, y, x, x); + public readonly int4 xyxy => new(x, y, x, y); + public readonly int4 xyxz => new(x, y, x, z); + public readonly int4 xyxw => new(x, y, x, w); + public readonly int4 xyyx => new(x, y, y, x); + public readonly int4 xyyy => new(x, y, y, y); + public readonly int4 xyyz => new(x, y, y, z); + public readonly int4 xyyw => new(x, y, y, w); + public readonly int4 xyzx => new(x, y, z, x); + public readonly int4 xyzy => new(x, y, z, y); + public readonly int4 xyzz => new(x, y, z, z); + public int4 xyzw { readonly get => new(x, y, z, w); set { x = value.x; y = value.y; z = value.z; w = value.w; } } + public readonly int4 xywx => new(x, y, w, x); + public readonly int4 xywy => new(x, y, w, y); + public int4 xywz { readonly get => new(x, y, w, z); set { x = value.x; y = value.y; w = value.z; z = value.w; } } + public readonly int4 xyww => new(x, y, w, w); + public readonly int4 xzxx => new(x, z, x, x); + public readonly int4 xzxy => new(x, z, x, y); + public readonly int4 xzxz => new(x, z, x, z); + public readonly int4 xzxw => new(x, z, x, w); + public readonly int4 xzyx => new(x, z, y, x); + public readonly int4 xzyy => new(x, z, y, y); + public readonly int4 xzyz => new(x, z, y, z); + public int4 xzyw { readonly get => new(x, z, y, w); set { x = value.x; z = value.y; y = value.z; w = value.w; } } + public readonly int4 xzzx => new(x, z, z, x); + public readonly int4 xzzy => new(x, z, z, y); + public readonly int4 xzzz => new(x, z, z, z); + public readonly int4 xzzw => new(x, z, z, w); + public readonly int4 xzwx => new(x, z, w, x); + public int4 xzwy { readonly get => new(x, z, w, y); set { x = value.x; z = value.y; w = value.z; y = value.w; } } + public readonly int4 xzwz => new(x, z, w, z); + public readonly int4 xzww => new(x, z, w, w); + public readonly int4 xwxx => new(x, w, x, x); + public readonly int4 xwxy => new(x, w, x, y); + public readonly int4 xwxz => new(x, w, x, z); + public readonly int4 xwxw => new(x, w, x, w); + public readonly int4 xwyx => new(x, w, y, x); + public readonly int4 xwyy => new(x, w, y, y); + public int4 xwyz { readonly get => new(x, w, y, z); set { x = value.x; w = value.y; y = value.z; z = value.w; } } + public readonly int4 xwyw => new(x, w, y, w); + public readonly int4 xwzx => new(x, w, z, x); + public int4 xwzy { readonly get => new(x, w, z, y); set { x = value.x; w = value.y; z = value.z; y = value.w; } } + public readonly int4 xwzz => new(x, w, z, z); + public readonly int4 xwzw => new(x, w, z, w); + public readonly int4 xwwx => new(x, w, w, x); + public readonly int4 xwwy => new(x, w, w, y); + public readonly int4 xwwz => new(x, w, w, z); + public readonly int4 xwww => new(x, w, w, w); + public readonly int4 yxxx => new(y, x, x, x); + public readonly int4 yxxy => new(y, x, x, y); + public readonly int4 yxxz => new(y, x, x, z); + public readonly int4 yxxw => new(y, x, x, w); + public readonly int4 yxyx => new(y, x, y, x); + public readonly int4 yxyy => new(y, x, y, y); + public readonly int4 yxyz => new(y, x, y, z); + public readonly int4 yxyw => new(y, x, y, w); + public readonly int4 yxzx => new(y, x, z, x); + public readonly int4 yxzy => new(y, x, z, y); + public readonly int4 yxzz => new(y, x, z, z); + public int4 yxzw { readonly get => new(y, x, z, w); set { y = value.x; x = value.y; z = value.z; w = value.w; } } + public readonly int4 yxwx => new(y, x, w, x); + public readonly int4 yxwy => new(y, x, w, y); + public int4 yxwz { readonly get => new(y, x, w, z); set { y = value.x; x = value.y; w = value.z; z = value.w; } } + public readonly int4 yxww => new(y, x, w, w); + public readonly int4 yyxx => new(y, y, x, x); + public readonly int4 yyxy => new(y, y, x, y); + public readonly int4 yyxz => new(y, y, x, z); + public readonly int4 yyxw => new(y, y, x, w); + public readonly int4 yyyx => new(y, y, y, x); + public readonly int4 yyyy => new(y, y, y, y); + public readonly int4 yyyz => new(y, y, y, z); + public readonly int4 yyyw => new(y, y, y, w); + public readonly int4 yyzx => new(y, y, z, x); + public readonly int4 yyzy => new(y, y, z, y); + public readonly int4 yyzz => new(y, y, z, z); + public readonly int4 yyzw => new(y, y, z, w); + public readonly int4 yywx => new(y, y, w, x); + public readonly int4 yywy => new(y, y, w, y); + public readonly int4 yywz => new(y, y, w, z); + public readonly int4 yyww => new(y, y, w, w); + public readonly int4 yzxx => new(y, z, x, x); + public readonly int4 yzxy => new(y, z, x, y); + public readonly int4 yzxz => new(y, z, x, z); + public int4 yzxw { readonly get => new(y, z, x, w); set { y = value.x; z = value.y; x = value.z; w = value.w; } } + public readonly int4 yzyx => new(y, z, y, x); + public readonly int4 yzyy => new(y, z, y, y); + public readonly int4 yzyz => new(y, z, y, z); + public readonly int4 yzyw => new(y, z, y, w); + public readonly int4 yzzx => new(y, z, z, x); + public readonly int4 yzzy => new(y, z, z, y); + public readonly int4 yzzz => new(y, z, z, z); + public readonly int4 yzzw => new(y, z, z, w); + public int4 yzwx { readonly get => new(y, z, w, x); set { y = value.x; z = value.y; w = value.z; x = value.w; } } + public readonly int4 yzwy => new(y, z, w, y); + public readonly int4 yzwz => new(y, z, w, z); + public readonly int4 yzww => new(y, z, w, w); + public readonly int4 ywxx => new(y, w, x, x); + public readonly int4 ywxy => new(y, w, x, y); + public int4 ywxz { readonly get => new(y, w, x, z); set { y = value.x; w = value.y; x = value.z; z = value.w; } } + public readonly int4 ywxw => new(y, w, x, w); + public readonly int4 ywyx => new(y, w, y, x); + public readonly int4 ywyy => new(y, w, y, y); + public readonly int4 ywyz => new(y, w, y, z); + public readonly int4 ywyw => new(y, w, y, w); + public int4 ywzx { readonly get => new(y, w, z, x); set { y = value.x; w = value.y; z = value.z; x = value.w; } } + public readonly int4 ywzy => new(y, w, z, y); + public readonly int4 ywzz => new(y, w, z, z); + public readonly int4 ywzw => new(y, w, z, w); + public readonly int4 ywwx => new(y, w, w, x); + public readonly int4 ywwy => new(y, w, w, y); + public readonly int4 ywwz => new(y, w, w, z); + public readonly int4 ywww => new(y, w, w, w); + public readonly int4 zxxx => new(z, x, x, x); + public readonly int4 zxxy => new(z, x, x, y); + public readonly int4 zxxz => new(z, x, x, z); + public readonly int4 zxxw => new(z, x, x, w); + public readonly int4 zxyx => new(z, x, y, x); + public readonly int4 zxyy => new(z, x, y, y); + public readonly int4 zxyz => new(z, x, y, z); + public int4 zxyw { readonly get => new(z, x, y, w); set { z = value.x; x = value.y; y = value.z; w = value.w; } } + public readonly int4 zxzx => new(z, x, z, x); + public readonly int4 zxzy => new(z, x, z, y); + public readonly int4 zxzz => new(z, x, z, z); + public readonly int4 zxzw => new(z, x, z, w); + public readonly int4 zxwx => new(z, x, w, x); + public int4 zxwy { readonly get => new(z, x, w, y); set { z = value.x; x = value.y; w = value.z; y = value.w; } } + public readonly int4 zxwz => new(z, x, w, z); + public readonly int4 zxww => new(z, x, w, w); + public readonly int4 zyxx => new(z, y, x, x); + public readonly int4 zyxy => new(z, y, x, y); + public readonly int4 zyxz => new(z, y, x, z); + public int4 zyxw { readonly get => new(z, y, x, w); set { z = value.x; y = value.y; x = value.z; w = value.w; } } + public readonly int4 zyyx => new(z, y, y, x); + public readonly int4 zyyy => new(z, y, y, y); + public readonly int4 zyyz => new(z, y, y, z); + public readonly int4 zyyw => new(z, y, y, w); + public readonly int4 zyzx => new(z, y, z, x); + public readonly int4 zyzy => new(z, y, z, y); + public readonly int4 zyzz => new(z, y, z, z); + public readonly int4 zyzw => new(z, y, z, w); + public int4 zywx { readonly get => new(z, y, w, x); set { z = value.x; y = value.y; w = value.z; x = value.w; } } + public readonly int4 zywy => new(z, y, w, y); + public readonly int4 zywz => new(z, y, w, z); + public readonly int4 zyww => new(z, y, w, w); + public readonly int4 zzxx => new(z, z, x, x); + public readonly int4 zzxy => new(z, z, x, y); + public readonly int4 zzxz => new(z, z, x, z); + public readonly int4 zzxw => new(z, z, x, w); + public readonly int4 zzyx => new(z, z, y, x); + public readonly int4 zzyy => new(z, z, y, y); + public readonly int4 zzyz => new(z, z, y, z); + public readonly int4 zzyw => new(z, z, y, w); + public readonly int4 zzzx => new(z, z, z, x); + public readonly int4 zzzy => new(z, z, z, y); + public readonly int4 zzzz => new(z, z, z, z); + public readonly int4 zzzw => new(z, z, z, w); + public readonly int4 zzwx => new(z, z, w, x); + public readonly int4 zzwy => new(z, z, w, y); + public readonly int4 zzwz => new(z, z, w, z); + public readonly int4 zzww => new(z, z, w, w); + public readonly int4 zwxx => new(z, w, x, x); + public int4 zwxy { readonly get => new(z, w, x, y); set { z = value.x; w = value.y; x = value.z; y = value.w; } } + public readonly int4 zwxz => new(z, w, x, z); + public readonly int4 zwxw => new(z, w, x, w); + public int4 zwyx { readonly get => new(z, w, y, x); set { z = value.x; w = value.y; y = value.z; x = value.w; } } + public readonly int4 zwyy => new(z, w, y, y); + public readonly int4 zwyz => new(z, w, y, z); + public readonly int4 zwyw => new(z, w, y, w); + public readonly int4 zwzx => new(z, w, z, x); + public readonly int4 zwzy => new(z, w, z, y); + public readonly int4 zwzz => new(z, w, z, z); + public readonly int4 zwzw => new(z, w, z, w); + public readonly int4 zwwx => new(z, w, w, x); + public readonly int4 zwwy => new(z, w, w, y); + public readonly int4 zwwz => new(z, w, w, z); + public readonly int4 zwww => new(z, w, w, w); + public readonly int4 wxxx => new(w, x, x, x); + public readonly int4 wxxy => new(w, x, x, y); + public readonly int4 wxxz => new(w, x, x, z); + public readonly int4 wxxw => new(w, x, x, w); + public readonly int4 wxyx => new(w, x, y, x); + public readonly int4 wxyy => new(w, x, y, y); + public int4 wxyz { readonly get => new(w, x, y, z); set { w = value.x; x = value.y; y = value.z; z = value.w; } } + public readonly int4 wxyw => new(w, x, y, w); + public readonly int4 wxzx => new(w, x, z, x); + public int4 wxzy { readonly get => new(w, x, z, y); set { w = value.x; x = value.y; z = value.z; y = value.w; } } + public readonly int4 wxzz => new(w, x, z, z); + public readonly int4 wxzw => new(w, x, z, w); + public readonly int4 wxwx => new(w, x, w, x); + public readonly int4 wxwy => new(w, x, w, y); + public readonly int4 wxwz => new(w, x, w, z); + public readonly int4 wxww => new(w, x, w, w); + public readonly int4 wyxx => new(w, y, x, x); + public readonly int4 wyxy => new(w, y, x, y); + public int4 wyxz { readonly get => new(w, y, x, z); set { w = value.x; y = value.y; x = value.z; z = value.w; } } + public readonly int4 wyxw => new(w, y, x, w); + public readonly int4 wyyx => new(w, y, y, x); + public readonly int4 wyyy => new(w, y, y, y); + public readonly int4 wyyz => new(w, y, y, z); + public readonly int4 wyyw => new(w, y, y, w); + public int4 wyzx { readonly get => new(w, y, z, x); set { w = value.x; y = value.y; z = value.z; x = value.w; } } + public readonly int4 wyzy => new(w, y, z, y); + public readonly int4 wyzz => new(w, y, z, z); + public readonly int4 wyzw => new(w, y, z, w); + public readonly int4 wywx => new(w, y, w, x); + public readonly int4 wywy => new(w, y, w, y); + public readonly int4 wywz => new(w, y, w, z); + public readonly int4 wyww => new(w, y, w, w); + public readonly int4 wzxx => new(w, z, x, x); + public int4 wzxy { readonly get => new(w, z, x, y); set { w = value.x; z = value.y; x = value.z; y = value.w; } } + public readonly int4 wzxz => new(w, z, x, z); + public readonly int4 wzxw => new(w, z, x, w); + public int4 wzyx { readonly get => new(w, z, y, x); set { w = value.x; z = value.y; y = value.z; x = value.w; } } + public readonly int4 wzyy => new(w, z, y, y); + public readonly int4 wzyz => new(w, z, y, z); + public readonly int4 wzyw => new(w, z, y, w); + public readonly int4 wzzx => new(w, z, z, x); + public readonly int4 wzzy => new(w, z, z, y); + public readonly int4 wzzz => new(w, z, z, z); + public readonly int4 wzzw => new(w, z, z, w); + public readonly int4 wzwx => new(w, z, w, x); + public readonly int4 wzwy => new(w, z, w, y); + public readonly int4 wzwz => new(w, z, w, z); + public readonly int4 wzww => new(w, z, w, w); + public readonly int4 wwxx => new(w, w, x, x); + public readonly int4 wwxy => new(w, w, x, y); + public readonly int4 wwxz => new(w, w, x, z); + public readonly int4 wwxw => new(w, w, x, w); + public readonly int4 wwyx => new(w, w, y, x); + public readonly int4 wwyy => new(w, w, y, y); + public readonly int4 wwyz => new(w, w, y, z); + public readonly int4 wwyw => new(w, w, y, w); + public readonly int4 wwzx => new(w, w, z, x); + public readonly int4 wwzy => new(w, w, z, y); + public readonly int4 wwzz => new(w, w, z, z); + public readonly int4 wwzw => new(w, w, z, w); + public readonly int4 wwwx => new(w, w, w, x); + public readonly int4 wwwy => new(w, w, w, y); + public readonly int4 wwwz => new(w, w, w, z); + public readonly int4 wwww => new(w, w, w, w); + public readonly int2 rr => new(x, x); + public int2 rg { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public int2 rb { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public int2 ra { readonly get => new(x, w); set { x = value.x; w = value.y; } } + public int2 gr { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly int2 gg => new(y, y); + public int2 gb { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public int2 ga { readonly get => new(y, w); set { y = value.x; w = value.y; } } + public int2 br { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public int2 bg { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly int2 bb => new(z, z); + public int2 ba { readonly get => new(z, w); set { z = value.x; w = value.y; } } + public int2 ar { readonly get => new(w, x); set { w = value.x; x = value.y; } } + public int2 ag { readonly get => new(w, y); set { w = value.x; y = value.y; } } + public int2 ab { readonly get => new(w, z); set { w = value.x; z = value.y; } } + public readonly int2 aa => new(w, w); + public readonly int3 rrr => new(x, x, x); + public readonly int3 rrg => new(x, x, y); + public readonly int3 rrb => new(x, x, z); + public readonly int3 rra => new(x, x, w); + public readonly int3 rgr => new(x, y, x); + public readonly int3 rgg => new(x, y, y); + public int3 rgb { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public int3 rga { readonly get => new(x, y, w); set { x = value.x; y = value.y; w = value.z; } } + public readonly int3 rbr => new(x, z, x); + public int3 rbg { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly int3 rbb => new(x, z, z); + public int3 rba { readonly get => new(x, z, w); set { x = value.x; z = value.y; w = value.z; } } + public readonly int3 rar => new(x, w, x); + public int3 rag { readonly get => new(x, w, y); set { x = value.x; w = value.y; y = value.z; } } + public int3 rab { readonly get => new(x, w, z); set { x = value.x; w = value.y; z = value.z; } } + public readonly int3 raa => new(x, w, w); + public readonly int3 grr => new(y, x, x); + public readonly int3 grg => new(y, x, y); + public int3 grb { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public int3 gra { readonly get => new(y, x, w); set { y = value.x; x = value.y; w = value.z; } } + public readonly int3 ggr => new(y, y, x); + public readonly int3 ggg => new(y, y, y); + public readonly int3 ggb => new(y, y, z); + public readonly int3 gga => new(y, y, w); + public int3 gbr { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly int3 gbg => new(y, z, y); + public readonly int3 gbb => new(y, z, z); + public int3 gba { readonly get => new(y, z, w); set { y = value.x; z = value.y; w = value.z; } } + public int3 gar { readonly get => new(y, w, x); set { y = value.x; w = value.y; x = value.z; } } + public readonly int3 gag => new(y, w, y); + public int3 gab { readonly get => new(y, w, z); set { y = value.x; w = value.y; z = value.z; } } + public readonly int3 gaa => new(y, w, w); + public readonly int3 brr => new(z, x, x); + public int3 brg { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly int3 brb => new(z, x, z); + public int3 bra { readonly get => new(z, x, w); set { z = value.x; x = value.y; w = value.z; } } + public int3 bgr { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly int3 bgg => new(z, y, y); + public readonly int3 bgb => new(z, y, z); + public int3 bga { readonly get => new(z, y, w); set { z = value.x; y = value.y; w = value.z; } } + public readonly int3 bbr => new(z, z, x); + public readonly int3 bbg => new(z, z, y); + public readonly int3 bbb => new(z, z, z); + public readonly int3 bba => new(z, z, w); + public int3 bar { readonly get => new(z, w, x); set { z = value.x; w = value.y; x = value.z; } } + public int3 bag { readonly get => new(z, w, y); set { z = value.x; w = value.y; y = value.z; } } + public readonly int3 bab => new(z, w, z); + public readonly int3 baa => new(z, w, w); + public readonly int3 arr => new(w, x, x); + public int3 arg { readonly get => new(w, x, y); set { w = value.x; x = value.y; y = value.z; } } + public int3 arb { readonly get => new(w, x, z); set { w = value.x; x = value.y; z = value.z; } } + public readonly int3 ara => new(w, x, w); + public int3 agr { readonly get => new(w, y, x); set { w = value.x; y = value.y; x = value.z; } } + public readonly int3 agg => new(w, y, y); + public int3 agb { readonly get => new(w, y, z); set { w = value.x; y = value.y; z = value.z; } } + public readonly int3 aga => new(w, y, w); + public int3 abr { readonly get => new(w, z, x); set { w = value.x; z = value.y; x = value.z; } } + public int3 abg { readonly get => new(w, z, y); set { w = value.x; z = value.y; y = value.z; } } + public readonly int3 abb => new(w, z, z); + public readonly int3 aba => new(w, z, w); + public readonly int3 aar => new(w, w, x); + public readonly int3 aag => new(w, w, y); + public readonly int3 aab => new(w, w, z); + public readonly int3 aaa => new(w, w, w); + public readonly int4 rrrr => new(x, x, x, x); + public readonly int4 rrrg => new(x, x, x, y); + public readonly int4 rrrb => new(x, x, x, z); + public readonly int4 rrra => new(x, x, x, w); + public readonly int4 rrgr => new(x, x, y, x); + public readonly int4 rrgg => new(x, x, y, y); + public readonly int4 rrgb => new(x, x, y, z); + public readonly int4 rrga => new(x, x, y, w); + public readonly int4 rrbr => new(x, x, z, x); + public readonly int4 rrbg => new(x, x, z, y); + public readonly int4 rrbb => new(x, x, z, z); + public readonly int4 rrba => new(x, x, z, w); + public readonly int4 rrar => new(x, x, w, x); + public readonly int4 rrag => new(x, x, w, y); + public readonly int4 rrab => new(x, x, w, z); + public readonly int4 rraa => new(x, x, w, w); + public readonly int4 rgrr => new(x, y, x, x); + public readonly int4 rgrg => new(x, y, x, y); + public readonly int4 rgrb => new(x, y, x, z); + public readonly int4 rgra => new(x, y, x, w); + public readonly int4 rggr => new(x, y, y, x); + public readonly int4 rggg => new(x, y, y, y); + public readonly int4 rggb => new(x, y, y, z); + public readonly int4 rgga => new(x, y, y, w); + public readonly int4 rgbr => new(x, y, z, x); + public readonly int4 rgbg => new(x, y, z, y); + public readonly int4 rgbb => new(x, y, z, z); + public int4 rgba { readonly get => new(x, y, z, w); set { x = value.x; y = value.y; z = value.z; w = value.w; } } + public readonly int4 rgar => new(x, y, w, x); + public readonly int4 rgag => new(x, y, w, y); + public int4 rgab { readonly get => new(x, y, w, z); set { x = value.x; y = value.y; w = value.z; z = value.w; } } + public readonly int4 rgaa => new(x, y, w, w); + public readonly int4 rbrr => new(x, z, x, x); + public readonly int4 rbrg => new(x, z, x, y); + public readonly int4 rbrb => new(x, z, x, z); + public readonly int4 rbra => new(x, z, x, w); + public readonly int4 rbgr => new(x, z, y, x); + public readonly int4 rbgg => new(x, z, y, y); + public readonly int4 rbgb => new(x, z, y, z); + public int4 rbga { readonly get => new(x, z, y, w); set { x = value.x; z = value.y; y = value.z; w = value.w; } } + public readonly int4 rbbr => new(x, z, z, x); + public readonly int4 rbbg => new(x, z, z, y); + public readonly int4 rbbb => new(x, z, z, z); + public readonly int4 rbba => new(x, z, z, w); + public readonly int4 rbar => new(x, z, w, x); + public int4 rbag { readonly get => new(x, z, w, y); set { x = value.x; z = value.y; w = value.z; y = value.w; } } + public readonly int4 rbab => new(x, z, w, z); + public readonly int4 rbaa => new(x, z, w, w); + public readonly int4 rarr => new(x, w, x, x); + public readonly int4 rarg => new(x, w, x, y); + public readonly int4 rarb => new(x, w, x, z); + public readonly int4 rara => new(x, w, x, w); + public readonly int4 ragr => new(x, w, y, x); + public readonly int4 ragg => new(x, w, y, y); + public int4 ragb { readonly get => new(x, w, y, z); set { x = value.x; w = value.y; y = value.z; z = value.w; } } + public readonly int4 raga => new(x, w, y, w); + public readonly int4 rabr => new(x, w, z, x); + public int4 rabg { readonly get => new(x, w, z, y); set { x = value.x; w = value.y; z = value.z; y = value.w; } } + public readonly int4 rabb => new(x, w, z, z); + public readonly int4 raba => new(x, w, z, w); + public readonly int4 raar => new(x, w, w, x); + public readonly int4 raag => new(x, w, w, y); + public readonly int4 raab => new(x, w, w, z); + public readonly int4 raaa => new(x, w, w, w); + public readonly int4 grrr => new(y, x, x, x); + public readonly int4 grrg => new(y, x, x, y); + public readonly int4 grrb => new(y, x, x, z); + public readonly int4 grra => new(y, x, x, w); + public readonly int4 grgr => new(y, x, y, x); + public readonly int4 grgg => new(y, x, y, y); + public readonly int4 grgb => new(y, x, y, z); + public readonly int4 grga => new(y, x, y, w); + public readonly int4 grbr => new(y, x, z, x); + public readonly int4 grbg => new(y, x, z, y); + public readonly int4 grbb => new(y, x, z, z); + public int4 grba { readonly get => new(y, x, z, w); set { y = value.x; x = value.y; z = value.z; w = value.w; } } + public readonly int4 grar => new(y, x, w, x); + public readonly int4 grag => new(y, x, w, y); + public int4 grab { readonly get => new(y, x, w, z); set { y = value.x; x = value.y; w = value.z; z = value.w; } } + public readonly int4 graa => new(y, x, w, w); + public readonly int4 ggrr => new(y, y, x, x); + public readonly int4 ggrg => new(y, y, x, y); + public readonly int4 ggrb => new(y, y, x, z); + public readonly int4 ggra => new(y, y, x, w); + public readonly int4 gggr => new(y, y, y, x); + public readonly int4 gggg => new(y, y, y, y); + public readonly int4 gggb => new(y, y, y, z); + public readonly int4 ggga => new(y, y, y, w); + public readonly int4 ggbr => new(y, y, z, x); + public readonly int4 ggbg => new(y, y, z, y); + public readonly int4 ggbb => new(y, y, z, z); + public readonly int4 ggba => new(y, y, z, w); + public readonly int4 ggar => new(y, y, w, x); + public readonly int4 ggag => new(y, y, w, y); + public readonly int4 ggab => new(y, y, w, z); + public readonly int4 ggaa => new(y, y, w, w); + public readonly int4 gbrr => new(y, z, x, x); + public readonly int4 gbrg => new(y, z, x, y); + public readonly int4 gbrb => new(y, z, x, z); + public int4 gbra { readonly get => new(y, z, x, w); set { y = value.x; z = value.y; x = value.z; w = value.w; } } + public readonly int4 gbgr => new(y, z, y, x); + public readonly int4 gbgg => new(y, z, y, y); + public readonly int4 gbgb => new(y, z, y, z); + public readonly int4 gbga => new(y, z, y, w); + public readonly int4 gbbr => new(y, z, z, x); + public readonly int4 gbbg => new(y, z, z, y); + public readonly int4 gbbb => new(y, z, z, z); + public readonly int4 gbba => new(y, z, z, w); + public int4 gbar { readonly get => new(y, z, w, x); set { y = value.x; z = value.y; w = value.z; x = value.w; } } + public readonly int4 gbag => new(y, z, w, y); + public readonly int4 gbab => new(y, z, w, z); + public readonly int4 gbaa => new(y, z, w, w); + public readonly int4 garr => new(y, w, x, x); + public readonly int4 garg => new(y, w, x, y); + public int4 garb { readonly get => new(y, w, x, z); set { y = value.x; w = value.y; x = value.z; z = value.w; } } + public readonly int4 gara => new(y, w, x, w); + public readonly int4 gagr => new(y, w, y, x); + public readonly int4 gagg => new(y, w, y, y); + public readonly int4 gagb => new(y, w, y, z); + public readonly int4 gaga => new(y, w, y, w); + public int4 gabr { readonly get => new(y, w, z, x); set { y = value.x; w = value.y; z = value.z; x = value.w; } } + public readonly int4 gabg => new(y, w, z, y); + public readonly int4 gabb => new(y, w, z, z); + public readonly int4 gaba => new(y, w, z, w); + public readonly int4 gaar => new(y, w, w, x); + public readonly int4 gaag => new(y, w, w, y); + public readonly int4 gaab => new(y, w, w, z); + public readonly int4 gaaa => new(y, w, w, w); + public readonly int4 brrr => new(z, x, x, x); + public readonly int4 brrg => new(z, x, x, y); + public readonly int4 brrb => new(z, x, x, z); + public readonly int4 brra => new(z, x, x, w); + public readonly int4 brgr => new(z, x, y, x); + public readonly int4 brgg => new(z, x, y, y); + public readonly int4 brgb => new(z, x, y, z); + public int4 brga { readonly get => new(z, x, y, w); set { z = value.x; x = value.y; y = value.z; w = value.w; } } + public readonly int4 brbr => new(z, x, z, x); + public readonly int4 brbg => new(z, x, z, y); + public readonly int4 brbb => new(z, x, z, z); + public readonly int4 brba => new(z, x, z, w); + public readonly int4 brar => new(z, x, w, x); + public int4 brag { readonly get => new(z, x, w, y); set { z = value.x; x = value.y; w = value.z; y = value.w; } } + public readonly int4 brab => new(z, x, w, z); + public readonly int4 braa => new(z, x, w, w); + public readonly int4 bgrr => new(z, y, x, x); + public readonly int4 bgrg => new(z, y, x, y); + public readonly int4 bgrb => new(z, y, x, z); + public int4 bgra { readonly get => new(z, y, x, w); set { z = value.x; y = value.y; x = value.z; w = value.w; } } + public readonly int4 bggr => new(z, y, y, x); + public readonly int4 bggg => new(z, y, y, y); + public readonly int4 bggb => new(z, y, y, z); + public readonly int4 bgga => new(z, y, y, w); + public readonly int4 bgbr => new(z, y, z, x); + public readonly int4 bgbg => new(z, y, z, y); + public readonly int4 bgbb => new(z, y, z, z); + public readonly int4 bgba => new(z, y, z, w); + public int4 bgar { readonly get => new(z, y, w, x); set { z = value.x; y = value.y; w = value.z; x = value.w; } } + public readonly int4 bgag => new(z, y, w, y); + public readonly int4 bgab => new(z, y, w, z); + public readonly int4 bgaa => new(z, y, w, w); + public readonly int4 bbrr => new(z, z, x, x); + public readonly int4 bbrg => new(z, z, x, y); + public readonly int4 bbrb => new(z, z, x, z); + public readonly int4 bbra => new(z, z, x, w); + public readonly int4 bbgr => new(z, z, y, x); + public readonly int4 bbgg => new(z, z, y, y); + public readonly int4 bbgb => new(z, z, y, z); + public readonly int4 bbga => new(z, z, y, w); + public readonly int4 bbbr => new(z, z, z, x); + public readonly int4 bbbg => new(z, z, z, y); + public readonly int4 bbbb => new(z, z, z, z); + public readonly int4 bbba => new(z, z, z, w); + public readonly int4 bbar => new(z, z, w, x); + public readonly int4 bbag => new(z, z, w, y); + public readonly int4 bbab => new(z, z, w, z); + public readonly int4 bbaa => new(z, z, w, w); + public readonly int4 barr => new(z, w, x, x); + public int4 barg { readonly get => new(z, w, x, y); set { z = value.x; w = value.y; x = value.z; y = value.w; } } + public readonly int4 barb => new(z, w, x, z); + public readonly int4 bara => new(z, w, x, w); + public int4 bagr { readonly get => new(z, w, y, x); set { z = value.x; w = value.y; y = value.z; x = value.w; } } + public readonly int4 bagg => new(z, w, y, y); + public readonly int4 bagb => new(z, w, y, z); + public readonly int4 baga => new(z, w, y, w); + public readonly int4 babr => new(z, w, z, x); + public readonly int4 babg => new(z, w, z, y); + public readonly int4 babb => new(z, w, z, z); + public readonly int4 baba => new(z, w, z, w); + public readonly int4 baar => new(z, w, w, x); + public readonly int4 baag => new(z, w, w, y); + public readonly int4 baab => new(z, w, w, z); + public readonly int4 baaa => new(z, w, w, w); + public readonly int4 arrr => new(w, x, x, x); + public readonly int4 arrg => new(w, x, x, y); + public readonly int4 arrb => new(w, x, x, z); + public readonly int4 arra => new(w, x, x, w); + public readonly int4 argr => new(w, x, y, x); + public readonly int4 argg => new(w, x, y, y); + public int4 argb { readonly get => new(w, x, y, z); set { w = value.x; x = value.y; y = value.z; z = value.w; } } + public readonly int4 arga => new(w, x, y, w); + public readonly int4 arbr => new(w, x, z, x); + public int4 arbg { readonly get => new(w, x, z, y); set { w = value.x; x = value.y; z = value.z; y = value.w; } } + public readonly int4 arbb => new(w, x, z, z); + public readonly int4 arba => new(w, x, z, w); + public readonly int4 arar => new(w, x, w, x); + public readonly int4 arag => new(w, x, w, y); + public readonly int4 arab => new(w, x, w, z); + public readonly int4 araa => new(w, x, w, w); + public readonly int4 agrr => new(w, y, x, x); + public readonly int4 agrg => new(w, y, x, y); + public int4 agrb { readonly get => new(w, y, x, z); set { w = value.x; y = value.y; x = value.z; z = value.w; } } + public readonly int4 agra => new(w, y, x, w); + public readonly int4 aggr => new(w, y, y, x); + public readonly int4 aggg => new(w, y, y, y); + public readonly int4 aggb => new(w, y, y, z); + public readonly int4 agga => new(w, y, y, w); + public int4 agbr { readonly get => new(w, y, z, x); set { w = value.x; y = value.y; z = value.z; x = value.w; } } + public readonly int4 agbg => new(w, y, z, y); + public readonly int4 agbb => new(w, y, z, z); + public readonly int4 agba => new(w, y, z, w); + public readonly int4 agar => new(w, y, w, x); + public readonly int4 agag => new(w, y, w, y); + public readonly int4 agab => new(w, y, w, z); + public readonly int4 agaa => new(w, y, w, w); + public readonly int4 abrr => new(w, z, x, x); + public int4 abrg { readonly get => new(w, z, x, y); set { w = value.x; z = value.y; x = value.z; y = value.w; } } + public readonly int4 abrb => new(w, z, x, z); + public readonly int4 abra => new(w, z, x, w); + public int4 abgr { readonly get => new(w, z, y, x); set { w = value.x; z = value.y; y = value.z; x = value.w; } } + public readonly int4 abgg => new(w, z, y, y); + public readonly int4 abgb => new(w, z, y, z); + public readonly int4 abga => new(w, z, y, w); + public readonly int4 abbr => new(w, z, z, x); + public readonly int4 abbg => new(w, z, z, y); + public readonly int4 abbb => new(w, z, z, z); + public readonly int4 abba => new(w, z, z, w); + public readonly int4 abar => new(w, z, w, x); + public readonly int4 abag => new(w, z, w, y); + public readonly int4 abab => new(w, z, w, z); + public readonly int4 abaa => new(w, z, w, w); + public readonly int4 aarr => new(w, w, x, x); + public readonly int4 aarg => new(w, w, x, y); + public readonly int4 aarb => new(w, w, x, z); + public readonly int4 aara => new(w, w, x, w); + public readonly int4 aagr => new(w, w, y, x); + public readonly int4 aagg => new(w, w, y, y); + public readonly int4 aagb => new(w, w, y, z); + public readonly int4 aaga => new(w, w, y, w); + public readonly int4 aabr => new(w, w, z, x); + public readonly int4 aabg => new(w, w, z, y); + public readonly int4 aabb => new(w, w, z, z); + public readonly int4 aaba => new(w, w, z, w); + public readonly int4 aaar => new(w, w, w, x); + public readonly int4 aaag => new(w, w, w, y); + public readonly int4 aaab => new(w, w, w, z); + public readonly int4 aaaa => new(w, w, w, w); +} + +public partial struct bool2 +{ + public bool2(bool2 xy) : this(xy.x, xy.y) { } + public bool r { readonly get => x; set => x = value; } + public bool g { readonly get => y; set => y = value; } + public readonly bool2 xx => new(x, x); + public bool2 xy { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public bool2 yx { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly bool2 yy => new(y, y); + public readonly bool3 xxx => new(x, x, x); + public readonly bool3 xxy => new(x, x, y); + public readonly bool3 xyx => new(x, y, x); + public readonly bool3 xyy => new(x, y, y); + public readonly bool3 yxx => new(y, x, x); + public readonly bool3 yxy => new(y, x, y); + public readonly bool3 yyx => new(y, y, x); + public readonly bool3 yyy => new(y, y, y); + public readonly bool4 xxxx => new(x, x, x, x); + public readonly bool4 xxxy => new(x, x, x, y); + public readonly bool4 xxyx => new(x, x, y, x); + public readonly bool4 xxyy => new(x, x, y, y); + public readonly bool4 xyxx => new(x, y, x, x); + public readonly bool4 xyxy => new(x, y, x, y); + public readonly bool4 xyyx => new(x, y, y, x); + public readonly bool4 xyyy => new(x, y, y, y); + public readonly bool4 yxxx => new(y, x, x, x); + public readonly bool4 yxxy => new(y, x, x, y); + public readonly bool4 yxyx => new(y, x, y, x); + public readonly bool4 yxyy => new(y, x, y, y); + public readonly bool4 yyxx => new(y, y, x, x); + public readonly bool4 yyxy => new(y, y, x, y); + public readonly bool4 yyyx => new(y, y, y, x); + public readonly bool4 yyyy => new(y, y, y, y); + public readonly bool2 rr => new(x, x); + public bool2 rg { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public bool2 gr { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly bool2 gg => new(y, y); + public readonly bool3 rrr => new(x, x, x); + public readonly bool3 rrg => new(x, x, y); + public readonly bool3 rgr => new(x, y, x); + public readonly bool3 rgg => new(x, y, y); + public readonly bool3 grr => new(y, x, x); + public readonly bool3 grg => new(y, x, y); + public readonly bool3 ggr => new(y, y, x); + public readonly bool3 ggg => new(y, y, y); + public readonly bool4 rrrr => new(x, x, x, x); + public readonly bool4 rrrg => new(x, x, x, y); + public readonly bool4 rrgr => new(x, x, y, x); + public readonly bool4 rrgg => new(x, x, y, y); + public readonly bool4 rgrr => new(x, y, x, x); + public readonly bool4 rgrg => new(x, y, x, y); + public readonly bool4 rggr => new(x, y, y, x); + public readonly bool4 rggg => new(x, y, y, y); + public readonly bool4 grrr => new(y, x, x, x); + public readonly bool4 grrg => new(y, x, x, y); + public readonly bool4 grgr => new(y, x, y, x); + public readonly bool4 grgg => new(y, x, y, y); + public readonly bool4 ggrr => new(y, y, x, x); + public readonly bool4 ggrg => new(y, y, x, y); + public readonly bool4 gggr => new(y, y, y, x); + public readonly bool4 gggg => new(y, y, y, y); +} + +public partial struct bool3 +{ + public bool3(bool x, bool2 yz) : this(x, yz.x, yz.y) { } + public bool3(bool2 xy, bool z) : this(xy.x, xy.y, z) { } + public bool3(bool3 xyz) : this(xyz.x, xyz.y, xyz.z) { } + public bool r { readonly get => x; set => x = value; } + public bool g { readonly get => y; set => y = value; } + public bool b { readonly get => z; set => z = value; } + public readonly bool2 xx => new(x, x); + public bool2 xy { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public bool2 xz { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public bool2 yx { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly bool2 yy => new(y, y); + public bool2 yz { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public bool2 zx { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public bool2 zy { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly bool2 zz => new(z, z); + public readonly bool3 xxx => new(x, x, x); + public readonly bool3 xxy => new(x, x, y); + public readonly bool3 xxz => new(x, x, z); + public readonly bool3 xyx => new(x, y, x); + public readonly bool3 xyy => new(x, y, y); + public bool3 xyz { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public readonly bool3 xzx => new(x, z, x); + public bool3 xzy { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly bool3 xzz => new(x, z, z); + public readonly bool3 yxx => new(y, x, x); + public readonly bool3 yxy => new(y, x, y); + public bool3 yxz { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public readonly bool3 yyx => new(y, y, x); + public readonly bool3 yyy => new(y, y, y); + public readonly bool3 yyz => new(y, y, z); + public bool3 yzx { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly bool3 yzy => new(y, z, y); + public readonly bool3 yzz => new(y, z, z); + public readonly bool3 zxx => new(z, x, x); + public bool3 zxy { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly bool3 zxz => new(z, x, z); + public bool3 zyx { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly bool3 zyy => new(z, y, y); + public readonly bool3 zyz => new(z, y, z); + public readonly bool3 zzx => new(z, z, x); + public readonly bool3 zzy => new(z, z, y); + public readonly bool3 zzz => new(z, z, z); + public readonly bool4 xxxx => new(x, x, x, x); + public readonly bool4 xxxy => new(x, x, x, y); + public readonly bool4 xxxz => new(x, x, x, z); + public readonly bool4 xxyx => new(x, x, y, x); + public readonly bool4 xxyy => new(x, x, y, y); + public readonly bool4 xxyz => new(x, x, y, z); + public readonly bool4 xxzx => new(x, x, z, x); + public readonly bool4 xxzy => new(x, x, z, y); + public readonly bool4 xxzz => new(x, x, z, z); + public readonly bool4 xyxx => new(x, y, x, x); + public readonly bool4 xyxy => new(x, y, x, y); + public readonly bool4 xyxz => new(x, y, x, z); + public readonly bool4 xyyx => new(x, y, y, x); + public readonly bool4 xyyy => new(x, y, y, y); + public readonly bool4 xyyz => new(x, y, y, z); + public readonly bool4 xyzx => new(x, y, z, x); + public readonly bool4 xyzy => new(x, y, z, y); + public readonly bool4 xyzz => new(x, y, z, z); + public readonly bool4 xzxx => new(x, z, x, x); + public readonly bool4 xzxy => new(x, z, x, y); + public readonly bool4 xzxz => new(x, z, x, z); + public readonly bool4 xzyx => new(x, z, y, x); + public readonly bool4 xzyy => new(x, z, y, y); + public readonly bool4 xzyz => new(x, z, y, z); + public readonly bool4 xzzx => new(x, z, z, x); + public readonly bool4 xzzy => new(x, z, z, y); + public readonly bool4 xzzz => new(x, z, z, z); + public readonly bool4 yxxx => new(y, x, x, x); + public readonly bool4 yxxy => new(y, x, x, y); + public readonly bool4 yxxz => new(y, x, x, z); + public readonly bool4 yxyx => new(y, x, y, x); + public readonly bool4 yxyy => new(y, x, y, y); + public readonly bool4 yxyz => new(y, x, y, z); + public readonly bool4 yxzx => new(y, x, z, x); + public readonly bool4 yxzy => new(y, x, z, y); + public readonly bool4 yxzz => new(y, x, z, z); + public readonly bool4 yyxx => new(y, y, x, x); + public readonly bool4 yyxy => new(y, y, x, y); + public readonly bool4 yyxz => new(y, y, x, z); + public readonly bool4 yyyx => new(y, y, y, x); + public readonly bool4 yyyy => new(y, y, y, y); + public readonly bool4 yyyz => new(y, y, y, z); + public readonly bool4 yyzx => new(y, y, z, x); + public readonly bool4 yyzy => new(y, y, z, y); + public readonly bool4 yyzz => new(y, y, z, z); + public readonly bool4 yzxx => new(y, z, x, x); + public readonly bool4 yzxy => new(y, z, x, y); + public readonly bool4 yzxz => new(y, z, x, z); + public readonly bool4 yzyx => new(y, z, y, x); + public readonly bool4 yzyy => new(y, z, y, y); + public readonly bool4 yzyz => new(y, z, y, z); + public readonly bool4 yzzx => new(y, z, z, x); + public readonly bool4 yzzy => new(y, z, z, y); + public readonly bool4 yzzz => new(y, z, z, z); + public readonly bool4 zxxx => new(z, x, x, x); + public readonly bool4 zxxy => new(z, x, x, y); + public readonly bool4 zxxz => new(z, x, x, z); + public readonly bool4 zxyx => new(z, x, y, x); + public readonly bool4 zxyy => new(z, x, y, y); + public readonly bool4 zxyz => new(z, x, y, z); + public readonly bool4 zxzx => new(z, x, z, x); + public readonly bool4 zxzy => new(z, x, z, y); + public readonly bool4 zxzz => new(z, x, z, z); + public readonly bool4 zyxx => new(z, y, x, x); + public readonly bool4 zyxy => new(z, y, x, y); + public readonly bool4 zyxz => new(z, y, x, z); + public readonly bool4 zyyx => new(z, y, y, x); + public readonly bool4 zyyy => new(z, y, y, y); + public readonly bool4 zyyz => new(z, y, y, z); + public readonly bool4 zyzx => new(z, y, z, x); + public readonly bool4 zyzy => new(z, y, z, y); + public readonly bool4 zyzz => new(z, y, z, z); + public readonly bool4 zzxx => new(z, z, x, x); + public readonly bool4 zzxy => new(z, z, x, y); + public readonly bool4 zzxz => new(z, z, x, z); + public readonly bool4 zzyx => new(z, z, y, x); + public readonly bool4 zzyy => new(z, z, y, y); + public readonly bool4 zzyz => new(z, z, y, z); + public readonly bool4 zzzx => new(z, z, z, x); + public readonly bool4 zzzy => new(z, z, z, y); + public readonly bool4 zzzz => new(z, z, z, z); + public readonly bool2 rr => new(x, x); + public bool2 rg { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public bool2 rb { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public bool2 gr { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly bool2 gg => new(y, y); + public bool2 gb { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public bool2 br { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public bool2 bg { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly bool2 bb => new(z, z); + public readonly bool3 rrr => new(x, x, x); + public readonly bool3 rrg => new(x, x, y); + public readonly bool3 rrb => new(x, x, z); + public readonly bool3 rgr => new(x, y, x); + public readonly bool3 rgg => new(x, y, y); + public bool3 rgb { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public readonly bool3 rbr => new(x, z, x); + public bool3 rbg { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly bool3 rbb => new(x, z, z); + public readonly bool3 grr => new(y, x, x); + public readonly bool3 grg => new(y, x, y); + public bool3 grb { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public readonly bool3 ggr => new(y, y, x); + public readonly bool3 ggg => new(y, y, y); + public readonly bool3 ggb => new(y, y, z); + public bool3 gbr { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly bool3 gbg => new(y, z, y); + public readonly bool3 gbb => new(y, z, z); + public readonly bool3 brr => new(z, x, x); + public bool3 brg { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly bool3 brb => new(z, x, z); + public bool3 bgr { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly bool3 bgg => new(z, y, y); + public readonly bool3 bgb => new(z, y, z); + public readonly bool3 bbr => new(z, z, x); + public readonly bool3 bbg => new(z, z, y); + public readonly bool3 bbb => new(z, z, z); + public readonly bool4 rrrr => new(x, x, x, x); + public readonly bool4 rrrg => new(x, x, x, y); + public readonly bool4 rrrb => new(x, x, x, z); + public readonly bool4 rrgr => new(x, x, y, x); + public readonly bool4 rrgg => new(x, x, y, y); + public readonly bool4 rrgb => new(x, x, y, z); + public readonly bool4 rrbr => new(x, x, z, x); + public readonly bool4 rrbg => new(x, x, z, y); + public readonly bool4 rrbb => new(x, x, z, z); + public readonly bool4 rgrr => new(x, y, x, x); + public readonly bool4 rgrg => new(x, y, x, y); + public readonly bool4 rgrb => new(x, y, x, z); + public readonly bool4 rggr => new(x, y, y, x); + public readonly bool4 rggg => new(x, y, y, y); + public readonly bool4 rggb => new(x, y, y, z); + public readonly bool4 rgbr => new(x, y, z, x); + public readonly bool4 rgbg => new(x, y, z, y); + public readonly bool4 rgbb => new(x, y, z, z); + public readonly bool4 rbrr => new(x, z, x, x); + public readonly bool4 rbrg => new(x, z, x, y); + public readonly bool4 rbrb => new(x, z, x, z); + public readonly bool4 rbgr => new(x, z, y, x); + public readonly bool4 rbgg => new(x, z, y, y); + public readonly bool4 rbgb => new(x, z, y, z); + public readonly bool4 rbbr => new(x, z, z, x); + public readonly bool4 rbbg => new(x, z, z, y); + public readonly bool4 rbbb => new(x, z, z, z); + public readonly bool4 grrr => new(y, x, x, x); + public readonly bool4 grrg => new(y, x, x, y); + public readonly bool4 grrb => new(y, x, x, z); + public readonly bool4 grgr => new(y, x, y, x); + public readonly bool4 grgg => new(y, x, y, y); + public readonly bool4 grgb => new(y, x, y, z); + public readonly bool4 grbr => new(y, x, z, x); + public readonly bool4 grbg => new(y, x, z, y); + public readonly bool4 grbb => new(y, x, z, z); + public readonly bool4 ggrr => new(y, y, x, x); + public readonly bool4 ggrg => new(y, y, x, y); + public readonly bool4 ggrb => new(y, y, x, z); + public readonly bool4 gggr => new(y, y, y, x); + public readonly bool4 gggg => new(y, y, y, y); + public readonly bool4 gggb => new(y, y, y, z); + public readonly bool4 ggbr => new(y, y, z, x); + public readonly bool4 ggbg => new(y, y, z, y); + public readonly bool4 ggbb => new(y, y, z, z); + public readonly bool4 gbrr => new(y, z, x, x); + public readonly bool4 gbrg => new(y, z, x, y); + public readonly bool4 gbrb => new(y, z, x, z); + public readonly bool4 gbgr => new(y, z, y, x); + public readonly bool4 gbgg => new(y, z, y, y); + public readonly bool4 gbgb => new(y, z, y, z); + public readonly bool4 gbbr => new(y, z, z, x); + public readonly bool4 gbbg => new(y, z, z, y); + public readonly bool4 gbbb => new(y, z, z, z); + public readonly bool4 brrr => new(z, x, x, x); + public readonly bool4 brrg => new(z, x, x, y); + public readonly bool4 brrb => new(z, x, x, z); + public readonly bool4 brgr => new(z, x, y, x); + public readonly bool4 brgg => new(z, x, y, y); + public readonly bool4 brgb => new(z, x, y, z); + public readonly bool4 brbr => new(z, x, z, x); + public readonly bool4 brbg => new(z, x, z, y); + public readonly bool4 brbb => new(z, x, z, z); + public readonly bool4 bgrr => new(z, y, x, x); + public readonly bool4 bgrg => new(z, y, x, y); + public readonly bool4 bgrb => new(z, y, x, z); + public readonly bool4 bggr => new(z, y, y, x); + public readonly bool4 bggg => new(z, y, y, y); + public readonly bool4 bggb => new(z, y, y, z); + public readonly bool4 bgbr => new(z, y, z, x); + public readonly bool4 bgbg => new(z, y, z, y); + public readonly bool4 bgbb => new(z, y, z, z); + public readonly bool4 bbrr => new(z, z, x, x); + public readonly bool4 bbrg => new(z, z, x, y); + public readonly bool4 bbrb => new(z, z, x, z); + public readonly bool4 bbgr => new(z, z, y, x); + public readonly bool4 bbgg => new(z, z, y, y); + public readonly bool4 bbgb => new(z, z, y, z); + public readonly bool4 bbbr => new(z, z, z, x); + public readonly bool4 bbbg => new(z, z, z, y); + public readonly bool4 bbbb => new(z, z, z, z); +} + +public partial struct bool4 +{ + public bool4(bool x, bool y, bool2 zw) : this(x, y, zw.x, zw.y) { } + public bool4(bool x, bool2 yz, bool w) : this(x, yz.x, yz.y, w) { } + public bool4(bool x, bool3 yzw) : this(x, yzw.x, yzw.y, yzw.z) { } + public bool4(bool2 xy, bool z, bool w) : this(xy.x, xy.y, z, w) { } + public bool4(bool2 xy, bool2 zw) : this(xy.x, xy.y, zw.x, zw.y) { } + public bool4(bool3 xyz, bool w) : this(xyz.x, xyz.y, xyz.z, w) { } + public bool4(bool4 xyzw) : this(xyzw.x, xyzw.y, xyzw.z, xyzw.w) { } + public bool r { readonly get => x; set => x = value; } + public bool g { readonly get => y; set => y = value; } + public bool b { readonly get => z; set => z = value; } + public bool a { readonly get => w; set => w = value; } + public readonly bool2 xx => new(x, x); + public bool2 xy { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public bool2 xz { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public bool2 xw { readonly get => new(x, w); set { x = value.x; w = value.y; } } + public bool2 yx { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly bool2 yy => new(y, y); + public bool2 yz { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public bool2 yw { readonly get => new(y, w); set { y = value.x; w = value.y; } } + public bool2 zx { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public bool2 zy { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly bool2 zz => new(z, z); + public bool2 zw { readonly get => new(z, w); set { z = value.x; w = value.y; } } + public bool2 wx { readonly get => new(w, x); set { w = value.x; x = value.y; } } + public bool2 wy { readonly get => new(w, y); set { w = value.x; y = value.y; } } + public bool2 wz { readonly get => new(w, z); set { w = value.x; z = value.y; } } + public readonly bool2 ww => new(w, w); + public readonly bool3 xxx => new(x, x, x); + public readonly bool3 xxy => new(x, x, y); + public readonly bool3 xxz => new(x, x, z); + public readonly bool3 xxw => new(x, x, w); + public readonly bool3 xyx => new(x, y, x); + public readonly bool3 xyy => new(x, y, y); + public bool3 xyz { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public bool3 xyw { readonly get => new(x, y, w); set { x = value.x; y = value.y; w = value.z; } } + public readonly bool3 xzx => new(x, z, x); + public bool3 xzy { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly bool3 xzz => new(x, z, z); + public bool3 xzw { readonly get => new(x, z, w); set { x = value.x; z = value.y; w = value.z; } } + public readonly bool3 xwx => new(x, w, x); + public bool3 xwy { readonly get => new(x, w, y); set { x = value.x; w = value.y; y = value.z; } } + public bool3 xwz { readonly get => new(x, w, z); set { x = value.x; w = value.y; z = value.z; } } + public readonly bool3 xww => new(x, w, w); + public readonly bool3 yxx => new(y, x, x); + public readonly bool3 yxy => new(y, x, y); + public bool3 yxz { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public bool3 yxw { readonly get => new(y, x, w); set { y = value.x; x = value.y; w = value.z; } } + public readonly bool3 yyx => new(y, y, x); + public readonly bool3 yyy => new(y, y, y); + public readonly bool3 yyz => new(y, y, z); + public readonly bool3 yyw => new(y, y, w); + public bool3 yzx { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly bool3 yzy => new(y, z, y); + public readonly bool3 yzz => new(y, z, z); + public bool3 yzw { readonly get => new(y, z, w); set { y = value.x; z = value.y; w = value.z; } } + public bool3 ywx { readonly get => new(y, w, x); set { y = value.x; w = value.y; x = value.z; } } + public readonly bool3 ywy => new(y, w, y); + public bool3 ywz { readonly get => new(y, w, z); set { y = value.x; w = value.y; z = value.z; } } + public readonly bool3 yww => new(y, w, w); + public readonly bool3 zxx => new(z, x, x); + public bool3 zxy { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly bool3 zxz => new(z, x, z); + public bool3 zxw { readonly get => new(z, x, w); set { z = value.x; x = value.y; w = value.z; } } + public bool3 zyx { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly bool3 zyy => new(z, y, y); + public readonly bool3 zyz => new(z, y, z); + public bool3 zyw { readonly get => new(z, y, w); set { z = value.x; y = value.y; w = value.z; } } + public readonly bool3 zzx => new(z, z, x); + public readonly bool3 zzy => new(z, z, y); + public readonly bool3 zzz => new(z, z, z); + public readonly bool3 zzw => new(z, z, w); + public bool3 zwx { readonly get => new(z, w, x); set { z = value.x; w = value.y; x = value.z; } } + public bool3 zwy { readonly get => new(z, w, y); set { z = value.x; w = value.y; y = value.z; } } + public readonly bool3 zwz => new(z, w, z); + public readonly bool3 zww => new(z, w, w); + public readonly bool3 wxx => new(w, x, x); + public bool3 wxy { readonly get => new(w, x, y); set { w = value.x; x = value.y; y = value.z; } } + public bool3 wxz { readonly get => new(w, x, z); set { w = value.x; x = value.y; z = value.z; } } + public readonly bool3 wxw => new(w, x, w); + public bool3 wyx { readonly get => new(w, y, x); set { w = value.x; y = value.y; x = value.z; } } + public readonly bool3 wyy => new(w, y, y); + public bool3 wyz { readonly get => new(w, y, z); set { w = value.x; y = value.y; z = value.z; } } + public readonly bool3 wyw => new(w, y, w); + public bool3 wzx { readonly get => new(w, z, x); set { w = value.x; z = value.y; x = value.z; } } + public bool3 wzy { readonly get => new(w, z, y); set { w = value.x; z = value.y; y = value.z; } } + public readonly bool3 wzz => new(w, z, z); + public readonly bool3 wzw => new(w, z, w); + public readonly bool3 wwx => new(w, w, x); + public readonly bool3 wwy => new(w, w, y); + public readonly bool3 wwz => new(w, w, z); + public readonly bool3 www => new(w, w, w); + public readonly bool4 xxxx => new(x, x, x, x); + public readonly bool4 xxxy => new(x, x, x, y); + public readonly bool4 xxxz => new(x, x, x, z); + public readonly bool4 xxxw => new(x, x, x, w); + public readonly bool4 xxyx => new(x, x, y, x); + public readonly bool4 xxyy => new(x, x, y, y); + public readonly bool4 xxyz => new(x, x, y, z); + public readonly bool4 xxyw => new(x, x, y, w); + public readonly bool4 xxzx => new(x, x, z, x); + public readonly bool4 xxzy => new(x, x, z, y); + public readonly bool4 xxzz => new(x, x, z, z); + public readonly bool4 xxzw => new(x, x, z, w); + public readonly bool4 xxwx => new(x, x, w, x); + public readonly bool4 xxwy => new(x, x, w, y); + public readonly bool4 xxwz => new(x, x, w, z); + public readonly bool4 xxww => new(x, x, w, w); + public readonly bool4 xyxx => new(x, y, x, x); + public readonly bool4 xyxy => new(x, y, x, y); + public readonly bool4 xyxz => new(x, y, x, z); + public readonly bool4 xyxw => new(x, y, x, w); + public readonly bool4 xyyx => new(x, y, y, x); + public readonly bool4 xyyy => new(x, y, y, y); + public readonly bool4 xyyz => new(x, y, y, z); + public readonly bool4 xyyw => new(x, y, y, w); + public readonly bool4 xyzx => new(x, y, z, x); + public readonly bool4 xyzy => new(x, y, z, y); + public readonly bool4 xyzz => new(x, y, z, z); + public bool4 xyzw { readonly get => new(x, y, z, w); set { x = value.x; y = value.y; z = value.z; w = value.w; } } + public readonly bool4 xywx => new(x, y, w, x); + public readonly bool4 xywy => new(x, y, w, y); + public bool4 xywz { readonly get => new(x, y, w, z); set { x = value.x; y = value.y; w = value.z; z = value.w; } } + public readonly bool4 xyww => new(x, y, w, w); + public readonly bool4 xzxx => new(x, z, x, x); + public readonly bool4 xzxy => new(x, z, x, y); + public readonly bool4 xzxz => new(x, z, x, z); + public readonly bool4 xzxw => new(x, z, x, w); + public readonly bool4 xzyx => new(x, z, y, x); + public readonly bool4 xzyy => new(x, z, y, y); + public readonly bool4 xzyz => new(x, z, y, z); + public bool4 xzyw { readonly get => new(x, z, y, w); set { x = value.x; z = value.y; y = value.z; w = value.w; } } + public readonly bool4 xzzx => new(x, z, z, x); + public readonly bool4 xzzy => new(x, z, z, y); + public readonly bool4 xzzz => new(x, z, z, z); + public readonly bool4 xzzw => new(x, z, z, w); + public readonly bool4 xzwx => new(x, z, w, x); + public bool4 xzwy { readonly get => new(x, z, w, y); set { x = value.x; z = value.y; w = value.z; y = value.w; } } + public readonly bool4 xzwz => new(x, z, w, z); + public readonly bool4 xzww => new(x, z, w, w); + public readonly bool4 xwxx => new(x, w, x, x); + public readonly bool4 xwxy => new(x, w, x, y); + public readonly bool4 xwxz => new(x, w, x, z); + public readonly bool4 xwxw => new(x, w, x, w); + public readonly bool4 xwyx => new(x, w, y, x); + public readonly bool4 xwyy => new(x, w, y, y); + public bool4 xwyz { readonly get => new(x, w, y, z); set { x = value.x; w = value.y; y = value.z; z = value.w; } } + public readonly bool4 xwyw => new(x, w, y, w); + public readonly bool4 xwzx => new(x, w, z, x); + public bool4 xwzy { readonly get => new(x, w, z, y); set { x = value.x; w = value.y; z = value.z; y = value.w; } } + public readonly bool4 xwzz => new(x, w, z, z); + public readonly bool4 xwzw => new(x, w, z, w); + public readonly bool4 xwwx => new(x, w, w, x); + public readonly bool4 xwwy => new(x, w, w, y); + public readonly bool4 xwwz => new(x, w, w, z); + public readonly bool4 xwww => new(x, w, w, w); + public readonly bool4 yxxx => new(y, x, x, x); + public readonly bool4 yxxy => new(y, x, x, y); + public readonly bool4 yxxz => new(y, x, x, z); + public readonly bool4 yxxw => new(y, x, x, w); + public readonly bool4 yxyx => new(y, x, y, x); + public readonly bool4 yxyy => new(y, x, y, y); + public readonly bool4 yxyz => new(y, x, y, z); + public readonly bool4 yxyw => new(y, x, y, w); + public readonly bool4 yxzx => new(y, x, z, x); + public readonly bool4 yxzy => new(y, x, z, y); + public readonly bool4 yxzz => new(y, x, z, z); + public bool4 yxzw { readonly get => new(y, x, z, w); set { y = value.x; x = value.y; z = value.z; w = value.w; } } + public readonly bool4 yxwx => new(y, x, w, x); + public readonly bool4 yxwy => new(y, x, w, y); + public bool4 yxwz { readonly get => new(y, x, w, z); set { y = value.x; x = value.y; w = value.z; z = value.w; } } + public readonly bool4 yxww => new(y, x, w, w); + public readonly bool4 yyxx => new(y, y, x, x); + public readonly bool4 yyxy => new(y, y, x, y); + public readonly bool4 yyxz => new(y, y, x, z); + public readonly bool4 yyxw => new(y, y, x, w); + public readonly bool4 yyyx => new(y, y, y, x); + public readonly bool4 yyyy => new(y, y, y, y); + public readonly bool4 yyyz => new(y, y, y, z); + public readonly bool4 yyyw => new(y, y, y, w); + public readonly bool4 yyzx => new(y, y, z, x); + public readonly bool4 yyzy => new(y, y, z, y); + public readonly bool4 yyzz => new(y, y, z, z); + public readonly bool4 yyzw => new(y, y, z, w); + public readonly bool4 yywx => new(y, y, w, x); + public readonly bool4 yywy => new(y, y, w, y); + public readonly bool4 yywz => new(y, y, w, z); + public readonly bool4 yyww => new(y, y, w, w); + public readonly bool4 yzxx => new(y, z, x, x); + public readonly bool4 yzxy => new(y, z, x, y); + public readonly bool4 yzxz => new(y, z, x, z); + public bool4 yzxw { readonly get => new(y, z, x, w); set { y = value.x; z = value.y; x = value.z; w = value.w; } } + public readonly bool4 yzyx => new(y, z, y, x); + public readonly bool4 yzyy => new(y, z, y, y); + public readonly bool4 yzyz => new(y, z, y, z); + public readonly bool4 yzyw => new(y, z, y, w); + public readonly bool4 yzzx => new(y, z, z, x); + public readonly bool4 yzzy => new(y, z, z, y); + public readonly bool4 yzzz => new(y, z, z, z); + public readonly bool4 yzzw => new(y, z, z, w); + public bool4 yzwx { readonly get => new(y, z, w, x); set { y = value.x; z = value.y; w = value.z; x = value.w; } } + public readonly bool4 yzwy => new(y, z, w, y); + public readonly bool4 yzwz => new(y, z, w, z); + public readonly bool4 yzww => new(y, z, w, w); + public readonly bool4 ywxx => new(y, w, x, x); + public readonly bool4 ywxy => new(y, w, x, y); + public bool4 ywxz { readonly get => new(y, w, x, z); set { y = value.x; w = value.y; x = value.z; z = value.w; } } + public readonly bool4 ywxw => new(y, w, x, w); + public readonly bool4 ywyx => new(y, w, y, x); + public readonly bool4 ywyy => new(y, w, y, y); + public readonly bool4 ywyz => new(y, w, y, z); + public readonly bool4 ywyw => new(y, w, y, w); + public bool4 ywzx { readonly get => new(y, w, z, x); set { y = value.x; w = value.y; z = value.z; x = value.w; } } + public readonly bool4 ywzy => new(y, w, z, y); + public readonly bool4 ywzz => new(y, w, z, z); + public readonly bool4 ywzw => new(y, w, z, w); + public readonly bool4 ywwx => new(y, w, w, x); + public readonly bool4 ywwy => new(y, w, w, y); + public readonly bool4 ywwz => new(y, w, w, z); + public readonly bool4 ywww => new(y, w, w, w); + public readonly bool4 zxxx => new(z, x, x, x); + public readonly bool4 zxxy => new(z, x, x, y); + public readonly bool4 zxxz => new(z, x, x, z); + public readonly bool4 zxxw => new(z, x, x, w); + public readonly bool4 zxyx => new(z, x, y, x); + public readonly bool4 zxyy => new(z, x, y, y); + public readonly bool4 zxyz => new(z, x, y, z); + public bool4 zxyw { readonly get => new(z, x, y, w); set { z = value.x; x = value.y; y = value.z; w = value.w; } } + public readonly bool4 zxzx => new(z, x, z, x); + public readonly bool4 zxzy => new(z, x, z, y); + public readonly bool4 zxzz => new(z, x, z, z); + public readonly bool4 zxzw => new(z, x, z, w); + public readonly bool4 zxwx => new(z, x, w, x); + public bool4 zxwy { readonly get => new(z, x, w, y); set { z = value.x; x = value.y; w = value.z; y = value.w; } } + public readonly bool4 zxwz => new(z, x, w, z); + public readonly bool4 zxww => new(z, x, w, w); + public readonly bool4 zyxx => new(z, y, x, x); + public readonly bool4 zyxy => new(z, y, x, y); + public readonly bool4 zyxz => new(z, y, x, z); + public bool4 zyxw { readonly get => new(z, y, x, w); set { z = value.x; y = value.y; x = value.z; w = value.w; } } + public readonly bool4 zyyx => new(z, y, y, x); + public readonly bool4 zyyy => new(z, y, y, y); + public readonly bool4 zyyz => new(z, y, y, z); + public readonly bool4 zyyw => new(z, y, y, w); + public readonly bool4 zyzx => new(z, y, z, x); + public readonly bool4 zyzy => new(z, y, z, y); + public readonly bool4 zyzz => new(z, y, z, z); + public readonly bool4 zyzw => new(z, y, z, w); + public bool4 zywx { readonly get => new(z, y, w, x); set { z = value.x; y = value.y; w = value.z; x = value.w; } } + public readonly bool4 zywy => new(z, y, w, y); + public readonly bool4 zywz => new(z, y, w, z); + public readonly bool4 zyww => new(z, y, w, w); + public readonly bool4 zzxx => new(z, z, x, x); + public readonly bool4 zzxy => new(z, z, x, y); + public readonly bool4 zzxz => new(z, z, x, z); + public readonly bool4 zzxw => new(z, z, x, w); + public readonly bool4 zzyx => new(z, z, y, x); + public readonly bool4 zzyy => new(z, z, y, y); + public readonly bool4 zzyz => new(z, z, y, z); + public readonly bool4 zzyw => new(z, z, y, w); + public readonly bool4 zzzx => new(z, z, z, x); + public readonly bool4 zzzy => new(z, z, z, y); + public readonly bool4 zzzz => new(z, z, z, z); + public readonly bool4 zzzw => new(z, z, z, w); + public readonly bool4 zzwx => new(z, z, w, x); + public readonly bool4 zzwy => new(z, z, w, y); + public readonly bool4 zzwz => new(z, z, w, z); + public readonly bool4 zzww => new(z, z, w, w); + public readonly bool4 zwxx => new(z, w, x, x); + public bool4 zwxy { readonly get => new(z, w, x, y); set { z = value.x; w = value.y; x = value.z; y = value.w; } } + public readonly bool4 zwxz => new(z, w, x, z); + public readonly bool4 zwxw => new(z, w, x, w); + public bool4 zwyx { readonly get => new(z, w, y, x); set { z = value.x; w = value.y; y = value.z; x = value.w; } } + public readonly bool4 zwyy => new(z, w, y, y); + public readonly bool4 zwyz => new(z, w, y, z); + public readonly bool4 zwyw => new(z, w, y, w); + public readonly bool4 zwzx => new(z, w, z, x); + public readonly bool4 zwzy => new(z, w, z, y); + public readonly bool4 zwzz => new(z, w, z, z); + public readonly bool4 zwzw => new(z, w, z, w); + public readonly bool4 zwwx => new(z, w, w, x); + public readonly bool4 zwwy => new(z, w, w, y); + public readonly bool4 zwwz => new(z, w, w, z); + public readonly bool4 zwww => new(z, w, w, w); + public readonly bool4 wxxx => new(w, x, x, x); + public readonly bool4 wxxy => new(w, x, x, y); + public readonly bool4 wxxz => new(w, x, x, z); + public readonly bool4 wxxw => new(w, x, x, w); + public readonly bool4 wxyx => new(w, x, y, x); + public readonly bool4 wxyy => new(w, x, y, y); + public bool4 wxyz { readonly get => new(w, x, y, z); set { w = value.x; x = value.y; y = value.z; z = value.w; } } + public readonly bool4 wxyw => new(w, x, y, w); + public readonly bool4 wxzx => new(w, x, z, x); + public bool4 wxzy { readonly get => new(w, x, z, y); set { w = value.x; x = value.y; z = value.z; y = value.w; } } + public readonly bool4 wxzz => new(w, x, z, z); + public readonly bool4 wxzw => new(w, x, z, w); + public readonly bool4 wxwx => new(w, x, w, x); + public readonly bool4 wxwy => new(w, x, w, y); + public readonly bool4 wxwz => new(w, x, w, z); + public readonly bool4 wxww => new(w, x, w, w); + public readonly bool4 wyxx => new(w, y, x, x); + public readonly bool4 wyxy => new(w, y, x, y); + public bool4 wyxz { readonly get => new(w, y, x, z); set { w = value.x; y = value.y; x = value.z; z = value.w; } } + public readonly bool4 wyxw => new(w, y, x, w); + public readonly bool4 wyyx => new(w, y, y, x); + public readonly bool4 wyyy => new(w, y, y, y); + public readonly bool4 wyyz => new(w, y, y, z); + public readonly bool4 wyyw => new(w, y, y, w); + public bool4 wyzx { readonly get => new(w, y, z, x); set { w = value.x; y = value.y; z = value.z; x = value.w; } } + public readonly bool4 wyzy => new(w, y, z, y); + public readonly bool4 wyzz => new(w, y, z, z); + public readonly bool4 wyzw => new(w, y, z, w); + public readonly bool4 wywx => new(w, y, w, x); + public readonly bool4 wywy => new(w, y, w, y); + public readonly bool4 wywz => new(w, y, w, z); + public readonly bool4 wyww => new(w, y, w, w); + public readonly bool4 wzxx => new(w, z, x, x); + public bool4 wzxy { readonly get => new(w, z, x, y); set { w = value.x; z = value.y; x = value.z; y = value.w; } } + public readonly bool4 wzxz => new(w, z, x, z); + public readonly bool4 wzxw => new(w, z, x, w); + public bool4 wzyx { readonly get => new(w, z, y, x); set { w = value.x; z = value.y; y = value.z; x = value.w; } } + public readonly bool4 wzyy => new(w, z, y, y); + public readonly bool4 wzyz => new(w, z, y, z); + public readonly bool4 wzyw => new(w, z, y, w); + public readonly bool4 wzzx => new(w, z, z, x); + public readonly bool4 wzzy => new(w, z, z, y); + public readonly bool4 wzzz => new(w, z, z, z); + public readonly bool4 wzzw => new(w, z, z, w); + public readonly bool4 wzwx => new(w, z, w, x); + public readonly bool4 wzwy => new(w, z, w, y); + public readonly bool4 wzwz => new(w, z, w, z); + public readonly bool4 wzww => new(w, z, w, w); + public readonly bool4 wwxx => new(w, w, x, x); + public readonly bool4 wwxy => new(w, w, x, y); + public readonly bool4 wwxz => new(w, w, x, z); + public readonly bool4 wwxw => new(w, w, x, w); + public readonly bool4 wwyx => new(w, w, y, x); + public readonly bool4 wwyy => new(w, w, y, y); + public readonly bool4 wwyz => new(w, w, y, z); + public readonly bool4 wwyw => new(w, w, y, w); + public readonly bool4 wwzx => new(w, w, z, x); + public readonly bool4 wwzy => new(w, w, z, y); + public readonly bool4 wwzz => new(w, w, z, z); + public readonly bool4 wwzw => new(w, w, z, w); + public readonly bool4 wwwx => new(w, w, w, x); + public readonly bool4 wwwy => new(w, w, w, y); + public readonly bool4 wwwz => new(w, w, w, z); + public readonly bool4 wwww => new(w, w, w, w); + public readonly bool2 rr => new(x, x); + public bool2 rg { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public bool2 rb { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public bool2 ra { readonly get => new(x, w); set { x = value.x; w = value.y; } } + public bool2 gr { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly bool2 gg => new(y, y); + public bool2 gb { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public bool2 ga { readonly get => new(y, w); set { y = value.x; w = value.y; } } + public bool2 br { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public bool2 bg { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly bool2 bb => new(z, z); + public bool2 ba { readonly get => new(z, w); set { z = value.x; w = value.y; } } + public bool2 ar { readonly get => new(w, x); set { w = value.x; x = value.y; } } + public bool2 ag { readonly get => new(w, y); set { w = value.x; y = value.y; } } + public bool2 ab { readonly get => new(w, z); set { w = value.x; z = value.y; } } + public readonly bool2 aa => new(w, w); + public readonly bool3 rrr => new(x, x, x); + public readonly bool3 rrg => new(x, x, y); + public readonly bool3 rrb => new(x, x, z); + public readonly bool3 rra => new(x, x, w); + public readonly bool3 rgr => new(x, y, x); + public readonly bool3 rgg => new(x, y, y); + public bool3 rgb { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public bool3 rga { readonly get => new(x, y, w); set { x = value.x; y = value.y; w = value.z; } } + public readonly bool3 rbr => new(x, z, x); + public bool3 rbg { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly bool3 rbb => new(x, z, z); + public bool3 rba { readonly get => new(x, z, w); set { x = value.x; z = value.y; w = value.z; } } + public readonly bool3 rar => new(x, w, x); + public bool3 rag { readonly get => new(x, w, y); set { x = value.x; w = value.y; y = value.z; } } + public bool3 rab { readonly get => new(x, w, z); set { x = value.x; w = value.y; z = value.z; } } + public readonly bool3 raa => new(x, w, w); + public readonly bool3 grr => new(y, x, x); + public readonly bool3 grg => new(y, x, y); + public bool3 grb { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public bool3 gra { readonly get => new(y, x, w); set { y = value.x; x = value.y; w = value.z; } } + public readonly bool3 ggr => new(y, y, x); + public readonly bool3 ggg => new(y, y, y); + public readonly bool3 ggb => new(y, y, z); + public readonly bool3 gga => new(y, y, w); + public bool3 gbr { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly bool3 gbg => new(y, z, y); + public readonly bool3 gbb => new(y, z, z); + public bool3 gba { readonly get => new(y, z, w); set { y = value.x; z = value.y; w = value.z; } } + public bool3 gar { readonly get => new(y, w, x); set { y = value.x; w = value.y; x = value.z; } } + public readonly bool3 gag => new(y, w, y); + public bool3 gab { readonly get => new(y, w, z); set { y = value.x; w = value.y; z = value.z; } } + public readonly bool3 gaa => new(y, w, w); + public readonly bool3 brr => new(z, x, x); + public bool3 brg { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly bool3 brb => new(z, x, z); + public bool3 bra { readonly get => new(z, x, w); set { z = value.x; x = value.y; w = value.z; } } + public bool3 bgr { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly bool3 bgg => new(z, y, y); + public readonly bool3 bgb => new(z, y, z); + public bool3 bga { readonly get => new(z, y, w); set { z = value.x; y = value.y; w = value.z; } } + public readonly bool3 bbr => new(z, z, x); + public readonly bool3 bbg => new(z, z, y); + public readonly bool3 bbb => new(z, z, z); + public readonly bool3 bba => new(z, z, w); + public bool3 bar { readonly get => new(z, w, x); set { z = value.x; w = value.y; x = value.z; } } + public bool3 bag { readonly get => new(z, w, y); set { z = value.x; w = value.y; y = value.z; } } + public readonly bool3 bab => new(z, w, z); + public readonly bool3 baa => new(z, w, w); + public readonly bool3 arr => new(w, x, x); + public bool3 arg { readonly get => new(w, x, y); set { w = value.x; x = value.y; y = value.z; } } + public bool3 arb { readonly get => new(w, x, z); set { w = value.x; x = value.y; z = value.z; } } + public readonly bool3 ara => new(w, x, w); + public bool3 agr { readonly get => new(w, y, x); set { w = value.x; y = value.y; x = value.z; } } + public readonly bool3 agg => new(w, y, y); + public bool3 agb { readonly get => new(w, y, z); set { w = value.x; y = value.y; z = value.z; } } + public readonly bool3 aga => new(w, y, w); + public bool3 abr { readonly get => new(w, z, x); set { w = value.x; z = value.y; x = value.z; } } + public bool3 abg { readonly get => new(w, z, y); set { w = value.x; z = value.y; y = value.z; } } + public readonly bool3 abb => new(w, z, z); + public readonly bool3 aba => new(w, z, w); + public readonly bool3 aar => new(w, w, x); + public readonly bool3 aag => new(w, w, y); + public readonly bool3 aab => new(w, w, z); + public readonly bool3 aaa => new(w, w, w); + public readonly bool4 rrrr => new(x, x, x, x); + public readonly bool4 rrrg => new(x, x, x, y); + public readonly bool4 rrrb => new(x, x, x, z); + public readonly bool4 rrra => new(x, x, x, w); + public readonly bool4 rrgr => new(x, x, y, x); + public readonly bool4 rrgg => new(x, x, y, y); + public readonly bool4 rrgb => new(x, x, y, z); + public readonly bool4 rrga => new(x, x, y, w); + public readonly bool4 rrbr => new(x, x, z, x); + public readonly bool4 rrbg => new(x, x, z, y); + public readonly bool4 rrbb => new(x, x, z, z); + public readonly bool4 rrba => new(x, x, z, w); + public readonly bool4 rrar => new(x, x, w, x); + public readonly bool4 rrag => new(x, x, w, y); + public readonly bool4 rrab => new(x, x, w, z); + public readonly bool4 rraa => new(x, x, w, w); + public readonly bool4 rgrr => new(x, y, x, x); + public readonly bool4 rgrg => new(x, y, x, y); + public readonly bool4 rgrb => new(x, y, x, z); + public readonly bool4 rgra => new(x, y, x, w); + public readonly bool4 rggr => new(x, y, y, x); + public readonly bool4 rggg => new(x, y, y, y); + public readonly bool4 rggb => new(x, y, y, z); + public readonly bool4 rgga => new(x, y, y, w); + public readonly bool4 rgbr => new(x, y, z, x); + public readonly bool4 rgbg => new(x, y, z, y); + public readonly bool4 rgbb => new(x, y, z, z); + public bool4 rgba { readonly get => new(x, y, z, w); set { x = value.x; y = value.y; z = value.z; w = value.w; } } + public readonly bool4 rgar => new(x, y, w, x); + public readonly bool4 rgag => new(x, y, w, y); + public bool4 rgab { readonly get => new(x, y, w, z); set { x = value.x; y = value.y; w = value.z; z = value.w; } } + public readonly bool4 rgaa => new(x, y, w, w); + public readonly bool4 rbrr => new(x, z, x, x); + public readonly bool4 rbrg => new(x, z, x, y); + public readonly bool4 rbrb => new(x, z, x, z); + public readonly bool4 rbra => new(x, z, x, w); + public readonly bool4 rbgr => new(x, z, y, x); + public readonly bool4 rbgg => new(x, z, y, y); + public readonly bool4 rbgb => new(x, z, y, z); + public bool4 rbga { readonly get => new(x, z, y, w); set { x = value.x; z = value.y; y = value.z; w = value.w; } } + public readonly bool4 rbbr => new(x, z, z, x); + public readonly bool4 rbbg => new(x, z, z, y); + public readonly bool4 rbbb => new(x, z, z, z); + public readonly bool4 rbba => new(x, z, z, w); + public readonly bool4 rbar => new(x, z, w, x); + public bool4 rbag { readonly get => new(x, z, w, y); set { x = value.x; z = value.y; w = value.z; y = value.w; } } + public readonly bool4 rbab => new(x, z, w, z); + public readonly bool4 rbaa => new(x, z, w, w); + public readonly bool4 rarr => new(x, w, x, x); + public readonly bool4 rarg => new(x, w, x, y); + public readonly bool4 rarb => new(x, w, x, z); + public readonly bool4 rara => new(x, w, x, w); + public readonly bool4 ragr => new(x, w, y, x); + public readonly bool4 ragg => new(x, w, y, y); + public bool4 ragb { readonly get => new(x, w, y, z); set { x = value.x; w = value.y; y = value.z; z = value.w; } } + public readonly bool4 raga => new(x, w, y, w); + public readonly bool4 rabr => new(x, w, z, x); + public bool4 rabg { readonly get => new(x, w, z, y); set { x = value.x; w = value.y; z = value.z; y = value.w; } } + public readonly bool4 rabb => new(x, w, z, z); + public readonly bool4 raba => new(x, w, z, w); + public readonly bool4 raar => new(x, w, w, x); + public readonly bool4 raag => new(x, w, w, y); + public readonly bool4 raab => new(x, w, w, z); + public readonly bool4 raaa => new(x, w, w, w); + public readonly bool4 grrr => new(y, x, x, x); + public readonly bool4 grrg => new(y, x, x, y); + public readonly bool4 grrb => new(y, x, x, z); + public readonly bool4 grra => new(y, x, x, w); + public readonly bool4 grgr => new(y, x, y, x); + public readonly bool4 grgg => new(y, x, y, y); + public readonly bool4 grgb => new(y, x, y, z); + public readonly bool4 grga => new(y, x, y, w); + public readonly bool4 grbr => new(y, x, z, x); + public readonly bool4 grbg => new(y, x, z, y); + public readonly bool4 grbb => new(y, x, z, z); + public bool4 grba { readonly get => new(y, x, z, w); set { y = value.x; x = value.y; z = value.z; w = value.w; } } + public readonly bool4 grar => new(y, x, w, x); + public readonly bool4 grag => new(y, x, w, y); + public bool4 grab { readonly get => new(y, x, w, z); set { y = value.x; x = value.y; w = value.z; z = value.w; } } + public readonly bool4 graa => new(y, x, w, w); + public readonly bool4 ggrr => new(y, y, x, x); + public readonly bool4 ggrg => new(y, y, x, y); + public readonly bool4 ggrb => new(y, y, x, z); + public readonly bool4 ggra => new(y, y, x, w); + public readonly bool4 gggr => new(y, y, y, x); + public readonly bool4 gggg => new(y, y, y, y); + public readonly bool4 gggb => new(y, y, y, z); + public readonly bool4 ggga => new(y, y, y, w); + public readonly bool4 ggbr => new(y, y, z, x); + public readonly bool4 ggbg => new(y, y, z, y); + public readonly bool4 ggbb => new(y, y, z, z); + public readonly bool4 ggba => new(y, y, z, w); + public readonly bool4 ggar => new(y, y, w, x); + public readonly bool4 ggag => new(y, y, w, y); + public readonly bool4 ggab => new(y, y, w, z); + public readonly bool4 ggaa => new(y, y, w, w); + public readonly bool4 gbrr => new(y, z, x, x); + public readonly bool4 gbrg => new(y, z, x, y); + public readonly bool4 gbrb => new(y, z, x, z); + public bool4 gbra { readonly get => new(y, z, x, w); set { y = value.x; z = value.y; x = value.z; w = value.w; } } + public readonly bool4 gbgr => new(y, z, y, x); + public readonly bool4 gbgg => new(y, z, y, y); + public readonly bool4 gbgb => new(y, z, y, z); + public readonly bool4 gbga => new(y, z, y, w); + public readonly bool4 gbbr => new(y, z, z, x); + public readonly bool4 gbbg => new(y, z, z, y); + public readonly bool4 gbbb => new(y, z, z, z); + public readonly bool4 gbba => new(y, z, z, w); + public bool4 gbar { readonly get => new(y, z, w, x); set { y = value.x; z = value.y; w = value.z; x = value.w; } } + public readonly bool4 gbag => new(y, z, w, y); + public readonly bool4 gbab => new(y, z, w, z); + public readonly bool4 gbaa => new(y, z, w, w); + public readonly bool4 garr => new(y, w, x, x); + public readonly bool4 garg => new(y, w, x, y); + public bool4 garb { readonly get => new(y, w, x, z); set { y = value.x; w = value.y; x = value.z; z = value.w; } } + public readonly bool4 gara => new(y, w, x, w); + public readonly bool4 gagr => new(y, w, y, x); + public readonly bool4 gagg => new(y, w, y, y); + public readonly bool4 gagb => new(y, w, y, z); + public readonly bool4 gaga => new(y, w, y, w); + public bool4 gabr { readonly get => new(y, w, z, x); set { y = value.x; w = value.y; z = value.z; x = value.w; } } + public readonly bool4 gabg => new(y, w, z, y); + public readonly bool4 gabb => new(y, w, z, z); + public readonly bool4 gaba => new(y, w, z, w); + public readonly bool4 gaar => new(y, w, w, x); + public readonly bool4 gaag => new(y, w, w, y); + public readonly bool4 gaab => new(y, w, w, z); + public readonly bool4 gaaa => new(y, w, w, w); + public readonly bool4 brrr => new(z, x, x, x); + public readonly bool4 brrg => new(z, x, x, y); + public readonly bool4 brrb => new(z, x, x, z); + public readonly bool4 brra => new(z, x, x, w); + public readonly bool4 brgr => new(z, x, y, x); + public readonly bool4 brgg => new(z, x, y, y); + public readonly bool4 brgb => new(z, x, y, z); + public bool4 brga { readonly get => new(z, x, y, w); set { z = value.x; x = value.y; y = value.z; w = value.w; } } + public readonly bool4 brbr => new(z, x, z, x); + public readonly bool4 brbg => new(z, x, z, y); + public readonly bool4 brbb => new(z, x, z, z); + public readonly bool4 brba => new(z, x, z, w); + public readonly bool4 brar => new(z, x, w, x); + public bool4 brag { readonly get => new(z, x, w, y); set { z = value.x; x = value.y; w = value.z; y = value.w; } } + public readonly bool4 brab => new(z, x, w, z); + public readonly bool4 braa => new(z, x, w, w); + public readonly bool4 bgrr => new(z, y, x, x); + public readonly bool4 bgrg => new(z, y, x, y); + public readonly bool4 bgrb => new(z, y, x, z); + public bool4 bgra { readonly get => new(z, y, x, w); set { z = value.x; y = value.y; x = value.z; w = value.w; } } + public readonly bool4 bggr => new(z, y, y, x); + public readonly bool4 bggg => new(z, y, y, y); + public readonly bool4 bggb => new(z, y, y, z); + public readonly bool4 bgga => new(z, y, y, w); + public readonly bool4 bgbr => new(z, y, z, x); + public readonly bool4 bgbg => new(z, y, z, y); + public readonly bool4 bgbb => new(z, y, z, z); + public readonly bool4 bgba => new(z, y, z, w); + public bool4 bgar { readonly get => new(z, y, w, x); set { z = value.x; y = value.y; w = value.z; x = value.w; } } + public readonly bool4 bgag => new(z, y, w, y); + public readonly bool4 bgab => new(z, y, w, z); + public readonly bool4 bgaa => new(z, y, w, w); + public readonly bool4 bbrr => new(z, z, x, x); + public readonly bool4 bbrg => new(z, z, x, y); + public readonly bool4 bbrb => new(z, z, x, z); + public readonly bool4 bbra => new(z, z, x, w); + public readonly bool4 bbgr => new(z, z, y, x); + public readonly bool4 bbgg => new(z, z, y, y); + public readonly bool4 bbgb => new(z, z, y, z); + public readonly bool4 bbga => new(z, z, y, w); + public readonly bool4 bbbr => new(z, z, z, x); + public readonly bool4 bbbg => new(z, z, z, y); + public readonly bool4 bbbb => new(z, z, z, z); + public readonly bool4 bbba => new(z, z, z, w); + public readonly bool4 bbar => new(z, z, w, x); + public readonly bool4 bbag => new(z, z, w, y); + public readonly bool4 bbab => new(z, z, w, z); + public readonly bool4 bbaa => new(z, z, w, w); + public readonly bool4 barr => new(z, w, x, x); + public bool4 barg { readonly get => new(z, w, x, y); set { z = value.x; w = value.y; x = value.z; y = value.w; } } + public readonly bool4 barb => new(z, w, x, z); + public readonly bool4 bara => new(z, w, x, w); + public bool4 bagr { readonly get => new(z, w, y, x); set { z = value.x; w = value.y; y = value.z; x = value.w; } } + public readonly bool4 bagg => new(z, w, y, y); + public readonly bool4 bagb => new(z, w, y, z); + public readonly bool4 baga => new(z, w, y, w); + public readonly bool4 babr => new(z, w, z, x); + public readonly bool4 babg => new(z, w, z, y); + public readonly bool4 babb => new(z, w, z, z); + public readonly bool4 baba => new(z, w, z, w); + public readonly bool4 baar => new(z, w, w, x); + public readonly bool4 baag => new(z, w, w, y); + public readonly bool4 baab => new(z, w, w, z); + public readonly bool4 baaa => new(z, w, w, w); + public readonly bool4 arrr => new(w, x, x, x); + public readonly bool4 arrg => new(w, x, x, y); + public readonly bool4 arrb => new(w, x, x, z); + public readonly bool4 arra => new(w, x, x, w); + public readonly bool4 argr => new(w, x, y, x); + public readonly bool4 argg => new(w, x, y, y); + public bool4 argb { readonly get => new(w, x, y, z); set { w = value.x; x = value.y; y = value.z; z = value.w; } } + public readonly bool4 arga => new(w, x, y, w); + public readonly bool4 arbr => new(w, x, z, x); + public bool4 arbg { readonly get => new(w, x, z, y); set { w = value.x; x = value.y; z = value.z; y = value.w; } } + public readonly bool4 arbb => new(w, x, z, z); + public readonly bool4 arba => new(w, x, z, w); + public readonly bool4 arar => new(w, x, w, x); + public readonly bool4 arag => new(w, x, w, y); + public readonly bool4 arab => new(w, x, w, z); + public readonly bool4 araa => new(w, x, w, w); + public readonly bool4 agrr => new(w, y, x, x); + public readonly bool4 agrg => new(w, y, x, y); + public bool4 agrb { readonly get => new(w, y, x, z); set { w = value.x; y = value.y; x = value.z; z = value.w; } } + public readonly bool4 agra => new(w, y, x, w); + public readonly bool4 aggr => new(w, y, y, x); + public readonly bool4 aggg => new(w, y, y, y); + public readonly bool4 aggb => new(w, y, y, z); + public readonly bool4 agga => new(w, y, y, w); + public bool4 agbr { readonly get => new(w, y, z, x); set { w = value.x; y = value.y; z = value.z; x = value.w; } } + public readonly bool4 agbg => new(w, y, z, y); + public readonly bool4 agbb => new(w, y, z, z); + public readonly bool4 agba => new(w, y, z, w); + public readonly bool4 agar => new(w, y, w, x); + public readonly bool4 agag => new(w, y, w, y); + public readonly bool4 agab => new(w, y, w, z); + public readonly bool4 agaa => new(w, y, w, w); + public readonly bool4 abrr => new(w, z, x, x); + public bool4 abrg { readonly get => new(w, z, x, y); set { w = value.x; z = value.y; x = value.z; y = value.w; } } + public readonly bool4 abrb => new(w, z, x, z); + public readonly bool4 abra => new(w, z, x, w); + public bool4 abgr { readonly get => new(w, z, y, x); set { w = value.x; z = value.y; y = value.z; x = value.w; } } + public readonly bool4 abgg => new(w, z, y, y); + public readonly bool4 abgb => new(w, z, y, z); + public readonly bool4 abga => new(w, z, y, w); + public readonly bool4 abbr => new(w, z, z, x); + public readonly bool4 abbg => new(w, z, z, y); + public readonly bool4 abbb => new(w, z, z, z); + public readonly bool4 abba => new(w, z, z, w); + public readonly bool4 abar => new(w, z, w, x); + public readonly bool4 abag => new(w, z, w, y); + public readonly bool4 abab => new(w, z, w, z); + public readonly bool4 abaa => new(w, z, w, w); + public readonly bool4 aarr => new(w, w, x, x); + public readonly bool4 aarg => new(w, w, x, y); + public readonly bool4 aarb => new(w, w, x, z); + public readonly bool4 aara => new(w, w, x, w); + public readonly bool4 aagr => new(w, w, y, x); + public readonly bool4 aagg => new(w, w, y, y); + public readonly bool4 aagb => new(w, w, y, z); + public readonly bool4 aaga => new(w, w, y, w); + public readonly bool4 aabr => new(w, w, z, x); + public readonly bool4 aabg => new(w, w, z, y); + public readonly bool4 aabb => new(w, w, z, z); + public readonly bool4 aaba => new(w, w, z, w); + public readonly bool4 aaar => new(w, w, w, x); + public readonly bool4 aaag => new(w, w, w, y); + public readonly bool4 aaab => new(w, w, w, z); + public readonly bool4 aaaa => new(w, w, w, w); +} + +public partial struct double2 +{ + public double2(double2 xy) : this(xy.x, xy.y) { } + public double r { readonly get => x; set => x = value; } + public double g { readonly get => y; set => y = value; } + public readonly double2 xx => new(x, x); + public double2 xy { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public double2 yx { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly double2 yy => new(y, y); + public readonly double3 xxx => new(x, x, x); + public readonly double3 xxy => new(x, x, y); + public readonly double3 xyx => new(x, y, x); + public readonly double3 xyy => new(x, y, y); + public readonly double3 yxx => new(y, x, x); + public readonly double3 yxy => new(y, x, y); + public readonly double3 yyx => new(y, y, x); + public readonly double3 yyy => new(y, y, y); + public readonly double2 rr => new(x, x); + public double2 rg { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public double2 gr { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly double2 gg => new(y, y); + public readonly double3 rrr => new(x, x, x); + public readonly double3 rrg => new(x, x, y); + public readonly double3 rgr => new(x, y, x); + public readonly double3 rgg => new(x, y, y); + public readonly double3 grr => new(y, x, x); + public readonly double3 grg => new(y, x, y); + public readonly double3 ggr => new(y, y, x); + public readonly double3 ggg => new(y, y, y); +} + +public partial struct double3 +{ + public double3(double x, double2 yz) : this(x, yz.x, yz.y) { } + public double3(double2 xy, double z) : this(xy.x, xy.y, z) { } + public double3(double3 xyz) : this(xyz.x, xyz.y, xyz.z) { } + public double r { readonly get => x; set => x = value; } + public double g { readonly get => y; set => y = value; } + public double b { readonly get => z; set => z = value; } + public readonly double2 xx => new(x, x); + public double2 xy { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public double2 xz { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public double2 yx { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly double2 yy => new(y, y); + public double2 yz { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public double2 zx { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public double2 zy { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly double2 zz => new(z, z); + public readonly double3 xxx => new(x, x, x); + public readonly double3 xxy => new(x, x, y); + public readonly double3 xxz => new(x, x, z); + public readonly double3 xyx => new(x, y, x); + public readonly double3 xyy => new(x, y, y); + public double3 xyz { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public readonly double3 xzx => new(x, z, x); + public double3 xzy { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly double3 xzz => new(x, z, z); + public readonly double3 yxx => new(y, x, x); + public readonly double3 yxy => new(y, x, y); + public double3 yxz { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public readonly double3 yyx => new(y, y, x); + public readonly double3 yyy => new(y, y, y); + public readonly double3 yyz => new(y, y, z); + public double3 yzx { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly double3 yzy => new(y, z, y); + public readonly double3 yzz => new(y, z, z); + public readonly double3 zxx => new(z, x, x); + public double3 zxy { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly double3 zxz => new(z, x, z); + public double3 zyx { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly double3 zyy => new(z, y, y); + public readonly double3 zyz => new(z, y, z); + public readonly double3 zzx => new(z, z, x); + public readonly double3 zzy => new(z, z, y); + public readonly double3 zzz => new(z, z, z); + public readonly double2 rr => new(x, x); + public double2 rg { readonly get => new(x, y); set { x = value.x; y = value.y; } } + public double2 rb { readonly get => new(x, z); set { x = value.x; z = value.y; } } + public double2 gr { readonly get => new(y, x); set { y = value.x; x = value.y; } } + public readonly double2 gg => new(y, y); + public double2 gb { readonly get => new(y, z); set { y = value.x; z = value.y; } } + public double2 br { readonly get => new(z, x); set { z = value.x; x = value.y; } } + public double2 bg { readonly get => new(z, y); set { z = value.x; y = value.y; } } + public readonly double2 bb => new(z, z); + public readonly double3 rrr => new(x, x, x); + public readonly double3 rrg => new(x, x, y); + public readonly double3 rrb => new(x, x, z); + public readonly double3 rgr => new(x, y, x); + public readonly double3 rgg => new(x, y, y); + public double3 rgb { readonly get => new(x, y, z); set { x = value.x; y = value.y; z = value.z; } } + public readonly double3 rbr => new(x, z, x); + public double3 rbg { readonly get => new(x, z, y); set { x = value.x; z = value.y; y = value.z; } } + public readonly double3 rbb => new(x, z, z); + public readonly double3 grr => new(y, x, x); + public readonly double3 grg => new(y, x, y); + public double3 grb { readonly get => new(y, x, z); set { y = value.x; x = value.y; z = value.z; } } + public readonly double3 ggr => new(y, y, x); + public readonly double3 ggg => new(y, y, y); + public readonly double3 ggb => new(y, y, z); + public double3 gbr { readonly get => new(y, z, x); set { y = value.x; z = value.y; x = value.z; } } + public readonly double3 gbg => new(y, z, y); + public readonly double3 gbb => new(y, z, z); + public readonly double3 brr => new(z, x, x); + public double3 brg { readonly get => new(z, x, y); set { z = value.x; x = value.y; y = value.z; } } + public readonly double3 brb => new(z, x, z); + public double3 bgr { readonly get => new(z, y, x); set { z = value.x; y = value.y; x = value.z; } } + public readonly double3 bgg => new(z, y, y); + public readonly double3 bgb => new(z, y, z); + public readonly double3 bbr => new(z, z, x); + public readonly double3 bbg => new(z, z, y); + public readonly double3 bbb => new(z, z, z); +} + +public static partial class math +{ + public static float2 float2(float2 xy) => new(xy); + public static float3 float3(float x, float2 yz) => new(x, yz); + public static float3 float3(float2 xy, float z) => new(xy, z); + public static float3 float3(float3 xyz) => new(xyz); + public static float4 float4(float x, float y, float2 zw) => new(x, y, zw); + public static float4 float4(float x, float2 yz, float w) => new(x, yz, w); + public static float4 float4(float x, float3 yzw) => new(x, yzw); + public static float4 float4(float2 xy, float z, float w) => new(xy, z, w); + public static float4 float4(float2 xy, float2 zw) => new(xy, zw); + public static float4 float4(float3 xyz, float w) => new(xyz, w); + public static float4 float4(float4 xyzw) => new(xyzw); + public static int2 int2(int2 xy) => new(xy); + public static int3 int3(int x, int2 yz) => new(x, yz); + public static int3 int3(int2 xy, int z) => new(xy, z); + public static int3 int3(int3 xyz) => new(xyz); + public static int4 int4(int x, int y, int2 zw) => new(x, y, zw); + public static int4 int4(int x, int2 yz, int w) => new(x, yz, w); + public static int4 int4(int x, int3 yzw) => new(x, yzw); + public static int4 int4(int2 xy, int z, int w) => new(xy, z, w); + public static int4 int4(int2 xy, int2 zw) => new(xy, zw); + public static int4 int4(int3 xyz, int w) => new(xyz, w); + public static int4 int4(int4 xyzw) => new(xyzw); + public static bool2 bool2(bool2 xy) => new(xy); + public static bool3 bool3(bool x, bool2 yz) => new(x, yz); + public static bool3 bool3(bool2 xy, bool z) => new(xy, z); + public static bool3 bool3(bool3 xyz) => new(xyz); + public static bool4 bool4(bool x, bool y, bool2 zw) => new(x, y, zw); + public static bool4 bool4(bool x, bool2 yz, bool w) => new(x, yz, w); + public static bool4 bool4(bool x, bool3 yzw) => new(x, yzw); + public static bool4 bool4(bool2 xy, bool z, bool w) => new(xy, z, w); + public static bool4 bool4(bool2 xy, bool2 zw) => new(xy, zw); + public static bool4 bool4(bool3 xyz, bool w) => new(xyz, w); + public static bool4 bool4(bool4 xyzw) => new(xyzw); +} diff --git a/packages/cultmath/src/CultMath/Swizzles.g.cs.meta b/packages/cultmath/src/CultMath/Swizzles.g.cs.meta new file mode 100644 index 00000000..035e577f --- /dev/null +++ b/packages/cultmath/src/CultMath/Swizzles.g.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e89d6089b354459788eb6f0faebfd6c0 diff --git a/packages/cultmath/src/CultMath/UnscopedRefAttribute.cs b/packages/cultmath/src/CultMath/UnscopedRefAttribute.cs new file mode 100644 index 00000000..6e1e3148 --- /dev/null +++ b/packages/cultmath/src/CultMath/UnscopedRefAttribute.cs @@ -0,0 +1,10 @@ +#if !NET7_0_OR_GREATER +namespace System.Diagnostics.CodeAnalysis; + +// Compiler-recognized polyfill so netstandard2.1 builds can return refs into a struct's +// own fields (the matrix row indexer). net7.0+ supplies the real attribute. +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] +internal sealed class UnscopedRefAttribute : Attribute +{ +} +#endif diff --git a/packages/cultmath/src/CultMath/UnscopedRefAttribute.cs.meta b/packages/cultmath/src/CultMath/UnscopedRefAttribute.cs.meta new file mode 100644 index 00000000..61df7771 --- /dev/null +++ b/packages/cultmath/src/CultMath/UnscopedRefAttribute.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3f204000f9854517a8dd754e2d9dc975 diff --git a/packages/cultmath/src/CultMath/bool2.cs b/packages/cultmath/src/CultMath/bool2.cs index 95ab29eb..29710f0c 100644 --- a/packages/cultmath/src/CultMath/bool2.cs +++ b/packages/cultmath/src/CultMath/bool2.cs @@ -2,17 +2,50 @@ using System; namespace CultMath; -public record struct bool2(bool x, bool y) +public partial struct bool2 : IEquatable { public static readonly bool2 @false = new(false, false); public static readonly bool2 @true = new(true, true); - public bool this[int index] => index switch + public bool x; + public bool y; + + public bool2(bool x, bool y) { - 0 => x, - 1 => y, - _ => throw new ArgumentOutOfRangeException(nameof(index)), - }; + this.x = x; + this.y = y; + } + + public bool this[int index] + { + readonly get => index switch + { + 0 => x, + 1 => y, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: x = value; break; + case 1: y = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } public static implicit operator bool2(bool value) => new(value, value); + + public static bool2 operator !(bool2 value) => new(!value.x, !value.y); + public static bool2 operator &(bool2 left, bool2 right) => new(left.x & right.x, left.y & right.y); + public static bool2 operator |(bool2 left, bool2 right) => new(left.x | right.x, left.y | right.y); + public static bool2 operator ^(bool2 left, bool2 right) => new(left.x ^ right.x, left.y ^ right.y); + public static bool2 operator ==(bool2 left, bool2 right) => new(left.x == right.x, left.y == right.y); + public static bool2 operator !=(bool2 left, bool2 right) => new(left.x != right.x, left.y != right.y); + + public readonly bool Equals(bool2 other) => x == other.x && y == other.y; + public override readonly bool Equals(object? obj) => obj is bool2 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y); + public override readonly string ToString() => $"bool2({x}, {y})"; } diff --git a/packages/cultmath/src/CultMath/bool3.cs b/packages/cultmath/src/CultMath/bool3.cs new file mode 100644 index 00000000..9e4029c7 --- /dev/null +++ b/packages/cultmath/src/CultMath/bool3.cs @@ -0,0 +1,55 @@ +using System; + +namespace CultMath; + +public partial struct bool3 : IEquatable +{ + public static readonly bool3 @false = new(false, false, false); + public static readonly bool3 @true = new(true, true, true); + + public bool x; + public bool y; + public bool z; + + public bool3(bool x, bool y, bool z) + { + this.x = x; + this.y = y; + this.z = z; + } + + public bool this[int index] + { + readonly get => index switch + { + 0 => x, + 1 => y, + 2 => z, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: x = value; break; + case 1: y = value; break; + case 2: z = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } + + public static implicit operator bool3(bool value) => new(value, value, value); + + public static bool3 operator !(bool3 value) => new(!value.x, !value.y, !value.z); + public static bool3 operator &(bool3 left, bool3 right) => new(left.x & right.x, left.y & right.y, left.z & right.z); + public static bool3 operator |(bool3 left, bool3 right) => new(left.x | right.x, left.y | right.y, left.z | right.z); + public static bool3 operator ^(bool3 left, bool3 right) => new(left.x ^ right.x, left.y ^ right.y, left.z ^ right.z); + public static bool3 operator ==(bool3 left, bool3 right) => new(left.x == right.x, left.y == right.y, left.z == right.z); + public static bool3 operator !=(bool3 left, bool3 right) => new(left.x != right.x, left.y != right.y, left.z != right.z); + + public readonly bool Equals(bool3 other) => x == other.x && y == other.y && z == other.z; + public override readonly bool Equals(object? obj) => obj is bool3 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y, z); + public override readonly string ToString() => $"bool3({x}, {y}, {z})"; +} diff --git a/packages/cultmath/src/CultMath/bool3.cs.meta b/packages/cultmath/src/CultMath/bool3.cs.meta new file mode 100644 index 00000000..ada81b6d --- /dev/null +++ b/packages/cultmath/src/CultMath/bool3.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 323ec670fc374c8ba4d87b4bf4c91f34 diff --git a/packages/cultmath/src/CultMath/bool4.cs b/packages/cultmath/src/CultMath/bool4.cs new file mode 100644 index 00000000..b12f15b6 --- /dev/null +++ b/packages/cultmath/src/CultMath/bool4.cs @@ -0,0 +1,59 @@ +using System; + +namespace CultMath; + +public partial struct bool4 : IEquatable +{ + public static readonly bool4 @false = new(false, false, false, false); + public static readonly bool4 @true = new(true, true, true, true); + + public bool x; + public bool y; + public bool z; + public bool w; + + public bool4(bool x, bool y, bool z, bool w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public bool this[int index] + { + readonly get => index switch + { + 0 => x, + 1 => y, + 2 => z, + 3 => w, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: x = value; break; + case 1: y = value; break; + case 2: z = value; break; + case 3: w = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } + + public static implicit operator bool4(bool value) => new(value, value, value, value); + + public static bool4 operator !(bool4 value) => new(!value.x, !value.y, !value.z, !value.w); + public static bool4 operator &(bool4 left, bool4 right) => new(left.x & right.x, left.y & right.y, left.z & right.z, left.w & right.w); + public static bool4 operator |(bool4 left, bool4 right) => new(left.x | right.x, left.y | right.y, left.z | right.z, left.w | right.w); + public static bool4 operator ^(bool4 left, bool4 right) => new(left.x ^ right.x, left.y ^ right.y, left.z ^ right.z, left.w ^ right.w); + public static bool4 operator ==(bool4 left, bool4 right) => new(left.x == right.x, left.y == right.y, left.z == right.z, left.w == right.w); + public static bool4 operator !=(bool4 left, bool4 right) => new(left.x != right.x, left.y != right.y, left.z != right.z, left.w != right.w); + + public readonly bool Equals(bool4 other) => x == other.x && y == other.y && z == other.z && w == other.w; + public override readonly bool Equals(object? obj) => obj is bool4 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y, z, w); + public override readonly string ToString() => $"bool4({x}, {y}, {z}, {w})"; +} diff --git a/packages/cultmath/src/CultMath/bool4.cs.meta b/packages/cultmath/src/CultMath/bool4.cs.meta new file mode 100644 index 00000000..625bb46b --- /dev/null +++ b/packages/cultmath/src/CultMath/bool4.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d38b9f70d88c459584441b85ecbfd92a diff --git a/packages/cultmath/src/CultMath/double2.cs b/packages/cultmath/src/CultMath/double2.cs index 000c3cb0..958b409d 100644 --- a/packages/cultmath/src/CultMath/double2.cs +++ b/packages/cultmath/src/CultMath/double2.cs @@ -2,17 +2,38 @@ using System; namespace CultMath; -public record struct double2(double x, double y) +public partial struct double2 : IEquatable { public static readonly double2 zero = new(0.0, 0.0); public static readonly double2 one = new(1.0, 1.0); - public double this[int index] => index switch + public double x; + public double y; + + public double2(double x, double y) { - 0 => x, - 1 => y, - _ => throw new ArgumentOutOfRangeException(nameof(index)), - }; + this.x = x; + this.y = y; + } + + public double this[int index] + { + readonly get => index switch + { + 0 => x, + 1 => y, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: x = value; break; + case 1: y = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } public static implicit operator double2(double value) => new(value, value); @@ -22,4 +43,16 @@ public record struct double2(double x, double y) public static double2 operator -(double2 left, double2 right) => new(left.x - right.x, left.y - right.y); public static double2 operator *(double2 left, double2 right) => new(left.x * right.x, left.y * right.y); public static double2 operator /(double2 left, double2 right) => new(left.x / right.x, left.y / right.y); + + public static bool2 operator <(double2 left, double2 right) => new(left.x < right.x, left.y < right.y); + public static bool2 operator >(double2 left, double2 right) => new(left.x > right.x, left.y > right.y); + public static bool2 operator <=(double2 left, double2 right) => new(left.x <= right.x, left.y <= right.y); + public static bool2 operator >=(double2 left, double2 right) => new(left.x >= right.x, left.y >= right.y); + public static bool2 operator ==(double2 left, double2 right) => new(left.x == right.x, left.y == right.y); + public static bool2 operator !=(double2 left, double2 right) => new(left.x != right.x, left.y != right.y); + + public readonly bool Equals(double2 other) => x.Equals(other.x) && y.Equals(other.y); + public override readonly bool Equals(object? obj) => obj is double2 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y); + public override readonly string ToString() => FormattableString.Invariant($"double2({x:R}, {y:R})"); } diff --git a/packages/cultmath/src/CultMath/double3.cs b/packages/cultmath/src/CultMath/double3.cs index b1427505..5fbe9c65 100644 --- a/packages/cultmath/src/CultMath/double3.cs +++ b/packages/cultmath/src/CultMath/double3.cs @@ -2,28 +2,42 @@ using System; namespace CultMath; -public record struct double3(double x, double y, double z) +public partial struct double3 : IEquatable { public static readonly double3 zero = new(0.0, 0.0, 0.0); public static readonly double3 one = new(1.0, 1.0, 1.0); - public double2 xy + public double x; + public double y; + public double z; + + public double3(double x, double y, double z) { - get => new(x, y); - set - { - x = value.x; - y = value.y; - } + this.x = x; + this.y = y; + this.z = z; } - public double this[int index] => index switch + public double this[int index] { - 0 => x, - 1 => y, - 2 => z, - _ => throw new ArgumentOutOfRangeException(nameof(index)), - }; + readonly get => index switch + { + 0 => x, + 1 => y, + 2 => z, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: x = value; break; + case 1: y = value; break; + case 2: z = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } public static implicit operator double3(double value) => new(value, value, value); @@ -33,4 +47,16 @@ public record struct double3(double x, double y, double z) public static double3 operator -(double3 left, double3 right) => new(left.x - right.x, left.y - right.y, left.z - right.z); public static double3 operator *(double3 left, double3 right) => new(left.x * right.x, left.y * right.y, left.z * right.z); public static double3 operator /(double3 left, double3 right) => new(left.x / right.x, left.y / right.y, left.z / right.z); + + public static bool3 operator <(double3 left, double3 right) => new(left.x < right.x, left.y < right.y, left.z < right.z); + public static bool3 operator >(double3 left, double3 right) => new(left.x > right.x, left.y > right.y, left.z > right.z); + public static bool3 operator <=(double3 left, double3 right) => new(left.x <= right.x, left.y <= right.y, left.z <= right.z); + public static bool3 operator >=(double3 left, double3 right) => new(left.x >= right.x, left.y >= right.y, left.z >= right.z); + public static bool3 operator ==(double3 left, double3 right) => new(left.x == right.x, left.y == right.y, left.z == right.z); + public static bool3 operator !=(double3 left, double3 right) => new(left.x != right.x, left.y != right.y, left.z != right.z); + + public readonly bool Equals(double3 other) => x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z); + public override readonly bool Equals(object? obj) => obj is double3 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y, z); + public override readonly string ToString() => FormattableString.Invariant($"double3({x:R}, {y:R}, {z:R})"); } diff --git a/packages/cultmath/src/CultMath/float2.cs b/packages/cultmath/src/CultMath/float2.cs index 5298494b..e33655a8 100644 --- a/packages/cultmath/src/CultMath/float2.cs +++ b/packages/cultmath/src/CultMath/float2.cs @@ -2,17 +2,38 @@ using System; namespace CultMath; -public record struct float2(float x, float y) +public partial struct float2 : IEquatable { public static readonly float2 zero = new(0.0f, 0.0f); public static readonly float2 one = new(1.0f, 1.0f); - public float this[int index] => index switch + public float x; + public float y; + + public float2(float x, float y) { - 0 => x, - 1 => y, - _ => throw new ArgumentOutOfRangeException(nameof(index)), - }; + this.x = x; + this.y = y; + } + + public float this[int index] + { + readonly get => index switch + { + 0 => x, + 1 => y, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: x = value; break; + case 1: y = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } public static implicit operator float2(float value) => new(value, value); public static explicit operator System.Numerics.Vector2(float2 value) => new(value.x, value.y); @@ -20,9 +41,44 @@ public record struct float2(float x, float y) public static float2 operator +(float2 value) => value; public static float2 operator -(float2 value) => new(-value.x, -value.y); + public static float2 operator +(float2 left, float2 right) => new(left.x + right.x, left.y + right.y); + public static float2 operator +(float2 left, float right) => new(left.x + right, left.y + right); + public static float2 operator +(float left, float2 right) => new(left + right.x, left + right.y); public static float2 operator -(float2 left, float2 right) => new(left.x - right.x, left.y - right.y); + public static float2 operator -(float2 left, float right) => new(left.x - right, left.y - right); + public static float2 operator -(float left, float2 right) => new(left - right.x, left - right.y); public static float2 operator *(float2 left, float2 right) => new(left.x * right.x, left.y * right.y); + public static float2 operator *(float2 left, float right) => new(left.x * right, left.y * right); + public static float2 operator *(float left, float2 right) => new(left * right.x, left * right.y); public static float2 operator /(float2 left, float2 right) => new(left.x / right.x, left.y / right.y); + public static float2 operator /(float2 left, float right) => new(left.x / right, left.y / right); + public static float2 operator /(float left, float2 right) => new(left / right.x, left / right.y); public static float2 operator %(float2 left, float2 right) => new(left.x % right.x, left.y % right.y); + public static float2 operator %(float2 left, float right) => new(left.x % right, left.y % right); + public static float2 operator %(float left, float2 right) => new(left % right.x, left % right.y); + + public static bool2 operator <(float2 left, float2 right) => new(left.x < right.x, left.y < right.y); + public static bool2 operator <(float2 left, float right) => new(left.x < right, left.y < right); + public static bool2 operator <(float left, float2 right) => new(left < right.x, left < right.y); + public static bool2 operator >(float2 left, float2 right) => new(left.x > right.x, left.y > right.y); + public static bool2 operator >(float2 left, float right) => new(left.x > right, left.y > right); + public static bool2 operator >(float left, float2 right) => new(left > right.x, left > right.y); + public static bool2 operator <=(float2 left, float2 right) => new(left.x <= right.x, left.y <= right.y); + public static bool2 operator <=(float2 left, float right) => new(left.x <= right, left.y <= right); + public static bool2 operator <=(float left, float2 right) => new(left <= right.x, left <= right.y); + public static bool2 operator >=(float2 left, float2 right) => new(left.x >= right.x, left.y >= right.y); + public static bool2 operator >=(float2 left, float right) => new(left.x >= right, left.y >= right); + public static bool2 operator >=(float left, float2 right) => new(left >= right.x, left >= right.y); + public static bool2 operator ==(float2 left, float2 right) => new(left.x == right.x, left.y == right.y); + public static bool2 operator ==(float2 left, float right) => new(left.x == right, left.y == right); + public static bool2 operator ==(float left, float2 right) => new(left == right.x, left == right.y); + public static bool2 operator !=(float2 left, float2 right) => new(left.x != right.x, left.y != right.y); + public static bool2 operator !=(float2 left, float right) => new(left.x != right, left.y != right); + public static bool2 operator !=(float left, float2 right) => new(left != right.x, left != right.y); + + public readonly bool Equals(float2 other) => x.Equals(other.x) && y.Equals(other.y); + public override readonly bool Equals(object? obj) => obj is float2 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y); + public override readonly string ToString() => FormattableString.Invariant($"float2({x:R}, {y:R})"); } diff --git a/packages/cultmath/src/CultMath/float2x2.cs b/packages/cultmath/src/CultMath/float2x2.cs new file mode 100644 index 00000000..bd8673ae --- /dev/null +++ b/packages/cultmath/src/CultMath/float2x2.cs @@ -0,0 +1,67 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace CultMath; + +/// +/// HLSL float2x2: row-major. Constructors take rows, m[i] is a writable reference to +/// row i (so m[1][0] = s works), and _mRC names row R, column C exactly as HLSL +/// does. Use for products; there is no * +/// matrix product because HLSL's * is component-wise. +/// +public struct float2x2 : IEquatable +{ + public static readonly float2x2 identity = new(1.0f, 0.0f, 0.0f, 1.0f); + public static readonly float2x2 zero = new(0.0f, 0.0f, 0.0f, 0.0f); + + private float2 _row0; + private float2 _row1; + + public float2x2(float m00, float m01, float m10, float m11) + { + _row0 = new float2(m00, m01); + _row1 = new float2(m10, m11); + } + + public float2x2(float2 row0, float2 row1) + { + _row0 = row0; + _row1 = row1; + } + + public float _m00 { readonly get => _row0.x; set => _row0.x = value; } + public float _m01 { readonly get => _row0.y; set => _row0.y = value; } + public float _m10 { readonly get => _row1.x; set => _row1.x = value; } + public float _m11 { readonly get => _row1.y; set => _row1.y = value; } + + /// Writes through a readonly field, in parameter, property, or identity hit a defensive copy and are lost; write to a local and assign it back. + [UnscopedRef] + public ref float2 this[int row] + { + get + { + switch (row) + { + case 0: return ref _row0; + case 1: return ref _row1; + default: throw new ArgumentOutOfRangeException(nameof(row)); + } + } + } + + /// + /// Counter-clockwise rotation for column vectors: mul(Rotate(a), v) equals + /// math.rotate(v, a); mul(v, Rotate(a)) rotates the other way. + /// + public static float2x2 Rotate(float radians) + { + var s = MathF.Sin(radians); + var c = MathF.Cos(radians); + return new float2x2(c, -s, s, c); + } + + public readonly bool Equals(float2x2 other) => _row0.Equals(other._row0) && _row1.Equals(other._row1); + public override readonly bool Equals(object? obj) => obj is float2x2 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(_row0, _row1); + public override readonly string ToString() => FormattableString.Invariant($"float2x2({_row0.x:R}, {_row0.y:R}, {_row1.x:R}, {_row1.y:R})"); +} diff --git a/packages/cultmath/src/CultMath/float2x2.cs.meta b/packages/cultmath/src/CultMath/float2x2.cs.meta new file mode 100644 index 00000000..56840df7 --- /dev/null +++ b/packages/cultmath/src/CultMath/float2x2.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c0dcec14c0b7432dbd8ba0bebb0a7c2f diff --git a/packages/cultmath/src/CultMath/float3.cs b/packages/cultmath/src/CultMath/float3.cs index 4a4efeba..ca83628b 100644 --- a/packages/cultmath/src/CultMath/float3.cs +++ b/packages/cultmath/src/CultMath/float3.cs @@ -2,78 +2,87 @@ using System; namespace CultMath; -public record struct float3(float x, float y, float z) +public partial struct float3 : IEquatable { public static readonly float3 zero = new(0.0f, 0.0f, 0.0f); public static readonly float3 one = new(1.0f, 1.0f, 1.0f); - public float3(float2 xy, float z) - : this(xy.x, xy.y, z) + public float x; + public float y; + public float z; + + public float3(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; } - public float2 xy + public float this[int index] { - get => new(x, y); + readonly get => index switch + { + 0 => x, + 1 => y, + 2 => z, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; set { - x = value.x; - y = value.y; + switch (index) + { + case 0: x = value; break; + case 1: y = value; break; + case 2: z = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } } } - public float2 xz - { - get => new(x, z); - set - { - x = value.x; - z = value.y; - } - } - - public float2 yz - { - get => new(y, z); - set - { - y = value.x; - z = value.y; - } - } - - public float2 zy - { - get => new(z, y); - set - { - z = value.x; - y = value.y; - } - } - - public float3 yzx => new(y, z, x); - public float3 zyx => new(z, y, x); - - public float this[int index] => index switch - { - 0 => x, - 1 => y, - 2 => z, - _ => throw new ArgumentOutOfRangeException(nameof(index)), - }; - public static implicit operator float3(float value) => new(value, value, value); public static explicit operator System.Numerics.Vector3(float3 value) => new(value.x, value.y, value.z); public static explicit operator float3(System.Numerics.Vector3 value) => new(value.X, value.Y, value.Z); - public override readonly string ToString() => $"float3({x:R}, {y:R}, {z:R})"; - public static float3 operator +(float3 value) => value; public static float3 operator -(float3 value) => new(-value.x, -value.y, -value.z); + public static float3 operator +(float3 left, float3 right) => new(left.x + right.x, left.y + right.y, left.z + right.z); + public static float3 operator +(float3 left, float right) => new(left.x + right, left.y + right, left.z + right); + public static float3 operator +(float left, float3 right) => new(left + right.x, left + right.y, left + right.z); public static float3 operator -(float3 left, float3 right) => new(left.x - right.x, left.y - right.y, left.z - right.z); + public static float3 operator -(float3 left, float right) => new(left.x - right, left.y - right, left.z - right); + public static float3 operator -(float left, float3 right) => new(left - right.x, left - right.y, left - right.z); public static float3 operator *(float3 left, float3 right) => new(left.x * right.x, left.y * right.y, left.z * right.z); + public static float3 operator *(float3 left, float right) => new(left.x * right, left.y * right, left.z * right); + public static float3 operator *(float left, float3 right) => new(left * right.x, left * right.y, left * right.z); public static float3 operator /(float3 left, float3 right) => new(left.x / right.x, left.y / right.y, left.z / right.z); + public static float3 operator /(float3 left, float right) => new(left.x / right, left.y / right, left.z / right); + public static float3 operator /(float left, float3 right) => new(left / right.x, left / right.y, left / right.z); public static float3 operator %(float3 left, float3 right) => new(left.x % right.x, left.y % right.y, left.z % right.z); + public static float3 operator %(float3 left, float right) => new(left.x % right, left.y % right, left.z % right); + public static float3 operator %(float left, float3 right) => new(left % right.x, left % right.y, left % right.z); + + public static bool3 operator <(float3 left, float3 right) => new(left.x < right.x, left.y < right.y, left.z < right.z); + public static bool3 operator <(float3 left, float right) => new(left.x < right, left.y < right, left.z < right); + public static bool3 operator <(float left, float3 right) => new(left < right.x, left < right.y, left < right.z); + public static bool3 operator >(float3 left, float3 right) => new(left.x > right.x, left.y > right.y, left.z > right.z); + public static bool3 operator >(float3 left, float right) => new(left.x > right, left.y > right, left.z > right); + public static bool3 operator >(float left, float3 right) => new(left > right.x, left > right.y, left > right.z); + public static bool3 operator <=(float3 left, float3 right) => new(left.x <= right.x, left.y <= right.y, left.z <= right.z); + public static bool3 operator <=(float3 left, float right) => new(left.x <= right, left.y <= right, left.z <= right); + public static bool3 operator <=(float left, float3 right) => new(left <= right.x, left <= right.y, left <= right.z); + public static bool3 operator >=(float3 left, float3 right) => new(left.x >= right.x, left.y >= right.y, left.z >= right.z); + public static bool3 operator >=(float3 left, float right) => new(left.x >= right, left.y >= right, left.z >= right); + public static bool3 operator >=(float left, float3 right) => new(left >= right.x, left >= right.y, left >= right.z); + public static bool3 operator ==(float3 left, float3 right) => new(left.x == right.x, left.y == right.y, left.z == right.z); + public static bool3 operator ==(float3 left, float right) => new(left.x == right, left.y == right, left.z == right); + public static bool3 operator ==(float left, float3 right) => new(left == right.x, left == right.y, left == right.z); + public static bool3 operator !=(float3 left, float3 right) => new(left.x != right.x, left.y != right.y, left.z != right.z); + public static bool3 operator !=(float3 left, float right) => new(left.x != right, left.y != right, left.z != right); + public static bool3 operator !=(float left, float3 right) => new(left != right.x, left != right.y, left != right.z); + + public readonly bool Equals(float3 other) => x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z); + public override readonly bool Equals(object? obj) => obj is float3 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y, z); + public override readonly string ToString() => FormattableString.Invariant($"float3({x:R}, {y:R}, {z:R})"); } diff --git a/packages/cultmath/src/CultMath/float3x3.cs b/packages/cultmath/src/CultMath/float3x3.cs new file mode 100644 index 00000000..d7aad21c --- /dev/null +++ b/packages/cultmath/src/CultMath/float3x3.cs @@ -0,0 +1,116 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace CultMath; + +/// +/// HLSL float3x3: row-major. Constructors take rows, m[i] is a writable reference to +/// row i (so m[1][2] = s works), and _mRC names row R, column C exactly as HLSL +/// does. Use for products; there is no * +/// matrix product because HLSL's * is component-wise. +/// +public struct float3x3 : IEquatable +{ + public static readonly float3x3 identity = new(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f); + public static readonly float3x3 zero = new(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + + private float3 _row0; + private float3 _row1; + private float3 _row2; + + public float3x3(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) + { + _row0 = new float3(m00, m01, m02); + _row1 = new float3(m10, m11, m12); + _row2 = new float3(m20, m21, m22); + } + + public float3x3(float3 row0, float3 row1, float3 row2) + { + _row0 = row0; + _row1 = row1; + _row2 = row2; + } + + public float _m00 { readonly get => _row0.x; set => _row0.x = value; } + public float _m01 { readonly get => _row0.y; set => _row0.y = value; } + public float _m02 { readonly get => _row0.z; set => _row0.z = value; } + public float _m10 { readonly get => _row1.x; set => _row1.x = value; } + public float _m11 { readonly get => _row1.y; set => _row1.y = value; } + public float _m12 { readonly get => _row1.z; set => _row1.z = value; } + public float _m20 { readonly get => _row2.x; set => _row2.x = value; } + public float _m21 { readonly get => _row2.y; set => _row2.y = value; } + public float _m22 { readonly get => _row2.z; set => _row2.z = value; } + + /// Writes through a readonly field, in parameter, property, or identity hit a defensive copy and are lost; write to a local and assign it back. + [UnscopedRef] + public ref float3 this[int row] + { + get + { + switch (row) + { + case 0: return ref _row0; + case 1: return ref _row1; + case 2: return ref _row2; + default: throw new ArgumentOutOfRangeException(nameof(row)); + } + } + } + + /// Rotation about +X for column vectors (mul(m, v)). + public static float3x3 RotateX(float radians) + { + var s = MathF.Sin(radians); + var c = MathF.Cos(radians); + return new float3x3(1.0f, 0.0f, 0.0f, 0.0f, c, -s, 0.0f, s, c); + } + + /// Rotation about +Y for column vectors (mul(m, v)). + public static float3x3 RotateY(float radians) + { + var s = MathF.Sin(radians); + var c = MathF.Cos(radians); + return new float3x3(c, 0.0f, s, 0.0f, 1.0f, 0.0f, -s, 0.0f, c); + } + + /// Rotation about +Z for column vectors (mul(m, v)). + public static float3x3 RotateZ(float radians) + { + var s = MathF.Sin(radians); + var c = MathF.Cos(radians); + return new float3x3(c, -s, 0.0f, s, c, 0.0f, 0.0f, 0.0f, 1.0f); + } + + /// + /// Extrinsic Euler rotation in radians. The order names the axis applied first: + /// rotates about Y, then X, then Z, so the + /// matrix is mul(RotateZ, mul(RotateX, RotateY)) for column vectors. + /// + public static float3x3 Euler(float3 xyz, math.RotationOrder order = math.RotationOrder.Default) + { + var x = RotateX(xyz.x); + var y = RotateY(xyz.y); + var z = RotateZ(xyz.z); + return order switch + { + math.RotationOrder.XYZ => math.mul(z, math.mul(y, x)), + math.RotationOrder.XZY => math.mul(y, math.mul(z, x)), + math.RotationOrder.YXZ => math.mul(z, math.mul(x, y)), + math.RotationOrder.YZX => math.mul(x, math.mul(z, y)), + math.RotationOrder.ZXY => math.mul(y, math.mul(x, z)), + math.RotationOrder.ZYX => math.mul(x, math.mul(y, z)), + _ => throw new ArgumentOutOfRangeException(nameof(order)), + }; + } + + public static float3x3 Euler(float x, float y, float z, math.RotationOrder order = math.RotationOrder.Default) => + Euler(new float3(x, y, z), order); + + public readonly bool Equals(float3x3 other) => _row0.Equals(other._row0) && _row1.Equals(other._row1) && _row2.Equals(other._row2); + public override readonly bool Equals(object? obj) => obj is float3x3 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(_row0, _row1, _row2); + + public override readonly string ToString() => + FormattableString.Invariant($"float3x3({_row0.x:R}, {_row0.y:R}, {_row0.z:R}, {_row1.x:R}, {_row1.y:R}, {_row1.z:R}, {_row2.x:R}, {_row2.y:R}, {_row2.z:R})"); +} diff --git a/packages/cultmath/src/CultMath/float3x3.cs.meta b/packages/cultmath/src/CultMath/float3x3.cs.meta new file mode 100644 index 00000000..99b25ed3 --- /dev/null +++ b/packages/cultmath/src/CultMath/float3x3.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9ca36a9bfff144a9b51478a6d7b19c8e diff --git a/packages/cultmath/src/CultMath/float4.cs b/packages/cultmath/src/CultMath/float4.cs index b6eed097..3f647971 100644 --- a/packages/cultmath/src/CultMath/float4.cs +++ b/packages/cultmath/src/CultMath/float4.cs @@ -2,47 +2,46 @@ using System; namespace CultMath; -public record struct float4(float x, float y, float z, float w) +public partial struct float4 : IEquatable { public static readonly float4 zero = new(0.0f, 0.0f, 0.0f, 0.0f); public static readonly float4 one = new(1.0f, 1.0f, 1.0f, 1.0f); - public float4(float2 xy, float z, float w) - : this(xy.x, xy.y, z, w) + public float x; + public float y; + public float z; + public float w; + + public float4(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; } - public float4(float3 xyz, float w) - : this(xyz.x, xyz.y, xyz.z, w) + public float this[int index] { - } - - public float4(float x, float3 yzw) - : this(x, yzw.x, yzw.y, yzw.z) - { - } - - public float2 xy - { - get => new(x, y); + readonly get => index switch + { + 0 => x, + 1 => y, + 2 => z, + 3 => w, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; set { - x = value.x; - y = value.y; + switch (index) + { + case 0: x = value; break; + case 1: y = value; break; + case 2: z = value; break; + case 3: w = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } } } - public float3 xyw => new(x, y, w); - public float3 yzx => new(y, z, x); - public float3 xyz => new(x, y, z); - - public float this[int index] => index switch - { - 0 => x, - 1 => y, - 2 => z, - 3 => w, - _ => throw new ArgumentOutOfRangeException(nameof(index)), - }; public static implicit operator float4(float value) => new(value, value, value, value); public static explicit operator System.Numerics.Vector4(float4 value) => new(value.x, value.y, value.z, value.w); @@ -50,9 +49,44 @@ public record struct float4(float x, float y, float z, float w) public static float4 operator +(float4 value) => value; public static float4 operator -(float4 value) => new(-value.x, -value.y, -value.z, -value.w); + public static float4 operator +(float4 left, float4 right) => new(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); + public static float4 operator +(float4 left, float right) => new(left.x + right, left.y + right, left.z + right, left.w + right); + public static float4 operator +(float left, float4 right) => new(left + right.x, left + right.y, left + right.z, left + right.w); public static float4 operator -(float4 left, float4 right) => new(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); + public static float4 operator -(float4 left, float right) => new(left.x - right, left.y - right, left.z - right, left.w - right); + public static float4 operator -(float left, float4 right) => new(left - right.x, left - right.y, left - right.z, left - right.w); public static float4 operator *(float4 left, float4 right) => new(left.x * right.x, left.y * right.y, left.z * right.z, left.w * right.w); + public static float4 operator *(float4 left, float right) => new(left.x * right, left.y * right, left.z * right, left.w * right); + public static float4 operator *(float left, float4 right) => new(left * right.x, left * right.y, left * right.z, left * right.w); public static float4 operator /(float4 left, float4 right) => new(left.x / right.x, left.y / right.y, left.z / right.z, left.w / right.w); + public static float4 operator /(float4 left, float right) => new(left.x / right, left.y / right, left.z / right, left.w / right); + public static float4 operator /(float left, float4 right) => new(left / right.x, left / right.y, left / right.z, left / right.w); public static float4 operator %(float4 left, float4 right) => new(left.x % right.x, left.y % right.y, left.z % right.z, left.w % right.w); + public static float4 operator %(float4 left, float right) => new(left.x % right, left.y % right, left.z % right, left.w % right); + public static float4 operator %(float left, float4 right) => new(left % right.x, left % right.y, left % right.z, left % right.w); + + public static bool4 operator <(float4 left, float4 right) => new(left.x < right.x, left.y < right.y, left.z < right.z, left.w < right.w); + public static bool4 operator <(float4 left, float right) => new(left.x < right, left.y < right, left.z < right, left.w < right); + public static bool4 operator <(float left, float4 right) => new(left < right.x, left < right.y, left < right.z, left < right.w); + public static bool4 operator >(float4 left, float4 right) => new(left.x > right.x, left.y > right.y, left.z > right.z, left.w > right.w); + public static bool4 operator >(float4 left, float right) => new(left.x > right, left.y > right, left.z > right, left.w > right); + public static bool4 operator >(float left, float4 right) => new(left > right.x, left > right.y, left > right.z, left > right.w); + public static bool4 operator <=(float4 left, float4 right) => new(left.x <= right.x, left.y <= right.y, left.z <= right.z, left.w <= right.w); + public static bool4 operator <=(float4 left, float right) => new(left.x <= right, left.y <= right, left.z <= right, left.w <= right); + public static bool4 operator <=(float left, float4 right) => new(left <= right.x, left <= right.y, left <= right.z, left <= right.w); + public static bool4 operator >=(float4 left, float4 right) => new(left.x >= right.x, left.y >= right.y, left.z >= right.z, left.w >= right.w); + public static bool4 operator >=(float4 left, float right) => new(left.x >= right, left.y >= right, left.z >= right, left.w >= right); + public static bool4 operator >=(float left, float4 right) => new(left >= right.x, left >= right.y, left >= right.z, left >= right.w); + public static bool4 operator ==(float4 left, float4 right) => new(left.x == right.x, left.y == right.y, left.z == right.z, left.w == right.w); + public static bool4 operator ==(float4 left, float right) => new(left.x == right, left.y == right, left.z == right, left.w == right); + public static bool4 operator ==(float left, float4 right) => new(left == right.x, left == right.y, left == right.z, left == right.w); + public static bool4 operator !=(float4 left, float4 right) => new(left.x != right.x, left.y != right.y, left.z != right.z, left.w != right.w); + public static bool4 operator !=(float4 left, float right) => new(left.x != right, left.y != right, left.z != right, left.w != right); + public static bool4 operator !=(float left, float4 right) => new(left != right.x, left != right.y, left != right.z, left != right.w); + + public readonly bool Equals(float4 other) => x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z) && w.Equals(other.w); + public override readonly bool Equals(object? obj) => obj is float4 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y, z, w); + public override readonly string ToString() => FormattableString.Invariant($"float4({x:R}, {y:R}, {z:R}, {w:R})"); } diff --git a/packages/cultmath/src/CultMath/int2.cs b/packages/cultmath/src/CultMath/int2.cs index 7c2f350b..f690a25d 100644 --- a/packages/cultmath/src/CultMath/int2.cs +++ b/packages/cultmath/src/CultMath/int2.cs @@ -2,17 +2,38 @@ using System; namespace CultMath; -public record struct int2(int x, int y) +public partial struct int2 : IEquatable { public static readonly int2 zero = new(0, 0); public static readonly int2 one = new(1, 1); - public int this[int index] => index switch + public int x; + public int y; + + public int2(int x, int y) { - 0 => x, - 1 => y, - _ => throw new ArgumentOutOfRangeException(nameof(index)), - }; + this.x = x; + this.y = y; + } + + public int this[int index] + { + readonly get => index switch + { + 0 => x, + 1 => y, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: x = value; break; + case 1: y = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } public static implicit operator int2(int value) => new(value, value); public static implicit operator float2(int2 value) => new(value.x, value.y); @@ -26,4 +47,16 @@ public record struct int2(int x, int y) public static int2 operator *(int2 left, int2 right) => new(left.x * right.x, left.y * right.y); public static int2 operator /(int2 left, int2 right) => new(left.x / right.x, left.y / right.y); public static int2 operator %(int2 left, int2 right) => new(left.x % right.x, left.y % right.y); + + public static bool2 operator <(int2 left, int2 right) => new(left.x < right.x, left.y < right.y); + public static bool2 operator >(int2 left, int2 right) => new(left.x > right.x, left.y > right.y); + public static bool2 operator <=(int2 left, int2 right) => new(left.x <= right.x, left.y <= right.y); + public static bool2 operator >=(int2 left, int2 right) => new(left.x >= right.x, left.y >= right.y); + public static bool2 operator ==(int2 left, int2 right) => new(left.x == right.x, left.y == right.y); + public static bool2 operator !=(int2 left, int2 right) => new(left.x != right.x, left.y != right.y); + + public readonly bool Equals(int2 other) => x == other.x && y == other.y; + public override readonly bool Equals(object? obj) => obj is int2 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y); + public override readonly string ToString() => FormattableString.Invariant($"int2({x}, {y})"); } diff --git a/packages/cultmath/src/CultMath/int3.cs b/packages/cultmath/src/CultMath/int3.cs new file mode 100644 index 00000000..454a8970 --- /dev/null +++ b/packages/cultmath/src/CultMath/int3.cs @@ -0,0 +1,64 @@ +using System; + +namespace CultMath; + +public partial struct int3 : IEquatable +{ + public static readonly int3 zero = new(0, 0, 0); + public static readonly int3 one = new(1, 1, 1); + + public int x; + public int y; + public int z; + + public int3(int x, int y, int z) + { + this.x = x; + this.y = y; + this.z = z; + } + + public int this[int index] + { + readonly get => index switch + { + 0 => x, + 1 => y, + 2 => z, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: x = value; break; + case 1: y = value; break; + case 2: z = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } + + public static implicit operator int3(int value) => new(value, value, value); + public static implicit operator float3(int3 value) => new(value.x, value.y, value.z); + + public static int3 operator +(int3 value) => value; + public static int3 operator -(int3 value) => new(-value.x, -value.y, -value.z); + public static int3 operator +(int3 left, int3 right) => new(left.x + right.x, left.y + right.y, left.z + right.z); + public static int3 operator -(int3 left, int3 right) => new(left.x - right.x, left.y - right.y, left.z - right.z); + public static int3 operator *(int3 left, int3 right) => new(left.x * right.x, left.y * right.y, left.z * right.z); + public static int3 operator /(int3 left, int3 right) => new(left.x / right.x, left.y / right.y, left.z / right.z); + public static int3 operator %(int3 left, int3 right) => new(left.x % right.x, left.y % right.y, left.z % right.z); + + public static bool3 operator <(int3 left, int3 right) => new(left.x < right.x, left.y < right.y, left.z < right.z); + public static bool3 operator >(int3 left, int3 right) => new(left.x > right.x, left.y > right.y, left.z > right.z); + public static bool3 operator <=(int3 left, int3 right) => new(left.x <= right.x, left.y <= right.y, left.z <= right.z); + public static bool3 operator >=(int3 left, int3 right) => new(left.x >= right.x, left.y >= right.y, left.z >= right.z); + public static bool3 operator ==(int3 left, int3 right) => new(left.x == right.x, left.y == right.y, left.z == right.z); + public static bool3 operator !=(int3 left, int3 right) => new(left.x != right.x, left.y != right.y, left.z != right.z); + + public readonly bool Equals(int3 other) => x == other.x && y == other.y && z == other.z; + public override readonly bool Equals(object? obj) => obj is int3 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y, z); + public override readonly string ToString() => FormattableString.Invariant($"int3({x}, {y}, {z})"); +} diff --git a/packages/cultmath/src/CultMath/int3.cs.meta b/packages/cultmath/src/CultMath/int3.cs.meta new file mode 100644 index 00000000..d0400303 --- /dev/null +++ b/packages/cultmath/src/CultMath/int3.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 42ee98aace354db19b3ae87bdcbc1df9 diff --git a/packages/cultmath/src/CultMath/int4.cs b/packages/cultmath/src/CultMath/int4.cs new file mode 100644 index 00000000..caaaeb9e --- /dev/null +++ b/packages/cultmath/src/CultMath/int4.cs @@ -0,0 +1,68 @@ +using System; + +namespace CultMath; + +public partial struct int4 : IEquatable +{ + public static readonly int4 zero = new(0, 0, 0, 0); + public static readonly int4 one = new(1, 1, 1, 1); + + public int x; + public int y; + public int z; + public int w; + + public int4(int x, int y, int z, int w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public int this[int index] + { + readonly get => index switch + { + 0 => x, + 1 => y, + 2 => z, + 3 => w, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: x = value; break; + case 1: y = value; break; + case 2: z = value; break; + case 3: w = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } + + public static implicit operator int4(int value) => new(value, value, value, value); + public static implicit operator float4(int4 value) => new(value.x, value.y, value.z, value.w); + + public static int4 operator +(int4 value) => value; + public static int4 operator -(int4 value) => new(-value.x, -value.y, -value.z, -value.w); + public static int4 operator +(int4 left, int4 right) => new(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); + public static int4 operator -(int4 left, int4 right) => new(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); + public static int4 operator *(int4 left, int4 right) => new(left.x * right.x, left.y * right.y, left.z * right.z, left.w * right.w); + public static int4 operator /(int4 left, int4 right) => new(left.x / right.x, left.y / right.y, left.z / right.z, left.w / right.w); + public static int4 operator %(int4 left, int4 right) => new(left.x % right.x, left.y % right.y, left.z % right.z, left.w % right.w); + + public static bool4 operator <(int4 left, int4 right) => new(left.x < right.x, left.y < right.y, left.z < right.z, left.w < right.w); + public static bool4 operator >(int4 left, int4 right) => new(left.x > right.x, left.y > right.y, left.z > right.z, left.w > right.w); + public static bool4 operator <=(int4 left, int4 right) => new(left.x <= right.x, left.y <= right.y, left.z <= right.z, left.w <= right.w); + public static bool4 operator >=(int4 left, int4 right) => new(left.x >= right.x, left.y >= right.y, left.z >= right.z, left.w >= right.w); + public static bool4 operator ==(int4 left, int4 right) => new(left.x == right.x, left.y == right.y, left.z == right.z, left.w == right.w); + public static bool4 operator !=(int4 left, int4 right) => new(left.x != right.x, left.y != right.y, left.z != right.z, left.w != right.w); + + public readonly bool Equals(int4 other) => x == other.x && y == other.y && z == other.z && w == other.w; + public override readonly bool Equals(object? obj) => obj is int4 other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y, z, w); + public override readonly string ToString() => FormattableString.Invariant($"int4({x}, {y}, {z}, {w})"); +} diff --git a/packages/cultmath/src/CultMath/int4.cs.meta b/packages/cultmath/src/CultMath/int4.cs.meta new file mode 100644 index 00000000..48d0eaba --- /dev/null +++ b/packages/cultmath/src/CultMath/int4.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1a313709f76a430eb83901bb2d05c74e diff --git a/packages/cultmath/src/CultMath/math.cs b/packages/cultmath/src/CultMath/math.cs index d0a56e76..ca74857f 100644 --- a/packages/cultmath/src/CultMath/math.cs +++ b/packages/cultmath/src/CultMath/math.cs @@ -2,26 +2,56 @@ using System; namespace CultMath; -public static class math +public static partial class math { public const float PI = MathF.PI; public const float TAU = MathF.PI * 2.0f; public const float HALF_PI = MathF.PI * 0.5f; + /// + /// Extrinsic Euler order for : + /// the first letter is the axis applied first. Nested here so using static CultMath.math; + /// brings it into scope with the rest of the shader vocabulary. + /// + public enum RotationOrder : byte + { + XYZ, + XZY, + YXZ, + YZX, + ZXY, + ZYX, + Default = ZXY, + } + public static float2 float2(float x, float y) => new(x, y); public static float2 float2(float value) => new(value, value); public static bool2 bool2(bool x, bool y) => new(x, y); public static bool2 bool2(bool value) => new(value, value); public static int2 int2(int x, int y) => new(x, y); public static int2 int2(int value) => new(value, value); + // Mixed constructor functions (float4(float2, float2), ...) are generated in Swizzles.g.cs. public static float3 float3(float x, float y, float z) => new(x, y, z); - public static float3 float3(float2 xy, float z) => new(xy, z); public static float3 float3(float value) => new(value, value, value); public static float4 float4(float x, float y, float z, float w) => new(x, y, z, w); - public static float4 float4(float2 xy, float z, float w) => new(xy, z, w); - public static float4 float4(float3 xyz, float w) => new(xyz, w); - public static float4 float4(float x, float3 yzw) => new(x, yzw); public static float4 float4(float value) => new(value, value, value, value); + public static bool3 bool3(bool x, bool y, bool z) => new(x, y, z); + public static bool3 bool3(bool value) => new(value, value, value); + public static bool4 bool4(bool x, bool y, bool z, bool w) => new(x, y, z, w); + public static bool4 bool4(bool value) => new(value, value, value, value); + public static int3 int3(int x, int y, int z) => new(x, y, z); + public static int3 int3(int value) => new(value, value, value); + public static int4 int4(int x, int y, int z, int w) => new(x, y, z, w); + public static int4 int4(int value) => new(value, value, value, value); + + // Matrix constructor functions take rows, as HLSL does. Under `using static CultMath.math;` + // these method groups hide the type names in member access, so static members need a + // qualified type: `CultMath.float3x3.Euler(...)`, `CultMath.float2x2.Rotate(...)`. + public static float2x2 float2x2(float m00, float m01, float m10, float m11) => new(m00, m01, m10, m11); + public static float2x2 float2x2(float2 row0, float2 row1) => new(row0, row1); + public static float3x3 float3x3(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) => + new(m00, m01, m02, m10, m11, m12, m20, m21, m22); + public static float3x3 float3x3(float3 row0, float3 row1, float3 row2) => new(row0, row1, row2); public static float radians(float degrees) => degrees * (PI / 180.0f); public static float degrees(float radians) => radians * (180.0f / PI); public static float sin(float value) => MathF.Sin(value); @@ -46,11 +76,20 @@ public static class math public static float2 abs(float2 value) => new(abs(value.x), abs(value.y)); public static float3 abs(float3 value) => new(abs(value.x), abs(value.y), abs(value.z)); public static float4 abs(float4 value) => new(abs(value.x), abs(value.y), abs(value.z), abs(value.w)); + public static int abs(int value) => Math.Abs(value); + public static int2 abs(int2 value) => new(abs(value.x), abs(value.y)); + public static int3 abs(int3 value) => new(abs(value.x), abs(value.y), abs(value.z)); + public static int4 abs(int4 value) => new(abs(value.x), abs(value.y), abs(value.z), abs(value.w)); - public static float sign(float value) => MathF.Sign(value); - public static float2 sign(float2 value) => new(sign(value.x), sign(value.y)); - public static float3 sign(float3 value) => new(sign(value.x), sign(value.y), sign(value.z)); - public static float4 sign(float4 value) => new(sign(value.x), sign(value.y), sign(value.z), sign(value.w)); + // HLSL sign returns int. NaN compares false both ways, so it yields 0 instead of throwing. + public static int sign(float value) => value > 0.0f ? 1 : value < 0.0f ? -1 : 0; + public static int2 sign(float2 value) => new(sign(value.x), sign(value.y)); + public static int3 sign(float3 value) => new(sign(value.x), sign(value.y), sign(value.z)); + public static int4 sign(float4 value) => new(sign(value.x), sign(value.y), sign(value.z), sign(value.w)); + public static int sign(int value) => value > 0 ? 1 : value < 0 ? -1 : 0; + public static int2 sign(int2 value) => new(sign(value.x), sign(value.y)); + public static int3 sign(int3 value) => new(sign(value.x), sign(value.y), sign(value.z)); + public static int4 sign(int4 value) => new(sign(value.x), sign(value.y), sign(value.z), sign(value.w)); public static float floor(float value) => MathF.Floor(value); public static float2 floor(float2 value) => new(floor(value.x), floor(value.y)); @@ -67,27 +106,36 @@ public static class math public static float3 frac(float3 value) => value - floor(value); public static float4 frac(float4 value) => value - floor(value); - public static float min(float left, float right) => MathF.Min(left, right); + // dxc lowers float min/max/clamp to DXIL FMin/FMax (saturate to Saturate, defined as FMin(1, FMax(0, x))). + // DXIL.rst: FMin(a, b) is a < b ? a : b, FMax(a, b) is a >= b ? a : b, and a NaN operand returns the other. + public static float min(float left, float right) => left < right || float.IsNaN(right) ? left : right; public static int min(int left, int right) => Math.Min(left, right); public static float2 min(float2 left, float2 right) => new(min(left.x, right.x), min(left.y, right.y)); public static float3 min(float3 left, float3 right) => new(min(left.x, right.x), min(left.y, right.y), min(left.z, right.z)); public static float4 min(float4 left, float4 right) => new(min(left.x, right.x), min(left.y, right.y), min(left.z, right.z), min(left.w, right.w)); + public static int2 min(int2 left, int2 right) => new(min(left.x, right.x), min(left.y, right.y)); + public static int3 min(int3 left, int3 right) => new(min(left.x, right.x), min(left.y, right.y), min(left.z, right.z)); + public static int4 min(int4 left, int4 right) => new(min(left.x, right.x), min(left.y, right.y), min(left.z, right.z), min(left.w, right.w)); - public static float max(float left, float right) => MathF.Max(left, right); + public static float max(float left, float right) => left >= right || float.IsNaN(right) ? left : right; public static int max(int left, int right) => Math.Max(left, right); public static float2 max(float2 left, float2 right) => new(max(left.x, right.x), max(left.y, right.y)); public static float3 max(float3 left, float3 right) => new(max(left.x, right.x), max(left.y, right.y), max(left.z, right.z)); public static float4 max(float4 left, float4 right) => new(max(left.x, right.x), max(left.y, right.y), max(left.z, right.z), max(left.w, right.w)); + public static int2 max(int2 left, int2 right) => new(max(left.x, right.x), max(left.y, right.y)); + public static int3 max(int3 left, int3 right) => new(max(left.x, right.x), max(left.y, right.y), max(left.z, right.z)); + public static int4 max(int4 left, int4 right) => new(max(left.x, right.x), max(left.y, right.y), max(left.z, right.z), max(left.w, right.w)); public static float clamp(float value, float minimum, float maximum) => min(max(value, minimum), maximum); public static float2 clamp(float2 value, float2 minimum, float2 maximum) => min(max(value, minimum), maximum); public static float3 clamp(float3 value, float3 minimum, float3 maximum) => min(max(value, minimum), maximum); public static float4 clamp(float4 value, float4 minimum, float4 maximum) => min(max(value, minimum), maximum); - public static float saturate(float value) => clamp(value, 0.0f, 1.0f); - public static float2 saturate(float2 value) => clamp(value, 0.0f, 1.0f); - public static float3 saturate(float3 value) => clamp(value, 0.0f, 1.0f); - public static float4 saturate(float4 value) => clamp(value, 0.0f, 1.0f); + // DXIL Saturate is FMin(1, FMax(0, x)); that operand order, unlike clamp(x, 0, 1), maps -0 to +0. + public static float saturate(float value) => min(1.0f, max(0.0f, value)); + public static float2 saturate(float2 value) => min(1.0f, max(0.0f, value)); + public static float3 saturate(float3 value) => min(1.0f, max(0.0f, value)); + public static float4 saturate(float4 value) => min(1.0f, max(0.0f, value)); public static float lerp(float start, float end, float amount) => start + (end - start) * amount; public static double lerp(double start, double end, double amount) => start + (end - start) * amount; @@ -105,6 +153,7 @@ public static class math public static double2 unlerp(double2 start, double2 end, double2 value) => (value - start) / (end - start); public static double3 unlerp(double3 start, double3 end, double3 value) => (value - start) / (end - start); + // dxc lowers step(y, x) to x < y ? 0 : 1 (fcmp olt, then select), so a NaN on either side yields 1. public static float step(float edge, float value) => value < edge ? 0.0f : 1.0f; public static float2 step(float2 edge, float2 value) => new(step(edge.x, value.x), step(edge.y, value.y)); public static float3 step(float3 edge, float3 value) => new(step(edge.x, value.x), step(edge.y, value.y), step(edge.z, value.z)); @@ -156,9 +205,9 @@ public static class math public static float distance(float3 left, float3 right) => length(left - right); public static float distance(float4 left, float4 right) => length(left - right); - public static float2 normalize(float2 value) => value / MathF.Max(length(value), 1.0e-20f); - public static float3 normalize(float3 value) => value / MathF.Max(length(value), 1.0e-20f); - public static float4 normalize(float4 value) => value / MathF.Max(length(value), 1.0e-20f); + public static float2 normalize(float2 value) => value / max(length(value), 1.0e-20f); + public static float3 normalize(float3 value) => value / max(length(value), 1.0e-20f); + public static float4 normalize(float4 value) => value / max(length(value), 1.0e-20f); public static quaternion normalize(quaternion value) { var length = MathF.Sqrt(value.x * value.x + value.y * value.y + value.z * value.z + value.w * value.w); @@ -324,9 +373,164 @@ public static class math public static float hash(float2 value) => hash(dot(value, new float2(127.1f, 311.7f))); public static float hash(float3 value) => hash(dot(value, new float3(127.1f, 311.7f, 74.7f))); + public static float log(float value) => MathF.Log(value); + public static float2 log(float2 value) => new(log(value.x), log(value.y)); + public static float3 log(float3 value) => new(log(value.x), log(value.y), log(value.z)); + public static float4 log(float4 value) => new(log(value.x), log(value.y), log(value.z), log(value.w)); + + public static float2 pow(float2 value, float2 power) => new(pow(value.x, power.x), pow(value.y, power.y)); + public static float3 pow(float3 value, float3 power) => new(pow(value.x, power.x), pow(value.y, power.y), pow(value.z, power.z)); + public static float4 pow(float4 value, float4 power) => new(pow(value.x, power.x), pow(value.y, power.y), pow(value.z, power.z), pow(value.w, power.w)); + + public static float2 sqrt(float2 value) => new(sqrt(value.x), sqrt(value.y)); + public static float3 sqrt(float3 value) => new(sqrt(value.x), sqrt(value.y), sqrt(value.z)); + public static float4 sqrt(float4 value) => new(sqrt(value.x), sqrt(value.y), sqrt(value.z), sqrt(value.w)); + + public static float2 sin(float2 value) => new(sin(value.x), sin(value.y)); + public static float3 sin(float3 value) => new(sin(value.x), sin(value.y), sin(value.z)); + public static float4 sin(float4 value) => new(sin(value.x), sin(value.y), sin(value.z), sin(value.w)); + + public static float2 cos(float2 value) => new(cos(value.x), cos(value.y)); + public static float3 cos(float3 value) => new(cos(value.x), cos(value.y), cos(value.z)); + public static float4 cos(float4 value) => new(cos(value.x), cos(value.y), cos(value.z), cos(value.w)); + + public static float2 acos(float2 value) => new(acos(value.x), acos(value.y)); + public static float3 acos(float3 value) => new(acos(value.x), acos(value.y), acos(value.z)); + public static float4 acos(float4 value) => new(acos(value.x), acos(value.y), acos(value.z), acos(value.w)); + + public static float2 atan2(float2 y, float2 x) => new(atan2(y.x, x.x), atan2(y.y, x.y)); + public static float3 atan2(float3 y, float3 x) => new(atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)); + public static float4 atan2(float4 y, float4 x) => new(atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)); + + // DXIL FMin/FMax take double overloads with the same rules as float (see min/max above). + public static double min(double left, double right) => left < right || double.IsNaN(right) ? left : right; + public static double max(double left, double right) => left >= right || double.IsNaN(right) ? left : right; + + public static bool any(bool2 value) => value.x || value.y; + public static bool any(bool3 value) => value.x || value.y || value.z; + public static bool any(bool4 value) => value.x || value.y || value.z || value.w; + public static bool all(bool2 value) => value.x && value.y; + public static bool all(bool3 value) => value.x && value.y && value.z; + public static bool all(bool4 value) => value.x && value.y && value.z && value.w; + + // HLSL any/all on numeric vectors test components against zero (NaN != 0 counts as true). + public static bool any(float2 value) => value.x != 0.0f || value.y != 0.0f; + public static bool any(float3 value) => value.x != 0.0f || value.y != 0.0f || value.z != 0.0f; + public static bool any(float4 value) => value.x != 0.0f || value.y != 0.0f || value.z != 0.0f || value.w != 0.0f; + public static bool all(float2 value) => value.x != 0.0f && value.y != 0.0f; + public static bool all(float3 value) => value.x != 0.0f && value.y != 0.0f && value.z != 0.0f; + public static bool all(float4 value) => value.x != 0.0f && value.y != 0.0f && value.z != 0.0f && value.w != 0.0f; + public static bool any(int2 value) => value.x != 0 || value.y != 0; + public static bool any(int3 value) => value.x != 0 || value.y != 0 || value.z != 0; + public static bool any(int4 value) => value.x != 0 || value.y != 0 || value.z != 0 || value.w != 0; + public static bool all(int2 value) => value.x != 0 && value.y != 0; + public static bool all(int3 value) => value.x != 0 && value.y != 0 && value.z != 0; + public static bool all(int4 value) => value.x != 0 && value.y != 0 && value.z != 0 && value.w != 0; + + // HLSL 2021 argument order: select(condition, whenTrue, whenFalse). + // Unity.Mathematics' select(falseValue, trueValue, condition) is the reverse. + public static float select(bool condition, float whenTrue, float whenFalse) => condition ? whenTrue : whenFalse; + public static float2 select(bool2 condition, float2 whenTrue, float2 whenFalse) => + new(condition.x ? whenTrue.x : whenFalse.x, condition.y ? whenTrue.y : whenFalse.y); + public static float3 select(bool3 condition, float3 whenTrue, float3 whenFalse) => + new(condition.x ? whenTrue.x : whenFalse.x, condition.y ? whenTrue.y : whenFalse.y, condition.z ? whenTrue.z : whenFalse.z); + public static float4 select(bool4 condition, float4 whenTrue, float4 whenFalse) => + new(condition.x ? whenTrue.x : whenFalse.x, condition.y ? whenTrue.y : whenFalse.y, condition.z ? whenTrue.z : whenFalse.z, condition.w ? whenTrue.w : whenFalse.w); + + // HLSL mul: mul(m, v) treats v as a column vector, mul(v, m) as a row vector. + public static float mul(float left, float right) => left * right; + + public static float2 mul(float2x2 m, float2 v) => + new(m._m00 * v.x + m._m01 * v.y, m._m10 * v.x + m._m11 * v.y); + + public static float2 mul(float2 v, float2x2 m) => + new(v.x * m._m00 + v.y * m._m10, v.x * m._m01 + v.y * m._m11); + + public static float2x2 mul(float2x2 a, float2x2 b) => + new( + a._m00 * b._m00 + a._m01 * b._m10, a._m00 * b._m01 + a._m01 * b._m11, + a._m10 * b._m00 + a._m11 * b._m10, a._m10 * b._m01 + a._m11 * b._m11); + + public static float3 mul(float3x3 m, float3 v) => + new( + m._m00 * v.x + m._m01 * v.y + m._m02 * v.z, + m._m10 * v.x + m._m11 * v.y + m._m12 * v.z, + m._m20 * v.x + m._m21 * v.y + m._m22 * v.z); + + public static float3 mul(float3 v, float3x3 m) => + new( + v.x * m._m00 + v.y * m._m10 + v.z * m._m20, + v.x * m._m01 + v.y * m._m11 + v.z * m._m21, + v.x * m._m02 + v.y * m._m12 + v.z * m._m22); + + public static float3x3 mul(float3x3 a, float3x3 b) => + new( + a._m00 * b._m00 + a._m01 * b._m10 + a._m02 * b._m20, + a._m00 * b._m01 + a._m01 * b._m11 + a._m02 * b._m21, + a._m00 * b._m02 + a._m01 * b._m12 + a._m02 * b._m22, + a._m10 * b._m00 + a._m11 * b._m10 + a._m12 * b._m20, + a._m10 * b._m01 + a._m11 * b._m11 + a._m12 * b._m21, + a._m10 * b._m02 + a._m11 * b._m12 + a._m12 * b._m22, + a._m20 * b._m00 + a._m21 * b._m10 + a._m22 * b._m20, + a._m20 * b._m01 + a._m21 * b._m11 + a._m22 * b._m21, + a._m20 * b._m02 + a._m21 * b._m12 + a._m22 * b._m22); + + // Ashima Arts / Ian McEwan 3D simplex noise (MIT), mirrored by cultmath_snoise(float3) + // in shaders/CultMath.hlsl with the same float32 evaluation order. + public static float snoise(float3 value) + { + const float cx = 1.0f / 6.0f; + const float cy = 1.0f / 3.0f; + var i = floor(value + dot(value, new float3(cy, cy, cy))); + var x0 = value - i + dot(i, new float3(cx, cx, cx)); + var g = step(new float3(x0.y, x0.z, x0.x), x0); + var l = 1.0f - g; + var lzxy = new float3(l.z, l.x, l.y); + var i1 = min(g, lzxy); + var i2 = max(g, lzxy); + var x1 = x0 - i1 + cx; + var x2 = x0 - i2 + cy; + var x3 = x0 - 0.5f; + + i = snoise_mod289(i); + var p = snoise_permute(snoise_permute(snoise_permute( + i.z + new float4(0.0f, i1.z, i2.z, 1.0f)) + i.y + new float4(0.0f, i1.y, i2.y, 1.0f)) + i.x + new float4(0.0f, i1.x, i2.x, 1.0f)); + + const float n = 0.142857142857f; + var ns = new float3(2.0f * n, 0.5f * n - 1.0f, n); + var j = p - 49.0f * floor(p * ns.z * ns.z); + var xs = floor(j * ns.z); + var ys = floor(j - 7.0f * xs); + var x = xs * ns.x + ns.y; + var y = ys * ns.x + ns.y; + var h = 1.0f - abs(x) - abs(y); + + var b0 = new float4(x.x, x.y, y.x, y.y); + var b1 = new float4(x.z, x.w, y.z, y.w); + var s0 = floor(b0) * 2.0f + 1.0f; + var s1 = floor(b1) * 2.0f + 1.0f; + var sh = -step(h, 0.0f); + var a0 = new float4(b0.x, b0.z, b0.y, b0.w) + new float4(s0.x, s0.z, s0.y, s0.w) * new float4(sh.x, sh.x, sh.y, sh.y); + var a1 = new float4(b1.x, b1.z, b1.y, b1.w) + new float4(s1.x, s1.z, s1.y, s1.w) * new float4(sh.z, sh.z, sh.w, sh.w); + + var p0 = new float3(a0.x, a0.y, h.x); + var p1 = new float3(a0.z, a0.w, h.y); + var p2 = new float3(a1.x, a1.y, h.z); + var p3 = new float3(a1.z, a1.w, h.w); + var norm = 1.79284291400159f - 0.85373472095314f * new float4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + var m = max(0.6f - new float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0f); + m = m * m; + return 42.0f * dot(m * m, new float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); + } + // Ashima Arts / Ian McEwan 2D simplex noise, kept component-explicit so // the C# and HLSL mirrors preserve the same float32 evaluation order. - public static float simplex_noise(float2 value) + public static float snoise(float2 value) { var c = new float4( 0.211324865405187f, @@ -342,8 +546,8 @@ public static class math x0.x + c.z, x0.y + c.z); - i = simplex_mod289(i); - var p = simplex_permute(simplex_permute(i.y + new float3(0.0f, i1.y, 1.0f)) + i.x + new float3(0.0f, i1.x, 1.0f)); + i = snoise_mod289(i); + var p = snoise_permute(snoise_permute(i.y + new float3(0.0f, i1.y, 1.0f)) + i.x + new float3(0.0f, i1.x, 1.0f)); var m = max(0.5f - new float3( dot(x0, x0), x12.x * x12.x + x12.y * x12.y, @@ -364,9 +568,11 @@ public static class math return 130.0f * dot(m, g); } - private static float2 simplex_mod289(float2 value) => value - floor(value * (1.0f / 289.0f)) * 289.0f; - private static float3 simplex_mod289(float3 value) => value - floor(value * (1.0f / 289.0f)) * 289.0f; - private static float3 simplex_permute(float3 value) => simplex_mod289(((value * 34.0f) + 1.0f) * value); + private static float2 snoise_mod289(float2 value) => value - floor(value * (1.0f / 289.0f)) * 289.0f; + private static float3 snoise_mod289(float3 value) => value - floor(value * (1.0f / 289.0f)) * 289.0f; + private static float4 snoise_mod289(float4 value) => value - floor(value * (1.0f / 289.0f)) * 289.0f; + private static float3 snoise_permute(float3 value) => snoise_mod289(((value * 34.0f) + 1.0f) * value); + private static float4 snoise_permute(float4 value) => snoise_mod289(((value * 34.0f) + 1.0f) * value); public static float value_noise(float2 position) { diff --git a/packages/cultmath/src/CultMath/quaternion.cs b/packages/cultmath/src/CultMath/quaternion.cs index fe8dda8e..e8c4a48c 100644 --- a/packages/cultmath/src/CultMath/quaternion.cs +++ b/packages/cultmath/src/CultMath/quaternion.cs @@ -1,9 +1,28 @@ +using System; + namespace CultMath; -public record struct quaternion(float x, float y, float z, float w) +/// +/// Not an HLSL type: shaders carry rotations as float4. Equality therefore stays +/// scalar (== returns bool) instead of following the component-wise vector rule. +/// +public struct quaternion : IEquatable { public static readonly quaternion identity = new(0.0f, 0.0f, 0.0f, 1.0f); + public float x; + public float y; + public float z; + public float w; + + public quaternion(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + public static quaternion LookRotation(float3 forward, float3 up) { forward = math.normalize(forward); @@ -65,4 +84,12 @@ public record struct quaternion(float x, float y, float z, float w) public static implicit operator quaternion(float4 value) => new(value.x, value.y, value.z, value.w); public static implicit operator float4(quaternion value) => new(value.x, value.y, value.z, value.w); + + public static bool operator ==(quaternion left, quaternion right) => left.Equals(right); + public static bool operator !=(quaternion left, quaternion right) => !left.Equals(right); + + public readonly bool Equals(quaternion other) => x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z) && w.Equals(other.w); + public override readonly bool Equals(object? obj) => obj is quaternion other && Equals(other); + public override readonly int GetHashCode() => HashCode.Combine(x, y, z, w); + public override readonly string ToString() => FormattableString.Invariant($"quaternion({x:R}, {y:R}, {z:R}, {w:R})"); } diff --git a/packages/cultmath/tests/CultMath.Tests/CultMath.Tests.csproj b/packages/cultmath/tests/CultMath.Tests/CultMath.Tests.csproj index 1e77eba1..d77fd05f 100644 --- a/packages/cultmath/tests/CultMath.Tests/CultMath.Tests.csproj +++ b/packages/cultmath/tests/CultMath.Tests/CultMath.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/packages/cultmath/tests/CultMath.Tests/HlslMirrorTests.cs b/packages/cultmath/tests/CultMath.Tests/HlslMirrorTests.cs index 0034a283..445ad255 100644 --- a/packages/cultmath/tests/CultMath.Tests/HlslMirrorTests.cs +++ b/packages/cultmath/tests/CultMath.Tests/HlslMirrorTests.cs @@ -42,7 +42,8 @@ public sealed class HlslMirrorTests "cultmath_cubic_bezier", "cultmath_hash", "cultmath_value_noise", - "cultmath_simplex_noise", + "cultmath_snoise(float2", + "cultmath_snoise(float3", "cultmath_value_noise_bicubic", "cultmath_value_noise_texture", "cultmath_value_noise_texture_bicubic", diff --git a/packages/cultmath/tests/CultMath.Tests/HlslSemanticsTests.cs b/packages/cultmath/tests/CultMath.Tests/HlslSemanticsTests.cs new file mode 100644 index 00000000..7b845ea7 --- /dev/null +++ b/packages/cultmath/tests/CultMath.Tests/HlslSemanticsTests.cs @@ -0,0 +1,441 @@ +using System.Text.Json; +using Xunit; + +namespace CultMath.Tests; + +public sealed class HlslSemanticsTests +{ + [Fact] + public void ComponentsAndIndexersAreWritable() + { + var value = new float4(1.0f, 2.0f, 3.0f, 4.0f); + value.w = 9.0f; + value[0] = -1.0f; + + Assert.Equal(new float4(-1.0f, 2.0f, 3.0f, 9.0f), value); + + var cell = new int3(1, 2, 3); + cell[2] = 7; + Assert.Equal(new int3(1, 2, 7), cell); + + ref var component = ref value.y; + component = 20.0f; + Assert.Equal(20.0f, value.y); + } + + [Fact] + public void Float2SwizzlesReadAndWrite() + { + var value = new float2(1.0f, 2.0f); + Assert.Equal(new float2(2.0f, 1.0f), value.yx); + + value.yx = new float2(5.0f, 6.0f); + Assert.Equal(new float2(6.0f, 5.0f), value); + } + + [Fact] + public void Float3SwizzlesReadAndWrite() + { + var source = new float3(1.0f, 2.0f, 3.0f); + Assert.Equal(new float2(1.0f, 2.0f), source.xy); + Assert.Equal(new float2(1.0f, 3.0f), source.xz); + Assert.Equal(new float2(2.0f, 1.0f), source.yx); + Assert.Equal(new float2(2.0f, 3.0f), source.yz); + Assert.Equal(new float2(3.0f, 2.0f), source.zy); + Assert.Equal(new float3(1.0f, 2.0f, 3.0f), source.xyz); + Assert.Equal(new float3(1.0f, 3.0f, 2.0f), source.xzy); + Assert.Equal(new float3(2.0f, 3.0f, 1.0f), source.yzx); + Assert.Equal(new float3(3.0f, 2.0f, 1.0f), source.zyx); + + Assert.Equal(new float3(8.0f, 9.0f, 3.0f), Write(source, (ref float3 v) => v.xy = new float2(8.0f, 9.0f))); + Assert.Equal(new float3(8.0f, 2.0f, 9.0f), Write(source, (ref float3 v) => v.xz = new float2(8.0f, 9.0f))); + Assert.Equal(new float3(9.0f, 8.0f, 3.0f), Write(source, (ref float3 v) => v.yx = new float2(8.0f, 9.0f))); + Assert.Equal(new float3(1.0f, 8.0f, 9.0f), Write(source, (ref float3 v) => v.yz = new float2(8.0f, 9.0f))); + Assert.Equal(new float3(1.0f, 9.0f, 8.0f), Write(source, (ref float3 v) => v.zy = new float2(8.0f, 9.0f))); + Assert.Equal(new float3(7.0f, 8.0f, 9.0f), Write(source, (ref float3 v) => v.xyz = new float3(7.0f, 8.0f, 9.0f))); + Assert.Equal(new float3(7.0f, 9.0f, 8.0f), Write(source, (ref float3 v) => v.xzy = new float3(7.0f, 8.0f, 9.0f))); + Assert.Equal(new float3(9.0f, 7.0f, 8.0f), Write(source, (ref float3 v) => v.yzx = new float3(7.0f, 8.0f, 9.0f))); + Assert.Equal(new float3(9.0f, 8.0f, 7.0f), Write(source, (ref float3 v) => v.zyx = new float3(7.0f, 8.0f, 9.0f))); + } + + [Fact] + public void Float4SwizzlesReadAndWrite() + { + var source = new float4(1.0f, 2.0f, 3.0f, 4.0f); + Assert.Equal(new float2(1.0f, 2.0f), source.xy); + Assert.Equal(new float3(1.0f, 2.0f, 3.0f), source.xyz); + Assert.Equal(new float3(1.0f, 2.0f, 4.0f), source.xyw); + Assert.Equal(new float3(2.0f, 3.0f, 1.0f), source.yzx); + + var a = source; + a.xy = new float2(8.0f, 9.0f); + Assert.Equal(new float4(8.0f, 9.0f, 3.0f, 4.0f), a); + + var b = source; + b.xyz = new float3(7.0f, 8.0f, 9.0f); + Assert.Equal(new float4(7.0f, 8.0f, 9.0f, 4.0f), b); + + var c = source; + c.xyw = new float3(7.0f, 8.0f, 9.0f); + Assert.Equal(new float4(7.0f, 8.0f, 3.0f, 9.0f), c); + + var d = source; + d.yzx = new float3(7.0f, 8.0f, 9.0f); + Assert.Equal(new float4(9.0f, 7.0f, 8.0f, 4.0f), d); + } + + [Fact] + public void GeneratedSwizzlesCoverRepeatsColorNamesAndAllVectorFamilies() + { + var v = new float4(1.0f, 2.0f, 3.0f, 4.0f); + Assert.Equal(new float4(4.0f, 4.0f, 1.0f, 2.0f), v.wwxy); + Assert.Equal(new float3(3.0f, 2.0f, 1.0f), v.bgr); + Assert.Equal(4.0f, v.a); + Assert.Equal(new float4(2.0f, 2.0f, 2.0f, 2.0f), new float2(1.0f, 2.0f).yyyy); + + v.wx = new float2(9.0f, 8.0f); + Assert.Equal(new float4(8.0f, 2.0f, 3.0f, 9.0f), v); + v.r = 0.5f; + v.gb += new float2(1.0f, 1.0f); + Assert.Equal(new float4(0.5f, 3.0f, 4.0f, 9.0f), v); + + Assert.Equal(new int3(3, 1, 1), new int3(1, 2, 3).zxx); + Assert.Equal(new bool2(false, true), new bool4(true, false, false, true).zw); + Assert.Equal(new double2(2.0, 1.0), new double3(1.0, 2.0, 3.0).yx); + + var cell = new int4(1, 2, 3, 4); + cell.xyz = new int3(7, 8, 9); + Assert.Equal(new int4(7, 8, 9, 4), cell); + } + + [Fact] + public void MixedConstructorsMatchHlsl() + { + var xy = new float2(1.0f, 2.0f); + var zw = new float2(3.0f, 4.0f); + var expected = new float4(1.0f, 2.0f, 3.0f, 4.0f); + + Assert.Equal(expected, math.float4(xy, zw)); + Assert.Equal(expected, math.float4(1.0f, new float3(2.0f, 3.0f, 4.0f))); + Assert.Equal(expected, math.float4(new float3(1.0f, 2.0f, 3.0f), 4.0f)); + Assert.Equal(expected, math.float4(xy, 3.0f, 4.0f)); + Assert.Equal(expected, math.float4(1.0f, 2.0f, zw)); + Assert.Equal(expected, math.float4(1.0f, new float2(2.0f, 3.0f), 4.0f)); + Assert.Equal(expected, new float4(xy, zw)); + Assert.Equal(new float3(1.0f, 2.0f, 3.0f), math.float3(1.0f, zw - 1.0f)); + Assert.Equal(new int4(1, 2, 3, 4), math.int4(new int2(1, 2), new int2(3, 4))); + Assert.Equal(new bool3(true, false, true), math.bool3(true, new bool2(false, true))); + Assert.Equal(new double3(1.0, 2.0, 3.0), new double3(new double2(1.0, 2.0), 3.0)); + } + + [Fact] + public void MatrixRowElementsAreWritable() + { + var m = float3x3.identity; + m[1][2] = 5.0f; + m[2].x = 7.0f; + m._m00 = 2.0f; + + Assert.Equal(5.0f, m._m12); + Assert.Equal(7.0f, m._m20); + Assert.Equal(math.float3x3(2.0f, 0.0f, 0.0f, 0.0f, 1.0f, 5.0f, 7.0f, 0.0f, 1.0f), m); + Assert.Equal(float3x3.identity, math.float3x3(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f)); + + var r = float2x2.identity; + r[0][1] = 3.0f; + Assert.Equal(math.float2x2(1.0f, 3.0f, 0.0f, 1.0f), r); + Assert.Equal(new float2(0.0f, 1.0f), float2x2.identity[1]); + } + + [Fact] + public void IntrinsicEdgeCasesFollowHlsl() + { + Assert.True(math.any(new float3(0.0f, 0.0f, -2.0f))); + Assert.False(math.any(float4.zero)); + Assert.True(math.all(new int2(1, -1))); + Assert.False(math.all(new int3(1, 0, 1))); + Assert.True(math.any(new float2(float.NaN, 0.0f))); + + Assert.Equal(new int3(1, 2, 3), math.min(new int3(1, 5, 3), new int3(4, 2, 6))); + Assert.Equal(new int2(4, 5), math.max(new int2(1, 5), new int2(4, 2))); + Assert.Equal(new int4(1, 2, 0, 3), math.abs(new int4(-1, 2, 0, -3))); + Assert.Equal(7, math.abs(-7)); + + Assert.Equal(1.0f, math.step(1.0f, float.NaN)); + Assert.Equal(1.0f, math.step(float.NaN, 1.0f)); + Assert.Equal(1.0f, math.step(1.0f, 1.0f)); + + // DXIL FMin/FMax/Saturate: a NaN operand returns the other; max(a, b) is a >= b ? a : b. + Assert.Equal(1.0f, math.min(float.NaN, 1.0f)); + Assert.Equal(1.0f, math.min(1.0f, float.NaN)); + Assert.Equal(1.0f, math.max(float.NaN, 1.0f)); + Assert.Equal(1.0f, math.max(1.0f, float.NaN)); + Assert.True(float.IsNaN(math.max(float.NaN, float.NaN))); + Assert.Equal(2.0f, math.clamp(float.NaN, 2.0f, 3.0f)); + Assert.Equal(new float3(0.0f, 1.0f, 0.0f), math.saturate(new float3(float.NaN, 2.0f, -1.0f))); + Assert.Equal(new float2(-1.0f, 4.0f), math.max(new float2(float.NaN, 4.0f), new float2(-1.0f, float.NaN))); + static int Bits(float value) => BitConverter.SingleToInt32Bits(value); + Assert.Equal(Bits(-0.0f), Bits(math.max(-0.0f, 0.0f))); + Assert.Equal(Bits(0.0f), Bits(math.min(-0.0f, 0.0f))); + Assert.Equal(Bits(0.0f), Bits(math.saturate(-0.0f))); + Assert.Equal(1.0, math.min(double.NaN, 1.0)); + Assert.Equal(1.0, math.min(1.0, double.NaN)); + Assert.Equal(1.0, math.max(double.NaN, 1.0)); + Assert.Equal(1.0, math.max(1.0, double.NaN)); + Assert.True(double.IsNaN(math.max(double.NaN, double.NaN))); + static long DoubleBits(double value) => BitConverter.DoubleToInt64Bits(value); + Assert.Equal(DoubleBits(-0.0), DoubleBits(math.max(-0.0, 0.0))); + Assert.Equal(DoubleBits(0.0), DoubleBits(math.min(-0.0, 0.0))); + + Assert.Equal(0, math.sign(float.NaN)); + Assert.Equal(-1, math.sign(-0.5f)); + Assert.Equal(new int3(-1, 0, 1), math.sign(new float3(-2.0f, 0.0f, 4.0f))); + Assert.Equal(new int2(1, -1), math.sign(new int2(9, -9))); + } + + [Fact] + public void ComparisonOperatorsReturnComponentWiseBoolVectors() + { + var a = new float3(1.0f, 2.0f, 3.0f); + var b = new float3(3.0f, 2.0f, 1.0f); + + Assert.Equal(new bool3(true, false, false), a < b); + Assert.Equal(new bool3(false, false, true), a > b); + Assert.Equal(new bool3(true, true, false), a <= b); + Assert.Equal(new bool3(false, true, true), a >= b); + Assert.Equal(new bool3(false, true, false), a == b); + Assert.Equal(new bool3(true, false, true), a != b); + + Assert.Equal(new bool3(true, false, false), a < 2.0f); + Assert.Equal(new bool3(false, false, true), 2.0f < a); + Assert.Equal(new bool2(true, false), new float2(0.0f, 1.0f) == 0.0f); + Assert.Equal(new bool4(false, true, true, true), new float4(0.0f, 1.0f, 2.0f, 3.0f) >= 1.0f); + Assert.Equal(new bool2(false, true), new int2(1, 2) > new int2(1, 1)); + Assert.Equal(new bool3(true, false, true), new int3(1, 2, 3) != new int3(0, 2, 4)); + Assert.Equal(new bool4(true, true, false, false), new int4(1, 2, 3, 4) <= new int4(1, 2, 2, 2)); + Assert.Equal(new bool2(true, false), new double2(0.5, 1.0) < new double2(1.0, 1.0)); + Assert.Equal(new bool3(false, false, true), new double3(1.0, 1.0, 2.0) > new double3(1.0, 1.0, 1.0)); + } + + [Fact] + public void ScalarOperatorsApplyPerComponentOnBothSides() + { + var value = new float3(1.0f, 2.0f, 4.0f); + + Assert.Equal(new float3(3.0f, 2.0f, 0.0f), 4.0f - value); + Assert.Equal(new float3(0.0f, 1.0f, 3.0f), value - 1.0f); + Assert.Equal(new float3(2.0f, 3.0f, 5.0f), 1.0f + value); + Assert.Equal(new float3(4.0f, 2.0f, 1.0f), 4.0f / value); + Assert.Equal(new float3(0.5f, 1.0f, 2.0f), value / 2.0f); + Assert.Equal(new float3(2.0f, 4.0f, 8.0f), 2.0f * value); + Assert.Equal(new float3(1.0f, 0.0f, 0.0f), value % 2.0f); + Assert.Equal(new float2(1.5f, 2.5f), new float2(1.0f, 2.0f) + 0.5f); + Assert.Equal(new float4(0.0f, 1.0f, 2.0f, 3.0f), new float4(1.0f, 2.0f, 3.0f, 4.0f) - 1.0f); + } + + [Fact] + public void EqualsAndHashCodeStayValueBasedForDictionaryKeys() + { + var map = new Dictionary { [new float3(1.0f, 2.0f, 3.0f)] = "hit" }; + Assert.Equal("hit", map[new float3(1.0f, 2.0f, 3.0f)]); + Assert.False(map.ContainsKey(new float3(1.0f, 2.0f, 3.5f))); + + var cells = new HashSet { new int2(3, 4) }; + Assert.Contains(new int2(3, 4), cells); + Assert.Contains(new int3(1, 2, 3), new HashSet { new int3(1, 2, 3) }); + + var nan = new float2(float.NaN, 0.0f); + var sameNan = nan; + Assert.True(nan.Equals(sameNan)); + Assert.False((nan == sameNan).x); + + Assert.True(new quaternion(0.0f, 0.0f, 0.0f, 1.0f) == quaternion.identity); + Assert.Equal(float2x2.identity, math.float2x2(1.0f, 0.0f, 0.0f, 1.0f)); + Assert.Equal("float3(1, 2.5, -3)", new float3(1.0f, 2.5f, -3.0f).ToString()); + } + + [Fact] + public void AnyAllAndSelectFollowHlsl() + { + Assert.True(math.any(new bool2(false, true))); + Assert.False(math.any(bool3.@false)); + Assert.True(math.all(bool4.@true)); + Assert.False(math.all(new bool3(true, false, true))); + + var mask = new bool3(true, false, true); + Assert.Equal(new float3(1.0f, 20.0f, 3.0f), math.select(mask, new float3(1.0f, 2.0f, 3.0f), new float3(10.0f, 20.0f, 30.0f))); + Assert.Equal(7.0f, math.select(true, 7.0f, 8.0f)); + Assert.Equal(new float2(0.0f, 5.0f), math.select(new bool2(false, true), math.float2(5.0f), math.float2(0.0f))); + Assert.Equal(new float4(1.0f, 0.0f, 1.0f, 0.0f), math.select(new bool4(true, false, true, false), 1.0f, 0.0f)); + + Assert.Equal(new bool2(false, true), !new bool2(true, false)); + Assert.Equal(new bool3(true, false, false), new bool3(true, true, false) & new bool3(true, false, true)); + Assert.Equal(new bool3(true, true, true), new bool3(true, true, false) | new bool3(true, false, true)); + } + + [Fact] + public void AddedIntrinsicsAreComponentWise() + { + Assert.Equal(MathF.Log(3.0f), math.log(3.0f)); + Assert.Equal(new float3(0.0f, MathF.Log(2.0f), MathF.Log(4.0f)), math.log(new float3(1.0f, 2.0f, 4.0f))); + Assert.Equal(new float2(8.0f, 9.0f), math.pow(new float2(2.0f, 3.0f), new float2(3.0f, 2.0f))); + Assert.Equal(new float4(1.0f, 2.0f, 3.0f, 4.0f), math.sqrt(new float4(1.0f, 4.0f, 9.0f, 16.0f))); + var sines = math.sin(new float3(0.0f, math.HALF_PI, math.PI)); + Assert.Equal(0.0f, sines.x, precision: 5); + Assert.Equal(1.0f, sines.y, precision: 5); + Assert.Equal(0.0f, sines.z, precision: 5); + var cosines = math.cos(new float2(0.0f, math.PI)); + Assert.Equal(1.0f, cosines.x, precision: 5); + Assert.Equal(-1.0f, cosines.y, precision: 5); + var arccosines = math.acos(new float3(1.0f, 0.0f, -1.0f)); + Assert.Equal(0.0f, arccosines.x, precision: 5); + Assert.Equal(math.HALF_PI, arccosines.y, precision: 5); + Assert.Equal(math.PI, arccosines.z, precision: 5); + Assert.Equal(math.atan2(1.0f, -1.0f), math.atan2(new float2(1.0f, 0.0f), new float2(-1.0f, 1.0f)).x); + Assert.Equal(0.0f, math.atan2(new float4(1.0f, 0.0f, 1.0f, 0.0f), new float4(1.0f, 1.0f, 1.0f, 1.0f)).y); + Assert.Equal(2.5, math.min(2.5, 3.5)); + Assert.Equal(3.5, math.max(2.5, 3.5)); + } + + [Fact] + public void IntVectorsPromoteToFloatVectors() + { + float3 promoted3 = new int3(1, 2, 3); + float4 promoted4 = new int4(1, 2, 3, 4); + + Assert.Equal(new float3(1.0f, 2.0f, 3.0f), promoted3); + Assert.Equal(new float4(1.0f, 2.0f, 3.0f, 4.0f), promoted4); + Assert.Equal(new int3(2, 4, 6), new int3(1, 2, 3) * 2); + Assert.Equal(new int4(1, 0, 1, 0), new int4(3, 4, 5, 6) % 2); + Assert.Equal(new int3(1, 2, 3), math.int3(1, 2, 3)); + Assert.Equal(new int4(5, 5, 5, 5), math.int4(5)); + } + + [Fact] + public void MatrixMulFollowsHlslRowMajorConvention() + { + var m = math.float2x2(1.0f, 2.0f, 3.0f, 4.0f); + var v = new float2(5.0f, 6.0f); + + Assert.Equal(new float2(1.0f, 2.0f), m[0]); + Assert.Equal(new float2(17.0f, 39.0f), math.mul(m, v)); + Assert.Equal(new float2(23.0f, 34.0f), math.mul(v, m)); + Assert.Equal(math.float2x2(7.0f, 10.0f, 15.0f, 22.0f), math.mul(m, m)); + + var m3 = math.float3x3(new float3(1.0f, 2.0f, 3.0f), new float3(4.0f, 5.0f, 6.0f), new float3(7.0f, 8.0f, 9.0f)); + var v3 = new float3(1.0f, 0.0f, -1.0f); + Assert.Equal(new float3(-2.0f, -2.0f, -2.0f), math.mul(m3, v3)); + Assert.Equal(new float3(-6.0f, -6.0f, -6.0f), math.mul(v3, m3)); + Assert.Equal(math.float3x3(30.0f, 36.0f, 42.0f, 66.0f, 81.0f, 96.0f, 102.0f, 126.0f, 150.0f), math.mul(m3, m3)); + Assert.Equal(m3, math.mul(m3, float3x3.identity)); + + m3[1] = new float3(0.0f, 1.0f, 0.0f); + Assert.Equal(1.0f, m3._m11); + Assert.Equal(12.0f, math.mul(3.0f, 4.0f)); + } + + [Fact] + public void Float2x2RotateMatchesRotateAndUnityMathematics() + { + var rotation = float2x2.Rotate(0.5f); + var column = math.mul(rotation, new float2(1.0f, 0.0f)); + var row = math.mul(new float2(1.0f, 0.0f), rotation); + var reference = math.rotate(new float2(1.0f, 0.0f), 0.5f); + + Assert.Equal(reference.x, column.x, precision: 6); + Assert.Equal(reference.y, column.y, precision: 6); + + // Unity.Mathematics 1.x reference values: mul(float2(1, 0), float2x2.Rotate(0.5f)). + Assert.Equal(0.87758255f, row.x, precision: 6); + Assert.Equal(-0.47942555f, row.y, precision: 6); + } + + [Fact] + public void Float3x3EulerMatchesUnityMathematicsReference() + { + // Unity.Mathematics 1.x reference values captured from float3x3.Euler. + AssertRows( + float3x3.Euler(new float3(0.4f, -0.7f, 1.3f), math.RotationOrder.YXZ), + 0.44632244f, -0.8874959f, 0.114662126f, + 0.6698625f, 0.24638279f, -0.700414f, + 0.59336376f, 0.38941833f, 0.70446634f); + AssertRows( + float3x3.Euler(new float3(0.4f, -0.7f, 1.3f)), + -0.037133574f, -0.8040775f, -0.59336376f, + 0.8874959f, 0.24638279f, -0.38941833f, + 0.45931715f, -0.5410684f, 0.70446634f); + + // Aetheria ActionGameManager view direction: mul(float3(0, 0, 1), Euler(float3(yawPitch.yx, 0), YXZ)). + var yawPitch = new float2(0.3f, -1.1f); + var view = math.mul(new float3(0.0f, 0.0f, 1.0f), float3x3.Euler(new float3(yawPitch.yx, 0.0f), math.RotationOrder.YXZ)); + Assert.Equal(-0.13404681f, view.x, precision: 5); + Assert.Equal(-0.8912074f, view.y, precision: 5); + Assert.Equal(0.4333369f, view.z, precision: 5); + + var composed = math.mul(float3x3.RotateZ(0.2f), math.mul(float3x3.RotateY(0.3f), float3x3.RotateX(0.1f))); + AssertRows(float3x3.Euler(0.1f, 0.3f, 0.2f, math.RotationOrder.XYZ), + composed._m00, composed._m01, composed._m02, + composed._m10, composed._m11, composed._m12, + composed._m20, composed._m21, composed._m22); + } + + [Theory] + [InlineData(0.0f, 0.0f, 0.0f, -0.41219875f)] + [InlineData(0.25f, -0.5f, 1.75f, 0.5250269f)] + [InlineData(12.25f, -4.5f, 3.125f, 0.008141451f)] + [InlineData(-7.3f, 2.9f, 101.4f, 0.578015f)] + public void Snoise3MatchesUnityMathematicsReference(float x, float y, float z, float expected) + { + Assert.Equal(expected, math.snoise(new float3(x, y, z)), precision: 5); + } + + [Fact] + public void RandomFloat3OverloadsShareOneDrawOrder() + { + var a = new Random(99); + var b = new Random(99); + var c = new Random(99); + + var unit = a.NextFloat3(); + Assert.Equal(new float3(b.NextFloat(), b.NextFloat(), b.NextFloat()), unit); + Assert.Equal(unit * new float3(2.0f, 4.0f, 8.0f), c.NextFloat3(new float3(2.0f, 4.0f, 8.0f))); + + var d = new Random(99); + Assert.Equal(math.float3(-1.0f) + unit * 2.0f, d.NextFloat3(math.float3(-1.0f), math.float3(1.0f))); + } + + [Fact] + public void CoreAssemblyStaysEngineFree() + { + var references = typeof(math).Assembly.GetReferencedAssemblies(); + Assert.DoesNotContain(references, reference => reference.Name!.StartsWith("UnityEngine", StringComparison.Ordinal)); + + var root = new DirectoryInfo(AppContext.BaseDirectory); + while (root is not null && !File.Exists(Path.Combine(root.FullName, "packages", "cultmath", "src", "CultMath", "CultMath.asmdef"))) + { + root = root.Parent; + } + + Assert.NotNull(root); + var asmdefPath = Path.Combine(root!.FullName, "packages", "cultmath", "src", "CultMath", "CultMath.asmdef"); + using var asmdef = JsonDocument.Parse(File.ReadAllText(asmdefPath)); + Assert.True(asmdef.RootElement.GetProperty("noEngineReferences").GetBoolean()); + } + + private static float3 Write(float3 value, WriteAction action) + { + action(ref value); + return value; + } + + private delegate void WriteAction(ref float3 value); + + private static void AssertRows(float3x3 m, params float[] expected) + { + var actual = new[] { m._m00, m._m01, m._m02, m._m10, m._m11, m._m12, m._m20, m._m21, m._m22 }; + for (var index = 0; index < expected.Length; index++) + { + Assert.Equal(expected[index], actual[index], precision: 5); + } + } +} diff --git a/packages/cultmath/tests/CultMath.Tests/HlslSemanticsTests.cs.meta b/packages/cultmath/tests/CultMath.Tests/HlslSemanticsTests.cs.meta new file mode 100644 index 00000000..8158a66d --- /dev/null +++ b/packages/cultmath/tests/CultMath.Tests/HlslSemanticsTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7164d00a023e43578a31897347d8f106 diff --git a/packages/cultmath/tests/CultMath.Tests/HlslSourceCompatibilityTests.cs b/packages/cultmath/tests/CultMath.Tests/HlslSourceCompatibilityTests.cs new file mode 100644 index 00000000..ca4584c7 --- /dev/null +++ b/packages/cultmath/tests/CultMath.Tests/HlslSourceCompatibilityTests.cs @@ -0,0 +1,203 @@ +using System.Reflection; +using System.Text.RegularExpressions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Xunit; +using static CultMath.math; + +namespace CultMath.Tests; + +/// +/// The real shader mirror, shaders/CultMath.hlsl, compiles as C# against CultMath under +/// using static CultMath.math; after exactly the source transformations listed in +/// docs/design.md (HLSL Target: Source Transformations). is +/// that list in code; if a body needs anything else, this test fails. +/// +public sealed class HlslSourceCompatibilityTests +{ + [Fact] + public void ShaderMirrorBodiesCompileAsCSharp() + { + var (assembly, errors) = CompileShaderMirror(); + Assert.True(assembly is not null, "HLSL mirror did not compile as C#:" + Environment.NewLine + string.Join(Environment.NewLine, errors)); + + var shader = assembly!.GetType("CultMathHlsl.HlslShader")!; + Assert.NotNull(ShaderFunction(shader, "cultmath_snoise", typeof(float3))); + Assert.NotNull(ShaderFunction(shader, "cultmath_snoise", typeof(float2))); + Assert.NotNull(ShaderFunction(shader, "cultmath_value_noise_bicubic", typeof(float2))); + } + + // HLSL functions carry no access modifier, so they compile as private instance methods. + private static MethodInfo? ShaderFunction(Type shader, string name, params Type[] parameters) => + shader.GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic, null, parameters, null); + + // Mirror internals with no public C# math counterpart. Any other cultmath_* function must match one. + private static readonly string[] MirrorOnly = { "cultmath_snoise_mod289", "cultmath_snoise_permute" }; + + private static readonly float[] Specials = { 0.0f, -0.0f, 1.0f, -1.0f, 0.5f, -2.75f, 3.0f, 1.0e-7f, 1.0e4f, -1.0e4f, 1.0e7f, float.NaN }; + + /// + /// Every mirror function with a C# math counterpart returns the same bits (any NaN equals any NaN; + /// -0 differs from 0) over random inputs, every special value in every argument (ties, zeros, + /// negatives, NaN, large values, integer lattice points), special pairs, and vectors whose first two + /// components tie (x0.x == x0.y in snoise). This proves the text of CultMath.hlsl computes the same + /// float32 results as C# math on the CPU. It does not prove GPU agreement: driver sin precision alone + /// makes cultmath_hash and value noise differ bit for bit on hardware. The mirror's intrinsics are C# + /// math itself (it compiles under using static CultMath.math), so this proves the composition in the + /// text, not the intrinsic rules; HlslSemanticsTests alone pins those. + /// + [Fact] + public void EveryMirrorFunctionMatchesCSharpMath() + { + var (assembly, errors) = CompileShaderMirror(); + Assert.True(assembly is not null, string.Join(Environment.NewLine, errors)); + var shaderType = assembly!.GetType("CultMathHlsl.HlslShader")!; + var shader = Activator.CreateInstance(shaderType); + + var random = new System.Random(0x5EED); + float Next() => random.NextSingle() * 200.0f - 100.0f; + var cases = new List>(); + foreach (var s in Specials) cases.Add(_ => s); + for (var k = 0; k < Specials.Length; k++) { var o = k; cases.Add(i => Specials[(i + o + 1) % Specials.Length]); } + for (var k = 0; k < 64; k++) { var values = Enumerable.Range(0, 32).Select(_ => Next()).ToArray(); cases.Add(i => values[i]); } + for (var k = 0; k < 16; k++) { var values = Enumerable.Range(0, 32).Select(_ => Next()).ToArray(); cases.Add(i => values[i & ~1]); } + + var mismatches = new List(); + var compared = new HashSet(); + foreach (var mirror in shaderType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).Where(m => m.Name.StartsWith("cultmath_"))) + { + var types = mirror.GetParameters().Select(p => p.ParameterType).ToArray(); + var counterpart = typeof(math).GetMethod(mirror.Name["cultmath_".Length..], BindingFlags.Public | BindingFlags.Static, null, types, null); + if (counterpart is null) + { + Assert.Contains(mirror.Name, MirrorOnly); + continue; + } + + compared.Add(mirror.Name); + foreach (var input in cases) + { + var slot = 0; + var args = types.Select(t => t == typeof(float) ? (object)input(slot++) + : Activator.CreateInstance(t, Enumerable.Range(0, Components(t)).Select(_ => (object)input(slot++)).ToArray())!).ToArray(); + var expected = Floats(counterpart.Invoke(null, args)!); + var actual = Floats(mirror.Invoke(shader, args)!); + if (expected.Length != actual.Length || expected.Zip(actual).Any(p => !(float.IsNaN(p.First) && float.IsNaN(p.Second)) + && BitConverter.SingleToInt32Bits(p.First) != BitConverter.SingleToInt32Bits(p.Second))) + { + mismatches.Add($"{mirror.Name}({string.Join(", ", args.Select(a => string.Join(" ", Floats(a))))}): C# {string.Join(" ", expected)}, mirror {string.Join(" ", actual)}"); + } + } + } + + Assert.True(mismatches.Count == 0, $"{mismatches.Count} mismatches:{Environment.NewLine}{string.Join(Environment.NewLine, mismatches.Take(12))}"); + Assert.Equal(26, compared.Count); + } + + private static int Components(Type t) => t.GetFields().Count(f => f.FieldType == typeof(float) && !f.IsStatic); + + private static float[] Floats(object value) => value is float f ? new[] { f } + : value.GetType().GetFields().Where(field => field.FieldType == typeof(float) && !field.IsStatic).Select(field => (float)field.GetValue(value)!).ToArray(); + + /// The documented HLSL-to-C# transformations, and nothing else. + internal static string TransformHlslToCSharp(string hlsl) + { + // 1. Include guards (#ifndef/#define/#endif) are dropped; C# has no textual include. + var source = Regex.Replace(hlsl, @"(?m)^\s*#(ifndef|define|endif)\b.*$", string.Empty); + + // 2. Functions taking Texture2D/SamplerState resources are dropped; GPU resources have + // no CultMath analog. + source = Regex.Replace(source, @"(?ms)^\w+ \w+\([^)]*\b(Texture2D|SamplerState)\b[^)]*\)\s*\{.*?^\}[ \t]*\r?$", string.Empty); + + // 3. File-scope `static const` becomes `const` (C# consts are implicitly static). + source = Regex.Replace(source, @"(?m)^static const ", "const "); + + // 4. Floating literals gain the `f` suffix; an unsuffixed C# literal is a double. + source = Regex.Replace(source, @"(? Errors) CompileShaderMirror() + { + var hlsl = File.ReadAllText(Path.Combine(FindCultMathRoot(), "shaders", "CultMath.hlsl")); + var tree = CSharpSyntaxTree.ParseText(TransformHlslToCSharp(hlsl)); + var references = ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")!) + .Split(Path.PathSeparator) + .Append(typeof(math).Assembly.Location) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Select(path => MetadataReference.CreateFromFile(path)); + var compilation = CSharpCompilation.Create( + "CultMathHlslMirror", + new[] { tree }, + references, + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + + using var stream = new MemoryStream(); + var result = compilation.Emit(stream); + var errors = result.Diagnostics + .Where(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error) + .Select(diagnostic => diagnostic.ToString()) + .ToList(); + return (result.Success ? Assembly.Load(stream.ToArray()) : null, errors); + } + + private static string FindCultMathRoot() + { + var directory = new DirectoryInfo(AppContext.BaseDirectory); + while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "packages", "cultmath", "shaders", "CultMath.hlsl"))) + { + directory = directory.Parent; + } + + return Path.Combine(directory?.FullName ?? throw new InvalidOperationException("CultMath package root not found."), "packages", "cultmath"); + } + + // ---- Hand-written HLSL-shaped host code: swizzles, mixed constructors, matrix rows. ---- + static float3 shade(float3 position, float3 normal, float2 wind, float time) + { + float3 p = position; + p.xz = p.xz + wind * time; + p.y = saturate(p.y); + + float3x3 basis = float3x3(float3(1, 0, 0), float3(0, 1, 0), float3(0, 0, 1)); + basis[1][1] = 1.0f; + float3 n = normalize(mul(basis, normal)); + float ndotl = saturate(dot(n, normalize(float3(0.3f, 1, 0.2f)))); + float4 color = float4(lerp(float3(0.1f, 0.1f, 0.2f), float3(1, 0.9f, 0.7f), ndotl), 1); + + if (any(p < 0.0f)) + color.rgb *= 0.5f; + if (all(abs(n) <= 1.0f)) + color.bgr = color.bgr * float3(1, 1, 1); + + color.rgb = select(p > 10.0f, float3(0), color.rgb); + float2 uv = frac(p.xz * 0.25f); + color.rg = lerp(color.rg, uv, 0.1f); + return float4(color.xy, color.zw).xyz; + } + + static float2 steer(float2 direction, float torque) + { + float2x2 turn = float2x2(cos(torque), -sin(torque), sin(torque), cos(torque)); + float2 turned = mul(direction, turn); + return normalize(turned) * pow(length(turned), 1.0f); + } + + [Fact] + public void HandWrittenShaderStyleMathEvaluates() + { + var color = shade(float3(1.0f, 0.5f, 2.0f), float3(0.0f, 1.0f, 0.0f), float2(1.0f, 0.0f), 0.0f); + Assert.True(color.x > 0.1f && color.x <= 1.0f); + Assert.True(float.IsFinite(color.y) && float.IsFinite(color.z)); + + var far = shade(float3(20.0f, 20.0f, 20.0f), float3(0.0f, 1.0f, 0.0f), float2(0.0f), 0.0f); + Assert.Equal(0.0f, far.z); + + var steered = steer(float2(1.0f, 0.0f), 0.5f); + var viaRotate = mul(float2(1.0f, 0.0f), CultMath.float2x2.Rotate(0.5f)); + Assert.Equal(viaRotate.x, steered.x, precision: 5); + Assert.Equal(viaRotate.y, steered.y, precision: 5); + } +} diff --git a/packages/cultmath/tests/CultMath.Tests/HlslSourceCompatibilityTests.cs.meta b/packages/cultmath/tests/CultMath.Tests/HlslSourceCompatibilityTests.cs.meta new file mode 100644 index 00000000..0d673e1e --- /dev/null +++ b/packages/cultmath/tests/CultMath.Tests/HlslSourceCompatibilityTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 942fbcb9c28a4e559e5baf435dc24cf1 diff --git a/packages/cultmath/tests/CultMath.Tests/MathTests.cs b/packages/cultmath/tests/CultMath.Tests/MathTests.cs index 652ba23a..de1bf996 100644 --- a/packages/cultmath/tests/CultMath.Tests/MathTests.cs +++ b/packages/cultmath/tests/CultMath.Tests/MathTests.cs @@ -33,7 +33,7 @@ public sealed class MathTests } [Fact] - public void VectorsExposeCommonReadOnlySwizzles() + public void VectorsExposeCommonSwizzles() { Assert.Equal(new float3(2.0f, 3.0f, 1.0f), new float3(1.0f, 2.0f, 3.0f).yzx); Assert.Equal(new float3(3.0f, 2.0f, 1.0f), new float3(1.0f, 2.0f, 3.0f).zyx); @@ -170,10 +170,10 @@ public sealed class MathTests [Fact] public void SignIsComponentWiseForSimulationVectors() { - Assert.Equal(-1.0f, math.sign(-12.0f)); - Assert.Equal(0.0f, math.sign(0.0f)); - Assert.Equal(1.0f, math.sign(12.0f)); - Assert.Equal(new float3(-1.0f, 0.0f, 1.0f), math.sign(new float3(-2.0f, 0.0f, 4.0f))); + Assert.Equal(-1, math.sign(-12.0f)); + Assert.Equal(0, math.sign(0.0f)); + Assert.Equal(1, math.sign(12.0f)); + Assert.Equal(new int3(-1, 0, 1), math.sign(new float3(-2.0f, 0.0f, 4.0f))); } [Fact] @@ -277,7 +277,7 @@ public sealed class MathTests [InlineData(536.5106f, 536.5106f, -0.6901103f)] public void SimplexNoiseMatchesUnityMathematics(float x, float y, float expected) { - Assert.Equal(expected, math.simplex_noise(new float2(x, y)), precision: 6); + Assert.Equal(expected, math.snoise(new float2(x, y)), precision: 6); } [Fact] diff --git a/packages/cultmath/tools/generate-swizzles.ps1 b/packages/cultmath/tools/generate-swizzles.ps1 new file mode 100644 index 00000000..9030f2be --- /dev/null +++ b/packages/cultmath/tools/generate-swizzles.ps1 @@ -0,0 +1,132 @@ +param( + [string]$OutputPath = "" +) + +# Writes src\CultMath\Swizzles.g.cs: HLSL swizzles and mixed constructors for +# CultMath's vector types. The output is checked in; rerun after changing the +# family table below and commit both files together. + +$ErrorActionPreference = "Stop" + +if (-not $OutputPath) { + $OutputPath = Join-Path $PSScriptRoot "..\src\CultMath\Swizzles.g.cs" +} + +# Functions: emit math.(...) constructor functions. double has none because +# a math.double3 method group would hide the double3 type name under +# `using static CultMath.math;` and HLSL shader code does not construct doubles. +$families = @( + @{ Scalar = "float"; Sizes = @(2, 3, 4); Functions = $true }, + @{ Scalar = "int"; Sizes = @(2, 3, 4); Functions = $true }, + @{ Scalar = "bool"; Sizes = @(2, 3, 4); Functions = $true }, + @{ Scalar = "double"; Sizes = @(2, 3); Functions = $false } +) +$componentFields = @("x", "y", "z", "w") +$namings = @(@("x", "y", "z", "w"), @("r", "g", "b", "a")) + +function Get-Compositions([int]$total) { + # Ordered part sizes summing to $total, excluding the all-scalar form that the + # hand-written base constructor already owns. + $results = [System.Collections.Generic.List[object]]::new() + function Expand([int]$remaining, [int[]]$prefix) { + if ($remaining -eq 0) { + if (@($prefix | Where-Object { $_ -ne 1 }).Count -gt 0) { + $results.Add($prefix) + } + return + } + for ($part = 1; $part -le $remaining; $part++) { + Expand ($remaining - $part) ($prefix + $part) + } + } + Expand $total @() + return , $results +} + +$builder = [System.Text.StringBuilder]::new() +function Line([string]$text = "") { [void]$builder.Append($text).Append("`n") } + +Line "// " +Line "// Generated by tools/generate-swizzles.ps1. Do not edit by hand." +Line "// " +Line "" +Line "namespace CultMath;" + +$functionLines = [System.Collections.Generic.List[string]]::new() + +foreach ($family in $families) { + $scalar = $family.Scalar + $maxSize = ($family.Sizes | Measure-Object -Maximum).Maximum + foreach ($size in $family.Sizes) { + $type = "$scalar$size" + Line "" + Line "public partial struct $type" + Line "{" + + foreach ($composition in (Get-Compositions $size)) { + $parameters = @() + $arguments = @() + $offset = 0 + foreach ($part in $composition) { + $name = -join $componentFields[$offset..($offset + $part - 1)] + if ($part -eq 1) { + $parameters += "$scalar $name" + $arguments += $name + } else { + $parameters += "$scalar$part $name" + for ($i = 0; $i -lt $part; $i++) { $arguments += "$name.$($componentFields[$i])" } + } + $offset += $part + } + $parameterList = $parameters -join ", " + Line " public $type($parameterList) : this($($arguments -join ', ')) { }" + if ($family.Functions) { + $names = @($parameters | ForEach-Object { ($_ -split " ")[1] }) + $functionLines.Add(" public static $type $type($parameterList) => new($($names -join ', '));") + } + } + + # Single-component color aliases; x/y/z/w are the fields themselves. + $letters = $namings[1] + for ($i = 0; $i -lt $size; $i++) { + Line " public $scalar $($letters[$i]) { readonly get => $($componentFields[$i]); set => $($componentFields[$i]) = value; }" + } + + foreach ($letters in $namings) { + for ($length = 2; $length -le $maxSize; $length++) { + $count = [math]::Pow($size, $length) + for ($index = 0; $index -lt $count; $index++) { + [int[]]$digits = @() + [int]$remainder = $index + for ($d = 0; $d -lt $length; $d++) { + $digits = @([int]($remainder % $size)) + $digits + $remainder = [int][math]::Floor($remainder / $size) + } + $name = -join ($digits | ForEach-Object { $letters[$_] }) + $getter = ($digits | ForEach-Object { $componentFields[$_] }) -join ", " + $resultType = "$scalar$length" + $unique = @($digits | Select-Object -Unique).Count -eq $length + if ($unique) { + $assignments = for ($d = 0; $d -lt $length; $d++) { "$($componentFields[$digits[$d]]) = value.$($componentFields[$d]);" } + Line " public $resultType $name { readonly get => new($getter); set { $($assignments -join ' ') } }" + } else { + Line " public readonly $resultType $name => new($getter);" + } + } + } + } + + Line "}" + } +} + +Line "" +Line "public static partial class math" +Line "{" +foreach ($functionLine in $functionLines) { Line $functionLine } +Line "}" + +$fullPath = [System.IO.Path]::GetFullPath($OutputPath) +$content = $builder.ToString() -replace "`n", "`r`n" +[System.IO.File]::WriteAllText($fullPath, $content, [System.Text.UTF8Encoding]::new($false)) +Write-Host "Wrote $fullPath ($((Get-Item -LiteralPath $fullPath).Length) bytes)" diff --git a/packages/cultmath/tools/generate-swizzles.ps1.meta b/packages/cultmath/tools/generate-swizzles.ps1.meta new file mode 100644 index 00000000..74842405 --- /dev/null +++ b/packages/cultmath/tools/generate-swizzles.ps1.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d7a796fd2d87489c9f055fd6e73513b5 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/cultmath/tools/get-dxc.ps1 b/packages/cultmath/tools/get-dxc.ps1 index 2f3363f3..d9b12593 100644 --- a/packages/cultmath/tools/get-dxc.ps1 +++ b/packages/cultmath/tools/get-dxc.ps1 @@ -8,26 +8,27 @@ $repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..") $installPath = Join-Path $repoRoot $InstallRoot New-Item -ItemType Directory -Force -Path $installPath | Out-Null -$release = Invoke-RestMethod ` - -Uri "https://api.github.com/repos/microsoft/DirectXShaderCompiler/releases/latest" ` - -Headers @{ "User-Agent" = "CultMath-DXC-Bootstrap" } +# Pinned: parity means this compiler's lowering (dxcompiler 1.9.0.5402). The hash is the SHA256 +# of dxc_2026_07_29.zip downloaded from Microsoft's GitHub release v1.9.2607, and matches the +# digest GitHub publishes for that asset. +$zipName = "dxc_2026_07_29.zip" +$zipUrl = "https://github.com/microsoft/DirectXShaderCompiler/releases/download/v1.9.2607/$zipName" +$zipSha256 = "A1DFB116BA3EEAE6A1582291B53A8E7BF65AD760676BD3194685C8F7367CD241" -$asset = $release.assets | - Where-Object { $_.name -like "dxc_*.zip" } | - Select-Object -First 1 - -if (-not $asset) { - throw "No Windows dxc_*.zip asset found on the latest DirectXShaderCompiler release." -} - -$zipPath = Join-Path $installPath $asset.name +$zipPath = Join-Path $installPath $zipName Remove-Item -LiteralPath $zipPath -Force -ErrorAction SilentlyContinue -& curl.exe -L --retry 5 --retry-delay 2 --fail -o $zipPath $asset.browser_download_url +& curl.exe -L --retry 5 --retry-delay 2 --fail -o $zipPath $zipUrl if ($LASTEXITCODE -ne 0) { throw "curl.exe failed with exit code $LASTEXITCODE." } +$actualSha256 = (Get-FileHash -LiteralPath $zipPath -Algorithm SHA256).Hash +if ($actualSha256 -ne $zipSha256) { + Remove-Item -LiteralPath $zipPath -Force + throw "$zipName SHA256 is $actualSha256, expected $zipSha256." +} + $currentPath = Join-Path $installPath "current" Remove-Item -LiteralPath $currentPath -Recurse -Force -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $currentPath | Out-Null diff --git a/packages/cultmath/unity/org.gamecult.cultmath/README.md b/packages/cultmath/unity/org.gamecult.cultmath/README.md index 7d9c34bf..20e1840c 100644 --- a/packages/cultmath/unity/org.gamecult.cultmath/README.md +++ b/packages/cultmath/unity/org.gamecult.cultmath/README.md @@ -1,8 +1,16 @@ # CultMath for Unity This is CultMath's Unity 2021.3-compatible Git-UPM surface. It contains the -precompiled `CultMath.dll`, portable symbols, and the numeric -`Shaders/CultMath.hlsl` mirror. +precompiled `CultMath.dll`, portable symbols, the numeric +`Shaders/CultMath.hlsl` mirror, and the `CultMath.UnityBridge` assembly. + +`CultMath.UnityBridge` is the only C# source Unity compiles here. It holds the +UnityEngine conversions the engine-free core cannot declare: `ToCultMath()` on +`Vector2/3/4`, `Vector2Int/3Int`, `Quaternion`, and `Color`; `ToUnity()` on +`float2/3/4`, `int2/3`, and `quaternion`; `ToColor()` on `float4`. C# only +admits conversion operators on the source or target type, so these are +extension methods rather than implicit casts. Import `CultMath.UnityBridge` to +use them. Unity does not compile CultMath's repository source tree. That source uses the current C# language and remains owned by the normal .NET project. The package diff --git a/packages/cultmath/unity/org.gamecult.cultmath/Runtime/Plugins/CultMath.dll b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/Plugins/CultMath.dll index 379dad44..bf712012 100644 Binary files a/packages/cultmath/unity/org.gamecult.cultmath/Runtime/Plugins/CultMath.dll and b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/Plugins/CultMath.dll differ diff --git a/packages/cultmath/unity/org.gamecult.cultmath/Runtime/Plugins/CultMath.pdb b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/Plugins/CultMath.pdb index 223b4de8..1eaadd23 100644 Binary files a/packages/cultmath/unity/org.gamecult.cultmath/Runtime/Plugins/CultMath.pdb and b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/Plugins/CultMath.pdb differ diff --git a/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge.meta b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge.meta new file mode 100644 index 00000000..d34f2ef0 --- /dev/null +++ b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 01f9f472b0a645b1ab392567aa38911a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/CultMath.UnityBridge.asmdef b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/CultMath.UnityBridge.asmdef new file mode 100644 index 00000000..c54da7da --- /dev/null +++ b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/CultMath.UnityBridge.asmdef @@ -0,0 +1,16 @@ +{ + "name": "CultMath.UnityBridge", + "rootNamespace": "CultMath.UnityBridge", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "CultMath.dll" + ], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} diff --git a/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/CultMath.UnityBridge.asmdef.meta b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/CultMath.UnityBridge.asmdef.meta new file mode 100644 index 00000000..6875b788 --- /dev/null +++ b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/CultMath.UnityBridge.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 77e50a05974842bc99d285ef6d5d04f7 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/UnityConversions.cs b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/UnityConversions.cs new file mode 100644 index 00000000..c8289a77 --- /dev/null +++ b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/UnityConversions.cs @@ -0,0 +1,28 @@ +using UnityEngine; + +namespace CultMath.UnityBridge +{ + /// + /// Explicit conversions between UnityEngine and CultMath value types. + /// C# only admits user-defined conversion operators declared on the source or target + /// type, and CultMath's core types stay engine-free, so the bridge is extension methods. + /// + public static class UnityConversions + { + public static float2 ToCultMath(this Vector2 value) => new float2(value.x, value.y); + public static float3 ToCultMath(this Vector3 value) => new float3(value.x, value.y, value.z); + public static float4 ToCultMath(this Vector4 value) => new float4(value.x, value.y, value.z, value.w); + public static float4 ToCultMath(this Color value) => new float4(value.r, value.g, value.b, value.a); + public static quaternion ToCultMath(this Quaternion value) => new quaternion(value.x, value.y, value.z, value.w); + public static int2 ToCultMath(this Vector2Int value) => new int2(value.x, value.y); + public static int3 ToCultMath(this Vector3Int value) => new int3(value.x, value.y, value.z); + + public static Vector2 ToUnity(this float2 value) => new Vector2(value.x, value.y); + public static Vector3 ToUnity(this float3 value) => new Vector3(value.x, value.y, value.z); + public static Vector4 ToUnity(this float4 value) => new Vector4(value.x, value.y, value.z, value.w); + public static Quaternion ToUnity(this quaternion value) => new Quaternion(value.x, value.y, value.z, value.w); + public static Vector2Int ToUnity(this int2 value) => new Vector2Int(value.x, value.y); + public static Vector3Int ToUnity(this int3 value) => new Vector3Int(value.x, value.y, value.z); + public static Color ToColor(this float4 value) => new Color(value.x, value.y, value.z, value.w); + } +} diff --git a/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/UnityConversions.cs.meta b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/UnityConversions.cs.meta new file mode 100644 index 00000000..ccbd3f10 --- /dev/null +++ b/packages/cultmath/unity/org.gamecult.cultmath/Runtime/UnityBridge/UnityConversions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8fc742177b3f4715b204afe0e69a400c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/cultmath/unity/org.gamecult.cultmath/Shaders/CultMath.hlsl b/packages/cultmath/unity/org.gamecult.cultmath/Shaders/CultMath.hlsl index 4c00fb69..365705f5 100644 --- a/packages/cultmath/unity/org.gamecult.cultmath/Shaders/CultMath.hlsl +++ b/packages/cultmath/unity/org.gamecult.cultmath/Shaders/CultMath.hlsl @@ -26,10 +26,11 @@ float2 cultmath_clamp(float2 value, float2 minimum, float2 maximum) { return min float3 cultmath_clamp(float3 value, float3 minimum, float3 maximum) { return min(max(value, minimum), maximum); } float4 cultmath_clamp(float4 value, float4 minimum, float4 maximum) { return min(max(value, minimum), maximum); } -float cultmath_saturate(float value) { return cultmath_clamp(value, 0.0, 1.0); } -float2 cultmath_saturate(float2 value) { return cultmath_clamp(value, float2(0.0, 0.0), float2(1.0, 1.0)); } -float3 cultmath_saturate(float3 value) { return cultmath_clamp(value, float3(0.0, 0.0, 0.0), float3(1.0, 1.0, 1.0)); } -float4 cultmath_saturate(float4 value) { return cultmath_clamp(value, float4(0.0, 0.0, 0.0, 0.0), float4(1.0, 1.0, 1.0, 1.0)); } +// DXIL Saturate is FMin(1, FMax(0, x)); dxc lowers min/max to the same FMin/FMax, NaN rules included. +float cultmath_saturate(float value) { return min(1.0, max(0.0, value)); } +float2 cultmath_saturate(float2 value) { return min(float2(1.0, 1.0), max(float2(0.0, 0.0), value)); } +float3 cultmath_saturate(float3 value) { return min(float3(1.0, 1.0, 1.0), max(float3(0.0, 0.0, 0.0), value)); } +float4 cultmath_saturate(float4 value) { return min(float4(1.0, 1.0, 1.0, 1.0), max(float4(0.0, 0.0, 0.0, 0.0), value)); } float cultmath_lerp(float start, float end, float amount) { return start + (end - start) * amount; } float2 cultmath_lerp(float2 start, float2 end, float2 amount) { return start + (end - start) * amount; } @@ -73,13 +74,66 @@ float cultmath_smootherstep(float value) return value * value * value * (value * (value * 6.0 - 15.0) + 10.0); } -float2 cultmath_simplex_mod289(float2 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } -float3 cultmath_simplex_mod289(float3 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } -float3 cultmath_simplex_permute(float3 value) { return cultmath_simplex_mod289(((value * 34.0) + 1.0) * value); } +float2 cultmath_snoise_mod289(float2 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } +float3 cultmath_snoise_mod289(float3 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } +float4 cultmath_snoise_mod289(float4 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } +float3 cultmath_snoise_permute(float3 value) { return cultmath_snoise_mod289(((value * 34.0) + 1.0) * value); } +float4 cultmath_snoise_permute(float4 value) { return cultmath_snoise_mod289(((value * 34.0) + 1.0) * value); } -float cultmath_simplex_noise(float2 value) +// Ashima Arts / Ian McEwan 3D simplex noise (MIT), same float32 evaluation +// order as the C# math.snoise(float3) mirror. +float cultmath_snoise(float3 value) { - const float4 c = float4( + float2 c = float2(1.0 / 6.0, 1.0 / 3.0); + float3 i = floor(value + dot(value, c.yyy)); + float3 x0 = value - i + dot(i, c.xxx); + float3 g = step(x0.yzx, x0.xyz); + float3 l = 1.0 - g; + float3 i1 = min(g.xyz, l.zxy); + float3 i2 = max(g.xyz, l.zxy); + float3 x1 = x0 - i1 + c.xxx; + float3 x2 = x0 - i2 + c.yyy; + float3 x3 = x0 - 0.5; + + i = cultmath_snoise_mod289(i); + float4 p = cultmath_snoise_permute(cultmath_snoise_permute(cultmath_snoise_permute( + i.z + float4(0.0, i1.z, i2.z, 1.0)) + i.y + float4(0.0, i1.y, i2.y, 1.0)) + i.x + float4(0.0, i1.x, i2.x, 1.0)); + + const float n_ = 0.142857142857; + float3 ns = float3(2.0 * n_, 0.5 * n_ - 1.0, n_); + float4 j = p - 49.0 * floor(p * ns.z * ns.z); + float4 x_ = floor(j * ns.z); + float4 y_ = floor(j - 7.0 * x_); + float4 x = x_ * ns.x + ns.yyyy; + float4 y = y_ * ns.x + ns.yyyy; + float4 h = 1.0 - abs(x) - abs(y); + + float4 b0 = float4(x.xy, y.xy); + float4 b1 = float4(x.zw, y.zw); + float4 s0 = floor(b0) * 2.0 + 1.0; + float4 s1 = floor(b1) * 2.0 + 1.0; + float4 sh = -step(h, 0.0); + float4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + float4 a1 = b1.xzyw + s1.xzyw * sh.zzww; + + float3 p0 = float3(a0.xy, h.x); + float3 p1 = float3(a0.zw, h.y); + float3 p2 = float3(a1.xy, h.z); + float3 p3 = float3(a1.zw, h.w); + float4 norm = 1.79284291400159 - 0.85373472095314 * float4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + float4 m = max(0.6 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); + m = m * m; + return 42.0 * dot(m * m, float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); +} + +float cultmath_snoise(float2 value) +{ + float4 c = float4( 0.211324865405187, 0.366025403784439, -0.577350269189626, @@ -89,9 +143,9 @@ float cultmath_simplex_noise(float2 value) float2 i1 = x0.x > x0.y ? float2(1.0, 0.0) : float2(0.0, 1.0); float4 x12 = float4(x0.x + c.x - i1.x, x0.y + c.x - i1.y, x0.x + c.z, x0.y + c.z); - i = cultmath_simplex_mod289(i); - float3 p = cultmath_simplex_permute( - cultmath_simplex_permute(i.y + float3(0.0, i1.y, 1.0)) + i.x + float3(0.0, i1.x, 1.0)); + i = cultmath_snoise_mod289(i); + float3 p = cultmath_snoise_permute( + cultmath_snoise_permute(i.y + float3(0.0, i1.y, 1.0)) + i.x + float3(0.0, i1.x, 1.0)); float3 m = max(0.5 - float3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0); m *= m; m *= m; diff --git a/packages/cultmath/unity/org.gamecult.cultmath/THIRD-PARTY-NOTICES.md b/packages/cultmath/unity/org.gamecult.cultmath/THIRD-PARTY-NOTICES.md index c283df2b..06ebb6c3 100644 --- a/packages/cultmath/unity/org.gamecult.cultmath/THIRD-PARTY-NOTICES.md +++ b/packages/cultmath/unity/org.gamecult.cultmath/THIRD-PARTY-NOTICES.md @@ -1,9 +1,9 @@ # Third-Party Notices -## 2D Simplex Noise +## 2D and 3D Simplex Noise -`math.simplex_noise` and `cultmath_simplex_noise` port the textureless 2D -simplex-noise function by Ian McEwan and Ashima Arts, as distributed by Stefan +`math.snoise` and `cultmath_snoise` port the textureless 2D and 3D +simplex-noise functions by Ian McEwan and Ashima Arts, as distributed by Stefan Gustavson's `webgl-noise` project and Unity Mathematics. - Copyright 2011 Ashima Arts. diff --git a/packages/cultmath/unity/org.gamecult.cultmath/package.json b/packages/cultmath/unity/org.gamecult.cultmath/package.json index 80ceddad..c09bf6bb 100644 --- a/packages/cultmath/unity/org.gamecult.cultmath/package.json +++ b/packages/cultmath/unity/org.gamecult.cultmath/package.json @@ -1,7 +1,7 @@ { "name": "org.gamecult.cultmath", "displayName": "CultMath", - "version": "0.1.2", + "version": "0.2.0", "unity": "2021.3", "description": "Precompiled CultMath numeric primitives and matching HLSL semantics for GameCult Unity runtimes.", "license": "MPL-2.0",