The Omega Update(TM) Part 5 (Finishing)
This commit is contained in:
		@@ -10,9 +10,10 @@ A reimplementation of *The Binding of Isaac: Rebirth*'s proprietary animation ed
 | 
				
			|||||||
- New features
 | 
					- New features
 | 
				
			||||||
    - Can output .webm or *.png sequence
 | 
					    - Can output .webm or *.png sequence
 | 
				
			||||||
    - Cutting, copying and pasting
 | 
					    - Cutting, copying and pasting
 | 
				
			||||||
 | 
					    - Additional wizard options
 | 
				
			||||||
    - Robust snapshot (undo/redo) system
 | 
					    - Robust snapshot (undo/redo) system
 | 
				
			||||||
    - Additional hotkeys/shortcuts
 | 
					    - Additional hotkeys/shortcuts
 | 
				
			||||||
    - Settings that will preserve on exit
 | 
					    - Settings that will preserve on exit (stored in %APPDATA% on Windows or ~/.local/share on Linux)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Dependencies
 | 
					## Dependencies
 | 
				
			||||||
Download these from your package manager:
 | 
					Download these from your package manager:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										72
									
								
								src/COMMON.h
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								src/COMMON.h
									
									
									
									
									
								
							@@ -23,7 +23,9 @@
 | 
				
			|||||||
#include <ranges>                      
 | 
					#include <ranges>                      
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <unordered_set>                      
 | 
					#include <unordered_set>                      
 | 
				
			||||||
#include <vector>                      
 | 
					#include <vector>                  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef uint8_t u8;
 | 
					typedef uint8_t u8;
 | 
				
			||||||
typedef uint16_t u16;
 | 
					typedef uint16_t u16;
 | 
				
			||||||
@@ -123,6 +125,65 @@ static inline bool string_to_bool(const std::string& string)
 | 
				
			|||||||
    return lower == "true";
 | 
					    return lower == "true";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline std::string path_canonical_resolve
 | 
				
			||||||
 | 
					(
 | 
				
			||||||
 | 
					    const std::string& inputPath,
 | 
				
			||||||
 | 
					    const std::string& basePath = std::filesystem::current_path().string()
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    auto strings_equal_ignore_case = [](std::string a, std::string b) {
 | 
				
			||||||
 | 
					        auto to_lower = [](unsigned char c) { return static_cast<char>(std::tolower(c)); };
 | 
				
			||||||
 | 
					        std::transform(a.begin(), a.end(), a.begin(), to_lower);
 | 
				
			||||||
 | 
					        std::transform(b.begin(), b.end(), b.begin(), to_lower);
 | 
				
			||||||
 | 
					        return a == b;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string sanitized = inputPath;
 | 
				
			||||||
 | 
					    std::replace(sanitized.begin(), sanitized.end(), '\\', '/');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::filesystem::path normalizedPath = sanitized;
 | 
				
			||||||
 | 
					    std::filesystem::path absolutePath = normalizedPath.is_absolute()
 | 
				
			||||||
 | 
					        ? normalizedPath
 | 
				
			||||||
 | 
					        : (std::filesystem::path(basePath) / normalizedPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::error_code error;
 | 
				
			||||||
 | 
					    if (std::filesystem::exists(absolutePath, error)) {
 | 
				
			||||||
 | 
					        std::error_code canonicalError;
 | 
				
			||||||
 | 
					        std::filesystem::path canonicalPath = std::filesystem::weakly_canonical(absolutePath, canonicalError);
 | 
				
			||||||
 | 
					        return (canonicalError ? absolutePath : canonicalPath).generic_string();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::filesystem::path resolvedPath = absolutePath.root_path();
 | 
				
			||||||
 | 
					    std::filesystem::path remainingPath = absolutePath.relative_path();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const std::filesystem::path& segment : remainingPath) {
 | 
				
			||||||
 | 
					        std::filesystem::path candidatePath = resolvedPath / segment;
 | 
				
			||||||
 | 
					        if (std::filesystem::exists(candidatePath, error)) {
 | 
				
			||||||
 | 
					            resolvedPath = candidatePath;
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool matched = false;
 | 
				
			||||||
 | 
					        if (std::filesystem::exists(resolvedPath, error) && std::filesystem::is_directory(resolvedPath, error)) {
 | 
				
			||||||
 | 
					            for (const auto& directoryEntry : std::filesystem::directory_iterator(resolvedPath, error)) {
 | 
				
			||||||
 | 
					                if (strings_equal_ignore_case(directoryEntry.path().filename().string(), segment.string())) {
 | 
				
			||||||
 | 
					                    resolvedPath = directoryEntry.path();
 | 
				
			||||||
 | 
					                    matched = true;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!matched) return sanitized; 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!std::filesystem::exists(resolvedPath, error))
 | 
				
			||||||
 | 
					        return sanitized;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::error_code canonicalError;
 | 
				
			||||||
 | 
					    std::filesystem::path canonicalPath = std::filesystem::weakly_canonical(resolvedPath, canonicalError);
 | 
				
			||||||
 | 
					    return (canonicalError ? resolvedPath : canonicalPath).generic_string();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline std::string working_directory_from_file_set(const std::string& path)
 | 
					static inline std::string working_directory_from_file_set(const std::string& path)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    std::filesystem::path filePath = path;
 | 
					    std::filesystem::path filePath = path;
 | 
				
			||||||
@@ -138,13 +199,20 @@ static inline std::string path_extension_change(const std::string& path, const s
 | 
				
			|||||||
    return filePath.string();
 | 
					    return filePath.string();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool path_is_extension(const std::string& path, const std::string& extension)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    auto e = std::filesystem::path(path).extension().string();
 | 
				
			||||||
 | 
					    std::transform(e.begin(), e.end(), e.begin(), ::tolower);
 | 
				
			||||||
 | 
					    return e == ("." + extension);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline bool path_exists(const std::filesystem::path& pathCheck)
 | 
					static inline bool path_exists(const std::filesystem::path& pathCheck)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    std::error_code errorCode;
 | 
					    std::error_code errorCode;
 | 
				
			||||||
    return std::filesystem::exists(pathCheck, errorCode) && ((void)std::filesystem::status(pathCheck, errorCode), !errorCode);
 | 
					    return std::filesystem::exists(pathCheck, errorCode) && ((void)std::filesystem::status(pathCheck, errorCode), !errorCode);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline bool path_valid(const std::filesystem::path& pathCheck)
 | 
					static inline bool path_is_valid(const std::filesystem::path& pathCheck)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    namespace fs = std::filesystem;
 | 
					    namespace fs = std::filesystem;
 | 
				
			||||||
    std::error_code ec;
 | 
					    std::error_code ec;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										51
									
								
								src/PACKED.h
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								src/PACKED.h
									
									
									
									
									
								
							@@ -217,6 +217,7 @@ enum ShaderType
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    SHADER_LINE,
 | 
					    SHADER_LINE,
 | 
				
			||||||
    SHADER_TEXTURE,
 | 
					    SHADER_TEXTURE,
 | 
				
			||||||
 | 
					    SHADER_GRID,
 | 
				
			||||||
    SHADER_COUNT
 | 
					    SHADER_COUNT
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -259,14 +260,62 @@ void main()
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
)";
 | 
					)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const std::string SHADER_GRID_VERTEX = R"(
 | 
				
			||||||
 | 
					#version 330 core
 | 
				
			||||||
 | 
					layout ( location = 0 ) in vec2 i_position;
 | 
				
			||||||
 | 
					out vec2 clip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void main() {
 | 
				
			||||||
 | 
					    clip = i_position;
 | 
				
			||||||
 | 
					    gl_Position = vec4(i_position, 0.0, 1.0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const std::string SHADER_GRID_FRAGMENT = R"(
 | 
				
			||||||
 | 
					#version 330 core
 | 
				
			||||||
 | 
					in vec2 clip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform mat4 u_model;   // inverse of your world->clip matrix (MVP)
 | 
				
			||||||
 | 
					uniform vec2 u_size;     // world-space cell size (e.g. 64,64)
 | 
				
			||||||
 | 
					uniform vec2 u_offset;   // world-space grid offset (shifts entire grid)
 | 
				
			||||||
 | 
					uniform vec4 u_color;    // RGBA
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out vec4 o_fragColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void main() 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // clip -> world on z=0 plane
 | 
				
			||||||
 | 
					    vec4 w = u_model * vec4(clip, 0.0, 1.0);
 | 
				
			||||||
 | 
					    w /= w.w;
 | 
				
			||||||
 | 
					    vec2 world = w.xy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // grid space
 | 
				
			||||||
 | 
					    vec2 g = (world - u_offset) / u_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vec2 d = abs(fract(g) - 0.5);
 | 
				
			||||||
 | 
					    float distance = min(d.x, d.y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float fw = min(fwidth(g.x), fwidth(g.y));
 | 
				
			||||||
 | 
					    float alpha = 1.0 - smoothstep(0.0, fw, distance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (alpha <= 0.0) discard;
 | 
				
			||||||
 | 
					    o_fragColor = vec4(u_color.rgb, u_color.a * alpha);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SHADER_UNIFORM_COLOR "u_color"
 | 
					#define SHADER_UNIFORM_COLOR "u_color"
 | 
				
			||||||
#define SHADER_UNIFORM_TRANSFORM "u_transform"
 | 
					#define SHADER_UNIFORM_TRANSFORM "u_transform"
 | 
				
			||||||
#define SHADER_UNIFORM_TINT "u_tint"
 | 
					#define SHADER_UNIFORM_TINT "u_tint"
 | 
				
			||||||
#define SHADER_UNIFORM_COLOR_OFFSET "u_color_offset"
 | 
					#define SHADER_UNIFORM_COLOR_OFFSET "u_color_offset"
 | 
				
			||||||
 | 
					#define SHADER_UNIFORM_OFFSET "u_offset"
 | 
				
			||||||
 | 
					#define SHADER_UNIFORM_SIZE "u_size"
 | 
				
			||||||
 | 
					#define SHADER_UNIFORM_MODEL "u_model"
 | 
				
			||||||
#define SHADER_UNIFORM_TEXTURE "u_texture"
 | 
					#define SHADER_UNIFORM_TEXTURE "u_texture"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ShaderData SHADER_DATA[SHADER_COUNT] = 
 | 
					const ShaderData SHADER_DATA[SHADER_COUNT] = 
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  {SHADER_VERTEX, SHADER_FRAGMENT},
 | 
					  {SHADER_VERTEX, SHADER_FRAGMENT},
 | 
				
			||||||
  {SHADER_VERTEX, SHADER_TEXTURE_FRAGMENT}
 | 
					  {SHADER_VERTEX, SHADER_TEXTURE_FRAGMENT},
 | 
				
			||||||
 | 
					  {SHADER_GRID_VERTEX, SHADER_GRID_FRAGMENT}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
							
								
								
									
										61
									
								
								src/anm2.cpp
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								src/anm2.cpp
									
									
									
									
									
								
							@@ -588,8 +588,8 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
 | 
				
			|||||||
			xmlAttribute = xmlAttribute->Next();
 | 
								xmlAttribute = xmlAttribute->Next();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (anm2Element == ANM2_ELEMENT_SPRITESHEET) 
 | 
							if (anm2Element == ANM2_ELEMENT_SPRITESHEET && resources) 
 | 
				
			||||||
			resources_texture_init(resources, spritesheet->path , id);
 | 
								resources_texture_init(resources, spritesheet->path, id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		xmlChild = xmlElement->FirstChildElement();
 | 
							xmlChild = xmlElement->FirstChildElement();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -630,14 +630,6 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void anm2_layer_add(Anm2* self)
 | 
					void anm2_layer_add(Anm2* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	s32 id = map_next_id_get(self->layers);
 | 
						s32 id = map_next_id_get(self->layers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	self->layers[id] = Anm2Layer{};
 | 
						self->layers[id] = Anm2Layer{};
 | 
				
			||||||
@@ -1126,4 +1118,53 @@ void anm2_frame_bake(Anm2* self, Anm2Reference* reference, s32 interval, bool is
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		delay += baked.delay;
 | 
							delay += baked.delay;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void anm2_scale(Anm2* self, f32 scale)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					 	auto frame_scale = [&](Anm2Frame& frame)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							frame.position = vec2((s32)(frame.position.x * scale), (s32)(frame.position.y * scale));
 | 
				
			||||||
 | 
							frame.size = vec2((s32)(frame.size.x * scale), (s32)(frame.size.y * scale));
 | 
				
			||||||
 | 
							frame.crop = vec2((s32)(frame.crop.x * scale), (s32)(frame.crop.y * scale));
 | 
				
			||||||
 | 
							frame.pivot = vec2((s32)(frame.pivot.x * scale), (s32)(frame.pivot.y * scale));
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (auto& [_, animation] : self->animations)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							for (auto& frame : animation.rootAnimation.frames)
 | 
				
			||||||
 | 
								frame_scale(frame);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
							for (auto& [_, layerAnimation] : animation.layerAnimations)
 | 
				
			||||||
 | 
									for (auto& frame : layerAnimation.frames)
 | 
				
			||||||
 | 
										frame_scale(frame);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (auto& [_, nullAnimation] : animation.nullAnimations)
 | 
				
			||||||
 | 
									for (auto& frame : nullAnimation.frames)
 | 
				
			||||||
 | 
										frame_scale(frame);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void anm2_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPosition, vec2 size, vec2 pivot, s32 columns, s32 count, s32 delay)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Anm2Item* item = anm2_item_from_reference(self, reference);
 | 
				
			||||||
 | 
						if (!item) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Anm2Reference frameReference = *reference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (s32 i = 0; i < count; i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const s32 row = i / columns;
 | 
				
			||||||
 | 
							const s32 column = i % columns;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Anm2Frame frame{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							frame.delay = delay;
 | 
				
			||||||
 | 
							frame.pivot = pivot;
 | 
				
			||||||
 | 
							frame.size = size;
 | 
				
			||||||
 | 
					        frame.crop  = startPosition + vec2(size.x * column, size.y * row);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							anm2_frame_add(self, &frame, &frameReference);
 | 
				
			||||||
 | 
							frameReference.frameIndex++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								src/anm2.h
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/anm2.h
									
									
									
									
									
								
							@@ -20,6 +20,9 @@
 | 
				
			|||||||
#define ANM2_WRITE_INFO "Wrote anm2 to file: {}"
 | 
					#define ANM2_WRITE_INFO "Wrote anm2 to file: {}"
 | 
				
			||||||
#define ANM2_CREATED_ON_FORMAT "%d-%B-%Y %I:%M:%S %p"
 | 
					#define ANM2_CREATED_ON_FORMAT "%d-%B-%Y %I:%M:%S %p"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ANM2_EXTENSION "anm2"
 | 
				
			||||||
 | 
					#define ANM2_SPRITESHEET_EXTENSION "png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Elements */
 | 
					/* Elements */
 | 
				
			||||||
#define ANM2_ELEMENT_LIST \
 | 
					#define ANM2_ELEMENT_LIST \
 | 
				
			||||||
    X(ANIMATED_ACTOR,     "AnimatedActor")     \
 | 
					    X(ANIMATED_ACTOR,     "AnimatedActor")     \
 | 
				
			||||||
@@ -151,7 +154,7 @@ struct Anm2Frame
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	bool isVisible = true;
 | 
						bool isVisible = true;
 | 
				
			||||||
	bool isInterpolated = false;
 | 
						bool isInterpolated = false;
 | 
				
			||||||
	f32 rotation = 1.0f;
 | 
						f32 rotation{};
 | 
				
			||||||
	s32 delay = ANM2_FRAME_DELAY_MIN;
 | 
						s32 delay = ANM2_FRAME_DELAY_MIN;
 | 
				
			||||||
    s32 atFrame = INDEX_NONE;
 | 
					    s32 atFrame = INDEX_NONE;
 | 
				
			||||||
    s32 eventID = ID_NONE;
 | 
					    s32 eventID = ID_NONE;
 | 
				
			||||||
@@ -159,7 +162,7 @@ struct Anm2Frame
 | 
				
			|||||||
	vec2 pivot{};
 | 
						vec2 pivot{};
 | 
				
			||||||
	vec2 position{};
 | 
						vec2 position{};
 | 
				
			||||||
	vec2 size{};
 | 
						vec2 size{};
 | 
				
			||||||
	vec2 scale{};
 | 
						vec2 scale = {100, 100};
 | 
				
			||||||
	vec3 offsetRGB{};
 | 
						vec3 offsetRGB{};
 | 
				
			||||||
	vec4 tintRGBA = {1.0f, 1.0f, 1.0f, 1.0f};
 | 
						vec4 tintRGBA = {1.0f, 1.0f, 1.0f, 1.0f};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -269,7 +272,7 @@ Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* referenc
 | 
				
			|||||||
Anm2Item* anm2_item_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_from_reference(Anm2* self, Anm2Reference* reference);
 | 
				
			||||||
s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time);
 | 
					s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time);
 | 
				
			||||||
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference, s32 time);
 | 
					Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference, s32 time = 0.0f);
 | 
				
			||||||
void anm2_frame_erase(Anm2* self, Anm2Reference* reference);
 | 
					void anm2_frame_erase(Anm2* self, Anm2Reference* reference);
 | 
				
			||||||
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time);
 | 
					void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time);
 | 
				
			||||||
void anm2_reference_clear(Anm2Reference* self);
 | 
					void anm2_reference_clear(Anm2Reference* self);
 | 
				
			||||||
@@ -279,4 +282,6 @@ s32 anm2_animation_length_get(Anm2Animation* self);
 | 
				
			|||||||
void anm2_animation_length_set(Anm2Animation* self);
 | 
					void anm2_animation_length_set(Anm2Animation* self);
 | 
				
			||||||
void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& mergeIDs, Anm2MergeType type);
 | 
					void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& mergeIDs, Anm2MergeType type);
 | 
				
			||||||
void anm2_frame_bake(Anm2* self, Anm2Reference* reference, s32 interval, bool isRoundScale, bool isRoundRotation);
 | 
					void anm2_frame_bake(Anm2* self, Anm2Reference* reference, s32 interval, bool isRoundScale, bool isRoundRotation);
 | 
				
			||||||
void anm2_item_frame_set(Anm2* self, Anm2Reference* reference, const Anm2FrameChange& change, Anm2ChangeType type, s32 start, s32 count);
 | 
					void anm2_item_frame_set(Anm2* self, Anm2Reference* reference, const Anm2FrameChange& change, Anm2ChangeType type, s32 start, s32 count);
 | 
				
			||||||
 | 
					void anm2_scale(Anm2* self, f32 scale);
 | 
				
			||||||
 | 
					void anm2_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPosition, vec2 size, vec2 pivot, s32 columns, s32 count, s32 delay);
 | 
				
			||||||
@@ -69,6 +69,19 @@ void canvas_init(Canvas* self, const vec2& size)
 | 
				
			|||||||
    glEnableVertexAttribArray(0);
 | 
					    glEnableVertexAttribArray(0);
 | 
				
			||||||
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
 | 
					    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(f32), (void*)0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Grid
 | 
				
			||||||
 | 
					    glGenVertexArrays(1, &self->gridVAO);
 | 
				
			||||||
 | 
					    glBindVertexArray(self->gridVAO);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    glGenBuffers(1, &self->gridVBO);
 | 
				
			||||||
 | 
					    glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
 | 
				
			||||||
 | 
					    glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_GRID_VERTICES), CANVAS_GRID_VERTICES, GL_STATIC_DRAW);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    glEnableVertexAttribArray(0);
 | 
				
			||||||
 | 
					    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    glBindVertexArray(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Texture
 | 
					    // Texture
 | 
				
			||||||
    glGenVertexArrays(1, &self->textureVAO);
 | 
					    glGenVertexArrays(1, &self->textureVAO);
 | 
				
			||||||
    glGenBuffers(1, &self->textureVBO);
 | 
					    glGenBuffers(1, &self->textureVBO);
 | 
				
			||||||
@@ -93,7 +106,7 @@ void canvas_init(Canvas* self, const vec2& size)
 | 
				
			|||||||
    _canvas_texture_init(self, size);
 | 
					    _canvas_texture_init(self, size);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mat4 canvas_transform_get(Canvas* self, vec2& pan, f32& zoom, OriginType origin)
 | 
					mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    f32 zoomFactor = PERCENT_TO_UNIT(zoom);
 | 
					    f32 zoomFactor = PERCENT_TO_UNIT(zoom);
 | 
				
			||||||
    mat4 projection = glm::ortho(0.0f, self->size.x, 0.0f, self->size.y, -1.0f, 1.0f);
 | 
					    mat4 projection = glm::ortho(0.0f, self->size.x, 0.0f, self->size.y, -1.0f, 1.0f);
 | 
				
			||||||
@@ -136,55 +149,21 @@ void canvas_texture_set(Canvas* self)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, f32& zoom, ivec2& size, ivec2& offset, vec4& color)
 | 
					void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (size.x <= 0 || size.y <= 0)
 | 
					    mat4 inverseTransform = glm::inverse(transform);
 | 
				
			||||||
        return; // avoid div-by-zero
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::vector<f32> vertices;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    vec2 gridSize = self->size * (PERCENT_TO_UNIT(CANVAS_ZOOM_MAX - zoom));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // First visible vertical line <= 0
 | 
					 | 
				
			||||||
    s32 startX = -(offset.x % size.x);
 | 
					 | 
				
			||||||
    if (startX > 0) startX -= size.x;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (s32 x = startX; x <= gridSize.x; x += size.x)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        vertices.push_back((f32)x);
 | 
					 | 
				
			||||||
        vertices.push_back(0.0f);
 | 
					 | 
				
			||||||
        vertices.push_back((f32)x);
 | 
					 | 
				
			||||||
        vertices.push_back((f32)gridSize.y);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // First visible horizontal line <= 0
 | 
					 | 
				
			||||||
    s32 startY = -(offset.y % size.y);
 | 
					 | 
				
			||||||
    if (startY > 0) startY -= size.y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (s32 y = startY; y <= gridSize.y; y += size.y)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        vertices.push_back(0.0f);
 | 
					 | 
				
			||||||
        vertices.push_back((f32)y);
 | 
					 | 
				
			||||||
        vertices.push_back((f32)gridSize.x);
 | 
					 | 
				
			||||||
        vertices.push_back((f32)y);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    s32 vertexCount = (s32)vertices.size() / 2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (vertexCount == 0)
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    glBindVertexArray(self->gridVAO);
 | 
					 | 
				
			||||||
    glBindBuffer(GL_ARRAY_BUFFER, self->gridVBO);
 | 
					 | 
				
			||||||
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(f32), vertices.data(), GL_DYNAMIC_DRAW);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    glUseProgram(shader);
 | 
					    glUseProgram(shader);
 | 
				
			||||||
    glBindVertexArray(self->gridVAO);
 | 
					 | 
				
			||||||
    glUniformMatrix4fv(glGetUniformLocation(shader, SHADER_UNIFORM_TRANSFORM), 1, GL_FALSE, value_ptr(transform));
 | 
					 | 
				
			||||||
    glUniform4f(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR), color.r, color.g, color.b, color.a);
 | 
					 | 
				
			||||||
    glDrawArrays(GL_LINES, 0, vertexCount);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    glUniformMatrix4fv(glGetUniformLocation(shader, SHADER_UNIFORM_MODEL), 1, GL_FALSE, glm::value_ptr(inverseTransform));
 | 
				
			||||||
 | 
					    glUniform2f(glGetUniformLocation(shader, SHADER_UNIFORM_SIZE),   size.x, size.y);
 | 
				
			||||||
 | 
					    glUniform2f(glGetUniformLocation(shader, SHADER_UNIFORM_OFFSET), offset.x, offset.y);
 | 
				
			||||||
 | 
					    glUniform4f(glGetUniformLocation(shader, SHADER_UNIFORM_COLOR),  color.r, color.g, color.b, color.a);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    glBindVertexArray(self->gridVAO);
 | 
				
			||||||
 | 
					    glDrawArrays(GL_TRIANGLES, 0, 3);
 | 
				
			||||||
    glBindVertexArray(0);
 | 
					    glBindVertexArray(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    glUseProgram(0);
 | 
					    glUseProgram(0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								src/canvas.h
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/canvas.h
									
									
									
									
									
								
							@@ -14,6 +14,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static const vec2 CANVAS_GRID_SIZE = {3200, 1600};
 | 
					static const vec2 CANVAS_GRID_SIZE = {3200, 1600};
 | 
				
			||||||
static const vec2 CANVAS_PIVOT_SIZE = {8, 8};
 | 
					static const vec2 CANVAS_PIVOT_SIZE = {8, 8};
 | 
				
			||||||
 | 
					static const vec2 CANVAS_SCALE_DEFAULT = {1.0f, 1.0f};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const f32 CANVAS_AXIS_VERTICES[] = 
 | 
					const f32 CANVAS_AXIS_VERTICES[] = 
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -23,6 +24,13 @@ const f32 CANVAS_AXIS_VERTICES[] =
 | 
				
			|||||||
    0.0f, CANVAS_LINE_LENGTH
 | 
					    0.0f, CANVAS_LINE_LENGTH
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const f32 CANVAS_GRID_VERTICES[] =
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   -1.0f, -1.0f,
 | 
				
			||||||
 | 
					    3.0f, -1.0f,
 | 
				
			||||||
 | 
					   -1.0f,  3.0f
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Canvas
 | 
					struct Canvas
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GLuint fbo{};
 | 
					    GLuint fbo{};
 | 
				
			||||||
@@ -41,13 +49,13 @@ struct Canvas
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void canvas_init(Canvas* self, const vec2& size);
 | 
					void canvas_init(Canvas* self, const vec2& size);
 | 
				
			||||||
mat4 canvas_transform_get(Canvas* self, vec2& pan, f32& zoom, OriginType origin);
 | 
					mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin);
 | 
				
			||||||
void canvas_clear(vec4& color);
 | 
					void canvas_clear(vec4& color);
 | 
				
			||||||
void canvas_bind(Canvas* self);
 | 
					void canvas_bind(Canvas* self);
 | 
				
			||||||
void canvas_viewport_set(Canvas* self);
 | 
					void canvas_viewport_set(Canvas* self);
 | 
				
			||||||
void canvas_unbind(void);
 | 
					void canvas_unbind(void);
 | 
				
			||||||
void canvas_texture_set(Canvas* self);
 | 
					void canvas_texture_set(Canvas* self);
 | 
				
			||||||
void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, f32& zoom, ivec2& size, ivec2& offset, vec4& color);
 | 
					void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color);
 | 
				
			||||||
void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color);
 | 
					void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color);
 | 
				
			||||||
void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color);
 | 
					void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color);
 | 
				
			||||||
void canvas_free(Canvas* self);
 | 
					void canvas_free(Canvas* self);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,9 @@
 | 
				
			|||||||
#include "dialog.h"
 | 
					#include "dialog.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					    #include <windows.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _dialog_callback(void* userdata, const char* const* filelist, s32 filter) 
 | 
					static void _dialog_callback(void* userdata, const char* const* filelist, s32 filter) 
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	Dialog* self;
 | 
						Dialog* self;
 | 
				
			||||||
@@ -67,6 +71,17 @@ void dialog_ffmpeg_path_set(Dialog* self)
 | 
				
			|||||||
	self->type = DIALOG_FFMPEG_PATH_SET;
 | 
						self->type = DIALOG_FFMPEG_PATH_SET;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dialog_explorer_open(const std::string& path)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
							ShellExecuteA(NULL, "open", path.c_str(), NULL, NULL, SW_SHOWNORMAL);
 | 
				
			||||||
 | 
					#else 
 | 
				
			||||||
 | 
							char cmd[512];
 | 
				
			||||||
 | 
							snprintf(cmd, sizeof(cmd), "xdg-open \"%s\" &", path.c_str());
 | 
				
			||||||
 | 
							system(cmd);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
dialog_reset(Dialog* self)
 | 
					dialog_reset(Dialog* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,4 +53,5 @@ void dialog_anm2_save(Dialog* self);
 | 
				
			|||||||
void dialog_render_path_set(Dialog* self);
 | 
					void dialog_render_path_set(Dialog* self);
 | 
				
			||||||
void dialog_render_directory_set(Dialog* self);
 | 
					void dialog_render_directory_set(Dialog* self);
 | 
				
			||||||
void dialog_ffmpeg_path_set(Dialog* self);
 | 
					void dialog_ffmpeg_path_set(Dialog* self);
 | 
				
			||||||
void dialog_reset(Dialog* self);
 | 
					void dialog_reset(Dialog* self);
 | 
				
			||||||
 | 
					void dialog_explorer_open(const std::string& path);
 | 
				
			||||||
@@ -17,6 +17,7 @@ void editor_draw(Editor* self)
 | 
				
			|||||||
    vec4& gridColor = self->settings->editorGridColor;
 | 
					    vec4& gridColor = self->settings->editorGridColor;
 | 
				
			||||||
    GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
 | 
					    GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
 | 
				
			||||||
    GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
 | 
					    GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
 | 
				
			||||||
 | 
					    GLuint& shaderGrid = self->resources->shaders[SHADER_GRID];
 | 
				
			||||||
    mat4 transform = canvas_transform_get(&self->canvas, self->settings->editorPan, self->settings->editorZoom, ORIGIN_TOP_LEFT);
 | 
					    mat4 transform = canvas_transform_get(&self->canvas, self->settings->editorPan, self->settings->editorZoom, ORIGIN_TOP_LEFT);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    canvas_texture_set(&self->canvas);
 | 
					    canvas_texture_set(&self->canvas);
 | 
				
			||||||
@@ -48,7 +49,7 @@ void editor_draw(Editor* self)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (self->settings->editorIsGrid)
 | 
					    if (self->settings->editorIsGrid)
 | 
				
			||||||
        canvas_grid_draw(&self->canvas, shaderLine, transform, self->settings->editorZoom, gridSize, gridOffset, gridColor);
 | 
					        canvas_grid_draw(&self->canvas, shaderGrid, transform, gridSize, gridOffset, gridColor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    canvas_unbind();
 | 
					    canvas_unbind();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,7 @@ struct Editor
 | 
				
			|||||||
    GLuint textureVBO;
 | 
					    GLuint textureVBO;
 | 
				
			||||||
    GLuint borderVAO;
 | 
					    GLuint borderVAO;
 | 
				
			||||||
    GLuint borderVBO;
 | 
					    GLuint borderVBO;
 | 
				
			||||||
    s32 spritesheetID = -1;
 | 
					    s32 spritesheetID = ID_NONE;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources,  Settings* settings);
 | 
					void editor_init(Editor* self, Anm2* anm2, Anm2Reference* reference, Resources* resources,  Settings* settings);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,87 +12,40 @@ void generate_preview_init(GeneratePreview* self, Anm2* anm2, Anm2Reference* ref
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void generate_preview_draw(GeneratePreview* self)
 | 
					void generate_preview_draw(GeneratePreview* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    
 | 
					    static auto& columns = self->settings->generateColumns;
 | 
				
			||||||
    /* TODO
 | 
					    static auto& count = self->settings->generateCount;
 | 
				
			||||||
    f32& zoom = self->settings->previewZoom;
 | 
					    static GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
 | 
				
			||||||
    GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
 | 
					    const mat4 transform = canvas_transform_get(&self->canvas, {}, CANVAS_ZOOM_DEFAULT, ORIGIN_CENTER);
 | 
				
			||||||
    GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
 | 
					 | 
				
			||||||
    mat4 transform = canvas_transform_get(&self->canvas, self->settings->previewPan, self->settings->previewZoom, ORIGIN_CENTER);
 | 
					 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
 | 
					    vec2 startPosition = {self->settings->generateStartPosition.x, self->settings->generateStartPosition.y};
 | 
				
			||||||
 | 
					    vec2 size = {self->settings->generateSize.x, self->settings->generateSize.y};
 | 
				
			||||||
 | 
					    vec2 pivot = {self->settings->generatePivot.x, self->settings->generatePivot.y};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    canvas_bind(&self->canvas);
 | 
					    canvas_bind(&self->canvas);
 | 
				
			||||||
    canvas_viewport_set(&self->canvas);
 | 
					    canvas_viewport_set(&self->canvas);
 | 
				
			||||||
    canvas_clear(self->settings->previewBackgroundColor);
 | 
					    canvas_clear(self->settings->previewBackgroundColor);
 | 
				
			||||||
   
 | 
					 
 | 
				
			||||||
	self->time = std::clamp(self->time, 0.0f, 1.0f);
 | 
					    Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference);
 | 
				
			||||||
    
 | 
					    Texture* texture = map_find(self->resources->textures, self->anm2->layers[self->reference->itemID].spritesheetID);
 | 
				
			||||||
    Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
 | 
					 | 
				
			||||||
    s32& animationID = self->reference->animationID;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (animation)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        Anm2Frame root;
 | 
					 | 
				
			||||||
        mat4 rootModel = mat4(1.0f);
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, self->time);
 | 
					    if (item && texture && !texture->isInvalid)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const s32 index = std::clamp((s32)(self->time * count), 0, count);
 | 
				
			||||||
 | 
					        const s32 row = index / columns;
 | 
				
			||||||
 | 
					        const s32 column = index % columns;
 | 
				
			||||||
 | 
					        vec2 crop = startPosition + vec2(size.x * column, size.y * row);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (self->settings->previewIsRootTransform)
 | 
					        vec2 uvMin = crop / vec2(texture->size);
 | 
				
			||||||
            rootModel = quad_parent_model_get(root.position, vec2(0.0f), root.rotation, PERCENT_TO_UNIT(root.scale));
 | 
					        vec2 uvMax = (crop + size) / vec2(texture->size);
 | 
				
			||||||
 | 
					        f32 vertices[] = UV_VERTICES(uvMin, uvMax);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Root
 | 
					        mat4 model = quad_model_get(size, {}, pivot, {}, CANVAS_SCALE_DEFAULT);
 | 
				
			||||||
        if (self->settings->previewIsTargets && animation->rootAnimation.isVisible && root.isVisible)
 | 
					        mat4 generateTransform = transform * model;
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            mat4 model = quad_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, root.rotation, PERCENT_TO_UNIT(root.scale));
 | 
					 | 
				
			||||||
            mat4 rootTransform = transform * model;
 | 
					 | 
				
			||||||
            f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_TARGET);
 | 
					 | 
				
			||||||
            canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, rootTransform, vertices, PREVIEW_ROOT_COLOR);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Layers
 | 
					 | 
				
			||||||
		for (auto [i, id] : self->anm2->layerMap)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Anm2Frame frame;
 | 
					 | 
				
			||||||
            Anm2Item& layerAnimation = animation->layerAnimations[id];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, self->time);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!frame.isVisible)
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Texture* texture = map_find(self->resources->textures, self->anm2->layers[id].spritesheetID);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!texture || texture->isInvalid)
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            vec2 uvMin = frame.crop / vec2(texture->size);
 | 
					 | 
				
			||||||
            vec2 uvMax = (frame.crop + frame.size) / vec2(texture->size);
 | 
					 | 
				
			||||||
            f32 vertices[] = UV_VERTICES(uvMin, uvMax);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, frame.rotation, PERCENT_TO_UNIT(frame.scale));
 | 
					 | 
				
			||||||
            mat4 layerTransform = transform * (rootModel * model);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            canvas_texture_draw(&self->canvas, shaderTexture, texture->id, layerTransform, vertices, frame.tintRGBA, frame.offsetRGB);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (self->settings->previewIsBorder)
 | 
					 | 
				
			||||||
                canvas_rect_draw(&self->canvas, shaderLine, layerTransform, PREVIEW_BORDER_COLOR);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (self->settings->previewIsPivots)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
 | 
					 | 
				
			||||||
                mat4 pivotModel = quad_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale));
 | 
					 | 
				
			||||||
                mat4 pivotTransform = transform * (rootModel * pivotModel);
 | 
					 | 
				
			||||||
                canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, PREVIEW_PIVOT_COLOR);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    s32& animationOverlayID = self->animationOverlayID;
 | 
					 | 
				
			||||||
    Anm2Animation* animationOverlay = map_find(self->anm2->animations, animationOverlayID);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        canvas_texture_draw(&self->canvas, shaderTexture, texture->id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    canvas_unbind();
 | 
					    canvas_unbind();
 | 
				
			||||||
    */
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void generate_preview_free(GeneratePreview* self)
 | 
					void generate_preview_free(GeneratePreview* self)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,9 @@
 | 
				
			|||||||
#include "settings.h"
 | 
					#include "settings.h"
 | 
				
			||||||
#include "canvas.h"
 | 
					#include "canvas.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GENERATE_PREVIEW_TIME_MIN 0.0f
 | 
				
			||||||
 | 
					#define GENERATE_PREVIEW_TIME_MAX 1.0f
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const vec2 GENERATE_PREVIEW_SIZE = {325, 215};
 | 
					const vec2 GENERATE_PREVIEW_SIZE = {325, 215};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct GeneratePreview
 | 
					struct GeneratePreview
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										273
									
								
								src/imgui.cpp
									
									
									
									
									
								
							
							
						
						
									
										273
									
								
								src/imgui.cpp
									
									
									
									
									
								
							@@ -19,6 +19,34 @@ static bool _imgui_window_color_from_position_get(SDL_Window* self, const vec2&
 | 
				
			|||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void _imgui_anm2_new(Imgui* self, const std::string& path)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						*self->reference = Anm2Reference{};
 | 
				
			||||||
 | 
						resources_textures_free(self->resources);
 | 
				
			||||||
 | 
						if (anm2_deserialize(self->anm2, self->resources, path))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							window_title_from_path_set(self->window, path);
 | 
				
			||||||
 | 
							snapshots_reset(self->snapshots);
 | 
				
			||||||
 | 
							imgui_log_push(self, std::format(IMGUI_LOG_FILE_OPEN_FORMAT, path));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							imgui_log_push(self, std::format(IMGUI_LOG_FILE_OPEN_FORMAT, path));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void _imgui_spritesheet_add(Imgui* self, const std::string& path)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::filesystem::path workingPath = std::filesystem::current_path();
 | 
				
			||||||
 | 
						std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path);
 | 
				
			||||||
 | 
						std::string spritesheetPath = std::filesystem::relative(path, anm2WorkingPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s32 id = map_next_id_get(self->resources->textures);
 | 
				
			||||||
 | 
						self->anm2->spritesheets[id] = Anm2Spritesheet{};
 | 
				
			||||||
 | 
						self->anm2->spritesheets[id].path = spritesheetPath;
 | 
				
			||||||
 | 
						resources_texture_init(self->resources, spritesheetPath, id);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						std::filesystem::current_path(workingPath);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template<typename T>
 | 
					template<typename T>
 | 
				
			||||||
static void _imgui_clipboard_hovered_item_set(Imgui* self, const T& data)
 | 
					static void _imgui_clipboard_hovered_item_set(Imgui* self, const T& data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -107,6 +135,7 @@ static void _imgui_item_pre(const ImguiItem& self, ImguiItemType type)
 | 
				
			|||||||
		case IMGUI_INPUT_INT:
 | 
							case IMGUI_INPUT_INT:
 | 
				
			||||||
		case IMGUI_INPUT_TEXT:
 | 
							case IMGUI_INPUT_TEXT:
 | 
				
			||||||
		case IMGUI_DRAG_FLOAT:
 | 
							case IMGUI_DRAG_FLOAT:
 | 
				
			||||||
 | 
							case IMGUI_SLIDER_FLOAT:
 | 
				
			||||||
		case IMGUI_COLOR_EDIT:
 | 
							case IMGUI_COLOR_EDIT:
 | 
				
			||||||
				ImGui::SetNextItemWidth(size.x);
 | 
									ImGui::SetNextItemWidth(size.x);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
@@ -360,6 +389,8 @@ IMGUI_ITEM_VALUE_FUNCTION(_imgui_color_button, IMGUI_COLOR_BUTTON, vec4, ImGui::
 | 
				
			|||||||
IMGUI_ITEM_VALUE_FUNCTION(_imgui_checkbox, IMGUI_CHECKBOX, bool, ImGui::Checkbox(self.label_get(), &value));
 | 
					IMGUI_ITEM_VALUE_FUNCTION(_imgui_checkbox, IMGUI_CHECKBOX, bool, ImGui::Checkbox(self.label_get(), &value));
 | 
				
			||||||
IMGUI_ITEM_VALUE_CLAMP_FUNCTION(_imgui_input_int, IMGUI_INPUT_INT, s32, ImGui::InputInt(self.label.c_str(), &value, self.step, self.stepFast, self.flags));
 | 
					IMGUI_ITEM_VALUE_CLAMP_FUNCTION(_imgui_input_int, IMGUI_INPUT_INT, s32, ImGui::InputInt(self.label.c_str(), &value, self.step, self.stepFast, self.flags));
 | 
				
			||||||
IMGUI_ITEM_VALUE_CLAMP_FUNCTION(_imgui_input_int2, IMGUI_INPUT_INT, ivec2, ImGui::InputInt2(self.label.c_str(), value_ptr(value), self.flags));
 | 
					IMGUI_ITEM_VALUE_CLAMP_FUNCTION(_imgui_input_int2, IMGUI_INPUT_INT, ivec2, ImGui::InputInt2(self.label.c_str(), value_ptr(value), self.flags));
 | 
				
			||||||
 | 
					IMGUI_ITEM_VALUE_CLAMP_FUNCTION(_imgui_input_float, IMGUI_INPUT_FLOAT, f32, ImGui::InputFloat(self.label.c_str(), &value, self.step, self.stepFast, self.format_get(), self.flags));
 | 
				
			||||||
 | 
					IMGUI_ITEM_VALUE_FUNCTION(_imgui_slider_float, IMGUI_SLIDER_FLOAT, f32, ImGui::SliderFloat(self.label_get(), &value, self.min, self.max, self.format_get(), self.flags));
 | 
				
			||||||
IMGUI_ITEM_VALUE_FUNCTION(_imgui_drag_float, IMGUI_DRAG_FLOAT, f32, ImGui::DragFloat(self.label_get(), &value, self.speed, self.min, self.max, self.format_get()));
 | 
					IMGUI_ITEM_VALUE_FUNCTION(_imgui_drag_float, IMGUI_DRAG_FLOAT, f32, ImGui::DragFloat(self.label_get(), &value, self.speed, self.min, self.max, self.format_get()));
 | 
				
			||||||
IMGUI_ITEM_VALUE_FUNCTION(_imgui_drag_float2, IMGUI_DRAG_FLOAT, vec2, ImGui::DragFloat2(self.label_get(), value_ptr(value), self.speed, self.min, self.max, self.format_get()));
 | 
					IMGUI_ITEM_VALUE_FUNCTION(_imgui_drag_float2, IMGUI_DRAG_FLOAT, vec2, ImGui::DragFloat2(self.label_get(), value_ptr(value), self.speed, self.min, self.max, self.format_get()));
 | 
				
			||||||
IMGUI_ITEM_VALUE_FUNCTION(_imgui_color_edit3, IMGUI_COLOR_EDIT, vec3, ImGui::ColorEdit3(self.label_get(), value_ptr(value), self.flags));
 | 
					IMGUI_ITEM_VALUE_FUNCTION(_imgui_color_edit3, IMGUI_COLOR_EDIT, vec3, ImGui::ColorEdit3(self.label_get(), value_ptr(value), self.flags));
 | 
				
			||||||
@@ -522,6 +553,24 @@ static bool _imgui_option_popup(ImguiItem self, Imgui* imgui)
 | 
				
			|||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void _imgui_context_menu(Imgui* self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!self->isContextualActionsEnabled) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if (_imgui_is_window_hovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right))
 | 
				
			||||||
 | 
							imgui_open_popup(IMGUI_CONTEXT_MENU.label_get());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (imgui_begin_popup(IMGUI_CONTEXT_MENU.label_get(), self))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							_imgui_selectable(IMGUI_CUT, self);			
 | 
				
			||||||
 | 
							_imgui_selectable(IMGUI_COPY, self);			
 | 
				
			||||||
 | 
							_imgui_selectable(IMGUI_PASTE.copy({self->clipboard->item.type == CLIPBOARD_NONE}), self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							imgui_end_popup(self);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _imgui_spritesheet_editor_set(Imgui* self, s32 id)
 | 
					static void _imgui_spritesheet_editor_set(Imgui* self, s32 id)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (self->anm2->spritesheets.contains(id)) self->editor->spritesheetID = id;
 | 
						if (self->anm2->spritesheets.contains(id)) self->editor->spritesheetID = id;
 | 
				
			||||||
@@ -1036,6 +1085,11 @@ static void _imgui_timeline(Imgui* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		timeline_item_frames(Anm2Reference(animationID, ANM2_TRIGGERS), index);
 | 
							timeline_item_frames(Anm2Reference(animationID, ANM2_TRIGGERS), index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, defaultItemSpacing);
 | 
				
			||||||
 | 
							ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, defaultWindowPadding);
 | 
				
			||||||
 | 
							_imgui_context_menu(self);
 | 
				
			||||||
 | 
							ImGui::PopStyleVar(2);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
		_imgui_end_child(); // IMGUI_TIMELINE_FRAMES_CHILD
 | 
							_imgui_end_child(); // IMGUI_TIMELINE_FRAMES_CHILD
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1056,6 +1110,7 @@ static void _imgui_timeline(Imgui* self)
 | 
				
			|||||||
	_imgui_end_child(); // IMGUI_TIMELINE_CHILD
 | 
						_imgui_end_child(); // IMGUI_TIMELINE_CHILD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Anm2Frame* frame = anm2_frame_from_reference(self->anm2, self->reference);
 | 
						Anm2Frame* frame = anm2_frame_from_reference(self->anm2, self->reference);
 | 
				
			||||||
 | 
						Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference);
 | 
				
			||||||
	_imgui_begin_child(IMGUI_TIMELINE_ITEM_FOOTER_CHILD, self);
 | 
						_imgui_begin_child(IMGUI_TIMELINE_ITEM_FOOTER_CHILD, self);
 | 
				
			||||||
	_imgui_button(IMGUI_TIMELINE_ADD_ITEM, self);
 | 
						_imgui_button(IMGUI_TIMELINE_ADD_ITEM, self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1066,7 +1121,7 @@ static void _imgui_timeline(Imgui* self)
 | 
				
			|||||||
		imgui_end_popup(self);
 | 
							imgui_end_popup(self);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (_imgui_button(IMGUI_TIMELINE_REMOVE_ITEM.copy({itemType == ANM2_NONE || itemType == ANM2_ROOT || itemType == ANM2_TRIGGERS}), self))
 | 
						if (_imgui_button(IMGUI_TIMELINE_REMOVE_ITEM.copy({!item || itemType == ANM2_ROOT || itemType == ANM2_TRIGGERS}), self))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		switch (itemType)
 | 
							switch (itemType)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -1098,8 +1153,13 @@ static void _imgui_timeline(Imgui* self)
 | 
				
			|||||||
		self->preview->isPlaying = !self->preview->isPlaying;
 | 
							self->preview->isPlaying = !self->preview->isPlaying;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (_imgui_button(IMGUI_ADD_FRAME, self))
 | 
						if (_imgui_button(IMGUI_ADD_FRAME.copy({!item}), self))
 | 
				
			||||||
		anm2_frame_add(self->anm2, nullptr, self->reference, (s32)time);
 | 
						{
 | 
				
			||||||
 | 
							Anm2Reference frameReference = *self->reference;
 | 
				
			||||||
 | 
							frameReference.frameIndex = std::clamp(frameReference.frameIndex, 0, (s32)item->frames.size() - 1);
 | 
				
			||||||
 | 
							Anm2Frame* addFrame = anm2_frame_from_reference(self->anm2, &frameReference);
 | 
				
			||||||
 | 
							anm2_frame_add(self->anm2, addFrame, &frameReference);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if(_imgui_button(IMGUI_REMOVE_FRAME.copy({!frame}), self))
 | 
						if(_imgui_button(IMGUI_REMOVE_FRAME.copy({!frame}), self))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -1163,23 +1223,18 @@ static void _imgui_taskbar(Imgui* self)
 | 
				
			|||||||
	
 | 
						
 | 
				
			||||||
	if (imgui_begin_popup(IMGUI_FILE.popup, self))
 | 
						if (imgui_begin_popup(IMGUI_FILE.popup, self))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		_imgui_selectable(IMGUI_NEW, self); 	// imgui_file_new
 | 
							_imgui_selectable(IMGUI_NEW, self);
 | 
				
			||||||
		_imgui_selectable(IMGUI_OPEN, self); 	// imgui_file_open
 | 
							_imgui_selectable(IMGUI_OPEN, self);
 | 
				
			||||||
	
 | 
							_imgui_selectable(IMGUI_SAVE, self);
 | 
				
			||||||
		_imgui_selectable(IMGUI_SAVE, self); 	// imgui_file_save
 | 
							_imgui_selectable(IMGUI_SAVE_AS, self);
 | 
				
			||||||
		_imgui_selectable(IMGUI_SAVE_AS, self); // imgui_file_save_as
 | 
							_imgui_selectable(IMGUI_EXPLORE_ANM2_LOCATION, self);
 | 
				
			||||||
 | 
							_imgui_selectable(IMGUI_EXIT, self);
 | 
				
			||||||
		imgui_end_popup(self);
 | 
							imgui_end_popup(self);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (self->dialog->isSelected && self->dialog->type == DIALOG_ANM2_OPEN)
 | 
						if (self->dialog->isSelected && self->dialog->type == DIALOG_ANM2_OPEN)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		*self->reference = Anm2Reference{};
 | 
							_imgui_anm2_new(self, self->dialog->path);
 | 
				
			||||||
		resources_textures_free(self->resources);
 | 
					 | 
				
			||||||
		anm2_deserialize(self->anm2, self->resources, self->dialog->path);
 | 
					 | 
				
			||||||
		window_title_from_path_set(self->window, self->dialog->path);
 | 
					 | 
				
			||||||
		snapshots_reset(self->snapshots);
 | 
					 | 
				
			||||||
		imgui_log_push(self, std::format(IMGUI_LOG_FILE_OPEN_FORMAT, self->dialog->path));
 | 
					 | 
				
			||||||
		dialog_reset(self->dialog);
 | 
							dialog_reset(self->dialog);
 | 
				
			||||||
	}			
 | 
						}			
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1190,13 +1245,21 @@ static void _imgui_taskbar(Imgui* self)
 | 
				
			|||||||
		imgui_log_push(self, std::format(IMGUI_LOG_FILE_SAVE_FORMAT, self->dialog->path));
 | 
							imgui_log_push(self, std::format(IMGUI_LOG_FILE_SAVE_FORMAT, self->dialog->path));
 | 
				
			||||||
		dialog_reset(self->dialog);
 | 
							dialog_reset(self->dialog);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
					
 | 
				
			||||||
 | 
						if (self->isTryQuit) imgui_open_popup(IMGUI_EXIT_CONFIRMATION.label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (_imgui_option_popup(IMGUI_EXIT_CONFIRMATION, self))
 | 
				
			||||||
 | 
							self->isQuit = true;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							self->isTryQuit = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_imgui_selectable(IMGUI_WIZARD.copy({}), self);
 | 
						_imgui_selectable(IMGUI_WIZARD.copy({}), self);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	if (imgui_begin_popup(IMGUI_WIZARD.popup, self))
 | 
						if (imgui_begin_popup(IMGUI_WIZARD.popup, self))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		_imgui_selectable(IMGUI_GENERATE_ANIMATION_FROM_GRID.copy({!item || (self->reference->itemType != ANM2_LAYER)}), self);
 | 
							_imgui_selectable(IMGUI_GENERATE_ANIMATION_FROM_GRID.copy({!item || (self->reference->itemType != ANM2_LAYER)}), self);
 | 
				
			||||||
		_imgui_selectable(IMGUI_CHANGE_ALL_FRAME_PROPERTIES.copy({!item}), self);
 | 
							_imgui_selectable(IMGUI_CHANGE_ALL_FRAME_PROPERTIES.copy({!item}), self);
 | 
				
			||||||
 | 
							_imgui_selectable(IMGUI_SCALE_ANM2.copy({self->anm2->animations.empty()}), self);
 | 
				
			||||||
		_imgui_selectable(IMGUI_RENDER_ANIMATION.copy({!animation}), self);
 | 
							_imgui_selectable(IMGUI_RENDER_ANIMATION.copy({!animation}), self);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
		imgui_end_popup(self);
 | 
							imgui_end_popup(self);
 | 
				
			||||||
@@ -1204,27 +1267,44 @@ static void _imgui_taskbar(Imgui* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (imgui_begin_popup_modal(IMGUI_GENERATE_ANIMATION_FROM_GRID.popup, self, IMGUI_GENERATE_ANIMATION_FROM_GRID.popupSize))
 | 
						if (imgui_begin_popup_modal(IMGUI_GENERATE_ANIMATION_FROM_GRID.popup, self, IMGUI_GENERATE_ANIMATION_FROM_GRID.popupSize))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							static auto& startPosition = self->settings->generateStartPosition;
 | 
				
			||||||
 | 
							static auto& size = self->settings->generateSize;
 | 
				
			||||||
 | 
							static auto& pivot = self->settings->generatePivot;
 | 
				
			||||||
 | 
							static auto& rows = self->settings->generateRows;
 | 
				
			||||||
 | 
							static auto& columns = self->settings->generateColumns;
 | 
				
			||||||
 | 
							static auto& count = self->settings->generateCount;
 | 
				
			||||||
 | 
							static auto& delay = self->settings->generateDelay;
 | 
				
			||||||
 | 
							static auto& time = self->generatePreview->time;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_imgui_begin_child(IMGUI_GENERATE_ANIMATION_FROM_GRID_OPTIONS_CHILD, self);
 | 
							_imgui_begin_child(IMGUI_GENERATE_ANIMATION_FROM_GRID_OPTIONS_CHILD, self);
 | 
				
			||||||
		_imgui_input_int2(IMGUI_GENERATE_ANIMATION_FROM_GRID_START_POSITION, self, self->settings->generateStartPosition);
 | 
							_imgui_input_int2(IMGUI_GENERATE_ANIMATION_FROM_GRID_START_POSITION, self, startPosition);
 | 
				
			||||||
		_imgui_input_int2(IMGUI_GENERATE_ANIMATION_FROM_GRID_FRAME_SIZE, self, self->settings->generateFrameSize);
 | 
							_imgui_input_int2(IMGUI_GENERATE_ANIMATION_FROM_GRID_SIZE, self, size);
 | 
				
			||||||
		_imgui_input_int2(IMGUI_GENERATE_ANIMATION_FROM_GRID_PIVOT, self, self->settings->generatePivot);
 | 
							_imgui_input_int2(IMGUI_GENERATE_ANIMATION_FROM_GRID_PIVOT, self, pivot);
 | 
				
			||||||
		_imgui_input_int(IMGUI_GENERATE_ANIMATION_FROM_GRID_ROWS, self, self->settings->generateRows);
 | 
							_imgui_input_int(IMGUI_GENERATE_ANIMATION_FROM_GRID_ROWS, self, rows);
 | 
				
			||||||
		_imgui_input_int(IMGUI_GENERATE_ANIMATION_FROM_GRID_COLUMNS, self, self->settings->generateColumns);
 | 
							_imgui_input_int(IMGUI_GENERATE_ANIMATION_FROM_GRID_COLUMNS, self, columns);
 | 
				
			||||||
		_imgui_input_int
 | 
							_imgui_input_int(IMGUI_GENERATE_ANIMATION_FROM_GRID_COUNT.copy({.max = rows * columns}), self, count);
 | 
				
			||||||
		(
 | 
							_imgui_input_int(IMGUI_GENERATE_ANIMATION_FROM_GRID_DELAY, self, delay);
 | 
				
			||||||
			IMGUI_GENERATE_ANIMATION_FROM_GRID_FRAME_COUNT.copy({.max = self->settings->generateRows * self->settings->generateColumns}),
 | 
					 | 
				
			||||||
			self, self->settings->generateFrameCount
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		_imgui_input_int(IMGUI_GENERATE_ANIMATION_FROM_GRID_DELAY, self, self->settings->generateDelay);
 | 
					 | 
				
			||||||
		_imgui_end_child(); //IMGUI_GENERATE_ANIMATION_FROM_GRID_OPTIONS_CHILD 
 | 
							_imgui_end_child(); //IMGUI_GENERATE_ANIMATION_FROM_GRID_OPTIONS_CHILD 
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		ImGui::SameLine();
 | 
							ImGui::SameLine();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_imgui_begin_child(IMGUI_GENERATE_ANIMATION_FROM_GRID_PREVIEW_CHILD, self);
 | 
							_imgui_begin_child(IMGUI_GENERATE_ANIMATION_FROM_GRID_PREVIEW_CHILD, self);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							generate_preview_draw(self->generatePreview);
 | 
				
			||||||
 | 
							ImGui::Image(self->generatePreview->canvas.texture, GENERATE_PREVIEW_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_imgui_begin_child(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER_CHILD, self);
 | 
				
			||||||
 | 
							_imgui_slider_float(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER, self, time);
 | 
				
			||||||
 | 
							_imgui_end_child(); // IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER_CHILD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_imgui_end_child(); //IMGUI_GENERATE_ANIMATION_FROM_GRID_PREVIEW_CHILD 
 | 
							_imgui_end_child(); //IMGUI_GENERATE_ANIMATION_FROM_GRID_PREVIEW_CHILD 
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
		_imgui_begin_child(IMGUI_FOOTER_CHILD, self);
 | 
							_imgui_begin_child(IMGUI_FOOTER_CHILD, self);
 | 
				
			||||||
		if (_imgui_button(IMGUI_GENERATE_ANIMATION_FROM_GRID_GENERATE, self)) imgui_close_current_popup(self);
 | 
							if (_imgui_button(IMGUI_GENERATE_ANIMATION_FROM_GRID_GENERATE, self))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								anm2_generate_from_grid(self->anm2, self->reference, startPosition, size, pivot, columns, count, delay);
 | 
				
			||||||
 | 
								imgui_close_current_popup(self);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if (_imgui_button(IMGUI_POPUP_CANCEL, self)) imgui_close_current_popup(self);
 | 
							if (_imgui_button(IMGUI_POPUP_CANCEL, self)) imgui_close_current_popup(self);
 | 
				
			||||||
		_imgui_end_child(); // IMGUI_FOOTER_CHILD
 | 
							_imgui_end_child(); // IMGUI_FOOTER_CHILD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1316,6 +1396,24 @@ static void _imgui_taskbar(Imgui* self)
 | 
				
			|||||||
		imgui_end_popup(self);
 | 
							imgui_end_popup(self);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (imgui_begin_popup_modal(IMGUI_SCALE_ANM2.popup, self, IMGUI_SCALE_ANM2.popupSize))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							_imgui_begin_child(IMGUI_SCALE_ANM2_OPTIONS_CHILD, self);
 | 
				
			||||||
 | 
							_imgui_input_float(IMGUI_SCALE_ANM2_VALUE, self, self->settings->scaleValue);	
 | 
				
			||||||
 | 
							_imgui_end_child(); // IMGUI_SCALE_ANM2_OPTIONS_CHILD
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							_imgui_begin_child(IMGUI_FOOTER_CHILD, self);
 | 
				
			||||||
 | 
							if (_imgui_button(IMGUI_SCALE_ANM2_SCALE, self)) 
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								anm2_scale(self->anm2, self->settings->scaleValue);
 | 
				
			||||||
 | 
								imgui_close_current_popup(self);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (_imgui_button(IMGUI_POPUP_CANCEL, self)) imgui_close_current_popup(self);
 | 
				
			||||||
 | 
							_imgui_end_child(); // IMGUI_FOOTER_CHILD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							imgui_end_popup(self);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (imgui_begin_popup_modal(IMGUI_RENDER_ANIMATION.popup, self, IMGUI_RENDER_ANIMATION.popupSize))
 | 
						if (imgui_begin_popup_modal(IMGUI_RENDER_ANIMATION.popup, self, IMGUI_RENDER_ANIMATION.popupSize))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		_imgui_begin_child(IMGUI_RENDER_ANIMATION_CHILD, self);
 | 
							_imgui_begin_child(IMGUI_RENDER_ANIMATION_CHILD, self);
 | 
				
			||||||
@@ -1331,7 +1429,11 @@ static void _imgui_taskbar(Imgui* self)
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (self->dialog->isSelected && self->dialog->type == DIALOG_RENDER_PATH_SET)
 | 
							if 
 | 
				
			||||||
 | 
							(
 | 
				
			||||||
 | 
								self->dialog->isSelected && 
 | 
				
			||||||
 | 
								(self->dialog->type == DIALOG_RENDER_PATH_SET || self->dialog->type == DIALOG_RENDER_DIRECTORY_SET)
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			self->settings->renderPath = self->dialog->path;
 | 
								self->settings->renderPath = self->dialog->path;
 | 
				
			||||||
			dialog_reset(self->dialog);
 | 
								dialog_reset(self->dialog);
 | 
				
			||||||
@@ -1356,24 +1458,33 @@ static void _imgui_taskbar(Imgui* self)
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			bool isRenderStart = true;
 | 
								bool isRenderStart = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			switch (self->settings->renderType)
 | 
								if (!std::filesystem::exists(self->settings->ffmpegPath))
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				case RENDER_PNG:
 | 
									imgui_log_push(self, IMGUI_LOG_RENDER_ANIMATION_FFMPEG_PATH_ERROR);
 | 
				
			||||||
					if (!std::filesystem::is_directory(self->settings->renderPath))
 | 
									isRenderStart = false;
 | 
				
			||||||
					{
 | 
								}
 | 
				
			||||||
						imgui_log_push(self, IMGUI_LOG_RENDER_ANIMATION_DIRECTORY_ERROR);
 | 
					
 | 
				
			||||||
						isRenderStart = false;
 | 
								if (isRenderStart)
 | 
				
			||||||
					}
 | 
								{
 | 
				
			||||||
					break;
 | 
									switch (self->settings->renderType)
 | 
				
			||||||
				case RENDER_GIF:
 | 
									{
 | 
				
			||||||
				case RENDER_WEBM:
 | 
										case RENDER_PNG:
 | 
				
			||||||
					if (!path_valid(self->settings->renderPath))
 | 
											if (!std::filesystem::is_directory(self->settings->renderPath))
 | 
				
			||||||
					{
 | 
											{
 | 
				
			||||||
						imgui_log_push(self, IMGUI_LOG_RENDER_ANIMATION_PATH_ERROR);
 | 
												imgui_log_push(self, IMGUI_LOG_RENDER_ANIMATION_DIRECTORY_ERROR);
 | 
				
			||||||
						isRenderStart = false;
 | 
												isRenderStart = false;
 | 
				
			||||||
					}
 | 
											}
 | 
				
			||||||
				default:
 | 
											break;
 | 
				
			||||||
					break;
 | 
										case RENDER_GIF:
 | 
				
			||||||
 | 
										case RENDER_WEBM:
 | 
				
			||||||
 | 
											if (!path_is_valid(self->settings->renderPath))
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												imgui_log_push(self, IMGUI_LOG_RENDER_ANIMATION_PATH_ERROR);
 | 
				
			||||||
 | 
												isRenderStart = false;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										default:
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (isRenderStart)
 | 
								if (isRenderStart)
 | 
				
			||||||
@@ -1577,6 +1688,8 @@ static void _imgui_animations(Imgui* self)
 | 
				
			|||||||
		ImGui::PopID();
 | 
							ImGui::PopID();
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_imgui_context_menu(self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_imgui_end_child(); // animationsChild
 | 
						_imgui_end_child(); // animationsChild
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
 | 
						Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
 | 
				
			||||||
@@ -1800,7 +1913,7 @@ static void _imgui_spritesheets(Imgui* self)
 | 
				
			|||||||
		Texture* texture = &self->resources->textures[id];
 | 
							Texture* texture = &self->resources->textures[id];
 | 
				
			||||||
		bool isContains = selectedIDs.contains(id);
 | 
							bool isContains = selectedIDs.contains(id);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		_imgui_begin_child(IMGUI_SPRITESHEET_CHILD.copy({.size = {windowSize.x, IMGUI_SPRITESHEET_CHILD.size.y}}), self);
 | 
							_imgui_begin_child(IMGUI_SPRITESHEET_CHILD, self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (_imgui_checkbox(IMGUI_SPRITESHEET_SELECTED, self, isContains))
 | 
							if (_imgui_checkbox(IMGUI_SPRITESHEET_SELECTED, self, isContains))
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -1864,17 +1977,8 @@ static void _imgui_spritesheets(Imgui* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (self->dialog->isSelected && self->dialog->type == DIALOG_SPRITESHEET_ADD)
 | 
						if (self->dialog->isSelected && self->dialog->type == DIALOG_SPRITESHEET_ADD)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		std::filesystem::path workingPath = std::filesystem::current_path();
 | 
							_imgui_spritesheet_add(self, self->dialog->path);
 | 
				
			||||||
		std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path);
 | 
					 | 
				
			||||||
		std::string spritesheetPath = std::filesystem::relative(self->dialog->path, anm2WorkingPath);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
		s32 id = map_next_id_get(self->resources->textures);
 | 
					 | 
				
			||||||
		self->anm2->spritesheets[id] = Anm2Spritesheet{};
 | 
					 | 
				
			||||||
		self->anm2->spritesheets[id].path = spritesheetPath;
 | 
					 | 
				
			||||||
		resources_texture_init(self->resources, spritesheetPath, id);
 | 
					 | 
				
			||||||
		dialog_reset(self->dialog);
 | 
							dialog_reset(self->dialog);
 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		std::filesystem::current_path(workingPath);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
	if (_imgui_button(IMGUI_SPRITESHEETS_RELOAD.copy({selectedIDs.empty()}), self))
 | 
						if (_imgui_button(IMGUI_SPRITESHEETS_RELOAD.copy({selectedIDs.empty()}), self))
 | 
				
			||||||
@@ -1935,10 +2039,10 @@ static void _imgui_spritesheets(Imgui* self)
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		for (auto& id : selectedIDs)
 | 
							for (auto& id : selectedIDs)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			std::filesystem::path workingPath = std::filesystem::current_path();
 | 
					 | 
				
			||||||
			working_directory_from_file_set(self->anm2->path);
 | 
					 | 
				
			||||||
			Anm2Spritesheet* spritesheet = &self->anm2->spritesheets[id];
 | 
								Anm2Spritesheet* spritesheet = &self->anm2->spritesheets[id];
 | 
				
			||||||
			Texture* texture = &self->resources->textures[id];
 | 
								Texture* texture = &self->resources->textures[id];
 | 
				
			||||||
 | 
								std::filesystem::path workingPath = std::filesystem::current_path();
 | 
				
			||||||
 | 
								working_directory_from_file_set(self->anm2->path);
 | 
				
			||||||
			texture_from_gl_write(texture, spritesheet->path);
 | 
								texture_from_gl_write(texture, spritesheet->path);
 | 
				
			||||||
			imgui_log_push(self, std::format(IMGUI_LOG_SPRITESHEET_SAVE_FORMAT, id, spritesheet->path));
 | 
								imgui_log_push(self, std::format(IMGUI_LOG_SPRITESHEET_SAVE_FORMAT, id, spritesheet->path));
 | 
				
			||||||
			std::filesystem::current_path(workingPath);
 | 
								std::filesystem::current_path(workingPath);
 | 
				
			||||||
@@ -2064,7 +2168,7 @@ static void _imgui_animation_preview(Imgui* self)
 | 
				
			|||||||
	const bool isDown = ImGui::IsKeyPressed(IMGUI_INPUT_DOWN);
 | 
						const bool isDown = ImGui::IsKeyPressed(IMGUI_INPUT_DOWN);
 | 
				
			||||||
	const bool isMod = ImGui::IsKeyDown(IMGUI_INPUT_SHIFT);
 | 
						const bool isMod = ImGui::IsKeyDown(IMGUI_INPUT_SHIFT);
 | 
				
			||||||
	const bool isZoomIn = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_IN);
 | 
						const bool isZoomIn = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_IN);
 | 
				
			||||||
	const bool isZoomOut = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_IN);
 | 
						const bool isZoomOut = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_OUT);
 | 
				
			||||||
	const bool isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
 | 
						const bool isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
 | 
				
			||||||
	const bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
 | 
						const bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
 | 
				
			||||||
	const bool isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
 | 
						const bool isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
 | 
				
			||||||
@@ -2194,7 +2298,7 @@ static void _imgui_spritesheet_editor(Imgui* self)
 | 
				
			|||||||
	const bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
 | 
						const bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
 | 
				
			||||||
	const bool isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
 | 
						const bool isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
 | 
				
			||||||
	const bool isZoomIn = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_IN);
 | 
						const bool isZoomIn = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_IN);
 | 
				
			||||||
	const bool isZoomOut = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_IN);
 | 
						const bool isZoomOut = ImGui::IsKeyDown(IMGUI_INPUT_ZOOM_OUT);
 | 
				
			||||||
	const bool isMod = ImGui::IsKeyDown(IMGUI_INPUT_SHIFT);
 | 
						const bool isMod = ImGui::IsKeyDown(IMGUI_INPUT_SHIFT);
 | 
				
			||||||
	const f32 mouseWheel = ImGui::GetIO().MouseWheel;
 | 
						const f32 mouseWheel = ImGui::GetIO().MouseWheel;
 | 
				
			||||||
	const ImVec2 mouseDelta = ImGui::GetIO().MouseDelta;
 | 
						const ImVec2 mouseDelta = ImGui::GetIO().MouseDelta;
 | 
				
			||||||
@@ -2358,22 +2462,6 @@ static void _imgui_log(Imgui* self)
 | 
				
			|||||||
  	}
 | 
					  	}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _imgui_persistent(Imgui* self)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!self->isContextualActionsEnabled) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (ImGui::IsMouseClicked(ImGuiMouseButton_Right))
 | 
					 | 
				
			||||||
		imgui_open_popup(IMGUI_CONTEXT_MENU.label_get());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (imgui_begin_popup(IMGUI_CONTEXT_MENU.label_get(), self))
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		_imgui_selectable(IMGUI_CUT, self);			
 | 
					 | 
				
			||||||
		_imgui_selectable(IMGUI_COPY, self);			
 | 
					 | 
				
			||||||
		_imgui_selectable(IMGUI_PASTE.copy({self->clipboard->item.type == CLIPBOARD_NONE}), self);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		imgui_end_popup(self);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _imgui_dock(Imgui* self)
 | 
					static void _imgui_dock(Imgui* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -2408,6 +2496,7 @@ void imgui_init
 | 
				
			|||||||
    Anm2Reference* reference,
 | 
					    Anm2Reference* reference,
 | 
				
			||||||
    Editor* editor,
 | 
					    Editor* editor,
 | 
				
			||||||
    Preview* preview,
 | 
					    Preview* preview,
 | 
				
			||||||
 | 
					    GeneratePreview* generatePreview,
 | 
				
			||||||
    Settings* settings,
 | 
					    Settings* settings,
 | 
				
			||||||
    Snapshots* snapshots,
 | 
					    Snapshots* snapshots,
 | 
				
			||||||
    Clipboard* clipboard,
 | 
					    Clipboard* clipboard,
 | 
				
			||||||
@@ -2423,6 +2512,7 @@ void imgui_init
 | 
				
			|||||||
	self->reference = reference;
 | 
						self->reference = reference;
 | 
				
			||||||
	self->editor = editor;
 | 
						self->editor = editor;
 | 
				
			||||||
	self->preview = preview;
 | 
						self->preview = preview;
 | 
				
			||||||
 | 
						self->generatePreview = generatePreview;
 | 
				
			||||||
	self->settings = settings;
 | 
						self->settings = settings;
 | 
				
			||||||
	self->snapshots = snapshots;
 | 
						self->snapshots = snapshots;
 | 
				
			||||||
	self->clipboard = clipboard;
 | 
						self->clipboard = clipboard;
 | 
				
			||||||
@@ -2441,7 +2531,7 @@ void imgui_init
 | 
				
			|||||||
	io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
 | 
						io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
 | 
				
			||||||
	io.ConfigWindowsMoveFromTitleBarOnly = true;
 | 
						io.ConfigWindowsMoveFromTitleBarOnly = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ImGui::LoadIniSettingsFromDisk(SETTINGS_PATH);
 | 
						ImGui::LoadIniSettingsFromDisk(settings_path_get().c_str());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void imgui_update(Imgui* self)
 | 
					void imgui_update(Imgui* self)
 | 
				
			||||||
@@ -2453,7 +2543,6 @@ void imgui_update(Imgui* self)
 | 
				
			|||||||
	_imgui_taskbar(self);
 | 
						_imgui_taskbar(self);
 | 
				
			||||||
	_imgui_dock(self);
 | 
						_imgui_dock(self);
 | 
				
			||||||
	_imgui_log(self);
 | 
						_imgui_log(self);
 | 
				
			||||||
	_imgui_persistent(self);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (self->isContextualActionsEnabled)
 | 
						if (self->isContextualActionsEnabled)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -2483,15 +2572,20 @@ void imgui_update(Imgui* self)
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		switch (event.type)
 | 
							switch (event.type)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
 | 
								case SDL_EVENT_DROP_FILE:
 | 
				
			||||||
 | 
					        	{
 | 
				
			||||||
 | 
					            	const char* droppedFile = event.drop.data;
 | 
				
			||||||
 | 
					            	
 | 
				
			||||||
 | 
									if (path_is_extension(droppedFile, ANM2_EXTENSION))  
 | 
				
			||||||
 | 
										_imgui_anm2_new(self, droppedFile);
 | 
				
			||||||
 | 
									else if (path_is_extension(droppedFile, ANM2_SPRITESHEET_EXTENSION))
 | 
				
			||||||
 | 
										_imgui_spritesheet_add(self, droppedFile);
 | 
				
			||||||
 | 
					            	
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			case SDL_EVENT_QUIT:
 | 
								case SDL_EVENT_QUIT:
 | 
				
			||||||
				if (!self->snapshots->undoStack.is_empty())
 | 
									imgui_quit(self);
 | 
				
			||||||
				{
 | 
									if (imgui_is_popup_open(IMGUI_EXIT_CONFIRMATION.popup))
 | 
				
			||||||
					if (imgui_is_popup_open(IMGUI_EXIT_CONFIRMATION.label_get()))
 | 
					 | 
				
			||||||
						self->isQuit = true;
 | 
					 | 
				
			||||||
					else
 | 
					 | 
				
			||||||
						imgui_open_popup(IMGUI_EXIT_CONFIRMATION.label_get());
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				else
 | 
					 | 
				
			||||||
					self->isQuit = true;
 | 
										self->isQuit = true;
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
@@ -2499,8 +2593,6 @@ void imgui_update(Imgui* self)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (_imgui_option_popup(IMGUI_EXIT_CONFIRMATION, self))
 | 
					 | 
				
			||||||
		self->isQuit = true;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void imgui_draw(void)
 | 
					void imgui_draw(void)
 | 
				
			||||||
@@ -2513,7 +2605,6 @@ void imgui_free(void)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	ImGui_ImplSDL3_Shutdown();
 | 
						ImGui_ImplSDL3_Shutdown();
 | 
				
			||||||
	ImGui_ImplOpenGL3_Shutdown();
 | 
						ImGui_ImplOpenGL3_Shutdown();
 | 
				
			||||||
 | 
						ImGui::SaveIniSettingsToDisk(settings_path_get().c_str());
 | 
				
			||||||
	ImGui::SaveIniSettingsToDisk(SETTINGS_PATH);
 | 
					 | 
				
			||||||
	ImGui::DestroyContext();
 | 
						ImGui::DestroyContext();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										153
									
								
								src/imgui.h
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								src/imgui.h
									
									
									
									
									
								
							@@ -5,6 +5,7 @@
 | 
				
			|||||||
#include "editor.h"
 | 
					#include "editor.h"
 | 
				
			||||||
#include "ffmpeg.h"
 | 
					#include "ffmpeg.h"
 | 
				
			||||||
#include "preview.h"
 | 
					#include "preview.h"
 | 
				
			||||||
 | 
					#include "generate_preview.h"
 | 
				
			||||||
#include "resources.h"
 | 
					#include "resources.h"
 | 
				
			||||||
#include "settings.h"
 | 
					#include "settings.h"
 | 
				
			||||||
#include "snapshots.h"
 | 
					#include "snapshots.h"
 | 
				
			||||||
@@ -67,13 +68,16 @@
 | 
				
			|||||||
#define IMGUI_ACTION_TRIGGER_MOVE "Trigger AtFrame"
 | 
					#define IMGUI_ACTION_TRIGGER_MOVE "Trigger AtFrame"
 | 
				
			||||||
#define IMGUI_ACTION_MOVE_PLAYHEAD "Move Playhead"
 | 
					#define IMGUI_ACTION_MOVE_PLAYHEAD "Move Playhead"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define IMGUI_POPUP_FLAGS ImGuiWindowFlags_NoMove
 | 
				
			||||||
 | 
					#define IMGUI_POPUP_MODAL_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define IMGUI_LOG_FILE_OPEN_FORMAT "Opened anm2: {}" 
 | 
					#define IMGUI_LOG_FILE_OPEN_FORMAT "Opened anm2: {}" 
 | 
				
			||||||
#define IMGUI_LOG_FILE_SAVE_FORMAT "Saved anm2 to: {}" 
 | 
					#define IMGUI_LOG_FILE_SAVE_FORMAT "Saved anm2 to: {}" 
 | 
				
			||||||
#define IMGUI_LOG_RENDER_ANIMATION_FRAMES_SAVE_FORMAT "Saved rendered frames to: {}" 
 | 
					#define IMGUI_LOG_RENDER_ANIMATION_FRAMES_SAVE_FORMAT "Saved rendered frames to: {}" 
 | 
				
			||||||
#define IMGUI_LOG_RENDER_ANIMATION_SAVE_FORMAT "Saved rendered animation to: {}" 
 | 
					#define IMGUI_LOG_RENDER_ANIMATION_SAVE_FORMAT "Saved rendered animation to: {}" 
 | 
				
			||||||
#define IMGUI_LOG_RENDER_ANIMATION_NO_ANIMATION_ERROR "No animation selected; rendering cancelled."
 | 
					#define IMGUI_LOG_RENDER_ANIMATION_NO_ANIMATION_ERROR "No animation selected; rendering cancelled."
 | 
				
			||||||
#define IMGUI_LOG_RENDER_ANIMATION_NO_FRAMES_ERROR "No frames to render; rendering cancelled."
 | 
					#define IMGUI_LOG_RENDER_ANIMATION_NO_FRAMES_ERROR "No frames to render; rendering cancelled."
 | 
				
			||||||
#define IMGUI_LOG_RENDER_ANIMATION_DIRECTORY_ERROR "Invalid directory! Make sure it's valid and you have write permissions."
 | 
					#define IMGUI_LOG_RENDER_ANIMATION_DIRECTORY_ERROR "Invalid directory! Make sure it exists and you have write permissions."
 | 
				
			||||||
#define IMGUI_LOG_RENDER_ANIMATION_PATH_ERROR "Invalid path! Make sure it's valid and you have write permissions."
 | 
					#define IMGUI_LOG_RENDER_ANIMATION_PATH_ERROR "Invalid path! Make sure it's valid and you have write permissions."
 | 
				
			||||||
#define IMGUI_LOG_RENDER_ANIMATION_FFMPEG_PATH_ERROR "Invalid FFmpeg path! Make sure you have it installed and the path is correct."
 | 
					#define IMGUI_LOG_RENDER_ANIMATION_FFMPEG_PATH_ERROR "Invalid FFmpeg path! Make sure you have it installed and the path is correct."
 | 
				
			||||||
#define IMGUI_LOG_RENDER_ANIMATION_FFMPEG_ERROR "FFmpeg could not render animation! Check paths or your FFmpeg installation."
 | 
					#define IMGUI_LOG_RENDER_ANIMATION_FFMPEG_ERROR "FFmpeg could not render animation! Check paths or your FFmpeg installation."
 | 
				
			||||||
@@ -116,7 +120,7 @@ const ImVec4 IMGUI_TIMELINE_HEADER_FRAME_MULTIPLE_INACTIVE_COLOR = {0.113, 0.184
 | 
				
			|||||||
const ImVec4 IMGUI_ACTIVE_COLOR = {1.0, 1.0, 1.0, 1.0};
 | 
					const ImVec4 IMGUI_ACTIVE_COLOR = {1.0, 1.0, 1.0, 1.0};
 | 
				
			||||||
const ImVec4 IMGUI_INACTIVE_COLOR = {1.0, 1.0, 1.0, 0.25};
 | 
					const ImVec4 IMGUI_INACTIVE_COLOR = {1.0, 1.0, 1.0, 0.25};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ImVec2 IMGUI_SPRITESHEET_PREVIEW_SIZE = {125.0, 125.0};
 | 
					const ImVec2 IMGUI_SPRITESHEET_PREVIEW_SIZE = {50.0, 50.0};
 | 
				
			||||||
const ImVec2 IMGUI_TOOLTIP_OFFSET = {16, 8};
 | 
					const ImVec2 IMGUI_TOOLTIP_OFFSET = {16, 8};
 | 
				
			||||||
const vec2 IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS = {1, 1};
 | 
					const vec2 IMGUI_SPRITESHEET_EDITOR_CROP_FORGIVENESS = {1, 1};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -168,6 +172,7 @@ struct Imgui
 | 
				
			|||||||
    Anm2Reference* reference = nullptr;
 | 
					    Anm2Reference* reference = nullptr;
 | 
				
			||||||
    Editor* editor = nullptr;
 | 
					    Editor* editor = nullptr;
 | 
				
			||||||
    Preview* preview = nullptr;
 | 
					    Preview* preview = nullptr;
 | 
				
			||||||
 | 
					    GeneratePreview* generatePreview = nullptr;
 | 
				
			||||||
    Settings* settings = nullptr;
 | 
					    Settings* settings = nullptr;
 | 
				
			||||||
    Snapshots* snapshots = nullptr;
 | 
					    Snapshots* snapshots = nullptr;
 | 
				
			||||||
    Clipboard* clipboard = nullptr;
 | 
					    Clipboard* clipboard = nullptr;
 | 
				
			||||||
@@ -182,6 +187,7 @@ struct Imgui
 | 
				
			|||||||
    bool isCursorSet = false;
 | 
					    bool isCursorSet = false;
 | 
				
			||||||
    bool isContextualActionsEnabled = true;
 | 
					    bool isContextualActionsEnabled = true;
 | 
				
			||||||
    bool isQuit = false;
 | 
					    bool isQuit = false;
 | 
				
			||||||
 | 
					    bool isTryQuit = false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef void(*ImguiFunction)(Imgui*);
 | 
					typedef void(*ImguiFunction)(Imgui*);
 | 
				
			||||||
@@ -235,6 +241,21 @@ static inline void imgui_file_save_as(Imgui* self)
 | 
				
			|||||||
	dialog_anm2_save(self->dialog);
 | 
						dialog_anm2_save(self->dialog);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void imgui_quit(Imgui* self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!self->snapshots->undoStack.is_empty())
 | 
				
			||||||
 | 
					        self->isTryQuit = true;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        self->isQuit = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void imgui_explore(Imgui* self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::filesystem::path filePath = self->anm2->path;
 | 
				
			||||||
 | 
					    std::filesystem::path parentPath = filePath.parent_path();
 | 
				
			||||||
 | 
					    dialog_explorer_open(parentPath);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void imgui_undo_push(Imgui* self, const std::string& action = SNAPSHOT_ACTION)
 | 
					static inline void imgui_undo_push(Imgui* self, const std::string& action = SNAPSHOT_ACTION)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Snapshot snapshot = {*self->anm2, *self->reference, self->preview->time, action};
 | 
					    Snapshot snapshot = {*self->anm2, *self->reference, self->preview->time, action};
 | 
				
			||||||
@@ -377,7 +398,7 @@ static inline bool imgui_begin_popup(const std::string& label, Imgui* imgui, ImV
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	imgui_pending_popup_process(imgui);
 | 
						imgui_pending_popup_process(imgui);
 | 
				
			||||||
	if (size != ImVec2()) ImGui::SetNextWindowSizeConstraints(size, ImVec2(FLT_MAX, FLT_MAX));
 | 
						if (size != ImVec2()) ImGui::SetNextWindowSizeConstraints(size, ImVec2(FLT_MAX, FLT_MAX));
 | 
				
			||||||
	bool isActivated = ImGui::BeginPopup(label.c_str());
 | 
						bool isActivated = ImGui::BeginPopup(label.c_str(), IMGUI_POPUP_FLAGS);
 | 
				
			||||||
	return isActivated;
 | 
						return isActivated;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -385,7 +406,7 @@ static inline bool imgui_begin_popup_modal(const std::string& label, Imgui* imgu
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	imgui_pending_popup_process(imgui);
 | 
						imgui_pending_popup_process(imgui);
 | 
				
			||||||
	if (size != ImVec2()) ImGui::SetNextWindowSizeConstraints(size, ImVec2(FLT_MAX, FLT_MAX));
 | 
						if (size != ImVec2()) ImGui::SetNextWindowSizeConstraints(size, ImVec2(FLT_MAX, FLT_MAX));
 | 
				
			||||||
	bool isActivated = ImGui::BeginPopupModal(label.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize);
 | 
						bool isActivated = ImGui::BeginPopupModal(label.c_str(), nullptr, IMGUI_POPUP_MODAL_FLAGS);
 | 
				
			||||||
	if (isActivated) imgui_contextual_actions_disable(imgui);
 | 
						if (isActivated) imgui_contextual_actions_disable(imgui);
 | 
				
			||||||
	return isActivated;
 | 
						return isActivated;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -402,6 +423,8 @@ static inline void imgui_end_popup(Imgui* imgui)
 | 
				
			|||||||
	imgui_pending_popup_process(imgui);
 | 
						imgui_pending_popup_process(imgui);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum ImguiItemType
 | 
					enum ImguiItemType
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    IMGUI_ITEM,
 | 
					    IMGUI_ITEM,
 | 
				
			||||||
@@ -417,6 +440,8 @@ enum ImguiItemType
 | 
				
			|||||||
    IMGUI_CHECKBOX,
 | 
					    IMGUI_CHECKBOX,
 | 
				
			||||||
    IMGUI_INPUT_INT,
 | 
					    IMGUI_INPUT_INT,
 | 
				
			||||||
    IMGUI_INPUT_TEXT,
 | 
					    IMGUI_INPUT_TEXT,
 | 
				
			||||||
 | 
					    IMGUI_INPUT_FLOAT,
 | 
				
			||||||
 | 
					    IMGUI_SLIDER_FLOAT,
 | 
				
			||||||
    IMGUI_DRAG_FLOAT,
 | 
					    IMGUI_DRAG_FLOAT,
 | 
				
			||||||
    IMGUI_COLOR_EDIT,
 | 
					    IMGUI_COLOR_EDIT,
 | 
				
			||||||
    IMGUI_COMBO,
 | 
					    IMGUI_COMBO,
 | 
				
			||||||
@@ -470,14 +495,15 @@ struct ImguiItem
 | 
				
			|||||||
    f32 speed = 0.25f;
 | 
					    f32 speed = 0.25f;
 | 
				
			||||||
    s32 step = 1;
 | 
					    s32 step = 1;
 | 
				
			||||||
    s32 stepFast = 10;
 | 
					    s32 stepFast = 10;
 | 
				
			||||||
    s32 border{};
 | 
					 | 
				
			||||||
    s32 max{};
 | 
					 | 
				
			||||||
    s32 min{};
 | 
					    s32 min{};
 | 
				
			||||||
 | 
					    s32 max{};
 | 
				
			||||||
    s32 value{};
 | 
					    s32 value{};
 | 
				
			||||||
 | 
					    vec2 atlasOffset;
 | 
				
			||||||
 | 
					    s32 border{};
 | 
				
			||||||
    s32 flags{};
 | 
					    s32 flags{};
 | 
				
			||||||
    s32 windowFlags{};
 | 
					    s32 windowFlags{};
 | 
				
			||||||
    s32 rowCount = 0;
 | 
					    s32 rowCount = 0;
 | 
				
			||||||
    vec2 atlasOffset;
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void construct()
 | 
					    void construct()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -638,6 +664,28 @@ IMGUI_ITEM(IMGUI_SAVE_AS,
 | 
				
			|||||||
    self.isShortcutInLabel = true
 | 
					    self.isShortcutInLabel = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					IMGUI_ITEM(IMGUI_EXPLORE_ANM2_LOCATION,
 | 
				
			||||||
 | 
					    self.label = "E&xplore Anm2 Location",
 | 
				
			||||||
 | 
					    self.tooltip = "Open the system's file explorer in the anm2's path.",
 | 
				
			||||||
 | 
					    self.function = imgui_explore,
 | 
				
			||||||
 | 
					    self.isSizeToText = true,
 | 
				
			||||||
 | 
					    self.isSeparator = true
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					IMGUI_ITEM(IMGUI_EXIT,
 | 
				
			||||||
 | 
					    self.label = "&Exit          ",
 | 
				
			||||||
 | 
					    self.tooltip = "Exits the program.",
 | 
				
			||||||
 | 
					    self.function = imgui_quit,
 | 
				
			||||||
 | 
					    self.chord = ImGuiMod_Alt | ImGuiKey_F4,
 | 
				
			||||||
 | 
					    self.isSizeToText  = true,
 | 
				
			||||||
 | 
					    self.isShortcutInLabel = true
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					IMGUI_ITEM(IMGUI_EXIT_CONFIRMATION,
 | 
				
			||||||
 | 
					    self.label = "Exit Confirmation",
 | 
				
			||||||
 | 
					    self.text = "Unsaved changes will be lost!\nAre you sure you want to exit?"
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_WIZARD,
 | 
					IMGUI_ITEM(IMGUI_WIZARD,
 | 
				
			||||||
    self.label = "&Wizard",
 | 
					    self.label = "&Wizard",
 | 
				
			||||||
    self.tooltip = "Opens the wizard menu, for neat functions related to the .anm2.",
 | 
					    self.tooltip = "Opens the wizard menu, for neat functions related to the .anm2.",
 | 
				
			||||||
@@ -647,12 +695,17 @@ IMGUI_ITEM(IMGUI_WIZARD,
 | 
				
			|||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define IMGUI_GENERATE_ANIMATION_FROM_GRID_PADDING 40
 | 
				
			||||||
IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID,
 | 
					IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID,
 | 
				
			||||||
    self.label = "&Generate Animation from Grid",
 | 
					    self.label = "&Generate Animation from Grid",
 | 
				
			||||||
    self.tooltip = "Generate a new animation from grid values.",
 | 
					    self.tooltip = "Generate a new animation from grid values.",
 | 
				
			||||||
    self.popup = "Generate Animation from Grid",
 | 
					    self.popup = "Generate Animation from Grid",
 | 
				
			||||||
    self.popupType = IMGUI_POPUP_CENTER_WINDOW,
 | 
					    self.popupType = IMGUI_POPUP_CENTER_WINDOW,
 | 
				
			||||||
    self.popupSize = {650, 215}
 | 
					    self.popupSize = 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        (GENERATE_PREVIEW_SIZE.x * 2) + IMGUI_GENERATE_ANIMATION_FROM_GRID_PADDING, 
 | 
				
			||||||
 | 
					        GENERATE_PREVIEW_SIZE.y + (IMGUI_FOOTER_CHILD.size.y * 2) + (IMGUI_GENERATE_ANIMATION_FROM_GRID_PADDING / 2)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_OPTIONS_CHILD,
 | 
					IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_OPTIONS_CHILD,
 | 
				
			||||||
@@ -670,8 +723,8 @@ IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_START_POSITION,
 | 
				
			|||||||
    self.tooltip = "Set the starting position on the layer's spritesheet for the generated animation."
 | 
					    self.tooltip = "Set the starting position on the layer's spritesheet for the generated animation."
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_FRAME_SIZE,
 | 
					IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_SIZE,
 | 
				
			||||||
    self.label = "Frame Size",
 | 
					    self.label = "Size",
 | 
				
			||||||
    self.tooltip = "Set the size of each frame in the generated animation."
 | 
					    self.tooltip = "Set the size of each frame in the generated animation."
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -692,9 +745,10 @@ IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_COLUMNS,
 | 
				
			|||||||
    self.max = 1000
 | 
					    self.max = 1000
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_FRAME_COUNT,
 | 
					IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_COUNT,
 | 
				
			||||||
    self.label = "Count",
 | 
					    self.label = "Count",
 | 
				
			||||||
    self.tooltip = "Set how many frames will be made for the generated animation."
 | 
					    self.tooltip = "Set how many frames will be made for the generated animation.",
 | 
				
			||||||
 | 
					    self.value = ANM2_FRAME_NUM_MIN
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_DELAY,
 | 
					IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_DELAY,
 | 
				
			||||||
@@ -713,9 +767,30 @@ IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_PREVIEW_CHILD,
 | 
				
			|||||||
    self.flags = true
 | 
					    self.flags = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER_CHILD,
 | 
				
			||||||
 | 
					    self.label = "## Generate Animation From Grid Slider Child",
 | 
				
			||||||
 | 
					    self.size = 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        (IMGUI_GENERATE_ANIMATION_FROM_GRID.popupSize.x / 2) - (IMGUI_GENERATE_ANIMATION_FROM_GRID_PADDING / 2), 
 | 
				
			||||||
 | 
					        IMGUI_FOOTER_CHILD.size.y
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    self.flags = true
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER,
 | 
				
			||||||
 | 
					    self.label = "## Generate Animation From Grid Slider",
 | 
				
			||||||
 | 
					    self.tooltip = "Change the time of the generated animation preview.",
 | 
				
			||||||
 | 
					    self.min = GENERATE_PREVIEW_TIME_MIN,
 | 
				
			||||||
 | 
					    self.max = GENERATE_PREVIEW_TIME_MAX,
 | 
				
			||||||
 | 
					    self.value = GENERATE_PREVIEW_TIME_MIN,
 | 
				
			||||||
 | 
					    self.rowCount = 1,
 | 
				
			||||||
 | 
					    self.flags = ImGuiSliderFlags_NoInput
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_GENERATE,
 | 
					IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_GENERATE,
 | 
				
			||||||
    self.label = "Generate",
 | 
					    self.label = "Generate",
 | 
				
			||||||
    self.tooltip = "Generate an animation with the used settings.",
 | 
					    self.tooltip = "Generate an animation with the used settings.",
 | 
				
			||||||
 | 
					    self.undoAction = "Generate Animation from Grid",
 | 
				
			||||||
    self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -725,8 +800,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES,
 | 
				
			|||||||
    self.tooltip = "Change all frame properties in the selected animation item (or selected frame).",
 | 
					    self.tooltip = "Change all frame properties in the selected animation item (or selected frame).",
 | 
				
			||||||
    self.popup = "Change All Frame Properties",
 | 
					    self.popup = "Change All Frame Properties",
 | 
				
			||||||
    self.popupType = IMGUI_POPUP_CENTER_WINDOW,
 | 
					    self.popupType = IMGUI_POPUP_CENTER_WINDOW,
 | 
				
			||||||
    self.popupSize = {500, 380},
 | 
					    self.popupSize = {500, 380}
 | 
				
			||||||
    self.isSeparator = true
 | 
					 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_CHILD,
 | 
					IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_CHILD,
 | 
				
			||||||
@@ -796,6 +870,39 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_CANCEL,
 | 
				
			|||||||
    self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT
 | 
					    self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					IMGUI_ITEM(IMGUI_SCALE_ANM2,
 | 
				
			||||||
 | 
					    self.label = "&Scale Anm2",
 | 
				
			||||||
 | 
					    self.tooltip = "Scale up all size and position-related frame properties in the anm2.",
 | 
				
			||||||
 | 
					    self.popup = "Scale Anm2",
 | 
				
			||||||
 | 
					    self.popupType = IMGUI_POPUP_CENTER_WINDOW,
 | 
				
			||||||
 | 
					    self.popupSize = {260, 72},
 | 
				
			||||||
 | 
					    self.isSizeToText = true,
 | 
				
			||||||
 | 
					    self.isSeparator = true
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					IMGUI_ITEM(IMGUI_SCALE_ANM2_OPTIONS_CHILD,
 | 
				
			||||||
 | 
					    self.label = "## Scale Anm2 Options Child",
 | 
				
			||||||
 | 
					    self.size = {IMGUI_SCALE_ANM2.popupSize.x, IMGUI_SCALE_ANM2.popupSize.y - IMGUI_FOOTER_CHILD.size.y},
 | 
				
			||||||
 | 
					    self.flags = true
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					IMGUI_ITEM(IMGUI_SCALE_ANM2_VALUE,
 | 
				
			||||||
 | 
					    self.label = "Value",
 | 
				
			||||||
 | 
					    self.tooltip = "The size and position-related frame properties in the anm2 will be scaled by this value.",
 | 
				
			||||||
 | 
					    self.format = "%.2f",
 | 
				
			||||||
 | 
					    self.value = 1,
 | 
				
			||||||
 | 
					    self.step = 0.25,
 | 
				
			||||||
 | 
					    self.stepFast = 1
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					IMGUI_ITEM(IMGUI_SCALE_ANM2_SCALE,
 | 
				
			||||||
 | 
					    self.label = "Scale",
 | 
				
			||||||
 | 
					    self.tooltip = "Scale the anm2 with the value specified.",
 | 
				
			||||||
 | 
					    self.undoAction = "Scale Anm2",
 | 
				
			||||||
 | 
					    self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
 | 
				
			||||||
 | 
					    self.isSameLine = true
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_RENDER_ANIMATION,
 | 
					IMGUI_ITEM(IMGUI_RENDER_ANIMATION,
 | 
				
			||||||
    self.label = "&Render Animation",
 | 
					    self.label = "&Render Animation",
 | 
				
			||||||
    self.tooltip = "Renders the current animation preview; output options can be customized.",
 | 
					    self.tooltip = "Renders the current animation preview; output options can be customized.",
 | 
				
			||||||
@@ -1043,7 +1150,8 @@ IMGUI_ITEM(IMGUI_SPRITESHEETS_CHILD, self.label = "## Spritesheets Child", self.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_SPRITESHEET_CHILD, 
 | 
					IMGUI_ITEM(IMGUI_SPRITESHEET_CHILD, 
 | 
				
			||||||
    self.label = "## Spritesheet Child",
 | 
					    self.label = "## Spritesheet Child",
 | 
				
			||||||
    self.size = {0, 175},
 | 
					    self.rowCount = 1,
 | 
				
			||||||
 | 
					    self.size = {0, IMGUI_SPRITESHEET_PREVIEW_SIZE.y + 40},
 | 
				
			||||||
    self.flags = true
 | 
					    self.flags = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1253,6 +1361,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_POSITION,
 | 
				
			|||||||
    self.label = "Position",
 | 
					    self.label = "Position",
 | 
				
			||||||
    self.tooltip = "Change the position of the selected frame.",
 | 
					    self.tooltip = "Change the position of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Position",
 | 
					    self.undoAction = "Frame Position",
 | 
				
			||||||
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.format = "%.0f"
 | 
					    self.format = "%.0f"
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1260,6 +1369,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_CROP,
 | 
				
			|||||||
    self.label = "Crop",
 | 
					    self.label = "Crop",
 | 
				
			||||||
    self.tooltip = "Change the crop position of the selected frame.",
 | 
					    self.tooltip = "Change the crop position of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Crop",
 | 
					    self.undoAction = "Frame Crop",
 | 
				
			||||||
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.format = "%.0f"
 | 
					    self.format = "%.0f"
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1267,6 +1377,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_SIZE,
 | 
				
			|||||||
    self.label = "Size",
 | 
					    self.label = "Size",
 | 
				
			||||||
    self.tooltip = "Change the size of the crop of the selected frame.",
 | 
					    self.tooltip = "Change the size of the crop of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Size",
 | 
					    self.undoAction = "Frame Size",
 | 
				
			||||||
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.format = "%.0f"
 | 
					    self.format = "%.0f"
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1274,6 +1385,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_PIVOT,
 | 
				
			|||||||
    self.label = "Pivot",
 | 
					    self.label = "Pivot",
 | 
				
			||||||
    self.tooltip = "Change the pivot of the selected frame.",
 | 
					    self.tooltip = "Change the pivot of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Pivot",
 | 
					    self.undoAction = "Frame Pivot",
 | 
				
			||||||
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.format = "%.0f"
 | 
					    self.format = "%.0f"
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1282,6 +1394,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_SCALE,
 | 
				
			|||||||
    self.tooltip = "Change the scale of the selected frame.",
 | 
					    self.tooltip = "Change the scale of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Scale",
 | 
					    self.undoAction = "Frame Scale",
 | 
				
			||||||
    self.format = "%.0f",
 | 
					    self.format = "%.0f",
 | 
				
			||||||
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.value = 100
 | 
					    self.value = 100
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1289,6 +1402,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_ROTATION,
 | 
				
			|||||||
    self.label = "Rotation",
 | 
					    self.label = "Rotation",
 | 
				
			||||||
    self.tooltip = "Change the rotation of the selected frame.",
 | 
					    self.tooltip = "Change the rotation of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Rotation",
 | 
					    self.undoAction = "Frame Rotation",
 | 
				
			||||||
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.format = "%.0f"
 | 
					    self.format = "%.0f"
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1296,6 +1410,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_DELAY,
 | 
				
			|||||||
    self.label = "Duration",
 | 
					    self.label = "Duration",
 | 
				
			||||||
    self.tooltip = "Change the duration of the selected frame.",
 | 
					    self.tooltip = "Change the duration of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Duration",
 | 
					    self.undoAction = "Frame Duration",
 | 
				
			||||||
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.min = ANM2_FRAME_NUM_MIN,
 | 
					    self.min = ANM2_FRAME_NUM_MIN,
 | 
				
			||||||
    self.max = ANM2_FRAME_NUM_MAX,
 | 
					    self.max = ANM2_FRAME_NUM_MAX,
 | 
				
			||||||
    self.value = ANM2_FRAME_NUM_MIN
 | 
					    self.value = ANM2_FRAME_NUM_MIN
 | 
				
			||||||
@@ -1305,6 +1420,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_TINT,
 | 
				
			|||||||
    self.label = "Tint",
 | 
					    self.label = "Tint",
 | 
				
			||||||
    self.tooltip = "Change the tint of the selected frame.",
 | 
					    self.tooltip = "Change the tint of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Tint",
 | 
					    self.undoAction = "Frame Tint",
 | 
				
			||||||
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.value = 1
 | 
					    self.value = 1
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1312,6 +1428,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_COLOR_OFFSET,
 | 
				
			|||||||
    self.label = "Color Offset",
 | 
					    self.label = "Color Offset",
 | 
				
			||||||
    self.tooltip = "Change the color offset of the selected frame.",
 | 
					    self.tooltip = "Change the color offset of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Color Offset",
 | 
					    self.undoAction = "Frame Color Offset",
 | 
				
			||||||
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.value = 0
 | 
					    self.value = 0
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1898,10 +2015,7 @@ IMGUI_ITEM(IMGUI_CHANGE_INPUT_INT,
 | 
				
			|||||||
    self.step = 0
 | 
					    self.step = 0
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_EXIT_CONFIRMATION,
 | 
					
 | 
				
			||||||
    self.label = "Exit Confirmation",
 | 
					 | 
				
			||||||
    self.text = "Unsaved changes will be lost!\nAre you sure you want to exit?"
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define IMGUI_OPTION_POPUP_ROW_COUNT 2
 | 
					#define IMGUI_OPTION_POPUP_ROW_COUNT 2
 | 
				
			||||||
IMGUI_ITEM(IMGUI_POPUP_OK,
 | 
					IMGUI_ITEM(IMGUI_POPUP_OK,
 | 
				
			||||||
@@ -1938,6 +2052,7 @@ void imgui_init
 | 
				
			|||||||
    Anm2Reference* reference,
 | 
					    Anm2Reference* reference,
 | 
				
			||||||
    Editor* editor,
 | 
					    Editor* editor,
 | 
				
			||||||
    Preview* preview,
 | 
					    Preview* preview,
 | 
				
			||||||
 | 
					    GeneratePreview* generatePreview,
 | 
				
			||||||
    Settings* settings,
 | 
					    Settings* settings,
 | 
				
			||||||
    Snapshots* snapshots,
 | 
					    Snapshots* snapshots,
 | 
				
			||||||
    Clipboard* clipboard,
 | 
					    Clipboard* clipboard,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										32
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								src/main.cpp
									
									
									
									
									
								
							@@ -1,12 +1,42 @@
 | 
				
			|||||||
#include "main.h"
 | 
					#include "main.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool _anm2_rescale(const std::string& file, f32 scale)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Anm2 anm2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!anm2_deserialize(&anm2, nullptr, file)) return false;
 | 
				
			||||||
 | 
						anm2_scale(&anm2, scale);
 | 
				
			||||||
 | 
						return anm2_serialize(&anm2, file);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
s32
 | 
					s32
 | 
				
			||||||
main(s32 argc, char* argv[])
 | 
					main(s32 argc, char* argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	State state;
 | 
						State state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (argc > 0 && argv[1])
 | 
						if (argc > 0 && argv[1])
 | 
				
			||||||
		state.argument = argv[1];
 | 
						{
 | 
				
			||||||
 | 
							if (std::string(argv[1]) == ARGUMENT_RESCALE)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (argv[2] && argv[3])
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if (_anm2_rescale(std::string(argv[2]), atof(argv[3])))
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										log_info(std::format(ARGUMENT_RESCALE_ANM2_INFO, argv[2], argv[3]));
 | 
				
			||||||
 | 
										return EXIT_SUCCESS;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
										log_error(ARGUMENT_RESCALE_ANM2_ERROR);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									log_error(ARGUMENT_RESCALE_ARGUMENT_ERROR);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								return EXIT_FAILURE;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								if (argv[1])
 | 
				
			||||||
 | 
									state.argument = argv[1];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init(&state);
 | 
						init(&state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,8 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ARGUMENT_RESCALE "--rescale"
 | 
				
			||||||
 | 
					#define ARGUMENT_RESCALE_ARGUMENT_ERROR "--rescale: specify both anm2 and scale arguments" 
 | 
				
			||||||
 | 
					#define ARGUMENT_RESCALE_ANM2_ERROR "Unable to rescale anm2 {} by value {}. Make sure the file is valid."
 | 
				
			||||||
 | 
					#define ARGUMENT_RESCALE_ANM2_INFO "Scaled anm2 {} by {}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "state.h"
 | 
					#include "state.h"
 | 
				
			||||||
@@ -83,9 +83,9 @@ void preview_draw(Preview* self)
 | 
				
			|||||||
    ivec2& gridSize = self->settings->previewGridSize;
 | 
					    ivec2& gridSize = self->settings->previewGridSize;
 | 
				
			||||||
    ivec2& gridOffset = self->settings->previewGridOffset;
 | 
					    ivec2& gridOffset = self->settings->previewGridOffset;
 | 
				
			||||||
    vec4& gridColor = self->settings->previewGridColor;
 | 
					    vec4& gridColor = self->settings->previewGridColor;
 | 
				
			||||||
    f32& zoom = self->settings->previewZoom;
 | 
					 | 
				
			||||||
    GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
 | 
					    GLuint& shaderLine = self->resources->shaders[SHADER_LINE];
 | 
				
			||||||
    GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
 | 
					    GLuint& shaderTexture = self->resources->shaders[SHADER_TEXTURE];
 | 
				
			||||||
 | 
					    GLuint& shaderGrid = self->resources->shaders[SHADER_GRID];
 | 
				
			||||||
    mat4 transform = canvas_transform_get(&self->canvas, self->settings->previewPan, self->settings->previewZoom, ORIGIN_CENTER);
 | 
					    mat4 transform = canvas_transform_get(&self->canvas, self->settings->previewPan, self->settings->previewZoom, ORIGIN_CENTER);
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
    canvas_texture_set(&self->canvas);
 | 
					    canvas_texture_set(&self->canvas);
 | 
				
			||||||
@@ -95,7 +95,7 @@ void preview_draw(Preview* self)
 | 
				
			|||||||
    canvas_clear(self->settings->previewBackgroundColor);
 | 
					    canvas_clear(self->settings->previewBackgroundColor);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (self->settings->previewIsGrid)
 | 
					    if (self->settings->previewIsGrid)
 | 
				
			||||||
        canvas_grid_draw(&self->canvas, shaderLine, transform, zoom, gridSize, gridOffset, gridColor);
 | 
					        canvas_grid_draw(&self->canvas, shaderGrid, transform, gridSize, gridOffset, gridColor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (self->settings->previewIsAxes)
 | 
					    if (self->settings->previewIsAxes)
 | 
				
			||||||
        canvas_axes_draw(&self->canvas, shaderLine, transform, self->settings->previewAxesColor);
 | 
					        canvas_axes_draw(&self->canvas, shaderLine, transform, self->settings->previewAxesColor);
 | 
				
			||||||
@@ -136,20 +136,20 @@ void preview_draw(Preview* self)
 | 
				
			|||||||
            if (!frame.isVisible)
 | 
					            if (!frame.isVisible)
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Texture* texture = map_find(self->resources->textures, self->anm2->layers[id].spritesheetID);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!texture || texture->isInvalid)
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            vec2 uvMin = frame.crop / vec2(texture->size);
 | 
					 | 
				
			||||||
            vec2 uvMax = (frame.crop + frame.size) / vec2(texture->size);
 | 
					 | 
				
			||||||
            f32 vertices[] = UV_VERTICES(uvMin, uvMax);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, frame.rotation, PERCENT_TO_UNIT(frame.scale));
 | 
					            mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, frame.rotation, PERCENT_TO_UNIT(frame.scale));
 | 
				
			||||||
            mat4 layerTransform = transform * (rootModel * model);
 | 
					            mat4 layerTransform = transform * (rootModel * model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            canvas_texture_draw(&self->canvas, shaderTexture, texture->id, layerTransform, vertices, frame.tintRGBA, frame.offsetRGB);
 | 
					            Texture* texture = map_find(self->resources->textures, self->anm2->layers[id].spritesheetID);
 | 
				
			||||||
 | 
					           
 | 
				
			||||||
 | 
					            if (texture && !texture->isInvalid)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                vec2 uvMin = frame.crop / vec2(texture->size);
 | 
				
			||||||
 | 
					                vec2 uvMax = (frame.crop + frame.size) / vec2(texture->size);
 | 
				
			||||||
 | 
					                f32 vertices[] = UV_VERTICES(uvMin, uvMax);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                canvas_texture_draw(&self->canvas, shaderTexture, texture->id, layerTransform, vertices, frame.tintRGBA, frame.offsetRGB);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
            if (self->settings->previewIsBorder)
 | 
					            if (self->settings->previewIsBorder)
 | 
				
			||||||
                canvas_rect_draw(&self->canvas, shaderLine, layerTransform, PREVIEW_BORDER_COLOR);
 | 
					                canvas_rect_draw(&self->canvas, shaderLine, layerTransform, PREVIEW_BORDER_COLOR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										134
									
								
								src/settings.cpp
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								src/settings.cpp
									
									
									
									
									
								
							@@ -72,6 +72,14 @@ static void _settings_setting_load(Settings* self, const std::string& line)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string settings_path_get(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char* path = SDL_GetPrefPath("", SETTINGS_FOLDER);
 | 
				
			||||||
 | 
					    std::string filePath = std::string(path) + SETTINGS_PATH;
 | 
				
			||||||
 | 
					    SDL_free(path);
 | 
				
			||||||
 | 
					    return filePath;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _settings_setting_write(Settings* self, std::ostream& out, SettingsEntry entry)
 | 
					static void _settings_setting_write(Settings* self, std::ostream& out, SettingsEntry entry)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    u8* selfPointer = (u8*)self;
 | 
					    u8* selfPointer = (u8*)self;
 | 
				
			||||||
@@ -136,59 +144,119 @@ static void _settings_setting_write(Settings* self, std::ostream& out, SettingsE
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void settings_save(Settings* self)
 | 
					void settings_save(Settings* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    std::ifstream input(SETTINGS_PATH);
 | 
					    const std::string path = settings_path_get();
 | 
				
			||||||
    std::string oldContents;
 | 
					    const std::filesystem::path filesystemPath(path);
 | 
				
			||||||
 | 
					    const std::filesystem::path directory = filesystemPath.parent_path();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!input)
 | 
					    if (!directory.empty()) 
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        log_error(std::format(SETTINGS_INIT_ERROR, SETTINGS_PATH));
 | 
					        std::error_code errorCode;
 | 
				
			||||||
 | 
					        std::filesystem::create_directories(directory, errorCode);
 | 
				
			||||||
 | 
					        if (errorCode) 
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            log_error(std::format(SETTINGS_DIRECTORY_ERROR, directory.string(), errorCode.message()));
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string data;
 | 
				
			||||||
 | 
					    if (std::filesystem::exists(filesystemPath)) 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (std::ifstream in(path, std::ios::binary); in)
 | 
				
			||||||
 | 
					            data.assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::filesystem::path temp = filesystemPath;
 | 
				
			||||||
 | 
					    temp += SETTINGS_TEMPORARY_EXTENSION;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::ofstream out(temp, std::ios::binary | std::ios::trunc);
 | 
				
			||||||
 | 
					    if (!out) 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        log_error(std::format(SETTINGS_INIT_ERROR, temp.string()));
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    oldContents.assign((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
 | 
					    out << SETTINGS_SECTION << "\n";
 | 
				
			||||||
    input.close();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::ofstream output(SETTINGS_PATH);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!output)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        log_error(std::format(SETTINGS_INIT_ERROR, SETTINGS_PATH));
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    output << SETTINGS_SECTION << "\n";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (s32 i = 0; i < SETTINGS_COUNT; i++)
 | 
					    for (s32 i = 0; i < SETTINGS_COUNT; i++)
 | 
				
			||||||
        _settings_setting_write(self, output, SETTINGS_ENTRIES[i]);
 | 
					        _settings_setting_write(self, out, SETTINGS_ENTRIES[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    output << "\n" << SETTINGS_SECTION_IMGUI << "\n";
 | 
					    out << "\n" << SETTINGS_SECTION_IMGUI << "\n";
 | 
				
			||||||
    output << oldContents;
 | 
					    out << data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    output.close();
 | 
					    out.flush();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!out.good()) 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        log_error(std::format(SETTINGS_SAVE_ERROR, temp.string()));
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    out.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::error_code errorCode;
 | 
				
			||||||
 | 
					    std::filesystem::rename(temp, filesystemPath, errorCode);
 | 
				
			||||||
 | 
					    if (errorCode) 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Windows can block rename if target exists; try remove+rename
 | 
				
			||||||
 | 
					        std::filesystem::remove(filesystemPath, errorCode);
 | 
				
			||||||
 | 
					        errorCode = {};
 | 
				
			||||||
 | 
					        std::filesystem::rename(temp, filesystemPath, errorCode);
 | 
				
			||||||
 | 
					        if (errorCode) 
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            log_error(std::format(SETTINGS_SAVE_FINALIZE_ERROR, filesystemPath.string(), errorCode.message()));
 | 
				
			||||||
 | 
					            std::filesystem::remove(temp);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    log_info(std::format(SETTINGS_SAVE_INFO, path));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void settings_init(Settings* self)
 | 
					void settings_init(Settings* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    std::ifstream file(SETTINGS_PATH);
 | 
					    const std::string path = settings_path_get();
 | 
				
			||||||
 | 
					    std::ifstream file(path, std::ios::binary);
 | 
				
			||||||
 | 
					    std::istream* in = nullptr;
 | 
				
			||||||
 | 
					    std::istringstream defaultSettings;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (!file)
 | 
					    if (file)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        log_error(std::format(SETTINGS_INIT_ERROR, SETTINGS_PATH));
 | 
					        log_info(std::format(SETTINGS_INIT_INFO, path));
 | 
				
			||||||
        return;
 | 
					        in = &file; 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        log_error(std::format(SETTINGS_INIT_ERROR, path));
 | 
				
			||||||
 | 
					        log_info(SETTINGS_DEFAULT_INFO);
 | 
				
			||||||
 | 
					        defaultSettings.str(SETTINGS_DEFAULT);
 | 
				
			||||||
 | 
					        in = &defaultSettings;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string line;
 | 
					    std::string line;
 | 
				
			||||||
    bool inSettingsSection = false;
 | 
					    bool inSettingsSection = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while (std::getline(file, line))
 | 
					    while (std::getline(*in, line)) 
 | 
				
			||||||
    {
 | 
					    { 
 | 
				
			||||||
        if (line == SETTINGS_SECTION)
 | 
					        if (line == SETTINGS_SECTION)
 | 
				
			||||||
        {
 | 
					        { 
 | 
				
			||||||
            inSettingsSection = true;
 | 
					            inSettingsSection = true; 
 | 
				
			||||||
            continue;
 | 
					            continue; 
 | 
				
			||||||
        }
 | 
					        } 
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (line == SETTINGS_SECTION_IMGUI) break; 
 | 
					        if (line == SETTINGS_SECTION_IMGUI) break; 
 | 
				
			||||||
        if (inSettingsSection) _settings_setting_load(self, line);
 | 
					        if (inSettingsSection) _settings_setting_load(self, line); 
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Save default settings
 | 
				
			||||||
 | 
					    if (!file) 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::ofstream out(path, std::ios::binary | std::ios::trunc);
 | 
				
			||||||
 | 
					        if (out) 
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            out << SETTINGS_DEFAULT;
 | 
				
			||||||
 | 
					            out.flush();
 | 
				
			||||||
 | 
					            log_info(std::format(SETTINGS_SAVE_INFO, path));
 | 
				
			||||||
 | 
					        } 
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            log_error(std::format(SETTINGS_DEFAULT_ERROR, path));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										219
									
								
								src/settings.h
									
									
									
									
									
								
							
							
						
						
									
										219
									
								
								src/settings.h
									
									
									
									
									
								
							@@ -8,9 +8,19 @@
 | 
				
			|||||||
#define SETTINGS_BUFFER_ITEM 0xFF
 | 
					#define SETTINGS_BUFFER_ITEM 0xFF
 | 
				
			||||||
#define SETTINGS_SECTION "[Settings]"
 | 
					#define SETTINGS_SECTION "[Settings]"
 | 
				
			||||||
#define SETTINGS_SECTION_IMGUI "# Dear ImGui"
 | 
					#define SETTINGS_SECTION_IMGUI "# Dear ImGui"
 | 
				
			||||||
#define SETTINGS_INIT_ERROR "Failed to read settings file! ({})"
 | 
					#define SETTINGS_INIT_ERROR "Failed to read settings file: {}"
 | 
				
			||||||
#define SETTINGS_PATH "settings.ini"
 | 
					#define SETTINGS_DEFAULT_ERROR "Failed to write default settings file: {}"
 | 
				
			||||||
 | 
					#define SETTINGS_SAVE_ERROR "Failed to write settings file: {}"
 | 
				
			||||||
 | 
					#define SETTINGS_SAVE_FINALIZE_ERROR "Failed to write settings file: {} ({})"
 | 
				
			||||||
#define SETTINGS_FLOAT_FORMAT "{:.3f}"
 | 
					#define SETTINGS_FLOAT_FORMAT "{:.3f}"
 | 
				
			||||||
 | 
					#define SETTINGS_INIT_INFO "Initialized settings from: {}"
 | 
				
			||||||
 | 
					#define SETTINGS_DEFAULT_INFO "Using default settings"
 | 
				
			||||||
 | 
					#define SETTINGS_DIRECTORY_ERROR "Failed to create settings directory: {} ({})"
 | 
				
			||||||
 | 
					#define SETTINGS_SAVE_INFO "Saved settings to: {}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SETTINGS_FOLDER "anm2ed"
 | 
				
			||||||
 | 
					#define SETTINGS_PATH "settings.ini"
 | 
				
			||||||
 | 
					#define SETTINGS_TEMPORARY_EXTENSION ".tmp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct SettingsEntry
 | 
					struct SettingsEntry
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -48,6 +58,7 @@ struct Settings
 | 
				
			|||||||
    bool changeIsVisible{};
 | 
					    bool changeIsVisible{};
 | 
				
			||||||
    bool changeIsInterpolated{};
 | 
					    bool changeIsInterpolated{};
 | 
				
			||||||
    s32 changeNumberFrames = 1;
 | 
					    s32 changeNumberFrames = 1;
 | 
				
			||||||
 | 
					    f32 scaleValue = 1.0f;
 | 
				
			||||||
    bool previewIsAxes = true;
 | 
					    bool previewIsAxes = true;
 | 
				
			||||||
    bool previewIsGrid = true;
 | 
					    bool previewIsGrid = true;
 | 
				
			||||||
    bool previewIsRootTransform = false;
 | 
					    bool previewIsRootTransform = false;
 | 
				
			||||||
@@ -64,11 +75,11 @@ struct Settings
 | 
				
			|||||||
    vec4 previewAxesColor = {1.0, 1.0, 1.0, 0.125};
 | 
					    vec4 previewAxesColor = {1.0, 1.0, 1.0, 0.125};
 | 
				
			||||||
    vec4 previewBackgroundColor = {0.113, 0.184, 0.286, 1.0};
 | 
					    vec4 previewBackgroundColor = {0.113, 0.184, 0.286, 1.0};
 | 
				
			||||||
    ivec2 generateStartPosition = {0, 0};
 | 
					    ivec2 generateStartPosition = {0, 0};
 | 
				
			||||||
    ivec2 generateFrameSize = {64, 64};
 | 
					    ivec2 generateSize = {64, 64};
 | 
				
			||||||
    ivec2 generatePivot = {32, 32};
 | 
					    ivec2 generatePivot = {32, 32};
 | 
				
			||||||
    s32 generateRows = 4;
 | 
					    s32 generateRows = 4;
 | 
				
			||||||
    s32 generateColumns = 4;
 | 
					    s32 generateColumns = 4;
 | 
				
			||||||
    s32 generateFrameCount = 16;
 | 
					    s32 generateCount = 16;
 | 
				
			||||||
    s32 generateDelay = 1;
 | 
					    s32 generateDelay = 1;
 | 
				
			||||||
    bool editorIsGrid = true;
 | 
					    bool editorIsGrid = true;
 | 
				
			||||||
    bool editorIsGridSnap = true;
 | 
					    bool editorIsGridSnap = true;
 | 
				
			||||||
@@ -89,7 +100,7 @@ struct Settings
 | 
				
			|||||||
    s32 renderType = RENDER_PNG;
 | 
					    s32 renderType = RENDER_PNG;
 | 
				
			||||||
    std::string renderPath = ".";
 | 
					    std::string renderPath = ".";
 | 
				
			||||||
    std::string renderFormat = "{}.png";
 | 
					    std::string renderFormat = "{}.png";
 | 
				
			||||||
    std::string ffmpegPath = "/usr/bin/ffmpeg";
 | 
					    std::string ffmpegPath{};
 | 
				
			||||||
}; 
 | 
					}; 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SettingsEntry SETTINGS_ENTRIES[] =
 | 
					const SettingsEntry SETTINGS_ENTRIES[] =
 | 
				
			||||||
@@ -121,6 +132,7 @@ const SettingsEntry SETTINGS_ENTRIES[] =
 | 
				
			|||||||
    {"changeIsVisible", TYPE_BOOL, offsetof(Settings, changeIsVisibleSet)},
 | 
					    {"changeIsVisible", TYPE_BOOL, offsetof(Settings, changeIsVisibleSet)},
 | 
				
			||||||
    {"changeIsInterpolated", TYPE_BOOL, offsetof(Settings, changeIsInterpolatedSet)},
 | 
					    {"changeIsInterpolated", TYPE_BOOL, offsetof(Settings, changeIsInterpolatedSet)},
 | 
				
			||||||
    {"changeNumberFrames", TYPE_INT, offsetof(Settings, changeNumberFrames)},
 | 
					    {"changeNumberFrames", TYPE_INT, offsetof(Settings, changeNumberFrames)},
 | 
				
			||||||
 | 
					    {"scaleValue", TYPE_FLOAT, offsetof(Settings, scaleValue)},
 | 
				
			||||||
    {"previewIsAxes", TYPE_BOOL, offsetof(Settings, previewIsAxes)},
 | 
					    {"previewIsAxes", TYPE_BOOL, offsetof(Settings, previewIsAxes)},
 | 
				
			||||||
    {"previewIsGrid", TYPE_BOOL, offsetof(Settings, previewIsGrid)},
 | 
					    {"previewIsGrid", TYPE_BOOL, offsetof(Settings, previewIsGrid)},
 | 
				
			||||||
    {"previewIsRootTransform", TYPE_BOOL, offsetof(Settings, previewIsRootTransform)},
 | 
					    {"previewIsRootTransform", TYPE_BOOL, offsetof(Settings, previewIsRootTransform)},
 | 
				
			||||||
@@ -136,12 +148,12 @@ const SettingsEntry SETTINGS_ENTRIES[] =
 | 
				
			|||||||
    {"previewGridColor", TYPE_VEC4, offsetof(Settings, previewGridColor)},
 | 
					    {"previewGridColor", TYPE_VEC4, offsetof(Settings, previewGridColor)},
 | 
				
			||||||
    {"previewAxesColor", TYPE_VEC4, offsetof(Settings, previewAxesColor)},
 | 
					    {"previewAxesColor", TYPE_VEC4, offsetof(Settings, previewAxesColor)},
 | 
				
			||||||
    {"previewBackgroundColor", TYPE_VEC4, offsetof(Settings, previewBackgroundColor)},
 | 
					    {"previewBackgroundColor", TYPE_VEC4, offsetof(Settings, previewBackgroundColor)},
 | 
				
			||||||
    {"generateStartPosition", TYPE_VEC2, offsetof(Settings, generateStartPosition)},
 | 
					    {"generateStartPosition", TYPE_IVEC2, offsetof(Settings, generateStartPosition)},
 | 
				
			||||||
    {"generateFrameSize", TYPE_VEC2, offsetof(Settings, generateFrameSize)},
 | 
					    {"generateSize", TYPE_IVEC2, offsetof(Settings, generateSize)},
 | 
				
			||||||
    {"generatePivot", TYPE_VEC2, offsetof(Settings, generatePivot)},
 | 
					    {"generatePivot", TYPE_IVEC2, offsetof(Settings, generatePivot)},
 | 
				
			||||||
    {"generateRows", TYPE_INT, offsetof(Settings, generateRows)},
 | 
					    {"generateRows", TYPE_INT, offsetof(Settings, generateRows)},
 | 
				
			||||||
    {"generateColumns", TYPE_INT, offsetof(Settings, generateColumns)},
 | 
					    {"generateColumns", TYPE_INT, offsetof(Settings, generateColumns)},
 | 
				
			||||||
    {"generateFrameCount", TYPE_INT, offsetof(Settings, generateFrameCount)},
 | 
					    {"generateCount", TYPE_INT, offsetof(Settings, generateCount)},
 | 
				
			||||||
    {"generateDelay", TYPE_INT, offsetof(Settings, generateDelay)},
 | 
					    {"generateDelay", TYPE_INT, offsetof(Settings, generateDelay)},
 | 
				
			||||||
    {"editorIsGrid", TYPE_BOOL, offsetof(Settings, editorIsGrid)},
 | 
					    {"editorIsGrid", TYPE_BOOL, offsetof(Settings, editorIsGrid)},
 | 
				
			||||||
    {"editorIsGridSnap", TYPE_BOOL, offsetof(Settings, editorIsGridSnap)},
 | 
					    {"editorIsGridSnap", TYPE_BOOL, offsetof(Settings, editorIsGridSnap)},
 | 
				
			||||||
@@ -166,5 +178,192 @@ const SettingsEntry SETTINGS_ENTRIES[] =
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
constexpr s32 SETTINGS_COUNT = (s32)std::size(SETTINGS_ENTRIES);
 | 
					constexpr s32 SETTINGS_COUNT = (s32)std::size(SETTINGS_ENTRIES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const std::string SETTINGS_DEFAULT = R"(
 | 
				
			||||||
 | 
					[Settings]
 | 
				
			||||||
 | 
					windowX=1920
 | 
				
			||||||
 | 
					windowY=1080
 | 
				
			||||||
 | 
					playbackIsLoop=true
 | 
				
			||||||
 | 
					playbackIsClampPlayhead=false
 | 
				
			||||||
 | 
					changeIsCrop=false
 | 
				
			||||||
 | 
					changeIsSize=false
 | 
				
			||||||
 | 
					changeIsPosition=false
 | 
				
			||||||
 | 
					changeIsPivot=false
 | 
				
			||||||
 | 
					changeIsScale=false
 | 
				
			||||||
 | 
					changeIsRotation=false
 | 
				
			||||||
 | 
					changeIsDelay=false
 | 
				
			||||||
 | 
					changeIsTint=false
 | 
				
			||||||
 | 
					changeIsColorOffset=false
 | 
				
			||||||
 | 
					changeIsVisibleSet=false
 | 
				
			||||||
 | 
					changeIsInterpolatedSet=false
 | 
				
			||||||
 | 
					changeIsFromSelectedFrame=false
 | 
				
			||||||
 | 
					changeCropX=0.000
 | 
				
			||||||
 | 
					changeCropY=0.000
 | 
				
			||||||
 | 
					changeSizeX=0.000
 | 
				
			||||||
 | 
					changeSizeY=0.000
 | 
				
			||||||
 | 
					changePositionX=0.000
 | 
				
			||||||
 | 
					changePositionY=0.000
 | 
				
			||||||
 | 
					changePivotX=0.000
 | 
				
			||||||
 | 
					changePivotY=0.000
 | 
				
			||||||
 | 
					changeScaleX=0.000
 | 
				
			||||||
 | 
					changeScaleY=0.000
 | 
				
			||||||
 | 
					changeRotation=0.000
 | 
				
			||||||
 | 
					changeDelay=1
 | 
				
			||||||
 | 
					changeTintR=0.000
 | 
				
			||||||
 | 
					changeTintG=0.000
 | 
				
			||||||
 | 
					changeTintB=0.000
 | 
				
			||||||
 | 
					changeTintA=0.000
 | 
				
			||||||
 | 
					changeColorOffsetR=0.000
 | 
				
			||||||
 | 
					changeColorOffsetG=0.000
 | 
				
			||||||
 | 
					changeColorOffsetB=0.000
 | 
				
			||||||
 | 
					changeIsVisible=false
 | 
				
			||||||
 | 
					changeIsInterpolated=false
 | 
				
			||||||
 | 
					changeNumberFrames=1
 | 
				
			||||||
 | 
					scaleValue=1.000
 | 
				
			||||||
 | 
					previewIsAxes=true
 | 
				
			||||||
 | 
					previewIsGrid=false
 | 
				
			||||||
 | 
					previewIsRootTransform=true
 | 
				
			||||||
 | 
					previewIsTriggers=false
 | 
				
			||||||
 | 
					previewIsPivots=false
 | 
				
			||||||
 | 
					previewIsTargets=true
 | 
				
			||||||
 | 
					previewIsBorder=false
 | 
				
			||||||
 | 
					previewOverlayTransparency=255.000
 | 
				
			||||||
 | 
					previewZoom=400.000
 | 
				
			||||||
 | 
					previewPanX=0.000
 | 
				
			||||||
 | 
					previewPanY=0.000
 | 
				
			||||||
 | 
					previewGridSizeX=32
 | 
				
			||||||
 | 
					previewGridSizeY=32
 | 
				
			||||||
 | 
					previewGridOffsetX=0
 | 
				
			||||||
 | 
					previewGridOffsetY=0
 | 
				
			||||||
 | 
					previewGridColorR=1.000
 | 
				
			||||||
 | 
					previewGridColorG=1.000
 | 
				
			||||||
 | 
					previewGridColorB=1.000
 | 
				
			||||||
 | 
					previewGridColorA=0.125
 | 
				
			||||||
 | 
					previewAxesColorR=1.000
 | 
				
			||||||
 | 
					previewAxesColorG=1.000
 | 
				
			||||||
 | 
					previewAxesColorB=1.000
 | 
				
			||||||
 | 
					previewAxesColorA=0.125
 | 
				
			||||||
 | 
					previewBackgroundColorR=0.114
 | 
				
			||||||
 | 
					previewBackgroundColorG=0.184
 | 
				
			||||||
 | 
					previewBackgroundColorB=0.286
 | 
				
			||||||
 | 
					previewBackgroundColorA=1.000
 | 
				
			||||||
 | 
					generateStartPositionX=0.000
 | 
				
			||||||
 | 
					generateStartPositionY=0.000
 | 
				
			||||||
 | 
					generateSizeX=0.000
 | 
				
			||||||
 | 
					generateSizeY=0.000
 | 
				
			||||||
 | 
					generatePivotX=0.000
 | 
				
			||||||
 | 
					generatePivotY=0.000
 | 
				
			||||||
 | 
					generateRows=4
 | 
				
			||||||
 | 
					generateColumns=4
 | 
				
			||||||
 | 
					generateCount=16
 | 
				
			||||||
 | 
					generateDelay=1
 | 
				
			||||||
 | 
					editorIsGrid=true
 | 
				
			||||||
 | 
					editorIsGridSnap=true
 | 
				
			||||||
 | 
					editorIsBorder=true
 | 
				
			||||||
 | 
					editorZoom=400.000
 | 
				
			||||||
 | 
					editorPanX=0.000
 | 
				
			||||||
 | 
					editorPanY=0.000
 | 
				
			||||||
 | 
					editorGridSizeX=32
 | 
				
			||||||
 | 
					editorGridSizeY=32
 | 
				
			||||||
 | 
					editorGridOffsetX=0
 | 
				
			||||||
 | 
					editorGridOffsetY=0
 | 
				
			||||||
 | 
					editorGridColorR=1.000
 | 
				
			||||||
 | 
					editorGridColorG=1.000
 | 
				
			||||||
 | 
					editorGridColorB=1.000
 | 
				
			||||||
 | 
					editorGridColorA=0.125
 | 
				
			||||||
 | 
					editorBackgroundColorR=0.113
 | 
				
			||||||
 | 
					editorBackgroundColorG=0.183
 | 
				
			||||||
 | 
					editorBackgroundColorB=0.286
 | 
				
			||||||
 | 
					editorBackgroundColorA=1.000
 | 
				
			||||||
 | 
					mergeType=1
 | 
				
			||||||
 | 
					mergeIsDeleteAnimationsAfter=false
 | 
				
			||||||
 | 
					bakeInterval=1
 | 
				
			||||||
 | 
					bakeRoundScale=true
 | 
				
			||||||
 | 
					bakeRoundRotation=true
 | 
				
			||||||
 | 
					tool=0
 | 
				
			||||||
 | 
					toolColorR=0.000
 | 
				
			||||||
 | 
					toolColorG=0.000
 | 
				
			||||||
 | 
					toolColorB=0.000
 | 
				
			||||||
 | 
					toolColorA=1.000
 | 
				
			||||||
 | 
					renderType=0
 | 
				
			||||||
 | 
					renderPath=.
 | 
				
			||||||
 | 
					renderFormat={}.png
 | 
				
			||||||
 | 
					ffmpegPath=/usr/bin/ffmpeg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Dear ImGui
 | 
				
			||||||
 | 
					[Window][## Window]
 | 
				
			||||||
 | 
					Pos=0,32
 | 
				
			||||||
 | 
					Size=1918,1032
 | 
				
			||||||
 | 
					Collapsed=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Window][Debug##Default]
 | 
				
			||||||
 | 
					Pos=60,60
 | 
				
			||||||
 | 
					Size=400,400
 | 
				
			||||||
 | 
					Collapsed=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Window][Tools]
 | 
				
			||||||
 | 
					Pos=8,40
 | 
				
			||||||
 | 
					Size=39,654
 | 
				
			||||||
 | 
					Collapsed=0
 | 
				
			||||||
 | 
					DockId=0x0000000B,0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Window][Animations]
 | 
				
			||||||
 | 
					Pos=1452,388
 | 
				
			||||||
 | 
					Size=458,306
 | 
				
			||||||
 | 
					Collapsed=0
 | 
				
			||||||
 | 
					DockId=0x0000000A,0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Window][Events]
 | 
				
			||||||
 | 
					Pos=1025,348
 | 
				
			||||||
 | 
					Size=425,346
 | 
				
			||||||
 | 
					Collapsed=0
 | 
				
			||||||
 | 
					DockId=0x00000008,0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Window][Spritesheets]
 | 
				
			||||||
 | 
					Pos=1452,40
 | 
				
			||||||
 | 
					Size=458,346
 | 
				
			||||||
 | 
					Collapsed=0
 | 
				
			||||||
 | 
					DockId=0x00000009,0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Window][Animation Preview]
 | 
				
			||||||
 | 
					Pos=49,40
 | 
				
			||||||
 | 
					Size=974,654
 | 
				
			||||||
 | 
					Collapsed=0
 | 
				
			||||||
 | 
					DockId=0x0000000C,0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Window][Spritesheet Editor]
 | 
				
			||||||
 | 
					Pos=49,40
 | 
				
			||||||
 | 
					Size=974,654
 | 
				
			||||||
 | 
					Collapsed=0
 | 
				
			||||||
 | 
					DockId=0x0000000C,1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Window][Timeline]
 | 
				
			||||||
 | 
					Pos=8,696
 | 
				
			||||||
 | 
					Size=1902,360
 | 
				
			||||||
 | 
					Collapsed=0
 | 
				
			||||||
 | 
					DockId=0x00000004,0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Window][Frame Properties]
 | 
				
			||||||
 | 
					Pos=1025,40
 | 
				
			||||||
 | 
					Size=425,306
 | 
				
			||||||
 | 
					Collapsed=0
 | 
				
			||||||
 | 
					DockId=0x00000007,0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Docking][Data]
 | 
				
			||||||
 | 
					DockSpace         ID=0xFC02A410 Window=0x0E46F4F7 Pos=8,40 Size=1902,1016 Split=Y
 | 
				
			||||||
 | 
					  DockNode        ID=0x00000003 Parent=0xFC02A410 SizeRef=1902,654 Split=X
 | 
				
			||||||
 | 
					    DockNode      ID=0x00000001 Parent=0x00000003 SizeRef=1442,1016 Split=X Selected=0x024430EF
 | 
				
			||||||
 | 
					      DockNode    ID=0x00000005 Parent=0x00000001 SizeRef=1015,654 Split=X Selected=0x024430EF
 | 
				
			||||||
 | 
					        DockNode  ID=0x0000000B Parent=0x00000005 SizeRef=39,654 Selected=0x18A5FDB9
 | 
				
			||||||
 | 
					        DockNode  ID=0x0000000C Parent=0x00000005 SizeRef=974,654 CentralNode=1 Selected=0x024430EF
 | 
				
			||||||
 | 
					      DockNode    ID=0x00000006 Parent=0x00000001 SizeRef=425,654 Split=Y Selected=0x754E368F
 | 
				
			||||||
 | 
					        DockNode  ID=0x00000007 Parent=0x00000006 SizeRef=631,306 Selected=0x754E368F
 | 
				
			||||||
 | 
					        DockNode  ID=0x00000008 Parent=0x00000006 SizeRef=631,346 Selected=0x8A65D963
 | 
				
			||||||
 | 
					    DockNode      ID=0x00000002 Parent=0x00000003 SizeRef=458,1016 Split=Y Selected=0x4EFD0020
 | 
				
			||||||
 | 
					      DockNode    ID=0x00000009 Parent=0x00000002 SizeRef=634,346 Selected=0x4EFD0020
 | 
				
			||||||
 | 
					      DockNode    ID=0x0000000A Parent=0x00000002 SizeRef=634,306 Selected=0xC1986EE2
 | 
				
			||||||
 | 
					  DockNode        ID=0x00000004 Parent=0xFC02A410 SizeRef=1902,360 Selected=0x4F89F0DC
 | 
				
			||||||
 | 
					)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void settings_save(Settings* self);
 | 
					void settings_save(Settings* self);
 | 
				
			||||||
void settings_init(Settings* self);
 | 
					void settings_init(Settings* self);
 | 
				
			||||||
 | 
					std::string settings_path_get(void);
 | 
				
			||||||
@@ -24,9 +24,10 @@ static void _draw(State* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void init(State* self)
 | 
					void init(State* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	settings_init(&self->settings);
 | 
					
 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	log_info(STATE_INIT_INFO);
 | 
						log_info(STATE_INIT_INFO);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						settings_init(&self->settings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!SDL_Init(SDL_INIT_VIDEO))
 | 
						if (!SDL_Init(SDL_INIT_VIDEO))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -76,8 +77,6 @@ void init(State* self)
 | 
				
			|||||||
   	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
 | 
					   	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
 | 
				
			||||||
   	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
 | 
					   	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	glewInit();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	self->glContext = SDL_GL_CreateContext(self->window);
 | 
						self->glContext = SDL_GL_CreateContext(self->window);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	if (!self->glContext)
 | 
						if (!self->glContext)
 | 
				
			||||||
@@ -86,6 +85,8 @@ void init(State* self)
 | 
				
			|||||||
		quit(self);
 | 
							quit(self);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						glewInit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log_info(std::format(STATE_GL_CONTEXT_INIT_INFO, (const char*)glGetString(GL_VERSION)));
 | 
						log_info(std::format(STATE_GL_CONTEXT_INIT_INFO, (const char*)glGetString(GL_VERSION)));
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	glEnable(GL_BLEND);
 | 
						glEnable(GL_BLEND);
 | 
				
			||||||
@@ -100,6 +101,7 @@ void init(State* self)
 | 
				
			|||||||
	clipboard_init(&self->clipboard, &self->anm2);
 | 
						clipboard_init(&self->clipboard, &self->anm2);
 | 
				
			||||||
	snapshots_init(&self->snapshots, &self->anm2, &self->reference, &self->preview);
 | 
						snapshots_init(&self->snapshots, &self->anm2, &self->reference, &self->preview);
 | 
				
			||||||
	preview_init(&self->preview, &self->anm2, &self->reference, &self->resources, &self->settings);
 | 
						preview_init(&self->preview, &self->anm2, &self->reference, &self->resources, &self->settings);
 | 
				
			||||||
 | 
						generate_preview_init(&self->generatePreview, &self->anm2, &self->reference, &self->resources, &self->settings);
 | 
				
			||||||
	editor_init(&self->editor, &self->anm2, &self->reference, &self->resources, &self->settings);
 | 
						editor_init(&self->editor, &self->anm2, &self->reference, &self->resources, &self->settings);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	imgui_init
 | 
						imgui_init
 | 
				
			||||||
@@ -111,6 +113,7 @@ void init(State* self)
 | 
				
			|||||||
		&self->reference,
 | 
							&self->reference,
 | 
				
			||||||
		&self->editor,
 | 
							&self->editor,
 | 
				
			||||||
		&self->preview,
 | 
							&self->preview,
 | 
				
			||||||
 | 
							&self->generatePreview,
 | 
				
			||||||
		&self->settings,
 | 
							&self->settings,
 | 
				
			||||||
		&self->snapshots,
 | 
							&self->snapshots,
 | 
				
			||||||
		&self->clipboard,
 | 
							&self->clipboard,
 | 
				
			||||||
@@ -118,7 +121,7 @@ void init(State* self)
 | 
				
			|||||||
		&self->glContext
 | 
							&self->glContext
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (self->is_argument())
 | 
						if (!self->argument.empty())
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		anm2_deserialize(&self->anm2, &self->resources, self->argument);
 | 
							anm2_deserialize(&self->anm2, &self->resources, self->argument);
 | 
				
			||||||
		window_title_from_path_set(self->window, self->argument);
 | 
							window_title_from_path_set(self->window, self->argument);
 | 
				
			||||||
@@ -152,6 +155,7 @@ void loop(State* self)
 | 
				
			|||||||
void quit(State* self)
 | 
					void quit(State* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	imgui_free();
 | 
						imgui_free();
 | 
				
			||||||
 | 
						generate_preview_free(&self->generatePreview);
 | 
				
			||||||
	preview_free(&self->preview);
 | 
						preview_free(&self->preview);
 | 
				
			||||||
	editor_free(&self->editor);
 | 
						editor_free(&self->editor);
 | 
				
			||||||
	resources_free(&self->resources);
 | 
						resources_free(&self->resources);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,7 @@ struct State
 | 
				
			|||||||
	Dialog dialog;
 | 
						Dialog dialog;
 | 
				
			||||||
	Editor editor;
 | 
						Editor editor;
 | 
				
			||||||
	Preview preview;
 | 
						Preview preview;
 | 
				
			||||||
 | 
						GeneratePreview generatePreview;
 | 
				
			||||||
    Anm2 anm2;
 | 
					    Anm2 anm2;
 | 
				
			||||||
	Anm2Reference reference;
 | 
						Anm2Reference reference;
 | 
				
			||||||
	Resources resources;
 | 
						Resources resources;
 | 
				
			||||||
@@ -43,9 +44,6 @@ struct State
 | 
				
			|||||||
	u64 lastTick{};
 | 
						u64 lastTick{};
 | 
				
			||||||
	u64 tick{};
 | 
						u64 tick{};
 | 
				
			||||||
	bool isRunning = true;
 | 
						bool isRunning = true;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool is_last_action() const { return !lastAction.empty(); }
 | 
					 | 
				
			||||||
	bool is_argument() const { return !argument.empty(); }
 | 
					 | 
				
			||||||
}; 
 | 
					}; 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void init(State* state);
 | 
					void init(State* state);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,12 +35,16 @@ bool texture_from_path_init(Texture* self, const std::string& path)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	*self = Texture{};
 | 
						*self = Texture{};
 | 
				
			||||||
	u8* data = stbi_load(path.c_str(), &self->size.x, &self->size.y, &self->channels, TEXTURE_CHANNELS);
 | 
						u8* data = stbi_load(path.c_str(), &self->size.x, &self->size.y, &self->channels, TEXTURE_CHANNELS);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	if (!data)
 | 
						if (!data)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		log_error(std::format(TEXTURE_INIT_ERROR, path));
 | 
							data = stbi_load(path_canonical_resolve(path).c_str(), &self->size.x, &self->size.y, &self->channels, TEXTURE_CHANNELS);
 | 
				
			||||||
		self->isInvalid = true;
 | 
							if (!data)
 | 
				
			||||||
		return false;
 | 
							{
 | 
				
			||||||
 | 
								log_error(std::format(TEXTURE_INIT_ERROR, path));
 | 
				
			||||||
 | 
								self->isInvalid = true;
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log_info(std::format(TEXTURE_INIT_INFO, path));
 | 
						log_info(std::format(TEXTURE_INIT_INFO, path));
 | 
				
			||||||
@@ -82,8 +86,16 @@ bool texture_from_rgba_init(Texture* self, ivec2 size, s32 channels, const u8* d
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
bool texture_from_rgba_write(const std::string& path, const u8* data, ivec2 size)
 | 
					bool texture_from_rgba_write(const std::string& path, const u8* data, ivec2 size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						bool isSuccess = stbi_write_png(path.c_str(), size.x, size.y, TEXTURE_CHANNELS, data, size.x * TEXTURE_CHANNELS);
 | 
				
			||||||
 | 
						if (!isSuccess)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							isSuccess = stbi_write_png(path_canonical_resolve(path).c_str(), size.x, size.y, TEXTURE_CHANNELS, data, size.x * TEXTURE_CHANNELS);
 | 
				
			||||||
 | 
							if (!isSuccess) log_info(std::format(TEXTURE_SAVE_ERROR, path));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
	log_info(std::format(TEXTURE_SAVE_INFO, path));
 | 
						log_info(std::format(TEXTURE_SAVE_INFO, path));
 | 
				
			||||||
	return (bool)stbi_write_png(path.c_str(), size.x, size.y, TEXTURE_CHANNELS, data, size.x * TEXTURE_CHANNELS);
 | 
							
 | 
				
			||||||
 | 
						return isSuccess;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool texture_from_gl_write(Texture* self, const std::string& path)
 | 
					bool texture_from_gl_write(Texture* self, const std::string& path)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
#define TEXTURE_INIT_INFO "Initialized texture from file: {}"
 | 
					#define TEXTURE_INIT_INFO "Initialized texture from file: {}"
 | 
				
			||||||
#define TEXTURE_INIT_ERROR "Failed to initialize texture from file: {}"
 | 
					#define TEXTURE_INIT_ERROR "Failed to initialize texture from file: {}"
 | 
				
			||||||
#define TEXTURE_SAVE_INFO "Saved texture to: {}"
 | 
					#define TEXTURE_SAVE_INFO "Saved texture to: {}"
 | 
				
			||||||
 | 
					#define TEXTURE_SAVE_ERROR "Failed to save texture to: {}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Texture
 | 
					struct Texture
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user