Moved spritesheet texture handling to anm2 instead of resources; added undoing for spritesheet texture changes; refactoring
This commit is contained in:
		@@ -26,7 +26,7 @@ Note, to render animations, you'll need to download [FFmpeg](https://ffmpeg.org/
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
After cloning and enter the repository's directory, make sure to initialize the submodules:
 | 
					After cloning and enter the repository's directory, make sure to initialize the submodules:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```git submodules update --init```
 | 
					```git submodule update --init```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Then:
 | 
					Then:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										55
									
								
								src/COMMON.h
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								src/COMMON.h
									
									
									
									
									
								
							@@ -56,6 +56,7 @@ using namespace glm;
 | 
				
			|||||||
#define ID_NONE -1
 | 
					#define ID_NONE -1
 | 
				
			||||||
#define INDEX_NONE -1
 | 
					#define INDEX_NONE -1
 | 
				
			||||||
#define TIME_NONE -1.0f
 | 
					#define TIME_NONE -1.0f
 | 
				
			||||||
 | 
					#define GL_ID_NONE 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(_WIN32)
 | 
					#if defined(_WIN32)
 | 
				
			||||||
  #define POPEN  _popen
 | 
					  #define POPEN  _popen
 | 
				
			||||||
@@ -69,29 +70,6 @@ using namespace glm;
 | 
				
			|||||||
  #define PREAD_MODE "r"
 | 
					  #define PREAD_MODE "r"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define UV_VERTICES(uvMin, uvMax) \
 | 
					 | 
				
			||||||
{ \
 | 
					 | 
				
			||||||
  0, 0, uvMin.x, uvMin.y, \
 | 
					 | 
				
			||||||
  1, 0, uvMax.x, uvMin.y, \
 | 
					 | 
				
			||||||
  1, 1, uvMax.x, uvMax.y, \
 | 
					 | 
				
			||||||
  0, 1, uvMin.x, uvMax.y  \
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const f32 GL_VERTICES[] =
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    0, 0,  
 | 
					 | 
				
			||||||
    1, 0,  
 | 
					 | 
				
			||||||
    1, 1,  
 | 
					 | 
				
			||||||
    0, 1  
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
constexpr f32 GL_UV_VERTICES[] = 
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    0, 0, 0.0f, 0.0f,
 | 
					 | 
				
			||||||
    1, 0, 1.0f, 0.0f,
 | 
					 | 
				
			||||||
    1, 1, 1.0f, 1.0f,
 | 
					 | 
				
			||||||
    0, 1, 0.0f, 1.0f
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const GLuint GL_TEXTURE_INDICES[] = {0, 1, 2, 2, 3, 0};
 | 
					static const GLuint GL_TEXTURE_INDICES[] = {0, 1, 2, 2, 3, 0};
 | 
				
			||||||
static const vec4 COLOR_RED = {1.0f, 0.0f, 0.0f, 1.0f};
 | 
					static const vec4 COLOR_RED = {1.0f, 0.0f, 0.0f, 1.0f};
 | 
				
			||||||
@@ -364,38 +342,7 @@ static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& v
 | 
				
			|||||||
    map[insertIndex] = value;
 | 
					    map[insertIndex] = value;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline mat4 quad_model_get(vec2 size, vec2 position, vec2 pivot, f32 rotation, vec2 scale)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    vec2 scaleAbsolute  = glm::abs(scale);
 | 
					 | 
				
			||||||
    vec2 scaleSign = glm::sign(scale);
 | 
					 | 
				
			||||||
    vec2 pivotScaled = pivot * scaleAbsolute;
 | 
					 | 
				
			||||||
    vec2 sizeScaled  = size  * scaleAbsolute;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mat4 model(1.0f);
 | 
					 | 
				
			||||||
    model = glm::translate(model, vec3(position - pivotScaled, 0.0f));
 | 
					 | 
				
			||||||
    model = glm::translate(model, vec3(pivotScaled, 0.0f));
 | 
					 | 
				
			||||||
    model = glm::scale(model, vec3(scaleSign, 1.0f));
 | 
					 | 
				
			||||||
    model = glm::rotate(model, glm::radians(rotation), vec3(0, 0, 1));
 | 
					 | 
				
			||||||
    model = glm::translate(model, vec3(-pivotScaled, 0.0f));
 | 
					 | 
				
			||||||
    model = glm::scale(model, vec3(sizeScaled, 1.0f));
 | 
					 | 
				
			||||||
    return model;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline mat4 quad_parent_model_get(vec2 position, vec2 pivot, f32 rotation, vec2 scale)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    vec2 scaleSign = glm::sign(scale);
 | 
					 | 
				
			||||||
    vec2 scaleAbsolute  = glm::abs(scale);
 | 
					 | 
				
			||||||
    f32 handedness = (scaleSign.x * scaleSign.y) < 0.0f ? -1.0f : 1.0f;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    mat4 local(1.0f);
 | 
					 | 
				
			||||||
    local = glm::translate(local, vec3(pivot, 0.0f));
 | 
					 | 
				
			||||||
    local = glm::scale(local, vec3(scaleSign, 1.0f)); // mirror if needed
 | 
					 | 
				
			||||||
    local = glm::rotate(local, glm::radians(rotation) * handedness, vec3(0, 0, 1));
 | 
					 | 
				
			||||||
    local = glm::translate(local, vec3(-pivot, 0.0f));
 | 
					 | 
				
			||||||
    local = glm::scale(local, vec3(scaleAbsolute, 1.0f));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return glm::translate(mat4(1.0f), vec3(position, 0.0f)) * local;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DEFINE_ENUM_TO_STRING_FUNCTION(function, array, count) \
 | 
					#define DEFINE_ENUM_TO_STRING_FUNCTION(function, array, count) \
 | 
				
			||||||
    static inline std::string function(s32 index)              \
 | 
					    static inline std::string function(s32 index)              \
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								src/PACKED.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/PACKED.h
									
									
									
									
									
								
							@@ -275,21 +275,19 @@ const std::string SHADER_GRID_FRAGMENT = R"(
 | 
				
			|||||||
#version 330 core
 | 
					#version 330 core
 | 
				
			||||||
in vec2 clip;
 | 
					in vec2 clip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uniform mat4 u_model;   // inverse of your world->clip matrix (MVP)
 | 
					uniform mat4 u_model;
 | 
				
			||||||
uniform vec2 u_size;     // world-space cell size (e.g. 64,64)
 | 
					uniform vec2 u_size; 
 | 
				
			||||||
uniform vec2 u_offset;   // world-space grid offset (shifts entire grid)
 | 
					uniform vec2 u_offset;
 | 
				
			||||||
uniform vec4 u_color;    // RGBA
 | 
					uniform vec4 u_color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out vec4 o_fragColor;
 | 
					out vec4 o_fragColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void main() 
 | 
					void main() 
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // clip -> world on z=0 plane
 | 
					 | 
				
			||||||
    vec4 w = u_model * vec4(clip, 0.0, 1.0);
 | 
					    vec4 w = u_model * vec4(clip, 0.0, 1.0);
 | 
				
			||||||
    w /= w.w;
 | 
					    w /= w.w;
 | 
				
			||||||
    vec2 world = w.xy;
 | 
					    vec2 world = w.xy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // grid space
 | 
					 | 
				
			||||||
    vec2 g = (world - u_offset) / u_size;
 | 
					    vec2 g = (world - u_offset) / u_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    vec2 d = abs(fract(g) - 0.5);
 | 
					    vec2 d = abs(fract(g) - 0.5);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										47
									
								
								src/anm2.cpp
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								src/anm2.cpp
									
									
									
									
									
								
							@@ -303,7 +303,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
 | 
				
			|||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
 | 
					bool anm2_deserialize(Anm2* self, const std::string& path)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	XMLDocument xmlDocument;
 | 
						XMLDocument xmlDocument;
 | 
				
			||||||
	XMLError xmlError;
 | 
						XMLError xmlError;
 | 
				
			||||||
@@ -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 && resources) 
 | 
							if (anm2Element == ANM2_ELEMENT_SPRITESHEET)
 | 
				
			||||||
			resources_texture_init(resources, spritesheet->path, id);
 | 
								texture_from_path_init(&spritesheet->texture, spritesheet->path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		xmlChild = xmlElement->FirstChildElement();
 | 
							xmlChild = xmlElement->FirstChildElement();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -620,6 +620,10 @@ bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path)
 | 
				
			|||||||
		if (animation.name == defaultAnimation)
 | 
							if (animation.name == defaultAnimation)
 | 
				
			||||||
			self->defaultAnimationID = id;
 | 
								self->defaultAnimationID = id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Copy texture data to pixels (used for snapshots)
 | 
				
			||||||
 | 
						anm2_spritesheet_texture_pixels_download(self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Read
 | 
				
			||||||
	log_info(std::format(ANM2_READ_INFO, path));
 | 
						log_info(std::format(ANM2_READ_INFO, path));
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	// Return to old working directory
 | 
						// Return to old working directory
 | 
				
			||||||
@@ -706,7 +710,6 @@ void anm2_null_remove(Anm2* self, s32 id)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
s32 anm2_animation_add(Anm2* self)
 | 
					s32 anm2_animation_add(Anm2* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    s32 id  = map_next_id_get(self->animations);
 | 
					    s32 id  = map_next_id_get(self->animations);
 | 
				
			||||||
@@ -1168,3 +1171,39 @@ void anm2_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPos
 | 
				
			|||||||
		frameReference.frameIndex++;
 | 
							frameReference.frameIndex++;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void anm2_free(Anm2* self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (auto& [id, spritesheet] : self->spritesheets)
 | 
				
			||||||
 | 
							texture_free(&spritesheet.texture);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void anm2_spritesheet_texture_pixels_upload(Anm2* self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    for (auto& [_, spritesheet] : self->spritesheets)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Texture& texture = spritesheet.texture;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (texture.id != GL_ID_NONE && !texture.isInvalid)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            assert(!spritesheet.pixels.empty());
 | 
				
			||||||
 | 
					            glBindTexture(GL_TEXTURE_2D, texture.id);
 | 
				
			||||||
 | 
					            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture.size.x, texture.size.y, GL_RGBA, GL_UNSIGNED_BYTE, spritesheet.pixels.data());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void anm2_spritesheet_texture_pixels_download(Anm2* self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    for (auto& [_, spritesheet] : self->spritesheets)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Texture& texture = spritesheet.texture;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (texture.id != GL_ID_NONE && !texture.isInvalid)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            spritesheet.pixels.resize(texture.size.x * texture.size.y * texture.channels);
 | 
				
			||||||
 | 
					            glBindTexture(GL_TEXTURE_2D, texture.id);
 | 
				
			||||||
 | 
					            glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, spritesheet.pixels.data());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/anm2.h
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/anm2.h
									
									
									
									
									
								
							@@ -130,6 +130,8 @@ enum Anm2Type
 | 
				
			|||||||
struct Anm2Spritesheet
 | 
					struct Anm2Spritesheet
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    std::string path{};
 | 
					    std::string path{};
 | 
				
			||||||
 | 
					    Texture texture;
 | 
				
			||||||
 | 
					    std::vector<u8> pixels; 
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Anm2Layer
 | 
					struct Anm2Layer
 | 
				
			||||||
@@ -162,8 +164,8 @@ struct Anm2Frame
 | 
				
			|||||||
	vec2 position{};
 | 
						vec2 position{};
 | 
				
			||||||
	vec2 size{};
 | 
						vec2 size{};
 | 
				
			||||||
	vec2 scale = {100, 100};
 | 
						vec2 scale = {100, 100};
 | 
				
			||||||
	vec3 offsetRGB{};
 | 
						vec3 offsetRGB = COLOR_OFFSET_NONE;
 | 
				
			||||||
	vec4 tintRGBA = {1.0f, 1.0f, 1.0f, 1.0f};
 | 
						vec4 tintRGBA = COLOR_OPAQUE;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Anm2FrameChange
 | 
					struct Anm2FrameChange
 | 
				
			||||||
@@ -261,12 +263,12 @@ void anm2_layer_remove(Anm2* self, s32 id);
 | 
				
			|||||||
void anm2_null_add(Anm2* self);
 | 
					void anm2_null_add(Anm2* self);
 | 
				
			||||||
void anm2_null_remove(Anm2* self, s32 id);
 | 
					void anm2_null_remove(Anm2* self, s32 id);
 | 
				
			||||||
bool anm2_serialize(Anm2* self, const std::string& path);
 | 
					bool anm2_serialize(Anm2* self, const std::string& path);
 | 
				
			||||||
bool anm2_deserialize(Anm2* self, Resources* resources, const std::string& path);
 | 
					bool anm2_deserialize(Anm2* self, const std::string& path);
 | 
				
			||||||
void anm2_new(Anm2* self);
 | 
					void anm2_new(Anm2* self);
 | 
				
			||||||
 | 
					void anm2_free(Anm2* self);
 | 
				
			||||||
void anm2_created_on_set(Anm2* self);
 | 
					void anm2_created_on_set(Anm2* self);
 | 
				
			||||||
s32 anm2_animation_add(Anm2* self);
 | 
					s32 anm2_animation_add(Anm2* self);
 | 
				
			||||||
void anm2_animation_remove(Anm2* self, s32 id);
 | 
					void anm2_animation_remove(Anm2* self, s32 id);
 | 
				
			||||||
void anm2_spritesheet_texture_load(Anm2* self, Resources* resources, const std::string& path, s32 id);
 | 
					 | 
				
			||||||
Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference);
 | 
					Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference);
 | 
				
			||||||
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);
 | 
				
			||||||
@@ -284,3 +286,5 @@ void anm2_frame_bake(Anm2* self, Anm2Reference* reference, s32 interval, bool is
 | 
				
			|||||||
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_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);
 | 
					void anm2_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPosition, vec2 size, vec2 pivot, s32 columns, s32 count, s32 delay);
 | 
				
			||||||
 | 
					void anm2_spritesheet_texture_pixels_upload(Anm2* self);
 | 
				
			||||||
 | 
					void anm2_spritesheet_texture_pixels_download(Anm2* self);
 | 
				
			||||||
@@ -1,32 +1,19 @@
 | 
				
			|||||||
#include "canvas.h"
 | 
					#include "canvas.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _canvas_texture_free(Canvas* self)
 | 
					static void _canvas_framebuffer_set(Canvas* self, const ivec2& size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (self->fbo != 0)     glDeleteFramebuffers(1, &self->fbo);
 | 
					 | 
				
			||||||
    if (self->rbo != 0)     glDeleteRenderbuffers(1, &self->rbo);
 | 
					 | 
				
			||||||
    if (self->texture != 0) glDeleteTextures(1, &self->texture);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void _canvas_texture_init(Canvas* self, const ivec2& size)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    _canvas_texture_free(self);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    self->size = size;
 | 
					    self->size = size;
 | 
				
			||||||
    self->previousSize = size;
 | 
					    self->previousSize = size;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    glGenFramebuffers(1, &self->fbo);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
 | 
					    glBindFramebuffer(GL_FRAMEBUFFER, self->fbo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    glGenTextures(1, &self->texture);
 | 
					    glBindTexture(GL_TEXTURE_2D, self->framebuffer);
 | 
				
			||||||
    glBindTexture(GL_TEXTURE_2D, self->texture);
 | 
					 | 
				
			||||||
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
 | 
					    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
 | 
				
			||||||
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
					    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
				
			||||||
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
					    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->texture, 0);
 | 
					    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->framebuffer, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    glGenRenderbuffers(1, &self->rbo);
 | 
					 | 
				
			||||||
    glBindRenderbuffer(GL_RENDERBUFFER, self->rbo);
 | 
					    glBindRenderbuffer(GL_RENDERBUFFER, self->rbo);
 | 
				
			||||||
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, self->size.x, self->size.y);
 | 
					    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, self->size.x, self->size.y);
 | 
				
			||||||
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo);
 | 
					    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, self->rbo);
 | 
				
			||||||
@@ -65,7 +52,7 @@ void canvas_init(Canvas* self, const ivec2& size)
 | 
				
			|||||||
    glBindVertexArray(self->rectVAO);
 | 
					    glBindVertexArray(self->rectVAO);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    glBindBuffer(GL_ARRAY_BUFFER, self->rectVBO);
 | 
					    glBindBuffer(GL_ARRAY_BUFFER, self->rectVBO);
 | 
				
			||||||
    glBufferData(GL_ARRAY_BUFFER, sizeof(GL_VERTICES), GL_VERTICES, GL_STATIC_DRAW);
 | 
					    glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_RECT_VERTICES), CANVAS_RECT_VERTICES, GL_STATIC_DRAW);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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);
 | 
				
			||||||
@@ -104,7 +91,11 @@ void canvas_init(Canvas* self, const ivec2& size)
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    glBindVertexArray(0);
 | 
					    glBindVertexArray(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _canvas_texture_init(self, size);
 | 
					    // Framebuffer
 | 
				
			||||||
 | 
					    glGenTextures(1, &self->framebuffer);
 | 
				
			||||||
 | 
					    glGenFramebuffers(1, &self->fbo);
 | 
				
			||||||
 | 
					    glGenRenderbuffers(1, &self->rbo);
 | 
				
			||||||
 | 
					    _canvas_framebuffer_set(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)
 | 
				
			||||||
@@ -140,10 +131,10 @@ void canvas_viewport_set(Canvas* self)
 | 
				
			|||||||
    glViewport(0, 0, (s32)self->size.x, (s32)self->size.y);
 | 
					    glViewport(0, 0, (s32)self->size.x, (s32)self->size.y);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void canvas_texture_set(Canvas* self)
 | 
					void canvas_framebuffer_resize_check(Canvas* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (self->previousSize != self->size)
 | 
					    if (self->previousSize != self->size)
 | 
				
			||||||
        _canvas_texture_init(self, self->size);
 | 
					        _canvas_framebuffer_set(self, self->size);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color)
 | 
					void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color)
 | 
				
			||||||
@@ -171,7 +162,7 @@ void canvas_texture_draw(Canvas* self, GLuint& shader, GLuint& texture, mat4& tr
 | 
				
			|||||||
    glBindVertexArray(self->textureVAO);
 | 
					    glBindVertexArray(self->textureVAO);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
 | 
					    glBindBuffer(GL_ARRAY_BUFFER, self->textureVBO);
 | 
				
			||||||
    glBufferData(GL_ARRAY_BUFFER, sizeof(GL_UV_VERTICES), vertices, GL_DYNAMIC_DRAW);
 | 
					    glBufferData(GL_ARRAY_BUFFER, sizeof(CANVAS_TEXTURE_VERTICES), vertices, GL_DYNAMIC_DRAW);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    glActiveTexture(GL_TEXTURE0);
 | 
					    glActiveTexture(GL_TEXTURE0);
 | 
				
			||||||
    glBindTexture(GL_TEXTURE_2D, texture);
 | 
					    glBindTexture(GL_TEXTURE_2D, texture);
 | 
				
			||||||
@@ -227,30 +218,49 @@ void canvas_unbind(void)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void canvas_free(Canvas* self)
 | 
					void canvas_free(Canvas* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    _canvas_texture_free(self);
 | 
					    glDeleteFramebuffers(1, &self->fbo);
 | 
				
			||||||
 | 
					    glDeleteRenderbuffers(1, &self->rbo);
 | 
				
			||||||
 | 
					    glDeleteTextures(1, &self->framebuffer);
 | 
				
			||||||
 | 
					    glDeleteVertexArrays(1, &self->axisVAO);
 | 
				
			||||||
 | 
					    glDeleteVertexArrays(1, &self->rectVAO);
 | 
				
			||||||
 | 
					    glDeleteVertexArrays(1, &self->gridVAO);
 | 
				
			||||||
 | 
					    glDeleteVertexArrays(1, &self->textureVAO);
 | 
				
			||||||
 | 
					    glDeleteBuffers(1, &self->axisVBO);
 | 
				
			||||||
 | 
					    glDeleteBuffers(1, &self->rectVBO);
 | 
				
			||||||
 | 
					    glDeleteBuffers(1, &self->gridVBO);
 | 
				
			||||||
 | 
					    glDeleteBuffers(1, &self->textureVBO);
 | 
				
			||||||
 | 
					    glDeleteBuffers(1, &self->textureEBO);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mat4 canvas_mvp_get(mat4& transform, vec2 size, vec2 position, vec2 pivot, f32 rotation, vec2 scale, vec2 pivotAlt, f32 rotationAlt)
 | 
					mat4 canvas_model_get(vec2 size, vec2 position, vec2 pivot, vec2 scale, f32 rotation)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    vec2 scaleAbsolute  = glm::abs(scale);
 | 
					    vec2 scaleAbsolute  = glm::abs(scale);
 | 
				
			||||||
    vec2 scaleSign = glm::sign(scale);
 | 
					    vec2 scaleSign = glm::sign(scale);
 | 
				
			||||||
    f32 usedSign = (scaleSign.x * scaleSign.y) < 0.0f ? -1.0f : 1.0f;
 | 
					    vec2 pivotScaled = pivot * scaleAbsolute;
 | 
				
			||||||
 | 
					    vec2 sizeScaled  = size  * scaleAbsolute;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    vec2 sizeScaled = size * scaleAbsolute;
 | 
					    mat4 model(1.0f);
 | 
				
			||||||
    vec2 pivotScaled  = pivot * scaleAbsolute;
 | 
					    model = glm::translate(model, vec3(position - pivotScaled, 0.0f));
 | 
				
			||||||
    vec2 pivotAltScaled = pivotAlt * scaleAbsolute;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    vec2 pivotAltMirrored = pivotScaled + (pivotAltScaled - pivotScaled) * scaleSign;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    mat4 model = glm::translate(mat4(1.0f), vec3(position - pivotScaled, 0.0f));
 | 
					 | 
				
			||||||
    model = glm::translate(model, vec3(pivotScaled, 0.0f));
 | 
					    model = glm::translate(model, vec3(pivotScaled, 0.0f));
 | 
				
			||||||
    model = glm::scale(model, vec3(scaleSign, 1.0f));
 | 
					    model = glm::scale(model, vec3(scaleSign, 1.0f));
 | 
				
			||||||
    model = glm::rotate(model, glm::radians(rotation) * usedSign, vec3(0,0,1));
 | 
					    model = glm::rotate(model, glm::radians(rotation), vec3(0, 0, 1));
 | 
				
			||||||
    model = glm::translate(model, vec3(-pivotScaled, 0.0f));
 | 
					    model = glm::translate(model, vec3(-pivotScaled, 0.0f));
 | 
				
			||||||
    model = glm::translate(model, vec3(pivotAltMirrored, 0.0f));
 | 
					 | 
				
			||||||
    model = glm::rotate(model, glm::radians(rotationAlt) * usedSign, vec3(0,0,1));
 | 
					 | 
				
			||||||
    model = glm::translate(model, vec3(-pivotAltMirrored, 0.0f));
 | 
					 | 
				
			||||||
    model = glm::scale(model, vec3(sizeScaled, 1.0f));
 | 
					    model = glm::scale(model, vec3(sizeScaled, 1.0f));
 | 
				
			||||||
 | 
					    return model;
 | 
				
			||||||
    return transform * model;
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mat4 canvas_parent_model_get(vec2 position, vec2 pivot, vec2 scale, f32 rotation)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    vec2 scaleSign = glm::sign(scale);
 | 
				
			||||||
 | 
					    vec2 scaleAbsolute  = glm::abs(scale);
 | 
				
			||||||
 | 
					    f32 handedness = (scaleSign.x * scaleSign.y) < 0.0f ? -1.0f : 1.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mat4 local(1.0f);
 | 
				
			||||||
 | 
					    local = glm::translate(local, vec3(pivot, 0.0f));
 | 
				
			||||||
 | 
					    local = glm::scale(local, vec3(scaleSign, 1.0f));
 | 
				
			||||||
 | 
					    local = glm::rotate(local, glm::radians(rotation) * handedness, vec3(0, 0, 1));
 | 
				
			||||||
 | 
					    local = glm::translate(local, vec3(-pivot, 0.0f));
 | 
				
			||||||
 | 
					    local = glm::scale(local, vec3(scaleAbsolute, 1.0f));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return glm::translate(mat4(1.0f), vec3(position, 0.0f)) * local;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										74
									
								
								src/canvas.h
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								src/canvas.h
									
									
									
									
									
								
							@@ -12,10 +12,10 @@
 | 
				
			|||||||
#define CANVAS_GRID_DEFAULT 32
 | 
					#define CANVAS_GRID_DEFAULT 32
 | 
				
			||||||
#define CANVAS_LINE_LENGTH (FLT_MAX * 0.001f)
 | 
					#define CANVAS_LINE_LENGTH (FLT_MAX * 0.001f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const vec2 CANVAS_PIVOT_SIZE = {8, 8};
 | 
					const inline vec2 CANVAS_PIVOT_SIZE = {8, 8};
 | 
				
			||||||
static const vec2 CANVAS_SCALE_DEFAULT = {1.0f, 1.0f};
 | 
					const inline vec2 CANVAS_SCALE_DEFAULT = {1.0f, 1.0f};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const f32 CANVAS_AXIS_VERTICES[] = 
 | 
					const inline f32 CANVAS_AXIS_VERTICES[] = 
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    -CANVAS_LINE_LENGTH, 0.0f,
 | 
					    -CANVAS_LINE_LENGTH, 0.0f,
 | 
				
			||||||
    CANVAS_LINE_LENGTH, 0.0f,
 | 
					    CANVAS_LINE_LENGTH, 0.0f,
 | 
				
			||||||
@@ -23,13 +23,29 @@ const f32 CANVAS_AXIS_VERTICES[] =
 | 
				
			|||||||
    0.0f, CANVAS_LINE_LENGTH
 | 
					    0.0f, CANVAS_LINE_LENGTH
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const f32 CANVAS_GRID_VERTICES[] =
 | 
					const inline f32 CANVAS_GRID_VERTICES[] =
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   -1.0f, -1.0f,
 | 
					   -1.0f, -1.0f,
 | 
				
			||||||
    3.0f, -1.0f,
 | 
					    3.0f, -1.0f,
 | 
				
			||||||
   -1.0f,  3.0f
 | 
					   -1.0f,  3.0f
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const inline f32 CANVAS_RECT_VERTICES[] =
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    0, 0,  
 | 
				
			||||||
 | 
					    1, 0,  
 | 
				
			||||||
 | 
					    1, 1,  
 | 
				
			||||||
 | 
					    0, 1  
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const inline f32 CANVAS_TEXTURE_VERTICES[] = 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    0, 0, 0.0f, 0.0f,
 | 
				
			||||||
 | 
					    1, 0, 1.0f, 0.0f,
 | 
				
			||||||
 | 
					    1, 1, 1.0f, 1.0f,
 | 
				
			||||||
 | 
					    0, 1, 0.0f, 1.0f
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Canvas
 | 
					struct Canvas
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GLuint fbo{};
 | 
					    GLuint fbo{};
 | 
				
			||||||
@@ -40,38 +56,36 @@ struct Canvas
 | 
				
			|||||||
    GLuint rectVBO{};
 | 
					    GLuint rectVBO{};
 | 
				
			||||||
    GLuint gridVAO{};
 | 
					    GLuint gridVAO{};
 | 
				
			||||||
    GLuint gridVBO{};
 | 
					    GLuint gridVBO{};
 | 
				
			||||||
    GLuint texture{};
 | 
					    GLuint framebuffer{};
 | 
				
			||||||
    GLuint textureEBO{};
 | 
					 | 
				
			||||||
    GLuint textureVAO{};
 | 
					    GLuint textureVAO{};
 | 
				
			||||||
    GLuint textureVBO{};
 | 
					    GLuint textureVBO{};
 | 
				
			||||||
 | 
					    GLuint textureEBO{};
 | 
				
			||||||
    ivec2 size{};
 | 
					    ivec2 size{};
 | 
				
			||||||
    ivec2 previousSize{};
 | 
					    ivec2 previousSize{};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void canvas_init(Canvas* self, const ivec2& size);
 | 
					#define UV_VERTICES(uvMin, uvMax) \
 | 
				
			||||||
mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin);
 | 
					{ \
 | 
				
			||||||
void canvas_clear(vec4& color);
 | 
					  0, 0, uvMin.x, uvMin.y, \
 | 
				
			||||||
void canvas_bind(Canvas* self);
 | 
					  1, 0, uvMax.x, uvMin.y, \
 | 
				
			||||||
void canvas_viewport_set(Canvas* self);
 | 
					  1, 1, uvMax.x, uvMax.y, \
 | 
				
			||||||
void canvas_unbind(void);
 | 
					  0, 1, uvMin.x, uvMax.y  \
 | 
				
			||||||
void canvas_texture_set(Canvas* self);
 | 
					}
 | 
				
			||||||
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_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color);
 | 
					 | 
				
			||||||
void canvas_free(Canvas* self);
 | 
					 | 
				
			||||||
void canvas_draw(Canvas* self);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
mat4 canvas_mvp_get
 | 
					mat4 canvas_transform_get(Canvas* self, vec2 pan, f32 zoom, OriginType origin);
 | 
				
			||||||
(
 | 
					void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color);
 | 
				
			||||||
    mat4& transform, 
 | 
					void canvas_bind(Canvas* self);
 | 
				
			||||||
    vec2 size,
 | 
					void canvas_clear(vec4& color);
 | 
				
			||||||
    vec2 position = {0.0f, 0.0f}, 
 | 
					void canvas_draw(Canvas* self);
 | 
				
			||||||
    vec2 pivot = {0.0f, 0.0f}, 
 | 
					void canvas_free(Canvas* self);
 | 
				
			||||||
    f32 rotation = {0.0f},
 | 
					void canvas_grid_draw(Canvas* self, GLuint& shader, mat4& transform, ivec2& size, ivec2& offset, vec4& color);
 | 
				
			||||||
    vec2 scale = {1.0f, 1.0f},
 | 
					void canvas_init(Canvas* self, const ivec2& size);
 | 
				
			||||||
    vec2 pivotAlt = {0.0f, 0.0f},
 | 
					void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform, const vec4& color);
 | 
				
			||||||
    f32 rotationAlt = {0.0f}
 | 
					void canvas_framebuffer_resize_check(Canvas* self);
 | 
				
			||||||
);
 | 
					void canvas_unbind(void);
 | 
				
			||||||
 | 
					void canvas_viewport_set(Canvas* self);
 | 
				
			||||||
 | 
					mat4 canvas_model_get(vec2 size = {}, vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {});
 | 
				
			||||||
 | 
					mat4 canvas_parent_model_get(vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void canvas_texture_draw
 | 
					void canvas_texture_draw
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
@@ -79,7 +93,7 @@ void canvas_texture_draw
 | 
				
			|||||||
    GLuint& shader, 
 | 
					    GLuint& shader, 
 | 
				
			||||||
    GLuint& texture, 
 | 
					    GLuint& texture, 
 | 
				
			||||||
    mat4& transform, 
 | 
					    mat4& transform, 
 | 
				
			||||||
    const f32* vertices = GL_UV_VERTICES,
 | 
					    const f32* vertices = CANVAS_TEXTURE_VERTICES, 
 | 
				
			||||||
    vec4 tint = COLOR_OPAQUE, 
 | 
					    vec4 tint = COLOR_OPAQUE, 
 | 
				
			||||||
    vec3 colorOffset = COLOR_OFFSET_NONE
 | 
					    vec3 colorOffset = COLOR_OFFSET_NONE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -20,7 +20,7 @@ void editor_draw(Editor* self)
 | 
				
			|||||||
    GLuint& shaderGrid = self->resources->shaders[SHADER_GRID];
 | 
					    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_framebuffer_resize_check(&self->canvas);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    canvas_bind(&self->canvas);
 | 
					    canvas_bind(&self->canvas);
 | 
				
			||||||
    canvas_viewport_set(&self->canvas);
 | 
					    canvas_viewport_set(&self->canvas);
 | 
				
			||||||
@@ -28,23 +28,24 @@ void editor_draw(Editor* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (self->spritesheetID != ID_NONE)
 | 
					    if (self->spritesheetID != ID_NONE)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Texture texture = self->resources->textures[self->spritesheetID];
 | 
					        Texture& texture = self->anm2->spritesheets[self->spritesheetID].texture;
 | 
				
			||||||
        mat4 mvp = canvas_mvp_get(transform, texture.size);
 | 
					        
 | 
				
			||||||
        canvas_texture_draw(&self->canvas, shaderTexture, texture.id, mvp);
 | 
					        mat4 spritesheetTransform = transform * canvas_model_get(texture.size);
 | 
				
			||||||
 | 
					        canvas_texture_draw(&self->canvas, shaderTexture, texture.id, spritesheetTransform);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (self->settings->editorIsBorder)
 | 
					        if (self->settings->editorIsBorder)
 | 
				
			||||||
            canvas_rect_draw(&self->canvas, shaderLine, mvp, EDITOR_BORDER_COLOR);
 | 
					            canvas_rect_draw(&self->canvas, shaderLine, spritesheetTransform, EDITOR_BORDER_COLOR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference);
 | 
					        Anm2Frame* frame = (Anm2Frame*)anm2_frame_from_reference(self->anm2, self->reference);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
        if (frame)
 | 
					        if (frame)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            mvp = canvas_mvp_get(transform, frame->size, frame->crop);
 | 
					            mat4 cropTransform = transform * canvas_model_get(frame->size, frame->crop);
 | 
				
			||||||
            canvas_rect_draw(&self->canvas, shaderLine, mvp, EDITOR_FRAME_COLOR);
 | 
					            canvas_rect_draw(&self->canvas, shaderLine, cropTransform, EDITOR_FRAME_COLOR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            mvp = canvas_mvp_get(transform, CANVAS_PIVOT_SIZE, frame->crop + frame->pivot, CANVAS_PIVOT_SIZE * 0.5f);
 | 
					            mat4 pivotTransform = transform * canvas_model_get(CANVAS_PIVOT_SIZE, frame->crop + frame->pivot, CANVAS_PIVOT_SIZE * 0.5f);
 | 
				
			||||||
            f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
 | 
					            f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
 | 
				
			||||||
            canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, mvp, vertices, EDITOR_PIVOT_COLOR);
 | 
					            canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, EDITOR_PIVOT_COLOR);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,10 @@
 | 
				
			|||||||
#include "ffmpeg.h"
 | 
					#include "ffmpeg.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static std::string ffmpeg_log_path_get(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return preferences_path_get() + FFMPEG_LOG_PATH;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool 
 | 
					bool 
 | 
				
			||||||
ffmpeg_render
 | 
					ffmpeg_render
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
@@ -31,7 +36,7 @@ ffmpeg_render
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ffmpeg output will be piped into the log
 | 
					    // ffmpeg output will be piped into the log
 | 
				
			||||||
    std::string logOutput = " 2>> \"" + log_path_get() + "\"";
 | 
					    std::string logOutput = " 2>> \"" + ffmpeg_log_path_get() + "\"";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if _WIN32
 | 
					#if _WIN32
 | 
				
			||||||
    command = string_quote(command) + logOutput;
 | 
					    command = string_quote(command) + logOutput;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#define FFMPEG_POPEN_ERROR "popen() (for FFmpeg) failed!\n{}"
 | 
					#define FFMPEG_POPEN_ERROR "popen() (for FFmpeg) failed!\n{}"
 | 
				
			||||||
#define FFMPEG_LOG_BUFFER_SIZE 256
 | 
					#define FFMPEG_LOG_BUFFER_SIZE 256
 | 
				
			||||||
 | 
					#define FFMPEG_LOG_PATH "ffmpeg.txt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static constexpr const char* FFMPEG_GIF_FORMAT =
 | 
					static constexpr const char* FFMPEG_GIF_FORMAT =
 | 
				
			||||||
"\"{0}\" -y "
 | 
					"\"{0}\" -y "
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,23 +26,21 @@ void generate_preview_draw(GeneratePreview* self)
 | 
				
			|||||||
    canvas_clear(self->settings->previewBackgroundColor);
 | 
					    canvas_clear(self->settings->previewBackgroundColor);
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
    Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference);
 | 
					    Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference);
 | 
				
			||||||
    Texture* texture = map_find(self->resources->textures, self->anm2->layers[self->reference->itemID].spritesheetID);
 | 
					    Texture& texture = self->anm2->spritesheets[self->anm2->layers[self->reference->itemID].spritesheetID].texture;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    if (item && texture && !texture->isInvalid)
 | 
					    if (item && !texture.isInvalid)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        const s32 index = std::clamp((s32)(self->time * count), 0, count);
 | 
					        const s32 index = std::clamp((s32)(self->time * count), 0, count);
 | 
				
			||||||
        const s32 row = index / columns;
 | 
					        const s32 row = index / columns;
 | 
				
			||||||
        const s32 column = index % columns;
 | 
					        const s32 column = index % columns;
 | 
				
			||||||
        vec2 crop = startPosition + vec2(size.x * column, size.y * row);
 | 
					        vec2 crop = startPosition + vec2(size.x * column, size.y * row);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        vec2 uvMin = crop / vec2(texture->size);
 | 
					        vec2 uvMin = crop / vec2(texture.size);
 | 
				
			||||||
        vec2 uvMax = (crop + size) / vec2(texture->size);
 | 
					        vec2 uvMax = (crop + size) / vec2(texture.size);
 | 
				
			||||||
        f32 vertices[] = UV_VERTICES(uvMin, uvMax);
 | 
					        f32 vertices[] = UV_VERTICES(uvMin, uvMax);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mat4 model = quad_model_get(size, {}, pivot, {}, CANVAS_SCALE_DEFAULT);
 | 
					        mat4 generateTransform = transform * canvas_model_get(size, {}, pivot);
 | 
				
			||||||
        mat4 generateTransform = transform * model;
 | 
					        canvas_texture_draw(&self->canvas, shaderTexture, texture.id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        canvas_texture_draw(&self->canvas, shaderTexture, texture->id, generateTransform, vertices, COLOR_OPAQUE, COLOR_OFFSET_NONE);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    canvas_unbind();
 | 
					    canvas_unbind();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										120
									
								
								src/imgui.cpp
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								src/imgui.cpp
									
									
									
									
									
								
							@@ -19,11 +19,11 @@ 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)
 | 
					static void _imgui_anm2_open(Imgui* self, const std::string& path)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	*self->reference = Anm2Reference{};
 | 
						imgui_file_new(self);
 | 
				
			||||||
	resources_textures_free(self->resources);
 | 
					
 | 
				
			||||||
	if (anm2_deserialize(self->anm2, self->resources, path))
 | 
						if (anm2_deserialize(self->anm2, path))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		window_title_from_path_set(self->window, path);
 | 
							window_title_from_path_set(self->window, path);
 | 
				
			||||||
		snapshots_reset(self->snapshots);
 | 
							snapshots_reset(self->snapshots);
 | 
				
			||||||
@@ -36,13 +36,18 @@ static void _imgui_anm2_new(Imgui* self, const std::string& path)
 | 
				
			|||||||
static void _imgui_spritesheet_add(Imgui* self, const std::string& path)
 | 
					static void _imgui_spritesheet_add(Imgui* self, const std::string& path)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	std::filesystem::path workingPath = std::filesystem::current_path();
 | 
						std::filesystem::path workingPath = std::filesystem::current_path();
 | 
				
			||||||
	std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path);
 | 
						std::string spritesheetPath = path;
 | 
				
			||||||
	std::string spritesheetPath = std::filesystem::relative(path, anm2WorkingPath).string();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s32 id = map_next_id_get(self->resources->textures);
 | 
						if (!self->anm2->path.empty())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path);
 | 
				
			||||||
 | 
							spritesheetPath = std::filesystem::relative(path, anm2WorkingPath).string();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s32 id = map_next_id_get(self->anm2->spritesheets);
 | 
				
			||||||
	self->anm2->spritesheets[id] = Anm2Spritesheet{};
 | 
						self->anm2->spritesheets[id] = Anm2Spritesheet{};
 | 
				
			||||||
	self->anm2->spritesheets[id].path = spritesheetPath;
 | 
						self->anm2->spritesheets[id].path = spritesheetPath;
 | 
				
			||||||
	resources_texture_init(self->resources, spritesheetPath, id);
 | 
						texture_from_path_init(&self->anm2->spritesheets[id].texture, spritesheetPath);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	std::filesystem::current_path(workingPath);
 | 
						std::filesystem::current_path(workingPath);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -202,7 +207,7 @@ static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType
 | 
				
			|||||||
	if (isActivated)
 | 
						if (isActivated)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (self.is_undoable()) 
 | 
							if (self.is_undoable()) 
 | 
				
			||||||
			imgui_undo_push(imgui, self.undoAction);
 | 
								imgui_snapshot(imgui, self.snapshotAction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (self.function) self.function(imgui);
 | 
							if (self.function) self.function(imgui);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -666,7 +671,7 @@ static void _imgui_timeline(Imgui* self)
 | 
				
			|||||||
		if (ImGui::IsMouseDown(0) && _imgui_is_window_hovered()) 
 | 
							if (ImGui::IsMouseDown(0) && _imgui_is_window_hovered()) 
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			if (!isPlayheadDrag)
 | 
								if (!isPlayheadDrag)
 | 
				
			||||||
				imgui_undo_push(self, IMGUI_ACTION_MOVE_PLAYHEAD);
 | 
									imgui_snapshot(self, IMGUI_ACTION_MOVE_PLAYHEAD);
 | 
				
			||||||
			isPlayheadDrag = true;
 | 
								isPlayheadDrag = true;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1018,10 +1023,10 @@ static void _imgui_timeline(Imgui* self)
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (type == ANM2_TRIGGERS)
 | 
									if (type == ANM2_TRIGGERS)
 | 
				
			||||||
					imgui_undo_push(self, IMGUI_ACTION_TRIGGER_MOVE);
 | 
										imgui_snapshot(self, IMGUI_ACTION_TRIGGER_MOVE);
 | 
				
			||||||
				else if (isModCtrl)
 | 
									else if (isModCtrl)
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					imgui_undo_push(self, IMGUI_ACTION_FRAME_DELAY);
 | 
										imgui_snapshot(self, IMGUI_ACTION_FRAME_DELAY);
 | 
				
			||||||
					frameDelayStart = draggingFrame->delay;
 | 
										frameDelayStart = draggingFrame->delay;
 | 
				
			||||||
					frameDelayTimeStart = frameTime;
 | 
										frameDelayTimeStart = frameTime;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -1067,7 +1072,7 @@ static void _imgui_timeline(Imgui* self)
 | 
				
			|||||||
						Anm2Reference swapReference = *(Anm2Reference*)payload->Data;
 | 
											Anm2Reference swapReference = *(Anm2Reference*)payload->Data;
 | 
				
			||||||
						if (swapReference != reference)
 | 
											if (swapReference != reference)
 | 
				
			||||||
						{
 | 
											{
 | 
				
			||||||
							imgui_undo_push(self, IMGUI_ACTION_FRAME_SWAP);
 | 
												imgui_snapshot(self, IMGUI_ACTION_FRAME_SWAP);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							Anm2Frame* swapFrame = anm2_frame_from_reference(self->anm2, &reference);
 | 
												Anm2Frame* swapFrame = anm2_frame_from_reference(self->anm2, &reference);
 | 
				
			||||||
							Anm2Frame* dragFrame = anm2_frame_from_reference(self->anm2, &swapReference);
 | 
												Anm2Frame* dragFrame = anm2_frame_from_reference(self->anm2, &swapReference);
 | 
				
			||||||
@@ -1274,7 +1279,7 @@ static void _imgui_taskbar(Imgui* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (self->dialog->isSelected && self->dialog->type == DIALOG_ANM2_OPEN)
 | 
						if (self->dialog->isSelected && self->dialog->type == DIALOG_ANM2_OPEN)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		_imgui_anm2_new(self, self->dialog->path);
 | 
							_imgui_anm2_open(self, self->dialog->path);
 | 
				
			||||||
		dialog_reset(self->dialog);
 | 
							dialog_reset(self->dialog);
 | 
				
			||||||
	}			
 | 
						}			
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1336,7 +1341,7 @@ static void _imgui_taskbar(Imgui* self)
 | 
				
			|||||||
		_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);
 | 
							generate_preview_draw(self->generatePreview);
 | 
				
			||||||
		ImGui::Image(self->generatePreview->canvas.texture, GENERATE_PREVIEW_SIZE);
 | 
							ImGui::Image(self->generatePreview->canvas.framebuffer, GENERATE_PREVIEW_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_imgui_begin_child(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER_CHILD, self);
 | 
							_imgui_begin_child(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER_CHILD, self);
 | 
				
			||||||
		_imgui_slider_float(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER, self, time);
 | 
							_imgui_slider_float(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER, self, time);
 | 
				
			||||||
@@ -1730,7 +1735,7 @@ static void _imgui_animations(Imgui* self)
 | 
				
			|||||||
				s32 sourceID = *(s32*)payload->Data;
 | 
									s32 sourceID = *(s32*)payload->Data;
 | 
				
			||||||
				if (sourceID != id)
 | 
									if (sourceID != id)
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					imgui_undo_push(self, IMGUI_ACTION_ANIMATION_SWAP);
 | 
										imgui_snapshot(self, IMGUI_ACTION_ANIMATION_SWAP);
 | 
				
			||||||
					map_swap(self->anm2->animations, sourceID, id);
 | 
										map_swap(self->anm2->animations, sourceID, id);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -1963,7 +1968,7 @@ static void _imgui_spritesheets(Imgui* self)
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		ImGui::PushID(id);
 | 
							ImGui::PushID(id);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		Texture* texture = &self->resources->textures[id];
 | 
							Texture& texture = spritesheet.texture;
 | 
				
			||||||
		bool isContains = selectedIDs.contains(id);
 | 
							bool isContains = selectedIDs.contains(id);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		_imgui_begin_child(IMGUI_SPRITESHEET_CHILD, self);
 | 
							_imgui_begin_child(IMGUI_SPRITESHEET_CHILD, self);
 | 
				
			||||||
@@ -1992,26 +1997,23 @@ static void _imgui_spritesheets(Imgui* self)
 | 
				
			|||||||
			{
 | 
								{
 | 
				
			||||||
				s32 sourceID = *(s32*)payload->Data;
 | 
									s32 sourceID = *(s32*)payload->Data;
 | 
				
			||||||
				if (sourceID != id)
 | 
									if (sourceID != id)
 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					map_swap(self->anm2->spritesheets, sourceID, id);
 | 
										map_swap(self->anm2->spritesheets, sourceID, id);
 | 
				
			||||||
					map_swap(self->resources->textures, sourceID, id);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ImGui::EndDragDropTarget();
 | 
								ImGui::EndDragDropTarget();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ImVec2 spritesheetPreviewSize = IMGUI_SPRITESHEET_PREVIEW_SIZE;
 | 
							ImVec2 spritesheetPreviewSize = IMGUI_SPRITESHEET_PREVIEW_SIZE;
 | 
				
			||||||
		f32 spritesheetAspect = (f32)self->resources->textures[id].size.x / self->resources->textures[id].size.y;
 | 
							f32 spritesheetAspect = (f32)texture.size.x / texture.size.y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ((IMGUI_SPRITESHEET_PREVIEW_SIZE.x / IMGUI_SPRITESHEET_PREVIEW_SIZE.y) > spritesheetAspect)
 | 
							if ((IMGUI_SPRITESHEET_PREVIEW_SIZE.x / IMGUI_SPRITESHEET_PREVIEW_SIZE.y) > spritesheetAspect)
 | 
				
			||||||
			spritesheetPreviewSize.x = IMGUI_SPRITESHEET_PREVIEW_SIZE.y * spritesheetAspect;
 | 
								spritesheetPreviewSize.x = IMGUI_SPRITESHEET_PREVIEW_SIZE.y * spritesheetAspect;
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			spritesheetPreviewSize.y = IMGUI_SPRITESHEET_PREVIEW_SIZE.x / spritesheetAspect;
 | 
								spritesheetPreviewSize.y = IMGUI_SPRITESHEET_PREVIEW_SIZE.x / spritesheetAspect;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (texture->isInvalid)
 | 
							if (texture.isInvalid)
 | 
				
			||||||
			_imgui_atlas(ATLAS_NONE, self);
 | 
								_imgui_atlas(ATLAS_NONE, self);
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			ImGui::Image(texture->id, spritesheetPreviewSize);
 | 
								ImGui::Image(texture.id, spritesheetPreviewSize);
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
		_imgui_end_child(); // IMGUI_SPRITESHEET_CHILD
 | 
							_imgui_end_child(); // IMGUI_SPRITESHEET_CHILD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2036,11 +2038,16 @@ static void _imgui_spritesheets(Imgui* self)
 | 
				
			|||||||
	
 | 
						
 | 
				
			||||||
	if (_imgui_button(IMGUI_SPRITESHEETS_RELOAD.copy({selectedIDs.empty()}), self))
 | 
						if (_imgui_button(IMGUI_SPRITESHEETS_RELOAD.copy({selectedIDs.empty()}), self))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							if (selectedIDs.size() > 0) 
 | 
				
			||||||
 | 
								imgui_snapshot(self, IMGUI_ACTION_RELOAD_SPRITESHEET);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (auto& id : selectedIDs)
 | 
							for (auto& id : selectedIDs)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			std::filesystem::path workingPath = std::filesystem::current_path();
 | 
								std::filesystem::path workingPath = std::filesystem::current_path();
 | 
				
			||||||
			working_directory_from_file_set(self->anm2->path);
 | 
								working_directory_from_file_set(self->anm2->path);
 | 
				
			||||||
			resources_texture_init(self->resources, self->anm2->spritesheets[id].path, id);
 | 
								Texture texture;
 | 
				
			||||||
 | 
								texture_from_path_init(&texture, self->anm2->spritesheets[id].path);
 | 
				
			||||||
 | 
								self->anm2->spritesheets[id].texture = texture;
 | 
				
			||||||
			std::filesystem::current_path(workingPath);
 | 
								std::filesystem::current_path(workingPath);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -2050,14 +2057,17 @@ static void _imgui_spritesheets(Imgui* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (self->dialog->isSelected && self->dialog->type == DIALOG_SPRITESHEET_REPLACE)
 | 
						if (self->dialog->isSelected && self->dialog->type == DIALOG_SPRITESHEET_REPLACE)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							imgui_snapshot(self, IMGUI_ACTION_REPLACE_SPRITESHEET);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
		std::filesystem::path workingPath = std::filesystem::current_path();
 | 
							std::filesystem::path workingPath = std::filesystem::current_path();
 | 
				
			||||||
		std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path);
 | 
							std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path);
 | 
				
			||||||
		std::string spritesheetPath = std::filesystem::relative(self->dialog->path, anm2WorkingPath).string();
 | 
							std::string spritesheetPath = std::filesystem::relative(self->dialog->path, anm2WorkingPath).string();
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
		self->anm2->spritesheets[self->dialog->replaceID].path = spritesheetPath;
 | 
							self->anm2->spritesheets[self->dialog->replaceID].path = spritesheetPath;
 | 
				
			||||||
		resources_texture_init(self->resources, spritesheetPath, self->dialog->replaceID);
 | 
							Texture texture;
 | 
				
			||||||
 | 
							texture_from_path_init(&texture, spritesheetPath);
 | 
				
			||||||
 | 
							self->anm2->spritesheets[self->dialog->replaceID].texture = texture; 
 | 
				
			||||||
		dialog_reset(self->dialog);
 | 
							dialog_reset(self->dialog);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		std::filesystem::current_path(workingPath);
 | 
							std::filesystem::current_path(workingPath);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2073,8 +2083,8 @@ static void _imgui_spritesheets(Imgui* self)
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			if (!usedSpritesheetIDs.count(it->first))
 | 
								if (!usedSpritesheetIDs.count(it->first))
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
 | 
									texture_free(&self->anm2->spritesheets[it->first].texture);
 | 
				
			||||||
				it = self->anm2->spritesheets.erase(it);
 | 
									it = self->anm2->spritesheets.erase(it);
 | 
				
			||||||
				texture_free(&self->resources->textures[it->first]);
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
				it++;
 | 
									it++;
 | 
				
			||||||
@@ -2092,12 +2102,11 @@ static void _imgui_spritesheets(Imgui* self)
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		for (auto& id : selectedIDs)
 | 
							for (auto& id : selectedIDs)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Anm2Spritesheet* spritesheet = &self->anm2->spritesheets[id];
 | 
								Anm2Spritesheet& spritesheet = self->anm2->spritesheets[id];
 | 
				
			||||||
			Texture* texture = &self->resources->textures[id];
 | 
					 | 
				
			||||||
			std::filesystem::path workingPath = std::filesystem::current_path();
 | 
								std::filesystem::path workingPath = std::filesystem::current_path();
 | 
				
			||||||
			working_directory_from_file_set(self->anm2->path);
 | 
								working_directory_from_file_set(self->anm2->path);
 | 
				
			||||||
			texture_from_gl_write(texture, spritesheet->path);
 | 
								texture_from_gl_write(&spritesheet.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);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -2116,7 +2125,6 @@ static void _imgui_animation_preview(Imgui* self)
 | 
				
			|||||||
	static ivec2& size = self->preview->canvas.size;
 | 
						static ivec2& size = self->preview->canvas.size;
 | 
				
			||||||
	static vec2 mousePos{};
 | 
						static vec2 mousePos{};
 | 
				
			||||||
	static vec2 previewPos{};
 | 
						static vec2 previewPos{};
 | 
				
			||||||
	static ImVec2 previewScreenPos{};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::string mousePositionString = std::format(IMGUI_POSITION_FORMAT, (s32)mousePos.x, (s32)mousePos.y);
 | 
						std::string mousePositionString = std::format(IMGUI_POSITION_FORMAT, (s32)mousePos.x, (s32)mousePos.y);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
@@ -2182,13 +2190,10 @@ static void _imgui_animation_preview(Imgui* self)
 | 
				
			|||||||
	_imgui_checkbox(IMGUI_CANVAS_BORDER, self, self->settings->previewIsBorder);
 | 
						_imgui_checkbox(IMGUI_CANVAS_BORDER, self, self->settings->previewIsBorder);
 | 
				
			||||||
	_imgui_end_child(); // IMGUI_CANVAS_HELPER_CHILD
 | 
						_imgui_end_child(); // IMGUI_CANVAS_HELPER_CHILD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	previewPos = vec2(ImGui::GetCursorPos());
 | 
						ImVec2 previewCursorScreenPos = ImGui::GetCursorScreenPos();
 | 
				
			||||||
	previewScreenPos = vec2(ImGui::GetCursorScreenPos());
 | 
						size = ivec2(vec2(ImGui::GetContentRegionAvail()));
 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	vec2 imageSize = ImGui::GetContentRegionAvail();
 | 
					 | 
				
			||||||
	size = ivec2(imageSize);
 | 
					 | 
				
			||||||
	preview_draw(self->preview);
 | 
						preview_draw(self->preview);
 | 
				
			||||||
	ImGui::Image(self->preview->canvas.texture, imageSize);
 | 
						ImGui::Image(self->preview->canvas.framebuffer, vec2(size));
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	if (self->settings->previewIsTriggers)
 | 
						if (self->settings->previewIsTriggers)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -2198,7 +2203,7 @@ static void _imgui_animation_preview(Imgui* self)
 | 
				
			|||||||
		if (trigger.eventID != ID_NONE)
 | 
							if (trigger.eventID != ID_NONE)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			f32 textScale = ImGui::GetCurrentWindow()->FontWindowScale;
 | 
								f32 textScale = ImGui::GetCurrentWindow()->FontWindowScale;
 | 
				
			||||||
			ImVec2 textPos = previewScreenPos + ImGui::GetStyle().ItemSpacing;
 | 
								ImVec2 textPos = previewCursorScreenPos + ImGui::GetStyle().ItemSpacing;
 | 
				
			||||||
			ImGui::SetWindowFontScale(IMGUI_TRIGGERS_FONT_SCALE);
 | 
								ImGui::SetWindowFontScale(IMGUI_TRIGGERS_FONT_SCALE);
 | 
				
			||||||
			ImGui::GetWindowDrawList()->AddText(textPos, IMGUI_TRIGGERS_EVENT_COLOR, self->anm2->events[trigger.eventID].name.c_str());
 | 
								ImGui::GetWindowDrawList()->AddText(textPos, IMGUI_TRIGGERS_EVENT_COLOR, self->anm2->events[trigger.eventID].name.c_str());
 | 
				
			||||||
			ImGui::SetWindowFontScale(textScale);
 | 
								ImGui::SetWindowFontScale(textScale);
 | 
				
			||||||
@@ -2215,7 +2220,7 @@ static void _imgui_animation_preview(Imgui* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	_imgui_end(); // IMGUI_ANIMATION_PREVIEW
 | 
						_imgui_end(); // IMGUI_ANIMATION_PREVIEW
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mousePos = (vec2((ImGui::GetMousePos()) - (ImGui::GetWindowPos() + previewPos)) - (imageSize * 0.5f) - pan) / PERCENT_TO_UNIT(zoom);
 | 
						mousePos = vec2(ImGui::GetMousePos() - previewCursorScreenPos - pan - (vec2(size) * 0.5f)) / PERCENT_TO_UNIT(zoom);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	const bool isLeft = ImGui::IsKeyPressed(IMGUI_INPUT_LEFT);
 | 
						const bool isLeft = ImGui::IsKeyPressed(IMGUI_INPUT_LEFT);
 | 
				
			||||||
	const bool isRight = ImGui::IsKeyPressed(IMGUI_INPUT_RIGHT);
 | 
						const bool isRight = ImGui::IsKeyPressed(IMGUI_INPUT_RIGHT);
 | 
				
			||||||
@@ -2232,7 +2237,7 @@ static void _imgui_animation_preview(Imgui* self)
 | 
				
			|||||||
	
 | 
						
 | 
				
			||||||
	if (tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE)
 | 
						if (tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE)
 | 
				
			||||||
		if (isMouseClick || isLeft || isRight || isUp || isDown)
 | 
							if (isMouseClick || isLeft || isRight || isUp || isDown)
 | 
				
			||||||
			imgui_undo_push(self, IMGUI_ACTION_FRAME_TRANSFORM);
 | 
								imgui_snapshot(self, IMGUI_ACTION_FRAME_TRANSFORM);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	if ((tool == TOOL_PAN && isMouseDown) || isMouseMiddleDown)
 | 
						if ((tool == TOOL_PAN && isMouseDown) || isMouseMiddleDown)
 | 
				
			||||||
		pan += vec2(mouseDelta.x, mouseDelta.y);
 | 
							pan += vec2(mouseDelta.x, mouseDelta.y);
 | 
				
			||||||
@@ -2332,11 +2337,10 @@ static void _imgui_spritesheet_editor(Imgui* self)
 | 
				
			|||||||
	_imgui_checkbox(IMGUI_CANVAS_BORDER, self, self->settings->editorIsBorder);
 | 
						_imgui_checkbox(IMGUI_CANVAS_BORDER, self, self->settings->editorIsBorder);
 | 
				
			||||||
	_imgui_end_child(); // IMGUI_CANVAS_VISUAL_CHILD
 | 
						_imgui_end_child(); // IMGUI_CANVAS_VISUAL_CHILD
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	ImVec2 editorPos = ImGui::GetCursorPos();
 | 
						ImVec2 editorCursorScreenPos = ImGui::GetCursorScreenPos();
 | 
				
			||||||
	vec2 imageSize = ImGui::GetContentRegionAvail();
 | 
						size = ivec2(vec2(ImGui::GetContentRegionAvail()));
 | 
				
			||||||
	size = ivec2(imageSize);
 | 
					 | 
				
			||||||
	editor_draw(self->editor);
 | 
						editor_draw(self->editor);
 | 
				
			||||||
	ImGui::Image(self->editor->canvas.texture, imageSize);
 | 
						ImGui::Image(self->editor->canvas.framebuffer, vec2(size));
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	if (ImGui::IsItemHovered()) 
 | 
						if (ImGui::IsItemHovered()) 
 | 
				
			||||||
		self->pendingCursor = TOOL_CURSORS[tool];
 | 
							self->pendingCursor = TOOL_CURSORS[tool];
 | 
				
			||||||
@@ -2348,7 +2352,7 @@ static void _imgui_spritesheet_editor(Imgui* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	_imgui_end(); // IMGUI_SPRITESHEET_EDITOR
 | 
						_imgui_end(); // IMGUI_SPRITESHEET_EDITOR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mousePos = (vec2((ImGui::GetMousePos()) - (ImGui::GetWindowPos() + editorPos)) - pan) / PERCENT_TO_UNIT(zoom);
 | 
						mousePos = vec2(ImGui::GetMousePos() - editorCursorScreenPos - pan) / PERCENT_TO_UNIT(zoom);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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);
 | 
				
			||||||
@@ -2366,7 +2370,8 @@ static void _imgui_spritesheet_editor(Imgui* self)
 | 
				
			|||||||
	if (self->reference->itemType == ANM2_LAYER) 
 | 
						if (self->reference->itemType == ANM2_LAYER) 
 | 
				
			||||||
		frame = anm2_frame_from_reference(self->anm2, self->reference);
 | 
							frame = anm2_frame_from_reference(self->anm2, self->reference);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Texture* texture = map_find(self->resources->textures, self->editor->spritesheetID);
 | 
						Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->editor->spritesheetID);
 | 
				
			||||||
 | 
						Texture* texture = spritesheet ? &spritesheet->texture : nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vec2 position = mousePos;
 | 
						vec2 position = mousePos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2377,16 +2382,13 @@ static void _imgui_spritesheet_editor(Imgui* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			if (self->settings->editorIsGridSnap)
 | 
								if (self->settings->editorIsGridSnap)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				position = 
 | 
									position.x = roundf(position.x / gridSize.x) * gridSize.x + gridOffset.x - (gridSize.x * 0.5f);
 | 
				
			||||||
				{
 | 
									position.y = roundf(position.y / gridSize.y) * gridSize.y + gridOffset.y - (gridSize.y * 0.5f);
 | 
				
			||||||
					(s32)((position.x - gridOffset.x) / gridSize.x) * gridSize.x + gridOffset.x,
 | 
					 | 
				
			||||||
					(s32)((position.y - gridOffset.y) / gridSize.y) * gridSize.y + gridOffset.y
 | 
					 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			if (isMouseClick)
 | 
								if (isMouseClick)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				imgui_undo_push(self, IMGUI_ACTION_FRAME_CROP);
 | 
									imgui_snapshot(self, IMGUI_ACTION_FRAME_CROP);
 | 
				
			||||||
				frame->crop = position;
 | 
									frame->crop = position;
 | 
				
			||||||
				frame->size = ivec2(0,0);
 | 
									frame->size = ivec2(0,0);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -2396,9 +2398,13 @@ static void _imgui_spritesheet_editor(Imgui* self)
 | 
				
			|||||||
		case TOOL_DRAW:
 | 
							case TOOL_DRAW:
 | 
				
			||||||
		case TOOL_ERASE:
 | 
							case TOOL_ERASE:
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			if (!frame || !texture) break;
 | 
								if (!texture) break;
 | 
				
			||||||
 | 
								
 | 
				
			||||||
			vec4 color = tool == TOOL_ERASE ? COLOR_TRANSPARENT : toolColor;
 | 
								vec4 color = tool == TOOL_ERASE ? COLOR_TRANSPARENT : toolColor;
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
 | 
								if (isMouseClick)
 | 
				
			||||||
 | 
									imgui_snapshot(self, IMGUI_ACTION_DRAW);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (isMouseDown)
 | 
								if (isMouseDown)
 | 
				
			||||||
				texture_pixel_set(texture, position, color);
 | 
									texture_pixel_set(texture, position, color);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
@@ -2606,7 +2612,7 @@ void imgui_update(Imgui* self)
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			if (ImGui::IsKeyChordPressed(hotkey.chord))
 | 
								if (ImGui::IsKeyChordPressed(hotkey.chord))
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				if (hotkey.is_undoable()) imgui_undo_push(self, hotkey.undoAction);
 | 
									if (hotkey.is_undoable()) imgui_snapshot(self, hotkey.snapshotAction);
 | 
				
			||||||
				if (hotkey.is_focus_window()) continue;
 | 
									if (hotkey.is_focus_window()) continue;
 | 
				
			||||||
				hotkey.function(self);
 | 
									hotkey.function(self);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -2634,9 +2640,11 @@ void imgui_update(Imgui* self)
 | 
				
			|||||||
            	const char* droppedFile = event.drop.data;
 | 
					            	const char* droppedFile = event.drop.data;
 | 
				
			||||||
            	
 | 
					            	
 | 
				
			||||||
				if (path_is_extension(droppedFile, ANM2_EXTENSION))  
 | 
									if (path_is_extension(droppedFile, ANM2_EXTENSION))  
 | 
				
			||||||
					_imgui_anm2_new(self, droppedFile);
 | 
										_imgui_anm2_open(self, droppedFile);
 | 
				
			||||||
				else if (path_is_extension(droppedFile, ANM2_SPRITESHEET_EXTENSION))
 | 
									else if (path_is_extension(droppedFile, ANM2_SPRITESHEET_EXTENSION))
 | 
				
			||||||
					_imgui_spritesheet_add(self, droppedFile);
 | 
										_imgui_spritesheet_add(self, droppedFile);
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
										imgui_log_push(self, IMGUI_LOG_DRAG_DROP_ERROR);
 | 
				
			||||||
            	
 | 
					            	
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										136
									
								
								src/imgui.h
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								src/imgui.h
									
									
									
									
									
								
							@@ -68,12 +68,16 @@
 | 
				
			|||||||
#define IMGUI_ACTION_TRIGGER_MOVE "Trigger AtFrame"
 | 
					#define IMGUI_ACTION_TRIGGER_MOVE "Trigger AtFrame"
 | 
				
			||||||
#define IMGUI_ACTION_FRAME_DELAY "Frame Delay"
 | 
					#define IMGUI_ACTION_FRAME_DELAY "Frame Delay"
 | 
				
			||||||
#define IMGUI_ACTION_MOVE_PLAYHEAD "Move Playhead"
 | 
					#define IMGUI_ACTION_MOVE_PLAYHEAD "Move Playhead"
 | 
				
			||||||
 | 
					#define IMGUI_ACTION_DRAW "Draw"
 | 
				
			||||||
 | 
					#define IMGUI_ACTION_RELOAD_SPRITESHEET "Reload Spritesheet(s)"
 | 
				
			||||||
 | 
					#define IMGUI_ACTION_REPLACE_SPRITESHEET "Replace Spritesheet"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define IMGUI_POPUP_FLAGS ImGuiWindowFlags_NoMove
 | 
					#define IMGUI_POPUP_FLAGS ImGuiWindowFlags_NoMove
 | 
				
			||||||
#define IMGUI_POPUP_MODAL_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize
 | 
					#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_SPRITESHEET_RELOAD "Reloaded selected spritesheets"
 | 
				
			||||||
#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."
 | 
				
			||||||
@@ -83,6 +87,7 @@
 | 
				
			|||||||
#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."
 | 
				
			||||||
#define IMGUI_LOG_SPRITESHEET_SAVE_FORMAT "Saved spritesheet #{} to: {}" 
 | 
					#define IMGUI_LOG_SPRITESHEET_SAVE_FORMAT "Saved spritesheet #{} to: {}" 
 | 
				
			||||||
 | 
					#define IMGUI_LOG_DRAG_DROP_ERROR "Invalid file for dragging/dropping!"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define IMGUI_NONE "None"
 | 
					#define IMGUI_NONE "None"
 | 
				
			||||||
#define IMGUI_ANIMATION_DEFAULT_FORMAT "(*) {}"
 | 
					#define IMGUI_ANIMATION_DEFAULT_FORMAT "(*) {}"
 | 
				
			||||||
@@ -206,10 +211,10 @@ struct ImguiHotkey
 | 
				
			|||||||
    ImGuiKeyChord chord;
 | 
					    ImGuiKeyChord chord;
 | 
				
			||||||
    ImguiFunction function;
 | 
					    ImguiFunction function;
 | 
				
			||||||
    std::string focusWindow{};
 | 
					    std::string focusWindow{};
 | 
				
			||||||
    std::string undoAction{};
 | 
					    std::string snapshotAction{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool is_focus_window() const { return !focusWindow.empty(); }
 | 
					    bool is_focus_window() const { return !focusWindow.empty(); }
 | 
				
			||||||
    bool is_undoable() const { return !undoAction.empty(); }
 | 
					    bool is_undoable() const { return !snapshotAction.empty(); }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void imgui_log_push(Imgui* self, const std::string& text)
 | 
					static void imgui_log_push(Imgui* self, const std::string& text)
 | 
				
			||||||
@@ -227,8 +232,8 @@ static std::vector<ImguiHotkey>& imgui_hotkey_registry()
 | 
				
			|||||||
static inline void imgui_file_new(Imgui* self)
 | 
					static inline void imgui_file_new(Imgui* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    anm2_reference_clear(self->reference);
 | 
					    anm2_reference_clear(self->reference);
 | 
				
			||||||
 | 
					    anm2_free(self->anm2);
 | 
				
			||||||
	anm2_new(self->anm2);
 | 
						anm2_new(self->anm2);
 | 
				
			||||||
    resources_textures_free(self->resources);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void imgui_file_open(Imgui* self)
 | 
					static inline void imgui_file_open(Imgui* self)
 | 
				
			||||||
@@ -267,9 +272,9 @@ static inline void imgui_explore(Imgui* self)
 | 
				
			|||||||
    dialog_explorer_open(parentPath.string());
 | 
					    dialog_explorer_open(parentPath.string());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void imgui_undo_push(Imgui* self, const std::string& action = SNAPSHOT_ACTION)
 | 
					static inline void imgui_snapshot(Imgui* self, const std::string& action = SNAPSHOT_ACTION)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Snapshot snapshot = {*self->anm2, *self->reference, self->preview->time, action};
 | 
					    Snapshot snapshot = snapshot_get(self->snapshots);
 | 
				
			||||||
    snapshots_undo_push(self->snapshots, &snapshot);
 | 
					    snapshots_undo_push(self->snapshots, &snapshot);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -476,7 +481,7 @@ struct ImguiItem
 | 
				
			|||||||
    std::string label{};
 | 
					    std::string label{};
 | 
				
			||||||
    std::string tooltip{};
 | 
					    std::string tooltip{};
 | 
				
			||||||
    std::string& text = tooltip;
 | 
					    std::string& text = tooltip;
 | 
				
			||||||
    std::string undoAction{};
 | 
					    std::string snapshotAction{};
 | 
				
			||||||
    std::string popup{};
 | 
					    std::string popup{};
 | 
				
			||||||
    std::string dragDrop{};
 | 
					    std::string dragDrop{};
 | 
				
			||||||
    std::string focusWindow{};
 | 
					    std::string focusWindow{};
 | 
				
			||||||
@@ -524,7 +529,7 @@ struct ImguiItem
 | 
				
			|||||||
                label += std::format(IMGUI_LABEL_SHORTCUT_FORMAT, chordString);
 | 
					                label += std::format(IMGUI_LABEL_SHORTCUT_FORMAT, chordString);
 | 
				
			||||||
            tooltip += std::format(IMGUI_TOOLTIP_SHORTCUT_FORMAT, chordString);
 | 
					            tooltip += std::format(IMGUI_TOOLTIP_SHORTCUT_FORMAT, chordString);
 | 
				
			||||||
            if (function)
 | 
					            if (function)
 | 
				
			||||||
                imgui_hotkey_registry().push_back({chord, function, focusWindow, undoAction});
 | 
					                imgui_hotkey_registry().push_back({chord, function, focusWindow, snapshotAction});
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::string labelNew{};
 | 
					        std::string labelNew{};
 | 
				
			||||||
@@ -578,7 +583,7 @@ struct ImguiItem
 | 
				
			|||||||
    bool is_size() const { return size != ImVec2(); }
 | 
					    bool is_size() const { return size != ImVec2(); }
 | 
				
			||||||
    bool is_popup_size() const { return popupSize != ImVec2(); }
 | 
					    bool is_popup_size() const { return popupSize != ImVec2(); }
 | 
				
			||||||
    bool is_tooltip() const { return !tooltip.empty(); }
 | 
					    bool is_tooltip() const { return !tooltip.empty(); }
 | 
				
			||||||
    bool is_undoable() const { return !undoAction.empty(); }
 | 
					    bool is_undoable() const { return !snapshotAction.empty(); }
 | 
				
			||||||
    bool is_mnemonic() const { return mnemonicKey != ImGuiKey_None; }
 | 
					    bool is_mnemonic() const { return mnemonicKey != ImGuiKey_None; }
 | 
				
			||||||
    bool is_range() const { return min != 0 || max != 0; }
 | 
					    bool is_range() const { return min != 0 || max != 0; }
 | 
				
			||||||
    const char* label_get() const { return label.c_str(); }
 | 
					    const char* label_get() const { return label.c_str(); }
 | 
				
			||||||
@@ -792,7 +797,7 @@ IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_SLIDER,
 | 
				
			|||||||
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.snapshotAction = "Generate Animation from Grid",
 | 
				
			||||||
    self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -845,7 +850,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_NUMBER_FRAMES,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_ADD,
 | 
					IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_ADD,
 | 
				
			||||||
    self.label = "Add",
 | 
					    self.label = "Add",
 | 
				
			||||||
    self.tooltip = "The specified values will be added to all specified frames.",
 | 
					    self.tooltip = "The specified values will be added to all specified frames.",
 | 
				
			||||||
    self.undoAction = "Add Frame Properties",
 | 
					    self.snapshotAction = "Add Frame Properties",
 | 
				
			||||||
    self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -853,7 +858,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_ADD,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_SUBTRACT,
 | 
					IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_SUBTRACT,
 | 
				
			||||||
    self.label = "Subtract",
 | 
					    self.label = "Subtract",
 | 
				
			||||||
    self.tooltip = "The specified values will be added to all selected frames.",
 | 
					    self.tooltip = "The specified values will be added to all selected frames.",
 | 
				
			||||||
    self.undoAction = "Subtract Frame Properties",
 | 
					    self.snapshotAction = "Subtract Frame Properties",
 | 
				
			||||||
    self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -861,7 +866,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_SUBTRACT,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_SET,
 | 
					IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_SET,
 | 
				
			||||||
    self.label = "Set",
 | 
					    self.label = "Set",
 | 
				
			||||||
    self.tooltip = "The specified values will be set to the specified value in selected frames.",
 | 
					    self.tooltip = "The specified values will be set to the specified value in selected frames.",
 | 
				
			||||||
    self.undoAction = "Set Frame Properties",
 | 
					    self.snapshotAction = "Set Frame Properties",
 | 
				
			||||||
    self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -899,7 +904,7 @@ IMGUI_ITEM(IMGUI_SCALE_ANM2_VALUE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_SCALE_ANM2_SCALE,
 | 
					IMGUI_ITEM(IMGUI_SCALE_ANM2_SCALE,
 | 
				
			||||||
    self.label = "Scale",
 | 
					    self.label = "Scale",
 | 
				
			||||||
    self.tooltip = "Scale the anm2 with the value specified.",
 | 
					    self.tooltip = "Scale the anm2 with the value specified.",
 | 
				
			||||||
    self.undoAction = "Scale Anm2",
 | 
					    self.snapshotAction = "Scale Anm2",
 | 
				
			||||||
    self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -926,7 +931,7 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION_LOCATION_BROWSE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_LOCATION,
 | 
					IMGUI_ITEM(IMGUI_RENDER_ANIMATION_LOCATION,
 | 
				
			||||||
    self.label = "Location",
 | 
					    self.label = "Location",
 | 
				
			||||||
    self.tooltip = "Select the location of the rendered animation.",
 | 
					    self.tooltip = "Select the location of the rendered animation.",
 | 
				
			||||||
    self.max = 255
 | 
					    self.max = 1024
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FFMPEG_BROWSE,
 | 
					IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FFMPEG_BROWSE,
 | 
				
			||||||
@@ -939,7 +944,7 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FFMPEG_BROWSE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FFMPEG_PATH,
 | 
					IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FFMPEG_PATH,
 | 
				
			||||||
    self.label = "FFmpeg Path",
 | 
					    self.label = "FFmpeg Path",
 | 
				
			||||||
    self.tooltip = "Sets the path FFmpeg currently resides in.\nFFmpeg is required for rendering animations.\nDownload it from https://ffmpeg.org/, your package manager, or wherever else.",
 | 
					    self.tooltip = "Sets the path FFmpeg currently resides in.\nFFmpeg is required for rendering animations.\nDownload it from https://ffmpeg.org/, your package manager, or wherever else.",
 | 
				
			||||||
    self.max = 255
 | 
					    self.max = 1024
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_OUTPUT,
 | 
					IMGUI_ITEM(IMGUI_RENDER_ANIMATION_OUTPUT,
 | 
				
			||||||
@@ -1016,7 +1021,7 @@ IMGUI_ITEM(IMGUI_ANIMATIONS_CHILD, self.label = "## Animations Child", self.flag
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_ANIMATION,
 | 
					IMGUI_ITEM(IMGUI_ANIMATION,
 | 
				
			||||||
    self.label = "## Animation Item",
 | 
					    self.label = "## Animation Item",
 | 
				
			||||||
    self.undoAction = "Select Animation",
 | 
					    self.snapshotAction = "Select Animation",
 | 
				
			||||||
    self.dragDrop = "## Animation Drag Drop",
 | 
					    self.dragDrop = "## Animation Drag Drop",
 | 
				
			||||||
    self.atlas = ATLAS_ANIMATION,
 | 
					    self.atlas = ATLAS_ANIMATION,
 | 
				
			||||||
    self.idOffset = 2000
 | 
					    self.idOffset = 2000
 | 
				
			||||||
@@ -1026,7 +1031,7 @@ IMGUI_ITEM(IMGUI_ANIMATION,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_ANIMATION_ADD,
 | 
					IMGUI_ITEM(IMGUI_ANIMATION_ADD,
 | 
				
			||||||
    self.label = "Add",
 | 
					    self.label = "Add",
 | 
				
			||||||
    self.tooltip = "Adds a new animation.",
 | 
					    self.tooltip = "Adds a new animation.",
 | 
				
			||||||
    self.undoAction = "Add Animation",
 | 
					    self.snapshotAction = "Add Animation",
 | 
				
			||||||
    self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1034,7 +1039,7 @@ IMGUI_ITEM(IMGUI_ANIMATION_ADD,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_ANIMATION_DUPLICATE,
 | 
					IMGUI_ITEM(IMGUI_ANIMATION_DUPLICATE,
 | 
				
			||||||
    self.label = "Duplicate",
 | 
					    self.label = "Duplicate",
 | 
				
			||||||
    self.tooltip = "Duplicates the selected animation, placing it after.",
 | 
					    self.tooltip = "Duplicates the selected animation, placing it after.",
 | 
				
			||||||
    self.undoAction = "Duplicate Animation",
 | 
					    self.snapshotAction = "Duplicate Animation",
 | 
				
			||||||
    self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1102,7 +1107,7 @@ IMGUI_ITEM(IMGUI_MERGE_DELETE_ANIMATIONS_AFTER,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_MERGE_CONFIRM,
 | 
					IMGUI_ITEM(IMGUI_MERGE_CONFIRM,
 | 
				
			||||||
    self.label = "Merge",
 | 
					    self.label = "Merge",
 | 
				
			||||||
    self.tooltip = "Merge the selected animations with the options set.",
 | 
					    self.tooltip = "Merge the selected animations with the options set.",
 | 
				
			||||||
    self.undoAction = "Merge Animations",
 | 
					    self.snapshotAction = "Merge Animations",
 | 
				
			||||||
    self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1110,7 +1115,7 @@ IMGUI_ITEM(IMGUI_MERGE_CONFIRM,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_ANIMATION_REMOVE,
 | 
					IMGUI_ITEM(IMGUI_ANIMATION_REMOVE,
 | 
				
			||||||
    self.label = "Remove",
 | 
					    self.label = "Remove",
 | 
				
			||||||
    self.tooltip = "Remove the selected animation.",
 | 
					    self.tooltip = "Remove the selected animation.",
 | 
				
			||||||
    self.undoAction = "Remove Animation",
 | 
					    self.snapshotAction = "Remove Animation",
 | 
				
			||||||
    self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.chord = ImGuiKey_Delete,
 | 
					    self.chord = ImGuiKey_Delete,
 | 
				
			||||||
    self.focusWindow = IMGUI_ANIMATIONS.label,
 | 
					    self.focusWindow = IMGUI_ANIMATIONS.label,
 | 
				
			||||||
@@ -1120,7 +1125,7 @@ IMGUI_ITEM(IMGUI_ANIMATION_REMOVE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_ANIMATION_DEFAULT,
 | 
					IMGUI_ITEM(IMGUI_ANIMATION_DEFAULT,
 | 
				
			||||||
    self.label = "Default",
 | 
					    self.label = "Default",
 | 
				
			||||||
    self.tooltip = "Set the selected animation as the default one.",
 | 
					    self.tooltip = "Set the selected animation as the default one.",
 | 
				
			||||||
    self.undoAction = "Default Animation",
 | 
					    self.snapshotAction = "Default Animation",
 | 
				
			||||||
    self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_ANIMATIONS_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1143,7 +1148,7 @@ IMGUI_ITEM(IMGUI_EVENT,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_EVENTS_ADD,
 | 
					IMGUI_ITEM(IMGUI_EVENTS_ADD,
 | 
				
			||||||
    self.label = "Add",
 | 
					    self.label = "Add",
 | 
				
			||||||
    self.tooltip = "Adds a new event.",
 | 
					    self.tooltip = "Adds a new event.",
 | 
				
			||||||
    self.undoAction = "Add Event",
 | 
					    self.snapshotAction = "Add Event",
 | 
				
			||||||
    self.rowCount = IMGUI_EVENTS_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_EVENTS_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1151,7 +1156,7 @@ IMGUI_ITEM(IMGUI_EVENTS_ADD,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_EVENTS_REMOVE_UNUSED,
 | 
					IMGUI_ITEM(IMGUI_EVENTS_REMOVE_UNUSED,
 | 
				
			||||||
    self.label = "Remove Unused",
 | 
					    self.label = "Remove Unused",
 | 
				
			||||||
    self.tooltip = "Removes all unused events (i.e., not being used in any triggers in any animation).",
 | 
					    self.tooltip = "Removes all unused events (i.e., not being used in any triggers in any animation).",
 | 
				
			||||||
    self.undoAction = "Remove Unused Events",
 | 
					    self.snapshotAction = "Remove Unused Events",
 | 
				
			||||||
    self.rowCount = IMGUI_EVENTS_OPTIONS_ROW_COUNT
 | 
					    self.rowCount = IMGUI_EVENTS_OPTIONS_ROW_COUNT
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1197,6 +1202,7 @@ IMGUI_ITEM(IMGUI_SPRITESHEET_ADD,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_SPRITESHEETS_RELOAD,
 | 
					IMGUI_ITEM(IMGUI_SPRITESHEETS_RELOAD,
 | 
				
			||||||
    self.label = "Reload",
 | 
					    self.label = "Reload",
 | 
				
			||||||
    self.tooltip = "Reload the selected spritesheet.",
 | 
					    self.tooltip = "Reload the selected spritesheet.",
 | 
				
			||||||
 | 
					    self.snapshotAction = "Reload Spritesheet",
 | 
				
			||||||
    self.rowCount = IMGUI_SPRITESHEETS_OPTIONS_FIRST_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_SPRITESHEETS_OPTIONS_FIRST_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1373,35 +1379,35 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES, self.label = "Frame Properties");
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_POSITION,
 | 
					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.snapshotAction = "Frame Position",
 | 
				
			||||||
    self.isUseItemActivated = true
 | 
					    self.isUseItemActivated = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_CROP,
 | 
					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.snapshotAction = "Frame Crop",
 | 
				
			||||||
    self.isUseItemActivated = true
 | 
					    self.isUseItemActivated = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_SIZE,
 | 
					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.snapshotAction = "Frame Size",
 | 
				
			||||||
    self.isUseItemActivated = true
 | 
					    self.isUseItemActivated = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_PIVOT,
 | 
					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.snapshotAction = "Frame Pivot",
 | 
				
			||||||
    self.isUseItemActivated = true
 | 
					    self.isUseItemActivated = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_SCALE,
 | 
					IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_SCALE,
 | 
				
			||||||
    self.label = "Scale",
 | 
					    self.label = "Scale",
 | 
				
			||||||
    self.tooltip = "Change the scale of the selected frame.",
 | 
					    self.tooltip = "Change the scale of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Scale",
 | 
					    self.snapshotAction = "Frame Scale",
 | 
				
			||||||
    self.isUseItemActivated = true,
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.value = 100
 | 
					    self.value = 100
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1409,14 +1415,14 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_SCALE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_ROTATION,
 | 
					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.snapshotAction = "Frame Rotation",
 | 
				
			||||||
    self.isUseItemActivated = true
 | 
					    self.isUseItemActivated = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_DELAY,
 | 
					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.snapshotAction = "Frame Duration",
 | 
				
			||||||
    self.isUseItemActivated = true,
 | 
					    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,
 | 
				
			||||||
@@ -1426,7 +1432,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_DELAY,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_TINT,
 | 
					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.snapshotAction = "Frame Tint",
 | 
				
			||||||
    self.isUseItemActivated = true,
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.value = 1
 | 
					    self.value = 1
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1434,7 +1440,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_TINT,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_COLOR_OFFSET,
 | 
					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.snapshotAction = "Frame Color Offset",
 | 
				
			||||||
    self.isUseItemActivated = true,
 | 
					    self.isUseItemActivated = true,
 | 
				
			||||||
    self.value = 0
 | 
					    self.value = 0
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1443,7 +1449,7 @@ const ImVec2 IMGUI_FRAME_PROPERTIES_FLIP_BUTTON_SIZE = {75, 0};
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_FLIP_X,
 | 
					IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_FLIP_X,
 | 
				
			||||||
    self.label = "Flip X",
 | 
					    self.label = "Flip X",
 | 
				
			||||||
    self.tooltip = "Change the sign of the X scale, to cheat flipping the layer horizontally.\n(Anm2 doesn't support flipping directly.)",
 | 
					    self.tooltip = "Change the sign of the X scale, to cheat flipping the layer horizontally.\n(Anm2 doesn't support flipping directly.)",
 | 
				
			||||||
    self.undoAction = "Frame Flip X",
 | 
					    self.snapshotAction = "Frame Flip X",
 | 
				
			||||||
    self.size = IMGUI_FRAME_PROPERTIES_FLIP_BUTTON_SIZE,
 | 
					    self.size = IMGUI_FRAME_PROPERTIES_FLIP_BUTTON_SIZE,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1451,14 +1457,14 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_FLIP_X,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_FLIP_Y,
 | 
					IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_FLIP_Y,
 | 
				
			||||||
    self.label = "Flip Y",
 | 
					    self.label = "Flip Y",
 | 
				
			||||||
    self.tooltip = "Change the sign of the Y scale, to cheat flipping the layer vertically.\n(Anm2 doesn't support flipping directly.)",
 | 
					    self.tooltip = "Change the sign of the Y scale, to cheat flipping the layer vertically.\n(Anm2 doesn't support flipping directly.)",
 | 
				
			||||||
    self.undoAction = "Frame Flip Y",
 | 
					    self.snapshotAction = "Frame Flip Y",
 | 
				
			||||||
    self.size = IMGUI_FRAME_PROPERTIES_FLIP_BUTTON_SIZE 
 | 
					    self.size = IMGUI_FRAME_PROPERTIES_FLIP_BUTTON_SIZE 
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_VISIBLE,
 | 
					IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_VISIBLE,
 | 
				
			||||||
    self.label = "Visible",
 | 
					    self.label = "Visible",
 | 
				
			||||||
    self.tooltip = "Toggles the visibility of the selected frame.",
 | 
					    self.tooltip = "Toggles the visibility of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Visibility",
 | 
					    self.snapshotAction = "Frame Visibility",
 | 
				
			||||||
    self.isSameLine = true,
 | 
					    self.isSameLine = true,
 | 
				
			||||||
    self.value = true
 | 
					    self.value = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1466,20 +1472,20 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_VISIBLE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_INTERPOLATED,
 | 
					IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_INTERPOLATED,
 | 
				
			||||||
    self.label = "Interpolation",
 | 
					    self.label = "Interpolation",
 | 
				
			||||||
    self.tooltip = "Toggles the interpolation of the selected frame.",
 | 
					    self.tooltip = "Toggles the interpolation of the selected frame.",
 | 
				
			||||||
    self.undoAction = "Frame Interpolation",
 | 
					    self.snapshotAction = "Frame Interpolation",
 | 
				
			||||||
    self.value = true
 | 
					    self.value = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_EVENT,
 | 
					IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_EVENT,
 | 
				
			||||||
    self.label = "Event",
 | 
					    self.label = "Event",
 | 
				
			||||||
    self.tooltip = "Change the event the trigger uses.",
 | 
					    self.tooltip = "Change the event the trigger uses.",
 | 
				
			||||||
    self.undoAction = "Trigger Event"
 | 
					    self.snapshotAction = "Trigger Event"
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_AT_FRAME,
 | 
					IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_AT_FRAME,
 | 
				
			||||||
    self.label = "At Frame",
 | 
					    self.label = "At Frame",
 | 
				
			||||||
    self.tooltip = "Change the frame where the trigger occurs.",
 | 
					    self.tooltip = "Change the frame where the trigger occurs.",
 | 
				
			||||||
    self.undoAction = "Trigger At Frame"
 | 
					    self.snapshotAction = "Trigger At Frame"
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_TOOLS, self.label = "Tools");
 | 
					IMGUI_ITEM(IMGUI_TOOLS, self.label = "Tools");
 | 
				
			||||||
@@ -1674,7 +1680,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_SELECTABLE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_ROOT_SELECTABLE,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_ITEM_ROOT_SELECTABLE,
 | 
				
			||||||
    self.label = "Root",
 | 
					    self.label = "Root",
 | 
				
			||||||
    self.tooltip = "The root item of an animation.\nChanging its properties will transform the rest of the animation.",
 | 
					    self.tooltip = "The root item of an animation.\nChanging its properties will transform the rest of the animation.",
 | 
				
			||||||
    self.undoAction = "Root Item Select",
 | 
					    self.snapshotAction = "Root Item Select",
 | 
				
			||||||
    self.atlas = ATLAS_ROOT,
 | 
					    self.atlas = ATLAS_ROOT,
 | 
				
			||||||
    self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE
 | 
					    self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1682,7 +1688,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_ROOT_SELECTABLE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_LAYER_SELECTABLE,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_ITEM_LAYER_SELECTABLE,
 | 
				
			||||||
    self.label = "## Layer Selectable",
 | 
					    self.label = "## Layer Selectable",
 | 
				
			||||||
    self.tooltip = "A layer item.\nA graphical item within the animation.",
 | 
					    self.tooltip = "A layer item.\nA graphical item within the animation.",
 | 
				
			||||||
    self.undoAction = "Layer Item Select",
 | 
					    self.snapshotAction = "Layer Item Select",
 | 
				
			||||||
    self.dragDrop = "## Layer Drag Drop",
 | 
					    self.dragDrop = "## Layer Drag Drop",
 | 
				
			||||||
    self.atlas = ATLAS_LAYER,
 | 
					    self.atlas = ATLAS_LAYER,
 | 
				
			||||||
    self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE
 | 
					    self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE
 | 
				
			||||||
@@ -1691,7 +1697,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_LAYER_SELECTABLE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_NULL_SELECTABLE,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_ITEM_NULL_SELECTABLE,
 | 
				
			||||||
    self.label = "## Null Selectable",
 | 
					    self.label = "## Null Selectable",
 | 
				
			||||||
    self.tooltip = "A null item.\nAn invisible item within the animation that is accessible via a game engine.",
 | 
					    self.tooltip = "A null item.\nAn invisible item within the animation that is accessible via a game engine.",
 | 
				
			||||||
    self.undoAction = "Null Item Select",
 | 
					    self.snapshotAction = "Null Item Select",
 | 
				
			||||||
    self.dragDrop = "## Null Drag Drop",
 | 
					    self.dragDrop = "## Null Drag Drop",
 | 
				
			||||||
    self.atlas = ATLAS_NULL,
 | 
					    self.atlas = ATLAS_NULL,
 | 
				
			||||||
    self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE
 | 
					    self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE
 | 
				
			||||||
@@ -1700,7 +1706,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_NULL_SELECTABLE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_TRIGGERS_SELECTABLE,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_ITEM_TRIGGERS_SELECTABLE,
 | 
				
			||||||
    self.label = "Triggers",
 | 
					    self.label = "Triggers",
 | 
				
			||||||
    self.tooltip = "The animation's triggers.\nWill fire based on an event.",
 | 
					    self.tooltip = "The animation's triggers.\nWill fire based on an event.",
 | 
				
			||||||
    self.undoAction = "Triggers Item Select",
 | 
					    self.snapshotAction = "Triggers Item Select",
 | 
				
			||||||
    self.atlas = ATLAS_TRIGGERS,
 | 
					    self.atlas = ATLAS_TRIGGERS,
 | 
				
			||||||
    self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE
 | 
					    self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1717,28 +1723,28 @@ const inline ImguiItem* IMGUI_TIMELINE_ITEM_SELECTABLES[ANM2_COUNT]
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_VISIBLE,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_ITEM_VISIBLE,
 | 
				
			||||||
    self.label = "## Visible",
 | 
					    self.label = "## Visible",
 | 
				
			||||||
    self.tooltip = "The item is visible.\nPress to set to invisible.",
 | 
					    self.tooltip = "The item is visible.\nPress to set to invisible.",
 | 
				
			||||||
    self.undoAction = "Item Invisible",
 | 
					    self.snapshotAction = "Item Invisible",
 | 
				
			||||||
    self.atlas = ATLAS_VISIBLE
 | 
					    self.atlas = ATLAS_VISIBLE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_INVISIBLE,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_ITEM_INVISIBLE,
 | 
				
			||||||
    self.label = "## Invisible",
 | 
					    self.label = "## Invisible",
 | 
				
			||||||
    self.tooltip = "The item is invisible.\nPress to set to visible.",
 | 
					    self.tooltip = "The item is invisible.\nPress to set to visible.",
 | 
				
			||||||
    self.undoAction = "Item Visible",
 | 
					    self.snapshotAction = "Item Visible",
 | 
				
			||||||
    self.atlas = ATLAS_INVISIBLE
 | 
					    self.atlas = ATLAS_INVISIBLE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_SHOW_RECT,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_ITEM_SHOW_RECT,
 | 
				
			||||||
    self.label = "## Show Rect",
 | 
					    self.label = "## Show Rect",
 | 
				
			||||||
    self.tooltip = "The rect is shown.\nPress to hide rect.",
 | 
					    self.tooltip = "The rect is shown.\nPress to hide rect.",
 | 
				
			||||||
    self.undoAction = "Hide Rect",
 | 
					    self.snapshotAction = "Hide Rect",
 | 
				
			||||||
    self.atlas = ATLAS_SHOW_RECT
 | 
					    self.atlas = ATLAS_SHOW_RECT
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_HIDE_RECT,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_ITEM_HIDE_RECT,
 | 
				
			||||||
    self.label = "## Hide Rect",
 | 
					    self.label = "## Hide Rect",
 | 
				
			||||||
    self.tooltip = "The rect is hidden.\nPress to show rect.",
 | 
					    self.tooltip = "The rect is hidden.\nPress to show rect.",
 | 
				
			||||||
    self.undoAction = "Show Rect",
 | 
					    self.snapshotAction = "Show Rect",
 | 
				
			||||||
    self.atlas = ATLAS_HIDE_RECT
 | 
					    self.atlas = ATLAS_HIDE_RECT
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1765,7 +1771,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_FRAME, self.label = "## Frame");
 | 
				
			|||||||
static const vec4 IMGUI_FRAME_BORDER_COLOR = {1.0f, 1.0f, 1.0f, 0.25f};
 | 
					static const vec4 IMGUI_FRAME_BORDER_COLOR = {1.0f, 1.0f, 1.0f, 0.25f};
 | 
				
			||||||
IMGUI_ITEM(IMGUI_TIMELINE_ROOT_FRAME,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_ROOT_FRAME,
 | 
				
			||||||
    self.label = "## Root Frame",
 | 
					    self.label = "## Root Frame",
 | 
				
			||||||
    self.undoAction = "Root Frame Select",
 | 
					    self.snapshotAction = "Root Frame Select",
 | 
				
			||||||
    self.color = {{0.14f, 0.27f, 0.39f, 1.0f}, {0.28f, 0.54f, 0.78f, 1.0f}, {0.36f, 0.70f, 0.95f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
 | 
					    self.color = {{0.14f, 0.27f, 0.39f, 1.0f}, {0.28f, 0.54f, 0.78f, 1.0f}, {0.36f, 0.70f, 0.95f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
 | 
				
			||||||
    self.size = IMGUI_TIMELINE_FRAME_SIZE,
 | 
					    self.size = IMGUI_TIMELINE_FRAME_SIZE,
 | 
				
			||||||
    self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET,
 | 
					    self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET,
 | 
				
			||||||
@@ -1774,7 +1780,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_ROOT_FRAME,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_TIMELINE_LAYER_FRAME,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_LAYER_FRAME,
 | 
				
			||||||
    self.label = "## Layer Frame",
 | 
					    self.label = "## Layer Frame",
 | 
				
			||||||
    self.undoAction = "Layer Frame Select",
 | 
					    self.snapshotAction = "Layer Frame Select",
 | 
				
			||||||
    self.dragDrop = "## Layer Frame Drag Drop",
 | 
					    self.dragDrop = "## Layer Frame Drag Drop",
 | 
				
			||||||
    self.color = {{0.45f, 0.18f, 0.07f, 1.0f}, {0.78f, 0.32f, 0.12f, 1.0f}, {0.95f, 0.40f, 0.15f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
 | 
					    self.color = {{0.45f, 0.18f, 0.07f, 1.0f}, {0.78f, 0.32f, 0.12f, 1.0f}, {0.95f, 0.40f, 0.15f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
 | 
				
			||||||
    self.size = IMGUI_TIMELINE_FRAME_SIZE,
 | 
					    self.size = IMGUI_TIMELINE_FRAME_SIZE,
 | 
				
			||||||
@@ -1784,7 +1790,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_LAYER_FRAME,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_TIMELINE_NULL_FRAME,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_NULL_FRAME,
 | 
				
			||||||
    self.label = "## Null Frame",
 | 
					    self.label = "## Null Frame",
 | 
				
			||||||
    self.undoAction = "Null Frame Select",
 | 
					    self.snapshotAction = "Null Frame Select",
 | 
				
			||||||
    self.dragDrop = "## Null Frame Drag Drop",
 | 
					    self.dragDrop = "## Null Frame Drag Drop",
 | 
				
			||||||
    self.color = {{0.17f, 0.33f, 0.17f, 1.0f}, {0.34f, 0.68f, 0.34f, 1.0f}, {0.44f, 0.88f, 0.44f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
 | 
					    self.color = {{0.17f, 0.33f, 0.17f, 1.0f}, {0.34f, 0.68f, 0.34f, 1.0f}, {0.44f, 0.88f, 0.44f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
 | 
				
			||||||
    self.size = IMGUI_TIMELINE_FRAME_SIZE,
 | 
					    self.size = IMGUI_TIMELINE_FRAME_SIZE,
 | 
				
			||||||
@@ -1794,7 +1800,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_NULL_FRAME,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_TIMELINE_TRIGGERS_FRAME,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_TRIGGERS_FRAME,
 | 
				
			||||||
    self.label = "## Triggers Frame",
 | 
					    self.label = "## Triggers Frame",
 | 
				
			||||||
    self.undoAction = "Trigger Select",
 | 
					    self.snapshotAction = "Trigger Select",
 | 
				
			||||||
    self.color = {{0.36f, 0.14f, 0.24f, 1.0f}, {0.72f, 0.28f, 0.48f, 1.0f}, {0.92f, 0.36f, 0.60f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
 | 
					    self.color = {{0.36f, 0.14f, 0.24f, 1.0f}, {0.72f, 0.28f, 0.48f, 1.0f}, {0.92f, 0.36f, 0.60f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
 | 
				
			||||||
    self.size = IMGUI_TIMELINE_FRAME_SIZE,
 | 
					    self.size = IMGUI_TIMELINE_FRAME_SIZE,
 | 
				
			||||||
    self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET,
 | 
					    self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET,
 | 
				
			||||||
@@ -1834,21 +1840,21 @@ IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_LAYER,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_LAYER,
 | 
				
			||||||
    self.label = "Layer",
 | 
					    self.label = "Layer",
 | 
				
			||||||
    self.tooltip = "Adds a layer item.\nA layer item is a primary graphical item, using a spritesheet.",
 | 
					    self.tooltip = "Adds a layer item.\nA layer item is a primary graphical item, using a spritesheet.",
 | 
				
			||||||
    self.undoAction = "Add Layer",
 | 
					    self.snapshotAction = "Add Layer",
 | 
				
			||||||
    self.isSizeToText = true
 | 
					    self.isSizeToText = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_NULL,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_NULL,
 | 
				
			||||||
    self.label = "Null",
 | 
					    self.label = "Null",
 | 
				
			||||||
    self.tooltip = "Adds a null item.\nA null item is an invisible item, often accessed by the game engine.",
 | 
					    self.tooltip = "Adds a null item.\nA null item is an invisible item, often accessed by the game engine.",
 | 
				
			||||||
    self.undoAction = "Add Null",
 | 
					    self.snapshotAction = "Add Null",
 | 
				
			||||||
    self.isSizeToText = true
 | 
					    self.isSizeToText = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMGUI_ITEM(IMGUI_TIMELINE_REMOVE_ITEM,
 | 
					IMGUI_ITEM(IMGUI_TIMELINE_REMOVE_ITEM,
 | 
				
			||||||
    self.label = "Remove",
 | 
					    self.label = "Remove",
 | 
				
			||||||
    self.tooltip = "Removes the selected item (layer or null) from the animation.",
 | 
					    self.tooltip = "Removes the selected item (layer or null) from the animation.",
 | 
				
			||||||
    self.undoAction = "Remove Item",
 | 
					    self.snapshotAction = "Remove Item",
 | 
				
			||||||
    self.focusWindow = IMGUI_TIMELINE.label,
 | 
					    self.focusWindow = IMGUI_TIMELINE.label,
 | 
				
			||||||
    self.rowCount = IMGUI_TIMELINE_FOOTER_ITEM_CHILD_ITEM_COUNT
 | 
					    self.rowCount = IMGUI_TIMELINE_FOOTER_ITEM_CHILD_ITEM_COUNT
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1875,7 +1881,7 @@ IMGUI_ITEM(IMGUI_PAUSE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_ADD_FRAME,
 | 
					IMGUI_ITEM(IMGUI_ADD_FRAME,
 | 
				
			||||||
    self.label = "+ Insert Frame",
 | 
					    self.label = "+ Insert Frame",
 | 
				
			||||||
    self.tooltip = "Inserts a frame in the selected animation item, based on the preview time.",
 | 
					    self.tooltip = "Inserts a frame in the selected animation item, based on the preview time.",
 | 
				
			||||||
    self.undoAction = "Insert Frame",
 | 
					    self.snapshotAction = "Insert Frame",
 | 
				
			||||||
    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1883,7 +1889,7 @@ IMGUI_ITEM(IMGUI_ADD_FRAME,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_REMOVE_FRAME,
 | 
					IMGUI_ITEM(IMGUI_REMOVE_FRAME,
 | 
				
			||||||
    self.label = "- Delete Frame",
 | 
					    self.label = "- Delete Frame",
 | 
				
			||||||
    self.tooltip = "Removes the selected frame from the selected animation item.",
 | 
					    self.tooltip = "Removes the selected frame from the selected animation item.",
 | 
				
			||||||
    self.undoAction = "Delete Frame",
 | 
					    self.snapshotAction = "Delete Frame",
 | 
				
			||||||
    self.focusWindow = IMGUI_TIMELINE.label,
 | 
					    self.focusWindow = IMGUI_TIMELINE.label,
 | 
				
			||||||
    self.chord = ImGuiKey_Delete,
 | 
					    self.chord = ImGuiKey_Delete,
 | 
				
			||||||
    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
				
			||||||
@@ -1927,7 +1933,7 @@ IMGUI_ITEM(IMGUI_BAKE_ROUND_ROTATION,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_BAKE_CONFIRM,
 | 
					IMGUI_ITEM(IMGUI_BAKE_CONFIRM,
 | 
				
			||||||
    self.label = "Bake",
 | 
					    self.label = "Bake",
 | 
				
			||||||
    self.tooltip = "Bake the selected frame with the options selected.",
 | 
					    self.tooltip = "Bake the selected frame with the options selected.",
 | 
				
			||||||
    self.undoAction = "Bake Frames",
 | 
					    self.snapshotAction = "Bake Frames",
 | 
				
			||||||
    self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1935,7 +1941,7 @@ IMGUI_ITEM(IMGUI_BAKE_CONFIRM,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_FIT_ANIMATION_LENGTH, 
 | 
					IMGUI_ITEM(IMGUI_FIT_ANIMATION_LENGTH, 
 | 
				
			||||||
    self.label = "Fit Animation Length",
 | 
					    self.label = "Fit Animation Length",
 | 
				
			||||||
    self.tooltip = "Sets the animation's length to the latest frame.",
 | 
					    self.tooltip = "Sets the animation's length to the latest frame.",
 | 
				
			||||||
    self.undoAction = "Fit Animation Length",
 | 
					    self.snapshotAction = "Fit Animation Length",
 | 
				
			||||||
    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -1943,7 +1949,7 @@ IMGUI_ITEM(IMGUI_FIT_ANIMATION_LENGTH,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_ANIMATION_LENGTH, 
 | 
					IMGUI_ITEM(IMGUI_ANIMATION_LENGTH, 
 | 
				
			||||||
    self.label = "Length",
 | 
					    self.label = "Length",
 | 
				
			||||||
    self.tooltip = "Sets the animation length.\n(Will not change frames.)",
 | 
					    self.tooltip = "Sets the animation length.\n(Will not change frames.)",
 | 
				
			||||||
    self.undoAction = "Set Animation Length",
 | 
					    self.snapshotAction = "Set Animation Length",
 | 
				
			||||||
    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.min = ANM2_FRAME_NUM_MIN,
 | 
					    self.min = ANM2_FRAME_NUM_MIN,
 | 
				
			||||||
    self.max = ANM2_FRAME_NUM_MAX,
 | 
					    self.max = ANM2_FRAME_NUM_MAX,
 | 
				
			||||||
@@ -1954,7 +1960,7 @@ IMGUI_ITEM(IMGUI_ANIMATION_LENGTH,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_FPS, 
 | 
					IMGUI_ITEM(IMGUI_FPS, 
 | 
				
			||||||
    self.label = "FPS",
 | 
					    self.label = "FPS",
 | 
				
			||||||
    self.tooltip = "Sets the animation's frames per second (its speed).",
 | 
					    self.tooltip = "Sets the animation's frames per second (its speed).",
 | 
				
			||||||
    self.undoAction = "Set FPS",
 | 
					    self.snapshotAction = "Set FPS",
 | 
				
			||||||
    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.min = ANM2_FPS_MIN,
 | 
					    self.min = ANM2_FPS_MIN,
 | 
				
			||||||
    self.max = ANM2_FPS_MAX,
 | 
					    self.max = ANM2_FPS_MAX,
 | 
				
			||||||
@@ -1965,7 +1971,7 @@ IMGUI_ITEM(IMGUI_FPS,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_LOOP,
 | 
					IMGUI_ITEM(IMGUI_LOOP,
 | 
				
			||||||
    self.label = "Loop",
 | 
					    self.label = "Loop",
 | 
				
			||||||
    self.tooltip = "Toggles the animation looping.",
 | 
					    self.tooltip = "Toggles the animation looping.",
 | 
				
			||||||
    self.undoAction = "Set Loop",
 | 
					    self.snapshotAction = "Set Loop",
 | 
				
			||||||
    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
					    self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
 | 
				
			||||||
    self.value = true,
 | 
					    self.value = true,
 | 
				
			||||||
    self.isSameLine = true
 | 
					    self.isSameLine = true
 | 
				
			||||||
@@ -1983,7 +1989,7 @@ IMGUI_ITEM(IMGUI_CONTEXT_MENU, self.label = "## Context Menu");
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_CUT,
 | 
					IMGUI_ITEM(IMGUI_CUT,
 | 
				
			||||||
    self.label = "Cut",
 | 
					    self.label = "Cut",
 | 
				
			||||||
    self.tooltip = "Cuts the currently selected contextual element; removing it and putting it to the clipboard.",
 | 
					    self.tooltip = "Cuts the currently selected contextual element; removing it and putting it to the clipboard.",
 | 
				
			||||||
    self.undoAction = "Cut",
 | 
					    self.snapshotAction = "Cut",
 | 
				
			||||||
    self.function = imgui_cut,
 | 
					    self.function = imgui_cut,
 | 
				
			||||||
    self.chord = ImGuiMod_Ctrl | ImGuiKey_X,
 | 
					    self.chord = ImGuiMod_Ctrl | ImGuiKey_X,
 | 
				
			||||||
    self.isSizeToText = true
 | 
					    self.isSizeToText = true
 | 
				
			||||||
@@ -1992,7 +1998,7 @@ IMGUI_ITEM(IMGUI_CUT,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_COPY,
 | 
					IMGUI_ITEM(IMGUI_COPY,
 | 
				
			||||||
    self.label = "Copy",
 | 
					    self.label = "Copy",
 | 
				
			||||||
    self.tooltip = "Copies the currently selected contextual element to the clipboard.",
 | 
					    self.tooltip = "Copies the currently selected contextual element to the clipboard.",
 | 
				
			||||||
    self.undoAction = "Copy",
 | 
					    self.snapshotAction = "Copy",
 | 
				
			||||||
    self.function = imgui_copy,
 | 
					    self.function = imgui_copy,
 | 
				
			||||||
    self.chord = ImGuiMod_Ctrl | ImGuiKey_C,
 | 
					    self.chord = ImGuiMod_Ctrl | ImGuiKey_C,
 | 
				
			||||||
    self.isSizeToText = true
 | 
					    self.isSizeToText = true
 | 
				
			||||||
@@ -2001,7 +2007,7 @@ IMGUI_ITEM(IMGUI_COPY,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_PASTE,
 | 
					IMGUI_ITEM(IMGUI_PASTE,
 | 
				
			||||||
    self.label = "Paste",
 | 
					    self.label = "Paste",
 | 
				
			||||||
    self.tooltip = "Pastes the currently selection contextual element from the clipboard.",
 | 
					    self.tooltip = "Pastes the currently selection contextual element from the clipboard.",
 | 
				
			||||||
    self.undoAction = "Paste",
 | 
					    self.snapshotAction = "Paste",
 | 
				
			||||||
    self.function = imgui_paste,
 | 
					    self.function = imgui_paste,
 | 
				
			||||||
    self.chord = ImGuiMod_Ctrl | ImGuiKey_V,
 | 
					    self.chord = ImGuiMod_Ctrl | ImGuiKey_V,
 | 
				
			||||||
    self.isSizeToText = true
 | 
					    self.isSizeToText = true
 | 
				
			||||||
@@ -2010,7 +2016,7 @@ IMGUI_ITEM(IMGUI_PASTE,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_CHANGE_INPUT_TEXT,
 | 
					IMGUI_ITEM(IMGUI_CHANGE_INPUT_TEXT,
 | 
				
			||||||
    self.label = "## Input Text",
 | 
					    self.label = "## Input Text",
 | 
				
			||||||
    self.tooltip = "Rename the selected item.",
 | 
					    self.tooltip = "Rename the selected item.",
 | 
				
			||||||
    self.undoAction = "Rename Item",
 | 
					    self.snapshotAction = "Rename Item",
 | 
				
			||||||
    self.flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue,
 | 
					    self.flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue,
 | 
				
			||||||
    self.max = 255
 | 
					    self.max = 255
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -2018,7 +2024,7 @@ IMGUI_ITEM(IMGUI_CHANGE_INPUT_TEXT,
 | 
				
			|||||||
IMGUI_ITEM(IMGUI_CHANGE_INPUT_INT,
 | 
					IMGUI_ITEM(IMGUI_CHANGE_INPUT_INT,
 | 
				
			||||||
    self.label = "## Input Int",
 | 
					    self.label = "## Input Int",
 | 
				
			||||||
    self.tooltip = "Change the selected item's value.",
 | 
					    self.tooltip = "Change the selected item's value.",
 | 
				
			||||||
    self.undoAction = "Change Value",
 | 
					    self.snapshotAction = "Change Value",
 | 
				
			||||||
    self.step = 0
 | 
					    self.step = 0
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ static bool _anm2_rescale(const std::string& file, f32 scale)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	Anm2 anm2;
 | 
						Anm2 anm2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!anm2_deserialize(&anm2, nullptr, file)) return false;
 | 
						if (!anm2_deserialize(&anm2, file)) return false;
 | 
				
			||||||
	anm2_scale(&anm2, scale);
 | 
						anm2_scale(&anm2, scale);
 | 
				
			||||||
	return anm2_serialize(&anm2, file);
 | 
						return anm2_serialize(&anm2, file);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -85,7 +85,7 @@ void preview_draw(Preview* self)
 | 
				
			|||||||
    GLuint& shaderGrid = self->resources->shaders[SHADER_GRID];
 | 
					    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_framebuffer_resize_check(&self->canvas);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    canvas_bind(&self->canvas);
 | 
					    canvas_bind(&self->canvas);
 | 
				
			||||||
    canvas_viewport_set(&self->canvas);
 | 
					    canvas_viewport_set(&self->canvas);
 | 
				
			||||||
@@ -108,12 +108,12 @@ void preview_draw(Preview* self)
 | 
				
			|||||||
        anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, self->time);
 | 
					        anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, self->time);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (self->settings->previewIsRootTransform)
 | 
					        if (self->settings->previewIsRootTransform)
 | 
				
			||||||
            rootModel = quad_parent_model_get(root.position, vec2(0.0f), root.rotation, PERCENT_TO_UNIT(root.scale));
 | 
					            rootModel = canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale), root.rotation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Root
 | 
					        // Root
 | 
				
			||||||
        if (self->settings->previewIsTargets && animation->rootAnimation.isVisible && root.isVisible)
 | 
					        if (self->settings->previewIsTargets && animation->rootAnimation.isVisible && root.isVisible)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            mat4 model = quad_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, root.rotation, PERCENT_TO_UNIT(root.scale));
 | 
					            mat4 model = canvas_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, PERCENT_TO_UNIT(root.scale), root.rotation);
 | 
				
			||||||
            mat4 rootTransform = transform * model;
 | 
					            mat4 rootTransform = transform * model;
 | 
				
			||||||
            f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_TARGET);
 | 
					            f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_TARGET);
 | 
				
			||||||
            canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, rootTransform, vertices, PREVIEW_ROOT_COLOR);
 | 
					            canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, rootTransform, vertices, PREVIEW_ROOT_COLOR);
 | 
				
			||||||
@@ -133,18 +133,18 @@ void preview_draw(Preview* self)
 | 
				
			|||||||
            if (!frame.isVisible)
 | 
					            if (!frame.isVisible)
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            mat4 model = quad_model_get(frame.size, frame.position, frame.pivot, frame.rotation, PERCENT_TO_UNIT(frame.scale));
 | 
					            mat4 model = canvas_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation);
 | 
				
			||||||
            mat4 layerTransform = transform * (rootModel * model);
 | 
					            mat4 layerTransform = transform * (rootModel * model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Texture* texture = map_find(self->resources->textures, self->anm2->layers[id].spritesheetID);
 | 
					            Texture& texture = self->anm2->spritesheets[self->anm2->layers[id].spritesheetID].texture;
 | 
				
			||||||
           
 | 
					           
 | 
				
			||||||
            if (texture && !texture->isInvalid)
 | 
					            if (!texture.isInvalid)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                vec2 uvMin = frame.crop / vec2(texture->size);
 | 
					                vec2 uvMin = frame.crop / vec2(texture.size);
 | 
				
			||||||
                vec2 uvMax = (frame.crop + frame.size) / vec2(texture->size);
 | 
					                vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size);
 | 
				
			||||||
                f32 vertices[] = UV_VERTICES(uvMin, uvMax);
 | 
					                f32 vertices[] = UV_VERTICES(uvMin, uvMax);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                canvas_texture_draw(&self->canvas, shaderTexture, texture->id, layerTransform, vertices, frame.tintRGBA, frame.offsetRGB);
 | 
					                canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, frame.tintRGBA, frame.offsetRGB);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
            if (self->settings->previewIsBorder)
 | 
					            if (self->settings->previewIsBorder)
 | 
				
			||||||
@@ -153,7 +153,7 @@ void preview_draw(Preview* self)
 | 
				
			|||||||
            if (self->settings->previewIsPivots)
 | 
					            if (self->settings->previewIsPivots)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
 | 
					                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 pivotModel = canvas_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
 | 
				
			||||||
                mat4 pivotTransform = transform * (rootModel * pivotModel);
 | 
					                mat4 pivotTransform = transform * (rootModel * pivotModel);
 | 
				
			||||||
                canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, PREVIEW_PIVOT_COLOR);
 | 
					                canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, PREVIEW_PIVOT_COLOR);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -182,7 +182,7 @@ void preview_draw(Preview* self)
 | 
				
			|||||||
                vec2 size = null.isShowRect ? CANVAS_PIVOT_SIZE : PREVIEW_TARGET_SIZE;
 | 
					                vec2 size = null.isShowRect ? CANVAS_PIVOT_SIZE : PREVIEW_TARGET_SIZE;
 | 
				
			||||||
                AtlasType atlas = null.isShowRect ? ATLAS_SQUARE : ATLAS_TARGET;
 | 
					                AtlasType atlas = null.isShowRect ? ATLAS_SQUARE : ATLAS_TARGET;
 | 
				
			||||||
          
 | 
					          
 | 
				
			||||||
                mat4 model = quad_model_get(size, frame.position, size * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale));
 | 
					                mat4 model = canvas_model_get(size, frame.position, size * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
 | 
				
			||||||
                mat4 nullTransform = transform * (rootModel * model);
 | 
					                mat4 nullTransform = transform * (rootModel * model);
 | 
				
			||||||
     
 | 
					     
 | 
				
			||||||
                f32 vertices[] = ATLAS_UV_VERTICES(atlas);
 | 
					                f32 vertices[] = ATLAS_UV_VERTICES(atlas);
 | 
				
			||||||
@@ -191,7 +191,7 @@ void preview_draw(Preview* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (null.isShowRect)
 | 
					                if (null.isShowRect)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    mat4 rectModel = quad_model_get(PREVIEW_NULL_RECT_SIZE, frame.position, PREVIEW_NULL_RECT_SIZE * 0.5f, frame.rotation, PERCENT_TO_UNIT(frame.scale));
 | 
					                    mat4 rectModel = canvas_model_get(PREVIEW_NULL_RECT_SIZE, frame.position, PREVIEW_NULL_RECT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
 | 
				
			||||||
                    mat4 rectTransform = transform * (rootModel * rectModel);
 | 
					                    mat4 rectTransform = transform * (rootModel * rectModel);
 | 
				
			||||||
                    canvas_rect_draw(&self->canvas, shaderLine, rectTransform, color);
 | 
					                    canvas_rect_draw(&self->canvas, shaderLine, rectTransform, color);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -210,7 +210,7 @@ void preview_draw(Preview* self)
 | 
				
			|||||||
        anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationOverlayID, ANM2_ROOT}, self->time);
 | 
					        anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationOverlayID, ANM2_ROOT}, self->time);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (self->settings->previewIsRootTransform)
 | 
					        if (self->settings->previewIsRootTransform)
 | 
				
			||||||
            rootModel = quad_parent_model_get(root.position, vec2(0.0f), root.rotation, PERCENT_TO_UNIT(root.scale));
 | 
					            rootModel = canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (auto [i, id] : self->anm2->layerMap)
 | 
							for (auto [i, id] : self->anm2->layerMap)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -225,22 +225,21 @@ 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);
 | 
					            Texture& texture = self->anm2->spritesheets[self->anm2->layers[id].spritesheetID].texture;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            if (!texture || texture->isInvalid)
 | 
					            if (texture.isInvalid) continue;
 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            vec2 uvMin = frame.crop / vec2(texture->size);
 | 
					            vec2 uvMin = frame.crop / vec2(texture.size);
 | 
				
			||||||
            vec2 uvMax = (frame.crop + frame.size) / vec2(texture->size);
 | 
					            vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size);
 | 
				
			||||||
            f32 vertices[] = UV_VERTICES(uvMin, uvMax);
 | 
					            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 = canvas_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation);
 | 
				
			||||||
            mat4 layerTransform = transform * (rootModel * model);
 | 
					            mat4 layerTransform = transform * (rootModel * model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            vec4 tint = frame.tintRGBA;
 | 
					            vec4 tint = frame.tintRGBA;
 | 
				
			||||||
            tint.a *= U8_TO_FLOAT(self->settings->previewOverlayTransparency);
 | 
					            tint.a *= U8_TO_FLOAT(self->settings->previewOverlayTransparency);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            canvas_texture_draw(&self->canvas, shaderTexture, texture->id, layerTransform, vertices, tint, frame.offsetRGB);
 | 
					            canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, tint, frame.offsetRGB);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,5 @@
 | 
				
			|||||||
#include "resources.h"
 | 
					#include "resources.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void resources_texture_init(Resources* self, const std::string& path, s32 id)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	Texture texture;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (map_find(self->textures, id))
 | 
					 | 
				
			||||||
		texture_free(&self->textures[id]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	texture_from_path_init(&texture, path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    self->textures[id] = texture;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void resources_init(Resources* self)
 | 
					void resources_init(Resources* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    texture_from_encoded_data_init(&self->atlas, TEXTURE_ATLAS_SIZE, TEXTURE_CHANNELS, (u8*)TEXTURE_ATLAS, TEXTURE_ATLAS_LENGTH);
 | 
					    texture_from_encoded_data_init(&self->atlas, TEXTURE_ATLAS_SIZE, TEXTURE_CHANNELS, (u8*)TEXTURE_ATLAS, TEXTURE_ATLAS_LENGTH);
 | 
				
			||||||
@@ -22,18 +10,8 @@ void resources_init(Resources* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void resources_free(Resources* self)
 | 
					void resources_free(Resources* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    resources_textures_free(self);
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    for (auto& shader : self->shaders)
 | 
					    for (auto& shader : self->shaders)
 | 
				
			||||||
        shader_free(&shader);
 | 
					        shader_free(&shader);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    texture_free(&self->atlas);
 | 
					    texture_free(&self->atlas);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
void resources_textures_free(Resources* self)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    for (auto& [id, texture] : self->textures) 
 | 
					 | 
				
			||||||
        texture_free(&self->textures[id]);
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    log_info(RESOURCES_TEXTURES_FREE_INFO);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -10,10 +10,7 @@ struct Resources
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    GLuint shaders[SHADER_COUNT];
 | 
					    GLuint shaders[SHADER_COUNT];
 | 
				
			||||||
    Texture atlas;
 | 
					    Texture atlas;
 | 
				
			||||||
    std::map<s32, Texture> textures;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void resources_init(Resources* self);
 | 
					void resources_init(Resources* self);
 | 
				
			||||||
void resources_texture_init(Resources* self, const std::string& path, s32 id);
 | 
					 | 
				
			||||||
void resources_free(Resources* self);
 | 
					void resources_free(Resources* self);
 | 
				
			||||||
void resources_textures_free(Resources* self);
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
#include "snapshots.h"
 | 
					#include "snapshots.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _snapshot_stack_push(SnapshotStack* stack, const Snapshot* snapshot)
 | 
					static void _snapshot_stack_push(SnapshotStack* stack, Snapshot* snapshot)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (stack->top >= SNAPSHOT_STACK_MAX)
 | 
					    if (stack->top >= SNAPSHOT_STACK_MAX)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -11,20 +11,29 @@ static void _snapshot_stack_push(SnapshotStack* stack, const Snapshot* snapshot)
 | 
				
			|||||||
    stack->snapshots[stack->top++] = *snapshot;
 | 
					    stack->snapshots[stack->top++] = *snapshot;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool _snapshot_stack_pop(SnapshotStack* stack, Snapshot* snapshot)
 | 
					static Snapshot* _snapshot_stack_pop(SnapshotStack* stack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (stack->top == 0) return false;
 | 
					    if (stack->top == 0) return nullptr;
 | 
				
			||||||
 | 
					    return &stack->snapshots[--stack->top];
 | 
				
			||||||
    *snapshot = stack->snapshots[--stack->top];
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _snapshot_set(Snapshots* self, const Snapshot& snapshot)
 | 
					static void _snapshot_set(Snapshots* self, Snapshot* snapshot)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    *self->anm2 = snapshot.anm2;
 | 
					    if (!snapshot) return;
 | 
				
			||||||
    *self->reference = snapshot.reference;
 | 
					
 | 
				
			||||||
    self->preview->time = snapshot.time;
 | 
					    *self->anm2 = snapshot->anm2;
 | 
				
			||||||
    self->action = snapshot.action;
 | 
					    *self->reference = snapshot->reference;
 | 
				
			||||||
 | 
					    self->preview->time = snapshot->time;
 | 
				
			||||||
 | 
					    self->action = snapshot->action;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    anm2_spritesheet_texture_pixels_upload(self->anm2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Snapshot snapshot_get(Snapshots* self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Snapshot snapshot = {*self->anm2, *self->reference, self->preview->time, self->action};
 | 
				
			||||||
 | 
					    anm2_spritesheet_texture_pixels_download(&snapshot.anm2);
 | 
				
			||||||
 | 
					    return snapshot;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview)
 | 
					void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview)
 | 
				
			||||||
@@ -41,18 +50,17 @@ void snapshots_reset(Snapshots* self)
 | 
				
			|||||||
    self->action.clear();
 | 
					    self->action.clear();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void snapshots_undo_push(Snapshots* self, const Snapshot* snapshot)
 | 
					void snapshots_undo_push(Snapshots* self, Snapshot* snapshot)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    _snapshot_stack_push(&self->undoStack, snapshot);
 | 
					 | 
				
			||||||
    self->redoStack.top = 0;
 | 
					    self->redoStack.top = 0;
 | 
				
			||||||
 | 
					    _snapshot_stack_push(&self->undoStack, snapshot);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void snapshots_undo(Snapshots* self)
 | 
					void snapshots_undo(Snapshots* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Snapshot snapshot;
 | 
					    if (Snapshot* snapshot = _snapshot_stack_pop(&self->undoStack))
 | 
				
			||||||
    if (_snapshot_stack_pop(&self->undoStack, &snapshot))
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Snapshot current = {*self->anm2, *self->reference, self->preview->time, self->action};
 | 
					        Snapshot current = snapshot_get(self);
 | 
				
			||||||
        _snapshot_stack_push(&self->redoStack, ¤t);
 | 
					        _snapshot_stack_push(&self->redoStack, ¤t);
 | 
				
			||||||
        _snapshot_set(self, snapshot);
 | 
					        _snapshot_set(self, snapshot);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -60,10 +68,9 @@ void snapshots_undo(Snapshots* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void snapshots_redo(Snapshots* self)
 | 
					void snapshots_redo(Snapshots* self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Snapshot snapshot;
 | 
					    if (Snapshot* snapshot = _snapshot_stack_pop(&self->redoStack))
 | 
				
			||||||
    if (_snapshot_stack_pop(&self->redoStack, &snapshot))
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Snapshot current = {*self->anm2, *self->reference, self->preview->time, self->action};
 | 
					        Snapshot current = snapshot_get(self);
 | 
				
			||||||
        _snapshot_stack_push(&self->undoStack, ¤t);
 | 
					        _snapshot_stack_push(&self->undoStack, ¤t);
 | 
				
			||||||
        _snapshot_set(self, snapshot);
 | 
					        _snapshot_set(self, snapshot);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
#include "preview.h"
 | 
					#include "preview.h"
 | 
				
			||||||
#include "texture.h"
 | 
					#include "texture.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SNAPSHOT_STACK_MAX 1000
 | 
					#define SNAPSHOT_STACK_MAX 100
 | 
				
			||||||
#define SNAPSHOT_ACTION "Action"
 | 
					#define SNAPSHOT_ACTION "Action"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Snapshot
 | 
					struct Snapshot
 | 
				
			||||||
@@ -33,8 +33,9 @@ struct Snapshots
 | 
				
			|||||||
    SnapshotStack redoStack;
 | 
					    SnapshotStack redoStack;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void snapshots_undo_push(Snapshots* self, const Snapshot* snapshot);
 | 
					void snapshots_undo_push(Snapshots* self, Snapshot* snapshot);
 | 
				
			||||||
void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview);
 | 
					void snapshots_init(Snapshots* self, Anm2* anm2, Anm2Reference* reference, Preview* preview);
 | 
				
			||||||
void snapshots_undo(Snapshots* self);
 | 
					void snapshots_undo(Snapshots* self);
 | 
				
			||||||
void snapshots_redo(Snapshots* self);
 | 
					void snapshots_redo(Snapshots* self);
 | 
				
			||||||
void snapshots_reset(Snapshots* self);
 | 
					void snapshots_reset(Snapshots* self);
 | 
				
			||||||
 | 
					Snapshot snapshot_get(Snapshots* self);
 | 
				
			||||||
@@ -99,13 +99,21 @@ void init(State* self)
 | 
				
			|||||||
	glDisable(GL_DEPTH_TEST);
 | 
						glDisable(GL_DEPTH_TEST);
 | 
				
			||||||
	glDisable(GL_LINE_SMOOTH);
 | 
						glDisable(GL_LINE_SMOOTH);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
						if (!self->argument.empty())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							anm2_deserialize(&self->anm2, self->argument);
 | 
				
			||||||
 | 
							window_title_from_path_set(self->window, self->argument);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							anm2_new(&self->anm2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resources_init(&self->resources);
 | 
						resources_init(&self->resources);
 | 
				
			||||||
	dialog_init(&self->dialog, self->window);
 | 
						dialog_init(&self->dialog, self->window);
 | 
				
			||||||
	clipboard_init(&self->clipboard, &self->anm2);
 | 
						clipboard_init(&self->clipboard, &self->anm2);
 | 
				
			||||||
	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);
 | 
						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);
 | 
				
			||||||
 | 
						snapshots_init(&self->snapshots, &self->anm2, &self->reference, &self->preview);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	imgui_init
 | 
						imgui_init
 | 
				
			||||||
	(
 | 
						(
 | 
				
			||||||
@@ -123,14 +131,6 @@ void init(State* self)
 | 
				
			|||||||
		self->window,
 | 
							self->window,
 | 
				
			||||||
		&self->glContext
 | 
							&self->glContext
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!self->argument.empty())
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		anm2_deserialize(&self->anm2, &self->resources, self->argument);
 | 
					 | 
				
			||||||
		window_title_from_path_set(self->window, self->argument);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		anm2_new(&self->anm2);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void loop(State* self)
 | 
					void loop(State* self)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static void _texture_gl_set(Texture* self, const u8* data)
 | 
					static void _texture_gl_set(Texture* self, const u8* data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	glGenTextures(1, &self->id);
 | 
						if (self->id == GL_ID_NONE) 
 | 
				
			||||||
 | 
							glGenTextures(1, &self->id);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	glBindTexture(GL_TEXTURE_2D, self->id);
 | 
						glBindTexture(GL_TEXTURE_2D, self->id);
 | 
				
			||||||
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
 | 
						glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
 | 
				
			||||||
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 | 
						glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 | 
				
			||||||
@@ -40,7 +42,6 @@ std::vector<u8> texture_download(const Texture* self)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
bool texture_from_path_init(Texture* self, const std::string& path)
 | 
					bool texture_from_path_init(Texture* self, const std::string& path)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	*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)
 | 
				
			||||||
@@ -49,11 +50,12 @@ bool texture_from_path_init(Texture* self, const std::string& path)
 | 
				
			|||||||
		if (!data)
 | 
							if (!data)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			log_error(std::format(TEXTURE_INIT_ERROR, path));
 | 
								log_error(std::format(TEXTURE_INIT_ERROR, path));
 | 
				
			||||||
			self->isInvalid = true;
 | 
					 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						self->isInvalid = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log_info(std::format(TEXTURE_INIT_INFO, path));
 | 
						log_info(std::format(TEXTURE_INIT_INFO, path));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_texture_gl_set(self, data);
 | 
						_texture_gl_set(self, data);
 | 
				
			||||||
@@ -133,33 +135,3 @@ bool texture_pixel_set(Texture* self, ivec2 position, vec4 color)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
Texture texture_copy(Texture* self)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	Texture copy = *self;
 | 
					 | 
				
			||||||
	_texture_gl_set(©, nullptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    GLuint fboSource;
 | 
					 | 
				
			||||||
	GLuint fboDestination;
 | 
					 | 
				
			||||||
    glGenFramebuffers(1, &fboSource);
 | 
					 | 
				
			||||||
    glGenFramebuffers(1, &fboDestination);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    glBindFramebuffer(GL_READ_FRAMEBUFFER, fboSource);
 | 
					 | 
				
			||||||
    glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->id, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboDestination);
 | 
					 | 
				
			||||||
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copy.id, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    glBlitFramebuffer
 | 
					 | 
				
			||||||
	(
 | 
					 | 
				
			||||||
        0, 0, self->size.x, self->size.y,
 | 
					 | 
				
			||||||
        0, 0, self->size.x, self->size.y,
 | 
					 | 
				
			||||||
        GL_COLOR_BUFFER_BIT, GL_NEAREST
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
 | 
					 | 
				
			||||||
    glDeleteFramebuffers(1, &fboSource);
 | 
					 | 
				
			||||||
    glDeleteFramebuffers(1, &fboDestination);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return copy;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -10,10 +10,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct Texture
 | 
					struct Texture
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GLuint id = 0;
 | 
					    GLuint id = GL_ID_NONE;
 | 
				
			||||||
    ivec2 size = {0, 0};
 | 
					    ivec2 size{};
 | 
				
			||||||
    s32 channels = -1;
 | 
					    s32 channels = TEXTURE_CHANNELS;
 | 
				
			||||||
    bool isInvalid = false;
 | 
					    bool isInvalid = true;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool texture_from_encoded_data_init(Texture* self, ivec2 size, s32 channels, const u8* data, u32 length);
 | 
					bool texture_from_encoded_data_init(Texture* self, ivec2 size, s32 channels, const u8* data, u32 length);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user