Migrate native flight environment contract
This commit is contained in:
parent
3d36fe5da4
commit
94d3a7ba84
16 changed files with 260 additions and 47 deletions
|
|
@ -222,13 +222,22 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
|
|||
var volume = Flatten(surface.Surface.Root).Single(node => node.Kind == "field.volume3d");
|
||||
Require(volume.Props.Values.All(value => value == null || !value.Contains("_Nebula", StringComparison.Ordinal)) &&
|
||||
volume.Props["layerBindings"].Contains("fog.surface_height=surfaceHeight", StringComparison.Ordinal) &&
|
||||
volume.Props["layerTargetDescriptors"].Contains("fog.surface_height=2,2,false,bilinear", StringComparison.Ordinal) &&
|
||||
volume.Props["layerTargetDescriptors"].Contains("fog.tint=0.5,0.5,true,trilinear", StringComparison.Ordinal) &&
|
||||
volume.Props["viewportTextureScaleBindings"] == "ditherCoordinates=dither" &&
|
||||
volume.Props["documentFloatBindings"] == "simulationTimeSeconds=flowScroll,0.025,0" &&
|
||||
!volume.Props.ContainsKey("vectorParameters"),
|
||||
"portable Eve volume surfaces must name logical ports rather than Unity shader properties");
|
||||
|
||||
var shader = AetheriaRuntimeAssets.ProjectManifest(null).Assets.Single(asset =>
|
||||
asset.Ref.AssetKey == "shader.environment.gravity-fog");
|
||||
var postProcess = AetheriaRuntimeAssets.ProjectManifest(null).Assets.Single(asset =>
|
||||
asset.Ref.AssetKey == "profile.environment.flight");
|
||||
Require(postProcess.Ref.Kind == AetheriaRuntimeAssetKinds.VolumeProfile &&
|
||||
postProcess.Ref.Metadata["presentationRole"] == "environment.post-process.flight",
|
||||
"provider asset catalog must carry the advertised flight post-process profile variant");
|
||||
Require(shader.Ref.Metadata["unity.volume.texturePort.surfaceHeight"] == "_NebulaSurfaceHeight" &&
|
||||
shader.Ref.Metadata["unity.volume.matrixPort.cameraToWorld"] == "_CamToWorld" &&
|
||||
shader.Ref.Metadata["unity.volume.pass.raymarch"] == "0" &&
|
||||
shader.Ref.Metadata["unity.volume.pass.temporal"] == "1" &&
|
||||
shader.Ref.Metadata["unity.volume.pass.composite"] == "2" &&
|
||||
|
|
@ -715,6 +724,7 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
|
|||
"undocked world must preserve the authored ARPG Third Person Rig rather than the distinct Docked Rig");
|
||||
Require(world.Props["skyboxAssetRef"] == "material.environment.skybox" &&
|
||||
world.Props["reflectionAssetRef"] == "texture.environment.reflection" &&
|
||||
world.Props["postProcessProfileAssetRef"] == "profile.environment.flight" &&
|
||||
world.Props["reflectionIntensity"] == "1" &&
|
||||
world.Props["ambientLightIntensity"] == "1.46" &&
|
||||
world.Props["keyLightDirection"] == "0.4,-1,0.25" &&
|
||||
|
|
@ -3169,7 +3179,10 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
|
|||
RequireEqual("effect.beam.tractor", tractorEffect.Ref.Metadata["presentationRole"],
|
||||
"provider manifest must bind the stripped fossil tractor prefab to the generic beam role");
|
||||
var thermalProfiles = assets.Where(asset =>
|
||||
string.Equals(asset.Ref.Kind, AetheriaRuntimeAssetKinds.VolumeProfile, StringComparison.Ordinal)).ToArray();
|
||||
string.Equals(asset.Ref.Kind, AetheriaRuntimeAssetKinds.VolumeProfile, StringComparison.Ordinal) &&
|
||||
asset.Ref.Metadata.TryGetValue("presentationRole", out var role) &&
|
||||
(role.StartsWith("post.thermal.", StringComparison.Ordinal) ||
|
||||
string.Equals(role, "post.death", StringComparison.Ordinal))).ToArray();
|
||||
RequireEqual(5, thermalProfiles.Length,
|
||||
"provider manifest must advertise every original thermal and death volume profile");
|
||||
Require(thermalProfiles.All(asset =>
|
||||
|
|
|
|||
|
|
@ -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.3\"",
|
||||
"\"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.44\""
|
||||
"\"org.gamecult.eve.unity-scene\": \"https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.49\""
|
||||
};
|
||||
|
||||
var missingManifestSymbols = requiredManifestSymbols
|
||||
|
|
@ -813,7 +813,7 @@ static void RequireSharedEvePackagesImportedFromEveRepo(string root)
|
|||
"cultlib-unity-v1.0.13",
|
||||
"eve-plugin-fields-unity-v0.2.3",
|
||||
"eveunity-surface-v0.2.2",
|
||||
"eveunity-scene-v0.3.44",
|
||||
"eveunity-scene-v0.3.49",
|
||||
"\"source\": \"git\""
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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.3",
|
||||
"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.44"
|
||||
"org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.49"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.44",
|
||||
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.49",
|
||||
"depth": 0,
|
||||
"source": "git",
|
||||
"dependencies": {
|
||||
|
|
@ -152,9 +152,10 @@
|
|||
"org.gamecult.eve.plugin-fields": "0.2.3",
|
||||
"org.gamecult.eve.surface": "0.2.2",
|
||||
"com.unity.mathematics": "1.3.2",
|
||||
"com.unity.render-pipelines.universal": "17.4.0",
|
||||
"com.unity.modules.particlesystem": "1.0.0"
|
||||
},
|
||||
"hash": "03ce78031f28d7bfae4dcdc477a7273c6ecae55b"
|
||||
"hash": "e6ba1a36f6f0bce8df2c9834c25a8a980f03413c"
|
||||
},
|
||||
"com.unity.modules.assetbundle": {
|
||||
"version": "1.0.0",
|
||||
|
|
|
|||
|
|
@ -3,13 +3,16 @@ using System.IO;
|
|||
using System.Text;
|
||||
using GameCult.Aetheria.State.Verse;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
[InitializeOnLoad]
|
||||
public static class AetheriaRenderProbe
|
||||
{
|
||||
private const string RelativeOutputDirectory = "Temp/AetheriaRenderProbe";
|
||||
private const string RelativeOutputDirectory = "Library/AetheriaRenderProbe";
|
||||
private const string ReferenceCaptureSessionKey = "AetheriaRenderProbe.CaptureArpgReference";
|
||||
private const string ReferenceCaptureDeadlineSessionKey = "AetheriaRenderProbe.CaptureArpgReference.Deadline";
|
||||
|
||||
private static string ProjectRoot =>
|
||||
Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
|
||||
|
|
@ -23,6 +26,7 @@ public static class AetheriaRenderProbe
|
|||
static AetheriaRenderProbe()
|
||||
{
|
||||
EditorApplication.update += Poll;
|
||||
EditorApplication.update += CaptureArpgWhenReady;
|
||||
}
|
||||
|
||||
private static void Poll()
|
||||
|
|
@ -72,6 +76,9 @@ public static class AetheriaRenderProbe
|
|||
if (camera != null)
|
||||
VolumeSampling.RenderForCamera(camera);
|
||||
|
||||
if (camera != null)
|
||||
DumpCamera(camera, summary);
|
||||
|
||||
DumpVolumeSamplers(summary);
|
||||
DumpVolumeCloudRenderers(camera, summary);
|
||||
DumpGlobalTexture(summary, "_NebulaSurfaceHeight", "nebula-surface-height.png");
|
||||
|
|
@ -80,14 +87,63 @@ public static class AetheriaRenderProbe
|
|||
DumpGlobalTexture(summary, "_NebulaTint", "nebula-tint.png");
|
||||
DumpGlobalTexture(summary, "_AetheriaGravityHeight", "gravity-height.png");
|
||||
|
||||
if (camera != null)
|
||||
DumpCamera(camera, summary);
|
||||
|
||||
File.WriteAllText(Path.Combine(OutputDirectory, "summary.txt"), summary.ToString());
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log($"Aetheria render probe wrote {Path.GetFullPath(OutputDirectory)}");
|
||||
}
|
||||
|
||||
public static void CaptureArpgReference()
|
||||
{
|
||||
EditorSceneManager.OpenScene("Assets/Scenes/ARPG.unity", OpenSceneMode.Single);
|
||||
AetheriaUrpBootstrap.EnsureUrpAssetsAndAssign();
|
||||
SessionState.SetBool(ReferenceCaptureSessionKey, true);
|
||||
SessionState.SetString(
|
||||
ReferenceCaptureDeadlineSessionKey,
|
||||
(EditorApplication.timeSinceStartup + 15.0).ToString("R", System.Globalization.CultureInfo.InvariantCulture));
|
||||
EditorApplication.EnterPlaymode();
|
||||
}
|
||||
|
||||
private static void CaptureArpgWhenReady()
|
||||
{
|
||||
if (!SessionState.GetBool(ReferenceCaptureSessionKey, false))
|
||||
return;
|
||||
if (!EditorApplication.isPlaying || EditorApplication.isCompiling)
|
||||
return;
|
||||
|
||||
var rendered = false;
|
||||
foreach (var renderer in Resources.FindObjectsOfTypeAll<VolumeCloudRenderer>())
|
||||
{
|
||||
if (renderer != null && renderer.gameObject.scene.IsValid() && renderer.DebugRenderCount > 0)
|
||||
{
|
||||
rendered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var deadlineText = SessionState.GetString(ReferenceCaptureDeadlineSessionKey, "0");
|
||||
double.TryParse(
|
||||
deadlineText,
|
||||
System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out var deadline);
|
||||
if (!rendered && EditorApplication.timeSinceStartup < deadline)
|
||||
return;
|
||||
|
||||
SessionState.SetBool(ReferenceCaptureSessionKey, false);
|
||||
try
|
||||
{
|
||||
Capture();
|
||||
EditorApplication.Exit(0);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
Directory.CreateDirectory(OutputDirectory);
|
||||
File.WriteAllText(Path.Combine(OutputDirectory, "error.txt"), error.ToString());
|
||||
Debug.LogException(error);
|
||||
EditorApplication.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static Camera ResolveCamera()
|
||||
{
|
||||
if (Camera.main != null)
|
||||
|
|
@ -155,15 +211,16 @@ public static class AetheriaRenderProbe
|
|||
|
||||
summary.AppendLine($"- {sampler.name} enabled={sampler.enabled} active={sampler.gameObject.activeInHierarchy}");
|
||||
summary.AppendLine($" GridCamera={NameOf(sampler.GridCamera)} GridMesh={NameOf(sampler.GridMesh)}");
|
||||
summary.AppendLine($" SceneSplatSource={NameOf(sampler.SceneSplatSource)} SplatLayerRenderer={NameOf(sampler.SplatLayerRenderer)}");
|
||||
summary.AppendLine($" SplatLayerRenderer={NameOf(sampler.SplatLayerRenderer)}");
|
||||
summary.AppendLine($" Surface={TextureFacts(sampler.NebulaSurfaceHeight)}");
|
||||
summary.AppendLine($" PatchHeight={TextureFacts(sampler.NebulaPatchHeight)}");
|
||||
summary.AppendLine($" Patch={TextureFacts(sampler.NebulaPatch)}");
|
||||
summary.AppendLine($" Tint={TextureFacts(sampler.NebulaTint)}");
|
||||
DumpSamplerEnvironment(sampler, summary);
|
||||
|
||||
if (sampler.SceneSplatSource != null)
|
||||
DumpSceneSplatDocument(sampler, summary);
|
||||
SaveTextureExr(sampler.NebulaSurfaceHeight, Path.Combine(OutputDirectory, "fossil-nebula-surface-height.exr"));
|
||||
SaveTextureExr(sampler.NebulaPatchHeight, Path.Combine(OutputDirectory, "fossil-nebula-patch-height.exr"));
|
||||
SaveTextureExr(sampler.NebulaPatch, Path.Combine(OutputDirectory, "fossil-nebula-patch.exr"));
|
||||
SaveTextureExr(sampler.NebulaTint, Path.Combine(OutputDirectory, "fossil-nebula-tint.exr"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -194,27 +251,6 @@ public static class AetheriaRenderProbe
|
|||
$" Grid Enabled={environment.Grid.Enabled} Offset={environment.Grid.Offset}");
|
||||
}
|
||||
|
||||
private static void DumpSceneSplatDocument(VolumeSampling sampler, StringBuilder summary)
|
||||
{
|
||||
var size = sampler.GridCamera != null
|
||||
? new Vector2(sampler.GridCamera.orthographicSize * 2f, sampler.GridCamera.orthographicSize * 2f)
|
||||
: new Vector2(1024f, 1024f);
|
||||
var centerTransform = sampler.GridCamera != null ? sampler.GridCamera.transform : sampler.transform;
|
||||
var center = centerTransform != null
|
||||
? new Vector2(centerTransform.position.x, centerTransform.position.z)
|
||||
: Vector2.zero;
|
||||
var half = size * 0.5f;
|
||||
var document = sampler.SceneSplatSource.BuildDocument(new AetheriaRuntimeViewportBounds
|
||||
{
|
||||
MinX = center.x - half.x,
|
||||
MinY = center.y - half.y,
|
||||
MaxX = center.x + half.x,
|
||||
MaxY = center.y + half.y
|
||||
});
|
||||
|
||||
summary.AppendLine($" SceneDocument layers={document.Layers?.Count ?? 0} splats={document.Splats?.Count ?? 0}");
|
||||
}
|
||||
|
||||
private static void DumpGlobalTexture(StringBuilder summary, string globalName, string fileName)
|
||||
{
|
||||
var texture = Shader.GetGlobalTexture(globalName);
|
||||
|
|
@ -263,6 +299,37 @@ public static class AetheriaRenderProbe
|
|||
}
|
||||
}
|
||||
|
||||
private static void SaveTextureExr(Texture texture, string path)
|
||||
{
|
||||
if (texture == null)
|
||||
return;
|
||||
var width = Mathf.Max(1, texture.width);
|
||||
var height = Mathf.Max(1, texture.height);
|
||||
var target = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGBFloat);
|
||||
var previous = RenderTexture.active;
|
||||
try
|
||||
{
|
||||
Graphics.Blit(texture, target);
|
||||
RenderTexture.active = target;
|
||||
var image = new Texture2D(width, height, TextureFormat.RGBAFloat, false, true);
|
||||
try
|
||||
{
|
||||
image.ReadPixels(new Rect(0, 0, width, height), 0, 0);
|
||||
image.Apply();
|
||||
File.WriteAllBytes(path, image.EncodeToEXR(Texture2D.EXRFlags.OutputAsFloat));
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(image);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
RenderTexture.active = previous;
|
||||
RenderTexture.ReleaseTemporary(target);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SaveRenderTexture(RenderTexture target, string path)
|
||||
{
|
||||
var previous = RenderTexture.active;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ namespace Aetheria.Editor
|
|||
private static void Build(BuildTarget target)
|
||||
{
|
||||
EveThermalProfileMigrator.EnsureGenerated();
|
||||
EveEnvironmentProfileMigrator.EnsureGenerated();
|
||||
var output = ResolveOutput(target);
|
||||
Directory.CreateDirectory(output);
|
||||
|
||||
|
|
@ -529,6 +530,9 @@ namespace Aetheria.Editor
|
|||
var reflection = LoadAuthoredAsset<Cubemap>(bundle, assetNames, "Assets/Textures/studio2.hdr");
|
||||
if (reflection == null)
|
||||
throw new InvalidOperationException("Aetheria Eve bundle has no provider reflection cubemap.");
|
||||
var postProcess = LoadAuthoredAsset<VolumeProfile>(bundle, assetNames, EveEnvironmentProfileMigrator.FlightProfilePath);
|
||||
if (postProcess == null)
|
||||
throw new InvalidOperationException("Aetheria Eve bundle has no provider flight post-process profile.");
|
||||
var gravityFog = LoadAuthoredAsset<Shader>(bundle, assetNames, "Assets/Shaders/Raymarching/CloudShader.shader");
|
||||
if (gravityFog == null || !gravityFog.isSupported)
|
||||
throw new InvalidOperationException("Aetheria Eve bundle has no supported gravity-fog volume shader.");
|
||||
|
|
|
|||
50
Assets/Editor/EveEnvironmentProfileMigrator.cs
Normal file
50
Assets/Editor/EveEnvironmentProfileMigrator.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace Aetheria.Editor
|
||||
{
|
||||
public static class EveEnvironmentProfileMigrator
|
||||
{
|
||||
public const string Root = "Assets/Generated/Eve/Environment";
|
||||
public const string FlightProfilePath = Root + "/Flight.asset";
|
||||
|
||||
public static void EnsureGenerated()
|
||||
{
|
||||
Directory.CreateDirectory(Root);
|
||||
AssetDatabase.DeleteAsset(FlightProfilePath);
|
||||
var profile = ScriptableObject.CreateInstance<VolumeProfile>();
|
||||
profile.name = "Aetheria Flight";
|
||||
AssetDatabase.CreateAsset(profile, FlightProfilePath);
|
||||
|
||||
var tonemapping = profile.Add<Tonemapping>(true);
|
||||
tonemapping.mode.Override(TonemappingMode.ACES);
|
||||
|
||||
var color = profile.Add<ColorAdjustments>(true);
|
||||
// The fossil histogram exposure clamps average luminance to 2^-3 and
|
||||
// uses a 0.5 middle-grey key. At that dark-scene bound the resulting
|
||||
// exposure is 4x, which is the fixed URP equivalent available here.
|
||||
color.postExposure.Override(2f);
|
||||
color.contrast.Override(15f);
|
||||
|
||||
var bloom = profile.Add<Bloom>(true);
|
||||
bloom.intensity.Override(3f);
|
||||
bloom.threshold.Override(1.5f);
|
||||
bloom.scatter.Override(1f);
|
||||
|
||||
var vignette = profile.Add<Vignette>(true);
|
||||
vignette.intensity.Override(0.3f);
|
||||
vignette.smoothness.Override(0.4f);
|
||||
|
||||
var grain = profile.Add<FilmGrain>(true);
|
||||
grain.type.Override(FilmGrainLookup.Thin1);
|
||||
grain.intensity.Override(0.1f);
|
||||
|
||||
EditorUtility.SetDirty(profile);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Editor/EveEnvironmentProfileMigrator.cs.meta
Normal file
2
Assets/Editor/EveEnvironmentProfileMigrator.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2fc598800cd0521408627e17b566c973
|
||||
|
|
@ -719,12 +719,17 @@ public class DaemonRuntimeDocumentTests
|
|||
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.matrixPort.cameraToWorld"] == "_CamToWorld" &&
|
||||
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));
|
||||
Assert.IsTrue(assetManifest.Assets.Any(asset =>
|
||||
asset.Ref.AssetKey == "profile.environment.flight" &&
|
||||
asset.Ref.Kind == AetheriaRuntimeAssetKinds.VolumeProfile &&
|
||||
asset.Ref.Metadata["presentationRole"] == "environment.post-process.flight"));
|
||||
var editorTuiSurface = result.EditorTuiSurface;
|
||||
Assert.AreEqual("aetheria.daemon", editorTuiSurface.ProviderId);
|
||||
Assert.AreEqual("editor.daemon", editorTuiSurface.ProviderKind);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ Shader "Aetheria/CloudShader"
|
|||
[HideInInspector] _NebulaPatch("NebulaPatch", 2D) = "black" {}
|
||||
[HideInInspector] _NebulaTint("NebulaTint", 2D) = "black" {}
|
||||
[HideInInspector] _CloudTex("CloudTex", 2D) = "black" {}
|
||||
[HideInInspector] _UndersampleCloudTex("UndersampleCloudTex", 2D) = "black" {}
|
||||
[HideInInspector] _CompositeOpacity("CompositeOpacity", Range(0, 1)) = 1
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +50,7 @@ Shader "Aetheria/CloudShader"
|
|||
sampler2D _DitheringTex;
|
||||
float4 _DitheringCoords;
|
||||
uniform float4x4 _CamInvProj;
|
||||
uniform float4x4 _CamToWorld;
|
||||
|
||||
float _ExtinctionCoefficient;
|
||||
|
||||
|
|
@ -164,7 +166,7 @@ Shader "Aetheria/CloudShader"
|
|||
float4 frag (Interpolator i) : SV_Target
|
||||
{
|
||||
float3 vspos = float3(i.vsray, 1.0);
|
||||
float4 worldPos = mul(unity_CameraToWorld,float4(vspos,1.0));
|
||||
float4 worldPos = mul(_CamToWorld,float4(vspos,1.0));
|
||||
worldPos /= worldPos.w;
|
||||
float2 screenUV = i.screenPos.xy / i.screenPos.w;
|
||||
float depthSample = tex2D(_CameraDepthTexture, screenUV).r;
|
||||
|
|
@ -205,6 +207,7 @@ Shader "Aetheria/CloudShader"
|
|||
float4 _UndersampleCloudTex_TexelSize;
|
||||
|
||||
float4x4 _PrevVP; //View projection matrix of last frame. Used to temporal reprojection.
|
||||
float4x4 _CamToWorld;
|
||||
float _ResetHistory;
|
||||
|
||||
//These values are needed for doing extra raymarch when out of bound.
|
||||
|
|
@ -279,7 +282,7 @@ Shader "Aetheria/CloudShader"
|
|||
|
||||
half outOfBound;
|
||||
float3 previousWorldPosition = mul(
|
||||
unity_CameraToWorld,
|
||||
_CamToWorld,
|
||||
float4(normalize(vspos) * distance, 1.0f)).xyz;
|
||||
float2 prevUV = PrevUV(float4(previousWorldPosition, 1.0f), outOfBound);
|
||||
float4 prevSample = tex2D(_MainTex, prevUV);
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@
|
|||
"org.gamecult.cultmath": "file:../../CultMath",
|
||||
"org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.13",
|
||||
"org.gamecult.eve.surface": "file:../../EveUnity/packages/org.gamecult.eve.surface",
|
||||
"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.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.3",
|
||||
"org.gamecult.eve.unity-scene": "file:../../EveUnity/packages/org.gamecult.eve.unity-scene",
|
||||
"org.gamecult.eve.unity-uitoolkit": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.0",
|
||||
"org.gamecult.eve.unity-uitoolkit": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.1",
|
||||
"com.unity.modules.accessibility": "1.0.0",
|
||||
"com.unity.modules.adaptiveperformance": "1.0.0",
|
||||
"com.unity.modules.ai": "1.0.0",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
var entries = new Dictionary<string, AetheriaRuntimeAssetManifestEntry>(StringComparer.Ordinal);
|
||||
Add(entries, EnvironmentSkyboxMaterial());
|
||||
Add(entries, EnvironmentReflectionCubemap());
|
||||
Add(entries, EnvironmentPostProcessProfile());
|
||||
Add(entries, EnvironmentGravityFogShader());
|
||||
Add(entries, EnvironmentDitherTexture());
|
||||
Add(entries, MapIcon("entity.player", "Player", "Sprites/Icons/Stroked/Ship"));
|
||||
|
|
@ -240,6 +241,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
["unity.volume.texturePort.cloud"] = "_CloudTex",
|
||||
["unity.volume.vectorPort.viewportTransform"] = "_GridTransform",
|
||||
["unity.volume.matrixPort.cameraInverseViewProjection"] = "_CamInvProj",
|
||||
["unity.volume.matrixPort.cameraToWorld"] = "_CamToWorld",
|
||||
["unity.volume.matrixPort.previousViewProjection"] = "_PrevVP",
|
||||
["unity.volume.vectorPort.cameraProjectionExtents"] = "_ProjectionExtents",
|
||||
["unity.volume.floatPort.raymarchOffset"] = "_RaymarchOffset",
|
||||
|
|
@ -259,6 +261,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
["unity.volume.floatPort.safetyDistance"] = "_SafetyDistance",
|
||||
["unity.volume.floatPort.flowScale"] = "_FlowScale",
|
||||
["unity.volume.floatPort.flowAmplitude"] = "_FlowAmplitude",
|
||||
["unity.volume.floatPort.flowScroll"] = "_FlowScroll",
|
||||
["unity.volume.floatPort.flowPeriod"] = "_FlowPeriod",
|
||||
["unity.volume.floatPort.flowSlopeAmplitude"] = "_FlowSlopeAmplitude",
|
||||
["unity.volume.floatPort.flowSwirlAmplitude"] = "_FlowSwirlAmplitude",
|
||||
|
|
@ -278,6 +281,28 @@ namespace GameCult.Aetheria.State.Verse
|
|||
};
|
||||
}
|
||||
|
||||
public static AetheriaRuntimeAssetManifestEntry EnvironmentPostProcessProfile()
|
||||
{
|
||||
return new AetheriaRuntimeAssetManifestEntry
|
||||
{
|
||||
Ref = new AetheriaRuntimeAssetRef
|
||||
{
|
||||
AssetKey = "profile.environment.flight",
|
||||
Kind = AetheriaRuntimeAssetKinds.VolumeProfile,
|
||||
Uri = CultMeshAssetUri("profile.environment.flight"),
|
||||
MimeType = "application/vnd.unity.volume-profile",
|
||||
Metadata = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["unityAssetPath"] = "Assets/Generated/Eve/Environment/Flight.asset",
|
||||
["presentationRole"] = "environment.post-process.flight",
|
||||
["sourceProfile"] = "Assets/Scenes/ARPG_Profiles/Postprocessing Profile.asset",
|
||||
["profileSemantics"] = "aces;bloom;fixed-dark-scene-exposure;contrast;vignette;grain"
|
||||
}
|
||||
},
|
||||
Tags = new[] { "presentation", "environment", "post-process", "flight" }
|
||||
};
|
||||
}
|
||||
|
||||
public static AetheriaRuntimeAssetManifestEntry EnvironmentDitherTexture()
|
||||
{
|
||||
return new AetheriaRuntimeAssetManifestEntry
|
||||
|
|
|
|||
|
|
@ -621,6 +621,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
["skyboxAssetRef"] = "material.environment.skybox",
|
||||
["reflectionAssetRef"] = "texture.environment.reflection",
|
||||
["reflectionIntensity"] = "1",
|
||||
["postProcessProfileAssetRef"] = "profile.environment.flight",
|
||||
["keyLightDirection"] = "0.4,-1,0.25",
|
||||
["keyLightColor"] = "1,0.95,0.9",
|
||||
["keyLightIntensity"] = "0.75",
|
||||
|
|
@ -698,8 +699,13 @@ namespace GameCult.Aetheria.State.Verse
|
|||
["textureHeight"] = "1024",
|
||||
["downsample"] = "0",
|
||||
["layerBindings"] = "fog.surface_height=surfaceHeight;fog.patch_height=patchHeight;fog.patch=patch;fog.tint=tint",
|
||||
["layerTargetDescriptors"] = "fog.surface_height=2,2,false,bilinear;" +
|
||||
"fog.patch_height=0.25,0.25,false,bilinear;" +
|
||||
"fog.patch=0.5,0.5,false,bilinear;" +
|
||||
"fog.tint=0.5,0.5,true,trilinear",
|
||||
["assetTextureBindings"] = "texture.environment.volume-dither=dither",
|
||||
["viewportTextureScaleBindings"] = "ditherCoordinates=dither",
|
||||
["documentFloatBindings"] = "simulationTimeSeconds=flowScroll,0.025,0",
|
||||
["floatParameters"] = "fillDensity=0.000000001;fillDistance=120;fillExponent=5;fillOffset=70;" +
|
||||
"patchDensity=0.35;floorOffset=-20;floorBlend=10;patchBlend=25;luminance=1;extinction=0.5;" +
|
||||
"tintLodExponent=-0.45;safetyDistance=30;flowScale=512;flowAmplitude=15;flowPeriod=8;" +
|
||||
|
|
|
|||
|
|
@ -324,13 +324,13 @@
|
|||
"dependencies": {}
|
||||
},
|
||||
"org.gamecult.eve.plugin-fields": {
|
||||
"version": "https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.2.0",
|
||||
"version": "https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.2.3",
|
||||
"depth": 0,
|
||||
"source": "git",
|
||||
"dependencies": {
|
||||
"org.gamecult.cultlib": "1.0.13"
|
||||
},
|
||||
"hash": "382a23d8f8a07e4b5eef5f81f84655861a858367"
|
||||
"hash": "c5a4a75c1b727499b16c2dae1895f29e2a9f72f0"
|
||||
},
|
||||
"org.gamecult.eve.surface": {
|
||||
"version": "file:../../EveUnity/packages/org.gamecult.eve.surface",
|
||||
|
|
@ -346,20 +346,21 @@
|
|||
"source": "local",
|
||||
"dependencies": {
|
||||
"org.gamecult.cultlib": "1.0.13",
|
||||
"org.gamecult.eve.plugin-fields": "0.2.0",
|
||||
"org.gamecult.eve.plugin-fields": "0.2.3",
|
||||
"org.gamecult.eve.surface": "0.2.2",
|
||||
"com.unity.mathematics": "1.3.2",
|
||||
"com.unity.render-pipelines.universal": "17.4.0",
|
||||
"com.unity.modules.particlesystem": "1.0.0"
|
||||
}
|
||||
},
|
||||
"org.gamecult.eve.unity-uitoolkit": {
|
||||
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.0",
|
||||
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.1",
|
||||
"depth": 0,
|
||||
"source": "git",
|
||||
"dependencies": {
|
||||
"org.gamecult.eve.surface": "0.1.0"
|
||||
},
|
||||
"hash": "e10821f1cc81f619479c6507438f6a097d1f0ce2"
|
||||
"hash": "4d0cbe0185bdc4fc65eb63503a7c5cb578539669"
|
||||
},
|
||||
"com.unity.modules.accessibility": {
|
||||
"version": "1.0.0",
|
||||
|
|
|
|||
|
|
@ -884,6 +884,27 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
- Cut line: the unconditional `planar.top-down-follow.v1` projection no longer
|
||||
collapses the fossil dock and flight cameras into one false top-down view.
|
||||
|
||||
### Flight Post-Processing Contract
|
||||
|
||||
- Owner: Aetheria selects the logical flight profile and owns its native asset
|
||||
variants. EveUnity owns only a reversible lowering lease for the active world
|
||||
camera.
|
||||
- Inputs: `postProcessProfileAssetRef` from the world surface and the matching
|
||||
provider-catalog variant. The Unity authoring build derives
|
||||
`profile.environment.flight` from the fossil ARPG global profile's ACES,
|
||||
bloom, histogram dark-scene exposure bound, contrast, vignette, and grain.
|
||||
- Outputs: the generic Unity lowerer resolves a `VolumeProfile`, enables URP
|
||||
post-processing on the leased camera, and installs one global runtime volume.
|
||||
- Derived state: the Unity `Volume`, additional-camera data, and enabled flag are
|
||||
client presentation state. They restore or disappear when the camera lease
|
||||
ends.
|
||||
- Forbidden writers: scene-local Aetheria scripts, provider-id checks, fog-color
|
||||
heuristics, and client-authored exposure values cannot choose flight
|
||||
presentation.
|
||||
- Cut line: the fossil scene's global `PostProcessVolume` is reference evidence,
|
||||
not a surviving runtime owner. Missing or incompatible advertised profiles
|
||||
fail closed instead of silently rendering a different look.
|
||||
|
||||
### Gravity Fog Presentation Contract
|
||||
|
||||
- Owner: Aetheria owns gravity/fog brush placement and values, authored volume
|
||||
|
|
@ -902,6 +923,15 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
raymarch targets, ping-pong history textures, previous camera matrices, and
|
||||
viewport-derived texture scales and composite counters are runtime projections.
|
||||
They are neither daemon truth nor portable Eve state.
|
||||
- Native target topology: the provider advertises the fossil layer shapes rather
|
||||
than letting the lowerer invent one common texture: surface height is
|
||||
`2048x2048`, patch height is `256x256`, patch and tint are `512x512`, and only
|
||||
tint is mipmapped/trilinear. EveUnity applies those descriptors without owning
|
||||
the layer meanings.
|
||||
- Time and camera bindings: the splat document's daemon-authored simulation time
|
||||
drives logical `flowScroll` through an advertised scale. The provider variant
|
||||
maps logical camera-to-world and flow-scroll ports to native uniforms; the
|
||||
generic runtime does not use local gameplay time or provider identifiers.
|
||||
- Semantic split: `gravity.height` is the negative gameplay terrain projection;
|
||||
`fog.surface_height` is the positive visual displacement the fossil
|
||||
`PowerBrush` camera supplied to `_NebulaSurfaceHeight`. Both derive from the
|
||||
|
|
@ -949,8 +979,14 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
complete temporal-program acceptance, partial-program rejection, portable
|
||||
`PowerPulse` GPU evaluation, and absence of Aetheria assembly references.
|
||||
Aetheria daemon smoke proves radial brush parity and that the exact fossil
|
||||
shader ABI is advertised outside the portable surface. A live capture is still
|
||||
required for visual parity.
|
||||
shader ABI is advertised outside the portable surface. Released package
|
||||
`0.3.49` proves target topology, authoritative flow time, explicit camera
|
||||
transforms, float32 packed-output diagnostics, and both camera-channel masks.
|
||||
The live warm capture remains visually failing: the fossil vertical-noise
|
||||
displacement drives raymarch density to `1` within roughly three metres.
|
||||
A controlled zero-amplitude run restores a fog horizon, isolating the open
|
||||
parity defect to the noise/daemon-height interaction rather than transport,
|
||||
pass ordering, camera pose, or missing field textures.
|
||||
|
||||
### Daemon Asset Content Delivery
|
||||
|
||||
|
|
|
|||
|
|
@ -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 | daemon dock state selects distinct contracts instead of collapsing both fossil cameras: undocked flight advertises the entity-forward `Third Person Rig` framing (distance 30, screen 0.64/0.81, FOV 60, lens 1-4096) and the shared `aim.presentation` convergence point as its look-at; EveUnity solves native pose while preserving both optical aim and advertised entity framing, without provider types; the retained dock projection remains separate | reproduce the dock camera's separate follow/look-at subjects, blend timeline, noise, and reconnect state |
|
||||
| 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 | daemon publishes canonical gravity/fog splat layers through `field.volume3d`; Eve Fields `0.2.3` owns portable `PowerPulse` and `AnimatedRadialCosine` semantics; Aetheria emits the fossil global animated simplex/cellular/ambient stack, body-owned radial waves, and separate negative gameplay `gravity.height` and positive visual `fog.surface_height`; daemon entities follow fossil terrain height plus hull grid offset after Ymir XZ motion; released EveUnity `0.3.44` lowers the field sources, temporal history, viewport-derived dither scale, and finite aim convergence without provider code; both camera-channel assertions pass and all field/HDR targets are populated, but the pilot capture is nearly black with sparse clipped geometry rather than the fossil blue fog sea | diagnose native composition and geometry visibility at the pilot camera, then restore tint/color, lighting, patch structure, and gravity-wave visual parity from fossil settings and measured native outputs; keep visual parity explicitly open |
|
||||
| Fields/fog/gravity | zone renderer and shaders | daemon/plugin semantics | Fields plugin documents | EveUnity fields lowerer | daemon publishes canonical gravity/fog splat layers through `field.volume3d`; Eve Fields `0.2.3` owns portable `PowerPulse` and `AnimatedRadialCosine` semantics; Aetheria emits the fossil global animated simplex/cellular/ambient stack, body-owned radial waves, and separate negative gameplay `gravity.height` and positive visual `fog.surface_height`; daemon entities follow fossil terrain height plus hull grid offset after Ymir XZ motion; released EveUnity `0.3.49` lowers the authored 2048/256/512 layer topology, provider shader ABI, pre-postprocess URP pass, authoritative flow time, explicit camera matrix, and float32 packed-output diagnostics without provider code; both camera-channel assertions pass. The warm capture remains an opaque brown slab because fossil vertical noise saturates ray density to `1`; a controlled zero-amplitude run restores the expected fog horizon and isolates the remaining mismatch to noise versus daemon-generated heights | establish a measured parity rule for the fossil noise displacement against daemon height gradients, then restore blue tint, patch structure, and gravity-wave visual parity; keep the zero-noise result diagnostic-only and visual parity explicitly open |
|
||||
| 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 |
|
||||
|
|
|
|||
Loading…
Reference in a new issue