Add Drag and StretchLimit to SplineRopeBody for more advanced simulation control
This commit is contained in:
@@ -3,12 +3,10 @@
|
||||
#include "SplineRopeBody.h"
|
||||
#include "Engine/Level/Actors/Spline.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Physics/Physics.h"
|
||||
#include "Engine/Physics/PhysicsScene.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
|
||||
SplineRopeBody::SplineRopeBody(const SpawnParams& params)
|
||||
: Actor(params)
|
||||
@@ -28,7 +26,7 @@ void SplineRopeBody::Tick()
|
||||
const Transform splineTransform = _spline->GetTransform();
|
||||
const int32 keyframesCount = keyframes.Count();
|
||||
const float substepTime = SubstepTime;
|
||||
const float substepTimeSqr = substepTime * substepTime;
|
||||
// TODO: scale substep time based on distance to the camera to have better performance when rope is far away
|
||||
bool splineDirty = false;
|
||||
|
||||
// Synchronize spline keyframes with simulated masses
|
||||
@@ -41,7 +39,7 @@ void SplineRopeBody::Tick()
|
||||
{
|
||||
const int32 i = _masses.Count();
|
||||
auto& mass = _masses.AddOne();
|
||||
mass.PrevPosition = splineTransform.LocalToWorld(keyframes[i].Value.Translation);
|
||||
mass.Position = mass.PrevPosition = splineTransform.LocalToWorld(keyframes[i].Value.Translation);
|
||||
if (i != 0)
|
||||
mass.SegmentLength = Vector3::Distance(mass.PrevPosition, _masses[i - 1].PrevPosition);
|
||||
else
|
||||
@@ -51,7 +49,7 @@ void SplineRopeBody::Tick()
|
||||
{
|
||||
// Rope start
|
||||
auto& mass = _masses.First();
|
||||
mass.Position = mass.PrevPosition = GetPosition();
|
||||
mass.Position = GetPosition();
|
||||
mass.Unconstrained = false;
|
||||
if (splineTransform.LocalToWorld(keyframes.First().Value.Translation) != mass.Position)
|
||||
splineDirty = true;
|
||||
@@ -76,29 +74,29 @@ void SplineRopeBody::Tick()
|
||||
{
|
||||
// Rope end
|
||||
auto& mass = _masses.Last();
|
||||
mass.Position = mass.PrevPosition = AttachEnd->GetPosition();
|
||||
mass.Position = AttachEnd->GetPosition();
|
||||
mass.Unconstrained = false;
|
||||
if (splineTransform.LocalToWorld(keyframes.Last().Value.Translation) != mass.Position)
|
||||
splineDirty = true;
|
||||
}
|
||||
|
||||
// Perform simulation in substeps to have better stability
|
||||
_time += Time::Update.DeltaTime.GetTotalSeconds();
|
||||
_time += Time::Physics.DeltaTime.GetTotalSeconds();
|
||||
float stretchLimit = StretchLimit + 1;
|
||||
while (_time > substepTime)
|
||||
{
|
||||
// Verlet integration
|
||||
// [Reference: https://en.wikipedia.org/wiki/Verlet_integration]
|
||||
const Vector3 force = gravity + AdditionalForce;
|
||||
const Vector3 force = (gravity + AdditionalForce) * (substepTime * substepTime);
|
||||
for (int32 i = 0; i < keyframesCount; i++)
|
||||
{
|
||||
auto& mass = _masses[i];
|
||||
Vector3 position = mass.Position;
|
||||
if (mass.Unconstrained)
|
||||
{
|
||||
const Vector3 velocity = mass.Position - mass.PrevPosition;
|
||||
mass.PrevPosition = mass.Position;
|
||||
mass.Position = mass.Position + velocity + (substepTimeSqr * force);
|
||||
keyframes[i].Value.Translation = splineTransform.WorldToLocal(mass.Position);
|
||||
const Vector3 velocity = (mass.Position - mass.PrevPosition) * (1 - Drag);
|
||||
mass.Position += velocity + force;
|
||||
}
|
||||
mass.PrevPosition = position;
|
||||
}
|
||||
|
||||
// Distance constraint
|
||||
@@ -108,7 +106,11 @@ void SplineRopeBody::Tick()
|
||||
auto& massB = _masses[i];
|
||||
Vector3 offset = massB.Position - massA.Position;
|
||||
const Real distance = offset.Length();
|
||||
const Real scale = (distance - massB.SegmentLength) / Math::Max<Real>(distance, ZeroTolerance);
|
||||
const Real minDistance = massB.SegmentLength;
|
||||
const Real maxDistance = massB.SegmentLength * stretchLimit;
|
||||
if ((distance <= maxDistance && distance >= minDistance) || distance <= ZeroTolerance)
|
||||
continue;
|
||||
const Real scale = (distance - maxDistance) / distance;
|
||||
if (massA.Unconstrained && massB.Unconstrained)
|
||||
{
|
||||
offset *= scale * 0.5f;
|
||||
@@ -134,7 +136,10 @@ void SplineRopeBody::Tick()
|
||||
auto& massB = _masses[i];
|
||||
Vector3 offset = massB.Position - massA.Position;
|
||||
const Real distance = offset.Length();
|
||||
const Real scale = (distance - massB.SegmentLength * 2.0f) / Math::Max<Real>(distance, ZeroTolerance);
|
||||
const Real maxDistance = massB.SegmentLength * stretchLimit * 2.0f;
|
||||
if (distance <= ZeroTolerance)
|
||||
continue;
|
||||
const Real scale = (distance - maxDistance) / distance;
|
||||
if (massA.Unconstrained && massB.Unconstrained)
|
||||
{
|
||||
offset *= scale * 0.5f;
|
||||
|
||||
@@ -31,17 +31,29 @@ private:
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The target actor too attach the rope end to. If unset the rope end will run freely.
|
||||
/// The target actor to attach the rope end to. If unset the rope end will run freely.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(0), DefaultValue(null), EditorDisplay(\"Rope\")")
|
||||
ScriptingObjectReference<Actor> AttachEnd;
|
||||
|
||||
/// <summary>
|
||||
/// The world gravity scale applied to the rope. Can be used to adjust gravity force or disable it.
|
||||
/// The world gravity scale. Can be used to adjust gravity force or disable it.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"Rope\")")
|
||||
float GravityScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The scale of velocity applied to rope. The greater the value, the more suppressed the movement.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(15), EditorDisplay(\"Rope\"), Limit(0, 0.99f, 0.001f)")
|
||||
float Drag = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum bones stretch scale. The value of 0 means no stretch, 1 - can stretch up to double length.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes = "EditorOrder(19), EditorDisplay(\"Rope\"), Limit(0)")
|
||||
float StretchLimit = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The additional, external force applied to rope (world-space). This can be eg. wind force.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user