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_controls.h"
#include <numeric>
namespace rsx
{
namespace overlays
{
void animation_translate::apply(compiled_resource& resource)
{
if (progress == vector3i(0, 0, 0))
if (!active)
{
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& v : cmd.verts)
@ -23,49 +26,54 @@ namespace rsx
}
}
void animation_translate::update(u64 t)
void animation_translate::update(u64 frame)
{
if (!active)
{
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;
}
const u64 delta = (t - time);
const u64 frames = delta / 16667;
if (frames)
if (frame >= frame_end)
{
const int mag = frames * speed;
const vector3i new_progress = progress + direction * mag;
const auto d = new_progress.distance(progress_limit);
if (distance >= 0)
{
if (d > distance)
{
// Exit condition
finish();
return;
}
}
progress = new_progress;
distance = d;
time = t;
// Exit condition
finish();
return;
}
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()
{
active = false;
distance = -1;
time = 0;
frame_start = 0;
frame_end = 0;
current = end; // Snap current to limit in case we went over
if (on_finish)
{

View File

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

View File

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