Add support for multiple media events on audio, postfx and camera tracks in Scene Animations
#519
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user