diff --git a/src/SkinnedMesh.h b/src/SkinnedMesh.h index 99a1e3e..ddf8442 100644 --- a/src/SkinnedMesh.h +++ b/src/SkinnedMesh.h @@ -25,53 +25,90 @@ constexpr int cSyncTrackMaxIntervals = 8; struct SyncTrack { float m_duration; int m_num_intervals; + float m_sync_markers[cSyncTrackMaxIntervals]; + float m_interval_start[cSyncTrackMaxIntervals]; + float m_interval_end[cSyncTrackMaxIntervals]; float m_interval_durations[cSyncTrackMaxIntervals]; - float CalcSyncFromAbsTime (float abs_time) { - float sync_time = fmodf (abs_time, m_duration) / m_duration; + void CalcIntervals() { + int i; + + if (m_num_intervals == 0) { + m_num_intervals = 1; + m_interval_start[0] = 0.f; + m_interval_end[0] = 1.f; + } else { + for (i = 0; i < m_num_intervals; i++) { + int end_index = i < m_num_intervals - 1 ? i + 1 : 0; + + m_interval_start[i] = m_sync_markers[i]; + m_interval_end[i] = m_sync_markers[end_index]; + + if (m_interval_end[i] > m_interval_start[i]) { + m_interval_durations[i] = m_interval_end[i] - m_interval_start[i]; + } else { + m_interval_durations[i] = + m_interval_end[i] + (1. - 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_durations[interval_index]) { sync_time -= m_interval_durations[interval_index]; - interval_index ++; + interval_index++; } - return float(interval_index) + sync_time / m_interval_durations[interval_index]; + return float(interval_index) + + sync_time / m_interval_durations[interval_index]; } - float CalcRatioFromSyncTime (float sync_time) { + float CalcRatioFromSyncTime(float sync_time) { float interval_ratio = fmodf(sync_time, 1.0f); int interval = int(sync_time - interval_ratio); - float result = 0.f; - int i = 0; - while (i < interval) { - result += m_interval_durations[i]; - i++; - } - - result += m_interval_durations[i] * interval_ratio; - - return result; + return fmodf( + m_interval_start[interval] + + m_interval_durations[interval] * interval_ratio, + 1.0f); } - static SyncTrack Blend(float weight, const SyncTrack& track_A, const SyncTrack& track_B) { - SyncTrack 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); - 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; + result.m_duration = + (1.0f - weight) * track_A.m_duration + weight * track_B.m_duration; for (int i = 0; i < result.m_num_intervals; i++) { - result.m_interval_durations[i] = (1.0f - weight) * track_A.m_interval_durations[i] + weight * track_B.m_interval_durations[i]; + result.m_interval_durations[i] = + (1.0f - weight) * track_A.m_interval_durations[i] + + weight * track_B.m_interval_durations[i]; + + result.m_interval_start[i] = + (1.0f - weight) * track_A.m_interval_start[i] + + weight * track_B.m_interval_start[i]; + + result.m_interval_end[i] = + (1.0f - weight) * track_A.m_interval_end[i] + + weight * track_B.m_interval_end[i]; + + result.m_sync_markers[i] = + (1.0f - weight) * track_A.m_sync_markers[i] + + weight * track_B.m_sync_markers[i]; } return result; } }; - struct SkinnedMesh { virtual ~SkinnedMesh(); bool LoadSkeleton(const char* filename); @@ -79,8 +116,12 @@ struct SkinnedMesh { //bool LoadMesh (const char* filename); void SetCurrentAnimation(int index); - const ozz::animation::Animation* GetCurrentAnimation() {return m_current_animation; }; - float GetCurrentAnimationDuration() {return m_current_animation->duration(); }; + const ozz::animation::Animation* GetCurrentAnimation() { + return m_current_animation; + }; + float GetCurrentAnimationDuration() { + return m_current_animation->duration(); + }; void EvalAnimation(float in_time); void DrawSkeleton(); diff --git a/tests/SyncTrackTests.cc b/tests/SyncTrackTests.cc index 3c1da49..e7887e0 100644 --- a/tests/SyncTrackTests.cc +++ b/tests/SyncTrackTests.cc @@ -2,79 +2,154 @@ // Created by martin on 16.11.21. // +#include "SkinnedMesh.h" #include "catch.hpp" -#include "SkinnedMesh.h" - -TEST_CASE("SyncTrackBlendSimple", "[SyncTrackBlend]") { +TEST_CASE("Basic", "[SyncTrack]") { SyncTrack track_A; track_A.m_num_intervals = 2; track_A.m_duration = 2.0; + track_A.m_interval_start[0] = 0.f; + track_A.m_interval_end[0] = 0.7f; track_A.m_interval_durations[0] = 0.7; + track_A.m_interval_start[1] = 0.7f; + track_A.m_interval_end[1] = 1.0f; track_A.m_interval_durations[1] = 0.3; SyncTrack track_B; track_B.m_num_intervals = 2; track_B.m_duration = 1.5; + track_B.m_interval_start[0] = 0.0f; + track_B.m_interval_end[0] = 0.6f; track_B.m_interval_durations[0] = 0.6; + track_B.m_interval_start[1] = 0.6f; + track_B.m_interval_end[1] = 1.0f; track_B.m_interval_durations[1] = 0.4; - WHEN ("Calculating sync time of track_B at 0.5 duration") { - float sync_time_at_0_75 = track_B.CalcSyncFromAbsTime(0.5 * track_B.m_duration); + WHEN("Calculating sync time of track_B at 0.5 duration") { + float sync_time_at_0_75 = + track_B.CalcSyncFromAbsTime(0.5 * track_B.m_duration); REQUIRE(sync_time_at_0_75 == Catch::Detail::Approx(0.83333)); } - WHEN ("Calculating sync time of track_B at 0.6 duration") { - float sync_time_at_0_6 = track_B.CalcSyncFromAbsTime(0.6 * track_B.m_duration); + WHEN("Calculating sync time of track_B at 0.6 duration") { + float sync_time_at_0_6 = + track_B.CalcSyncFromAbsTime(0.6 * track_B.m_duration); REQUIRE(sync_time_at_0_6 == Catch::Detail::Approx(1.0)); } - WHEN ("Calculating sync time of track_B at 0.7 duration") { - float sync_time_at_0_7 = track_B.CalcSyncFromAbsTime(0.7 * track_B.m_duration); + WHEN("Calculating sync time of track_B at 0.7 duration") { + float sync_time_at_0_7 = + track_B.CalcSyncFromAbsTime(0.7 * track_B.m_duration); REQUIRE(sync_time_at_0_7 == Catch::Detail::Approx(1.25)); } - WHEN ("Calculating sync time of track_B at 0.0 duration") { - float sync_time_at_1_0 = track_B.CalcSyncFromAbsTime(0.0 * track_B.m_duration); + WHEN("Calculating sync time of track_B at 0.0 duration") { + float sync_time_at_1_0 = + track_B.CalcSyncFromAbsTime(0.0 * track_B.m_duration); REQUIRE(sync_time_at_1_0 == Catch::Detail::Approx(0.0)); } - WHEN ("Calculating sync time of track_B at 1.0 duration") { - float sync_time_at_1_0 = track_B.CalcSyncFromAbsTime(0.9999 * track_B.m_duration); + WHEN("Calculating sync time of track_B at 1.0 duration") { + float sync_time_at_1_0 = + track_B.CalcSyncFromAbsTime(0.9999 * track_B.m_duration); REQUIRE(sync_time_at_1_0 == Catch::Detail::Approx(2.0).epsilon(0.001f)); } - WHEN ("Calculating ratio from sync time on track_A at 0.83333") { + WHEN("Calculating ratio from sync time on track_A at 0.83333") { float ratio = track_A.CalcRatioFromSyncTime(0.83333333); - REQUIRE (ratio == Catch::Detail::Approx(0.5833333)); + REQUIRE(ratio == Catch::Detail::Approx(0.5833333)); } - WHEN ("Calculating ratio from sync time on track_A at 0.83333") { + WHEN("Calculating ratio from sync time on track_A at 0.83333") { float ratio = track_A.CalcRatioFromSyncTime(1.25); - REQUIRE (ratio == Catch::Detail::Approx(0.775)); + REQUIRE(ratio == Catch::Detail::Approx(0.775)); } WHEN("Blending two synctracks with weight 0.") { SyncTrack blended = SyncTrack::Blend(0.f, track_A, track_B); - THEN ("Result must equal track_A") { + THEN("Result must equal track_A") { REQUIRE(blended.m_duration == track_A.m_duration); REQUIRE( blended.m_interval_durations[0] == track_A.m_interval_durations[0]); REQUIRE( blended.m_interval_durations[1] == track_A.m_interval_durations[1]); + + REQUIRE(blended.m_sync_markers[0] == track_A.m_sync_markers[0]); + REQUIRE(blended.m_sync_markers[1] == track_A.m_sync_markers[1]); + + REQUIRE(blended.m_interval_start[0] == track_A.m_interval_start[0]); + REQUIRE(blended.m_interval_start[1] == track_A.m_interval_start[1]); + + REQUIRE(blended.m_interval_end[0] == track_A.m_interval_end[0]); + REQUIRE(blended.m_interval_end[1] == track_A.m_interval_end[1]); } } WHEN("Blending two synctracks with weight 1.") { SyncTrack blended = SyncTrack::Blend(1.f, track_A, track_B); - THEN ("Result must equal track_B") { + THEN("Result must equal track_B") { REQUIRE(blended.m_duration == track_B.m_duration); REQUIRE( blended.m_interval_durations[0] == track_B.m_interval_durations[0]); REQUIRE( blended.m_interval_durations[1] == track_B.m_interval_durations[1]); + + REQUIRE(blended.m_sync_markers[0] == track_B.m_sync_markers[0]); + REQUIRE(blended.m_sync_markers[1] == track_B.m_sync_markers[1]); + + REQUIRE(blended.m_interval_start[0] == track_B.m_interval_start[0]); + REQUIRE(blended.m_interval_start[1] == track_B.m_interval_start[1]); + + REQUIRE(blended.m_interval_end[0] == track_B.m_interval_end[0]); + REQUIRE(blended.m_interval_end[1] == track_B.m_interval_end[1]); } } } + + + +TEST_CASE("Sync Marker Interval Calculation", "[SyncTrack]") { + SyncTrack track_A; + track_A.m_num_intervals = 2; + track_A.m_duration = 2.0; + track_A.m_sync_markers[0] = 0.9; + track_A.m_sync_markers[1] = 0.2; + + WHEN("Calculating intervals") { + track_A.CalcIntervals(); + + CHECK(track_A.m_interval_start[0] == 0.9f); + CHECK(track_A.m_interval_end[0] == 0.2f); + CHECK(track_A.m_interval_durations[0] == 0.3f); + + CHECK(track_A.m_interval_start[1] == 0.2f); + CHECK(track_A.m_interval_end[1] == 0.9f); + CHECK(track_A.m_interval_durations[1] == 0.7f); + + WHEN("Querying ratio at sync time at 1.001") { + float ratio = track_A.CalcRatioFromSyncTime(1.0001f); + CHECK(ratio == Catch::Detail::Approx(0.2).epsilon(0.001)); + } + + WHEN("Querying ratio at sync time at 1.001") { + float ratio = track_A.CalcRatioFromSyncTime(0.0001f); + CHECK(ratio == Catch::Detail::Approx(0.9).epsilon(0.001)); + } + + WHEN("Querying ratio at sync time at 1.9999") { + float ratio = track_A.CalcRatioFromSyncTime(0.9999f); + CHECK(ratio == Catch::Detail::Approx(0.2).epsilon(0.001)); + } + } + + WHEN ("Blending with another sync track") { + SyncTrack track_B; + track_B.m_num_intervals = 2; + track_B.m_duration = 1.0; + track_B.m_sync_markers[0] = 0.9; + track_B.m_sync_markers[1] = 0.2; + } +} \ No newline at end of file