Refactor, Vsync, FFmpeg fixes
This commit is contained in:
13
src/COMMON.h
13
src/COMMON.h
@@ -23,10 +23,9 @@
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
@@ -49,10 +48,9 @@ using namespace glm;
|
||||
#define FLOAT_TO_U8(x) (static_cast<u8>((x) * 255.0f))
|
||||
#define U8_TO_FLOAT(x) ((x) / 255.0f)
|
||||
#define PERCENT_TO_UNIT(x) (x / 100.0f)
|
||||
#define TICK_DELAY 33.3f
|
||||
#define TICK_CATCH_UP_MAX (33.3f * 5)
|
||||
#define SECOND 1000.0f
|
||||
#define TICK_RATE (SECOND / TICK_DELAY)
|
||||
#define TICK_DELAY (SECOND / 30.0)
|
||||
#define UPDATE_DELAY (SECOND / 120.0)
|
||||
#define ID_NONE -1
|
||||
#define INDEX_NONE -1
|
||||
#define TIME_NONE -1.0f
|
||||
@@ -125,6 +123,11 @@ static inline bool string_to_bool(const std::string& string)
|
||||
return lower == "true";
|
||||
}
|
||||
|
||||
static inline std::string string_quote(const std::string& string)
|
||||
{
|
||||
return "\"" + string + "\"";
|
||||
}
|
||||
|
||||
static inline std::string path_canonical_resolve
|
||||
(
|
||||
const std::string& inputPath,
|
||||
|
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "anm2.h"
|
||||
#include <variant>
|
||||
|
||||
enum ClipboardItemType
|
||||
{
|
||||
@@ -34,4 +33,3 @@ void clipboard_copy(Clipboard* self);
|
||||
void clipboard_cut(Clipboard* self);
|
||||
void clipboard_paste(Clipboard* self);
|
||||
void clipboard_init(Clipboard* self, Anm2* anm2);
|
||||
|
||||
|
@@ -30,44 +30,43 @@ void dialog_init(Dialog* self, SDL_Window* window)
|
||||
|
||||
void dialog_anm2_open(Dialog* self)
|
||||
{
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_ANM2, 1, nullptr, false);
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_ANM2, std::size(DIALOG_FILE_FILTER_ANM2), nullptr, false);
|
||||
self->type = DIALOG_ANM2_OPEN;
|
||||
}
|
||||
|
||||
void dialog_anm2_save(Dialog* self)
|
||||
{
|
||||
SDL_ShowSaveFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_ANM2, 1, nullptr);
|
||||
SDL_ShowSaveFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_ANM2, std::size(DIALOG_FILE_FILTER_ANM2), nullptr);
|
||||
self->type = DIALOG_ANM2_SAVE;
|
||||
}
|
||||
|
||||
void dialog_spritesheet_add(Dialog* self)
|
||||
{
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_PNG, 1, nullptr, false);
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_PNG, std::size(DIALOG_FILE_FILTER_PNG), nullptr, false);
|
||||
self->type = DIALOG_SPRITESHEET_ADD;
|
||||
}
|
||||
|
||||
void dialog_spritesheet_replace(Dialog* self, s32 id)
|
||||
{
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_PNG, 1, nullptr, false);
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_PNG, std::size(DIALOG_FILE_FILTER_PNG), nullptr, false);
|
||||
self->replaceID = id;
|
||||
self->type = DIALOG_SPRITESHEET_REPLACE;
|
||||
}
|
||||
|
||||
void dialog_render_path_set(Dialog* self)
|
||||
void dialog_render_path_set(Dialog* self, RenderType type)
|
||||
{
|
||||
SDL_ShowSaveFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_RENDER, 2, nullptr);
|
||||
self->type = DIALOG_RENDER_PATH_SET;
|
||||
}
|
||||
SDL_DialogFileFilter filter = DIALOG_RENDER_FILE_FILTERS[type];
|
||||
|
||||
void dialog_render_directory_set(Dialog* self)
|
||||
{
|
||||
SDL_ShowOpenFolderDialog(_dialog_callback, self, self->window, nullptr, false);
|
||||
self->type = DIALOG_RENDER_DIRECTORY_SET;
|
||||
if (type == RENDER_PNG)
|
||||
SDL_ShowOpenFolderDialog(_dialog_callback, self, self->window, nullptr, false);
|
||||
else
|
||||
SDL_ShowSaveFileDialog(_dialog_callback, self, self->window, &filter, 1, nullptr);
|
||||
self->type = DIALOG_RENDER_PATH_SET;
|
||||
}
|
||||
|
||||
void dialog_ffmpeg_path_set(Dialog* self)
|
||||
{
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_FFMPEG, 1, nullptr, false);
|
||||
SDL_ShowOpenFileDialog(_dialog_callback, self, self->window, DIALOG_FILE_FILTER_FFMPEG, std::size(DIALOG_FILE_FILTER_FFMPEG), nullptr, false);
|
||||
self->type = DIALOG_FFMPEG_PATH_SET;
|
||||
}
|
||||
|
||||
|
12
src/dialog.h
12
src/dialog.h
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "render.h"
|
||||
#include "window.h"
|
||||
|
||||
const SDL_DialogFileFilter DIALOG_FILE_FILTER_ANM2[] =
|
||||
@@ -12,15 +13,17 @@ const SDL_DialogFileFilter DIALOG_FILE_FILTER_PNG[] =
|
||||
{"PNG image", "png"}
|
||||
};
|
||||
|
||||
const SDL_DialogFileFilter DIALOG_FILE_FILTER_RENDER[] =
|
||||
const SDL_DialogFileFilter DIALOG_RENDER_FILE_FILTERS[] =
|
||||
{
|
||||
{"PNG image", "png"},
|
||||
{"GIF image", "gif"},
|
||||
{"WebM video", "webm"}
|
||||
{"WebM video", "webm"},
|
||||
{"MP4 video", "mp4"}
|
||||
};
|
||||
|
||||
const SDL_DialogFileFilter DIALOG_FILE_FILTER_FFMPEG[] =
|
||||
{
|
||||
{"Executable", ""}
|
||||
{"Executable", "exe"}
|
||||
};
|
||||
|
||||
enum DialogType
|
||||
@@ -31,7 +34,6 @@ enum DialogType
|
||||
DIALOG_SPRITESHEET_ADD,
|
||||
DIALOG_SPRITESHEET_REPLACE,
|
||||
DIALOG_RENDER_PATH_SET,
|
||||
DIALOG_RENDER_DIRECTORY_SET,
|
||||
DIALOG_FFMPEG_PATH_SET
|
||||
};
|
||||
|
||||
@@ -50,7 +52,7 @@ void dialog_anm2_open(Dialog* self);
|
||||
void dialog_spritesheet_add(Dialog* self);
|
||||
void dialog_spritesheet_replace(Dialog* self, s32 id);
|
||||
void dialog_anm2_save(Dialog* self);
|
||||
void dialog_render_path_set(Dialog* self);
|
||||
void dialog_render_path_set(Dialog* self, RenderType type);
|
||||
void dialog_render_directory_set(Dialog* self);
|
||||
void dialog_ffmpeg_path_set(Dialog* self);
|
||||
void dialog_reset(Dialog* self);
|
||||
|
@@ -23,10 +23,17 @@ ffmpeg_render
|
||||
case RENDER_WEBM:
|
||||
command = std::format(FFMPEG_WEBM_FORMAT, ffmpegPath, size.x, size.y, fps, outputPath);
|
||||
break;
|
||||
case RENDER_MP4:
|
||||
command = std::format(FFMPEG_MP4_FORMAT, ffmpegPath, size.x, size.y, fps, outputPath);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
command = string_quote(command)
|
||||
#endif
|
||||
|
||||
FILE* fp = POPEN(command.c_str(), PWRITE_MODE);
|
||||
|
||||
if (!fp)
|
||||
|
14
src/ffmpeg.h
14
src/ffmpeg.h
@@ -6,19 +6,25 @@
|
||||
#define FFMPEG_POPEN_ERROR "popen() (for FFmpeg) failed!\n{}"
|
||||
|
||||
static constexpr const char* FFMPEG_GIF_FORMAT =
|
||||
"\"{0}\" -y "
|
||||
"{0} -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-lavfi \"split[s0][s1];"
|
||||
"[s0]palettegen=stats_mode=full[p];"
|
||||
"[s1][p]paletteuse=dither=floyd_steinberg\" "
|
||||
"-loop 0 \"{4}\"";
|
||||
"-loop 0 {4}";
|
||||
|
||||
static constexpr const char* FFMPEG_WEBM_FORMAT =
|
||||
"\"{0}\" -y "
|
||||
"{0} -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-c:v libvpx-vp9 -crf 30 -b:v 0 -pix_fmt yuva420p -row-mt 1 -threads 0 -speed 2 "
|
||||
"-auto-alt-ref 0 -an \"{4}\"";
|
||||
"-auto-alt-ref 0 -an {4}";
|
||||
|
||||
static constexpr const char* FFMPEG_MP4_FORMAT =
|
||||
"{0} -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-vf \"format=yuv420p,scale=trunc(iw/2)*2:trunc(ih/2)*2\" "
|
||||
"-c:v libx265 -crf 20 -preset slow "
|
||||
"-tag:v hvc1 -movflags +faststart -an {4}";
|
||||
|
||||
bool
|
||||
ffmpeg_render
|
||||
|
@@ -354,10 +354,11 @@ static bool NAME(const ImguiItem& self, Imgui* imgui, bool& boolValue)
|
||||
ImguiItem checkboxItem = self.copy \
|
||||
({.label = std::format(IMGUI_INVISIBLE_FORMAT, self.label), .isMnemonicDisabled = true}); \
|
||||
checkboxItem.isDisabled = false; \
|
||||
_imgui_checkbox(checkboxItem, imgui, boolValue); \
|
||||
bool isCheckboxActivated = _imgui_checkbox(checkboxItem, imgui, boolValue); \
|
||||
ImGui::SameLine(); \
|
||||
bool isActivated = ([&] { return FUNCTION; })(); \
|
||||
if (isActivated) boolValue = !boolValue; \
|
||||
if (isCheckboxActivated) isActivated = true; \
|
||||
return isActivated; \
|
||||
}
|
||||
|
||||
@@ -367,9 +368,10 @@ static bool NAME(const ImguiItem& self, Imgui* imgui, VALUE& value, bool& boolVa
|
||||
ImguiItem checkboxItem = self.copy \
|
||||
({.label = std::format(IMGUI_INVISIBLE_FORMAT, self.label), .isMnemonicDisabled = true}); \
|
||||
checkboxItem.isDisabled = false; \
|
||||
_imgui_checkbox(checkboxItem, imgui, boolValue); \
|
||||
bool isCheckboxActivated = _imgui_checkbox(checkboxItem, imgui, boolValue); \
|
||||
ImGui::SameLine(); \
|
||||
bool isActivated = ([&](VALUE& value) { return FUNCTION; })(value); \
|
||||
if (isCheckboxActivated) isActivated = true; \
|
||||
return isActivated; \
|
||||
}
|
||||
|
||||
@@ -1413,49 +1415,45 @@ static void _imgui_taskbar(Imgui* self)
|
||||
|
||||
if (imgui_begin_popup_modal(IMGUI_RENDER_ANIMATION.popup, self, IMGUI_RENDER_ANIMATION.popupSize))
|
||||
{
|
||||
static DialogType& dialogType = self->dialog->type;
|
||||
static bool& dialogIsSelected = self->dialog->isSelected;
|
||||
static s32& type = self->settings->renderType;
|
||||
static std::string& dialogPath = self->dialog->path;
|
||||
static std::string& ffmpegPath = self->settings->ffmpegPath;
|
||||
static std::string& format = self->settings->renderFormat;
|
||||
static std::string& path = self->settings->renderPath;
|
||||
|
||||
_imgui_begin_child(IMGUI_RENDER_ANIMATION_CHILD, self);
|
||||
|
||||
if (_imgui_atlas_button(IMGUI_RENDER_ANIMATION_LOCATION_BROWSE, self))
|
||||
{
|
||||
switch (self->settings->renderType)
|
||||
{
|
||||
case RENDER_PNG: dialog_render_directory_set(self->dialog); break;
|
||||
default:
|
||||
dialog_render_path_set(self->dialog);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dialog_render_path_set(self->dialog, (RenderType)type);
|
||||
|
||||
if
|
||||
(
|
||||
self->dialog->isSelected &&
|
||||
(self->dialog->type == DIALOG_RENDER_PATH_SET || self->dialog->type == DIALOG_RENDER_DIRECTORY_SET)
|
||||
)
|
||||
if (dialogIsSelected && (dialogType == DIALOG_RENDER_PATH_SET))
|
||||
{
|
||||
self->settings->renderPath = self->dialog->path;
|
||||
path = path_extension_change(dialogPath, RENDER_EXTENSIONS[type]);
|
||||
dialog_reset(self->dialog);
|
||||
}
|
||||
|
||||
_imgui_input_text(IMGUI_RENDER_ANIMATION_LOCATION, self, self->settings->renderPath);
|
||||
_imgui_input_text(IMGUI_RENDER_ANIMATION_LOCATION, self, path);
|
||||
|
||||
if (_imgui_atlas_button(IMGUI_RENDER_ANIMATION_FFMPEG_BROWSE, self))
|
||||
dialog_ffmpeg_path_set(self->dialog);
|
||||
|
||||
if (self->dialog->isSelected && self->dialog->type == DIALOG_FFMPEG_PATH_SET)
|
||||
if (dialogIsSelected && dialogType == DIALOG_FFMPEG_PATH_SET)
|
||||
{
|
||||
self->settings->ffmpegPath = self->dialog->path;
|
||||
ffmpegPath = self->dialog->path;
|
||||
dialog_reset(self->dialog);
|
||||
}
|
||||
|
||||
_imgui_input_text(IMGUI_RENDER_ANIMATION_FFMPEG_PATH, self, self->settings->ffmpegPath);
|
||||
_imgui_input_text(IMGUI_RENDER_ANIMATION_FORMAT, self, self->settings->renderFormat);
|
||||
_imgui_combo(IMGUI_RENDER_ANIMATION_OUTPUT, self, &self->settings->renderType);
|
||||
_imgui_input_text(IMGUI_RENDER_ANIMATION_FFMPEG_PATH, self, ffmpegPath);
|
||||
_imgui_input_text(IMGUI_RENDER_ANIMATION_FORMAT, self, format);
|
||||
_imgui_combo(IMGUI_RENDER_ANIMATION_OUTPUT, self, &type);
|
||||
|
||||
if (_imgui_button(IMGUI_RENDER_ANIMATION_CONFIRM, self))
|
||||
{
|
||||
bool isRenderStart = true;
|
||||
|
||||
if (!std::filesystem::exists(self->settings->ffmpegPath))
|
||||
if (!std::filesystem::exists(ffmpegPath))
|
||||
{
|
||||
imgui_log_push(self, IMGUI_LOG_RENDER_ANIMATION_FFMPEG_PATH_ERROR);
|
||||
isRenderStart = false;
|
||||
@@ -1466,7 +1464,7 @@ static void _imgui_taskbar(Imgui* self)
|
||||
switch (self->settings->renderType)
|
||||
{
|
||||
case RENDER_PNG:
|
||||
if (!std::filesystem::is_directory(self->settings->renderPath))
|
||||
if (!std::filesystem::is_directory(path))
|
||||
{
|
||||
imgui_log_push(self, IMGUI_LOG_RENDER_ANIMATION_DIRECTORY_ERROR);
|
||||
isRenderStart = false;
|
||||
@@ -1474,7 +1472,8 @@ static void _imgui_taskbar(Imgui* self)
|
||||
break;
|
||||
case RENDER_GIF:
|
||||
case RENDER_WEBM:
|
||||
if (!path_is_valid(self->settings->renderPath))
|
||||
case RENDER_MP4:
|
||||
if (!path_is_valid(path))
|
||||
{
|
||||
imgui_log_push(self, IMGUI_LOG_RENDER_ANIMATION_PATH_ERROR);
|
||||
isRenderStart = false;
|
||||
@@ -1502,6 +1501,9 @@ static void _imgui_taskbar(Imgui* self)
|
||||
|
||||
if (imgui_begin_popup_modal(IMGUI_RENDER_ANIMATION_CONFIRM.popup, self, IMGUI_RENDER_ANIMATION_CONFIRM.popupSize))
|
||||
{
|
||||
static s32& type = self->settings->renderType;
|
||||
static std::string& format = self->settings->renderFormat;
|
||||
|
||||
auto rendering_end = [&]()
|
||||
{
|
||||
preview_render_end(self->preview);
|
||||
@@ -1537,7 +1539,7 @@ static void _imgui_taskbar(Imgui* self)
|
||||
|
||||
if (self->preview->isRenderFinished)
|
||||
{
|
||||
switch (self->settings->renderType)
|
||||
switch (type)
|
||||
{
|
||||
case RENDER_PNG:
|
||||
{
|
||||
@@ -1546,10 +1548,9 @@ static void _imgui_taskbar(Imgui* self)
|
||||
|
||||
for (auto [i, frame] : std::views::enumerate(frames))
|
||||
{
|
||||
std::string framePath = std::vformat(self->settings->renderFormat, std::make_format_args(i));
|
||||
framePath = path_extension_change(framePath, RENDER_EXTENSIONS[self->settings->renderType]);
|
||||
if (!frame.isInvalid)
|
||||
texture_from_gl_write(&frame, framePath);
|
||||
std::string framePath = std::vformat(format, std::make_format_args(i));
|
||||
framePath = path_extension_change(framePath, RENDER_EXTENSIONS[type]);
|
||||
if (!frame.isInvalid) texture_from_gl_write(&frame, framePath);
|
||||
}
|
||||
|
||||
std::filesystem::current_path(workingPath);
|
||||
@@ -1558,11 +1559,12 @@ static void _imgui_taskbar(Imgui* self)
|
||||
}
|
||||
case RENDER_GIF:
|
||||
case RENDER_WEBM:
|
||||
case RENDER_MP4:
|
||||
{
|
||||
std::string ffmpegPath = std::string(self->settings->ffmpegPath.c_str());
|
||||
path = path_extension_change(path, RENDER_EXTENSIONS[self->settings->renderType]);
|
||||
|
||||
if (ffmpeg_render(ffmpegPath, path, frames, self->preview->canvas.size, self->anm2->fps, (RenderType)self->settings->renderType))
|
||||
if (ffmpeg_render(ffmpegPath, path, frames, self->preview->canvas.size, self->anm2->fps, (RenderType)type))
|
||||
imgui_log_push(self, std::format(IMGUI_LOG_RENDER_ANIMATION_SAVE_FORMAT, path));
|
||||
else
|
||||
imgui_log_push(self, std::format(IMGUI_LOG_RENDER_ANIMATION_FFMPEG_ERROR, path));
|
||||
@@ -1587,6 +1589,14 @@ static void _imgui_taskbar(Imgui* self)
|
||||
imgui_end_popup(self);
|
||||
}
|
||||
|
||||
_imgui_selectable(IMGUI_SETTINGS.copy({}), self);
|
||||
|
||||
if (imgui_begin_popup(IMGUI_SETTINGS.popup, self, IMGUI_SETTINGS.popupSize))
|
||||
{
|
||||
if (_imgui_checkbox_selectable(IMGUI_VSYNC, self, self->settings->isVsync)) window_vsync_set(self->settings->isVsync);
|
||||
imgui_end_popup(self);
|
||||
}
|
||||
|
||||
_imgui_end();
|
||||
}
|
||||
|
||||
|
18
src/imgui.h
18
src/imgui.h
@@ -975,8 +975,8 @@ IMGUI_ITEM(IMGUI_PLAYBACK,
|
||||
self.tooltip = "Opens the playback menu, for configuring playback settings.",
|
||||
self.popup = "## Playback Popup",
|
||||
self.popupType = IMGUI_POPUP_BY_ITEM,
|
||||
self.popupSize = {150, 0},
|
||||
self.isSizeToText = true
|
||||
self.isSizeToText = true,
|
||||
self.isSameLine = true
|
||||
);
|
||||
|
||||
IMGUI_ITEM(IMGUI_ALWAYS_LOOP,
|
||||
@@ -991,6 +991,20 @@ IMGUI_ITEM(IMGUI_CLAMP_PLAYHEAD,
|
||||
self.isSizeToText = true
|
||||
);
|
||||
|
||||
IMGUI_ITEM(IMGUI_SETTINGS,
|
||||
self.label = "&Settings",
|
||||
self.tooltip = "Opens the setting menu, for configuring general program settings.",
|
||||
self.popup = "## Settings Popup",
|
||||
self.popupType = IMGUI_POPUP_BY_ITEM,
|
||||
self.isSizeToText = true
|
||||
);
|
||||
|
||||
IMGUI_ITEM(IMGUI_VSYNC,
|
||||
self.label = "&Vsync",
|
||||
self.tooltip = "Toggle vertical sync; synchronizes program framerate with your monitor's refresh rate.",
|
||||
self.isSizeToText = true
|
||||
);
|
||||
|
||||
IMGUI_ITEM(IMGUI_ANIMATIONS,
|
||||
self.label = "Animations",
|
||||
self.flags = ImGuiWindowFlags_NoScrollbar |
|
||||
|
@@ -44,7 +44,7 @@ void preview_tick(Preview* self)
|
||||
self->renderFrames.push_back(frameTexture);
|
||||
}
|
||||
|
||||
time += (f32)self->anm2->fps / TICK_RATE;
|
||||
time += (f32)self->anm2->fps / TICK_DELAY;
|
||||
|
||||
if (time >= (f32)animation->frameNum - 1)
|
||||
{
|
||||
@@ -72,10 +72,7 @@ void preview_tick(Preview* self)
|
||||
time = std::clamp(time, 0.0f, std::max(0.0f, (f32)animation->frameNum - 1));
|
||||
else
|
||||
time = std::max(time, 0.0f);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void preview_draw(Preview* self)
|
||||
|
@@ -7,19 +7,23 @@ enum RenderType
|
||||
RENDER_PNG,
|
||||
RENDER_GIF,
|
||||
RENDER_WEBM,
|
||||
RENDER_COUNT
|
||||
RENDER_MP4
|
||||
};
|
||||
|
||||
constexpr inline s32 RENDER_COUNT = RENDER_MP4 + 1;
|
||||
|
||||
const inline std::string RENDER_TYPE_STRINGS[] =
|
||||
{
|
||||
"PNG Images",
|
||||
"GIF image",
|
||||
"WebM video",
|
||||
"MP4 video"
|
||||
};
|
||||
|
||||
const inline std::string RENDER_EXTENSIONS[RENDER_COUNT] =
|
||||
{
|
||||
".png",
|
||||
".gif",
|
||||
".webm"
|
||||
".webm",
|
||||
".mp4"
|
||||
};
|
@@ -32,6 +32,7 @@ struct SettingsEntry
|
||||
struct Settings
|
||||
{
|
||||
ivec2 windowSize = {1080, 720};
|
||||
bool isVsync = true;
|
||||
bool playbackIsLoop = true;
|
||||
bool playbackIsClampPlayhead = true;
|
||||
bool changeIsCrop = false;
|
||||
@@ -106,6 +107,7 @@ struct Settings
|
||||
const SettingsEntry SETTINGS_ENTRIES[] =
|
||||
{
|
||||
{"window", TYPE_IVEC2, offsetof(Settings, windowSize)},
|
||||
{"isVsync", TYPE_BOOL, offsetof(Settings, isVsync)},
|
||||
{"playbackIsLoop", TYPE_BOOL, offsetof(Settings, playbackIsLoop)},
|
||||
{"playbackIsClampPlayhead", TYPE_BOOL, offsetof(Settings, playbackIsClampPlayhead)},
|
||||
{"changeIsCrop", TYPE_BOOL, offsetof(Settings, changeIsCrop)},
|
||||
@@ -182,6 +184,7 @@ const std::string SETTINGS_DEFAULT = R"(
|
||||
[Settings]
|
||||
windowX=1920
|
||||
windowY=1080
|
||||
isVsync=true
|
||||
playbackIsLoop=true
|
||||
playbackIsClampPlayhead=false
|
||||
changeIsCrop=false
|
||||
|
@@ -79,6 +79,8 @@ void init(State* self)
|
||||
|
||||
self->glContext = SDL_GL_CreateContext(self->window);
|
||||
|
||||
window_vsync_set(self->settings.isVsync);
|
||||
|
||||
if (!self->glContext)
|
||||
{
|
||||
log_error(std::format(STATE_GL_CONTEXT_INIT_ERROR, SDL_GetError()));
|
||||
@@ -96,8 +98,6 @@ void init(State* self)
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
resources_init(&self->resources);
|
||||
dialog_init(&self->dialog, self->window);
|
||||
clipboard_init(&self->clipboard, &self->anm2);
|
||||
@@ -135,6 +135,7 @@ void init(State* self)
|
||||
void loop(State* self)
|
||||
{
|
||||
self->tick = SDL_GetTicks();
|
||||
self->update = self->tick;
|
||||
|
||||
while (self->tick > self->lastTick + TICK_DELAY)
|
||||
{
|
||||
@@ -148,10 +149,26 @@ void loop(State* self)
|
||||
self->lastTick = self->tick;
|
||||
}
|
||||
|
||||
_update(self);
|
||||
_draw(self);
|
||||
if (self->settings.isVsync)
|
||||
{
|
||||
_update(self);
|
||||
_draw(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (self->update > self->lastUpdate + UPDATE_DELAY)
|
||||
{
|
||||
self->update = SDL_GetTicks();
|
||||
|
||||
SDL_Delay(STATE_DELAY_MIN);
|
||||
if (self->update - self->lastUpdate < UPDATE_DELAY)
|
||||
SDL_Delay(UPDATE_DELAY - (self->update - self->lastUpdate));
|
||||
|
||||
_update(self);
|
||||
_draw(self);
|
||||
|
||||
self->lastUpdate = self->update;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void quit(State* self)
|
||||
|
@@ -43,6 +43,8 @@ struct State
|
||||
std::string lastAction{};
|
||||
u64 lastTick{};
|
||||
u64 tick{};
|
||||
u64 update{};
|
||||
u64 lastUpdate{};
|
||||
bool isRunning = true;
|
||||
};
|
||||
|
||||
|
@@ -7,3 +7,11 @@ void window_title_from_path_set(SDL_Window* self, const std::string& path)
|
||||
else
|
||||
SDL_SetWindowTitle(self, WINDOW_TITLE);
|
||||
}
|
||||
|
||||
void window_vsync_set(bool isVsync)
|
||||
{
|
||||
if (isVsync)
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
else
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
}
|
@@ -8,3 +8,4 @@
|
||||
|
||||
void window_title_from_path_set(SDL_Window* self, const std::string& path);
|
||||
bool window_color_from_position_get(SDL_Window* self, vec2 position, vec4* color);
|
||||
void window_vsync_set(bool isVsync);
|
Reference in New Issue
Block a user