From a55ffa8ba874a5a3652d2fbb3b71c044ceda9671 Mon Sep 17 00:00:00 2001 From: Vantha Date: Fri, 3 Apr 2026 11:41:34 +0200 Subject: [PATCH] Complete the cinema path timescale implementation This patch fixes the previously broken implementation of the timescale property of cinema paths, now cinema paths with timescale 0.5, for example, play twice as fast. Unlike the comment stated, negative timescales weren't actually supported, which is now enforced. For demonstration, the timescale of the demo map's path is changed to 0.85. --- .../public/maps/scenarios/cinema_demo.xml | 2 +- source/simulation2/helpers/CinemaPath.cpp | 28 +++++++++++++------ source/simulation2/helpers/CinemaPath.h | 4 ++- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/binaries/data/mods/public/maps/scenarios/cinema_demo.xml b/binaries/data/mods/public/maps/scenarios/cinema_demo.xml index 18789a536e..768d8beca3 100644 --- a/binaries/data/mods/public/maps/scenarios/cinema_demo.xml +++ b/binaries/data/mods/public/maps/scenarios/cinema_demo.xml @@ -14063,7 +14063,7 @@ - + diff --git a/source/simulation2/helpers/CinemaPath.cpp b/source/simulation2/helpers/CinemaPath.cpp index 0e9b1830ff..51f6b35567 100644 --- a/source/simulation2/helpers/CinemaPath.cpp +++ b/source/simulation2/helpers/CinemaPath.cpp @@ -76,6 +76,12 @@ CCinemaPath::CCinemaPath(const CCinemaData& data, const TNSpline& spline, const LOGWARNING("Cinematic style not found for '%s'", data.m_Style.ToUTF8().c_str()); DistStylePtr = &CCinemaPath::EaseDefault; } + + if (m_Timescale <= fixed::FromInt(0)) + { + LOGWARNING("Invalid cinema path timescale specified: %f. It must be greater than 0.", m_Timescale.ToFloat()); + m_Timescale = fixed::FromInt(1); + } } CVector3D CCinemaPath::GetNodePosition(const int index) const @@ -85,17 +91,17 @@ CVector3D CCinemaPath::GetNodePosition(const int index) const fixed CCinemaPath::GetNodeDuration(const int index) const { - return m_Nodes[index].Distance; + return m_Nodes[index].Distance.Multiply(m_Timescale); } fixed CCinemaPath::GetDuration() const { - return GetMaxDistance(); + return GetMaxDistance().Multiply(m_Timescale); } float CCinemaPath::GetNodeFraction() const { - return (m_TimeElapsed - m_PreviousNodeTime) / m_Nodes[m_CurrentNode].Distance.ToFloat(); + return (m_TimeElapsed - m_PreviousNodeTime) / GetNodeDuration(m_CurrentNode).ToFloat(); } float CCinemaPath::GetElapsedTime() const @@ -110,7 +116,10 @@ const CStrW& CCinemaPath::GetName() const void CCinemaPath::SetTimescale(fixed scale) { - m_Timescale = scale; + if (scale <= fixed::FromInt(0)) + LOGWARNING("Invalid cinema path timescale specified: %f. It must be greater than 0.", scale.ToFloat()); + else + m_Timescale = scale; } void CCinemaPath::MoveToPointAt(float t, float nodet, const CVector3D& startRotation, CCamera& camera) const @@ -121,8 +130,9 @@ void CCinemaPath::MoveToPointAt(float t, float nodet, const CVector3D& startRota if (m_LookAtTarget) { - if (m_TimeElapsed <= m_TargetSpline.GetMaxDistance().ToFloat()) - camera.LookAt(pos, m_TargetSpline.GetPosition(m_TimeElapsed / m_TargetSpline.GetMaxDistance().ToFloat()), CVector3D(0, 1, 0)); + float targetDuration = m_TargetSpline.GetMaxDistance().Multiply(m_Timescale).ToFloat(); + if (m_TimeElapsed <= targetDuration) + camera.LookAt(pos, m_TargetSpline.GetPosition(m_TimeElapsed / targetDuration), CVector3D(0, 1, 0)); else camera.LookAt(pos, m_TargetSpline.GetAllNodes().back().Position, CVector3D(0, 1, 0)); } @@ -222,7 +232,7 @@ bool CCinemaPath::Validate() // Ignore the last node, since it is a blank (node time values are shifted down one from interface) for (size_t i = 0; i < m_Nodes.size() - 1; ++i) { - cumulation += m_Nodes[i].Distance.ToFloat(); + cumulation += GetNodeDuration(i).ToFloat(); if (m_TimeElapsed <= cumulation) { m_PreviousNodeTime = previousTime; @@ -230,7 +240,7 @@ bool CCinemaPath::Validate() m_CurrentNode = i; // We're moving toward this next node, so use its rotation return true; } - previousTime += m_Nodes[i].Distance.ToFloat(); + previousTime += GetNodeDuration(i).ToFloat(); } debug_warn("validation of cinema path is wrong\n"); return false; @@ -238,7 +248,7 @@ bool CCinemaPath::Validate() bool CCinemaPath::Play(const float deltaRealTime, CCamera& camera) { - m_TimeElapsed += m_Timescale.ToFloat() * deltaRealTime; + m_TimeElapsed += deltaRealTime; if (!Validate()) return false; diff --git a/source/simulation2/helpers/CinemaPath.h b/source/simulation2/helpers/CinemaPath.h index c1d6be49af..cca61cf040 100644 --- a/source/simulation2/helpers/CinemaPath.h +++ b/source/simulation2/helpers/CinemaPath.h @@ -42,7 +42,9 @@ public: bool m_LookAtTarget; - fixed m_Timescale; // a negative timescale results in backwards play + // A value by which the total duration is multiplied, e.g. values less than 1 result in the path + // playing faster. Must be greater than 0. + fixed m_Timescale; // Distortion variables mutable float m_GrowthCount;