diff --git a/src/COMMON.h b/src/COMMON.h index ca5712e..4ebd0f5 100644 --- a/src/COMMON.h +++ b/src/COMMON.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -16,9 +17,10 @@ #include #include +#include +#include #include #include -#include #include "STRINGS.h" @@ -48,6 +50,14 @@ static inline const char* enum_to_string(const char* arr[], s32 count, s32 index static inline s32 string_to_enum(const char* str, const char* const* arr, s32 n) { for (s32 i=0; i static inline s32 map_next_id_get(const std::map& map) { s32 id = 0; for (const auto& [key, _] : map) if (key != id) break; else ++id; return id; diff --git a/src/PACKED.h b/src/RESOURCES.h similarity index 92% rename from src/PACKED.h rename to src/RESOURCES.h index d5cd79f..4c07271 100644 --- a/src/PACKED.h +++ b/src/RESOURCES.h @@ -2,7 +2,8 @@ #include "COMMON.h" -struct PackedData +/* TextureData */ +struct TextureData { const u8* data; u32 length; @@ -266,25 +267,25 @@ static const unsigned char texture_arrow_down[] = { }; static const unsigned int texture_arrow_down_len = 153; -#define PACKED_TEXTURE_COUNT (PACKED_TEXTURE_ARROW_DOWN + 1) -enum PackedTextureType +#define TEXTURE_COUNT (TEXTURE_ARROW_DOWN + 1) +enum TextureType { - PACKED_TEXTURE_EYE_OPEN, - PACKED_TEXTURE_EYE_CLOSED, - PACKED_TEXTURE_ANIMATION, - PACKED_TEXTURE_EVENT, - PACKED_TEXTURE_LAYER, - PACKED_TEXTURE_NULL, - PACKED_TEXTURE_ROOT, - PACKED_TEXTURE_SPRITESHEET, - PACKED_TEXTURE_TRIGGER, - PACKED_TEXTURE_RECT_SHOW, - PACKED_TEXTURE_RECT_HIDE, - PACKED_TEXTURE_ARROW_UP, - PACKED_TEXTURE_ARROW_DOWN + TEXTURE_EYE_OPEN, + TEXTURE_EYE_CLOSED, + TEXTURE_ANIMATION, + TEXTURE_EVENT, + TEXTURE_LAYER, + TEXTURE_NULL, + TEXTURE_ROOT, + TEXTURE_SPRITESHEET, + TEXTURE_TRIGGER, + TEXTURE_RECT_SHOW, + TEXTURE_RECT_HIDE, + TEXTURE_ARROW_UP, + TEXTURE_ARROW_DOWN }; -static const PackedData PACKED_TEXTURE_DATA[PACKED_TEXTURE_COUNT] = +static const TextureData TEXTURE_DATA[TEXTURE_COUNT] = { {texture_eye_open, texture_eye_open_len}, {texture_eye_closed, texture_eye_closed_len}, @@ -300,3 +301,45 @@ static const PackedData PACKED_TEXTURE_DATA[PACKED_TEXTURE_COUNT] = {texture_arrow_up, texture_arrow_up_len}, {texture_arrow_down, texture_arrow_down_len}, }; + +/* Shaders */ +struct ShaderData +{ + const char* vertex; + const char* fragment; +}; + +#define SHADER_COUNT (SHADER + 1) +enum ShaderType +{ + SHADER +}; + +static const char SHADER_VERTEX[] = +"#version 330 core\n" \ +"layout (location = 0)\n" \ +"in vec2 i_position;\n" \ +"uniform mat4 u_transform;\n" \ +"\n" \ +"void main()\n" \ +"{\n" \ +" vec4 pos = vec4(i_position, 0.0, 1.0);\n" \ +" gl_Position = u_transform * pos;\n" \ +"}\n"; + +static const char SHADER_FRAGMENT[] = "#version 330 core\n" \ +"out vec4 FragColor;\n" \ +"uniform vec4 u_color;\n" \ +"\n" \ +"void main()\n" \ +"{\n" \ +" FragColor = u_color;\n" \ +"}\n"; + +static const char SHADER_UNIFORM_COLOR[] = "u_color"; +static const char SHADER_UNIFORM_TRANSFORM[] = "u_transform"; + +static const ShaderData SHADER_DATA[SHADER_COUNT] = +{ + {SHADER_VERTEX, SHADER_FRAGMENT} +}; diff --git a/src/STRINGS.h b/src/STRINGS.h index 48c4a3d..6665d71 100644 --- a/src/STRINGS.h +++ b/src/STRINGS.h @@ -97,15 +97,20 @@ #define STRING_IMGUI_FRAME_PROPERTIES_COLOR_OFFSET "Color Offset" #define STRING_IMGUI_ANIMATION_PREVIEW "Animation Preview" +#define STRING_IMGUI_ANIMATION_PREVIEW_LABEL "##Animation Preview" #define STRING_IMGUI_ANIMATION_PREVIEW_SETTINGS "##Animation Preview Settings" #define STRING_IMGUI_ANIMATION_PREVIEW_GRID_SETTINGS "##Grid Settings" #define STRING_IMGUI_ANIMATION_PREVIEW_GRID "Grid" #define STRING_IMGUI_ANIMATION_PREVIEW_GRID_SIZE "##Grid Size" #define STRING_IMGUI_ANIMATION_PREVIEW_GRID_COLOR "Color" -#define STRING_IMGUI_ANIMATION_PREVIEW_ZOOM_SETTINGS "##Zoom Settings" +#define STRING_IMGUI_ANIMATION_PREVIEW_VIEW_SETTINGS "##View Settings" #define STRING_IMGUI_ANIMATION_PREVIEW_ZOOM "Zoom" #define STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_SETTINGS "##Background Settings" #define STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_COLOR "Background Color" +#define STRING_IMGUI_ANIMATION_PREVIEW_HELPER_SETTINGS "##Helper Settings" +#define STRING_IMGUI_ANIMATION_PREVIEW_CENTER_VIEW "Center View" +#define STRING_IMGUI_ANIMATION_PREVIEW_AXIS "Axes" +#define STRING_IMGUI_ANIMATION_PREVIEW_AXIS_COLOR "Color" #define STRING_IMGUI_SPRITESHEET_EDITOR "Spritesheet Editor" @@ -124,98 +129,75 @@ #define STRING_IMGUI_TIMELINE_ANIMATIONS "Animations" #define STRING_IMGUI_TIMELINE_PLAY "|> Play" #define STRING_IMGUI_TIMELINE_PAUSE "|| Pause" -#define STRING_IMGUI_TIMELINE_ADD_FRAME "+ Add Frame" -#define STRING_IMGUI_TIMELINE_REMOVE_FRAME "- Remove Frame" +#define STRING_IMGUI_TIMELINE_FRAME_ADD "Add Frame" +#define STRING_IMGUI_TIMELINE_FRAME_REMOVE "Remove Frame" #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" #define STRING_IMGUI_TIMELINE_NULL "Null" #define STRING_IMGUI_TIMELINE_RECT "Rect" +#define STRING_IMGUI_TIMELINE_FRAME_INDICES "Frame Indices" +#define STRING_IMGUI_TIMELINE_ANIMATION_LENGTH "Animation Length" +#define STRING_IMGUI_TIMELINE_ELEMENT_TIMELINE "Element Timeline" #define STRING_IMGUI_TIMELINE_ELEMENTS "Elements" -#define STRING_IMGUI_TIMELINE_ELEMENT_ADD "Add" +#define STRING_IMGUI_TIMELINE_ELEMENT_ADD "Add Element" #define STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU "Element Add Menu" #define STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_LAYER "Add Layer..." #define STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_NULL "Add Null..." -#define STRING_IMGUI_TIMELINE_ELEMENT_REMOVE "Remove" +#define STRING_IMGUI_TIMELINE_ELEMENT_REMOVE "Remove Element" -#define STRING_IMGUI_TOOLTIP_ANIMATIONS_SELECT "Select the animation to edit.\nYou can also click the name to edit it." #define STRING_IMGUI_TOOLTIP_ANIMATIONS_ADD "Add a new animation." -#define STRING_IMGUI_TOOLTIP_ANIMATIONS_REMOVE "Removes the selected animation." #define STRING_IMGUI_TOOLTIP_ANIMATIONS_DUPLICATE "Duplicates the selected animation." +#define STRING_IMGUI_TOOLTIP_ANIMATIONS_REMOVE "Removes the selected animation." +#define STRING_IMGUI_TOOLTIP_ANIMATIONS_SELECT "Select the animation to edit.\nYou can also click the name to edit it." #define STRING_IMGUI_TOOLTIP_ANIMATIONS_SET_AS_DEFAULT "Sets the selected animation as the default.\nDefault animations are marked with \"(*)\"." -#define STRING_IMGUI_TOOLTIP_EVENTS_SELECT "Set the event for the trigger, or rename it." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_AXIS "Toggles the display of the X/Y axes." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_AXIS_COLOR "Changes the color of the X/Y axes." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_BACKGROUND_COLOR "Changes the background color of the animation preview." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_CENTER_VIEW "Centers the preview's pan." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID "Toggles grid visibility on the animation preview." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_SIZE "Changes the grid's size, in pixels." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_COLOR "Changes the animation preview grid color." +#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ZOOM "Changes the animation preview zoom level." #define STRING_IMGUI_TOOLTIP_EVENTS_ADD "Add a new event." #define STRING_IMGUI_TOOLTIP_EVENTS_REMOVE "Removes the selected event." -#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_ADD "Opens the file dialog to load in a new sprite." -#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_REMOVE "Removes the selected spritesheet." -#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_RELOAD "Reloads the selected spritesheet." -#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_REPLACE "Replaces the selected spritesheet; opens up the file dialog." -#define STRING_IMGUI_TOOLTIP_PROPERTIES_FPS "Change the FPS of the animation." -#define STRING_IMGUI_TOOLTIP_PROPERTIES_CREATED_BY "Change the author of the animation." -#define STRING_IMGUI_TOOLTIP_PROPERTIES_CREATED_ON_NOW "Set the date of creation to the current system time." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_POSITION "Change the position of the frame." +#define STRING_IMGUI_TOOLTIP_EVENTS_SELECT "Set the event for the trigger, or rename it." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_COLOR_OFFSET "Change the color offset of the frame." #define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_POSITION "Change the crop position of the frame." #define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_CROP_SIZE "Change the crop size of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_PIVOT "Change the pivot of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_SCALE "Change the scale of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_ROTATION "Change the rotation of the frame." #define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_DURATION "Change the duration of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_TINT "Change the tint of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_COLOR_OFFSET "Change the color offset of the frame." -#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_VISIBLE "Toggles the visibility of the frame." #define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_INTERPOLATED "Toggles the interpolation of the frame.\nBetween keyframes, will transform the values in the in-betweens to be smooth." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_PIVOT "Change the pivot of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_POSITION "Change the position of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_ROTATION "Change the rotation of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_SCALE "Change the scale of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_TINT "Change the tint of the frame." +#define STRING_IMGUI_TOOLTIP_FRAME_PROPERTIES_VISIBLE "Toggles the visibility of the frame." +#define STRING_IMGUI_TOOLTIP_PROPERTIES_CREATED_BY "Change the author of the animation." +#define STRING_IMGUI_TOOLTIP_PROPERTIES_CREATED_ON_NOW "Set the date of creation to the current system time." +#define STRING_IMGUI_TOOLTIP_PROPERTIES_FPS "Change the FPS of the animation." +#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_ADD "Opens the file dialog to load in a new spritesheet." +#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_RELOAD "Reloads the selected spritesheet." +#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_REMOVE "Removes the selected spritesheet." +#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_REPLACE "Replaces the selected spritesheet; opens up the file dialog." +#define STRING_IMGUI_TOOLTIP_SPRITESHEETS_SELECT "Select the spritesheet element." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ANIMATION_LENGTH "Changes the current animation's length." #define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ADD "Add a layer or null timeline element." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_NAME "Click to rename the element." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SPRITESHEET "Click to change the spritesheet the layer is using." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_REMOVE "Remove a timeline element." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_UP "Shift this timeline element above.\nElements with higher indices will display further behind." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_DOWN "Shift this timeline element below.\nElements with lower indices will display further in front." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_VISIBLE "Toggle visibility for this element." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_RECT "Toggle visibility for a rectangle around the null." -#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ROOT "This is the Root element." #define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_LAYER "This is a Layer element.\nThese are the main graphical animation elements.\nClick to rename." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_NAME "Click to rename the element." #define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_NULL "This is a Null element.\nThese are invisible elements where a game engine may have access to them for additional effects.\nClick to rename." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_RECT "Toggle visibility for a rectangle around the null." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_REMOVE "Remove a timeline element." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ROOT "This is the Root element." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_DOWN "Shift this timeline element below.\nElements with lower indices will display further in front." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SHIFT_UP "Shift this timeline element above.\nElements with higher indices will display further behind." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_SPRITESHEET "Click to change the spritesheet the layer is using." #define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_TRIGGERS "This is the animation's Triggers.\nTriggers are special activations; each is bound to an Event." -#define STRING_IMGUI_TOOLTIP_TIMELINE_PLAY "Plays the animation." +#define STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_VISIBLE "Toggle visibility for this 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_PAUSE "Pauses the animation." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID "Toggles grid visibility on the animation preview." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_COLOR "Changes the animation preview grid color." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_GRID_SIZE "Changes the animation preview grid size (number of rows/columns)." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ZOOM "Changes the animation preview zoom level." -#define STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_BACKGROUND_COLOR "Changes the background color of the animation preview." +#define STRING_IMGUI_TOOLTIP_TIMELINE_PLAY "Plays the animation." -#define STRING_OPENGL_VERSION "#version 330" - -#define SHADER_VERTEX "#version 330 core\n" \ -"in vec2 l_position;\n" \ -"in vec2 l_uv;\n" \ -"\n" \ -"uniform mat3 u_view;\n" \ -"uniform mat3 u_projection;\n" \ -"uniform mat3 u_model;\n" \ -"\n" \ -"out vec2 o_uv;\n" \ -"\n" \ -"void main()\n" \ -"{\n" \ -" gl_Position = vec4(u_projection * u_view * u_model * vec3(l_position, 1.0), 1.0);\n" \ -" o_uv = l_uv;\n" \ -"}" - -#define SHADER_FRAGMENT "#version 330 core\n" \ -"uniform vec4 u_tint;\n" \ -"uniform vec4 u_color_offset;\n" \ -"uniform sampler2D u_texture;\n" \ -"\n" \ -"in vec2 i_uv;\n" \ -"\n" \ -"out vec4 FragColor;\n" \ -"\n" \ -"void main()\n" \ -"{\n" \ -" FragColor = texture(u_texture, i_uv) * u_tint;\n" \ -" FragColor.rgb += u_color_offset.rgb;\n" \ -"\n" \ -" if (FragColor.a == 0.0) discard;\n" \ -"}\n" +#define STRING_OPENGL_VERSION "#version 330" \ No newline at end of file diff --git a/src/anm2.cpp b/src/anm2.cpp index c8589eb..9008b74 100644 --- a/src/anm2.cpp +++ b/src/anm2.cpp @@ -37,6 +37,7 @@ anm2_serialize(Anm2* self, const char* path) if (!self || !path) return false; + /* Update creation date on first version */ if (self->version == 0) anm2_created_on_set(self); @@ -308,6 +309,8 @@ 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); @@ -316,7 +319,7 @@ anm2_serialize(Anm2* self, const char* path) /* Loads the .anm2 file and deserializes it into the struct equivalent */ bool -anm2_deserialize(Anm2* self, const char* path) +anm2_deserialize(Anm2* self, Resources* resources, const char* path) { XMLDocument document; XMLError error; @@ -349,6 +352,7 @@ anm2_deserialize(Anm2* self, const char* path) return false; } + resources_loaded_textures_free(resources); strncpy(self->path, path, PATH_MAX - 1); root = document.FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]); @@ -422,7 +426,6 @@ anm2_deserialize(Anm2* self, const char* path) break; } - /* Attributes */ attribute = element->FirstAttribute(); @@ -469,7 +472,8 @@ anm2_deserialize(Anm2* self, const char* path) } break; case ANM2_ATTRIBUTE_PATH: - strncpy(lastSpritesheet->path, attribute->Value(), ANM2_STRING_MAX - 1); + strncpy(lastSpritesheet->path, attribute->Value(), PATH_MAX - 1); + anm2_spritesheet_texture_load(self, resources, attribute->Value(), id); break; case ANM2_ATTRIBUTE_NAME: switch (anm2Element) @@ -638,6 +642,8 @@ anm2_deserialize(Anm2* self, const char* path) } } + working_directory_from_path_set(path); + printf(STRING_INFO_ANM2_READ, path); return true; @@ -719,4 +725,17 @@ anm2_new(Anm2* self) { *self = Anm2{}; anm2_created_on_set(self); +} + +void +anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const char* path, s32 id) +{ + Texture texture; + + /* free texture if it exists */ + if (resources->loadedTextures.find(id) != resources->loadedTextures.end()) + texture_free(&resources->loadedTextures[id]); + + texture_from_path_init(&texture, path); + resources->loadedTextures[id] = texture; } \ No newline at end of file diff --git a/src/anm2.h b/src/anm2.h index aa9c93e..5ee2984 100644 --- a/src/anm2.h +++ b/src/anm2.h @@ -1,12 +1,14 @@ #pragma once #include "COMMON.h" +#include "resources.h" #define ANM2_SCALE_CONVERT(x) ((f32)x / 100.0f) #define ANM2_TINT_CONVERT(x) ((f32)x / 255.0f) #define ANM2_STRING_MAX 0xFF #define ANM2_STRING_FORMATTED_MAX 0xFFF +#define ANM2_PATH_FORMATTED_MAX PATH_MAX + 0xFF #define ANM2_BUFFER_MAX 0xFFFFF #define ANM2_FPS_MIN 0 #define ANM2_FPS_DEFAULT 30 @@ -117,7 +119,7 @@ enum Anm2AnimationType struct Anm2Spritesheet { - char path[ANM2_STRING_MAX] = STRING_EMPTY; + char path[PATH_MAX] = STRING_EMPTY; }; struct Anm2Layer @@ -213,8 +215,9 @@ void anm2_layer_remove(Anm2* self, s32 id); void anm2_null_add(Anm2* self); void anm2_null_remove(Anm2* self, s32 id); bool anm2_serialize(Anm2* self, const char* path); -bool anm2_deserialize(Anm2* self, const char* path); +bool anm2_deserialize(Anm2* self, Resources* resources, const char* path); void anm2_new(Anm2* self); void anm2_created_on_set(Anm2* self); s32 anm2_animation_add(Anm2* self); -void anm2_animation_remove(Anm2* self, s32 id); \ No newline at end of file +void anm2_animation_remove(Anm2* self, s32 id); +void anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const char* path, s32 id); \ No newline at end of file diff --git a/src/dialog.cpp b/src/dialog.cpp index d049743..eb3fa6a 100644 --- a/src/dialog.cpp +++ b/src/dialog.cpp @@ -19,11 +19,18 @@ _dialog_callback(void* userdata, const char* const* filelist, s32 filter) } +void +dialog_init(Dialog* self, Anm2* anm2, Resources* resources) +{ + self->anm2 = anm2; + self->resources = resources; +} + /* Opens file dialog for user to pick anm2 files */ void dialog_anm2_open(Dialog* self) { - SDL_ShowOpenFileDialog(_dialog_callback, self, NULL, ANM2_DIALOG_FILE_FILTER, 1, NULL, false); + SDL_ShowOpenFileDialog(_dialog_callback, self, NULL, DIALOG_FILE_FILTER_ANM2, 1, NULL, false); self->type = DIALOG_ANM2_OPEN; } @@ -31,22 +38,60 @@ dialog_anm2_open(Dialog* self) void dialog_anm2_save(Dialog* self) { - SDL_ShowSaveFileDialog(_dialog_callback, self, NULL, ANM2_DIALOG_FILE_FILTER, 1, NULL); + SDL_ShowSaveFileDialog(_dialog_callback, self, NULL, DIALOG_FILE_FILTER_ANM2, 1, NULL); self->type = DIALOG_ANM2_SAVE; } +/* Opens file dialog for user to pick png files for spritesheets */ +void +dialog_png_open(Dialog* self) +{ + SDL_ShowOpenFileDialog(_dialog_callback, self, NULL, DIALOG_FILE_FILTER_PNG, 1, NULL, false); + self->type = DIALOG_PNG_OPEN; +} + +/* Opens file dialog for user to pick png file to replace selected one */ +void +dialog_png_replace(Dialog* self) +{ + SDL_ShowOpenFileDialog(_dialog_callback, self, NULL, DIALOG_FILE_FILTER_PNG, 1, NULL, false); + self->type = DIALOG_PNG_REPLACE; +} + void dialog_tick(Dialog* self) { if (self->isSelected) { + 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) { case DIALOG_ANM2_OPEN: - anm2_deserialize(self->anm2, self->path); + anm2_deserialize(self->anm2, self->resources, relativePath); + resources_loaded_textures_free(self->resources); break; case DIALOG_ANM2_SAVE: - anm2_serialize(self->anm2, self->path); + anm2_serialize(self->anm2, relativePath); + break; + case DIALOG_PNG_OPEN: + id = map_next_id_get(self->resources->loadedTextures); + self->anm2->spritesheets[id] = Anm2Spritesheet{}; + strncpy(self->anm2->spritesheets[id].path, relativePath, PATH_MAX); + anm2_spritesheet_texture_load(self->anm2, self->resources, relativePath, 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); + self->replaceID = -1; break; default: break; diff --git a/src/dialog.h b/src/dialog.h index c772457..774ab48 100644 --- a/src/dialog.h +++ b/src/dialog.h @@ -1,26 +1,40 @@ #pragma once #include "anm2.h" +#include "resources.h" -static const SDL_DialogFileFilter ANM2_DIALOG_FILE_FILTER[] = +static const SDL_DialogFileFilter DIALOG_FILE_FILTER_ANM2[] = { {"Anm2", "anm2;xml"} }; +static const SDL_DialogFileFilter DIALOG_FILE_FILTER_PNG[] = +{ + {"png", "png"} +}; + enum DialogType { + DIALOG_NONE, DIALOG_ANM2_OPEN, - DIALOG_ANM2_SAVE + DIALOG_ANM2_SAVE, + DIALOG_PNG_OPEN, + DIALOG_PNG_REPLACE }; struct Dialog { Anm2* anm2 = NULL; - enum DialogType type = DIALOG_ANM2_OPEN; + Resources* resources = NULL; + s32 replaceID = -1; + enum DialogType type = DIALOG_NONE; char path[PATH_MAX] = ""; bool isSelected = false; }; +void dialog_init(Dialog* self, Anm2* anm2, Resources* resources); void dialog_anm2_open(Dialog* self); +void dialog_png_open(Dialog* self); +void dialog_png_replace(Dialog* self); void dialog_anm2_save(Dialog* self); void dialog_tick(Dialog* self); \ No newline at end of file diff --git a/src/imgui.cpp b/src/imgui.cpp index e201a95..660eaac 100644 --- a/src/imgui.cpp +++ b/src/imgui.cpp @@ -1,7 +1,19 @@ #include "imgui.h" static void _imgui_tooltip(const char* tooltip); -static void _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2AnimationType type, Anm2AnimationType* selectType, s32* selectID); + +static void +_imgui_timeline_element +( + Imgui* self, + Anm2Animation* animation, + void* element, + s32* id, + s32* index, + Anm2AnimationType type, + Anm2AnimationType* selectType, + s32* selectID +); /* Makes a tooltip! */ static void _imgui_tooltip(const char* tooltip) @@ -12,17 +24,32 @@ static void _imgui_tooltip(const char* tooltip) /* Displays each element of the timeline of a selected animation */ static void -_imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2AnimationType type, Anm2AnimationType* selectType, s32* selectID) +_imgui_timeline_element +( + Imgui* self, + Anm2Animation* animation, + void* element, + s32* id, + s32* index, + Anm2AnimationType type, + Anm2AnimationType* selectType, + s32* selectID +) { static s32 selectedElementIndex = -1; static s32 selectedSpritesheetIndex = -1; + void* frames = NULL; Anm2Layer* layer = NULL; Anm2LayerAnimation* layerAnimation = NULL; Anm2Null* null = NULL; Anm2NullAnimation* nullAnimation = NULL; Anm2RootAnimation* rootAnimation = NULL; Anm2Triggers* triggers = NULL; + ImVec2 frameListSize = {IMGUI_TIMELINE_FRAME_SIZE.x * animation->frameNum, IMGUI_TIMELINE_ELEMENTS_TIMELINE_SIZE.y}; ImTextureID iconTexture = -1; + ImGuiWindowFlags windowFlags = 0 | + ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoScrollWithMouse; bool isArrows = false; bool* isShowRect = NULL; @@ -38,33 +65,37 @@ _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2Ani { case ANM2_ROOT_ANIMATION: rootAnimation = (Anm2RootAnimation*)element; - iconTexture = self->packed->textures[PACKED_TEXTURE_ROOT].handle; + iconTexture = self->resources->textures[TEXTURE_ROOT].handle; strncpy(nameVisible, STRING_IMGUI_TIMELINE_ROOT, ANM2_STRING_FORMATTED_MAX); isVisible = &rootAnimation->isVisible; + frames = &rootAnimation->frames; break; case ANM2_LAYER_ANIMATION: layerAnimation = (Anm2LayerAnimation*)element; layer = &self->anm2->layers[*id]; - iconTexture = self->packed->textures[PACKED_TEXTURE_LAYER].handle; + iconTexture = self->resources->textures[TEXTURE_LAYER].handle; isVisible = &layerAnimation->isVisible; spritesheetID = &layer->spritesheetID; namePointer = layer->name; + frames = &layerAnimation->frames; snprintf(nameBuffer, ANM2_STRING_MAX, "%s", namePointer); snprintf(nameVisible, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_TIMELINE_ELEMENT_FORMAT, *id, namePointer); break; case ANM2_NULL_ANIMATION: nullAnimation = (Anm2NullAnimation*)element; null = &self->anm2->nulls[*id]; - iconTexture = self->packed->textures[PACKED_TEXTURE_NULL].handle; + iconTexture = self->resources->textures[TEXTURE_NULL].handle; isVisible = &nullAnimation->isVisible; isShowRect = &null->isShowRect; namePointer = null->name; + frames = &nullAnimation->frames; snprintf(nameBuffer, ANM2_STRING_MAX, "%s", namePointer); snprintf(nameVisible, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_TIMELINE_ELEMENT_FORMAT, *id, namePointer); break; case ANM2_TRIGGERS: triggers = (Anm2Triggers*)element; - iconTexture = self->packed->textures[PACKED_TEXTURE_TRIGGER].handle; + frames = &triggers->items; + iconTexture = self->resources->textures[TEXTURE_TRIGGER].handle; strncpy(nameVisible, STRING_IMGUI_TIMELINE_TRIGGERS, ANM2_STRING_FORMATTED_MAX); isVisible = &triggers->isVisible; break; @@ -72,7 +103,7 @@ _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2Ani break; } - ImGui::BeginChild(nameVisible, IMGUI_TIMELINE_ELEMENT_SIZE, true, ImGuiWindowFlags_NoScrollbar); + ImGui::BeginChild(nameVisible, IMGUI_TIMELINE_ELEMENT_SIZE, true, windowFlags); ImGui::PushID(*index); @@ -95,7 +126,7 @@ _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2Ani if (canMoveUp) { auto target = isReversed ? std::next(it) : std::prev(it); - if (target != map.end() && ImGui::ImageButton(STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_ABOVE, self->packed->textures[PACKED_TEXTURE_ARROW_UP].handle, IMGUI_ICON_SIZE)) + if (target != map.end() && ImGui::ImageButton(STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_ABOVE, self->resources->textures[TEXTURE_ARROW_UP].handle, IMGUI_ICON_SIZE)) { map_swap(map, it->first, target->first); *didSwap = true; @@ -107,11 +138,14 @@ _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2Ani if (canMoveDown) { + if (!canMoveUp) ImGui::Dummy(IMGUI_ICON_BUTTON_SIZE); + + ImGui::SameLine(); auto target = isReversed ? std::prev(it) : std::next(it); - if (target != map.end() && ImGui::ImageButton(STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_BELOW, self->packed->textures[PACKED_TEXTURE_ARROW_DOWN].handle, IMGUI_ICON_SIZE)) + if (target != map.end() && ImGui::ImageButton(STRING_IMGUI_TIMELINE_ELEMENT_SHIFT_BELOW, self->resources->textures[TEXTURE_ARROW_DOWN].handle, IMGUI_ICON_SIZE)) { map_swap(map, it->first, target->first); *didSwap = true; @@ -176,7 +210,7 @@ _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2Ani } } - + switch (type) { case ANM2_ROOT_ANIMATION: @@ -200,18 +234,22 @@ _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2Ani /* Visiblity */ if (isVisible) { + ImVec2 cursorPos; ImTextureID visibilityIcon = *isVisible - ? self->packed->textures[PACKED_TEXTURE_EYE_OPEN].handle - : self->packed->textures[PACKED_TEXTURE_EYE_CLOSED].handle; + ? self->resources->textures[TEXTURE_EYE_OPEN].handle + : self->resources->textures[TEXTURE_EYE_CLOSED].handle; ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - IMGUI_ICON_BUTTON_SIZE.x - ImGui::GetStyle().FramePadding.x * 2); - + cursorPos = ImGui::GetCursorPos(); + ImGui::SetCursorPosX(cursorPos.x + ImGui::GetContentRegionAvail().x - IMGUI_ICON_BUTTON_SIZE.x - ImGui::GetStyle().FramePadding.x * 2); + if (ImGui::ImageButton(STRING_IMGUI_TIMELINE_VISIBLE, visibilityIcon, IMGUI_ICON_SIZE)) *isVisible = !*isVisible; _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_VISIBLE); + + ImGui::SetCursorPos(cursorPos); } /* Spritesheet IDs */ @@ -224,14 +262,14 @@ _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2Ani else snprintf(spritesheetIDName, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_TIMELINE_SPRITESHEET_FORMAT, *spritesheetID); - ImGui::Image(self->packed->textures[PACKED_TEXTURE_SPRITESHEET].handle, IMGUI_ICON_SIZE); + ImGui::Image(self->resources->textures[TEXTURE_SPRITESHEET].handle, IMGUI_ICON_SIZE); ImGui::SameLine(); ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_LABEL, IMGUI_TIMELINE_SPRITESHEET_ID_SIZE); if (selectedSpritesheetIndex == *index) { - if (ImGui::DragInt(STRING_IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_LABEL, spritesheetID, 0, 1, self->anm2->spritesheets.size() - 1)) + if (ImGui::InputInt(STRING_IMGUI_TIMELINE_ELEMENT_SPRITESHEET_ID_LABEL, spritesheetID, 0, 0, ImGuiInputTextFlags_None)) selectedSpritesheetIndex = -1; } else @@ -249,10 +287,10 @@ _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2Ani if (isShowRect) { ImTextureID rectIcon = *isShowRect - ? self->packed->textures[PACKED_TEXTURE_RECT_HIDE].handle - : self->packed->textures[PACKED_TEXTURE_RECT_SHOW].handle; + ? self->resources->textures[TEXTURE_RECT_HIDE].handle + : self->resources->textures[TEXTURE_RECT_SHOW].handle; - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - IMGUI_ICON_BUTTON_SIZE.x - ImGui::GetStyle().FramePadding.x * 2); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - ((IMGUI_ICON_BUTTON_SIZE.x - ImGui::GetStyle().FramePadding.x * 2) * 4)); if (ImGui::ImageButton(STRING_IMGUI_TIMELINE_RECT, rectIcon, IMGUI_ICON_SIZE)) *isShowRect = !*isShowRect; @@ -265,25 +303,69 @@ _imgui_timeline_element(Imgui* self, void* element, s32* id, s32* index, Anm2Ani selectedElementIndex = -1; selectedSpritesheetIndex = -1; } - - ImGui::PopID(); ImGui::EndGroup(); + ImGui::PopID(); + ImGui::EndChild(); + if (animation->frameNum > 0) + { + ImGui::SameLine(); + + ImGui::PushID(*index); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENT_TIMELINE, frameListSize, true); + + for (s32 i = 0; i < animation->frameNum; i++) + { + ImGui::PushID(i); + if (ImGui::Button(" ", IMGUI_TIMELINE_FRAME_SIZE)) + { + + } + ImGui::SameLine(0.0f, 0.0f); + ImGui::PopID(); + } + + ImGui::EndChild(); + ImGui::PopStyleVar(); + + ImGui::PopID(); + } + *index = *index + 1; } void -imgui_init(SDL_Window* window, SDL_GLContext glContext) +imgui_init +( + Imgui* self, + Dialog* dialog, + Resources* resources, + Input* input, + Anm2* anm2, + Preview* preview, + SDL_Window* window, + SDL_GLContext* glContext +) { IMGUI_CHECKVERSION(); + self->dialog = dialog; + self->resources = resources; + self->input = input; + self->anm2 = anm2; + self->preview = preview; + self->window = window; + self->glContext = glContext; + ImGui::CreateContext(); ImGui::StyleColorsDark(); - ImGui_ImplSDL3_InitForOpenGL(window, glContext); + ImGui_ImplSDL3_InitForOpenGL(self->window, *self->glContext); ImGui_ImplOpenGL3_Init(STRING_OPENGL_VERSION); ImGuiIO& io = ImGui::GetIO(); @@ -302,6 +384,10 @@ imgui_tick(Imgui* self) static Anm2AnimationType selectedTimelineElementType = ANM2_NONE; static bool isInterpolated = true; static bool isVisible = true; + static bool isHoverPreview = false; + static ImRect previewWindowRect; + static ImVec2 settingsCursorPos; + static bool isFirstTick = true; static f32 rotation = 0; static s32 duration = 0; @@ -313,27 +399,15 @@ imgui_tick(Imgui* self) static vec3 offset = {0.0f, 0.0f, 0.0f}; static vec4 tint = {0.0f, 0.0f, 0.0f, 1.0f}; - ImVec2 previewWindowSize; - ImVec2 previewSize; - f32 previewAspect = 0.0f; - f32 previewWindowAspect = 0.0f; - - ImGuiWindowFlags taskbarWindowFlags; - ImGuiWindowFlags dockspaceWindowFlags; - ImGuiDockNodeFlags dockNodeFlags; - - ImGui_ImplSDL3_NewFrame(); - ImGui_ImplOpenGL3_NewFrame(); - ImGui::NewFrame(); - - taskbarWindowFlags = 0 | + ImGuiWindowFlags taskbarWindowFlags = 0 | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoSavedSettings; - dockspaceWindowFlags = 0 | + ImGuiWindowFlags dockspaceWindowFlags = 0 | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | @@ -341,9 +415,23 @@ imgui_tick(Imgui* self) ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; - dockNodeFlags = 0 | + ImGuiWindowFlags childWindowFlags = 0 | + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoBringToFrontOnFocus | + ImGuiWindowFlags_NoNavFocus | + ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoScrollWithMouse; + + ImGuiDockNodeFlags dockNodeFlags = 0 | ImGuiDockNodeFlags_PassthruCentralNode; - + + ImGui_ImplSDL3_NewFrame(); + ImGui_ImplOpenGL3_NewFrame(); + ImGui::NewFrame(); + ImGuiViewport* viewport = ImGui::GetMainViewport(); /* Taskbar */ @@ -457,7 +545,7 @@ imgui_tick(Imgui* self) ImGui::PushID(id); - ImGui::Image(self->packed->textures[PACKED_TEXTURE_ANIMATION].handle, IMGUI_ICON_SIZE); + ImGui::Image(self->resources->textures[TEXTURE_ANIMATION].handle, IMGUI_ICON_SIZE); ImGui::SameLine(); if (isSelected) @@ -545,7 +633,7 @@ imgui_tick(Imgui* self) ImGui::PushID(id); - ImGui::Image(self->packed->textures[PACKED_TEXTURE_EVENT].handle, IMGUI_ICON_SIZE); + ImGui::Image(self->resources->textures[TEXTURE_EVENT].handle, IMGUI_ICON_SIZE); ImGui::SameLine(); isSelected = selectedEventID == id; @@ -596,46 +684,70 @@ imgui_tick(Imgui* self) for (auto [id, spritesheet] : self->anm2->spritesheets) { + ImVec2 spritesheetPreviewSize = IMGUI_SPRITESHEET_PREVIEW_SIZE; char spritesheetString[ANM2_STRING_FORMATTED_MAX]; bool isSelected = false; - snprintf(spritesheetString, ANM2_STRING_FORMATTED_MAX, STRING_IMGUI_SPRITESHEET_FORMAT, (s32)id, spritesheet.path); + f32 spritesheetAspect = (f32)self->resources->loadedTextures[id].size.x / self->resources->loadedTextures[id].size.y; + + if ((IMGUI_SPRITESHEET_PREVIEW_SIZE.x / IMGUI_SPRITESHEET_PREVIEW_SIZE.y) > spritesheetAspect) + spritesheetPreviewSize.x = IMGUI_SPRITESHEET_PREVIEW_SIZE.y * spritesheetAspect; + else + spritesheetPreviewSize.y = IMGUI_SPRITESHEET_PREVIEW_SIZE.x / spritesheetAspect; + + snprintf(spritesheetString, ANM2_PATH_FORMATTED_MAX, STRING_IMGUI_SPRITESHEET_FORMAT, (s32)id, spritesheet.path); ImGui::BeginChild(spritesheetString, IMGUI_SPRITESHEET_SIZE, true, ImGuiWindowFlags_None); ImGui::PushID(id); - ImGui::Image(self->packed->textures[PACKED_TEXTURE_SPRITESHEET].handle, IMGUI_ICON_SIZE); + ImGui::Image(self->resources->textures[TEXTURE_SPRITESHEET].handle, IMGUI_ICON_SIZE); ImGui::SameLine(); isSelected = selectedSpritesheetID == id; if (ImGui::Selectable(spritesheetString, isSelected)) selectedSpritesheetID = id; + _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_SELECT); + + ImGui::Image(self->resources->loadedTextures[id].handle, spritesheetPreviewSize); + ImGui::PopID(); ImGui::EndChild(); } - ImGui::Button(STRING_IMGUI_SPRITESHEETS_ADD); + if (ImGui::Button(STRING_IMGUI_SPRITESHEETS_ADD)) + dialog_png_open(self->dialog); _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_ADD); ImGui::SameLine(); if (selectedSpritesheetID != -1) { + /* Remove */ if (ImGui::Button(STRING_IMGUI_SPRITESHEETS_REMOVE)) { + self->resources->loadedTextures.erase(selectedSpritesheetID); self->anm2->spritesheets.erase(selectedSpritesheetID); selectedSpritesheetID = -1; } _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_REMOVE); ImGui::SameLine(); - ImGui::Button(STRING_IMGUI_SPRITESHEETS_RELOAD); + + /* Reload */ + if (ImGui::Button(STRING_IMGUI_SPRITESHEETS_RELOAD)) + anm2_spritesheet_texture_load(self->anm2, self->resources, self->anm2->spritesheets[selectedSpritesheetID].path, selectedSpritesheetID); _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_RELOAD); ImGui::SameLine(); - ImGui::Button(STRING_IMGUI_SPRITESHEETS_REPLACE); + + /* Replace */ + if (ImGui::Button(STRING_IMGUI_SPRITESHEETS_REPLACE)) + { + self->dialog->replaceID = selectedSpritesheetID; + dialog_png_replace(self->dialog); + } _imgui_tooltip(STRING_IMGUI_TOOLTIP_SPRITESHEETS_REPLACE); } @@ -686,14 +798,58 @@ imgui_tick(Imgui* self) ImGui::End(); /* -- Animation Preview -- */ - ImGui::Begin(STRING_IMGUI_ANIMATION_PREVIEW); + /* elements drawn out of order in order to get the size of the preview before how it visually appears */ + settingsCursorPos = ImGui::GetCursorPos(); + ImGui::SetCursorPos(IMGUI_ANIMATION_PREVIEW_POSITION); + + ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_LABEL, ImVec2(0, 0), true, childWindowFlags); + + previewWindowRect = ImGui::GetCurrentWindow()->ClipRect; + + ImGui::Image(self->preview->texture, ImVec2(PREVIEW_SIZE.x, PREVIEW_SIZE.y)); + + /* Panning */ + if (ImGui::IsItemHovered()) + { + SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE)); + isHoverPreview = true; + + if (mouse_held(&self->input->mouse, MOUSE_LEFT)) + { + self->preview->pan.x += self->input->mouse.delta.x; + self->preview->pan.y -= self->input->mouse.delta.y; + } + + self->preview->zoom = self->preview->zoom == PREVIEW_ZOOM_MIN ? 0 : self->preview->zoom; + + if (self->input->mouse.wheelDeltaY > 0) + self->preview->zoom += PREVIEW_ZOOM_STEP; + + if (self->input->mouse.wheelDeltaY < 0) + self->preview->zoom -= PREVIEW_ZOOM_STEP; + + self->preview->zoom = CLAMP(self->preview->zoom, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX); + } + else + { + if (isHoverPreview) + { + SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT)); + isHoverPreview = false; + } + } + + ImGui::EndChild(); + + ImGui::SetCursorPos(settingsCursorPos); + /* Settings */ - ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE, true); + ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE, true, childWindowFlags); /* Grid settings */ - ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_GRID_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_GRID_SIZE, true); + ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_GRID_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE, true); /* Grid toggle */ ImGui::Checkbox(STRING_IMGUI_ANIMATION_PREVIEW_GRID, &self->preview->isGrid); @@ -715,32 +871,60 @@ imgui_tick(Imgui* self) ImGui::SameLine(); - /* Zoom settings */ - ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_ZOOM_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_ZOOM_SIZE, true); + /* Helper settings */ + ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_HELPER_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE, true); + + /* Axis toggle */ + ImGui::Checkbox(STRING_IMGUI_ANIMATION_PREVIEW_AXIS, &self->preview->isAxis); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_AXIS); + + ImGui::SameLine(); + + /* Axis colors*/ + ImGui::ColorEdit4(STRING_IMGUI_ANIMATION_PREVIEW_AXIS_COLOR, value_ptr(self->preview->axisColor), ImGuiColorEditFlags_NoInputs); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_AXIS_COLOR); + + ImGui::EndChild(); + + ImGui::SameLine(); + + /* View settings */ + ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_VIEW_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE, true); /* Zoom */ ImGui::DragFloat(STRING_IMGUI_ANIMATION_PREVIEW_ZOOM, &self->preview->zoom, 1, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX, "%.0f"); _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_ZOOM); + /* Center view */ + if (ImGui::Button(STRING_IMGUI_ANIMATION_PREVIEW_CENTER_VIEW) || isFirstTick) + { + ImVec2 previewWindowSize = previewWindowRect.GetSize(); + + previewWindowSize.x = MAX(previewWindowSize.x, PREVIEW_SIZE.x); + previewWindowSize.y = MAX(previewWindowSize.y, PREVIEW_SIZE.y); + + /* Based on the preview's crop in its window, adjust the preview */ + self->preview->pan.x = PREVIEW_CENTER.x + ((previewWindowSize.x - PREVIEW_SIZE.x) / 2); + self->preview->pan.y = PREVIEW_CENTER.y - ((previewWindowSize.y - PREVIEW_SIZE.y) / 2); + + } + _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_CENTER_VIEW); + ImGui::EndChild(); ImGui::SameLine(); /* Background settings */ - ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_BACKGROUND_SIZE, true); + ImGui::BeginChild(STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_SETTINGS, IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE, true); /* Background color */ - ImGui::ColorEdit4(STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_COLOR, value_ptr(self->preview->color), ImGuiColorEditFlags_NoInputs); + ImGui::ColorEdit4(STRING_IMGUI_ANIMATION_PREVIEW_BACKGROUND_COLOR, value_ptr(self->preview->backgroundColor), ImGuiColorEditFlags_NoInputs); _imgui_tooltip(STRING_IMGUI_TOOLTIP_ANIMATION_PREVIEW_BACKGROUND_COLOR); ImGui::EndChild(); - ImGui::SameLine(); - ImGui::EndChild(); - ImGui::Image(self->preview->texture, ImVec2(PREVIEW_SIZE.x, PREVIEW_SIZE.y)); - ImGui::End(); /* -- Spritesheet Editor -- */ @@ -755,12 +939,31 @@ imgui_tick(Imgui* self) if (selectedAnimationID != -1) { s32 index = 0; + ImVec2 timelineSize = IMGUI_TIMELINE_SIZE; Anm2Animation* animation = &self->anm2->animations[selectedAnimationID]; - - ImGui::BeginChild(STRING_IMGUI_TIMELINE_ANIMATIONS, IMGUI_TIMELINE_ELEMENT_LIST_SIZE, true); + ImVec2 frameIndicesSize = + {IMGUI_TIMELINE_FRAME_SIZE.x * animation->frameNum, IMGUI_TIMELINE_FRAME_INDICES_SIZE.y}; + + timelineSize.y = ImGui::GetContentRegionAvail().y - IMGUI_TIMELINE_OFFSET_Y; + ImGui::BeginChild(STRING_IMGUI_TIMELINE_ANIMATIONS, timelineSize, true); + + /* Element top bar */ + ImGui::BeginChild(STRING_IMGUI_TIMELINE_ELEMENTS, IMGUI_TIMELINE_ELEMENTS_BAR_SIZE, true); + ImGui::Text(STRING_IMGUI_TIMELINE_ELEMENTS); + ImGui::EndChild(); + + + /* Frame indicies */ + if (animation->frameNum > 0) + { + ImGui::SameLine(); + ImGui::BeginChild(STRING_IMGUI_TIMELINE_FRAME_INDICES, frameIndicesSize, true); + ImGui::EndChild(); + } + /* Root */ - _imgui_timeline_element(self, &animation->rootAnimation, NULL, &index, ANM2_ROOT_ANIMATION, NULL, NULL); + _imgui_timeline_element(self, animation, &animation->rootAnimation, NULL, &index, ANM2_ROOT_ANIMATION, NULL, NULL); /* reverse order */ for (auto it = animation->layerAnimations.rbegin(); it != animation->layerAnimations.rend(); it++) @@ -768,21 +971,23 @@ imgui_tick(Imgui* self) s32 id = it->first; Anm2LayerAnimation& layer = it->second; - _imgui_timeline_element(self, &layer, &id, &index, ANM2_LAYER_ANIMATION, &selectedTimelineElementType, &selectedTimelineElementID); + _imgui_timeline_element(self, animation, &layer, &id, &index, ANM2_LAYER_ANIMATION, &selectedTimelineElementType, &selectedTimelineElementID); } for (auto & [id, null] : animation->nullAnimations) - _imgui_timeline_element(self, &null, (s32*)&id, &index, ANM2_NULL_ANIMATION, &selectedTimelineElementType, &selectedTimelineElementID); + _imgui_timeline_element(self, animation, &null, (s32*)&id, &index, ANM2_NULL_ANIMATION, &selectedTimelineElementType, &selectedTimelineElementID); /* Triggers */ - _imgui_timeline_element(self, &animation->triggers, NULL, &index, ANM2_TRIGGERS, NULL, NULL); + _imgui_timeline_element(self, animation, &animation->triggers, NULL, &index, ANM2_TRIGGERS, NULL, NULL); + + ImGui::EndChild(); /* Element configuration */ if (ImGui::Button(STRING_IMGUI_TIMELINE_ELEMENT_ADD)) ImGui::OpenPopup(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU); _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_ADD); - + if (ImGui::BeginPopup(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU)) { if (ImGui::Selectable(STRING_IMGUI_TIMELINE_ELEMENT_ADD_MENU_LAYER)) @@ -798,41 +1003,29 @@ imgui_tick(Imgui* self) if ( - selectedTimelineElementID != -1 && - ((selectedTimelineElementType == ANM2_LAYER_ANIMATION) || (selectedTimelineElementType == ANM2_NULL_ANIMATION)) + ImGui::Button(STRING_IMGUI_TIMELINE_ELEMENT_REMOVE) && + selectedTimelineElementID != -1 ) { - if (ImGui::Button(STRING_IMGUI_TIMELINE_ELEMENT_REMOVE)) + switch (selectedTimelineElementType) { - switch (selectedTimelineElementType) - { - case ANM2_LAYER_ANIMATION: - anm2_layer_remove(self->anm2, selectedTimelineElementID); - break; - case ANM2_NULL_ANIMATION: - anm2_null_remove(self->anm2, selectedTimelineElementID); - break; - default: - break; - } - - selectedTimelineElementID = -1; - selectedTimelineElementType = ANM2_NONE; + case ANM2_LAYER_ANIMATION: + anm2_layer_remove(self->anm2, selectedTimelineElementID); + break; + case ANM2_NULL_ANIMATION: + anm2_null_remove(self->anm2, selectedTimelineElementID); + break; + default: + break; } - _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_REMOVE); - } - else - { selectedTimelineElementID = -1; selectedTimelineElementType = ANM2_NONE; } - - ImGui::EndChild(); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ELEMENT_REMOVE); ImGui::SameLine(); - /* Animation playback and frames */ static bool isPlaying = false; if (isPlaying) @@ -853,10 +1046,9 @@ imgui_tick(Imgui* self) _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_PAUSE); } - /* ImGui::SameLine(); - if (ImGui::Button(STRING_IMGUI_TIMELINE_ADD_FRAME)) + if (ImGui::Button(STRING_IMGUI_TIMELINE_FRAME_ADD)) { } @@ -864,15 +1056,24 @@ imgui_tick(Imgui* self) ImGui::SameLine(); - if (ImGui::Button(STRING_IMGUI_TIMELINE_REMOVE_FRAME)) + if (ImGui::Button(STRING_IMGUI_TIMELINE_FRAME_REMOVE)) { } - */ + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_FRAME_REMOVE); + + ImGui::SameLine(); + + ImGui::PushItemWidth(IMGUI_TIMELINE_ANIMATION_LENGTH_WIDTH); + ImGui::InputInt(STRING_IMGUI_TIMELINE_ANIMATION_LENGTH, &animation->frameNum); + ImGui::PopItemWidth(); + _imgui_tooltip(STRING_IMGUI_TOOLTIP_TIMELINE_ANIMATION_LENGTH); } ImGui::End(); + if (isFirstTick) + isFirstTick = false; } void diff --git a/src/imgui.h b/src/imgui.h index e29546e..655a213 100644 --- a/src/imgui.h +++ b/src/imgui.h @@ -1,9 +1,10 @@ #pragma once #include "dialog.h" -#include "packed.h" +#include "resources.h" #include "preview.h" #include "window.h" +#include "input.h" #define IMGUI_IMPL_OPENGL_LOADER_CUSTOM #define IMGUI_ENABLE_DOCKING @@ -14,23 +15,32 @@ #define IMGUI_DRAG_SPEED 1.0 #define IMGUI_TASKBAR_HEIGHT 32 +#define IMGUI_TIMELINE_OFFSET_Y 32 +#define IMGUI_TIMELINE_ANIMATION_LENGTH_WIDTH 200 static const vec2 IMGUI_TASKBAR_MARGINS = {8, 4}; -static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE = {1280, 64}; -static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_GRID_SIZE = {150, 64}; -static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_ZOOM_SIZE = {150, 64}; -static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_BACKGROUND_SIZE = {150, 64}; +static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_SIZE = {1280, 80}; +static const ImVec2 IMGUI_ANIMATION_PREVIEW_SETTINGS_CHILD_SIZE = {150, 64}; +static const ImVec2 IMGUI_ANIMATION_PREVIEW_POSITION = {8, 110}; -static const ImVec2 IMGUI_TIMELINE_ELEMENT_LIST_SIZE = {260, 0}; -static const ImVec2 IMGUI_TIMELINE_ELEMENT_SIZE = {230, 64}; -static const ImVec2 IMGUI_TIMELINE_SPRITESHEET_ID_SIZE = {50, 20}; -static const ImVec2 IMGUI_TIMELINE_SHIFT_ARROWS_SIZE = {24, 48}; -static const ImVec2 IMGUI_TIMELINE_ELEMENT_NAME_SIZE = {125, 20}; +static const ImVec2 IMGUI_TIMELINE_FRAME_SIZE = {20, 40}; +static const ImVec2 IMGUI_TIMELINE_SIZE = {0, 0}; +static const ImVec2 IMGUI_TIMELINE_ELEMENTS_BAR_SIZE = {300, 32}; +static const ImVec2 IMGUI_TIMELINE_ELEMENTS_TIMELINE_SIZE = {0, 40}; +static const ImVec2 IMGUI_TIMELINE_FRAME_INDICES_SIZE = {0, 32}; +static const ImVec2 IMGUI_TIMELINE_ELEMENT_LIST_SIZE = {300, 0}; +static const ImVec2 IMGUI_TIMELINE_ELEMENT_SIZE = {300, 40}; +static const ImVec2 IMGUI_TIMELINE_SPRITESHEET_ID_SIZE = {40, 20}; +static const ImVec2 IMGUI_TIMELINE_SHIFT_ARROWS_SIZE = {64, 40}; +static const ImVec2 IMGUI_TIMELINE_ELEMENT_NAME_SIZE = {85, 20}; static const ImVec2 IMGUI_SPRITESHEET_SIZE = {0, 150}; +static const ImVec2 IMGUI_SPRITESHEET_PREVIEW_SIZE = {100, 100}; static const ImVec2 IMGUI_ICON_SIZE = {16, 16}; -static const ImVec2 IMGUI_ICON_BUTTON_SIZE = {22, 22}; +static const ImVec2 IMGUI_ICON_DUMMY_SIZE = {20, 16}; +static const ImVec2 IMGUI_ICON_BUTTON_SIZE = {24, 24}; +static const ImVec2 IMGUI_IMAGE_TARGET_SIZE = {125, 125}; static const ImVec2 IMGUI_DUMMY_SIZE = {1, 1}; #define IMGUI_TIMELINE_SHIFT_ARROWS_WIDTH (IMGUI_TIMELINE_SHIFT_ARROWS_SIZE.x * 1.35) @@ -38,13 +48,26 @@ static const ImVec2 IMGUI_DUMMY_SIZE = {1, 1}; struct Imgui { Dialog* dialog = NULL; - Packed* packed = NULL; + Resources* resources = NULL; + Input* input = NULL; Anm2* anm2 = NULL; Preview* preview = NULL; SDL_Window* window = NULL; + SDL_GLContext* glContext = NULL; }; -void imgui_init(SDL_Window* window, SDL_GLContext glContext); +void imgui_init +( + Imgui* self, + Dialog* dialog, + Resources* resources, + Input* input, + Anm2* anm2, + Preview* preview, + SDL_Window* window, + SDL_GLContext* glContext +); + void imgui_tick(Imgui* self); void imgui_draw(Imgui* self); void imgui_free(Imgui* self); diff --git a/src/input.cpp b/src/input.cpp new file mode 100644 index 0000000..1e1d94d --- /dev/null +++ b/src/input.cpp @@ -0,0 +1,54 @@ +#include "input.h" + +static void _mouse_tick(Mouse* self); + +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); + + state = SDL_GetMouseState(NULL, NULL); + + if (state & SDL_BUTTON_LMASK != 0) + { + self->current[MOUSE_LEFT] = true; + } + + if (state & SDL_BUTTON_RMASK != 0) + { + self->current[MOUSE_RIGHT] = true; + } + + SDL_GetMouseState(&self->position.x, &self->position.y); + + self->delta = self->position - self->oldPosition; + self->oldPosition = self->position; +} + +bool +mouse_press(Mouse* self, MouseType type) +{ + return (self->current[type] && !self->previous[type]); +} + +bool +mouse_held(Mouse* self, MouseType type) +{ + return (self->current[type] && self->previous[type]); +} + +bool +mouse_release(Mouse* self, MouseType type) +{ + return (!self->current[type] && self->previous[type]); +} + +void +input_tick(Input* self) +{ + _mouse_tick(&self->mouse); +} diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..3daeb31 --- /dev/null +++ b/src/input.h @@ -0,0 +1,35 @@ +#pragma once + +#include "COMMON.h" + +#define MOUSE_COUNT (MOUSE_RIGHT + 1) +enum MouseType +{ + MOUSE_LEFT, + MOUSE_RIGHT +}; + +enum InputType +{ + INPUT_MOUSE_CLICK +}; + +struct Mouse +{ + bool current[MOUSE_COUNT]; + bool previous[MOUSE_COUNT]; + vec2 position = {-1, -1}; + vec2 oldPosition = {-1, -1}; + vec2 delta = {-1, -1}; + s32 wheelDeltaY = 0; +}; + +struct Input +{ + Mouse mouse; +}; + +bool mouse_press(Mouse* self, MouseType type); +bool mouse_held(Mouse* self, MouseType type); +bool mouse_release(Mouse* self, MouseType type); +void input_tick(Input* self); \ No newline at end of file diff --git a/src/packed.cpp b/src/packed.cpp deleted file mode 100644 index 6a35bcc..0000000 --- a/src/packed.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "packed.h" - -/* Loads in packed data */ -void -packed_init(Packed* self) -{ - for (s32 i = 0; i < PACKED_TEXTURE_COUNT; i++) - texture_from_data_init(&self->textures[i], (u8*)PACKED_TEXTURE_DATA[i].data, PACKED_TEXTURE_DATA[i].length); -} - -/* Frees packed data */ -void -packed_free(Packed* self) -{ - for (s32 i = 0; i < PACKED_TEXTURE_COUNT; i++) - texture_free(&self->textures[i]); -} \ No newline at end of file diff --git a/src/packed.h b/src/packed.h deleted file mode 100644 index 220cc89..0000000 --- a/src/packed.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "PACKED.h" -#include "texture.h" - -struct Packed -{ - Texture textures[PACKED_TEXTURE_COUNT]; -}; - -void packed_init(Packed* self); -void packed_free(Packed* self); \ No newline at end of file diff --git a/src/preview.cpp b/src/preview.cpp index ce5d377..bd08186 100644 --- a/src/preview.cpp +++ b/src/preview.cpp @@ -1,10 +1,69 @@ #include "preview.h" +static void _preview_axis_set(Preview* self); +static void _preview_grid_set(Preview* self); -/* Initializes the preview */ -void -preview_init(Preview* self) +static void +_preview_axis_set(Preview* self) { + glBindVertexArray(self->axisVAO); + glBindBuffer(GL_ARRAY_BUFFER, self->axisVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(PREVIEW_AXIS_VERTICES), PREVIEW_AXIS_VERTICES, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); + + glBindVertexArray(0); +} + +static void +_preview_grid_set(Preview* self) +{ + std::vector vertices; + + s32 verticalLineCount = PREVIEW_SIZE.x / self->gridSize.x; + s32 horizontalLineCount = PREVIEW_SIZE.y / self->gridSize.y; + + /* Vertical */ + for (s32 i = 0; i <= verticalLineCount; i++) + { + s32 x = i * self->gridSize.x; + f32 normX = (2.0f * x) / PREVIEW_SIZE.x - 1.0f; + + vertices.push_back(normX); + vertices.push_back(-1.0f); + vertices.push_back(normX); + vertices.push_back(1.0f); + } + + /* Horizontal */ + for (s32 i = 0; i <= horizontalLineCount; i++) + { + s32 y = i * self->gridSize.y; + f32 normY = (2.0f * y) / PREVIEW_SIZE.y - 1.0f; + + vertices.push_back(-1.0f); + vertices.push_back(normY); + vertices.push_back(1.0f); + vertices.push_back(normY); + } + + self->gridVertexCount = (s32)vertices.size(); + + glBindVertexArray(self->gridVAO); + glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0); +} + +void +preview_init(Preview* self, Resources* resources, Input* input) +{ + self->resources = resources; + self->input = input; + glGenFramebuffers(1, &self->fbo); glBindFramebuffer(GL_FRAMEBUFFER, self->fbo); @@ -23,18 +82,98 @@ preview_init(Preview* self) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo); glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glGenVertexArrays(1, &self->axisVAO); + glGenBuffers(1, &self->axisVBO); + + glGenVertexArrays(1, &self->gridVAO); + glGenBuffers(1, &self->gridVBO); + + _preview_axis_set(self); + _preview_grid_set(self); +} + +void +preview_tick(Preview* self) +{ + self->zoom = CLAMP(self->zoom, PREVIEW_ZOOM_MIN, PREVIEW_ZOOM_MAX); + self->oldGridSize = self->gridSize; } void preview_draw(Preview* self) { + GLuint shader = self->resources->shaders[SHADER]; + float zoomFactor = self->zoom / 100.0f; + + /* Convert pan to pixels */ + glm::vec2 ndcPan = glm::vec2( + self->pan.x / (PREVIEW_SIZE.x / 2.0f), + -self->pan.y / (PREVIEW_SIZE.y / 2.0f) + ); + + /* Transformation matrix */ + glm::mat4 transform = glm::translate(glm::mat4(1.0f), glm::vec3(ndcPan, 0.0f)); + transform = glm::scale(transform, glm::vec3(zoomFactor, zoomFactor, 1.0f)); + glBindFramebuffer(GL_FRAMEBUFFER, self->fbo); glViewport(0, 0, PREVIEW_SIZE.x, PREVIEW_SIZE.y); - glClearColor(self->color.r, self->color.g, self->color.b, self->color.a); + glClearColor + ( + self->backgroundColor.r, + self->backgroundColor.g, + self->backgroundColor.b, + self->backgroundColor.a + ); glClear(GL_COLOR_BUFFER_BIT); - glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind + glUseProgram(shader); + glUniformMatrix4fv(glGetUniformLocation(shader, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, (f32*)value_ptr(transform)); + + if (self->isGrid) + { + if (self->gridSize != self->oldGridSize) + _preview_grid_set(self); + + glBindVertexArray(self->gridVAO); + + glUniform4f + ( + glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), + self->gridColor.r, self->gridColor.g, self->gridColor.b, self->gridColor.a + ); + + glDrawArrays(GL_LINES, 0, self->gridVertexCount); + + glBindVertexArray(0); + } + + if (self->isAxis) + { + glBindVertexArray(self->axisVAO); + + /* Axes */ + glUniform4f + ( + glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), + self->axisColor.r, self->axisColor.g, self->axisColor.b, self->axisColor.a + ); + glDrawArrays(GL_LINES, 0, 2); + + glUniform4f + ( + glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), + self->axisColor.r, self->axisColor.g, self->axisColor.b, self->axisColor.a + ); + glDrawArrays(GL_LINES, 2, 2); + + glBindVertexArray(0); + } + glUseProgram(0); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } void diff --git a/src/preview.h b/src/preview.h index 17c9c56..b1436d1 100644 --- a/src/preview.h +++ b/src/preview.h @@ -1,26 +1,50 @@ #pragma once -#include "texture.h" +#include "resources.h" +#include "input.h" -static const ivec2 PREVIEW_SIZE = {1280, 720}; +static const ivec2 PREVIEW_SIZE = {960, 720}; +static const ivec2 PREVIEW_CENTER = {0, 0}; -#define PREVIEW_ZOOM_MIN 0 +#define PREVIEW_ZOOM_MIN 1 #define PREVIEW_ZOOM_MAX 400 -#define PREVIEW_GRID_MIN 0 -#define PREVIEW_GRID_MAX 20 +#define PREVIEW_GRID_MIN 1 +#define PREVIEW_GRID_MAX 50 +#define PREVIEW_ZOOM_STEP 10 + +static const f32 PREVIEW_AXIS_VERTICES[] = +{ + -1.0f, 0.0f, + 1.0f, 0.0f, + 0.0f, -1.0f, + 0.0f, 1.0f +}; struct Preview { GLuint texture; GLuint fbo; GLuint rbo; - bool isGrid; + GLuint gridVAO; + GLuint gridVBO; + GLuint axisVAO; + GLuint axisVBO; + Input* input; + Resources* resources; + bool isGrid = false; + bool isAxis = true; + ivec2 viewport = PREVIEW_SIZE; f32 zoom = 100; + vec2 pan = PREVIEW_CENTER; ivec2 gridSize = {10, 10}; - vec4 color = {0.69, 0.69, 0.69, 1.0f}; - vec4 gridColor = {0.35, 0.35, 0.35, 1.0f}; + ivec2 oldGridSize = {-1, -1}; + s32 gridVertexCount = -1; + vec4 backgroundColor = {0.113, 0.184, 0.286, 1.0f}; + vec4 gridColor = {1.0, 1.0, 1.0, 0.125f}; + vec4 axisColor = {1.0, 1.0, 1.0, 0.5f}; }; -void preview_init(Preview* self); +void preview_init(Preview* self, Resources* resources, Input* input); void preview_draw(Preview* self); +void preview_tick(Preview* self); void preview_free(Preview* self); diff --git a/src/resources.cpp b/src/resources.cpp new file mode 100644 index 0000000..0ee28f5 --- /dev/null +++ b/src/resources.cpp @@ -0,0 +1,33 @@ +#include "resources.h" + +/* Loads in resources */ +void +resources_init(Resources* self) +{ + /* Textures */ + for (s32 i = 0; i < TEXTURE_COUNT; i++) + texture_from_data_init(&self->textures[i], (u8*)TEXTURE_DATA[i].data, TEXTURE_DATA[i].length); + + for (s32 i = 0; i < SHADER_COUNT; i++) + shader_init(&self->shaders[i], SHADER_DATA[i].vertex, SHADER_DATA[i].fragment); +} + +/* Frees resources*/ +void +resources_free(Resources* self) +{ + for (s32 i = 0; i < TEXTURE_COUNT; i++) + texture_free(&self->textures[i]); + + for (s32 i = 0; i < SHADER_COUNT; i++) + shader_free(&self->shaders[i]); + + resources_loaded_textures_free(self); +} + +void +resources_loaded_textures_free(Resources* self) +{ + for (auto & [id, texture] : self->loadedTextures) + texture_free(&self->textures[id]); +} \ No newline at end of file diff --git a/src/resources.h b/src/resources.h new file mode 100644 index 0000000..fcc402b --- /dev/null +++ b/src/resources.h @@ -0,0 +1,16 @@ +#pragma once + +#include "RESOURCES.h" +#include "texture.h" +#include "shader.h" + +struct Resources +{ + Texture textures[TEXTURE_COUNT]; + GLuint shaders[SHADER_COUNT]; + std::map loadedTextures; +}; + +void resources_init(Resources* self); +void resources_free(Resources* self); +void resources_loaded_textures_free(Resources* self); diff --git a/src/shader.cpp b/src/shader.cpp index 1c58b08..1afa350 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -1,24 +1,24 @@ #include "shader.h" -static bool _shader_compile(GLuint handle, const char* text); +static bool _shader_compile(GLuint* self, const char* text); static bool -_shader_compile(GLuint handle, const char* text) +_shader_compile(GLuint* self, const char* text) { char compileLog[SHADER_BUFFER_MAX]; s32 isCompile; const GLchar* source = text; - glShaderSource(handle, 1, &source, NULL); + glShaderSource(*self, 1, &source, NULL); - glCompileShader(handle); - glGetShaderiv(handle, GL_COMPILE_STATUS, &isCompile); + glCompileShader(*self); + glGetShaderiv(*self, GL_COMPILE_STATUS, &isCompile); if (!isCompile) { - glGetShaderInfoLog(handle, SHADER_BUFFER_MAX, NULL, compileLog); - printf(STRING_ERROR_SHADER_INIT, handle, compileLog); + glGetShaderInfoLog(*self, SHADER_BUFFER_MAX, NULL, compileLog); + printf(STRING_ERROR_SHADER_INIT, *self, compileLog); return false; } @@ -26,47 +26,39 @@ _shader_compile(GLuint handle, const char* text) } bool -shader_init(Shader* self, const char* vertex, const char* fragment) +shader_init(GLuint* self, const char* vertex, const char* fragment) { GLuint vertexHandle; GLuint fragmentHandle; bool isSuccess; - memset(self, '\0', sizeof(Shader)); - vertexHandle = glCreateShader(GL_VERTEX_SHADER); fragmentHandle = glCreateShader(GL_FRAGMENT_SHADER); if ( - !_shader_compile(vertexHandle, vertex) || - !_shader_compile(fragmentHandle, fragment) + !_shader_compile(&vertexHandle, vertex) || + !_shader_compile(&fragmentHandle, fragment) ) return false; - self->handle = glCreateProgram(); + *self = glCreateProgram(); - glAttachShader(self->handle, vertexHandle); - glAttachShader(self->handle, fragmentHandle); + glAttachShader(*self, vertexHandle); + glAttachShader(*self, fragmentHandle); - glLinkProgram(self->handle); + glLinkProgram(*self); glDeleteShader(vertexHandle); glDeleteShader(fragmentHandle); - printf(STRING_INFO_SHADER_INIT, self->handle); + printf(STRING_INFO_SHADER_INIT, *self); return true; } void -shader_use(Shader* self) +shader_free(GLuint* self) { - glUseProgram(self->handle); -} - -void -shader_free(Shader* self) -{ - glDeleteProgram(self->handle); -} + glDeleteProgram(*self); +} \ No newline at end of file diff --git a/src/shader.h b/src/shader.h index f85e69d..3c09b26 100644 --- a/src/shader.h +++ b/src/shader.h @@ -4,11 +4,5 @@ #define SHADER_BUFFER_MAX 2048 -struct Shader -{ - GLuint handle = 0; -}; - -bool shader_init(Shader* self, const char* vertex, const char* fragment); -void shader_free(Shader* self); -void shader_use(Shader* self); +bool shader_init(GLuint* self, const char* vertex, const char* fragment); +void shader_free(GLuint* self); \ No newline at end of file diff --git a/src/state.cpp b/src/state.cpp index 5f3c1b8..4b52d74 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -7,15 +7,30 @@ static void _tick(State* state) { SDL_Event event; + SDL_MouseWheelEvent* mouseWheelEvent; + + state->input.mouse.wheelDeltaY = 0; while(SDL_PollEvent(&event)) { - ImGui_ImplSDL3_ProcessEvent(&event); + ImGui_ImplSDL3_ProcessEvent(&event); - if (event.type == SDL_EVENT_QUIT) - state->isRunning = false; + switch (event.type) + { + case SDL_EVENT_QUIT: + state->isRunning = false; + break; + case SDL_EVENT_MOUSE_WHEEL: + mouseWheelEvent = &event.wheel; + state->input.mouse.wheelDeltaY = mouseWheelEvent->y; + break; + default: + break; + } } + input_tick(&state->input); + preview_tick(&state->preview); dialog_tick(&state->dialog); imgui_tick(&state->imgui); } @@ -32,8 +47,6 @@ _draw(State* state) void init(State* state) { - Shader shader; - printf(STRING_INFO_INIT); if (!SDL_Init(SDL_INIT_VIDEO)) @@ -64,31 +77,34 @@ init(State* state) glewInit(); - preview_init(&state->preview); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); printf(STRING_INFO_GLEW_INIT); - - packed_init(&state->packed); + + resources_init(&state->resources); + dialog_init(&state->dialog, &state->anm2, &state->resources); + + preview_init(&state->preview, &state->resources, &state->input); if (state->isArgument) - anm2_deserialize(&state->anm2, state->argument); + anm2_deserialize(&state->anm2, &state->resources, state->argument); else anm2_new(&state->anm2); window_title_from_anm2_set(state->window, &state->anm2); - state->imgui = - { + imgui_init + ( + &state->imgui, &state->dialog, - &state->packed, - &state->anm2, + &state->resources, + &state->input, + &state->anm2, &state->preview, - state->window - }; - - imgui_init(state->window, state->glContext); - - state->dialog.anm2 = &state->anm2; + state->window, + &state->glContext + ); } void @@ -104,7 +120,6 @@ loop(State* state) SDL_Delay(TICK_DELAY - (state->tick - state->lastTick)); _tick(state); - _draw(state); state->lastTick = state->tick; @@ -116,13 +131,11 @@ quit(State* state) { imgui_free(&state->imgui); preview_free(&state->preview); - packed_free(&state->packed); + resources_free(&state->resources); SDL_GL_DestroyContext(state->glContext); SDL_Quit(); - printf(STRING_INFO_SDL_QUIT); - printf(STRING_INFO_QUIT); } diff --git a/src/state.h b/src/state.h index 1e79174..a5b5a30 100644 --- a/src/state.h +++ b/src/state.h @@ -14,10 +14,11 @@ struct State SDL_Renderer* renderer; SDL_GLContext glContext; Imgui imgui; + Input input; Dialog dialog; Preview preview; Anm2 anm2; - Packed packed; + Resources resources; char argument[PATH_MAX] = STRING_EMPTY; bool isArgument = false; u64 tick = 0; diff --git a/src/texture.cpp b/src/texture.cpp index fa07ba2..7f436ef 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -63,4 +63,4 @@ texture_free(Texture* self) { glDeleteTextures(1, &self->handle); memset(self, '\0', sizeof(Texture)); -} +} \ No newline at end of file