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)
 | 
					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
									
								
							
							
						
						
									
										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 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;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										177
									
								
								src/anm2.cpp
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								src/anm2.cpp
									
									
									
									
									
								
							@@ -13,8 +13,8 @@ anm2_created_on_set(Anm2* self)
 | 
				
			|||||||
	currentTime = time(NULL);
 | 
						currentTime = time(NULL);
 | 
				
			||||||
	local = localtime(¤tTime);
 | 
						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);
 | 
						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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								src/anm2.h
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/anm2.h
									
									
									
									
									
								
							@@ -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);
 | 
				
			||||||
@@ -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:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
        (
 | 
					        (
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								src/editor.h
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/editor.h
									
									
									
									
									
								
							@@ -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);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										498
									
								
								src/imgui.cpp
									
									
									
									
									
								
							
							
						
						
									
										498
									
								
								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_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,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										118
									
								
								src/preview.cpp
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								src/preview.cpp
									
									
									
									
									
								
							@@ -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); 
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
@@ -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"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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, ¤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
 | 
					#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);
 | 
				
			||||||
@@ -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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
}; 
 | 
					}; 
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
		Reference in New Issue
	
	Block a user