Undo + Recording

This commit is contained in:
2025-06-29 20:33:52 -04:00
parent 93ab62e494
commit 4b12153512
27 changed files with 2485 additions and 367 deletions

View File

@@ -18,7 +18,7 @@ add_executable(${PROJECT_NAME} ${SOURCES})
target_include_directories(${PROJECT_NAME} PRIVATE include include/imgui include/tinyxml2 include/inih src)
set(CMAKE_CXX_FLAGS "-g -O2 -std=c++23 -Wall -Wextra -pedantic -Wno-unused-variable -Wno-unused-parameter -Wno-ignored-qualifiers -Wno-parentheses -Wno-unused-function -Wno-class-memaccess -Wno-delete-incomplete")
set(CMAKE_CXX_FLAGS "-g -O2 -std=c++23 -Wall -Wextra -pedantic -Wno-unused-variable -Wno-unused-parameter -Wno-ignored-qualifiers -Wno-parentheses -Wno-missing-field-initializers -Wno-unused-function -Wno-class-memaccess -Wno-delete-incomplete")
target_link_libraries(${PROJECT_NAME} PRIVATE m GL GLEW SDL3)

1724
include/stb_image_write.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -80,6 +80,7 @@ static const f32 GL_UV_VERTICES[] =
};
#define IMVEC2_VEC2(value) ImVec2(value.x, value.y)
#define VEC2_IMVEC2(value) glm::vec2(value.x, value.y)
#define IMVEC4_VEC4(value) ImVec4(value.r, value.g, value.b, value.a)
static const GLuint GL_TEXTURE_INDICES[] = {0, 1, 2, 2, 3, 0};
@@ -90,19 +91,10 @@ static const vec4 COLOR_OPAQUE = {1.0f, 1.0f, 1.0f, 1.0f};
static const vec4 COLOR_TRANSPARENT = {0.0f, 0.0f, 0.0f, 1.0f};
static const vec3 COLOR_OFFSET_NONE = {0.0f, 0.0f, 0.0f};
static inline const char* enum_to_string(const char* arr[], s32 count, s32 index) { return (index >= 0 && index < count) ? arr[index] : "undefined"; }
static inline s32 string_to_enum(const char* str, const char* const* arr, s32 n) { for (s32 i=0; i<n; ++i) if (!strcmp(str, arr[i])) return i; return -1; }
static inline bool string_to_bool(const char* str) { if (strcmp(str, "true") == 0) return true; return false; }
static inline void working_directory_from_path_set(const char* path)
{
std::filesystem::path filePath = path;
std::filesystem::path dir = filePath.parent_path();
if (!dir.empty())
std::filesystem::current_path(dir);
};
template<typename T>
static inline s32 map_next_id_get(const std::map<s32, T>& map) {
s32 id = 0; for (const auto& [key, _] : map) if (key != id) break; else ++id; return id;

View File

@@ -6,6 +6,8 @@
#define STRING_WINDOW_TITLE "Anm2Ed"
#define STRING_WINDOW_TITLE_EDITING "Anm2Ed (%s)"
#define STRING_ANM2_CREATED_ON_FORMAT "%d-%B-%Y %I:%M:%S %p"
#define STRING_ERROR_SDL_INIT "[ERROR] Could not initialize SDL (%s)\n"
#define STRING_ERROR_GL_CONTEXT_INIT "[ERROR] Could not initialize OpenGL context (%s)\n"
#define STRING_ERROR_FILE_READ "[ERROR] Could not read from file: %s\n"
@@ -45,10 +47,16 @@
#define STRING_ANM2_NEW_EVENT "New Event"
#define STRING_ANM2_ROOT "Root"
#define STRING_PREVIEW_FRAMES_DIRECTORY "frames/"
#define STRING_PREVIEW_FRAMES_FORMAT "%s/%03d.png"
#define STRING_IMGUI_WINDOW "Window"
#define STRING_IMGUI_DOCKSPACE "Dockspace"
#define STRING_IMGUI_TASKBAR "Taskbar"
#define STRING_IMGUI_TASKBAR_PLAYBACK "Playback"
#define STRING_IMGUI_TASKBAR_FILE "File"
#define STRING_IMGUI_TASKBAR_WIZARD "Wizard"
#define STRING_IMGUI_FILE_MENU "File Menu"
#define STRING_IMGUI_FILE_NEW "New"
@@ -56,6 +64,14 @@
#define STRING_IMGUI_FILE_SAVE "Save"
#define STRING_IMGUI_FILE_SAVE_AS "Save As"
#define STRING_IMGUI_PLAYBACK_MENU "Playback Menu"
#define STRING_IMGUI_PLAYBACK_LOOP "Loop"
#define STRING_IMGUI_WIZARD_MENU "Wizard Menu"
#define STRING_IMGUI_WIZARD_EXPORT_FRAMES_TO_PNG "Export Frames to PNG"
#define STRING_IMGUI_RECORDING "Recording..."
#define STRING_IMGUI_ANIMATIONS "Animations"
#define STRING_IMGUI_ANIMATIONS_ANIMATION_LABEL "##Animation"
#define STRING_IMGUI_ANIMATIONS_ADD "Add"
@@ -162,7 +178,7 @@
#define STRING_IMGUI_TIMELINE_FRAME_LABEL "##Frame"
#define STRING_IMGUI_TIMELINE_TRIGGER_LABEL "##Trigger"
#define STRING_IMGUI_TIMELINE_FRAME_REMOVE "Remove Frame"
#define STRING_IMGUI_TIMELINE_FIT_ANIMATION_LENGTH "= Fit Animation Length"
#define STRING_IMGUI_TIMELINE_FIT_ANIMATION_LENGTH "Fit Animation Length"
#define STRING_IMGUI_TIMELINE_ANIMATION LENGTH "Animation Length:"
#define STRING_IMGUI_TIMELINE_VISIBLE "##Visible"
#define STRING_IMGUI_TIMELINE_LAYER "Layer"
@@ -251,6 +267,7 @@
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_TRIGGERS "This is the animation's Triggers.\nTriggers are special activations; each is bound to an Event.\nClick to select."
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_VISIBLE "Toggle visibility for this element."
#define STRING_IMGUI_TOOLTIP_TIMELINE_FPS "Change the FPS of the animation."
#define STRING_IMGUI_TOOLTIP_TIMELINE_FIT_ANIMATION_LENGTH "Sets the animation length to the latest frame."
#define STRING_IMGUI_TOOLTIP_TIMELINE_FRAME_ADD "Add a frame to the current selected element."
#define STRING_IMGUI_TOOLTIP_TIMELINE_FRAME_REMOVE "Removes the selected frame from the current selected element."
#define STRING_IMGUI_TOOLTIP_TIMELINE_LOOP "Toggles the animation looping."
@@ -272,6 +289,15 @@
#define STRING_IMGUI_TOOLTIP_TOOLS_ROTATE "Use the rotate tool.\nWill rotate selected elements as the cursor is dragged.\n(Animation Preview only.)"
#define STRING_IMGUI_TOOLTIP_TOOLS_SCALE "Use the scale tool.\nWill scale the selected elements as the cursor is dragged.\n(Animation Preview only.)"
#define STRING_IMGUI_TOOLTIP_TOOLS_CROP "Use the crop tool.\nWill set the crop of the selected elements as the cursor is dragged.\n(Spritesheet Editor only.)"
#define STRING_IMGUI_TOOLTIP_FILE_NEW "Load a blank .anm2 file to edit."
#define STRING_IMGUI_TOOLTIP_FILE_OPEN "Open an existing .anm2 file to edit."
#define STRING_IMGUI_TOOLTIP_FILE_SAVE "Save the current .anm2 file."
#define STRING_IMGUI_TOOLTIP_FILE_SAVE_AS "Save the current .anm2 file to a location."
#define STRING_IMGUI_TOOLTIP_PLAYBACK_IS_LOOP "Toggles the animation playback looping when the time reaches the end."
#define STRING_IMGUI_TOOLTIP_WIZARD_EXPORT_FRAMES_TO_PNG "Records the current animation and exports the frames as .pngs.\nLook in the working directory of the program for them."
#define STRING_IMGUI_TOOLTIP_FILE_MENU "Opens the file menu, for reading/writing anm2 files."
#define STRING_IMGUI_TOOLTIP_PLAYBACK_MENU "Opens the playback menu, for managing playback settings."
#define STRING_IMGUI_TOOLTIP_WIZARD_MENU "Opens the wizard menu, for handy functions involving the anm2."
#define STRING_OPENGL_VERSION "#version 330"

View File

@@ -13,7 +13,7 @@ anm2_created_on_set(Anm2* self)
currentTime = time(NULL);
local = localtime(&currentTime);
strftime(date, ANM2_STRING_MAX, "%d-%B-%Y %I:%M:%S %p", local);
strftime(date, ANM2_STRING_MAX, STRING_ANM2_CREATED_ON_FORMAT, local);
strncpy(self->createdOn, date, ANM2_STRING_MAX);
}
@@ -306,8 +306,6 @@ anm2_serialize(Anm2* self, const char* path)
return false;
}
working_directory_from_path_set(path);
printf(STRING_INFO_ANM2_WRITE, path);
strncpy(self->path, path, PATH_MAX - 1);
@@ -331,6 +329,11 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
Anm2Spritesheet* spritesheet = NULL;
Anm2Element anm2Element = ANM2_ELEMENT_ANIMATED_ACTOR;
Anm2Attribute anm2Attribute = ANM2_ATTRIBUTE_ID;
Anm2Item addItem;
Anm2Layer addLayer;
Anm2Null addNull;
Anm2Event addEvent;
Anm2Spritesheet addSpritesheet;
*self = Anm2{};
@@ -343,15 +346,20 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
}
resources_textures_free(resources);
/* Save old working directory and then use anm2's path as directory */
/* (useful for loading textures from anm2 correctly) */
std::filesystem::path workingPath = std::filesystem::current_path();
std::filesystem::path filePath = path;
std::filesystem::path parentPath = filePath.parent_path();
std::filesystem::current_path(parentPath);
strncpy(self->path, path, PATH_MAX - 1);
working_directory_from_path_set(path);
xmlRoot = xmlDocument.FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]);
xmlElement = xmlRoot;
while (xmlElement)
{
const XMLAttribute* xmlAttribute = NULL;
const XMLElement* xmlChild = NULL;
s32 id = 0;
@@ -362,24 +370,16 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
switch (anm2Element)
{
case ANM2_ELEMENT_SPRITESHEET:
id = map_next_id_get(self->spritesheets);
self->spritesheets[id] = Anm2Spritesheet{};
spritesheet = &self->spritesheets[id];
spritesheet = &addSpritesheet;
break;
case ANM2_ELEMENT_LAYER:
id = map_next_id_get(self->layers);
self->layers[id] = Anm2Layer{};
layer = &self->layers[id];
layer = &addLayer;
break;
case ANM2_ELEMENT_NULL:
id = map_next_id_get(self->nulls);
self->nulls[id] = Anm2Null{};
null = &self->nulls[id];
null = &addNull;
break;
case ANM2_ELEMENT_EVENT:
id = map_next_id_get(self->events);
self->events[id] = Anm2Event{};
event = &self->events[id];
event = &addEvent;
break;
case ANM2_ELEMENT_ANIMATION:
id = map_next_id_get(self->animations);
@@ -390,14 +390,8 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
item = &animation->rootAnimation;
break;
case ANM2_ELEMENT_LAYER_ANIMATION:
id = map_next_id_get(animation->layerAnimations);
animation->layerAnimations[id] = Anm2Item{};
item = &animation->layerAnimations[id];
break;
case ANM2_ELEMENT_NULL_ANIMATION:
id = map_next_id_get(animation->nullAnimations);
animation->nullAnimations[id] = Anm2Item{};
item = &animation->nullAnimations[id];
item = &addItem;
break;
case ANM2_ELEMENT_TRIGGERS:
item = &animation->triggers;
@@ -431,6 +425,40 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
case ANM2_ATTRIBUTE_FPS:
self->fps = atoi(xmlAttribute->Value());
break;
case ANM2_ATTRIBUTE_ID:
id = atoi(xmlAttribute->Value());
switch (anm2Element)
{
case ANM2_ELEMENT_SPRITESHEET:
self->spritesheets[id] = addSpritesheet;
spritesheet = &self->spritesheets[id];
break;
case ANM2_ELEMENT_LAYER:
self->layers[id] = addLayer;
layer = &self->layers[id];
break;
case ANM2_ELEMENT_NULL:
self->nulls[id] = addNull;
null = &self->nulls[id];
break;
case ANM2_ELEMENT_EVENT:
self->events[id] = addEvent;
event = &self->events[id];
break;
default:
break;
}
break;
case ANM2_ATTRIBUTE_LAYER_ID:
id = atoi(xmlAttribute->Value());
animation->layerAnimations[id] = addItem;
item = &animation->layerAnimations[id];
break;
case ANM2_ATTRIBUTE_NULL_ID:
id = atoi(xmlAttribute->Value());
animation->nullAnimations[id] = addItem;
item = &animation->nullAnimations[id];
break;
case ANM2_ATTRIBUTE_PATH:
strncpy(spritesheet->path, xmlAttribute->Value(), PATH_MAX - 1);
break;
@@ -589,6 +617,9 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
printf(STRING_INFO_ANM2_READ, path);
/* Set working directory back to old */
std::filesystem::current_path(workingPath);
return true;
}
@@ -694,9 +725,9 @@ anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const char* path
}
Anm2Animation*
anm2_animation_from_id(Anm2* self, s32 animationID)
anm2_animation_from_reference(Anm2* self, Anm2Reference* reference)
{
auto it = self->animations.find(animationID);
auto it = self->animations.find(reference->animationID);
if (it == self->animations.end())
return NULL;
return &it->second;
@@ -704,27 +735,27 @@ anm2_animation_from_id(Anm2* self, s32 animationID)
/* Returns the item from a anm2 reference. */
Anm2Item*
anm2_item_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID)
anm2_item_from_reference(Anm2* self, Anm2Reference* reference)
{
Anm2Animation* animation = anm2_animation_from_id(self, animationID);
Anm2Animation* animation = anm2_animation_from_reference(self, reference);
if (!animation)
return NULL;
switch (reference->type)
switch (reference->itemType)
{
case ANM2_ROOT:
return &animation->rootAnimation;
case ANM2_LAYER:
{
auto it = animation->layerAnimations.find(reference->id);
auto it = animation->layerAnimations.find(reference->itemID);
if (it == animation->layerAnimations.end())
return NULL;
return &it->second;
}
case ANM2_NULL:
{
auto it = animation->nullAnimations.find(reference->id);
auto it = animation->nullAnimations.find(reference->itemID);
if (it == animation->nullAnimations.end())
return NULL;
return &it->second;
@@ -738,31 +769,31 @@ anm2_item_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID)
/* Gets the frame from the reference's properties */
Anm2Frame*
anm2_frame_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID)
anm2_frame_from_reference(Anm2* self, Anm2Reference* reference)
{
Anm2Item* item = anm2_item_from_reference(self, reference, animationID);
Anm2Item* item = anm2_item_from_reference(self, reference);
if (!item)
return NULL;
if (reference->index < 0 || reference->index >= (s32)item->frames.size())
if (reference->frameIndex < 0 || reference->frameIndex >= (s32)item->frames.size())
return NULL;
return &item->frames[reference->index];
return &item->frames[reference->frameIndex];
}
/* Creates/fetches a frame from a given time. */
/* Returns true/false if frame will be valid or not. */
void
anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, s32 animationID, f32 time)
anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time)
{
Anm2Animation* animation = anm2_animation_from_id(self, animationID);
Anm2Animation* animation = anm2_animation_from_reference(self, &reference);
/* Out of range */
if (time < 0 || time > animation->frameNum)
return;
Anm2Item* item = anm2_item_from_reference(self, &reference, animationID);
Anm2Item* item = anm2_item_from_reference(self, &reference);
if (!item)
return;
@@ -803,12 +834,45 @@ anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, s32
}
}
/* Returns the current length of the animation, from the used frames. */
s32
anm2_animation_length_get(Anm2* self, s32 animationID)
{
/* Get valid animation */
auto it = self->animations.find(animationID);
if (it == self->animations.end())
return -1;
Anm2Animation* animation = &it->second;
s32 delayHighest = 0;
/* Root frames */
for (auto & frame : animation->rootAnimation.frames)
delayHighest = std::max(delayHighest, frame.delay);
/* Layer frames */
for (auto & [id, item] : animation->layerAnimations)
for (auto & frame : item.frames)
delayHighest = std::max(delayHighest, frame.delay);
/* Null frames */
for (auto & [id, item] : animation->nullAnimations)
for (auto & frame : item.frames)
delayHighest = std::max(delayHighest, frame.delay);
/* Trigger frames (assuming this is from `animation->triggers.frames`) */
for (auto & trigger : animation->triggers.frames)
delayHighest = std::max(delayHighest, trigger.atFrame);
return delayHighest;
}
/* Will try adding a frame to the anm2 given the specified reference */
Anm2Frame*
anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time)
{
Anm2Animation* animation = anm2_animation_from_id(self, animationID);
Anm2Item* item = anm2_item_from_reference(self, reference, animationID);
Anm2Animation* animation = anm2_animation_from_reference(self, reference);
Anm2Item* item = anm2_item_from_reference(self, reference);
if (!animation || !item)
return NULL;
@@ -818,7 +882,7 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
Anm2Frame frame = Anm2Frame{};
s32 index = -1;
if (reference->type == ANM2_TRIGGERS)
if (reference->itemType == ANM2_TRIGGERS)
{
/* don't add redudant triggers (i.e. at same time) */
for (auto & frameCheck : item->frames)
@@ -832,7 +896,6 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
}
else
{
s32 delay = 0;
s32 frameDelayCount = 0;
/* Add up all delay to see where this new frame might lie */
@@ -844,7 +907,7 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
return NULL;
/* Will insert next to frame if frame exists */
Anm2Frame* checkFrame = anm2_frame_from_reference(self, reference, animationID);
Anm2Frame* checkFrame = anm2_frame_from_reference(self, reference);
if (checkFrame)
{
@@ -852,7 +915,7 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
if (frameDelayCount + checkFrame->delay > animation->frameNum)
frame.delay = animation->frameNum - frameDelayCount;
index = reference->index + 1;
index = reference->frameIndex + 1;
}
else
index = (s32)item->frames.size();
@@ -865,3 +928,25 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
return NULL;
}
/* Clears anm2 reference */
void
anm2_reference_clear(Anm2Reference* self)
{
*self = Anm2Reference{};
}
/* Clears anm2 item reference */
void
anm2_reference_item_clear(Anm2Reference* self)
{
self->itemType = ANM2_NONE;
self->itemID = -1;
}
/* Clears anm2 reference */
void
anm2_reference_frame_clear(Anm2Reference* self)
{
self->frameIndex = -1;
}

View File

@@ -192,9 +192,10 @@ struct Anm2
struct Anm2Reference
{
Anm2Type type = ANM2_NONE;
s32 id = -1;
s32 index = -1;
Anm2Type itemType = ANM2_NONE;
s32 animationID = -1;
s32 itemID = -1;
s32 frameIndex = -1;
auto operator<=>(const Anm2Reference&) const = default;
};
@@ -210,8 +211,12 @@ void anm2_created_on_set(Anm2* self);
s32 anm2_animation_add(Anm2* self);
void anm2_animation_remove(Anm2* self, s32 id);
void anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const char* path, s32 id);
Anm2Animation* anm2_animation_from_id(Anm2* self, s32 animationID);
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID);
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID);
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time);
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, s32 animationID, f32 time);
Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference);
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference);
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference);
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time);
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time);
void anm2_reference_clear(Anm2Reference* self);
void anm2_reference_item_clear(Anm2Reference* self);
void anm2_reference_frame_clear(Anm2Reference* self);
s32 anm2_animation_length_get(Anm2* self, s32 animationID);

View File

@@ -66,13 +66,6 @@ dialog_tick(Dialog* self)
{
Texture texture;
s32 id;
char relativePath[PATH_MAX];
/* Get the relative path */
std::filesystem::path baseDirectory = std::filesystem::current_path();
std::filesystem::path relativePathString = std::filesystem::relative(self->path, baseDirectory);
strncpy(relativePath, relativePathString.c_str(), PATH_MAX - 1);
switch (self->type)
{
@@ -80,21 +73,21 @@ dialog_tick(Dialog* self)
*self->reference = Anm2Reference{};
resources_textures_free(self->resources);
anm2_deserialize(self->anm2, self->resources, self->path);
window_title_from_anm2_set(self->window, self->anm2);
window_title_from_path_set(self->window, self->path);
break;
case DIALOG_ANM2_SAVE:
anm2_serialize(self->anm2, relativePath);
window_title_from_anm2_set(self->window, self->anm2);
anm2_serialize(self->anm2, self->path);
window_title_from_path_set(self->window, self->path);
break;
case DIALOG_PNG_OPEN:
id = map_next_id_get(self->resources->textures);
self->anm2->spritesheets[id] = Anm2Spritesheet{};
strncpy(self->anm2->spritesheets[id].path, relativePath, PATH_MAX);
anm2_spritesheet_texture_load(self->anm2, self->resources, relativePath, id);
strncpy(self->anm2->spritesheets[id].path, self->path, PATH_MAX);
anm2_spritesheet_texture_load(self->anm2, self->resources, self->path, id);
break;
case DIALOG_PNG_REPLACE:
strncpy(self->anm2->spritesheets[self->replaceID].path, relativePath, PATH_MAX);
anm2_spritesheet_texture_load(self->anm2, self->resources, relativePath, self->replaceID);
strncpy(self->anm2->spritesheets[self->replaceID].path, self->path, PATH_MAX);
anm2_spritesheet_texture_load(self->anm2, self->resources, self->path, self->replaceID);
self->replaceID = -1;
break;
default:

View File

@@ -45,21 +45,10 @@ _editor_grid_set(Editor* self)
}
void
editor_init
(
Editor* self,
Anm2* anm2,
Anm2Reference* reference,
s32* animationID,
s32* spritesheetID,
Resources* resources,
Settings* settings
)
editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings)
{
self->anm2 = anm2;
self->reference = reference;
self->animationID = animationID;
self->spritesheetID = spritesheetID;
self->resources = resources;
self->settings = settings;
@@ -153,9 +142,12 @@ editor_draw(Editor* self)
glClear(GL_COLOR_BUFFER_BIT);
if (*self->spritesheetID > -1)
s32 spritesheetID = self->reference->itemType == ANM2_LAYER ?
self->anm2->layers[self->reference->itemID].spritesheetID : -1;
if (spritesheetID > -1)
{
Texture* texture = &self->resources->textures[*self->spritesheetID];
Texture* texture = &self->resources->textures[spritesheetID];
glm::mat4 spritesheetTransform = editorTransform;
glm::vec2 ndcScale = glm::vec2(texture->size.x, texture->size.y) / (EDITOR_SIZE * 0.5f);
@@ -199,10 +191,10 @@ editor_draw(Editor* self)
glUseProgram(0);
}
Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference, *self->animationID);
Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference);
/* Draw the layer frame's crop and pivot */
if (frame && self->reference->type == ANM2_LAYER)
if (frame)
{
/* Rect */
glm::mat4 rectTransform = editorTransform;
@@ -265,6 +257,12 @@ editor_draw(Editor* self)
static ivec2 previousGridSize = {-1, -1};
static ivec2 previousGridOffset = {-1, -1};
static s32 gridVertexCount = -1;
glm::mat4 gridTransform = editorTransform;
glm::vec2 gridNDCPos = (EDITOR_SIZE / 2.0f) / (EDITOR_SIZE / 2.0f);
gridTransform = glm::translate(gridTransform, glm::vec3(gridNDCPos, 0.0f));
ivec2 gridSize = ivec2(self->settings->editorGridSizeX, self->settings->editorGridSizeY);
ivec2 gridOffset = ivec2(self->settings->editorGridOffsetX, self->settings->editorGridOffsetY);
@@ -277,7 +275,7 @@ editor_draw(Editor* self)
glUseProgram(shaderLine);
glBindVertexArray(self->gridVAO);
glUniformMatrix4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, (f32*)value_ptr(editorTransform));
glUniformMatrix4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, (f32*)value_ptr(gridTransform));
glUniform4f
(

View File

@@ -22,8 +22,6 @@ struct Editor
{
Anm2* anm2 = NULL;
Anm2Reference* reference = NULL;
s32* animationID = NULL;
s32* spritesheetID = NULL;
Resources* resources = NULL;
Settings* settings = NULL;
GLuint fbo;
@@ -38,16 +36,7 @@ struct Editor
GLuint borderVBO;
};
void editor_init
(
Editor* self,
Anm2* anm2,
Anm2Reference* reference,
s32* animationID,
s32* spritesheetID,
Resources* resources,
Settings* settings
);
void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings);
void editor_draw(Editor* self);
void editor_tick(Editor* self);

File diff suppressed because it is too large Load Diff

View File

@@ -37,6 +37,7 @@
static const vec2 IMGUI_TASKBAR_MARGINS = {8, 4};
static const vec2 IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS = {1, 1};
static const ImVec2 IMGUI_RECORD_TOOLTIP_OFFSET = {16, 16};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE = {1280, 105};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE = {200, 85};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_POSITION = {8, 135};
@@ -95,8 +96,7 @@ struct Imgui
Input* input = NULL;
Anm2* anm2 = NULL;
Anm2Reference* reference = NULL;
s32* animationID = NULL;
s32* spritesheetID = NULL;
f32* time = NULL;
Editor* editor = NULL;
Preview* preview = NULL;
Settings* settings = NULL;
@@ -117,8 +117,7 @@ imgui_init
Input* input,
Anm2* anm2,
Anm2Reference* reference,
s32* animationID,
s32* spritesheetID,
f32* time,
Editor* editor,
Preview* preview,
Settings* settings,

View File

@@ -6,7 +6,6 @@ static void
_mouse_tick(Mouse* self)
{
s32 state;
SDL_Event event;
memcpy(&self->previous, &self->current, sizeof(bool) * MOUSE_COUNT);
memset(&self->current, '\0', sizeof(bool) * MOUSE_COUNT);

View File

@@ -233,7 +233,7 @@ enum KeyType
KEY_RGUI = 231
};
#define INPUT_COUNT (INPUT_UNDO + 1)
#define INPUT_COUNT (INPUT_REDO + 1)
enum InputType
{
INPUT_PAN,
@@ -248,7 +248,8 @@ enum InputType
INPUT_ROTATE_RIGHT,
INPUT_ZOOM_IN,
INPUT_ZOOM_OUT,
INPUT_UNDO
INPUT_UNDO,
INPUT_REDO
};
static const KeyType INPUT_KEYS[INPUT_COUNT]
@@ -265,7 +266,8 @@ static const KeyType INPUT_KEYS[INPUT_COUNT]
KEY_W,
KEY_1,
KEY_2,
KEY_Z
KEY_Z,
KEY_Y
};
struct Keyboard

View File

@@ -60,11 +60,11 @@ _preview_grid_set(Preview* self)
}
void
preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, s32* animationID, Resources* resources, Settings* settings)
preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, f32* time, Resources* resources, Settings* settings)
{
self->anm2 = anm2;
self->reference = reference;
self->animationID = animationID;
self->time = time;
self->resources = resources;
self->settings = settings;
@@ -140,19 +140,25 @@ preview_tick(Preview* self)
{
self->settings->previewZoom = CLAMP(self->settings->previewZoom, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX);
Anm2Animation* animation = anm2_animation_from_id(self->anm2, *self->animationID);
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
if (animation)
{
if (self->isPlaying)
{
self->time += (f32)self->anm2->fps / TICK_RATE;
*self->time += (f32)self->anm2->fps / TICK_RATE;
if (self->time >= (f32)animation->frameNum - 1)
self->time = 0.0f;
}
if (*self->time >= (f32)animation->frameNum - 1)
{
if (self->settings->playbackIsLoop && !self->isRecording)
*self->time = 0.0f;
else
self->time = CLAMP(self->time, 0.0f, (f32)animation->frameNum);
self->isPlaying = false;
}
}
if (!self->isPlaying)
*self->time = CLAMP(*self->time, 0.0f, (f32)animation->frameNum - 1);
}
}
@@ -161,6 +167,9 @@ preview_draw(Preview* self)
{
GLuint shaderLine = self->resources->shaders[SHADER_LINE];
GLuint shaderTexture = self->resources->shaders[SHADER_TEXTURE];
static bool isRecordThisFrame = false;
static f32 recordFrameTimeNext = 0.0f;
static s32 recordFrameIndex = 0;
f32 zoomFactor = self->settings->previewZoom / 100.0f;
glm::vec2 ndcPan = glm::vec2(-self->settings->previewPanX / (PREVIEW_SIZE.x / 2.0f), -self->settings->previewPanY / (PREVIEW_SIZE.y / 2.0f));
@@ -239,13 +248,14 @@ preview_draw(Preview* self)
glUseProgram(0);
}
Anm2Animation* animation = anm2_animation_from_id(self->anm2, *self->animationID);
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
/* Animation */
if (animation)
{
Anm2Frame rootFrame;
anm2_frame_from_time(self->anm2, &rootFrame, Anm2Reference{ANM2_ROOT, 0, 0}, *self->animationID, self->time);
Anm2Frame frame;
anm2_frame_from_time(self->anm2, &rootFrame, Anm2Reference{ANM2_ROOT, self->reference->animationID, 0, 0}, *self->time);
/* Layers */
for (auto & [id, layerAnimation] : animation->layerAnimations)
@@ -253,9 +263,7 @@ preview_draw(Preview* self)
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue;
Anm2Frame frame;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, id, 0}, *self->animationID, self->time);
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, self->reference->animationID, id, 0}, *self->time);
if (!frame.isVisible)
continue;
@@ -351,9 +359,7 @@ preview_draw(Preview* self)
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue;
Anm2Frame frame;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, id, 0}, *self->animationID, self->time);
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, self->reference->animationID, id, 0}, *self->time);
if (!frame.isVisible)
continue;
@@ -362,7 +368,7 @@ preview_draw(Preview* self)
glm::vec2 position = self->settings->previewIsRootTransform ? (frame.position + rootFrame.position) : frame.position;
glm::vec2 ndcPos = (position - (PREVIEW_PIVOT_SIZE / 2.0f)) / (PREVIEW_SIZE / 2.0f);
glm::vec2 ndcPos = position /(PREVIEW_SIZE / 2.0f);
glm::vec2 ndcScale = PREVIEW_PIVOT_SIZE / (PREVIEW_SIZE / 2.0f);
pivotTransform = glm::translate(pivotTransform, glm::vec3(ndcPos, 0.0f));
@@ -400,9 +406,7 @@ preview_draw(Preview* self)
if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0)
continue;
Anm2Frame frame;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_NULL, id, 0}, *self->animationID, self->time);
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_NULL, id, 0}, *self->time);
if (!frame.isVisible)
continue;
@@ -472,6 +476,76 @@ preview_draw(Preview* self)
}
}
if (self->isRecording && animation)
{
if (recordFrameIndex == 0)
{
if
(
std::filesystem::exists(STRING_PREVIEW_FRAMES_DIRECTORY) &&
std::filesystem::is_directory(STRING_PREVIEW_FRAMES_DIRECTORY))
{
for (const auto & entry : std::filesystem::directory_iterator(STRING_PREVIEW_FRAMES_DIRECTORY))
std::filesystem::remove(entry);
}
else
std::filesystem::create_directories(STRING_PREVIEW_FRAMES_DIRECTORY);
self->isPlaying = true;
}
if (isRecordThisFrame)
{
size_t frameSize = (self->recordSize.x * self->recordSize.y * 4);
u8* frame = (u8*)malloc(frameSize);
memset(frame, '\0',frameSize);
char path[PATH_MAX];
vec2 position =
{
self->settings->previewPanX - (PREVIEW_SIZE.x / 2.0f) + (self->recordSize.x / 2.0f),
self->settings->previewPanY - (PREVIEW_SIZE.y / 2.0f) + (self->recordSize.y / 2.0f)
};
memset(path, '\0', PATH_MAX);
snprintf(path, PATH_MAX, STRING_PREVIEW_FRAMES_FORMAT, STRING_PREVIEW_FRAMES_DIRECTORY, recordFrameIndex);
glReadBuffer(GL_FRONT);
glReadPixels
(
(s32)position.x,
(s32)position.y,
self->recordSize.x,
self->recordSize.y,
GL_RGBA,
GL_UNSIGNED_BYTE,
frame
);
texture_from_data_write(path, frame, self->recordSize.x, self->recordSize.y);
free(frame);
isRecordThisFrame = false;
recordFrameIndex++;
}
else
{
if (*self->time >= (f32)animation->frameNum - 1)
{
self->isRecording = false;
self->isPlaying = false;
recordFrameIndex = 0;
recordFrameTimeNext = 0;
*self->time = 0.0f;
}
else if (*self->time >= recordFrameTimeNext)
{
isRecordThisFrame = true;
recordFrameTimeNext = *self->time + (f32)self->anm2->fps / TICK_RATE;
}
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

View File

@@ -39,10 +39,10 @@ struct Preview
{
Anm2* anm2 = NULL;
Anm2Reference* reference = NULL;
f32* time = NULL;
Input* input = NULL;
Resources* resources = NULL;
Settings* settings = NULL;
s32* animationID = NULL;
GLuint axisVAO;
GLuint axisVBO;
GLuint fbo;
@@ -56,10 +56,12 @@ struct Preview
GLuint textureVAO;
GLuint textureVBO;
bool isPlaying = false;
f32 time = 0;
bool isRecording = false;
vec2 recordSize = {0.0f, 0.0f};
};
void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, s32* animationID, Resources* resources, Settings* settings);
void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, f32* time, Resources* resources, Settings* settings);
void preview_draw(Preview* self);
void preview_tick(Preview* self);
void preview_free(Preview* self);
void preview_record_set(Preview* self);

View File

@@ -1,6 +1,6 @@
#pragma once
#include "RESOURCES.h"
#include "PACKED.h"
#include "texture.h"
#include "shader.h"

View File

@@ -28,6 +28,7 @@ enum SettingsItem
{
SETTINGS_WINDOW_W,
SETTINGS_WINDOW_H,
SETTINGS_PLAYBACK_IS_LOOP,
SETTINGS_PREVIEW_IS_AXIS,
SETTINGS_PREVIEW_IS_GRID,
SETTINGS_PREVIEW_IS_ROOT_TRANSFORM,
@@ -68,13 +69,14 @@ enum SettingsItem
SETTINGS_EDITOR_BACKGROUND_COLOR_R,
SETTINGS_EDITOR_BACKGROUND_COLOR_G,
SETTINGS_EDITOR_BACKGROUND_COLOR_B,
SETTINGS_EDITOR_BACKGROUND_COLOR_A
SETTINGS_EDITOR_BACKGROUND_COLOR_A,
};
struct Settings
{
s32 windowW = 1920;
s32 windowH = 1080;
bool playbackIsLoop = true;
bool previewIsAxis = true;
bool previewIsGrid = true;
bool previewIsRootTransform = false;
@@ -122,6 +124,7 @@ static const SettingsEntry SETTINGS_ENTRIES[SETTINGS_COUNT] =
{
{"windowW=", "windowW=%i", SETTINGS_TYPE_INT, offsetof(Settings, windowW)},
{"windowH=", "windowH=%i", SETTINGS_TYPE_INT, offsetof(Settings, windowH)},
{"playbackIsLoop=", "playbackIsLoop=%i", SETTINGS_TYPE_BOOL, offsetof(Settings, playbackIsLoop)},
{"previewIsAxis=", "previewIsAxis=%i", SETTINGS_TYPE_BOOL, offsetof(Settings, previewIsAxis)},
{"previewIsGrid=", "previewIsGrid=%i", SETTINGS_TYPE_BOOL, offsetof(Settings, previewIsGrid)},
{"previewIsRootTransform=", "previewIsRootTransform=%i", SETTINGS_TYPE_BOOL, offsetof(Settings, previewIsRootTransform)},

View File

@@ -30,7 +30,6 @@ shader_init(GLuint* self, const char* vertex, const char* fragment)
{
GLuint vertexHandle;
GLuint fragmentHandle;
bool isSuccess;
vertexHandle = glCreateShader(GL_VERTEX_SHADER);
fragmentHandle = glCreateShader(GL_FRAGMENT_SHADER);

View File

@@ -1,27 +1,90 @@
#include "snapshots.h"
/* TODO */
/*
void
undo_stack_push(Snapshots* self, Anm2* anm2)
snapshots_undo_stack_push(Snapshots* self, Snapshot* snapshot)
{
if (self->top >= UNDO_STACK_MAX)
if (self->undoStack.top >= SNAPSHOT_STACK_MAX)
{
memmove(&self->snapshots[0], &self->snapshots[1], sizeof(Anm2) * (UNDO_STACK_MAX - 1));
self->top = UNDO_STACK_MAX - 1;
memmove(&self->undoStack.snapshots[0], &self->undoStack.snapshots[1], sizeof(Snapshot) * (SNAPSHOT_STACK_MAX - 1));
self->undoStack.top = SNAPSHOT_STACK_MAX - 1;
}
self->snapshots[self->top++] = *anm2;
self->undoStack.snapshots[self->undoStack.top++] = *snapshot;
self->redoStack.top = 0;
}
bool
undo_stack_pop(Snapshots* self, Anm2* anm2)
snapshots_undo_stack_pop(Snapshots* self, Snapshot* snapshot)
{
if (self->top == 0)
if (self->undoStack.top == 0)
return false;
*anm2 = self->snapshots[--self->top];
*snapshot = self->undoStack.snapshots[--self->undoStack.top];
return true;
}
*/
void
snapshots_redo_stack_push(Snapshots* self, Snapshot* snapshot)
{
if (self->redoStack.top >= SNAPSHOT_STACK_MAX)
{
memmove(&self->redoStack.snapshots[0], &self->redoStack.snapshots[1], sizeof(Snapshot) * (SNAPSHOT_STACK_MAX - 1));
self->redoStack.top = SNAPSHOT_STACK_MAX - 1;
}
self->redoStack.snapshots[self->redoStack.top++] = *snapshot;
}
bool
snapshots_redo_stack_pop(Snapshots* self, Snapshot* snapshot)
{
if (self->redoStack.top == 0)
return false;
*snapshot = self->redoStack.snapshots[--self->redoStack.top];
return true;
}
void
snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, f32* time, Input* input)
{
self->anm2 = anm2;
self->reference = reference;
self->time = time;
self->input = input;
}
void
snapshots_tick(Snapshots* self)
{
/* Undo */
if (key_press(&self->input->keyboard, INPUT_KEYS[INPUT_UNDO]))
{
Snapshot snapshot;
if (snapshots_undo_stack_pop(self, &snapshot))
{
Snapshot current = {*self->anm2, *self->reference, *self->time};
snapshots_redo_stack_push(self, &current);
*self->anm2 = snapshot.anm2;
*self->reference = snapshot.reference;
*self->time = snapshot.time;
}
}
/* Redo */
if (key_press(&self->input->keyboard, INPUT_KEYS[INPUT_REDO]))
{
Snapshot snapshot;
if (snapshots_redo_stack_pop(self, &snapshot))
{
Snapshot current = {*self->anm2, *self->reference, *self->time};
snapshots_undo_stack_push(self, &current);
*self->anm2 = snapshot.anm2;
*self->reference = snapshot.reference;
*self->time = snapshot.time;
}
}
}

View File

@@ -1,22 +1,34 @@
#pragma once
#include "anm2.h"
#include "input.h"
#define SNAPSHOT_STACK_MAX 100
struct Snapshot
{
Anm2 anm2;
Anm2Reference reference;
f32 time;
};
struct SnapshotStack
{
Anm2 snapshots[SNAPSHOT_STACK_MAX];
Snapshot snapshots[SNAPSHOT_STACK_MAX];
s32 top = 0;
};
struct Snapshots
{
Anm2* anm2 = NULL;
Anm2Reference* reference = NULL;
f32* time = NULL;
Input* input = NULL;
SnapshotStack undoStack;
SnapshotStack redoStack;
};
/*
void undo_stack_push(UndoStack* self, Anm2* anm2);
bool undo_stack_pop(UndoStack* self, Anm2* anm2);
*/
void snapshots_undo_stack_push(Snapshots* self, Snapshot* snapshot);
bool snapshots_undo_stack_pop(Snapshots* self, Snapshot* snapshot);
void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, f32* time, Input* input);
void snapshots_tick(Snapshots* self);

View File

@@ -35,6 +35,7 @@ _tick(State* state)
editor_tick(&state->editor);
preview_tick(&state->preview);
tool_tick(&state->tool);
snapshots_tick(&state->snapshots);
dialog_tick(&state->dialog);
imgui_tick(&state->imgui);
}
@@ -52,13 +53,6 @@ _draw(State* state)
void
init(State* state)
{
/* set start working directory */
std::filesystem::path startPath = std::filesystem::current_path();
memset(state->startPath, '\0', PATH_MAX - 1);
strncpy(state->startPath, startPath.c_str(), PATH_MAX - 1);
settings_load(&state->settings);
printf(STRING_INFO_INIT);
@@ -107,27 +101,9 @@ init(State* state)
resources_init(&state->resources);
dialog_init(&state->dialog, &state->anm2, &state->reference, &state->resources, state->window);
tool_init(&state->tool, &state->input);
preview_init
(
&state->preview,
&state->anm2,
&state->reference,
&state->animationID,
&state->resources,
&state->settings
);
editor_init
(
&state->editor,
&state->anm2,
&state->reference,
&state->animationID,
&state->spritesheetID,
&state->resources,
&state->settings
);
snapshots_init(&state->snapshots, &state->anm2, &state->reference, &state->time, &state->input);
preview_init(&state->preview, &state->anm2, &state->reference, &state->time, &state->resources, &state->settings);
editor_init(&state->editor, &state->anm2, &state->reference, &state->resources, &state->settings);
imgui_init
(
@@ -137,8 +113,7 @@ init(State* state)
&state->input,
&state->anm2,
&state->reference,
&state->animationID,
&state->spritesheetID,
&state->time,
&state->editor,
&state->preview,
&state->settings,
@@ -149,11 +124,12 @@ init(State* state)
);
if (state->isArgument)
{
anm2_deserialize(&state->anm2, &state->resources, state->argument);
window_title_from_path_set(state->window, state->argument);
}
else
anm2_new(&state->anm2);
window_title_from_anm2_set(state->window, &state->anm2);
}
void
@@ -179,9 +155,6 @@ loop(State* state)
void
quit(State* state)
{
/* return to base path */
std::filesystem::current_path(state->startPath);
imgui_free(&state->imgui);
settings_save(&state->settings);
preview_free(&state->preview);

View File

@@ -19,6 +19,7 @@ struct State
Preview preview;
Anm2 anm2;
Anm2Reference reference;
f32 time;
Resources resources;
Settings settings;
Tool tool;
@@ -26,9 +27,6 @@ struct State
bool isArgument = false;
bool isRunning = true;
char argument[PATH_MAX] = STRING_EMPTY;
char startPath[PATH_MAX] = STRING_EMPTY;
s32 animationID = -1;
s32 spritesheetID = -1;
u64 lastTick = 0;
u64 tick = 0;
};

View File

@@ -6,6 +6,9 @@
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
void
texture_gl_set(Texture* self, void* data)
{
@@ -58,6 +61,14 @@ texture_from_data_init(Texture* self, const u8* data, u32 length)
return true;
}
/* Writes a *.png to the path from the data/size */
/* Returns true on success */
bool
texture_from_data_write(const char* path, const u8* data, s32 width, s32 height)
{
return (bool)stbi_write_png(path, width, height, 4, data, width * 4);
}
void
texture_free(Texture* self)
{

View File

@@ -14,3 +14,4 @@ void texture_gl_set(Texture* self, void* data);
bool texture_from_path_init(Texture* self, const char* path);
bool texture_from_data_init(Texture* self, const u8* data, u32 length);
void texture_free(Texture* self);
bool texture_from_data_write(const char* path, const u8* data, s32 width, s32 height);

View File

@@ -2,12 +2,12 @@
/* Sets the window title from the given anm2 */
void
window_title_from_anm2_set(SDL_Window* self, Anm2* anm2)
window_title_from_path_set(SDL_Window* self, const char* path)
{
if (!strcmp(anm2->path, STRING_EMPTY) == 0)
if (!strcmp(path, STRING_EMPTY) == 0)
{
char windowTitle[WINDOW_TITLE_MAX];
snprintf(windowTitle, WINDOW_TITLE_MAX, STRING_WINDOW_TITLE_EDITING, anm2->path);
snprintf(windowTitle, WINDOW_TITLE_MAX, STRING_WINDOW_TITLE_EDITING, path);
SDL_SetWindowTitle(self, windowTitle);
}
else

View File

@@ -1,7 +1,7 @@
#pragma once
#include "anm2.h"
#include "COMMON.h"
#define WINDOW_TITLE_MAX 0xFF + PATH_MAX
void window_title_from_anm2_set(SDL_Window* self, Anm2* anm2);
void window_title_from_path_set(SDL_Window* self, const char* path);