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/scripts/build-unity-package.ps1 b/packages/cultmath/scripts/build-unity-package.ps1 index f5fcacc0..7bc3da7c 100644 --- a/packages/cultmath/scripts/build-unity-package.ps1 +++ b/packages/cultmath/scripts/build-unity-package.ps1 @@ -52,6 +52,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 +66,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 +82,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 @@ -97,23 +110,29 @@ foreach ($pair in @(@($freshAssembly, $trackedAssembly), @($freshSymbols, $track } } +$coreSourceNames = @(Get-ChildItem -LiteralPath (Join-Path $repoRoot "src\CultMath") -File -Filter "*.cs" | ForEach-Object Name) $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 ', ')" +$strayOrCoreSources = @($trackedSourceFiles | Where-Object { + $_.DirectoryName -ne $bridgeRoot -or $coreSourceNames -contains $_.Name +}) +if ($trackedSourceFiles.Count -eq 0 -or $strayOrCoreSources.Count -ne 0) { + throw "CultMath Unity package may compile only bridge sources under Runtime\UnityBridge, never core sources: $($strayOrCoreSources.FullName -join ', ')" } 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/src/CultMath/CultMath.csproj b/packages/cultmath/src/CultMath/CultMath.csproj index 0ca99063..a8f61d97 100644 --- a/packages/cultmath/src/CultMath/CultMath.csproj +++ b/packages/cultmath/src/CultMath/CultMath.csproj @@ -1,4 +1,4 @@ - + net8.0;netstandard2.1 @@ -8,7 +8,7 @@ CultMath CultMath GameCult.Math - 0.1.2 + 0.2.0 Clean-room C# math shaped for HLSL-style renderer code. GameCult README.md 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..8428d206 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..29e4b8f3 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..f3c398d6 100644 --- a/packages/cultmath/unity/org.gamecult.cultmath/Shaders/CultMath.hlsl +++ b/packages/cultmath/unity/org.gamecult.cultmath/Shaders/CultMath.hlsl @@ -73,11 +73,64 @@ 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 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) { const float4 c = float4( 0.211324865405187, 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..302eb481 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", diff --git a/src/GameCult.Unity/Assets/Caching/package.json b/src/GameCult.Unity/Assets/Caching/package.json index cde27289..560600a9 100644 --- a/src/GameCult.Unity/Assets/Caching/package.json +++ b/src/GameCult.Unity/Assets/Caching/package.json @@ -1,4 +1,4 @@ -{ +{ "name": "org.gamecult.caching.unity", "displayName": "GameCult CultCache Unity Tools", "version": "1.1.0", @@ -11,7 +11,7 @@ ], "dependencies": { "org.gamecult.cultlib": "1.0.57", - "org.gamecult.cultmath": "0.1.2" + "org.gamecult.cultmath": "0.2.0" }, "license": "MIT", "category": "Scripting"