Ship CultMath 0.2.0 to Unity with a UnityEngine conversion bridge

- Bump GameCult.Math and org.gamecult.cultmath to 0.2.0; the CultCache
  Unity tools package now depends on cultmath 0.2.0.
- Rebuild the tracked netstandard2.1 CultMath.dll/pdb with the builder's
  deterministic command; build-unity-package.ps1 verifies it byte-for-byte.
- Add Runtime/UnityBridge (CultMath.UnityBridge asmdef) with ToCultMath /
  ToUnity / ToColor extension methods for Vector2/3/4, Vector2Int/3Int,
  Quaternion and Color. C# only admits conversion operators on the source
  or target type, and the core DLL stays engine-free.
- The builder admits exactly that one bridge assembly: it must reference
  UnityEngine and CultMath.dll, and no core source may be compiled in the
  package.
- Sync the Unity copy of CultMath.hlsl and the third-party notice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-14 13:30:00 +02:00
commit 23cc86c4e9
15 changed files with 179 additions and 29 deletions

View file

@ -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.

View file

@ -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)"

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;netstandard2.1</TargetFrameworks>
@ -8,7 +8,7 @@
<RootNamespace>CultMath</RootNamespace>
<AssemblyName>CultMath</AssemblyName>
<PackageId>GameCult.Math</PackageId>
<Version>0.1.2</Version>
<Version>0.2.0</Version>
<Description>Clean-room C# math shaped for HLSL-style renderer code.</Description>
<Authors>GameCult</Authors>
<PackageReadmeFile>README.md</PackageReadmeFile>

View file

@ -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

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 01f9f472b0a645b1ab392567aa38911a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 77e50a05974842bc99d285ef6d5d04f7
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,28 @@
using UnityEngine;
namespace CultMath.UnityBridge
{
/// <summary>
/// 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.
/// </summary>
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);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8fc742177b3f4715b204afe0e69a400c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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,

View file

@ -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.

View file

@ -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",

View file

@ -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"