diff --git a/docs/authority-map.md b/docs/authority-map.md index 235f69e..3731576 100644 --- a/docs/authority-map.md +++ b/docs/authority-map.md @@ -19,6 +19,7 @@ drivers, asset-provider hooks, package release, runtime tests, and captures. - provider asset-manifest documents - runtime-selected asset-variant metadata, including native rendering-program bindings - generic field-volume lifecycle declarations and logical texture/scalar/matrix ports +- generic viewport-to-texture scale bindings between logical vector and texture ports - provider command receipts - semantic render-channel inclusion and exclusion policy - Unity input and local presentation assets @@ -51,6 +52,13 @@ temporal pass must also advertise its current-sample, history, previous-view-projection, and reset-history ports; partial temporal programs fail closed. +Viewport-sized shader inputs are likewise presentation state. A provider may +relate a logical vector port to a logical native texture port through +`viewportTextureScaleBindings`; EveUnity derives the vector from the active +camera viewport and resolved texture dimensions on every render. A provider +literal is not allowed to impersonate those runtime dimensions, and an +unresolvable relation fails closed. + The provider selects camera semantics and framing from its authoritative mode. `planar.top-down-follow.v1` derives a downward presentation from the advertised target and framing values. `perspective.entity-forward-follow.v1` derives its diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/Fields/EveUnityFieldsVolumeRenderer.cs b/packages/org.gamecult.eve.unity-scene/Runtime/Fields/EveUnityFieldsVolumeRenderer.cs index 2509849..add27f9 100644 --- a/packages/org.gamecult.eve.unity-scene/Runtime/Fields/EveUnityFieldsVolumeRenderer.cs +++ b/packages/org.gamecult.eve.unity-scene/Runtime/Fields/EveUnityFieldsVolumeRenderer.cs @@ -20,6 +20,8 @@ namespace GameCult.Eve.UnityScene.Fields private Material? _material; private Shader? _sourceShader; private IReadOnlyDictionary? _programMetadata; + private readonly Dictionary _assetTexturesByPort = + new Dictionary(StringComparer.Ordinal); private RenderTexture? _raymarchTexture; private readonly RenderTexture?[] _historyTextures = new RenderTexture?[2]; private int _historyTextureIndex = -1; @@ -89,7 +91,7 @@ namespace GameCult.Eve.UnityScene.Fields ApplyFieldBindings(field, _document); camera.depthTextureMode |= DepthTextureMode.Depth; EnsureVolumeTextures(camera, field); - if (_raymarchTexture == null) return; + if (_raymarchTexture == null || !ApplyViewportTextureScaleBindings(field, camera)) return; var gpuProjection = GL.GetGPUProjectionMatrix(camera.projectionMatrix, true); var viewProjection = gpuProjection * camera.worldToCameraMatrix; @@ -168,6 +170,7 @@ namespace GameCult.Eve.UnityScene.Fields _sourceShader = shader; _activeNodeId = field.NodeId; _material = new Material(shader) { hideFlags = HideFlags.DontSave }; + _assetTexturesByPort.Clear(); ResetTemporalHistory(); foreach (var parameter in ParseBindings(Prop(field, "floatParameters"))) { @@ -180,7 +183,11 @@ namespace GameCult.Eve.UnityScene.Fields var texture = provider.ResolveAsset( new EveUnityPlayableWorldAssetBinding(binding.Key, "", "provider-asset-ref"), typeof(Texture)) as Texture; - if (texture != null) SetTexturePort(binding.Value, texture); + if (texture != null) + { + _assetTexturesByPort[binding.Value] = texture; + SetTexturePort(binding.Value, texture); + } } var quality = Prop(field, "quality").Trim().ToLowerInvariant(); if (TryProgramValue($"unity.volume.quality.{quality}.keyword", out var keyword) && @@ -262,6 +269,7 @@ namespace GameCult.Eve.UnityScene.Fields if (_material != null) { Destroy(_material); _material = null; } _sourceShader = null; _programMetadata = null; + _assetTexturesByPort.Clear(); _activeNodeId = ""; PresentedFrameId = -1; CompositeCount = 0; @@ -276,6 +284,36 @@ namespace GameCult.Eve.UnityScene.Fields private bool HasTemporalProgram() => ProgramPass("temporal") >= 0; + private bool ApplyViewportTextureScaleBindings(EveUnityFieldVolumeProjection field, Camera camera) + { + foreach (var binding in ParseBindings(Prop(field, "viewportTextureScaleBindings"))) + { + if (string.IsNullOrWhiteSpace(ProgramPort("vector", binding.Key)) || + string.IsNullOrWhiteSpace(ProgramPort("texture", binding.Value)) || + !_assetTexturesByPort.TryGetValue(binding.Value, out var texture) || + !TryComputeViewportTextureScale(camera.pixelWidth, camera.pixelHeight, texture, out var scale)) + return false; + SetVectorPort(binding.Key, scale); + } + return true; + } + + public static bool TryComputeViewportTextureScale( + int viewportWidth, + int viewportHeight, + Texture? texture, + out Vector4 scale) + { + scale = default; + if (texture == null || texture.width <= 0 || texture.height <= 0) return false; + scale = new Vector4( + (float)Math.Max(1, viewportWidth) / texture.width, + (float)Math.Max(1, viewportHeight) / texture.height, + 0f, + 0f); + return true; + } + private static bool EnsureRenderTexture(ref RenderTexture? texture, int width, int height, string name) { if (texture != null && texture.width == width && texture.height == height) return false; diff --git a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveFieldsUnitySceneProjectionTests.cs b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveFieldsUnitySceneProjectionTests.cs index 25fd08b..cf10e64 100644 --- a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveFieldsUnitySceneProjectionTests.cs +++ b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveFieldsUnitySceneProjectionTests.cs @@ -73,6 +73,25 @@ namespace GameCult.Eve.UnityScene.Tests Assert.That(error, Does.Contain("history port")); } + [Test] + public void VolumeRendererDerivesViewportTextureScaleFromNativeDimensions() + { + var texture = new Texture2D(128, 64, TextureFormat.RGBA32, false); + try + { + Assert.That(EveUnityFieldsVolumeRenderer.TryComputeViewportTextureScale( + 640, + 360, + texture, + out var scale), Is.True); + Assert.That(scale, Is.EqualTo(new Vector4(5f, 5.625f, 0f, 0f))); + } + finally + { + UnityEngine.Object.DestroyImmediate(texture); + } + } + [Test] public void PackageContainsGenericFieldsShader() { diff --git a/packages/org.gamecult.eve.unity-scene/package.json b/packages/org.gamecult.eve.unity-scene/package.json index b2951a7..5e9720a 100644 --- a/packages/org.gamecult.eve.unity-scene/package.json +++ b/packages/org.gamecult.eve.unity-scene/package.json @@ -1,7 +1,7 @@ { "name": "org.gamecult.eve.unity-scene", "displayName": "Eve Unity Scene Lowering", - "version": "0.3.39", + "version": "0.3.40", "unity": "2021.3", "description": "Provider-agnostic Unity scene shell for gamecult.eve.surface.v1 interactive world surfaces.", "dependencies": {