From db0a91bb9c8831b227afdbf0097bbed4ec81131e Mon Sep 17 00:00:00 2001 From: freezy Date: Fri, 23 Jul 2021 23:07:26 +0200 Subject: [PATCH 01/14] renderer: Ditch Hybrid renderer and don't destroy GameObjects. --- .../VisualPinball.Unity/Game/Player.cs | 2 + .../Import/ConvertedItem.cs | 4 +- .../Collision/StaticCollisionSystem.cs | 16 ++--- .../VPT/Bumper/BumperApi.cs | 5 +- .../VPT/Bumper/BumperAuthoring.cs | 32 ++++++++++ .../Bumper/BumperRingAnimationAuthoring.cs | 6 +- .../Bumper/BumperSkirtAnimationAuthoring.cs | 6 +- .../VPT/Bumper/BumperStaticData.cs | 4 +- .../VPT/Flipper/FlipperAuthoring.cs | 1 - .../VPT/Flipper/FlipperMovementData.cs | 3 +- .../VPT/Flipper/FlipperRotateSystem.cs | 14 ++++- .../VPT/Gate/GateAuthoring.cs | 9 +++ .../VPT/Gate/GateDisplacementSystem.cs | 6 +- .../VPT/Gate/GateStaticData.cs | 2 +- .../VPT/Gate/GateVelocitySystem.cs | 6 +- .../VPT/Gate/GateWireAnimationAuthoring.cs | 6 +- .../Plunger/PlungerTransformationSystem.cs | 60 +++++++++---------- .../VPT/Spinner/SpinnerAuthoring.cs | 5 ++ .../VPT/Spinner/SpinnerDisplacementSystem.cs | 6 +- .../Spinner/SpinnerPlateAnimationAuthoring.cs | 6 +- .../VPT/Spinner/SpinnerStaticData.cs | 2 +- .../VPT/Spinner/SpinnerVelocitySystem.cs | 6 +- .../VPT/Surface/SurfaceApi.cs | 4 ++ package.json | 1 - 24 files changed, 127 insertions(+), 85 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 06507c25a..5202d90b3 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -73,6 +73,7 @@ public class Player : MonoBehaviour private readonly Dictionary _slingshots = new Dictionary(); internal readonly Dictionary Flippers = new Dictionary(); + internal readonly Dictionary FlipperTransforms = new Dictionary(); internal IEnumerable ColliderGenerators => _colliderGenerators; @@ -215,6 +216,7 @@ public void RegisterFlipper(Flipper flipper, Entity entity, Entity parentEntity, _switchPlayer.RegisterSwitch(flipper, flipperApi); _coilPlayer.RegisterCoil(flipper, flipperApi); _wirePlayer.RegisterWire(flipper, flipperApi); + FlipperTransforms[entity] = go.transform; if (EngineProvider.Exists) { EngineProvider.Get().OnRegisterFlipper(entity, flipper.Name); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Import/ConvertedItem.cs b/VisualPinball.Unity/VisualPinball.Unity/Import/ConvertedItem.cs index ff873e5cd..88e6ad22f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Import/ConvertedItem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Import/ConvertedItem.cs @@ -19,6 +19,7 @@ using System.Linq; using Unity.Entities; using UnityEngine; +using UnityEngine.UI; using VisualPinball.Engine.VPT; using Object = UnityEngine.Object; @@ -192,7 +193,8 @@ public void SetAnimationAuthoring(string name) where T : Component, IItemAnim public IConvertedItem AddConvertToEntity(bool componentsAdded) { if (!componentsAdded) { - _gameObject.AddComponent(); + var cte = _gameObject.AddComponent(); + cte.ConversionMode = ConvertToEntity.Mode.ConvertAndInjectGameObject; } return this; diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs index 5df0c0a0c..f454ae162 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Collision/StaticCollisionSystem.cs @@ -95,12 +95,12 @@ protected override void OnUpdate() switch (coll.Type) { case ColliderType.Bumper: { var bumperStaticData = GetComponent(coll.Entity); - var ringData = GetComponent(bumperStaticData.RingEntity); - var skirtData = GetComponent(bumperStaticData.SkirtEntity); + var ringData = GetComponent(coll.Entity); + var skirtData = GetComponent(coll.Entity); BumperCollider.Collide(ref ballData, ref events, ref collEvent, ref ringData, ref skirtData, in ballEntity, in coll, bumperStaticData, ref random); - SetComponent(bumperStaticData.RingEntity, ringData); - SetComponent(bumperStaticData.SkirtEntity, skirtData); + SetComponent(coll.Entity, ringData); + SetComponent(coll.Entity, skirtData); break; } @@ -120,12 +120,12 @@ protected override void OnUpdate() case ColliderType.Gate: { var gateStaticData = GetComponent(coll.Entity); - var gateMovementData = GetComponent(gateStaticData.WireEntity); + var gateMovementData = GetComponent(coll.Entity); GateCollider.Collide( ref ballData, ref collEvent, ref gateMovementData, ref events, in ballEntity, in coll, in gateStaticData ); - SetComponent(gateStaticData.WireEntity, gateMovementData); + SetComponent(coll.Entity, gateMovementData); break; } @@ -149,12 +149,12 @@ protected override void OnUpdate() case ColliderType.Spinner: { var spinnerStaticData = GetComponent(coll.Entity); - var spinnerMovementData = GetComponent(spinnerStaticData.PlateEntity); + var spinnerMovementData = GetComponent(coll.Entity); SpinnerCollider.Collide( in ballData, ref collEvent, ref spinnerMovementData, in spinnerStaticData ); - SetComponent(spinnerStaticData.PlateEntity, spinnerMovementData); + SetComponent(coll.Entity, spinnerMovementData); break; } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs index 645e10711..b10a88da5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs @@ -50,10 +50,9 @@ public BumperApi(Bumper item, Entity entity, Entity parentEntity, Player player) void IApiCoil.OnCoil(bool enabled, bool _) { if (enabled) { - var bumperData = EntityManager.GetComponentData(Entity); - var ringAnimation = EntityManager.GetComponentData(bumperData.RingEntity); + var ringAnimation = EntityManager.GetComponentData(Entity); ringAnimation.IsHit = true; - EntityManager.SetComponentData(bumperData.RingEntity, ringAnimation); + EntityManager.SetComponentData(Entity, ringAnimation); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperAuthoring.cs index 612627b22..db9dba7f7 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperAuthoring.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.Linq; using Unity.Entities; +using Unity.Mathematics; using UnityEngine; using VisualPinball.Engine.Game; using VisualPinball.Engine.VPT.Bumper; @@ -58,6 +59,37 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio Threshold = Data.Threshold }); + var table = Table; + var bumper = Item; + + // add ring data + dstManager.AddComponentData(entity, new BumperRingAnimationData { + + // dynamic + IsHit = false, + Offset = 0, + AnimateDown = false, + DoAnimate = false, + + // static + DropOffset = bumper.Data.RingDropOffset, + HeightScale = bumper.Data.HeightScale, + Speed = bumper.Data.RingSpeed, + ScaleZ = table.GetScaleZ() + }); + + // add ring data + dstManager.AddComponentData(entity, new BumperSkirtAnimationData { + BallPosition = default, + AnimationCounter = 0f, + DoAnimate = false, + DoUpdate = false, + EnableAnimation = true, + Rotation = new float2(0, 0), + HitEvent = bumper.Data.HitEvent, + Center = bumper.Data.Center.ToUnityFloat2() + }); + transform.GetComponentInParent().RegisterBumper(Item, entity, ParentEntity, gameObject); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingAnimationAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingAnimationAuthoring.cs index 0671453ec..5a08ab415 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingAnimationAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingAnimationAuthoring.cs @@ -34,9 +34,9 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio var bumperEntity = MainEntity; // update parent - var bumperStaticData = dstManager.GetComponentData(bumperEntity); - bumperStaticData.RingEntity = entity; - dstManager.SetComponentData(bumperEntity, bumperStaticData); + // var bumperStaticData = dstManager.GetComponentData(bumperEntity); + // bumperStaticData.RingEntity = entity; + // dstManager.SetComponentData(bumperEntity, bumperStaticData); // add ring data dstManager.AddComponentData(entity, new BumperRingAnimationData { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtAnimationAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtAnimationAuthoring.cs index 1da770a70..5ac29f45c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtAnimationAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtAnimationAuthoring.cs @@ -34,9 +34,9 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio var bumperEntity = MainEntity; // update parent - var bumperStaticData = dstManager.GetComponentData(bumperEntity); - bumperStaticData.SkirtEntity = entity; - dstManager.SetComponentData(bumperEntity, bumperStaticData); + // var bumperStaticData = dstManager.GetComponentData(bumperEntity); + // bumperStaticData.SkirtEntity = entity; + // dstManager.SetComponentData(bumperEntity, bumperStaticData); // add ring data dstManager.AddComponentData(entity, new BumperSkirtAnimationData { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperStaticData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperStaticData.cs index 4c8605922..675cda308 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperStaticData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperStaticData.cs @@ -23,7 +23,7 @@ internal struct BumperStaticData : IComponentData public float Force; public float Threshold; public bool HitEvent; - public Entity RingEntity; - public Entity SkirtEntity; + // public Entity RingEntity; + // public Entity SkirtEntity; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs index 631145ac9..584302051 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs @@ -368,7 +368,6 @@ private FlipperMovementData GetMovementData(FlipperStaticData d) AngleSpeed = 0f, AngularMomentum = 0f, EnableRotateEvent = 0, - BaseRotation = baseRotation, }; } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs index 4f6c0b3bf..61a439a7d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperMovementData.cs @@ -25,14 +25,13 @@ internal struct FlipperMovementData : IComponentData public float AngleSpeed; public float AngularMomentum; public sbyte EnableRotateEvent; - public quaternion BaseRotation; public uint LastHitTime; public uint StartRotateToEndTime; public float AngleAtRotateToEnd; public override string ToString() { - return $"FlipperMovementData(Angle: {Angle}, AngleSpeed: {AngleSpeed}, AngularMomentum: {AngularMomentum}, BaseRotation: {BaseRotation})"; + return $"FlipperMovementData(Angle: {Angle}, AngleSpeed: {AngleSpeed}, AngularMomentum: {AngularMomentum})"; } public void ApplyImpulse(in float3 rotI, float inertia) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperRotateSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperRotateSystem.cs index f0ba9963b..4fba2f034 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperRotateSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperRotateSystem.cs @@ -17,7 +17,7 @@ using Unity.Entities; using Unity.Mathematics; using Unity.Profiling; -using Unity.Transforms; +using UnityEngine; namespace VisualPinball.Unity { @@ -27,14 +27,22 @@ internal class FlipperRotateSystem : SystemBase { private static readonly ProfilerMarker PerfMarker = new ProfilerMarker("FlipperRotateSystem"); + private Player _player; + + protected override void OnStartRunning() + { + base.OnStartRunning(); + _player = Object.FindObjectOfType(); + } + protected override void OnUpdate() { var marker = PerfMarker; - Entities.WithName("FlipperRotateJob").ForEach((ref Rotation rot, in FlipperMovementData movement) => { + Entities.WithoutBurst().WithName("FlipperRotateJob").ForEach((Entity entity, in FlipperMovementData movement) => { marker.Begin(); - rot.Value = math.mul(movement.BaseRotation, quaternion.EulerXYZ(0, 0, movement.Angle)); + _player.FlipperTransforms[entity].localRotation = quaternion.Euler(0, 0, movement.Angle); marker.End(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateAuthoring.cs index b3d654e1b..05b6e6115 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateAuthoring.cs @@ -61,6 +61,15 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio GravityFactor = Data.GravityFactor, TwoWay = Data.TwoWay }); + + // add movement data + dstManager.AddComponentData(entity, new GateMovementData { + Angle = Data.AngleMin, + AngleSpeed = 0, + ForcedMove = false, + IsOpen = false, + HitDirection = false + }); // register transform.GetComponentInParent().RegisterGate(Item, entity, ParentEntity, gameObject); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateDisplacementSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateDisplacementSystem.cs index 2fa22b0ae..8ccbe89b2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateDisplacementSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateDisplacementSystem.cs @@ -52,12 +52,10 @@ protected override void OnUpdate() Entities .WithName("GateDisplacementJob") - .ForEach((Entity entity, in GateStaticData data) => { + .ForEach((Entity entity, ref GateMovementData movementData, in GateStaticData data) => { marker.Begin(); - var movementData = GetComponent(data.WireEntity); - if (data.TwoWay) { if (math.abs(movementData.Angle) > data.AngleMax) { if (movementData.Angle < 0.0) { @@ -120,8 +118,6 @@ protected override void OnUpdate() } movementData.Angle += movementData.AngleSpeed * dTime; - SetComponent(data.WireEntity, movementData); - marker.End(); }).Run(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateStaticData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateStaticData.cs index e5ef22567..62a1887fa 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateStaticData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateStaticData.cs @@ -26,6 +26,6 @@ internal struct GateStaticData : IComponentData public float GravityFactor; public float Damping; public bool TwoWay; - public Entity WireEntity; + //public Entity WireEntity; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateVelocitySystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateVelocitySystem.cs index 01df0167b..a9a52d404 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateVelocitySystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateVelocitySystem.cs @@ -33,12 +33,10 @@ protected override void OnUpdate() var marker = PerfMarker; Entities .WithName("GateVelocityJob") - .ForEach((in GateStaticData data) => { + .ForEach((ref GateMovementData movementData, in GateStaticData data) => { marker.Begin(); - var movementData = GetComponent(data.WireEntity); - if (!movementData.IsOpen) { if (math.abs(movementData.Angle) < data.AngleMin + 0.01f && math.abs(movementData.AngleSpeed) < 0.01f) { // stop a bit earlier to prevent a nearly endless animation (especially for slow balls) @@ -51,8 +49,6 @@ protected override void OnUpdate() } } - SetComponent(data.WireEntity, movementData); - marker.End(); }).Run(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateWireAnimationAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateWireAnimationAuthoring.cs index 37dcb6c79..893dee954 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateWireAnimationAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateWireAnimationAuthoring.cs @@ -30,9 +30,9 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio var gateEntity = MainEntity; // update parent - var gateStaticData = dstManager.GetComponentData(gateEntity); - gateStaticData.WireEntity = entity; - dstManager.SetComponentData(gateEntity, gateStaticData); + // var gateStaticData = dstManager.GetComponentData(gateEntity); + // gateStaticData.WireEntity = entity; + // dstManager.SetComponentData(gateEntity, gateStaticData); // add movement data dstManager.AddComponentData(entity, new GateMovementData { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerTransformationSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerTransformationSystem.cs index 68d67a8f7..626371379 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerTransformationSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerTransformationSystem.cs @@ -22,34 +22,34 @@ namespace VisualPinball.Unity { - [UpdateInGroup(typeof(TransformMeshesSystemGroup))] - internal class PlungerTransformationSystem : SystemBase - { - private static readonly ProfilerMarker PerfMarker = new ProfilerMarker("PlungerTransformationSystem"); - private static readonly int LerpPosition = Shader.PropertyToID("_LerpPosition"); - private static readonly int UVChannelVertices = Shader.PropertyToID("_UVChannelVertices"); - private static readonly int UVChannelNormals = Shader.PropertyToID("_UVChannelNormals"); - - protected override void OnUpdate() - { - var marker = PerfMarker; - - Entities.WithoutBurst().ForEach((Entity entity, ref PlungerAnimationData animationData, in RenderMesh renderMesh) => { - - if (!animationData.IsDirty) { - return; - } - animationData.IsDirty = false; - - marker.Begin(); - - var weight = math.clamp((float)animationData.CurrentFrame / animationData.NumFrames, 0, 1); - renderMesh.material.SetFloat(LerpPosition, weight); - renderMesh.material.SetFloat(UVChannelVertices, Engine.VPT.Mesh.AnimationUVChannelVertices); - renderMesh.material.SetFloat(UVChannelNormals, Engine.VPT.Mesh.AnimationUVChannelNormals); - marker.End(); - - }).Run(); - } - } + // [UpdateInGroup(typeof(TransformMeshesSystemGroup))] + // internal class PlungerTransformationSystem : SystemBase + // { + // private static readonly ProfilerMarker PerfMarker = new ProfilerMarker("PlungerTransformationSystem"); + // private static readonly int LerpPosition = Shader.PropertyToID("_LerpPosition"); + // private static readonly int UVChannelVertices = Shader.PropertyToID("_UVChannelVertices"); + // private static readonly int UVChannelNormals = Shader.PropertyToID("_UVChannelNormals"); + // + // protected override void OnUpdate() + // { + // var marker = PerfMarker; + // + // Entities.WithoutBurst().ForEach((Entity entity, ref PlungerAnimationData animationData, in RenderMesh renderMesh) => { + // + // if (!animationData.IsDirty) { + // return; + // } + // animationData.IsDirty = false; + // + // marker.Begin(); + // + // var weight = math.clamp((float)animationData.CurrentFrame / animationData.NumFrames, 0, 1); + // renderMesh.material.SetFloat(LerpPosition, weight); + // renderMesh.material.SetFloat(UVChannelVertices, Engine.VPT.Mesh.AnimationUVChannelVertices); + // renderMesh.material.SetFloat(UVChannelNormals, Engine.VPT.Mesh.AnimationUVChannelNormals); + // marker.End(); + // + // }).Run(); + // } + // } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerAuthoring.cs index ddcf2b0d1..d52661fc8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerAuthoring.cs @@ -61,6 +61,11 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio Height = Data.Height }); + dstManager.AddComponentData(entity, new SpinnerMovementData { + Angle = math.radians(math.clamp(0.0f, Data.AngleMin, Data.AngleMax)), + AngleSpeed = 0f + }); + // register transform.GetComponentInParent().RegisterSpinner(Item, entity, ParentEntity, gameObject); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerDisplacementSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerDisplacementSystem.cs index 254cadab8..d263929cb 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerDisplacementSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerDisplacementSystem.cs @@ -55,12 +55,10 @@ protected override void OnUpdate() Entities .WithName("SpinnerDisplacementJob") - .ForEach((Entity entity, in SpinnerStaticData data) => { + .ForEach((Entity entity, ref SpinnerMovementData movementData, in SpinnerStaticData data) => { marker.Begin(); - var movementData = GetComponent(data.PlateEntity); - // those are already converted to radian during authoring. var angleMin = data.AngleMin; var angleMax = data.AngleMax; @@ -121,8 +119,6 @@ protected override void OnUpdate() } } - SetComponent(data.PlateEntity, movementData); - marker.End(); }).Run(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerPlateAnimationAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerPlateAnimationAuthoring.cs index 7f4fd4b13..c324d9a11 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerPlateAnimationAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerPlateAnimationAuthoring.cs @@ -31,9 +31,9 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio var spinnerEntity = MainEntity; // update parent - var spinnerStaticData = dstManager.GetComponentData(spinnerEntity); - spinnerStaticData.PlateEntity = entity; - dstManager.SetComponentData(spinnerEntity, spinnerStaticData); + // var spinnerStaticData = dstManager.GetComponentData(spinnerEntity); + // spinnerStaticData.PlateEntity = entity; + // dstManager.SetComponentData(spinnerEntity, spinnerStaticData); dstManager.AddComponentData(entity, new SpinnerMovementData { Angle = math.radians(math.clamp(0.0f, Data.AngleMin, Data.AngleMax)), diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerStaticData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerStaticData.cs index 5e307de84..2d2c4b737 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerStaticData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerStaticData.cs @@ -25,6 +25,6 @@ internal struct SpinnerStaticData : IComponentData public float Height; public float Damping; public float Elasticity; - public Entity PlateEntity; + //public Entity PlateEntity; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerVelocitySystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerVelocitySystem.cs index 63fd27e29..7768dd619 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerVelocitySystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerVelocitySystem.cs @@ -31,18 +31,14 @@ protected override void OnUpdate() var marker = PerfMarker; Entities .WithName("SpinnerVelocityJob") - .ForEach((in SpinnerStaticData data) => { + .ForEach((ref SpinnerMovementData movementData, in SpinnerStaticData data) => { marker.Begin(); - var movementData = GetComponent(data.PlateEntity); - // Center of gravity towards bottom of object, makes it stop vertical movementData.AngleSpeed -= math.sin(movementData.Angle) * (float)(0.0025 * PhysicsConstants.PhysFactor); movementData.AngleSpeed *= data.Damping; - SetComponent(data.PlateEntity, movementData); - marker.End(); }).Run(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs index c3621a774..03fcb2c40 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Surface/SurfaceApi.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; using Unity.Entities; +using Unity.Entities.CodeGeneratedJobForEach; using VisualPinball.Engine.VPT.Table; namespace VisualPinball.Unity @@ -55,6 +56,9 @@ internal SurfaceApi(Engine.VPT.Surface.Surface item, Entity entity, Entity paren void IApiColliderGenerator.CreateColliders(Table table, List colliders) { var colliderGenerator = new SurfaceColliderGenerator(this); + if (Data.DragPoints.Length == 0) { + return; + } colliderGenerator.GenerateColliders(table, colliders); } diff --git a/package.json b/package.json index c834ff457..1edef8a4e 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "com.unity.burst": "1.4.4", "com.unity.collections": "0.15.0-preview.21", "com.unity.entities": "0.17.0-preview.41", - "com.unity.rendering.hybrid": "0.11.0-preview.42", "com.unity.jobs": "0.8.0-preview.23", "com.unity.mathematics": "1.2.1", "com.unity.inputsystem": "1.0.2", From 47e58eefdc0f4811900dde55a99f7587794afee4 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 24 Jul 2021 00:49:19 +0200 Subject: [PATCH 02/14] renderer: Migrate ball creation and transformation to non-hybrid. --- .../VisualPinball.Unity/Game/Player.cs | 6 +- .../Physics/Engine/DefaultPhysicsEngine.cs | 4 +- .../Physics/Engine/IPhysicsEngine.cs | 5 +- .../VPT/Ball/BallManager.cs | 61 +++++++++++-------- .../VPT/Ball/BallMovementSystem.cs | 16 +++-- 5 files changed, 58 insertions(+), 34 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 5202d90b3..01715b6fc 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -50,7 +50,7 @@ public class Player : MonoBehaviour public TableApi TableApi { get; private set; } // shortcuts - public Matrix4x4 TableToWorld => GetComponentInChildren().transform.localToWorldMatrix; + public GameObject Playfield => GetComponentInChildren().gameObject; [NonSerialized] public IGamelogicEngine GamelogicEngine; @@ -74,6 +74,8 @@ public class Player : MonoBehaviour internal readonly Dictionary Flippers = new Dictionary(); internal readonly Dictionary FlipperTransforms = new Dictionary(); + internal readonly Dictionary Balls = new Dictionary(); + internal IEnumerable ColliderGenerators => _colliderGenerators; @@ -125,7 +127,7 @@ private void Awake() Table = tableComponent.Table; //tableComponent.CreateTable(tableComponent.Data); _tableContainer = tableComponent.TableContainer; - BallManager = new BallManager(Table, TableToWorld); + BallManager = new BallManager(Table, this); _inputManager = new InputManager(); _inputManager.Enable(HandleInput); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs index d13f00d4d..ea3bc5634 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/DefaultPhysicsEngine.cs @@ -63,10 +63,10 @@ public void Init(TableAuthoring tableAuthoring, BallManager ballManager) _worldToLocal = transform.worldToLocalMatrix; } - public void BallCreate(in float3 worldPos, in float3 localPos, + public void BallCreate(GameObject ballGo, int id, in float3 worldPos, in float3 localPos, in float3 localVel, in float scale, in float mass, in float radius, in Entity kickerRef) { - _ballManager.CreateEntity(in worldPos, in localPos, in localVel,scale * radius * 2, + _ballManager.CreateEntity(ballGo, id, in worldPos, in localPos, in localVel,scale * radius * 2, in mass, in radius, in kickerRef); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/IPhysicsEngine.cs b/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/IPhysicsEngine.cs index 1583023a3..4f0ff4a8e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/IPhysicsEngine.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Physics/Engine/IPhysicsEngine.cs @@ -16,6 +16,7 @@ using Unity.Entities; using Unity.Mathematics; +using UnityEngine; using VisualPinball.Engine.Common; namespace VisualPinball.Unity @@ -39,6 +40,8 @@ public interface IPhysicsEngine : IEngine /// /// Create a new ball and returns its entity. /// + /// Created game object of the ball + /// Unique ID of the ball /// Position in world space /// Position in local space /// Velocity in local space @@ -46,7 +49,7 @@ public interface IPhysicsEngine : IEngine /// Physics mass /// Radius in local space /// If created within a kicker, this is the kicker entity - void BallCreate(in float3 worldPos, in float3 localPos, in float3 localVel, + void BallCreate(GameObject ballGo, int id, in float3 worldPos, in float3 localPos, in float3 localVel, in float scale, in float mass, in float radius, in Entity kickerRef); /// diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs index d993f56db..9f2466f6b 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs @@ -36,16 +36,18 @@ public class BallManager public int NumBalls { get; private set; } private readonly Table _table; - private readonly Matrix4x4 _ltw; + private readonly GameObject _playfield; + private readonly Player _player; private static EntityManager EntityManager => World.DefaultGameObjectInjectionWorld.EntityManager; private static Mesh _unitySphereMesh; // used to cache ball mesh from GameObject - public BallManager(Table table, Matrix4x4 ltw) + public BallManager(Table table, Player player) { _table = table; - _ltw = ltw; + _player = player; + _playfield = player.Playfield; } public void CreateBall(IBallCreationPosition ballCreator, float radius = 25f, float mass = 1f) @@ -59,51 +61,55 @@ public void CreateBall(IBallCreationPosition ballCreator, float radius, float ma var localVel = ballCreator.GetBallCreationVelocity(_table).ToUnityFloat3(); localPos.z += radius; - var worldPos = _ltw.MultiplyPoint(localPos); + var ltw = _playfield.transform.localToWorldMatrix; + var worldPos = ltw.MultiplyPoint(localPos); var scale3 = new Vector3( - _ltw.GetColumn(0).magnitude, - _ltw.GetColumn(1).magnitude, - _ltw.GetColumn(2).magnitude + ltw.GetColumn(0).magnitude, + ltw.GetColumn(1).magnitude, + ltw.GetColumn(2).magnitude ); var scale = (scale3.x + scale3.y + scale3.z) / 3.0f; // scale is only scale (without radiusfloat now, not vector. + + var ballId = NumBallsCreated++; + var ballPrefab = RenderPipeline.Current.BallConverter.CreateDefaultBall(); + var ballGo = Object.Instantiate(ballPrefab, _playfield.transform); + ballGo.name = $"Ball{ballId}"; + ballGo.transform.localScale = new Vector3(1000, 1000, 1000); + ballGo.transform.localPosition = localPos; + // create ball entity EngineProvider .Get() - .BallCreate(worldPos, localPos, localVel, scale, mass, radius, in kickerRef); + .BallCreate(ballGo, ballId, worldPos, localPos, localVel, scale, mass, radius, in kickerRef); } - public void CreateEntity(in float3 worldPos, in float3 localPos, in float3 localVel, in float scale, + public void CreateEntity(GameObject ballGo, int id, in float3 worldPos, in float3 localPos, in float3 localVel, in float scale, in float mass, in float radius, in Entity kickerEntity) { - var ballPrefab = RenderPipeline.Current.BallConverter.CreateDefaultBall(); - - // Create entity prefab from the game object hierarchy once - var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, null); - var prefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(ballPrefab, settings); - // Efficiently instantiate a bunch of entities from the already converted entity prefab - var entity = EntityManager.Instantiate(prefab); + var entity = EntityManager.CreateEntity( + typeof(OverlappingStaticColliderBufferElement), + typeof(OverlappingDynamicBufferElement), + typeof(BallInsideOfBufferElement), + typeof(BallLastPositionsBufferElement), + typeof(BallData), + typeof(CollisionEventData) + ); + + _player.Balls[entity] = ballGo; var world = World.DefaultGameObjectInjectionWorld; var ecbs = world.GetOrCreateSystem(); var ecb = ecbs.CreateCommandBuffer(); - ecb.AddComponent(entity, new Translation { - Value = worldPos - }); - - ecb.AddComponent(entity, new Scale { - Value = scale - }); - ecb.AddBuffer(entity); ecb.AddBuffer(entity); ecb.AddBuffer(entity); ecb.AddBuffer(entity); ecb.AddComponent(entity, new BallData { - Id = NumBallsCreated++, + Id = id, IsFrozen = false, Position = localPos, Radius = radius, @@ -147,6 +153,11 @@ public void CreateEntity(in float3 worldPos, in float3 localPos, in float3 local public void DestroyEntity(Entity ballEntity) { + // destroy game object + Object.DestroyImmediate(_player.Balls[ballEntity]); + _player.Balls.Remove(ballEntity); + + // destroy entity World.DefaultGameObjectInjectionWorld .GetOrCreateSystem() .CreateCommandBuffer() diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallMovementSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallMovementSystem.cs index abb8133c0..e82e08254 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallMovementSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallMovementSystem.cs @@ -17,7 +17,6 @@ using Unity.Entities; using Unity.Mathematics; using Unity.Profiling; -using Unity.Transforms; using UnityEngine; namespace VisualPinball.Unity @@ -27,6 +26,8 @@ namespace VisualPinball.Unity internal class BallMovementSystem : SystemBase { private float4x4 _baseTransform; + private Player _player; + private static readonly ProfilerMarker PerfMarker = new ProfilerMarker("BallMovementSystem"); protected override void OnStartRunning() @@ -39,22 +40,29 @@ protected override void OnStartRunning() ltw.m20, ltw.m21, ltw.m22, ltw.m23, ltw.m30, ltw.m31, ltw.m32, ltw.m33 ); + _player = Object.FindObjectOfType(); } protected override void OnUpdate() { var ltw = _baseTransform; var marker = PerfMarker; - Entities.WithName("BallMovementJob").ForEach((ref Translation translation, ref Rotation rot, in BallData ball) => { + Entities.WithoutBurst().WithName("BallMovementJob").ForEach((Entity entity, in BallData ball) => { marker.Begin(); + if (!_player.Balls.ContainsKey(entity)) { + marker.End(); + return; + } + // calculate/adapt height of ball var zHeight = !ball.IsFrozen ? ball.Position.z : ball.Position.z - ball.Radius; - translation.Value = math.transform(ltw, new float3(ball.Position.x, ball.Position.y, zHeight)); var or = ball.Orientation; - rot.Value = quaternion.LookRotation(or.c2, or.c1); + var ballTransform = _player.Balls[entity].transform; + ballTransform.localPosition = new Vector3(ball.Position.x, ball.Position.y, zHeight); + ballTransform.localRotation = Quaternion.LookRotation(or.c2, or.c1); marker.End(); From b2ced8f725cbcfd7507affbaec0cb4631ab6e198 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 24 Jul 2021 22:27:45 +0200 Subject: [PATCH 03/14] renderer: Migrate bumper animations to non-hybrid. --- .../VisualPinball.Unity/Game/Player.cs | 11 +++++++ .../Bumper/BumperRingAnimationAuthoring.cs | 32 +------------------ .../VPT/Bumper/BumperRingMovementSystem.cs | 19 +++++++++-- .../Bumper/BumperSkirtAnimationAuthoring.cs | 27 +--------------- .../VPT/Bumper/BumperSkirtMovementSystem.cs | 14 ++++++-- 5 files changed, 40 insertions(+), 63 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 01715b6fc..34957c480 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -74,6 +74,8 @@ public class Player : MonoBehaviour internal readonly Dictionary Flippers = new Dictionary(); internal readonly Dictionary FlipperTransforms = new Dictionary(); + internal readonly Dictionary BumperSkirtTransforms = new Dictionary(); + internal readonly Dictionary BumperRingTransforms = new Dictionary(); internal readonly Dictionary Balls = new Dictionary(); @@ -199,6 +201,15 @@ public void RegisterBumper(Bumper bumper, Entity entity, Entity parentEntity, Ga _switchPlayer.RegisterSwitch(bumper, bumperApi); _coilPlayer.RegisterCoil(bumper, bumperApi); _wirePlayer.RegisterWire(bumper, bumperApi); + + var ringAnimationAuth = go.GetComponentInChildren(); + if (ringAnimationAuth) { + BumperRingTransforms[entity] = ringAnimationAuth.gameObject.transform; + } + var skirtAnimationAuth = go.GetComponentInChildren(); + if (skirtAnimationAuth) { + BumperSkirtTransforms[entity] = skirtAnimationAuth.gameObject.transform; + } } public void RegisterFlipper(Flipper flipper, Entity entity, Entity parentEntity, GameObject go) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingAnimationAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingAnimationAuthoring.cs index 5a08ab415..fce104451 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingAnimationAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingAnimationAuthoring.cs @@ -23,38 +23,8 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Animation/Bumper Ring Animation")] - public class BumperRingAnimationAuthoring : ItemAnimationAuthoring, IConvertGameObjectToEntity + public class BumperRingAnimationAuthoring : ItemAnimationAuthoring { public override IEnumerable ValidParents { get; } = new Type[0]; // animation components only apply to their own - - public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) - { - var table = Table; - var bumper = Item; - var bumperEntity = MainEntity; - - // update parent - // var bumperStaticData = dstManager.GetComponentData(bumperEntity); - // bumperStaticData.RingEntity = entity; - // dstManager.SetComponentData(bumperEntity, bumperStaticData); - - // add ring data - dstManager.AddComponentData(entity, new BumperRingAnimationData { - - // dynamic - IsHit = false, - Offset = 0, - AnimateDown = false, - DoAnimate = false, - - // static - DropOffset = bumper.Data.RingDropOffset, - HeightScale = bumper.Data.HeightScale, - Speed = bumper.Data.RingSpeed, - ScaleZ = table.GetScaleZ() - }); - - LinkToParentEntity(entity, dstManager); - } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingMovementSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingMovementSystem.cs index 1124f6eaa..74e273dfb 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingMovementSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperRingMovementSystem.cs @@ -16,7 +16,7 @@ using Unity.Entities; using Unity.Profiling; -using Unity.Transforms; +using UnityEngine; namespace VisualPinball.Unity { @@ -25,14 +25,27 @@ internal class BumperRingMovementSystem : SystemBase { private static readonly ProfilerMarker PerfMarker = new ProfilerMarker("BumperRingMovementSystem"); + private Player _player; + + protected override void OnStartRunning() + { + base.OnStartRunning(); + _player = Object.FindObjectOfType(); + } + protected override void OnUpdate() { var marker = PerfMarker; - Entities.WithName("BumperRingMovementJob").ForEach((ref Translation trans, in BumperRingAnimationData data) => { + Entities.WithoutBurst().WithName("BumperRingMovementJob").ForEach((Entity entity, in BumperRingAnimationData data) => { marker.Begin(); - trans.Value.z = data.Offset; + var localPos = _player.BumperRingTransforms[entity].transform.localPosition; + _player.BumperRingTransforms[entity].transform.localPosition= new Vector3( + localPos.x, + localPos.y, + data.Offset + ); marker.End(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtAnimationAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtAnimationAuthoring.cs index 5ac29f45c..ffc5ed998 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtAnimationAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtAnimationAuthoring.cs @@ -24,33 +24,8 @@ namespace VisualPinball.Unity { [AddComponentMenu("Visual Pinball/Animation/Bumper Skirt Animation")] - public class BumperSkirtAnimationAuthoring : ItemAnimationAuthoring, IConvertGameObjectToEntity + public class BumperSkirtAnimationAuthoring : ItemAnimationAuthoring { public override IEnumerable ValidParents { get; } = new Type[0]; // animation components only apply to their own - - public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) - { - var bumper = Item; - var bumperEntity = MainEntity; - - // update parent - // var bumperStaticData = dstManager.GetComponentData(bumperEntity); - // bumperStaticData.SkirtEntity = entity; - // dstManager.SetComponentData(bumperEntity, bumperStaticData); - - // add ring data - dstManager.AddComponentData(entity, new BumperSkirtAnimationData { - BallPosition = default, - AnimationCounter = 0f, - DoAnimate = false, - DoUpdate = false, - EnableAnimation = true, - Rotation = new float2(0, 0), - HitEvent = bumper.Data.HitEvent, - Center = bumper.Data.Center.ToUnityFloat2() - }); - - LinkToParentEntity(entity, dstManager); - } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtMovementSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtMovementSystem.cs index 55cac914d..1a95291c3 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtMovementSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperSkirtMovementSystem.cs @@ -17,7 +17,7 @@ using Unity.Entities; using Unity.Mathematics; using Unity.Profiling; -using Unity.Transforms; +using UnityEngine; namespace VisualPinball.Unity { @@ -26,14 +26,22 @@ internal class BumperSkirtMovementSystem : SystemBase { private static readonly ProfilerMarker PerfMarker = new ProfilerMarker("BumperSkirtMovementSystem"); + private Player _player; + + protected override void OnStartRunning() + { + base.OnStartRunning(); + _player = Object.FindObjectOfType(); + } + protected override void OnUpdate() { var marker = PerfMarker; - Entities.WithName("BumperSkirtMovementJob").ForEach((ref Rotation rot, in BumperSkirtAnimationData data) => { + Entities.WithoutBurst().WithName("BumperSkirtMovementJob").ForEach((Entity entity, in BumperSkirtAnimationData data) => { marker.Begin(); - rot.Value = quaternion.EulerXYZ(math.radians(data.Rotation.x), math.radians(data.Rotation.y), 0f); + _player.BumperSkirtTransforms[entity].localRotation = quaternion.EulerXYZ(math.radians(data.Rotation.x), math.radians(data.Rotation.y), 0f); marker.End(); From baf8713a5b55c9982f64080e0fab11a209470b29 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 24 Jul 2021 22:37:58 +0200 Subject: [PATCH 04/14] renderer: Migrate gate animations to non-hybrid. --- .../VisualPinball.Unity/Game/Player.cs | 6 +++ .../VPT/Bumper/BumperAuthoring.cs | 52 ++++++++++--------- .../VPT/Flipper/FlipperAuthoring.cs | 2 - .../VPT/Gate/GateAuthoring.cs | 16 +++--- .../VPT/Gate/GateMovementSystem.cs | 14 +++-- 5 files changed, 54 insertions(+), 36 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 34957c480..f121b274c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -76,6 +76,7 @@ public class Player : MonoBehaviour internal readonly Dictionary FlipperTransforms = new Dictionary(); internal readonly Dictionary BumperSkirtTransforms = new Dictionary(); internal readonly Dictionary BumperRingTransforms = new Dictionary(); + internal readonly Dictionary GateWireTransforms = new Dictionary(); internal readonly Dictionary Balls = new Dictionary(); @@ -249,6 +250,11 @@ public void RegisterGate(Gate gate, Entity entity, Entity parentEntity, GameObje } _rotatables[entity] = gateApi; _switchPlayer.RegisterSwitch(gate, gateApi); + + var wireAnimAuthoring = go.GetComponentInChildren(); + if (wireAnimAuthoring) { + GateWireTransforms[entity] = wireAnimAuthoring.gameObject.transform; + } } public void RegisterHitTarget(HitTarget hitTarget, Entity entity, Entity parentEntity, GameObject go) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperAuthoring.cs index db9dba7f7..130e1a93c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperAuthoring.cs @@ -63,32 +63,36 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio var bumper = Item; // add ring data - dstManager.AddComponentData(entity, new BumperRingAnimationData { - - // dynamic - IsHit = false, - Offset = 0, - AnimateDown = false, - DoAnimate = false, - - // static - DropOffset = bumper.Data.RingDropOffset, - HeightScale = bumper.Data.HeightScale, - Speed = bumper.Data.RingSpeed, - ScaleZ = table.GetScaleZ() - }); + if (GetComponentInChildren()) { + dstManager.AddComponentData(entity, new BumperRingAnimationData { + + // dynamic + IsHit = false, + Offset = 0, + AnimateDown = false, + DoAnimate = false, + + // static + DropOffset = bumper.Data.RingDropOffset, + HeightScale = bumper.Data.HeightScale, + Speed = bumper.Data.RingSpeed, + ScaleZ = table.GetScaleZ() + }); + } // add ring data - dstManager.AddComponentData(entity, new BumperSkirtAnimationData { - BallPosition = default, - AnimationCounter = 0f, - DoAnimate = false, - DoUpdate = false, - EnableAnimation = true, - Rotation = new float2(0, 0), - HitEvent = bumper.Data.HitEvent, - Center = bumper.Data.Center.ToUnityFloat2() - }); + if (GetComponentInChildren()) { + dstManager.AddComponentData(entity, new BumperSkirtAnimationData { + BallPosition = default, + AnimationCounter = 0f, + DoAnimate = false, + DoUpdate = false, + EnableAnimation = true, + Rotation = new float2(0, 0), + HitEvent = bumper.Data.HitEvent, + Center = bumper.Data.Center.ToUnityFloat2() + }); + } transform.GetComponentInParent().RegisterBumper(Item, entity, ParentEntity, gameObject); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs index 584302051..5579b3733 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Flipper/FlipperAuthoring.cs @@ -52,8 +52,6 @@ public class FlipperAuthoring : ItemMainRenderableAuthoring Data.EndAngle < Data.StartAngle; - private static readonly Color EndAngleMeshColor = new Color32(0, 255, 248, 10); - public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { Convert(entity, dstManager); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateAuthoring.cs index 05b6e6115..db69bfd01 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateAuthoring.cs @@ -63,13 +63,15 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio }); // add movement data - dstManager.AddComponentData(entity, new GateMovementData { - Angle = Data.AngleMin, - AngleSpeed = 0, - ForcedMove = false, - IsOpen = false, - HitDirection = false - }); + if (GetComponentInChildren()) { + dstManager.AddComponentData(entity, new GateMovementData { + Angle = Data.AngleMin, + AngleSpeed = 0, + ForcedMove = false, + IsOpen = false, + HitDirection = false + }); + } // register transform.GetComponentInParent().RegisterGate(Item, entity, ParentEntity, gameObject); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateMovementSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateMovementSystem.cs index cd00da0cd..8f5c132e1 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateMovementSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateMovementSystem.cs @@ -17,7 +17,7 @@ using Unity.Entities; using Unity.Mathematics; using Unity.Profiling; -using Unity.Transforms; +using UnityEngine; namespace VisualPinball.Unity { @@ -26,14 +26,22 @@ internal class GateMovementSystem : SystemBase { private static readonly ProfilerMarker PerfMarker = new ProfilerMarker("GateMovementSystem"); + private Player _player; + + protected override void OnStartRunning() + { + base.OnStartRunning(); + _player = Object.FindObjectOfType(); + } + protected override void OnUpdate() { var marker = PerfMarker; - Entities.WithName("GateMovementJob").ForEach((ref Rotation rot, in GateMovementData movementData) => { + Entities.WithoutBurst().WithName("GateMovementJob").ForEach((Entity entity, in GateMovementData movementData) => { marker.Begin(); - rot.Value = quaternion.RotateX(-movementData.Angle); + _player.GateWireTransforms[entity].localRotation = quaternion.RotateX(-movementData.Angle); marker.End(); From 40c90bd49312fe1e99a2e544fbdeb593c63ff94d Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 24 Jul 2021 22:43:03 +0200 Subject: [PATCH 05/14] renderer: Migrate spinner animations to non-hybrid. --- .../VisualPinball.Unity/Game/Player.cs | 6 ++++++ .../VPT/Spinner/SpinnerAuthoring.cs | 10 ++++++---- .../VPT/Spinner/SpinnerMovementSystem.cs | 13 +++++++++++-- .../Spinner/SpinnerPlateAnimationAuthoring.cs | 19 +------------------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index f121b274c..1fa9cd7d5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -77,6 +77,7 @@ public class Player : MonoBehaviour internal readonly Dictionary BumperSkirtTransforms = new Dictionary(); internal readonly Dictionary BumperRingTransforms = new Dictionary(); internal readonly Dictionary GateWireTransforms = new Dictionary(); + internal readonly Dictionary SpinnerPlateTransforms = new Dictionary(); internal readonly Dictionary Balls = new Dictionary(); @@ -379,6 +380,11 @@ public void RegisterSpinner(Spinner spinner, Entity entity, Entity parentEntity, _spinnables[entity] = spinnerApi; _rotatables[entity] = spinnerApi; _switchPlayer.RegisterSwitch(spinner, spinnerApi); + + var plateAnimAuthoring = go.GetComponentInChildren(); + if (plateAnimAuthoring) { + SpinnerPlateTransforms[entity] = plateAnimAuthoring.gameObject.transform; + } } public void RegisterTrigger(Trigger trigger, Entity entity, Entity parentEntity, GameObject go) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerAuthoring.cs index d52661fc8..a622c20f2 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerAuthoring.cs @@ -61,10 +61,12 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio Height = Data.Height }); - dstManager.AddComponentData(entity, new SpinnerMovementData { - Angle = math.radians(math.clamp(0.0f, Data.AngleMin, Data.AngleMax)), - AngleSpeed = 0f - }); + if (GetComponentInChildren()) { + dstManager.AddComponentData(entity, new SpinnerMovementData { + Angle = math.radians(math.clamp(0.0f, Data.AngleMin, Data.AngleMax)), + AngleSpeed = 0f + }); + } // register transform.GetComponentInParent().RegisterSpinner(Item, entity, ParentEntity, gameObject); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerMovementSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerMovementSystem.cs index 07e11f4fe..e2eeaef50 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerMovementSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerMovementSystem.cs @@ -18,6 +18,7 @@ using Unity.Mathematics; using Unity.Profiling; using Unity.Transforms; +using UnityEngine; namespace VisualPinball.Unity { @@ -26,14 +27,22 @@ internal class SpinnerMovementSystem : SystemBase { private static readonly ProfilerMarker PerfMarker = new ProfilerMarker("SpinnerMovementSystem"); + private Player _player; + + protected override void OnStartRunning() + { + base.OnStartRunning(); + _player = Object.FindObjectOfType(); + } + protected override void OnUpdate() { var marker = PerfMarker; - Entities.WithName("SpinnerMovementJob").ForEach((ref Rotation rot, in SpinnerMovementData movementData) => { + Entities.WithoutBurst().WithName("SpinnerMovementJob").ForEach((Entity entity, in SpinnerMovementData movementData) => { marker.Begin(); - rot.Value = quaternion.RotateX(-movementData.Angle); + _player.SpinnerPlateTransforms[entity].localRotation = quaternion.RotateX(-movementData.Angle); marker.End(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerPlateAnimationAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerPlateAnimationAuthoring.cs index c324d9a11..c5bd032ae 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerPlateAnimationAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Spinner/SpinnerPlateAnimationAuthoring.cs @@ -22,25 +22,8 @@ namespace VisualPinball.Unity { - public class SpinnerPlateAnimationAuthoring : ItemAnimationAuthoring, IConvertGameObjectToEntity + public class SpinnerPlateAnimationAuthoring : ItemAnimationAuthoring { public override IEnumerable ValidParents { get; } = new Type[0]; // animation components only apply to their own - - public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) - { - var spinnerEntity = MainEntity; - - // update parent - // var spinnerStaticData = dstManager.GetComponentData(spinnerEntity); - // spinnerStaticData.PlateEntity = entity; - // dstManager.SetComponentData(spinnerEntity, spinnerStaticData); - - dstManager.AddComponentData(entity, new SpinnerMovementData { - Angle = math.radians(math.clamp(0.0f, Data.AngleMin, Data.AngleMax)), - AngleSpeed = 0f - }); - - LinkToParentEntity(entity, dstManager); - } } } From 21d14bec181f7eb259bac4b5b740e33fa9c3b028 Mon Sep 17 00:00:00 2001 From: freezy Date: Sat, 24 Jul 2021 22:50:43 +0200 Subject: [PATCH 06/14] renderer: Migrate trigger animations to non-hybrid. --- .../VisualPinball.Unity/Game/Player.cs | 6 ++++- .../VPT/Trigger/TriggerMovementSystem.cs | 25 +++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 1fa9cd7d5..543c420e7 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -78,6 +78,7 @@ public class Player : MonoBehaviour internal readonly Dictionary BumperRingTransforms = new Dictionary(); internal readonly Dictionary GateWireTransforms = new Dictionary(); internal readonly Dictionary SpinnerPlateTransforms = new Dictionary(); + internal readonly Dictionary TriggerTransforms = new Dictionary(); internal readonly Dictionary Balls = new Dictionary(); @@ -231,11 +232,12 @@ public void RegisterFlipper(Flipper flipper, Entity entity, Entity parentEntity, _switchPlayer.RegisterSwitch(flipper, flipperApi); _coilPlayer.RegisterCoil(flipper, flipperApi); _wirePlayer.RegisterWire(flipper, flipperApi); - FlipperTransforms[entity] = go.transform; if (EngineProvider.Exists) { EngineProvider.Get().OnRegisterFlipper(entity, flipper.Name); } + + FlipperTransforms[entity] = go.transform; } public void RegisterGate(Gate gate, Entity entity, Entity parentEntity, GameObject go) @@ -399,6 +401,8 @@ public void RegisterTrigger(Trigger trigger, Entity entity, Entity parentEntity, _hittables[entity] = triggerApi; } _switchPlayer.RegisterSwitch(trigger, triggerApi); + + TriggerTransforms[entity] = go.transform; } public void RegisterTrigger(Trigger trigger, Entity entity) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerMovementSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerMovementSystem.cs index 7864b4993..a8b064390 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerMovementSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trigger/TriggerMovementSystem.cs @@ -25,31 +25,30 @@ namespace VisualPinball.Unity [UpdateInGroup(typeof(TransformMeshesSystemGroup))] internal class TriggerMovementSystem : SystemBase { - private float4x4 _baseTransform; private static readonly ProfilerMarker PerfMarker = new ProfilerMarker("TriggerMovementSystem"); + private Player _player; + protected override void OnStartRunning() { - var root = Object.FindObjectOfType(); - var ltw = root.gameObject.transform.localToWorldMatrix; - _baseTransform = new float4x4( - ltw.m00, ltw.m01, ltw.m02, ltw.m03, - ltw.m10, ltw.m11, ltw.m12, ltw.m13, - ltw.m20, ltw.m21, ltw.m22, ltw.m23, - ltw.m30, ltw.m31, ltw.m32, ltw.m33 - ); + base.OnStartRunning(); + _player = Object.FindObjectOfType(); } protected override void OnUpdate() { - var ltw = _baseTransform; var marker = PerfMarker; - Entities.WithName("TriggerMovementJob").ForEach((ref Translation translation, in TriggerMovementData data) => { + + Entities.WithoutBurst().WithName("TriggerMovementJob").ForEach((Entity entity, in TriggerMovementData data) => { marker.Begin(); - var t = math.transform(math.inverse(ltw), translation.Value); - translation.Value = math.transform(ltw, new float3(t.x, t.y, data.HeightOffset)); + var localPos = _player.TriggerTransforms[entity].transform.localPosition; + _player.TriggerTransforms[entity].transform.localPosition= new Vector3( + localPos.x, + localPos.y, + data.HeightOffset + ); marker.End(); From 9b0ff6c57d3c300034f88385f035f85207583423 Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 25 Jul 2021 00:31:18 +0200 Subject: [PATCH 07/14] renderer: Migrate plunger animations to non-hybrid. --- .../Fixtures~/PlungerTest.vpx | Bin 32768 -> 32768 bytes VisualPinball.Engine/VPT/PbrMaterial.cs | 7 +- .../VPT/Plunger/PlungerMeshGenerator.cs | 6 +- .../Extensions/MeshExtensions.cs | 19 +----- .../VisualPinball.Unity/Game/Player.cs | 5 +- .../Standard/StandardMaterialConverter.cs | 4 +- .../VPT/ItemMeshAuthoring.cs | 2 +- .../VPT/Plunger/PlungerAnimationData.cs | 5 +- .../VPT/Plunger/PlungerAnimationSystem.cs | 37 ++--------- .../VPT/Plunger/PlungerApi.cs | 1 + .../VPT/Plunger/PlungerAuthoring.cs | 38 +++-------- .../VPT/Plunger/PlungerFlatMeshAuthoring.cs | 22 ------- .../VPT/Plunger/PlungerMeshAuthoring.cs | 28 +------- .../VPT/Plunger/PlungerRodMeshAuthoring.cs | 10 --- .../VPT/Plunger/PlungerSpringMeshAuthoring.cs | 10 --- .../VPT/Plunger/PlungerStaticData.cs | 5 -- .../Plunger/PlungerTransformationSystem.cs | 60 +++++++++--------- .../VPT/Plunger/PlungerUvBufferElement.cs | 32 ---------- .../Plunger/PlungerUvBufferElement.cs.meta | 11 ---- 19 files changed, 56 insertions(+), 246 deletions(-) delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerUvBufferElement.cs delete mode 100644 VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerUvBufferElement.cs.meta diff --git a/VisualPinball.Engine.Test/Fixtures~/PlungerTest.vpx b/VisualPinball.Engine.Test/Fixtures~/PlungerTest.vpx index 1b237841b6825bb92a2586cd4f8026b1ddb089f1..976c79dce1d775cff659650b12231a764b3a3318 100644 GIT binary patch delta 715 zcmYk&NoW&M7zgn8#>VE5t&3P}MT=Dm3Qm${bIZhRZ8h5@73xKE2uW)an`I>QGShp+ z<-3R{4;}@JbJsvsJiFjU@aU;1=v<0M|5H=(!S8!-=6i3xd7q{UnkH;kMMp3CqV!h# z$SsVq-XyJyGItRd4muwh&vz4p4fI`XI5(&MZXV3ka&PcF8E%!!qd@8?0hA(3qLpdk>#z46lzC?;&dRjaXw|y65)N%aFMLn3)!6C%9-kfR!Eg zDp5O>p)HSF{}Jk1o=I<9%YCHgu)_y_2tW`*a0br8IXDj&APf?i=PqK7LJZ=NfF#Vr z0`Z)e3c1*H8BY~hfmOHxRj5H7`ud0~>2TFYzkPl(6EUoxwjb*cJipL3QJ1cFt;39c zh_W8hyGoAlqvI=gLS_rBAjjkBy>)&`HtVw~NJmm}v&mT$Qc$Ff4mFv`MC#s5(CO9N zdau&7I3)8M`K%oO(hW*hU8)}u=vs4{w6#O@uz7-dYvc63dFp;+f+kwCy3{BPO)|C< zHIHQyiDkytW9Is5CX>RXKUH1{_A9t(3ssA#eUvsjW1cK>=8!=P+jcLlh5Ps;v+>#a eR3uwf>g7^1Rcy0ccN^Qo^tv<4hxD(V5z8NX-*C8<=d5XLbtm= z=-XEjA)?MPcGGncVxWZ1s6B>twYMh3V z9NnKj&rWfWJ9rw)UBRw4A4{T*?@RHnX7RL^3D!y*5muzJsYip)<*uS-k(z`YmX z=;1Xb%opGyT!PC`1|6n&+|G_w(OFOf8|ttC4Oj$j*oNo@BT6%&6ph!v8{fCTyxZEI zusaLy@vZ4VzrBHnJXnq-`X_7k8KN;gN)+giPD+0Mbe#RxaUPN>?zEDeR3|puNBM=C zlKn`#F?6EzENUQ6R;6+ywYxazZ(5c~bSilB NormalMap != null; public BlendMode MapBlendMode => GetBlendMode(); - public readonly bool VertexLerpWithUvEnabled; - public Color Color => _material?.BaseColor.WithAlpha(255) ?? new Color(0xffffffff, ColorFormat.Bgr); public bool IsMetal => _material?.IsMetal ?? false; public bool IsOpacityActive => _material?.IsOpacityActive ?? false; @@ -55,19 +53,17 @@ public class PbrMaterial private readonly Material _material; - public PbrMaterial(Material material = null, Texture map = null, Texture normalMap = null, Texture envMap = null, bool vertexLerp = false, string id = null) + public PbrMaterial(Material material = null, Texture map = null, Texture normalMap = null, Texture envMap = null, string id = null) { _material = material; Map = map; NormalMap = normalMap; EnvMap = envMap; - VertexLerpWithUvEnabled = vertexLerp; Id = id ?? string.Join("-", new[] { _material?.Name.ToNormalizedName() ?? NameNoMaterial, Map?.Name.ToNormalizedName() ?? NameNoMap, NormalMap?.Name.ToNormalizedName() ?? NameNoNormalMap, EnvMap?.Name.ToNormalizedName() ?? NameNoEnvMap, - vertexLerp ? "skinned" : NameNoLerp } .Reverse() .SkipWhile(s => s.StartsWith("__no_")) @@ -127,7 +123,6 @@ public override string ToString() sb.AppendLine($"Map {Map?.ToString() ?? "none"}".Trim()); sb.AppendLine($"MapBlendMode {MapBlendMode}"); sb.AppendLine($"NormalMap {NormalMap?.ToString() ?? "none"}".Trim()); - sb.AppendLine($"Skinned? {VertexLerpWithUvEnabled}"); return sb.ToString(); } diff --git a/VisualPinball.Engine/VPT/Plunger/PlungerMeshGenerator.cs b/VisualPinball.Engine/VPT/Plunger/PlungerMeshGenerator.cs index 43b0c9967..2a7bc8ab5 100644 --- a/VisualPinball.Engine/VPT/Plunger/PlungerMeshGenerator.cs +++ b/VisualPinball.Engine/VPT/Plunger/PlungerMeshGenerator.cs @@ -77,7 +77,7 @@ public RenderObject GetRenderObject(Table.Table table, string id, Origin origin, return new RenderObject( id, asRightHanded ? flatMesh.Transform(Matrix3D.RightHanded) : flatMesh, - new PbrMaterial(id: $"__plunger_flat_{_data.Name}", material: mat, map: tex, vertexLerp: true), + new PbrMaterial(id: $"__plunger_flat_{_data.Name}", material: mat, map: tex), true ); case Rod: @@ -86,7 +86,7 @@ public RenderObject GetRenderObject(Table.Table table, string id, Origin origin, return new RenderObject( id, asRightHanded ? rodMesh.Transform(Matrix3D.RightHanded) : rodMesh, - new PbrMaterial(id: $"__plunger_rod_{_data.Name}", material: mat, map: tex, vertexLerp: true), + new PbrMaterial(id: $"__plunger_rod_{_data.Name}", material: mat, map: tex), true ); case Spring: @@ -95,7 +95,7 @@ public RenderObject GetRenderObject(Table.Table table, string id, Origin origin, return new RenderObject( id, asRightHanded ? springMesh.Transform(Matrix3D.RightHanded) : springMesh, - new PbrMaterial(id: $"__plunger_spring_{_data.Name}", material: mat, map: tex, vertexLerp: true), + new PbrMaterial(id: $"__plunger_spring_{_data.Name}", material: mat, map: tex), true ); default: diff --git a/VisualPinball.Unity/VisualPinball.Unity/Extensions/MeshExtensions.cs b/VisualPinball.Unity/VisualPinball.Unity/Extensions/MeshExtensions.cs index 051a08ba6..f9d5f802d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Extensions/MeshExtensions.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Extensions/MeshExtensions.cs @@ -108,24 +108,7 @@ public static void ApplyToUnityMesh(this Mesh vpMesh, UnityEngine.Mesh mesh) mesh.triangles = vpMesh.Indices; // animation - if (vpMesh.AnimationFrames.Count == 1) { - - // if there's only one frame, we assume a linear interpolation and just - // add it in form of UV sets. we then have a shader that interpolates - // the mesh. - - var deltaVertices = new Vector3[vpMesh.Vertices.Length]; - var deltaNormals = new Vector3[vpMesh.Vertices.Length]; - - var blendVertices = vpMesh.AnimationFrames[0]; - for (var i = 0; i < vpMesh.Vertices.Length; i++) { - deltaVertices[i] = blendVertices[i].ToUnityVector3() - vertices[i]; - deltaNormals[i] = blendVertices[i].ToUnityNormalVector3() - normals[i]; - } - mesh.SetUVs(Mesh.AnimationUVChannelVertices, deltaVertices); - mesh.SetUVs(Mesh.AnimationUVChannelNormals, deltaNormals); - - } else if (vpMesh.AnimationFrames.Count > 0) { + if (vpMesh.AnimationFrames.Count > 0) { var deltaWeight = 1f / vpMesh.AnimationFrames.Count; var deltaVertices = new Vector3[vpMesh.Vertices.Length]; diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index 543c420e7..a8c7011b9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -79,6 +79,7 @@ public class Player : MonoBehaviour internal readonly Dictionary GateWireTransforms = new Dictionary(); internal readonly Dictionary SpinnerPlateTransforms = new Dictionary(); internal readonly Dictionary TriggerTransforms = new Dictionary(); + internal readonly Dictionary PlungerSkinnedMeshRenderers = new Dictionary(); internal readonly Dictionary Balls = new Dictionary(); @@ -300,7 +301,7 @@ public void RegisterLamp(Light lamp, GameObject go) _wirePlayer.RegisterWire(lamp, lightApi); } - public void RegisterPlunger(Plunger plunger, Entity entity, Entity parentEntity, InputActionReference actionRef) + public void RegisterPlunger(Plunger plunger, Entity entity, Entity parentEntity, InputActionReference actionRef, GameObject go) { var plungerApi = new PlungerApi(plunger, entity, parentEntity, this); TableApi.Plungers[plunger.Name] = plungerApi; @@ -315,6 +316,8 @@ public void RegisterPlunger(Plunger plunger, Entity entity, Entity parentEntity, actionRef.action.performed += plungerApi.OnAnalogPlunge; _actions.Add((actionRef.action, plungerApi.OnAnalogPlunge)); } + + PlungerSkinnedMeshRenderers[entity] = go.GetComponentsInChildren(); } public void RegisterPrimitive(Primitive primitive, Entity entity, Entity parentEntity, GameObject go) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Rendering/Standard/StandardMaterialConverter.cs b/VisualPinball.Unity/VisualPinball.Unity/Rendering/Standard/StandardMaterialConverter.cs index 6f40b50ca..055973f3e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Rendering/Standard/StandardMaterialConverter.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Rendering/Standard/StandardMaterialConverter.cs @@ -49,9 +49,7 @@ public Shader GetShader() private Shader GetShader(PbrMaterial vpxMaterial) { - return vpxMaterial.VertexLerpWithUvEnabled - ? Shader.Find("Visual Pinball/Built-In/LerpVertex") - : GetShader(); + return GetShader(); } public static Material GetDefaultMaterial(BlendMode blendMode) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ItemMeshAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ItemMeshAuthoring.cs index fe61b447a..5999d3429 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ItemMeshAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ItemMeshAuthoring.cs @@ -97,7 +97,7 @@ public void CreateMesh(string parentName, ITextureProvider texProvider, IMateria mf.sharedMesh = mesh; // apply material - if (ro.Mesh.AnimationFrames.Count > 1) { // if number of animations frames are 1, the blend vertices are in the uvs are handle by the lerp shader. + if (ro.Mesh.AnimationFrames.Count > 0) { // if number of animations frames are 1, the blend vertices are in the uvs are handle by the lerp shader. var smr = loadFromAsset ? gameObject.GetComponent() : gameObject.AddComponent(); smr.sharedMaterial = ro.Material.ToUnityMaterial(matProvider, texProvider, MainAuthoring.Item.GetType()); smr.sharedMesh = mesh; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAnimationData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAnimationData.cs index 4df472936..4153c8a1a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAnimationData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAnimationData.cs @@ -20,9 +20,6 @@ namespace VisualPinball.Unity { internal struct PlungerAnimationData : IComponentData { - public int CurrentFrame; - public int NumFrames; - public bool IsDirty; - + public float Position; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAnimationSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAnimationSystem.cs index 33cdeafa9..17922fb69 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAnimationSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAnimationSystem.cs @@ -15,6 +15,7 @@ // along with this program. If not, see . using Unity.Entities; +using Unity.Mathematics; using Unity.Profiling; namespace VisualPinball.Unity @@ -27,11 +28,9 @@ internal class PlungerAnimationSystem : SystemBase protected override void OnUpdate() { var marker = PerfMarker; - var animationDatas = GetComponentDataFromEntity(); - Entities - .WithNativeDisableParallelForRestriction(animationDatas) - .ForEach((in PlungerMovementData movementData, in PlungerStaticData staticData) => + Entities.WithName("PlungerAnimationJob") + .ForEach((ref PlungerAnimationData animationData, in PlungerMovementData movementData, in PlungerStaticData staticData) => { marker.Begin(); @@ -40,35 +39,7 @@ protected override void OnUpdate() //Debug.Log($"[plunger] frame0 = {frame0} frame = {frame}"); - if (animationDatas.HasComponent(staticData.RodEntity)) { - var rodAnimData = animationDatas[staticData.RodEntity]; - if (rodAnimData.CurrentFrame != frame) { - rodAnimData.CurrentFrame = frame; - rodAnimData.NumFrames = staticData.NumFrames; - rodAnimData.IsDirty = true; - animationDatas[staticData.RodEntity] = rodAnimData; - } - } - - if (animationDatas.HasComponent(staticData.SpringEntity)) { - var springAnimData = animationDatas[staticData.SpringEntity]; - if (springAnimData.CurrentFrame != frame) { - springAnimData.CurrentFrame = frame; - springAnimData.NumFrames = staticData.NumFrames; - springAnimData.IsDirty = true; - animationDatas[staticData.SpringEntity] = springAnimData; - } - } - - if (animationDatas.HasComponent(staticData.FlatEntity)) { - var flatAnimData = animationDatas[staticData.FlatEntity]; - if (flatAnimData.CurrentFrame != frame) { - flatAnimData.CurrentFrame = frame; - flatAnimData.NumFrames = staticData.NumFrames; - flatAnimData.IsDirty = true; - animationDatas[staticData.FlatEntity] = flatAnimData; - } - } + animationData.Position = math.clamp((float)frame / staticData.NumFrames, 0, 1); marker.End(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs index 8cf7a2d9b..bb27aecb8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerApi.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; using Unity.Entities; +using UnityEngine; using UnityEngine.InputSystem; using VisualPinball.Engine.VPT.Plunger; using VisualPinball.Engine.VPT.Table; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAuthoring.cs index c1d476c1f..88e5c6ee0 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerAuthoring.cs @@ -42,15 +42,6 @@ public class PlungerAuthoring : ItemMainRenderableAuthoring); protected override Type ColliderAuthoringType { get; } = typeof(ItemColliderAuthoring); - private static readonly int LerpPosition = Shader.PropertyToID("_LerpPosition"); - private static readonly int UVChannelVertices = Shader.PropertyToID("_UVChannelVertices"); - private static readonly int UVChannelNormals = Shader.PropertyToID("_UVChannelNormals"); - - private void Start() - { - UpdateParkPosition(1 - Data.ParkPosition); - } - public override IEnumerable ValidParents => PlungerColliderAuthoring.ValidParentTypes .Concat(PlungerFlatMeshAuthoring.ValidParentTypes) .Concat(PlungerRodMeshAuthoring.ValidParentTypes) @@ -60,8 +51,9 @@ private void Start() public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { Convert(entity, dstManager); - var table = gameObject.GetComponentInParent().Item; - transform.GetComponentInParent().RegisterPlunger(Item, entity, ParentEntity, analogPlungerAction); + var go = gameObject; + var table = go.GetComponentInParent().Item; + transform.GetComponentInParent().RegisterPlunger(Item, entity, ParentEntity, analogPlungerAction, go); var zHeight = table.GetSurfaceHeight(Data.Surface, Data.Center.X, Data.Center.Y); var x = Data.Center.X - Data.Width; @@ -125,6 +117,10 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio RetractWaitLoop = 0, MechStrength = Data.MechStrength }); + + dstManager.AddComponentData(entity, new PlungerAnimationData { + Position = _data.ParkPosition + }); } public override void Restore() @@ -196,28 +192,12 @@ public void OnTypeChanged(int plungerTypeBefore, int plungerTypeAfter) } break; } - - UpdateParkPosition(1 - Data.ParkPosition); } public void UpdateParkPosition(float pos) { - SetMaterialProperty(UVChannelVertices, Mesh.AnimationUVChannelVertices); - SetMaterialProperty(UVChannelNormals, Mesh.AnimationUVChannelNormals); - switch (Data.Type) { - case PlungerType.PlungerTypeFlat: { - SetMaterialProperty(LerpPosition, pos); - break; - } - case PlungerType.PlungerTypeCustom: { - SetMaterialProperty(LerpPosition, pos); - SetMaterialProperty(LerpPosition, pos); - break; - } - case PlungerType.PlungerTypeModern: { - SetMaterialProperty(LerpPosition, pos); - break; - } + foreach (var skinnedMeshRenderer in GetComponentsInChildren()) { + skinnedMeshRenderer.SetBlendShapeWeight(0, pos); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerFlatMeshAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerFlatMeshAuthoring.cs index 0d0d836d3..b914caffa 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerFlatMeshAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerFlatMeshAuthoring.cs @@ -33,27 +33,5 @@ public class PlungerFlatMeshAuthoring : PlungerMeshAuthoring public override IEnumerable ValidParents => ValidParentTypes; protected override string MeshId => PlungerMeshGenerator.Flat; - - internal override void SetChildEntity(ref PlungerStaticData staticData, Entity entity) - { - staticData.FlatEntity = entity; - } - - protected override IEnumerable GetVertices(PlungerMeshGenerator meshGenerator, int frame) - { - return meshGenerator.BuildFlatVertices(frame); - } - - protected override void PostConvert(Entity entity, EntityManager dstManager, PlungerMeshGenerator meshGenerator) - { - // add mesh data - var uvBuffer = dstManager.AddBuffer(entity); - for (var frame = 0; frame < meshGenerator.NumFrames; frame++) { - var vertices = meshGenerator.BuildFlatVertices(frame); - foreach (var v in vertices) { - uvBuffer.Add(new PlungerUvBufferElement(new float2(v.Tu, v.Tv))); - } - } - } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerMeshAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerMeshAuthoring.cs index f218921ed..0060d6f6f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerMeshAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerMeshAuthoring.cs @@ -21,37 +21,11 @@ namespace VisualPinball.Unity { - public abstract class PlungerMeshAuthoring : ItemMeshAuthoring, IConvertGameObjectToEntity + public abstract class PlungerMeshAuthoring : ItemMeshAuthoring { - internal abstract void SetChildEntity(ref PlungerStaticData staticData, Entity entity); - - protected abstract IEnumerable GetVertices(PlungerMeshGenerator meshGenerator, int frame); - protected override bool IsVisible { get => Data.IsVisible; set => Data.IsVisible = value; } - - public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) - { - var plunger = transform.parent.gameObject.GetComponent().Item; - var plungerEntity = new Entity {Index = plunger.Index, Version = plunger.Version}; - - // update parent - var plungerStaticData = dstManager.GetComponentData(plungerEntity); - SetChildEntity(ref plungerStaticData, entity); - dstManager.SetComponentData(plungerEntity, plungerStaticData); - - // add animation data - dstManager.AddComponentData(entity, new PlungerAnimationData { - CurrentFrame = 0 - }); - - PostConvert(entity, dstManager, plunger.MeshGenerator); - } - - protected virtual void PostConvert(Entity entity, EntityManager dstManager, PlungerMeshGenerator meshGenerator) - { - } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerRodMeshAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerRodMeshAuthoring.cs index 0895dc870..8b338b34d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerRodMeshAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerRodMeshAuthoring.cs @@ -32,15 +32,5 @@ public class PlungerRodMeshAuthoring : PlungerMeshAuthoring public override IEnumerable ValidParents => ValidParentTypes; protected override string MeshId => PlungerMeshGenerator.Rod; - - internal override void SetChildEntity(ref PlungerStaticData staticData, Entity entity) - { - staticData.RodEntity = entity; - } - - protected override IEnumerable GetVertices(PlungerMeshGenerator meshGenerator, int frame) - { - return meshGenerator.BuildRodVertices(frame); - } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerSpringMeshAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerSpringMeshAuthoring.cs index 62fce4b8b..58fdf8817 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerSpringMeshAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerSpringMeshAuthoring.cs @@ -32,15 +32,5 @@ public class PlungerSpringMeshAuthoring : PlungerMeshAuthoring public override IEnumerable ValidParents => ValidParentTypes; protected override string MeshId => PlungerMeshGenerator.Spring; - - internal override void SetChildEntity(ref PlungerStaticData staticData, Entity entity) - { - staticData.SpringEntity = entity; - } - - protected override IEnumerable GetVertices(PlungerMeshGenerator meshGenerator, int frame) - { - return meshGenerator.BuildSpringVertices(frame); - } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerStaticData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerStaticData.cs index 2ba17f5c4..d686a06ec 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerStaticData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerStaticData.cs @@ -20,11 +20,6 @@ namespace VisualPinball.Unity { internal struct PlungerStaticData : IComponentData { - // general - public Entity RodEntity; - public Entity SpringEntity; - public Entity FlatEntity; - // collision public float MomentumXfer; public float ScatterVelocity; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerTransformationSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerTransformationSystem.cs index 626371379..2cecc5280 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerTransformationSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerTransformationSystem.cs @@ -17,39 +17,37 @@ using Unity.Entities; using Unity.Mathematics; using Unity.Profiling; -using Unity.Rendering; using UnityEngine; namespace VisualPinball.Unity { - // [UpdateInGroup(typeof(TransformMeshesSystemGroup))] - // internal class PlungerTransformationSystem : SystemBase - // { - // private static readonly ProfilerMarker PerfMarker = new ProfilerMarker("PlungerTransformationSystem"); - // private static readonly int LerpPosition = Shader.PropertyToID("_LerpPosition"); - // private static readonly int UVChannelVertices = Shader.PropertyToID("_UVChannelVertices"); - // private static readonly int UVChannelNormals = Shader.PropertyToID("_UVChannelNormals"); - // - // protected override void OnUpdate() - // { - // var marker = PerfMarker; - // - // Entities.WithoutBurst().ForEach((Entity entity, ref PlungerAnimationData animationData, in RenderMesh renderMesh) => { - // - // if (!animationData.IsDirty) { - // return; - // } - // animationData.IsDirty = false; - // - // marker.Begin(); - // - // var weight = math.clamp((float)animationData.CurrentFrame / animationData.NumFrames, 0, 1); - // renderMesh.material.SetFloat(LerpPosition, weight); - // renderMesh.material.SetFloat(UVChannelVertices, Engine.VPT.Mesh.AnimationUVChannelVertices); - // renderMesh.material.SetFloat(UVChannelNormals, Engine.VPT.Mesh.AnimationUVChannelNormals); - // marker.End(); - // - // }).Run(); - // } - // } + [UpdateInGroup(typeof(TransformMeshesSystemGroup))] + internal class PlungerTransformationSystem : SystemBase + { + private static readonly ProfilerMarker PerfMarker = new ProfilerMarker("PlungerTransformationSystem"); + + private Player _player; + + protected override void OnStartRunning() + { + base.OnStartRunning(); + _player = Object.FindObjectOfType(); + } + + protected override void OnUpdate() + { + var marker = PerfMarker; + + Entities.WithoutBurst().WithName("PlungerTransformationJob").ForEach((Entity entity, in PlungerAnimationData animationData) => { + + marker.Begin(); + + foreach (var skinnedMeshRenderer in _player.PlungerSkinnedMeshRenderers[entity]) { + skinnedMeshRenderer.SetBlendShapeWeight(0, animationData.Position); + } + marker.End(); + + }).Run(); + } + } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerUvBufferElement.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerUvBufferElement.cs deleted file mode 100644 index bed5b2fc1..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerUvBufferElement.cs +++ /dev/null @@ -1,32 +0,0 @@ -// 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 Unity.Entities; -using Unity.Mathematics; - -namespace VisualPinball.Unity -{ - [InternalBufferCapacity(1)] - internal struct PlungerUvBufferElement : IBufferElementData - { - public float2 Value; - - public PlungerUvBufferElement(float2 v) - { - Value = v; - } - } -} diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerUvBufferElement.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerUvBufferElement.cs.meta deleted file mode 100644 index 3d1565b22..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Plunger/PlungerUvBufferElement.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 7b6e73a52d48cf04d87b79234f446827 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From 7818bef4b7e7aa9d0ac176d91a3e38b27473263d Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 25 Jul 2021 00:42:50 +0200 Subject: [PATCH 08/14] fix: CI compilation error from silly import. --- VisualPinball.Unity/VisualPinball.Unity/Import/ConvertedItem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Import/ConvertedItem.cs b/VisualPinball.Unity/VisualPinball.Unity/Import/ConvertedItem.cs index 88e6ad22f..6f5ff7a45 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Import/ConvertedItem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Import/ConvertedItem.cs @@ -19,7 +19,6 @@ using System.Linq; using Unity.Entities; using UnityEngine; -using UnityEngine.UI; using VisualPinball.Engine.VPT; using Object = UnityEngine.Object; From 19a098632a82f75f3fe235cef349d8408694f320 Mon Sep 17 00:00:00 2001 From: freezy Date: Mon, 26 Jul 2021 00:20:58 +0200 Subject: [PATCH 09/14] style: Some cleanup. --- .../VPT/Bumper/BumperStaticData.cs | 2 -- .../VPT/Gate/GateStaticData.cs | 1 - .../VPT/Gate/GateWireAnimationAuthoring.cs | 24 +------------------ 3 files changed, 1 insertion(+), 26 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperStaticData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperStaticData.cs index 675cda308..6d2a56cfa 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperStaticData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperStaticData.cs @@ -23,7 +23,5 @@ internal struct BumperStaticData : IComponentData public float Force; public float Threshold; public bool HitEvent; - // public Entity RingEntity; - // public Entity SkirtEntity; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateStaticData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateStaticData.cs index 62a1887fa..4c220073a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateStaticData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateStaticData.cs @@ -26,6 +26,5 @@ internal struct GateStaticData : IComponentData public float GravityFactor; public float Damping; public bool TwoWay; - //public Entity WireEntity; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateWireAnimationAuthoring.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateWireAnimationAuthoring.cs index 893dee954..08d3973f7 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateWireAnimationAuthoring.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Gate/GateWireAnimationAuthoring.cs @@ -16,34 +16,12 @@ using System; using System.Collections.Generic; -using Unity.Entities; using VisualPinball.Engine.VPT.Gate; namespace VisualPinball.Unity { - public class GateWireAnimationAuthoring : ItemAnimationAuthoring, IConvertGameObjectToEntity + public class GateWireAnimationAuthoring : ItemAnimationAuthoring { public override IEnumerable ValidParents { get; } = new Type[0]; // animation components only apply to their own - - public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) - { - var gateEntity = MainEntity; - - // update parent - // var gateStaticData = dstManager.GetComponentData(gateEntity); - // gateStaticData.WireEntity = entity; - // dstManager.SetComponentData(gateEntity, gateStaticData); - - // add movement data - dstManager.AddComponentData(entity, new GateMovementData { - Angle = Data.AngleMin, - AngleSpeed = 0, - ForcedMove = false, - IsOpen = false, - HitDirection = false - }); - - LinkToParentEntity(entity, dstManager); - } } } From dbd224e8a32a7a97d6dd9f92fe67cbd738574d4a Mon Sep 17 00:00:00 2001 From: freezy Date: Tue, 27 Jul 2021 22:31:07 +0200 Subject: [PATCH 10/14] doc: Update CHANGELOG. --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5188d775e..8bf3b9593 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,11 @@ ## Unreleased -Built with [Unity 2020.2](https://github.com/freezy/VisualPinball.Engine/pull/255). +Built with Unity 2020.3. ### Added +- Remove Hybrid Renderer ([#316](https://github.com/freezy/VisualPinball.Engine/pull/316)). +- Create and use Unity assets when importing ([#320](https://github.com/freezy/VisualPinball.Engine/pull/302)). - Native support for nFozzy flipper physics ([#305](https://github.com/freezy/VisualPinball.Engine/pull/305)). - 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)). From 0117ce8491cad4b6bc01c262ebc655f4b909a831 Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 28 Jul 2021 21:55:37 +0200 Subject: [PATCH 11/14] doc: Remove hybrid renderer instructions. --- .../creators-guide/setup/running-vpe.md | 6 ------ .../setup/unity-settings-hybridv2.png | Bin 108792 -> 0 bytes 2 files changed, 6 deletions(-) delete mode 100644 VisualPinball.Unity/Documentation~/creators-guide/setup/unity-settings-hybridv2.png diff --git a/VisualPinball.Unity/Documentation~/creators-guide/setup/running-vpe.md b/VisualPinball.Unity/Documentation~/creators-guide/setup/running-vpe.md index 4130d082a..47a267ed2 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/setup/running-vpe.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/setup/running-vpe.md @@ -37,12 +37,6 @@ This camera can be moved [using Unity's gizmos](https://docs.unity3d.com/Manual/ > [!TIP] > A quick way to fix the game camera is to align it with the scene view camera. To do that, select the camera in the hierarchy, then click on the *GameObject* menu and select *Align with view*. -One last thing we need to do before playing is enable version 2 of the [Hybrid Renderer](https://docs.unity3d.com/Packages/com.unity.rendering.hybrid@0.10/manual/index.html) we're using. Go to *Edit -> Project Settings*, select *Player* on the left, open the *Other Settings* tab, scroll down a bit on the right and add `ENABLE_HYBRID_RENDERER_V2` under *Script Define Symbols*. - -![Enable hybrid renderer](unity-settings-hybridv2.png) - -Then click on *Apply* and close the window. - Let's start the game by clicking on the play button. This will run your scene. Test that the shift keys move the flippers. `ENTER` will launch a ball. If you expand *Table1* in the hierarchy and select the *Trough*, you can watch its status in the inspector in real time. Cool! You can also right-click on the scene view tab and select *Maximize*. diff --git a/VisualPinball.Unity/Documentation~/creators-guide/setup/unity-settings-hybridv2.png b/VisualPinball.Unity/Documentation~/creators-guide/setup/unity-settings-hybridv2.png deleted file mode 100644 index f46e31c26c9cb135966a551158ee7944c84ec7d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108792 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

{90 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⋙PpJO^6rw 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 jV!7zt{~VeB{l}3?K`+J@MYX Date: Wed, 28 Jul 2021 21:56:39 +0200 Subject: [PATCH 12/14] fix: Tests when compat flag enabled. --- .../VisualPinball.Unity.Test/VPT/TroughTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/TroughTests.cs b/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/TroughTests.cs index d5aacbad6..6dbf5bbf4 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/TroughTests.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/TroughTests.cs @@ -26,6 +26,9 @@ namespace VisualPinball.Unity.Test { public class TroughTests { + + #if !WRITE_VP106 && !WRITE_VP107 + [Test] public void ShouldWriteImportedTroughData() { @@ -40,5 +43,7 @@ public void ShouldWriteImportedTroughData() File.Delete(tmpFileName); Object.DestroyImmediate(go); } + + #endif } } From cf0b45808891dfc2189cce250813392aff419bd2 Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 28 Jul 2021 21:57:16 +0200 Subject: [PATCH 13/14] renderer: Assume ball size is 1 and scale accordingly. --- VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs index 9f2466f6b..6c83675b0 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallManager.cs @@ -75,7 +75,7 @@ public void CreateBall(IBallCreationPosition ballCreator, float radius, float ma var ballPrefab = RenderPipeline.Current.BallConverter.CreateDefaultBall(); var ballGo = Object.Instantiate(ballPrefab, _playfield.transform); ballGo.name = $"Ball{ballId}"; - ballGo.transform.localScale = new Vector3(1000, 1000, 1000); + ballGo.transform.localScale = new Vector3(radius, radius, radius) * 2f; ballGo.transform.localPosition = localPos; // create ball entity From 339df254d8efb5f1ab01751d9b45b5e388e5aae9 Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 28 Jul 2021 22:48:43 +0200 Subject: [PATCH 14/14] ball: Fix ball size of prefab. --- VisualPinball.Unity/Assets/Resources/Prefabs/DefaultBall.prefab | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VisualPinball.Unity/Assets/Resources/Prefabs/DefaultBall.prefab b/VisualPinball.Unity/Assets/Resources/Prefabs/DefaultBall.prefab index f6106a510..b0f27b09b 100644 --- a/VisualPinball.Unity/Assets/Resources/Prefabs/DefaultBall.prefab +++ b/VisualPinball.Unity/Assets/Resources/Prefabs/DefaultBall.prefab @@ -27,7 +27,7 @@ Transform: m_GameObject: {fileID: 8289283333368007096} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 0.05, y: 0.05, z: 0.05} + m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0