Undo + Recording
This commit is contained in:
@@ -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
1724
include/stb_image_write.h
Normal file
File diff suppressed because it is too large
Load Diff
10
src/COMMON.h
10
src/COMMON.h
@@ -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;
|
||||
|
@@ -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"
|
||||
|
||||
|
175
src/anm2.cpp
175
src/anm2.cpp
@@ -13,7 +13,7 @@ anm2_created_on_set(Anm2* self)
|
||||
currentTime = time(NULL);
|
||||
local = localtime(¤tTime);
|
||||
|
||||
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;
|
||||
}
|
21
src/anm2.h
21
src/anm2.h
@@ -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);
|
@@ -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:
|
||||
|
@@ -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
|
||||
(
|
||||
|
13
src/editor.h
13
src/editor.h
@@ -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);
|
||||
|
434
src/imgui.cpp
434
src/imgui.cpp
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
114
src/preview.cpp
114
src/preview.cpp
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "RESOURCES.h"
|
||||
#include "PACKED.h"
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
|
||||
|
@@ -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)},
|
||||
|
@@ -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);
|
||||
|
@@ -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, ¤t);
|
||||
|
||||
*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, ¤t);
|
||||
|
||||
*self->anm2 = snapshot.anm2;
|
||||
*self->reference = snapshot.reference;
|
||||
*self->time = snapshot.time;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
@@ -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);
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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);
|
@@ -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
|
||||
|
@@ -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);
|
Reference in New Issue
Block a user