From 7e2c2fa720ca3437aa1b569ffba3a57e6b6f05c5 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 15 Jul 2026 06:38:47 +0100 Subject: [PATCH] Make native environment leases scene-global --- .../Runtime/EveUnityPlayableWorldCameraRig.cs | 28 +++++++++++++- .../EveUnitySceneSurfaceLowererTests.cs | 37 ++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityPlayableWorldCameraRig.cs b/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityPlayableWorldCameraRig.cs index e82a86a..4ca6b15 100644 --- a/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityPlayableWorldCameraRig.cs +++ b/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityPlayableWorldCameraRig.cs @@ -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(); 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() diff --git a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs index a790608..41ab39c 100644 --- a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs +++ b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs @@ -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.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(); + secondaryHost.Configure(secondaryRootObject.transform, provider, provider, provider, provider, nativeAssets); + secondaryHost.Connect(); + secondaryHostObject.SetActive(true); + secondaryCameraObject.AddComponent(); + var secondaryRig = secondaryCameraObject.AddComponent(); + 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(); 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 _assets = new Dictionary(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