// // Created by martin on 19.11.21. // #ifndef ANIMTESTBED_SYNCTRACK_H #define ANIMTESTBED_SYNCTRACK_H #include #include #include constexpr int cSyncTrackMaxIntervals = 8; struct SyncTrack { SyncTrack() : m_duration(0.f), m_num_intervals(0) { for (int i = 0; i < cSyncTrackMaxIntervals; i++) { m_sync_markers[i] = 0.f; m_interval_ratio[i] = 0.f; m_interval_ratio[i] = 0.f; } } float m_duration; int m_num_intervals; float m_sync_markers[cSyncTrackMaxIntervals]; float m_interval_start[cSyncTrackMaxIntervals]; float m_interval_ratio[cSyncTrackMaxIntervals]; void CalcIntervals() { if (m_num_intervals == 0) { m_num_intervals = 1; m_sync_markers[0] = 0.f; } for (int i = 0; i < m_num_intervals; i++) { assert(m_sync_markers[i] >= 0.f && m_sync_markers[i] <= 1.0f); int end_index = i < m_num_intervals - 1 ? i + 1 : 0; m_interval_start[i] = m_sync_markers[i]; float interval_end = m_sync_markers[end_index]; if (interval_end < m_interval_start[i]) { interval_end += 1.0f; } m_interval_ratio[i] = interval_end - m_interval_start[i]; } } float CalcSyncFromAbsTime(float abs_time) { float sync_time = fmodf(abs_time, m_duration) / m_duration; int interval_index = 0; while (sync_time >= m_interval_ratio[interval_index]) { sync_time -= m_interval_ratio[interval_index]; interval_index++; } return float(interval_index) + sync_time / m_interval_ratio[interval_index]; } float CalcRatioFromSyncTime(float sync_time) { float interval_ratio = fmodf(sync_time, 1.0f); int interval = int(sync_time - interval_ratio); return fmodf( m_interval_start[interval] + m_interval_ratio[interval] * interval_ratio, 1.0f); } bool operator==(const SyncTrack& other) const { bool result = m_duration == other.m_duration && m_num_intervals == other.m_num_intervals; if (!result) { return false; } for (int i = 0; i < m_num_intervals; i++) { if ((fabsf(m_interval_start[i] - other.m_interval_start[i]) > 1.0e-5) || (fabsf(m_interval_ratio[i] - other.m_interval_ratio[i]) > 1.0e-5)) { return false; } } return true; } static SyncTrack CreateFromMarkers( float duration, int n_markers, float markers[cSyncTrackMaxIntervals]) { SyncTrack result; result.m_duration = duration; result.m_num_intervals = n_markers; for (int i = 0; i < n_markers; i++) { result.m_sync_markers[i] = markers[i]; } result.CalcIntervals(); return result; } static SyncTrack Blend(float weight, const SyncTrack& track_A, const SyncTrack& track_B) { assert(track_A.m_num_intervals == track_B.m_num_intervals); SyncTrack result; result.m_num_intervals = track_A.m_num_intervals; result.m_duration = (1.0f - weight) * track_A.m_duration + weight * track_B.m_duration; float interval_0_offset = track_B.m_interval_start[0] - track_A.m_interval_start[0]; if (interval_0_offset > 0.5f) { interval_0_offset = -fmodf(1.f - interval_0_offset, 1.0f); } else if (interval_0_offset < -0.5) { interval_0_offset = fmodf(1.f + interval_0_offset, 1.0f); } result.m_interval_start[0] = fmodf( 1.0 + (1.0f - weight) * track_A.m_interval_start[0] + weight * (track_A.m_interval_start[0] + interval_0_offset), 1.0f); result.m_sync_markers[0] = result.m_interval_start[0]; for (int i = 0; i < result.m_num_intervals; i++) { float interval_duration_A = track_A.m_interval_ratio[i]; float interval_duration_B = track_B.m_interval_ratio[i]; result.m_interval_ratio[i] = (1.0f - weight) * interval_duration_A + weight * interval_duration_B; if (i < cSyncTrackMaxIntervals) { result.m_interval_start[i + 1] = result.m_interval_start[i] + result.m_interval_ratio[i]; if (result.m_interval_start[i + 1] > 1.0f) { result.m_interval_start[i + 1] = fmodf(result.m_interval_start[i + 1], 1.0f); } result.m_sync_markers[i + 1] = result.m_interval_start[i + 1]; } } assert (result.m_num_intervals < cSyncTrackMaxIntervals); return result; } }; #endif //ANIMTESTBED_SYNCTRACK_H