Undo + Recording
This commit is contained in:
		@@ -18,7 +18,7 @@ add_executable(${PROJECT_NAME} ${SOURCES})
 | 
			
		||||
 | 
			
		||||
target_include_directories(${PROJECT_NAME} PRIVATE include include/imgui include/tinyxml2 include/inih src)
 | 
			
		||||
 | 
			
		||||
set(CMAKE_CXX_FLAGS "-g -O2 -std=c++23 -Wall -Wextra -pedantic -Wno-unused-variable -Wno-unused-parameter -Wno-ignored-qualifiers -Wno-parentheses -Wno-unused-function -Wno-class-memaccess -Wno-delete-incomplete")
 | 
			
		||||
set(CMAKE_CXX_FLAGS "-g -O2 -std=c++23 -Wall -Wextra -pedantic -Wno-unused-variable -Wno-unused-parameter -Wno-ignored-qualifiers -Wno-parentheses -Wno-missing-field-initializers -Wno-unused-function -Wno-class-memaccess -Wno-delete-incomplete")
 | 
			
		||||
 | 
			
		||||
target_link_libraries(${PROJECT_NAME} PRIVATE m GL GLEW SDL3)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1724
									
								
								include/stb_image_write.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1724
									
								
								include/stb_image_write.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								src/COMMON.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/COMMON.h
									
									
									
									
									
								
							@@ -80,6 +80,7 @@ static const f32 GL_UV_VERTICES[] =
 | 
			
		||||
};
 | 
			
		||||
#define IMVEC2_VEC2(value) ImVec2(value.x, value.y)
 | 
			
		||||
#define VEC2_IMVEC2(value) glm::vec2(value.x, value.y)
 | 
			
		||||
#define IMVEC4_VEC4(value) ImVec4(value.r, value.g, value.b, value.a)
 | 
			
		||||
 | 
			
		||||
static const GLuint GL_TEXTURE_INDICES[] = {0, 1, 2, 2, 3, 0};
 | 
			
		||||
 | 
			
		||||
@@ -90,19 +91,10 @@ static const vec4 COLOR_OPAQUE = {1.0f, 1.0f, 1.0f, 1.0f};
 | 
			
		||||
static const vec4 COLOR_TRANSPARENT = {0.0f, 0.0f, 0.0f, 1.0f};
 | 
			
		||||
static const vec3 COLOR_OFFSET_NONE = {0.0f, 0.0f, 0.0f};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static inline const char* enum_to_string(const char* arr[], s32 count, s32 index) { return (index >= 0 && index < count) ? arr[index] : "undefined"; }
 | 
			
		||||
static inline s32 string_to_enum(const char* str, const char* const* arr, s32 n) { for (s32 i=0; i<n; ++i) if (!strcmp(str, arr[i])) return i; return -1; }
 | 
			
		||||
static inline bool string_to_bool(const char* str) { if (strcmp(str, "true") == 0) return true; return false; }
 | 
			
		||||
 | 
			
		||||
static inline void working_directory_from_path_set(const char* path)
 | 
			
		||||
{
 | 
			
		||||
    std::filesystem::path filePath = path;
 | 
			
		||||
	std::filesystem::path dir = filePath.parent_path();
 | 
			
		||||
    if (!dir.empty())
 | 
			
		||||
        std::filesystem::current_path(dir);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename T>
 | 
			
		||||
static inline s32 map_next_id_get(const std::map<s32, T>& map) {
 | 
			
		||||
    s32 id = 0; for (const auto& [key, _] : map) if (key != id) break; else ++id; return id;
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@
 | 
			
		||||
#define STRING_WINDOW_TITLE "Anm2Ed"
 | 
			
		||||
#define STRING_WINDOW_TITLE_EDITING "Anm2Ed (%s)"
 | 
			
		||||
 | 
			
		||||
#define STRING_ANM2_CREATED_ON_FORMAT "%d-%B-%Y %I:%M:%S %p"
 | 
			
		||||
 | 
			
		||||
#define STRING_ERROR_SDL_INIT "[ERROR] Could not initialize SDL (%s)\n"
 | 
			
		||||
#define STRING_ERROR_GL_CONTEXT_INIT "[ERROR] Could not initialize OpenGL context (%s)\n"
 | 
			
		||||
#define STRING_ERROR_FILE_READ "[ERROR] Could not read from file: %s\n"
 | 
			
		||||
@@ -45,10 +47,16 @@
 | 
			
		||||
#define STRING_ANM2_NEW_EVENT "New Event"
 | 
			
		||||
#define STRING_ANM2_ROOT "Root"
 | 
			
		||||
 | 
			
		||||
#define STRING_PREVIEW_FRAMES_DIRECTORY "frames/"
 | 
			
		||||
#define STRING_PREVIEW_FRAMES_FORMAT "%s/%03d.png"
 | 
			
		||||
 | 
			
		||||
#define STRING_IMGUI_WINDOW "Window"
 | 
			
		||||
#define STRING_IMGUI_DOCKSPACE "Dockspace"
 | 
			
		||||
 | 
			
		||||
#define STRING_IMGUI_TASKBAR "Taskbar"
 | 
			
		||||
#define STRING_IMGUI_TASKBAR_PLAYBACK "Playback"
 | 
			
		||||
#define STRING_IMGUI_TASKBAR_FILE "File"
 | 
			
		||||
#define STRING_IMGUI_TASKBAR_WIZARD "Wizard"
 | 
			
		||||
 | 
			
		||||
#define STRING_IMGUI_FILE_MENU "File Menu"
 | 
			
		||||
#define STRING_IMGUI_FILE_NEW "New"
 | 
			
		||||
@@ -56,6 +64,14 @@
 | 
			
		||||
#define STRING_IMGUI_FILE_SAVE "Save"
 | 
			
		||||
#define STRING_IMGUI_FILE_SAVE_AS "Save As"
 | 
			
		||||
 | 
			
		||||
#define STRING_IMGUI_PLAYBACK_MENU "Playback Menu"
 | 
			
		||||
#define STRING_IMGUI_PLAYBACK_LOOP "Loop"
 | 
			
		||||
 | 
			
		||||
#define STRING_IMGUI_WIZARD_MENU "Wizard Menu"
 | 
			
		||||
#define STRING_IMGUI_WIZARD_EXPORT_FRAMES_TO_PNG "Export Frames to PNG"
 | 
			
		||||
 | 
			
		||||
#define STRING_IMGUI_RECORDING "Recording..."
 | 
			
		||||
 | 
			
		||||
#define STRING_IMGUI_ANIMATIONS "Animations"
 | 
			
		||||
#define STRING_IMGUI_ANIMATIONS_ANIMATION_LABEL "##Animation"
 | 
			
		||||
#define STRING_IMGUI_ANIMATIONS_ADD "Add"
 | 
			
		||||
@@ -162,7 +178,7 @@
 | 
			
		||||
#define STRING_IMGUI_TIMELINE_FRAME_LABEL    "##Frame"
 | 
			
		||||
#define STRING_IMGUI_TIMELINE_TRIGGER_LABEL    "##Trigger"
 | 
			
		||||
#define STRING_IMGUI_TIMELINE_FRAME_REMOVE "Remove Frame"
 | 
			
		||||
#define STRING_IMGUI_TIMELINE_FIT_ANIMATION_LENGTH "= Fit Animation Length"
 | 
			
		||||
#define STRING_IMGUI_TIMELINE_FIT_ANIMATION_LENGTH "Fit Animation Length"
 | 
			
		||||
#define STRING_IMGUI_TIMELINE_ANIMATION LENGTH "Animation Length:"
 | 
			
		||||
#define STRING_IMGUI_TIMELINE_VISIBLE "##Visible"
 | 
			
		||||
#define STRING_IMGUI_TIMELINE_LAYER "Layer"
 | 
			
		||||
@@ -251,6 +267,7 @@
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_TRIGGERS "This is the animation's Triggers.\nTriggers are special activations; each is bound to an Event.\nClick to select."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_VISIBLE "Toggle visibility for this element."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_TIMELINE_FPS "Change the FPS of the animation."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_TIMELINE_FIT_ANIMATION_LENGTH "Sets the animation length to the latest frame."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_TIMELINE_FRAME_ADD "Add a frame to the current selected element."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_TIMELINE_FRAME_REMOVE "Removes the selected frame from the current selected element."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_TIMELINE_LOOP "Toggles the animation looping."
 | 
			
		||||
@@ -272,6 +289,15 @@
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_TOOLS_ROTATE "Use the rotate tool.\nWill rotate selected elements as the cursor is dragged.\n(Animation Preview only.)"
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_TOOLS_SCALE "Use the scale tool.\nWill scale the selected elements as the cursor is dragged.\n(Animation Preview only.)"
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_TOOLS_CROP "Use the crop tool.\nWill set the crop of the selected elements as the cursor is dragged.\n(Spritesheet Editor only.)"
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_FILE_NEW "Load a blank .anm2 file to edit."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_FILE_OPEN "Open an existing .anm2 file to edit."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_FILE_SAVE "Save the current .anm2 file."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_FILE_SAVE_AS "Save the current .anm2 file to a location."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_PLAYBACK_IS_LOOP "Toggles the animation playback looping when the time reaches the end."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_WIZARD_EXPORT_FRAMES_TO_PNG "Records the current animation and exports the frames as .pngs.\nLook in the working directory of the program for them."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_FILE_MENU "Opens the file menu, for reading/writing anm2 files."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_PLAYBACK_MENU "Opens the playback menu, for managing playback settings."
 | 
			
		||||
#define STRING_IMGUI_TOOLTIP_WIZARD_MENU "Opens the wizard menu, for handy functions involving the anm2."
 | 
			
		||||
 | 
			
		||||
#define STRING_OPENGL_VERSION "#version 330"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										175
									
								
								src/anm2.cpp
									
									
									
									
									
								
							
							
						
						
									
										175
									
								
								src/anm2.cpp
									
									
									
									
									
								
							@@ -13,7 +13,7 @@ anm2_created_on_set(Anm2* self)
 | 
			
		||||
	currentTime = time(NULL);
 | 
			
		||||
	local = localtime(¤tTime);
 | 
			
		||||
 | 
			
		||||
    strftime(date, ANM2_STRING_MAX, "%d-%B-%Y %I:%M:%S %p", local);
 | 
			
		||||
    strftime(date, ANM2_STRING_MAX, STRING_ANM2_CREATED_ON_FORMAT, local);
 | 
			
		||||
 | 
			
		||||
	strncpy(self->createdOn, date, ANM2_STRING_MAX);
 | 
			
		||||
}
 | 
			
		||||
@@ -306,8 +306,6 @@ anm2_serialize(Anm2* self, const char* path)
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	working_directory_from_path_set(path);
 | 
			
		||||
 | 
			
		||||
	printf(STRING_INFO_ANM2_WRITE, path);
 | 
			
		||||
	strncpy(self->path, path, PATH_MAX - 1);
 | 
			
		||||
	
 | 
			
		||||
@@ -331,6 +329,11 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
 | 
			
		||||
	Anm2Spritesheet* spritesheet = NULL;
 | 
			
		||||
	Anm2Element anm2Element = ANM2_ELEMENT_ANIMATED_ACTOR;
 | 
			
		||||
	Anm2Attribute anm2Attribute =  ANM2_ATTRIBUTE_ID;
 | 
			
		||||
	Anm2Item addItem;
 | 
			
		||||
	Anm2Layer addLayer;
 | 
			
		||||
	Anm2Null addNull;
 | 
			
		||||
	Anm2Event addEvent;
 | 
			
		||||
	Anm2Spritesheet addSpritesheet;
 | 
			
		||||
 | 
			
		||||
	*self = Anm2{};
 | 
			
		||||
 | 
			
		||||
@@ -343,15 +346,20 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resources_textures_free(resources);
 | 
			
		||||
	
 | 
			
		||||
	/* Save old working directory and then use anm2's path as directory */
 | 
			
		||||
	/* (useful for loading textures from anm2 correctly) */
 | 
			
		||||
    std::filesystem::path workingPath = std::filesystem::current_path();
 | 
			
		||||
    std::filesystem::path filePath = path;
 | 
			
		||||
    std::filesystem::path parentPath = filePath.parent_path();
 | 
			
		||||
	std::filesystem::current_path(parentPath);
 | 
			
		||||
	strncpy(self->path, path, PATH_MAX - 1);
 | 
			
		||||
	working_directory_from_path_set(path);
 | 
			
		||||
	
 | 
			
		||||
    xmlRoot = xmlDocument.FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]);
 | 
			
		||||
	xmlElement = xmlRoot;
 | 
			
		||||
 | 
			
		||||
	while (xmlElement)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
		const XMLAttribute* xmlAttribute = NULL;
 | 
			
		||||
		const XMLElement* xmlChild = NULL;
 | 
			
		||||
		s32 id = 0;
 | 
			
		||||
@@ -362,24 +370,16 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
 | 
			
		||||
		switch (anm2Element)
 | 
			
		||||
		{
 | 
			
		||||
			case ANM2_ELEMENT_SPRITESHEET:
 | 
			
		||||
				id = map_next_id_get(self->spritesheets);
 | 
			
		||||
				self->spritesheets[id] = Anm2Spritesheet{};
 | 
			
		||||
				spritesheet = &self->spritesheets[id];
 | 
			
		||||
				spritesheet = &addSpritesheet;
 | 
			
		||||
				break;
 | 
			
		||||
			case ANM2_ELEMENT_LAYER:
 | 
			
		||||
				id = map_next_id_get(self->layers);
 | 
			
		||||
				self->layers[id] = Anm2Layer{};
 | 
			
		||||
				layer = &self->layers[id];
 | 
			
		||||
				layer = &addLayer;
 | 
			
		||||
				break;
 | 
			
		||||
			case ANM2_ELEMENT_NULL:
 | 
			
		||||
				id = map_next_id_get(self->nulls);
 | 
			
		||||
				self->nulls[id] = Anm2Null{};
 | 
			
		||||
				null = &self->nulls[id];
 | 
			
		||||
				null = &addNull;
 | 
			
		||||
				break;
 | 
			
		||||
			case ANM2_ELEMENT_EVENT:
 | 
			
		||||
				id = map_next_id_get(self->events);
 | 
			
		||||
				self->events[id] = Anm2Event{};
 | 
			
		||||
				event = &self->events[id];
 | 
			
		||||
				event = &addEvent;
 | 
			
		||||
				break;
 | 
			
		||||
			case ANM2_ELEMENT_ANIMATION:
 | 
			
		||||
				id = map_next_id_get(self->animations);
 | 
			
		||||
@@ -390,14 +390,8 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
 | 
			
		||||
				item = &animation->rootAnimation;
 | 
			
		||||
				break;
 | 
			
		||||
			case ANM2_ELEMENT_LAYER_ANIMATION:
 | 
			
		||||
				id = map_next_id_get(animation->layerAnimations);
 | 
			
		||||
				animation->layerAnimations[id] = Anm2Item{};
 | 
			
		||||
				item = &animation->layerAnimations[id];
 | 
			
		||||
				break;
 | 
			
		||||
			case ANM2_ELEMENT_NULL_ANIMATION:
 | 
			
		||||
				id = map_next_id_get(animation->nullAnimations);
 | 
			
		||||
				animation->nullAnimations[id] = Anm2Item{};
 | 
			
		||||
				item = &animation->nullAnimations[id];
 | 
			
		||||
				item = &addItem;
 | 
			
		||||
				break;
 | 
			
		||||
			case ANM2_ELEMENT_TRIGGERS:
 | 
			
		||||
				item = &animation->triggers;
 | 
			
		||||
@@ -431,6 +425,40 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
 | 
			
		||||
				case ANM2_ATTRIBUTE_FPS:
 | 
			
		||||
					self->fps = atoi(xmlAttribute->Value());
 | 
			
		||||
					break;
 | 
			
		||||
				case ANM2_ATTRIBUTE_ID:
 | 
			
		||||
					id = atoi(xmlAttribute->Value());
 | 
			
		||||
					switch (anm2Element)
 | 
			
		||||
					{
 | 
			
		||||
						case ANM2_ELEMENT_SPRITESHEET:
 | 
			
		||||
							self->spritesheets[id] = addSpritesheet;
 | 
			
		||||
							spritesheet = &self->spritesheets[id];
 | 
			
		||||
							break;
 | 
			
		||||
						case ANM2_ELEMENT_LAYER:
 | 
			
		||||
							self->layers[id] = addLayer;
 | 
			
		||||
							layer = &self->layers[id];
 | 
			
		||||
							break;
 | 
			
		||||
						case ANM2_ELEMENT_NULL:
 | 
			
		||||
							self->nulls[id] = addNull;
 | 
			
		||||
							null = &self->nulls[id];
 | 
			
		||||
							break;
 | 
			
		||||
						case ANM2_ELEMENT_EVENT:
 | 
			
		||||
							self->events[id] = addEvent;
 | 
			
		||||
							event = &self->events[id];
 | 
			
		||||
							break;
 | 
			
		||||
						default:
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				case ANM2_ATTRIBUTE_LAYER_ID:
 | 
			
		||||
					id = atoi(xmlAttribute->Value());
 | 
			
		||||
					animation->layerAnimations[id] = addItem;
 | 
			
		||||
					item = &animation->layerAnimations[id];
 | 
			
		||||
					break;
 | 
			
		||||
				case ANM2_ATTRIBUTE_NULL_ID:
 | 
			
		||||
					id = atoi(xmlAttribute->Value());
 | 
			
		||||
					animation->nullAnimations[id] = addItem;
 | 
			
		||||
					item = &animation->nullAnimations[id];
 | 
			
		||||
					break;
 | 
			
		||||
				case ANM2_ATTRIBUTE_PATH:
 | 
			
		||||
					strncpy(spritesheet->path, xmlAttribute->Value(), PATH_MAX - 1);
 | 
			
		||||
					break;
 | 
			
		||||
@@ -589,6 +617,9 @@ anm2_deserialize(Anm2* self, Resources* resources, const char* path)
 | 
			
		||||
 | 
			
		||||
	printf(STRING_INFO_ANM2_READ, path);
 | 
			
		||||
 | 
			
		||||
	/* Set working directory back to old */
 | 
			
		||||
	std::filesystem::current_path(workingPath);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -694,9 +725,9 @@ anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const char* path
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Anm2Animation*
 | 
			
		||||
anm2_animation_from_id(Anm2* self, s32 animationID)
 | 
			
		||||
anm2_animation_from_reference(Anm2* self, Anm2Reference* reference)
 | 
			
		||||
{
 | 
			
		||||
	auto it = self->animations.find(animationID);
 | 
			
		||||
	auto it = self->animations.find(reference->animationID);
 | 
			
		||||
	if (it == self->animations.end())
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return &it->second;
 | 
			
		||||
@@ -704,27 +735,27 @@ anm2_animation_from_id(Anm2* self, s32 animationID)
 | 
			
		||||
 | 
			
		||||
/* Returns the item from a anm2 reference. */
 | 
			
		||||
Anm2Item*
 | 
			
		||||
anm2_item_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID)
 | 
			
		||||
anm2_item_from_reference(Anm2* self, Anm2Reference* reference)
 | 
			
		||||
{
 | 
			
		||||
	Anm2Animation* animation = anm2_animation_from_id(self, animationID);
 | 
			
		||||
	Anm2Animation* animation = anm2_animation_from_reference(self, reference);
 | 
			
		||||
	
 | 
			
		||||
	if (!animation)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	switch (reference->type)
 | 
			
		||||
	switch (reference->itemType)
 | 
			
		||||
	{
 | 
			
		||||
		case ANM2_ROOT:
 | 
			
		||||
			return &animation->rootAnimation;
 | 
			
		||||
		case ANM2_LAYER:
 | 
			
		||||
		{
 | 
			
		||||
			auto it = animation->layerAnimations.find(reference->id);
 | 
			
		||||
			auto it = animation->layerAnimations.find(reference->itemID);
 | 
			
		||||
			if (it == animation->layerAnimations.end())
 | 
			
		||||
				return NULL;
 | 
			
		||||
			return &it->second;
 | 
			
		||||
		}
 | 
			
		||||
		case ANM2_NULL:
 | 
			
		||||
		{
 | 
			
		||||
			auto it = animation->nullAnimations.find(reference->id);
 | 
			
		||||
			auto it = animation->nullAnimations.find(reference->itemID);
 | 
			
		||||
			if (it == animation->nullAnimations.end())
 | 
			
		||||
				return NULL;
 | 
			
		||||
			return &it->second;
 | 
			
		||||
@@ -738,31 +769,31 @@ anm2_item_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID)
 | 
			
		||||
 | 
			
		||||
/* Gets the frame from the reference's properties */
 | 
			
		||||
Anm2Frame*
 | 
			
		||||
anm2_frame_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID)
 | 
			
		||||
anm2_frame_from_reference(Anm2* self, Anm2Reference* reference)
 | 
			
		||||
{
 | 
			
		||||
	Anm2Item* item = anm2_item_from_reference(self, reference, animationID);
 | 
			
		||||
	Anm2Item* item = anm2_item_from_reference(self, reference);
 | 
			
		||||
 | 
			
		||||
	if (!item)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (reference->index < 0 || reference->index >= (s32)item->frames.size())
 | 
			
		||||
	if (reference->frameIndex < 0 || reference->frameIndex >= (s32)item->frames.size())
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	return &item->frames[reference->index];
 | 
			
		||||
	return &item->frames[reference->frameIndex];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Creates/fetches a frame from a given time. */
 | 
			
		||||
/* Returns true/false if frame will be valid or not. */
 | 
			
		||||
void 
 | 
			
		||||
anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, s32 animationID, f32 time)
 | 
			
		||||
anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time)
 | 
			
		||||
{
 | 
			
		||||
	Anm2Animation* animation = anm2_animation_from_id(self, animationID);
 | 
			
		||||
	Anm2Animation* animation = anm2_animation_from_reference(self, &reference);
 | 
			
		||||
 | 
			
		||||
	/* Out of range */
 | 
			
		||||
	if (time < 0 || time > animation->frameNum)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	Anm2Item* item = anm2_item_from_reference(self, &reference, animationID);
 | 
			
		||||
	Anm2Item* item = anm2_item_from_reference(self, &reference);
 | 
			
		||||
 | 
			
		||||
	if (!item)
 | 
			
		||||
		return;
 | 
			
		||||
@@ -803,12 +834,45 @@ anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, s32
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns the current length of the animation, from the used frames. */
 | 
			
		||||
s32
 | 
			
		||||
anm2_animation_length_get(Anm2* self, s32 animationID)
 | 
			
		||||
{
 | 
			
		||||
	/* Get valid animation */
 | 
			
		||||
	auto it = self->animations.find(animationID);
 | 
			
		||||
	if (it == self->animations.end())
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	Anm2Animation* animation = &it->second;
 | 
			
		||||
	s32 delayHighest = 0;
 | 
			
		||||
 | 
			
		||||
	/* Root frames */
 | 
			
		||||
	for (auto & frame : animation->rootAnimation.frames)
 | 
			
		||||
		delayHighest = std::max(delayHighest, frame.delay);
 | 
			
		||||
 | 
			
		||||
	/* Layer frames */
 | 
			
		||||
	for (auto & [id, item] : animation->layerAnimations)
 | 
			
		||||
		for (auto & frame : item.frames)
 | 
			
		||||
			delayHighest = std::max(delayHighest, frame.delay);
 | 
			
		||||
 | 
			
		||||
	/* Null frames */
 | 
			
		||||
	for (auto & [id, item] : animation->nullAnimations)
 | 
			
		||||
		for (auto & frame : item.frames)
 | 
			
		||||
			delayHighest = std::max(delayHighest, frame.delay);
 | 
			
		||||
 | 
			
		||||
	/* Trigger frames (assuming this is from `animation->triggers.frames`) */
 | 
			
		||||
	for (auto & trigger : animation->triggers.frames)
 | 
			
		||||
		delayHighest = std::max(delayHighest, trigger.atFrame);
 | 
			
		||||
 | 
			
		||||
	return delayHighest;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Will try adding a frame to the anm2 given the specified reference */
 | 
			
		||||
Anm2Frame*
 | 
			
		||||
anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
 | 
			
		||||
anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time)
 | 
			
		||||
{
 | 
			
		||||
	Anm2Animation* animation = anm2_animation_from_id(self, animationID);
 | 
			
		||||
	Anm2Item* item = anm2_item_from_reference(self, reference, animationID);
 | 
			
		||||
	Anm2Animation* animation = anm2_animation_from_reference(self, reference);
 | 
			
		||||
	Anm2Item* item = anm2_item_from_reference(self, reference);
 | 
			
		||||
	
 | 
			
		||||
	if (!animation || !item)
 | 
			
		||||
		return NULL;
 | 
			
		||||
@@ -818,7 +882,7 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
 | 
			
		||||
		Anm2Frame frame = Anm2Frame{};
 | 
			
		||||
		s32 index = -1;
 | 
			
		||||
 | 
			
		||||
		if (reference->type == ANM2_TRIGGERS)
 | 
			
		||||
		if (reference->itemType == ANM2_TRIGGERS)
 | 
			
		||||
		{
 | 
			
		||||
			/* don't add redudant triggers (i.e. at same time) */
 | 
			
		||||
			for (auto & frameCheck : item->frames)
 | 
			
		||||
@@ -832,7 +896,6 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			s32 delay = 0;
 | 
			
		||||
			s32 frameDelayCount = 0;
 | 
			
		||||
 | 
			
		||||
			/* Add up all delay to see where this new frame might lie */
 | 
			
		||||
@@ -844,7 +907,7 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
 | 
			
		||||
				return NULL;
 | 
			
		||||
 | 
			
		||||
			/* Will insert next to frame if frame exists */
 | 
			
		||||
			Anm2Frame* checkFrame = anm2_frame_from_reference(self, reference, animationID);
 | 
			
		||||
			Anm2Frame* checkFrame = anm2_frame_from_reference(self, reference);
 | 
			
		||||
 | 
			
		||||
			if (checkFrame)
 | 
			
		||||
			{
 | 
			
		||||
@@ -852,7 +915,7 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
 | 
			
		||||
				if (frameDelayCount + checkFrame->delay > animation->frameNum)
 | 
			
		||||
					frame.delay = animation->frameNum - frameDelayCount;
 | 
			
		||||
 | 
			
		||||
				index = reference->index + 1;
 | 
			
		||||
				index = reference->frameIndex + 1;
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
				index = (s32)item->frames.size();
 | 
			
		||||
@@ -865,3 +928,25 @@ anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time)
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Clears anm2 reference */
 | 
			
		||||
void
 | 
			
		||||
anm2_reference_clear(Anm2Reference* self)
 | 
			
		||||
{
 | 
			
		||||
	*self = Anm2Reference{};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Clears anm2 item reference */
 | 
			
		||||
void
 | 
			
		||||
anm2_reference_item_clear(Anm2Reference* self)
 | 
			
		||||
{
 | 
			
		||||
	self->itemType = ANM2_NONE;
 | 
			
		||||
	self->itemID = -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Clears anm2 reference */
 | 
			
		||||
void
 | 
			
		||||
anm2_reference_frame_clear(Anm2Reference* self)
 | 
			
		||||
{
 | 
			
		||||
	self->frameIndex = -1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								src/anm2.h
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/anm2.h
									
									
									
									
									
								
							@@ -192,9 +192,10 @@ struct Anm2
 | 
			
		||||
 | 
			
		||||
struct Anm2Reference
 | 
			
		||||
{
 | 
			
		||||
    Anm2Type type = ANM2_NONE;
 | 
			
		||||
    s32 id = -1;
 | 
			
		||||
    s32 index = -1;
 | 
			
		||||
    Anm2Type itemType = ANM2_NONE;
 | 
			
		||||
    s32 animationID = -1;
 | 
			
		||||
    s32 itemID = -1;
 | 
			
		||||
    s32 frameIndex = -1;
 | 
			
		||||
 | 
			
		||||
    auto operator<=>(const Anm2Reference&) const = default; 
 | 
			
		||||
};
 | 
			
		||||
@@ -210,8 +211,12 @@ void anm2_created_on_set(Anm2* self);
 | 
			
		||||
s32 anm2_animation_add(Anm2* self);
 | 
			
		||||
void anm2_animation_remove(Anm2* self, s32 id);
 | 
			
		||||
void anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const char* path, s32 id);
 | 
			
		||||
Anm2Animation* anm2_animation_from_id(Anm2* self, s32 animationID);
 | 
			
		||||
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID);
 | 
			
		||||
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference, s32 animationID);
 | 
			
		||||
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 animationID, s32 time);
 | 
			
		||||
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, s32 animationID, f32 time);
 | 
			
		||||
Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference);
 | 
			
		||||
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference);
 | 
			
		||||
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference);
 | 
			
		||||
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Reference* reference, s32 time);
 | 
			
		||||
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time);
 | 
			
		||||
void anm2_reference_clear(Anm2Reference* self);
 | 
			
		||||
void anm2_reference_item_clear(Anm2Reference* self);
 | 
			
		||||
void anm2_reference_frame_clear(Anm2Reference* self);
 | 
			
		||||
s32 anm2_animation_length_get(Anm2* self, s32 animationID);
 | 
			
		||||
@@ -66,13 +66,6 @@ dialog_tick(Dialog* self)
 | 
			
		||||
	{
 | 
			
		||||
		Texture texture;
 | 
			
		||||
		s32 id;
 | 
			
		||||
		char relativePath[PATH_MAX];
 | 
			
		||||
 | 
			
		||||
		/* Get the relative path */
 | 
			
		||||
		std::filesystem::path baseDirectory = std::filesystem::current_path();
 | 
			
		||||
		std::filesystem::path relativePathString = std::filesystem::relative(self->path, baseDirectory);
 | 
			
		||||
		
 | 
			
		||||
		strncpy(relativePath, relativePathString.c_str(), PATH_MAX - 1);
 | 
			
		||||
		
 | 
			
		||||
		switch (self->type)
 | 
			
		||||
		{
 | 
			
		||||
@@ -80,21 +73,21 @@ dialog_tick(Dialog* self)
 | 
			
		||||
				*self->reference = Anm2Reference{};
 | 
			
		||||
				resources_textures_free(self->resources);
 | 
			
		||||
				anm2_deserialize(self->anm2, self->resources, self->path);
 | 
			
		||||
				window_title_from_anm2_set(self->window, self->anm2);
 | 
			
		||||
				window_title_from_path_set(self->window, self->path);
 | 
			
		||||
				break;
 | 
			
		||||
			case DIALOG_ANM2_SAVE:
 | 
			
		||||
				anm2_serialize(self->anm2, relativePath);
 | 
			
		||||
				window_title_from_anm2_set(self->window, self->anm2);
 | 
			
		||||
				anm2_serialize(self->anm2, self->path);
 | 
			
		||||
				window_title_from_path_set(self->window, self->path);
 | 
			
		||||
				break;
 | 
			
		||||
			case DIALOG_PNG_OPEN:
 | 
			
		||||
				id = map_next_id_get(self->resources->textures);
 | 
			
		||||
				self->anm2->spritesheets[id] = Anm2Spritesheet{};
 | 
			
		||||
				strncpy(self->anm2->spritesheets[id].path, relativePath, PATH_MAX);
 | 
			
		||||
				anm2_spritesheet_texture_load(self->anm2, self->resources, relativePath, id);
 | 
			
		||||
				strncpy(self->anm2->spritesheets[id].path, self->path, PATH_MAX);
 | 
			
		||||
				anm2_spritesheet_texture_load(self->anm2, self->resources, self->path, id);
 | 
			
		||||
				break;
 | 
			
		||||
			case DIALOG_PNG_REPLACE:
 | 
			
		||||
				strncpy(self->anm2->spritesheets[self->replaceID].path, relativePath, PATH_MAX);
 | 
			
		||||
				anm2_spritesheet_texture_load(self->anm2, self->resources, relativePath, self->replaceID);
 | 
			
		||||
				strncpy(self->anm2->spritesheets[self->replaceID].path, self->path, PATH_MAX);
 | 
			
		||||
				anm2_spritesheet_texture_load(self->anm2, self->resources, self->path, self->replaceID);
 | 
			
		||||
				self->replaceID = -1;
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
 
 | 
			
		||||
@@ -45,21 +45,10 @@ _editor_grid_set(Editor* self)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
editor_init
 | 
			
		||||
(
 | 
			
		||||
    Editor* self, 
 | 
			
		||||
    Anm2* anm2, 
 | 
			
		||||
    Anm2Reference* reference, 
 | 
			
		||||
    s32* animationID, 
 | 
			
		||||
    s32* spritesheetID, 
 | 
			
		||||
    Resources* resources, 
 | 
			
		||||
    Settings* settings
 | 
			
		||||
)
 | 
			
		||||
editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings)
 | 
			
		||||
{
 | 
			
		||||
    self->anm2 = anm2;
 | 
			
		||||
    self->reference = reference;
 | 
			
		||||
    self->animationID = animationID;
 | 
			
		||||
    self->spritesheetID = spritesheetID;
 | 
			
		||||
    self->resources = resources;
 | 
			
		||||
    self->settings = settings;
 | 
			
		||||
 | 
			
		||||
@@ -153,9 +142,12 @@ editor_draw(Editor* self)
 | 
			
		||||
 | 
			
		||||
    glClear(GL_COLOR_BUFFER_BIT);
 | 
			
		||||
 | 
			
		||||
    if (*self->spritesheetID > -1)
 | 
			
		||||
    s32 spritesheetID = self->reference->itemType == ANM2_LAYER ? 
 | 
			
		||||
    self->anm2->layers[self->reference->itemID].spritesheetID : -1;
 | 
			
		||||
 | 
			
		||||
    if (spritesheetID > -1)
 | 
			
		||||
    {
 | 
			
		||||
        Texture* texture = &self->resources->textures[*self->spritesheetID];
 | 
			
		||||
        Texture* texture = &self->resources->textures[spritesheetID];
 | 
			
		||||
 | 
			
		||||
        glm::mat4 spritesheetTransform = editorTransform;
 | 
			
		||||
        glm::vec2 ndcScale = glm::vec2(texture->size.x, texture->size.y) / (EDITOR_SIZE * 0.5f);
 | 
			
		||||
@@ -199,10 +191,10 @@ editor_draw(Editor* self)
 | 
			
		||||
            glUseProgram(0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference, *self->animationID);
 | 
			
		||||
        Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference);
 | 
			
		||||
 | 
			
		||||
        /* Draw the layer frame's crop and pivot */
 | 
			
		||||
        if (frame && self->reference->type == ANM2_LAYER)
 | 
			
		||||
        if (frame)
 | 
			
		||||
        {
 | 
			
		||||
            /* Rect */
 | 
			
		||||
            glm::mat4 rectTransform = editorTransform;  
 | 
			
		||||
@@ -265,6 +257,12 @@ editor_draw(Editor* self)
 | 
			
		||||
        static ivec2 previousGridSize = {-1, -1};
 | 
			
		||||
        static ivec2 previousGridOffset = {-1, -1};
 | 
			
		||||
        static s32 gridVertexCount = -1;
 | 
			
		||||
 | 
			
		||||
        glm::mat4 gridTransform = editorTransform;
 | 
			
		||||
        glm::vec2 gridNDCPos = (EDITOR_SIZE / 2.0f) / (EDITOR_SIZE / 2.0f);
 | 
			
		||||
 | 
			
		||||
        gridTransform = glm::translate(gridTransform, glm::vec3(gridNDCPos, 0.0f));
 | 
			
		||||
 | 
			
		||||
        ivec2 gridSize = ivec2(self->settings->editorGridSizeX, self->settings->editorGridSizeY);
 | 
			
		||||
        ivec2 gridOffset = ivec2(self->settings->editorGridOffsetX, self->settings->editorGridOffsetY);
 | 
			
		||||
 | 
			
		||||
@@ -277,7 +275,7 @@ editor_draw(Editor* self)
 | 
			
		||||
 | 
			
		||||
        glUseProgram(shaderLine);
 | 
			
		||||
        glBindVertexArray(self->gridVAO);
 | 
			
		||||
        glUniformMatrix4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, (f32*)value_ptr(editorTransform));
 | 
			
		||||
        glUniformMatrix4fv(glGetUniformLocation(shaderLine, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, (f32*)value_ptr(gridTransform));
 | 
			
		||||
        
 | 
			
		||||
        glUniform4f
 | 
			
		||||
        (
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								src/editor.h
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/editor.h
									
									
									
									
									
								
							@@ -22,8 +22,6 @@ struct Editor
 | 
			
		||||
{
 | 
			
		||||
    Anm2* anm2 = NULL;
 | 
			
		||||
    Anm2Reference* reference = NULL;
 | 
			
		||||
    s32* animationID = NULL;
 | 
			
		||||
    s32* spritesheetID = NULL;
 | 
			
		||||
    Resources* resources = NULL;
 | 
			
		||||
    Settings* settings = NULL;
 | 
			
		||||
    GLuint fbo;
 | 
			
		||||
@@ -38,16 +36,7 @@ struct Editor
 | 
			
		||||
    GLuint borderVBO;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void editor_init
 | 
			
		||||
(
 | 
			
		||||
    Editor* self, 
 | 
			
		||||
    Anm2* anm2, 
 | 
			
		||||
    Anm2Reference* reference, 
 | 
			
		||||
    s32* animationID, 
 | 
			
		||||
    s32* spritesheetID, 
 | 
			
		||||
    Resources* resources, 
 | 
			
		||||
    Settings* settings
 | 
			
		||||
);
 | 
			
		||||
void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources,  Settings* settings);
 | 
			
		||||
 | 
			
		||||
void editor_draw(Editor* self);
 | 
			
		||||
void editor_tick(Editor* self);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										434
									
								
								src/imgui.cpp
									
									
									
									
									
								
							
							
						
						
									
										434
									
								
								src/imgui.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -37,6 +37,7 @@
 | 
			
		||||
static const vec2 IMGUI_TASKBAR_MARGINS = {8, 4};
 | 
			
		||||
static const vec2 IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS = {1, 1};
 | 
			
		||||
 | 
			
		||||
static const ImVec2 IMGUI_RECORD_TOOLTIP_OFFSET = {16, 16};
 | 
			
		||||
static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE = {1280, 105};
 | 
			
		||||
static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE = {200, 85};
 | 
			
		||||
static const ImVec2 IMGUI_ANIMATION_PREVIEW_POSITION = {8, 135};
 | 
			
		||||
@@ -95,8 +96,7 @@ struct Imgui
 | 
			
		||||
    Input* input = NULL;
 | 
			
		||||
    Anm2* anm2 = NULL;
 | 
			
		||||
    Anm2Reference* reference = NULL;
 | 
			
		||||
    s32* animationID = NULL;
 | 
			
		||||
    s32* spritesheetID = NULL;
 | 
			
		||||
    f32* time = NULL;
 | 
			
		||||
    Editor* editor = NULL;
 | 
			
		||||
    Preview* preview = NULL;
 | 
			
		||||
    Settings* settings = NULL;
 | 
			
		||||
@@ -117,8 +117,7 @@ imgui_init
 | 
			
		||||
    Input* input,
 | 
			
		||||
    Anm2* anm2,
 | 
			
		||||
    Anm2Reference* reference,
 | 
			
		||||
    s32* animationID,
 | 
			
		||||
    s32* spritesheetID,
 | 
			
		||||
    f32* time,
 | 
			
		||||
    Editor* editor,
 | 
			
		||||
    Preview* preview,
 | 
			
		||||
    Settings* settings,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ static void
 | 
			
		||||
_mouse_tick(Mouse* self)
 | 
			
		||||
{
 | 
			
		||||
    s32 state;
 | 
			
		||||
    SDL_Event event;
 | 
			
		||||
 | 
			
		||||
    memcpy(&self->previous, &self->current, sizeof(bool) * MOUSE_COUNT);
 | 
			
		||||
    memset(&self->current, '\0', sizeof(bool) * MOUSE_COUNT);
 | 
			
		||||
 
 | 
			
		||||
@@ -233,7 +233,7 @@ enum KeyType
 | 
			
		||||
        KEY_RGUI = 231
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define INPUT_COUNT (INPUT_UNDO + 1)
 | 
			
		||||
#define INPUT_COUNT (INPUT_REDO + 1)
 | 
			
		||||
enum InputType
 | 
			
		||||
{
 | 
			
		||||
    INPUT_PAN,
 | 
			
		||||
@@ -248,7 +248,8 @@ enum InputType
 | 
			
		||||
    INPUT_ROTATE_RIGHT,
 | 
			
		||||
    INPUT_ZOOM_IN,
 | 
			
		||||
    INPUT_ZOOM_OUT,
 | 
			
		||||
    INPUT_UNDO
 | 
			
		||||
    INPUT_UNDO,
 | 
			
		||||
    INPUT_REDO
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const KeyType INPUT_KEYS[INPUT_COUNT]
 | 
			
		||||
@@ -265,7 +266,8 @@ static const KeyType INPUT_KEYS[INPUT_COUNT]
 | 
			
		||||
    KEY_W,
 | 
			
		||||
    KEY_1,
 | 
			
		||||
    KEY_2,
 | 
			
		||||
    KEY_Z
 | 
			
		||||
    KEY_Z,
 | 
			
		||||
    KEY_Y
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Keyboard
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										114
									
								
								src/preview.cpp
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								src/preview.cpp
									
									
									
									
									
								
							@@ -60,11 +60,11 @@ _preview_grid_set(Preview* self)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, s32* animationID, Resources* resources, Settings* settings)
 | 
			
		||||
preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, f32* time, Resources* resources, Settings* settings)
 | 
			
		||||
{
 | 
			
		||||
    self->anm2 = anm2;
 | 
			
		||||
    self->reference = reference;
 | 
			
		||||
    self->animationID = animationID;
 | 
			
		||||
    self->time = time;
 | 
			
		||||
    self->resources = resources;
 | 
			
		||||
    self->settings = settings;
 | 
			
		||||
 | 
			
		||||
@@ -140,19 +140,25 @@ preview_tick(Preview* self)
 | 
			
		||||
{
 | 
			
		||||
    self->settings->previewZoom = CLAMP(self->settings->previewZoom, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX);
 | 
			
		||||
 
 | 
			
		||||
    Anm2Animation* animation = anm2_animation_from_id(self->anm2, *self->animationID);
 | 
			
		||||
    Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
 | 
			
		||||
 | 
			
		||||
    if (animation)
 | 
			
		||||
    {
 | 
			
		||||
        if (self->isPlaying)
 | 
			
		||||
        {  
 | 
			
		||||
            self->time += (f32)self->anm2->fps / TICK_RATE;
 | 
			
		||||
            *self->time += (f32)self->anm2->fps / TICK_RATE;
 | 
			
		||||
 | 
			
		||||
            if (self->time >= (f32)animation->frameNum - 1)
 | 
			
		||||
                self->time = 0.0f;
 | 
			
		||||
        }
 | 
			
		||||
            if (*self->time >= (f32)animation->frameNum - 1)
 | 
			
		||||
            {
 | 
			
		||||
                if (self->settings->playbackIsLoop && !self->isRecording)
 | 
			
		||||
                    *self->time = 0.0f;
 | 
			
		||||
                else
 | 
			
		||||
            self->time = CLAMP(self->time, 0.0f, (f32)animation->frameNum);
 | 
			
		||||
                    self->isPlaying = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (!self->isPlaying)
 | 
			
		||||
            *self->time = CLAMP(*self->time, 0.0f, (f32)animation->frameNum - 1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -161,6 +167,9 @@ preview_draw(Preview* self)
 | 
			
		||||
{
 | 
			
		||||
    GLuint shaderLine = self->resources->shaders[SHADER_LINE];
 | 
			
		||||
    GLuint shaderTexture = self->resources->shaders[SHADER_TEXTURE];
 | 
			
		||||
    static bool isRecordThisFrame = false;
 | 
			
		||||
    static f32 recordFrameTimeNext = 0.0f;
 | 
			
		||||
    static s32 recordFrameIndex = 0;
 | 
			
		||||
 | 
			
		||||
    f32 zoomFactor = self->settings->previewZoom / 100.0f;
 | 
			
		||||
    glm::vec2 ndcPan = glm::vec2(-self->settings->previewPanX / (PREVIEW_SIZE.x / 2.0f), -self->settings->previewPanY / (PREVIEW_SIZE.y / 2.0f));
 | 
			
		||||
@@ -239,13 +248,14 @@ preview_draw(Preview* self)
 | 
			
		||||
        glUseProgram(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Anm2Animation* animation = anm2_animation_from_id(self->anm2, *self->animationID);
 | 
			
		||||
    Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
 | 
			
		||||
    
 | 
			
		||||
    /* Animation */
 | 
			
		||||
    if (animation)
 | 
			
		||||
    {
 | 
			
		||||
        Anm2Frame rootFrame;
 | 
			
		||||
        anm2_frame_from_time(self->anm2, &rootFrame, Anm2Reference{ANM2_ROOT, 0, 0}, *self->animationID, self->time);
 | 
			
		||||
        Anm2Frame frame;
 | 
			
		||||
        anm2_frame_from_time(self->anm2, &rootFrame, Anm2Reference{ANM2_ROOT, self->reference->animationID, 0, 0}, *self->time);
 | 
			
		||||
 | 
			
		||||
        /* Layers */
 | 
			
		||||
        for (auto & [id, layerAnimation] : animation->layerAnimations)
 | 
			
		||||
@@ -253,9 +263,7 @@ preview_draw(Preview* self)
 | 
			
		||||
            if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            Anm2Frame frame;
 | 
			
		||||
 | 
			
		||||
            anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, id, 0}, *self->animationID, self->time);
 | 
			
		||||
            anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, self->reference->animationID, id, 0}, *self->time);
 | 
			
		||||
 | 
			
		||||
            if (!frame.isVisible)
 | 
			
		||||
                continue;
 | 
			
		||||
@@ -351,9 +359,7 @@ preview_draw(Preview* self)
 | 
			
		||||
                if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
 | 
			
		||||
                    continue;
 | 
			
		||||
                
 | 
			
		||||
                Anm2Frame frame;
 | 
			
		||||
 | 
			
		||||
                anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, id, 0}, *self->animationID, self->time);
 | 
			
		||||
                anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_LAYER, self->reference->animationID, id, 0}, *self->time);
 | 
			
		||||
 | 
			
		||||
                if (!frame.isVisible)
 | 
			
		||||
                    continue;
 | 
			
		||||
@@ -362,7 +368,7 @@ preview_draw(Preview* self)
 | 
			
		||||
                    
 | 
			
		||||
                glm::vec2 position = self->settings->previewIsRootTransform ? (frame.position + rootFrame.position) : frame.position;
 | 
			
		||||
 | 
			
		||||
                glm::vec2 ndcPos = (position - (PREVIEW_PIVOT_SIZE / 2.0f)) / (PREVIEW_SIZE / 2.0f);
 | 
			
		||||
                glm::vec2 ndcPos = position /(PREVIEW_SIZE / 2.0f);
 | 
			
		||||
                glm::vec2 ndcScale = PREVIEW_PIVOT_SIZE / (PREVIEW_SIZE / 2.0f);
 | 
			
		||||
 | 
			
		||||
                pivotTransform = glm::translate(pivotTransform, glm::vec3(ndcPos, 0.0f));
 | 
			
		||||
@@ -400,9 +406,7 @@ preview_draw(Preview* self)
 | 
			
		||||
            if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            Anm2Frame frame;
 | 
			
		||||
            
 | 
			
		||||
            anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_NULL, id, 0}, *self->animationID, self->time);
 | 
			
		||||
            anm2_frame_from_time(self->anm2, &frame, Anm2Reference{ANM2_NULL, id, 0}, *self->time);
 | 
			
		||||
 | 
			
		||||
            if (!frame.isVisible)
 | 
			
		||||
                continue;
 | 
			
		||||
@@ -472,6 +476,76 @@ preview_draw(Preview* self)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (self->isRecording && animation)
 | 
			
		||||
    {
 | 
			
		||||
        if (recordFrameIndex == 0)
 | 
			
		||||
        {
 | 
			
		||||
            if 
 | 
			
		||||
            (
 | 
			
		||||
                std::filesystem::exists(STRING_PREVIEW_FRAMES_DIRECTORY) && 
 | 
			
		||||
                std::filesystem::is_directory(STRING_PREVIEW_FRAMES_DIRECTORY)) 
 | 
			
		||||
            {
 | 
			
		||||
                for (const auto & entry : std::filesystem::directory_iterator(STRING_PREVIEW_FRAMES_DIRECTORY)) 
 | 
			
		||||
                    std::filesystem::remove(entry);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                std::filesystem::create_directories(STRING_PREVIEW_FRAMES_DIRECTORY); 
 | 
			
		||||
            self->isPlaying = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isRecordThisFrame)
 | 
			
		||||
        {
 | 
			
		||||
            size_t frameSize = (self->recordSize.x * self->recordSize.y * 4);
 | 
			
		||||
            u8* frame = (u8*)malloc(frameSize);
 | 
			
		||||
            memset(frame, '\0',frameSize);
 | 
			
		||||
            char path[PATH_MAX];
 | 
			
		||||
            vec2 position = 
 | 
			
		||||
            {
 | 
			
		||||
                self->settings->previewPanX - (PREVIEW_SIZE.x / 2.0f) + (self->recordSize.x / 2.0f),
 | 
			
		||||
                self->settings->previewPanY - (PREVIEW_SIZE.y / 2.0f) + (self->recordSize.y / 2.0f)
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            memset(path, '\0', PATH_MAX);
 | 
			
		||||
 | 
			
		||||
            snprintf(path, PATH_MAX, STRING_PREVIEW_FRAMES_FORMAT, STRING_PREVIEW_FRAMES_DIRECTORY, recordFrameIndex);
 | 
			
		||||
 | 
			
		||||
            glReadBuffer(GL_FRONT);
 | 
			
		||||
            glReadPixels
 | 
			
		||||
            (
 | 
			
		||||
                (s32)position.x,
 | 
			
		||||
                (s32)position.y,
 | 
			
		||||
                self->recordSize.x, 
 | 
			
		||||
                self->recordSize.y, 
 | 
			
		||||
                GL_RGBA, 
 | 
			
		||||
                GL_UNSIGNED_BYTE, 
 | 
			
		||||
                frame
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            texture_from_data_write(path, frame, self->recordSize.x, self->recordSize.y);
 | 
			
		||||
 | 
			
		||||
            free(frame);
 | 
			
		||||
 | 
			
		||||
            isRecordThisFrame = false;
 | 
			
		||||
            recordFrameIndex++;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (*self->time >= (f32)animation->frameNum - 1)
 | 
			
		||||
            {
 | 
			
		||||
                self->isRecording = false;
 | 
			
		||||
                self->isPlaying = false;
 | 
			
		||||
                recordFrameIndex = 0;
 | 
			
		||||
                recordFrameTimeNext = 0;
 | 
			
		||||
                *self->time = 0.0f;
 | 
			
		||||
            }
 | 
			
		||||
            else if (*self->time >= recordFrameTimeNext)
 | 
			
		||||
            {
 | 
			
		||||
                isRecordThisFrame = true;
 | 
			
		||||
                recordFrameTimeNext = *self->time + (f32)self->anm2->fps / TICK_RATE;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    glBindFramebuffer(GL_FRAMEBUFFER, 0); 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,10 +39,10 @@ struct Preview
 | 
			
		||||
{
 | 
			
		||||
    Anm2* anm2 = NULL;
 | 
			
		||||
    Anm2Reference* reference = NULL;
 | 
			
		||||
    f32* time = NULL;
 | 
			
		||||
    Input* input = NULL;
 | 
			
		||||
    Resources* resources = NULL;
 | 
			
		||||
    Settings* settings = NULL;
 | 
			
		||||
    s32* animationID = NULL;
 | 
			
		||||
    GLuint axisVAO;
 | 
			
		||||
    GLuint axisVBO;
 | 
			
		||||
    GLuint fbo;
 | 
			
		||||
@@ -56,10 +56,12 @@ struct Preview
 | 
			
		||||
    GLuint textureVAO;
 | 
			
		||||
    GLuint textureVBO;
 | 
			
		||||
    bool isPlaying = false;
 | 
			
		||||
    f32 time = 0;
 | 
			
		||||
    bool isRecording = false;
 | 
			
		||||
    vec2 recordSize = {0.0f, 0.0f};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, s32* animationID, Resources* resources, Settings* settings);
 | 
			
		||||
void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, f32* time, Resources* resources, Settings* settings);
 | 
			
		||||
void preview_draw(Preview* self);
 | 
			
		||||
void preview_tick(Preview* self);
 | 
			
		||||
void preview_free(Preview* self);
 | 
			
		||||
void preview_record_set(Preview* self);
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "RESOURCES.h"
 | 
			
		||||
#include "PACKED.h"
 | 
			
		||||
#include "texture.h"
 | 
			
		||||
#include "shader.h"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ enum SettingsItem
 | 
			
		||||
{
 | 
			
		||||
    SETTINGS_WINDOW_W,
 | 
			
		||||
    SETTINGS_WINDOW_H,
 | 
			
		||||
    SETTINGS_PLAYBACK_IS_LOOP,
 | 
			
		||||
    SETTINGS_PREVIEW_IS_AXIS,
 | 
			
		||||
    SETTINGS_PREVIEW_IS_GRID,
 | 
			
		||||
    SETTINGS_PREVIEW_IS_ROOT_TRANSFORM,
 | 
			
		||||
@@ -68,13 +69,14 @@ enum SettingsItem
 | 
			
		||||
    SETTINGS_EDITOR_BACKGROUND_COLOR_R,
 | 
			
		||||
    SETTINGS_EDITOR_BACKGROUND_COLOR_G,
 | 
			
		||||
    SETTINGS_EDITOR_BACKGROUND_COLOR_B,
 | 
			
		||||
    SETTINGS_EDITOR_BACKGROUND_COLOR_A
 | 
			
		||||
    SETTINGS_EDITOR_BACKGROUND_COLOR_A,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Settings
 | 
			
		||||
{
 | 
			
		||||
    s32 windowW = 1920;
 | 
			
		||||
    s32 windowH = 1080;
 | 
			
		||||
    bool playbackIsLoop = true;
 | 
			
		||||
    bool previewIsAxis = true;
 | 
			
		||||
    bool previewIsGrid = true;
 | 
			
		||||
    bool previewIsRootTransform = false;
 | 
			
		||||
@@ -122,6 +124,7 @@ static const SettingsEntry SETTINGS_ENTRIES[SETTINGS_COUNT] =
 | 
			
		||||
{
 | 
			
		||||
    {"windowW=", "windowW=%i", SETTINGS_TYPE_INT, offsetof(Settings, windowW)},
 | 
			
		||||
    {"windowH=", "windowH=%i", SETTINGS_TYPE_INT, offsetof(Settings, windowH)},
 | 
			
		||||
    {"playbackIsLoop=", "playbackIsLoop=%i", SETTINGS_TYPE_BOOL, offsetof(Settings, playbackIsLoop)},
 | 
			
		||||
    {"previewIsAxis=", "previewIsAxis=%i", SETTINGS_TYPE_BOOL, offsetof(Settings, previewIsAxis)},
 | 
			
		||||
    {"previewIsGrid=", "previewIsGrid=%i", SETTINGS_TYPE_BOOL, offsetof(Settings, previewIsGrid)},
 | 
			
		||||
    {"previewIsRootTransform=", "previewIsRootTransform=%i", SETTINGS_TYPE_BOOL, offsetof(Settings, previewIsRootTransform)},
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,6 @@ shader_init(GLuint* self, const char* vertex, const char* fragment)
 | 
			
		||||
{
 | 
			
		||||
	GLuint vertexHandle;
 | 
			
		||||
	GLuint fragmentHandle;
 | 
			
		||||
	bool isSuccess;
 | 
			
		||||
 | 
			
		||||
	vertexHandle = glCreateShader(GL_VERTEX_SHADER);
 | 
			
		||||
	fragmentHandle = glCreateShader(GL_FRAGMENT_SHADER);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,90 @@
 | 
			
		||||
#include "snapshots.h"
 | 
			
		||||
 | 
			
		||||
/* TODO */
 | 
			
		||||
/*
 | 
			
		||||
void 
 | 
			
		||||
undo_stack_push(Snapshots* self, Anm2* anm2)
 | 
			
		||||
snapshots_undo_stack_push(Snapshots* self, Snapshot* snapshot)
 | 
			
		||||
{
 | 
			
		||||
    if (self->top >= UNDO_STACK_MAX)
 | 
			
		||||
    if (self->undoStack.top >= SNAPSHOT_STACK_MAX)
 | 
			
		||||
    {
 | 
			
		||||
        memmove(&self->snapshots[0], &self->snapshots[1], sizeof(Anm2) * (UNDO_STACK_MAX - 1));
 | 
			
		||||
        self->top = UNDO_STACK_MAX - 1;
 | 
			
		||||
        memmove(&self->undoStack.snapshots[0], &self->undoStack.snapshots[1], sizeof(Snapshot) * (SNAPSHOT_STACK_MAX - 1));
 | 
			
		||||
        self->undoStack.top = SNAPSHOT_STACK_MAX - 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self->snapshots[self->top++] = *anm2;
 | 
			
		||||
    self->undoStack.snapshots[self->undoStack.top++] = *snapshot;
 | 
			
		||||
    self->redoStack.top = 0; 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
undo_stack_pop(Snapshots* self, Anm2* anm2)
 | 
			
		||||
snapshots_undo_stack_pop(Snapshots* self, Snapshot* snapshot)
 | 
			
		||||
{
 | 
			
		||||
    if (self->top == 0)
 | 
			
		||||
    if (self->undoStack.top == 0)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    *anm2 = self->snapshots[--self->top];
 | 
			
		||||
    *snapshot = self->undoStack.snapshots[--self->undoStack.top];
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
snapshots_redo_stack_push(Snapshots* self, Snapshot* snapshot)
 | 
			
		||||
{
 | 
			
		||||
    if (self->redoStack.top >= SNAPSHOT_STACK_MAX)
 | 
			
		||||
    {
 | 
			
		||||
        memmove(&self->redoStack.snapshots[0], &self->redoStack.snapshots[1], sizeof(Snapshot) * (SNAPSHOT_STACK_MAX - 1));
 | 
			
		||||
        self->redoStack.top = SNAPSHOT_STACK_MAX - 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self->redoStack.snapshots[self->redoStack.top++] = *snapshot;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
snapshots_redo_stack_pop(Snapshots* self, Snapshot* snapshot)
 | 
			
		||||
{
 | 
			
		||||
    if (self->redoStack.top == 0)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    *snapshot = self->redoStack.snapshots[--self->redoStack.top];
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void 
 | 
			
		||||
snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, f32* time, Input* input)
 | 
			
		||||
{
 | 
			
		||||
    self->anm2 = anm2;
 | 
			
		||||
    self->reference = reference;
 | 
			
		||||
    self->time = time;
 | 
			
		||||
    self->input = input;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
snapshots_tick(Snapshots* self)
 | 
			
		||||
{
 | 
			
		||||
    /* Undo */
 | 
			
		||||
    if (key_press(&self->input->keyboard, INPUT_KEYS[INPUT_UNDO]))
 | 
			
		||||
    {
 | 
			
		||||
        Snapshot snapshot;
 | 
			
		||||
        if (snapshots_undo_stack_pop(self, &snapshot))
 | 
			
		||||
        {
 | 
			
		||||
            Snapshot current = {*self->anm2, *self->reference, *self->time};
 | 
			
		||||
            snapshots_redo_stack_push(self, ¤t);
 | 
			
		||||
 | 
			
		||||
            *self->anm2 = snapshot.anm2;
 | 
			
		||||
            *self->reference = snapshot.reference;
 | 
			
		||||
            *self->time = snapshot.time;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Redo */
 | 
			
		||||
    if (key_press(&self->input->keyboard, INPUT_KEYS[INPUT_REDO]))
 | 
			
		||||
    {
 | 
			
		||||
        Snapshot snapshot;
 | 
			
		||||
        if (snapshots_redo_stack_pop(self, &snapshot))
 | 
			
		||||
        {
 | 
			
		||||
            Snapshot current = {*self->anm2, *self->reference, *self->time};
 | 
			
		||||
            snapshots_undo_stack_push(self, ¤t);
 | 
			
		||||
 | 
			
		||||
            *self->anm2 = snapshot.anm2;
 | 
			
		||||
            *self->reference = snapshot.reference;
 | 
			
		||||
            *self->time = snapshot.time;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,22 +1,34 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "anm2.h"
 | 
			
		||||
#include "input.h"
 | 
			
		||||
 | 
			
		||||
#define SNAPSHOT_STACK_MAX 100
 | 
			
		||||
 | 
			
		||||
struct Snapshot
 | 
			
		||||
{
 | 
			
		||||
    Anm2 anm2;
 | 
			
		||||
    Anm2Reference reference;
 | 
			
		||||
    f32 time;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SnapshotStack
 | 
			
		||||
{
 | 
			
		||||
    Anm2 snapshots[SNAPSHOT_STACK_MAX];
 | 
			
		||||
    Snapshot snapshots[SNAPSHOT_STACK_MAX];
 | 
			
		||||
    s32 top = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Snapshots
 | 
			
		||||
{
 | 
			
		||||
    Anm2* anm2 = NULL;
 | 
			
		||||
    Anm2Reference* reference = NULL;
 | 
			
		||||
    f32* time = NULL;
 | 
			
		||||
    Input* input = NULL;
 | 
			
		||||
    SnapshotStack undoStack;
 | 
			
		||||
    SnapshotStack redoStack;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
void undo_stack_push(UndoStack* self, Anm2* anm2);
 | 
			
		||||
bool undo_stack_pop(UndoStack* self, Anm2* anm2);
 | 
			
		||||
*/
 | 
			
		||||
void snapshots_undo_stack_push(Snapshots* self, Snapshot* snapshot);
 | 
			
		||||
bool snapshots_undo_stack_pop(Snapshots* self, Snapshot* snapshot);
 | 
			
		||||
void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, f32* time, Input* input);
 | 
			
		||||
void snapshots_tick(Snapshots* self);
 | 
			
		||||
@@ -35,6 +35,7 @@ _tick(State* state)
 | 
			
		||||
	editor_tick(&state->editor);
 | 
			
		||||
	preview_tick(&state->preview);
 | 
			
		||||
	tool_tick(&state->tool);
 | 
			
		||||
	snapshots_tick(&state->snapshots);
 | 
			
		||||
	dialog_tick(&state->dialog);
 | 
			
		||||
	imgui_tick(&state->imgui);
 | 
			
		||||
}
 | 
			
		||||
@@ -52,13 +53,6 @@ _draw(State* state)
 | 
			
		||||
void
 | 
			
		||||
init(State* state)
 | 
			
		||||
{
 | 
			
		||||
	/* set start working directory */
 | 
			
		||||
    std::filesystem::path startPath = std::filesystem::current_path();
 | 
			
		||||
 | 
			
		||||
	memset(state->startPath, '\0', PATH_MAX - 1);
 | 
			
		||||
 | 
			
		||||
	strncpy(state->startPath, startPath.c_str(), PATH_MAX - 1);
 | 
			
		||||
	
 | 
			
		||||
	settings_load(&state->settings);
 | 
			
		||||
	
 | 
			
		||||
	printf(STRING_INFO_INIT);
 | 
			
		||||
@@ -107,27 +101,9 @@ init(State* state)
 | 
			
		||||
	resources_init(&state->resources);
 | 
			
		||||
	dialog_init(&state->dialog, &state->anm2, &state->reference, &state->resources, state->window);
 | 
			
		||||
	tool_init(&state->tool, &state->input);
 | 
			
		||||
 | 
			
		||||
	preview_init
 | 
			
		||||
	(
 | 
			
		||||
		&state->preview, 
 | 
			
		||||
		&state->anm2, 
 | 
			
		||||
		&state->reference, 
 | 
			
		||||
		&state->animationID,
 | 
			
		||||
		&state->resources, 
 | 
			
		||||
		&state->settings
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	editor_init
 | 
			
		||||
	(
 | 
			
		||||
		&state->editor, 
 | 
			
		||||
		&state->anm2, 
 | 
			
		||||
		&state->reference, 
 | 
			
		||||
		&state->animationID, 
 | 
			
		||||
		&state->spritesheetID, 
 | 
			
		||||
		&state->resources, 
 | 
			
		||||
		&state->settings
 | 
			
		||||
	);
 | 
			
		||||
	snapshots_init(&state->snapshots, &state->anm2, &state->reference, &state->time, &state->input);
 | 
			
		||||
	preview_init(&state->preview, &state->anm2, &state->reference, &state->time, &state->resources, &state->settings);
 | 
			
		||||
	editor_init(&state->editor, &state->anm2, &state->reference, &state->resources, &state->settings);
 | 
			
		||||
	
 | 
			
		||||
	imgui_init
 | 
			
		||||
	(
 | 
			
		||||
@@ -137,8 +113,7 @@ init(State* state)
 | 
			
		||||
		&state->input,
 | 
			
		||||
		&state->anm2,
 | 
			
		||||
		&state->reference,
 | 
			
		||||
		&state->animationID,
 | 
			
		||||
		&state->spritesheetID,
 | 
			
		||||
		&state->time,
 | 
			
		||||
		&state->editor,
 | 
			
		||||
		&state->preview,
 | 
			
		||||
		&state->settings,
 | 
			
		||||
@@ -149,11 +124,12 @@ init(State* state)
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	if (state->isArgument)
 | 
			
		||||
	{
 | 
			
		||||
		anm2_deserialize(&state->anm2, &state->resources, state->argument);
 | 
			
		||||
		window_title_from_path_set(state->window, state->argument);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		anm2_new(&state->anm2);
 | 
			
		||||
 | 
			
		||||
	window_title_from_anm2_set(state->window, &state->anm2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -179,9 +155,6 @@ loop(State* state)
 | 
			
		||||
void
 | 
			
		||||
quit(State* state)
 | 
			
		||||
{
 | 
			
		||||
	/* return to base path */
 | 
			
		||||
    std::filesystem::current_path(state->startPath);
 | 
			
		||||
 | 
			
		||||
	imgui_free(&state->imgui);
 | 
			
		||||
	settings_save(&state->settings);
 | 
			
		||||
	preview_free(&state->preview);
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ struct State
 | 
			
		||||
	Preview preview;
 | 
			
		||||
    Anm2 anm2;
 | 
			
		||||
	Anm2Reference reference;
 | 
			
		||||
	f32 time;
 | 
			
		||||
	Resources resources;
 | 
			
		||||
	Settings settings;
 | 
			
		||||
	Tool tool;
 | 
			
		||||
@@ -26,9 +27,6 @@ struct State
 | 
			
		||||
	bool isArgument = false;
 | 
			
		||||
	bool isRunning = true;
 | 
			
		||||
	char argument[PATH_MAX] = STRING_EMPTY;
 | 
			
		||||
	char startPath[PATH_MAX] = STRING_EMPTY;
 | 
			
		||||
	s32 animationID = -1;
 | 
			
		||||
	s32 spritesheetID = -1;
 | 
			
		||||
	u64 lastTick = 0;
 | 
			
		||||
	u64 tick = 0;
 | 
			
		||||
}; 
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,9 @@
 | 
			
		||||
#define STB_IMAGE_IMPLEMENTATION
 | 
			
		||||
#include <stb_image.h>
 | 
			
		||||
 | 
			
		||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
 | 
			
		||||
#include <stb_image_write.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
texture_gl_set(Texture* self, void* data)
 | 
			
		||||
{
 | 
			
		||||
@@ -58,6 +61,14 @@ texture_from_data_init(Texture* self, const u8* data, u32 length)
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Writes a *.png to the path from the data/size */
 | 
			
		||||
/* Returns true on success */
 | 
			
		||||
bool
 | 
			
		||||
texture_from_data_write(const char* path, const u8* data, s32 width, s32 height)
 | 
			
		||||
{
 | 
			
		||||
	return (bool)stbi_write_png(path, width, height, 4, data, width * 4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
texture_free(Texture* self)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -14,3 +14,4 @@ void texture_gl_set(Texture* self, void* data);
 | 
			
		||||
bool texture_from_path_init(Texture* self, const char* path);
 | 
			
		||||
bool texture_from_data_init(Texture* self, const u8* data, u32 length);
 | 
			
		||||
void texture_free(Texture* self);
 | 
			
		||||
bool texture_from_data_write(const char* path, const u8* data, s32 width, s32 height);
 | 
			
		||||
@@ -2,12 +2,12 @@
 | 
			
		||||
 | 
			
		||||
/* Sets the window title from the given anm2 */
 | 
			
		||||
void
 | 
			
		||||
window_title_from_anm2_set(SDL_Window* self, Anm2* anm2)
 | 
			
		||||
window_title_from_path_set(SDL_Window* self, const char* path)
 | 
			
		||||
{
 | 
			
		||||
    if (!strcmp(anm2->path, STRING_EMPTY) == 0)
 | 
			
		||||
    if (!strcmp(path, STRING_EMPTY) == 0)
 | 
			
		||||
    {
 | 
			
		||||
        char windowTitle[WINDOW_TITLE_MAX];
 | 
			
		||||
        snprintf(windowTitle, WINDOW_TITLE_MAX, STRING_WINDOW_TITLE_EDITING, anm2->path);
 | 
			
		||||
        snprintf(windowTitle, WINDOW_TITLE_MAX, STRING_WINDOW_TITLE_EDITING, path);
 | 
			
		||||
        SDL_SetWindowTitle(self, windowTitle);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "anm2.h"
 | 
			
		||||
#include "COMMON.h"
 | 
			
		||||
 | 
			
		||||
#define WINDOW_TITLE_MAX 0xFF + PATH_MAX 
 | 
			
		||||
 | 
			
		||||
void window_title_from_anm2_set(SDL_Window* self, Anm2* anm2);
 | 
			
		||||
void window_title_from_path_set(SDL_Window* self, const char* path);
 | 
			
		||||
		Reference in New Issue
	
	Block a user