Tracks #125
Replies: 6 comments 9 replies
-
nice tracks Are those link cylinders actual physics bodies, or are they just cosmetic and derived from the wheels? |
Beta Was this translation helpful? Give feedback.
-
yep, actual bodies. I wanted them to swing and follow geometry: video |
Beta Was this translation helpful? Give feedback.
-
Okay, here we go: video |
Beta Was this translation helpful? Give feedback.
-
Hello Ross, I have found a great way to optimize my track tech, but there's one little thing left. Can I have a custom variant of DistanceLimit without MaximumDistance (just to have a one-two less cpu instructions 🙃)? So the only thing, that will prevent links from passing through wheels, is that constraint (and not clumsy mess of welded wheels of different radii). |
Beta Was this translation helpful? Give feedback.
-
hooray, it works. using BepuUtilities;
using BepuUtilities.Memory;
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using BepuPhysics;
using BepuPhysics.Constraints;
using static BepuUtilities.GatherScatter;
namespace Engine.Physics.CustomConstraints
{
/// <summary>
/// Constrains points on two bodies to be separated by a minimum distance.
/// </summary>
public struct DistanceMinLimit : ITwoBodyConstraintDescription<DistanceMinLimit>
{
/// <summary>
/// Local offset from the center of body A to its attachment point.
/// </summary>
public Vector3 LocalOffsetA;
/// <summary>
/// Local offset from the center of body B to its attachment point.
/// </summary>
public Vector3 LocalOffsetB;
/// <summary>
/// Minimum distance permitted between the point on A and the point on B.
/// </summary>
public float MinimumDistance;
/// <summary>
/// Spring frequency and damping parameters.
/// </summary>
public SpringSettings SpringSettings;
/// <summary>
/// Creates a distance limit description.
/// </summary>
/// <param name="localOffsetA">Local offset from the center of body A to its attachment point.</param>
/// <param name="localOffsetB">Local offset from the center of body B to its attachment point.</param>
/// <param name="minimumDistance">Minimum distance permitted between the point on A and the point on B.</param>
/// <param name="springSettings">Spring frequency and damping parameters.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DistanceMinLimit(in Vector3 localOffsetA, in Vector3 localOffsetB, float minimumDistance, in SpringSettings springSettings)
{
LocalOffsetA = localOffsetA;
LocalOffsetB = localOffsetB;
MinimumDistance = minimumDistance;
SpringSettings = springSettings;
}
public int ConstraintTypeId
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return DistanceLimitTypeProcessor.BatchTypeId;
}
}
public Type TypeProcessorType => typeof(DistanceLimitTypeProcessor);
public void ApplyDescription(ref TypeBatch batch, int bundleIndex, int innerIndex)
{
Debug.Assert(MinimumDistance >= 0, "DistanceLimit.MinimumDistance must be nonnegative.");
ConstraintChecker.AssertValid(SpringSettings, nameof(DistanceLimit));
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<DistanceMinLimitPrestepData>.Get(ref batch.PrestepData, bundleIndex), innerIndex);
Vector3Wide.WriteFirst(LocalOffsetA, ref target.LocalOffsetA);
Vector3Wide.WriteFirst(LocalOffsetB, ref target.LocalOffsetB);
GatherScatter.GetFirst(ref target.MinimumDistance) = MinimumDistance;
SpringSettingsWide.WriteFirst(SpringSettings, ref target.SpringSettings);
}
public void BuildDescription(ref TypeBatch batch, int bundleIndex, int innerIndex, out DistanceMinLimit 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<DistanceMinLimitPrestepData>.Get(ref batch.PrestepData, bundleIndex), innerIndex);
Vector3Wide.ReadFirst(source.LocalOffsetA, out description.LocalOffsetA);
Vector3Wide.ReadFirst(source.LocalOffsetB, out description.LocalOffsetB);
description.MinimumDistance = GatherScatter.GetFirst(ref source.MinimumDistance);
SpringSettingsWide.ReadFirst(source.SpringSettings, out description.SpringSettings);
}
}
public struct DistanceMinLimitPrestepData
{
public Vector3Wide LocalOffsetA;
public Vector3Wide LocalOffsetB;
public Vector<float> MinimumDistance;
public SpringSettingsWide SpringSettings;
}
public struct DistanceMinLimitFunctions : IConstraintFunctions<DistanceMinLimitPrestepData, DistanceLimitProjection, Vector<float>>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Prestep(Bodies bodies, ref TwoBodyReferences bodyReferences, int count, float dt, float inverseDt, ref BodyInertias inertiaA, ref BodyInertias inertiaB,
ref DistanceMinLimitPrestepData prestep, out DistanceLimitProjection projection)
{
DistanceServoFunctions.GetDistance(bodies, ref bodyReferences, count, prestep.LocalOffsetA, prestep.LocalOffsetB,
out var anchorOffsetA, out var anchorOffsetB, out var anchorOffset, out var distance);
//If the current distance is closer to the minimum, calibrate for the minimum.
Vector3Wide.Scale(anchorOffset, new Vector<float>(-1f) / distance, out var direction);
DistanceServoFunctions.ComputeTransforms(inertiaA, inertiaB, anchorOffsetA, anchorOffsetB, distance, ref direction, dt,
prestep.SpringSettings, out var positionErrorToVelocity, out projection.SoftnessImpulseScale, out var effectiveMass,
out projection.LinearVelocityToImpulseA, out projection.AngularVelocityToImpulseA, out projection.AngularVelocityToImpulseB,
out projection.LinearImpulseToVelocityA, out projection.AngularImpulseToVelocityA, out projection.LinearImpulseToVelocityB, out projection.AngularImpulseToVelocityB);
//Compute the position error and bias velocities. Note the order of subtraction when calculating error- we want the bias velocity to counteract the separation.
var error = prestep.MinimumDistance - distance;
InequalityHelpers.ComputeBiasVelocity(error, positionErrorToVelocity, inverseDt, out var biasVelocity);
projection.BiasImpulse = biasVelocity * effectiveMass;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WarmStart(ref BodyVelocities velocityA, ref BodyVelocities velocityB, ref DistanceLimitProjection projection, ref Vector<float> accumulatedImpulse)
{
DistanceServoFunctions.ApplyImpulse(ref velocityA, ref velocityB,
projection.LinearImpulseToVelocityA, projection.AngularImpulseToVelocityA, projection.LinearImpulseToVelocityB, projection.AngularImpulseToVelocityB, ref accumulatedImpulse);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Solve(ref BodyVelocities velocityA, ref BodyVelocities velocityB, ref DistanceLimitProjection projection, ref Vector<float> accumulatedImpulse)
{
//csi = projection.BiasImpulse - accumulatedImpulse * projection.SoftnessImpulseScale - (csiaLinear + csiaAngular + csibLinear + csibAngular);
Vector3Wide.Dot(velocityA.Linear, projection.LinearVelocityToImpulseA, out var linearCSIA);
Vector3Wide.Dot(velocityB.Linear, projection.LinearVelocityToImpulseA, out var negatedLinearCSIB);
Vector3Wide.Dot(velocityA.Angular, projection.AngularVelocityToImpulseA, out var angularCSIA);
Vector3Wide.Dot(velocityB.Angular, projection.AngularVelocityToImpulseB, out var angularCSIB);
var csi = projection.BiasImpulse - accumulatedImpulse * projection.SoftnessImpulseScale - (linearCSIA + angularCSIA - negatedLinearCSIB + angularCSIB);
InequalityHelpers.ClampPositive(ref accumulatedImpulse, ref csi);
DistanceServoFunctions.ApplyImpulse(ref velocityA, ref velocityB,
projection.LinearImpulseToVelocityA, projection.AngularImpulseToVelocityA, projection.LinearImpulseToVelocityB, projection.AngularImpulseToVelocityB, ref csi);
}
}
/// <summary>
/// Handles the solve iterations of a bunch of distance servos.
/// </summary>
public class DistanceLimitTypeProcessor : TwoBodyTypeProcessor<DistanceMinLimitPrestepData, DistanceLimitProjection, Vector<float>, DistanceMinLimitFunctions>
{
public const int BatchTypeId = 70;
}
} |
Beta Was this translation helpful? Give feedback.
-
got myself some tracks
But sometimes it fails ಥ_ಥ
So these link-cylinders capture how track swings. There are two welded wheels: original one, that links touch, and invisible actual one with radius = original radius + link radius * 2, and length = link length, that actually rolls on the ground
Beta Was this translation helpful? Give feedback.
All reactions