From 1c8b21a71e4da914359f2851a9a771c552050f17 Mon Sep 17 00:00:00 2001 From: Pandelii Date: Thu, 10 Jun 2021 21:11:59 -0700 Subject: [PATCH 1/8] Clipping plane standalone component. Added a camera clipping plane adjustment component. --- .../Game/CameraControllerInspector.cs | 61 +++++ .../Game/CameraClipPlane.cs | 142 +++++++++++ .../Game/CameraClipPlane.cs.meta | 11 + .../Game/CameraController.cs | 227 ++++++++++++++++-- .../VPT/Table/TableAuthoring.cs | 22 +- 5 files changed, 445 insertions(+), 18 deletions(-) create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs.meta diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Game/CameraControllerInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Game/CameraControllerInspector.cs index be71cc191..37b92cff1 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Game/CameraControllerInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Game/CameraControllerInspector.cs @@ -134,6 +134,67 @@ public override void OnInspectorGUI() EditorGUILayout.Space(); EditorGUILayout.Separator(); EditorGUILayout.PropertyField(_cameraPresetsProp); + + EditorGUILayout.Space(); + EditorGUILayout.Separator(); + + //Editor Play Mode Camera Controls + EditorGUILayout.LabelField("Runtime Motion Controls", EditorStyles.boldLabel); + EditorGUILayout.Space(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Horizontal Speed", EditorStyles.boldLabel); + _cameraController.mouseSpeedH = EditorGUILayout.Slider("", _cameraController.mouseSpeedH, 0f, 5f); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Vertical Speed", EditorStyles.boldLabel); + _cameraController.mouseSpeedV = EditorGUILayout.Slider("", _cameraController.mouseSpeedV, 0f, 5f); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Translation Speed", EditorStyles.boldLabel); + _cameraController.mouseSpeedT = EditorGUILayout.Slider("", _cameraController.mouseSpeedT, 0f, 5f); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(); + EditorGUILayout.Separator(); + + /* + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Distance Change Multiplier", EditorStyles.boldLabel); + _cameraController.mouseSpeedD = EditorGUILayout.Slider("", _cameraController.mouseSpeedD, 0f, 2f); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("FOV Change Multiplier", EditorStyles.boldLabel); + _cameraController.mouseSpeedZ = EditorGUILayout.Slider("", _cameraController.mouseSpeedZ, 0f, 2f); + EditorGUILayout.EndHorizontal(); + */ + + EditorGUILayout.Space(); + EditorGUILayout.Separator(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Invert Horizontal Axis", EditorStyles.boldLabel); + _cameraController.invertX = EditorGUILayout.Toggle(_cameraController.invertX); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Invert Vertical Axis", EditorStyles.boldLabel); + _cameraController.invertY = EditorGUILayout.Toggle(_cameraController.invertY); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(); + EditorGUILayout.Separator(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Use Inertia", EditorStyles.boldLabel); + _cameraController.useInertia = EditorGUILayout.Toggle(_cameraController.useInertia); + EditorGUILayout.EndHorizontal(); + + + } private void ApplySetting() diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs new file mode 100644 index 000000000..455b018a1 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs @@ -0,0 +1,142 @@ +// Visual Pinball Engine +// Copyright (C) 2021 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using Unity.Mathematics; +using UnityEngine; + + +namespace VisualPinball.Unity +{ + [ExecuteAlways] + public class CameraClipPlane : MonoBehaviour + { + + [NonSerialized] + public Camera Camera; + + //Stores the table bounds. + private Bounds tableBounds; + private Vector3 previousCameraPosition = Vector3.zero; + + private void OnEnable() + { + SetClipPlanes(0.001f, 9f); + } + + private void Update() + { + if(!GetCameraComponent()) + { + return; + } + + if(previousCameraPosition != Camera.transform.position) + { + UpdateClipPlanes(); + previousCameraPosition = Camera.transform.position; + } + } + + /// + /// Updates the camera clipping planes based on the table bounds. + /// + public bool UpdateClipPlanes() + { + + //Early out if no table is selected. + if(!TableSelector.Instance.HasSelectedTable) + { + return false; + } + + //Early out if we don't have a camera to work on. + if(!GetCameraComponent()) + { + return false; + } + + //Get selected table reference. + var table = TableSelector.Instance.SelectedTable; + + if(Application.isPlaying) + { + tableBounds = table._tableBounds; //When playing at runtime, get the stored table bounds value instead of calculating. + } + else + { + tableBounds = table.GetTableBounds(); //When in editor, calculate the bounds in case things have changed. + } + + var trans = Camera.transform; + + var cameraPos = trans.position; // camera position. + var sphereExtent = tableBounds.extents.magnitude; //sphere radius of the bounds. + float cameraToCenter = Vector3.Distance(cameraPos, tableBounds.center); + var nearPlane = math.max(cameraPos.magnitude - sphereExtent, 0.001f); //Assign initial near plane used when camera is not in the sphere. + var farPlane = math.max(1f, nearPlane + sphereExtent * 2f); //initial far bounds + + if(cameraToCenter < sphereExtent) + { + nearPlane = 0.01f; //camera is in the bounds so drop the near plane to very low. + farPlane = math.max(0.01f, sphereExtent + Vector3.Distance(cameraPos, tableBounds.center)); //set far plane to delta between camera and furthest bound. + + } + + SetClipPlanes(nearPlane, farPlane); + + return true; + } + + /// + /// Gets the currently active or attached camera component. + /// + /// False when no camera can be found. + private bool GetCameraComponent() + { + Camera = GetComponent(); //Get the camera component we are attached to if present. + + if(Camera == null) + { + Camera = UnityEngine.Camera.current; //Get the current active camera if not on the camera component. + } + + if(Camera == null) + { + return false; + } + + return true; + } + + /// + /// Sets the camera clipping planes based on manually derived values. + /// + /// The near clip plane value + /// The far clip plane value + /// False when no camera could be found. + public bool SetClipPlanes(float near, float far) + { + if(Camera == null) return false; + Camera.nearClipPlane = math.max(0.001f, near); + Camera.farClipPlane = math.max(0.01f, far); + + return true; + } + + + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs.meta new file mode 100644 index 000000000..b37bbf699 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 869da57aeeeeca34f879e2e9be2e23bb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraController.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraController.cs index 7627e3895..3b882f843 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraController.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraController.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using Unity.Mathematics; using UnityEngine; +using UnityEngine.InputSystem; namespace VisualPinball.Unity { @@ -28,13 +29,38 @@ public class CameraController : MonoBehaviour public List cameraPresets; + + public float mouseSpeedH = 1f; //Horizontal Mouse Speed + public float mouseSpeedV = 1f; //Vertical Mouse Speed + public float mouseSpeedT =1f; //Translation Speed + //public float mouseSpeedD = 1f; //Distance Increment Speed. Disabled Temporarily + //public float mouseSpeedZ = 1f; //FOV Change Speed Disabled Temporarily + public bool useInertia = true; //Enables inertia on orientation change + public bool invertX = false; //Inverts the horizontal movement + public bool invertY = false; //Inverts the vertical movement + [NonSerialized] public Camera Camera; + + //Private variables used for motion functions. + private Vector3 tableCenter = Vector3.zero; //Stored table center for offset + private float hAccum = 0f; //Accumulated horizontal value + private float vAccum = 0f; //Accumulated vertical value + private Vector3 tAccum = Vector3.zero; //Translation accumulation + private float cDistance = 0f; //Current camera local Z distance + private float hInertial = 0f; //Horizontal inertia value + private float vInertial = 0f; //Vertical inertia value + private bool inertia = false; //Current inertia state + private Vector2 lastAngularVector; //Last vector for motion from camera angle shift. + private Vector2 prevLMBPosition = Vector2.zero; //Previous LMB Position + private Vector2 prevRMBPosition = Vector2.zero; //Previous RMB Position + private float cFOV; //Current FOV + private void OnEnable() { Camera = GetComponentInChildren(); - + // validate structure var trans = Camera.transform; var altitude = trans.transform.parent; @@ -51,19 +77,37 @@ private void OnEnable() if (offset == null) { Camera = null; } + + } + private void Start() + { + //Initialize values from active settings + hAccum = activeSetting.orbit; + vAccum = activeSetting.angle; + cDistance = activeSetting.distance; + cFOV = activeSetting.fov; + + } + + /// + /// Applies the current controller settings to the camera. + /// public void ApplySetting() { - if (!activeSetting) { + if(!activeSetting) + { return; } - if (!TableSelector.Instance.HasSelectedTable) { + if(!TableSelector.Instance.HasSelectedTable) + { return; } var table = TableSelector.Instance.SelectedTable; + var trans = Camera.transform; var altitude = trans.transform.parent; var azimuth = altitude.parent; @@ -72,21 +116,176 @@ public void ApplySetting() trans.localPosition = new Vector3(0, 0, -activeSetting.distance); altitude.localRotation = Quaternion.Euler(new Vector3(activeSetting.angle, 0, 0)); azimuth.localRotation = Quaternion.Euler(new Vector3(0, activeSetting.orbit, 0)); - offset.localPosition = table.GetTableCenter() + activeSetting.offset; + Camera.fieldOfView = activeSetting.fov; - var tb = table.GetTableBounds(); - var p = trans.position; - var nearPoint = tb.ClosestPoint(p); - var deltaN = Vector3.Magnitude(p - nearPoint); - var deltaF = math.max(Vector3.Distance(p, tb.max), Vector3.Distance(p, tb.min)); - //TODO: Replace this with proper frustum distances. - var nearPlane = math.max(0.001f, math.abs(deltaN * 0.9f)); - var farPlane = math.max(1f, math.abs(deltaF*1.1f)); + if(Application.isPlaying) + { + tableCenter = table._tableCenter; + } + else + { + tableCenter = table.GetTableCenter(); + } + + offset.localPosition = tableCenter + activeSetting.offset; - Camera.nearClipPlane = nearPlane; - Camera.farClipPlane = farPlane; } + + public void FixedUpdate() + { + if(!Application.isPlaying) + { + return; + } + + if(Mouse.current != null) + { + Debug.Log("I has mouse"); + + var trans = Camera.transform; + var altitude = trans.transform.parent; + var azimuth = altitude.parent; + var offset = azimuth.parent; + + + //LMB Initialization + if(Mouse.current.leftButton.wasPressedThisFrame) + { + prevLMBPosition = Mouse.current.position.ReadValueFromPreviousFrame(); + float deltaMagnitude = Vector2.Distance(Mouse.current.position.ReadValue(), prevLMBPosition); + + //Assume that a large magnitude means they clicked on another part of the screen and give it a default value. + if(deltaMagnitude > 10) + { + prevLMBPosition = Mouse.current.position.ReadValue(); + + } + + cDistance = Camera.transform.localPosition.z; + } + + //LMB Hold Behavior: Azimuth and Elevation adjustment. + if(Mouse.current.leftButton.isPressed) + { + var curPos = Mouse.current.position.ReadValue(); + float h = curPos.x - prevLMBPosition.x; + float v = curPos.y - prevLMBPosition.y; + + prevLMBPosition = curPos; + + if(invertX) h = -h; + if(invertY) v = -v; + + hAccum = math.lerp(hAccum, hAccum + h, Time.deltaTime * (3f * mouseSpeedH)); + vAccum = math.lerp(vAccum, vAccum - v, Time.deltaTime * (3f * mouseSpeedV)); + + azimuth.localRotation = Quaternion.Euler(new Vector3(0, hAccum, 0)); + altitude.localRotation = Quaternion.Euler(new Vector3(vAccum, 0, 0)); + + + lastAngularVector = Mouse.current.position.ReadValue() - Mouse.current.position.ReadValueFromPreviousFrame(); + lastAngularVector.Normalize(); + hInertial = lastAngularVector.x * mouseSpeedH; + vInertial = lastAngularVector.y * mouseSpeedV; + + } + + //Scroll Behavior: Distance to pivot adjustment + if(Mouse.current.scroll.IsActuated() && !Keyboard.current.ctrlKey.isPressed) + { + var scrollDist = Mouse.current.scroll.ReadValue(); + cDistance += 0.002f * scrollDist.y - Mouse.current.scroll.ReadValueFromPreviousFrame().y; + Camera.transform.localPosition = new Vector3(0, 0, math.clamp(cDistance, -8f, -0.1f)); + + } + + //LMB Release + if(Mouse.current.leftButton.wasReleasedThisFrame) + { + inertia = useInertia; + } + + + //RMB Initialization + if(Mouse.current.rightButton.wasPressedThisFrame) + { + //tAccum = Vector3.zero; // offset.localPosition; + prevRMBPosition = Mouse.current.position.ReadValue(); + + } + + //RMB Hold Behavior: Pivot translation adjustment + if(Mouse.current.rightButton.isPressed) + { + var curTPos = Mouse.current.position.ReadValue(); + float h = curTPos.x - prevRMBPosition.x; + float v = curTPos.y - prevRMBPosition.y; + + //Angle correction based on current azimuth + var theta = math.radians(-azimuth.localRotation.eulerAngles.y); + var cos = math.cos(theta); + var sin = math.sin(theta); + + if(invertX) h = -h; + if(invertY) v = -v; + + h = -h; + v = -v; + + var newh = h * cos - v * sin; + var newv = h * sin + v * cos; + + var posDelta = new Vector3(newh*0.03f, 0, newv*0.03f); + prevRMBPosition = curTPos; + + tAccum = Vector3.Lerp(tAccum, tAccum + posDelta, Time.deltaTime * (3f * mouseSpeedT)); + + + offset.localPosition = tableCenter + tAccum; + + } + + //FOV Control + if(Mouse.current.scroll.IsActuated() && Keyboard.current.ctrlKey.isPressed) + { + var scrollDist = Mouse.current.scroll.ReadValue(); + cFOV += 0.002f * scrollDist.y - Mouse.current.scroll.ReadValueFromPreviousFrame().y; + Camera.fieldOfView = cFOV; + + } + + //Inertia + if(inertia == true) + { + hInertial *= 0.8f; + vInertial *= 0.8f; + + hAccum += hInertial * mouseSpeedH; + vAccum += -vInertial * mouseSpeedV; + + azimuth.localRotation = Quaternion.Euler(new Vector3(0, hAccum, 0)); + altitude.localRotation = Quaternion.Euler(new Vector3(vAccum, 0, 0)); + + + if(hInertial <= 0.01f && vInertial <= 0.01f) + { + inertia = false; + } + } + } + + } + + public void AdjustCameraHorizontal(float amount) + { + activeSetting.orbit += amount; + ApplySetting(); + } + + + + } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableAuthoring.cs index 3818b23a1..87bbeb7b1 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableAuthoring.cs @@ -85,8 +85,20 @@ public class TableAuthoring : ItemMainRenderableAuthoring private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + //Private runtime values needed for camera adjustments. + [HideInInspector] [SerializeField] public Bounds _tableBounds; + [HideInInspector] [SerializeField] public Vector3 _tableCenter; + + public void Awake() + { + //Store table information + _tableBounds = GetTableBounds(); + _tableCenter = GetTableCenter(); + } + protected virtual void Start() { + if (EngineProvider.Exists) { EngineProvider.Get().Init(this); } @@ -266,13 +278,15 @@ public Vector3 GetTableCenter() public Bounds GetTableBounds() { + var tableBounds = new Bounds(); + var mrs = GetComponentsInChildren(); - foreach(var mr in mrs) { - tableBounds.Encapsulate(mr.bounds.max); - tableBounds.Encapsulate(mr.bounds.min); - tableBounds.Encapsulate(mr.bounds.center); + foreach(var mr in mrs) + { + tableBounds.Encapsulate(mr.bounds); } + return tableBounds; } From 0a2685fbfd90e52fffba93f535a0ec1748ea46f6 Mon Sep 17 00:00:00 2001 From: Pandelii Date: Fri, 11 Jun 2021 08:30:48 -0700 Subject: [PATCH 2/8] Update VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs Co-authored-by: freezy --- .../VisualPinball.Unity/Game/CameraClipPlane.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs index 455b018a1..7e672d6dd 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs @@ -130,7 +130,9 @@ private bool GetCameraComponent() /// False when no camera could be found. public bool SetClipPlanes(float near, float far) { - if(Camera == null) return false; + if (Camera == null) { + return false; + } Camera.nearClipPlane = math.max(0.001f, near); Camera.farClipPlane = math.max(0.01f, far); From 246fdf74959f8f7beae914274ca22e86b7231b2a Mon Sep 17 00:00:00 2001 From: Pandelii Date: Fri, 11 Jun 2021 08:34:18 -0700 Subject: [PATCH 3/8] Update VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs Co-authored-by: freezy --- .../VisualPinball.Unity/Game/CameraClipPlane.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs index 7e672d6dd..61c4ab865 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs @@ -114,12 +114,7 @@ private bool GetCameraComponent() Camera = UnityEngine.Camera.current; //Get the current active camera if not on the camera component. } - if(Camera == null) - { - return false; - } - - return true; + return Camera != null; } /// From 2198f432f42da278633ecb60333e2ad4e77c32a2 Mon Sep 17 00:00:00 2001 From: Pandelii Date: Fri, 11 Jun 2021 20:57:59 -0700 Subject: [PATCH 4/8] Clip plane component style adjustments. Adjusted the style and removed unused returns. --- .../Game/CameraClipPlane.cs | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs index 61c4ab865..2ee14e5ef 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs @@ -24,7 +24,7 @@ namespace VisualPinball.Unity [ExecuteAlways] public class CameraClipPlane : MonoBehaviour { - + [NonSerialized] public Camera Camera; @@ -34,43 +34,51 @@ public class CameraClipPlane : MonoBehaviour private void OnEnable() { - SetClipPlanes(0.001f, 9f); + SetClipPlanes(0.001f, 9f); + } + + private void Awake() + { + RetrieveCameraComponent(); } private void Update() { - if(!GetCameraComponent()) + if(Camera == null) { - return; + RetrieveCameraComponent(); } - - if(previousCameraPosition != Camera.transform.position) + + if(Camera != null) { - UpdateClipPlanes(); - previousCameraPosition = Camera.transform.position; + if(previousCameraPosition != Camera.transform.position) + { + UpdateClipPlanes(); + previousCameraPosition = Camera.transform.position; + } } } /// /// Updates the camera clipping planes based on the table bounds. /// - public bool UpdateClipPlanes() + public void UpdateClipPlanes() { //Early out if no table is selected. if(!TableSelector.Instance.HasSelectedTable) { - return false; + return; } //Early out if we don't have a camera to work on. - if(!GetCameraComponent()) + if(Camera == null) { - return false; + return; } //Get selected table reference. - var table = TableSelector.Instance.SelectedTable; + var table = TableSelector.Instance.SelectedTable; if(Application.isPlaying) { @@ -96,16 +104,14 @@ public bool UpdateClipPlanes() } - SetClipPlanes(nearPlane, farPlane); + SetClipPlanes(nearPlane, farPlane); - return true; } /// /// Gets the currently active or attached camera component. /// - /// False when no camera can be found. - private bool GetCameraComponent() + private void RetrieveCameraComponent() { Camera = GetComponent(); //Get the camera component we are attached to if present. @@ -114,24 +120,24 @@ private bool GetCameraComponent() Camera = UnityEngine.Camera.current; //Get the current active camera if not on the camera component. } - return Camera != null; } - + /// /// Sets the camera clipping planes based on manually derived values. /// /// The near clip plane value /// The far clip plane value /// False when no camera could be found. - public bool SetClipPlanes(float near, float far) - { - if (Camera == null) { - return false; + public void SetClipPlanes(float near, float far) + { + if(Camera == null) + { + return; } + Camera.nearClipPlane = math.max(0.001f, near); Camera.farClipPlane = math.max(0.01f, far); - return true; } From 5872f368e0e347c7fd5151f6aa07dc9f469a739a Mon Sep 17 00:00:00 2001 From: freezy Date: Tue, 15 Jun 2021 22:42:59 +0200 Subject: [PATCH 5/8] style: Formatting and naming. --- .../Game/CameraClipPlane.cs | 286 +++++++++--------- .../Game/CameraController.cs | 172 +++++------ 2 files changed, 224 insertions(+), 234 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs index 2ee14e5ef..f9a9eec46 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs @@ -1,145 +1,141 @@ -// Visual Pinball Engine -// Copyright (C) 2021 freezy and VPE Team -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using System; -using Unity.Mathematics; -using UnityEngine; - - -namespace VisualPinball.Unity -{ - [ExecuteAlways] - public class CameraClipPlane : MonoBehaviour - { - - [NonSerialized] - public Camera Camera; - - //Stores the table bounds. - private Bounds tableBounds; - private Vector3 previousCameraPosition = Vector3.zero; - - private void OnEnable() - { - SetClipPlanes(0.001f, 9f); - } - - private void Awake() - { - RetrieveCameraComponent(); - } - - private void Update() - { - if(Camera == null) - { - RetrieveCameraComponent(); - } - - if(Camera != null) - { - if(previousCameraPosition != Camera.transform.position) - { - UpdateClipPlanes(); - previousCameraPosition = Camera.transform.position; - } - } - } - - /// - /// Updates the camera clipping planes based on the table bounds. - /// - public void UpdateClipPlanes() - { - - //Early out if no table is selected. - if(!TableSelector.Instance.HasSelectedTable) - { - return; - } - - //Early out if we don't have a camera to work on. - if(Camera == null) - { - return; - } - - //Get selected table reference. - var table = TableSelector.Instance.SelectedTable; - - if(Application.isPlaying) - { - tableBounds = table._tableBounds; //When playing at runtime, get the stored table bounds value instead of calculating. - } - else - { - tableBounds = table.GetTableBounds(); //When in editor, calculate the bounds in case things have changed. - } - - var trans = Camera.transform; - - var cameraPos = trans.position; // camera position. - var sphereExtent = tableBounds.extents.magnitude; //sphere radius of the bounds. - float cameraToCenter = Vector3.Distance(cameraPos, tableBounds.center); - var nearPlane = math.max(cameraPos.magnitude - sphereExtent, 0.001f); //Assign initial near plane used when camera is not in the sphere. - var farPlane = math.max(1f, nearPlane + sphereExtent * 2f); //initial far bounds - - if(cameraToCenter < sphereExtent) - { - nearPlane = 0.01f; //camera is in the bounds so drop the near plane to very low. - farPlane = math.max(0.01f, sphereExtent + Vector3.Distance(cameraPos, tableBounds.center)); //set far plane to delta between camera and furthest bound. - - } - - SetClipPlanes(nearPlane, farPlane); - - } - - /// - /// Gets the currently active or attached camera component. - /// - private void RetrieveCameraComponent() - { - Camera = GetComponent(); //Get the camera component we are attached to if present. - - if(Camera == null) - { - Camera = UnityEngine.Camera.current; //Get the current active camera if not on the camera component. - } - - } - - /// - /// Sets the camera clipping planes based on manually derived values. - /// - /// The near clip plane value - /// The far clip plane value - /// False when no camera could be found. - public void SetClipPlanes(float near, float far) - { - if(Camera == null) - { - return; - } - - Camera.nearClipPlane = math.max(0.001f, near); - Camera.farClipPlane = math.max(0.01f, far); - - } - - - } -} +// Visual Pinball Engine +// Copyright (C) 2021 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using Unity.Mathematics; +using UnityEngine; + +namespace VisualPinball.Unity +{ + [ExecuteAlways] + public class CameraClipPlane : MonoBehaviour + { + + [NonSerialized] + private Camera _camera; + + //Stores the table bounds. + private Bounds _tableBounds; + private Vector3 _previousCameraPosition = Vector3.zero; + + private void OnEnable() + { + SetClipPlanes(0.001f, 9f); + } + + private void Awake() + { + RetrieveCameraComponent(); + } + + private void Update() + { + if(_camera == null) + { + RetrieveCameraComponent(); + } + + if(_camera != null) + { + if(_previousCameraPosition != _camera.transform.position) + { + UpdateClipPlanes(); + _previousCameraPosition = _camera.transform.position; + } + } + } + + /// + /// Updates the camera clipping planes based on the table bounds. + /// + private void UpdateClipPlanes() + { + + //Early out if no table is selected. + if(!TableSelector.Instance.HasSelectedTable) + { + return; + } + + //Early out if we don't have a camera to work on. + if(_camera == null) + { + return; + } + + //Get selected table reference. + var table = TableSelector.Instance.SelectedTable; + + if(Application.isPlaying) + { + _tableBounds = table._tableBounds; //When playing at runtime, get the stored table bounds value instead of calculating. + } + else + { + _tableBounds = table.GetTableBounds(); //When in editor, calculate the bounds in case things have changed. + } + + var trans = _camera.transform; + + var cameraPos = trans.position; // camera position. + var sphereExtent = _tableBounds.extents.magnitude; //sphere radius of the bounds. + float cameraToCenter = Vector3.Distance(cameraPos, _tableBounds.center); + var nearPlane = math.max(cameraPos.magnitude - sphereExtent, 0.001f); //Assign initial near plane used when camera is not in the sphere. + var farPlane = math.max(1f, nearPlane + sphereExtent * 2f); //initial far bounds + + if(cameraToCenter < sphereExtent) + { + nearPlane = 0.01f; //camera is in the bounds so drop the near plane to very low. + farPlane = math.max(0.01f, sphereExtent + Vector3.Distance(cameraPos, _tableBounds.center)); //set far plane to delta between camera and furthest bound. + + } + + SetClipPlanes(nearPlane, farPlane); + + } + + /// + /// Gets the currently active or attached camera component. + /// + private void RetrieveCameraComponent() + { + _camera = GetComponent(); //Get the camera component we are attached to if present. + + if(_camera == null) + { + _camera = UnityEngine.Camera.current; //Get the current active camera if not on the camera component. + } + + } + + /// + /// Sets the camera clipping planes based on manually derived values. + /// + /// The near clip plane value + /// The far clip plane value + /// False when no camera could be found. + private void SetClipPlanes(float near, float far) + { + if(_camera == null) + { + return; + } + + _camera.nearClipPlane = math.max(0.001f, near); + _camera.farClipPlane = math.max(0.01f, far); + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraController.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraController.cs index 3b882f843..8da299903 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraController.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraController.cs @@ -18,7 +18,7 @@ using System.Collections.Generic; using Unity.Mathematics; using UnityEngine; -using UnityEngine.InputSystem; +using UnityEngine.InputSystem; namespace VisualPinball.Unity { @@ -29,38 +29,37 @@ public class CameraController : MonoBehaviour public List cameraPresets; - - public float mouseSpeedH = 1f; //Horizontal Mouse Speed - public float mouseSpeedV = 1f; //Vertical Mouse Speed - public float mouseSpeedT =1f; //Translation Speed + public float mouseSpeedH = 1f; //Horizontal Mouse Speed + public float mouseSpeedV = 1f; //Vertical Mouse Speed + public float mouseSpeedT =1f; //Translation Speed //public float mouseSpeedD = 1f; //Distance Increment Speed. Disabled Temporarily - //public float mouseSpeedZ = 1f; //FOV Change Speed Disabled Temporarily + //public float mouseSpeedZ = 1f; //FOV Change Speed Disabled Temporarily public bool useInertia = true; //Enables inertia on orientation change - public bool invertX = false; //Inverts the horizontal movement + public bool invertX = false; //Inverts the horizontal movement public bool invertY = false; //Inverts the vertical movement [NonSerialized] public Camera Camera; - //Private variables used for motion functions. - private Vector3 tableCenter = Vector3.zero; //Stored table center for offset - private float hAccum = 0f; //Accumulated horizontal value - private float vAccum = 0f; //Accumulated vertical value - private Vector3 tAccum = Vector3.zero; //Translation accumulation - private float cDistance = 0f; //Current camera local Z distance - private float hInertial = 0f; //Horizontal inertia value - private float vInertial = 0f; //Vertical inertia value - private bool inertia = false; //Current inertia state - private Vector2 lastAngularVector; //Last vector for motion from camera angle shift. - private Vector2 prevLMBPosition = Vector2.zero; //Previous LMB Position - private Vector2 prevRMBPosition = Vector2.zero; //Previous RMB Position - private float cFOV; //Current FOV + //Private variables used for motion functions. + private Vector3 _tableCenter = Vector3.zero; //Stored table center for offset + private float _hAccum = 0f; //Accumulated horizontal value + private float _vAccum = 0f; //Accumulated vertical value + private Vector3 _tAccum = Vector3.zero; //Translation accumulation + private float _cDistance = 0f; //Current camera local Z distance + private float _hInertial = 0f; //Horizontal inertia value + private float _vInertial = 0f; //Vertical inertia value + private bool _inertia = false; //Current inertia state + private Vector2 _lastAngularVector; //Last vector for motion from camera angle shift. + private Vector2 _prevLmbPosition = Vector2.zero;//Previous LMB Position + private Vector2 _prevRmbPosition = Vector2.zero; //Previous RMB Position + private float _cFOV; //Current FOV private void OnEnable() { Camera = GetComponentInChildren(); - + // validate structure var trans = Camera.transform; var altitude = trans.transform.parent; @@ -77,22 +76,19 @@ private void OnEnable() if (offset == null) { Camera = null; } - - } private void Start() { //Initialize values from active settings - hAccum = activeSetting.orbit; - vAccum = activeSetting.angle; - cDistance = activeSetting.distance; - cFOV = activeSetting.fov; - + _hAccum = activeSetting.orbit; + _vAccum = activeSetting.angle; + _cDistance = activeSetting.distance; + _cFOV = activeSetting.fov; } /// - /// Applies the current controller settings to the camera. + /// Applies the current controller settings to the camera. /// public void ApplySetting() { @@ -119,25 +115,23 @@ public void ApplySetting() Camera.fieldOfView = activeSetting.fov; - if(Application.isPlaying) { - tableCenter = table._tableCenter; + _tableCenter = table._tableCenter; } else { - tableCenter = table.GetTableCenter(); + _tableCenter = table.GetTableCenter(); } - offset.localPosition = tableCenter + activeSetting.offset; - + offset.localPosition = _tableCenter + activeSetting.offset; } public void FixedUpdate() { if(!Application.isPlaying) { - return; + return; } if(Mouse.current != null) @@ -153,75 +147,75 @@ public void FixedUpdate() //LMB Initialization if(Mouse.current.leftButton.wasPressedThisFrame) { - prevLMBPosition = Mouse.current.position.ReadValueFromPreviousFrame(); - float deltaMagnitude = Vector2.Distance(Mouse.current.position.ReadValue(), prevLMBPosition); + _prevLmbPosition = Mouse.current.position.ReadValueFromPreviousFrame(); + float deltaMagnitude = Vector2.Distance(Mouse.current.position.ReadValue(), _prevLmbPosition); - //Assume that a large magnitude means they clicked on another part of the screen and give it a default value. + //Assume that a large magnitude means they clicked on another part of the screen and give it a default value. if(deltaMagnitude > 10) { - prevLMBPosition = Mouse.current.position.ReadValue(); + _prevLmbPosition = Mouse.current.position.ReadValue(); } - - cDistance = Camera.transform.localPosition.z; + + _cDistance = Camera.transform.localPosition.z; } - //LMB Hold Behavior: Azimuth and Elevation adjustment. + //LMB Hold Behavior: Azimuth and Elevation adjustment. if(Mouse.current.leftButton.isPressed) { var curPos = Mouse.current.position.ReadValue(); - float h = curPos.x - prevLMBPosition.x; - float v = curPos.y - prevLMBPosition.y; + float h = curPos.x - _prevLmbPosition.x; + float v = curPos.y - _prevLmbPosition.y; - prevLMBPosition = curPos; + _prevLmbPosition = curPos; if(invertX) h = -h; - if(invertY) v = -v; + if(invertY) v = -v; - hAccum = math.lerp(hAccum, hAccum + h, Time.deltaTime * (3f * mouseSpeedH)); - vAccum = math.lerp(vAccum, vAccum - v, Time.deltaTime * (3f * mouseSpeedV)); + _hAccum = math.lerp(_hAccum, _hAccum + h, Time.deltaTime * (3f * mouseSpeedH)); + _vAccum = math.lerp(_vAccum, _vAccum - v, Time.deltaTime * (3f * mouseSpeedV)); - azimuth.localRotation = Quaternion.Euler(new Vector3(0, hAccum, 0)); - altitude.localRotation = Quaternion.Euler(new Vector3(vAccum, 0, 0)); + azimuth.localRotation = Quaternion.Euler(new Vector3(0, _hAccum, 0)); + altitude.localRotation = Quaternion.Euler(new Vector3(_vAccum, 0, 0)); - lastAngularVector = Mouse.current.position.ReadValue() - Mouse.current.position.ReadValueFromPreviousFrame(); - lastAngularVector.Normalize(); - hInertial = lastAngularVector.x * mouseSpeedH; - vInertial = lastAngularVector.y * mouseSpeedV; + _lastAngularVector = Mouse.current.position.ReadValue() - Mouse.current.position.ReadValueFromPreviousFrame(); + _lastAngularVector.Normalize(); + _hInertial = _lastAngularVector.x * mouseSpeedH; + _vInertial = _lastAngularVector.y * mouseSpeedV; } - //Scroll Behavior: Distance to pivot adjustment + //Scroll Behavior: Distance to pivot adjustment if(Mouse.current.scroll.IsActuated() && !Keyboard.current.ctrlKey.isPressed) { var scrollDist = Mouse.current.scroll.ReadValue(); - cDistance += 0.002f * scrollDist.y - Mouse.current.scroll.ReadValueFromPreviousFrame().y; - Camera.transform.localPosition = new Vector3(0, 0, math.clamp(cDistance, -8f, -0.1f)); + _cDistance += 0.002f * scrollDist.y - Mouse.current.scroll.ReadValueFromPreviousFrame().y; + Camera.transform.localPosition = new Vector3(0, 0, math.clamp(_cDistance, -8f, -0.1f)); } - //LMB Release + //LMB Release if(Mouse.current.leftButton.wasReleasedThisFrame) { - inertia = useInertia; + _inertia = useInertia; } - //RMB Initialization + //RMB Initialization if(Mouse.current.rightButton.wasPressedThisFrame) { - //tAccum = Vector3.zero; // offset.localPosition; - prevRMBPosition = Mouse.current.position.ReadValue(); - + //tAccum = Vector3.zero; // offset.localPosition; + _prevRmbPosition = Mouse.current.position.ReadValue(); + } - //RMB Hold Behavior: Pivot translation adjustment + //RMB Hold Behavior: Pivot translation adjustment if(Mouse.current.rightButton.isPressed) { var curTPos = Mouse.current.position.ReadValue(); - float h = curTPos.x - prevRMBPosition.x; - float v = curTPos.y - prevRMBPosition.y; + float h = curTPos.x - _prevRmbPosition.x; + float v = curTPos.y - _prevRmbPosition.y; //Angle correction based on current azimuth var theta = math.radians(-azimuth.localRotation.eulerAngles.y); @@ -230,48 +224,48 @@ public void FixedUpdate() if(invertX) h = -h; if(invertY) v = -v; - + h = -h; v = -v; - + var newh = h * cos - v * sin; - var newv = h * sin + v * cos; + var newv = h * sin + v * cos; + + var posDelta = new Vector3(newh*0.03f, 0, newv*0.03f); + _prevRmbPosition = curTPos; - var posDelta = new Vector3(newh*0.03f, 0, newv*0.03f); - prevRMBPosition = curTPos; - - tAccum = Vector3.Lerp(tAccum, tAccum + posDelta, Time.deltaTime * (3f * mouseSpeedT)); + _tAccum = Vector3.Lerp(_tAccum, _tAccum + posDelta, Time.deltaTime * (3f * mouseSpeedT)); - offset.localPosition = tableCenter + tAccum; + offset.localPosition = _tableCenter + _tAccum; } - //FOV Control + //FOV Control if(Mouse.current.scroll.IsActuated() && Keyboard.current.ctrlKey.isPressed) { var scrollDist = Mouse.current.scroll.ReadValue(); - cFOV += 0.002f * scrollDist.y - Mouse.current.scroll.ReadValueFromPreviousFrame().y; - Camera.fieldOfView = cFOV; + _cFOV += 0.002f * scrollDist.y - Mouse.current.scroll.ReadValueFromPreviousFrame().y; + Camera.fieldOfView = _cFOV; } - //Inertia - if(inertia == true) + //Inertia + if(_inertia == true) { - hInertial *= 0.8f; - vInertial *= 0.8f; + _hInertial *= 0.8f; + _vInertial *= 0.8f; - hAccum += hInertial * mouseSpeedH; - vAccum += -vInertial * mouseSpeedV; + _hAccum += _hInertial * mouseSpeedH; + _vAccum += -_vInertial * mouseSpeedV; - azimuth.localRotation = Quaternion.Euler(new Vector3(0, hAccum, 0)); - altitude.localRotation = Quaternion.Euler(new Vector3(vAccum, 0, 0)); + azimuth.localRotation = Quaternion.Euler(new Vector3(0, _hAccum, 0)); + altitude.localRotation = Quaternion.Euler(new Vector3(_vAccum, 0, 0)); - if(hInertial <= 0.01f && vInertial <= 0.01f) + if(_hInertial <= 0.01f && _vInertial <= 0.01f) { - inertia = false; + _inertia = false; } } } @@ -281,11 +275,11 @@ public void FixedUpdate() public void AdjustCameraHorizontal(float amount) { activeSetting.orbit += amount; - ApplySetting(); + ApplySetting(); } - + } } From 682e5c3a506d048260d8fd440ac783225a7d6000 Mon Sep 17 00:00:00 2001 From: freezy Date: Tue, 15 Jun 2021 22:51:53 +0200 Subject: [PATCH 6/8] editor: Add camera clipping icon. --- .../Icons/small_orange/camera_clipping.png | Bin 0 -> 408 bytes .../small_orange/camera_clipping.png.meta | 108 ++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_orange/camera_clipping.png create mode 100644 VisualPinball.Unity/Assets/Editor/Icons/small_orange/camera_clipping.png.meta diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/camera_clipping.png b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/camera_clipping.png new file mode 100644 index 0000000000000000000000000000000000000000..6b0a9ee9587f267356be6aa244d69c29add7be2c GIT binary patch literal 408 zcmV;J0cZY+P)lKQzK()*50^5>JD_44dqbX~_3xSWLLlti!ofkThz?GkwY z@!QI{u+-8^qoUHNs5H~6?6#)b8edwZhM+d1$KT%T$Q?4!4> zapB_+!4`M{Zh`Tl`QZ{t^7ARDJtElTYxiSA&+-Y4-p+uGQmqrlwU%&RN!|abcGZ z5r7-u?Ual|(S<;n_BrPlY4Xp@;SSi03GGc!X)4cCe$}mraUty@=6M7hFPcNrO9t(E zN*TIeLi=b1R8$%jl}69-3s~o4rEk7^y8h233h)UMrcq;sA~sS00000 Date: Tue, 15 Jun 2021 22:54:25 +0200 Subject: [PATCH 7/8] doc: Update changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4103c1e1..c48bdbe3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Built with [Unity 2020.2](https://github.com/freezy/VisualPinball.Engine/pull/255). ### Added +- Automated camera clipping ([#304](https://github.com/freezy/VisualPinball.Engine/pull/304/files)). - DMD and segment display support ([Documentation](https://docs.visualpinball.org/creators-guide/manual/displays.html)). - Plugin: Mission Pinball Framework ([Documentation](https://docs.visualpinball.org/plugins/mpf/index.html)) - Gamelogic Engine: Support for hardware rules ([#293](https://github.com/freezy/VisualPinball.Engine/pull/293)). From c8c314e738030d70d71e6590aed983a6d24bd415 Mon Sep 17 00:00:00 2001 From: freezy Date: Tue, 15 Jun 2021 23:04:25 +0200 Subject: [PATCH 8/8] editor: Assign icon. --- .../VisualPinball.Unity/Game/CameraClipPlane.cs.meta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs.meta index b37bbf699..71efeeea6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraClipPlane.cs.meta @@ -5,7 +5,7 @@ MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 - icon: {instanceID: 0} + icon: {fileID: 2800000, guid: dd801084fa3a35646b55f2503bc1d775, type: 3} userData: assetBundleName: assetBundleVariant: