Complete planar camera lifecycle and lens
This commit is contained in:
parent
ab51d72c5e
commit
e43f2d7455
4 changed files with 111 additions and 7 deletions
|
|
@ -20,6 +20,9 @@ namespace GameCult.Eve.UnityScene
|
|||
[SerializeField] private float yawDegrees = 35f;
|
||||
[SerializeField] private float followDamping = 12f;
|
||||
private IEveUnityCameraRenderPolicySource? _renderPolicySource;
|
||||
private bool _ownsAmbientEnvironment;
|
||||
private AmbientMode _previousAmbientMode;
|
||||
private Color _previousAmbientLight;
|
||||
|
||||
public EveUnityPlayableWorldClientHost? Host
|
||||
{
|
||||
|
|
@ -39,12 +42,27 @@ namespace GameCult.Eve.UnityScene
|
|||
set => _renderPolicySource = value;
|
||||
}
|
||||
|
||||
public void ReleaseRig() => RestoreAmbientEnvironment();
|
||||
|
||||
public bool ApplyRig(float deltaTime)
|
||||
{
|
||||
var resolvedHost = ResolveHost();
|
||||
var activeWorld = resolvedHost?.ActiveWorld;
|
||||
if (resolvedHost == null || activeWorld == null)
|
||||
{
|
||||
RestoreAmbientEnvironment();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (activeWorld.CameraRig == "planar.top-down-follow.v1" && !HasValidPlanarContract(activeWorld))
|
||||
{
|
||||
RestoreAmbientEnvironment();
|
||||
return false;
|
||||
}
|
||||
if (activeWorld.CameraRig == "planar.top-down-follow.v1")
|
||||
ApplyAmbientEnvironment(activeWorld);
|
||||
else
|
||||
RestoreAmbientEnvironment();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(activeWorld.CameraRig) &&
|
||||
activeWorld.CameraRig != "planar.top-down-follow.v1" &&
|
||||
|
|
@ -106,10 +124,16 @@ namespace GameCult.Eve.UnityScene
|
|||
Vector3 target,
|
||||
float deltaTime)
|
||||
{
|
||||
var resolvedDistance = Mathf.Max(0.1f, world.CameraDistance);
|
||||
var verticalFov = Mathf.Clamp(world.CameraVerticalFieldOfViewDegrees, 1f, 179f);
|
||||
var resolvedDistance = world.CameraDistance;
|
||||
var verticalFov = world.CameraVerticalFieldOfViewDegrees;
|
||||
if (cameraComponent != null)
|
||||
{
|
||||
cameraComponent.orthographic = false;
|
||||
cameraComponent.lensShift = Vector2.zero;
|
||||
cameraComponent.fieldOfView = verticalFov;
|
||||
cameraComponent.nearClipPlane = world.CameraNearClipPlane;
|
||||
cameraComponent.farClipPlane = world.CameraFarClipPlane;
|
||||
}
|
||||
var aspect = cameraComponent != null ? cameraComponent.aspect : 16f / 9f;
|
||||
var verticalHalfExtent = resolvedDistance * Mathf.Tan(verticalFov * 0.5f * Mathf.Deg2Rad);
|
||||
var horizontalHalfExtent = verticalHalfExtent * Mathf.Max(0.01f, aspect);
|
||||
|
|
@ -125,13 +149,41 @@ namespace GameCult.Eve.UnityScene
|
|||
camera.position = Vector3.Lerp(camera.position, desiredPosition, t);
|
||||
camera.rotation = Quaternion.LookRotation(Vector3.down, Vector3.forward);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool HasValidPlanarContract(EveUnityPlayableWorldProjection world) =>
|
||||
world.CameraDistance > 0f &&
|
||||
world.CameraVerticalFieldOfViewDegrees > 0f &&
|
||||
world.CameraVerticalFieldOfViewDegrees < 180f &&
|
||||
world.CameraTargetScreenX >= 0f && world.CameraTargetScreenX <= 1f &&
|
||||
world.CameraTargetScreenY >= 0f && world.CameraTargetScreenY <= 1f &&
|
||||
world.CameraPositionDamping >= 0f &&
|
||||
world.CameraNearClipPlane > 0f &&
|
||||
world.CameraFarClipPlane > world.CameraNearClipPlane;
|
||||
|
||||
private void ApplyAmbientEnvironment(EveUnityPlayableWorldProjection world)
|
||||
{
|
||||
if (!_ownsAmbientEnvironment)
|
||||
{
|
||||
_previousAmbientMode = RenderSettings.ambientMode;
|
||||
_previousAmbientLight = RenderSettings.ambientLight;
|
||||
_ownsAmbientEnvironment = true;
|
||||
}
|
||||
RenderSettings.ambientMode = AmbientMode.Flat;
|
||||
RenderSettings.ambientLight = new Color(
|
||||
Mathf.Max(0f, world.AmbientLightR * world.AmbientLightIntensity),
|
||||
Mathf.Max(0f, world.AmbientLightG * world.AmbientLightIntensity),
|
||||
Mathf.Max(0f, world.AmbientLightB * world.AmbientLightIntensity),
|
||||
1f);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RestoreAmbientEnvironment()
|
||||
{
|
||||
if (!_ownsAmbientEnvironment) return;
|
||||
RenderSettings.ambientMode = _previousAmbientMode;
|
||||
RenderSettings.ambientLight = _previousAmbientLight;
|
||||
_ownsAmbientEnvironment = false;
|
||||
}
|
||||
|
||||
private static Bounds? CalculateVisualBounds(Transform player, int cullingMask)
|
||||
|
|
@ -168,6 +220,10 @@ namespace GameCult.Eve.UnityScene
|
|||
ApplyRig(Time.deltaTime);
|
||||
}
|
||||
|
||||
private void OnDisable() => ReleaseRig();
|
||||
|
||||
private void OnDestroy() => ReleaseRig();
|
||||
|
||||
private EveUnityPlayableWorldClientHost? ResolveHost()
|
||||
{
|
||||
if (host != null)
|
||||
|
|
|
|||
|
|
@ -134,7 +134,9 @@ namespace GameCult.Eve.UnityScene
|
|||
ParseFloat(worldRoot.GetProp("cameraTargetScreenY"), 0.5f),
|
||||
ParseFloat(worldRoot.GetProp("cameraPositionDamping"), 0f),
|
||||
ParseVector3(worldRoot.GetProp("ambientLightColor")),
|
||||
ParseFloat(worldRoot.GetProp("ambientLightIntensity"), 1f));
|
||||
ParseFloat(worldRoot.GetProp("ambientLightIntensity"), 1f),
|
||||
ParseFloat(worldRoot.GetProp("cameraNearClipPlane"), 0f),
|
||||
ParseFloat(worldRoot.GetProp("cameraFarClipPlane"), 0f));
|
||||
}
|
||||
|
||||
private static EveUnityPlayableWorldEntity BuildPlayableEntity(EveSurfaceComponent component)
|
||||
|
|
@ -353,7 +355,9 @@ namespace GameCult.Eve.UnityScene
|
|||
float cameraTargetScreenY = 0.5f,
|
||||
float cameraPositionDamping = 0f,
|
||||
(float r, float g, float b) ambientLightColor = default,
|
||||
float ambientLightIntensity = 1f)
|
||||
float ambientLightIntensity = 1f,
|
||||
float cameraNearClipPlane = 0f,
|
||||
float cameraFarClipPlane = 0f)
|
||||
{
|
||||
WorldRootId = worldRootId ?? "";
|
||||
StatePointerId = statePointerId ?? "";
|
||||
|
|
@ -382,6 +386,8 @@ namespace GameCult.Eve.UnityScene
|
|||
AmbientLightG = ambientLightColor.g;
|
||||
AmbientLightB = ambientLightColor.b;
|
||||
AmbientLightIntensity = ambientLightIntensity;
|
||||
CameraNearClipPlane = cameraNearClipPlane;
|
||||
CameraFarClipPlane = cameraFarClipPlane;
|
||||
}
|
||||
|
||||
public string WorldRootId { get; }
|
||||
|
|
@ -414,6 +420,10 @@ namespace GameCult.Eve.UnityScene
|
|||
|
||||
public float CameraPositionDamping { get; }
|
||||
|
||||
public float CameraNearClipPlane { get; }
|
||||
|
||||
public float CameraFarClipPlane { get; }
|
||||
|
||||
public float AmbientLightR { get; }
|
||||
|
||||
public float AmbientLightG { get; }
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
|
||||
Assert.That(projection.Root.Children[0].SceneObjectKind, Is.EqualTo("playable-world-root"));
|
||||
Assert.That(projection.Root.Children[0].Children[0].SceneObjectKind, Is.EqualTo("playable-world-entity"));
|
||||
Assert.That(projection.Root.Children[0].Children[3].SceneObjectKind, Is.EqualTo("world-field-3d"));
|
||||
Assert.That(projection.Root.Children[0].Children.Any(child => child.SceneObjectKind == "world-field-3d"), Is.True);
|
||||
Assert.That(projection.PlayableWorld, Is.Not.Null);
|
||||
Assert.That(projection.PlayableWorld!.WorldRootId, Is.EqualTo("aetheria.daemon.game.playable"));
|
||||
Assert.That(projection.PlayableWorld.StatePointerId, Is.EqualTo("cultmesh://aetheria/run/current"));
|
||||
|
|
@ -405,6 +405,8 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
Assert.That(projection.PlayableWorld.CameraTargetScreenX, Is.EqualTo(0.9f));
|
||||
Assert.That(projection.PlayableWorld.CameraTargetScreenY, Is.EqualTo(0.55f));
|
||||
Assert.That(projection.PlayableWorld.CameraPositionDamping, Is.EqualTo(5f));
|
||||
Assert.That(projection.PlayableWorld.CameraNearClipPlane, Is.EqualTo(0.3f));
|
||||
Assert.That(projection.PlayableWorld.CameraFarClipPlane, Is.EqualTo(4096f));
|
||||
Assert.That(projection.PlayableWorld.AmbientLightR, Is.EqualTo(0.2f));
|
||||
Assert.That(projection.PlayableWorld.AmbientLightG, Is.EqualTo(0.2f));
|
||||
Assert.That(projection.PlayableWorld.AmbientLightB, Is.EqualTo(0.2f));
|
||||
|
|
@ -1057,6 +1059,7 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
var host = hostObject.AddComponent<EveUnityPlayableWorldClientHost>();
|
||||
host.Configure(rootObject.transform, provider, provider, provider, provider);
|
||||
host.Connect();
|
||||
hostObject.SetActive(true);
|
||||
|
||||
var rig = hostObject.AddComponent<EveUnityPlayableWorldCameraRig>();
|
||||
rig.Host = host;
|
||||
|
|
@ -1064,6 +1067,8 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
rig.RenderPolicySource = new FixedRenderChannelPolicy("map", 14);
|
||||
var camera = cameraObject.AddComponent<Camera>();
|
||||
camera.aspect = 16f / 9f;
|
||||
camera.orthographic = true;
|
||||
camera.lensShift = new Vector2(0.25f, -0.2f);
|
||||
|
||||
var player = rootObject.GetComponentInChildren<EveUnityPlayableWorldEntityMarker>();
|
||||
Assert.That(player, Is.Not.Null);
|
||||
|
|
@ -1075,6 +1080,10 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
Assert.That(rig.ApplyRig(0f), Is.True);
|
||||
Assert.That(camera.cullingMask & (1 << 14), Is.Zero);
|
||||
Assert.That(camera.fieldOfView, Is.EqualTo(60f).Within(0.001f));
|
||||
Assert.That(camera.orthographic, Is.False);
|
||||
Assert.That(camera.lensShift, Is.EqualTo(Vector2.zero));
|
||||
Assert.That(camera.nearClipPlane, Is.EqualTo(0.3f).Within(0.001f));
|
||||
Assert.That(camera.farClipPlane, Is.EqualTo(4096f).Within(0.001f));
|
||||
Assert.That(cameraObject.transform.position.y - player.transform.position.y, Is.EqualTo(150f).Within(0.001f));
|
||||
var viewport = camera.WorldToViewportPoint(player.transform.position);
|
||||
Assert.That(viewport.x, Is.EqualTo(0.9f).Within(0.001f));
|
||||
|
|
@ -1082,12 +1091,27 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
Assert.That(RenderSettings.ambientMode, Is.EqualTo(UnityEngine.Rendering.AmbientMode.Flat));
|
||||
Assert.That(RenderSettings.ambientLight.r, Is.EqualTo(0.2f).Within(0.001f));
|
||||
|
||||
var selectedTarget = rootObject.GetComponentsInChildren<EveUnityPlayableWorldEntityMarker>()
|
||||
.Single(marker => marker.EntityId == "raider-scout");
|
||||
Assert.That(EveUnityCombatPresentation.Find(host.ActiveProjection)?.SelectedTargetEntityId,
|
||||
Is.EqualTo("raider-scout"));
|
||||
var poseWithoutSelectedTarget = cameraObject.transform.position;
|
||||
selectedTarget.transform.position = new Vector3(10000f, 0f, -10000f);
|
||||
Assert.That(rig.ApplyRig(0f), Is.True);
|
||||
Assert.That(cameraObject.transform.position, Is.EqualTo(poseWithoutSelectedTarget));
|
||||
viewport = camera.WorldToViewportPoint(player.transform.position);
|
||||
Assert.That(viewport.x, Is.EqualTo(0.9f).Within(0.001f));
|
||||
Assert.That(viewport.y, Is.EqualTo(0.55f).Within(0.001f));
|
||||
|
||||
var beforeFollow = cameraObject.transform.position;
|
||||
player.transform.position += Vector3.right * 10f;
|
||||
Assert.That(rig.ApplyRig(0.1f), Is.True);
|
||||
Assert.That(
|
||||
cameraObject.transform.position.x - beforeFollow.x,
|
||||
Is.EqualTo(10f * (1f - Mathf.Exp(-0.5f))).Within(0.001f));
|
||||
rig.ReleaseRig();
|
||||
Assert.That(RenderSettings.ambientMode, Is.EqualTo(ambientMode));
|
||||
Assert.That(RenderSettings.ambientLight, Is.EqualTo(ambientLight));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -1664,6 +1688,18 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
"",
|
||||
""));
|
||||
|
||||
playableChildren.Add(new EveSurfaceComponent(
|
||||
"aetheria.daemon.game.combat",
|
||||
"combat.presentation",
|
||||
new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["controlledEntityId"] = "player-vanguard",
|
||||
["selectedTargetEntityId"] = "raider-scout",
|
||||
["targetVisible"] = "true",
|
||||
["targetHostile"] = "true"
|
||||
},
|
||||
Array.Empty<EveSurfaceComponent>()));
|
||||
|
||||
playableChildren.Add(new EveSurfaceComponent(
|
||||
"aetheria.daemon.game.flow",
|
||||
"field.vector3d",
|
||||
|
|
@ -1709,6 +1745,8 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
["cameraTargetScreenX"] = "0.9",
|
||||
["cameraTargetScreenY"] = "0.55",
|
||||
["cameraPositionDamping"] = "5",
|
||||
["cameraNearClipPlane"] = "0.3",
|
||||
["cameraFarClipPlane"] = "4096",
|
||||
["ambientLightColor"] = "0.2,0.2,0.2",
|
||||
["ambientLightIntensity"] = "1",
|
||||
["excludedRenderChannels"] = "map",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "org.gamecult.eve.unity-scene",
|
||||
"displayName": "Eve Unity Scene Lowering",
|
||||
"version": "0.3.22",
|
||||
"version": "0.3.23",
|
||||
"unity": "2021.3",
|
||||
"description": "Provider-agnostic Unity scene shell for gamecult.eve.surface.v1 interactive world surfaces.",
|
||||
"dependencies": {
|
||||
|
|
|
|||
Reference in a new issue