Anm2 merge and adjustments to render animation

This commit is contained in:
2025-11-13 01:19:25 -05:00
parent e4cb0056a0
commit bb6b68311b
5 changed files with 351 additions and 266 deletions

View File

@@ -104,7 +104,6 @@ namespace anm2ed
glDeleteFramebuffers(1, &fbo);
glDeleteRenderbuffers(1, &rbo);
glDeleteTextures(1, &texture);
glDeleteVertexArrays(1, &axisVAO);
glDeleteBuffers(1, &axisVBO);
@@ -115,10 +114,7 @@ namespace anm2ed
glDeleteBuffers(1, &rectVBO);
}
bool Canvas::is_valid() const
{
return fbo != 0;
}
bool Canvas::is_valid() const { return fbo != 0; }
void Canvas::framebuffer_set() const
{
@@ -229,6 +225,8 @@ namespace anm2ed
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glEnable(GL_BLEND);
}
void Canvas::rect_render(Shader& shader, const mat4& transform, const mat4& model, vec4 color, float dashLength,
@@ -267,25 +265,40 @@ namespace anm2ed
glUseProgram(0);
}
void Canvas::viewport_set() const
{
glViewport(0, 0, size.x, size.y);
}
void Canvas::viewport_set() const { glViewport(0, 0, size.x, size.y); }
void Canvas::clear(const vec4& color) const
{
glDisable(GL_BLEND);
glClearColor(color.r, color.g, color.b, color.a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_BLEND);
}
void Canvas::bind() const
{
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
GLboolean blendEnabled = glIsEnabled(GL_BLEND);
if (!blendEnabled) glEnable(GL_BLEND);
glGetIntegerv(GL_BLEND_SRC_RGB, &previousSrcRGB);
glGetIntegerv(GL_BLEND_DST_RGB, &previousDstRGB);
glGetIntegerv(GL_BLEND_SRC_ALPHA, &previousSrcAlpha);
glGetIntegerv(GL_BLEND_DST_ALPHA, &previousDstAlpha);
previousBlendStored = true;
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
void Canvas::unbind() const
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (previousBlendStored)
{
glBlendFuncSeparate(previousSrcRGB, previousDstRGB, previousSrcAlpha, previousDstAlpha);
previousBlendStored = false;
}
}
std::vector<unsigned char> Canvas::pixels_get() const

View File

@@ -46,6 +46,11 @@ namespace anm2ed
GLuint texture{};
glm::vec2 previousSize{};
glm::vec2 size{};
mutable GLint previousSrcRGB{};
mutable GLint previousDstRGB{};
mutable GLint previousSrcAlpha{};
mutable GLint previousDstAlpha{};
mutable bool previousBlendStored{};
Canvas();
Canvas(glm::vec2);

View File

@@ -5,7 +5,9 @@
#include <cfloat>
#include <cmath>
#include <filesystem>
#include <format>
#include <ranges>
#include <tuple>
#include <vector>
#include <imgui/imgui.h>
@@ -16,7 +18,6 @@
#include "types.h"
#include "icon.h"
#include "toast.h"
using namespace anm2ed::resource;
using namespace anm2ed::types;
@@ -26,6 +27,107 @@ using namespace glm;
namespace anm2ed::imgui
{
static constexpr auto ANM2ED_LABEL = "Anm2Ed";
static constexpr auto VERSION_LABEL = "Version 2.0";
static constexpr auto CREDIT_DELAY = 1.0f;
static constexpr auto CREDIT_SCROLL_SPEED = 25.0f;
struct Credit
{
const char* string{};
font::Type font{font::REGULAR};
};
struct ScrollingCredit
{
int index{};
float offset{};
};
struct CreditsState
{
std::vector<ScrollingCredit> active{};
float spawnTimer{1.0f};
int nextIndex{};
};
static constexpr Credit CREDITS[] = {
{"Anm2Ed", font::BOLD},
{"License: GPLv3"},
{""},
{"Designer", font::BOLD},
{"Shweet"},
{""},
{"Additional Help", font::BOLD},
{"im-tem"},
{""},
{"Based on the work of:", font::BOLD},
{"Adrian Gavrilita"},
{"Simon Parzer"},
{"Matt Kapuszczak"},
{""},
{"XM Music", font::BOLD},
{"Drozerix"},
{"\"Keygen Wraith\""},
{"https://modarchive.org/module.php?207854"},
{"License: CC0"},
{""},
{"Libraries", font::BOLD},
{"Dear ImGui"},
{"https://github.com/ocornut/imgui"},
{"License: MIT"},
{""},
{"SDL"},
{"https://github.com/libsdl-org/SDL"},
{"License: zlib"},
{""},
{"SDL_mixer"},
{"https://github.com/libsdl-org/SDL_mixer"},
{"License: zlib"},
{""},
{"tinyxml2"},
{"https://github.com/leethomason/tinyxml2"},
{"License: zlib"},
{""},
{"glm"},
{"https://github.com/g-truc/glm"},
{"License: MIT"},
{""},
{"lunasvg"},
{"https://github.com/sammycage/lunasvg"},
{"License: MIT"},
{""},
{"Icons", font::BOLD},
{"Remix Icons"},
{"remixicon.com"},
{"License: Apache"},
{""},
{"Font", font::BOLD},
{"Noto Sans"},
{"https://fonts.google.com/noto/specimen/Noto+Sans"},
{"License: OFL"},
{""},
{"Special Thanks", font::BOLD},
{"Edmund McMillen"},
{"Florian Himsl"},
{"Tyrone Rodriguez"},
{"The-Vinh Truong (_kilburn)"},
{"Everyone who waited patiently for this to be finished"},
{"Everyone else who has worked on The Binding of Isaac!"},
{""},
{""},
{""},
{""},
{""},
{"enjoy the jams :)"},
{""},
{""},
{""},
{""},
{""},
};
static constexpr auto CREDIT_COUNT = (int)(sizeof(CREDITS) / sizeof(Credit));
Taskbar::Taskbar() : generate(vec2()) {}
void Taskbar::update(Manager& manager, Settings& settings, Resources& resources, Dialog& dialog, bool& isQuitting)
@@ -43,9 +145,10 @@ namespace anm2ed::imgui
if (ImGui::MenuItem("New", settings.shortcutNew.c_str())) dialog.file_save(dialog::ANM2_NEW);
if (ImGui::MenuItem("Open", settings.shortcutOpen.c_str())) dialog.file_open(dialog::ANM2_OPEN);
if (ImGui::BeginMenu("Open Recent", !manager.recentFiles.empty()))
auto recentFiles = manager.recent_files_ordered();
if (ImGui::BeginMenu("Open Recent", !recentFiles.empty()))
{
for (auto [i, file] : std::views::enumerate(manager.recentFiles))
for (auto [i, file] : std::views::enumerate(recentFiles))
{
auto label = std::format(FILE_LABEL_FORMAT, file.filename().string(), file.string());
@@ -54,7 +157,7 @@ namespace anm2ed::imgui
ImGui::PopID();
}
if (!manager.recentFiles.empty())
if (!recentFiles.empty())
if (ImGui::MenuItem("Clear List")) manager.recent_files_clear();
ImGui::EndMenu();
@@ -407,7 +510,6 @@ namespace anm2ed::imgui
if (ImGui::BeginPopupModal(renderPopup.label, &renderPopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto& playback = document->playback;
auto& ffmpegPath = settings.renderFFmpegPath;
auto& path = settings.renderPath;
auto& format = settings.renderFormat;
@@ -416,27 +518,88 @@ namespace anm2ed::imgui
auto& type = settings.renderType;
auto& start = manager.recordingStart;
auto& end = manager.recordingEnd;
auto& rows = settings.renderRows;
auto& columns = settings.renderColumns;
auto& isRange = manager.isRecordingRange;
auto widgetSize = widget_size_with_row_get(2);
auto dialogType = type == render::PNGS ? dialog::PNG_DIRECTORY_SET
: type == render::GIF ? dialog::GIF_PATH_SET
: type == render::WEBM ? dialog::WEBM_PATH_SET
: dialog::NONE;
auto& frames = document->frames.selection;
int length = std::max(1, end - start + 1);
auto range_set = [&]()
{
if (!frames.empty())
{
if (auto item = document->item_get())
{
int duration{};
for (auto [i, frame] : std::views::enumerate(item->frames))
{
if (i == *frames.begin())
start = duration;
else if (i == *frames.rbegin())
{
end = duration;
break;
}
duration += frame.duration;
}
}
}
else if (!isRange)
{
start = 0;
end = animation->frameNum - 1;
}
length = std::max(1, end - start + 1);
};
auto rows_columns_set = [&]()
{
auto framesNeeded = std::max(1, length);
int bestRows = 1;
int bestColumns = framesNeeded;
auto bestScore = std::make_tuple(bestColumns - bestRows, bestColumns * bestRows - framesNeeded, -bestColumns);
for (int candidateRows = 1; candidateRows <= framesNeeded; ++candidateRows)
{
int candidateColumns = (framesNeeded + candidateRows - 1) / candidateRows;
if (candidateColumns < candidateRows) break;
auto candidateScore = std::make_tuple(candidateColumns - candidateRows,
candidateColumns * candidateRows - framesNeeded, -candidateColumns);
if (candidateScore < bestScore)
{
bestScore = candidateScore;
bestRows = candidateRows;
bestColumns = candidateColumns;
}
}
rows = bestRows;
columns = bestColumns;
};
auto replace_extension = [&]()
{ path = std::filesystem::path(path).replace_extension(render::EXTENSIONS[type]).string(); };
auto range_to_length = [&]()
{
start = 0;
end = animation->frameNum;
};
if (renderPopup.isJustOpened)
auto render_set = [&]()
{
replace_extension();
if (!isRange) range_to_length();
}
range_set();
rows_columns_set();
};
auto widgetSize = widget_size_with_row_get(2);
auto dialogType = type == render::PNGS ? dialog::PNG_DIRECTORY_SET
: type == render::SPRITESHEET ? dialog::PNG_PATH_SET
: type == render::GIF ? dialog::GIF_PATH_SET
: type == render::WEBM ? dialog::WEBM_PATH_SET
: dialog::NONE;
if (renderPopup.isJustOpened) render_set();
if (ImGui::ImageButton("##FFmpeg Path Set", resources.icons[icon::FOLDER].id, icon_size_get()))
dialog.file_open(dialog::FFMPEG_PATH_SET);
@@ -458,41 +621,64 @@ namespace anm2ed::imgui
ImGui::SetItemTooltip("Set the output path or directory for the animation.");
dialog.set_string_to_selected_path(path, dialogType);
if (ImGui::Combo("Type", &type, render::STRINGS, render::COUNT)) replace_extension();
if (ImGui::Combo("Type", &type, render::STRINGS, render::COUNT)) render_set();
ImGui::SetItemTooltip("Set the type of the output.");
if (type == render::PNGS || type == render::SPRITESHEET) ImGui::Separator();
if (type == render::PNGS)
{
ImGui::Separator();
input_text_string("Format", &format);
if (input_text_string("Format", &format)) format = std::filesystem::path(format).replace_extension(".png");
ImGui::SetItemTooltip(
"For outputted images, each image will use this format.\n{} represents the index of each image.");
}
else if (type == render::SPRITESHEET)
{
input_int_range("Rows", rows, 1, length);
ImGui::SetItemTooltip("Set how many rows the spritesheet will have.");
input_int_range("Columns", columns, 1, length);
ImGui::SetItemTooltip("Set how many columns the spritesheet will have.");
if (ImGui::Button("Set to Recommended")) rows_columns_set();
ImGui::SetItemTooltip("Use a recommended value for rows/columns.");
}
ImGui::Separator();
if (ImGui::Checkbox("Custom Range", &isRange))
if (!isRange) range_to_length();
ImGui::SetItemTooltip("Toggle using a custom range for the animation.");
{
range_set();
ImGui::SetItemTooltip("Toggle using a custom range for the animation.");
}
if (isRange)
ImGui::SameLine();
ImGui::BeginDisabled(frames.empty());
if (ImGui::Button("To Selected Frames")) range_set();
ImGui::SetItemTooltip("If frames are selected, use that range for the rendered animation.");
ImGui::EndDisabled();
ImGui::BeginDisabled(!isRange);
{
input_int_range("Start", start, 0, animation->frameNum - 1);
ImGui::SetItemTooltip("Set the starting time of the animation.");
ImGui::SetItemTooltip("Set the starting time of the animation.");
input_int_range("End", end, start + 1, animation->frameNum);
ImGui::SetItemTooltip("Set the ending time of the animation.");
ImGui::SetItemTooltip("Set the ending time of the animation.");
}
ImGui::EndDisabled();
ImGui::Separator();
ImGui::Checkbox("Raw", &isRaw);
ImGui::SetItemTooltip("Record only the raw animation; i.e., only its layers, to its bounds.");
if (isRaw)
ImGui::BeginDisabled(!isRaw);
{
input_float_range("Scale", scale, 1.0f, 100.0f, STEP, STEP_FAST, "%.1fx");
ImGui::SetItemTooltip("Set the output scale of the animation.");
}
ImGui::EndDisabled();
ImGui::Separator();
@@ -504,78 +690,11 @@ namespace anm2ed::imgui
if (ImGui::Button("Render", widgetSize))
{
bool canStart = true;
auto warn_and_close = [&](const std::string& message)
{
toasts.warning(message);
renderPopup.close();
canStart = false;
};
auto ffmpegPathValid = [&]() -> bool
{
if (ffmpegPath.empty()) return false;
std::error_code ec{};
std::filesystem::path ffmpeg(ffmpegPath);
if (!std::filesystem::exists(ffmpeg, ec) || !std::filesystem::is_regular_file(ffmpeg, ec)) return false;
#ifdef _WIN32
auto ext = ffmpeg.extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return (char)std::tolower(c); });
if (ext != ".exe") return false;
return true;
#else
auto permMask = std::filesystem::status(ffmpeg, ec).permissions();
using std::filesystem::perms;
if (permMask == perms::unknown) return true;
auto has_exec = [&](perms p)
{
return (perms::none != (p & perms::owner_exec)) || (perms::none != (p & perms::group_exec)) ||
(perms::none != (p & perms::others_exec));
};
return has_exec(permMask);
#endif
};
if (!ffmpegPathValid()) warn_and_close("Invalid FFmpeg executable. Please set a valid FFmpeg path.");
if (canStart)
{
std::error_code ec{};
if (type == render::PNGS)
{
if (path.empty())
warn_and_close("Select an output directory for PNG exports.");
else
{
std::filesystem::path directory(path);
if (!std::filesystem::exists(directory, ec) || !std::filesystem::is_directory(directory, ec))
warn_and_close("PNG exports require a valid directory.");
}
}
else
{
std::filesystem::path output(path);
auto parent = output.parent_path();
auto parentInvalid =
!parent.empty() && (!std::filesystem::exists(parent, ec) || !std::filesystem::is_directory(parent, ec));
if (path.empty() || std::filesystem::is_directory(output, ec) || parentInvalid)
{
output = std::filesystem::path("output").replace_extension(render::EXTENSIONS[type]);
path = output.string();
warn_and_close(std::format("Invalid output file. Using default path: {}", path));
}
}
}
if (canStart)
{
manager.isRecordingStart = true;
playback.time = start;
playback.isPlaying = true;
renderPopup.close();
manager.progressPopup.open();
}
manager.isRecordingStart = true;
renderPopup.close();
manager.progressPopup.open();
}
ImGui::SetItemTooltip("Render the animation using the current settings.");
ImGui::SameLine();
@@ -590,107 +709,6 @@ namespace anm2ed::imgui
if (ImGui::BeginPopupModal(aboutPopup.label, &aboutPopup.isOpen, ImGuiWindowFlags_NoResize))
{
struct Credit
{
const char* string{};
font::Type font{font::REGULAR};
};
struct ScrollingCredit
{
int index{};
float offset{};
};
struct CreditsState
{
std::vector<ScrollingCredit> active{};
float spawnTimer{1.0f};
int nextIndex{};
};
static constexpr auto ANM2ED_LABEL = "Anm2Ed";
static constexpr auto VERSION_LABEL = "Version 2.0";
static constexpr auto CREDIT_DELAY = 1.0f;
static constexpr auto CREDIT_SCROLL_SPEED = 25.0f;
static constexpr Credit CREDITS[] = {
{"Anm2Ed", font::BOLD},
{"License: GPLv3"},
{""},
{"Designer", font::BOLD},
{"Shweet"},
{""},
{"Additional Help", font::BOLD},
{"im-tem"},
{""},
{"Based on the work of:", font::BOLD},
{"Adrian Gavrilita"},
{"Simon Parzer"},
{"Matt Kapuszczak"},
{""},
{"XM Music", font::BOLD},
{"Drozerix"},
{"\"Keygen Wraith\""},
{"https://modarchive.org/module.php?207854"},
{"License: CC0"},
{""},
{"Libraries", font::BOLD},
{"Dear ImGui"},
{"https://github.com/ocornut/imgui"},
{"License: MIT"},
{""},
{"SDL"},
{"https://github.com/libsdl-org/SDL"},
{"License: zlib"},
{""},
{"SDL_mixer"},
{"https://github.com/libsdl-org/SDL_mixer"},
{"License: zlib"},
{""},
{"tinyxml2"},
{"https://github.com/leethomason/tinyxml2"},
{"License: zlib"},
{""},
{"glm"},
{"https://github.com/g-truc/glm"},
{"License: MIT"},
{""},
{"lunasvg"},
{"https://github.com/sammycage/lunasvg"},
{"License: MIT"},
{""},
{"Icons", font::BOLD},
{"Remix Icons"},
{"remixicon.com"},
{"License: Apache"},
{""},
{"Font", font::BOLD},
{"Noto Sans"},
{"https://fonts.google.com/noto/specimen/Noto+Sans"},
{"License: OFL"},
{""},
{"Special Thanks", font::BOLD},
{"Edmund McMillen"},
{"Florian Himsl"},
{"Tyrone Rodriguez"},
{"The-Vinh Truong (_kilburn)"},
{"Everyone who waited patiently for this to be finished"},
{"Everyone else who has worked on The Binding of Isaac!"},
{""},
{""},
{""},
{""},
{""},
{"enjoy the jams :)"},
{""},
{""},
{""},
{""},
{""},
};
static constexpr auto CREDIT_COUNT = (int)(sizeof(CREDITS) / sizeof(Credit));
static CreditsState creditsState{};
auto credits_reset = [&]()

View File

@@ -1,5 +1,7 @@
#include "animation_preview.h"
#include <algorithm>
#include <filesystem>
#include <ranges>
#include <glm/gtc/type_ptr.hpp>
@@ -14,6 +16,7 @@ using namespace anm2ed::canvas;
using namespace anm2ed::types;
using namespace anm2ed::util;
using namespace anm2ed::resource;
using namespace anm2ed::resource::texture;
using namespace glm;
namespace anm2ed::imgui
@@ -26,47 +29,27 @@ namespace anm2ed::imgui
AnimationPreview::AnimationPreview() : Canvas(vec2()) {}
void AnimationPreview::tick(Manager& manager, Document& document, Settings& settings)
void AnimationPreview::tick(Manager& manager, Settings& settings)
{
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& playback = document.playback;
auto& frameTime = document.frameTime;
auto& end = manager.recordingEnd;
auto& zoom = document.previewZoom;
auto& pan = document.previewPan;
auto& isRootTransform = settings.previewIsRootTransform;
auto& scale = settings.renderScale;
if (playback.isPlaying)
{
auto& isSound = settings.timelineIsSound;
auto& isOnlyShowLayers = settings.timelineIsOnlyShowLayers;
if (!anm2.content.sounds.empty() && isSound)
{
if (auto animation = document.animation_get();
animation && animation->triggers.isVisible && (!isOnlyShowLayers || manager.isRecording))
{
if (auto trigger = animation->triggers.frame_generate(playback.time, anm2::TRIGGER);
trigger.is_visible(anm2::TRIGGER))
if (anm2.content.sounds.contains(trigger.soundID))
anm2.content.sounds[trigger.soundID].audio.play(false, mixer);
}
}
frameTime = playback.time;
}
if (manager.isRecording)
{
auto& ffmpegPath = settings.renderFFmpegPath;
auto& path = settings.renderPath;
auto& type = settings.renderType;
auto pixels = pixels_get();
renderFrames.push_back(Texture(pixels.data(), size));
if (playback.time > manager.recordingEnd || playback.isFinished)
if (playback.time > end || playback.isFinished)
{
auto& ffmpegPath = settings.renderFFmpegPath;
auto& path = settings.renderPath;
auto& type = settings.renderType;
if (type == render::PNGS)
{
auto& format = settings.renderFormat;
@@ -89,6 +72,53 @@ namespace anm2ed::imgui
else
toasts.warning(std::format("Could not export frames to: {}", path));
}
else if (type == render::SPRITESHEET)
{
auto& rows = settings.renderRows;
auto& columns = settings.renderColumns;
if (renderFrames.empty())
{
toasts.warning("No frames captured for spritesheet export.");
}
else
{
const auto& firstFrame = renderFrames.front();
if (firstFrame.size.x <= 0 || firstFrame.size.y <= 0 || firstFrame.pixels.empty())
toasts.warning("Spritesheet export failed: captured frames are empty.");
else
{
auto frameWidth = firstFrame.size.x;
auto frameHeight = firstFrame.size.y;
ivec2 spritesheetSize = ivec2(frameWidth * columns, frameHeight * rows);
std::vector<uint8_t> spritesheet((size_t)(spritesheetSize.x) * spritesheetSize.y * CHANNELS);
for (auto [i, frame] : std::views::enumerate(renderFrames))
{
auto row = (int)(i / columns);
auto column = (int)(i % columns);
if (row >= rows || column >= columns) break;
if ((int)frame.pixels.size() < frameWidth * frameHeight * CHANNELS) continue;
for (int y = 0; y < frameHeight; ++y)
{
auto destY = (size_t)(row * frameHeight + y);
auto destX = (size_t)(column * frameWidth);
auto destOffset = (destY * spritesheetSize.x + destX) * CHANNELS;
auto srcOffset = (size_t)(y * frameWidth) * CHANNELS;
std::copy_n(frame.pixels.data() + srcOffset, frameWidth * CHANNELS, spritesheet.data() + destOffset);
}
}
Texture spritesheetTexture(spritesheet.data(), spritesheetSize);
if (spritesheetTexture.write_png(path))
toasts.info(std::format("Exported spritesheet to: {}", path));
else
toasts.warning(std::format("Could not export spritesheet to: {}", path));
}
}
}
else
{
if (animation_render(ffmpegPath, path, renderFrames, audioStream, (render::Type)type, size, anm2.info.fps))
@@ -112,41 +142,29 @@ namespace anm2ed::imgui
manager.progressPopup.close();
}
}
if (manager.isRecordingStart)
if (playback.isPlaying)
{
savedSettings = settings;
auto animation = document.animation_get();
auto& isSound = settings.timelineIsSound;
auto& isOnlyShowLayers = settings.timelineIsOnlyShowLayers;
if (settings.timelineIsSound) audioStream.capture_begin(mixer);
if (settings.renderIsRawAnimation)
if (!anm2.content.sounds.empty() && isSound)
{
settings.previewBackgroundColor = vec4();
settings.previewIsGrid = false;
settings.previewIsAxes = false;
settings.timelineIsOnlyShowLayers = true;
settings.onionskinIsEnabled = false;
savedZoom = zoom;
savedPan = pan;
if (auto animation = document.animation_get())
if (auto animation = document.animation_get();
animation && animation->triggers.isVisible && (!isOnlyShowLayers || manager.isRecording))
{
if (auto rect = animation->rect(isRootTransform); rect != vec4(-1.0f))
{
size_set(vec2(rect.z, rect.w) * scale);
set_to_rect(zoom, pan, rect);
}
if (auto trigger = animation->triggers.frame_generate(playback.time, anm2::TRIGGER);
trigger.is_visible(anm2::TRIGGER))
if (anm2.content.sounds.contains(trigger.soundID))
anm2.content.sounds[trigger.soundID].audio.play(false, mixer);
}
isSizeTrySet = false;
bind();
clear(settings.previewBackgroundColor);
unbind();
}
manager.isRecordingStart = false;
manager.isRecording = true;
playback.tick(anm2.info.fps, animation->frameNum,
(animation->isLoop || settings.playbackIsLoop) && !manager.isRecording);
frameTime = playback.time;
}
}
@@ -278,6 +296,38 @@ namespace anm2ed::imgui
auto cursorScreenPos = ImGui::GetCursorScreenPos();
if (manager.isRecordingStart)
{
savedSettings = settings;
if (settings.timelineIsSound) audioStream.capture_begin(mixer);
if (settings.renderIsRawAnimation)
{
settings.previewBackgroundColor = vec4();
settings.previewIsGrid = false;
settings.previewIsAxes = false;
settings.timelineIsOnlyShowLayers = true;
settings.onionskinIsEnabled = false;
savedZoom = zoom;
savedPan = pan;
if (auto rect = document.animation_get()->rect(isRootTransform); rect != vec4(-1.0f))
{
size_set(vec2(rect.z, rect.w) * settings.renderScale);
set_to_rect(zoom, pan, rect);
}
isSizeTrySet = false;
}
manager.isRecordingStart = false;
manager.isRecording = true;
playback.isPlaying = true;
playback.time = manager.recordingStart;
}
if (isSizeTrySet) size_set(to_vec2(ImGui::GetContentRegionAvail()));
viewport_set();
bind();

View File

@@ -151,7 +151,6 @@ namespace anm2ed
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glLineWidth(2.0f);
glDisable(GL_MULTISAMPLE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LINE_SMOOTH);
glClearColor(color::BLACK.r, color::BLACK.g, color::BLACK.b, color::BLACK.a);