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; 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()) if (auto it = map.find(id); it != map.end())
return &it->second; 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; 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 = {}) static inline mat4 quad_model_get(vec2 size = {}, vec2 position = {}, vec2 pivot = {}, vec2 scale = vec2(1.0f), f32 rotation = {})
{ {
vec2 scaleAbsolute = glm::abs(scale); 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 localDocument;
XMLDocument* useDocument = document ? document : &localDocument; XMLDocument* useDocument = document ? document : &localDocument;
@@ -86,7 +86,7 @@ void anm2_animation_serialize(Anm2* self, Anm2Animation* animation, XMLDocument*
// LayerAnimations // LayerAnimations
XMLElement* layersElement = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS]); XMLElement* layersElement = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS]);
for (auto& [i, id] : self->layerMap) for (auto& id : animation->layerOrder)
{ {
// LayerAnimation // LayerAnimation
Anm2Item& layerAnimation = animation->layerAnimations[id]; 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 animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION], self->animations[self->defaultAnimationID].name.c_str()); // DefaultAnimation
for (auto& [id, animation] : self->animations) for (auto& [id, animation] : self->animations)
anm2_animation_serialize(self, &animation, &document, animationsElement); anm2_animation_serialize(&animation, &document, animationsElement);
animatedActorElement->InsertEndChild(animationsElement); animatedActorElement->InsertEndChild(animationsElement);
@@ -248,7 +248,7 @@ bool anm2_serialize(Anm2* self, const std::string& path)
return true; 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()) 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_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_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_DELAY: frame->delay = std::atoi(attribute->Value()); break; // Delay
case ANM2_ATTRIBUTE_EVENT_ID: // EventID case ANM2_ATTRIBUTE_EVENT_ID: frame->eventID = std::atoi(attribute->Value()); break; // EventID
{
s32 eventID = std::atoi(attribute->Value());
frame->eventID = map_find(self->events, eventID) ? eventID : ID_NONE;
break;
}
default: break; 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) 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]); const XMLElement* frame = itemElement->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]);
frame; frame = frame->NextSiblingElement(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{}; s32 id{};
@@ -320,8 +315,6 @@ static void _anm2_animation_deserialize(Anm2* self, Anm2Animation* animation, co
// LayerAnimations // LayerAnimations
if (const XMLElement* layerAnimations = element->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS])) if (const XMLElement* layerAnimations = element->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS]))
{ {
s32 layerMapIndex = 0;
// LayerAnimation // LayerAnimation
for for
( (
@@ -330,7 +323,6 @@ static void _anm2_animation_deserialize(Anm2* self, Anm2Animation* animation, co
) )
{ {
Anm2Item layerAnimationItem; Anm2Item layerAnimationItem;
for (const XMLAttribute* attribute = layerAnimation->FirstAttribute(); attribute; attribute = attribute->Next()) for (const XMLAttribute* attribute = layerAnimation->FirstAttribute(); attribute; attribute = attribute->Next())
{ {
@@ -344,9 +336,7 @@ static void _anm2_animation_deserialize(Anm2* self, Anm2Animation* animation, co
frames_deserialize(layerAnimation, &layerAnimationItem); frames_deserialize(layerAnimation, &layerAnimationItem);
animation->layerAnimations[id] = layerAnimationItem; animation->layerAnimations[id] = layerAnimationItem;
animation->layerOrder.push_back(id);
self->layerMap[id] = layerMapIndex;
layerMapIndex++;
} }
} }
@@ -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]); const XMLElement* trigger = triggers->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER]);
trigger; trigger = trigger->NextSiblingElement(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]); const XMLElement* animation = animations->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]);
animation; animation = animation->NextSiblingElement(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) for (auto& [id, animation] : self->animations)
@@ -584,99 +574,68 @@ bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures)
return true; 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); s32 id = map_next_id_get(self->layers);
self->layers[id] = Anm2Layer{}; self->layers[id] = Anm2Layer{};
self->layerMap[self->layers.size() - 1] = id; return id;
for (auto& [_, animation] : self->animations)
animation.layerAnimations[id] = Anm2Item{};
} }
void anm2_layer_remove(Anm2* self, s32 id) void anm2_layer_remove(Anm2* self, s32 id)
{ {
if (!self->layers.contains(id)) return;
self->layers.erase(id); 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) 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); s32 id = map_next_id_get(self->nulls);
self->nulls[id] = Anm2Null{}; self->nulls[id] = Anm2Null{};
return id;
for (auto& [_, animation] : self->animations)
animation.nullAnimations[id] = Anm2Item{};
} }
void anm2_null_remove(Anm2* self, s32 id) void anm2_null_remove(Anm2* self, s32 id)
{ {
if (!self->nulls.contains(id)) if (!self->nulls.contains(id)) return;
return;
self->nulls.erase(id); self->nulls.erase(id);
std::map<s32, Anm2Null> newNulls; for (auto& [_, animation] : self->animations)
s32 newID = 0; anm2_animation_null_animation_remove(&animation, id);
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);
}
} }
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); s32 addID = map_next_id_get(self->animations);
Anm2Animation localAnimation; Anm2Animation localAnimation;
Anm2Animation* addAnimation = animation ? animation : &localAnimation; Anm2Animation* addAnimation = animation ? animation : &localAnimation;
for (auto& [layerID, layer] : self->layers) if (!animation) addAnimation->rootAnimation.frames.push_back(Anm2Frame{});
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 (id != ID_NONE) if (id != ID_NONE)
{ {
@@ -685,6 +644,7 @@ s32 anm2_animation_add(Anm2* self, bool isAddRootFrame, Anm2Animation* animation
} }
else else
self->animations[addID] = *addAnimation; self->animations[addID] = *addAnimation;
return addID; return addID;
} }
@@ -1153,7 +1113,7 @@ void anm2_spritesheet_texture_pixels_download(Anm2* self)
if (texture.id != GL_ID_NONE && !texture.isInvalid) 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); spritesheet.pixels.resize(bufferSize);
glBindTexture(GL_TEXTURE_2D, texture.id); glBindTexture(GL_TEXTURE_2D, texture.id);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, spritesheet.pixels.data()); 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}; 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; 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(); if (document.Parse(xml.c_str()) != XML_SUCCESS) return animation_deserialize_error();
const XMLElement* element = document.RootElement(); 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(); return animation_deserialize_error();
_anm2_animation_deserialize(self, animation, element); _anm2_animation_deserialize(animation, element);
return true; 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; XMLDocument document;
@@ -1243,18 +1204,17 @@ 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(); if (document.Parse(xml.c_str()) != XML_SUCCESS) return frame_deserialize_error();
const XMLElement* element = document.RootElement(); const XMLElement* element = document.RootElement();
if (!element) return frame_deserialize_error();
if 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(); return frame_deserialize_error();
_anm2_frame_deserialize(self, frame, element); _anm2_frame_deserialize(frame, element);
return true; return true;
} }

View File

@@ -142,7 +142,7 @@ struct Anm2Spritesheet
struct Anm2Layer struct Anm2Layer
{ {
std::string name = "New Layer"; std::string name = "New Layer";
s32 spritesheetID = ID_NONE; s32 spritesheetID{};
}; };
struct Anm2Null struct Anm2Null
@@ -199,9 +199,9 @@ struct Anm2Animation
s32 frameNum = ANM2_FRAME_NUM_MIN; s32 frameNum = ANM2_FRAME_NUM_MIN;
std::string name = "New Animation"; std::string name = "New Animation";
bool isLoop = true; bool isLoop = true;
bool isShowUnused = true;
Anm2Item rootAnimation; Anm2Item rootAnimation;
std::map<s32, Anm2Item> layerAnimations; std::unordered_map<s32, Anm2Item> layerAnimations;
std::vector<s32> layerOrder;
std::map<s32, Anm2Item> nullAnimations; std::map<s32, Anm2Item> nullAnimations;
Anm2Item triggers; Anm2Item triggers;
}; };
@@ -216,7 +216,6 @@ struct Anm2
std::map<s32, Anm2Null> nulls; std::map<s32, Anm2Null> nulls;
std::map<s32, Anm2Event> events; std::map<s32, Anm2Event> events;
std::map<s32, Anm2Animation> animations; std::map<s32, Anm2Animation> animations;
std::map<s32, s32> layerMap; // index, id
s32 defaultAnimationID = ID_NONE; s32 defaultAnimationID = ID_NONE;
s32 fps = ANM2_FPS_DEFAULT; s32 fps = ANM2_FPS_DEFAULT;
s32 version{}; s32 version{};
@@ -253,38 +252,42 @@ enum OnionskinDrawOrder
ONIONSKIN_ABOVE 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); 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); Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference);
void anm2_frame_remove(Anm2* self, Anm2Reference* reference); Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference);
void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time); Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference);
void anm2_reference_clear(Anm2Reference* self); bool anm2_animation_deserialize_from_xml(Anm2Animation* animation, const std::string& xml);
void anm2_reference_item_clear(Anm2Reference* self); bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures = true);
void anm2_reference_frame_clear(Anm2Reference* self); 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_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_length_set(Anm2Animation* self);
void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& mergeIDs, Anm2MergeType type); void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& mergeIDs, Anm2MergeType type);
void anm2_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_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_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time);
void anm2_scale(Anm2* self, f32 scale); void anm2_frame_remove(Anm2* self, Anm2Reference* reference);
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_serialize(Anm2Frame* frame, Anm2Type type, XMLDocument* document, XMLElement* addElement, std::string* string); 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); void anm2_free(Anm2* self);
bool anm2_frame_deserialize_from_xml(Anm2* self, Anm2Frame* frame, const std::string& xml); void anm2_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPosition, vec2 size, vec2 pivot, s32 columns, s32 count, s32 delay);
bool anm2_animation_deserialize_from_xml(Anm2* self, Anm2Animation* frame, const std::string& xml); 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); glUseProgram(0);
} }
void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color) void canvas_axes_draw(Canvas* self, GLuint& shader, mat4& transform, vec4& color)
{ {
vec4 originNDC = transform * vec4(0.0f, 0.0f, 0.0f, 1.0f); 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; if (!id) break;
Anm2Animation* animation = map_find(self->anm2->animations, *id); Anm2Animation* animation = map_find(self->anm2->animations, *id);
if (!animation) break; if (!animation) break;
anm2_animation_serialize(self->anm2, animation, nullptr, nullptr, &clipboardText); anm2_animation_serialize(animation, nullptr, nullptr, &clipboardText);
clipboard_text_set(); clipboard_text_set();
break; break;
} }
@@ -63,7 +63,7 @@ void clipboard_cut(Clipboard* self)
} }
} }
void clipboard_paste(Clipboard* self) bool clipboard_paste(Clipboard* self)
{ {
auto clipboard_string = [&]() auto clipboard_string = [&]()
{ {
@@ -80,8 +80,9 @@ void clipboard_paste(Clipboard* self)
Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location); Anm2Reference* reference = std::get_if<Anm2Reference>(&self->location);
if (!reference) break; if (!reference) break;
Anm2Frame frame; 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); anm2_frame_add(self->anm2, &frame, reference);
else return false;
break; break;
} }
case CLIPBOARD_ANIMATION: case CLIPBOARD_ANIMATION:
@@ -89,13 +90,16 @@ void clipboard_paste(Clipboard* self)
s32* id = std::get_if<s32>(&self->location); s32* id = std::get_if<s32>(&self->location);
if (!id) break; if (!id) break;
Anm2Animation animation; Anm2Animation animation;
if (anm2_animation_deserialize_from_xml(self->anm2, &animation, clipboard_string())) if (anm2_animation_deserialize_from_xml(&animation, clipboard_string()))
anm2_animation_add(self->anm2, false, &animation, *id); anm2_animation_add(self->anm2, &animation, *id);
else return false;
break; break;
} }
default: default:
break; break;
} }
return true;
} }
void clipboard_init(Clipboard* self, Anm2* anm2) void clipboard_init(Clipboard* self, Anm2* anm2)

View File

@@ -23,5 +23,5 @@ struct Clipboard
bool clipboard_is_value(void); bool clipboard_is_value(void);
void clipboard_copy(Clipboard* self); void clipboard_copy(Clipboard* self);
void clipboard_cut(Clipboard* self); void clipboard_cut(Clipboard* self);
void clipboard_paste(Clipboard* self); bool clipboard_paste(Clipboard* self);
void clipboard_init(Clipboard* self, Anm2* anm2); 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; return;
} }
imgui_snapshot(self, IMGUI_ACTION_ADD_SPRITESHEET);
std::filesystem::path workingPath = std::filesystem::current_path(); std::filesystem::path workingPath = std::filesystem::current_path();
std::string spritesheetPath = path; std::string spritesheetPath = path;
std::string anm2WorkingPath = working_directory_from_file_set(self->anm2->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); 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) static bool _imgui_is_no_click_on_item(void)
{ {
return ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered(); 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_WINDOW:
case IMGUI_DOCKSPACE: case IMGUI_DOCKSPACE:
case IMGUI_CHILD: case IMGUI_CHILD:
case IMGUI_OPTION_POPUP: case IMGUI_CONFIRM_POPUP:
break; break;
default: default:
ImGui::BeginDisabled(self.isDisabled); ImGui::BeginDisabled(self.isDisabled);
@@ -264,7 +276,7 @@ static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType
case IMGUI_WINDOW: case IMGUI_WINDOW:
case IMGUI_DOCKSPACE: case IMGUI_DOCKSPACE:
case IMGUI_CHILD: case IMGUI_CHILD:
case IMGUI_OPTION_POPUP: case IMGUI_CONFIRM_POPUP:
break; break;
default: default:
ImGui::EndDisabled(); 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_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)); 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)); ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
@@ -582,20 +594,23 @@ static bool _imgui_option_popup(ImguiItem self, Imgui* imgui, ImguiPopupState* s
ImGui::Text(self.text_get()); ImGui::Text(self.text_get());
ImGui::Separator(); 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_close_current_popup(imgui);
imgui_end_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; return true;
} }
ImGui::SameLine(); ImGui::SameLine();
if (_imgui_button(IMGUI_POPUP_CANCEL, imgui)) if (!isOnlyConfirm)
{ {
imgui_close_current_popup(imgui); if (_imgui_button(IMGUI_POPUP_CANCEL, imgui))
if (state) *state = IMGUI_POPUP_STATE_CANCEL; {
imgui_close_current_popup(imgui);
if (state) *state = IMGUI_POPUP_STATE_CANCEL;
}
} }
imgui_end_popup(imgui); imgui_end_popup(imgui);
@@ -604,6 +619,13 @@ static bool _imgui_option_popup(ImguiItem self, Imgui* imgui, ImguiPopupState* s
return false; 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) static void _imgui_context_menu(Imgui* self)
{ {
if (!self->isContextualActionsEnabled) return; if (!self->isContextualActionsEnabled) return;
@@ -650,6 +672,7 @@ static void _imgui_timeline(Imgui* self)
static s32& itemID = self->reference->itemID; static s32& itemID = self->reference->itemID;
IMGUI_BEGIN_OR_RETURN(IMGUI_TIMELINE, self); IMGUI_BEGIN_OR_RETURN(IMGUI_TIMELINE, self);
_imgui_no_anm2_path_check(self);
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference); 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); localMousePos = ImVec2(mousePos.x - itemMin.x + scroll.x, mousePos.y - itemMin.y);
frameTime = (s32)(localMousePos.x / frameSize.x); frameTime = (s32)(localMousePos.x / frameSize.x);
if (ImGui::IsMouseDown(0) && _imgui_is_window_hovered()) if (ImGui::IsMouseDown(0) && _imgui_is_window_hovered()) isPlayheadDrag = true;
{
if (!isPlayheadDrag)
imgui_snapshot(self, IMGUI_ACTION_MOVE_PLAYHEAD);
isPlayheadDrag = true;
}
if (isPlayheadDrag) if (isPlayheadDrag)
{ {
@@ -790,12 +808,12 @@ static void _imgui_timeline(Imgui* self)
std::function<void(Anm2Reference, s32&)> timeline_item_child = [&](Anm2Reference reference, s32& index) std::function<void(Anm2Reference, s32&)> timeline_item_child = [&](Anm2Reference reference, s32& index)
{ {
Anm2Item* item = anm2_item_from_reference(self->anm2, &reference); Anm2Item* item = anm2_item_from_reference(self->anm2, &reference);
Anm2Type& type = reference.itemType;
if (!item) return; 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)); ImVec2 buttonSize = ImVec2(ATLAS_SIZE_NORMAL) + (defaultFramePadding * ImVec2(2, 2));
Anm2Type& type = reference.itemType;
Anm2Layer* layer = nullptr; Anm2Layer* layer = nullptr;
Anm2Null* null = nullptr; Anm2Null* null = nullptr;
s32 buttonCount = type == ANM2_NULL ? 2 : 1; s32 buttonCount = type == ANM2_NULL ? 2 : 1;
@@ -824,13 +842,13 @@ static void _imgui_timeline(Imgui* self)
layer = &self->anm2->layers[reference.itemID]; layer = &self->anm2->layers[reference.itemID];
if if
( (
_imgui_atlas_selectable_input_text(IMGUI_TIMELINE_ITEM_SELECTABLES[type]->copy _imgui_atlas_selectable(IMGUI_TIMELINE_ITEM_SELECTABLES[type]->copy
({ ({
.isSelected = isSelected, .isSelected = isSelected,
.label = std::format(IMGUI_TIMELINE_ITEM_CHILD_FORMAT, reference.itemID, layer->name), .label = std::format(IMGUI_TIMELINE_ITEM_CHILD_FORMAT, reference.itemID, layer->name)
.id = index
}), }),
self, layer->name) self
)
) )
*self->reference = reference; *self->reference = reference;
break; break;
@@ -838,13 +856,12 @@ static void _imgui_timeline(Imgui* self)
null = &self->anm2->nulls[reference.itemID]; null = &self->anm2->nulls[reference.itemID];
if if
( (
_imgui_atlas_selectable_input_text(IMGUI_TIMELINE_ITEM_SELECTABLES[type]->copy _imgui_atlas_selectable(IMGUI_TIMELINE_ITEM_SELECTABLES[type]->copy
({ ({
.isSelected = isSelected, .isSelected = isSelected,
.label = std::format(IMGUI_TIMELINE_ITEM_CHILD_FORMAT, reference.itemID, null->name), .label = std::format(IMGUI_TIMELINE_ITEM_CHILD_FORMAT, reference.itemID, null->name)
.id = index
}), }),
self, null->name) self)
) )
*self->reference = reference; *self->reference = reference;
break; break;
@@ -878,13 +895,6 @@ static void _imgui_timeline(Imgui* self)
ImGui::EndDragDropTarget(); 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}); ImGui::SetCursorScreenPos({childPos.x + childSize.x - buttonAreaWidth, childPos.y + defaultWindowPadding.y});
if (type == ANM2_NULL) if (type == ANM2_NULL)
@@ -916,7 +926,7 @@ static void _imgui_timeline(Imgui* self)
timeline_item_child({animationID, ANM2_ROOT}, index); 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); timeline_item_child({animationID, ANM2_LAYER, id}, index);
for (auto & [id, null] : animation->nullAnimations) for (auto & [id, null] : animation->nullAnimations)
@@ -933,28 +943,12 @@ static void _imgui_timeline(Imgui* self)
switch (swapItemReference.itemType) switch (swapItemReference.itemType)
{ {
case ANM2_LAYER: case ANM2_LAYER:
{ vector_value_swap(animation->layerOrder, self->reference->itemID, swapItemReference.itemID);
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]);
break; break;
}
case ANM2_NULL: case ANM2_NULL:
map_swap(self->anm2->nulls, self->reference->itemID, swapItemReference.itemID);
map_swap(animation->nullAnimations, self->reference->itemID, swapItemReference.itemID); map_swap(animation->nullAnimations, self->reference->itemID, swapItemReference.itemID);
break; break;
default: default: break;
break;
} }
self->reference->itemID = swapItemReference.itemID; 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) std::function<void(Anm2Reference, s32&)> timeline_item_frames = [&](Anm2Reference reference, s32& index)
{ {
Anm2Item* item = anm2_item_from_reference(self->anm2, &reference); Anm2Item* item = anm2_item_from_reference(self->anm2, &reference);
if (!item) return;
Anm2Type& type = reference.itemType; Anm2Type& type = reference.itemType;
if (!self->settings->timelineIsShowUnused && item->frames.empty() && (type == ANM2_LAYER || type == ANM2_NULL)) return;
ImGui::PushID(index); ImGui::PushID(index);
@@ -1150,7 +1146,7 @@ static void _imgui_timeline(Imgui* self)
timeline_item_frames(Anm2Reference(animationID, ANM2_ROOT), index); 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); timeline_item_frames(Anm2Reference(animationID, ANM2_LAYER, id), index);
for (auto & [id, null] : animation->nullAnimations) for (auto & [id, null] : animation->nullAnimations)
@@ -1171,7 +1167,15 @@ static void _imgui_timeline(Imgui* self)
timeline_frames_child(); timeline_frames_child();
ImGui::SetCursorPos(ImVec2()); ImGui::SetCursorPos(ImVec2());
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, defaultItemSpacing);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, defaultWindowPadding);
_imgui_begin_child(IMGUI_TIMELINE_ITEM_CHILD, self); _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_end_child(); // IMGUI_TIMELINE_ITEM_CHILD
ImGui::SameLine(); ImGui::SameLine();
timeline_header(); timeline_header();
@@ -1185,12 +1189,91 @@ static void _imgui_timeline(Imgui* self)
Anm2Frame* frame = anm2_frame_from_reference(self->anm2, self->reference); Anm2Frame* frame = anm2_frame_from_reference(self->anm2, self->reference);
Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference); Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference);
_imgui_begin_child(IMGUI_TIMELINE_ITEM_FOOTER_CHILD, self); _imgui_begin_child(IMGUI_TIMELINE_ITEM_FOOTER_CHILD, self);
_imgui_button(IMGUI_TIMELINE_ADD_ITEM, self); _imgui_button(IMGUI_TIMELINE_ADD_ITEM, self);
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); static s32 selectedLayerID = ID_NONE;
if (_imgui_selectable(IMGUI_TIMELINE_ADD_ITEM_NULL, self)) anm2_null_add(self->anm2); 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); imgui_end_popup(self);
} }
@@ -1198,14 +1281,9 @@ static void _imgui_timeline(Imgui* self)
{ {
switch (itemType) switch (itemType)
{ {
case ANM2_LAYER: case ANM2_LAYER: anm2_animation_layer_animation_remove(animation, itemID); break;
anm2_layer_remove(self->anm2, itemID); case ANM2_NULL: anm2_animation_null_animation_remove(animation, itemID); break;
break; default: break;
case ANM2_NULL:
anm2_null_remove(self->anm2, itemID);
break;
default:
break;
} }
anm2_reference_item_clear(self->reference); anm2_reference_item_clear(self->reference);
@@ -1260,8 +1338,7 @@ static void _imgui_timeline(Imgui* self)
imgui_close_current_popup(self); imgui_close_current_popup(self);
} }
if (_imgui_button(IMGUI_POPUP_CANCEL, self)) if (_imgui_button(IMGUI_POPUP_CANCEL, self)) imgui_close_current_popup(self);
imgui_close_current_popup(self);
_imgui_end_child(); //IMGUI_BAKE_CHILD) _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); 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) 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_CONFIRM: self->isQuit = true; break;
case IMGUI_POPUP_STATE_CANCEL: self->isTryQuit = false; break; case IMGUI_POPUP_STATE_CANCEL: self->isTryQuit = false; break;
default: break;
} }
_imgui_selectable(IMGUI_WIZARD.copy({}), self); _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_input_text(IMGUI_RENDER_ANIMATION_FORMAT, self, format);
_imgui_combo(IMGUI_RENDER_ANIMATION_OUTPUT, self, &type); _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)) if (_imgui_button(IMGUI_RENDER_ANIMATION_CONFIRM, self))
{ {
bool isRenderStart = true; bool isRenderStart = true;
@@ -1609,7 +1689,7 @@ static void _imgui_taskbar(Imgui* self)
if (_imgui_button(IMGUI_POPUP_CANCEL, self)) if (_imgui_button(IMGUI_POPUP_CANCEL, self))
imgui_close_current_popup(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); imgui_end_popup(self);
} }
@@ -1826,9 +1906,110 @@ static void _imgui_tools(Imgui* self)
_imgui_end(); // IMGUI_TOOLS _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) static void _imgui_animations(Imgui* self)
{ {
IMGUI_BEGIN_OR_RETURN(IMGUI_ANIMATIONS, self); IMGUI_BEGIN_OR_RETURN(IMGUI_ANIMATIONS, self);
_imgui_no_anm2_path_check(self);
ImVec2 size = ImGui::GetContentRegionAvail(); ImVec2 size = ImGui::GetContentRegionAvail();
_imgui_begin_child(IMGUI_ANIMATIONS_CHILD.copy({.size = {size.x, size.y - IMGUI_FOOTER_CHILD.size.y}}), self); _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)) 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); _imgui_button(IMGUI_ANIMATION_MERGE.copy({!animation}), self);
@@ -2035,6 +2216,8 @@ static void _imgui_events(Imgui* self)
static s32 selectedID = ID_NONE; static s32 selectedID = ID_NONE;
IMGUI_BEGIN_OR_RETURN(IMGUI_EVENTS, self); IMGUI_BEGIN_OR_RETURN(IMGUI_EVENTS, self);
_imgui_no_anm2_path_check(self);
ImVec2 windowSize = ImGui::GetContentRegionAvail(); ImVec2 windowSize = ImGui::GetContentRegionAvail();
_imgui_begin_child(IMGUI_EVENTS_CHILD.copy({.size = {windowSize.x, windowSize.y - IMGUI_FOOTER_CHILD.size.y}}), self); _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; static s32 highlightedID = ID_NONE;
IMGUI_BEGIN_OR_RETURN(IMGUI_SPRITESHEETS, self); IMGUI_BEGIN_OR_RETURN(IMGUI_SPRITESHEETS, self);
_imgui_no_anm2_path_check(self);
ImVec2 windowSize = ImGui::GetContentRegionAvail(); 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 (_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();
@@ -2182,6 +2363,8 @@ static void _imgui_spritesheets(Imgui* self)
self->anm2->spritesheets[id].texture = texture; self->anm2->spritesheets[id].texture = texture;
std::filesystem::current_path(workingPath); std::filesystem::current_path(workingPath);
} }
imgui_log_push(self, IMGUI_LOG_RELOAD_SPRITESHEET);
} }
if (_imgui_button(IMGUI_SPRITESHEETS_REPLACE.copy({highlightedID == ID_NONE}), self)) 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_begin_child(IMGUI_CANVAS_VIEW_CHILD, self);
_imgui_drag_float(IMGUI_CANVAS_ZOOM, self, zoom); _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_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::Text(mousePositionString.c_str());
_imgui_end_child(); // IMGUI_CANVAS_VIEW_CHILD _imgui_end_child(); // IMGUI_CANVAS_VIEW_CHILD
@@ -2558,8 +2756,8 @@ static void _imgui_spritesheet_editor(Imgui* self)
{ {
if (self->settings->editorIsGridSnap) if (self->settings->editorIsGridSnap)
{ {
position.x = roundf(position.x / gridSize.x) * gridSize.x + gridOffset.x - (gridSize.x * 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 + gridOffset.y - (gridSize.y * 0.5f); position.y = roundf((position.y - gridSize.y) / gridSize.y) * gridSize.y + gridOffset.y - (gridSize.y * 0.5f);
} }
frame->pivot = position - frame->crop; frame->pivot = position - frame->crop;
@@ -2770,6 +2968,8 @@ static void _imgui_dock(Imgui* self)
_imgui_spritesheets(self); _imgui_spritesheets(self);
_imgui_animation_preview(self); _imgui_animation_preview(self);
_imgui_spritesheet_editor(self); _imgui_spritesheet_editor(self);
_imgui_layers(self);
_imgui_nulls(self);
_imgui_timeline(self); _imgui_timeline(self);
_imgui_onionskin(self); _imgui_onionskin(self);
_imgui_frame_properties(self); _imgui_frame_properties(self);
@@ -2875,20 +3075,15 @@ 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_open(self, droppedFile);
_imgui_anm2_open(self, droppedFile); else if (path_is_extension(droppedFile, ANM2_SPRITESHEET_EXTENSION)) _imgui_spritesheet_add(self, droppedFile);
else if (path_is_extension(droppedFile, ANM2_SPRITESHEET_EXTENSION)) else imgui_log_push(self, IMGUI_LOG_DRAG_DROP_ERROR);
_imgui_spritesheet_add(self, droppedFile);
else
imgui_log_push(self, IMGUI_LOG_DRAG_DROP_ERROR);
break; break;
} }
case SDL_EVENT_QUIT: case SDL_EVENT_QUIT:
if (self->isTryQuit) if (self->isTryQuit) self->isQuit = true;
self->isQuit = true; else imgui_quit(self);
else
imgui_quit(self);
break; break;
default: default:
break; break;

View File

@@ -59,7 +59,7 @@
#define IMGUI_TIMELINE_FRAME_MULTIPLE 5 #define IMGUI_TIMELINE_FRAME_MULTIPLE 5
#define IMGUI_TIMELINE_MERGE #define IMGUI_TIMELINE_MERGE
#define IMGUI_TOOL_COLOR_PICKER_DURATION 0.25f #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_CHORD_REPEAT_TIME 0.25f
#define IMGUI_ACTION_FRAME_CROP "Frame Crop" #define IMGUI_ACTION_FRAME_CROP "Frame Crop"
@@ -68,14 +68,13 @@
#define IMGUI_ACTION_ANIMATION_SWAP "Animation Swap" #define IMGUI_ACTION_ANIMATION_SWAP "Animation Swap"
#define IMGUI_ACTION_TRIGGER_MOVE "Trigger At Frame" #define IMGUI_ACTION_TRIGGER_MOVE "Trigger At Frame"
#define IMGUI_ACTION_FRAME_DELAY "Frame Delay" #define IMGUI_ACTION_FRAME_DELAY "Frame Delay"
#define IMGUI_ACTION_MOVE_PLAYHEAD "Move Playhead"
#define IMGUI_ACTION_DRAW "Draw" #define IMGUI_ACTION_DRAW "Draw"
#define IMGUI_ACTION_ERASE "Erase" #define IMGUI_ACTION_ERASE "Erase"
#define IMGUI_ACTION_MOVE "Move" #define IMGUI_ACTION_MOVE "Move"
#define IMGUI_ACTION_SCALE "Scale" #define IMGUI_ACTION_SCALE "Scale"
#define IMGUI_ACTION_ROTATE "Rotate" #define IMGUI_ACTION_ROTATE "Rotate"
#define IMGUI_ACTION_CROP "Crop" #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_REPLACE_SPRITESHEET "Replace Spritesheet"
#define IMGUI_ACTION_OPEN_FILE "Open File" #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_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_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_NONE "None"
#define IMGUI_ANIMATION_DEFAULT_FORMAT "(*) {}" #define IMGUI_ANIMATION_DEFAULT_FORMAT "(*) {}"
@@ -110,6 +112,8 @@
#define IMGUI_SPRITESHEET_FORMAT "#{} {}" #define IMGUI_SPRITESHEET_FORMAT "#{} {}"
#define IMGUI_SPRITESHEET_ID_FORMAT "#{}" #define IMGUI_SPRITESHEET_ID_FORMAT "#{}"
#define IMGUI_TIMELINE_ITEM_CHILD_FORMAT "#{} {}" #define IMGUI_TIMELINE_ITEM_CHILD_FORMAT "#{} {}"
#define IMGUI_LAYER_FORMAT "#{} {}"
#define IMGUI_NULL_FORMAT "#{} {}"
#define IMGUI_TIMELINE_FRAME_LABEL_FORMAT "## {}" #define IMGUI_TIMELINE_FRAME_LABEL_FORMAT "## {}"
#define IMGUI_SELECTABLE_INPUT_INT_FORMAT "#{}" #define IMGUI_SELECTABLE_INPUT_INT_FORMAT "#{}"
#define IMGUI_TIMELINE_ANIMATION_NONE "Select an animation to show timeline..." #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) 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) static inline void imgui_onionskin_toggle(Imgui* self)
@@ -604,7 +616,7 @@ enum ImguiItemType
IMGUI_DOCKSPACE, IMGUI_DOCKSPACE,
IMGUI_CHILD, IMGUI_CHILD,
IMGUI_TABLE, IMGUI_TABLE,
IMGUI_OPTION_POPUP, IMGUI_CONFIRM_POPUP,
IMGUI_SELECTABLE, IMGUI_SELECTABLE,
IMGUI_BUTTON, IMGUI_BUTTON,
IMGUI_RADIO_BUTTON, IMGUI_RADIO_BUTTON,
@@ -631,6 +643,7 @@ struct ImguiItemOverride
AtlasType atlas = ATLAS_NONE; AtlasType atlas = ATLAS_NONE;
bool isMnemonicDisabled{}; bool isMnemonicDisabled{};
s32 value{}; s32 value{};
s32 rowCount{};
}; };
struct ImguiItem; struct ImguiItem;
@@ -754,6 +767,7 @@ struct ImguiItem
if (override.size != ImVec2{}) out.size = override.size; if (override.size != ImVec2{}) out.size = override.size;
if (override.max != 0) out.max = override.max; if (override.max != 0) out.max = override.max;
if (override.value != 0) out.value = override.value; 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.atlas != ATLAS_NONE) out.atlas = override.atlas;
if (override.isMnemonicDisabled) out.isMnemonicDisabled = override.isMnemonicDisabled; if (override.isMnemonicDisabled) out.isMnemonicDisabled = override.isMnemonicDisabled;
return out; 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?" 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, IMGUI_ITEM(IMGUI_WIZARD,
self.label = "&Wizard", self.label = "&Wizard",
self.tooltip = "Opens the wizard menu, for neat functions related to the .anm2.", self.tooltip = "Opens the wizard menu, for neat functions related to the .anm2.",
@@ -996,7 +1015,7 @@ 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.snapshotAction = "Generate Animation from Grid", self.snapshotAction = "Generate Animation from Grid",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT, self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT,
self.isSameLine = true self.isSameLine = true
); );
@@ -1103,7 +1122,7 @@ 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.snapshotAction = "Scale Anm2", self.snapshotAction = "Scale Anm2",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT, self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT,
self.isSameLine = true self.isSameLine = true
); );
@@ -1111,12 +1130,20 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION,
self.label = "&Render Animation", self.label = "&Render Animation",
self.tooltip = "Renders the current animation preview; output options can be customized.", self.tooltip = "Renders the current animation preview; output options can be customized.",
self.popup = "Render Animation", self.popup = "Render Animation",
self.popupSize = {600, 125} self.popupSize = {600, 150},
self.popupType = IMGUI_POPUP_CENTER_WINDOW
); );
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_CHILD, IMGUI_ITEM(IMGUI_RENDER_ANIMATION_CHILD,
self.label = "## 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, IMGUI_ITEM(IMGUI_RENDER_ANIMATION_LOCATION_BROWSE,
@@ -1149,8 +1176,7 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION_OUTPUT,
self.label = "Output", self.label = "Output",
self.tooltip = "Select the rendered animation output.\nIt can either be one animated image or a sequence of frames.", 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.items = {std::begin(RENDER_TYPE_STRINGS), std::end(RENDER_TYPE_STRINGS)},
self.value = RENDER_PNG, self.value = RENDER_PNG
self.isSeparator = true
); );
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FORMAT, IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FORMAT,
@@ -1166,7 +1192,7 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION_CONFIRM,
self.popupType = IMGUI_POPUP_CENTER_WINDOW, self.popupType = IMGUI_POPUP_CENTER_WINDOW,
self.popupSize = {300, 60}, self.popupSize = {300, 60},
self.isSameLine = true, self.isSameLine = true,
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT
); );
IMGUI_ITEM(IMGUI_RENDERING_ANIMATION_CHILD, IMGUI_ITEM(IMGUI_RENDERING_ANIMATION_CHILD,
@@ -1259,6 +1285,73 @@ IMGUI_ITEM(IMGUI_DEFAULT_SETTINGS,
self.isSizeToText = true 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, IMGUI_ITEM(IMGUI_ANIMATIONS,
self.label = "Animations", self.label = "Animations",
self.flags = ImGuiWindowFlags_NoScrollbar | self.flags = ImGuiWindowFlags_NoScrollbar |
@@ -1354,7 +1447,7 @@ 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.snapshotAction = "Merge Animations", self.snapshotAction = "Merge Animations",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT, self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT,
self.isSameLine = true self.isSameLine = true
); );
@@ -1440,15 +1533,15 @@ IMGUI_ITEM(IMGUI_SPRITESHEETS_FOOTER_CHILD,
#define IMGUI_SPRITESHEETS_OPTIONS_SECOND_ROW_COUNT 3 #define IMGUI_SPRITESHEETS_OPTIONS_SECOND_ROW_COUNT 3
IMGUI_ITEM(IMGUI_SPRITESHEET_ADD, IMGUI_ITEM(IMGUI_SPRITESHEET_ADD,
self.label = "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.rowCount = IMGUI_SPRITESHEETS_OPTIONS_FIRST_ROW_COUNT,
self.isSameLine = true self.isSameLine = true
); );
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(s).",
self.snapshotAction = "Reload Spritesheet", self.snapshotAction = "Reload Spritesheet(s)",
self.rowCount = IMGUI_SPRITESHEETS_OPTIONS_FIRST_ROW_COUNT, self.rowCount = IMGUI_SPRITESHEETS_OPTIONS_FIRST_ROW_COUNT,
self.isSameLine = true self.isSameLine = true
); );
@@ -1490,7 +1583,9 @@ const ImVec2 IMGUI_CANVAS_CHILD_SIZE = {230, 85};
IMGUI_ITEM(IMGUI_CANVAS_GRID_CHILD, IMGUI_ITEM(IMGUI_CANVAS_GRID_CHILD,
self.label = "## Canvas Grid Child", self.label = "## Canvas Grid Child",
self.size = IMGUI_CANVAS_CHILD_SIZE, self.size = IMGUI_CANVAS_CHILD_SIZE,
self.flags = true self.flags = true,
self.windowFlags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
); );
IMGUI_ITEM(IMGUI_CANVAS_GRID, IMGUI_ITEM(IMGUI_CANVAS_GRID,
@@ -1525,7 +1620,9 @@ IMGUI_ITEM(IMGUI_CANVAS_GRID_OFFSET,
IMGUI_ITEM(IMGUI_CANVAS_VIEW_CHILD, IMGUI_ITEM(IMGUI_CANVAS_VIEW_CHILD,
self.label = "## View Child", self.label = "## View Child",
self.size = IMGUI_CANVAS_CHILD_SIZE, self.size = IMGUI_CANVAS_CHILD_SIZE,
self.flags = true self.flags = true,
self.windowFlags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
); );
IMGUI_ITEM(IMGUI_CANVAS_ZOOM, IMGUI_ITEM(IMGUI_CANVAS_ZOOM,
@@ -1537,12 +1634,12 @@ IMGUI_ITEM(IMGUI_CANVAS_ZOOM,
self.value = CANVAS_ZOOM_DEFAULT self.value = CANVAS_ZOOM_DEFAULT
); );
IMGUI_ITEM(IMGUI_CANVAS_VISUAL_CHILD, IMGUI_ITEM(IMGUI_CANVAS_VISUAL_CHILD,
self.label = "## Animation Preview Visual Child", self.label = "## Animation Preview Visual Child",
self.size = IMGUI_CANVAS_CHILD_SIZE, self.size = IMGUI_CANVAS_CHILD_SIZE,
self.flags = true self.flags = true,
self.windowFlags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
); );
IMGUI_ITEM(IMGUI_CANVAS_BACKGROUND_COLOR, IMGUI_ITEM(IMGUI_CANVAS_BACKGROUND_COLOR,
@@ -1566,7 +1663,9 @@ IMGUI_ITEM(IMGUI_CANVAS_ANIMATION_OVERLAY_TRANSPARENCY,
IMGUI_ITEM(IMGUI_CANVAS_HELPER_CHILD, IMGUI_ITEM(IMGUI_CANVAS_HELPER_CHILD,
self.label = "## Animation Preview Helper Child", self.label = "## Animation Preview Helper Child",
self.size = IMGUI_CANVAS_CHILD_SIZE, self.size = IMGUI_CANVAS_CHILD_SIZE,
self.flags = true self.flags = true,
self.windowFlags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse
); );
IMGUI_ITEM(IMGUI_CANVAS_AXES, IMGUI_ITEM(IMGUI_CANVAS_AXES,
@@ -1644,12 +1743,22 @@ IMGUI_ITEM(IMGUI_SPRITESHEET_EDITOR,
self.flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse self.flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse
); );
#define IMGUI_SPRITESHEET_EDITOR_VIEW_ROW_COUNT 2
IMGUI_ITEM(IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW, IMGUI_ITEM(IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW,
self.label = "Center View", self.label = "Center View",
self.tooltip = "Centers the current view on the spritesheet editor.", self.tooltip = "Centers the current view on the spritesheet editor.",
self.hotkey = HOTKEY_CENTER_VIEW, self.hotkey = HOTKEY_CENTER_VIEW,
self.focusWindow = IMGUI_SPRITESHEET_EDITOR.label, 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"); 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_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, 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.",
@@ -2028,12 +2151,6 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_HIDE_RECT,
self.atlas = ATLAS_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, IMGUI_ITEM(IMGUI_TIMELINE_FRAMES_CHILD,
self.label = "## 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, IMGUI_ITEM(IMGUI_TIMELINE_ITEM_FOOTER_CHILD,
self.label = "## Item Footer Child", self.label = "## Item Footer Child",
self.size = {IMGUI_TIMELINE_ITEM_CHILD.size.x, IMGUI_FOOTER_CHILD.size.y}, 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, IMGUI_ITEM(IMGUI_TIMELINE_OPTIONS_FOOTER_CHILD,
self.label = "## Options Footer Child", self.label = "## Options Footer Child",
self.size = {0, IMGUI_FOOTER_CHILD.size.y}, 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 #define IMGUI_TIMELINE_FOOTER_ITEM_CHILD_ITEM_COUNT 2
IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM, IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM,
self.label = "Add", self.label = "Add",
self.tooltip = "Adds an item (layer or null) to the animation.", 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 Popup", self.popup = "Add Item",
self.popupType = IMGUI_POPUP_BY_ITEM, self.popupType = IMGUI_POPUP_CENTER_WINDOW,
self.popupSize = {300, 350},
self.rowCount = IMGUI_TIMELINE_FOOTER_ITEM_CHILD_ITEM_COUNT, self.rowCount = IMGUI_TIMELINE_FOOTER_ITEM_CHILD_ITEM_COUNT,
self.isSameLine = true 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, 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.snapshotAction = "Add Layer", self.isSizeToText = true,
self.isSizeToText = true self.value = ANM2_LAYER,
self.isSameLine = 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 a game engine.",
self.snapshotAction = "Add Null", self.isSizeToText = true,
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, IMGUI_ITEM(IMGUI_TIMELINE_REMOVE_ITEM,
@@ -2211,7 +2360,7 @@ 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.snapshotAction = "Bake Frames", self.snapshotAction = "Bake Frames",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT, self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT,
self.isSameLine = true self.isSameLine = true
); );
@@ -2353,17 +2502,17 @@ IMGUI_ITEM(IMGUI_CHANGE_INPUT_INT,
self.step = 0 self.step = 0
); );
#define IMGUI_OPTION_POPUP_ROW_COUNT 2 #define IMGUI_CONFIRM_POPUP_ROW_COUNT 2
IMGUI_ITEM(IMGUI_POPUP_OK, IMGUI_ITEM(IMGUI_POPUP_OK,
self.label = "OK", self.label = "OK",
self.tooltip = "Confirm the action.", self.tooltip = "Confirm the action.",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT
); );
IMGUI_ITEM(IMGUI_POPUP_CANCEL, IMGUI_ITEM(IMGUI_POPUP_CANCEL,
self.label = "Cancel", self.label = "Cancel",
self.tooltip = "Cancel the action.", self.tooltip = "Cancel the action.",
self.rowCount = IMGUI_OPTION_POPUP_ROW_COUNT self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT
); );
IMGUI_ITEM(IMGUI_LOG_WINDOW, 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()); glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, framebufferPixels.data());
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); 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); self->renderFrames.push_back(frameTexture);
} }
@@ -199,7 +199,7 @@ void preview_draw(Preview* self)
if (self->settings->previewIsIcons && animation->rootAnimation.isVisible && root.isVisible) if (self->settings->previewIsIcons && animation->rootAnimation.isVisible && root.isVisible)
root_draw(root, colorOffset, alphaOffset, isOnionskin); 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); layer_draw(rootModel, id, time, colorOffset, alphaOffset, isOnionskin);
if (self->settings->previewIsIcons) if (self->settings->previewIsIcons)

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
#include "imgui.h" #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_ERROR "Failed to initialize SDL! {}"
#define STATE_SDL_INIT_INFO "Initialized SDL" #define STATE_SDL_INIT_INFO "Initialized SDL"
#define STATE_MIX_INIT_WARNING "Unable to initialize SDL_mixer! {}" #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) static void _texture_gl_set(Texture* self, const u8* data)
{ {
if (self->id == GL_ID_NONE) if (self->id == GL_ID_NONE) glGenTextures(1, &self->id);
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);
@@ -43,7 +42,7 @@ 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)
{ {
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) if (!data)
{ {
@@ -60,13 +59,12 @@ bool texture_from_path_init(Texture* self, const std::string& path)
return true; 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 = Texture{};
self->size = size; 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) if (!textureData)
{ {
@@ -79,11 +77,11 @@ bool texture_from_encoded_data_init(Texture* self, ivec2 size, s32 channels, con
return true; 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 = Texture{};
self->size = size; self->size = size;
self->channels = channels; self->isInvalid = false;
_texture_gl_set(self, data); _texture_gl_set(self, data);

View File

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