Frame movement on timeline changed
This commit is contained in:
@@ -114,6 +114,14 @@ static inline std::string string_to_lowercase(std::string string) {
|
||||
return string;
|
||||
}
|
||||
|
||||
static inline std::string string_backslash_replace(std::string string)
|
||||
{
|
||||
for (char& character : string)
|
||||
if (character == '\\')
|
||||
character = '/';
|
||||
return string;
|
||||
}
|
||||
|
||||
#define FLOAT_FORMAT_MAX_DECIMALS 5
|
||||
#define FLOAT_FORMAT_EPSILON 1e-5f
|
||||
static constexpr f32 FLOAT_FORMAT_POW10[] = {
|
||||
|
||||
125
src/anm2.cpp
125
src/anm2.cpp
@@ -450,9 +450,12 @@ bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures)
|
||||
// the paths are case-insensitive (as the game was developed on Windows)
|
||||
// However when using the resource dumper, the spritesheet paths are all lowercase (on Linux anyways)
|
||||
// If the check doesn't work, set the spritesheet path to lowercase
|
||||
// If the check doesn't work, replace backslashes with slashes
|
||||
// At the minimum this should make all textures be able to be loaded on Linux
|
||||
// If it doesn't work beyond that then that's on the user :^)
|
||||
addSpritesheet.path = attribute->Value();
|
||||
if (!path_exists(addSpritesheet.path)) addSpritesheet.path = string_to_lowercase(addSpritesheet.path);
|
||||
if (!path_exists(addSpritesheet.path)) addSpritesheet.path = string_backslash_replace(addSpritesheet.path);
|
||||
if (isTextures) texture_from_path_init(&addSpritesheet.texture, addSpritesheet.path);
|
||||
break;
|
||||
case ANM2_ATTRIBUTE_ID: id = std::atoi(attribute->Value()); break;
|
||||
@@ -846,9 +849,9 @@ Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 index = reference->frameIndex + 1;
|
||||
s32 index = reference->frameIndex;
|
||||
|
||||
if (index >= (s32)item->frames.size())
|
||||
if (index < 0 || index >= (s32)item->frames.size())
|
||||
{
|
||||
item->frames.push_back(frameAdd);
|
||||
return &item->frames.back();
|
||||
@@ -951,13 +954,13 @@ void anm2_animation_merge(Anm2* self, s32 animationID, const std::vector<s32>& m
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ANM2_MERGE_APPEND_FRAMES:
|
||||
case ANM2_MERGE_APPEND:
|
||||
destinationItem.frames.insert(destinationItem.frames.end(), sourceItem.frames.begin(), sourceItem.frames.end());
|
||||
break;
|
||||
case ANM2_MERGE_PREPEND_FRAMES:
|
||||
case ANM2_MERGE_PREPEND:
|
||||
destinationItem.frames.insert(destinationItem.frames.begin(), sourceItem.frames.begin(), sourceItem.frames.end());
|
||||
break;
|
||||
case ANM2_MERGE_REPLACE_FRAMES:
|
||||
case ANM2_MERGE_REPLACE:
|
||||
if (destinationItem.frames.size() < sourceItem.frames.size())
|
||||
destinationItem.frames.resize(sourceItem.frames.size());
|
||||
for (s32 i = 0; i < (s32)sourceItem.frames.size(); i++)
|
||||
@@ -1230,4 +1233,114 @@ bool anm2_frame_deserialize_from_xml(Anm2Frame* frame, const std::string& xml)
|
||||
_anm2_frame_deserialize(frame, element);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void anm2_merge(Anm2* self, const std::string& path, Anm2MergeType type)
|
||||
{
|
||||
Anm2 anm2;
|
||||
|
||||
if (anm2_deserialize(&anm2, path, false))
|
||||
{
|
||||
std::unordered_map<s32, s32> spritesheetMap;
|
||||
for (auto& [id, spritesheet] : anm2.spritesheets)
|
||||
{
|
||||
bool isExists = false;
|
||||
|
||||
for (auto& [selfID, selfSpritesheet] : self->spritesheets)
|
||||
{
|
||||
if (spritesheet.path == selfSpritesheet.path) isExists = true;
|
||||
spritesheetMap[id] = selfID;
|
||||
}
|
||||
|
||||
if (isExists) continue;
|
||||
|
||||
s32 nextID = map_next_id_get(self->spritesheets);
|
||||
self->spritesheet[nextID] = spritesheet;
|
||||
spritesheetMap[id] = nextID;
|
||||
}
|
||||
|
||||
std::unordered_map<s32, s32> layerMap;
|
||||
for (auto& [id, layer] : anm2.layers)
|
||||
{
|
||||
bool isExists = false;
|
||||
|
||||
layer.spritesheetID = spritesheetMap[layer.spritesheetID];
|
||||
|
||||
for (auto& [selfID, selfLayer] : self->layers)
|
||||
{
|
||||
if (layer.name == selfLayer.name) isExists = true;
|
||||
layerMap[id] = selfID;
|
||||
}
|
||||
|
||||
if (isExists) continue;
|
||||
|
||||
s32 nextID = map_next_id_get(self->layers);
|
||||
self->layer[nextID] = layer;
|
||||
layerMap[id] = nextID;
|
||||
}
|
||||
|
||||
std::unordered_map<s32, s32> nullMap;
|
||||
for (auto& [id, null] : anm2.nulls)
|
||||
{
|
||||
bool isExists = false;
|
||||
|
||||
for (auto& [selfID, selfNull] : self->nulls)
|
||||
{
|
||||
if (null.name == selfNull.name) isExists = true;
|
||||
nullMap[id] = selfID;
|
||||
}
|
||||
|
||||
if (isExists) continue;
|
||||
|
||||
s32 nextID = map_next_id_get(self->nulls);
|
||||
self->null[nextID] = null;
|
||||
nullMap[id] = nextID;
|
||||
}
|
||||
|
||||
std::unordered_map<s32, s32> eventMap;
|
||||
for (auto& [id, event] : anm2.events)
|
||||
{
|
||||
bool isExists = false;
|
||||
|
||||
for (auto& [selfID, selfEvent] : self->events)
|
||||
{
|
||||
if (event.name == selfEvent.name) isExists = true;
|
||||
eventMap[id] = selfID;
|
||||
}
|
||||
|
||||
if (isExists) continue;
|
||||
|
||||
s32 nextID = map_next_id_get(self->events);
|
||||
self->event[nextID] = event;
|
||||
eventMap[id] = nextID;
|
||||
}
|
||||
|
||||
for (auto& [id, animation] : anm2.animations)
|
||||
{
|
||||
bool isExists = false;
|
||||
|
||||
for (auto& [selfID, selfAnimation] : self->animations)
|
||||
{
|
||||
if (event.name == selfAnimation.name) isExists = true;
|
||||
eventMap[id] = selfID;
|
||||
}
|
||||
|
||||
if (isExists) continue;
|
||||
|
||||
for (auto& frame : animation.rootAnimation.frames)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
for (auto& [layerID, layerAnimation] : animation.layerAnimations)
|
||||
{
|
||||
s32 newLayerID = layerMap[layerID];
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -249,9 +249,9 @@ struct Anm2FrameChange
|
||||
|
||||
enum Anm2MergeType
|
||||
{
|
||||
ANM2_MERGE_APPEND_FRAMES,
|
||||
ANM2_MERGE_REPLACE_FRAMES,
|
||||
ANM2_MERGE_PREPEND_FRAMES,
|
||||
ANM2_MERGE_APPEND,
|
||||
ANM2_MERGE_REPLACE,
|
||||
ANM2_MERGE_PREPEND,
|
||||
ANM2_MERGE_IGNORE
|
||||
};
|
||||
|
||||
|
||||
292
src/imgui.cpp
292
src/imgui.cpp
@@ -706,7 +706,7 @@ static void _imgui_timeline(Imgui* self)
|
||||
|
||||
ImVec2 scrollDelta{};
|
||||
|
||||
if (_imgui_is_window_hovered())
|
||||
if (_imgui_is_window_hovered() && !imgui_is_any_popup_open())
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
f32 lineHeight = ImGui::GetTextLineHeight();
|
||||
@@ -851,6 +851,7 @@ static void _imgui_timeline(Imgui* self)
|
||||
)
|
||||
)
|
||||
*self->reference = reference;
|
||||
|
||||
break;
|
||||
case ANM2_NULL:
|
||||
null = &self->anm2->nulls[reference.itemID];
|
||||
@@ -869,6 +870,117 @@ static void _imgui_timeline(Imgui* self)
|
||||
break;
|
||||
}
|
||||
|
||||
if (imgui_begin_popup_modal(IMGUI_POPUP_ITEM_PROPERTIES, self, IMGUI_POPUP_ITEM_PROPERTIES_SIZE))
|
||||
{
|
||||
static s32 selectedLayerID = ID_NONE;
|
||||
static s32 selectedNullID = ID_NONE;
|
||||
Anm2Type& type = reference.itemType;
|
||||
|
||||
_imgui_begin_child(IMGUI_TIMELINE_ITEM_PROPERTIES_TYPE_CHILD, self);
|
||||
|
||||
_imgui_radio_button(IMGUI_TIMELINE_ITEM_PROPERTIES_LAYER.copy({true}), self, (s32&)type);
|
||||
_imgui_radio_button(IMGUI_TIMELINE_ITEM_PROPERTIES_NULL.copy({true}), self, (s32&)type);
|
||||
|
||||
_imgui_end_child(); // IMGUI_TIMELINE_ITEM_PROPERTIES_TYPE_CHILD
|
||||
|
||||
_imgui_begin_child(IMGUI_TIMELINE_ITEM_PROPERTIES_ITEMS_CHILD, self);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ANM2_LAYER:
|
||||
default:
|
||||
{
|
||||
for (auto & [id, layer] : self->anm2->layers)
|
||||
{
|
||||
if (id == reference.itemID) continue;
|
||||
|
||||
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)
|
||||
{
|
||||
if (id == reference.itemID) continue;
|
||||
|
||||
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_ITEM_PROPERTIES_ITEMS_CHILD
|
||||
|
||||
_imgui_begin_child(IMGUI_TIMELINE_ITEM_PROPERTIES_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_ITEM_PROPERTIES_CONFIRM.copy({isDisabled}), self))
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ANM2_LAYER:
|
||||
if (!map_find(animation->layerAnimations, selectedLayerID))
|
||||
{
|
||||
anm2_animation_layer_animation_add(animation, selectedLayerID);
|
||||
anm2_animation_layer_animation_remove(animation, reference.itemID);
|
||||
}
|
||||
else
|
||||
map_swap(animation->layerAnimations, selectedLayerID, reference.itemID);
|
||||
|
||||
*self->reference = {self->reference->animationID, ANM2_LAYER, selectedLayerID};
|
||||
break;
|
||||
case ANM2_NULL:
|
||||
if (!map_find(animation->nullAnimations, selectedNullID))
|
||||
{
|
||||
anm2_animation_null_animation_add(animation, selectedNullID);
|
||||
anm2_animation_null_animation_remove(animation, reference.itemID);
|
||||
|
||||
}
|
||||
else
|
||||
map_swap(animation->nullAnimations, selectedNullID, reference.itemID);
|
||||
|
||||
*self->reference = {self->reference->animationID, ANM2_NULL, 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_ITEM_PROPERTIES_OPTIONS_CHILD
|
||||
|
||||
imgui_end_popup(self);
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None) && !dragDrop.empty())
|
||||
{
|
||||
@@ -940,6 +1052,8 @@ static void _imgui_timeline(Imgui* self)
|
||||
{
|
||||
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
|
||||
|
||||
imgui_snapshot(self, IMGUI_ACTION_ITEM_SWAP);
|
||||
|
||||
switch (swapItemReference.itemType)
|
||||
{
|
||||
case ANM2_LAYER:
|
||||
@@ -990,6 +1104,8 @@ static void _imgui_timeline(Imgui* self)
|
||||
_imgui_spritesheet_editor_set(self, self->anm2->layers[self->reference->itemID].spritesheetID);
|
||||
}
|
||||
}
|
||||
else
|
||||
hoverReference.frameIndex = INDEX_NONE;
|
||||
|
||||
s32 start = (s32)std::floor(scroll.x / frameSize.x) - 1;
|
||||
if (start < 0) start = 0;
|
||||
@@ -1018,8 +1134,8 @@ static void _imgui_timeline(Imgui* self)
|
||||
static s32 frameDelayStart{};
|
||||
static f32 frameDelayTimeStart{};
|
||||
const bool isModCtrl = ImGui::IsKeyDown(IMGUI_INPUT_CTRL);
|
||||
static Anm2Frame* draggingFrame = nullptr;
|
||||
static Anm2Type draggingFrameType = ANM2_NONE;
|
||||
static bool isFrameSwap = false;
|
||||
static bool isDrag = false;
|
||||
|
||||
ImGui::PushID(i);
|
||||
reference.frameIndex = i;
|
||||
@@ -1037,90 +1153,91 @@ static void _imgui_timeline(Imgui* self)
|
||||
|
||||
ImGui::SetCursorPos(framePos);
|
||||
|
||||
if (_imgui_atlas_button(frameButton, self)) *self->reference = reference;
|
||||
|
||||
if (ImGui::IsItemActivated())
|
||||
if (_imgui_atlas_button(frameButton, self))
|
||||
{
|
||||
if (type == ANM2_TRIGGERS || isModCtrl)
|
||||
{
|
||||
draggingFrame = &frame;
|
||||
draggingFrameType = type;
|
||||
*self->reference = reference;
|
||||
}
|
||||
|
||||
if (type == ANM2_TRIGGERS)
|
||||
imgui_snapshot(self, IMGUI_ACTION_TRIGGER_MOVE);
|
||||
else if (isModCtrl)
|
||||
{
|
||||
imgui_snapshot(self, IMGUI_ACTION_FRAME_DELAY);
|
||||
frameDelayStart = draggingFrame->delay;
|
||||
frameDelayTimeStart = frameTime;
|
||||
}
|
||||
*self->reference = reference;
|
||||
frameDelayStart = frame.delay;
|
||||
frameDelayTimeStart = frameTime;
|
||||
}
|
||||
|
||||
if (draggingFrame)
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoDisableHover))
|
||||
{
|
||||
if (draggingFrameType == ANM2_TRIGGERS)
|
||||
if (!isDrag)
|
||||
{
|
||||
draggingFrame->atFrame = std::max(frameTime, 0);
|
||||
for (auto& frameCheck : animation->triggers.frames)
|
||||
*self->reference = reference;
|
||||
frameDelayStart = frame.delay;
|
||||
frameDelayTimeStart = frameTime;
|
||||
isFrameSwap = false;
|
||||
isDrag = true;
|
||||
}
|
||||
|
||||
ImGui::SetDragDropPayload(frameButton.drag_drop_get(), &reference, sizeof(Anm2Reference));
|
||||
if (!isModCtrl) timeline_item_frame(i, frame);
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
if (isDrag)
|
||||
{
|
||||
if (Anm2Frame* referenceFrame = anm2_frame_from_reference(self->anm2, self->reference))
|
||||
{
|
||||
switch (self->reference->itemType)
|
||||
{
|
||||
if (draggingFrame == &frameCheck) continue;
|
||||
if (draggingFrame->atFrame == frameCheck.atFrame)
|
||||
case ANM2_TRIGGERS:
|
||||
{
|
||||
draggingFrame->atFrame++;
|
||||
referenceFrame->atFrame = std::max(frameTime, 0);
|
||||
for (auto& trigger : animation->triggers.frames)
|
||||
{
|
||||
if (&trigger == referenceFrame) continue;
|
||||
if (trigger.atFrame == referenceFrame->atFrame)
|
||||
{
|
||||
referenceFrame->atFrame++; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isModCtrl)
|
||||
draggingFrame->delay = std::max(frameDelayStart + (s32)(frameTime - frameDelayTimeStart), ANM2_FRAME_NUM_MIN);
|
||||
|
||||
if (ImGui::IsMouseReleased(0))
|
||||
{
|
||||
draggingFrame = nullptr;
|
||||
draggingFrameType = ANM2_NONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
|
||||
{
|
||||
ImGui::SetDragDropPayload(frameButton.drag_drop_get(), &reference, sizeof(Anm2Reference));
|
||||
timeline_item_frame(i, frame);
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
if (ImGui::BeginDragDropTarget())
|
||||
{
|
||||
Anm2Reference swapReference;
|
||||
if (const ImGuiPayload* payload = ImGui::GetDragDropPayload())
|
||||
swapReference = *(Anm2Reference*)payload->Data;
|
||||
|
||||
if (swapReference != reference && reference.itemType == swapReference.itemType)
|
||||
{
|
||||
if (ImGui::AcceptDragDropPayload(frameButton.drag_drop_get()))
|
||||
case ANM2_ROOT:
|
||||
case ANM2_LAYER:
|
||||
case ANM2_NULL:
|
||||
{
|
||||
imgui_snapshot(self, IMGUI_ACTION_FRAME_SWAP);
|
||||
|
||||
Anm2Frame* swapFrame = anm2_frame_from_reference(self->anm2, &reference);
|
||||
Anm2Frame* dragFrame = anm2_frame_from_reference(self->anm2, &swapReference);
|
||||
|
||||
if (swapFrame && dragFrame)
|
||||
{
|
||||
Anm2Frame oldFrame = *swapFrame;
|
||||
|
||||
*swapFrame = *dragFrame;
|
||||
*dragFrame = oldFrame;
|
||||
|
||||
*self->reference = swapReference;
|
||||
}
|
||||
if (isModCtrl)
|
||||
referenceFrame->delay = std::max(frameDelayStart + (s32)(frameTime - frameDelayTimeStart), ANM2_FRAME_NUM_MIN);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::BeginDragDropTarget())
|
||||
{
|
||||
ImGui::AcceptDragDropPayload(frameButton.drag_drop_get());
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
if (isDrag && ImGui::IsMouseReleased(0))
|
||||
{
|
||||
if
|
||||
( !isFrameSwap &&
|
||||
*self->reference != hoverReference &&
|
||||
self->reference->itemType == hoverReference.itemType
|
||||
)
|
||||
{
|
||||
imgui_snapshot(self, IMGUI_ACTION_FRAME_MOVE);
|
||||
if (Anm2Frame* referenceFrame = anm2_frame_from_reference(self->anm2, self->reference))
|
||||
{
|
||||
Anm2Reference addReference = hoverReference;
|
||||
addReference.frameIndex = std::min(0, addReference.frameIndex - 1);
|
||||
Anm2Frame addFrame = *referenceFrame;
|
||||
anm2_frame_remove(self->anm2, self->reference);
|
||||
anm2_frame_add(self->anm2, &addFrame, &hoverReference);
|
||||
*self->reference = hoverReference;
|
||||
hoverReference = Anm2Reference();
|
||||
}
|
||||
}
|
||||
|
||||
isDrag = false;
|
||||
}
|
||||
|
||||
if (i < (s32)item->frames.size() - 1) ImGui::SameLine();
|
||||
|
||||
ImGui::PopID();
|
||||
@@ -1201,14 +1318,14 @@ static void _imgui_timeline(Imgui* self)
|
||||
static s32 selectedNullID = ID_NONE;
|
||||
s32& type = self->settings->timelineAddItemType;
|
||||
|
||||
_imgui_begin_child(IMGUI_TIMELINE_ADD_ITEM_TYPE_CHILD, self);
|
||||
_imgui_begin_child(IMGUI_TIMELINE_ITEM_PROPERTIES_TYPE_CHILD, self);
|
||||
|
||||
_imgui_radio_button(IMGUI_TIMELINE_ADD_ITEM_LAYER, self, type);
|
||||
_imgui_radio_button(IMGUI_TIMELINE_ADD_ITEM_NULL, self, type);
|
||||
_imgui_radio_button(IMGUI_TIMELINE_ITEM_PROPERTIES_LAYER, self, type);
|
||||
_imgui_radio_button(IMGUI_TIMELINE_ITEM_PROPERTIES_NULL, self, type);
|
||||
|
||||
_imgui_end_child(); // IMGUI_TIMELINE_ADD_ITEM_TYPE_CHILD
|
||||
_imgui_end_child(); // IMGUI_TIMELINE_ITEM_PROPERTIES_TYPE_CHILD
|
||||
|
||||
_imgui_begin_child(IMGUI_TIMELINE_ADD_ITEM_ITEMS_CHILD, self);
|
||||
_imgui_begin_child(IMGUI_TIMELINE_ITEM_PROPERTIES_ITEMS_CHILD, self);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
@@ -1251,9 +1368,9 @@ static void _imgui_timeline(Imgui* self)
|
||||
}
|
||||
}
|
||||
|
||||
_imgui_end_child(); // IMGUI_TIMELINE_ADD_ITEM_ITEMS_CHILD
|
||||
_imgui_end_child(); // IMGUI_TIMELINE_ITEM_PROPERTIES_ITEMS_CHILD
|
||||
|
||||
_imgui_begin_child(IMGUI_TIMELINE_ADD_ITEM_OPTIONS_CHILD, self);
|
||||
_imgui_begin_child(IMGUI_TIMELINE_ITEM_PROPERTIES_OPTIONS_CHILD, self);
|
||||
|
||||
if (self->anm2->layers.size() == 0) selectedLayerID = ID_NONE;
|
||||
if (self->anm2->nulls.size() == 0) selectedNullID = ID_NONE;
|
||||
@@ -1262,20 +1379,27 @@ static void _imgui_timeline(Imgui* self)
|
||||
(type == ANM2_LAYER && selectedLayerID == ID_NONE) ||
|
||||
(type == ANM2_NULL && selectedNullID == ID_NONE);
|
||||
|
||||
if (_imgui_button(IMGUI_TIMELINE_ADD_ITEM_ADD.copy({isDisabled}), self))
|
||||
if (_imgui_button(IMGUI_TIMELINE_ITEM_PROPERTIES_CONFIRM.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;
|
||||
case ANM2_LAYER:
|
||||
anm2_animation_layer_animation_add(animation, selectedLayerID);
|
||||
*self->reference = {self->reference->animationID, ANM2_LAYER, selectedLayerID};
|
||||
break;
|
||||
case ANM2_NULL:
|
||||
anm2_animation_null_animation_add(animation, selectedNullID);
|
||||
*self->reference = {self->reference->animationID, ANM2_NULL, 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_child(); // IMGUI_TIMELINE_ITEM_PROPERTIES_OPTIONS_CHILD
|
||||
|
||||
imgui_end_popup(self);
|
||||
}
|
||||
|
||||
104
src/imgui.h
104
src/imgui.h
@@ -63,10 +63,11 @@
|
||||
#define IMGUI_CHORD_REPEAT_TIME 0.25f
|
||||
|
||||
#define IMGUI_ACTION_FRAME_CROP "Frame Crop"
|
||||
#define IMGUI_ACTION_FRAME_SWAP "Frame Swap"
|
||||
#define IMGUI_ACTION_FRAME_MOVE "Frame Move"
|
||||
|
||||
#define IMGUI_ACTION_ANIMATION_SWAP "Animation Swap"
|
||||
#define IMGUI_ACTION_TRIGGER_MOVE "Trigger At Frame"
|
||||
#define IMGUI_ACTION_ITEM_SWAP "Item Swap"
|
||||
#define IMGUI_ACTION_FRAME_DELAY "Frame Delay"
|
||||
#define IMGUI_ACTION_DRAW "Draw"
|
||||
#define IMGUI_ACTION_ERASE "Erase"
|
||||
@@ -78,6 +79,7 @@
|
||||
#define IMGUI_ACTION_REPLACE_SPRITESHEET "Replace Spritesheet"
|
||||
#define IMGUI_ACTION_OPEN_FILE "Open File"
|
||||
|
||||
#define IMGUI_SET_ITEM_PROPERTIES_POPUP "Item Properties"
|
||||
#define IMGUI_POPUP_FLAGS ImGuiWindowFlags_NoMove
|
||||
#define IMGUI_POPUP_MODAL_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize
|
||||
|
||||
@@ -1429,20 +1431,20 @@ IMGUI_ITEM(IMGUI_MERGE_ON_CONFLICT, self.label = "On Conflict");
|
||||
IMGUI_ITEM(IMGUI_MERGE_APPEND_FRAMES,
|
||||
self.label = "Append Frames ",
|
||||
self.tooltip = "On frame conflict, the merged animation will have the selected animations' frames appended.",
|
||||
self.value = ANM2_MERGE_APPEND_FRAMES,
|
||||
self.value = ANM2_MERGE_APPEND,
|
||||
self.isSameLine = true
|
||||
);
|
||||
|
||||
IMGUI_ITEM(IMGUI_MERGE_REPLACE_FRAMES,
|
||||
self.label = "Replace Frames",
|
||||
self.tooltip = "On frame conflict, the merged animation will have the latest selected animations' frames.",
|
||||
self.value = ANM2_MERGE_REPLACE_FRAMES
|
||||
self.value = ANM2_MERGE_REPLACE
|
||||
);
|
||||
|
||||
IMGUI_ITEM(IMGUI_MERGE_PREPEND_FRAMES,
|
||||
self.label = "Prepend Frames",
|
||||
self.tooltip = "On frame conflict, the merged animation will have the selected animations' frames prepended.",
|
||||
self.value = ANM2_MERGE_PREPEND_FRAMES,
|
||||
self.value = ANM2_MERGE_PREPEND,
|
||||
self.isSameLine = true
|
||||
);
|
||||
|
||||
@@ -2085,6 +2087,51 @@ const inline ImguiItem* IMGUI_TIMELINE_ITEM_CHILDS[ANM2_COUNT]
|
||||
&IMGUI_TIMELINE_ITEM_TRIGGERS_CHILD
|
||||
};
|
||||
|
||||
#define IMGUI_POPUP_ITEM_PROPERTIES "Item Properties"
|
||||
#define IMGUI_POPUP_ITEM_PROPERTIES_TYPE IMGUI_POPUP_CENTER_WINDOW
|
||||
const ImVec2 IMGUI_POPUP_ITEM_PROPERTIES_SIZE = {300, 350};
|
||||
|
||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_PROPERTIES_TYPE_CHILD,
|
||||
self.label = "## Item Properties Type Child",
|
||||
self.size = {IMGUI_POPUP_ITEM_PROPERTIES_SIZE.x, 35},
|
||||
self.flags = true
|
||||
);
|
||||
|
||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_PROPERTIES_LAYER,
|
||||
self.label = "Layer",
|
||||
self.tooltip = "The item will be a layer item.\nA layer item is a primary graphical item, using a spritesheet.",
|
||||
self.isSizeToText = true,
|
||||
self.value = ANM2_LAYER,
|
||||
self.isSameLine = true
|
||||
);
|
||||
|
||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_PROPERTIES_NULL,
|
||||
self.label = "Null",
|
||||
self.tooltip = "The item will be 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_ITEM_PROPERTIES_ITEMS_CHILD,
|
||||
self.label = "## Item Properties Items",
|
||||
self.size = {IMGUI_POPUP_ITEM_PROPERTIES_SIZE.x, 250},
|
||||
self.flags = true
|
||||
);
|
||||
|
||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_PROPERTIES_OPTIONS_CHILD,
|
||||
self.label = "## Item Properties Options Child",
|
||||
self.size = {IMGUI_POPUP_ITEM_PROPERTIES_SIZE.x, 35},
|
||||
self.flags = true
|
||||
);
|
||||
|
||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_PROPERTIES_CONFIRM,
|
||||
self.label = "Confirm",
|
||||
self.tooltip = "Set the timeline item's properties.",
|
||||
self.snapshotAction = "Timeline Item Change",
|
||||
self.rowCount = IMGUI_CONFIRM_POPUP_ROW_COUNT,
|
||||
self.isSameLine = true
|
||||
);
|
||||
|
||||
IMGUI_ITEM(IMGUI_TIMELINE_ITEM_SELECTABLE,
|
||||
self.label = "## Selectable",
|
||||
self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE
|
||||
@@ -2101,6 +2148,8 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_LAYER_SELECTABLE,
|
||||
self.label = "## Layer Selectable",
|
||||
self.tooltip = "A layer item.\nA graphical item within the animation.",
|
||||
self.dragDrop = "## Layer Drag Drop",
|
||||
self.popup = IMGUI_POPUP_ITEM_PROPERTIES,
|
||||
self.popupType = IMGUI_POPUP_ITEM_PROPERTIES_TYPE,
|
||||
self.atlas = ATLAS_LAYER,
|
||||
self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE
|
||||
);
|
||||
@@ -2109,6 +2158,8 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_NULL_SELECTABLE,
|
||||
self.label = "## Null Selectable",
|
||||
self.tooltip = "A null item.\nAn invisible item within the animation that is accessible via a game engine.",
|
||||
self.dragDrop = "## Null Drag Drop",
|
||||
self.popup = IMGUI_POPUP_ITEM_PROPERTIES,
|
||||
self.popupType = IMGUI_POPUP_ITEM_PROPERTIES_TYPE,
|
||||
self.atlas = ATLAS_NULL,
|
||||
self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE
|
||||
);
|
||||
@@ -2188,6 +2239,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_FRAME, self.label = "## Frame");
|
||||
static const vec4 IMGUI_FRAME_BORDER_COLOR = {1.0f, 1.0f, 1.0f, 0.25f};
|
||||
IMGUI_ITEM(IMGUI_TIMELINE_ROOT_FRAME,
|
||||
self.label = "## Root Frame",
|
||||
self.snapshotAction = "Root Frame",
|
||||
self.color = {{0.14f, 0.27f, 0.39f, 1.0f}, {0.28f, 0.54f, 0.78f, 1.0f}, {0.36f, 0.70f, 0.95f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
|
||||
self.size = IMGUI_TIMELINE_FRAME_SIZE,
|
||||
self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET,
|
||||
@@ -2197,6 +2249,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_ROOT_FRAME,
|
||||
IMGUI_ITEM(IMGUI_TIMELINE_LAYER_FRAME,
|
||||
self.label = "## Layer Frame",
|
||||
self.dragDrop = "## Layer Frame Drag Drop",
|
||||
self.snapshotAction = "Layer Frame",
|
||||
self.color = {{0.45f, 0.18f, 0.07f, 1.0f}, {0.78f, 0.32f, 0.12f, 1.0f}, {0.95f, 0.40f, 0.15f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
|
||||
self.size = IMGUI_TIMELINE_FRAME_SIZE,
|
||||
self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET,
|
||||
@@ -2206,6 +2259,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_LAYER_FRAME,
|
||||
IMGUI_ITEM(IMGUI_TIMELINE_NULL_FRAME,
|
||||
self.label = "## Null Frame",
|
||||
self.dragDrop = "## Null Frame Drag Drop",
|
||||
self.snapshotAction = "Null Frame",
|
||||
self.color = {{0.17f, 0.33f, 0.17f, 1.0f}, {0.34f, 0.68f, 0.34f, 1.0f}, {0.44f, 0.88f, 0.44f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
|
||||
self.size = IMGUI_TIMELINE_FRAME_SIZE,
|
||||
self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET,
|
||||
@@ -2214,6 +2268,7 @@ IMGUI_ITEM(IMGUI_TIMELINE_NULL_FRAME,
|
||||
|
||||
IMGUI_ITEM(IMGUI_TIMELINE_TRIGGERS_FRAME,
|
||||
self.label = "## Triggers Frame",
|
||||
self.snapshotAction = "Trigger",
|
||||
self.color = {{0.36f, 0.14f, 0.24f, 1.0f}, {0.72f, 0.28f, 0.48f, 1.0f}, {0.92f, 0.36f, 0.60f, 1.0f}, IMGUI_FRAME_BORDER_COLOR},
|
||||
self.size = IMGUI_TIMELINE_FRAME_SIZE,
|
||||
self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET,
|
||||
@@ -2249,52 +2304,13 @@ IMGUI_ITEM(IMGUI_TIMELINE_ADD_ITEM,
|
||||
self.label = "Add",
|
||||
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.popupType = IMGUI_POPUP_ITEM_PROPERTIES_TYPE,
|
||||
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.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 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,
|
||||
self.label = "Remove",
|
||||
|
||||
@@ -37,6 +37,7 @@ void preview_tick(Preview* self)
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, self->canvas.fbo);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, framebufferPixels.data());
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
X(editorGridColor, EDITOR_GRID_COLOR, TYPE_VEC4, {1.0,1.0,1.0,0.125}) \
|
||||
X(editorBackgroundColor, EDITOR_BACKGROUND_COLOR, TYPE_VEC4, {0.113,0.184,0.286,1.0}) \
|
||||
\
|
||||
X(mergeType, MERGE_TYPE, TYPE_INT, ANM2_MERGE_APPEND_FRAMES) \
|
||||
X(mergeType, MERGE_TYPE, TYPE_INT, ANM2_MERGE_APPEND) \
|
||||
X(mergeIsDeleteAnimationsAfter,MERGE_IS_DELETE_ANIMATIONS_AFTER,TYPE_BOOL, false) \
|
||||
\
|
||||
X(bakeInterval, BAKE_INTERVAL, TYPE_INT, 1) \
|
||||
|
||||
@@ -43,6 +43,6 @@ Alternatively, if you have subscribed to the mod, you can find the latest releas
|
||||
[h3]Happy animating![/h3]
|
||||
[img]https://files.catbox.moe/4auc1c.gif[/img]
|
||||
</description>
|
||||
<version>1.2</version>
|
||||
<version>1.3</version>
|
||||
<visibility>Public</visibility>
|
||||
</metadata>
|
||||
|
||||
Reference in New Issue
Block a user