-
-
Notifications
You must be signed in to change notification settings - Fork 277
/
TwistMotor.cs
133 lines (116 loc) · 7.55 KB
/
TwistMotor.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
using BepuUtilities;
using BepuUtilities.Memory;
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using static BepuUtilities.GatherScatter;
namespace BepuPhysics.Constraints
{
/// <summary>
/// Constrains the twist velocity between two bodies to a target.
/// </summary>
public struct TwistMotor : ITwoBodyConstraintDescription<TwistMotor>
{
/// <summary>
/// Local twist axis attached to body A.
/// </summary>
public Vector3 LocalAxisA;
/// <summary>
/// Local twist axis attached to body B.
/// </summary>
public Vector3 LocalAxisB;
/// <summary>
/// Goal relative twist velocity around the body axes.
/// </summary>
public float TargetVelocity;
/// <summary>
/// Motor control parameters.
/// </summary>
public MotorSettings Settings;
public static int ConstraintTypeId
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return TwistMotorTypeProcessor.BatchTypeId;
}
}
public static Type TypeProcessorType => typeof(TwistMotorTypeProcessor);
public static TypeProcessor CreateTypeProcessor() => new TwistMotorTypeProcessor();
public readonly void ApplyDescription(ref TypeBatch batch, int bundleIndex, int innerIndex)
{
ConstraintChecker.AssertUnitLength(LocalAxisA, nameof(TwistMotor), nameof(LocalAxisA));
ConstraintChecker.AssertUnitLength(LocalAxisB, nameof(TwistMotor), nameof(LocalAxisB));
ConstraintChecker.AssertValid(Settings, nameof(TwistMotor));
Debug.Assert(ConstraintTypeId == batch.TypeId, "The type batch passed to the description must match the description's expected type.");
ref var target = ref GetOffsetInstance(ref Buffer<TwistMotorPrestepData>.Get(ref batch.PrestepData, bundleIndex), innerIndex);
Vector3Wide.WriteFirst(LocalAxisA, ref target.LocalAxisA);
Vector3Wide.WriteFirst(LocalAxisB, ref target.LocalAxisB);
GetFirst(ref target.TargetVelocity) = TargetVelocity;
MotorSettingsWide.WriteFirst(Settings, ref target.Settings);
}
public static void BuildDescription(ref TypeBatch batch, int bundleIndex, int innerIndex, out TwistMotor description)
{
Debug.Assert(ConstraintTypeId == batch.TypeId, "The type batch passed to the description must match the description's expected type.");
ref var source = ref GetOffsetInstance(ref Buffer<TwistMotorPrestepData>.Get(ref batch.PrestepData, bundleIndex), innerIndex);
Vector3Wide.ReadFirst(source.LocalAxisA, out description.LocalAxisA);
Vector3Wide.ReadFirst(source.LocalAxisB, out description.LocalAxisB);
description.TargetVelocity = GetFirst(ref source.TargetVelocity);
MotorSettingsWide.ReadFirst(source.Settings, out description.Settings);
}
}
public struct TwistMotorPrestepData
{
public Vector3Wide LocalAxisA;
public Vector3Wide LocalAxisB;
public Vector<float> TargetVelocity;
public MotorSettingsWide Settings;
}
public struct TwistMotorFunctions : ITwoBodyConstraintFunctions<TwistMotorPrestepData, Vector<float>>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ComputeJacobian(in QuaternionWide orientationA, in QuaternionWide orientationB, in Vector3Wide localAxisA, in Vector3Wide localAxisB, out Vector3Wide jacobianA)
{
//We don't need any measurement basis in a velocity motor, so the prestep data needs only the axes.
QuaternionWide.TransformWithoutOverlap(localAxisA, orientationA, out var axisA);
QuaternionWide.TransformWithoutOverlap(localAxisB, orientationB, out var axisB);
Vector3Wide.Add(axisA, axisB, out jacobianA);
Vector3Wide.Length(jacobianA, out var length);
Vector3Wide.Scale(jacobianA, Vector<float>.One / length, out jacobianA);
Vector3Wide.ConditionalSelect(Vector.LessThan(length, new Vector<float>(1e-10f)), axisA, jacobianA, out jacobianA);
}
public static void WarmStart(in Vector3Wide positionA, in QuaternionWide orientationA, in BodyInertiaWide inertiaA, in Vector3Wide positionB, in QuaternionWide orientationB, in BodyInertiaWide inertiaB, ref TwistMotorPrestepData prestep, ref Vector<float> accumulatedImpulses, ref BodyVelocityWide wsvA, ref BodyVelocityWide wsvB)
{
ComputeJacobian(orientationA, orientationB, prestep.LocalAxisA, prestep.LocalAxisB, out var jacobianA);
Symmetric3x3Wide.TransformWithoutOverlap(jacobianA, inertiaA.InverseInertiaTensor, out var impulseToVelocityA);
Symmetric3x3Wide.TransformWithoutOverlap(jacobianA, inertiaB.InverseInertiaTensor, out var negatedImpulseToVelocityB);
TwistServoFunctions.ApplyImpulse(ref wsvA.Angular, ref wsvB.Angular, impulseToVelocityA, negatedImpulseToVelocityB, accumulatedImpulses);
}
public static void Solve(in Vector3Wide positionA, in QuaternionWide orientationA, in BodyInertiaWide inertiaA, in Vector3Wide positionB, in QuaternionWide orientationB, in BodyInertiaWide inertiaB, float dt, float inverseDt, ref TwistMotorPrestepData prestep, ref Vector<float> accumulatedImpulses, ref BodyVelocityWide wsvA, ref BodyVelocityWide wsvB)
{
ComputeJacobian(orientationA, orientationB, prestep.LocalAxisA, prestep.LocalAxisB, out var jacobianA);
TwistServoFunctions.ComputeEffectiveMassContributions(inertiaA.InverseInertiaTensor, inertiaB.InverseInertiaTensor, jacobianA,
out var impulseToVelocityA, out var negatedImpulseToVelocityB, out var unsoftenedInverseEffectiveMass);
MotorSettingsWide.ComputeSoftness(prestep.Settings, dt, out var effectiveMassCFMScale, out var softnessImpulseScale, out var maximumImpulse);
var effectiveMass = effectiveMassCFMScale / unsoftenedInverseEffectiveMass;
Vector3Wide.Scale(jacobianA, effectiveMass, out var velocityToImpulseA);
var biasImpulse = prestep.TargetVelocity * effectiveMass;
Vector3Wide.Subtract(wsvA.Angular, wsvB.Angular, out var netVelocity);
Vector3Wide.Dot(netVelocity, velocityToImpulseA, out var csiVelocityComponent);
//csi = projection.BiasImpulse - accumulatedImpulse * projection.SoftnessImpulseScale - (csiaLinear + csiaAngular + csibLinear + csibAngular);
var csi = biasImpulse - accumulatedImpulses * softnessImpulseScale - csiVelocityComponent;
var previousAccumulatedImpulse = accumulatedImpulses;
accumulatedImpulses = Vector.Max(Vector.Min(accumulatedImpulses + csi, maximumImpulse), -maximumImpulse);
csi = accumulatedImpulses - previousAccumulatedImpulse;
TwistServoFunctions.ApplyImpulse(ref wsvA.Angular, ref wsvB.Angular, impulseToVelocityA, negatedImpulseToVelocityB, csi);
}
public static bool RequiresIncrementalSubstepUpdates => false;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IncrementallyUpdateForSubstep(in Vector<float> dt, in BodyVelocityWide wsvA, in BodyVelocityWide wsvB, ref TwistMotorPrestepData prestepData) { }
}
public class TwistMotorTypeProcessor : TwoBodyTypeProcessor<TwistMotorPrestepData, Vector<float>, TwistMotorFunctions, AccessOnlyAngular, AccessOnlyAngular, AccessOnlyAngular, AccessOnlyAngular>
{
public const int BatchTypeId = 28;
}
}