From ec1c236f902ba2b6d7b2e014ff23bfc1adfdbd40 Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 26 Mar 2025 22:37:47 +0100 Subject: [PATCH 01/14] doc: Minor fixes. --- .../creators-guide/editor/asset-library-styleguide.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/asset-library-styleguide.md b/VisualPinball.Unity/Documentation~/creators-guide/editor/asset-library-styleguide.md index 846653455..45ca3a137 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/editor/asset-library-styleguide.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/editor/asset-library-styleguide.md @@ -100,7 +100,7 @@ All models must be [UV-mapped](https://en.wikipedia.org/wiki/UV_mapping). If your model contains art that varies from instance to instance, use a [decal mesh](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@17.2/manual/understand-decals.html). Decals should be used where you would find literal decals or imprints in the real world. Examples include spinners, aprons, targets, and bumpers. -On the right side you see two drop target meshes which their corresponding decal meshes in orange. +On the right side you see two drop target meshes with their corresponding decal meshes in orange. The decal geometry should be in a separate object parented to the main object. The UVs of the decal mesh should be laid out in a way that allows its textures to be created with non-specialized image editors. @@ -211,7 +211,7 @@ We're aiming for a resolution of about 6 pixels per millimeter (approximately 15 > [!note] > You can determine the resolution by looking at your UV map and the size of the asset. Let's take the gate from the previous section as an example. > -> 1. Take a large section of your mesh, and measure it. The larger, the more precise it will be. Here I'm measuring the top surface, from where the bevel starts:
+> 1. Take a large section of your mesh, and measure it. The larger, the more precise it will be. Here we're measuring the top surface, from where the bevel starts:
> > 2. Next, identify that section on your UV map, and note where in UV space they are:
>
@@ -220,9 +220,9 @@ We're aiming for a resolution of about 6 pixels per millimeter (approximately 15 > - Width in UV space: 0.955 - 0.36 = 0.595 > - Width in real world space: 29.2mm > 4. At 6px / mm, that makes 6px × 29.2mm = 175.2px for the 0.595 UVs -> 5. To get the resolution of the whole UV map: 175.2px / 0.592 = 296px +> 5. To get the resolution of the whole UV map: 175.2px / 0.592 = **296px** > -> So, a texture map at 296×296 would correspond to 6px / mm. Since we're at power of twos, we can go for either 512×512 or 256×256. +> So, a texture map at 296×296 would correspond to 6px / mm. Since we're at power of twos, we could go for either 512×512 or 256×256. ### File Format From 7c04360c8edd1b33e515694d2fcf40ed179518ab Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 26 Mar 2025 22:38:20 +0100 Subject: [PATCH 02/14] fix: Make surface not error out outside of playfield. --- .../VPT/ColliderComponent.cs | 8 ++++-- .../VPT/Surface/SurfaceTopMeshComponent.cs | 26 ++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs index e69eaf353..014c33295 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs @@ -183,6 +183,10 @@ private void OnDrawGizmos() return; } } + var playfieldComponent = GetComponentInParent(); + if (!playfieldComponent) { + return; + } Profiler.BeginSample("ItemColliderComponent.OnDrawGizmosSelected"); @@ -202,8 +206,8 @@ private void OnDrawGizmos() return; } - var playfieldToWorld = GetComponentInParent().transform.localToWorldMatrix; - var worldToPlayfield = GetComponentInParent().transform.worldToLocalMatrix; + var playfieldToWorld = playfieldComponent.transform.localToWorldMatrix; + var worldToPlayfield = playfieldComponent.transform.worldToLocalMatrix; var localToPlayfieldMatrixInVpx = GetLocalToPlayfieldMatrixInVpx(worldToPlayfield); var unmodifiedLocalToPlayfieldMatrixInVpx = GetUnmodifiedLocalToPlayfieldMatrixInVpx(worldToPlayfield); var nonTransformableColliderTransforms = new NativeParallelHashMap(0, Allocator.Temp); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceTopMeshComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceTopMeshComponent.cs index 2deddc08f..71f3364f6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceTopMeshComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceTopMeshComponent.cs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// ReSharper disable InconsistentNaming + using UnityEngine; using VisualPinball.Engine.Math; using VisualPinball.Engine.VPT; @@ -28,11 +30,33 @@ namespace VisualPinball.Unity [AddComponentMenu("Pinball/Mesh/Surface Top Mesh")] public class SurfaceTopMeshComponent : MeshComponent, IPackable { + +#if UNITY_EDITOR + [SerializeField] private Vector2 _playfieldDimensions; +#endif protected override Mesh GetMesh(SurfaceData data) { var playfieldComponent = GetComponentInParent(); + var playfieldDimensions = Vector2.zero; + + #if UNITY_EDITOR + if (playfieldComponent) { + _playfieldDimensions = new Vector2(playfieldComponent.Width, playfieldComponent.Height); + playfieldDimensions = _playfieldDimensions; + } + #endif + + if (playfieldComponent) { + playfieldDimensions = new Vector2(playfieldComponent.Width, playfieldComponent.Height); + } + + if (playfieldDimensions == Vector2.zero) { + Debug.LogError($"SurfaceTopMeshComponent of {transform.parent.name} must be a child of a PlayfieldComponent."); + return null; + } + return new SurfaceMeshGenerator(data, MainComponent.uvOffset.ToVertex3D()) - .GetMesh(SurfaceMeshGenerator.Top, playfieldComponent.Width, playfieldComponent.Height, 0, false) + .GetMesh(SurfaceMeshGenerator.Top, playfieldDimensions.x, playfieldDimensions.y, 0, false) .TransformToWorld(); } From 13871090168c7f84a25ddecab9046ea375d4bd6a Mon Sep 17 00:00:00 2001 From: freezy Date: Thu, 27 Mar 2025 23:49:09 +0100 Subject: [PATCH 03/14] refactor: Move playfield dimension cache into parent class. --- .../VisualPinball.Unity/VPT/MeshComponent.cs | 25 +++++++++++++++++++ .../VPT/Surface/SurfaceSideMeshComponent.cs | 8 ++++-- .../VPT/Surface/SurfaceTopMeshComponent.cs | 19 +------------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/MeshComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/MeshComponent.cs index 8b6b7cbe8..c1496fe41 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/MeshComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/MeshComponent.cs @@ -55,6 +55,10 @@ private void OnDisable() #endregion +#if UNITY_EDITOR + [SerializeField] private Vector2 _playfieldDimensions = Vector2.zero; +#endif + public virtual void RebuildMeshes() { UpdateMesh(); @@ -78,6 +82,27 @@ public void ClearMeshVertices() protected abstract PbrMaterial GetMaterial(TData data, Table table); + + protected Vector2 GetPlayfieldDimensions() + { + var playfieldComponent = GetComponentInParent(); + // ReSharper disable once RedundantAssignment + var playfieldDimensions = Vector2.zero; + +#if UNITY_EDITOR + if (playfieldComponent) { + _playfieldDimensions = new Vector2(playfieldComponent.Width, playfieldComponent.Height); + } + playfieldDimensions = _playfieldDimensions; +#endif + + if (playfieldComponent) { + playfieldDimensions = new Vector2(playfieldComponent.Width, playfieldComponent.Height); + } + + return playfieldDimensions; + } + public void CreateMesh(TData data, Table table, ITextureProvider texProvider, IMaterialProvider matProvider) { CreateMesh(gameObject, GetMesh(data), GetMaterial(data, table), data.GetName(), texProvider, matProvider); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceSideMeshComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceSideMeshComponent.cs index ae6dcbb45..88ec6d2a2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceSideMeshComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceSideMeshComponent.cs @@ -30,9 +30,13 @@ public class SurfaceSideMeshComponent : MeshComponent(); + var playfieldDimensions = GetPlayfieldDimensions(); + if (playfieldDimensions == Vector2.zero) { + Debug.LogError($"SurfaceTopMeshComponent of {transform.parent.name} must be a child of a PlayfieldComponent."); + return null; + } return new SurfaceMeshGenerator(data, Vertex3D.Zero) - .GetMesh(SurfaceMeshGenerator.Side, playfieldComponent.Width, playfieldComponent.Height, 0, false) + .GetMesh(SurfaceMeshGenerator.Side, playfieldDimensions.x, playfieldDimensions.y, 0, false) .TransformToWorld(); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceTopMeshComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceTopMeshComponent.cs index 71f3364f6..efbfd2b3b 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceTopMeshComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceTopMeshComponent.cs @@ -31,30 +31,13 @@ namespace VisualPinball.Unity public class SurfaceTopMeshComponent : MeshComponent, IPackable { -#if UNITY_EDITOR - [SerializeField] private Vector2 _playfieldDimensions; -#endif protected override Mesh GetMesh(SurfaceData data) { - var playfieldComponent = GetComponentInParent(); - var playfieldDimensions = Vector2.zero; - - #if UNITY_EDITOR - if (playfieldComponent) { - _playfieldDimensions = new Vector2(playfieldComponent.Width, playfieldComponent.Height); - playfieldDimensions = _playfieldDimensions; - } - #endif - - if (playfieldComponent) { - playfieldDimensions = new Vector2(playfieldComponent.Width, playfieldComponent.Height); - } - + var playfieldDimensions = GetPlayfieldDimensions(); if (playfieldDimensions == Vector2.zero) { Debug.LogError($"SurfaceTopMeshComponent of {transform.parent.name} must be a child of a PlayfieldComponent."); return null; } - return new SurfaceMeshGenerator(data, MainComponent.uvOffset.ToVertex3D()) .GetMesh(SurfaceMeshGenerator.Top, playfieldDimensions.x, playfieldDimensions.y, 0, false) .TransformToWorld(); From bdd465438fffbb1e6cdd188fd3b59f9a5596ad00 Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 28 Mar 2025 10:06:53 +0100 Subject: [PATCH 04/14] editor: Only recompute collider gizmos when dirty. --- .../VisualPinball.Unity/VPT/ColliderComponent.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs index 014c33295..0b9920909 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs @@ -213,7 +213,7 @@ private void OnDrawGizmos() var nonTransformableColliderTransforms = new NativeParallelHashMap(0, Allocator.Temp); var generateColliders = ShowAabbs || showColliders && !HasCachedColliders || ShowColliderOctree; - if (generateColliders) { + if (generateColliders && _collidersDirty) { if (Application.isPlaying) { InstantiateRuntimeColliders(showColliders); } else { @@ -325,6 +325,7 @@ private void InstantiateRuntimeColliders(bool showColliders) private void InstantiateEditorColliders(bool showColliders, ref NativeParallelHashMap nonTransformableColliderTransforms, float4x4 localToPlayfieldMatrixInVpx) { + Debug.Log("InstantiateEditorColliders"); var api = InstantiateColliderApi(_player, PhysicsEngine); var colliders = new ColliderReference(ref nonTransformableColliderTransforms, Allocator.Temp, IsKinematic); try { From 20af49b9b2cf7fe41112b75dcfb19ec8d7bd4e22 Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 28 Mar 2025 12:39:42 +0100 Subject: [PATCH 05/14] editor: Move collider gizmos through parent's transformation matrix. --- .../VisualPinball.Unity.Editor/VPT/ColliderInspector.cs | 2 +- .../VisualPinball.Unity/VPT/ColliderComponent.cs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ColliderInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ColliderInspector.cs index bca768b8d..a2603ed02 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ColliderInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ColliderInspector.cs @@ -59,7 +59,7 @@ protected override void OnEnable() protected override void OnPreInspectorGUI() { - PropertyField(IsKinematicProperty, "Movable"); + PropertyField(IsKinematicProperty, "Movable", updateColliders: true); base.OnPreInspectorGUI(); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs index 0b9920909..bf774e0bc 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs @@ -208,7 +208,6 @@ private void OnDrawGizmos() var playfieldToWorld = playfieldComponent.transform.localToWorldMatrix; var worldToPlayfield = playfieldComponent.transform.worldToLocalMatrix; - var localToPlayfieldMatrixInVpx = GetLocalToPlayfieldMatrixInVpx(worldToPlayfield); var unmodifiedLocalToPlayfieldMatrixInVpx = GetUnmodifiedLocalToPlayfieldMatrixInVpx(worldToPlayfield); var nonTransformableColliderTransforms = new NativeParallelHashMap(0, Allocator.Temp); @@ -217,7 +216,7 @@ private void OnDrawGizmos() if (Application.isPlaying) { InstantiateRuntimeColliders(showColliders); } else { - InstantiateEditorColliders(showColliders, ref nonTransformableColliderTransforms, localToPlayfieldMatrixInVpx); + InstantiateEditorColliders(showColliders, ref nonTransformableColliderTransforms, worldToPlayfield); } } @@ -246,7 +245,7 @@ private void OnDrawGizmos() } if (_transformedColliderMesh || _transformedKinematicColliderMesh) { - Gizmos.matrix = playfieldToWorld * (Matrix4x4)Physics.VpxToWorld; + Gizmos.matrix = MainComponent.transform.localToWorldMatrix * playfieldToWorld * (Matrix4x4)Physics.VpxToWorld; if (_transformedColliderMesh) { Gizmos.color = colliderEnabled ? ColliderColor.TransformedColliderSelected : ColliderColor.DisabledColliderSelected; Gizmos.DrawMesh(_transformedColliderMesh); @@ -325,7 +324,6 @@ private void InstantiateRuntimeColliders(bool showColliders) private void InstantiateEditorColliders(bool showColliders, ref NativeParallelHashMap nonTransformableColliderTransforms, float4x4 localToPlayfieldMatrixInVpx) { - Debug.Log("InstantiateEditorColliders"); var api = InstantiateColliderApi(_player, PhysicsEngine); var colliders = new ColliderReference(ref nonTransformableColliderTransforms, Allocator.Temp, IsKinematic); try { From 9ca397fc8b5f43c2b28d891b0b22a378ac2a4984 Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 28 Mar 2025 22:57:25 +0100 Subject: [PATCH 06/14] dragpoints: Ignore `IsLocked`. --- VisualPinball.Engine/Math/DragPointData.cs | 3 -- .../DragPoint/DragPointsHandler.cs | 38 +++++++------------ .../DragPoint/DragPointsSceneViewHandler.cs | 6 +-- .../Packaging/CommonPackables.cs | 3 -- 4 files changed, 15 insertions(+), 35 deletions(-) diff --git a/VisualPinball.Engine/Math/DragPointData.cs b/VisualPinball.Engine/Math/DragPointData.cs index 51224ce18..35241a880 100644 --- a/VisualPinball.Engine/Math/DragPointData.cs +++ b/VisualPinball.Engine/Math/DragPointData.cs @@ -88,7 +88,6 @@ public DragPointData Lerp(DragPointData dp, float pos) IsSlingshot = dp.IsSlingshot, HasAutoTexture = dp.HasAutoTexture, TextureCoord = dp.TextureCoord, - IsLocked = dp.IsLocked, EditorLayer = dp.EditorLayer, EditorLayerName = dp.EditorLayerName, EditorLayerVisibility = EditorLayerVisibility @@ -109,7 +108,6 @@ public DragPointData Clone() IsSlingshot = IsSlingshot, HasAutoTexture = HasAutoTexture, TextureCoord = TextureCoord, - IsLocked = IsLocked, EditorLayer = EditorLayer, EditorLayerName = EditorLayerName, EditorLayerVisibility = EditorLayerVisibility, @@ -147,7 +145,6 @@ public DragPointData(DragPointData rf) : base(null) IsSlingshot = rf.IsSlingshot; HasAutoTexture = rf.HasAutoTexture; TextureCoord = rf.TextureCoord; - IsLocked = rf.IsLocked; EditorLayer = rf.EditorLayer; CalcHeight = rf.CalcHeight; } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsHandler.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsHandler.cs index da83adb1b..0eea5b2ae 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsHandler.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsHandler.cs @@ -150,10 +150,7 @@ public void AddDragPointOnTraveller() return; } - var dragPoint = new DragPointData(DragPointInspector.DragPoints[CurveTravellerControlPointIdx]) { - IsLocked = false - }; - + var dragPoint = new DragPointData(DragPointInspector.DragPoints[CurveTravellerControlPointIdx]); var newIdx = CurveTravellerControlPointIdx + 1; var dragPointPosition = CurveTravellerPosition.TranslateToVpx(Transform); dragPointPosition.z = 0; @@ -181,13 +178,6 @@ public void RemoveDragPoint(int controlId) if (idx < 0) { return; } - var removalOk = !ControlPoints[idx].DragPoint.IsLocked; - if (!removalOk) { - removalOk = EditorUtility.DisplayDialog("Locked DragPoint Removal", "This drag point is locked!\nAre you really sure you want to remove it?", "Yes", "No"); - } - if (!removalOk) { - return; - } var dragPoints = DragPointInspector.DragPoints.ToList(); dragPoints.RemoveAt(idx); DragPointInspector.DragPoints = dragPoints.ToArray(); @@ -256,17 +246,17 @@ public void CenterPivot() /// /// New lock status /// True if at least one lock status changed, false otherwise. - public bool UpdateDragPointsLock(bool itemLock) - { - var lockChanged = false; - foreach (var controlPoint in ControlPoints) { - if (controlPoint.DragPoint.IsLocked != itemLock) { - controlPoint.DragPoint.IsLocked = itemLock; - lockChanged = true; - } - } - return lockChanged; - } + // public bool UpdateDragPointsLock(bool itemLock) + // { + // var lockChanged = false; + // foreach (var controlPoint in ControlPoints) { + // if (controlPoint.DragPoint.IsLocked != itemLock) { + // controlPoint.DragPoint.IsLocked = itemLock; + // lockChanged = true; + // } + // } + // return lockChanged; + // } /// /// Re-creates the control points of the scene view and references their @@ -377,7 +367,7 @@ private void OnSceneLayout() //Setup Screen positions & controlID for control points (in case of modification of drag points coordinates outside) foreach (var controlPoint in ControlPoints) { _center += controlPoint.AbsolutePosition; - if (controlPoint.IsSelected && !controlPoint.DragPoint.IsLocked) { + if (controlPoint.IsSelected) { SelectedControlPoints.Add(controlPoint); } @@ -416,7 +406,7 @@ private void OnMouseDown() { if (Event.current.button == 0) { var nearestControlPoint = ControlPoints.Find(cp => cp.ControlId == HandleUtility.nearestControl); - if (nearestControlPoint != null && !nearestControlPoint.DragPoint.IsLocked) { + if (nearestControlPoint != null) { if (!Event.current.control) { ClearAllSelection(); nearestControlPoint.IsSelected = true; diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsSceneViewHandler.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsSceneViewHandler.cs index 31f6660a7..d73c40760 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsSceneViewHandler.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/DragPoint/DragPointsSceneViewHandler.cs @@ -225,11 +225,7 @@ private void DisplayControlPoints() }; for (var i = 0; i < _handler.ControlPoints.Count; ++i) { var controlPoint = _handler.ControlPoints[i]; - Handles.color = controlPoint.DragPoint.IsLocked - ? Color.red - : controlPoint.IsSelected - ? Color.green - : Color.gray; + Handles.color = controlPoint.IsSelected ? Color.green : Color.gray; var pos = controlPoint.EditorPositionWorld; var handleSize = controlPoint.HandleSize; diff --git a/VisualPinball.Unity/VisualPinball.Unity/Packaging/CommonPackables.cs b/VisualPinball.Unity/VisualPinball.Unity/Packaging/CommonPackables.cs index a0888fd7d..e529f8f16 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Packaging/CommonPackables.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Packaging/CommonPackables.cs @@ -44,7 +44,6 @@ public struct DragPointPackable public bool IsSlingshot; public bool HasAutoTexture; public float TextureCoord; - public bool IsLocked; public int EditorLayer; public string EditorLayerName; public bool EditorLayerVisibility; @@ -59,7 +58,6 @@ public static DragPointPackable From(DragPointData data) IsSlingshot = data.IsSlingshot, HasAutoTexture = data.HasAutoTexture, TextureCoord = data.TextureCoord, - IsLocked = data.IsLocked, EditorLayer = data.EditorLayer, EditorLayerName = data.EditorLayerName, EditorLayerVisibility = data.EditorLayerVisibility, @@ -75,7 +73,6 @@ public DragPointData ToDragPoint() IsSlingshot = IsSlingshot, HasAutoTexture = HasAutoTexture, TextureCoord = TextureCoord, - IsLocked = IsLocked, EditorLayer = EditorLayer, EditorLayerName = EditorLayerName, EditorLayerVisibility = EditorLayerVisibility, From a1e1d5af94861a28623072247fe9ba2af9017cb1 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 29 Mar 2025 00:23:41 +0100 Subject: [PATCH 07/14] surface: Generate side mesh as before (because there were sling shot colliders!) --- .../VPT/Surface/SurfaceApi.cs | 2 +- .../VPT/Surface/SurfaceColliderGenerator.cs | 70 ++++++++++++++++++- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs index 8afa97e2a..ca46d79c9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs @@ -53,7 +53,7 @@ protected override void CreateColliders(ref ColliderReference colliders, float4x if (MainComponent.DragPoints.Length == 0) { return; } - var colliderGenerator = new SurfaceColliderGenerator(this, MainComponent, translateWithinPlayfieldMatrix); + var colliderGenerator = new SurfaceColliderGenerator(this, MainComponent, ColliderComponent, translateWithinPlayfieldMatrix); colliderGenerator.GenerateColliders(ref colliders); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceColliderGenerator.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceColliderGenerator.cs index 96fc61360..41f2edcdd 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceColliderGenerator.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceColliderGenerator.cs @@ -25,13 +25,17 @@ namespace VisualPinball.Unity public class SurfaceColliderGenerator { private readonly IApiColliderGenerator _api; + private readonly SurfaceComponent _component; + private readonly SurfaceColliderComponent _colliderComponent; private readonly float4x4 _matrix; private readonly SurfaceMeshGenerator _meshGen; - public SurfaceColliderGenerator(SurfaceApi surfaceApi, SurfaceComponent component, float4x4 matrix) + public SurfaceColliderGenerator(SurfaceApi surfaceApi, SurfaceComponent component, SurfaceColliderComponent colliderComponent, float4x4 matrix) { _api = surfaceApi; _matrix = matrix; + _component = component; + _colliderComponent = colliderComponent; var data = new SurfaceData(); component.CopyDataTo(data, null, null, false); @@ -42,10 +46,70 @@ public SurfaceColliderGenerator(SurfaceApi surfaceApi, SurfaceComponent componen internal void GenerateColliders(ref ColliderReference colliders) { var topMesh = _meshGen.GetMesh(SurfaceMeshGenerator.Top, 0, 0, 0, false); - var sideMesh = _meshGen.GetMesh(SurfaceMeshGenerator.Side, 0, 0, 0, false); + //var sideMesh = _meshGen.GetMesh(SurfaceMeshGenerator.Side, 0, 0, 0, false); ColliderUtils.GenerateCollidersFromMesh(topMesh, _api.GetColliderInfo(), ref colliders, _matrix); - ColliderUtils.GenerateCollidersFromMesh(sideMesh, _api.GetColliderInfo(), ref colliders, _matrix); + GenerateSideColliders(ref colliders); + // ColliderUtils.GenerateCollidersFromMesh(sideMesh, _api.GetColliderInfo(), ref colliders, _matrix); + } + + private void GenerateSideColliders(ref ColliderReference colliders, float margin = 0f) + { + var vVertex = DragPoint.GetRgVertex(_component.DragPoints); + + var count = vVertex.Length; + var rgv3Dt = new float3[count]; + var rgv3Db = _colliderComponent.IsBottomSolid ? new float3[count] : null; + + var bottom = _component.HeightBottom - margin; + var top = _component.HeightTop + margin; + + for (var i = 0; i < count; ++i) { + var pv1 = vVertex[i]; + rgv3Dt[i] = new float3(pv1.X, pv1.Y, top); + + if (rgv3Db != null) { + rgv3Db[count - 1 - i] = new float3(pv1.X, pv1.Y, bottom); + } + + var pv2 = vVertex[(i + 1) % count]; + var pv3 = vVertex[(i + 2) % count]; + GenerateLinePolys(pv2, pv3, ref colliders); + } + } + + /// + /// Returns the hit line polygons for the surface. + /// + private void GenerateLinePolys(RenderVertex2D pv1, Vertex2D pv2, ref ColliderReference colliders) + { + var bottom = _component.HeightBottom; + var top = _component.HeightTop; + + if (!pv1.IsSlingshot) { + colliders.Add(new LineCollider(pv1.ToUnityFloat2(), pv2.ToUnityFloat2(), bottom, top, _api.GetColliderInfo()), _matrix); + + } else { + colliders.Add(new LineSlingshotCollider(_colliderComponent.SlingshotForce, pv1.ToUnityFloat2(), pv2.ToUnityFloat2(), bottom, top, _api.GetColliderInfo()), _matrix); + } + + if (_component.HeightBottom != 0) { + // add lower edge as a line + colliders.Add(new Line3DCollider(new float3(pv1.X, pv1.Y, bottom), new float3(pv2.X, pv2.Y, bottom), _api.GetColliderInfo()), _matrix); + } + + // add upper edge as a line + colliders.Add(new Line3DCollider(new float3(pv1.X, pv1.Y, top), new float3(pv2.X, pv2.Y, top), _api.GetColliderInfo()), _matrix); + + // create vertical joint between the two line segments + colliders.Add(new LineZCollider(pv1.ToUnityFloat2(), bottom, top, _api.GetColliderInfo()), _matrix); + + // add upper and lower end points of line + if (_component.HeightBottom != 0) { + colliders.Add(new PointCollider(new float3(pv1.X, pv1.Y, bottom), _api.GetColliderInfo()), _matrix); + } + + colliders.Add(new PointCollider(new float3(pv1.X, pv1.Y, top), _api.GetColliderInfo()), _matrix); } } } From 273aa2db0569c7bdfb765a2dc28691142f091f2c Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 29 Mar 2025 02:01:50 +0100 Subject: [PATCH 08/14] hit-target: Fix collider generation. --- .../VPT/HitTarget/HitTargetColliderComponent.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetColliderComponent.cs index 9a78f3a5d..32b3f5824 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/HitTargetColliderComponent.cs @@ -102,9 +102,6 @@ public override bool PhysicsOverwrite { protected override IApiColliderGenerator InstantiateColliderApi(Player player, PhysicsEngine physicsEngine) => (MainComponent as HitTargetComponent)?.HitTargetApi ?? new HitTargetApi(gameObject, player, physicsEngine); - public override float4x4 GetLocalToPlayfieldMatrixInVpx(float4x4 worldToPlayfield) - => base.GetLocalToPlayfieldMatrixInVpx(worldToPlayfield).TransformToVpx(); - public int NumColliderMeshes => 1; public Mesh GetColliderMesh(int index) => FrontColliderMesh; // there's only one } From d39540b524e9dd4a5828dbc90bc0df092a15aec5 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 29 Mar 2025 02:02:59 +0100 Subject: [PATCH 09/14] colliders: Fix non-mesh gizmos. --- .../VisualPinball.Unity/VPT/ColliderComponent.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs index bf774e0bc..30fc9a2a9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs @@ -596,6 +596,11 @@ private void GenerateColliderMesh(IEnumerable colliders, out Mesh tra private void DrawNonMeshColliders() { + var playfieldComponent = GetComponentInParent(); + var playfieldToWorld = playfieldComponent.transform.localToWorldMatrix; + var worldToPlayfield = playfieldComponent.transform.worldToLocalMatrix; + var localToPlayfieldMatrixInVpx = GetLocalToPlayfieldMatrixInVpx(worldToPlayfield); + Handles.matrix = playfieldToWorld * (Matrix4x4)Physics.VpxToWorld * (Matrix4x4)localToPlayfieldMatrixInVpx; foreach (var col in _nonMeshColliders) { switch (col) { case LineZCollider lineZCol: { From 351d1d2b5d9a6128ca8347eef6e1b1ed333216db Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 30 Mar 2025 21:32:34 +0200 Subject: [PATCH 10/14] camera: Make smoothing configurable and fix zooming. --- .../Game/CameraTranslateAndOrbit.cs | 87 ++++++++++--------- 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraTranslateAndOrbit.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraTranslateAndOrbit.cs index 0da2e2d91..1df65ee2d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/CameraTranslateAndOrbit.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/CameraTranslateAndOrbit.cs @@ -1,7 +1,6 @@ using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.EnhancedTouch; -using VisualPinball.Unity; using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch; using TouchPhase = UnityEngine.InputSystem.TouchPhase; @@ -10,16 +9,22 @@ /// public class CameraTranslateAndOrbit : MonoBehaviour { + public float panSpeed = 0.5f; + public float orbitSpeed = 0.4f; public float zoomSpeed = 0.1f; - public Transform transformCache; + public float smoothing = 1f; + + public Transform initialOrbit; + + private Transform _transformCache; public bool isAnimating; - public GameObject dummyForRotation; - public Transform dummyTransform; + private GameObject _dummyForRotation; + private Transform _dummyTransform; private float _radius; - + private Vector3 _focusPoint; private float _radiusCurrent = 15f; @@ -49,24 +54,22 @@ private void Awake() private void Start() { - - var playfield = FindObjectOfType(); - var pfr = playfield == null ? null : playfield.GetComponent(); + var pfr = initialOrbit == null ? null : initialOrbit.GetComponent(); if (pfr != null) { positionOffset = pfr.bounds.center; } - + _radius = Vector3.Distance(Vector3.zero, transform.position); - transformCache = transform; - _focusPoint = transformCache.forward * -1f * _radius; + _transformCache = transform; + _focusPoint = _transformCache.forward * -1f * _radius; _positionOffsetCurrent = positionOffset; _radiusCurrent = _radius; - dummyForRotation = new GameObject(); - dummyTransform = dummyForRotation.transform; + _dummyForRotation = new GameObject(); + _dummyTransform = _dummyForRotation.transform; - dummyTransform.rotation = transformCache.rotation; - dummyTransform.position = transformCache.position; - _rot2 = transformCache.rotation; + _dummyTransform.rotation = _transformCache.rotation; + _dummyTransform.position = _transformCache.position; + _rot2 = _transformCache.rotation; } private void OrbitAroundObject(Vector3 newOffset, float radiusRef) @@ -96,7 +99,7 @@ private void UpdateTouchscreen() if (Touch.activeFingers.Count == 1) { Touch touch = Touch.activeTouches[0]; - transformCache.position = dummyTransform.position = Vector3.zero; + _transformCache.position = _dummyTransform.position = Vector3.zero; var hasHitRestrictedHitArea = false; @@ -112,14 +115,14 @@ private void UpdateTouchscreen() if (!hasHitRestrictedHitArea) { if (_isTrackingTouch0) { Vector3 touchPositionDifference = touch.screenPosition - _touch0StartPosition; - dummyTransform.Rotate(Vector3.up, touchPositionDifference.x * 0.4f, Space.World); + _dummyTransform.Rotate(Vector3.up, touchPositionDifference.x * orbitSpeed / 4, Space.World); - dummyTransform.Rotate(dummyTransform.right.normalized, touchPositionDifference.y * -0.4f, + _dummyTransform.Rotate(_dummyTransform.right.normalized, touchPositionDifference.y * -orbitSpeed, Space.World); - _rot2 = dummyTransform.rotation; + _rot2 = _dummyTransform.rotation; _touch0StartPosition = touch.screenPosition; } - transformCache.rotation = Quaternion.Lerp(transformCache.rotation, _rot2, Time.deltaTime * 4f); + _transformCache.rotation = Quaternion.Lerp(_transformCache.rotation, _rot2, Time.deltaTime / smoothing * 4); } if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled) { @@ -130,7 +133,7 @@ private void UpdateTouchscreen() var firstTouch = Touch.activeTouches[0]; var secondTouch = Touch.activeTouches[1]; - transformCache.position = dummyTransform.position = Vector3.zero; + _transformCache.position = _dummyTransform.position = Vector3.zero; if (firstTouch.phase == TouchPhase.Began || secondTouch.phase == TouchPhase.Began) { _startMultiTouchDistance = Vector2.Distance(firstTouch.screenPosition, secondTouch.screenPosition); @@ -141,14 +144,14 @@ private void UpdateTouchscreen() if (firstTouch.phase == TouchPhase.Moved || secondTouch.phase == TouchPhase.Moved) { _radius = _startMultiTouchRadius; - var distance = Vector2.Distance(firstTouch.screenPosition, secondTouch.screenPosition) - _startMultiTouchDistance; + var distance = Vector2.Distance(firstTouch.screenPosition, secondTouch.screenPosition) - _startMultiTouchDistance; var delta = distance / 250 * zoomSpeed * -_radius; _radius += delta; if (_radius < RadiusMin) { var radDiff = RadiusMin - _radius; - positionOffset += transformCache.forward * (radDiff * 4f); + positionOffset += _transformCache.forward * (radDiff * 4f); _radius = RadiusMin; } } @@ -156,14 +159,14 @@ private void UpdateTouchscreen() _positionOffsetCurrent = Vector3.Lerp(_positionOffsetCurrent, positionOffset, Time.deltaTime * 4f); _radiusCurrent = Mathf.Lerp(_radiusCurrent, _radius, Time.deltaTime * 4f); - _focusPoint = transformCache.forward * -1f * _radiusCurrent; - transformCache.position = _focusPoint + _positionOffsetCurrent; - dummyTransform.position = transformCache.position; + _focusPoint = _transformCache.forward * -1f * _radiusCurrent; + _transformCache.position = _focusPoint + _positionOffsetCurrent; + _dummyTransform.position = _transformCache.position; } private void UpdateMouse() { - transformCache.position = dummyTransform.position = Vector3.zero; + _transformCache.position = _dummyTransform.position = Vector3.zero; var hasHitRestrictedHitArea = false; @@ -179,14 +182,14 @@ private void UpdateMouse() if (!hasHitRestrictedHitArea) { if (_isTrackingMouse0) { Vector3 mousePositionDifference = Mouse.current.position.ReadValue() - _mouse0StartPosition; - dummyTransform.Rotate(Vector3.up, mousePositionDifference.x * 0.4f, Space.World); + _dummyTransform.Rotate(Vector3.up, mousePositionDifference.x * orbitSpeed, Space.World); - dummyTransform.Rotate(dummyTransform.right.normalized, mousePositionDifference.y * -0.4f, + _dummyTransform.Rotate(_dummyTransform.right.normalized, mousePositionDifference.y * -orbitSpeed, Space.World); - _rot2 = dummyTransform.rotation; + _rot2 = _dummyTransform.rotation; _mouse0StartPosition = Mouse.current.position.ReadValue(); } - transformCache.rotation = Quaternion.Lerp(transformCache.rotation, _rot2, Time.deltaTime * 4f); + _transformCache.rotation = Quaternion.Lerp(_transformCache.rotation, _rot2, Time.deltaTime / smoothing * 4); } if (Mouse.current.leftButton.wasReleasedThisFrame) { @@ -204,10 +207,9 @@ private void UpdateMouse() //Vector3 XZPlanerDirection = transformCache.forward.normalized; //XZPlanerDirection.y = 0; - positionOffset += transformCache.up.normalized * (mousePositionDifference.y * -(_radius / 2f / 100f)); + positionOffset += _transformCache.up.normalized * (mousePositionDifference.y * -(_radius * panSpeed / 100f)); + positionOffset += _transformCache.right.normalized * (mousePositionDifference.x * -(_radius * panSpeed / 100f)); - positionOffset += transformCache.right.normalized * - (mousePositionDifference.x * -(_radius / 2f / 100f)); /* if(positionOffset.y < 0){ positionOffset.y = 0; @@ -223,20 +225,21 @@ private void UpdateMouse() if (!hasHitRestrictedHitArea) { if (!isAnimating) { - var delta = Mouse.current.scroll.y.ReadValue() / 1000f * zoomSpeed * -_radius; + var delta = Mouse.current.scroll.y.ReadValue() / 10f * zoomSpeed * -_radius; _radius += delta; if (_radius < RadiusMin) { var radDiff = RadiusMin - _radius; - positionOffset += transformCache.forward * (radDiff * 4f); + positionOffset += _transformCache.forward * (radDiff * 4f); _radius = RadiusMin; } } } - _positionOffsetCurrent = Vector3.Lerp(_positionOffsetCurrent, positionOffset, Time.deltaTime * 4f); - _radiusCurrent = Mathf.Lerp(_radiusCurrent, _radius, Time.deltaTime * 4f); - _focusPoint = transformCache.forward * -1f * _radiusCurrent; - transformCache.position = _focusPoint + _positionOffsetCurrent; - dummyTransform.position = transformCache.position; + _positionOffsetCurrent = Vector3.Lerp(_positionOffsetCurrent, positionOffset, Time.deltaTime / smoothing * 4); + _radiusCurrent = Mathf.Lerp(_radiusCurrent, _radius, Time.deltaTime / smoothing * 4); + + _focusPoint = _transformCache.forward * -1f * _radiusCurrent; + _transformCache.position = _focusPoint + _positionOffsetCurrent; + _dummyTransform.position = _transformCache.position; } } From 2a31dedb0a80ca6a542626f68a5ed1045c6f9c6d Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 30 Mar 2025 21:34:00 +0200 Subject: [PATCH 11/14] editor: Don't generate collider gizmos every frame. --- .../VPT/ColliderInspector.cs | 4 +++ .../VPT/ColliderComponent.cs | 34 +++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ColliderInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ColliderInspector.cs index a2603ed02..e44e5ecfc 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ColliderInspector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/ColliderInspector.cs @@ -83,7 +83,11 @@ public override void OnInspectorGUI() refresh = showAabbs != ColliderComponent.ShowAabbs; ColliderComponent.ShowAabbs = showAabbs; + EditorGUI.BeginChangeCheck(); var showColliders = EditorGUILayout.Toggle("Show Colliders", ColliderComponent.ShowColliderMesh); + if (EditorGUI.EndChangeCheck()) { + ColliderComponent.CollidersDirty = true; + } refresh = refresh || showColliders != ColliderComponent.ShowColliderMesh; ColliderComponent.ShowColliderMesh = showColliders; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs index 30fc9a2a9..81eaa9dd7 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs @@ -85,13 +85,19 @@ public virtual float4x4 GetLocalToPlayfieldMatrixInVpx(float4x4 worldToPlayfield public float4x4 GetUnmodifiedLocalToPlayfieldMatrixInVpx(float4x4 worldToPlayfield) => Physics.GetLocalToPlayfieldMatrixInVpx(MainComponent.transform.localToWorldMatrix, worldToPlayfield); - private bool HasCachedColliders => false;// _colliderMesh != null && !_collidersDirty; + private bool HasCachedColliders => !_collidersDirty && ( + _transformedColliderMesh != null || + _transformedKinematicColliderMesh != null || + _untransformedColliderMesh != null || + _untransformedKinematicColliderMesh != null + ); private void Start() { _transformedColliderMesh = null; _transformedKinematicColliderMesh = null; _untransformedColliderMesh = null; + _untransformedKinematicColliderMesh = null; _collidersDirty = true; // make enable checkbox visible } @@ -173,9 +179,9 @@ public virtual void OnTransformationChanged(float4x4 currTransformationMatrix) #if UNITY_EDITOR private Player _player; - private NativeOctree _octree = default; + private NativeOctree _octree; - private void OnDrawGizmos() + private void OnDrawGizmosSelected() { if (!_player) { _player = GetComponentInParent(); @@ -208,15 +214,16 @@ private void OnDrawGizmos() var playfieldToWorld = playfieldComponent.transform.localToWorldMatrix; var worldToPlayfield = playfieldComponent.transform.worldToLocalMatrix; + var localToPlayfieldMatrixInVpx = GetLocalToPlayfieldMatrixInVpx(worldToPlayfield); var unmodifiedLocalToPlayfieldMatrixInVpx = GetUnmodifiedLocalToPlayfieldMatrixInVpx(worldToPlayfield); var nonTransformableColliderTransforms = new NativeParallelHashMap(0, Allocator.Temp); - var generateColliders = ShowAabbs || showColliders && !HasCachedColliders || ShowColliderOctree; - if (generateColliders && _collidersDirty) { + var generateColliders = !HasCachedColliders && (ShowAabbs || showColliders || ShowColliderOctree); + if (generateColliders) { if (Application.isPlaying) { InstantiateRuntimeColliders(showColliders); } else { - InstantiateEditorColliders(showColliders, ref nonTransformableColliderTransforms, worldToPlayfield); + InstantiateEditorColliders(showColliders, ref nonTransformableColliderTransforms); } } @@ -245,7 +252,8 @@ private void OnDrawGizmos() } if (_transformedColliderMesh || _transformedKinematicColliderMesh) { - Gizmos.matrix = MainComponent.transform.localToWorldMatrix * playfieldToWorld * (Matrix4x4)Physics.VpxToWorld; + Gizmos.matrix = playfieldToWorld * (Matrix4x4)Physics.VpxToWorld * (Matrix4x4)localToPlayfieldMatrixInVpx; + //Gizmos.matrix = MainComponent.transform.localToWorldMatrix * playfieldToWorld * (Matrix4x4)Physics.VpxToWorld; if (_transformedColliderMesh) { Gizmos.color = colliderEnabled ? ColliderColor.TransformedColliderSelected : ColliderColor.DisabledColliderSelected; Gizmos.DrawMesh(_transformedColliderMesh); @@ -322,12 +330,12 @@ private void InstantiateRuntimeColliders(bool showColliders) } } - private void InstantiateEditorColliders(bool showColliders, ref NativeParallelHashMap nonTransformableColliderTransforms, float4x4 localToPlayfieldMatrixInVpx) + private void InstantiateEditorColliders(bool showColliders, ref NativeParallelHashMap nonTransformableColliderTransforms) { var api = InstantiateColliderApi(_player, PhysicsEngine); var colliders = new ColliderReference(ref nonTransformableColliderTransforms, Allocator.Temp, IsKinematic); try { - api.CreateColliders(ref colliders, localToPlayfieldMatrixInVpx, 0.1f); + api.CreateColliders(ref colliders, float4x4.identity, 0.1f); if (showColliders) { if (IsKinematic) { @@ -388,7 +396,7 @@ private void GenerateColliderMesh(ref ColliderReference colliders, out Mesh tran } foreach (var col in colliders.FlipperColliders) { if (col.Header.IsTransformed) { - AddFlipperCollider(vertices, normals, indices, Origin.Global); + AddFlipperCollider(vertices, normals, indices, Origin.Original); } else { AddFlipperCollider(verticesNonTransformable, normalsNonTransformable, indicesNonTransformable, Origin.Original); } @@ -783,10 +791,8 @@ private static void DrawAabb(Aabb aabb, bool isSelected) #endregion - void ICollidableComponent.GetColliders(Player player, PhysicsEngine physicsEngine, ref ColliderReference colliders, - float4x4 translateWithinPlayfieldMatrix, float margin) - => InstantiateColliderApi(player, physicsEngine) - .CreateColliders(ref colliders, translateWithinPlayfieldMatrix, margin); + void ICollidableComponent.GetColliders(Player player, PhysicsEngine physicsEngine, ref ColliderReference colliders, float4x4 translateWithinPlayfieldMatrix, float margin) + => InstantiateColliderApi(player, physicsEngine).CreateColliders(ref colliders, translateWithinPlayfieldMatrix, margin); int ICollidableComponent.ItemId => MainComponent.gameObject.GetInstanceID(); bool ICollidableComponent.IsCollidable => isActiveAndEnabled; From 1444a8b72169b997d8a7898740e8bfc68c9235a8 Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 30 Mar 2025 23:59:40 +0200 Subject: [PATCH 12/14] editor: Merge non-kinematic and kinematic gizmo meshes, fix flipper correction triggers, and properly render gizmos during gameplay. --- .../VPT/ColliderComponent.cs | 84 +++++++------------ .../VPT/Flipper/FlipperComponent.cs | 16 ++-- .../Playfield/PlayfieldColliderComponent.cs | 1 - 3 files changed, 42 insertions(+), 59 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs index 81eaa9dd7..b6fe2d4c8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs @@ -56,9 +56,7 @@ public abstract class ColliderComponent : SubComponent _collidersDirty = value; } [NonSerialized] private Mesh _transformedColliderMesh; - [NonSerialized] private Mesh _transformedKinematicColliderMesh; [NonSerialized] private Mesh _untransformedColliderMesh; - [NonSerialized] private Mesh _untransformedKinematicColliderMesh; [NonSerialized] private Aabb[] _aabbs; [NonSerialized] private readonly List _nonMeshColliders = new List(); @@ -85,19 +83,12 @@ public virtual float4x4 GetLocalToPlayfieldMatrixInVpx(float4x4 worldToPlayfield public float4x4 GetUnmodifiedLocalToPlayfieldMatrixInVpx(float4x4 worldToPlayfield) => Physics.GetLocalToPlayfieldMatrixInVpx(MainComponent.transform.localToWorldMatrix, worldToPlayfield); - private bool HasCachedColliders => !_collidersDirty && ( - _transformedColliderMesh != null || - _transformedKinematicColliderMesh != null || - _untransformedColliderMesh != null || - _untransformedKinematicColliderMesh != null - ); + private bool HasCachedColliders => !_collidersDirty && (_transformedColliderMesh != null ||_untransformedColliderMesh != null); private void Start() { _transformedColliderMesh = null; - _transformedKinematicColliderMesh = null; _untransformedColliderMesh = null; - _untransformedKinematicColliderMesh = null; _collidersDirty = true; // make enable checkbox visible } @@ -235,37 +226,43 @@ private void OnDrawGizmosSelected() var colliderEnabled = !Application.isPlaying || PhysicsEngine.IsColliderEnabled(MainComponent.gameObject.GetInstanceID()); - if (_untransformedColliderMesh || _untransformedKinematicColliderMesh) { + if (_untransformedColliderMesh) { Gizmos.matrix = playfieldToWorld * (Matrix4x4)Physics.VpxToWorld * (Matrix4x4)unmodifiedLocalToPlayfieldMatrixInVpx; if (_untransformedColliderMesh) { - Gizmos.color = colliderEnabled ? ColliderColor.UntransformedColliderSelected : ColliderColor.DisabledColliderSelected; + if (IsKinematic) { + Gizmos.color = colliderEnabled ? ColliderColor.UntransformedKineticColliderSelected : ColliderColor.DisabledColliderSelected; + } else { + Gizmos.color = colliderEnabled ? ColliderColor.UntransformedColliderSelected : ColliderColor.DisabledColliderSelected; + } Gizmos.DrawMesh(_untransformedColliderMesh); - Gizmos.color = Application.isPlaying ? ColliderColor.UntransformedCollider : white; + if (IsKinematic) { + Gizmos.color = Application.isPlaying ? ColliderColor.UntransformedKineticCollider : white; + } else { + Gizmos.color = Application.isPlaying ? ColliderColor.UntransformedCollider : white; + } Gizmos.DrawWireMesh(_untransformedColliderMesh); } - if (_untransformedKinematicColliderMesh) { - Gizmos.color = colliderEnabled ? ColliderColor.UntransformedKineticColliderSelected : ColliderColor.DisabledColliderSelected; - Gizmos.DrawMesh(_untransformedKinematicColliderMesh); - Gizmos.color = Application.isPlaying ? ColliderColor.UntransformedKineticCollider : white; - Gizmos.DrawWireMesh(_untransformedKinematicColliderMesh); - } } - if (_transformedColliderMesh || _transformedKinematicColliderMesh) { - Gizmos.matrix = playfieldToWorld * (Matrix4x4)Physics.VpxToWorld * (Matrix4x4)localToPlayfieldMatrixInVpx; + if (_transformedColliderMesh) { + Gizmos.matrix = Application.isPlaying + ? playfieldToWorld * (Matrix4x4)Physics.VpxToWorld + : playfieldToWorld * (Matrix4x4)Physics.VpxToWorld * (Matrix4x4)localToPlayfieldMatrixInVpx; //Gizmos.matrix = MainComponent.transform.localToWorldMatrix * playfieldToWorld * (Matrix4x4)Physics.VpxToWorld; if (_transformedColliderMesh) { - Gizmos.color = colliderEnabled ? ColliderColor.TransformedColliderSelected : ColliderColor.DisabledColliderSelected; + if (IsKinematic) { + Gizmos.color = colliderEnabled ? ColliderColor.TransformedKineticColliderSelected : ColliderColor.DisabledColliderSelected; + } else { + Gizmos.color = colliderEnabled ? ColliderColor.TransformedColliderSelected : ColliderColor.DisabledColliderSelected; + } Gizmos.DrawMesh(_transformedColliderMesh); - Gizmos.color = Application.isPlaying ? ColliderColor.TransformedCollider : white; + if (IsKinematic) { + Gizmos.color = Application.isPlaying ? ColliderColor.TransformedKineticCollider : white; + } else { + Gizmos.color = Application.isPlaying ? ColliderColor.TransformedCollider : white; + } Gizmos.DrawWireMesh(_transformedColliderMesh); } - if (_transformedKinematicColliderMesh) { - Gizmos.color = colliderEnabled ? ColliderColor.TransformedKineticColliderSelected : ColliderColor.DisabledColliderSelected; - Gizmos.DrawMesh(_transformedKinematicColliderMesh); - Gizmos.color = Application.isPlaying ? ColliderColor.TransformedKineticCollider : white; - Gizmos.DrawWireMesh(_transformedKinematicColliderMesh); - } } DrawNonMeshColliders(); } @@ -303,19 +300,8 @@ private void InstantiateRuntimeColliders(bool showColliders) ? PhysicsEngine.GetKinematicColliders(MainComponent.gameObject.GetInstanceID()) : PhysicsEngine.GetColliders(MainComponent.gameObject.GetInstanceID()); - if (IsKinematic) { - _transformedColliderMesh = null; - _untransformedColliderMesh = null; - if (showColliders) { - GenerateColliderMesh(colliders, out _transformedKinematicColliderMesh, out _untransformedKinematicColliderMesh); - } - - } else { - _transformedKinematicColliderMesh = null; - _untransformedKinematicColliderMesh = null; - if (showColliders) { - GenerateColliderMesh(colliders, out _transformedColliderMesh, out _untransformedColliderMesh); - } + if (showColliders) { + GenerateColliderMesh(colliders, out _transformedColliderMesh, out _untransformedColliderMesh); } if (ShowAabbs) { @@ -338,15 +324,7 @@ private void InstantiateEditorColliders(bool showColliders, ref NativeParallelHa api.CreateColliders(ref colliders, float4x4.identity, 0.1f); if (showColliders) { - if (IsKinematic) { - _transformedColliderMesh = null; - _untransformedColliderMesh = null; - GenerateColliderMesh(ref colliders, out _transformedKinematicColliderMesh, out _untransformedKinematicColliderMesh); - } else { - _transformedKinematicColliderMesh = null; - _untransformedKinematicColliderMesh = null; - GenerateColliderMesh(ref colliders, out _transformedColliderMesh, out _untransformedColliderMesh); - } + GenerateColliderMesh(ref colliders, out _transformedColliderMesh, out _untransformedColliderMesh); _collidersDirty = false; } @@ -608,7 +586,9 @@ private void DrawNonMeshColliders() var playfieldToWorld = playfieldComponent.transform.localToWorldMatrix; var worldToPlayfield = playfieldComponent.transform.worldToLocalMatrix; var localToPlayfieldMatrixInVpx = GetLocalToPlayfieldMatrixInVpx(worldToPlayfield); - Handles.matrix = playfieldToWorld * (Matrix4x4)Physics.VpxToWorld * (Matrix4x4)localToPlayfieldMatrixInVpx; + Handles.matrix = Application.isPlaying + ? playfieldToWorld * (Matrix4x4)Physics.VpxToWorld + : playfieldToWorld * (Matrix4x4)Physics.VpxToWorld * (Matrix4x4)localToPlayfieldMatrixInVpx; foreach (var col in _nonMeshColliders) { switch (col) { case LineZCollider lineZCol: { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs index 37d378f71..fc702e561 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperComponent.cs @@ -657,12 +657,16 @@ private void SetupFlipperCorrection(FlipperColliderComponent colliderComponent) // but I couldn't get this transformation correctly from our current transforms. // using Matrix4x4.Rotate(quaternion.Euler(new float3(0, 0, -StartAngle))) and transforming // to localPos was close, but not close enough. - var flipperToPlayfield = new Matrix4x4( - new Vector4(-0.50754f, 0.86163f, 0, 0), - new Vector4(-0.86163f, -0.50754f, 0, 0), - new Vector4(0, 0, 1f, 0), - new Vector4(278.21380f, 1803.27200f, 0, 1f) - ); + // + // var flipperToPlayfield = new Matrix4x4( + // new Vector4(-0.50754f, 0.86163f, 0, 0), + // new Vector4(-0.86163f, -0.50754f, 0, 0), + // new Vector4(0, 0, 1f, 0), + // new Vector4(278.21380f, 1803.27200f, 0, 1f) + // ); + + // UPDATE: just rotating the points by the start angle seems to work fine. + var flipperToPlayfield = Matrix4x4.Rotate(Quaternion.Euler(0, 0, StartAngle)); for (var i = 0; i < poly.Count; i++) { // Poly points are expressed in flipper's frame: rotate to get it to the correct position. diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Playfield/PlayfieldColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Playfield/PlayfieldColliderComponent.cs index cfe39cc19..6c4f2c68d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Playfield/PlayfieldColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Playfield/PlayfieldColliderComponent.cs @@ -17,7 +17,6 @@ // ReSharper disable InconsistentNaming using System; -using System.Collections.Generic; using Unity.Mathematics; using UnityEngine; using VisualPinball.Engine.VPT.Table; From 3c5b563e7a80fbc2919380adda138e3fd41520f7 Mon Sep 17 00:00:00 2001 From: freezy Date: Tue, 1 Apr 2025 21:41:41 +0200 Subject: [PATCH 13/14] assetlib: Make thumb and db path library-specific. --- .../VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.cs | 1 - .../AssetBrowser/AssetBrowser_Init.cs | 2 +- .../VisualPinball.Unity.Editor/AssetBrowser/AssetLibrary.cs | 6 +++++- .../AssetBrowser/AssetStructure/AssetDetails.cs | 2 +- .../AssetBrowser/AssetStructure/AssetMaterialCombination.cs | 2 +- .../AssetStructure/AssetMaterialCombinationElement.cs | 4 ++-- .../AssetStructure/AssetMaterialVariationsElement.cs | 2 +- .../AssetBrowser/LibraryDatabase.cs | 4 +--- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.cs index be6b5f9ad..74c85f262 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.cs @@ -50,7 +50,6 @@ public partial class AssetBrowser : EditorWindow, IDragHandler [NonSerialized] public AssetQuery Query; - public const string ThumbPath = "Packages/org.visualpinball.unity.assetlibrary/Editor/Thumbnails~"; public const int ThumbSize = 256; private AssetResult LastSelectedResult { diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser_Init.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser_Init.cs index 255fb7823..c6acdb4a9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser_Init.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser_Init.cs @@ -166,7 +166,7 @@ private VisualElement NewItem(AssetResult result) private void LoadThumb(VisualElement el, Asset asset) { if (!_thumbCache.ContainsKey(asset.GUID)) { - var thumbPath = $"{ThumbPath}/{asset.GUID}.png"; + var thumbPath = $"{asset.Library.ThumbnailRoot}/{asset.GUID}.png"; if (File.Exists(thumbPath)) { var tex = new Texture2D(ThumbSize, ThumbSize); tex.LoadImage(File.ReadAllBytes(thumbPath)); diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetLibrary.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetLibrary.cs index 781c66758..cc49fd08c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetLibrary.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetLibrary.cs @@ -42,6 +42,10 @@ public class AssetLibrary : ScriptableObject, ISerializationCallbackReceiver public string LibraryRoot; + public string ThumbnailRoot; + + public string DatabaseRoot; + public bool IsLocked; public Preset DefaultThumbCameraPreset; @@ -210,7 +214,7 @@ public void OnBeforeSerialize() if (string.IsNullOrEmpty(LibraryRoot)) { var path = AssetDatabase.GetAssetPath(this); if (!string.IsNullOrEmpty(path)) { - LibraryRoot = Path.GetDirectoryName(path); + LibraryRoot = Path.GetDirectoryName(path)?.Replace("\\", "/"); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetDetails.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetDetails.cs index c2e57739f..2874f767e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetDetails.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetDetails.cs @@ -446,7 +446,7 @@ private static (int, int, int, int, int, int) CountVertices(GameObject go) if (mesh != null) { meshes++; vertices += mesh.vertexCount; - triangles += mesh.triangles.Length; + triangles += mesh.triangles.Length / 3; uvs += mesh.uv.Length; subMeshes += mesh.subMeshCount; } diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetMaterialCombination.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetMaterialCombination.cs index b1ea4d9c6..c96617dd4 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetMaterialCombination.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetMaterialCombination.cs @@ -27,7 +27,7 @@ public class AssetMaterialCombination public string Name => string.Join(", ", _variations.Select(v => $"{v.Item2.Name} {v.Item1.Name}")); public string ThumbId => GenerateThumbID(); - public string ThumbPath => $"{AssetBrowser.ThumbPath}/{ThumbId}.png"; + public string ThumbPath => $"{Asset.Library.ThumbnailRoot}/{ThumbId}.png"; public bool IsOriginal => _variations.Length == 0; diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetMaterialCombinationElement.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetMaterialCombinationElement.cs index e02a3fb89..3fd7e88df 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetMaterialCombinationElement.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetStructure/AssetMaterialCombinationElement.cs @@ -33,7 +33,7 @@ public class AssetMaterialCombinationElement: VisualElement public readonly AssetMaterialCombination Combination; - public AssetMaterialCombinationElement(AssetMaterialCombination combination) + public AssetMaterialCombinationElement(AssetMaterialCombination combination, Asset asset) { Combination = combination; @@ -49,7 +49,7 @@ public AssetMaterialCombinationElement(AssetMaterialCombination combination) ui.Q