ui: Improve logo animation

This commit is contained in:
Matt Borgerson 2022-06-18 03:41:07 -07:00 committed by mborgerson
parent 3a6907b0ea
commit f672185b1c
7 changed files with 179 additions and 135 deletions

View File

@ -2,30 +2,30 @@
#define msdf tex
uniform sampler2D tex;
uniform vec4 in_ColorPrimary;
uniform vec4 in_ColorSecondary;
uniform vec4 in_ColorFill;
uniform float scale;
uniform float iTime;
in vec2 Texcoord;
out vec4 out_Color;
float pxRange = 6.0;
vec4 bgColor = in_ColorFill;
vec4 fgColor = in_ColorPrimary;
vec4 particleColor = in_ColorSecondary;
const int numParticles = 40;
const float duration = 1.1;
const float pause = 5.0;
const vec4 textPos = vec4(0.01, 0, 0.98, 0.125);
const float lineWidth = 0.15;
float pxRange = 6.0;
vec4 fgColor = in_ColorPrimary;
vec4 bgColor = in_ColorFill;
const vec4 textPos = vec4(0.01, 0, 0.98, 0.125);
const float pi = 3.14159265359;
const float lineWidth = 0.175;
const float duration = 1.25;
const float pause = 6.;
const int numParticles = 35;
const int numSpotlights = 5;
// Thanks to: https://www.shadertoy.com/view/Xl2SRR
float random(float co)
{
return fract(sin(co*12.989) * 43758.545);
return fract(abs(sin(co*12.989)) * 43758.545);
}
float median(float r, float g, float b) {
float median(float r, float g, float b)
{
return max(min(r, g), min(max(r, g), b));
}
@ -42,9 +42,9 @@ float getCurrentTime()
float getBox(vec2 uv, float x, float width)
{
float rhs = sign(clamp(x-uv.x+width, 0., 1.));
float lhs = sign(clamp(x-uv.x, 0., 1.));
return rhs-lhs;
float lhs = sign(clamp(x - uv.x, 0., 1.));
float rhs = sign(clamp(x - uv.x + width, 0., 1.));
return rhs-lhs;
}
float getSweepingLinePos()
@ -60,51 +60,88 @@ float getSweepingLine(vec2 uv)
float getGradients(vec2 uv)
{
float t = getCurrentTime();
float gw = lineWidth/2.;
float left = getBox(uv, getSweepingLinePos() - gw, gw)*smoothstep(0., 1., (gw + lineWidth - (t - uv.x + textPos.x))/lineWidth);
float right = getBox(uv, getSweepingLinePos() + lineWidth, gw)*smoothstep(0., 1., (gw + (t - uv.x + textPos.x))/lineWidth);
float gradient_y = smoothstep(0.8, 1., 1.-abs(0.5-uv.y));
float l_s = abs(cos(t*pi*2.));
float pos = t - uv.x + textPos.x;
float left = l_s*smoothstep(0., 1., 0.5-abs(pos - lineWidth)*(20.+80.*(1.-l_s)));
float r_s = abs(sin(t*pi*2.));
float right = r_s*smoothstep(0., 1., 0.5-abs(pos)*(20.+80.*(1.-r_s)));
float gradient_y = smoothstep(0.55, 1., 1.-abs(0.5-uv.y));
return (left + right) * gradient_y;
}
vec2 getSpotlightPos(int i)
{
float t = getCurrentTime();
vec2 initialPos = textPos.zw*vec2(
float(i)/float(numSpotlights-1), // Even
sign(random(float(i+62)) - 0.6)*2.); // Top biased
vec2 velocity;
velocity.x = sign(random(float(i+63)) - 0.5)*0.7*(0.3+0.6*random(float(i+100)));
velocity.y = -sign(initialPos.y)*0.8*(0.1+0.9*random(float(i+62)));
return initialPos + velocity * t + vec2(textPos.x, 0.5); // Offset to center
}
float getSpotlights(vec2 uv)
{
float t = getCurrentTime();
float right = smoothstep(0.3, 0.7, 0.8-8.*abs(t - uv.x + textPos.x + 0.05));
// Compute contribution from all spotlights to this frag
float c = 0.;
for (int j = 0; j < numSpotlights; j++) {
vec2 pos = getSpotlightPos(j);
float d = distance(uv, pos);
c += (1.-smoothstep(0.04, 0.07835, d));
}
return 0.6*right + 0.4*c;
}
// Note: Does not include offset, added in getParticlePosition
vec2 getParticleInitialPosition(int i)
{
vec2 pos;
pos.x = float(i)/float(numParticles-1); // Even
pos.y = sign(random(float(i)) - 0.1); // Top biased
return pos*textPos.zw;
return textPos.zw*vec2(
float(i)/float(numParticles-1), // Even
sign(random(float(i)) - 0.2)); // Top biased
}
float prob(float p, int i)
{
return sign(clamp(random(float(i*30))-(1.-p), 0., 1.));
}
float getParticleLifespan(int i)
{
return 1. + 1.25*exp(-10.*random(float(i*30))) + 0.5*prob(0.3, i);
}
float getParticleTime(int i)
{
// Compute based on initial x due to sweeping reveal
return getCurrentTime()-getParticleInitialPosition(i).x;
}
float getParticleAlive(int i)
{
return clamp(sign(getParticleTime(i)), 0., 1.);
}
float getParticleIntensity(int i)
{
float lifespan = 1.0 + 0.4*(random(float(i*44))-0.5);
float alive = clamp(sign(getParticleTime(i)), 0., 1.);
return alive*clamp(lifespan-getParticleTime(i), 0., 1.);
return getParticleAlive(i)*clamp(getParticleLifespan(i)-getParticleTime(i), 0., 1.);
}
vec2 getParticlePosition(int i)
{
float pt = getParticleTime(i);
float alive = clamp(sign(pt), 0., 1.);
float falloff = 10.;
float impulse = quaImpulse(falloff, pt+0.8)+0.2;
vec2 pos = getParticleInitialPosition(i);
float impulse = quaImpulse(20., pt*0.25+0.05+0.4*random(float(i+30)));
vec2 initialPos = getParticleInitialPosition(i);
vec2 velocity;
// Move mostly right, but sometimes left
velocity.x = sign(random(float(i+32*3030))-0.2);
velocity.x *= impulse*1.25*(0.00 + random(float(i+62)));
// Move vertically in whatever direction we spawned in
velocity.y = sign(pos.y);
velocity.y *= impulse*1.40*(0.05 + random(float(i+62)));
return pos + alive * velocity * pt + vec2(textPos.x, 0.5); // Offset to center
// Move mostly right, sometimes left
velocity.x = 0.4*impulse*sign(random(float(i+66)) - 0.1)*(0.3 + 0.6*random(float(i + 100)));
// Move vertically in whatever direction particle spawned in
velocity.y = 0.8*impulse*sign(initialPos.y)*(0.1 + 0.9*random(float(i + 62)));
return initialPos + getParticleAlive(i) * velocity * pt + vec2(textPos.x, 0.5); // Offset to center
}
float getParticles(vec2 uv)
@ -114,7 +151,7 @@ float getParticles(vec2 uv)
for (int j = 0; j < numParticles; j++) {
vec2 pos = getParticlePosition(j);
float d = distance(uv, pos);
c += (1.-smoothstep(0.01, 0.01035,d))*getParticleIntensity(j);
c += (1.-smoothstep(0.004, 0.00835,d))*getParticleIntensity(j);
}
return c;
@ -122,32 +159,25 @@ float getParticles(vec2 uv)
void main()
{
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = gl_FragCoord.xy/vec2(512);
float scale = 1.4;
// Center when scaling
uv -= 0.5 * (1.-1./scale);
uv *= scale;
vec2 pos = uv;
// Get signed distance from the input texture
// Thanks to https://github.com/Chlumsky/msdfgen
vec2 msdfUnit = pxRange/vec2(textureSize(msdf, 0));
vec3 s = texture(msdf, pos).rgb;
vec3 msd = texture2D(tex, vec2(pos.x, pos.y)).rgb;
float sd = median(msd.r, msd.g, msd.b);
float screenPxDistance = pxRange*(sd - 0.5);
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
vec4 fill_color = mix(bgColor, fgColor, opacity);
float outline = clamp(screenPxDistance + 1.6, 0., 1.);
outline -= clamp(screenPxDistance - 1.6, 0., 1.);
outline = smoothstep(0.5, 1., outline);
// Create an alpha mask for the text
float sigDist = median(s.r, s.g, s.b) - 0.5;
sigDist *= dot(msdfUnit, 0.5/fwidth(pos));
float smoothing = 4.-scale;
float fill = smoothstep(0.5 - smoothing, 0.5 + smoothing, sigDist);
vec4 fill_color = mix(bgColor, fgColor, fill);
float outline = smoothstep(0.65, 0.80, 1.0-abs(sigDist/15.0-(-0.3333*scale+0.6667)+0.05));
vec4 line_color = mix(bgColor, fgColor, outline);
out_Color = mix(fill_color, line_color, getSweepingLine(uv));
out_Color += mix(bgColor, particleColor, getParticles(uv));
out_Color += 2.*vec4(1.)*getBox(uv, textPos.x, textPos.z)*getGradients(uv);
float mask_rhs = clamp(sign(uv.x-lineWidth-getSweepingLinePos()),0.,1.);
out_Color += fill_color*mask_rhs*getSpotlights(uv);
out_Color += mix(vec4(0), fgColor, getParticles(uv));
out_Color += 2.*fgColor*getBox(uv, textPos.x, textPos.z)*getGradients(uv);
}

View File

@ -368,20 +368,38 @@ void RenderDecal(DecalShader *s, float x, float y, float w, float h,
float tw = tw_i, th = th_i;
#define COL(color, c) (float)(((color)>>((c)*8)) & 0xff)/255.0
glUniform1i(s->flipy_loc, s->flip);
glUniform4f(s->scale_offset_loc, w / ww, h / wh, -1 + ((2 * x + w) / ww),
-1 + ((2 * y + h) / wh));
glUniform4f(s->tex_scale_offset_loc, tex_w / tw, tex_h / th, tex_x / tw,
tex_y / th);
glUniform1i(s->tex_loc, 0);
glUniform4f(s->color_primary_loc, COL(primary, 3), COL(primary, 2),
COL(primary, 1), COL(primary, 0));
glUniform4f(s->color_secondary_loc, COL(secondary, 3), COL(secondary, 2),
COL(secondary, 1), COL(secondary, 0));
glUniform4f(s->color_fill_loc, COL(fill, 3), COL(fill, 2), COL(fill, 1),
COL(fill, 0));
if (s->time_loc >= 0) glUniform1f(s->time_loc, s->time/1000.0f);
if (s->scale_loc >= 0) glUniform1f(s->scale_loc, s->scale);
if (s->flipy_loc >= 0) {
glUniform1i(s->flipy_loc, s->flip);
}
if (s->scale_offset_loc >= 0) {
glUniform4f(s->scale_offset_loc, w / ww, h / wh, -1 + ((2 * x + w) / ww),
-1 + ((2 * y + h) / wh));
}
if (s->tex_scale_offset_loc >= 0) {
glUniform4f(s->tex_scale_offset_loc, tex_w / tw, tex_h / th, tex_x / tw,
tex_y / th);
}
if (s->tex_loc >= 0) {
glUniform1i(s->tex_loc, 0);
}
if (s->color_primary_loc >= 0) {
glUniform4f(s->color_primary_loc, COL(primary, 3), COL(primary, 2),
COL(primary, 1), COL(primary, 0));
}
if (s->color_secondary_loc >= 0) {
glUniform4f(s->color_secondary_loc, COL(secondary, 3), COL(secondary, 2),
COL(secondary, 1), COL(secondary, 0));
}
if (s->color_fill_loc >= 0) {
glUniform4f(s->color_fill_loc, COL(fill, 3), COL(fill, 2), COL(fill, 1),
COL(fill, 0));
}
if (s->time_loc >= 0) {
glUniform1f(s->time_loc, s->time/1000.0f);
}
if (s->scale_loc >= 0) {
glUniform1f(s->scale_loc, s->scale);
}
#undef COL
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
}
@ -623,17 +641,18 @@ void RenderControllerPort(float frame_x, float frame_y, int i,
glUseProgram(0);
}
void RenderLogo(uint32_t time, uint32_t primary_color, uint32_t secondary_color,
uint32_t fill_color)
void RenderLogo(uint32_t time)
{
uint32_t color = 0x62ca13ff;
g_logo_shader->time = time;
glUseProgram(g_logo_shader->prog);
glBindVertexArray(g_decal_shader->vao);
glBlendFunc(GL_ONE, GL_ZERO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_logo_tex);
RenderDecal(g_logo_shader, 0, 0, 512, 512, 0, 0, 128, 128, primary_color,
secondary_color, fill_color);
RenderDecal(g_logo_shader, 0, 0, 512, 512, 0, 0, 128, 128, color,
color, 0x00000000);
glBindVertexArray(0);
glUseProgram(0);
}

View File

@ -40,8 +40,7 @@ public:
extern Fbo *controller_fbo, *logo_fbo;
void InitCustomRendering(void);
void RenderLogo(uint32_t time, uint32_t primary_color, uint32_t secondary_color,
uint32_t fill_color);
void RenderLogo(uint32_t time);
void RenderController(float frame_x, float frame_y, uint32_t primary_color,
uint32_t secondary_color, ControllerState *state);
void RenderControllerPort(float frame_x, float frame_y, int i,

View File

@ -819,31 +819,7 @@ void MainMenuAboutView::Draw()
gl_renderer, gl_version, gl_shader_version);
}
static uint32_t time_start = 0;
if (ImGui::IsWindowAppearing()) {
time_start = SDL_GetTicks();
}
uint32_t now = SDL_GetTicks() - time_start;
ImGui::SetCursorPosY(ImGui::GetCursorPosY()-50*g_viewport_mgr.m_scale);
ImGui::SetCursorPosX((ImGui::GetWindowWidth()-256*g_viewport_mgr.m_scale)/2);
logo_fbo->Target();
ImTextureID id = (ImTextureID)(intptr_t)logo_fbo->Texture();
float t_w = 256.0;
float t_h = 256.0;
float x_off = 0;
ImGui::Image(id,
ImVec2((t_w-x_off)*g_viewport_mgr.m_scale, t_h*g_viewport_mgr.m_scale),
ImVec2(x_off/t_w, t_h/t_h),
ImVec2(t_w/t_w, 0));
if (ImGui::IsItemClicked()) {
time_start = SDL_GetTicks();
}
RenderLogo(now, 0x42e335ff, 0x42e335ff, 0x00000000);
logo_fbo->Restore();
ImGui::SetCursorPosY(ImGui::GetCursorPosY()-75*g_viewport_mgr.m_scale);
Logo();
SectionTitle("Build Information");
ImGui::PushFont(g_font_mgr.m_fixed_width_font);

View File

@ -43,56 +43,33 @@ void FirstBootWindow::Draw()
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always);
ImGui::SetNextWindowSize(size, ImGuiCond_Appearing);
if (!ImGui::Begin("First Boot", &is_open, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDecoration)) {
if (!ImGui::Begin("First Boot", &is_open, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::End();
return;
}
static uint32_t time_start = 0;
if (ImGui::IsWindowAppearing()) {
time_start = SDL_GetTicks();
}
uint32_t now = SDL_GetTicks() - time_start;
ImGui::SetCursorPosY(ImGui::GetCursorPosY()-50*g_viewport_mgr.m_scale);
ImGui::SetCursorPosX((ImGui::GetWindowWidth()-256*g_viewport_mgr.m_scale)/2);
logo_fbo->Target();
ImTextureID id = (ImTextureID)(intptr_t)logo_fbo->Texture();
float t_w = 256.0;
float t_h = 256.0;
float x_off = 0;
ImGui::Image(id,
ImVec2((t_w-x_off)*g_viewport_mgr.m_scale, t_h*g_viewport_mgr.m_scale),
ImVec2(x_off/t_w, t_h/t_h),
ImVec2(t_w/t_w, 0));
if (ImGui::IsItemClicked()) {
time_start = SDL_GetTicks();
}
RenderLogo(now, 0x42e335ff, 0x42e335ff, 0x00000000);
logo_fbo->Restore();
ImGui::SetCursorPosY(ImGui::GetCursorPosY()-100*g_viewport_mgr.m_scale);
ImGui::SetCursorPosX(10*g_viewport_mgr.m_scale);
ImGui::Dummy(ImVec2(0,20*g_viewport_mgr.m_scale));
Logo();
const char *msg = "Configure machine settings to get started";
ImGui::SetCursorPosX((ImGui::GetWindowWidth()-ImGui::CalcTextSize(msg).x)/2);
ImGui::Text("%s", msg);
ImGui::Dummy(ImVec2(0,20*g_viewport_mgr.m_scale));
ImGui::SetCursorPosX((ImGui::GetWindowWidth()-120*g_viewport_mgr.m_scale)/2);
ImGui::SetItemDefaultFocus();
if (ImGui::Button("Settings", ImVec2(120*g_viewport_mgr.m_scale, 0))) {
g_main_menu.ShowSystem();
g_config.general.show_welcome = false;
}
ImGui::Dummy(ImVec2(0,20*g_viewport_mgr.m_scale));
ImGui::Dummy(ImVec2(0,50*g_viewport_mgr.m_scale));
msg = "Visit https://xemu.app for more information";
ImGui::SetCursorPosX((ImGui::GetWindowWidth()-ImGui::CalcTextSize(msg).x)/2);
Hyperlink(msg, "https://xemu.app");
ImGui::Dummy(ImVec2(400*g_viewport_mgr.m_scale,20*g_viewport_mgr.m_scale));
ImGui::End();
}

View File

@ -21,6 +21,7 @@
#include "font-manager.hh"
#include "viewport-manager.hh"
#include "ui/xemu-os-utils.h"
#include "gl-helpers.hh"
void Separator()
{
@ -505,3 +506,44 @@ void HelpMarker(const char* desc)
ImGui::EndTooltip();
}
}
void Logo()
{
ImGui::SetCursorPosY(ImGui::GetCursorPosY()-25*g_viewport_mgr.m_scale);
ImGui::SetCursorPosX((ImGui::GetWindowWidth()-256*g_viewport_mgr.m_scale)/2);
static uint32_t time_start = 0;
static uint32_t offset = 0;
uint32_t now = SDL_GetTicks();
if (ImGui::IsWindowAppearing()) {
time_start = now;
}
logo_fbo->Target();
ImTextureID id = (ImTextureID)(intptr_t)logo_fbo->Texture();
float t_w = 256.0;
float t_h = 256.0;
float x_off = 0;
ImVec2 pos = ImGui::GetCursorPos();
ImGui::Image(id,
ImVec2((t_w-x_off)*g_viewport_mgr.m_scale, t_h*g_viewport_mgr.m_scale),
ImVec2(x_off/t_w, t_h/t_h),
ImVec2(t_w/t_w, 0));
ImVec2 size = ImGui::GetItemRectSize();
ImGui::SetCursorPos(pos);
ImGui::InvisibleButton("###empty", ImVec2(size.x, size.y*0.8));
if (ImGui::IsItemClicked()) {
time_start = now;
offset = 0;
}
if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
ImVec2 item_min = ImGui::GetItemRectMin();
ImVec2 mouse = ImGui::GetMousePos();
time_start = now;
offset = 1500 * fmin(fmax(0, (mouse.x - item_min.x) / (size.x)), 1);
}
RenderLogo(now - time_start + offset);
logo_fbo->Restore();
}

View File

@ -46,3 +46,4 @@ bool ChevronCombo(const char *label, int *current_item,
bool ChevronCombo(const char* label, int* current_item, const char* items_separated_by_zeros, const char *description = NULL);
void Hyperlink(const char *text, const char *url);
void HelpMarker(const char* desc);
void Logo();