Redid layer system, added layer/null windows, more quality of life, polish

This commit is contained in:
2025-09-12 18:06:30 -04:00
parent 6deaaea374
commit f8fb3df8d4
16 changed files with 658 additions and 312 deletions

View File

@@ -233,8 +233,10 @@ static inline s32 map_next_id_get(const std::map<s32, T>& map)
return id;
}
template<typename T>
static inline T* map_find(std::map<s32, T>& map, s32 id)
template <typename Map>
static inline auto map_find(Map& map, typename Map::key_type id)
-> typename Map::mapped_type*
{
if (auto it = map.find(id); it != map.end())
return &it->second;
@@ -289,6 +291,22 @@ static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& v
map[insertIndex] = value;
}
template <typename T>
void vector_value_erase(std::vector<T>& v, const T& value)
{
v.erase(std::remove(v.begin(), v.end(), value), v.end());
}
template <typename T>
void vector_value_swap(std::vector<T>& v, const T& a, const T& b)
{
for (auto& element : v)
{
if (element == a) element = b;
else if (element == b) element = a;
}
}
static inline mat4 quad_model_get(vec2 size = {}, vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {})
{
vec2 scaleAbsolute = glm::abs(scale);

View File

@@ -64,7 +64,7 @@ void anm2_frame_serialize(Anm2Frame* frame, Anm2Type type, XMLDocument* document
}
}
void anm2_animation_serialize(Anm2* self, Anm2Animation* animation, XMLDocument* document = nullptr, XMLElement* addElement = nullptr, std::string* string = nullptr)
void anm2_animation_serialize(Anm2Animation* animation, XMLDocument* document = nullptr, XMLElement* addElement = nullptr, std::string* string = nullptr)
{
XMLDocument localDocument;
XMLDocument* useDocument = document ? document : &localDocument;
@@ -86,7 +86,7 @@ void anm2_animation_serialize(Anm2* self, Anm2Animation* animation, XMLDocument*
// LayerAnimations
XMLElement* layersElement = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS]);
for (auto& [i, id] : self->layerMap)
for (auto& id : animation->layerOrder)
{
// LayerAnimation
Anm2Item& layerAnimation = animation->layerAnimations[id];
@@ -231,7 +231,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION], self->animations[self->defaultAnimationID].name.c_str()); // DefaultAnimation
for (auto& [id, animation] : self->animations)
anm2_animation_serialize(self, &animation, &document, animationsElement);
anm2_animation_serialize(&animation, &document, animationsElement);
animatedActorElement->InsertEndChild(animationsElement);
@@ -248,7 +248,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
return true;
}
static void _anm2_frame_deserialize(Anm2* self, Anm2Frame* frame, const XMLElement* element)
static void _anm2_frame_deserialize(Anm2Frame* frame, const XMLElement* element)
{
for (const XMLAttribute* attribute = element->FirstAttribute(); attribute; attribute = attribute->Next())
{
@@ -276,18 +276,13 @@ static void _anm2_frame_deserialize(Anm2* self, Anm2Frame* frame, const XMLEleme
case ANM2_ATTRIBUTE_INTERPOLATED: frame->isInterpolated = string_to_bool(attribute->Value()); break; // Interpolated
case ANM2_ATTRIBUTE_AT_FRAME: frame->atFrame = std::atoi(attribute->Value()); break; // AtFrame
case ANM2_ATTRIBUTE_DELAY: frame->delay = std::atoi(attribute->Value()); break; // Delay
case ANM2_ATTRIBUTE_EVENT_ID: // EventID
{
s32 eventID = std::atoi(attribute->Value());
frame->eventID = map_find(self->events, eventID) ? eventID : ID_NONE;
break;
}
case ANM2_ATTRIBUTE_EVENT_ID: frame->eventID = std::atoi(attribute->Value()); break; // EventID
default: break;
}
}
}
static void _anm2_animation_deserialize(Anm2* self, Anm2Animation* animation, const XMLElement* element)
static void _anm2_animation_deserialize(Anm2Animation* animation, const XMLElement* element)
{
auto frames_deserialize = [&](const XMLElement* itemElement, Anm2Item* item)
{
@@ -297,7 +292,7 @@ static void _anm2_animation_deserialize(Anm2* self, Anm2Animation* animation, co
const XMLElement* frame = itemElement->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]);
frame; frame = frame->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME])
)
_anm2_frame_deserialize(self, &item->frames.emplace_back(Anm2Frame()), frame);
_anm2_frame_deserialize(&item->frames.emplace_back(Anm2Frame()), frame);
};
s32 id{};
@@ -320,8 +315,6 @@ static void _anm2_animation_deserialize(Anm2* self, Anm2Animation* animation, co
// LayerAnimations
if (const XMLElement* layerAnimations = element->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS]))
{
s32 layerMapIndex = 0;
// LayerAnimation
for
(
@@ -331,7 +324,6 @@ static void _anm2_animation_deserialize(Anm2* self, Anm2Animation* animation, co
{
Anm2Item layerAnimationItem;
for (const XMLAttribute* attribute = layerAnimation->FirstAttribute(); attribute; attribute = attribute->Next())
{
switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name()))
@@ -344,9 +336,7 @@ static void _anm2_animation_deserialize(Anm2* self, Anm2Animation* animation, co
frames_deserialize(layerAnimation, &layerAnimationItem);
animation->layerAnimations[id] = layerAnimationItem;
self->layerMap[id] = layerMapIndex;
layerMapIndex++;
animation->layerOrder.push_back(id);
}
}
@@ -386,7 +376,7 @@ static void _anm2_animation_deserialize(Anm2* self, Anm2Animation* animation, co
const XMLElement* trigger = triggers->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER]);
trigger; trigger = trigger->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER])
)
_anm2_frame_deserialize(self, &animation->triggers.frames.emplace_back(Anm2Frame()), trigger);
_anm2_frame_deserialize(&animation->triggers.frames.emplace_back(Anm2Frame()), trigger);
}
}
@@ -568,7 +558,7 @@ bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures)
const XMLElement* animation = animations->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]);
animation; animation = animation->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION])
)
_anm2_animation_deserialize(self, &self->animations[map_next_id_get(self->animations)], animation);
_anm2_animation_deserialize(&self->animations[map_next_id_get(self->animations)], animation);
}
for (auto& [id, animation] : self->animations)
@@ -584,99 +574,68 @@ bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures)
return true;
}
void anm2_layer_add(Anm2* self)
void anm2_animation_layer_animation_add(Anm2Animation* animation, s32 id)
{
animation->layerAnimations[id] = Anm2Item{};
animation->layerOrder.push_back(id);
}
void anm2_animation_layer_animation_remove(Anm2Animation* animation, s32 id)
{
animation->layerAnimations.erase(id);
vector_value_erase(animation->layerOrder, id);
}
void anm2_animation_null_animation_add(Anm2Animation* animation, s32 id)
{
animation->nullAnimations[id] = Anm2Item{};
}
void anm2_animation_null_animation_remove(Anm2Animation* animation, s32 id)
{
animation->nullAnimations.erase(id);
}
s32 anm2_layer_add(Anm2* self)
{
s32 id = map_next_id_get(self->layers);
self->layers[id] = Anm2Layer{};
self->layerMap[self->layers.size() - 1] = id;
for (auto& [_, animation] : self->animations)
animation.layerAnimations[id] = Anm2Item{};
return id;
}
void anm2_layer_remove(Anm2* self, s32 id)
{
if (!self->layers.contains(id)) return;
self->layers.erase(id);
for (auto it = self->layerMap.begin(); it != self->layerMap.end(); ++it)
{
if (it->second == id)
{
self->layerMap.erase(it);
break;
}
}
std::map<s32, s32> newLayerMap;
s32 newIndex = 0;
for (const auto& [_, layerID] : self->layerMap)
newLayerMap[newIndex++] = layerID;
self->layerMap = std::move(newLayerMap);
for (auto& [_, animation] : self->animations)
animation.layerAnimations.erase(id);
anm2_animation_layer_animation_remove(&animation, id);
}
void anm2_null_add(Anm2* self)
s32 anm2_null_add(Anm2* self)
{
s32 id = map_next_id_get(self->nulls);
self->nulls[id] = Anm2Null{};
for (auto& [_, animation] : self->animations)
animation.nullAnimations[id] = Anm2Item{};
return id;
}
void anm2_null_remove(Anm2* self, s32 id)
{
if (!self->nulls.contains(id))
return;
if (!self->nulls.contains(id)) return;
self->nulls.erase(id);
std::map<s32, Anm2Null> newNulls;
s32 newID = 0;
for (const auto& [_, null] : self->nulls)
newNulls[newID++] = null;
self->nulls = std::move(newNulls);
for (auto& [_, animation] : self->animations)
{
if (animation.nullAnimations.contains(id))
animation.nullAnimations.erase(id);
std::map<s32, Anm2Item> newNullAnims;
s32 newAnimID = 0;
for (const auto& [_, nullAnim] : animation.nullAnimations)
newNullAnims[newAnimID++] = nullAnim;
animation.nullAnimations = std::move(newNullAnims);
}
anm2_animation_null_animation_remove(&animation, id);
}
s32 anm2_animation_add(Anm2* self, bool isAddRootFrame, Anm2Animation* animation, s32 id)
s32 anm2_animation_add(Anm2* self, Anm2Animation* animation, s32 id)
{
s32 addID = map_next_id_get(self->animations);
Anm2Animation localAnimation;
Anm2Animation* addAnimation = animation ? animation : &localAnimation;
for (auto& [layerID, layer] : self->layers)
if (!map_find(addAnimation->layerAnimations, layerID))
addAnimation->layerAnimations[layerID] = Anm2Item{};
for (auto& [nullID, null] : self->nulls)
if (!map_find(addAnimation->nullAnimations, nullID))
addAnimation->nullAnimations[nullID] = Anm2Item{};
if (isAddRootFrame)
addAnimation->rootAnimation.frames.push_back(Anm2Frame{});
if (!animation) addAnimation->rootAnimation.frames.push_back(Anm2Frame{});
if (id != ID_NONE)
{
@@ -685,6 +644,7 @@ s32 anm2_animation_add(Anm2* self, bool isAddRootFrame, Anm2Animation* animation
}
else
self->animations[addID] = *addAnimation;
return addID;
}
@@ -1153,7 +1113,7 @@ void anm2_spritesheet_texture_pixels_download(Anm2* self)
if (texture.id != GL_ID_NONE && !texture.isInvalid)
{
size_t bufferSize = (size_t)texture.size.x * (size_t)texture.size.y * (size_t)texture.channels;
size_t bufferSize = (size_t)texture.size.x * (size_t)texture.size.y * TEXTURE_CHANNELS;
spritesheet.pixels.resize(bufferSize);
glBindTexture(GL_TEXTURE_2D, texture.id);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, spritesheet.pixels.data());
@@ -1212,7 +1172,7 @@ vec4 anm2_animation_rect_get(Anm2* self, Anm2Reference* reference, bool isRootTr
return {minX, minY, maxX - minX, maxY - minY};
}
bool anm2_animation_deserialize_from_xml(Anm2* self, Anm2Animation* animation, const std::string& xml)
bool anm2_animation_deserialize_from_xml(Anm2Animation* animation, const std::string& xml)
{
XMLDocument document;
@@ -1225,14 +1185,15 @@ bool anm2_animation_deserialize_from_xml(Anm2* self, Anm2Animation* animation, c
if (document.Parse(xml.c_str()) != XML_SUCCESS) return animation_deserialize_error();
const XMLElement* element = document.RootElement();
if (element && std::string(element->Name()) != std::string(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]))
if (!element) return animation_deserialize_error();
if (std::string(element->Name()) != std::string(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]))
return animation_deserialize_error();
_anm2_animation_deserialize(self, animation, element);
_anm2_animation_deserialize(animation, element);
return true;
}
bool anm2_frame_deserialize_from_xml(Anm2* self, Anm2Frame* frame, const std::string& xml)
bool anm2_frame_deserialize_from_xml(Anm2Frame* frame, const std::string& xml)
{
XMLDocument document;
@@ -1245,16 +1206,15 @@ bool anm2_frame_deserialize_from_xml(Anm2* self, Anm2Frame* frame, const std::st
if (document.Parse(xml.c_str()) != XML_SUCCESS) return frame_deserialize_error();
const XMLElement* element = document.RootElement();
if (!element) return frame_deserialize_error();
if
(
element &&
(
std::string(element->Name()) == std::string(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]) ||
std::string(element->Name()) == std::string(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER])
)
std::string(element->Name()) != std::string(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]) &&
std::string(element->Name()) != std::string(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER])
)
return frame_deserialize_error();
_anm2_frame_deserialize(self, frame, element);
_anm2_frame_deserialize(frame, element);
return true;
}

View File

@@ -142,7 +142,7 @@ struct Anm2Spritesheet
struct Anm2Layer
{
std::string name = "New Layer";
s32 spritesheetID = ID_NONE;
s32 spritesheetID{};
};
struct Anm2Null
@@ -199,9 +199,9 @@ struct Anm2Animation
s32 frameNum = ANM2_FRAME_NUM_MIN;
std::string name = "New Animation";
bool isLoop = true;
bool isShowUnused = true;
Anm2Item rootAnimation;
std::map<s32, Anm2Item> layerAnimations;
std::unordered_map<s32, Anm2Item> layerAnimations;
std::vector<s32> layerOrder;
std::map<s32, Anm2Item> nullAnimations;
Anm2Item triggers;
};
@@ -216,7 +216,6 @@ struct Anm2
std::map<s32, Anm2Null> nulls;
std::map<s32, Anm2Event> events;
std::map<s32, Anm2Animation> animations;
std::map<s32, s32> layerMap; // index, id
s32 defaultAnimationID = ID_NONE;
s32 fps = ANM2_FPS_DEFAULT;
s32 version{};
@@ -253,38 +252,42 @@ enum OnionskinDrawOrder
ONIONSKIN_ABOVE
};
void anm2_layer_add(Anm2* self);
void anm2_layer_remove(Anm2* self, s32 id);
void anm2_null_add(Anm2* self);
void anm2_null_remove(Anm2* self, s32 id);
bool anm2_serialize(Anm2* self, const std::string& path);
bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures = true);
void anm2_new(Anm2* self);
void anm2_free(Anm2* self);
void anm2_created_on_set(Anm2* self);
s32 anm2_animation_add(Anm2* self, bool isAddRootFrame = true, Anm2Animation* animation = nullptr, s32 id = ID_NONE);
void anm2_animation_remove(Anm2* self, s32 id);
Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference);
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference);
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference);
s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time);
Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference);
void anm2_frame_remove(Anm2* self, Anm2Reference* reference);
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time);
void anm2_reference_clear(Anm2Reference* self);
void anm2_reference_item_clear(Anm2Reference* self);
void anm2_reference_frame_clear(Anm2Reference* self);
Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference);
Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference);
bool anm2_animation_deserialize_from_xml(Anm2Animation* animation, const std::string& xml);
bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures = true);
bool anm2_frame_deserialize_from_xml(Anm2Frame* frame, const std::string& xml);
bool anm2_serialize(Anm2* self, const std::string& path);
s32 anm2_animation_add(Anm2* self, Anm2Animation* animation = nullptr, s32 id = ID_NONE);
s32 anm2_animation_length_get(Anm2Animation* self);
s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time);
s32 anm2_layer_add(Anm2* self);
s32 anm2_null_add(Anm2* self);
vec4 anm2_animation_rect_get(Anm2* anm2, Anm2Reference* reference, bool isRootTransform);
void anm2_animation_layer_animation_add(Anm2Animation* animation, s32 id);
void anm2_animation_layer_animation_remove(Anm2Animation* animation, s32 id);
void anm2_animation_length_set(Anm2Animation* self);
void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& mergeIDs, Anm2MergeType type);
void anm2_animation_null_animation_add(Anm2Animation* animation, s32 id);
void anm2_animation_null_animation_remove(Anm2Animation* animation, s32 id);
void anm2_animation_remove(Anm2* self, s32 id);
void anm2_animation_serialize(Anm2Animation* animation, XMLDocument* document, XMLElement* addElement, std::string* string);
void anm2_created_on_set(Anm2* self);
void anm2_frame_bake(Anm2* self, Anm2Reference* reference, s32 interval, bool isRoundScale, bool isRoundRotation);
void anm2_item_frame_set(Anm2* self, Anm2Reference* reference, const Anm2FrameChange& change, Anm2ChangeType type, s32 start, s32 count);
void anm2_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_spritesheet_texture_pixels_upload(Anm2* self);
void anm2_spritesheet_texture_pixels_download(Anm2* self);
vec4 anm2_animation_rect_get(Anm2* anm2, Anm2Reference* reference, bool isRootTransform);
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time);
void anm2_frame_remove(Anm2* self, Anm2Reference* reference);
void anm2_frame_serialize(Anm2Frame* frame, Anm2Type type, XMLDocument* document, XMLElement* addElement, std::string* string);
void anm2_animation_serialize(Anm2* self, Anm2Animation* animation, XMLDocument* document, XMLElement* addElement, std::string* string);
bool anm2_frame_deserialize_from_xml(Anm2* self, Anm2Frame* frame, const std::string& xml);
bool anm2_animation_deserialize_from_xml(Anm2* self, Anm2Animation* frame, const std::string& xml);
void anm2_free(Anm2* self);
void anm2_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPosition, vec2 size, vec2 pivot, s32 columns, s32 count, s32 delay);
void anm2_item_frame_set(Anm2* self, Anm2Reference* reference, const Anm2FrameChange& change, Anm2ChangeType type, s32 start, s32 count);
void anm2_layer_remove(Anm2* self, s32 id);
void anm2_new(Anm2* self);
void anm2_null_remove(Anm2* self, s32 id);
void anm2_reference_clear(Anm2Reference* self);
void anm2_reference_frame_clear(Anm2Reference* self);
void anm2_reference_item_clear(Anm2Reference* self);
void anm2_scale(Anm2* self, f32 scale);
void anm2_spritesheet_texture_pixels_download(Anm2* self);
void anm2_spritesheet_texture_pixels_upload(Anm2* self);

View File

@@ -197,6 +197,7 @@ void canvas_rect_draw(Canvas* self, const GLuint& shader, const mat4& transform,
glUseProgram(0);
}
void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color)
{
vec4 originNDC = transform * vec4(0.0f, 0.0f, 0.0f, 1.0f);

View File

@@ -28,7 +28,7 @@ void clipboard_copy(Clipboard* self)
if (!id) break;
Anm2Animation* animation = map_find(self->anm2->animations, *id);
if (!animation) break;
anm2_animation_serialize(self->anm2, animation, nullptr, nullptr, &clipboardText);
anm2_animation_serialize(animation, nullptr, nullptr, &clipboardText);
clipboard_text_set();
break;
}
@@ -63,7 +63,7 @@ void clipboard_cut(Clipboard* self)
}
}
void clipboard_paste(Clipboard* self)
bool clipboard_paste(Clipboard* self)
{
auto clipboard_string = [&]()
{
@@ -80,8 +80,9 @@ void clipboard_paste(Clipboard* self)
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
if (!reference) break;
Anm2Frame frame;
if (anm2_frame_deserialize_from_xml(self->anm2, &frame, clipboard_string()))
if (anm2_frame_deserialize_from_xml(&frame, clipboard_string()))
anm2_frame_add(self->anm2, &frame, reference);
else return false;
break;
}
case CLIPBOARD_ANIMATION:
@@ -89,13 +90,16 @@ void clipboard_paste(Clipboard* self)
s32* id = std::get_if<s32>(&self->location);
if (!id) break;
Anm2Animation animation;
if (anm2_animation_deserialize_from_xml(self->anm2, &animation, clipboard_string()))
anm2_animation_add(self->anm2, false, &animation, *id);
if (anm2_animation_deserialize_from_xml(&animation, clipboard_string()))
anm2_animation_add(self->anm2, &animation, *id);
else return false;
break;
}
default:
break;
}
return true;
}
void clipboard_init(Clipboard* self, Anm2* anm2)

View File

@@ -23,5 +23,5 @@ struct Clipboard
bool clipboard_is_value(void);
void clipboard_copy(Clipboard* self);
void clipboard_cut(Clipboard* self);
void clipboard_paste(Clipboard* self);
bool clipboard_paste(Clipboard* self);
void clipboard_init(Clipboard* self, Anm2* anm2);

View File

@@ -62,6 +62,8 @@ static void _imgui_spritesheet_add(Imgui* self, const std::string& path)
return;
}
imgui_snapshot(self, IMGUI_ACTION_ADD_SPRITESHEET);
std::filesystem::path workingPath = std::filesystem::current_path();
std::string spritesheetPath = path;
std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->path);
@@ -79,6 +81,16 @@ static bool _imgui_is_window_hovered(void)
return ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
}
static bool _imgui_is_window_hovered_and_click(void)
{
return _imgui_is_window_hovered() && ImGui::IsMouseClicked(0);
}
static bool _imgui_is_window_hovered_and_click_no_anm2_path(Imgui* self)
{
return _imgui_is_window_hovered_and_click() && self->anm2->path.empty();
}
static bool _imgui_is_no_click_on_item(void)
{
return ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered();
@@ -141,7 +153,7 @@ static void _imgui_item_pre(const ImguiItem& self, ImguiItemType type)
case IMGUI_WINDOW:
case IMGUI_DOCKSPACE:
case IMGUI_CHILD:
case IMGUI_OPTION_POPUP:
case IMGUI_CONFIRM_POPUP:
break;
default:
ImGui::BeginDisabled(self.isDisabled);
@@ -264,7 +276,7 @@ static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType
case IMGUI_WINDOW:
case IMGUI_DOCKSPACE:
case IMGUI_CHILD:
case IMGUI_OPTION_POPUP:
case IMGUI_CONFIRM_POPUP:
break;
default:
ImGui::EndDisabled();
@@ -569,7 +581,7 @@ IMGUI_ITEM_ATLAS_FUNCTION(_imgui_atlas_selectable, _imgui_selectable(self, imgui
IMGUI_ITEM_ATLAS_VALUE_FUNCTION(_imgui_atlas_selectable_input_int, s32, _imgui_selectable_input_int(self, imgui, value));
IMGUI_ITEM_ATLAS_VALUE_FUNCTION(_imgui_atlas_selectable_input_text, std::string, _imgui_selectable_input_text(self, imgui, value));
static bool _imgui_option_popup(ImguiItem self, Imgui* imgui, ImguiPopupState* state = nullptr)
static bool _imgui_confirm_popup(ImguiItem self, Imgui* imgui, ImguiPopupState* state = nullptr, bool isOnlyConfirm = false)
{
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
@@ -582,21 +594,24 @@ static bool _imgui_option_popup(ImguiItem self, Imgui* imgui, ImguiPopupState* s
ImGui::Text(self.text_get());
ImGui::Separator();
if (_imgui_button(IMGUI_POPUP_OK, imgui))
if (_imgui_button(IMGUI_POPUP_OK.copy({.rowCount = isOnlyConfirm ? 1 : IMGUI_CONFIRM_POPUP_ROW_COUNT}), imgui))
{
imgui_close_current_popup(imgui);
imgui_end_popup(imgui);
if (state) *state = IMGUI_POPUP_STATE_CONFIRM;
if (state) *state = isOnlyConfirm ? IMGUI_POPUP_STATE_CANCEL : IMGUI_POPUP_STATE_CONFIRM;
return true;
}
ImGui::SameLine();
if (!isOnlyConfirm)
{
if (_imgui_button(IMGUI_POPUP_CANCEL, imgui))
{
imgui_close_current_popup(imgui);
if (state) *state = IMGUI_POPUP_STATE_CANCEL;
}
}
imgui_end_popup(imgui);
}
@@ -604,6 +619,13 @@ static bool _imgui_option_popup(ImguiItem self, Imgui* imgui, ImguiPopupState* s
return false;
}
static void _imgui_no_anm2_path_check(Imgui* self)
{
if (_imgui_is_window_hovered_and_click_no_anm2_path(self) && !imgui_is_any_popup_open())
imgui_open_popup(IMGUI_NO_ANM2_PATH_CONFIRMATION.label);
_imgui_confirm_popup(IMGUI_NO_ANM2_PATH_CONFIRMATION, self, nullptr, true);
}
static void _imgui_context_menu(Imgui* self)
{
if (!self->isContextualActionsEnabled) return;
@@ -650,6 +672,7 @@ static void _imgui_timeline(Imgui* self)
static s32& itemID = self->reference->itemID;
IMGUI_BEGIN_OR_RETURN(IMGUI_TIMELINE, self);
_imgui_no_anm2_path_check(self);
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
@@ -704,12 +727,7 @@ static void _imgui_timeline(Imgui* self)
localMousePos = ImVec2(mousePos.x - itemMin.x + scroll.x, mousePos.y - itemMin.y);
frameTime = (s32)(localMousePos.x / frameSize.x);
if (ImGui::IsMouseDown(0) && _imgui_is_window_hovered())
{
if (!isPlayheadDrag)
imgui_snapshot(self, IMGUI_ACTION_MOVE_PLAYHEAD);
isPlayheadDrag = true;
}
if (ImGui::IsMouseDown(0) && _imgui_is_window_hovered()) isPlayheadDrag = true;
if (isPlayheadDrag)
{
@@ -790,12 +808,12 @@ static void _imgui_timeline(Imgui* self)
std::function<void(Anm2Reference, s32&)> timeline_item_child = [&](Anm2Reference reference, s32& index)
{
Anm2Item* item = anm2_item_from_reference(self->anm2, &reference);
Anm2Type& type = reference.itemType;
if (!item) return;
if (!self->settings->timelineIsShowUnused && item->frames.empty() && (type == ANM2_LAYER || type == ANM2_NULL)) return;
ImVec2 buttonSize = ImVec2(ATLAS_SIZE_NORMAL) + (defaultFramePadding * ImVec2(2, 2));
Anm2Type& type = reference.itemType;
Anm2Layer* layer = nullptr;
Anm2Null* null = nullptr;
s32 buttonCount = type == ANM2_NULL ? 2 : 1;
@@ -824,13 +842,13 @@ static void _imgui_timeline(Imgui* self)
layer = &self->anm2->layers[reference.itemID];
if
(
_imgui_atlas_selectable_input_text(IMGUI_TIMELINE_ITEM_SELECTABLES[type]->copy
_imgui_atlas_selectable(IMGUI_TIMELINE_ITEM_SELECTABLES[type]->copy
({
.isSelected = isSelected,
.label = std::format(IMGUI_TIMELINE_ITEM_CHILD_FORMAT, reference.itemID, layer->name),
.id = index
.label = std::format(IMGUI_TIMELINE_ITEM_CHILD_FORMAT, reference.itemID, layer->name)
}),
self, layer->name)
self
)
)
*self->reference = reference;
break;
@@ -838,13 +856,12 @@ static void _imgui_timeline(Imgui* self)
null = &self->anm2->nulls[reference.itemID];
if
(
_imgui_atlas_selectable_input_text(IMGUI_TIMELINE_ITEM_SELECTABLES[type]->copy
_imgui_atlas_selectable(IMGUI_TIMELINE_ITEM_SELECTABLES[type]->copy
({
.isSelected = isSelected,
.label = std::format(IMGUI_TIMELINE_ITEM_CHILD_FORMAT, reference.itemID, null->name),
.id = index
.label = std::format(IMGUI_TIMELINE_ITEM_CHILD_FORMAT, reference.itemID, null->name)
}),
self, null->name)
self)
)
*self->reference = reference;
break;
@@ -878,13 +895,6 @@ static void _imgui_timeline(Imgui* self)
ImGui::EndDragDropTarget();
}
if (type == ANM2_LAYER)
{
ImGui::SameLine();
_imgui_atlas_selectable_input_int(IMGUI_TIMELINE_SPRITESHEET_ID.copy
({.label = std::format(IMGUI_SPRITESHEET_ID_FORMAT, layer->spritesheetID), .id = index}), self, layer->spritesheetID);
}
ImGui::SetCursorScreenPos({childPos.x + childSize.x - buttonAreaWidth, childPos.y + defaultWindowPadding.y});
if (type == ANM2_NULL)
@@ -916,7 +926,7 @@ static void _imgui_timeline(Imgui* self)
timeline_item_child({animationID, ANM2_ROOT}, index);
for (auto& [i, id] : std::ranges::reverse_view(self->anm2->layerMap))
for (auto& id : std::ranges::reverse_view(animation->layerOrder))
timeline_item_child({animationID, ANM2_LAYER, id}, index);
for (auto & [id, null] : animation->nullAnimations)
@@ -933,28 +943,12 @@ static void _imgui_timeline(Imgui* self)
switch (swapItemReference.itemType)
{
case ANM2_LAYER:
{
s32 indexA = INDEX_NONE;
s32 indexB = INDEX_NONE;
for (const auto& [index, id] : self->anm2->layerMap)
{
if (id == self->reference->itemID)
indexA = index;
else if (id == swapItemReference.itemID)
indexB = index;
}
if ((indexA != INDEX_NONE) && (indexB != INDEX_NONE))
std::swap(self->anm2->layerMap[indexA], self->anm2->layerMap[indexB]);
vector_value_swap(animation->layerOrder, self->reference->itemID, swapItemReference.itemID);
break;
}
case ANM2_NULL:
map_swap(self->anm2->nulls, self->reference->itemID, swapItemReference.itemID);
map_swap(animation->nullAnimations, self->reference->itemID, swapItemReference.itemID);
break;
default:
break;
default: break;
}
self->reference->itemID = swapItemReference.itemID;
@@ -966,7 +960,9 @@ static void _imgui_timeline(Imgui* self)
std::function<void(Anm2Reference, s32&)> timeline_item_frames = [&](Anm2Reference reference, s32& index)
{
Anm2Item* item = anm2_item_from_reference(self->anm2, &reference);
if (!item) return;
Anm2Type& type = reference.itemType;
if (!self->settings->timelineIsShowUnused && item->frames.empty() && (type == ANM2_LAYER || type == ANM2_NULL)) return;
ImGui::PushID(index);
@@ -1150,7 +1146,7 @@ static void _imgui_timeline(Imgui* self)
timeline_item_frames(Anm2Reference(animationID, ANM2_ROOT), index);
for (auto& [i, id] : std::ranges::reverse_view(self->anm2->layerMap))
for (auto& id : std::ranges::reverse_view(animation->layerOrder))
timeline_item_frames(Anm2Reference(animationID, ANM2_LAYER, id), index);
for (auto & [id, null] : animation->nullAnimations)
@@ -1171,7 +1167,15 @@ static void _imgui_timeline(Imgui* self)
timeline_frames_child();
ImGui::SetCursorPos(ImVec2());
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, defaultItemSpacing);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, defaultWindowPadding);
_imgui_begin_child(IMGUI_TIMELINE_ITEM_CHILD, self);
const ImguiItem& unusedItem = self->settings->timelineIsShowUnused ? IMGUI_TIMELINE_SHOW_UNUSED : IMGUI_TIMELINE_HIDE_UNUSED;
if (_imgui_atlas_button(unusedItem, self)) self->settings->timelineIsShowUnused = !self->settings->timelineIsShowUnused;
ImGui::PopStyleVar(2);
_imgui_end_child(); // IMGUI_TIMELINE_ITEM_CHILD
ImGui::SameLine();
timeline_header();
@@ -1185,12 +1189,91 @@ static void _imgui_timeline(Imgui* self)
Anm2Frame* frame = anm2_frame_from_reference(self->anm2, self->reference);
Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference);
_imgui_begin_child(IMGUI_TIMELINE_ITEM_FOOTER_CHILD, self);
_imgui_button(IMGUI_TIMELINE_ADD_ITEM, self);
if (imgui_begin_popup(IMGUI_TIMELINE_ADD_ITEM.popup, self))
if (imgui_begin_popup_modal(IMGUI_TIMELINE_ADD_ITEM.popup, self, IMGUI_TIMELINE_ADD_ITEM.popupSize))
{
if (_imgui_selectable(IMGUI_TIMELINE_ADD_ITEM_LAYER, self)) anm2_layer_add(self->anm2);
if (_imgui_selectable(IMGUI_TIMELINE_ADD_ITEM_NULL, self)) anm2_null_add(self->anm2);
static s32 selectedLayerID = ID_NONE;
static s32 selectedNullID = ID_NONE;
s32& type = self->settings->timelineAddItemType;
_imgui_begin_child(IMGUI_TIMELINE_ADD_ITEM_TYPE_CHILD, self);
_imgui_radio_button(IMGUI_TIMELINE_ADD_ITEM_LAYER, self, type);
_imgui_radio_button(IMGUI_TIMELINE_ADD_ITEM_NULL, self, type);
_imgui_end_child(); // IMGUI_TIMELINE_ADD_ITEM_TYPE_CHILD
_imgui_begin_child(IMGUI_TIMELINE_ADD_ITEM_ITEMS_CHILD, self);
switch (type)
{
case ANM2_LAYER:
default:
{
for (auto & [id, layer] : self->anm2->layers)
{
ImGui::PushID(id);
ImguiItem layerItem = IMGUI_LAYER.copy
({
.isSelected = selectedLayerID == id,
.label = std::format(IMGUI_LAYER_FORMAT, id, layer.name),
.id = id
});
if (_imgui_atlas_selectable(layerItem, self)) selectedLayerID = id;
ImGui::PopID();
};
break;
}
case ANM2_NULL:
{
for (auto & [id, null] : self->anm2->nulls)
{
ImGui::PushID(id);
ImguiItem nullItem = IMGUI_NULL.copy
({
.isSelected = selectedNullID == id,
.label = std::format(IMGUI_NULL_FORMAT, id, null.name),
.id = id
});
if (_imgui_atlas_selectable(nullItem, self)) selectedNullID = id;
ImGui::PopID();
};
break;
}
}
_imgui_end_child(); // IMGUI_TIMELINE_ADD_ITEM_ITEMS_CHILD
_imgui_begin_child(IMGUI_TIMELINE_ADD_ITEM_OPTIONS_CHILD, self);
if (self->anm2->layers.size() == 0) selectedLayerID = ID_NONE;
if (self->anm2->nulls.size() == 0) selectedNullID = ID_NONE;
bool isDisabled = type == ANM2_NONE ||
(type == ANM2_LAYER && selectedLayerID == ID_NONE) ||
(type == ANM2_NULL && selectedNullID == ID_NONE);
if (_imgui_button(IMGUI_TIMELINE_ADD_ITEM_ADD.copy({isDisabled}), self))
{
switch (type)
{
case ANM2_LAYER: anm2_animation_layer_animation_add(animation, selectedLayerID); break;
case ANM2_NULL: anm2_animation_null_animation_add(animation, selectedNullID); break;
default: break;
}
imgui_close_current_popup(self);
}
if (_imgui_button(IMGUI_POPUP_CANCEL, self)) imgui_close_current_popup(self);
_imgui_end_child(); // IMGUI_TIMELINE_ADD_ITEM_OPTIONS_CHILD
imgui_end_popup(self);
}
@@ -1198,14 +1281,9 @@ static void _imgui_timeline(Imgui* self)
{
switch (itemType)
{
case ANM2_LAYER:
anm2_layer_remove(self->anm2, itemID);
break;
case ANM2_NULL:
anm2_null_remove(self->anm2, itemID);
break;
default:
break;
case ANM2_LAYER: anm2_animation_layer_animation_remove(animation, itemID); break;
case ANM2_NULL: anm2_animation_null_animation_remove(animation, itemID); break;
default: break;
}
anm2_reference_item_clear(self->reference);
@@ -1260,8 +1338,7 @@ static void _imgui_timeline(Imgui* self)
imgui_close_current_popup(self);
}
if (_imgui_button(IMGUI_POPUP_CANCEL, self))
imgui_close_current_popup(self);
if (_imgui_button(IMGUI_POPUP_CANCEL, self)) imgui_close_current_popup(self);
_imgui_end_child(); //IMGUI_BAKE_CHILD)
@@ -1356,14 +1433,13 @@ static void _imgui_taskbar(Imgui* self)
if (self->isTryQuit) imgui_open_popup(IMGUI_EXIT_CONFIRMATION.label);
_imgui_option_popup(IMGUI_EXIT_CONFIRMATION, self, &exitConfirmState);
_imgui_confirm_popup(IMGUI_EXIT_CONFIRMATION, self, &exitConfirmState);
switch (exitConfirmState)
{
case IMGUI_POPUP_STATE_CLOSED: self->isTryQuit = false; break;
case IMGUI_POPUP_STATE_OPEN: self->isTryQuit = true; break;
case IMGUI_POPUP_STATE_CONFIRM: self->isQuit = true; break;
case IMGUI_POPUP_STATE_CANCEL: self->isTryQuit = false; break;
default: break;
}
_imgui_selectable(IMGUI_WIZARD.copy({}), self);
@@ -1564,6 +1640,10 @@ static void _imgui_taskbar(Imgui* self)
_imgui_input_text(IMGUI_RENDER_ANIMATION_FORMAT, self, format);
_imgui_combo(IMGUI_RENDER_ANIMATION_OUTPUT, self, &type);
_imgui_end_child(); // IMGUI_RENDER_ANIMATION_CHILD
_imgui_begin_child(IMGUI_RENDER_ANIMATION_FOOTER_CHILD, self);
if (_imgui_button(IMGUI_RENDER_ANIMATION_CONFIRM, self))
{
bool isRenderStart = true;
@@ -1609,7 +1689,7 @@ static void _imgui_taskbar(Imgui* self)
if (_imgui_button(IMGUI_POPUP_CANCEL, self))
imgui_close_current_popup(self);
_imgui_end_child(); //IMGUI_RENDER_ANIMATION_CHILD
_imgui_end_child(); // IMGUI_RENDER_ANIMATION_FOOTER_CHILD
imgui_end_popup(self);
}
@@ -1826,9 +1906,110 @@ static void _imgui_tools(Imgui* self)
_imgui_end(); // IMGUI_TOOLS
}
static void _imgui_layers(Imgui* self)
{
static s32 selectedLayerID = ID_NONE;
IMGUI_BEGIN_OR_RETURN(IMGUI_LAYERS, self);
_imgui_no_anm2_path_check(self);
ImVec2 size = ImGui::GetContentRegionAvail();
_imgui_begin_child(IMGUI_LAYERS_CHILD.copy({.size = {size.x, size.y - IMGUI_FOOTER_CHILD.size.y}}), self);
ImGui::SetScrollX(0.0f);
for (auto & [id, layer] : self->anm2->layers)
{
ImGui::PushID(id);
ImguiItem layerItem = IMGUI_LAYER.copy
({
.isSelected = selectedLayerID == id,
.label = std::format(IMGUI_LAYER_FORMAT, id, layer.name),
.size = {ImGui::GetContentRegionAvail().x - (IMGUI_LAYER_SPRITESHEET_ID.size.x * 2.0f), 0},
.id = id
});
if (_imgui_atlas_selectable_input_text(layerItem, self, layer.name)) selectedLayerID = id;
ImGui::SameLine();
ImguiItem spritesheetItem = IMGUI_LAYER_SPRITESHEET_ID.copy
({
.isSelected = selectedLayerID == id,
.label = std::format(IMGUI_LAYER_FORMAT, id, layer.name),
.id = id
});
_imgui_atlas_selectable_input_int(spritesheetItem, self, layer.spritesheetID);
ImGui::PopID();
};
_imgui_end_child(); // layersChild
_imgui_begin_child(IMGUI_FOOTER_CHILD, self);
if (_imgui_button(IMGUI_LAYER_ADD.copy({self->anm2->path.empty()}), self))
selectedLayerID = anm2_layer_add(self->anm2);
if (_imgui_button(IMGUI_LAYER_REMOVE.copy({selectedLayerID == ID_NONE}), self))
{
anm2_layer_remove(self->anm2, selectedLayerID);
selectedLayerID = ID_NONE;
}
_imgui_end_child(); // IMGUI_FOOTER_CHILD
_imgui_end(); // IMGUI_LAYERS
}
static void _imgui_nulls(Imgui* self)
{
static s32 selectedNullID = ID_NONE;
IMGUI_BEGIN_OR_RETURN(IMGUI_NULLS, self);
_imgui_no_anm2_path_check(self);
ImVec2 size = ImGui::GetContentRegionAvail();
_imgui_begin_child(IMGUI_NULLS_CHILD.copy({.size = {size.x, size.y - IMGUI_FOOTER_CHILD.size.y}}), self);
for (auto & [id, null] : self->anm2->nulls)
{
ImGui::PushID(id);
ImguiItem nullItem = IMGUI_NULL.copy
({
.isSelected = selectedNullID == id,
.label = std::format(IMGUI_NULL_FORMAT, id, null.name),
.id = id
});
if (_imgui_atlas_selectable_input_text(nullItem, self, null.name)) selectedNullID = id;
ImGui::PopID();
};
_imgui_end_child(); // nullsChild
_imgui_begin_child(IMGUI_FOOTER_CHILD, self);
if (_imgui_button(IMGUI_NULL_ADD.copy({self->anm2->path.empty()}), self))
selectedNullID = anm2_null_add(self->anm2);
if (_imgui_button(IMGUI_NULL_REMOVE.copy({selectedNullID == ID_NONE}), self))
{
anm2_null_remove(self->anm2, selectedNullID);
selectedNullID = ID_NONE;
}
_imgui_end_child(); // IMGUI_FOOTER_CHILD
_imgui_end(); // IMGUI_NULLS
}
static void _imgui_animations(Imgui* self)
{
IMGUI_BEGIN_OR_RETURN(IMGUI_ANIMATIONS, self);
_imgui_no_anm2_path_check(self);
ImVec2 size = ImGui::GetContentRegionAvail();
_imgui_begin_child(IMGUI_ANIMATIONS_CHILD.copy({.size = {size.x, size.y - IMGUI_FOOTER_CHILD.size.y}}), self);
@@ -1900,7 +2081,7 @@ static void _imgui_animations(Imgui* self)
}
if (_imgui_button(IMGUI_ANIMATION_DUPLICATE.copy({!animation}), self))
self->reference->animationID = anm2_animation_add(self->anm2, false, animation, self->reference->animationID);
self->reference->animationID = anm2_animation_add(self->anm2, animation, self->reference->animationID);
_imgui_button(IMGUI_ANIMATION_MERGE.copy({!animation}), self);
@@ -2035,6 +2216,8 @@ static void _imgui_events(Imgui* self)
static s32 selectedID = ID_NONE;
IMGUI_BEGIN_OR_RETURN(IMGUI_EVENTS, self);
_imgui_no_anm2_path_check(self);
ImVec2 windowSize = ImGui::GetContentRegionAvail();
_imgui_begin_child(IMGUI_EVENTS_CHILD.copy({.size = {windowSize.x, windowSize.y - IMGUI_FOOTER_CHILD.size.y}}), self);
@@ -2091,6 +2274,7 @@ static void _imgui_spritesheets(Imgui* self)
static s32 highlightedID = ID_NONE;
IMGUI_BEGIN_OR_RETURN(IMGUI_SPRITESHEETS, self);
_imgui_no_anm2_path_check(self);
ImVec2 windowSize = ImGui::GetContentRegionAvail();
@@ -2170,9 +2354,6 @@ static void _imgui_spritesheets(Imgui* 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)
{
std::filesystem::path workingPath = std::filesystem::current_path();
@@ -2182,6 +2363,8 @@ static void _imgui_spritesheets(Imgui* self)
self->anm2->spritesheets[id].texture = texture;
std::filesystem::current_path(workingPath);
}
imgui_log_push(self, IMGUI_LOG_RELOAD_SPRITESHEET);
}
if (_imgui_button(IMGUI_SPRITESHEETS_REPLACE.copy({highlightedID == ID_NONE}), self))
@@ -2488,6 +2671,21 @@ static void _imgui_spritesheet_editor(Imgui* self)
_imgui_begin_child(IMGUI_CANVAS_VIEW_CHILD, self);
_imgui_drag_float(IMGUI_CANVAS_ZOOM, self, zoom);
if (_imgui_button(IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW.copy({pan == vec2()}), self)) pan = vec2();
if (_imgui_button(IMGUI_SPRITESHEET_EDITOR_FIT.copy({self->editor->spritesheetID == ID_NONE}), self))
{
vec4 rect = {0, 0, self->anm2->spritesheets[self->editor->spritesheetID].texture.size.x,
self->anm2->spritesheets[self->editor->spritesheetID].texture.size.y};
if ((rect.z > 0 && rect.w > 0))
{
f32 scaleX = self->editor->canvas.size.x / rect.z;
f32 scaleY = self->editor->canvas.size.y / rect.w;
f32 fitScale = std::min(scaleX, scaleY);
zoom = UNIT_TO_PERCENT(fitScale);
pan = {};
}
}
ImGui::Text(mousePositionString.c_str());
_imgui_end_child(); // IMGUI_CANVAS_VIEW_CHILD
@@ -2558,8 +2756,8 @@ static void _imgui_spritesheet_editor(Imgui* self)
{
if (self->settings->editorIsGridSnap)
{
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);
position.x = roundf((position.x - gridSize.x) / gridSize.x) * gridSize.x + gridOffset.x - (gridSize.x * 0.5f);
position.y = roundf((position.y - gridSize.y) / gridSize.y) * gridSize.y + gridOffset.y - (gridSize.y * 0.5f);
}
frame->pivot = position - frame->crop;
@@ -2770,6 +2968,8 @@ static void _imgui_dock(Imgui* self)
_imgui_spritesheets(self);
_imgui_animation_preview(self);
_imgui_spritesheet_editor(self);
_imgui_layers(self);
_imgui_nulls(self);
_imgui_timeline(self);
_imgui_onionskin(self);
_imgui_frame_properties(self);
@@ -2875,20 +3075,15 @@ void imgui_update(Imgui* self)
{
const char* droppedFile = event.drop.data;
if (path_is_extension(droppedFile, ANM2_EXTENSION))
_imgui_anm2_open(self, droppedFile);
else if (path_is_extension(droppedFile, ANM2_SPRITESHEET_EXTENSION))
_imgui_spritesheet_add(self, droppedFile);
else
imgui_log_push(self, IMGUI_LOG_DRAG_DROP_ERROR);
if (path_is_extension(droppedFile, ANM2_EXTENSION)) _imgui_anm2_open(self, droppedFile);
else if (path_is_extension(droppedFile, ANM2_SPRITESHEET_EXTENSION)) _imgui_spritesheet_add(self, droppedFile);
else imgui_log_push(self, IMGUI_LOG_DRAG_DROP_ERROR);
break;
}
case SDL_EVENT_QUIT:
if (self->isTryQuit)
self->isQuit = true;
else
imgui_quit(self);
if (self->isTryQuit) self->isQuit = true;
else imgui_quit(self);
break;
default:
break;

View File

@@ -59,7 +59,7 @@
#define IMGUI_TIMELINE_FRAME_MULTIPLE 5
#define IMGUI_TIMELINE_MERGE
#define IMGUI_TOOL_COLOR_PICKER_DURATION 0.25f
#define IMGUI_OPTION_POPUP_ROW_COUNT 2
#define IMGUI_CONFIRM_POPUP_ROW_COUNT 2
#define IMGUI_CHORD_REPEAT_TIME 0.25f
#define IMGUI_ACTION_FRAME_CROP "Frame Crop"
@@ -68,14 +68,13 @@
#define IMGUI_ACTION_ANIMATION_SWAP "Animation Swap"
#define IMGUI_ACTION_TRIGGER_MOVE "Trigger At Frame"
#define IMGUI_ACTION_FRAME_DELAY "Frame Delay"
#define IMGUI_ACTION_MOVE_PLAYHEAD "Move Playhead"
#define IMGUI_ACTION_DRAW "Draw"
#define IMGUI_ACTION_ERASE "Erase"
#define IMGUI_ACTION_MOVE "Move"
#define IMGUI_ACTION_SCALE "Scale"
#define IMGUI_ACTION_ROTATE "Rotate"
#define IMGUI_ACTION_CROP "Crop"
#define IMGUI_ACTION_RELOAD_SPRITESHEET "Reload Spritesheet(s)"
#define IMGUI_ACTION_ADD_SPRITESHEET "Add Spritesheet"
#define IMGUI_ACTION_REPLACE_SPRITESHEET "Replace Spritesheet"
#define IMGUI_ACTION_OPEN_FILE "Open File"
@@ -96,6 +95,9 @@
#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_DRAG_DROP_ERROR "Invalid file for dragging/dropping!"
#define IMGUI_LOG_ANIMATION_PASTE_ERROR "Failed to parse clipboard text as an animation."
#define IMGUI_LOG_FRAME_PASTE_ERROR "Failed to parse clipboard text as a frame."
#define IMGUI_LOG_RELOAD_SPRITESHEET "Reloaded spritesheet(s)."
#define IMGUI_NONE "None"
#define IMGUI_ANIMATION_DEFAULT_FORMAT "(*) {}"
@@ -110,6 +112,8 @@
#define IMGUI_SPRITESHEET_FORMAT "#{} {}"
#define IMGUI_SPRITESHEET_ID_FORMAT "#{}"
#define IMGUI_TIMELINE_ITEM_CHILD_FORMAT "#{} {}"
#define IMGUI_LAYER_FORMAT "#{} {}"
#define IMGUI_NULL_FORMAT "#{} {}"
#define IMGUI_TIMELINE_FRAME_LABEL_FORMAT "## {}"
#define IMGUI_SELECTABLE_INPUT_INT_FORMAT "#{}"
#define IMGUI_TIMELINE_ANIMATION_NONE "Select an animation to show timeline..."
@@ -347,7 +351,15 @@ static inline void imgui_copy(Imgui* self)
static inline void imgui_paste(Imgui* self)
{
clipboard_paste(self->clipboard);
if (!clipboard_paste(self->clipboard))
{
switch (self->clipboard->type)
{
case CLIPBOARD_FRAME: imgui_log_push(self, IMGUI_LOG_FRAME_PASTE_ERROR); break;
case CLIPBOARD_ANIMATION: imgui_log_push(self, IMGUI_LOG_ANIMATION_PASTE_ERROR); break;
default: break;
}
}
}
static inline void imgui_onionskin_toggle(Imgui* self)
@@ -604,7 +616,7 @@ enum ImguiItemType
IMGUI_DOCKSPACE,
IMGUI_CHILD,
IMGUI_TABLE,
IMGUI_OPTION_POPUP,
IMGUI_CONFIRM_POPUP,
IMGUI_SELECTABLE,
IMGUI_BUTTON,
IMGUI_RADIO_BUTTON,
@@ -631,6 +643,7 @@ struct ImguiItemOverride
AtlasType atlas = ATLAS_NONE;
bool isMnemonicDisabled{};
s32 value{};
s32 rowCount{};
};
struct ImguiItem;
@@ -754,6 +767,7 @@ struct ImguiItem
if (override.size != ImVec2{}) out.size = override.size;
if (override.max != 0) out.max = override.max;
if (override.value != 0) out.value = override.value;
if (override.rowCount != 0) out.rowCount = override.rowCount;
if (override.atlas != ATLAS_NONE) out.atlas = override.atlas;
if (override.isMnemonicDisabled) out.isMnemonicDisabled = override.isMnemonicDisabled;
return out;
@@ -888,6 +902,11 @@ IMGUI_ITEM(IMGUI_OPEN_CONFIRMATION,
self.text = "Unsaved changes will be lost!\nAre you sure you open a new file?"
);
IMGUI_ITEM(IMGUI_NO_ANM2_PATH_CONFIRMATION,
self.label = "No Anm2 Path",
self.text = "You will need to load or make a new .anm2 file first!\n"
);
IMGUI_ITEM(IMGUI_WIZARD,
self.label = "&Wizard",
self.tooltip = "Opens the wizard menu, for neat functions related to the .anm2.",
@@ -996,7 +1015,7 @@ IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_GENERATE,
self.label = "Generate",
self.tooltip = "Generate an animation with the used settings.",
self.snapshotAction = "Generate Animation from Grid",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT,
self.isSameLine = true
);
@@ -1103,7 +1122,7 @@ IMGUI_ITEM(IMGUI_SCALE_ANM2_SCALE,
self.label = "Scale",
self.tooltip = "Scale the anm2 with the value specified.",
self.snapshotAction = "Scale Anm2",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT,
self.isSameLine = true
);
@@ -1111,12 +1130,20 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION,
self.label = "&Render Animation",
self.tooltip = "Renders the current animation preview; output options can be customized.",
self.popup = "Render Animation",
self.popupSize = {600, 125}
self.popupSize = {600, 150},
self.popupType = IMGUI_POPUP_CENTER_WINDOW
);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_CHILD,
self.label = "## Render Animation Child",
self.size = {600, 125}
self.size = {IMGUI_RENDER_ANIMATION.popupSize.x, IMGUI_RENDER_ANIMATION.popupSize.y - IMGUI_FOOTER_CHILD.size.y},
self.flags = true
);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FOOTER_CHILD,
self.label = "## Render Animation Footer Child",
self.size = {IMGUI_RENDER_ANIMATION.popupSize.x, IMGUI_FOOTER_CHILD.size.y},
self.flags = true
);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_LOCATION_BROWSE,
@@ -1149,8 +1176,7 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION_OUTPUT,
self.label = "Output",
self.tooltip = "Select the rendered animation output.\nIt can either be one animated image or a sequence of frames.",
self.items = {std::begin(RENDER_TYPE_STRINGS), std::end(RENDER_TYPE_STRINGS)},
self.value = RENDER_PNG,
self.isSeparator = true
self.value = RENDER_PNG
);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FORMAT,
@@ -1166,7 +1192,7 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION_CONFIRM,
self.popupType = IMGUI_POPUP_CENTER_WINDOW,
self.popupSize = {300, 60},
self.isSameLine = true,
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT
self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT
);
IMGUI_ITEM(IMGUI_RENDERING_ANIMATION_CHILD,
@@ -1259,6 +1285,73 @@ IMGUI_ITEM(IMGUI_DEFAULT_SETTINGS,
self.isSizeToText = true
);
IMGUI_ITEM(IMGUI_LAYERS,
self.label = "Layers",
self.flags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
);
IMGUI_ITEM(IMGUI_LAYERS_CHILD, self.label = "## Layers Child", self.flags = true);
IMGUI_ITEM(IMGUI_LAYER,
self.label = "## Layer Item",
self.dragDrop = "## Layer Drag Drop",
self.atlas = ATLAS_LAYER,
self.idOffset = 3000
);
IMGUI_ITEM(IMGUI_LAYER_SPRITESHEET_ID,
self.label = "## Spritesheet ID",
self.tooltip = "Change the spritesheet ID this layer uses.",
self.atlas = ATLAS_SPRITESHEET,
self.size = {50, 0}
);
#define IMGUI_LAYERS_OPTIONS_ROW_COUNT 2
IMGUI_ITEM(IMGUI_LAYER_ADD,
self.label = "Add",
self.tooltip = "Adds a new layer.",
self.snapshotAction = "Add Layer",
self.rowCount = IMGUI_LAYERS_OPTIONS_ROW_COUNT,
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_LAYER_REMOVE,
self.label = "Remove",
self.tooltip = "Removes the selected layer.\nThis will remove all layer animations that use this layer from all animations.",
self.snapshotAction = "Remove Layer",
self.rowCount = IMGUI_LAYERS_OPTIONS_ROW_COUNT
);
IMGUI_ITEM(IMGUI_NULLS,
self.label = "Nulls",
self.flags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
);
IMGUI_ITEM(IMGUI_NULLS_CHILD, self.label = "## Nulls Child", self.flags = true);
IMGUI_ITEM(IMGUI_NULL,
self.label = "## Null Item",
self.dragDrop = "## Null Drag Drop",
self.atlas = ATLAS_NULL,
self.idOffset = 4000
);
#define IMGUI_NULLS_OPTIONS_ROW_COUNT 2
IMGUI_ITEM(IMGUI_NULL_ADD,
self.label = "Add",
self.tooltip = "Adds a null layer.",
self.snapshotAction = "Add Null",
self.rowCount = IMGUI_NULLS_OPTIONS_ROW_COUNT,
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_NULL_REMOVE,
self.label = "Remove",
self.tooltip = "Removes the selected null.\nThis will remove all null animations that use this null from all animations.",
self.snapshotAction = "Remove Null",
self.rowCount = IMGUI_NULLS_OPTIONS_ROW_COUNT
);
IMGUI_ITEM(IMGUI_ANIMATIONS,
self.label = "Animations",
self.flags = ImGuiWindowFlags_NoScrollbar |
@@ -1354,7 +1447,7 @@ IMGUI_ITEM(IMGUI_MERGE_CONFIRM,
self.label = "Merge",
self.tooltip = "Merge the selected animations with the options set.",
self.snapshotAction = "Merge Animations",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT,
self.isSameLine = true
);
@@ -1440,15 +1533,15 @@ IMGUI_ITEM(IMGUI_SPRITESHEETS_FOOTER_CHILD,
#define IMGUI_SPRITESHEETS_OPTIONS_SECOND_ROW_COUNT 3
IMGUI_ITEM(IMGUI_SPRITESHEET_ADD,
self.label = "Add",
self.tooltip = "Select an image to add as a spritesheet.",
self.tooltip = "Select a .png image to add as a spritesheet.",
self.rowCount = IMGUI_SPRITESHEETS_OPTIONS_FIRST_ROW_COUNT,
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_SPRITESHEETS_RELOAD,
self.label = "Reload",
self.tooltip = "Reload the selected spritesheet.",
self.snapshotAction = "Reload Spritesheet",
self.tooltip = "Reload the selected spritesheet(s).",
self.snapshotAction = "Reload Spritesheet(s)",
self.rowCount = IMGUI_SPRITESHEETS_OPTIONS_FIRST_ROW_COUNT,
self.isSameLine = true
);
@@ -1490,7 +1583,9 @@ const ImVec2 IMGUI_CANVAS_CHILD_SIZE = {230, 85};
IMGUI_ITEM(IMGUI_CANVAS_GRID_CHILD,
self.label = "## Canvas Grid Child",
self.size = IMGUI_CANVAS_CHILD_SIZE,
self.flags = true
self.flags = true,
self.windowFlags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
);
IMGUI_ITEM(IMGUI_CANVAS_GRID,
@@ -1525,7 +1620,9 @@ IMGUI_ITEM(IMGUI_CANVAS_GRID_OFFSET,
IMGUI_ITEM(IMGUI_CANVAS_VIEW_CHILD,
self.label = "## View Child",
self.size = IMGUI_CANVAS_CHILD_SIZE,
self.flags = true
self.flags = true,
self.windowFlags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
);
IMGUI_ITEM(IMGUI_CANVAS_ZOOM,
@@ -1537,12 +1634,12 @@ IMGUI_ITEM(IMGUI_CANVAS_ZOOM,
self.value = CANVAS_ZOOM_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_VISUAL_CHILD,
self.label = "## Animation Preview Visual Child",
self.size = IMGUI_CANVAS_CHILD_SIZE,
self.flags = true
self.flags = true,
self.windowFlags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
);
IMGUI_ITEM(IMGUI_CANVAS_BACKGROUND_COLOR,
@@ -1566,7 +1663,9 @@ IMGUI_ITEM(IMGUI_CANVAS_ANIMATION_OVERLAY_TRANSPARENCY,
IMGUI_ITEM(IMGUI_CANVAS_HELPER_CHILD,
self.label = "## Animation Preview Helper Child",
self.size = IMGUI_CANVAS_CHILD_SIZE,
self.flags = true
self.flags = true,
self.windowFlags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
);
IMGUI_ITEM(IMGUI_CANVAS_AXES,
@@ -1644,12 +1743,22 @@ IMGUI_ITEM(IMGUI_SPRITESHEET_EDITOR,
self.flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse
);
#define IMGUI_SPRITESHEET_EDITOR_VIEW_ROW_COUNT 2
IMGUI_ITEM(IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW,
self.label = "Center View",
self.tooltip = "Centers the current view on the spritesheet editor.",
self.hotkey = HOTKEY_CENTER_VIEW,
self.focusWindow = IMGUI_SPRITESHEET_EDITOR.label,
self.size = {-FLT_MIN, 0}
self.rowCount = IMGUI_SPRITESHEET_EDITOR_VIEW_ROW_COUNT,
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_SPRITESHEET_EDITOR_FIT,
self.label = "Fit",
self.tooltip = "Adjust the view/pan based on the size of the spritesheet, to fit the canvas' size.",
self.hotkey = HOTKEY_FIT,
self.focusWindow = IMGUI_SPRITESHEET_EDITOR.label,
self.rowCount = IMGUI_SPRITESHEET_EDITOR_VIEW_ROW_COUNT
);
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES, self.label = "Frame Properties");
@@ -2000,6 +2109,20 @@ const inline ImguiItem* IMGUI_TIMELINE_ITEM_SELECTABLES[ANM2_COUNT]
&IMGUI_TIMELINE_ITEM_TRIGGERS_SELECTABLE
};
IMGUI_ITEM(IMGUI_TIMELINE_SHOW_UNUSED,
self.label = "## Show Unused",
self.tooltip = "Layers/nulls without any frames will be hidden.",
self.snapshotAction = "Hide Unused",
self.atlas = ATLAS_SHOW_UNUSED
);
IMGUI_ITEM(IMGUI_TIMELINE_HIDE_UNUSED,
self.label = "## Hide Unused",
self.tooltip = "Layers/nulls without any frames will be shown.",
self.snapshotAction = "Show Unused",
self.atlas = ATLAS_HIDE_UNUSED
);
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_VISIBLE,
self.label = "## Visible",
self.tooltip = "The item is visible.\nPress to set to invisible.",
@@ -2028,12 +2151,6 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_HIDE_RECT,
self.atlas = ATLAS_HIDE_RECT
);
IMGUI_ITEM(IMGUI_TIMELINE_SPRITESHEET_ID,
self.label = "## Spritesheet ID",
self.tooltip = "Change the spritesheet ID this item uses.",
self.atlas = ATLAS_SPRITESHEET,
self.size = {32, 0}
);
IMGUI_ITEM(IMGUI_TIMELINE_FRAMES_CHILD,
self.label = "## Timeline Frames Child",
@@ -2095,36 +2212,68 @@ const inline ImguiItem* IMGUI_TIMELINE_FRAMES[ANM2_COUNT]
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_FOOTER_CHILD,
self.label = "## Item Footer Child",
self.size = {IMGUI_TIMELINE_ITEM_CHILD.size.x, IMGUI_FOOTER_CHILD.size.y},
self.flags = true
self.flags = true,
self.windowFlags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
);
IMGUI_ITEM(IMGUI_TIMELINE_OPTIONS_FOOTER_CHILD,
self.label = "## Options Footer Child",
self.size = {0, IMGUI_FOOTER_CHILD.size.y},
self.flags = true
self.flags = true,
self.windowFlags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
);
#define IMGUI_TIMELINE_FOOTER_ITEM_CHILD_ITEM_COUNT 2
IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM,
self.label = "Add",
self.tooltip = "Adds an item (layer or null) to the animation.",
self.popup = "## Add Item Popup",
self.popupType = IMGUI_POPUP_BY_ITEM,
self.tooltip = "Adds an item (layer or null) to the animation.\nMake sure to add a Layer/Null first in the Layers or Nulls windows.",
self.popup = "Add Item",
self.popupType = IMGUI_POPUP_CENTER_WINDOW,
self.popupSize = {300, 350},
self.rowCount = IMGUI_TIMELINE_FOOTER_ITEM_CHILD_ITEM_COUNT,
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_TYPE_CHILD,
self.label = "## Add Item Type Child",
self.size = {IMGUI_TIMELINE_ADD_ITEM.popupSize.x, 35},
self.flags = true
);
IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_LAYER,
self.label = "Layer",
self.tooltip = "Adds a layer item.\nA layer item is a primary graphical item, using a spritesheet.",
self.snapshotAction = "Add Layer",
self.isSizeToText = true
self.isSizeToText = true,
self.value = ANM2_LAYER,
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_NULL,
self.label = "Null",
self.tooltip = "Adds a null item.\nA null item is an invisible item, often accessed by the game engine.",
self.snapshotAction = "Add Null",
self.isSizeToText = true
self.tooltip = "Adds a null item.\nA null item is an invisible item, often accessed by a game engine.",
self.isSizeToText = true,
self.value = ANM2_NULL
);
IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_ITEMS_CHILD,
self.label = "## Add Item Items",
self.size = {IMGUI_TIMELINE_ADD_ITEM.popupSize.x, 250},
self.flags = true
);
IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_OPTIONS_CHILD,
self.label = "## Add Item Options Child",
self.size = {IMGUI_TIMELINE_ADD_ITEM.popupSize.x, 35},
self.flags = true
);
IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM_ADD,
self.label = "Add",
self.tooltip = "Add the selected item.",
self.snapshotAction = "Add Animation",
self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT,
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_TIMELINE_REMOVE_ITEM,
@@ -2211,7 +2360,7 @@ IMGUI_ITEM(IMGUI_BAKE_CONFIRM,
self.label = "Bake",
self.tooltip = "Bake the selected frame with the options selected.",
self.snapshotAction = "Bake Frames",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT,
self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT,
self.isSameLine = true
);
@@ -2353,17 +2502,17 @@ IMGUI_ITEM(IMGUI_CHANGE_INPUT_INT,
self.step = 0
);
#define IMGUI_OPTION_POPUP_ROW_COUNT 2
#define IMGUI_CONFIRM_POPUP_ROW_COUNT 2
IMGUI_ITEM(IMGUI_POPUP_OK,
self.label = "OK",
self.tooltip = "Confirm the action.",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT
self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT
);
IMGUI_ITEM(IMGUI_POPUP_CANCEL,
self.label = "Cancel",
self.tooltip = "Cancel the action.",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT
self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT
);
IMGUI_ITEM(IMGUI_LOG_WINDOW,

View File

@@ -40,7 +40,7 @@ void preview_tick(Preview* self)
glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, framebufferPixels.data());
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
texture_from_rgba_init(&frameTexture, size, TEXTURE_CHANNELS, framebufferPixels.data());
texture_from_rgba_init(&frameTexture, size, framebufferPixels.data());
self->renderFrames.push_back(frameTexture);
}
@@ -199,7 +199,7 @@ void preview_draw(Preview* self)
if (self->settings->previewIsIcons && animation->rootAnimation.isVisible && root.isVisible)
root_draw(root, colorOffset, alphaOffset, isOnionskin);
for (auto [i, id] : self->anm2->layerMap)
for (auto id : animation->layerOrder)
layer_draw(rootModel, id, time, colorOffset, alphaOffset, isOnionskin);
if (self->settings->previewIsIcons)

View File

@@ -2,7 +2,7 @@
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, (u8*)TEXTURE_ATLAS, TEXTURE_ATLAS_LENGTH);
for (s32 i = 0; i < SHADER_COUNT; i++)
shader_init(&self->shaders[i], SHADER_DATA[i].vertex, SHADER_DATA[i].fragment);

View File

@@ -28,7 +28,7 @@
#define SETTINGS_LIST \
/* name, symbol, type, defaultValue */ \
X(windowSize, WINDOW_SIZE, TYPE_IVEC2_WH, {1280, 720}) \
X(windowSize, WINDOW_SIZE, TYPE_IVEC2_WH, {1600, 900}) \
X(isVsync, IS_VSYNC, TYPE_BOOL, true) \
\
X(hotkeyCenterView, HOTKEY_CENTER_VIEW, TYPE_STRING, "Home") \
@@ -130,6 +130,9 @@
X(bakeIsRoundScale, BAKE_IS_ROUND_SCALE, TYPE_BOOL, true) \
X(bakeIsRoundRotation, BAKE_IS_ROUND_ROTATION, TYPE_BOOL, true) \
\
X(timelineAddItemType, TIMELINE_ADD_ITEM_TYPE, TYPE_INT, ANM2_LAYER) \
X(timelineIsShowUnused, TIMELINE_IS_SHOW_UNUSED, TYPE_BOOL, true) \
\
X(onionskinIsEnabled, ONIONSKIN_IS_ENABLED, TYPE_BOOL, false) \
X(onionskinDrawOrder, ONIONSKIN_DRAW_ORDER, TYPE_INT, ONIONSKIN_BELOW) \
X(onionskinBeforeCount, ONIONSKIN_BEFORE_COUNT, TYPE_INT, 1) \
@@ -262,72 +265,86 @@ Collapsed=0
[Window][Tools]
Pos=8,40
Size=37,460
Size=38,516
Collapsed=0
DockId=0x0000000B,0
[Window][Animations]
Pos=1288,284
Size=304,216
Pos=1289,307
Size=303,249
Collapsed=0
DockId=0x0000000A,0
[Window][Events]
Pos=1007,332
Size=279,168
Pos=957,264
Size=330,292
Collapsed=0
DockId=0x00000008,0
DockId=0x00000008,2
[Window][Spritesheets]
Pos=1288,40
Size=304,242
Pos=1289,40
Size=303,265
Collapsed=0
DockId=0x00000009,0
[Window][Animation Preview]
Pos=47,40
Size=958,460
Pos=48,40
Size=907,516
Collapsed=0
DockId=0x0000000C,0
[Window][Spritesheet Editor]
Pos=47,40
Size=958,460
Pos=48,40
Size=907,516
Collapsed=0
DockId=0x0000000C,1
[Window][Timeline]
Pos=8,502
Size=1584,390
Pos=8,558
Size=1584,334
Collapsed=0
DockId=0x00000004,0
[Window][Frame Properties]
Pos=1007,40
Size=279,290
Pos=957,40
Size=330,222
Collapsed=0
DockId=0x00000007,0
[Window][Onionskin]
Pos=8,502
Size=1584,390
Pos=957,264
Size=330,292
Collapsed=0
DockId=0x00000004,1
DockId=0x00000008,3
[Window][Layers]
Pos=957,264
Size=330,292
Collapsed=0
DockId=0x00000008,0
[Window][Nulls]
Pos=957,264
Size=330,292
Collapsed=0
DockId=0x00000008,1
[Docking][Data]
DockSpace ID=0xFC02A410 Window=0x0E46F4F7 Pos=8,40 Size=1584,852 Split=Y
DockNode ID=0x00000003 Parent=0xFC02A410 SizeRef=1902,568 Split=X
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1595,1016 Split=X Selected=0x024430EF
DockNode ID=0x00000005 Parent=0x00000001 SizeRef=997,654 Split=X Selected=0x024430EF
DockNode ID=0x0000000B Parent=0x00000005 SizeRef=37,654 Selected=0x18A5FDB9
DockNode ID=0x0000000C Parent=0x00000005 SizeRef=958,654 CentralNode=1 Selected=0x024430EF
DockNode ID=0x00000006 Parent=0x00000001 SizeRef=279,654 Split=Y Selected=0x754E368F
DockNode ID=0x00000007 Parent=0x00000006 SizeRef=631,359 Selected=0x754E368F
DockNode ID=0x00000008 Parent=0x00000006 SizeRef=631,207 Selected=0x8A65D963
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=304,1016 Split=Y Selected=0x4EFD0020
DockNode ID=0x00000009 Parent=0x00000002 SizeRef=634,299 Selected=0x4EFD0020
DockNode ID=0x0000000A Parent=0x00000002 SizeRef=634,267 Selected=0xC1986EE2
DockNode ID=0x00000004 Parent=0xFC02A410 SizeRef=1902,390 Selected=0x4F89F0DC
DockNode ID=0x00000003 Parent=0xFC02A410 SizeRef=1902,680 Split=X
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1017,1016 Split=X Selected=0x024430EF
DockNode ID=0x00000005 Parent=0x00000001 SizeRef=1264,654 Split=X Selected=0x024430EF
DockNode ID=0x0000000B Parent=0x00000005 SizeRef=38,654 Selected=0x18A5FDB9
DockNode ID=0x0000000C Parent=0x00000005 SizeRef=1224,654 CentralNode=1 Selected=0x024430EF
DockNode ID=0x00000006 Parent=0x00000001 SizeRef=330,654 Split=Y Selected=0x754E368F
DockNode ID=0x00000007 Parent=0x00000006 SizeRef=631,293 Selected=0x754E368F
DockNode ID=0x00000008 Parent=0x00000006 SizeRef=631,385 Selected=0xCD8384B1
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=303,1016 Split=Y Selected=0x4EFD0020
DockNode ID=0x00000009 Parent=0x00000002 SizeRef=634,349 Selected=0x4EFD0020
DockNode ID=0x0000000A Parent=0x00000002 SizeRef=634,329 Selected=0xC1986EE2
DockNode ID=0x00000004 Parent=0xFC02A410 SizeRef=1902,334 Selected=0x4F89F0DC
)";
void settings_save(Settings* self);

View File

@@ -26,13 +26,13 @@ static void _snapshot_set(Snapshots* self, Snapshot* snapshot)
self->preview->time = snapshot->time;
self->action = snapshot->action;
//anm2_spritesheet_texture_pixels_upload(self->anm2);
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);
anm2_spritesheet_texture_pixels_download(&snapshot.anm2);
return snapshot;
}

View File

@@ -116,6 +116,8 @@ bool sdl_init(State* self, bool isTestMode = false)
void init(State* self)
{
log_info(STATE_INIT_INFO);
settings_init(&self->settings);
if (!sdl_init(self)) return;

View File

@@ -2,7 +2,7 @@
#include "imgui.h"
#define STATE_INIT_INFO "Initializing..."
#define STATE_INIT_INFO "Initializing anm2ed (Version 1.1)"
#define STATE_SDL_INIT_ERROR "Failed to initialize SDL! {}"
#define STATE_SDL_INIT_INFO "Initialized SDL"
#define STATE_MIX_INIT_WARNING "Unable to initialize SDL_mixer! {}"

View File

@@ -17,8 +17,7 @@
static void _texture_gl_set(Texture* self, const u8* data)
{
if (self->id == GL_ID_NONE)
glGenTextures(1, &self->id);
if (self->id == GL_ID_NONE) glGenTextures(1, &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);
@@ -43,7 +42,7 @@ std::vector<u8> texture_download(const Texture* self)
bool texture_from_path_init(Texture* self, const std::string& path)
{
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, nullptr, TEXTURE_CHANNELS);
if (!data)
{
@@ -60,13 +59,12 @@ bool texture_from_path_init(Texture* self, const std::string& path)
return 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, const u8* data, u32 length)
{
*self = Texture{};
self->size = size;
self->channels = channels;
u8* textureData = stbi_load_from_memory(data, length, &self->size.x, &self->size.y, &self->channels, TEXTURE_CHANNELS);
u8* textureData = stbi_load_from_memory(data, length, &self->size.x, &self->size.y, nullptr, TEXTURE_CHANNELS);
if (!textureData)
{
@@ -79,11 +77,11 @@ bool texture_from_encoded_data_init(Texture* self, ivec2 size, s32 channels, con
return true;
}
bool texture_from_rgba_init(Texture* self, ivec2 size, s32 channels, const u8* data)
bool texture_from_rgba_init(Texture* self, ivec2 size, const u8* data)
{
*self = Texture{};
self->size = size;
self->channels = channels;
self->isInvalid = false;
_texture_gl_set(self, data);

View File

@@ -12,14 +12,13 @@ struct Texture
{
GLuint id = GL_ID_NONE;
ivec2 size{};
s32 channels = TEXTURE_CHANNELS;
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, const u8* data, u32 length);
bool texture_from_gl_write(Texture* self, const std::string& path);
bool texture_from_path_init(Texture* self, const std::string& path);
bool texture_from_rgba_init(Texture* self, ivec2 size, s32 channels, const u8* data);
bool texture_from_rgba_init(Texture* self, ivec2 size, const u8* data);
bool texture_from_rgba_write(const std::string& path, const u8* data, ivec2 size);
bool texture_pixel_set(Texture* self, ivec2 position, vec4 color);
void texture_free(Texture* self);