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) 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) 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 IMVEC2_VEC2(value) ImVec2(value.x, value.y)
#define VEC2_IMVEC2(value) glm::vec2(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}; 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 vec4 COLOR_TRANSPARENT = {0.0f, 0.0f, 0.0f, 1.0f};
static const vec3 COLOR_OFFSET_NONE = {0.0f, 0.0f, 0.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 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 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 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> template<typename T>
static inline s32 map_next_id_get(const std::map<s32, T>& map) { 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; 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 "Anm2Ed"
#define STRING_WINDOW_TITLE_EDITING "Anm2Ed (%s)" #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_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_GL_CONTEXT_INIT "[ERROR] Could not initialize OpenGL context (%s)\n"
#define STRING_ERROR_FILE_READ "[ERROR] Could not read from file: %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_NEW_EVENT "New Event"
#define STRING_ANM2_ROOT "Root" #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_WINDOW "Window"
#define STRING_IMGUI_DOCKSPACE "Dockspace" #define STRING_IMGUI_DOCKSPACE "Dockspace"
#define STRING_IMGUI_TASKBAR "Taskbar" #define STRING_IMGUI_TASKBAR "Taskbar"
#define STRING_IMGUI_TASKBAR_PLAYBACK "Playback"
#define STRING_IMGUI_TASKBAR_FILE "File" #define STRING_IMGUI_TASKBAR_FILE "File"
#define STRING_IMGUI_TASKBAR_WIZARD "Wizard"
#define STRING_IMGUI_FILE_MENU "File Menu" #define STRING_IMGUI_FILE_MENU "File Menu"
#define STRING_IMGUI_FILE_NEW "New" #define STRING_IMGUI_FILE_NEW "New"
@@ -56,6 +64,14 @@
#define STRING_IMGUI_FILE_SAVE "Save" #define STRING_IMGUI_FILE_SAVE "Save"
#define STRING_IMGUI_FILE_SAVE_AS "Save As" #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 "Animations"
#define STRING_IMGUI_ANIMATIONS_ANIMATION_LABEL "##Animation" #define STRING_IMGUI_ANIMATIONS_ANIMATION_LABEL "##Animation"
#define STRING_IMGUI_ANIMATIONS_ADD "Add" #define STRING_IMGUI_ANIMATIONS_ADD "Add"
@@ -162,7 +178,7 @@
#define STRING_IMGUI_TIMELINE_FRAME_LABEL "##Frame" #define STRING_IMGUI_TIMELINE_FRAME_LABEL "##Frame"
#define STRING_IMGUI_TIMELINE_TRIGGER_LABEL "##Trigger" #define STRING_IMGUI_TIMELINE_TRIGGER_LABEL "##Trigger"
#define STRING_IMGUI_TIMELINE_FRAME_REMOVE "Remove Frame" #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_ANIMATION LENGTH "Animation Length:"
#define STRING_IMGUI_TIMELINE_VISIBLE "##Visible" #define STRING_IMGUI_TIMELINE_VISIBLE "##Visible"
#define STRING_IMGUI_TIMELINE_LAYER "Layer" #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_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_ELEMENT_VISIBLE "Toggle visibility for this element."
#define STRING_IMGUI_TOOLTIP_TIMELINE_FPS "Change the FPS of the animation." #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_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_FRAME_REMOVE "Removes the selected frame from the current selected element."
#define STRING_IMGUI_TOOLTIP_TIMELINE_LOOP "Toggles the animation looping." #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_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_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_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" #define STRING_OPENGL_VERSION "#version 330"

View File

@@ -13,8 +13,8 @@ anm2_created_on_set(Anm2* self)
currentTime = time(NULL); currentTime = time(NULL);
local = localtime(&currentTime); 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); strncpy(self->createdOn, date, ANM2_STRING_MAX);
} }
@@ -306,8 +306,6 @@ anm2_serialize(Anm2* self, const char* path)
return false; return false;
} }
working_directory_from_path_set(path);
printf(STRING_INFO_ANM2_WRITE, path); printf(STRING_INFO_ANM2_WRITE, path);
strncpy(self->path, path, PATH_MAX - 1); strncpy(self->path, path, PATH_MAX - 1);
@@ -331,6 +329,11 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
Anm2Spritesheet* spritesheet = NULL; Anm2Spritesheet* spritesheet = NULL;
Anm2Element anm2Element = ANM2_ELEMENT_ANIMATED_ACTOR; Anm2Element anm2Element = ANM2_ELEMENT_ANIMATED_ACTOR;
Anm2Attribute anm2Attribute = ANM2_ATTRIBUTE_ID; Anm2Attribute anm2Attribute = ANM2_ATTRIBUTE_ID;
Anm2Item addItem;
Anm2Layer addLayer;
Anm2Null addNull;
Anm2Event addEvent;
Anm2Spritesheet addSpritesheet;
*self = Anm2{}; *self = Anm2{};
@@ -343,15 +346,20 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
} }
resources_textures_free(resources); 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); strncpy(self->path, path, PATH_MAX - 1);
working_directory_from_path_set(path);
xmlRoot = xmlDocument.FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]); xmlRoot = xmlDocument.FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]);
xmlElement = xmlRoot; xmlElement = xmlRoot;
while (xmlElement) while (xmlElement)
{ {
const XMLAttribute* xmlAttribute = NULL; const XMLAttribute* xmlAttribute = NULL;
const XMLElement* xmlChild = NULL; const XMLElement* xmlChild = NULL;
s32 id = 0; s32 id = 0;
@@ -362,24 +370,16 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
switch (anm2Element) switch (anm2Element)
{ {
case ANM2_ELEMENT_SPRITESHEET: case ANM2_ELEMENT_SPRITESHEET:
id = map_next_id_get(self->spritesheets); spritesheet = &addSpritesheet;
self->spritesheets[id] = Anm2Spritesheet{};
spritesheet = &self->spritesheets[id];
break; break;
case ANM2_ELEMENT_LAYER: case ANM2_ELEMENT_LAYER:
id = map_next_id_get(self->layers); layer = &addLayer;
self->layers[id] = Anm2Layer{};
layer = &self->layers[id];
break; break;
case ANM2_ELEMENT_NULL: case ANM2_ELEMENT_NULL:
id = map_next_id_get(self->nulls); null = &addNull;
self->nulls[id] = Anm2Null{};
null = &self->nulls[id];
break; break;
case ANM2_ELEMENT_EVENT: case ANM2_ELEMENT_EVENT:
id = map_next_id_get(self->events); event = &addEvent;
self->events[id] = Anm2Event{};
event = &self->events[id];
break; break;
case ANM2_ELEMENT_ANIMATION: case ANM2_ELEMENT_ANIMATION:
id = map_next_id_get(self->animations); id = map_next_id_get(self->animations);
@@ -390,14 +390,8 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
item = &animation->rootAnimation; item = &animation->rootAnimation;
break; break;
case ANM2_ELEMENT_LAYER_ANIMATION: 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: case ANM2_ELEMENT_NULL_ANIMATION:
id = map_next_id_get(animation->nullAnimations); item = &addItem;
animation->nullAnimations[id] = Anm2Item{};
item = &animation->nullAnimations[id];
break; break;
case ANM2_ELEMENT_TRIGGERS: case ANM2_ELEMENT_TRIGGERS:
item = &animation->triggers; item = &animation->triggers;
@@ -431,6 +425,40 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
case ANM2_ATTRIBUTE_FPS: case ANM2_ATTRIBUTE_FPS:
self->fps = atoi(xmlAttribute->Value()); self->fps = atoi(xmlAttribute->Value());
break; 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: case ANM2_ATTRIBUTE_PATH:
strncpy(spritesheet->path, xmlAttribute->Value(), PATH_MAX - 1); strncpy(spritesheet->path, xmlAttribute->Value(), PATH_MAX - 1);
break; break;
@@ -589,6 +617,9 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
printf(STRING_INFO_ANM2_READ, path); printf(STRING_INFO_ANM2_READ, path);
/* Set working directory back to old */
std::filesystem::current_path(workingPath);
return true; return true;
} }
@@ -694,9 +725,9 @@ anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const char* path
} }
Anm2Animation* 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()) if (it == self->animations.end())
return NULL; return NULL;
return &it->second; return &it->second;
@@ -704,27 +735,27 @@ anm2_animation_from_id(Anm2* self, s32 animationID)
/* Returns the item from a anm2 reference. */ /* Returns the item from a anm2 reference. */
Anm2Item* 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) if (!animation)
return NULL; return NULL;
switch (reference->type) switch (reference->itemType)
{ {
case ANM2_ROOT: case ANM2_ROOT:
return &animation->rootAnimation; return &animation->rootAnimation;
case ANM2_LAYER: case ANM2_LAYER:
{ {
auto it = animation->layerAnimations.find(reference->id); auto it = animation->layerAnimations.find(reference->itemID);
if (it == animation->layerAnimations.end()) if (it == animation->layerAnimations.end())
return NULL; return NULL;
return &it->second; return &it->second;
} }
case ANM2_NULL: case ANM2_NULL:
{ {
auto it = animation->nullAnimations.find(reference->id); auto it = animation->nullAnimations.find(reference->itemID);
if (it == animation->nullAnimations.end()) if (it == animation->nullAnimations.end())
return NULL; return NULL;
return &it->second; 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 */ /* Gets the frame from the reference's properties */
Anm2Frame* 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) if (!item)
return NULL; 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 NULL;
return &item->frames[reference->index]; return &item->frames[reference->frameIndex];
} }
/* Creates/fetches a frame from a given time. */ /* Creates/fetches a frame from a given time. */
/* Returns true/false if frame will be valid or not. */ /* Returns true/false if frame will be valid or not. */
void 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 */ /* Out of range */
if (time < 0 || time > animation->frameNum) if (time < 0 || time > animation->frameNum)
return; return;
Anm2Item* item = anm2_item_from_reference(self, &reference, animationID); Anm2Item* item = anm2_item_from_reference(self, &reference);
if (!item) if (!item)
return; 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 */ /* Will try adding a frame to the anm2 given the specified reference */
Anm2Frame* 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); Anm2Animation* animation = anm2_animation_from_reference(self, reference);
Anm2Item* item = anm2_item_from_reference(self, reference, animationID); Anm2Item* item = anm2_item_from_reference(self, reference);
if (!animation || !item) if (!animation || !item)
return NULL; return NULL;
@@ -818,7 +882,7 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
Anm2Frame frame = Anm2Frame{}; Anm2Frame frame = Anm2Frame{};
s32 index = -1; s32 index = -1;
if (reference->type == ANM2_TRIGGERS) if (reference->itemType == ANM2_TRIGGERS)
{ {
/* don't add redudant triggers (i.e. at same time) */ /* don't add redudant triggers (i.e. at same time) */
for (auto & frameCheck : item->frames) for (auto & frameCheck : item->frames)
@@ -832,7 +896,6 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
} }
else else
{ {
s32 delay = 0;
s32 frameDelayCount = 0; s32 frameDelayCount = 0;
/* Add up all delay to see where this new frame might lie */ /* 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; return NULL;
/* Will insert next to frame if frame exists */ /* 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) if (checkFrame)
{ {
@@ -852,7 +915,7 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
if (frameDelayCount + checkFrame->delay > animation->frameNum) if (frameDelayCount + checkFrame->delay > animation->frameNum)
frame.delay = animation->frameNum - frameDelayCount; frame.delay = animation->frameNum - frameDelayCount;
index = reference->index + 1; index = reference->frameIndex + 1;
} }
else else
index = (s32)item->frames.size(); index = (s32)item->frames.size();
@@ -864,4 +927,26 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
} }
return NULL; 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 struct Anm2Reference
{ {
Anm2Type type = ANM2_NONE; Anm2Type itemType = ANM2_NONE;
s32 id = -1; s32 animationID = -1;
s32 index = -1; s32 itemID = -1;
s32 frameIndex = -1;
auto operator<=>(const Anm2Reference&) const = default; auto operator<=>(const Anm2Reference&) const = default;
}; };
@@ -210,8 +211,12 @@ void anm2_created_on_set(Anm2* self);
s32 anm2_animation_add(Anm2* self); s32 anm2_animation_add(Anm2* self);
void anm2_animation_remove(Anm2* self, s32 id); void anm2_animation_remove(Anm2* self, s32 id);
void anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const char* path, 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); Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference);
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID); Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference);
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID); Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference);
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time); Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time);
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, s32 animationID, f32 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; Texture texture;
s32 id; 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) switch (self->type)
{ {
@@ -80,21 +73,21 @@ dialog_tick(Dialog* self)
*self->reference = Anm2Reference{}; *self->reference = Anm2Reference{};
resources_textures_free(self->resources); resources_textures_free(self->resources);
anm2_deserialize(self->anm2, self->resources, self->path); 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; break;
case DIALOG_ANM2_SAVE: case DIALOG_ANM2_SAVE:
anm2_serialize(self->anm2, relativePath); anm2_serialize(self->anm2, self->path);
window_title_from_anm2_set(self->window, self->anm2); window_title_from_path_set(self->window, self->path);
break; break;
case DIALOG_PNG_OPEN: case DIALOG_PNG_OPEN:
id = map_next_id_get(self->resources->textures); id = map_next_id_get(self->resources->textures);
self->anm2->spritesheets[id] = Anm2Spritesheet{}; self->anm2->spritesheets[id] = Anm2Spritesheet{};
strncpy(self->anm2->spritesheets[id].path, relativePath, PATH_MAX); strncpy(self->anm2->spritesheets[id].path, self->path, PATH_MAX);
anm2_spritesheet_texture_load(self->anm2, self->resources, relativePath, id); anm2_spritesheet_texture_load(self->anm2, self->resources, self->path, id);
break; break;
case DIALOG_PNG_REPLACE: case DIALOG_PNG_REPLACE:
strncpy(self->anm2->spritesheets[self->replaceID].path, relativePath, PATH_MAX); strncpy(self->anm2->spritesheets[self->replaceID].path, self->path, PATH_MAX);
anm2_spritesheet_texture_load(self->anm2, self->resources, relativePath, self->replaceID); anm2_spritesheet_texture_load(self->anm2, self->resources, self->path, self->replaceID);
self->replaceID = -1; self->replaceID = -1;
break; break;
default: default:

View File

@@ -45,21 +45,10 @@ _editor_grid_set(Editor* self)
} }
void void
editor_init editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings)
(
Editor* self,
Anm2* anm2,
Anm2Reference* reference,
s32* animationID,
s32* spritesheetID,
Resources* resources,
Settings* settings
)
{ {
self->anm2 = anm2; self->anm2 = anm2;
self->reference = reference; self->reference = reference;
self->animationID = animationID;
self->spritesheetID = spritesheetID;
self->resources = resources; self->resources = resources;
self->settings = settings; self->settings = settings;
@@ -153,9 +142,12 @@ editor_draw(Editor* self)
glClear(GL_COLOR_BUFFER_BIT); 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::mat4 spritesheetTransform = editorTransform;
glm::vec2 ndcScale = glm::vec2(texture->size.x, texture->size.y) / (EDITOR_SIZE * 0.5f); 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); 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 */ /* Draw the layer frame's crop and pivot */
if (frame && self->reference->type == ANM2_LAYER) if (frame)
{ {
/* Rect */ /* Rect */
glm::mat4 rectTransform = editorTransform; glm::mat4 rectTransform = editorTransform;
@@ -265,6 +257,12 @@ editor_draw(Editor* self)
static ivec2 previousGridSize = {-1, -1}; static ivec2 previousGridSize = {-1, -1};
static ivec2 previousGridOffset = {-1, -1}; static ivec2 previousGridOffset = {-1, -1};
static s32 gridVertexCount = -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 gridSize = ivec2(self->settings->editorGridSizeX, self->settings->editorGridSizeY);
ivec2 gridOffset = ivec2(self->settings->editorGridOffsetX, self->settings->editorGridOffsetY); ivec2 gridOffset = ivec2(self->settings->editorGridOffsetX, self->settings->editorGridOffsetY);
@@ -277,7 +275,7 @@ editor_draw(Editor* self)
glUseProgram(shaderLine); glUseProgram(shaderLine);
glBindVertexArray(self->gridVAO); 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 glUniform4f
( (

View File

@@ -22,8 +22,6 @@ struct Editor
{ {
Anm2* anm2 = NULL; Anm2* anm2 = NULL;
Anm2Reference* reference = NULL; Anm2Reference* reference = NULL;
s32* animationID = NULL;
s32* spritesheetID = NULL;
Resources* resources = NULL; Resources* resources = NULL;
Settings* settings = NULL; Settings* settings = NULL;
GLuint fbo; GLuint fbo;
@@ -38,16 +36,7 @@ struct Editor
GLuint borderVBO; GLuint borderVBO;
}; };
void editor_init void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings);
(
Editor* self,
Anm2* anm2,
Anm2Reference* reference,
s32* animationID,
s32* spritesheetID,
Resources* resources,
Settings* settings
);
void editor_draw(Editor* self); void editor_draw(Editor* self);
void editor_tick(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_TASKBAR_MARGINS = {8, 4};
static const vec2 IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS = {1, 1}; 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_SIZE = {1280, 105};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE = {200, 85}; static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE = {200, 85};
static const ImVec2 IMGUI_ANIMATION_PREVIEW_POSITION = {8, 135}; static const ImVec2 IMGUI_ANIMATION_PREVIEW_POSITION = {8, 135};
@@ -95,8 +96,7 @@ struct Imgui
Input* input = NULL; Input* input = NULL;
Anm2* anm2 = NULL; Anm2* anm2 = NULL;
Anm2Reference* reference = NULL; Anm2Reference* reference = NULL;
s32* animationID = NULL; f32* time = NULL;
s32* spritesheetID = NULL;
Editor* editor = NULL; Editor* editor = NULL;
Preview* preview = NULL; Preview* preview = NULL;
Settings* settings = NULL; Settings* settings = NULL;
@@ -117,8 +117,7 @@ imgui_init
Input* input, Input* input,
Anm2* anm2, Anm2* anm2,
Anm2Reference* reference, Anm2Reference* reference,
s32* animationID, f32* time,
s32* spritesheetID,
Editor* editor, Editor* editor,
Preview* preview, Preview* preview,
Settings* settings, Settings* settings,

View File

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

View File

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

View File

@@ -60,11 +60,11 @@ _preview_grid_set(Preview* self)
} }
void 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->anm2 = anm2;
self->reference = reference; self->reference = reference;
self->animationID = animationID; self->time = time;
self->resources = resources; self->resources = resources;
self->settings = settings; self->settings = settings;
@@ -140,19 +140,25 @@ preview_tick(Preview* self)
{ {
self->settings->previewZoom = CLAMP(self->settings->previewZoom, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX); 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 (animation)
{ {
if (self->isPlaying) 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) if (*self->time >= (f32)animation->frameNum - 1)
self->time = 0.0f; {
if (self->settings->playbackIsLoop && !self->isRecording)
*self->time = 0.0f;
else
self->isPlaying = false;
}
} }
else
self->time = CLAMP(self->time, 0.0f, (f32)animation->frameNum); 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 shaderLine = self->resources->shaders[SHADER_LINE];
GLuint shaderTexture = self->resources->shaders[SHADER_TEXTURE]; 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; 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)); 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); glUseProgram(0);
} }
Anm2Animation* animation = anm2_animation_from_id(self->anm2, *self->animationID); Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
/* Animation */ /* Animation */
if (animation) if (animation)
{ {
Anm2Frame rootFrame; 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 */ /* Layers */
for (auto & [id, layerAnimation] : animation->layerAnimations) for (auto & [id, layerAnimation] : animation->layerAnimations)
@@ -253,9 +263,7 @@ preview_draw(Preview* self)
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0) if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue; continue;
Anm2Frame frame; anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, self->reference->animationID, id, 0}, *self->time);
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, id, 0}, *self->animationID, self->time);
if (!frame.isVisible) if (!frame.isVisible)
continue; continue;
@@ -351,9 +359,7 @@ preview_draw(Preview* self)
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0) if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue; continue;
Anm2Frame frame; anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, self->reference->animationID, id, 0}, *self->time);
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, id, 0}, *self->animationID, self->time);
if (!frame.isVisible) if (!frame.isVisible)
continue; continue;
@@ -361,8 +367,8 @@ preview_draw(Preview* self)
glm::mat4 pivotTransform = previewTransform; glm::mat4 pivotTransform = previewTransform;
glm::vec2 position = self->settings->previewIsRootTransform ? (frame.position + rootFrame.position) : frame.position; 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); glm::vec2 ndcScale = PREVIEW_PIVOT_SIZE / (PREVIEW_SIZE / 2.0f);
pivotTransform = glm::translate(pivotTransform, glm::vec3(ndcPos, 0.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) if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0)
continue; continue;
Anm2Frame frame; anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_NULL, id, 0}, *self->time);
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_NULL, id, 0}, *self->animationID, self->time);
if (!frame.isVisible) if (!frame.isVisible)
continue; continue;
@@ -470,7 +474,77 @@ preview_draw(Preview* self)
glUseProgram(0); glUseProgram(0);
} }
} }
} }
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); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }

View File

@@ -39,10 +39,10 @@ struct Preview
{ {
Anm2* anm2 = NULL; Anm2* anm2 = NULL;
Anm2Reference* reference = NULL; Anm2Reference* reference = NULL;
f32* time = NULL;
Input* input = NULL; Input* input = NULL;
Resources* resources = NULL; Resources* resources = NULL;
Settings* settings = NULL; Settings* settings = NULL;
s32* animationID = NULL;
GLuint axisVAO; GLuint axisVAO;
GLuint axisVBO; GLuint axisVBO;
GLuint fbo; GLuint fbo;
@@ -56,10 +56,12 @@ struct Preview
GLuint textureVAO; GLuint textureVAO;
GLuint textureVBO; GLuint textureVBO;
bool isPlaying = false; 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_draw(Preview* self);
void preview_tick(Preview* self); void preview_tick(Preview* self);
void preview_free(Preview* self); void preview_free(Preview* self);
void preview_record_set(Preview* self);

View File

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

View File

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

View File

@@ -1,27 +1,90 @@
#include "snapshots.h" #include "snapshots.h"
/* TODO */
/*
void 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)); memmove(&self->undoStack.snapshots[0], &self->undoStack.snapshots[1], sizeof(Snapshot) * (SNAPSHOT_STACK_MAX - 1));
self->top = UNDO_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 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; return false;
*anm2 = self->snapshots[--self->top]; *snapshot = self->undoStack.snapshots[--self->undoStack.top];
return true; 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 #pragma once
#include "anm2.h" #include "anm2.h"
#include "input.h"
#define SNAPSHOT_STACK_MAX 100 #define SNAPSHOT_STACK_MAX 100
struct Snapshot
{
Anm2 anm2;
Anm2Reference reference;
f32 time;
};
struct SnapshotStack struct SnapshotStack
{ {
Anm2 snapshots[SNAPSHOT_STACK_MAX]; Snapshot snapshots[SNAPSHOT_STACK_MAX];
s32 top = 0; s32 top = 0;
}; };
struct Snapshots struct Snapshots
{ {
Anm2* anm2 = NULL;
Anm2Reference* reference = NULL;
f32* time = NULL;
Input* input = NULL;
SnapshotStack undoStack; SnapshotStack undoStack;
SnapshotStack redoStack; SnapshotStack redoStack;
}; };
/* void snapshots_undo_stack_push(Snapshots* self, Snapshot* snapshot);
void undo_stack_push(UndoStack* self, Anm2* anm2); bool snapshots_undo_stack_pop(Snapshots* self, Snapshot* snapshot);
bool undo_stack_pop(UndoStack* self, Anm2* anm2); 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); editor_tick(&state->editor);
preview_tick(&state->preview); preview_tick(&state->preview);
tool_tick(&state->tool); tool_tick(&state->tool);
snapshots_tick(&state->snapshots);
dialog_tick(&state->dialog); dialog_tick(&state->dialog);
imgui_tick(&state->imgui); imgui_tick(&state->imgui);
} }
@@ -52,13 +53,6 @@ _draw(State* state)
void void
init(State* state) 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); settings_load(&state->settings);
printf(STRING_INFO_INIT); printf(STRING_INFO_INIT);
@@ -107,27 +101,9 @@ init(State* state)
resources_init(&state->resources); resources_init(&state->resources);
dialog_init(&state->dialog, &state->anm2, &state->reference, &state->resources, state->window); dialog_init(&state->dialog, &state->anm2, &state->reference, &state->resources, state->window);
tool_init(&state->tool, &state->input); tool_init(&state->tool, &state->input);
snapshots_init(&state->snapshots, &state->anm2, &state->reference, &state->time, &state->input);
preview_init 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);
&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
);
imgui_init imgui_init
( (
@@ -137,8 +113,7 @@ init(State* state)
&state->input, &state->input,
&state->anm2, &state->anm2,
&state->reference, &state->reference,
&state->animationID, &state->time,
&state->spritesheetID,
&state->editor, &state->editor,
&state->preview, &state->preview,
&state->settings, &state->settings,
@@ -149,11 +124,12 @@ init(State* state)
); );
if (state->isArgument) if (state->isArgument)
{
anm2_deserialize(&state->anm2, &state->resources, state->argument); anm2_deserialize(&state->anm2, &state->resources, state->argument);
window_title_from_path_set(state->window, state->argument);
}
else else
anm2_new(&state->anm2); anm2_new(&state->anm2);
window_title_from_anm2_set(state->window, &state->anm2);
} }
void void
@@ -179,9 +155,6 @@ loop(State* state)
void void
quit(State* state) quit(State* state)
{ {
/* return to base path */
std::filesystem::current_path(state->startPath);
imgui_free(&state->imgui); imgui_free(&state->imgui);
settings_save(&state->settings); settings_save(&state->settings);
preview_free(&state->preview); preview_free(&state->preview);

View File

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

View File

@@ -6,6 +6,9 @@
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h> #include <stb_image.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
void void
texture_gl_set(Texture* self, void* data) texture_gl_set(Texture* self, void* data)
{ {
@@ -58,6 +61,14 @@ texture_from_data_init(Texture* self, const u8* data, u32 length)
return true; 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 void
texture_free(Texture* self) 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_path_init(Texture* self, const char* path);
bool texture_from_data_init(Texture* self, const u8* data, u32 length); bool texture_from_data_init(Texture* self, const u8* data, u32 length);
void texture_free(Texture* self); 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 */ /* Sets the window title from the given anm2 */
void 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]; 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); SDL_SetWindowTitle(self, windowTitle);
} }
else else

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include "anm2.h" #include "COMMON.h"
#define WINDOW_TITLE_MAX 0xFF + PATH_MAX #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);