Refactor engine loop to have better sync between game update, physics and drawing
This commit is contained in:
@@ -10,7 +10,6 @@ void InverseKinematics::SolveAimIK(const Transform& node, const Vector3& target,
|
||||
Quaternion::FindBetween(fromNode, toTarget, outNodeCorrection);
|
||||
}
|
||||
|
||||
|
||||
void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJointTransform, Transform& endEffectorTransform, const Vector3& targetPosition, const Vector3& poleVector, bool allowStretching, float maxStretchScale)
|
||||
{
|
||||
// Calculate limb segment lengths
|
||||
@@ -82,17 +81,20 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
||||
Vector3 newEndEffectorPos = targetPosition;
|
||||
Vector3 newMidJointPos = midJointPos;
|
||||
|
||||
if (toTargetLength >= totalLimbLength) {
|
||||
if (toTargetLength >= totalLimbLength)
|
||||
{
|
||||
// Target is beyond the reach of the limb
|
||||
Vector3 rootToEnd = (targetPosition - rootTransform.Translation).GetNormalized();
|
||||
|
||||
// Calculate the slight offset towards the pole vector
|
||||
Vector3 rootToPole = (poleVector - rootTransform.Translation).GetNormalized();
|
||||
Vector3 slightBendDirection = Vector3::Cross(rootToEnd, rootToPole);
|
||||
if (slightBendDirection.LengthSquared() < ZeroTolerance * ZeroTolerance) {
|
||||
if (slightBendDirection.LengthSquared() < ZeroTolerance * ZeroTolerance)
|
||||
{
|
||||
slightBendDirection = Vector3::Up;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
slightBendDirection.Normalize();
|
||||
}
|
||||
|
||||
@@ -140,7 +142,6 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
||||
rootTransform.Orientation = newRootJointOrientation;
|
||||
}
|
||||
|
||||
|
||||
// Update mid joint orientation to point Y-axis towards the end effector and Z-axis perpendicular to the IK plane
|
||||
{
|
||||
// Vector from mid joint to end effector (local Y-axis direction after rotation)
|
||||
@@ -150,7 +151,6 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
||||
Vector3 rootToMid = (newMidJointPos - rootTransform.Translation).GetNormalized();
|
||||
Vector3 planeNormal = Vector3::Cross(rootToMid, midToEnd).GetNormalized();
|
||||
|
||||
|
||||
// Vector from mid joint to end effector (local Y-axis direction)
|
||||
Vector3 localY = (newEndEffectorPos - newMidJointPos).GetNormalized();
|
||||
|
||||
@@ -163,7 +163,6 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
||||
// Correct the local Z-axis direction based on the cross product of X and Y to ensure orthogonality
|
||||
localZ = Vector3::Cross(localX, localY).GetNormalized();
|
||||
|
||||
|
||||
// Construct a rotation from the orthogonal basis vectors
|
||||
// The axes are used differently here than a standard LookRotation to align Z towards the end and Y perpendicular
|
||||
Quaternion newMidJointOrientation = Quaternion::LookRotation(localZ, localY); // Assuming FromLookRotation creates a rotation with the first vector as forward and the second as up
|
||||
|
||||
@@ -192,8 +192,11 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
OnUnpause();
|
||||
}
|
||||
|
||||
// Use the same time for all ticks to improve synchronization
|
||||
const double time = Platform::GetTimeSeconds();
|
||||
|
||||
// Update game logic
|
||||
if (Time::OnBeginUpdate())
|
||||
if (Time::OnBeginUpdate(time))
|
||||
{
|
||||
OnUpdate();
|
||||
OnLateUpdate();
|
||||
@@ -201,7 +204,7 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
}
|
||||
|
||||
// Start physics simulation
|
||||
if (Time::OnBeginPhysics())
|
||||
if (Time::OnBeginPhysics(time))
|
||||
{
|
||||
OnFixedUpdate();
|
||||
OnLateFixedUpdate();
|
||||
@@ -209,7 +212,7 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
}
|
||||
|
||||
// Draw frame
|
||||
if (Time::OnBeginDraw())
|
||||
if (Time::OnBeginDraw(time))
|
||||
{
|
||||
OnDraw();
|
||||
Time::OnEndDraw();
|
||||
|
||||
@@ -72,10 +72,9 @@ void Time::TickData::OnReset(float targetFps, double currentTime)
|
||||
LastEnd = currentTime;
|
||||
}
|
||||
|
||||
bool Time::TickData::OnTickBegin(float targetFps, float maxDeltaTime)
|
||||
bool Time::TickData::OnTickBegin(double time, float targetFps, float maxDeltaTime)
|
||||
{
|
||||
// Check if can perform a tick
|
||||
const double time = Platform::GetTimeSeconds();
|
||||
double deltaTime;
|
||||
if (FixedDeltaTimeEnable)
|
||||
{
|
||||
@@ -126,10 +125,9 @@ void Time::TickData::Advance(double time, double deltaTime)
|
||||
TicksCount++;
|
||||
}
|
||||
|
||||
bool Time::FixedStepTickData::OnTickBegin(float targetFps, float maxDeltaTime)
|
||||
bool Time::FixedStepTickData::OnTickBegin(double time, float targetFps, float maxDeltaTime)
|
||||
{
|
||||
// Check if can perform a tick
|
||||
double time = Platform::GetTimeSeconds();
|
||||
double deltaTime, minDeltaTime;
|
||||
if (FixedDeltaTimeEnable)
|
||||
{
|
||||
@@ -249,9 +247,9 @@ void Time::Synchronize()
|
||||
Draw.Synchronize(DrawFPS, time);
|
||||
}
|
||||
|
||||
bool Time::OnBeginUpdate()
|
||||
bool Time::OnBeginUpdate(double time)
|
||||
{
|
||||
if (Update.OnTickBegin(UpdateFPS, MaxUpdateDeltaTime))
|
||||
if (Update.OnTickBegin(time, UpdateFPS, MaxUpdateDeltaTime))
|
||||
{
|
||||
Current = &Update;
|
||||
return true;
|
||||
@@ -259,9 +257,9 @@ bool Time::OnBeginUpdate()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Time::OnBeginPhysics()
|
||||
bool Time::OnBeginPhysics(double time)
|
||||
{
|
||||
if (Physics.OnTickBegin(PhysicsFPS, _physicsMaxDeltaTime))
|
||||
if (Physics.OnTickBegin(time, PhysicsFPS, _physicsMaxDeltaTime))
|
||||
{
|
||||
Current = &Physics;
|
||||
return true;
|
||||
@@ -269,9 +267,9 @@ bool Time::OnBeginPhysics()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Time::OnBeginDraw()
|
||||
bool Time::OnBeginDraw(double time)
|
||||
{
|
||||
if (Draw.OnTickBegin(DrawFPS, 1.0f))
|
||||
if (Draw.OnTickBegin(time, DrawFPS, 1.0f))
|
||||
{
|
||||
Current = &Draw;
|
||||
return true;
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
/// </summary>
|
||||
API_CLASS(Static) class FLAXENGINE_API Time
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Time);
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Time);
|
||||
friend class Engine;
|
||||
friend class TimeService;
|
||||
friend class PhysicsSettings;
|
||||
public:
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Engine subsystem updating data.
|
||||
/// Used to invoke game logic updates, physics updates and rendering with possibly different frequencies.
|
||||
@@ -25,7 +25,6 @@ public:
|
||||
class FLAXENGINE_API TickData
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~TickData() = default;
|
||||
|
||||
/// <summary>
|
||||
@@ -75,14 +74,12 @@ public:
|
||||
TimeSpan UnscaledTime;
|
||||
|
||||
public:
|
||||
|
||||
virtual void OnBeforeRun(float targetFps, double currentTime);
|
||||
virtual void Synchronize(float targetFps, double currentTime);
|
||||
virtual void OnReset(float targetFps, double currentTime);
|
||||
virtual bool OnTickBegin(float targetFps, float maxDeltaTime);
|
||||
virtual bool OnTickBegin(double time, float targetFps, float maxDeltaTime);
|
||||
virtual void OnTickEnd();
|
||||
|
||||
protected:
|
||||
|
||||
void Advance(double time, double deltaTime);
|
||||
};
|
||||
|
||||
@@ -92,25 +89,21 @@ public:
|
||||
class FixedStepTickData : public TickData
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The last few ticks delta times. Used to check if can use fixed steps or whenever is running slowly so should use normal stepping.
|
||||
/// </summary>
|
||||
SamplesBuffer<double, 4> Samples;
|
||||
|
||||
public:
|
||||
|
||||
// [TickData]
|
||||
bool OnTickBegin(float targetFps, float maxDeltaTime) override;
|
||||
bool OnTickBegin(double time, float targetFps, float maxDeltaTime) override;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
static bool _gamePaused;
|
||||
static float _physicsMaxDeltaTime;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The time at which the game started (UTC local).
|
||||
/// </summary>
|
||||
@@ -140,7 +133,6 @@ public:
|
||||
API_FIELD() static float TimeScale;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The game logic updating data.
|
||||
/// </summary>
|
||||
@@ -162,7 +154,6 @@ public:
|
||||
static TickData* Current;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current tick data (safety so returns Update tick data if no active).
|
||||
/// </summary>
|
||||
@@ -233,9 +224,9 @@ public:
|
||||
private:
|
||||
// Methods used by the Engine class
|
||||
|
||||
static bool OnBeginUpdate();
|
||||
static bool OnBeginPhysics();
|
||||
static bool OnBeginDraw();
|
||||
static bool OnBeginUpdate(double time);
|
||||
static bool OnBeginPhysics(double time);
|
||||
static bool OnBeginDraw(double time);
|
||||
|
||||
static void OnEndUpdate();
|
||||
static void OnEndPhysics();
|
||||
|
||||
Reference in New Issue
Block a user