Add support for multiple media events on audio, postfx and camera tracks in Scene Animations

#519
This commit is contained in:
Wojtek Figat
2021-09-21 17:21:39 +02:00
parent f547e44d3d
commit 0ec16de569
16 changed files with 479 additions and 331 deletions

View File

@@ -22,6 +22,11 @@ SceneAnimation::SceneAnimation(const SpawnParams& params, const AssetInfo* info)
{
}
float SceneAnimation::GetDuration() const
{
return (float)DurationFrames / FramesPerSecond;
}
const BytesContainer& SceneAnimation::LoadTimeline()
{
WaitForLoaded();
@@ -110,8 +115,9 @@ Asset::LoadResult SceneAnimation::load()
stream.ReadInt32(&version);
switch (version)
{
case 2:
case 3:
case 2: // [Deprecated in 2020 expires on 03.09.2023]
case 3: // [Deprecated on 03.09.2021 expires on 03.09.2023]
case 4:
{
stream.ReadFloat(&FramesPerSecond);
stream.ReadInt32(&DurationFrames);
@@ -143,14 +149,25 @@ Asset::LoadResult SceneAnimation::load()
switch (track.Type)
{
case Track::Types::Folder:
{
break;
}
case Track::Types::PostProcessMaterial:
{
const auto trackData = stream.Read<PostProcessMaterialTrack::Data>();
track.Data = trackData;
track.Asset = Content::LoadAsync<MaterialBase>(trackData->AssetID);
const auto trackRuntime = _runtimeData.Move<PostProcessMaterialTrack::Runtime>();
track.RuntimeData = trackRuntime;
if (version <= 3)
{
// [Deprecated on 03.09.2021 expires on 03.09.2023]
trackRuntime->Count = 1;
trackRuntime->Media = stream.Read<Media>();
}
else
{
stream.ReadInt32(&trackRuntime->Count);
trackRuntime->Media = stream.Read<Media>(trackRuntime->Count);
}
if (trackData->AssetID.IsValid() && !track.Asset)
{
LOG(Warning, "Missing material for track {0} in {1}.", track.Name, ToString());
@@ -197,6 +214,20 @@ Asset::LoadResult SceneAnimation::load()
track.RuntimeData = trackRuntime;
track.TrackStateIndex = TrackStatesCount++;
trackRuntime->VolumeTrackIndex = -1;
if (version <= 3)
{
// [Deprecated on 03.09.2021 expires on 03.09.2023]
trackRuntime->Count = 1;
trackRuntime->Media = _runtimeData.Move<AudioTrack::Media>();
stream.ReadInt32(&trackRuntime->Media->StartFrame);
stream.ReadInt32(&trackRuntime->Media->DurationFrames);
trackRuntime->Media->Offset = 0.0f;
}
else
{
stream.ReadInt32(&trackRuntime->Count);
trackRuntime->Media = stream.Read<AudioTrack::Media>(trackRuntime->Count);
}
break;
}
case Track::Types::AudioVolume:
@@ -211,7 +242,7 @@ Asset::LoadResult SceneAnimation::load()
{
if (Tracks[track.ParentIndex].Type == Track::Types::Audio)
{
((AudioTrack::Runtime*)((byte*)_runtimeData.GetHandle() + (intptr)Tracks[track.ParentIndex].RuntimeData))->VolumeTrackIndex = i;
((AudioTrack::Runtime*)(_runtimeData.GetHandle() + (intptr)Tracks[track.ParentIndex].RuntimeData))->VolumeTrackIndex = i;
}
else
{
@@ -365,6 +396,17 @@ Asset::LoadResult SceneAnimation::load()
const auto trackRuntime = _runtimeData.Move<CameraCutTrack::Runtime>();
track.RuntimeData = trackRuntime;
track.TrackStateIndex = TrackStatesCount++;
if (version <= 3)
{
// [Deprecated on 03.09.2021 expires on 03.09.2023]
trackRuntime->Count = 1;
trackRuntime->Media = stream.Read<Media>();
}
else
{
stream.ReadInt32(&trackRuntime->Count);
trackRuntime->Media = stream.Read<Media>(trackRuntime->Count);
}
break;
}
default:

View File

@@ -129,24 +129,23 @@ public:
}
};
struct Media
{
int32 StartFrame;
int32 DurationFrames;
};
struct PostProcessMaterialTrack
{
struct Data
{
/// <summary>
/// The PostFx material asset ID.
/// </summary>
Guid AssetID;
};
/// <summary>
/// The start frame of the media play begin.
/// </summary>
int32 StartFrame;
/// <summary>
/// The total duration of the media playback in the timeline sequence frames amount.
/// </summary>
int32 DurationFrames;
struct Runtime
{
int32 Count;
Media* Media;
};
};
@@ -154,19 +153,8 @@ public:
{
struct Data
{
/// <summary>
/// The scene animation asset ID.
/// </summary>
Guid AssetID;
/// <summary>
/// The start frame of the media play begin.
/// </summary>
int32 StartFrame;
/// <summary>
/// The total duration of the media playback in the timeline sequence frames amount.
/// </summary>
int32 DurationFrames;
};
@@ -185,57 +173,36 @@ public:
struct Data
{
/// <summary>
/// The start frame of the media play begin.
/// </summary>
int32 StartFrame;
/// <summary>
/// The total duration of the media playback in the timeline sequence frames amount.
/// </summary>
int32 DurationFrames;
/// <summary>
/// The gradient stops count.
/// </summary>
int32 GradientStopsCount;
};
struct Runtime
{
/// <summary>
/// The pointer to the gradient stops array (items count is GradientStopsCount).
/// </summary>
GradientStop* GradientStops;
};
};
struct AudioTrack
{
struct Media
{
int32 StartFrame;
int32 DurationFrames;
float Offset;
};
struct Data
{
/// <summary>
/// The audio clip asset ID.
/// </summary>
Guid AssetID;
/// <summary>
/// The start frame of the media play begin.
/// </summary>
int32 StartFrame;
/// <summary>
/// The total duration of the media playback in the timeline sequence frames amount.
/// </summary>
int32 DurationFrames;
};
struct Runtime
{
/// <summary>
/// The index of the volume track. If not used then value is -1. Assigned on load.
/// </summary>
int32 VolumeTrackIndex;
int32 Count;
Media* Media;
};
};
@@ -245,22 +212,12 @@ public:
struct Data
{
/// <summary>
/// The keyframes count.
/// </summary>
int32 KeyframesCount;
};
struct Runtime
{
/// <summary>
/// The keyframes count.
/// </summary>
int32 KeyframesCount;
/// <summary>
/// The keyframes array (items count is KeyframesCount).
/// </summary>
BezierCurveKeyframe<float>* Keyframes;
};
};
@@ -269,9 +226,6 @@ public:
{
struct Data
{
/// <summary>
/// The object ID.
/// </summary>
Guid ID;
};
@@ -306,37 +260,15 @@ public:
{
struct Data
{
/// <summary>
/// The property value data size (in bytes).
/// </summary>
int32 ValueSize;
/// <summary>
/// The name length.
/// </summary>
int32 PropertyNameLength;
/// <summary>
/// The typename length.
/// </summary>
int32 PropertyTypeNameLength;
};
struct Runtime
{
/// <summary>
/// The property value data size (in bytes).
/// </summary>
int32 ValueSize;
/// <summary>
/// The name of the property (just a member name).
/// </summary>
char* PropertyName;
/// <summary>
/// The typename of the property value (fullname including namespace but not assembly).
/// </summary>
char* PropertyTypeName;
};
};
@@ -345,17 +277,11 @@ public:
{
struct Data : PropertyTrack::Data
{
/// <summary>
/// The keyframes count.
/// </summary>
int32 KeyframesCount;
};
struct Runtime : PropertyTrack::Runtime
{
/// <summary>
/// The keyframes count.
/// </summary>
int32 KeyframesCount;
/// <summary>
@@ -382,22 +308,12 @@ public:
struct Data : PropertyTrack::Data
{
/// <summary>
/// The keyframes count.
/// </summary>
int32 KeyframesCount;
};
struct Runtime : PropertyTrack::Runtime
{
/// <summary>
/// The cached curve data type.
/// </summary>
DataTypes DataType;
/// <summary>
/// The keyframes count.
/// </summary>
int32 KeyframesCount;
/// <summary>
@@ -411,17 +327,11 @@ public:
{
struct Data : PropertyTrack::Data
{
/// <summary>
/// The keyframes count.
/// </summary>
int32 KeyframesCount;
};
struct Runtime : PropertyTrack::Runtime
{
/// <summary>
/// The keyframes count.
/// </summary>
int32 KeyframesCount;
// ..followed by the keyframes times and the values arrays (separate)
@@ -486,19 +396,12 @@ public:
{
struct Data : ObjectTrack::Data
{
/// <summary>
/// The start frame of the media play begin.
/// </summary>
int32 StartFrame;
/// <summary>
/// The total duration of the media playback in the timeline sequence frames amount.
/// </summary>
int32 DurationFrames;
};
struct Runtime : ObjectTrack::Runtime
{
int32 Count;
Media* Media;
};
};
@@ -534,11 +437,7 @@ public:
/// <summary>
/// Gets the animation duration (in seconds).
/// </summary>
/// <returns>The animation duration (in seconds).</returns>
API_PROPERTY() FORCE_INLINE float GetDuration() const
{
return DurationFrames / FramesPerSecond;
}
API_PROPERTY() float GetDuration() const;
public:

View File

@@ -540,13 +540,18 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
{
case SceneAnimation::Track::Types::PostProcessMaterial:
{
const auto trackData = track.GetData<SceneAnimation::PostProcessMaterialTrack::Data>();
const float startTime = trackData->StartFrame / fps;
const float durationTime = trackData->DurationFrames / fps;
const bool isActive = Math::IsInRange(time, startTime, startTime + durationTime);
if (isActive && _postFxSettings.PostFxMaterials.Materials.Count() < POST_PROCESS_SETTINGS_MAX_MATERIALS)
const auto runtimeData = track.GetRuntimeData<SceneAnimation::PostProcessMaterialTrack::Runtime>();
for (int32 k = 0; k < runtimeData->Count; k++)
{
_postFxSettings.PostFxMaterials.Materials.Add(track.Asset.As<MaterialBase>());
const auto& media = runtimeData->Media[k];
const float startTime = media.StartFrame / fps;
const float durationTime = media.DurationFrames / fps;
const bool isActive = Math::IsInRange(time, startTime, startTime + durationTime);
if (isActive && _postFxSettings.PostFxMaterials.Materials.Count() < POST_PROCESS_SETTINGS_MAX_MATERIALS)
{
_postFxSettings.PostFxMaterials.Materials.Add(track.Asset.As<MaterialBase>());
break;
}
}
break;
}
@@ -648,29 +653,40 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
const auto clip = track.Asset.As<AudioClip>();
if (!clip || !clip->IsLoaded())
break;
const auto trackData = track.GetData<SceneAnimation::AudioTrack::Data>();
const auto runtimeData = track.GetRuntimeData<SceneAnimation::AudioTrack::Runtime>();
const float startTime = trackData->StartFrame / fps;
const float durationTime = trackData->DurationFrames / fps;
const bool loop = ((int32)track.Flag & (int32)SceneAnimation::Track::Flags::Loop) == (int32)SceneAnimation::Track::Flags::Loop;
float mediaTime = time - startTime;
auto& state = _tracks[stateIndexOffset + track.TrackStateIndex];
auto audioSource = state.Object.As<AudioSource>();
if (!audioSource)
float mediaTime = -1, mediaDuration, playTime;
for (int32 k = 0; k < runtimeData->Count; k++)
{
// Spawn audio source to play the clip
audioSource = New<AudioSource>();
audioSource->SetStaticFlags(StaticFlags::None);
audioSource->HideFlags = HideFlags::FullyHidden;
audioSource->Clip = clip;
audioSource->SetIsLooping(loop);
audioSource->SetParent(this, false, false);
_subActors.Add(audioSource);
state.Object = audioSource;
const auto& media = runtimeData->Media[k];
const float startTime = media.StartFrame / fps;
const float durationTime = media.DurationFrames / fps;
if (Math::IsInRange(time, startTime, startTime + durationTime))
{
mediaTime = time - startTime;
playTime = mediaTime + media.Offset;
mediaDuration = durationTime;
break;
}
}
if (mediaTime >= 0.0f && mediaTime <= durationTime)
auto& state = _tracks[stateIndexOffset + track.TrackStateIndex];
auto audioSource = state.Object.As<AudioSource>();
if (mediaTime >= 0.0f && mediaTime <= mediaDuration)
{
const bool loop = ((int32)track.Flag & (int32)SceneAnimation::Track::Flags::Loop) == (int32)SceneAnimation::Track::Flags::Loop;
if (!audioSource)
{
// Spawn audio source to play the clip
audioSource = New<AudioSource>();
audioSource->SetStaticFlags(StaticFlags::None);
audioSource->HideFlags = HideFlags::FullyHidden;
audioSource->Clip = clip;
audioSource->SetIsLooping(loop);
audioSource->SetParent(this, false, false);
_subActors.Add(audioSource);
state.Object = audioSource;
}
// Sample volume track
float volume = 1.0f;
if (runtimeData->VolumeTrackIndex != -1)
@@ -680,7 +696,9 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
if (volumeTrackRuntimeData)
{
SceneAnimation::AudioVolumeTrack::CurveType::KeyFrameData data(volumeTrackRuntimeData->Keyframes, volumeTrackRuntimeData->KeyframesCount);
volumeCurve.Evaluate(data, volume, mediaTime, false);
const auto& firstMedia = runtimeData->Media[0];
auto firstMediaTime = time - firstMedia.StartFrame / fps;
volumeCurve.Evaluate(data, volume, firstMediaTime, false);
}
}
@@ -688,9 +706,9 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
if (loop)
{
// Loop position
mediaTime = Math::Mod(mediaTime, clipLength);
playTime = Math::Mod(playTime, clipLength);
}
else if (mediaTime >= clipLength)
else if (playTime >= clipLength)
{
// Stop updating after end
break;
@@ -708,19 +726,19 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
// Synchronize playback position
const float maxAudioLag = 0.3f;
const auto audioTime = audioSource->GetTime();
//LOG(Info, "Audio: {0}, Media : {1}", audioTime, mediaTime);
if (Math::Abs(audioTime - mediaTime) > maxAudioLag &&
Math::Abs(audioTime + clipLength - mediaTime) > maxAudioLag &&
Math::Abs(mediaTime + clipLength - audioTime) > maxAudioLag)
//LOG(Info, "Audio: {0}, Media : {1}", audioTime, playTime);
if (Math::Abs(audioTime - playTime) > maxAudioLag &&
Math::Abs(audioTime + clipLength - playTime) > maxAudioLag &&
Math::Abs(playTime + clipLength - audioTime) > maxAudioLag)
{
audioSource->SetTime(mediaTime);
audioSource->SetTime(playTime);
//LOG(Info, "Set Time (current audio time: {0})", audioSource->GetTime());
}
// Keep playing
audioSource->Play();
}
else
else if (audioSource)
{
// End playback
audioSource->Stop();
@@ -998,6 +1016,27 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
}
case SceneAnimation::Track::Types::CameraCut:
{
// Check if any camera cut media on a track is active
bool isActive = false;
const auto runtimeData = track.GetRuntimeData<SceneAnimation::CameraCutTrack::Runtime>();
for (int32 k = 0; k < runtimeData->Count; k++)
{
const auto& media = runtimeData->Media[k];
const float startTime = media.StartFrame / fps;
const float durationTime = media.DurationFrames / fps;
if (Math::IsInRange(time, startTime, startTime + durationTime))
{
isActive = true;
break;
}
}
if (!isActive)
{
// Skip updating child tracks if the current position is outside the media clip range
j += track.ChildrenCount;
break;
}
// Cache actor to animate
const auto trackData = track.GetData<SceneAnimation::CameraCutTrack::Data>();
auto& state = _tracks[stateIndexOffset + track.TrackStateIndex];
@@ -1017,23 +1056,11 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
}
state.ManagedObject = state.Object.GetOrCreateManagedInstance();
// Use camera
_isUsingCameraCuts = true;
const float startTime = trackData->StartFrame / fps;
const float durationTime = trackData->DurationFrames / fps;
float mediaTime = time - startTime;
if (mediaTime >= 0.0f && mediaTime <= durationTime)
// Override camera
if (_cameraCutCam == nullptr)
{
if (_cameraCutCam == nullptr)
{
// Override camera
_cameraCutCam = (Camera*)state.Object.Get();
}
}
else
{
// Skip updating child tracks if the current position is outside the media clip range
j += track.ChildrenCount;
_cameraCutCam = (Camera*)state.Object.Get();
_isUsingCameraCuts = true;
}
break;
}