Fix arrow circle animation and added pulse animation

This commit is contained in:
PTKay 2024-12-04 12:10:29 +00:00
parent f6824c135a
commit 0f7780165c
2 changed files with 75 additions and 31 deletions

View file

@ -327,6 +327,7 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/in
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_007.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_007.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_007") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_007.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_007.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_007")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_008.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_008.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_008") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_008.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_008.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_008")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/miles_electric_icon.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/miles_electric_icon.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_miles_electric_icon") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/miles_electric_icon.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/miles_electric_icon.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_miles_electric_icon")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/pulse_install.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/pulse_install.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_pulse_install")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/general_window.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/general_window.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_general_window") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/general_window.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/general_window.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_general_window")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fade") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fade")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fill") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fill")

View file

@ -19,7 +19,9 @@
#include <res/images/installer/install_008.dds.h> #include <res/images/installer/install_008.dds.h>
#include <res/images/installer/miles_electric_icon.dds.h> #include <res/images/installer/miles_electric_icon.dds.h>
#include <res/images/installer/arrow_circle.dds.h> #include <res/images/installer/arrow_circle.dds.h>
#include <res/images/installer/pulse_install.dds.h>
// One Shot Animations Constants
static constexpr double SCANLINES_ANIMATION_TIME = 0.0; static constexpr double SCANLINES_ANIMATION_TIME = 0.0;
static constexpr double SCANLINES_ANIMATION_DURATION = 15.0; static constexpr double SCANLINES_ANIMATION_DURATION = 15.0;
@ -41,10 +43,15 @@ static constexpr double CONTAINER_OUTER_DURATION = 23.0;
static constexpr double CONTAINER_INNER_TIME = SCANLINES_ANIMATION_DURATION + CONTAINER_LINE_ANIMATION_DURATION + 8.0; static constexpr double CONTAINER_INNER_TIME = SCANLINES_ANIMATION_DURATION + CONTAINER_LINE_ANIMATION_DURATION + 8.0;
static constexpr double CONTAINER_INNER_DURATION = 15.0; static constexpr double CONTAINER_INNER_DURATION = 15.0;
static constexpr double ARROW_CIRCLE_SPIN_FACTOR = 2;
static constexpr double ALL_ANIMATIONS_FULL_DURATION = CONTAINER_INNER_TIME + CONTAINER_INNER_DURATION; static constexpr double ALL_ANIMATIONS_FULL_DURATION = CONTAINER_INNER_TIME + CONTAINER_INNER_DURATION;
// Loop Animations Constants - their time range is [0.0, 1.0 + DELAY]
static constexpr double ARROW_CIRCLE_LOOP_SPEED = 1;
static constexpr double PULSE_ANIMATION_LOOP_SPEED = 1.5;
static constexpr double PULSE_ANIMATION_LOOP_DELAY = 0.5;
static constexpr double PULSE_ANIMATION_LOOP_FADE_HIGH_POINT = 0.5;
constexpr float IMAGE_X = 165.0f; constexpr float IMAGE_X = 165.0f;
constexpr float IMAGE_Y = 106.0f; constexpr float IMAGE_Y = 106.0f;
constexpr float IMAGE_WIDTH = 512.0f; constexpr float IMAGE_WIDTH = 512.0f;
@ -87,6 +94,7 @@ static std::array<bool, int(DLC::Count)> g_dlcInstalled = {};
static std::array<std::unique_ptr<GuestTexture>, 8> g_installTextures; static std::array<std::unique_ptr<GuestTexture>, 8> g_installTextures;
static std::unique_ptr<GuestTexture> g_milesElectricIcon; static std::unique_ptr<GuestTexture> g_milesElectricIcon;
static std::unique_ptr<GuestTexture> g_arrowCircle; static std::unique_ptr<GuestTexture> g_arrowCircle;
static std::unique_ptr<GuestTexture> g_pulseInstall;
static Journal g_installerJournal; static Journal g_installerJournal;
static Installer::Sources g_installerSources; static Installer::Sources g_installerSources;
static uint64_t g_installerAvailableSize = 0; static uint64_t g_installerAvailableSize = 0;
@ -190,6 +198,14 @@ static double ComputeMotionInstaller(double timeAppear, double timeDisappear, do
return ComputeMotion(timeAppear, offset, total) * (1.0 - ComputeMotion(timeDisappear, ALL_ANIMATIONS_FULL_DURATION - offset - total, total)); return ComputeMotion(timeAppear, offset, total) * (1.0 - ComputeMotion(timeDisappear, ALL_ANIMATIONS_FULL_DURATION - offset - total, total));
} }
static double ComputeMotionInstallerLoop(double timeAppear, double speed, double offset) {
return std::clamp(fmodf((ImGui::GetTime() - timeAppear) * speed, 1.0f + offset) - offset, 0.0, 1.0) / 1.0;
}
static double ComputeHermiteMotionInstallerLoop(double timeAppear, double speed, double offset) {
return (cosf(M_PI * ComputeMotionInstallerLoop(timeAppear, speed, offset) + M_PI) + 1) / 2;
}
static void DrawBackground() static void DrawBackground()
{ {
auto &res = ImGui::GetIO().DisplaySize; auto &res = ImGui::GetIO().DisplaySize;
@ -221,11 +237,63 @@ static void DrawLeftImage()
drawList->AddRectFilledMultiColor(min, max, IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32(0, 0, 0, 255), IM_COL32(0, 0, 0, 255)); drawList->AddRectFilledMultiColor(min, max, IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32(0, 0, 0, 255), IM_COL32(0, 0, 0, 255));
} }
static void DrawHeaderIconsForInstallPhase(double iconsPosX, double iconsPosY, double iconsScale)
{
auto drawList = ImGui::GetForegroundDrawList();
// Arrow Circle Icon
ImVec2 arrowCircleMin = { Scale(iconsPosX - iconsScale / 2), Scale(iconsPosY - iconsScale / 2) };
ImVec2 arrowCircleMax = { Scale(iconsPosX + iconsScale / 2), Scale(iconsPosY + iconsScale / 2) };
ImVec2 center = { Scale(iconsPosX) + 0.5f, Scale(iconsPosY) - 0.5f };
float rotationMotion = ComputeMotionInstallerLoop(g_installerStartTime, ARROW_CIRCLE_LOOP_SPEED, 0);
float rotation = -2 * M_PI * rotationMotion;
// Calculate rotated corners
float cosCurrentAngle = cosf(rotation);
float sinCurrentAngle = sinf(rotation);
ImVec2 corners[4] =
{
ImRotate(ImVec2(arrowCircleMin.x - center.x, arrowCircleMin.y - center.y), cosCurrentAngle, sinCurrentAngle),
ImRotate(ImVec2(arrowCircleMax.x - center.x, arrowCircleMin.y - center.y), cosCurrentAngle, sinCurrentAngle),
ImRotate(ImVec2(arrowCircleMax.x - center.x, arrowCircleMax.y - center.y), cosCurrentAngle, sinCurrentAngle),
ImRotate(ImVec2(arrowCircleMin.x - center.x, arrowCircleMax.y - center.y), cosCurrentAngle, sinCurrentAngle),
};
for (int i = 0; i < IM_ARRAYSIZE(corners); ++i)
{
corners[i].x += center.x;
corners[i].y += center.y;
}
drawList->AddImageQuad(g_arrowCircle.get(), corners[0], corners[1], corners[2], corners[3], ImVec2(0, 0), ImVec2(1, 0), ImVec2(1, 1), ImVec2(0, 1), IM_COL32(255, 255, 255, 96));
// Pulse
float pulseMotion = ComputeMotionInstallerLoop(g_installerStartTime, PULSE_ANIMATION_LOOP_SPEED, PULSE_ANIMATION_LOOP_DELAY);
float pulseHermiteMotion = ComputeHermiteMotionInstallerLoop(g_installerStartTime, PULSE_ANIMATION_LOOP_SPEED, PULSE_ANIMATION_LOOP_DELAY);
float pulseFade = pulseMotion / PULSE_ANIMATION_LOOP_FADE_HIGH_POINT;
if (pulseMotion >= PULSE_ANIMATION_LOOP_FADE_HIGH_POINT) {
// Calculate linear fade-out from high point time - ({PULSE_ANIMATION_LOOP_FADE_HIGH_POINT}, 1) - to loop end - (1, 0) -.
float m = -1 / (1 - PULSE_ANIMATION_LOOP_FADE_HIGH_POINT);
float b = m * (-PULSE_ANIMATION_LOOP_FADE_HIGH_POINT) + 1;
pulseFade = m * pulseMotion + b;
}
float pulseScale = iconsScale * pulseHermiteMotion * 1.5;
ImVec2 pulseMin = { Scale(iconsPosX - pulseScale / 2), Scale(iconsPosY - pulseScale / 2) };
ImVec2 pulseMax = { Scale(iconsPosX + pulseScale / 2), Scale(iconsPosY + pulseScale / 2) };
drawList->AddImage(g_pulseInstall.get(), pulseMin, pulseMax, ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, 255 * pulseFade));
}
static void DrawHeaderIcons() static void DrawHeaderIcons()
{ {
auto drawList = ImGui::GetForegroundDrawList(); auto drawList = ImGui::GetForegroundDrawList();
float iconsPosX = 255.0f; float iconsPosX = 253.0f;
float iconsPosY = 79.0f; float iconsPosY = 79.0f;
float iconsScale = 58; float iconsScale = 58;
@ -237,36 +305,9 @@ static void DrawHeaderIcons()
ImVec2 milesElectricMax = { Scale(iconsPosX + milesIconScale / 2), Scale(iconsPosY + milesIconScale / 2) }; ImVec2 milesElectricMax = { Scale(iconsPosX + milesIconScale / 2), Scale(iconsPosY + milesIconScale / 2) };
drawList->AddImage(g_milesElectricIcon.get(), milesElectricMin, milesElectricMax, ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, 255 * milesIconMotion)); drawList->AddImage(g_milesElectricIcon.get(), milesElectricMin, milesElectricMax, ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, 255 * milesIconMotion));
// Arrow Circle Icon
if (g_currentPage == WizardPage::Installing) if (g_currentPage == WizardPage::Installing)
{ {
ImVec2 arrowCircleMin = { Scale(iconsPosX - iconsScale / 2), Scale(iconsPosY - iconsScale / 2) }; DrawHeaderIconsForInstallPhase(iconsPosX, iconsPosY, iconsScale);
ImVec2 arrowCircleMax = { Scale(iconsPosX + iconsScale / 2), Scale(iconsPosY + iconsScale / 2) };
ImVec2 center = { Scale(iconsPosX) + 0.5f, Scale(iconsPosY) - 0.5f };
float currentAngle = g_arrowCircleCurrentRotation * (3.14159f / 180.0f); // Rotation angle in radians
float cos_a = cosf(currentAngle);
float sin_a = sinf(currentAngle);
// Calculate rotated corners
ImVec2 corners[4] =
{
ImRotate(ImVec2(arrowCircleMin.x - center.x, arrowCircleMin.y - center.y), cos_a, sin_a),
ImRotate(ImVec2(arrowCircleMax.x - center.x, arrowCircleMin.y - center.y), cos_a, sin_a),
ImRotate(ImVec2(arrowCircleMax.x - center.x, arrowCircleMax.y - center.y), cos_a, sin_a),
ImRotate(ImVec2(arrowCircleMin.x - center.x, arrowCircleMax.y - center.y), cos_a, sin_a),
};
for (int i = 0; i < IM_ARRAYSIZE(corners); ++i)
{
corners[i].x += center.x; // Add center.x to corner.x
corners[i].y += center.y; // Add center.y to corner.y
}
drawList->AddImageQuad(g_arrowCircle.get(), corners[0], corners[1], corners[2], corners[3], ImVec2(0, 0), ImVec2(1, 0), ImVec2(1, 1), ImVec2(0, 1), IM_COL32(255, 255, 255, 96));
// Update rotation for next frame
g_arrowCircleCurrentRotation = fmodf(g_arrowCircleCurrentRotation - ARROW_CIRCLE_SPIN_FACTOR, 360.0f);
} }
} }
@ -1089,6 +1130,7 @@ void InstallerWizard::Init()
g_installTextures[7] = LoadTexture(g_install_008, sizeof(g_install_008)); g_installTextures[7] = LoadTexture(g_install_008, sizeof(g_install_008));
g_milesElectricIcon = LoadTexture(g_miles_electric_icon, sizeof(g_miles_electric_icon)); g_milesElectricIcon = LoadTexture(g_miles_electric_icon, sizeof(g_miles_electric_icon));
g_arrowCircle = LoadTexture(g_arrow_circle, sizeof(g_arrow_circle)); g_arrowCircle = LoadTexture(g_arrow_circle, sizeof(g_arrow_circle));
g_pulseInstall = LoadTexture(g_pulse_install, sizeof(g_pulse_install));
} }
void InstallerWizard::Draw() void InstallerWizard::Draw()
@ -1140,6 +1182,7 @@ void InstallerWizard::Shutdown()
// Erase the textures. // Erase the textures.
g_milesElectricIcon.reset(); g_milesElectricIcon.reset();
g_arrowCircle.reset(); g_arrowCircle.reset();
g_pulseInstall.reset();
for (auto &texture : g_installTextures) for (auto &texture : g_installTextures)
{ {