Make native environment leases scene-global

This commit is contained in:
Meta Vens 2026-07-15 06:38:47 +01:00
commit 7e2c2fa720
2 changed files with 61 additions and 4 deletions

View file

@ -12,6 +12,8 @@ namespace GameCult.Eve.UnityScene
public sealed class EveUnityPlayableWorldCameraRig : MonoBehaviour
{
private static EveUnityPlayableWorldCameraRig? _environmentOwner;
[SerializeField] private EveUnityPlayableWorldClientHost? host;
[SerializeField] private Transform? cameraTransform;
[SerializeField] private bool driveInLateUpdate = true;
@ -113,6 +115,12 @@ namespace GameCult.Eve.UnityScene
return false;
}
_cameraHost = resolvedHost;
if (_environmentOwner != null && !ReferenceEquals(_environmentOwner, this))
{
ReleaseCamera();
return false;
}
_environmentOwner = this;
var cameraComponent = camera.GetComponent<Camera>();
ApplyAmbientEnvironment(activeWorld, cameraComponent, skybox, reflection);
@ -205,13 +213,19 @@ namespace GameCult.Eve.UnityScene
{
skybox = null;
reflection = null;
if (!IsFinite(world.AmbientLightR) ||
!IsFinite(world.AmbientLightG) ||
!IsFinite(world.AmbientLightB) ||
!IsFinite(world.AmbientLightIntensity) ||
!IsFinite(world.ReflectionIntensity))
return false;
var assets = host.NativeAssetProvider;
if (!string.IsNullOrWhiteSpace(world.SkyboxAssetRef))
{
skybox = assets?.ResolveAsset(
new EveUnityPlayableWorldAssetBinding(
world.SkyboxAssetRef,
"environment.skybox",
"",
"provider-asset-ref"),
typeof(Material)) as Material;
if (skybox == null || skybox.shader == null || !skybox.shader.isSupported)
@ -222,7 +236,7 @@ namespace GameCult.Eve.UnityScene
reflection = assets?.ResolveAsset(
new EveUnityPlayableWorldAssetBinding(
world.ReflectionAssetRef,
"environment.reflection",
"",
"provider-asset-ref"),
typeof(Cubemap)) as Cubemap;
if (reflection == null)
@ -231,12 +245,15 @@ namespace GameCult.Eve.UnityScene
return true;
}
private static bool IsFinite(float value) => !float.IsNaN(value) && !float.IsInfinity(value);
private void ApplyAmbientEnvironment(
EveUnityPlayableWorldProjection world,
Camera? camera,
Material? skybox,
Cubemap? reflection)
{
var environmentChanged = !ReferenceEquals(RenderSettings.skybox, skybox);
if (!_ownsAmbientEnvironment)
{
_previousAmbientMode = RenderSettings.ambientMode;
@ -284,11 +301,14 @@ namespace GameCult.Eve.UnityScene
RenderSettings.customReflectionTexture = _previousCustomReflection;
RenderSettings.reflectionIntensity = _previousReflectionIntensity;
}
if (environmentChanged)
DynamicGI.UpdateEnvironment();
}
private void RestoreAmbientEnvironment()
{
if (!_ownsAmbientEnvironment) return;
var environmentChanged = !ReferenceEquals(RenderSettings.skybox, _previousSkybox);
RenderSettings.ambientMode = _previousAmbientMode;
RenderSettings.ambientLight = _previousAmbientLight;
RenderSettings.ambientIntensity = _previousAmbientIntensity;
@ -302,6 +322,10 @@ namespace GameCult.Eve.UnityScene
_previousSkybox = null;
_previousCustomReflection = null;
_ownsAmbientEnvironment = false;
if (ReferenceEquals(_environmentOwner, this))
_environmentOwner = null;
if (environmentChanged)
DynamicGI.UpdateEnvironment();
}
private void ReleaseCamera()

View file

@ -1080,7 +1080,11 @@ namespace GameCult.Eve.UnityScene.Tests
var hostObject = new GameObject("generic-eve-client");
var cameraObject = new GameObject("generic-eve-camera");
var competingCameraObject = new GameObject("competing-eve-camera");
var secondaryRootObject = new GameObject("secondary-eve-world-root");
var secondaryHostObject = new GameObject("secondary-eve-client");
var secondaryCameraObject = new GameObject("secondary-eve-camera");
hostObject.SetActive(false);
secondaryHostObject.SetActive(false);
var ambientMode = RenderSettings.ambientMode;
var ambientLight = RenderSettings.ambientLight;
var ambientIntensity = RenderSettings.ambientIntensity;
@ -1124,6 +1128,7 @@ namespace GameCult.Eve.UnityScene.Tests
rig.CameraTransform = cameraObject.transform;
rig.RenderPolicySource = new FixedRenderChannelPolicy("map", 14);
var camera = cameraObject.AddComponent<Camera>();
camera.clearFlags = CameraClearFlags.SolidColor;
camera.aspect = 16f / 9f;
camera.orthographic = true;
camera.lensShift = new Vector2(0.25f, -0.2f);
@ -1154,6 +1159,20 @@ namespace GameCult.Eve.UnityScene.Tests
Assert.That(RenderSettings.customReflectionTexture, Is.SameAs(reflectionCubemap));
Assert.That(RenderSettings.reflectionIntensity, Is.EqualTo(1f).Within(0.001f));
Assert.That(camera.clearFlags, Is.EqualTo(CameraClearFlags.Skybox));
Assert.That(nativeAssets.LastBinding?.AssetRef, Is.EqualTo("texture.environment.reflection"));
Assert.That(nativeAssets.LastBinding?.EntityKind, Is.Empty);
var secondaryHost = secondaryHostObject.AddComponent<EveUnityPlayableWorldClientHost>();
secondaryHost.Configure(secondaryRootObject.transform, provider, provider, provider, provider, nativeAssets);
secondaryHost.Connect();
secondaryHostObject.SetActive(true);
secondaryCameraObject.AddComponent<Camera>();
var secondaryRig = secondaryCameraObject.AddComponent<EveUnityPlayableWorldCameraRig>();
secondaryRig.Host = secondaryHost;
secondaryRig.CameraTransform = secondaryCameraObject.transform;
Assert.That(secondaryRig.ApplyRig(0f), Is.False);
Assert.That(secondaryHost.ActiveCameraTransform, Is.Null);
Assert.That(RenderSettings.skybox, Is.SameAs(skyboxMaterial));
var competingRig = competingCameraObject.AddComponent<EveUnityPlayableWorldCameraRig>();
competingRig.Host = host;
@ -1200,10 +1219,16 @@ namespace GameCult.Eve.UnityScene.Tests
Assert.That(RenderSettings.defaultReflectionMode, Is.EqualTo(previousReflectionMode));
Assert.That(RenderSettings.customReflectionTexture, Is.SameAs(previousCustomReflection));
Assert.That(RenderSettings.reflectionIntensity, Is.EqualTo(previousReflectionIntensity));
Assert.That(camera.clearFlags, Is.EqualTo(CameraClearFlags.SolidColor));
rig.CameraTransform = cameraObject.transform;
nativeAssets.Clear();
nativeAssets.Set("some.other.asset", skyboxMaterial);
Assert.That(rig.ApplyRig(0f), Is.False);
Assert.That(host.ActiveCameraTransform, Is.Null);
nativeAssets.Set("material.environment.skybox", reflectionCubemap);
nativeAssets.Set("texture.environment.reflection", skyboxMaterial);
Assert.That(rig.ApplyRig(0f), Is.False);
Assert.That(host.ActiveCameraTransform, Is.Null);
}
finally
{
@ -1216,6 +1241,9 @@ namespace GameCult.Eve.UnityScene.Tests
RenderSettings.reflectionIntensity = previousReflectionIntensity;
if (skyboxMaterial != null) UnityEngine.Object.DestroyImmediate(skyboxMaterial);
if (reflectionCubemap != null) UnityEngine.Object.DestroyImmediate(reflectionCubemap);
UnityEngine.Object.DestroyImmediate(secondaryCameraObject);
UnityEngine.Object.DestroyImmediate(secondaryHostObject);
UnityEngine.Object.DestroyImmediate(secondaryRootObject);
UnityEngine.Object.DestroyImmediate(competingCameraObject);
UnityEngine.Object.DestroyImmediate(cameraObject);
UnityEngine.Object.DestroyImmediate(hostObject);
@ -2437,6 +2465,8 @@ namespace GameCult.Eve.UnityScene.Tests
private readonly Dictionary<string, UnityEngine.Object> _assets =
new Dictionary<string, UnityEngine.Object>(StringComparer.Ordinal);
public EveUnityPlayableWorldAssetBinding? LastBinding { get; private set; }
public void Set(string assetRef, UnityEngine.Object asset)
{
_assets[assetRef ?? ""] = asset;
@ -2446,10 +2476,13 @@ namespace GameCult.Eve.UnityScene.Tests
public GameObject? ResolvePrefab(EveUnityPlayableWorldAssetBinding asset) => null;
public UnityEngine.Object? ResolveAsset(EveUnityPlayableWorldAssetBinding asset, Type assetType) =>
_assets.TryGetValue(asset?.AssetRef ?? "", out var value) && assetType.IsInstanceOfType(value)
public UnityEngine.Object? ResolveAsset(EveUnityPlayableWorldAssetBinding asset, Type assetType)
{
LastBinding = asset;
return _assets.TryGetValue(asset?.AssetRef ?? "", out var value) && assetType.IsInstanceOfType(value)
? value
: null;
}
}
private sealed class ObservingGameObjectAssetProvider : IEveUnityGameObjectAssetProvider