Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4190296
Time offset for curve bindings
JuhDuh26 Oct 16, 2026
102d6ee
Merge remote-tracking branch 'upstream/main' into time_offset_bindings
JuhDuh26 Oct 19, 2026
1eb676d
Merge branch 'carbonengine:main' into time_offset_bindings
JuhDuh26 Oct 20, 2026
99611ee
Sample offset bindings without moving the shared curve
JuhDuh26 Oct 20, 2026
a656136
Test that offset bindings don't move a curve owned by another curve set
JuhDuh26 Oct 20, 2026
abb729d
Merge time offset binding review fixes
JuhDuh26 Oct 20, 2026
806cba3
Merge remote-tracking branch 'origin/time_offset_bindings' into time_…
JuhDuh26 Oct 20, 2026
a3e2abf
Tighten time offset binding contract and cover all curve types
JuhDuh26 Oct 20, 2026
ee938bc
Merge time offset binding contract and test fixes
JuhDuh26 Oct 20, 2026
cf4f10e
Speed up sampling offset bindings from a shared curve
JuhDuh26 Oct 20, 2026
4625023
Test offset bindings on a long curve sampled far apart
JuhDuh26 Oct 20, 2026
7beed05
Merge offset binding sampling optimisations
JuhDuh26 Oct 20, 2026
919c5d2
Give offset bindings their own key segment cache
JuhDuh26 Oct 20, 2026
d943fcb
Merge per-binding key segment cache
JuhDuh26 Oct 20, 2026
1ea5447
Keep curve evaluation's key lookup call-free on cache hits
JuhDuh26 Oct 20, 2026
48b318e
Merge call-free key lookup on cache hits
JuhDuh26 Oct 20, 2026
2717b08
Check the cached key segment inline before searching
JuhDuh26 Oct 20, 2026
01144a3
Merge inline cached segment check
JuhDuh26 Oct 20, 2026
ab290f6
Give offset bindings key caches for vector, colour and Euler curves
JuhDuh26 Oct 21, 2026
891ccd9
Merge key caches for vector, colour and Euler curves
JuhDuh26 Oct 21, 2026
870f2d0
Merge main into time_offset_bindings
JuhDuh26 Oct 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions trinity/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ set(_SOURCES
Curves/Tr2CurveScalar.cpp
Curves/Tr2CurveScalar.h
Curves/Tr2CurveScalar_Blue.cpp
Curves/Tr2CurveSegmentSearch.h
Curves/Tr2CurveScalarExpression.cpp
Curves/Tr2CurveScalarExpression.h
Curves/Tr2CurveScalarExpression_Blue.cpp
Expand Down Expand Up @@ -236,8 +237,11 @@ set(_SOURCES
Curves/Tr2TranslationAdapter.cpp
Curves/Tr2TranslationAdapter.h
Curves/Tr2TranslationAdapter_Blue.cpp
Curves/TriCurveSampler.cpp
Curves/TriCurveSampler.h
Curves/TriCurveSet.cpp
Curves/TriCurveSet.h
Curves/TriCurveSetTime.h
Curves/TriCurveSet_Blue.cpp
Curves/TriEventCurve.cpp
Curves/TriEventCurve.h
Expand Down
18 changes: 17 additions & 1 deletion trinity/Curves/Tr2CurveColor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,23 @@ float Tr2CurveColor::Length()
Color Tr2CurveColor::GetValue( double time ) const
{
double t = time - m_timeOffset;
Color color( m_r.GetValue( t ), m_g.GetValue( t ), m_b.GetValue( t ), m_a.IsEmpty() ? 1.0f : m_a.GetValue( t ) );
return ToOutput( Color( m_r.GetValue( t ), m_g.GetValue( t ), m_b.GetValue( t ), m_a.IsEmpty() ? 1.0f : m_a.GetValue( t ) ) );
}

// --------------------------------------------------------------------------------
Color Tr2CurveColor::SampleValue( double time, Tr2CurveChannelSegments& segmentCaches ) const
{
double t = time - m_timeOffset;
return ToOutput( Color(
m_r.SampleValue( t, segmentCaches[0] ),
m_g.SampleValue( t, segmentCaches[1] ),
m_b.SampleValue( t, segmentCaches[2] ),
m_a.IsEmpty() ? 1.0f : m_a.SampleValue( t, segmentCaches[3] ) ) );
}

// --------------------------------------------------------------------------------
Color Tr2CurveColor::ToOutput( Color color ) const
{
if( m_srgbOutput )
{
color.r = std::max( color.r, 0.f );
Expand Down
7 changes: 7 additions & 0 deletions trinity/Curves/Tr2CurveColor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#pragma once

#include "Tr2CurveScalar.h"
#include "Tr2CurveSegmentSearch.h"

#include <ITriFunction.h>
#include <ITriCurveLength.h>
Expand All @@ -26,6 +27,9 @@ BLUE_CLASS( Tr2CurveColor ) :
virtual float Length();

Color GetValue( double time ) const;
// The value at time, like GetValue, but looking each channel's keys up from
// the caller's segment caches (see Tr2CurveScalar::SampleValue).
Color SampleValue( double time, Tr2CurveChannelSegments& segmentCaches ) const;

void AddKey(
float time,
Expand All @@ -43,6 +47,9 @@ BLUE_CLASS( Tr2CurveColor ) :
PTr2CurveScalar m_a;

private:
// Channel values sampled at one time, converted to sRGB if m_srgbOutput is set
Color ToOutput( Color color ) const;

std::string m_name;

Color m_currentValue;
Expand Down
8 changes: 8 additions & 0 deletions trinity/Curves/Tr2CurveEulerRotation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ Quaternion Tr2CurveEulerRotation::GetValue( double time ) const
return RotationQuaternion( yaw, pitch, roll );
}

Quaternion Tr2CurveEulerRotation::SampleValue( double time, Tr2CurveChannelSegments& segmentCaches ) const
{
float yaw = m_yaw.SampleValue( time, segmentCaches[0] );
float pitch = m_pitch.SampleValue( time, segmentCaches[1] );
float roll = m_roll.SampleValue( time, segmentCaches[2] );
return RotationQuaternion( yaw, pitch, roll );
}

// --------------------------------------------------------------------------------
Quaternion* Tr2CurveEulerRotation::Update( Quaternion* in, Be::Time time )
{
Expand Down
4 changes: 4 additions & 0 deletions trinity/Curves/Tr2CurveEulerRotation.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#pragma once

#include "Tr2CurveScalar.h"
#include "Tr2CurveSegmentSearch.h"

#include <ITriFunction.h>
#include <ITriCurveLength.h>
Expand Down Expand Up @@ -31,6 +32,9 @@ BLUE_CLASS( Tr2CurveEulerRotation ) :
virtual float Length();

Quaternion GetValue( double time ) const;
// The value at time, like GetValue, but looking each channel's keys up from
// the caller's segment caches (see Tr2CurveScalar::SampleValue).
Quaternion SampleValue( double time, Tr2CurveChannelSegments& segmentCaches ) const;

void AddKey(
float time,
Expand Down
73 changes: 17 additions & 56 deletions trinity/Curves/Tr2CurveQuaternion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "StdAfx.h"
#include "Tr2CurveQuaternion.h"
#include "Tr2CurveSegmentSearch.h"

extern Be::VarChooser Tr2CurveInterpolationChooser[];

Expand Down Expand Up @@ -35,34 +36,34 @@ Tr2CurveQuaternion::Tr2CurveQuaternion( IRoot* lockobj ) :
// --------------------------------------------------------------------------------
void Tr2CurveQuaternion::UpdateValue( double time )
{
m_currentValue = GetValue( time );
m_currentValue = SampleValue( time, m_lastSegment );
}

// --------------------------------------------------------------------------------
Quaternion* Tr2CurveQuaternion::Update( Quaternion* in, Be::Time time )
{
*in = m_currentValue = GetValue( TimeAsDouble( time ) );
*in = m_currentValue = SampleValue( TimeAsDouble( time ), m_lastSegment );
return in;
}

// --------------------------------------------------------------------------------
Quaternion* Tr2CurveQuaternion::Update( Quaternion* in, double time )
{
*in = m_currentValue = GetValue( time );
*in = m_currentValue = SampleValue( time, m_lastSegment );
return in;
}

// --------------------------------------------------------------------------------
Quaternion* Tr2CurveQuaternion::GetValueAt( Quaternion* in, Be::Time time )
{
*in = GetValue( TimeAsDouble( time ) );
*in = SampleValue( TimeAsDouble( time ), m_lastSegment );
return in;
}

// --------------------------------------------------------------------------------
Quaternion* Tr2CurveQuaternion::GetValueAt( Quaternion* in, double time )
{
*in = GetValue( time );
*in = SampleValue( time, m_lastSegment );
return in;
}

Expand Down Expand Up @@ -106,6 +107,12 @@ float Tr2CurveQuaternion::Length()

// --------------------------------------------------------------------------------
Quaternion Tr2CurveQuaternion::GetValue( double time ) const
{
return SampleValue( time, m_lastSegment );
}

// --------------------------------------------------------------------------------
Quaternion Tr2CurveQuaternion::SampleValue( double time, size_t& segmentCache ) const
{
if( m_keys.empty() )
{
Expand All @@ -130,59 +137,13 @@ Quaternion Tr2CurveQuaternion::GetValue( double time ) const
}

float t = GetLocalTime( time );

if( m_lastSegment + 1 < count )
{
// try cached last segment first
auto& k0 = m_keys[m_lastSegment];
auto& k1 = m_keys[m_lastSegment + 1];

if( t >= k0.m_time && t < k1.m_time )
{
return GetSegmentValue( t, k0, k1 );
}

if( m_lastSegment + 2 < count )
{
// try a segment to the right of the cached one
auto& k0 = m_keys[m_lastSegment + 1];
auto& k1 = m_keys[m_lastSegment + 2];

if( t >= k0.m_time && t < k1.m_time )
{
++m_lastSegment;
return GetSegmentValue( t, k0, k1 );
}
}

if( m_lastSegment > 1 )
{
// try a segment to the left of the cached one
auto& k0 = m_keys[m_lastSegment - 1];
auto& k1 = m_keys[m_lastSegment];

if( t >= k0.m_time && t < k1.m_time )
{
--m_lastSegment;
return GetSegmentValue( t, k0, k1 );
}
}
}

// cache miss: try all segments
for( size_t i = 0; i + 1 < count; ++i )
// Most frames land in the cached segment: check it here, without a call
size_t i = segmentCache;
if( i + 1 >= count || t < m_keys[i].m_time || t >= m_keys[i + 1].m_time )
{
auto& k0 = m_keys[i];
auto& k1 = m_keys[i + 1];

if( t >= k0.m_time && t < k1.m_time )
{
m_lastSegment = int32_t( i );
return GetSegmentValue( t, k0, k1 );
}
i = FindCurveSegment( m_keys, t, segmentCache );
}
m_lastSegment = count - 2;
return GetSegmentValue( t, m_keys[count - 2], m_keys[count - 1] );
return GetSegmentValue( t, m_keys[i], m_keys[i + 1] );
}

// --------------------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions trinity/Curves/Tr2CurveQuaternion.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ BLUE_CLASS( Tr2CurveQuaternion ) :
virtual float Length();

Quaternion GetValue( double time ) const;
// The value at time, like GetValue, but looking keys up from the caller's
// segment cache rather than the curve's own. For callers that sample the
// curve at times of their own (bindings with a time offset), so they and the
// curve's own UpdateValue don't keep moving each other's cache.
Quaternion SampleValue( double time, size_t& segmentCache ) const;
Quaternion GetCurrentValue() const;

void OnKeysChanged();
Expand Down
73 changes: 17 additions & 56 deletions trinity/Curves/Tr2CurveScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "StdAfx.h"
#include "Tr2CurveScalar.h"
#include "Tr2CurveSegmentSearch.h"

extern Be::VarChooser Tr2CurveInterpolationChooser[];
extern Be::VarChooser Tr2CurveTangentTypeChooser[];
Expand Down Expand Up @@ -96,25 +97,25 @@ void Tr2CurveScalar::SetName( const char* name )
// --------------------------------------------------------------------------------
void Tr2CurveScalar::UpdateValue( double time )
{
m_currentValue = GetValue( time );
m_currentValue = SampleValue( time, m_lastSegment );
}

// --------------------------------------------------------------------------------
float Tr2CurveScalar::Update( Be::Time time )
{
return m_currentValue = GetValue( TimeAsDouble( time ) );
return m_currentValue = SampleValue( TimeAsDouble( time ), m_lastSegment );
}

// --------------------------------------------------------------------------------
float Tr2CurveScalar::Update( double time )
{
return m_currentValue = GetValue( time );
return m_currentValue = SampleValue( time, m_lastSegment );
}

// --------------------------------------------------------------------------------
float Tr2CurveScalar::GetValueAt( Be::Time time )
{
return GetValue( TimeAsDouble( time ) );
return SampleValue( TimeAsDouble( time ), m_lastSegment );
}

// --------------------------------------------------------------------------------
Expand All @@ -126,11 +127,17 @@ void Tr2CurveScalar::ScaleTime( float s )
// --------------------------------------------------------------------------------
float Tr2CurveScalar::GetValueAt( double time )
{
return GetValue( time );
return SampleValue( time, m_lastSegment );
}

// --------------------------------------------------------------------------------
float Tr2CurveScalar::GetValue( double time ) const
{
return SampleValue( time, m_lastSegment );
}

// --------------------------------------------------------------------------------
float Tr2CurveScalar::SampleValue( double time, size_t& segmentCache ) const
{
if( m_keys.empty() )
{
Expand Down Expand Up @@ -169,59 +176,13 @@ float Tr2CurveScalar::GetValue( double time ) const
}

float t = GetLocalTime( time );

if( m_lastSegment + 1 < count )
// Most frames land in the cached segment: check it here, without a call
size_t i = segmentCache;
if( i + 1 >= count || t < m_keys[i].m_time || t >= m_keys[i + 1].m_time )
{
// try cached last segment first
auto& k0 = m_keys[m_lastSegment];
auto& k1 = m_keys[m_lastSegment + 1];

if( t >= k0.m_time && t < k1.m_time )
{
return GetSegmentValue( t, k0, k1 );
}

if( m_lastSegment + 2 < count )
{
// try a segment to the right of the cached one
auto& k0 = m_keys[m_lastSegment + 1];
auto& k1 = m_keys[m_lastSegment + 2];

if( t >= k0.m_time && t < k1.m_time )
{
++m_lastSegment;
return GetSegmentValue( t, k0, k1 );
}
}

if( m_lastSegment > 1 )
{
// try a segment to the left of the cached one
auto& k0 = m_keys[m_lastSegment - 1];
auto& k1 = m_keys[m_lastSegment];

if( t >= k0.m_time && t < k1.m_time )
{
--m_lastSegment;
return GetSegmentValue( t, k0, k1 );
}
}
}

// cache miss: try all segments
for( size_t i = 0; i + 1 < count; ++i )
{
auto& k0 = m_keys[i];
auto& k1 = m_keys[i + 1];

if( t >= k0.m_time && t < k1.m_time )
{
m_lastSegment = int32_t( i );
return GetSegmentValue( t, k0, k1 );
}
i = FindCurveSegment( m_keys, t, segmentCache );
}
m_lastSegment = count - 2;
return GetSegmentValue( t, m_keys[count - 2], m_keys[count - 1] );
return GetSegmentValue( t, m_keys[i], m_keys[i + 1] );
}

// --------------------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions trinity/Curves/Tr2CurveScalar.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ BLUE_CLASS( Tr2CurveScalar ) :
void SetName( const char* name );

float GetValue( double time ) const;
// The value at time, like GetValue, but looking keys up from the caller's
// segment cache rather than the curve's own. For callers that sample the
// curve at times of their own (bindings with a time offset), so they and the
// curve's own UpdateValue don't keep moving each other's cache.
float SampleValue( double time, size_t& segmentCache ) const;
float GetTangent( double time ) const;
float GetCurrentValue() const;

Expand Down
Loading