diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs index 66018dbb7..e64f1c14e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallData.cs @@ -26,7 +26,43 @@ internal struct BallData : IComponentData public float3 Position; public float3 EventPosition; // m_lastEventPos public float3 Velocity; + + /// + /// AngularVelocity - german: Winkelgeschwindigkeit + + /// * Set to 0 at Manual Roll + /// (in BallManualRoll(in Entity entity, in float3 targetWorldPosition) + /// (which is not used anywhere in this Project, but is at least used in Ravarcade's ImGui Physics Debugger - Addon) + /// * Is set to zero At RotatorComponent. Possibly an error and should be AngularVelocity + /// (in UpdateRotation(float angleDeg)) + /// * Calculated from AngularMomentum / inertia + /// (In BallDisplacementSystem.OnUpdate()) + /// (Where Inertia is a "constant" based on radius and mass (2/5 m r^2)) + /// * Used to get tangential velocity due to rotation when rolling / colliding on surfaces (alsways added to normal velocity) + /// (in BallData.SurfaceVelocity(in BallData ball, in float3 surfP)) + /// public float3 AngularVelocity; + /// + /// AngularMomentum - german: drehimpuls, Impulsmomemt + /// * Set to 0 at Manual Roll + /// (in BallManualRoll(in Entity entity, in float3 targetWorldPosition) + /// * Set to 0 at every new ball + /// (in Ballmanager.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) + /// * Set to 0 in KickerApi, KickerCollider and RotatorComponent + /// (in several places) + /// * Calculated when a survace applies an impulse, it applies it to velocity (div by mass) and to angMom fully. + /// (ApplySurfaceImpulse(in float3 rotI, in float3 impulse)) + /// (Where rotI seems to be the Rotation impulse and impulse is the (non angular)velocity (makes sense to divide by mass) + /// (angularMomenmtom = rotI;) + /// * used to calculate Angular Velocity (ball.AngularVelocity = ball.AngularMomentum / inertia;) + /// (in BalldisplacementSystem.OnUpdate()) + /// * used to add and thus calculate Orientation + /// (in BalldisplacementSystem.OnUpdate()) + /// skewSymmetricMatrix is created from the Angular Momentum divided by Inertia + /// The original orientation is multiplied with the skewSymmetric Matrix + /// and added to the old Orientation to form new orientation + /// + /// public float3 AngularMomentum; public float3x3 Orientation; public float Radius; @@ -72,7 +108,12 @@ public float CollisionRadiusSqr { } } + /// + /// Calculates Moment of Inertia for a Ball + /// https://en.wikipedia.org/wiki/Moment_of_inertia#Examples_2 + /// public float Inertia => 2.0f / 5.0f * Radius * Radius * Mass; + public float InvMass => 1f / Mass; public void ApplySurfaceImpulse(in float3 rotI, in float3 impulse) diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallDisplacementSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallDisplacementSystem.cs index 9ac1cd938..a20d7d805 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallDisplacementSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallDisplacementSystem.cs @@ -1,4 +1,4 @@ -// Visual Pinball Engine +// Visual Pinball Engineball.Orientation // Copyright (C) 2022 freezy and VPE Team // // This program is free software: you can redistribute it and/or modify @@ -57,6 +57,37 @@ protected override void OnUpdate() ball.Orientation += addedOrientation; math.orthonormalize(ball.Orientation); + // after Orthonormalization, the orientation vectors also have to be normalized - this is not done by othonomalize, since the skew matrix creates quite lengthy vectors. + // in fact, they dont have to be normalized, but just shortened, so we can abs-add the x, y and z together and just divide by the sum. + // This saves three sqrts in the game loop per ball. + float lengthX, lengthY, lengthZ; + /* Correct normalization would be: + * lengthX = math.sqrt(ball.Orientation.c0.x * ball.Orientation.c0.x + ball.Orientation.c0.y * ball.Orientation.c0.y + ball.Orientation.c0.z * ball.Orientation.c0.z); + * lengthY = math.sqrt(ball.Orientation.c1.x * ball.Orientation.c1.x + ball.Orientation.c1.y * ball.Orientation.c1.y + ball.Orientation.c1.z * ball.Orientation.c1.z); + * lengthZ = math.sqrt(ball.Orientation.c2.x * ball.Orientation.c2.x + ball.Orientation.c2.y * ball.Orientation.c2.y + ball.Orientation.c2.z * ball.Orientation.c2.z); + */ + lengthX = math.abs(ball.Orientation.c0.x) + math.abs(ball.Orientation.c0.y) + math.abs(ball.Orientation.c0.z); + lengthY = math.abs(ball.Orientation.c1.x) + math.abs(ball.Orientation.c1.y) + math.abs(ball.Orientation.c1.z); + lengthZ = math.abs(ball.Orientation.c2.x) + math.abs(ball.Orientation.c2.y) + math.abs(ball.Orientation.c2.z); + if (lengthX != 0f) + { + ball.Orientation.c0.x /= lengthX; + ball.Orientation.c0.y /= lengthX; + ball.Orientation.c0.z /= lengthX; + } + if (lengthY != 0f) + { + ball.Orientation.c1.x /= lengthY; + ball.Orientation.c1.y /= lengthY; + ball.Orientation.c1.z /= lengthY; + } + if (lengthZ != 0f) + { + ball.Orientation.c2.x /= lengthZ; + ball.Orientation.c2.y /= lengthZ; + ball.Orientation.c2.z /= lengthZ; + } + ball.AngularVelocity = ball.AngularMomentum / inertia; marker.End(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallMovementSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallMovementSystem.cs index 738d639cf..5a343a223 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallMovementSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallMovementSystem.cs @@ -58,11 +58,23 @@ protected override void OnUpdate() // calculate/adapt height of ball var zHeight = !ball.IsFrozen ? ball.Position.z : ball.Position.z - ball.Radius; - - var or = ball.Orientation; var ballTransform = _player.Balls[entity].transform; ballTransform.localPosition = new Vector3(ball.Position.x, ball.Position.y, zHeight); + + var or = ball.Orientation; + var vpright = new Vector3(or.c0.x, or.c1.x, or.c2.x); + var vpfront = new Vector3(or.c0.y, or.c1.y, or.c2.y); + var vptop = new Vector3(or.c0.z, or.c1.z, or.c2.z); + // Debug.Log("c0: (" + or.c0.x + ", " + or.c0.y + ", " + or.c0.z + ")"); + // Debug.Log("c1: (" + or.c1.x + ", " + or.c1.y + ", " + or.c1.z + ")"); + // Debug.Log("c2: (" + or.c2.x + ", " + or.c2.y + ", " + or.c2.z + ")"); + Vector3.OrthoNormalize(ref vptop, ref vpfront, ref vpright); + var unitytop = new Vector3(vptop.x, vptop.z, vptop.y); + var unityfront = new Vector3(vpfront.x, vpfront.z, vpfront.y); + + Vector3.OrthoNormalize(ref unitytop, ref unityfront); + // Following is the transistion from VP-Physics Ball Orientation to the Unity Ball-Orientation. // following statements: when looking at the backglass: // The problem here is, that we have @@ -81,7 +93,17 @@ protected override void OnUpdate() //1st iteration by (looks strange, but less strange) (cupiii) //ballTransform.localRotation = Quaternion.LookRotation(new Vector3(or.c0.x*-1, or.c1.x*-1, or.c2.x), new Vector3(or.c0.z*-1, or.c1.z*-1, or.c2.z)); //newest iteration (hopefully correct)) - ballTransform.localRotation = Quaternion.LookRotation(new Vector3(or.c0.z*1f, or.c2.z*1f, or.c1.z*1f), new Vector3(or.c0.y*1f, or.c2.y*1f, or.c1.y*1f)); + + // Better Ways than skew matrix: + // https://gamedev.stackexchange.com/questions/108920/applying-angular-velocity-to-quaternion + // https://stackoverflow.com/questions/23503151/how-to-update-quaternion-based-on-3d-gyro-data + // https://stackoverflow.com/questions/12053895/converting-angular-velocity-to-quaternion-in-opencv + + // also implementing VP's "Orthonormalize" Code - although it does not really orthonormalize could give a performance benefit: + // https://github.com/vpinball/vpinball/blob/be08b04d61096272df97bd45e6f0682043228a73/math/matrix.h#L208 + + ballTransform.localRotation = Quaternion.LookRotation(unityfront, unitytop); + marker.End();