overlays: Redesign animation system (add easing functions, fix bugs)

Instead of speed, direction and distance, the user now specifies
start/end offsets and how much time the transition should take.

Fixes:
- Stuttering caused from framerate estimation.
- An edge case where animations would go over their supposed limit.

Adds:
- The ability to specify arbitrary easing functions for the animations
  - Implemented quadratic ease in and ease out and cubic ease in/out.
- Usage of cubic ease in/out in the trophy notification
This commit is contained in:
Nick Renieris 2020-01-06 19:56:46 +02:00 committed by kd-11
parent 28770c1580
commit 5bace118a7
3 changed files with 68 additions and 48 deletions

View File

@ -1,19 +1,22 @@
#include "stdafx.h" #include "stdafx.h"
#pragma optimize("", off)
#include "overlay_animation.h" #include "overlay_animation.h"
#include "overlay_controls.h" #include "overlay_controls.h"
#include <numeric>
namespace rsx namespace rsx
{ {
namespace overlays namespace overlays
{ {
void animation_translate::apply(compiled_resource& resource) void animation_translate::apply(compiled_resource& resource)
{ {
if (progress == vector3i(0, 0, 0)) if (!active)
{ {
return; return;
} }
const vertex delta = { static_cast<float>(progress.x), static_cast<float>(progress.y), static_cast<float>(progress.z), 0.f }; const vertex delta = { current.x, current.y, current.z, 0.f };
for (auto& cmd : resource.draw_commands) for (auto& cmd : resource.draw_commands)
{ {
for (auto& v : cmd.verts) for (auto& v : cmd.verts)
@ -23,49 +26,54 @@ namespace rsx
} }
} }
void animation_translate::update(u64 t) void animation_translate::update(u64 frame)
{ {
if (!active) if (!active)
{ {
return; return;
} }
if (time == 0) if (frame_start == 0)
{ {
time = t; start = current;
frame_start = frame;
frame_end = u64(frame + duration * g_cfg.video.vblank_rate);
return; return;
} }
const u64 delta = (t - time); if (frame >= frame_end)
const u64 frames = delta / 16667;
if (frames)
{ {
const int mag = frames * speed; // Exit condition
const vector3i new_progress = progress + direction * mag; finish();
const auto d = new_progress.distance(progress_limit); return;
if (distance >= 0)
{
if (d > distance)
{
// Exit condition
finish();
return;
}
}
progress = new_progress;
distance = d;
time = t;
} }
f32 t = f32(frame - frame_start) / (frame_end - frame_start);
switch (type) {
case animation_type::linear:
break;
case animation_type::ease_in_quad:
t = t * t;
break;
case animation_type::ease_out_quad:
t = t * (2.0 - t);
break;
case animation_type::ease_in_out_cubic:
t = t > 0.5 ? 4.0 * std::pow((t - 1.0), 3.0) + 1.0 : 4.0 * std::pow(t, 3.0);
break;
}
current = (1.f - t) * start + t * end;
} }
void animation_translate::finish() void animation_translate::finish()
{ {
active = false; active = false;
distance = -1; frame_start = 0;
time = 0; frame_end = 0;
current = end; // Snap current to limit in case we went over
if (on_finish) if (on_finish)
{ {

View File

@ -1,4 +1,4 @@
#pragma once #pragma once
#include "Utilities/types.h" #include "Utilities/types.h"
#include "Utilities/geometry.h" #include "Utilities/geometry.h"
#include "overlay_utils.h" #include "overlay_utils.h"
@ -9,28 +9,40 @@ namespace rsx
{ {
struct compiled_resource; struct compiled_resource;
enum class animation_type
{
linear,
ease_in_quad,
ease_out_quad,
ease_in_out_cubic,
};
struct animation_base struct animation_base
{ {
bool active = false; bool active = false;
animation_type type { animation_type::linear };
std::function<void()> on_finish;
virtual void apply(compiled_resource&) = 0; virtual void apply(compiled_resource&) = 0;
virtual void update(u64 t) = 0; virtual void update(u64 frame) = 0;
}; };
struct animation_translate : animation_base struct animation_translate : animation_base
{ {
vector3i direction{}; private:
vector3i progress{}; vector3f start{}; // Set `current` instead of this
vector3i progress_limit{}; // NOTE: Necessary because update() is called after rendering,
// resulting in one frame of incorrect translation
std::function<void()> on_finish; u64 frame_start = 0;
u64 frame_end = 0;
int speed = 0; public:
int distance = -1; vector3f current{};
u64 time = 0; vector3f end{};
f32 duration{}; // in seconds
void apply(compiled_resource& data) override; void apply(compiled_resource& data) override;
void update(u64 t) override; void update(u64 frame) override;
void finish(); void finish();
}; };
} }

View File

@ -1,5 +1,6 @@
#include "stdafx.h" #include "stdafx.h"
#include "overlays.h" #include "overlays.h"
#include "Emu/RSX/RSXThread.h"
namespace rsx namespace rsx
{ {
@ -46,10 +47,10 @@ namespace rsx
text_view.align_text(overlay_element::text_align::center); text_view.align_text(overlay_element::text_align::center);
text_view.back_color.a = 0.f; text_view.back_color.a = 0.f;
sliding_animation.direction = { 1, 0, 0 }; sliding_animation.duration = 1.5;
sliding_animation.speed = 10; sliding_animation.type = animation_type::ease_in_out_cubic;
sliding_animation.progress = { -int(frame.w), 0, 0 }; sliding_animation.current = { -f32(frame.w), 0, 0 };
sliding_animation.progress_limit = { 0, 0, 0}; sliding_animation.end = { 0, 0, 0};
sliding_animation.active = true; sliding_animation.active = true;
} }
@ -73,8 +74,7 @@ namespace rsx
{ {
if (!sliding_animation.active) if (!sliding_animation.active)
{ {
sliding_animation.direction.x = -1; sliding_animation.end = { -f32(frame.w), 0, 0 };
sliding_animation.progress_limit = { -int(frame.w), 0, 0 };
sliding_animation.on_finish = [this] sliding_animation.on_finish = [this]
{ {
s_trophy_semaphore.release(); s_trophy_semaphore.release();
@ -87,7 +87,7 @@ namespace rsx
if (sliding_animation.active) if (sliding_animation.active)
{ {
sliding_animation.update(t); sliding_animation.update(rsx::get_current_renderer()->vblank_count);
} }
} }