diff --git a/packages/cultmath/tools/compile-glsl.ps1 b/packages/cultmath/tools/compile-glsl.ps1 new file mode 100644 index 00000000..eba1c6cb --- /dev/null +++ b/packages/cultmath/tools/compile-glsl.ps1 @@ -0,0 +1,46 @@ +param( + # A GLSL source to append after the generated wrapper's library instead of the call-every-function + # body; tools\compile-glsl.ps1 -Body 'float x = lerp(0.0, 1.0, 0.5);' is the negative control. + [string]$Body = "", + [string]$GlslangPath = "" +) + +$ErrorActionPreference = "Stop" + +$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..") +if (-not $GlslangPath) { + $found = Get-ChildItem -LiteralPath (Join-Path $repoRoot ".tools\glslang\current") -Recurse -File -Include "glslang.exe", "glslangValidator.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($found) { $GlslangPath = $found.FullName } +} + +# The wrapper is what a WebGL2 consumer writes: its own #version line and precision, then the library +# by string concatenation (GLSL has no #include), then a fragment shader that calls every public +# function once with zero arguments of its declared types. +$library = Get-Content -LiteralPath (Join-Path $repoRoot "shaders\CultMath.glsl") -Raw +$calls = foreach ($match in [regex]::Matches($library, '(?m)^\w+\s+(cultmath_\w+)\s*\(([^)]*)\)')) { + $arguments = foreach ($parameter in ($match.Groups[2].Value -split ',')) { + $type = ($parameter.Trim() -split '\s+')[0] + if ($type -eq 'int' -or $type -like 'ivec*') { "$type(0)" } + elseif ($type -eq 'uint' -or $type -like 'uvec*') { "$type(0u)" } + else { "$type(0.0)" } + } + " $($match.Groups[1].Value)($($arguments -join ', '));" +} +if (-not $Body) { + $Body = $calls -join "`n" +} + +$wrapper = "#version 300 es`nprecision highp float;`nprecision highp int;`n$library`nout vec4 cultmathColor;`nvoid main()`n{`n$Body`n cultmathColor = vec4(0.0);`n}`n" +$wrapperPath = Join-Path $repoRoot ".tools\glsl\cultmath-wrapper.frag" +New-Item -ItemType Directory -Force -Path (Split-Path -Parent $wrapperPath) | Out-Null +[System.IO.File]::WriteAllText($wrapperPath, $wrapper) +Write-Host "wrapper: $wrapperPath ($(@($calls).Count) functions)" + +if (-not $GlslangPath -or -not (Test-Path $GlslangPath)) { + throw "glslang not found. Run tools\get-glslang.ps1 first or pass -GlslangPath." +} + +& $GlslangPath -S frag $wrapperPath +if ($LASTEXITCODE -ne 0) { + throw "glslang failed with exit code $LASTEXITCODE." +} diff --git a/packages/cultmath/tools/compile-glsl.ps1.meta b/packages/cultmath/tools/compile-glsl.ps1.meta new file mode 100644 index 00000000..f8a18295 --- /dev/null +++ b/packages/cultmath/tools/compile-glsl.ps1.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 81a35b09c28246fe8d52cf8996540a46 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/cultmath/tools/get-glslang.ps1 b/packages/cultmath/tools/get-glslang.ps1 new file mode 100644 index 00000000..e22e1095 --- /dev/null +++ b/packages/cultmath/tools/get-glslang.ps1 @@ -0,0 +1,42 @@ +param( + [string]$InstallRoot = ".tools\glslang" +) + +$ErrorActionPreference = "Stop" + +$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..") +$installPath = Join-Path $repoRoot $InstallRoot +New-Item -ItemType Directory -Force -Path $installPath | Out-Null + +# Pinned: the GLSL compile check means this front end (Khronos glslang 16.6.0). The hash is the +# SHA256 digest GitHub publishes for this asset of KhronosGroup/glslang release 16.6.0. +$zipName = "glslang-16.6.0-windows-x86_64-release.zip" +$zipUrl = "https://github.com/KhronosGroup/glslang/releases/download/16.6.0/$zipName" +$zipSha256 = "82BF434E69B9BB4829DE7E2B4BC2C5E7A7861E53D66CF75E5CC70F5F694A8D9B" + +$zipPath = Join-Path $installPath $zipName +Remove-Item -LiteralPath $zipPath -Force -ErrorAction SilentlyContinue + +& 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 +Expand-Archive -LiteralPath $zipPath -DestinationPath $currentPath -Force + +$glslangPath = Get-ChildItem -LiteralPath $currentPath -Recurse -File -Include "glslang.exe", "glslangValidator.exe" | Select-Object -First 1 +if (-not $glslangPath) { + throw "glslang executable was not found after extraction." +} + +& $glslangPath.FullName --version +Write-Host "glslang installed at $($glslangPath.FullName)" diff --git a/packages/cultmath/tools/get-glslang.ps1.meta b/packages/cultmath/tools/get-glslang.ps1.meta new file mode 100644 index 00000000..e254d120 --- /dev/null +++ b/packages/cultmath/tools/get-glslang.ps1.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 37d057d5ae6847b7b4689d36514b3dc9 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: