From 636cff4878ee7ce4b49dba5ec4738fc9cccf41fa Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 15 Jul 2026 21:09:20 +0100 Subject: [PATCH] Advertise temporal gravity fog program --- Aetheria.State.Daemon.Smoke/Program.cs | 8 ++- Aetheria.State.Verify/Program.cs | 4 +- Aetheria.Unity/Packages/manifest.json | 2 +- Aetheria.Unity/Packages/packages-lock.json | 4 +- .../Tests/DaemonRuntimeDocumentTests.cs | 5 +- Assets/Shaders/Raymarching/CloudShader.shader | 54 ++++++++++++++++++- .../Runtime/AetheriaRuntimeAssets.cs | 5 ++ docs/aetheria-perfect-machine-map.md | 32 +++++++++++ docs/original-game-migration-matrix.md | 2 +- 9 files changed, 106 insertions(+), 10 deletions(-) diff --git a/Aetheria.State.Daemon.Smoke/Program.cs b/Aetheria.State.Daemon.Smoke/Program.cs index 6701de97b..33d1ad93a 100644 --- a/Aetheria.State.Daemon.Smoke/Program.cs +++ b/Aetheria.State.Daemon.Smoke/Program.cs @@ -226,7 +226,13 @@ internal sealed class AetheriaDaemonYmirSmokeChecks var shader = AetheriaRuntimeAssets.ProjectManifest(null).Assets.Single(asset => asset.Ref.AssetKey == "shader.environment.gravity-fog"); Require(shader.Ref.Metadata["unity.volume.texturePort.surfaceHeight"] == "_NebulaSurfaceHeight" && - shader.Ref.Metadata["unity.volume.pass.raymarch"] == "0", + shader.Ref.Metadata["unity.volume.pass.raymarch"] == "0" && + shader.Ref.Metadata["unity.volume.pass.temporal"] == "1" && + shader.Ref.Metadata["unity.volume.pass.composite"] == "2" && + shader.Ref.Metadata["unity.volume.texturePort.currentSample"] == "_UndersampleCloudTex" && + shader.Ref.Metadata["unity.volume.texturePort.history"] == "_MainTex" && + shader.Ref.Metadata["unity.volume.matrixPort.previousViewProjection"] == "_PrevVP" && + shader.Ref.Metadata["unity.volume.floatPort.resetHistory"] == "_ResetHistory", "provider asset metadata must own the concrete Unity volume-program ABI projected into runtime variants"); } diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 3271e7fb7..e6af9034d 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -794,7 +794,7 @@ static void RequireSharedEvePackagesImportedFromEveRepo(string root) "\"org.gamecult.cultlib\": \"https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.13\"", "\"org.gamecult.eve.plugin-fields\": \"https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.2.0\"", "\"org.gamecult.eve.surface\": \"https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.2.2\"", - "\"org.gamecult.eve.unity-scene\": \"https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.37\"" + "\"org.gamecult.eve.unity-scene\": \"https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.38\"" }; var missingManifestSymbols = requiredManifestSymbols @@ -813,7 +813,7 @@ static void RequireSharedEvePackagesImportedFromEveRepo(string root) "cultlib-unity-v1.0.13", "eve-plugin-fields-unity-v0.2.0", "eveunity-surface-v0.2.2", - "eveunity-scene-v0.3.37", + "eveunity-scene-v0.3.38", "\"source\": \"git\"" }; diff --git a/Aetheria.Unity/Packages/manifest.json b/Aetheria.Unity/Packages/manifest.json index fe943c3c3..9e430a81b 100644 --- a/Aetheria.Unity/Packages/manifest.json +++ b/Aetheria.Unity/Packages/manifest.json @@ -9,6 +9,6 @@ "org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.13", "org.gamecult.eve.plugin-fields": "https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.2.0", "org.gamecult.eve.surface": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.2.2", - "org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.37" + "org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.38" } } diff --git a/Aetheria.Unity/Packages/packages-lock.json b/Aetheria.Unity/Packages/packages-lock.json index 6f0044500..5f8d35ae3 100644 --- a/Aetheria.Unity/Packages/packages-lock.json +++ b/Aetheria.Unity/Packages/packages-lock.json @@ -144,7 +144,7 @@ "hash": "140e1bd963a0033e66777a3b2c5fe6e9c97dfe32" }, "org.gamecult.eve.unity-scene": { - "version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.37", + "version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.38", "depth": 0, "source": "git", "dependencies": { @@ -154,7 +154,7 @@ "com.unity.mathematics": "1.3.2", "com.unity.modules.particlesystem": "1.0.0" }, - "hash": "5ce654800760203154132d4b0fd1475294283ba6" + "hash": "de580fe88350f422e30f1e4b64ded5c33070b118" }, "com.unity.modules.assetbundle": { "version": "1.0.0", diff --git a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs index de4bdb91e..f7402fac4 100644 --- a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs +++ b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs @@ -718,7 +718,10 @@ public class DaemonRuntimeDocumentTests Assert.IsTrue(assetManifest.Assets.Any(asset => asset.Ref.AssetKey == "shader.environment.gravity-fog" && asset.Ref.Kind == AetheriaRuntimeAssetKinds.Shader && - asset.Ref.Metadata["unity.volume.texturePort.surfaceHeight"] == "_NebulaSurfaceHeight")); + asset.Ref.Metadata["unity.volume.texturePort.surfaceHeight"] == "_NebulaSurfaceHeight" && + asset.Ref.Metadata["unity.volume.pass.temporal"] == "1" && + asset.Ref.Metadata["unity.volume.texturePort.currentSample"] == "_UndersampleCloudTex" && + asset.Ref.Metadata["unity.volume.texturePort.history"] == "_MainTex")); Assert.IsTrue(assetManifest.Assets.Any(asset => asset.Ref.AssetKey == "texture.environment.volume-dither" && asset.Ref.Kind == AetheriaRuntimeAssetKinds.Texture)); diff --git a/Assets/Shaders/Raymarching/CloudShader.shader b/Assets/Shaders/Raymarching/CloudShader.shader index fef76e2ec..eef208263 100644 --- a/Assets/Shaders/Raymarching/CloudShader.shader +++ b/Assets/Shaders/Raymarching/CloudShader.shader @@ -267,9 +267,59 @@ Shader "Aetheria/CloudShader" float4 frag(v2f i) : SV_Target { float3 vspos = float3(i.vsray, 1.0); - // float4 worldPos = mul(unity_CameraToWorld, float4(vspos, 1.0f)); - // worldPos /= worldPos.w; float4 raymarchResult = tex2D(_UndersampleCloudTex, i.uv); + if (_ResetHistory >= 0.5f) + return raymarchResult; + + float distance; + float density; + float density2 = 0.0f; + unpack(raymarchResult.a, distance, density); + distance *= _ProjectionParams.z; + + half outOfBound; + float3 previousWorldPosition = mul( + unity_CameraToWorld, + float4(normalize(vspos) * distance, 1.0f)).xyz; + float2 prevUV = PrevUV(float4(previousWorldPosition, 1.0f), outOfBound); + float4 prevSample = tex2D(_MainTex, prevUV); + float2 xoffset = float2(_UndersampleCloudTex_TexelSize.x, 0.0f); + float2 yoffset = float2(0.0f, _UndersampleCloudTex_TexelSize.y); + float4 m1 = 0.0f; + float4 m2 = 0.0f; + + [unroll] + for (int x = -1; x <= 1; x++) + { + [unroll] + for (int y = -1; y <= 1; y++) + { + float4 value; + if (x == 0 && y == 0) + { + value = float4(raymarchResult.rgb, distance); + } + else + { + value = tex2Dlod( + _UndersampleCloudTex, + float4(i.uv + xoffset * x + yoffset * y, 0.0f, 0.0f)); + float neighborDistance; + unpack(value.a, neighborDistance, density2); + value = float4(value.rgb, neighborDistance); + } + m1 += value; + m2 += value * value; + } + } + + float4 mean = m1 / 9.0f; + float4 sigma = sqrt(abs(m2 / 9.0f - mean * mean)); + prevSample = ClipAABB(mean - 0.5f * sigma, mean + 0.5f * sigma, prevSample); + raymarchResult = lerp( + float4(prevSample.rgb, density), + float4(raymarchResult.rgb, density2), + max(0.05f, outOfBound)); return raymarchResult; } ENDCG diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs index 4f5d8a392..53bfc4ec8 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs @@ -224,6 +224,7 @@ namespace GameCult.Aetheria.State.Verse ["unityAssetPath"] = "Assets/Shaders/Raymarching/CloudShader.shader", ["presentationRole"] = "environment.gravity-fog.volume", ["unity.volume.pass.raymarch"] = "0", + ["unity.volume.pass.temporal"] = "1", ["unity.volume.pass.composite"] = "2", ["unity.volume.quality.high.keyword"] = "HIGH_QUALITY", ["unity.volume.quality.normal.keyword"] = "MEDIUM_QUALITY", @@ -234,11 +235,15 @@ namespace GameCult.Aetheria.State.Verse ["unity.volume.texturePort.patch"] = "_NebulaPatch", ["unity.volume.texturePort.tint"] = "_NebulaTint", ["unity.volume.texturePort.dither"] = "_DitheringTex", + ["unity.volume.texturePort.currentSample"] = "_UndersampleCloudTex", + ["unity.volume.texturePort.history"] = "_MainTex", ["unity.volume.texturePort.cloud"] = "_CloudTex", ["unity.volume.vectorPort.viewportTransform"] = "_GridTransform", ["unity.volume.matrixPort.cameraInverseViewProjection"] = "_CamInvProj", + ["unity.volume.matrixPort.previousViewProjection"] = "_PrevVP", ["unity.volume.vectorPort.cameraProjectionExtents"] = "_ProjectionExtents", ["unity.volume.floatPort.raymarchOffset"] = "_RaymarchOffset", + ["unity.volume.floatPort.resetHistory"] = "_ResetHistory", ["unity.volume.vectorPort.ditherCoordinates"] = "_DitheringCoords", ["unity.volume.floatPort.fillDensity"] = "_NebulaFillDensity", ["unity.volume.floatPort.fillDistance"] = "_NebulaFillDistance", diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 05034ca2c..30ac52b91 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -865,6 +865,38 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. height, and negative body splats. Released cold/warm witnesses exercise the persisted daemon path. +### Gravity Fog Presentation Contract + +- Owner: Aetheria owns the gravity/fog splat values, authored volume tuning, and + provider shader asset. Eve Fields owns the portable splat document and + `field.volume3d` logical ports. EveUnity owns only the native render lifecycle. +- Inputs: the canonical viewport splat document, logical surface-height, + patch-height, patch, tint, scalar/vector parameters, provider asset reference, + and the selected runtime asset variant's native program metadata. +- Outputs: the generic runtime rasterizes logical field layers, executes the + advertised raymarch, optional temporal-history, and composite passes, and + presents pixels. No pass can emit gameplay state or commands. +- Derived state: Unity materials, shader-property names, pass indices, keywords, + raymarch targets, ping-pong history textures, previous camera matrices, and + composite counters are runtime projections. They are neither daemon truth nor + portable Eve state. +- Forbidden writers: the old `VolumeSampling`/`VolumeCloudRenderer` presentation + path, shader code, Unity cameras, and generic lowerers cannot calculate gravity, + move bodies, or publish world truth. EveUnity may not import an Aetheria assembly + or branch on Aetheria asset identities. +- Shared paths: first frame, camera motion, reconnect, viewport resize, and warm + asset reuse all enter the same generic volume lifecycle. History resets on a + new node, program, or render size. A provider that advertises a temporal pass + without all required history ports fails closed. +- Cut line: the portable surface contains logical field and parameter names only. + `_Nebula*`, `_PrevVP`, `_ResetHistory`, shader keywords, and pass indices live + exclusively in the provider's Unity asset-variant metadata. +- Verification layer: EveUnity package tests prove two-pass compatibility, + complete temporal-program acceptance, partial-program rejection, and absence + of Aetheria assembly references. Aetheria daemon smoke proves the exact fossil + shader ABI is advertised outside the portable surface. A live capture is still + required for visual parity. + ### Daemon Asset Content Delivery - Owner: the Aetheria daemon owns bundle content and serves bytes only through diff --git a/docs/original-game-migration-matrix.md b/docs/original-game-migration-matrix.md index 108cbf08d..1062d14ba 100644 --- a/docs/original-game-migration-matrix.md +++ b/docs/original-game-migration-matrix.md @@ -34,7 +34,7 @@ validation proves contract shape, not wiring, exclusivity, lowering, or parity. | Pilot camera | ARPG follow/dock cameras | Eve view semantics + runtime | camera rig and semantic view id | EveUnity camera rig | authored ARPG follow distance, field of view, screen framing, and damping are provider-owned and generically lowered; dock/view transition parity remains absent | dock transition timeline and reconnect | | Render channels | Unity layers/culling masks | provider surface policy + runtime asset variant | semantic channel exclusions | EveUnity native layer mapping | wired, exclusive, and lowered: the pilot surface excludes the semantic `map` channel; the Unity asset variant maps `map` to its authored native layer; EveUnity subtracts that layer from the configured camera mask; portable state contains no Unity layer or mask; package tests and the clean-client dependency verifier pass | live pilot capture proving no map glyphs while the map camera retains them | | Minimap/tactical map | `ZoneRenderer`, map cameras | daemon world facts | explicit map projections | Unity and Electron | facts fragmented; generic lowerers absent | same state in both runtimes | -| Fields/fog/gravity | zone renderer and shaders | daemon/plugin semantics | Fields plugin documents | EveUnity fields lowerer | owner-exists; basic lowering; composition parity absent | visual parity captures | +| Fields/fog/gravity | zone renderer and shaders | daemon/plugin semantics | Fields plugin documents | EveUnity fields lowerer | daemon publishes four canonical gravity/fog splat layers through `field.volume3d`; the provider asset variant owns the fossil shader, native ports, quality/features, and raymarch/temporal/composite pass graph; released EveUnity `0.3.38` lowers the optional temporal-history lifecycle generically and fails closed on partial native programs; the warm released witness executes the provider bundle, four layers, and repeated composites without importing Aetheria code; the current pilot capture remains nearly black/geometric rather than the authored blue fog sea, so visual composition parity is explicitly absent | diagnose field values, colour/exposure/composition and camera-depth behavior against the fossil, then capture temporal stability and visual parity | | HUD/schematic | ARPG HUD and `SchematicDisplay` | Eve composition from daemon facts | semantic status/components | EveUnity UI | provider-authored transparent cockpit is wired, projected, and lowered through generic UI Toolkit panes, metrics, and progress components for hull, shield, capacitor, temperature, cargo, weapon cooldown/groups, selected target, target lock, target shield, and target hull; the original armor/equipment schematic and transient warning choreography remain reference-only | schematic topology, warning timelines, dynamic action state, and composited live capture | | Hit/lock/warnings | `ActionGameManager` feedback | daemon event facts | transient feedback stream | VFX/UI/audio | bounded deduplicated game-event ledger and Eve `feedback.stream` wired for pickup outcomes, projectile launch/impact, entity damage, alive-to-dead destruction, authoritative weapon reload chronology, and episode-deduplicated `weapon.fire.refused` facts for insufficient energy or missing ammunition; refusal identity, authored weapon, target, reason, and remaining magazine state are runtime-independent cockpit inputs | add lock transitions, thermal and docking event families, negative transaction fixtures, and runtime effect consumption | | Assets | `Assets/Resources` | Aetheria provider | asset catalog + CultMesh CDN | runtime cache/load | provider manifests name deterministic bundle-internal paths; the authoring build derives presentation-only prefabs, strips missing and non-Eve `MonoBehaviour` components, verifies both saved prefabs and the loaded bundle, and publishes the immutable bundle through CultMesh; released-client warm-cache loading, content verification, and rendered provider identity are proven, while cold transfer still times out because the 46.1 MB body travels through batched snapshot records | mapped/network body transport followed by empty-cache download, atomic promotion, and cache reuse proof |